diff -uNr a/arch/arm/configs/mainstone_defconfig b/arch/arm/configs/mainstone_defconfig --- a/arch/arm/configs/mainstone_defconfig 2004-10-19 05:54:37.000000000 +0800 +++ b/arch/arm/configs/mainstone_defconfig 2005-01-27 09:56:48.000000000 +0800 @@ -1,22 +1,25 @@ # # Automatically generated make config: don't edit +# Linux kernel version: 2.6.9-intc1 +# Wed Jan 12 16:59:54 2005 # CONFIG_ARM=y CONFIG_MMU=y CONFIG_UID16=y CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_IOMAP=y # # Code maturity level options # CONFIG_EXPERIMENTAL=y CONFIG_CLEAN_COMPILE=y -CONFIG_STANDALONE=y CONFIG_BROKEN_ON_SMP=y # # General setup # +CONFIG_LOCALVERSION="" CONFIG_SWAP=y CONFIG_SYSVIPC=y # CONFIG_POSIX_MQUEUE is not set @@ -29,6 +32,7 @@ # CONFIG_EMBEDDED is not set CONFIG_KALLSYMS=y # CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set CONFIG_FUTEX=y CONFIG_EPOLL=y CONFIG_IOSCHED_NOOP=y @@ -36,12 +40,15 @@ CONFIG_IOSCHED_DEADLINE=y CONFIG_IOSCHED_CFQ=y CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SHMEM=y +# CONFIG_TINY_SHMEM is not set # # Loadable module support # CONFIG_MODULES=y -# CONFIG_MODULE_UNLOAD is not set +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y CONFIG_OBSOLETE_MODPARM=y # CONFIG_MODVERSIONS is not set # CONFIG_KMOD is not set @@ -49,7 +56,6 @@ # # System Type # -# CONFIG_ARCH_ADIFCC is not set # CONFIG_ARCH_CLPS7500 is not set # CONFIG_ARCH_CLPS711X is not set # CONFIG_ARCH_CO285 is not set @@ -59,6 +65,7 @@ # CONFIG_ARCH_INTEGRATOR is not set # CONFIG_ARCH_IOP3XX is not set # CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_IXP2000 is not set # CONFIG_ARCH_L7200 is not set CONFIG_ARCH_PXA=y # CONFIG_ARCH_RPC is not set @@ -68,6 +75,8 @@ # CONFIG_ARCH_LH7A40X is not set # CONFIG_ARCH_OMAP is not set # CONFIG_ARCH_VERSATILE_PB is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_H720X is not set # # Intel PXA2xx Implementations @@ -75,10 +84,26 @@ # CONFIG_ARCH_LUBBOCK is not set CONFIG_MACH_MAINSTONE=y # CONFIG_ARCH_PXA_IDP is not set + +# +# Intel PXA27x Errata Fixes +# +CONFIG_PXA27x_E11=y +# CONFIG_PXA27x_E17 is not set +CONFIG_PXA27x_E20=y +CONFIG_PXA27x_E23=y +# CONFIG_PXA27x_E25 is not set +CONFIG_PXA27x_E28=y +CONFIG_PXA27x_E37=y +CONFIG_PXA27x_E38=y CONFIG_PXA27x=y CONFIG_IWMMXT=y # +# h720x Implementations +# + +# # Processor Type # CONFIG_CPU_32=y @@ -100,6 +125,16 @@ # CONFIG_ZBOOT_ROM is not set CONFIG_ZBOOT_ROM_TEXT=0x0 CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CPU_FREQ=y +CONFIG_DVFM=y +CONFIG_FAST_DVFM=y +# CONFIG_CPU_FREQ_PROC_INTF is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set # # PCMCIA/CardBus support @@ -112,10 +147,8 @@ # # At least one math emulation must be selected # -CONFIG_FPE_NWFPE=y -# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_NWFPE is not set # CONFIG_FPE_FASTFPE is not set -# CONFIG_VFP is not set CONFIG_BINFMT_ELF=y # CONFIG_BINFMT_AOUT is not set # CONFIG_BINFMT_MISC is not set @@ -123,13 +156,17 @@ # # Generic Driver Options # +CONFIG_STANDALONE=y CONFIG_PREVENT_FIRMWARE_BUILD=y # CONFIG_FW_LOADER is not set # CONFIG_DEBUG_DRIVER is not set -# CONFIG_PM is not set -# CONFIG_PREEMPT is not set +CONFIG_PM=y +CONFIG_SRAM_ALLOCATE=y +CONFIG_PREEMPT=y +CONFIG_APM=y # CONFIG_ARTHUR is not set -CONFIG_CMDLINE="root=/dev/nfs ip=bootp console=ttyS0,115200 mem=64M" +CONFIG_CMDLINE="root=/dev/mtdblock2 rootfstype=jffs2 ip=192.168.1.101:192.168.1.100::255.255.255.0::eth0:on console=ttyS0,115200 mem=128M" +# CONFIG_CMDLINE="root=/dev/nfs nfsroot=192.168.1.100:/nfs/ ip=192.168.1.101:192.168.1.100::255.255.255.0::eth0:on console=ttyS0,115200n8 mem=128M" CONFIG_LEDS=y CONFIG_LEDS_TIMER=y CONFIG_LEDS_CPU=y @@ -148,6 +185,8 @@ CONFIG_MTD_PARTITIONS=y # CONFIG_MTD_CONCAT is not set CONFIG_MTD_REDBOOT_PARTS=y +# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set +# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set # CONFIG_MTD_CMDLINE_PARTS is not set # CONFIG_MTD_AFS_PARTS is not set @@ -171,10 +210,12 @@ # CONFIG_MTD_CFI_BE_BYTE_SWAP is not set # CONFIG_MTD_CFI_LE_BYTE_SWAP is not set CONFIG_MTD_CFI_GEOMETRY=y -# CONFIG_MTD_CFI_B1 is not set -# CONFIG_MTD_CFI_B2 is not set -CONFIG_MTD_CFI_B4=y -# CONFIG_MTD_CFI_B8 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 is not set CONFIG_MTD_CFI_I2=y # CONFIG_MTD_CFI_I4 is not set @@ -182,10 +223,10 @@ CONFIG_MTD_CFI_INTELEXT=y # CONFIG_MTD_CFI_AMDSTD is not set # CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=y # CONFIG_MTD_RAM is not set # CONFIG_MTD_ROM is not set # CONFIG_MTD_ABSENT is not set -# CONFIG_MTD_OBSOLETE_CHIPS is not set # # Mapping drivers for chip access @@ -194,11 +235,13 @@ # CONFIG_MTD_PHYSMAP is not set # CONFIG_MTD_ARM_INTEGRATOR is not set # CONFIG_MTD_EDB7312 is not set +CONFIG_MTD_PXA27x=y # # Self-contained MTD device drivers # # CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set # CONFIG_MTD_MTDRAM is not set # CONFIG_MTD_BLKMTD is not set @@ -224,6 +267,7 @@ # CONFIG_BLK_DEV_FD is not set # CONFIG_BLK_DEV_LOOP is not set # CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set # CONFIG_BLK_DEV_RAM is not set # @@ -257,6 +301,7 @@ # CONFIG_INET_AH is not set # CONFIG_INET_ESP is not set # CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set # CONFIG_IPV6 is not set # CONFIG_NETFILTER is not set @@ -282,6 +327,7 @@ # QoS and/or fair queueing # # CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set # # Network testing @@ -290,7 +336,50 @@ # CONFIG_NETPOLL is not set # CONFIG_NET_POLL_CONTROLLER is not set # CONFIG_HAMRADIO is not set -# CONFIG_IRDA is not set +CONFIG_IRDA=y + +# +# IrDA protocols +# +# CONFIG_IRLAN is not set +# CONFIG_IRCOMM is not set +# CONFIG_IRDA_ULTRA is not set + +# +# IrDA options +# +# CONFIG_IRDA_CACHE_LAST_LSAP is not set +# CONFIG_IRDA_FAST_RR is not set +# CONFIG_IRDA_DEBUG is not set + +# +# Infrared-port device drivers +# + +# +# SIR device drivers +# +# CONFIG_IRTTY_SIR is not set + +# +# Dongle support +# + +# +# Old SIR device drivers +# +# CONFIG_IRPORT_SIR is not set + +# +# Old Serial dongle support +# + +# +# FIR device drivers +# +# CONFIG_USB_IRDA is not set +# CONFIG_SIGMATEL_FIR is not set +CONFIG_PXA_IRDA=y # CONFIG_BT is not set CONFIG_NETDEVICES=y # CONFIG_DUMMY is not set @@ -325,7 +414,15 @@ # # PCMCIA network device support # -# CONFIG_NET_PCMCIA is not set +CONFIG_NET_PCMCIA=y +# CONFIG_PCMCIA_3C589 is not set +CONFIG_PCMCIA_3C574=y +# CONFIG_PCMCIA_FMVJ18X is not set +# CONFIG_PCMCIA_PCNET is not set +# CONFIG_PCMCIA_NMCLAN is not set +# CONFIG_PCMCIA_SMC91C92 is not set +CONFIG_PCMCIA_XIRC2PS=y +# CONFIG_PCMCIA_AXNET is not set # # Wan interfaces @@ -345,12 +442,14 @@ # # Please see Documentation/ide.txt for help/info on IDE drives # +# CONFIG_BLK_DEV_IDE_SATA is not set CONFIG_BLK_DEV_IDEDISK=y # CONFIG_IDEDISK_MULTI_MODE is not set CONFIG_BLK_DEV_IDECS=y # CONFIG_BLK_DEV_IDECD is not set # CONFIG_BLK_DEV_IDETAPE is not set # CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set # CONFIG_IDE_TASK_IOCTL is not set # CONFIG_IDE_TASKFILE_IO is not set @@ -366,7 +465,45 @@ # # SCSI device support # -# CONFIG_SCSI is not set +CONFIG_SCSI=y +CONFIG_SCSI_PROC_FS=y + +# +# 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=y + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI Transport Attributes +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_DEBUG is not set + +# +# PCMCIA SCSI adapter support +# +# CONFIG_PCMCIA_AHA152X is not set +# CONFIG_PCMCIA_FDOMAIN is not set +# CONFIG_PCMCIA_NINJA_SCSI is not set +# CONFIG_PCMCIA_QLOGIC is not set +# CONFIG_PCMCIA_SYM53C500 is not set # # Fusion MPT device support @@ -375,7 +512,6 @@ # # IEEE 1394 (FireWire) support # -# CONFIG_IEEE1394 is not set # # I2O device support @@ -409,9 +545,9 @@ # CONFIG_GAMEPORT is not set CONFIG_SOUND_GAMEPORT=y CONFIG_SERIO=y -# CONFIG_SERIO_I8042 is not set # CONFIG_SERIO_SERPORT is not set # CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_RAW is not set # # Input Device Drivers @@ -421,6 +557,7 @@ # CONFIG_KEYBOARD_SUNKBD is not set # CONFIG_KEYBOARD_LKKBD is not set # CONFIG_KEYBOARD_XTKBD is not set +CONFIG_KEYPAD_MAINSTONE=y # CONFIG_KEYBOARD_NEWTON is not set # CONFIG_INPUT_MOUSE is not set # CONFIG_INPUT_JOYSTICK is not set @@ -450,7 +587,6 @@ CONFIG_UNIX98_PTYS=y CONFIG_LEGACY_PTYS=y CONFIG_LEGACY_PTY_COUNT=256 -# CONFIG_QIC02_TAPE is not set # # IPMI @@ -460,19 +596,28 @@ # # Watchdog Cards # -# CONFIG_WATCHDOG is not set +CONFIG_WATCHDOG=y +# CONFIG_WATCHDOG_NOWAYOUT is not set + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +CONFIG_SA1100_WATCHDOG=y + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set # CONFIG_NVRAM is not set # CONFIG_RTC is not set -# CONFIG_GEN_RTC is not set +CONFIG_PXA_RTC=y # CONFIG_DTLK is not set # CONFIG_R3964 is not set -# CONFIG_APPLICOM is not set # # Ftape, the floppy tape device driver # -# CONFIG_FTAPE is not set -# CONFIG_AGP is not set # CONFIG_DRM is not set # @@ -484,12 +629,89 @@ # # I2C support # -# CONFIG_I2C is not set +CONFIG_I2C=y +# CONFIG_I2C_CHARDEV is not set + +# +# I2C Algorithms +# +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set +CONFIG_I2C_ALGOPXA=y + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_AMD756 is not set +# CONFIG_I2C_AMD8111 is not set +# CONFIG_I2C_ISA is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_SCx200_ACB is not set +# CONFIG_I2C_PCA_ISA is not set +CONFIG_I2C_ADAP_PXA=y + +# +# Hardware Sensors Chip support +# +CONFIG_I2C_SENSOR=y +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_FSCHER is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83627HF is not set +CONFIG_SENSORS_ADCM2650=y + +# +# Other I2C Chip support +# +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_RTC8564 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 # # Multimedia devices # -# CONFIG_VIDEO_DEV is not set +CONFIG_VIDEO_DEV=y + +# +# Video For Linux +# + +# +# Video Adapters +# +# CONFIG_VIDEO_CPIA is not set +# CONFIG_VIDEO_SAA5246A is not set +# CONFIG_VIDEO_SAA5249 is not set +# CONFIG_TUNER_3036 is not set +# CONFIG_VIDEO_OVCAMCHIP is not set +CONFIG_PXA_CAMERA_MS=y + +# +# Radio Adapters +# +# CONFIG_RADIO_MAESTRO is not set # # Digital Video Broadcasting Devices @@ -523,8 +745,9 @@ # CONFIG_FAT_FS=y CONFIG_MSDOS_FS=y -# CONFIG_VFAT_FS is not set +CONFIG_VFAT_FS=y CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" # CONFIG_NTFS_FS is not set # @@ -552,6 +775,13 @@ CONFIG_JFFS2_FS=y CONFIG_JFFS2_FS_DEBUG=0 # CONFIG_JFFS2_FS_NAND is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +CONFIG_JFFS2_RUBIN=y +# CONFIG_JFFS2_CMODE_NONE is not set +CONFIG_JFFS2_CMODE_PRIORITY=y +# CONFIG_JFFS2_CMODE_SIZE is not set # CONFIG_CRAMFS is not set # CONFIG_VXFS_FS is not set # CONFIG_HPFS_FS is not set @@ -563,15 +793,17 @@ # Network File Systems # CONFIG_NFS_FS=y -# CONFIG_NFS_V3 is not set +CONFIG_NFS_V3=y # 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_EXPORTFS is not set CONFIG_SUNRPC=y # 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 @@ -581,14 +813,29 @@ # # Partition Types # -# CONFIG_PARTITION_ADVANCED is not set +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_EFI_PARTITION is not set # # Native Language Support # CONFIG_NLS=y CONFIG_NLS_DEFAULT="iso8859-1" -# CONFIG_NLS_CODEPAGE_437 is not set +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 @@ -636,7 +883,11 @@ # Graphics support # CONFIG_FB=y +CONFIG_FB_MODE_HELPERS=y CONFIG_FB_PXA=y +CONFIG_FB_PXA_LCD_QVGA=y +# CONFIG_FB_PXA_LCD_VGA is not set +CONFIG_FB_PXA_OVERLAY=y # CONFIG_FB_PXA_PARAMETERS is not set # CONFIG_FB_VIRTUAL is not set @@ -644,10 +895,8 @@ # Console display driver support # # CONFIG_VGA_CONSOLE is not set -# CONFIG_MDA_CONSOLE is not set CONFIG_DUMMY_CONSOLE=y CONFIG_FRAMEBUFFER_CONSOLE=y -CONFIG_PCI_CONSOLE=y # CONFIG_FONTS is not set CONFIG_FONT_8x8=y CONFIG_FONT_8x16=y @@ -663,7 +912,31 @@ # # Sound # -# CONFIG_SOUND is not set +CONFIG_SOUND=y + +# +# Advanced Linux Sound Architecture +# +# CONFIG_SND is not set + +# +# Open Sound System +# +CONFIG_SOUND_PRIME=y +# CONFIG_SOUND_BT878 is not set +# CONFIG_SOUND_FUSION is not set +# CONFIG_SOUND_CS4281 is not set +# CONFIG_SOUND_SONICVIBES is not set +# CONFIG_SOUND_TRIDENT is not set +# CONFIG_SOUND_MSNDCLAS is not set +# CONFIG_SOUND_MSNDPIN is not set +# CONFIG_SOUND_OSS is not set +# CONFIG_SOUND_TVMIXER is not set +# CONFIG_SOUND_AD1980 is not set +CONFIG_SOUND_PXA=y +CONFIG_SOUND_PXA_AC97=y +CONFIG_SOUND_UCB1X00=y +CONFIG_SOUND_UCB1X00_TS=y # # Misc devices @@ -672,24 +945,196 @@ # # USB support # +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set +# CONFIG_USB_OTG is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_EHCI_HCD is not set +CONFIG_USB_OHCI_HCD=y +# CONFIG_USB_UHCI_HCD is not set +# CONFIG_USB_SL811HS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH_TTY is not set +# CONFIG_USB_MIDI is not set +CONFIG_USB_ACM=y +# CONFIG_USB_PRINTER is not set +CONFIG_USB_STORAGE=y +CONFIG_USB_STORAGE_DEBUG=y +# CONFIG_USB_STORAGE_RW_DETECT 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_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set + +# +# USB Human Interface Devices (HID) +# +CONFIG_USB_HID=y +CONFIG_USB_HIDINPUT=y +# CONFIG_HID_FF is not set +# CONFIG_USB_HIDDEV is not set +# CONFIG_USB_AIPTEK is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_POWERMATE is not set +# CONFIG_USB_MTOUCH is not set +# CONFIG_USB_EGALAX is not set +# CONFIG_USB_XPAD is not set +# CONFIG_USB_ATI_REMOTE is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set + +# +# USB Multimedia devices +# +# CONFIG_USB_DABUSB is not set +# CONFIG_USB_VICAM is not set +# CONFIG_USB_DSBR is not set +# CONFIG_USB_IBMCAM is not set +# CONFIG_USB_KONICAWC is not set +# CONFIG_USB_OV511 is not set +# CONFIG_USB_SE401 is not set +# CONFIG_USB_SN9C102 is not set +# CONFIG_USB_STV680 is not set + +# +# USB Network adaptors +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +CONFIG_USB_USBNET=m + +# +# USB Host-to-Host Cables +# +# CONFIG_USB_ALI_M5632 is not set +# CONFIG_USB_AN2720 is not set +# CONFIG_USB_BELKIN is not set +# CONFIG_USB_GENESYS is not set +# CONFIG_USB_NET1080 is not set +# CONFIG_USB_PL2301 is not set + +# +# Intelligent USB Devices/Gadgets +# +CONFIG_USB_ARMLINUX=y +# CONFIG_USB_EPSON2888 is not set +# CONFIG_USB_ZAURUS is not set +# CONFIG_USB_CDCETHER is not set + +# +# USB Network Adapters +# +# CONFIG_USB_AX8817X is not set + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_GENERIC=y +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO 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_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_PL2303 is not set +# CONFIG_USB_SERIAL_SAFE is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OMNINET is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_TIGL 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_LED is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_PHIDGETSERVO is not set # # USB Gadget Support # -# CONFIG_USB_GADGET is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_NET2280 is not set +# CONFIG_USB_GADGET_PXA2XX is not set +CONFIG_USB_GADGET_PXA27X=y +CONFIG_USB_PXA27X=y +# CONFIG_USB_GADGET_GOKU is not set +# CONFIG_USB_GADGET_SA1100 is not set +# CONFIG_USB_GADGET_LH7A40X is not set +# CONFIG_USB_GADGET_DUMMY_HCD is not set +# CONFIG_USB_GADGET_OMAP is not set +# CONFIG_USB_GADGET_DUALSPEED is not set +# CONFIG_USB_ZERO is not set +CONFIG_USB_ETH=y +# CONFIG_USB_ETH_RNDIS is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set + +# +# MMC/SD Card support +# +# CONFIG_MMC is not set # # Kernel hacking # -CONFIG_FRAME_POINTER=y -CONFIG_DEBUG_USER=y -CONFIG_DEBUG_INFO=y CONFIG_DEBUG_KERNEL=y -# CONFIG_DEBUG_SLAB is not set CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_SLAB is not set # CONFIG_DEBUG_SPINLOCK is not set -# CONFIG_DEBUG_WAITQ is not set CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +CONFIG_FRAME_POINTER=y +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_WAITQ is not set CONFIG_DEBUG_ERRORS=y CONFIG_DEBUG_LL=y # CONFIG_DEBUG_ICEDCC is not set @@ -707,6 +1152,7 @@ # # Library routines # +CONFIG_CRC_CCITT=y CONFIG_CRC32=y # CONFIG_LIBCRC32C is not set CONFIG_ZLIB_INFLATE=y diff -uNr a/arch/arm/Kconfig b/arch/arm/Kconfig --- a/arch/arm/Kconfig 2004-10-19 05:54:31.000000000 +0800 +++ b/arch/arm/Kconfig 2005-01-27 09:56:48.000000000 +0800 @@ -357,7 +357,7 @@ config CPU_FREQ bool "Support CPU clock change (EXPERIMENTAL)" - depends on (ARCH_SA1100 || ARCH_INTEGRATOR) && EXPERIMENTAL + depends on (ARCH_SA1100 || ARCH_INTEGRATOR || MACH_MAINSTONE) && EXPERIMENTAL help CPU clock scaling allows you to change the clock speed of the running CPU on the fly. This is a nice method to save battery power, @@ -393,7 +393,17 @@ If in doubt, say Y. -if (CPU_FREQ_INTEGRATOR) || (CPU_FREQ_SA1110) || (CPU_FREQ_SA1100) +config DVFM + bool + depends on CPU_FREQ + default y + +config FAST_DVFM + bool + depends on DVFM + default y + +if (CPU_FREQ_INTEGRATOR) || (CPU_FREQ_SA1110) || (CPU_FREQ_SA1100) || (DVFM) source "drivers/cpufreq/Kconfig" @@ -478,6 +488,13 @@ will issue the hlt instruction if nothing is to be done, thereby sending the processor to sleep and saving power. +config SRAM_ALLOCATE + bool "PXA27x SRAM Allocation Support" if ARCH_PXA + depends on ARCH_PXA + help + If you say Y here , the system will support dynamic allocation into + the sram region. + config PREEMPT bool "Preemptible Kernel (EXPERIMENTAL)" depends on CPU_32 && EXPERIMENTAL diff -uNr a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S --- a/arch/arm/kernel/entry-armv.S 2004-10-19 05:53:46.000000000 +0800 +++ b/arch/arm/kernel/entry-armv.S 2005-01-27 09:56:48.000000000 +0800 @@ -638,7 +638,22 @@ and \irqstat, \irqstat, \irqnr clz \irqnr, \irqstat rsb \irqnr, \irqnr, #(31 - PXA_IRQ_SKIP) +#ifdef CONFIG_PXA27x + b 1002f +#endif 1001: +#ifdef CONFIG_PXA27x + mrc p6, 0, \irqstat, c6, c0, 0 @ ICIP2 + mrc p6, 0, \irqnr, c7, c0, 0 @ ICMR2 + ands \irqstat, \irqstat, \irqnr + beq 1002f + rsb \irqnr, \irqstat, #0 + and \irqstat, \irqstat, \irqnr + clz \irqnr, \irqstat + rsb \irqnr, \irqnr, #31 + add \irqnr, \irqnr, #(32 - PXA_IRQ_SKIP) +1002: +#endif .endm .macro irq_prio_table diff -uNr a/arch/arm/mach-pxa/cpu-freq-pxa27x.c b/arch/arm/mach-pxa/cpu-freq-pxa27x.c --- a/arch/arm/mach-pxa/cpu-freq-pxa27x.c 1970-01-01 08:00:00.000000000 +0800 +++ b/arch/arm/mach-pxa/cpu-freq-pxa27x.c 2005-01-27 09:56:48.000000000 +0800 @@ -0,0 +1,678 @@ +/* + * CPUfreq driver for PXA27x + * + * cpufreq driver for PXA27x by Chao Xie + * Copyright (C) 2004, Intel Corporation + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cpu-freq-voltage-pxa27x.h" + +#define N_NUM 8 +#define L_NUM 31 + +#define CPUFREQ_SET_BASE (0x1 << 8) +#define CPUFREQ_SET_FREQ CPUFREQ_SET_BASE << 0x1 +#define CPUFREQ_SET_B CPUFREQ_SET_BASE << 0x2 +#define CPUFREQ_SET_T_HT CPUFREQ_SET_BASE << 0x3 +#define CPUFREQ_SET_A CPUFREQ_SET_BASE << 0x4 + +#define MSC0_MASK 0x7ff07ff0 +#define DRI_MASK 0xfff + +struct pxa27x_mem_set { + unsigned int memc_clock; + unsigned int msc0; + unsigned int dri; +}; + +static struct pxa27x_mem_set mem_op_val[] = { + {13000, 0x11101110, 0x002}, + {91000, 0x12701270, 0x015}, + {104000, 0x12801280, 0x018}, + {208000, 0x15d015d0, 0x031}, + {0, 0, 0}, +}; + +static unsigned int cpufreq_matrix[N_NUM-1][L_NUM-1]; +static unsigned int cpufreq_cur; +static DECLARE_MUTEX (pxa27x_freq_sem); + +struct cpufreq_info { + unsigned int l; + unsigned int n2; + unsigned int b; + unsigned int t; + unsigned int a; +}; + +static struct cpufreq_info cpufreq_mode; + +extern unsigned int get_clk_frequency_khz(int); +extern int pxa27x_set_voltage(unsigned int, unsigned int, unsigned int); +extern unsigned int pxa27x_get_voltage(void); +extern struct cpufreq_governor cpufreq_gov_performance; + +static int pxa27x_cpufreq_governor(struct cpufreq_policy *policy, + unsigned int event) +{ + return 0; +} + +static struct cpufreq_governor pxa27x_cpufreq_gov = { + .name = "pxa27x_governor", + .governor = pxa27x_cpufreq_governor, + .owner = THIS_MODULE, +}; + +static inline int set_mode(unsigned int l, unsigned int n2, unsigned int b, + unsigned int t, unsigned int a) +{ + unsigned int ret = 0; + + if (n2 == cpufreq_mode.n2 && l == cpufreq_mode.l && b == cpufreq_mode.b + && t == cpufreq_mode.t && a == cpufreq_mode.a) + return 0; + if (l == cpufreq_mode.l && n2 == cpufreq_mode.n2) { + if (b != cpufreq_mode.b) + ret |= CPUFREQ_SET_B; + if (t != cpufreq_mode.t) + ret |= CPUFREQ_SET_T_HT; + if (a != cpufreq_mode.a) + ret |= CPUFREQ_SET_A; + } + else { + ret |= CPUFREQ_SET_FREQ; + cpufreq_mode.l = l; + cpufreq_mode.n2 = n2; + } + cpufreq_mode.b = b; + cpufreq_mode.t = t; + cpufreq_mode.a = a; + return ret; +} + +#ifdef CONFIG_FAST_DVFM +void pxa27x_setspeed(unsigned int CLKCFGValue) +#else +static void pxa27x_setspeed(unsigned int CLKCFGValue) +#endif +{ + unsigned long flags; + unsigned int unused; + + /* NOTE: IRQs are already turned off in dpm idle, so it is not + necessary to do it here. */ + local_irq_save(flags); + + __asm__ __volatile__(" \n\ + ldr r4, [%1] @load MDREFR \n\ + mcr p14, 0, %2, c6, c0, 0 @ set CCLKCFG[FCS] \n\ + ldr r5, =0xe3dfefff \n\ + and r4, r4, r5 \n\ + str r4, [%1] @restore \n\ + " + : "=&r" (unused) + : "r" (&MDREFR), "r" (CLKCFGValue) + : "r4", "r5"); + + /* NOTE: if we don't turn off IRQs up top, there is no point + to restoring them here. */ + local_irq_restore(flags); +} +#ifdef CONFIG_FAST_DVFM +EXPORT_SYMBOL(pxa27x_setspeed); +#endif + +#ifdef CONFIG_FAST_DVFM +extern void pxafb_wait_for_eof(unsigned int); +#else +extern void pxafb_wait_for_eof(void); +#endif + +static void pxa27x_change_freq(unsigned int CLKCFGvalue) +{ +#ifdef CONFIG_FAST_DVFM + pxafb_wait_for_eof(CLKCFGvalue); +#else + pxafb_wait_for_eof(); + pxa27x_setspeed(CLKCFGvalue); +#endif +} + +static void pxa27x_set_mem_before_freq(unsigned int memc_clock, + unsigned int *pmdrefr, unsigned int *pmsc0, unsigned int *pdri) +{ + int i = 0; + + while(mem_op_val[i].memc_clock < memc_clock && mem_op_val[i].memc_clock != 0) + i++; + /* if current msc0 > next msc0, change after */ + if ( (MSC0 & MSC0_MASK) > mem_op_val[i].msc0) { + *pmsc0 = mem_op_val[i].msc0; + } + else { + *pmsc0 = 0; + MSC0 = (MSC0 & ~MSC0_MASK) | mem_op_val[i].msc0; + } + /* if current dri > next dri, change after */ + if ( (MDREFR & DRI_MASK) > mem_op_val[i].dri) { + *pdri = mem_op_val[i].dri; + } + else { + *pdri = 0; + MDREFR = (MDREFR & ~DRI_MASK) | mem_op_val[i].dri; + } + + if (memc_clock > 104000) { + /* K1DB2, K2DB2 set; memc/2 for sdram */ + /* K0DB4 set; memc/4 for flash */ + MDREFR |= ((1 << 14) | (1 << 17) | (1 << 19) | (1 << 29)); + *pmdrefr = 0; + } + else if (memc_clock > 52000) { + MDREFR |= (1 << 14); + /* K1DB2, K2DB2 will be clear; memc for sdram */ + /* K0DB4 will be clear; memc/2 for flash */ + *pmdrefr = (1 << 17) | (1 << 19) | (1 << 29); + } + else { + /* memc for flash and sdram*/ + *pmdrefr = (1 << 14) | (1 << 17) | (1 << 19) | (1 << 29); + } + +} + +void pxa27x_set_mem_after_freq(unsigned int mdrefr, unsigned int msc0, + unsigned int dri) +{ + if (mdrefr || dri) + MDREFR = (MDREFR & (~DRI_MASK | mdrefr)) | dri; + if (msc0) + MSC0 = (MSC0 & ~MSC0_MASK) | msc0; +} + +/* turbo_mode_flag = 2 : indicates Turbo + * turbo_mode_flag = 1 : indicates HalfTurbo + */ +static void pxa27x_set_core_freq(unsigned int l, unsigned int n2, + unsigned int fast_bus_mode_flag, + unsigned int turbo_mode_flag, + unsigned int memc_clock_flag, + unsigned int relation) +{ + int CLKCFGValue = 0; + int turbo_mode = 0; + int memc_clock, m, l_pll; + unsigned int mdrefr, msc0, dri; + + if (turbo_mode_flag == 2) + turbo_mode = 1; + else if (turbo_mode_flag == 1) + turbo_mode = 1 << 2; + + m = (l <= 10) ? 1 : (l <= 20) ? 2 : 4; + l_pll = l*13000; + memc_clock = (!memc_clock_flag) ? (l_pll/m) : ((fast_bus_mode_flag) ? l_pll : (l_pll/2)); + pxa27x_set_mem_before_freq(memc_clock, &mdrefr, &msc0, &dri); + /* SDRAM can not be greater than 104Mhz, and flash can not be greater + than 52Mhz*/ + if (relation & (CPUFREQ_SET_FREQ | CPUFREQ_SET_B | CPUFREQ_SET_A)) { + CCCR = (n2 << 7) | l | (memc_clock_flag << 25); + + /* set up the new CLKCFG value, for an new frequency */ + /* 0x2 is used to set FC bit in CLKCFG. */ + CLKCFGValue = ( 0x2 | (fast_bus_mode_flag << 3) | turbo_mode); + } + else { + CLKCFGValue = (fast_bus_mode_flag << 3) | turbo_mode; + } + + /* + * Program the CLKCFG (CP14, reigster 6) for frequency change + */ + pxa27x_change_freq(CLKCFGValue); + pxa27x_set_mem_after_freq(mdrefr, msc0, dri); +} + +unsigned int pxa27x_read_clkcfg(void) +{ + unsigned int value=0; + unsigned int un_used; + + __asm__ __volatile__("mrc p14, 0, %1, c6, c0, 0" :"=&r" (un_used) + :"r" (value)); + + return value; +} +EXPORT_SYMBOL_GPL(pxa27x_read_clkcfg); + +#define CCCR_CPDIS_BIT_ON (1 << 31) +#define CCCR_PPDIS_BIT_ON (1 << 30) +#define CCCR_PLL_EARLY_EN_BIT_ON (1 << 26) +#define CCCR_LCD_26_BIT_ON (1 << 27) + +static int old_clkcfg; +static int old_mdrefr; +static int old_msc0; +static int enter_13M(void) +{ + int cccr; + int clkcfg; + + if (CCCR & (1 << 31)) + return 1; + old_clkcfg = pxa27x_read_clkcfg(); + old_mdrefr = MDREFR; + old_msc0 = MSC0; +#ifdef CONFIG_13M_TWOSTEP + if (old_clkcfg & 0x5) { + pxa27x_setspeed(old_clkcfg & 0x8); + cccr = CCCR; + while (pxa27x_read_clkcfg() != (old_clkcfg & 0x8)) + ; + + } +#endif + cccr = CCCR; + cccr |= CCCR_CPDIS_BIT_ON; + cccr &= ~CCCR_PPDIS_BIT_ON; + cccr |= CCCR_LCD_26_BIT_ON; + CCCR = cccr; + LCCR4 |= (1 << 25); + pxa27x_setspeed(0x2); + MDREFR = (MDREFR & ~((1 << 14) | (1 << 17) | (1 << 19) | (1 << 29) | DRI_MASK)) | 0x002; + /* maybe this can optmize 13Mhz */ + MSC0 = (MSC0 & ~MSC0_MASK) | 0x11101110; + return 0; +} + +/* using new L, N2 to enbale CPDIS first, then change frequency */ +static void exit_13M_change_freq(unsigned int l, unsigned int n2, + unsigned int fast_bus_mode_flag, + unsigned int turbo_mode_flag, + unsigned int memc_clock_flag) +{ + int cccr, ccsr, clkcfg; + unsigned int time_delay; + + if (!(CCCR & (1 << 31))) + return; + + clkcfg = 0x2; + if (turbo_mode_flag) + clkcfg |= 0x1; + if (fast_bus_mode_flag) + clkcfg |= (0x1 << 3); + + cccr = CCCR; + cccr |= (CCCR_CPDIS_BIT_ON | CCCR_PLL_EARLY_EN_BIT_ON); + /* clear L and 2N, A bit */ + cccr &= ~(0x3ffffff); + cccr |= l | n2 << 7 | (memc_clock_flag << 25); + CCCR = cccr; + + /* Step 2 */ + cccr = CCCR; + if ((cccr & 0x84000000) != 0x84000000) { /* CPDIS, pll_early_en */ + printk("DPM: Warning: CPDIS or PLL_EARLY_EN not on\n"); + } + time_delay = OSCR; + while ((OSCR - time_delay) < 390) + ; + /* Step 3 */ + while ((CCSR & 0x30000000) == 0x30000000) + break; + /* Step 4: NOP */ + + /* Step 5 */ + /* Now clear the PLL disable bits */ + /* But leave EARLY_EN on; it will be cleared by the frequency change */ + cccr &= ~(CCCR_CPDIS_BIT_ON | CCCR_PPDIS_BIT_ON); /* Not ON */ + cccr |= CCCR_PLL_EARLY_EN_BIT_ON; + CCCR = cccr; + MDREFR = old_mdrefr; + MSC0 = old_msc0; +#ifdef CONFIG_13M_TWOSTEP + pxa27x_setspeed(0x2); +#endif + LCCR4 &= ~((1 << 25)); +#ifdef CONFIG_13M_TWOSTEP + cccr = CCCR; + while(pxa27x_read_clkcfg() != 0x2) + ; + if (turbo_mode_flag || fast_bus_mode_flag) { + CKEN &= ~CKEN16_LCD; + pxa27x_setspeed(clkcfg); + CKEN |= CKEN16_LCD; + } +#else + pxa27x_setspeed(clkcfg); +#endif +} + +void pxa27x_cpufreq_restore(void) +{ + int CLKCFGValue = 0; + int turbo_mode = 0; + int m, l_pll, memc_clock; + unsigned int mdrefr, msc0, dri; + + if (cpufreq_cur == 13000) { + enter_13M(); + return; + } + m = (cpufreq_mode.l <= 10) ? 1 : (cpufreq_mode.l <= 20) ? 2 : 4; + l_pll = cpufreq_mode.l*13000; + memc_clock = (!cpufreq_mode.a) ? (l_pll/m) : ((cpufreq_mode.b) ? l_pll : (l_pll/2)); + pxa27x_set_mem_before_freq(memc_clock, &mdrefr, &msc0, &dri); + if (cpufreq_mode.t == 2) + turbo_mode = 1; + else if (cpufreq_mode.t == 1) + turbo_mode = 1 << 2; + + CCCR = (cpufreq_mode.n2 << 7) | cpufreq_mode.l | (cpufreq_mode.a << 25); + CLKCFGValue = ( 0x2 | (cpufreq_mode.b << 3) | turbo_mode); + pxa27x_setspeed(CLKCFGValue); + pxa27x_set_mem_after_freq(mdrefr, msc0, dri); +} +EXPORT_SYMBOL_GPL(pxa27x_cpufreq_restore); + +static int pxa27x_cpufreq_init(struct cpufreq_policy *policy) +{ + unsigned int n2, l; + + /* we only have one CPU */ + if (policy->cpu != 0) + return -EINVAL; + /* init policy */ + policy->cur = cpufreq_cur; + /* we use CPU limits as default */ + policy->min = PXA27X_MIN_FREQ; + policy->max = PXA27X_MAX_FREQ; + policy->cpuinfo.min_freq = PXA27X_MIN_FREQ; + policy->cpuinfo.max_freq = PXA27X_MAX_FREQ; + policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; + /* Only simple governer is needed. Directly using pxa27x + * governor. In fact it is a NULL governor. + */ + policy->governor = &pxa27x_cpufreq_gov; + /* based on manual to set freq table */ + memset(&cpufreq_matrix, 0 ,sizeof(cpufreq_matrix)); + for (n2 = 2; n2 <= N_NUM; n2++) { + for (l = 2; l <= L_NUM; l++) { + cpufreq_matrix[n2-2][l-2] = n2*l*6500; + if( cpufreq_matrix[n2-2][l-2] > PXA27X_MAX_FREQ ) + cpufreq_matrix[n2-2][l-2] = 0; + } + } + return 0; +} + +static int pxa27x_cpufreq_exit(struct cpufreq_policy *policy) +{ + return 0; +} + +/* the frequency is gotten form L, N base on cpufreq_matrix */ +static int pxa27x_cpufreq_verify(struct cpufreq_policy *policy) +{ + if (policy->min < PXA27X_MIN_FREQ || policy->max > PXA27X_MAX_FREQ) + return -1; + return 0; +} + +/* every time invoke the function, frequency is set even nothing has changed */ +static int pxa27x_cpufreq_target (struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + unsigned int n2, l, b, t = 0, a; + struct cpufreq_freqs freqs; + + freqs.cpu = 0; + freqs.old = cpufreq_cur; + freqs.new = target_freq; + + if (target_freq == 13000) { + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + enter_13M(); + cpufreq_cur = target_freq; + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + calibrate_delay(); + return 0; + } + l = cpufreq_mode.l; + n2 = cpufreq_mode.n2; + b = cpufreq_mode.b; + t = cpufreq_mode.t; + a = cpufreq_mode.a; + +/* System hang when enabling RUN/TURBO switching at 520MHZ */ +#ifdef CONFIG_PXA27x_E37 + if (target_freq == 520000 && (relation & CPUFREQ_SET_T_HT)) { + printk("Workround for E37 about enabling RUN/TURBO switching at 520MHZ.\n If you want to disable workround, DO NOT set \"CONFIG_PXA27x_E37=y\"\n"); + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + enter_13M(); + exit_13M_change_freq(l, n2, b, t, a); + cpufreq_cur = target_freq; + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + calibrate_delay(); + return 0; + } +#endif + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + /* Using CCCR to make sure it is 13M */ + if ((cpufreq_cur == 13000) && (CCSR & (1 << 31))) + exit_13M_change_freq(l, n2, b, t, a); + else + pxa27x_set_core_freq(l, n2, b, t, a, relation); + cpufreq_cur = target_freq; + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + calibrate_delay(); + return 0; + +} +static unsigned int pxa27x_get_cur_freq(unsigned int cpu) +{ + if (cpu) + return 0; + return get_clk_frequency_khz(0); +} + +static ssize_t show_available_freqs (struct cpufreq_policy *policy, char *buf) +{ + unsigned int n2, l, count = 0; + + count += sprintf(&buf[count], "%s\t%s\t%s\n", "L", "2N", "Freq"); + for (n2 = 2; n2 <= N_NUM ; n2++) { + for (l = 2; l <= L_NUM; l++) { + if (cpufreq_matrix[n2-2][l-2] >= policy->min + && cpufreq_matrix[n2-2][l-2] <= policy->max) + count += sprintf(&buf[count], "%d\t%d\t%d\n", l, + n2, cpufreq_matrix[n2-2][l-2]); + } + } + return count; +} + +static struct freq_attr pxa27x_available_freqs = { + .attr = { .name = "scaling_available_frequencies", .mode = 0444 }, + .show = show_available_freqs, +}; + +static ssize_t show_freq_voltage (struct cpufreq_policy *policy, char *buf) +{ + int i = 0, count = 0; + + count += sprintf(&buf[count], "%s\t%s\n", "Freq", "Vol"); + while (ipm_fv_table[i].freq != 0 ) { + count += sprintf(&buf[count], "%u\t%u\n", ipm_fv_table[i].freq, ipm_fv_table[i].voltage); + i++; + } + return count; +} + +static struct freq_attr pxa27x_freq_voltage = { + .attr = { .name = "show_frequency_and_voltage", .mode = 0444 }, + .show = show_freq_voltage, +}; + +static ssize_t show_cpu_voltage (struct cpufreq_policy *policy, char *buf) +{ + unsigned int vol, count; + + vol = pxa27x_get_voltage(); + count = sprintf(&buf[0], "%dmv\n", vol); + return count; +} + +static ssize_t store_cpu_voltage (struct cpufreq_policy *policy, + const char *buf, size_t count) +{ + unsigned int vol, ret, n; + + n = sscanf(buf, "%u", &vol); + ret = pxa27x_set_voltage(cpufreq_cur, vol, 0); + if (ret < 0) { + printk(KERN_ERR "Error:Wrong voltage"); + } + return count; +} + +static struct freq_attr pxa27x_cpu_voltage = { + .attr = { .name = "cpu-voltage", .mode = 0644 }, + .show = show_cpu_voltage, + .store = store_cpu_voltage, +}; + +static struct freq_attr* pxa27x_attr[] = { + &pxa27x_available_freqs, + &pxa27x_cpu_voltage, + &pxa27x_freq_voltage, + NULL, +}; + +static struct cpufreq_driver pxa27x_cpufreq_driver = { + .name = "pxa27x", /* there's a 16 char limit */ + .flags = CPUFREQ_CONST_LOOPS, + .init = pxa27x_cpufreq_init, + .exit = pxa27x_cpufreq_exit, + .verify = pxa27x_cpufreq_verify, + .target = pxa27x_cpufreq_target, + .get = pxa27x_get_cur_freq, + .attr = pxa27x_attr, + .owner = THIS_MODULE, +}; + +/* + * validate l, n2 , b, t +*/ +int pxa27x_validate(unsigned int l, unsigned int n2, unsigned int b, + unsigned int t, unsigned int a) +{ + printk("L:%u, 2N:%u, b:%u, t:%u\n", l, n2 , b, t); +#ifdef CONFIG_PXA27x_E38 + if (t == 1) { + printk("HaltTurbo mode is disabled because it may hang the system.\n If you want to enbale it, DO NOT set \"CONFIG_PXA27x_E38=y\"\n"); + return -EINVAL; + } +#endif + /* This is 13M mode */ + if (b > 1 || t > 2 || a > 1) + return -EINVAL; + if (l == 1 && n2 == 2 && b == 0 && t == 0) + return 0; + if ((n2 > N_NUM) || (n2 < 2)) + return -EINVAL; + if (l < 2 || l > L_NUM) + return -EINVAL; + /* when B = 1, Max L = 16 */ + if (b && l > 16) + return -EINVAL; + if (t == 1 && (n2 != 6 && n2 != 8)) + return -EINVAL; + if (cpufreq_matrix[n2-2][l-2] == 0) + return -EINVAL; + return 0; +} + +EXPORT_SYMBOL(pxa27x_validate); +/* + * The interface for other module to change the frequency. + * If freq, n2, turbo, b, a are same as before, no frequency change occurs. + */ +int pxa27x_set_cpufreq(unsigned int l, unsigned int n2, unsigned int b, + unsigned int t, unsigned int a) +{ + struct cpufreq_policy policy; + int ret = 0; + unsigned int freq; + + down(&pxa27x_freq_sem); + ret = set_mode(l, n2, b, t, a); + if (!ret) { + up(&pxa27x_freq_sem); + return ret; + } + policy.cpu = 0; + freq = l*((t == 0)? 13000 : ((t == 2)? 6500*n2 : 3250*n2)); + printk("Set frequency to %u with L:%u, 2N:%u, B:%u, %s, A:%u\n", freq, + l, n2, b, (t==0)? "Run mode":(t==2)? "Turbo Mode":"Half Turbo mode", a); + ret = cpufreq_driver_target(&policy, freq, ret); + up(&pxa27x_freq_sem); + return ret; +} + +EXPORT_SYMBOL(pxa27x_set_cpufreq); + +static int __init pxa27x_init_cpufreq(void) +{ + unsigned int clkcfg, ccsr, cccr, l, n2, b, t, a; + int ret; + + ret = cpufreq_register_governor(&pxa27x_cpufreq_gov); + if (ret) + return ret; + clkcfg = pxa27x_read_clkcfg(); + ccsr = CCSR; + cccr = CCCR; + cpufreq_cur = get_clk_frequency_khz(0); + l = ccsr & 0x1F; + n2 = (ccsr >> 7) & 0xF; + b = clkcfg & (0x1 << 3); + t = (clkcfg & 0x1)? 2: (clkcfg & (0x1 << 2))? 1:0; + a = (cccr >> 25) & 0x1; + set_mode(l, n2, b, t, a); + return cpufreq_register_driver(&pxa27x_cpufreq_driver); +} + +static void __exit pxa27x_exit_cpufreq(void) +{ + cpufreq_unregister_driver(&pxa27x_cpufreq_driver); + cpufreq_unregister_governor(&pxa27x_cpufreq_gov); +} + +MODULE_AUTHOR ("Chao Xie "); +MODULE_DESCRIPTION ("cpufreq driver for Intel pxa27x."); +MODULE_LICENSE ("GPL"); + +late_initcall(pxa27x_init_cpufreq); +module_exit(pxa27x_exit_cpufreq); + diff -uNr a/arch/arm/mach-pxa/cpu-freq-voltage-pxa27x.h b/arch/arm/mach-pxa/cpu-freq-voltage-pxa27x.h --- a/arch/arm/mach-pxa/cpu-freq-voltage-pxa27x.h 1970-01-01 08:00:00.000000000 +0800 +++ b/arch/arm/mach-pxa/cpu-freq-voltage-pxa27x.h 2005-01-27 09:56:48.000000000 +0800 @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2003-2004 Intel Corporation. + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * + */ + +#ifndef CPU_FREQ_VOLTAGE_PXA27x_H +#define CPU_FREQ_VOLTAGE_PXA27x_H + +#define PXA27X_MAX_VOL 1500 /* in mV. */ +#define PXA27X_MIN_VOL 900 /* in Mv. */ +#define PXA27X_DEFAULT_VOL 1500 /* the default voltage. */ + +#define PXA27X_MIN_FREQ 13000 +#define PXA27X_MAX_FREQ 624000 + +struct fv_table { + unsigned int freq; + unsigned int voltage; +}; +extern struct fv_table ipm_fv_table[]; +#endif diff -uNr a/arch/arm/mach-pxa/cpu-voltage-pxa27x.c b/arch/arm/mach-pxa/cpu-voltage-pxa27x.c --- a/arch/arm/mach-pxa/cpu-voltage-pxa27x.c 1970-01-01 08:00:00.000000000 +0800 +++ b/arch/arm/mach-pxa/cpu-voltage-pxa27x.c 2005-01-27 09:56:48.000000000 +0800 @@ -0,0 +1,199 @@ +/* + * + * Bulverde voltage change driver. + * + * Author: Cain Yuan + * + * Upgrade for 2.6 kernel by Chao Xie + * + * Copyright (C) 2003-2004 Intel Corporation. + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "cpu-freq-voltage-pxa27x.h" + +/* + * Transfer desired mv to required DAC value. + * Vcore = 1.5v - ( 587uv * DACIn ) + */ +#define MV2DAC(mv) ((1500-mv)*1000/587) +struct fv_table ipm_fv_table[] = { + {26000, 900}, + {104000, 1100}, + {208000, 1200}, + {312000, 1300}, + {416000, 1400}, + {520000, 1500}, + {624000, 1500}, + {0, 0} +}; + +static unsigned int cpuvoltage_cur = PXA27X_DEFAULT_VOL; +static DECLARE_MUTEX (pxa27x_voltage_sem); + +extern void vm_setvoltage(unsigned int); +extern void vm_pre_setvoltage(unsigned int); + +static unsigned int mv2DAC(unsigned int mv) +{ + if (mv > PXA27X_MAX_VOL) + return MV2DAC(mv); + if (mv < PXA27X_MIN_VOL) + return MV2DAC(mv); + return MV2DAC(mv); +} + +static int check_voltage(unsigned int freq, unsigned int vol) +{ + int i = 0; + + printk("check:freq %u, vol %u",freq, vol); + while (ipm_fv_table[i].freq != 0 ) { + if (freq <= ipm_fv_table[i].freq) { + if(vol < ipm_fv_table[i].voltage) { + printk(KERN_WARNING "Warnning: CPU voltage is lower than standard, may cause error.\n"); + return 1; + } + else + return 0; + } + i++; + } + printk(KERN_ERR "Error: Can not find the voltage for frequency %u.\n", freq); + return -EINVAL; +} + +unsigned int pxa27x_find_voltage(unsigned int freq) +{ + int i = 0; + + while (ipm_fv_table[i].freq != 0 ) { + if (freq <= ipm_fv_table[i].freq) { + return ipm_fv_table[i].voltage; + } + i++; + } + return 0; +} +EXPORT_SYMBOL_GPL(pxa27x_find_voltage); + +/* + * According to bulverde's manual, set the core's voltage. + * mv == 0 indicates using default voltage arrcording frequency. + * If mv == cpuvoltage_cur, no voltage change happens. Because there + * is no convenient way of reading back voltage, it is important to mainstain + * cpuvoltage_cur correctly. + * combo == 0 means this is seperated voltage change process, otherwise it is + * combo change. + */ +int pxa27x_set_voltage(unsigned int freq, unsigned int mv, unsigned int combo) +{ + int ret; + + /* + * Currently voltage change is seperated with frequency change. + */ + if ((mv < PXA27X_MIN_VOL) && (mv > PXA27X_MAX_VOL) && mv != 0) + return -EINVAL; + if (mv == 0) { + if ((mv = pxa27x_find_voltage(freq)) == 0) + return -EINVAL; + } + else { + ret = check_voltage(freq, mv); + if (ret < 0) + return ret; + } + down(&pxa27x_voltage_sem); + if (mv == cpuvoltage_cur) { + up(&pxa27x_voltage_sem); + return 1; + } + if (!combo) + vm_setvoltage(mv2DAC(mv)); + else + vm_pre_setvoltage(mv2DAC(mv)); + cpuvoltage_cur = mv; + up(&pxa27x_voltage_sem); + return 0; +} +EXPORT_SYMBOL_GPL(pxa27x_set_voltage); + +unsigned int pxa27x_get_voltage(void) +{ + int vol; + + down(&pxa27x_voltage_sem); + vol = cpuvoltage_cur; + up(&pxa27x_voltage_sem); + + return vol; +} +EXPORT_SYMBOL_GPL(pxa27x_get_voltage); +#if 0 +int pxa27x_combo_set_voltage(unsigned int freq) +{ + unsigned int mv; + + if ((mv = pxa27x_find_voltage(freq)) == 0) + return -EINVAL; + vm_pre_setvoltage(mv2DAC(mv)); + cpuvoltage_cur = mv; + return mv; +} +/* This is the notifier of seperated voltage change for frequency change */ +static int voltage_cpufreq_notifier(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct cpufreq_freqs *freq = data; + + if ((val == CPUFREQ_PRECHANGE && freq->old < freq->new) || + (val == CPUFREQ_POSTCHANGE && freq->old > freq->new)) { + /* using default voltage according to the frequency */ + return pxa27x_set_voltage(freq->new, 0); + } + return 0; +} + +static struct notifier_block voltage_cpufreq_notifier_block = { + .notifier_call = voltage_cpufreq_notifier +}; + +/* This is the notifier of combo voltage change for frequency change */ +static int combo_voltage_cpufreq_notifier(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct cpufreq_freqs *freq = data; + + if (val == CPUFREQ_PRECHANGE) { + return pxa27x_combo_set_voltage(freq->new); + } + return 0; +} + +static struct notifier_block combo_voltage_cpufreq_notifier_block = { + .notifier_call = combo_voltage_cpufreq_notifier +}; + + +static int __init pxa27x_voltage_init(void) +{ + int ret; + + ret = cpufreq_register_notifier(&voltage_cpufreq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); + return ret; +} +late_initcall(pxa27x_voltage_init); +#endif diff -uNr a/arch/arm/mach-pxa/generic.c b/arch/arm/mach-pxa/generic.c --- a/arch/arm/mach-pxa/generic.c 2004-10-19 05:53:45.000000000 +0800 +++ b/arch/arm/mach-pxa/generic.c 2005-01-27 09:56:48.000000000 +0800 @@ -219,6 +219,63 @@ .id = 2, }; +static struct resource pxacamera_resources[] = { + [0] = { + .start = 0x50000000, + .end = 0x50000fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_CAMERA, + .end = IRQ_CAMERA, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 pxa_camera_dmamask = 0xffffffffUL; + +static struct platform_device pxacamera_device = { + .name = "pxa2xx-camera", + .id = -1, + .dev = { + .dma_mask = &pxa_camera_dmamask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(pxacamera_resources), + .resource = pxacamera_resources, +}; + +static struct resource pxaac97_resources[] = { + [0] = { + .start = 0x40500000, + .end = 0x40500fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_AC97, + .end = IRQ_AC97, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 pxa_ac97_dmamask = 0xffffffffUL; + +static struct platform_device pxaac97_device = { + .name = "pxa2xx-ac97", + .id = -1, + .dev = { + .dma_mask = &pxa_ac97_dmamask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(pxaac97_resources), + .resource = pxaac97_resources, +}; + +static struct platform_device pxaflash_device = { + .name = "pxa27x-flash", + .id = -1, +}; + static struct platform_device *devices[] __initdata = { &pxamci_device, &udc_device, @@ -226,6 +283,9 @@ &ffuart_device, &btuart_device, &stuart_device, + &pxacamera_device, + &pxaac97_device, + &pxaflash_device, }; static int __init pxa_init(void) diff -uNr a/arch/arm/mach-pxa/ipmc.c b/arch/arm/mach-pxa/ipmc.c --- a/arch/arm/mach-pxa/ipmc.c 1970-01-01 08:00:00.000000000 +0800 +++ b/arch/arm/mach-pxa/ipmc.c 2005-01-27 09:56:48.000000000 +0800 @@ -0,0 +1,562 @@ +/* + * linux/arch/arm/mach-pxa/ipmc.c + * + * Provide ioctl interface to application, to specify more detailed info and change + * system's behaviour. + * + * Copyright (C) 2003-2004 Intel Corporation. + * + * Author: Cain Yuan + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * + * If wana use it please use "mknod /dev/ipmc c 10 90" to create the dev file. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ipmc.h" + +extern unsigned int pxa27x_validate(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int); +extern unsigned int pxa27x_read_clkcfg(void); +extern int pxa27x_set_cpufreq(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int); +extern int pm_updatetimer(int); +extern int pxa27x_set_voltage(unsigned int, unsigned int, unsigned int); +extern unsigned int pxa27x_get_voltage(void); +#if 0 +extern void ipm_start_pmu(); +#endif + +static int ipmq_get(struct ipm_event *); +static int ipmq_put(struct ipm_event *); +//static int ipmq_empty(void); +static int ipmq_clear(void); + +struct ipm_config global_conf; +static DECLARE_MUTEX (ipm_conf_sem); +static struct ipme_queue ipme_queue; + +int ipmc_open(struct inode *inode, struct file *filep) +{ + MOD_INC_USE_COUNT; + return 0; /* success */ +} + +int ipmc_close( struct inode *inode, struct file *filep ) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * Return a ipm event or get blocked until there is a event. + */ + +static __inline int ipmq_empty(void) +{ + return (ipme_queue.len>0)?0:1; +} + +ssize_t ipmc_read(struct file *filep, char *buf, size_t count, + loff_t *f_pos) +{ + DECLARE_WAITQUEUE(wait, current); + struct ipm_event data; + ssize_t retval; + + if (count < sizeof(struct ipm_event)) + return -EINVAL; + + add_wait_queue(&ipme_queue.waitq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + for (;;) { + if (filep->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto out; + } + + if (signal_pending(current)) { + retval = -ERESTARTSYS; + goto out; + } + /* Get the event out. If no, blocked.*/ + if ( !ipmq_empty()){ + ipmq_get(&data); + break; + } + /* No events and no error, block it. */ + schedule(); + } + + /* pass the event to user space. */ + retval = copy_to_user( (struct ipm_event *)buf, &data, + sizeof(struct ipm_event) ); + if (retval) { + retval = -EFAULT; + goto out; + } + retval = sizeof(struct ipm_event); + +out: + set_current_state(TASK_RUNNING); + remove_wait_queue(&ipme_queue.waitq, &wait); + return retval; +} + +/* + * Write will do nothing. If need to pass down some information use ioctl + * interface instead. + */ +ssize_t ipmc_write(struct file *filep, const char *buf, size_t count, + loff_t *f_pos) +{ + return 0; +} + +/* poll for ipm event */ +unsigned int ipmc_poll(struct file *file, poll_table *wait) +{ + poll_wait(file, &ipme_queue.waitq, wait); + if (!ipmq_empty()) + return POLLIN | POLLRDNORM; + + return 0; +} + +#if 0 +/* + * This function will change voltage and freq together using + * VCS command and set PCMD[FVC] bit. + */ +static void combine_vltg_freq_change(struct ipm_config *conf) +{ + unsigned int l, n; + unsigned int turbo_mode, dac_value; + + cpu_voltage_cur = conf->core_vltg; + + l = bulverde_freq_getL(conf->core_freq, *conf); + n = conf->turbo_mode; + turbo_mode = (conf->turbo_mode ==0 )? 0:1; + dac_value = mv2DAC(conf->core_vltg ); + + combochange(l, n ,turbo_mode, conf->fast_bus_mode, dac_value); +} + +unsigned int check_ipm_config(struct ipm_config *target) +{ + int i=0; + struct ipm_config cur; + + cur.core_freq = cpufreq_get(0); + + /* Get current voltage. */ + while( ipm_fv_table[i].freq != 0 ) { + if( cur.core_freq < ipm_fv_table[i].freq ) { + cur.core_vltg = ipm_fv_table[i].voltage; + break; + } + i++; + } + i=0; + /* Get target voltage. */ + while( ipm_fv_table[i].freq != 0 ) { + if( target->core_freq < ipm_fv_table[i].freq ) { + target->core_vltg = ipm_fv_table[i].voltage; + break; + } + i++; + } +#ifdef DEBUG + printk(KERN_INFO "Current freq: %d, target freq: %d.\n", cur.core_freq, target->core_freq); + printk(KERN_INFO "Current vol: %d, target vol: %d.\n",cur.core_vltg, target->core_vltg); +#endif + if( target->core_vltg == cur.core_vltg ) return 0; + if( target->core_vltg > cur.core_vltg ) return HIGHER; + return LOWER; +} +#endif + + +void get_ipm_config(struct ipm_config *ret_conf) +{ + struct ipm_config conf; + unsigned long clk_cfg = 0; + unsigned int l, n2, m, k, run_freq, ccsr, cccr_a; + + memset(&conf, 0, sizeof(struct ipm_config)); + conf.cpu_mode =0; /* definitly in RUN mode. */ + + ccsr = CCSR; + l = ccsr & 0x1f; + n2 = (ccsr>>7) & 0xf; + m = (l <= 10) ? 1 : (l <= 20) ? 2 : 4; + k = (l <= 7) ? 1 : (l <= 16) ? 2 : 4; + cccr_a = CCCR & (1 << 25); + + clk_cfg = pxa27x_read_clkcfg(); + conf.fast_bus_mode = (clk_cfg >> 0x3) & 0x1; + conf.turbo_mode = (clk_cfg & 0x1)? 2: (clk_cfg & 0x4)? 1: 0; + conf.n2 = n2; + conf.l = l; + conf.mem_clk_conf = cccr_a >> 25; + run_freq = 13000*l; + if (ccsr & (1 << 31)) { + conf.core_freq = conf.sys_bus_freq = conf.mem_bus_freq = 13000; + conf.lcd_freq = (CCCR & (1 << 27))? 26000 : 13000; + memcpy(ret_conf, &conf,sizeof(struct ipm_config)); + return ; + } + conf.core_freq = l*((conf.turbo_mode == 0)? 13000 : + ((conf.turbo_mode == 2)? 6500*n2 : 3250*n2)); + conf.sys_bus_freq = (conf.fast_bus_mode)? run_freq : run_freq/2; + conf.mem_bus_freq = (!cccr_a) ? (run_freq/m) : ((conf.fast_bus_mode) ? + run_freq : (run_freq/2)); + conf.lcd_freq = run_freq / k; + + memcpy(ret_conf, &conf,sizeof(struct ipm_config)); + + return; +} + +/* + * Write the desired voltage to + * DPM voltage changer and the desired freq to DPM freq changer. + */ +void set_ipm_conf(struct ipm_config *conf) +{ + int mode, ret; + unsigned int vol = conf->core_vltg; + unsigned int freq, new_freq; + unsigned int l = conf->l, n2 = conf->n2; + unsigned int t = conf->turbo_mode, b = conf->fast_bus_mode; + unsigned int a = conf->mem_clk_conf; + int pre_change = 0; + + mode = conf->cpu_mode; + /* want to change CPU mode? */ + if( mode != 0) { /* not RUN mode. */ + /* allow to use this method to sleep the system...*/ + pm_suspend(mode); + } + + ret = pxa27x_validate(l, n2, b, t, a); + if (ret) { + printk(KERN_ERR "Error: Wrong L: %d, 2N: %d, B: %d, T: %d, A:%d\n", l, n2, b, t, a); + return ; + } + + freq = cpufreq_get(0); + new_freq = l*((t == 0)? 13000 : ((t == 2)? 6500*n2 : 3250*n2)); + /* + * It may need to change frequency. The voltage change is also invoked + * according to the frequency. + */ + if (vol == 0) { + if (freq < new_freq) + pre_change = 1; + } + else { + if (pxa27x_get_voltage() < vol) + pre_change = 1; + } + if (pre_change == 1) { + if (pxa27x_set_voltage(new_freq, vol, 0) < 0 && vol) + pxa27x_set_voltage(new_freq, 0, 0); + pxa27x_set_cpufreq(l, n2, b, t, a); + } + else { + pxa27x_set_cpufreq(l, n2, b, t, a); + if (pxa27x_set_voltage(new_freq, vol, 0) < 0 && vol) + pxa27x_set_voltage(new_freq, 0, 0); + } + /* + * combochange of voltage and frequency + */ +#if 0 + if (pxa27x_set_voltage(new_freq, vol, 1) < 0 && vol) + pxa27x_set_voltage(new_freq, 0, 1); + pxa27x_set_cpufreq(l, n2, b, t); +#endif + return; +} + +#define SLEEP_SYSTEM 0 +extern unsigned int ipm_sleep_level; +extern unsigned int pm_sleeptime; +extern unsigned int pm_waketime; +extern unsigned int pm_uitimeout; +extern unsigned int ipm_sleep_level; +#if 0 +extern unsigned long WindowSize; +#endif +int ipmc_ioctl (struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct ipm_config conf, target_conf; + int result = 0; + + memset(&conf, 0 ,sizeof(struct ipm_config)); + + switch (cmd) { + case IPMC_IOCTL_GET_DPM_CONFIG: + get_ipm_config(&conf); + return (copy_to_user((unsigned long *)arg, &conf,sizeof(conf)))? -EFAULT:0; + break; + + case IPMC_IOCTL_SET_DPM_CONFIG: + if(copy_from_user(&target_conf,(struct ipmc_conf *)arg,sizeof(conf))) { + printk("Erro from copy_from_user.\n"); + return -EFAULT; + } + + set_ipm_conf(&target_conf); + break; + + /* Reset the UI timer. */ + case IPMC_IOCTL_RESET_UI_TIMER: + pm_updatetimer(0); + break; + + /* get rid of those event before. */ + case IPMC_IOCTL_CLEAR_EVENTLIST: + ipmq_clear(); + break; + + case IPMC_IOCTL_SET_SLEEPTIME: + if( copy_from_user(&pm_sleeptime,(unsigned int *)arg,sizeof(int)) ) + return -EFAULT; + break; +#if 0 + case IPMC_IOCTL_SET_WINDOWSIZE: + if( copy_from_user(&WindowSize,(unsigned int)arg,sizeof(int)) ) + return -EFAULT; + break; +#endif + case IPMC_IOCTL_GET_SLEEPTIME: + return copy_to_user((unsigned int *)arg, &pm_sleeptime, sizeof(int)); + break; + + case IPMC_IOCTL_SET_WAKETIME: + if (copy_from_user(&pm_waketime,(unsigned int *)arg,sizeof(int)) ) + return -EFAULT; + break; + + case IPMC_IOCTL_GET_WAKETIME: + return copy_to_user((unsigned int *)arg, &pm_waketime, sizeof(int)); + break; + + case IPMC_IOCTL_SET_UITIME: + if( copy_from_user(&pm_uitimeout,(unsigned int *)arg,sizeof(int)) ) + return -EFAULT; + if( pm_uitimeout !=0 ) /* stop it. */ + pm_updatetimer(0); + break; + + case IPMC_IOCTL_GET_UITIME: + return copy_to_user((unsigned int *)arg, &pm_uitimeout, sizeof(int)); + break; + + case IPMC_IOCTL_SET_SLEEPLEVEL: + if( copy_from_user(&ipm_sleep_level,(unsigned int *)arg,sizeof(int)) ) + return -EFAULT; + break; + + case IPMC_IOCTL_GET_SLEEPLEVEL: + return copy_to_user((unsigned int *)arg, &ipm_sleep_level, sizeof(int)); + break; + + /* + * Do we need to decide those two ioctls into four? + * IPMC_IOCTL_CLAIMPMU, IPMC_IOCTL_STARTPMU, + * IPMC_IOCTL_STOPPMU, IPMC_IOCTL_RELEASEPMU + * or just + * IPMC_IOCTL_STARTPMU, IPMC_IOCTL_STOPPMU ? + */ + /* + * Did not need PMU events to policy maker? That 's really good. + * Still keep this bez we need to test PMU driver, right? + */ +#if 0 + case IPMC_IOCTL_STARTPMU: + ipm_start_pmu(); + break; + /* + * When using stop pmu the user application should provide a place + * to save the result. + */ + case IPMC_IOCTL_STOPPMU: + /* pmu should already stopped. */ + pmu_stop(&presult); + /* + * Release it? Since only one user can have it we just + * use the recorded pmu id. + */ + pmu_release(pmu_id); + /* Shall we check the arg pointer first? */ + if( arg!=NULL ) + return copy_to_user( (unsigned int)arg, &presult, + sizeof(struct pmu_results) ); + else return 0; + break; +#endif + + default: + result = -ENOIOCTLCMD; + break; + } + return result; +} + +static struct file_operations ipmc_fops = { + owner: THIS_MODULE, + open: ipmc_open, + read: ipmc_read, + write: ipmc_write, + poll: ipmc_poll, + ioctl: ipmc_ioctl, + release:ipmc_close, +}; + +static struct miscdevice ipmc_misc_device = { + minor: IPMC_MINOR, + name: "ipmc", + fops: &ipmc_fops +}; + +/* + * Below is the event list maintain functions, the same as keypad.c + */ +static int ipmq_init(void) +{ + /* init ipme queue */ + ipme_queue.head = ipme_queue.tail = 0; + ipme_queue.len = 0; + init_waitqueue_head(&ipme_queue.waitq); + return 0; +} + +static int __init ipmc_init( void ) +{ + int rc; + + /* Clear event queue. */ + ipmq_init(); + + if ( (rc = misc_register( &ipmc_misc_device )) != 0 ) { + printk( KERN_INFO "Could not register device ipmc, res = %d.\n ",rc ); + return -EBUSY; + } + printk( KERN_INFO "Register device ipmc successgul.\n " ); + return 0; +} + +static void ipmc_exit( void ) +{ + misc_deregister(&ipmc_misc_device); +} + + +static int ipmq_get(struct ipm_event *ipme) +{ + unsigned long flags; + + local_irq_save(flags); + + if (!ipme_queue.len) { + local_irq_restore(flags); + return -1; + } + + memcpy(ipme, ipme_queue.ipmes + ipme_queue.tail, sizeof(struct ipm_event)); + + ipme_queue.len--; + ipme_queue.tail = (ipme_queue.tail + 1) % MAX_IPME_NUM; + + local_irq_restore(flags); + + return 0; + +} + +static int ipmq_clear(void) +{ + unsigned long flags; + + local_irq_save(flags); + ipme_queue.head = ipme_queue.tail = 0; + ipme_queue.len = 0; + local_irq_restore(flags); + + return 0; +} + +static int ipmq_put(struct ipm_event *ipme) +{ + unsigned long flags; + + local_irq_save(flags); + + if (ipme_queue.len == MAX_IPME_NUM) { + local_irq_restore(flags); + return -1; + } + + memcpy(ipme_queue.ipmes + ipme_queue.head, ipme, sizeof(struct ipm_event)); + + ipme_queue.len ++; + ipme_queue.head = (ipme_queue.head + 1) % MAX_IPME_NUM; + + local_irq_restore(flags); + + /* wake up the waiting process */ + wake_up_interruptible(&ipme_queue.waitq); + + return 0; +} + +static struct ipm_event __inline IPM_Events(int type, int kind, int info) +{ + struct ipm_event event; + event.type = type; + event.kind = kind; + event.info = info; + return event; +} + +/* + * IPM event can be posted by this function. + * If we need to do some pre-processing of those events we can add code here. + * Also attach the processed result to info. + */ +void ipm_event_notify(int type, int kind, int info) +{ + struct ipm_event events; + events = IPM_Events(type, kind, info); + ipmq_put(&events); +} + +EXPORT_SYMBOL(ipm_event_notify); + +module_init(ipmc_init); +module_exit(ipmc_exit); diff -uNr a/arch/arm/mach-pxa/ipmc.h b/arch/arm/mach-pxa/ipmc.h --- a/arch/arm/mach-pxa/ipmc.h 1970-01-01 08:00:00.000000000 +0800 +++ b/arch/arm/mach-pxa/ipmc.h 2005-01-27 09:56:48.000000000 +0800 @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2003-2004 Intel Corporation. + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * + */ + +#ifndef __IPMC_H +#define __IPMC_H + +#ifdef __KERNEL__ +#include +#include +//#include +#endif + +/* Use 'K' as magic number */ +#define IPMC_IOC_MAGIC 'K' + +/* To keep compitable with old cpufreqd user application. */ +#if 0 +#define IPMC_IOCTL_GET _IOR(IPMC_IOC_MAGIC, 1, struct ipm_config) +#define IPMC_IOCTL_SET _IOW(IPMC_IOC_MAGIC, 2, struct ipm_config) + +#endif +#define IPMC_IOCTL_GET_DPM_CONFIG _IOR(IPMC_IOC_MAGIC, 3, struct ipm_config) +#define IPMC_IOCTL_SET_DPM_CONFIG _IOW(IPMC_IOC_MAGIC, 4, struct ipm_config) + +#define IPMC_IOCTL_GET_DVFM_CONFIG _IOR(IPMC_IOC_MAGIC, 3, struct ipm_config) +#define IPMC_IOCTL_SET_DVFM_CONFIG _IOW(IPMC_IOC_MAGIC, 4, struct ipm_config) + +#define IPMC_IOCTL_GET_EVENT _IOR(IPMC_IOC_MAGIC, 5, int) +#define IPMC_IOCTL_SET_EVENT _IOW(IPMC_IOC_MAGIC, 6, int) + +#define IPMC_IOCTL_RESET_UI_TIMER _IOW(IPMC_IOC_MAGIC, 8, int) + +#define IPMC_IOCTL_GET_SLEEPTIME _IOR(IPMC_IOC_MAGIC, 9, int) +#define IPMC_IOCTL_SET_SLEEPTIME _IOW(IPMC_IOC_MAGIC, 10, int) + +#define IPMC_IOCTL_GET_WAKETIME _IOR(IPMC_IOC_MAGIC, 11, int) +#define IPMC_IOCTL_SET_WAKETIME _IOW(IPMC_IOC_MAGIC, 12, int) + +#define IPMC_IOCTL_GET_UITIME _IOR(IPMC_IOC_MAGIC, 13, int) +#define IPMC_IOCTL_SET_UITIME _IOW(IPMC_IOC_MAGIC, 14, int) + +#define IPMC_IOCTL_GET_SLEEPLEVEL _IOR(IPMC_IOC_MAGIC, 15, int) +#define IPMC_IOCTL_SET_SLEEPLEVEL _IOW(IPMC_IOC_MAGIC, 16, int) + +#define IPMC_IOCTL_STARTPMU _IOW(IPMC_IOC_MAGIC, 17, int) +#define IPMC_IOCTL_STOPPMU _IOW(IPMC_IOC_MAGIC, 18, struct pmu_results) + +#define IPMC_IOCTL_CLEAR_EVENTLIST _IOW(IPMC_IOC_MAGIC, 20, int) + +#define IPMC_IOCTL_SET_WINDOWSIZE _IOW(IPMC_IOC_MAGIC, 22, int) +#define NOVOLCHG 0 +#define HIGHER 1 +#define LOWER 2 + + +#define MAX_IPME_NUM 20 /* 20 IPM event max */ +/* IPM events queue */ + +struct ipm_config { + /* Below items must be set to set configurations. */ + unsigned int cpu_mode; + unsigned int l; /* L */ + unsigned int n2; /* 2N */ + unsigned int core_vltg; /* in mV. */ + unsigned int turbo_mode; /* =2 turbo, =1 half turbo */ + unsigned int fast_bus_mode; + unsigned int mem_clk_conf; + /* Below items may need to get system DPM configurations. */ + unsigned int core_freq; + unsigned int sys_bus_freq; + unsigned int mem_bus_freq; + unsigned int lcd_freq; + unsigned int enabled_device; +}; + +struct ipm_event { + unsigned int type; /* What type of IPM events. */ + unsigned int kind; /* What kind, or sub-type of events.*/ +// void *infodata; /* events specific data. */ + unsigned int info; +}; + +#ifdef __KERNEL__ +struct ipme_queue{ + int head; + int tail; + int len; + struct ipm_event ipmes[MAX_IPME_NUM]; + wait_queue_head_t waitq; +}; +#endif + +#define IPM_EVENTS_TYPE(x) ((x&0xFF000000)>>24) +#define IPM_EVENTS_KIND(x) ((x&0x00F00000)>>16) +#define IPM_EVENTS_INFO(x) (x&0xFF) + +/* IPM event types. */ +#define IPM_EVENT_DEVICE 0x1 /* Device interrupt event. */ +#define IPM_EVENT_TIMER 0x2 /* Device Timer timeout event. */ + +#define IPM_EVENT_PROFILER 0x3 /* Profiler events. */ +#define IPM_EVENT_SYSTEM_WAKEUP 0x4 +#define IPM_EVENT_CPU 0x5 /* CPU utilization change event. */ + +/* IPM event kinds. */ +#define IPM_EVENT_POWER_LOW 0x1 +#define IPM_EVENT_POWER_FAULT 0x2 +#define IPM_EVENT_POWER_OK 0x3 + +#define IPM_EVENT_DEVICE_TIMEOUT 0x4 +#define IPM_EVENT_DEVICE_INT 0x5 + +#define IPM_EVENT_IDLE_PROFILER 0x0 +#define IPM_EVENT_PERF_PROFILER 0x1 + +#define IPM_EVENT_RTCWAKEUP 0x0 +#define IPM_EVENT_UIWAKEUP 0x1 + +#define IPM_EVENT_CPUVERYBUSY 0x0 /* report CPU is very busy now. */ +#define IPM_EVENT_CPUBUSY 0x1 /* report CPU is very busy now. */ +#define IPM_EVENT_CPUFREE 0x2 /* report CPU is free now. */ + +/* IPM event infos, not defined yet. */ +#define IPM_EVENT_NULLINFO 0x0 +#define IPM_WAKEUPBYRTC 0x1 +#define IPM_WAKEUPBYUI 0x2 + +/* IPM functions */ +#ifdef __KERNEL__ +int ipmc_open(struct inode *inode, struct file *filep); +int ipmc_close( struct inode *inode, struct file *filep ); +ssize_t ipmc_read(struct file *filep, char *buf, size_t count,loff_t *f_pos); +ssize_t ipmc_write(struct file *filep, const char *buf, size_t count,loff_t *f_pos); +int ipmc_ioctl (struct inode *inode, struct file *file, unsigned int cmd,unsigned long arg); +void set_ipm_conf(struct ipm_config *conf); +void ipm_event_notify(int type, int kind, int info); +#endif + + +#endif diff -uNr a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig --- a/arch/arm/mach-pxa/Kconfig 2004-10-19 05:53:43.000000000 +0800 +++ b/arch/arm/mach-pxa/Kconfig 2005-01-27 09:56:48.000000000 +0800 @@ -20,6 +20,61 @@ endchoice +menu "Intel PXA27x Errata Fixes" +depends on PXA27x + +config PXA27x_E11 + bool "Intel PXA27x Errata 11" + help + KEYPAD: Extra Keypad matrix interrupt in IMKP Mode + default y + +config PXA27x_E17 + bool "Intel PXA27x Errata 17" + help + LCD: Overlay1 is not enabled intermittently after re-enabling LCD. + default y + +config PXA27x_E20 + bool "Intel PXA27x Errata 20" + help + UART: Character timeout interrupt remains set under certain software + conditions + default y + +config PXA27x_E23 + bool "Intel PXA27x Errata 23" + help + CIP: Receiver Aborts randomly occur prematurely and without End of + frame/Error in FIFO interrupt + default y + +config PXA27x_E25 + bool "Intel PXA27x Errata 25" + help + LCD: Enabling Overlay 2 for YUV420 hangs LCD controller. + default y + +config PXA27x_E28 + bool "Intel PXA27x Errata 28" + help + Core hangs during voltage change when there are outstanding transaction on the bus. + default y + +config PXA27x_E37 + bool "Intel PXA27x Errata 37" + help + System Hangs when enabling RUN/TURBO switching at 520MHZ + default y + +config PXA27x_E38 + bool "Intel PXA27x Errata 38" + help + System Hangs when enabling HalfTurbo Switching + default y + +endmenu + endmenu config PXA25x diff -uNr a/arch/arm/mach-pxa/leds-mainstone.c b/arch/arm/mach-pxa/leds-mainstone.c --- a/arch/arm/mach-pxa/leds-mainstone.c 2004-10-19 05:53:06.000000000 +0800 +++ b/arch/arm/mach-pxa/leds-mainstone.c 2005-01-27 09:56:48.000000000 +0800 @@ -23,16 +23,6 @@ #include "leds.h" -/* 8 discrete leds available for general use: */ -#define D28 (1 << 0) -#define D27 (1 << 1) -#define D26 (1 << 2) -#define D25 (1 << 3) -#define D24 (1 << 4) -#define D23 (1 << 5) -#define D22 (1 << 6) -#define D21 (1 << 7) - #define LED_STATE_ENABLED 1 #define LED_STATE_CLAIMED 2 diff -uNr a/arch/arm/mach-pxa/mainstone.c b/arch/arm/mach-pxa/mainstone.c --- a/arch/arm/mach-pxa/mainstone.c 2004-10-19 05:55:29.000000000 +0800 +++ b/arch/arm/mach-pxa/mainstone.c 2005-01-27 09:56:48.000000000 +0800 @@ -159,11 +159,11 @@ .yres = 320, .bpp = 16, .hsync_len = 4, - .left_margin = 8, - .right_margin = 20, + .left_margin = 12, + .right_margin = 14, .vsync_len = 3, - .upper_margin = 1, - .lower_margin = 10, + .upper_margin = 5, + .lower_margin = 6, .sync = FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, .lccr0 = LCCR0_Act, .lccr3 = LCCR3_PCP, @@ -172,14 +172,21 @@ static void __init mainstone_init(void) { + /* system bus arbiter setting + * - Core_Park + * - LCD_wt:DMA_wt:CORE_Wt = 2:3:4 + */ + ARB_CNTRL = ARB_CORE_PARK | 0x234; + platform_device_register(&smc91x_device); /* reading Mainstone's "Virtual Configuration Register" might be handy to select LCD type here */ - if (0) +#ifdef CONFIG_FB_PXA_LCD_VGA set_pxa_fb_info(&toshiba_ltm04c380k); - else +#elif CONFIG_FB_PXA_LCD_QVGA set_pxa_fb_info(&toshiba_ltm035a776c); +#endif } @@ -196,6 +203,8 @@ MACHINE_START(MAINSTONE, "Intel HCDDBBVA0 Development Platform (aka Mainstone)") MAINTAINER("MontaVista Software Inc.") BOOT_MEM(0xa0000000, 0x40000000, io_p2v(0x40000000)) + /* FIXME: BLOB boot parameter setting */ + BOOT_PARAMS(0xa0000100) MAPIO(mainstone_map_io) INITIRQ(mainstone_init_irq) INITTIME(pxa_init_time) diff -uNr a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile --- a/arch/arm/mach-pxa/Makefile 2004-10-19 05:54:27.000000000 +0800 +++ b/arch/arm/mach-pxa/Makefile 2005-01-27 09:56:48.000000000 +0800 @@ -5,7 +5,7 @@ # Common support (must be linked before board specific support) obj-y += generic.o irq.o dma.o time.o obj-$(CONFIG_PXA25x) += pxa25x.o -obj-$(CONFIG_PXA27x) += pxa27x.o +obj-$(CONFIG_PXA27x) += pxa27x.o pmu-bvd.o pmu.o # Specific board support obj-$(CONFIG_ARCH_LUBBOCK) += lubbock.o @@ -22,3 +22,5 @@ # Misc features obj-$(CONFIG_PM) += pm.o sleep.o +obj-$(CONFIG_DVFM) += vcs.o cpu-freq-pxa27x.o cpu-voltage-pxa27x.o ipmc.o +obj-$(CONFIG_SRAM_ALLOCATE) += sram.o diff -uNr a/arch/arm/mach-pxa/pm.c b/arch/arm/mach-pxa/pm.c --- a/arch/arm/mach-pxa/pm.c 2004-10-19 05:54:32.000000000 +0800 +++ b/arch/arm/mach-pxa/pm.c 2005-01-27 09:56:48.000000000 +0800 @@ -7,11 +7,17 @@ * Modified for the PXA250 by Nicolas Pitre: * Copyright (c) 2002 Monta Vista Software, Inc. * + * Modified for the PXA27x based on Mainstone by Chao Xie + * Copyright (C) 2004, Intel Corporation + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License. + * */ + #include #include +#include #include #include #include @@ -20,17 +26,26 @@ #include #include #include -#include +#include #include +#include "cpu-freq-voltage-pxa27x.h" /* * Debug macros */ #undef DEBUG +extern struct subsystem power_subsys; + +extern int pxa27x_set_voltage(unsigned int, unsigned int, unsigned int); +extern void pxa27x_cpufreq_restore(void); extern void pxa_cpu_suspend(void); extern void pxa_cpu_resume(void); +extern void pxa_cpu_standby(void); +static int check_wakeup_src(void); +int pm_updatetimer(int); +void pm_timeout_proc(unsigned long); #define SAVE(x) sleep_save[SLEEP_SAVE_##x] = x #define RESTORE(x) x = sleep_save[SLEEP_SAVE_##x] @@ -40,6 +55,31 @@ GPCR##n = ~sleep_save[SLEEP_SAVE_GPLR##n]; \ } while (0) +/* How long we will in sleep mode if duty cycle. */ +unsigned int pm_sleeptime=58; /* In seconds. */ + +/* How long we will in run mode if duty cycle. */ +unsigned int pm_waketime = 2; /* In seconds. */ + +/* After pm_uitimeout long without input from keypad/touch, we will sleep. */ +unsigned int pm_uitimeout= 30; /* In seconds. */ + +/* Timer timeout event handled in kernel or in policy maker? */ +/* 0 is in kernel and 1 is for policy maker in user space. */ +unsigned int ipm_event_handler = 1; /* In seconds. */ + +/* + * The default sleep level, if we want to use Standy to replace sleep. + * set this to PM_SUSPEND_STANDBY. + */ +unsigned int ipm_sleep_level = PM_SUSPEND_MEM; /* Default is to Sleep mode. */ + +/* Shall we use standby as idle? */ +unsigned int ipm_sai = 0; + +static struct timer_list pm_timer; +struct semaphore pm_timer_wait; + /* * List of global PXA peripheral registers to preserve. * More ones like CP and general purpose register values are preserved @@ -57,12 +97,37 @@ SLEEP_SAVE_GAFR0_L, SLEEP_SAVE_GAFR1_L, SLEEP_SAVE_GAFR2_L, SLEEP_SAVE_GAFR0_U, SLEEP_SAVE_GAFR1_U, SLEEP_SAVE_GAFR2_U, - SLEEP_SAVE_ICMR, - SLEEP_SAVE_CKEN, +#if defined(CONFIG_PXA27x) + SLEEP_SAVE_GPDR3, SLEEP_SAVE_GRER3, SLEEP_SAVE_GFER3, + SLEEP_SAVE_GAFR3_L, SLEEP_SAVE_GAFR3_U, + SLEEP_SAVE_KPC, +#endif - SLEEP_SAVE_CKSUM, + SLEEP_SAVE_ICMR, + SLEEP_SAVE_CKEN, +#ifdef CONFIG_MACH_MAINSTONE + SLEEP_SAVE_MST_GPSWR, + SLEEP_SAVE_MST_MSCWR1, + SLEEP_SAVE_MST_MSCWR2, + SLEEP_SAVE_MST_MSCWR3, + SLEEP_SAVE_MST_MSCRD, + SLEEP_SAVE_MST_INTMSKENA, + SLEEP_SAVE_MST_INTSETCLR, +#endif - SLEEP_SAVE_SIZE + SLEEP_SAVE_PWER, + SLEEP_SAVE_PSTR, + SLEEP_SAVE_OMCR4, + SLEEP_SAVE_OSCR4, + SLEEP_SAVE_OSMR4, + SLEEP_SAVE_MDREFR, + SLEEP_SAVE_OSSR, + SLEEP_SAVE_PGSR0, + SLEEP_SAVE_PGSR1, + SLEEP_SAVE_PGSR2, + SLEEP_SAVE_PGSR3, + SLEEP_SAVE_CKSUM, + SLEEP_SAVE_SIZE }; @@ -73,15 +138,30 @@ struct timespec delta, rtc; int i; - if (state != PM_SUSPEND_MEM) + if (state != PM_SUSPEND_MEM && state != PM_SUSPEND_STANDBY) return -EINVAL; +// leds_event(led_stop); /* preserve current time */ rtc.tv_sec = RCNR; rtc.tv_nsec = 0; save_time_delta(&delta, &rtc); +#if defined(CONFIG_MACH_MAINSTONE) + SAVE(MST_GPSWR); + SAVE(MST_MSCWR1); + SAVE(MST_MSCWR2); + SAVE(MST_MSCWR3); + SAVE(MST_MSCRD); + SAVE(MST_INTMSKENA); + SAVE(MST_INTSETCLR); +#endif + + /* For SDRAM sightings. */ + SAVE(MDREFR); + /* save vital registers */ + SAVE(OSSR); SAVE(OSMR0); SAVE(OSMR1); SAVE(OSMR2); @@ -95,31 +175,150 @@ SAVE(GAFR0_L); SAVE(GAFR0_U); SAVE(GAFR1_L); SAVE(GAFR1_U); SAVE(GAFR2_L); SAVE(GAFR2_U); +#if defined(CONFIG_PXA27x) + SAVE(GPDR3); + SAVE(GRER3); + SAVE(GFER3); + SAVE(GAFR3_L); SAVE(GAFR3_U); + + SAVE(KPC); /* new added. */ +#endif SAVE(ICMR); ICMR = 0; SAVE(CKEN); - CKEN = 0; - - /* Note: wake up source are set up in each machine specific files */ + SAVE(PGSR0); + SAVE(PGSR1); + SAVE(PGSR2); + SAVE(PGSR3); + +#ifdef CONFIG_PXA27x + if (state == PM_SUSPEND_STANDBY) + CKEN = CKEN22_MEMC | CKEN9_OSTIMER | CKEN16_LCD |CKEN0_PWM0; + else + CKEN = CKEN22_MEMC | CKEN9_OSTIMER; + + PCFR = 0x66; + /* + * PSLR |= 0x4 is OK for serial console but not OK for + * LCD which is configured to use ISRAM as framebuffer when using QVGA. + * + * PSLR |= 0xF04 is OK for LCD driver but not OK for serial console, + * UART doesn't work correctly when PSLR|=0xF04 + */ + /* PSLR |= 0X4; */ + PSLR |= 0xF04; + + /* For Keypad wakeup. */ +#if defined(CONFIG_MACH_MAINSTONE) + KPC &=~KPC_ASACT; + KPC |=KPC_AS; +#endif + PGSR0 = 0x00008800; + PGSR1 = 0x00000002; + PGSR2 = 0x0001FC00; + PGSR3 = 0x00001F81; + + /* PWER = 0x80000002; */ + PWER = 0xC0000002; + PRER = 0x2; + PFER = 0x2; + + PKWR = 0x000FD001; + /* Need read PKWR back after set it. */ + PKWR; +#else + CKEN = 0; +#endif /* clear GPIO transition detect bits */ GEDR0 = GEDR0; GEDR1 = GEDR1; GEDR2 = GEDR2; - /* Clear sleep reset status */ - RCSR = RCSR_SMR; + /* Clear sleep reset status */ + RCSR = RCSR_SMR; - /* set resume return address */ - PSPR = virt_to_phys(pxa_cpu_resume); + /* Clear edge-detect status register. */ + PEDR = 0xCE00FE1B; + + /* set resume return address */ +#if defined(CONFIG_PXA27x) + if (state != PM_SUSPEND_STANDBY) + PSPR = virt_to_phys(pxa_cpu_resume); + + if (state == PM_SUSPEND_STANDBY) { + SAVE(PSTR); + SAVE(OMCR4); + SAVE(OSCR4); + SAVE(OSMR4); + SAVE(OIER); + } +#else + PSPR = virt_to_phys(pxa_cpu_resume); +#endif /* before sleeping, calculate and save a checksum */ for (i = 0; i < SLEEP_SAVE_SIZE - 1; i++) checksum += sleep_save[i]; sleep_save[SLEEP_SAVE_CKSUM] = checksum; - /* *** go zzz *** */ - pxa_cpu_suspend(); + /* Use RTC to wakeup system from sleep. */ + if (state != PM_SUSPEND_STANDBY) { + /* Check the sleeptime to see how we need to wake up,in ms. */ + /* come back sometimes later.*/ + if (pm_sleeptime != 0) { + /*65s,use periodic interrupt*/ + if (pm_sleeptime < 65) { + /* Clear PICE bit in RTSR. */ + RTSR &= ~RTSR_PICE; + /*set to sleep time,inms.*/ + /* set PIAR */ + PIAR = pm_sleeptime*1000; + /* Ensure at least 2 CPY cycles pass.*/ + udelay(10); + /* Clear PIALE bit in RTSR */ + RTSR &= ~RTSR_PIALE; + RTSR |= RTSR_PIALE; + RTSR |= RTSR_PICE; + } + else { /* we need to use RTC. */ + /* Need to use other RTC wakeep methods.. */ + } + + } + } + +#if defined(CONFIG_PXA27x) + /* Use OST 4 to wakeup system from standby */ + if (state == PM_SUSPEND_STANDBY) { + /* + * Here we use OS timer 4 as wakeup src. Maybe this is helpful + * when we decide to use standby to replace idle. + */ + /* All SRAM are on, PI is active with clock running. */ + PSTR = 0x00000F08; + /* For standby we use OSCR4 as prioridic wakeup source. */ + /* CRES field of OMCR is set to 011, ticks will be 1 seconds.*/ + OMCR4 = 0x0B; + OSCR4 = 1; + OSMR4 = OSCR4 + pm_sleeptime; + OIER = (OIER & 0xFFF) | 0x10; + } +#endif + + /* go Zzzz */ + switch (state) { + case PM_SUSPEND_MEM: + pxa_cpu_suspend(); + pxa27x_cpufreq_restore(); + break; +#if defined(CONFIG_PXA27x) + case PM_SUSPEND_STANDBY: + /* Standby the CPU. */ + pxa_cpu_standby(); + break; +#endif + } /* end of switch */ /* after sleeping, validate the checksum */ checksum = 0; @@ -137,6 +336,23 @@ /* ensure not to come back here if it wasn't intended */ PSPR = 0; +#ifdef CONFIG_PXA27x + if (state == PM_SUSPEND_STANDBY ) { + /* Clear PEDR */ + RESTORE(OIER); + RESTORE(MDREFR); + RESTORE(PSTR); + OSSR =0x10; + OSMR4 = OSCR4 + 3; + OSCR4 = OSCR4 + 4; + } + /* restore registers */ + RESTORE(PGSR0); + RESTORE(PGSR1); + RESTORE(PGSR2); + RESTORE(PGSR3); +#endif + /* restore registers */ RESTORE(GAFR0_L); RESTORE(GAFR0_U); RESTORE(GAFR1_L); RESTORE(GAFR1_U); @@ -146,7 +362,16 @@ RESTORE(GRER0); RESTORE(GRER1); RESTORE(GRER2); RESTORE(GFER0); RESTORE(GFER1); RESTORE(GFER2); - PSSR = PSSR_RDH | PSSR_PH; +#if defined(CONFIG_PXA27x) + RESTORE(KPC); /* new added. */ + RESTORE(GAFR3_L); RESTORE(GAFR3_U); + RESTORE(GPDR3); + RESTORE(GRER3); + RESTORE(GFER3); + PSSR = PSSR_PH | PSSR_OTGPH; +#else + PSSR = PSSR_PH; +#endif RESTORE(OSMR0); RESTORE(OSMR1); @@ -163,6 +388,17 @@ ICCR = 1; RESTORE(ICMR); +#if defined(CONFIG_MACH_MAINSTONE) + /* Restore Mainstone Board registers. */ + RESTORE(MST_GPSWR); + RESTORE(MST_MSCWR1); + RESTORE(MST_MSCWR2); + RESTORE(MST_MSCWR3); + RESTORE(MST_MSCRD); + RESTORE(MST_INTMSKENA); + RESTORE(MST_INTSETCLR); +#endif + /* restore current time */ rtc.tv_sec = RCNR; restore_time_delta(&delta, &rtc); @@ -170,6 +406,8 @@ #ifdef DEBUG printk(KERN_DEBUG "*** made it back from resume\n"); #endif + //leds_event(led_start); + /* pm_updatetimer( check_wakeup_src() );*/ return 0; } @@ -180,10 +418,60 @@ } /* + * Return the source which bring the system out of sleep. + */ +static int check_wakeup_src(void) +{ + int temp = 0; + if ( PEDR & 0x80000000) /* wakeup by RTC. */ + temp = 1; + /* Clear PEDR */ + PEDR = 0xCE00FE1B; + return temp; +} + +/* + * Let ipm_event_handler to choose whether timer timeout event handle in + * kernel space or by policy maker. + * 0 is in kernel and 1 is for policy maker in user space. + */ +void pm_timeout_proc(unsigned long ptr) +{ +#if 0 + if (ipm_event_handler == 0) + up(&pm_timer_wait); /* Let ipm_thread to sleep the system. */ + else /* notice the policy maker. */ + up(&event_sem); +#endif +} + +/* + * Flag is used to say whether it was called from UI update or from + * sleep call. In UI device call it with flag = 0, in sleep return + * call it with flag = 1. + */ +int pm_updatetimer(int flag) +{ + if (flag) + mod_timer(&pm_timer, jiffies + pm_waketime*HZ ); + else + mod_timer(&pm_timer, jiffies + pm_uitimeout*HZ ); + return 0; +} + +EXPORT_SYMBOL_GPL(pm_updatetimer); + +/* * Called after processes are frozen, but before we shut down devices. */ +static unsigned int old_core_voltage; static int pxa_pm_prepare(u32 state) { + if (state == PM_SUSPEND_MEM) { + /* set to highest voltage */ + old_core_voltage = pxa27x_get_voltage(); + pxa27x_set_voltage(PXA27X_MAX_FREQ, 0, 0); + } return 0; } @@ -192,6 +480,10 @@ */ static int pxa_pm_finish(u32 state) { + /* restore voltage, this should after restore CKEN */ + if (state == PM_SUSPEND_MEM) { + pxa27x_set_voltage(PXA27X_MIN_FREQ, old_core_voltage, 0); + } return 0; } @@ -205,9 +497,40 @@ .finish = pxa_pm_finish, }; +#define pm_attr(_name, object) \ +static ssize_t _name##_store(struct subsystem * subsys, const char * buf, size_t n) \ +{ \ + sscanf(buf, "%u", &object); \ + return n; \ +} \ +static ssize_t _name##_show(struct subsystem * subsys, char * buf) \ +{ \ + return sprintf(buf, "%u\n", object); \ +} \ +static struct subsys_attribute _name##_attr = { \ + .attr = { \ + .name = __stringify(_name), \ + .mode = 0644, \ + }, \ + .show = _name##_show, \ + .store = _name##_store, \ +} + + +pm_attr(sleeptime, pm_sleeptime); + static int __init pxa_pm_init(void) { pm_set_ops(&pxa_pm_ops); + sema_init(&pm_timer_wait, 0); + init_timer(&pm_timer); + pm_timer.function = pm_timeout_proc; + + if (pm_uitimeout) { + mod_timer(&pm_timer, jiffies + pm_uitimeout*HZ ); + } + + sysfs_create_file(&power_subsys.kset.kobj, &sleeptime_attr.attr); return 0; } diff -uNr a/arch/arm/mach-pxa/pmu-bvd.S b/arch/arm/mach-pxa/pmu-bvd.S --- a/arch/arm/mach-pxa/pmu-bvd.S 1970-01-01 08:00:00.000000000 +0800 +++ b/arch/arm/mach-pxa/pmu-bvd.S 2005-01-27 09:56:48.000000000 +0800 @@ -0,0 +1,177 @@ +/* + * Copyright 2000-2003 Intel Corporation All Rights Reserved. + * This file is for xsc2 PMU Access Assembly function + */ + +.text + +/* +@ +@ pmu_reg_read - Read the PMU Register +@ +@ Description: +@ This routine reads the designated PMU register via CoProcesser 14. +@ +@ Input Parameters: +@ r0 - arg1, PMU register number to read. Number between 0 to 8 +@ if r0 contains: +@ 0 -> PMNC, PMU Control Register +@ 1 -> CCNT, PMU Clock Counter +@ 2 -> PMN0, PMU Count Register 0 +@ 3 -> PMN1, PMU Count Register 1 +@ 4 -> PMN2, PMU Count Register 2 +@ 5 -> PMN3, PMU Count Register 3 +@ 6 -> INTEN, PMU Interupt Enable Register +@ 7 -> FLAG, PMU Overflow Flag Status Register +@ 8 -> EVTSEL PMU Event Select Register +@ +@ Returns: +@ r0 - 32-bit value read from CoProcessor +@ +@ Registers Modified: +@ CoProcessor Register Modified: None +@ General Purpose Registers Modified: r0 +@ +@ NOTE: +@ Error checking not included +@ +*/ +.global pmu_reg_read; +.align 0; +pmu_reg_read: + cmp r0, #8 + addls pc, pc, r0, lsl #2 + b RRet + b RdPMNC + b RdCCNT + b RdPMN0 + b RdPMN1 + b RdPMN2 + b RdPMN3 + b RdINTEN + b RdFLAG + b RdEVTSEL + +RdPMNC: + mrc p14, 0, r0, c0, c1, 0 @ Read PMNC + b RRet +RdCCNT: + mrc p14, 0, r0, c1, c1, 0 @ Read CCNT + b RRet +RdPMN0: + mrc p14, 0, r0, c0, c2, 0 @ Read PMN0 + b RRet +RdPMN1: + mrc p14, 0, r0, c1, c2, 0 @ Read PMN1 + b RRet +RdPMN2: + mrc p14, 0, r0, c2, c2, 0 @ Read PMN2 + b RRet +RdPMN3: + mrc p14, 0, r0, c3, c2, 0 @ Read PMN3 + b RRet +RdINTEN: + mrc p14, 0, r0, c4, c1, 0 @ Read INTEN + b RRet +RdFLAG: + mrc p14, 0, r0, c5, c1, 0 @ Read FLAG + b RRet +RdEVTSEL: + mrc p14, 0, r0, c8, c1, 0 @ Read EVTSEL + +RRet: + mov pc, lr @ return + +/* +@ +@ pmu_reg_write - Writes to the PMU Register +@ +@ Description: +@ This routine writes to the designated PMU register via CoProcesser 14. +@ +@ Input Parameters: +@ r0 - arg1 - PMU register number to write +@ r1 - arg2 - Value to write to PMU register +@ +@ if r0 contains: +@ 0 -> PMNC, PMU Control Register +@ 1 -> CCNT, PMU Clock Counter +@ 2 -> PMN0, PMU Count Register 0 +@ 3 -> PMN1, PMU Count Register 1 +@ 4 -> PMN2, PMU Count Register 2 +@ 5 -> PMN3, PMU Count Register 3 +@ 6 -> INTEN, PMU Interupt Enable Register +@ 7 -> FLAG, PMU Overflow Flag Status Register +@ 8 -> EVTSEL PMU Event Select Register +@ +@ Returns: +@ None +@ +@ Registers Modified: +@ CoProcessor Register Modified: PMU Register +@ General Purpose Registers Modified: None +@ +@ NOTE +@ Error checking not included +@ +*/ +.global pmu_reg_write; +.align 0; +pmu_reg_write: + cmp r0, #8 + addls pc, pc, r0, lsl #2 + b WRet + b WrPMNC + b WrCCNT + b WrPMN0 + b WrPMN1 + b WrPMN2 + b WrPMN3 + b WrINTEN + b WrFLAG + b WrEVTSEL + +WrPMNC: + mcr p14, 0, r1, c0, c1, 0 @ Write PMNC + b WRet +WrCCNT: + mcr p14, 0, r1, c1, c1, 0 @ Write CCNT + b WRet +WrPMN0: + mcr p14, 0, r1, c0, c2, 0 @ Write PMN0 + b WRet +WrPMN1: + mcr p14, 0, r1, c1, c2, 0 @ Write PMN1 + b WRet +WrPMN2: + mcr p14, 0, r1, c2, c2, 0 @ Write PMN2 + b WRet +WrPMN3: + mcr p14, 0, r1, c3, c2, 0 @ Write PMN3 + b WRet +WrINTEN: + mcr p14, 0, r1, c4, c1, 0 @ Write INTEN + b WRet +WrFLAG: + mcr p14, 0, r1, c5, c1, 0 @ Write FLAG + b WRet +WrEVTSEL: + mcr p14, 0, r1, c8, c1, 0 @ Write EVTSEL + +WRet: + mov pc, lr @ return + +/* +; XSC1GetCPUId - Get the CPU ID from CP15 R0 Register +; +; This routine reads R0 from CoProcesser 15 to get the CPU ID +; +; Uses r0 - return value of CPU ID +*/ +.global XSC1GetCPUId; +.align 0; +XSC1GetCPUId: + mrc p15, 0, r0, c0, c0, 0 + mov pc, lr + +.end diff -uNr a/arch/arm/mach-pxa/pmu.c b/arch/arm/mach-pxa/pmu.c --- a/arch/arm/mach-pxa/pmu.c 1970-01-01 08:00:00.000000000 +0800 +++ b/arch/arm/mach-pxa/pmu.c 2005-01-27 09:56:48.000000000 +0800 @@ -0,0 +1,177 @@ +/* + * This function provides the implementation of the access functions to + * the Performance Monitoring Unit on all CPUs based on the XScale core. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * Copyright (c) 2003 Intel Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static atomic_t usage = ATOMIC_INIT(0); +static unsigned long id = 0; +struct pmu_results results; + +static u32 pmnc; +static u32 evtsel; + +#define PMNC 0 /* PMU Control Register */ +#define CCNT 1 /* PMU Clock Counter (CCNT) */ +#define PMN0 2 /* PMU Count Register 0 (PMN0) */ +#define PMN1 3 /* PMU Count Register 1 (PMN1) */ +#define PMN2 4 +#define PMN3 5 +#define INTEN 6 +#define FLAG 7 +#define EVTSEL 8 + +extern void pmu_reg_write(unsigned long regno, unsigned long value); +extern unsigned long pmu_reg_read(unsigned long regno); + +#define PMU_ENABLE 0x001 /* Enable counters */ +#define PMN_RESET 0x002 /* Reset event counters */ +#define CCNT_RESET 0x004 /* Reset clock counter */ +#define PMU_RESET CCNT_RESET | PMN_RESET +#define CLK_DIV 0x008 /* Clock divide enbable */ + +#define PMN3_OVERFLOW 0x10 /* Perfromance counter 0 overflow */ +#define PMN2_OVERFLOW 0x08 /* Performance counter 1 overflow */ +#define PMN1_OVERFLOW 0x04 /* Performance counter 1 overflow */ +#define PMN0_OVERFLOW 0x02 /* Performance counter 1 overflow */ +#define CCNT_OVERFLOW 0x01 /* Clock counter overflow */ + +static irqreturn_t pmu_irq_handler(int, void *, struct pt_regs *); + +int pmu_claim(void) +{ + int err = 0; + + if(atomic_read(&usage)) + return -EBUSY; + + err = request_irq(IRQ_PMU, pmu_irq_handler, SA_INTERRUPT, + NULL, (void *) &results); + if(err < 0) + { + printk(KERN_ERR "unable to request IRQ %d for 80200 PMU: %d\n", + IRQ_PMU, err); + return err; + } + + atomic_inc(&usage); + pmnc = 0; + pmu_reg_write(PMNC, pmnc); + return ++id; +} + +int pmu_release(int claim_id) +{ + if(!atomic_read(&usage)) + return 0; + + if(claim_id != id) + return -EPERM; + + free_irq(IRQ_PMU, (void *)&results); + atomic_dec(&usage); + + return 0; +} + +int pmu_start(u32 pmn0, u32 pmn1, u32 pmn2, u32 pmn3) +{ + memset(&results, 0, sizeof(results)); + + evtsel = (pmn3 <<24) | (pmn2 <<16) | (pmn1 <<8) | pmn0; + + pmnc |= PMU_ENABLE | PMU_RESET; + + pmu_reg_write(EVTSEL, evtsel); + /* All interrupt are turned on. */ + pmu_reg_write(INTEN, 0x1F); + pmu_reg_write(PMNC, pmnc); + + return 0; +} + +int pmu_stop(struct pmu_results *results) +{ + u32 ccnt; + u32 pmn0; + u32 pmn1; + u32 pmn2; + u32 pmn3; + + if(!pmnc) + return -ENOSYS; + + ccnt = pmu_reg_read(CCNT); + pmn0 = pmu_reg_read(PMN0); + pmn1 = pmu_reg_read(PMN1); + pmn2 = pmu_reg_read(PMN2); + pmn3 = pmu_reg_read(PMN3); + + pmnc = 0; + + pmu_reg_write(PMNC, pmnc); + + results->ccnt = ccnt; + results->pmn0 = pmn0; + results->pmn1 = pmn1; + results->pmn2 = pmn2; + results->pmn3 = pmn3; + + return 0; +} + +static irqreturn_t pmu_irq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + struct pmu_results *results = (struct pmu_results *)dev_id; + unsigned int flag; + + /* read the status */ + flag = pmu_reg_read(FLAG); + pmnc = pmu_reg_read(PMNC); + + if(pmnc & PMN0_OVERFLOW) { + results->pmn0_of++; + } + + if(pmnc & PMN1_OVERFLOW) { + results->pmn1_of++; + } + + if(pmnc & PMN2_OVERFLOW) { + results->pmn2_of++; + } + + if(pmnc & PMN3_OVERFLOW) { + results->pmn3_of++; + } + + if(pmnc & CCNT_OVERFLOW) { + results->ccnt_of++; + } + + pmu_reg_write(FLAG, flag); + pmu_reg_write(PMNC, pmnc); + + return IRQ_HANDLED; +} + +EXPORT_SYMBOL(pmu_claim); +EXPORT_SYMBOL(pmu_release); +EXPORT_SYMBOL(pmu_start); +EXPORT_SYMBOL(pmu_stop); + + diff -uNr a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c --- a/arch/arm/mach-pxa/pxa27x.c 2004-10-19 05:54:08.000000000 +0800 +++ b/arch/arm/mach-pxa/pxa27x.c 2005-01-27 09:56:48.000000000 +0800 @@ -29,6 +29,7 @@ * Get the clock frequency as reflected by CCSR and the turbo flag. * We assume these values have been applied via a fcs. * If info is not 0 we also display the current settings. + * special situation about 13M */ unsigned int get_clk_frequency_khz( int info) { @@ -38,10 +39,11 @@ ccsr = CCSR; cccr_a = CCCR & (1 << 25); - + if (ccsr & (1 << 31)) + return 13000; /* Read clkcfg register: it has turbo, b, half-turbo (and f) */ asm( "mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg) ); - t = clkcfg & (1 << 1); + t = clkcfg & (1 << 0); ht = clkcfg & (1 << 2); b = clkcfg & (1 << 3); @@ -72,6 +74,7 @@ /* * Return the current mem clock frequency in units of 10kHz as * reflected by CCCR[A], B, and L + * in 13M, mem clock frequency is 13M */ unsigned int get_memclk_frequency_10khz(void) { @@ -81,7 +84,8 @@ ccsr = CCSR; cccr_a = CCCR & (1 << 25); - + if (ccsr & (1 << 31)) + return 1300; /* Read clkcfg register: it has turbo, b, half-turbo (and f) */ asm( "mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg) ); b = clkcfg & (1 << 3); @@ -97,6 +101,7 @@ /* * Return the current LCD clock frequency in units of 10kHz as + * in 13M, LCD clock frequency is 13M or 26 M */ unsigned int get_lcdclk_frequency_10khz(void) { @@ -105,12 +110,14 @@ ccsr = CCSR; + if (ccsr & (1 << 31)) + return (CCCR & (1 << 27))? 2600:1300; l = ccsr & 0x1f; k = (l <= 7) ? 1 : (l <= 16) ? 2 : 4; L = l * BASE_CLK; K = L / k; - + return (K / 10000); } diff -uNr a/arch/arm/mach-pxa/sleep.S b/arch/arm/mach-pxa/sleep.S --- a/arch/arm/mach-pxa/sleep.S 2004-10-19 05:55:07.000000000 +0800 +++ b/arch/arm/mach-pxa/sleep.S 2005-01-27 09:56:48.000000000 +0800 @@ -7,6 +7,9 @@ * Adapted for PXA by Nicolas Pitre: * Copyright (c) 2002 Monta Vista Software, Inc. * + * Modified for sleep/standby/wakeup on PXA27x by Cain Yuan + * Copyright (C) 2004, Intel Corporation + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License. */ @@ -19,6 +22,25 @@ #include .text +/* + * pxa_cpu_standby() + * + * Forces CPU into Standby state + */ +ENTRY(pxa_cpu_standby) + stmfd sp!, {r1 - r9, lr} @ save registers on stack + mov r2, #2 + mov r7, #UNCACHED_PHYS_0 @ Read mem context in. + ldr r8, [r7] + + b 1f + + .align 5 +1: + + mcr p14, 0, r2, c7, c0, 0 @ put the system into Standby + + ldmfd sp!, {r1 - r9, pc} @ Restore regs and return to caller /* * pxa_cpu_suspend() @@ -58,6 +80,7 @@ @ prepare value for sleep mode mov r1, #3 @ sleep mode +#ifndef CONFIG_PXA27x @ prepare to put SDRAM into self-refresh manually ldr r4, =MDREFR ldr r5, [r4] @@ -96,6 +119,7 @@ bic r0, r0, #2 @ clear change bit mcr p14, 0, r0, c6, c0, 0 orr r0, r0, #2 @ initiate change bit +#endif @ align execution to a cache line b 1f @@ -107,6 +131,7 @@ @ All needed values are now in registers. @ These last instructions should be in cache +#ifndef CONFIG_PXA27x @ initiate the frequency change... str r7, [r6] mcr p14, 0, r0, c6, c0, 0 @@ -119,6 +144,7 @@ @ force address lines low by reading at physical address 0 ldr r3, [r2] +#endif @ enter sleep mode mcr p14, 0, r1, c7, c0, 0 @@ -140,7 +166,7 @@ .data .align 5 ENTRY(pxa_cpu_resume) - mov r0, #PSR_I_BIT | PSR_F_BIT | MODE_SVC @ set SVC, irqs off + mov r0, #PSR_I_BIT | PSR_F_BIT | SVC_MODE @ set SVC, irqs off msr cpsr_c, r0 ldr r0, sleep_save_sp @ stack phys addr diff -uNr a/arch/arm/mach-pxa/sram.c b/arch/arm/mach-pxa/sram.c --- a/arch/arm/mach-pxa/sram.c 1970-01-01 08:00:00.000000000 +0800 +++ b/arch/arm/mach-pxa/sram.c 2005-01-27 09:56:48.000000000 +0800 @@ -0,0 +1,283 @@ +/* + * linux/arch/arm/mach-pxa/sram.c + * + * PXA27x Internal Memory + * + * Copyright (c) 2003, Intel Corporation (yu.tang@intel.com) + * + * This software program is licensed subject to the GNU + * General Public License(GPL).Version 2,June 1991. + * available at http://www.fsf.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static u32 sram_mem = 0; + +struct sram_block_t { + u32 start; /* pointer to start of sram memory block */ + u32 end; /* pointer to first address after sram memory block */ + u32 pid; /* each entry is associated with a process id */ + struct sram_block_t *prev; + struct sram_block_t *next; +}; + +static struct sram_block_t sram_list = { 0, 0, 0, &sram_list, &sram_list }; +static u32 space_available = 0; + +int remove_sram_entry(u32 start) { + struct sram_block_t *p = sram_list.next; + int found = 0; + + while(p != &sram_list) { + if(p->start == start) { + found = 1; + break; + } + p = p->next; + } + + if(!found) return -1; + + /* p now points to the memory block to be removed */ + + p->prev->next = p->next; + p->next->prev = p->prev; + space_available += p->end - p->start; + kfree(p); + return 0; +} + +int remove_sram_pid(u32 pid) { + struct sram_block_t *p = sram_list.next; + + while(p != &sram_list) { + if(p->pid == pid) { + /* p now points to the memory block to be removed */ + p->prev->next = p->next; + p->next->prev = p->prev; + space_available += p->end - p->start; + kfree(p); + } + p = p->next; + } + + return 0; +} + +struct sram_block_t * create_sram_entry(u32 size, u32 pid) { + + struct sram_block_t *p = &sram_list, *new; + int found = 0; + + while(1) { + /* check to see if there's a gap big enough for the request */ + if(p->next == &sram_list) { + /* we're at the end of the list */ + if(SRAM_SIZE - p->end >= size) + found = 1; + break; + } else if(p->next->start - p->end >= size) { + /* middle of the list */ + found = 1; + break; + } + p = p->next; + } + + if(!found) return NULL; + + /* p now points to the memory block just prior to the new block */ + new = kmalloc(sizeof(struct sram_block_t), GFP_ATOMIC); + if(new == NULL) return NULL; + + space_available -= size; + new->start = p->end; + new->end = p->end + size; + new->pid = pid; + + new->prev = p; + new->next = p->next; + new->prev->next = new; + new->next->prev = new; + + return new; +} + +static int __init sram_init (void) +{ + /* cachable, bufferable */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + sram_mem = (u32)__ioremap( SRAM_MEM_PHYS, SRAM_SIZE, L_PTE_BUFFERABLE | L_PTE_CACHEABLE, 0); +#else + sram_mem = (u32)__ioremap( SRAM_MEM_PHYS, SRAM_SIZE, L_PTE_BUFFERABLE | L_PTE_CACHEABLE); +#endif + if(!sram_mem) goto err; + +/* Enable clcok */ + CKEN |= CKEN20_IM; + + /* All the banks are + * - Automatic wakeup enabled + * - Enter Stand-by after 255 ms + */ + IMPMCR = ( 0xff << 16) | (0x0f << 8) | (0xff); + + space_available = SRAM_SIZE; + goto out; + +err: + if (sram_mem) { + iounmap((void*)sram_mem); + sram_mem = 0; + } + + printk (KERN_WARNING "Failed to initialize SRAM\n"); + + space_available = 0; +out: + return 0; +} + +/************************************************************************* + * + * Function: xscale_sram_malloc_local + * Description: Allocates a block and returns the offest + * into the sram array, not called externally + * + * Arguments: + * size - the size in bytes of the requested allocation + * res - the return val for the allocated address + * pid - the process id of the caller (0 for drivers) + * + *************************************************************************/ + +int xscale_sram_malloc_local(size_t size, u32 *res, u32 pid) { +struct sram_block_t *new; + + if((size < 4)||(size%4 != 0)) { + printk(KERN_WARNING "SRAM Memory Full, allocation failed\n"); + return -1; + } + if(size > space_available) { + printk(KERN_WARNING "SRAM Memory Full, allocation failed\n"); + return -1; + } + new = create_sram_entry(size, pid); + if(new == NULL) { + printk(KERN_WARNING "SRAM Memory Full, allocation failed\n"); + return -1; + } + *res = new->start; + return 0; +} + +/************************************************************************* + * + * Function: xscale_sram_malloc + * Description: Allocates a block and returns the kernel space + * address (sram array offset plus kernel virtual sram start) + * + * Arguments: + * size - the size in bytes of the requested allocation + * + *************************************************************************/ + +void *xscale_sram_malloc(size_t size) { +u32 addr; + + if(xscale_sram_malloc_local(size, &addr, 0)) return NULL; + return((void *)(sram_mem + addr)); +} + +/************************************************************************* +* + * Function: xscale_sram_malloc_user + * Description: Allocates a block and returns the user + * space address by adding it to the given virtual sram base address + * + * Arguments: + * size - the size in bytes of the requested allocation + * virtual_sram_base - the virtual address of sram_base as assigned + * by a call to mmap. + * pid - the pid of the calling process + * + *************************************************************************/ + +void *xscale_sram_malloc_user(size_t size, u32 virtual_sram_base, u32 pid) { +u32 addr; + + if(xscale_sram_malloc_local(size, &addr, pid)) return NULL; + return((void *)(virtual_sram_base + addr)); +} + +/************************************************************************* + * + * Function: xscale_sram_free + * Description: Frees a block of sram from kernel space + * + * Arguments: + * ptr - the address of the block to free + * + *************************************************************************/ + +void xscale_sram_free(void *ptr) { + + if((u32)ptr >= sram_mem) + remove_sram_entry((u32)ptr - sram_mem); +} +/************************************************************************* + * + * Function: xscale_sram_free + * Description: Frees a block of sram from user space + * + * Arguments: + * ptr - the address of the block to free + * + *************************************************************************/ + +void xscale_sram_free_user(void *ptr, u32 virtual_sram_base) { + + if((u32)ptr >= virtual_sram_base) + remove_sram_entry((u32)ptr - virtual_sram_base); +} + +/************************************************************************* + * + * Function: xscale_sram_free + * Description: Frees all sram blocks for a given process + * + * Arguments: + * pid - the pid for whom all sram allocations should be freed + * +*************************************************************************/ + +void xscale_sram_free_all_pid(u32 pid) { + + if(pid == 0) { + printk(KERN_WARNING "Attempt to free memory for invalid pid, free failed\n"); + return; + } + remove_sram_pid(pid); +} + +EXPORT_SYMBOL(xscale_sram_malloc); +EXPORT_SYMBOL(xscale_sram_free); +EXPORT_SYMBOL(xscale_sram_malloc_user); +EXPORT_SYMBOL(xscale_sram_free_user); +EXPORT_SYMBOL(xscale_sram_free_all_pid); + +__initcall(sram_init); + diff -uNr a/arch/arm/mach-pxa/vcs.c b/arch/arm/mach-pxa/vcs.c --- a/arch/arm/mach-pxa/vcs.c 1970-01-01 08:00:00.000000000 +0800 +++ b/arch/arm/mach-pxa/vcs.c 2005-01-27 09:56:48.000000000 +0800 @@ -0,0 +1,234 @@ +/* + * linux/arch/arm/mach-pxa/vcs.c + * + * Bulverde voltage change sequencer driver. + * + * Copyright (C) 2003-2004 Intel Corporation. + * + * Author: Cain Yuan + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Functionality: Initialize PWR I2C. + * Argument: None + * Return: void + */ +static int __init vcs_init(void) +{ + CKEN |= 0x1 << 15; + CKEN |= 0x1 << 14; + PCFR = PCFR_PI2CEN; +// PCFR = 0x60; + return 0; +} + +#ifdef CONFIG_PXA27x_E28 + +/* + * The voltage change process is the workround for + * E28: Core hangs during voltage change when there are outstanding + * transaction on the bus. + */ +static void pxa27x_change_voltage(void) +{ + unsigned long flags; + volatile int *ramstart; + unsigned int unused; + + printk("Workround for E28 about Core hangs during voltage change when there are outstanding transaction on the bus.\n If you want to disable workround, DO NOT set \"CONFIG_PXA27x_E28=y\"\n"); + + /* map the first page of sdram to an uncached virtual page */ + ramstart = (int *)ioremap(PHYS_OFFSET, 4096); + + local_irq_save(flags); + + __asm__ __volatile__("\n\ + @ WORKAROUND - Core hangs on voltage change at different \n\ + @ alignments and at different core clock frequencies \n\ + @ To ensure that no external fetches occur, we want to \n\ + @ store the next several instructions that occur after the \n\ + @ voltage change inside the cache. The load dependency \n\ + @ stall near the retry label ensures that any outstanding \n\ + @ instruction cacheline loads are complete before \n\ + @ the mcr instruction is executed on the 2nd pass. \n\ + @ This procedure ensures us that the internal bus will not \n\ + @ be busy. \n\ + \n\ + b 2f \n\ + nop \n\ + .align 5 \n\ + 2: \n\ + ldr r0, [%1] @ APB register read and compare \n\ + cmp r0, #0 @ fence for pending slow apb reads \n\ + \n\ + mov r0, #8 @ VC bit for PWRMODE \n\ + movs r1, #1 @ don't execute mcr on 1st pass \n\ + \n\ + @ %1 points to uncacheable memory to force memory read \n\ + \n\ + retry: \n\ + ldreq r3, [%2] @ only stall on the 2nd pass\n\ + cmpeq r3, #0 @ cmp causes fence on mem transfers\n\ + cmp r1, #0 @ is this the 2nd pass? \n\ + mcreq p14, 0, r0, c7, c0, 0 @ write to PWRMODE on 2nd pass only \n\ + \n\ + @ Read VC bit until it is 0, indicates that the VoltageChange is done.\n\ + @ On first pass, we never set the VC bit, so it will be clear already.\n\ + \n\ + VoltageChange_loop: \n\ + mrc p14, 0, r3, c7, c0, 0 \n\ + tst r3, #0x8 \n\ + bne VoltageChange_loop \n\ + \n\ + subs r1, r1, #1 @ update conditional execution counter\n\ + beq retry" + + : "=&r" (unused) + : "r" (&CCCR), "r" (ramstart) + : "r0", "r1", "r3" ); + + local_irq_restore(flags); + + /* unmap the page we used */ + iounmap(ramstart); +} + +#else + +static void pxa27x_change_voltage(void) +{ + unsigned long flags; + + local_irq_save(flags); + + __asm__ __volatile__(" \n\ + mrc p14, 0, r0, c7, c0, 0 @ read c7 \n\ + orr r0, r0, #0x8 @ Voltage change sequence begins \n\ + mcr p14, 0, r0, c7, c0, 0 @ set the bit. \n\ + mrc p14, 0, r0, c7, c0, 0 @ read c7 \n\ + mov r0, r0 \n\ + nop \n\ + nop" + : + : + :"r0" ); + + local_irq_restore(flags); +} + +#endif + +static void clr_all_sqc(void) +{ + int i = 0; + for (i = 0; i < 32; i++) + PCMD(i) &= ~PCMD_SQC; +} + +static void clr_all_mbc(void) +{ + int i = 0; + for (i = 0; i < 32; i++) + PCMD(i) &= ~PCMD_MBC; +} + +static void clr_all_dce(void) +{ + int i = 0; + for (i = 0; i < 32; i++) + PCMD(i) &= ~PCMD_DCE; +} + +static void set_mbc_bit(int ReadPointer, int NumOfBytes) +{ + PCMD0 |= PCMD_MBC; + PCMD1 |= PCMD_MBC; +} + +static void set_lc_bit(int ReadPointer, int NumOfBytes) +{ + PCMD0 |= PCMD_LC; + PCMD1 |= PCMD_LC; + PCMD2 |= PCMD_LC; +} + +static void set_cmd_data(unsigned char *DataArray, int StartPoint, int size) +{ + PCMD0 &= 0xFFFFFF00; + PCMD0 |= DataArray[0]; + PCMD1 &= 0xFFFFFF00; + PCMD1 |= DataArray[1]; + PCMD2 &= 0xFFFFFF00; + PCMD2 |= DataArray[2]; +} + +static void ipm_power_change_cmd(unsigned int DACValue) +{ + unsigned char dataArray[3]; + + dataArray[0] = 0; /* Command 0 */ + dataArray[1] = (DACValue & 0x000000FF); /* data LSB */ + dataArray[2] = (DACValue & 0x0000FF00) >> 8; /* data MSB */ + + PVCR = 0; + + PCFR &= ~PCFR_FVC; + PVCR &= 0xFFFFF07F; /* no delay is necessary */ + PVCR &= 0xFFFFFF80; /* clear slave address */ + PVCR |= 0x20; /* set slave address */ + + PVCR &= 0xFE0FFFFF; /* clear read pointer 0 */ + PVCR |= 0; + + /* DCE and SQC are not necessary for single command */ + clr_all_sqc(); + clr_all_dce(); + + clr_all_mbc(); + set_mbc_bit(0, 2); + + /* indicate the last byte of this command is holded in this register */ + PCMD2 &= ~PCMD_MBC; + + /* indicate this is the first command and last command also */ + set_lc_bit(0, 3); + + /* programming the command data bit */ + set_cmd_data(dataArray, 0, 2); +} + +void vm_setvoltage(unsigned int DACValue) +{ + printk("vm_setvoltage\n"); + ipm_power_change_cmd(DACValue); + /* Enable Power I2C */ + PCFR |= PCFR_PI2CEN; + /* Execute voltage change sequence */ + pxa27x_change_voltage(); //set VC on the PWRMODE on CP14 +} + +/* + * Prepare for a coupled voltage & frequency change + */ +void vm_pre_setvoltage(unsigned int DACValue) +{ + ipm_power_change_cmd(DACValue); + /* Enable Power I2C */ + PCFR |= (PCFR_PI2CEN | PCFR_FVC); +} + + +module_init(vcs_init); diff -uNr a/drivers/char/Kconfig b/drivers/char/Kconfig --- a/drivers/char/Kconfig 2004-10-19 05:53:07.000000000 +0800 +++ b/drivers/char/Kconfig 2005-01-27 09:56:48.000000000 +0800 @@ -729,6 +729,12 @@ To compile this driver as a module, choose M here: the module will be called rtc. +config PXA_RTC + tristate "PXA27x/25x/21x or SA1100 Real Time Clock Support" + depends on ARCH_PXA || ARCH_SA1100 + default y + ---help--- + config SGI_DS1286 tristate "SGI DS1286 RTC support" depends on SGI_IP22 diff -uNr a/drivers/char/Makefile b/drivers/char/Makefile --- a/drivers/char/Makefile 2004-10-19 05:55:28.000000000 +0800 +++ b/drivers/char/Makefile 2005-01-27 09:56:48.000000000 +0800 @@ -58,6 +58,7 @@ obj-$(CONFIG_APPLICOM) += applicom.o obj-$(CONFIG_SONYPI) += sonypi.o obj-$(CONFIG_RTC) += rtc.o +obj-$(CONFIG_PXA_RTC) += sa1100-rtc.o obj-$(CONFIG_HPET) += hpet.o obj-$(CONFIG_GEN_RTC) += genrtc.o obj-$(CONFIG_EFI_RTC) += efirtc.o diff -uNr a/drivers/char/sa1100-rtc.c b/drivers/char/sa1100-rtc.c --- a/drivers/char/sa1100-rtc.c 1970-01-01 08:00:00.000000000 +0800 +++ b/drivers/char/sa1100-rtc.c 2005-01-27 09:56:48.000000000 +0800 @@ -0,0 +1,491 @@ +/* + * Real Time Clock interface for Linux on StrongARM SA1100 + * and XScale PXA250/210. + * + * Copyright (c) 2000 Nils Faerber + * + * Based on rtc.c by Paul Gortmaker + * Date/time conversion routines taken from arch/arm/kernel/time.c + * by Linus Torvalds and Russel King + * and the GNU C Library + * ( ... I love the GPL ... just take what you need! ;) + * + * 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. + * + * 1.00 2001-06-08 Nicolas Pitre + * - added periodic timer capability using OSMR1 + * - flag compatibility with other RTC chips + * - permission checks for ioctls + * - major cleanup, partial rewrite + * + * 0.03 2001-03-07 CIH + * - Modify the bug setups RTC clock. + * + * 0.02 2001-02-27 Nils Faerber + * - removed mktime(), added alarm irq clear + * + * 0.01 2000-10-01 Nils Faerber + * - initial release + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION "1.00" + +#if defined(CONFIG_PXA27x) +/* + * Bulverde timer + */ +#define TIMER_FREQ 3250000 +#else +/* + * PXA250/210/SA timer + */ +#define TIMER_FREQ 3686400 +#endif + + +#define RTC_DEF_DIVIDER 32768 - 1 +#define RTC_DEF_TRIM 0 + +/* Those are the bits from a classic RTC we want to mimic */ +#define RTC_IRQF 0x80 /* any of the following 3 is active */ +#define RTC_PF 0x40 +#define RTC_AF 0x20 +#define RTC_UF 0x10 + +static unsigned long rtc_status; +static unsigned long rtc_irq_data; +static unsigned long rtc_freq = 1024; + +static struct fasync_struct *rtc_async_queue; +static DECLARE_WAIT_QUEUE_HEAD(rtc_wait); + +extern spinlock_t rtc_lock; + +static const unsigned char days_in_mo[] = + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +#define is_leap(year) \ + ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) + +/* + * Converts seconds since 1970-01-01 00:00:00 to Gregorian date. + */ + +static void decodetime (unsigned long t, struct rtc_time *tval) +{ + long days, month, year, rem; + + days = t / 86400; + rem = t % 86400; + tval->tm_hour = rem / 3600; + rem %= 3600; + tval->tm_min = rem / 60; + tval->tm_sec = rem % 60; + tval->tm_wday = (4 + days) % 7; + +#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400) + + year = 1970 + days / 365; + days -= ((year - 1970) * 365 + + LEAPS_THRU_END_OF (year - 1) + - LEAPS_THRU_END_OF (1970 - 1)); + if (days < 0) { + year -= 1; + days += 365 + is_leap(year); + } + tval->tm_year = year - 1900; + tval->tm_yday = days + 1; + + month = 0; + if (days >= 31) { + days -= 31; + month++; + if (days >= (28 + is_leap(year))) { + days -= (28 + is_leap(year)); + month++; + while (days >= days_in_mo[month]) { + days -= days_in_mo[month]; + month++; + } + } + } + tval->tm_mon = month; + tval->tm_mday = days + 1; +} + +static irqreturn_t rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned int rtsr = RTSR; + + /* clear interrupt sources */ + RTSR = 0; + RTSR = (RTSR_AL|RTSR_HZ); + + /* clear alarm interrupt if it has occurred */ + if (rtsr & RTSR_AL) + rtsr &= ~RTSR_ALE; + RTSR = rtsr & (RTSR_ALE|RTSR_HZE); + + /* update irq data & counter */ + if (rtsr & RTSR_AL) + rtc_irq_data |= (RTC_AF|RTC_IRQF); + if (rtsr & RTSR_HZ) + rtc_irq_data |= (RTC_UF|RTC_IRQF); + rtc_irq_data += 0x100; + + /* wake up waiting process */ + wake_up_interruptible(&rtc_wait); + kill_fasync (&rtc_async_queue, SIGIO, POLL_IN); + + return IRQ_HANDLED; +} + +static irqreturn_t timer1_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + /* + * If we match for the first time, the periodic interrupt flag won't + * be set. If it is, then we did wrap around (very unlikely but + * still possible) and compute the amount of missed periods. + * The match reg is updated only when the data is actually retrieved + * to avoid unnecessary interrupts. + */ + OSSR = OSSR_M1; /* clear match on timer1 */ + if (rtc_irq_data & RTC_PF) { + rtc_irq_data += (rtc_freq * ((1<<30)/(TIMER_FREQ>>2))) << 8; + } else { + rtc_irq_data += (0x100|RTC_PF|RTC_IRQF); + } + + wake_up_interruptible(&rtc_wait); + kill_fasync (&rtc_async_queue, SIGIO, POLL_IN); + + return IRQ_HANDLED; +} + +static int rtc_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit (1, &rtc_status)) + return -EBUSY; + rtc_irq_data = 0; + return 0; +} + +static int rtc_release(struct inode *inode, struct file *file) +{ + spin_lock_irq (&rtc_lock); + RTSR = 0; + RTSR = (RTSR_AL|RTSR_HZ); + OIER &= ~OIER_E1; + OSSR = OSSR_M1; + spin_unlock_irq (&rtc_lock); + rtc_status = 0; + return 0; +} + +static int rtc_fasync (int fd, struct file *filp, int on) +{ + return fasync_helper (fd, filp, on, &rtc_async_queue); +} + +static unsigned int rtc_poll(struct file *file, poll_table *wait) +{ + poll_wait (file, &rtc_wait, wait); + return (rtc_irq_data) ? 0 : POLLIN | POLLRDNORM; +} + +static loff_t rtc_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +ssize_t rtc_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long data; + ssize_t retval; + + if (count < sizeof(unsigned long)) + return -EINVAL; + + add_wait_queue(&rtc_wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + for (;;) { + spin_lock_irq (&rtc_lock); + data = rtc_irq_data; + if (data != 0) { + rtc_irq_data = 0; + break; + } + spin_unlock_irq (&rtc_lock); + + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto out; + } + + if (signal_pending(current)) { + retval = -ERESTARTSYS; + goto out; + } + + schedule(); + } + + if (data & RTC_PF) { + /* interpolate missed periods and set match for the next one */ + unsigned long period = TIMER_FREQ/rtc_freq; + unsigned long oscr = OSCR; + unsigned long osmr1 = OSMR1; + unsigned long missed = (oscr - osmr1)/period; + data += missed << 8; + OSSR = OSSR_M1; /* clear match on timer 1 */ + OSMR1 = osmr1 + (missed + 1)*period; + /* ensure we didn't miss another match in the mean time */ + while( (signed long)((osmr1 = OSMR1) - OSCR) <= 0 ) { + data += 0x100; + OSSR = OSSR_M1; /* clear match on timer 1 */ + OSMR1 = osmr1 + period; + } + } + spin_unlock_irq (&rtc_lock); + + data -= 0x100; /* the first IRQ wasn't actually missed */ + + retval = put_user(data, (unsigned long *)buf); + if (!retval) + retval = sizeof(unsigned long); + +out: + set_current_state(TASK_RUNNING); + remove_wait_queue(&rtc_wait, &wait); + return retval; +} + +static int rtc_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct rtc_time tm, tm2; + + switch (cmd) { + case RTC_AIE_OFF: + spin_lock_irq(&rtc_lock); + RTSR &= ~RTSR_ALE; + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_AIE_ON: + spin_lock_irq(&rtc_lock); + RTSR |= RTSR_ALE; + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_UIE_OFF: + spin_lock_irq(&rtc_lock); + RTSR &= ~RTSR_HZE; + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_UIE_ON: + spin_lock_irq(&rtc_lock); + RTSR |= RTSR_HZE; + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_PIE_OFF: + spin_lock_irq(&rtc_lock); + OIER &= ~OIER_E1; + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_PIE_ON: + if ((rtc_freq > 64) && !capable(CAP_SYS_RESOURCE)) + return -EACCES; + spin_lock_irq(&rtc_lock); + OSMR1 = TIMER_FREQ/rtc_freq + OSCR; + OIER |= OIER_E1; + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_ALM_READ: + decodetime (RTAR, &tm); + break; + case RTC_ALM_SET: + if (copy_from_user (&tm2, (struct rtc_time*)arg, sizeof (tm2))) + return -EFAULT; + decodetime (RCNR, &tm); + if ((unsigned)tm2.tm_hour < 24) + tm.tm_hour = tm2.tm_hour; + if ((unsigned)tm2.tm_min < 60) + tm.tm_min = tm2.tm_min; + if ((unsigned)tm2.tm_sec < 60) + tm.tm_sec = tm2.tm_sec; + RTAR = mktime ( tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + return 0; + case RTC_RD_TIME: + decodetime (RCNR, &tm); + break; + case RTC_SET_TIME: + if (!capable(CAP_SYS_TIME)) + return -EACCES; + if (copy_from_user (&tm, (struct rtc_time*)arg, sizeof (tm))) + return -EFAULT; + tm.tm_year += 1900; + if (tm.tm_year < 1970 || (unsigned)tm.tm_mon >= 12 || + tm.tm_mday < 1 || tm.tm_mday > (days_in_mo[tm.tm_mon] + + (tm.tm_mon == 1 && is_leap(tm.tm_year))) || + (unsigned)tm.tm_hour >= 24 || + (unsigned)tm.tm_min >= 60 || + (unsigned)tm.tm_sec >= 60) + return -EINVAL; + RCNR = mktime ( tm.tm_year, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + return 0; + case RTC_IRQP_READ: + return put_user(rtc_freq, (unsigned long *)arg); + case RTC_IRQP_SET: + if (arg < 1 || arg > TIMER_FREQ) + return -EINVAL; + if ((arg > 64) && (!capable(CAP_SYS_RESOURCE))) + return -EACCES; + rtc_freq = arg; + return 0; + case RTC_EPOCH_READ: + return put_user (1970, (unsigned long *)arg); + default: + return -EINVAL; + } + return copy_to_user ((void *)arg, &tm, sizeof (tm)) ? -EFAULT : 0; +} + +static struct file_operations rtc_fops = { + owner: THIS_MODULE, + llseek: rtc_llseek, + read: rtc_read, + poll: rtc_poll, + ioctl: rtc_ioctl, + open: rtc_open, + release: rtc_release, + fasync: rtc_fasync, +}; + +static struct miscdevice sa1100rtc_miscdev = { + RTC_MINOR, + "rtc", + &rtc_fops +}; + +static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + char *p = page; + int len; + struct rtc_time tm; + + decodetime (RCNR, &tm); + p += sprintf(p, "rtc_time\t: %02d:%02d:%02d\n" + "rtc_date\t: %04d-%02d-%02d\n" + "rtc_epoch\t: %04d\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 1970); + decodetime (RTAR, &tm); + p += sprintf(p, "alrm_time\t: %02d:%02d:%02d\n" + "alrm_date\t: %04d-%02d-%02d\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); + p += sprintf(p, "trim/divider\t: 0x%08x\n", RTTR); + p += sprintf(p, "alarm_IRQ\t: %s\n", (RTSR & RTSR_ALE) ? "yes" : "no" ); + p += sprintf(p, "update_IRQ\t: %s\n", (RTSR & RTSR_HZE) ? "yes" : "no"); + p += sprintf(p, "periodic_IRQ\t: %s\n", (OIER & OIER_E1) ? "yes" : "no"); + p += sprintf(p, "periodic_freq\t: %ld\n", rtc_freq); + + len = (p - page) - off; + if (len < 0) + len = 0; + + *eof = (len <= count) ? 1 : 0; + *start = page + off; + + return len; +} + +static int __init rtc_init(void) +{ + int ret; + + misc_register (&sa1100rtc_miscdev); + create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL); + ret = request_irq (IRQ_RTC1Hz, rtc_interrupt, SA_INTERRUPT, "rtc 1Hz", NULL); + if (ret) { + printk (KERN_ERR "rtc: IRQ %d already in use.\n", IRQ_RTC1Hz); + goto IRQ_RTC1Hz_failed; + } + ret = request_irq (IRQ_RTCAlrm, rtc_interrupt, SA_INTERRUPT, "rtc Alrm", NULL); + if (ret) { + printk(KERN_ERR "rtc: IRQ %d already in use.\n", IRQ_RTCAlrm); + goto IRQ_RTCAlrm_failed; + } + ret = request_irq (IRQ_OST1, timer1_interrupt, SA_INTERRUPT, "rtc timer", NULL); + if (ret) { + printk(KERN_ERR "rtc: IRQ %d already in use.\n", IRQ_OST1); + goto IRQ_OST1_failed; + } + + printk (KERN_INFO "SA1100 Real Time Clock driver v" DRIVER_VERSION "\n"); + + /* + * According to the manual we should be able to let RTTR be zero + * and then a default diviser for a 32.768KHz clock is used. + * Apparently this doesn't work, at least for my SA1110 rev 5. + * If the clock divider is uninitialized then reset it to the + * default value to get the 1Hz clock. + */ + if (RTTR == 0) { + RTTR = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16); + printk (KERN_WARNING "rtc: warning: initializing default clock divider/trim value\n"); + /* The current RTC value probably doesn't make sense either */ + RCNR = 0; + } + + return 0; + +IRQ_OST1_failed: + free_irq (IRQ_RTCAlrm, NULL); +IRQ_RTCAlrm_failed: + free_irq (IRQ_RTC1Hz, NULL); +IRQ_RTC1Hz_failed: + remove_proc_entry ("driver/rtc", NULL); + misc_deregister (&sa1100rtc_miscdev); + return ret; +} + +static void __exit rtc_exit(void) +{ + free_irq (IRQ_OST1, NULL); + free_irq (IRQ_RTCAlrm, NULL); + free_irq (IRQ_RTC1Hz, NULL); + remove_proc_entry ("driver/rtc", NULL); + misc_deregister (&sa1100rtc_miscdev); +} + +module_init(rtc_init); +module_exit(rtc_exit); + +MODULE_AUTHOR("Nils Faerber "); +MODULE_DESCRIPTION("SA1100/PXA Realtime Clock Driver (RTC)"); diff -uNr a/drivers/i2c/algos/i2c-algo-pxa.c b/drivers/i2c/algos/i2c-algo-pxa.c --- a/drivers/i2c/algos/i2c-algo-pxa.c 1970-01-01 08:00:00.000000000 +0800 +++ b/drivers/i2c/algos/i2c-algo-pxa.c 2005-01-27 09:56:48.000000000 +0800 @@ -0,0 +1,381 @@ +/* + * i2c-algo-pxa.c + * + * I2C algorithm for the PXA I2C bus access. + * Byte driven algorithm similar to pcf. + * + * Copyright (C) 2002 Intrinsyc Software 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. + * + * History: + * Apr 2002: Initial version [CS] + * Jun 2002: Properly seperated algo/adap [FB] + * Jan 2003: added limited signal handling [Kai-Uwe Bloem] + * Jan 2003: allow SMBUS_QUICK as valid msg [FB] + * Sep 2004: ported to 2.6 kernel, Yan Yin + * + */ +#include +#include + +#include +#include +#include +#include /* struct i2c_msg and others */ +#include + +#include + +/* + * Set this to zero to remove all the debug statements via dead code elimination. + */ +//#define DEBUG 1 + +#if DEBUG +static unsigned int i2c_debug = DEBUG; +#else +#define i2c_debug 0 +#endif + +static int pxa_scan = 1; + +static int i2c_pxa_valid_messages( struct i2c_msg msgs[], int num) +{ + int i; + if (num < 1 || num > MAX_MESSAGES){ + if( i2c_debug) + printk(KERN_INFO "Invalid number of messages (max=%d, num=%d)\n", + MAX_MESSAGES, num); + return -EINVAL; + } + + /* check consistency of our messages */ + for (i=0;ialgo_data; + + /* increment number of bytes to read by one -- read dummy byte */ + for (i = 0; i <= count; i++) { + if (i!=0){ + /* set ACK to NAK for last received byte ICR[ACKNAK] = 1 + only if not a repeated start */ + + if ((i == count) && last) { + adap->transfer( last, I2C_RECEIVE, 0); + }else{ + adap->transfer( 0, I2C_RECEIVE, 1); + } + + timeout = adap->wait_for_interrupt(I2C_RECEIVE); + +#ifdef DEBUG + if (timeout==BUS_ERROR){ + printk(KERN_INFO "i2c_pxa_readbytes: bus error -> forcing reset\n"); + adap->reset(); + return I2C_RETRY; + } else +#endif + if (timeout == -ERESTARTSYS) { + adap->abort(); + return timeout; + } else + if (timeout){ +#ifdef DEBUG + printk(KERN_INFO "i2c_pxa_readbytes: timeout -> forcing reset\n"); +#endif + adap->reset(); + return I2C_RETRY; + } + + } + + if (i) { + buf[i - 1] = adap->read_byte(); + } else { + adap->read_byte(); /* dummy read */ + } + } + return (i - 1); +} + +static int i2c_pxa_sendbytes(struct i2c_adapter *i2c_adap, const char *buf, + int count, int last) +{ + + struct i2c_algo_pxa_data *adap = i2c_adap->algo_data; + int wrcount, timeout; + + for (wrcount=0; wrcountwrite_byte(buf[wrcount]); + if ((wrcount==(count-1)) && last) { + adap->transfer( last, I2C_TRANSMIT, 0); + }else{ + adap->transfer( 0, I2C_TRANSMIT, 1); + } + + timeout = adap->wait_for_interrupt(I2C_TRANSMIT); + +#ifdef DEBUG + if (timeout==BUS_ERROR) { + printk(KERN_INFO "i2c_pxa_sendbytes: bus error -> forcing reset.\n"); + adap->reset(); + return I2C_RETRY; + } else +#endif + if (timeout == -ERESTARTSYS) { + adap->abort(); + return timeout; + } else + if (timeout) { +#ifdef DEBUG + printk(KERN_INFO "i2c_pxa_sendbytes: timeout -> forcing reset\n"); +#endif + adap->reset(); + return I2C_RETRY; + } + } + return (wrcount); +} + + +static inline int i2c_pxa_set_ctrl_byte(struct i2c_algo_pxa_data * adap, struct i2c_msg *msg) +{ + u16 flags = msg->flags; + u8 addr; + addr = (u8) ( (0x7f & msg->addr) << 1 ); + if (flags & I2C_M_RD ) + addr |= 1; + if (flags & I2C_M_REV_DIR_ADDR ) + addr ^= 1; + adap->write_byte(addr); + return 0; +} + +static int i2c_pxa_do_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) +{ + struct i2c_algo_pxa_data * adap; + struct i2c_msg *pmsg=NULL; + int i; + int ret=0, timeout; + + adap = i2c_adap->algo_data; + + timeout = adap->wait_bus_not_busy(); + + if (timeout) { + return I2C_RETRY; + } + + for (i = 0;ret >= 0 && i < num; i++) { + int last = i + 1 == num; + pmsg = &msgs[i]; + + ret = i2c_pxa_set_ctrl_byte(adap,pmsg); + + /* Send START */ + if (i == 0) { + adap->start(); + }else{ + adap->repeat_start(); + } + + adap->transfer(0, I2C_TRANSMIT, 0); + + /* Wait for ITE (transmit empty) */ + timeout = adap->wait_for_interrupt(I2C_TRANSMIT); + +#ifdef DEBUG + /* Check for ACK (bus error) */ + if (timeout==BUS_ERROR){ + printk(KERN_INFO "i2c_pxa_do_xfer: bus error -> forcing reset\n"); + adap->reset(); + return I2C_RETRY; + } else +#endif + if (timeout == -ERESTARTSYS) { + adap->abort(); + return timeout; + } else + if (timeout) { +#ifdef DEBUG + printk(KERN_INFO "i2c_pxa_do_xfer: timeout -> forcing reset\n"); +#endif + adap->reset(); + return I2C_RETRY; + } +/* FIXME: handle arbitration... */ +#if 0 + /* Check for bus arbitration loss */ + if (adap->arbitration_loss()){ + printk("Arbitration loss detected \n"); + adap->reset(); + return I2C_RETRY; + } +#endif + + /* Read */ + if (pmsg->flags & I2C_M_RD) { + /* read bytes into buffer*/ + ret = i2c_pxa_readbytes(i2c_adap, pmsg->buf, pmsg->len, last); +#if DEBUG > 2 + if (ret != pmsg->len) { + printk(KERN_INFO"i2c_pxa_do_xfer: read %d/%d bytes.\n", + ret, pmsg->len); + } else { + printk(KERN_INFO"i2c_pxa_do_xfer: read %d bytes.\n",ret); + } +#endif + } else { /* Write */ + ret = i2c_pxa_sendbytes(i2c_adap, pmsg->buf, pmsg->len, last); +#if DEBUG > 2 + if (ret != pmsg->len) { + printk(KERN_INFO"i2c_pxa_do_xfer: wrote %d/%d bytes.\n", + ret, pmsg->len); + } else { + printk(KERN_INFO"i2c_pxa_do_xfer: wrote %d bytes.\n",ret); + } +#endif + } + } + + if (ret<0){ + return ret; + }else{ + return i; + } +} + +static int i2c_pxa_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) +{ + int retval = i2c_pxa_valid_messages( msgs, num); + if( retval > 0) + { + int i; + for (i=i2c_adap->retries; i>=0; i--){ + int retval = i2c_pxa_do_xfer(i2c_adap,msgs,num); + if (retval!=I2C_RETRY){ + return retval; + } + if( i2c_debug)printk(KERN_INFO"Retrying transmission \n"); + udelay(100); + } + if( i2c_debug)printk(KERN_INFO"Retried %i times\n",i2c_adap->retries); + return -EREMOTEIO; + + } + return retval; +} + +static u32 i2c_pxa_functionality(struct i2c_adapter * adapter) +{ + /* Emulate the SMBUS functions */ + return I2C_FUNC_SMBUS_EMUL; +} + +struct i2c_algorithm i2c_pxa_algorithm = { + .name = "PXA I2C Algorithm", + .id = I2C_ALGO_PXA, + .master_xfer = i2c_pxa_xfer, + .functionality = i2c_pxa_functionality, +}; + +/* + * registering functions to load algorithms at runtime + */ +int i2c_pxa_add_bus(struct i2c_adapter *i2c_adap) +{ + struct i2c_algo_pxa_data *adap = i2c_adap->algo_data; + + printk(KERN_INFO"I2C: Adding %s.\n", i2c_adap->name); + + i2c_adap->algo = &i2c_pxa_algorithm; + + /* register new adapter to i2c module... */ + i2c_add_adapter(i2c_adap); + + adap->reset(); + + /* scan bus */ + if (pxa_scan) { + int i; + printk(KERN_INFO "I2C: Scanning bus "); + for (i = 0x02; i < 0xff; i+=2) { + if( i==(I2C_PXA_SLAVE_ADDR<<1)) continue; + + if (adap->wait_bus_not_busy()) { + printk(KERN_INFO "I2C: scanning bus %s - TIMEOUTed.\n", + i2c_adap->name); + return -EIO; + } + adap->write_byte(i); + adap->start(); + adap->transfer(0, I2C_TRANSMIT, 0); + + if ((adap->wait_for_interrupt(I2C_TRANSMIT) != BUS_ERROR)) { + printk("(%02x)",i>>1); + adap->abort(); + } else { + adap->stop(); + } + udelay(adap->udelay); + } + printk("\n"); + } + return 0; +} + +int i2c_pxa_del_bus(struct i2c_adapter *i2c_adap) +{ + int res; + if ((res = i2c_del_adapter(i2c_adap)) < 0) + return res; + + printk(KERN_INFO "I2C: Removing %s.\n", i2c_adap->name); + + return 0; +} + +static int __init i2c_algo_pxa_init (void) +{ + printk(KERN_INFO "I2C: PXA algorithm module loaded.\n"); + return 0; +} + +static void i2c_algo_pxa_exit(void) +{ + printk(KERN_INFO "I2C: PXA algorithm module removed.\n"); +} + +EXPORT_SYMBOL(i2c_pxa_add_bus); +EXPORT_SYMBOL(i2c_pxa_del_bus); + +MODULE_PARM(pxa_scan, "i"); +MODULE_PARM_DESC(pxa_scan, "Scan for active chips on the bus"); + +MODULE_AUTHOR("Intrinsyc Software Inc."); +MODULE_LICENSE("GPL"); + +module_init(i2c_algo_pxa_init); +module_exit(i2c_algo_pxa_exit); diff -uNr a/drivers/i2c/algos/Kconfig b/drivers/i2c/algos/Kconfig --- a/drivers/i2c/algos/Kconfig 2004-10-19 05:54:55.000000000 +0800 +++ b/drivers/i2c/algos/Kconfig 2005-01-27 09:56:48.000000000 +0800 @@ -53,5 +53,8 @@ tristate "MPC8xx CPM I2C interface" depends on 8xx && I2C +config I2C_ALGOPXA + tristate "PXA I2C Algorithm" + depends on PXA27x && I2C endmenu diff -uNr a/drivers/i2c/algos/Makefile b/drivers/i2c/algos/Makefile --- a/drivers/i2c/algos/Makefile 2004-10-19 05:54:40.000000000 +0800 +++ b/drivers/i2c/algos/Makefile 2005-01-27 09:56:48.000000000 +0800 @@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_ALGOPCF) += i2c-algo-pcf.o obj-$(CONFIG_I2C_ALGOPCA) += i2c-algo-pca.o obj-$(CONFIG_I2C_ALGOITE) += i2c-algo-ite.o +obj-$(CONFIG_I2C_ALGOPXA) += i2c-algo-pxa.o ifeq ($(CONFIG_I2C_DEBUG_ALGO),y) EXTRA_CFLAGS += -DDEBUG diff -uNr a/drivers/i2c/busses/i2c-adap-pxa.c b/drivers/i2c/busses/i2c-adap-pxa.c --- a/drivers/i2c/busses/i2c-adap-pxa.c 1970-01-01 08:00:00.000000000 +0800 +++ b/drivers/i2c/busses/i2c-adap-pxa.c 2005-01-27 09:56:48.000000000 +0800 @@ -0,0 +1,383 @@ +/* + * i2c_adap_pxa.c + * + * I2C adapter for the PXA I2C bus access. + * + * Copyright (C) 2002 Intrinsyc Software 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. + * + * History: + * Apr 2002: Initial version [CS] + * Jun 2002: Properly seperated algo/adap [FB] + * Jan 2003: Fixed several bugs concerning interrupt handling [Kai-Uwe Bloem] + * Jan 2003: added limited signal handling [Kai-Uwe Bloem] + * Sep 2004: ported to 2.6 kernel, Yan Yin + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include /* for IRQ_I2C */ + +#include +#include + +/* + * Set this to zero to remove all debug statements via dead code elimination. + */ +//#define DEBUG 1 + +#if DEBUG +static unsigned int i2c_debug = DEBUG; +#else +#define i2c_debug 0 +#endif + +static int irq = 0; +static volatile int i2c_pending = 0; /* interrupt pending when 1 */ +static volatile int bus_error = 0; +static volatile int tx_finished = 0; +static volatile int rx_finished = 0; + +static wait_queue_head_t i2c_wait; +static void i2c_pxa_transfer( int lastbyte, int receive, int midbyte); + +/* place a byte in the transmit register */ +static void i2c_pxa_write_byte(u8 value) +{ + IDBR = value; +} + +/* read byte in the receive register */ +static u8 i2c_pxa_read_byte(void) +{ + return (u8) (0xff & IDBR); +} + +static void i2c_pxa_start(void) +{ + unsigned long icr = ICR; + icr |= ICR_START; + icr &= ~(ICR_STOP | ICR_ALDIE | ICR_ACKNAK); + ICR = icr; + + bus_error=0; /* clear any bus_error from previous txfers */ + tx_finished=0; /* clear rx and tx interrupts from previous txfers */ + rx_finished=0; + i2c_pending = 0; +} + +static void i2c_pxa_repeat_start(void) +{ + unsigned long icr = ICR; + icr |= ICR_START; + icr &= ~(ICR_STOP | ICR_ALDIE); + ICR = icr; + + bus_error=0; /* clear any bus_error from previous txfers */ + tx_finished=0; /* clear rx and tx interrupts from previous txfers */ + rx_finished=0; + i2c_pending=0; +} + +static void i2c_pxa_stop(void) +{ + unsigned long icr = ICR; + icr |= ICR_STOP; + icr &= ~(ICR_START); + ICR = icr; +} + +static void i2c_pxa_midbyte(void) +{ + unsigned long icr = ICR; + icr &= ~(ICR_START | ICR_STOP); + ICR = icr; +} + +static void i2c_pxa_abort(void) +{ + unsigned long timeout = jiffies + HZ/4; + +#ifdef PXA_ABORT_MA + while ((long)(timeout - jiffies) > 0 && (ICR & ICR_TB)) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + } + + ICR |= ICR_MA; + udelay(100); +#else + while ((long)(timeout - jiffies) > 0 && (IBMR & 0x1) == 0) { + i2c_pxa_transfer( 1, I2C_RECEIVE, 1); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + } +#endif + ICR &= ~(ICR_MA | ICR_START | ICR_STOP); +} + +static int i2c_pxa_wait_bus_not_busy( void) +{ + int timeout = DEF_TIMEOUT; + + while (timeout-- && (ISR & ISR_IBB)) { + udelay(100); /* wait for 100 us */ + } + + return (timeout<=0); +} + +static void i2c_pxa_wait_for_ite(void){ + unsigned long flags; + if (irq > 0) { + local_irq_save(flags); + if (i2c_pending == 0) { + interruptible_sleep_on_timeout(&i2c_wait, I2C_SLEEP_TIMEOUT ); + } + i2c_pending = 0; + local_irq_restore(flags); + + } else { + udelay(100); + } +} + +static int i2c_pxa_wait_for_int( int wait_type) +{ + int timeout = DEF_TIMEOUT; +#ifdef DEBUG + if (bus_error) + printk(KERN_INFO"i2c_pxa_wait_for_int: Bus error on enter\n"); + if (rx_finished) + printk(KERN_INFO"i2c_pxa_wait_for_int: Receive interrupt on enter\n"); + if (tx_finished) + printk(KERN_INFO"i2c_pxa_wait_for_int: Transmit interrupt on enter\n"); +#endif + + if (wait_type == I2C_RECEIVE){ /* wait on receive */ + + do { + i2c_pxa_wait_for_ite(); + } while (!(rx_finished) && timeout-- && !signal_pending(current)); + +#ifdef DEBUG + if (timeout<0){ + if (tx_finished) + printk("Error: i2c-algo-pxa.o: received a tx" + " interrupt while waiting on a rx in wait_for_int"); + } +#endif + } else { /* wait on transmit */ + + do { + i2c_pxa_wait_for_ite(); + } while (!(tx_finished) && timeout-- && !signal_pending(current)); + +#ifdef DEBUG + if (timeout<0){ + if (rx_finished) + printk("Error: i2c-algo-pxa.o: received a rx" + " interrupt while waiting on a tx in wait_for_int"); + } +#endif + } + + udelay(ACK_DELAY); /* this is needed for the bus error */ + + tx_finished=0; + rx_finished=0; + + if (bus_error){ + bus_error=0; + if( i2c_debug > 2)printk("wait_for_int: error - no ack.\n"); + return BUS_ERROR; + } + + if (signal_pending(current)) { + return (-ERESTARTSYS); + } else if (timeout < 0) { + if( i2c_debug > 2)printk("wait_for_int: timeout.\n"); + return(-EIO); + } else + return(0); +} + +static void i2c_pxa_transfer( int lastbyte, int receive, int midbyte) +{ + if( lastbyte) + { + if( receive==I2C_RECEIVE) ICR |= ICR_ACKNAK; + i2c_pxa_stop(); + } + else if( midbyte) + { + i2c_pxa_midbyte(); + } + ICR |= ICR_TB; +} + +static void i2c_pxa_reset( void) +{ +#ifdef DEBUG + printk("Resetting I2C Controller Unit\n"); +#endif + + /* abort any transfer currently under way */ + i2c_pxa_abort(); + + /* reset according to 9.8 */ + ICR = ICR_UR; + ISR = I2C_ISR_INIT; + ICR &= ~ICR_UR; + + /* set the global I2C clock on */ + CKEN |= CKEN14_I2C; + + /* set our slave address */ + ISAR = I2C_PXA_SLAVE_ADDR; + + /* set control register values */ + ICR = I2C_ICR_INIT; + + /* clear any leftover states from prior transmissions */ + i2c_pending = rx_finished = tx_finished = bus_error = 0; + + /* enable unit */ + ICR |= ICR_IUE; + udelay(100); +} + +static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id, struct pt_regs *regs) +{ + int status, wakeup = 0; + status = (ISR); + + if (status & ISR_BED){ + (ISR) |= ISR_BED; + bus_error=ISR_BED; + wakeup = 1; + } + if (status & ISR_ITE){ + (ISR) |= ISR_ITE; + tx_finished=ISR_ITE; + wakeup = 1; + } + if (status & ISR_IRF){ + (ISR) |= ISR_IRF; + rx_finished=ISR_IRF; + wakeup = 1; + } + if (wakeup) { + i2c_pending = 1; + wake_up_interruptible(&i2c_wait); + } + return IRQ_HANDLED; +} + +static int i2c_pxa_resource_init( void) +{ + /* Set I2C GPIO pins */ +#ifdef CONFIG_PXA27x /* set i2c command & data GPIO */ + pxa_gpio_mode(GPIO117_I2CSCL_MD); + pxa_gpio_mode(GPIO118_I2CSDA_MD); +#endif + + init_waitqueue_head(&i2c_wait); + + if (request_irq(IRQ_I2C, &i2c_pxa_handler, SA_INTERRUPT, "I2C_PXA", 0) < 0) { + irq = 0; + if( i2c_debug) + printk(KERN_INFO "I2C: Failed to register I2C irq %i\n", IRQ_I2C); + return -ENODEV; + }else{ + irq = IRQ_I2C; + enable_irq(irq); + } + return 0; +} + +static void i2c_pxa_resource_release( void) +{ + if( irq > 0) + { + disable_irq(irq); + free_irq(irq,0); + irq=0; + } +} + +static struct i2c_algo_pxa_data i2c_pxa_data = { + write_byte: i2c_pxa_write_byte, + read_byte: i2c_pxa_read_byte, + + start: i2c_pxa_start, + repeat_start: i2c_pxa_repeat_start, + stop: i2c_pxa_stop, + abort: i2c_pxa_abort, + + wait_bus_not_busy: i2c_pxa_wait_bus_not_busy, + wait_for_interrupt: i2c_pxa_wait_for_int, + transfer: i2c_pxa_transfer, + reset: i2c_pxa_reset, + + udelay: 10, + timeout: DEF_TIMEOUT, +}; + +static struct i2c_adapter i2c_pxa_ops = { + .owner = THIS_MODULE, + .id = I2C_ALGO_PXA, + .name = "PXA i2c adapter", + .algo_data = &i2c_pxa_data, + .retries = 2, +}; + +extern int i2c_pxa_add_bus(struct i2c_adapter *); +extern int i2c_pxa_del_bus(struct i2c_adapter *); + +static int __init i2c_adap_pxa_init(void) +{ + if( i2c_pxa_resource_init() == 0) { + + if (i2c_pxa_add_bus(&i2c_pxa_ops) < 0) { + i2c_pxa_resource_release(); + printk(KERN_INFO "I2C: Failed to add bus\n"); + return -ENODEV; + } + } else { + return -ENODEV; + } + + printk(KERN_INFO "I2C: Successfully added bus\n"); + + return 0; +} + +static void i2c_adap_pxa_exit(void) +{ + i2c_pxa_del_bus( &i2c_pxa_ops); + i2c_pxa_resource_release(); + + printk(KERN_INFO "I2C: Successfully removed bus\n"); +} + +MODULE_DESCRIPTION("PXA27x i2c driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_adap_pxa_init); +module_exit(i2c_adap_pxa_exit); diff -uNr a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig --- a/drivers/i2c/busses/Kconfig 2004-10-19 05:55:06.000000000 +0800 +++ b/drivers/i2c/busses/Kconfig 2005-01-27 09:56:48.000000000 +0800 @@ -430,4 +430,9 @@ This driver can also be built as a module. If so, the module will be called i2c-pca-isa. +config I2C_ADAP_PXA + tristate "PXA I2C Adapter" + depends on I2C && I2C_ALGOPXA + help + endmenu diff -uNr a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile --- a/drivers/i2c/busses/Makefile 2004-10-19 05:53:11.000000000 +0800 +++ b/drivers/i2c/busses/Makefile 2005-01-27 09:56:48.000000000 +0800 @@ -35,6 +35,7 @@ obj-$(CONFIG_I2C_VOODOO3) += i2c-voodoo3.o obj-$(CONFIG_SCx200_ACB) += scx200_acb.o obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o +obj-$(CONFIG_I2C_ADAP_PXA) += i2c-adap-pxa.o ifeq ($(CONFIG_I2C_DEBUG_BUS),y) EXTRA_CFLAGS += -DDEBUG diff -uNr a/drivers/i2c/chips/i2c-adcm2650.c b/drivers/i2c/chips/i2c-adcm2650.c --- a/drivers/i2c/chips/i2c-adcm2650.c 1970-01-01 08:00:00.000000000 +0800 +++ b/drivers/i2c/chips/i2c-adcm2650.c 2005-01-27 09:56:48.000000000 +0800 @@ -0,0 +1,289 @@ +/* + i2c-adcm2650 - ADCM 2650 CMOS sensor I2C client driver + + Copyright (C) 2003, Intel Corporation + + 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. + + Code Status: + 2004/10/19: Yan Yin + - Ported to 2.6 kernel +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-adcm2650.h" + +void i2c_adcm2650_inc_use (struct i2c_client *client); +void i2c_adcm2650_dec_use (struct i2c_client *client); +int i2c_adcm2650_attach_adapter(struct i2c_adapter *adapter); +extern int i2c_adapter_id(struct i2c_adapter *adap); +int i2c_adcm2650_detect_client(struct i2c_adapter *adapter, int address, int flags); +int i2c_adcm2650_cleanup(void); +int i2c_adcm2650_detach_client(struct i2c_client *client); + +struct i2c_driver adcm2650_driver = +{ + .owner = THIS_MODULE, + .name = "adcm2650 driver", /* name */ + .id = I2C_DRIVERID_ADCM2650, /* id */ + .flags = I2C_DF_NOTIFY, /* flags */ + .attach_adapter = &i2c_adcm2650_attach_adapter, /* attach_adapter */ + .detach_client = &i2c_adcm2650_detach_client, /* detach_client */ + .command = NULL, +}; + +extern struct i2c_adapter *i2cdev_adaps[]; +/* Unique ID allocation */ +static int adcm2650_id = 0; +static struct i2c_client *g_client; +static unsigned short normal_i2c[] = { 0x52,I2C_CLIENT_END }; +static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; +I2C_CLIENT_INSMOD; + +static int ChgBlockAddr(u8 block) +{ + + struct adcm2650_data *p; + int res; + + p=(struct adcm2650_data *)i2c_get_clientdata(g_client); + + down(&p->update_lock); + /* FIXME: Shall we change the g_client->addr? */ + g_client->addr = PIPE_SLAVE_ADDR; + res = i2c_smbus_write_byte_data(g_client, BLOCK_SWITCH_CMD, block); + p->blockaddr = block; + up( &p->update_lock); + return res; +} + +int adcm2650_read(u16 addr, u16 *pvalue) +{ + int res=0; + struct adcm2650_data *p; + u8 blockaddr = BLOCK(addr); + u8 offset; + + if( g_client == NULL ) /* No global client pointer? */ + return -1; + + p=(struct adcm2650_data *)i2c_get_clientdata(g_client); + + res = ChgBlockAddr( blockaddr); + + if( res !=0 ) { + printk("Change block address failed. block = %2x \n", blockaddr); + return -1; + } + offset = (addr << 1) & 0xff; + res = i2c_smbus_read_word_data(g_client, offset); + *pvalue = (u16)res; + return res; +} + +int adcm2650_write(u16 addr, u16 value) +{ + int res=0; + struct adcm2650_data *p; + u8 blockaddr = BLOCK(addr); + u8 offset; + + if( g_client == NULL ) /* No global client pointer? */ + return -1; + + p=(struct adcm2650_data *)i2c_get_clientdata(g_client); + res = ChgBlockAddr( blockaddr ); + + if( res !=0 ) { + printk("Change block address failed. block = %2x \n", blockaddr); + return -1; + } + offset = (addr << 1) & 0xff; + return i2c_smbus_write_word_data(g_client, offset, value ); +} + + +int i2c_adcm2650_read(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_word_data(client,reg); +} + +int i2c_adcm2650_write(struct i2c_client *client, u8 reg, u16 value) +{ + return i2c_smbus_write_word_data(client,reg,value); +} + + +int i2c_adcm2650_attach_adapter(struct i2c_adapter *adap) +{ + printk(KERN_INFO "In function %s.\n", __FUNCTION__); + return i2c_probe(adap,&addr_data,i2c_adcm2650_detect_client); +} + + +int i2c_adcm2650_detect_client(struct i2c_adapter *adapter, int address, int flags) +{ + struct i2c_client *new_client=NULL; + int err = 0; + struct adcm2650_data *data; + + printk(KERN_INFO "In funtion %s.\n", __FUNCTION__); + /* Let's see whether this adapter can support what we need. + Please substitute the things you need here! */ + if ( !i2c_check_functionality(adapter,I2C_FUNC_SMBUS_WORD_DATA) ) { + printk(KERN_INFO "Word op is not permited.\n"); + goto ERROR0; + } + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access several i2c functions safely */ + + /* Note that we reserve some space for adcm2650_data too. If you don't + need it, remove it. We do it here to help to lessen memory + fragmentation. */ + new_client=kmalloc(sizeof(struct i2c_client), GFP_KERNEL ); + + if (!new_client) { + err = -ENOMEM; + goto ERROR0; + } + memset(new_client, 0, sizeof(struct i2c_client)); + + data = kmalloc(sizeof(struct adcm2650_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto ERROR1; + } + memset(data, 0, sizeof(struct adcm2650_data)); + + i2c_set_clientdata(new_client, data); + + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &adcm2650_driver; + new_client->flags = 0; + + g_client = new_client; + + /* Now, we do the remaining detection. If no `force' parameter is used. */ + + /* First, the generic detection (if any), that is skipped if any force + parameter was used. */ + +#if 0 + if (kind <= 0) { + /* The below is of course bogus */ + + printk("<1>I2C: Probe ADCM2650 chip.."); + if (i2c_adcm2650_read(new_client, REV) != 0x0600 ) { + printk(KERN_WARNING "Failed.\n"); + goto ERROR1; + } + else { + if ( adcm2650_id == 0 ) + printk("<1> detected.\n"); + } + + } +#endif + + strcpy(new_client->name, "ADCM2650"); + new_client->id = adcm2650_id++; /* Automatically unique */ + + data->valid = 0; /* Only if you use this field */ + init_MUTEX(&data->update_lock); /* Only if you use this field */ + + /* Tell the i2c layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* This function can write default values to the client registers, if + needed. */ + /* adcm2650_init_client(new_client); */ + return 0; + + /* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + +ERROR3: +ERROR1: + kfree(new_client); +ERROR0: + return err; +} + +int i2c_adcm2650_detach_client(struct i2c_client *client) +{ + int err; + + /* Try to detach the client from i2c space */ + if ((err = i2c_detach_client(client))) { + printk("adcm2650.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(i2c_get_clientdata(client)); /* Frees client data too, if allocated at the same time */ + g_client = NULL; + return 0; +} + +/* Keep track of how far we got in the initialization process. If several + things have to initialized, and we fail halfway, only those things + have to be cleaned up! */ +static int adcm2650_initialized = 0; + +int i2c_adcm2650_init(void) +{ + int res; + + printk(KERN_INFO "I2C: driver for device adcm2650.\n"); + if ( (res = i2c_add_driver(&adcm2650_driver)) ) { + printk("adcm2650: Driver registration failed, module not inserted.\n"); + i2c_adcm2650_cleanup(); + return res; + } + adcm2650_initialized = 1; + return 0; +} + +int i2c_adcm2650_cleanup(void) +{ + int res; + + if (adcm2650_initialized == 1) { + if ((res = i2c_del_driver(&adcm2650_driver))) { + printk("adcm2650: Driver registration failed, module not removed.\n"); + return res; + } + adcm2650_initialized = 0; + } + return 0; +} + +EXPORT_SYMBOL(i2c_adcm2650_init); +EXPORT_SYMBOL(adcm2650_write); +EXPORT_SYMBOL(adcm2650_read); +EXPORT_SYMBOL(i2c_adcm2650_cleanup); + +MODULE_DESCRIPTION("I2C client of adcm2650 camera interface "); +MODULE_LICENSE("GPL"); diff -uNr a/drivers/i2c/chips/i2c-adcm2650.h b/drivers/i2c/chips/i2c-adcm2650.h --- a/drivers/i2c/chips/i2c-adcm2650.h 1970-01-01 08:00:00.000000000 +0800 +++ b/drivers/i2c/chips/i2c-adcm2650.h 2005-01-27 09:56:48.000000000 +0800 @@ -0,0 +1,52 @@ +/* + i2c-adcm2650 - ADCM 2650 CMOS sensor I2C client driver + + Copyright (C) 2003, Intel Corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#ifndef _I2C_ADCM2650_H_ +#define _I2C_ADCM2650_H_ +/* Calculating the Module Block Number */ +#define BLOCK(a) (u8)((a) >> 7) /* register's module block address. */ +#define OFFSET(a) (u8)((a) & 0x7F ) /* register's offset to this block. */ + +/* Update the block address.*/ +#define BLOCK_SWITCH_CMD 0xFE +#define PIPE_SLAVE_ADDR 0x0052 +#define SENSOR_SLAVE_ADDR 0x0055 + +#define DEBUG +#define I2C_DRIVERID_ADCM2650 I2C_DRIVERID_EXP1 +/* Register definitions in ADCM2650's chip. */ +#define REV 0x0 + +struct adcm2650_data { + /* + * Because the i2c bus is slow, it is often useful to cache the read + * information of a chip for some time (for example, 1 or 2 seconds). + * It depends of course on the device whether this is really worthwhile + * or even sensible. + */ + struct semaphore update_lock; /* When we are reading lots of information, + another process should not update the + below information */ + char valid; /* != 0 if the following fields are valid. */ + int blockaddr; /* current using block address. */ + unsigned long last_updated; /* In jiffies */ +}; +#endif diff -uNr a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig --- a/drivers/i2c/chips/Kconfig 2004-10-19 05:54:31.000000000 +0800 +++ b/drivers/i2c/chips/Kconfig 2005-01-27 09:56:48.000000000 +0800 @@ -251,6 +251,10 @@ This driver can also be built as a module. If so, the module will be called w83627hf. +config SENSORS_ADCM2650 + tristate "ADCM2650 chip" + depends on I2C && I2C_ADAP_PXA + select I2C_SENSOR endmenu menu "Other I2C Chip support" diff -uNr a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile --- a/drivers/i2c/chips/Makefile 2004-10-19 05:55:36.000000000 +0800 +++ b/drivers/i2c/chips/Makefile 2005-01-27 09:56:48.000000000 +0800 @@ -30,6 +30,7 @@ obj-$(CONFIG_SENSORS_VIA686A) += via686a.o obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o +obj-$(CONFIG_SENSORS_ADCM2650) += i2c-adcm2650.o ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) EXTRA_CFLAGS += -DDEBUG diff -uNr a/drivers/ide/legacy/ide-cs.c b/drivers/ide/legacy/ide-cs.c --- a/drivers/ide/legacy/ide-cs.c 2004-10-19 05:54:39.000000000 +0800 +++ b/drivers/ide/legacy/ide-cs.c 2005-01-27 09:56:48.000000000 +0800 @@ -454,6 +454,7 @@ link->state |= DEV_SUSPEND; /* Fall through... */ case CS_EVENT_RESET_PHYSICAL: + disable_irq(link->irq.AssignedIRQ); if (link->state & DEV_CONFIG) pcmcia_release_configuration(link->handle); break; @@ -461,6 +462,7 @@ link->state &= ~DEV_SUSPEND; /* Fall through... */ case CS_EVENT_CARD_RESET: + enable_irq(link->irq.AssignedIRQ); if (DEV_OK(link)) pcmcia_request_configuration(link->handle, &link->conf); break; diff -uNr a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig --- a/drivers/input/keyboard/Kconfig 2004-10-19 05:55:28.000000000 +0800 +++ b/drivers/input/keyboard/Kconfig 2005-01-27 09:56:48.000000000 +0800 @@ -67,6 +67,13 @@ To compile this driver as a module, choose M here: the module will be called xtkbd. +config KEYPAD_MAINSTONE + tristate "Mainstone Keypad" + depends on INPUT && INPUT_KEYBOARD && MACH_MAINSTONE + default y + help + Say Y to support the mainstone board keypad. + config KEYBOARD_NEWTON tristate "Newton keyboard" depends on INPUT && INPUT_KEYBOARD diff -uNr a/drivers/input/keyboard/mainstone_kp.c b/drivers/input/keyboard/mainstone_kp.c --- a/drivers/input/keyboard/mainstone_kp.c 1970-01-01 08:00:00.000000000 +0800 +++ b/drivers/input/keyboard/mainstone_kp.c 2005-01-27 09:56:48.000000000 +0800 @@ -0,0 +1,266 @@ +/* + * linux/drivers/input/keyboard/mainstone_kp.c + * + * Keypad driver for mainstone Platform + * + * Copyright (C) 2004, Intel Corporation (jingqing.xu@intel.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +MODULE_AUTHOR("Xu Jingqing "); +MODULE_DESCRIPTION("Mainstone III keypad driver"); +MODULE_LICENSE("GPL"); + +static unsigned char mainstone_keycode[0x78] = { + [0] = KEY_A, + [1] = KEY_G, + [2] = KEY_M, + [3] = KEY_S, + [4] = KEY_DOT, + [5] = KEY_HOME, + [0xA] = KEY_UP, + [0xB] = KEY_DOWN, + [0XC] = KEY_KPENTER, + [0x10] = KEY_B, + [0x11] = KEY_H, + [0x12] = KEY_N, + [0x13] = KEY_T, + [0x14] = KEY_EMAIL, + [0x15] = KEY_UP, + [0x20] = KEY_C, + [0x21] = KEY_I, + [0x22] = KEY_O, + [0x23] = KEY_U, + [0x24] = KEY_Y, + [0x25] = KEY_SPACE, + [0x30] = KEY_D, + [0x31] = KEY_J, + [0x32] = KEY_P, + [0x33] = KEY_V, + [0x34] = KEY_Z, + [0x35] = KEY_SPACE, + [0x40] = KEY_E, + [0x41] = KEY_K, + [0x42] = KEY_Q, + [0x43] = KEY_W, + [0x44] = KEY_SLASH, + [0x45] = KEY_KPENTER, + [0x50] = KEY_F, + [0x51] = KEY_L, + [0x52] = KEY_R, + [0x53] = KEY_X, + [0x54] = KEY_BACKSLASH, + [0x55] = KEY_ESC, +}; +static struct input_dev mainstone_kp_dev; +/* scan code */ +#define ROTARY_DEFAULT 0x7F +#define NO_KEY 0xFF +#define SCAN_CODE_SCROLL_UP 0xA +#define SCAN_CODE_SCROLL_DOWN 0xB +#define SCAN_CODE_ACTION 0xC + +static int kp_direct_scan(void) +{ + static int prev; + u32 curr; + u32 direct, rotary; + u8 c; + direct = KPDK; + rotary = KPREC; + /* Rotary */ + curr = rotary & 0xFF; + if (rotary & KPREC_OF0) { + KPREC &= ~KPREC_OF0; + KPREC |= ROTARY_DEFAULT; + prev = ROTARY_DEFAULT; + c = SCAN_CODE_SCROLL_UP; + } + else if (rotary & KPREC_UF0) { + KPREC &= ~KPREC_UF0; + KPREC |= ROTARY_DEFAULT; + prev = ROTARY_DEFAULT; + c = SCAN_CODE_SCROLL_DOWN; + } + else if (curr > prev) { + c = SCAN_CODE_SCROLL_UP; + prev = curr; + } + else if (curr < prev) { + c = SCAN_CODE_SCROLL_DOWN; + prev = curr; + } + + if (direct & KPDK_DK2) { + c = SCAN_CODE_ACTION; + prev = curr; + } + + return 0; +} + +static int kp_matrix_scan(void) +{ + u32 autoscan, n; + u8 c; + autoscan = KPAS; + n = (autoscan >> 26) & 0x1f; + c = autoscan & 0xff; + /* encode scan-code */ + if ( n == 1 ){ + input_report_key(&mainstone_kp_dev,mainstone_keycode[c], 1 ); + input_report_key(&mainstone_kp_dev,mainstone_keycode[c], 0 ); + return 0; + } + else + return 1; +} + +/* Interrupt Handler for KEYPAD */ +static irqreturn_t kp_interrupt(int irq, void *ptr, struct pt_regs *regs) +{ + unsigned long kpc_val; + + /* ack interrupt */ + kpc_val = KPC; + + /* direct interrupt */ + if (kpc_val & KPC_DI) + kp_direct_scan(); + + /* matrix interrupt */ + if (kpc_val & KPC_MI) + kp_matrix_scan(); + + return IRQ_HANDLED; + +} +static void kpad_configure() +{ + pxa_gpio_mode(93 | GPIO_ALT_FN_1_IN); /* DKIN0 */ + pxa_gpio_mode(94 | GPIO_ALT_FN_1_IN); /* DKIN1 */ + pxa_gpio_mode( 98 | GPIO_ALT_FN_3_IN); /* MKIN4 */ + pxa_gpio_mode( 99 | GPIO_ALT_FN_3_IN); /* MKIN5 */ + pxa_gpio_mode(100 | GPIO_ALT_FN_1_IN); /* MKIN0 */ + pxa_gpio_mode(101 | GPIO_ALT_FN_1_IN); /* MKIN1 */ + pxa_gpio_mode(102 | GPIO_ALT_FN_1_IN); /* MKIN2 */ + pxa_gpio_mode( 97 | GPIO_ALT_FN_3_IN); /* MKIN3 */ + pxa_gpio_mode(103 | GPIO_ALT_FN_2_OUT); /* MKOUT0 */ + pxa_gpio_mode(104 | GPIO_ALT_FN_2_OUT); /* MKOUT1 */ + pxa_gpio_mode(105 | GPIO_ALT_FN_2_OUT); /* MKOUT2 */ + pxa_gpio_mode(106 | GPIO_ALT_FN_2_OUT); /* MKOUT3 */ + pxa_gpio_mode(107 | GPIO_ALT_FN_2_OUT); /* MKOUT4 */ + pxa_gpio_mode(108 | GPIO_ALT_FN_2_OUT); /* MKOUT5 */ + + /* enable unit clock */ + CKEN |= CKEN19_KEYPAD; + +//FIXME:PXA27x E11 +#ifdef CONFIG_PXA27x_E11 + /* debouce setting: matrix debouce + direct debouce */ + KPKDI= 10 + (2 << 8); +#else + KPKDI= 2 << 8; +#endif + /* set scroll wheel value to mid-point value */ + KPREC= ROTARY_DEFAULT; + + /* keypad control register */ + KPC = (KPC_ASACT | (5<<26) | (5<<23)| + KPC_ME | (2<<6) | KPC_REE0 | KPC_DE | + KPC_MS0 | KPC_MS1 | KPC_MS2 | KPC_MS3| + KPC_MS4 | KPC_MS5 | KPC_MIE | KPC_DIE | KPC_IMKP); + + +} +static int mb_kp_resume( struct device * dev, u32 level ) +{ + switch(level){ + case RESUME_POWER_ON: + kpad_configure(); + break; + } + + return 0; + +} + +static int mb_kp_suspend(struct device * dev, u32 state, u32 level) +{ + switch(level){ + case SUSPEND_POWER_DOWN: + CKEN &= ~CKEN19_KEYPAD; + break; + } + + return 0; + + +} + +static struct device_driver mb_kp_drv = { + .bus = &platform_bus_type, + .name = "mb_kp", + .resume = mb_kp_resume, + .suspend = mb_kp_suspend, + }; +static struct platform_device * mb_kp_platform_device; +static int __init mainstone_kp_init( void ) +{ + int i,err; + + init_input_dev(&mainstone_kp_dev); + + mainstone_kp_dev.evbit[0] = BIT(EV_KEY); + mainstone_kp_dev.keycode = mainstone_keycode; + mainstone_kp_dev.keycodesize = sizeof(unsigned char); + mainstone_kp_dev.keycodemax = ARRAY_SIZE(mainstone_keycode); + + for (i = 0; i < 0x55; i++) + if (mainstone_keycode[i]) + set_bit(mainstone_keycode[i], mainstone_kp_dev.keybit); + + err = request_irq (IRQ_KEYPAD, kp_interrupt, 0, "Keypad", NULL); + + if (err) { + printk (KERN_CRIT "Wow! Can't register IRQ[%d] for Keypad\n", IRQ_KEYPAD); + return -1; + } + kpad_configure(); + mainstone_kp_dev.name = "mainstoneIII keypad"; + mainstone_kp_dev.phys = "mainstoneIII/input0"; + mainstone_kp_dev.id.vendor = 0x0001; + mainstone_kp_dev.id.product = 0x0001; + mainstone_kp_dev.id.version = 0x0001; + + input_register_device(&mainstone_kp_dev); + printk(KERN_INFO "input: mainstoneIII keypad!\n"); + mb_kp_platform_device = platform_device_register_simple("mb_kp",0,0,0); + printk(KERN_INFO "platform bus: mainstoneIII keypad device!\n"); + driver_register(&mb_kp_drv); + printk(KERN_INFO "input: mainstoneIII keypad driver!\n"); + return 0; + +} + +static void __exit mainstone_kp_exit( void ) +{ + platform_device_unregister(mb_kp_platform_device); + driver_unregister(&mb_kp_drv); + input_unregister_device(&mainstone_kp_dev); + free_irq(IRQ_KEYPAD, NULL); +} +module_init(mainstone_kp_init); +module_exit(mainstone_kp_exit); + + diff -uNr a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile --- a/drivers/input/keyboard/Makefile 2004-10-19 05:55:36.000000000 +0800 +++ b/drivers/input/keyboard/Makefile 2005-01-27 09:56:48.000000000 +0800 @@ -12,3 +12,4 @@ obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o obj-$(CONFIG_KEYBOARD_98KBD) += 98kbd.o +obj-$(CONFIG_KEYPAD_MAINSTONE) += mainstone_kp.o diff -uNr a/drivers/media/video/adcm2650.c b/drivers/media/video/adcm2650.c --- a/drivers/media/video/adcm2650.c 1970-01-01 08:00:00.000000000 +0800 +++ b/drivers/media/video/adcm2650.c 2005-01-27 09:56:48.000000000 +0800 @@ -0,0 +1,214 @@ +/* + adcm2650 - ADCM 2650 CMOS sensor driver + + Copyright (C) 2003, Intel Corporation + + 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. + + Code Status: + 2004/10/19: Yan Yin + - Ported to 2.6 kernel +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "camera.h" +#include "ci.h" +#include "adcm2650.h" +#include "adcm2650_hw.h" + +#define MCLK_DEFT 7 // 7 MHz + +/*********************************************************************** + * + * ADCM2650 Functions + * + ***********************************************************************/ +int camera_func_adcm2650_init( p_camera_context_t camera_context ) +{ + u8 sensor_rev; + u16 cm_rev, regv; + int status; + + // Configure CI according to ADCM2650's hardware + // master parallel with 8 data pins + ci_set_mode(CI_MODE_MP, CI_DATA_WIDTH8); + + // enable pixel clock(sensor will provide pclock) and master clock = 7MHZ + //ci_set_clock(camera_context->clk_reg_base, 1, 1, MCLK_DEFT); + // reset mclk to 5.6MHz to enable camera work in different frequency. + ci_set_clock(camera_context->clk_reg_base, 1, 1, 560); + + // data sample on rising and h,vsync active high + ci_set_polarity(0, 0, 0); + + // fifo control + ci_set_fifo(0, CI_FIFO_THL_32, 1, 1); // quality + + // ADCM2650 Power on sequence + // 1 Turn on M_VCC and wait for 20 ms. + // initilization on Board Level + // In MiscWrite Reg1(Phys Addr: 0x0800_0080): + // Bit 15: CAMERA_ON (1:On 0: Off) + // Bit 14: CAMERA_SEL (1:Routed to Camera Interface 0: To SSP1, USIM, PWM1) + MST_MSCWR1 |= (MST_MSCWR1_CAMERA_ON | MST_MSCWR1_CAMERA_SEL); + + mdelay(20); + + // init I2C. + status = i2c_init(); + if (status) + return status; + + // 2 Turn on M_CLK using xx MHz and wait for 150 ms. + ci_enable(1); + mdelay(150); + + // read out version + status = adcm2650_version_revision(&cm_rev, &sensor_rev); + if (cm_rev != PIPE_REV || sensor_rev != SENSOR_REV) + return STATUS_FAILURE; + + // power on the module + // note: pll is NOT enabled, ADCM is using external clock + status = adcm2650_power_on(MCLK_DEFT);// external mclk = 7M HZ + + // set default output format + status = adcm2650_switch_to_normal(VIEWFINDER_MODE); + status = adcm2650_switch_to_normal(STILLFRAME_MODE); + status = adcm2650_viewfinder_cfg_output(O_FORMAT_422_B_YCbYCr); + status = adcm2650_stillframe_cfg_output(O_FORMAT_422_B_YCbYCr); + + // clear video sub-sampling and set v-mirror and h-mirror + status = adcm2650_pipeline_read(VIDEO_CONFIG, ®v); + regv &= ~VIDEO_CONFIG_SS; + //regv |= VIDEO_CONFIG_V_MIRROR | VIDEO_CONFIG_H_MIRROR; + regv |= VIDEO_CONFIG_V_MIRROR; + adcm2650_pipeline_write(VIDEO_CONFIG, regv); + + // testing only + // adcm2650_pipeline_write(TCTRL_VID, 0x0u << 6 | 0x8); // slow down the pixel output to match our MCLK + // adcm2650_pipeline_write(T_DGEN_M, 0x1 | (0x4u<<3) | (0x1u<<6) ); // all white test image + // adcm2650_pipeline_write(T_DGEN_M, 0x7 | (0x4u<<3) | (0x1u<<6) ); // color bars + // adcm2650_pipeline_write(T_DGEN_M, 0x5 | (0x4u<<3) | (0x1u<<6) ); // checker board + // adcm2650_pipeline_write(T_DGEN_M, 0x6 | (0x4u<<3) | (0x1u<<6) ); // diagonal resolution chart + + + // gives 34 fps QCIF + adcm2650_pipeline_read(PLL_CTRL_1, ®v); + adcm2650_pipeline_write(PLL_CTRL_1, 0x4848); + adcm2650_pipeline_read(PLL_CTRL_2, ®v); + adcm2650_pipeline_write(PLL_CTRL_2, 0x9010); + adcm2650_pipeline_write(CMD_2, 0x0300); + + return 0; +} + +int camera_func_adcm2650_deinit( p_camera_context_t camera_context ) +{ + // power off the external module + adcm2650_power_off(); + + // disable CI + ci_disable(1); // quick disable + i2c_deinit(); + // cut down at Board Level + MST_MSCWR1 |= (MST_MSCWR1_CAMERA_ON | MST_MSCWR1_CAMERA_SEL); + return 0; +} + +int camera_func_adcm2650_set_capture_format( p_camera_context_t camera_context ) +{ + adcm_window_size wsize; + u16 adcm_format; + + // set sensor input/output window + wsize.width = camera_context->capture_width; + wsize.height = camera_context->capture_height; + adcm2650_viewfinder_input_size(&wsize); + adcm2650_viewfinder_output_size(&wsize); + adcm2650_stillframe_input_size(&wsize); + adcm2650_stillframe_output_size(&wsize); + + // set sensor format + switch(camera_context->capture_input_format) { + case CAMERA_IMAGE_FORMAT_YCBCR422_PLANAR: + case CAMERA_IMAGE_FORMAT_YCBCR422_PACKED: + adcm_format = O_FORMAT_422_B_YCbYCr; + break; + case CAMERA_IMAGE_FORMAT_RGB565: + adcm_format = O_FORMAT_565_RGB; + break; + case CAMERA_IMAGE_FORMAT_RGB888_PACKED: + case CAMERA_IMAGE_FORMAT_RGB888_PLANAR: + adcm_format = O_FORMAT_888RGB; + break; + default: + adcm_format = O_FORMAT_422_B_YCbYCr; + break; + } + adcm2650_viewfinder_cfg_output(adcm_format); + adcm2650_stillframe_cfg_output(adcm_format); + + return 0; +} + +int camera_func_adcm2650_start_capture( p_camera_context_t camera_context, unsigned int frames ) +{ + // frames=0 means continues capture + if (frames == 0) { + // set viewfinder to infinite output + adcm2650_resume_to_full_output_mode(); + adcm2650_viewfinder_on(); + } + else { + // halt viewfinder output + adcm2650_halt_video_output(); + // limit output frames + adcm2650_pipeline_write(UART_CREDITS, frames); + } + + // turn viewfinder on + adcm2650_viewfinder_on(); + + return 0; +} + +int camera_func_adcm2650_stop_capture( p_camera_context_t camera_context ) +{ + adcm2650_viewfinder_off(); + mdelay(200); + + return 0; +} diff -uNr a/drivers/media/video/adcm2650.h b/drivers/media/video/adcm2650.h --- a/drivers/media/video/adcm2650.h 1970-01-01 08:00:00.000000000 +0800 +++ b/drivers/media/video/adcm2650.h 2005-01-27 09:56:48.000000000 +0800 @@ -0,0 +1,39 @@ +/* + adcm2650 - ADCM 2650 CMOS sensor driver + + Copyright (C) 2003, Intel Corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _ADCM2650_H_ +#define _ADCM2650_H_ + +#include "camera.h" + +////////////////////////////////////////////////////////////////////////////////////// +// +// Prototypes +// +////////////////////////////////////////////////////////////////////////////////////// + +int camera_func_adcm2650_init( p_camera_context_t ); +int camera_func_adcm2650_deinit( p_camera_context_t ); +int camera_func_adcm2650_set_capture_format( p_camera_context_t ); +int camera_func_adcm2650_start_capture( p_camera_context_t, unsigned int frames ); +int camera_func_adcm2650_stop_capture( p_camera_context_t ); + +#endif + diff -uNr a/drivers/media/video/adcm2650_hw.c b/drivers/media/video/adcm2650_hw.c --- a/drivers/media/video/adcm2650_hw.c 1970-01-01 08:00:00.000000000 +0800 +++ b/drivers/media/video/adcm2650_hw.c 2005-01-27 09:56:48.000000000 +0800 @@ -0,0 +1,934 @@ +/* + adcm2650_hw - ADCM 2650 CMOS sensor driver + + Copyright (C) 2003, Intel Corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "adcm2650_hw.h" + +extern int i2c_adcm2650_init(void); +extern int i2c_adcm2650_cleanup(void); +extern int adcm2650_read(u16 addr, u16 *pvalue); +extern int adcm2650_write(u16 addr, u16 value); + +#define I2C_SLAVE_ID 0x4E /* 0100_111x */ /* The Phillips spec says it must be a value between 0001_000xB and 1110_111xB */ + +firm_update_t firm_update[] = { +{0x00fc, 0x0008}, // index 1 +{0x00fe, 0x000c}, // index 2 +{0x42cc, 0x0002}, // index 3 +{0x42ce, 0xfd13}, // index 4 +{0x4308, 0x0002}, // index 5 +{0x430a, 0xfd2d}, // index 6 +{0x430c, 0x0002}, // index 7 +{0x430e, 0xfd00}, // index 8 +{0x4400, 0x0000}, // index 9 +{0x4402, 0x060e}, // index 10 +{0x4404, 0x0003}, // index 11 +{0x4406, 0x03b9}, // index 12 +{0x4408, 0x000d}, // index 13 +{0x440a, 0x3e80}, // index 14 +{0x440c, 0x0000}, // index 15 +{0x440e, 0x02ac}, // index 16 +{0x4410, 0x0000}, // index 17 +{0x4412, 0x5c04}, // index 18 +{0x4414, 0x0000}, // index 19 +{0x4416, 0x02a7}, // index 20 +{0x4418, 0x0000}, // index 21 +{0x441a, 0x093e}, // index 22 +{0x441c, 0x0000}, // index 23 +{0x441e, 0x0436}, // index 24 +{0x4420, 0x0000}, // index 25 +{0x4422, 0x5445}, // index 26 +{0x4424, 0x0000}, // index 27 +{0x4426, 0x042d}, // index 28 +{0x4428, 0x0000}, // index 29 +{0x442a, 0x041d}, // index 30 +{0x442c, 0x0000}, // index 31 +{0x442e, 0x026e}, // index 32 +{0x4430, 0x0000}, // index 33 +{0x4432, 0x01a6}, // index 34 +{0x4434, 0x000d}, // index 35 +{0x4436, 0x3300}, // index 36 +{0x4438, 0x0000}, // index 37 +{0x443a, 0x0175}, // index 38 +{0x443c, 0x000e}, // index 39 +{0x443e, 0xc350}, // index 40 +{0x4440, 0x0007}, // index 41 +{0x4442, 0x0681}, // index 42 +{0x4444, 0x000e}, // index 43 +{0x4446, 0x61a8}, // index 44 +{0x4448, 0x0002}, // index 45 +{0x444a, 0x0681}, // index 46 +{0x444c, 0x0003}, // index 47 +{0x444e, 0x01ba}, // index 48 +{0x4450, 0x0003}, // index 49 +{0x4452, 0x03b9}, // index 50 +{0x4454, 0x0000}, // index 51 +{0x4456, 0x5c04}, // index 52 +{0x4458, 0x0000}, // index 53 +{0x445a, 0x02bc}, // index 54 +{0x445c, 0x000f}, // index 55 +{0x445e, 0x010b}, // index 56 +{0x4460, 0x0000}, // index 57 +{0x4462, 0x0267}, // index 58 +{0x4464, 0x0000}, // index 59 +{0x4466, 0x01a7}, // index 60 +{0x4468, 0x0000}, // index 61 +{0x446a, 0x043f}, // index 62 +{0x446c, 0x0000}, // index 63 +{0x446e, 0x01fb}, // index 64 +{0x4470, 0x0004}, // index 65 +{0x4472, 0xfd1e}, // index 66 +{0x4474, 0x0000}, // index 67 +{0x4476, 0x0197}, // index 68 +{0x4478, 0x0000}, // index 69 +{0x447a, 0x5845}, // index 70 +{0x447c, 0x000d}, // index 71 +{0x447e, 0x3000}, // index 72 +{0x4480, 0x0000}, // index 73 +{0x4482, 0x02ae}, // index 74 +{0x4484, 0x0000}, // index 75 +{0x4486, 0x0196}, // index 76 +{0x4488, 0x0000}, // index 77 +{0x448a, 0x0177}, // index 78 +{0x448c, 0x0007}, // index 79 +{0x448e, 0xfd25}, // index 80 +{0x4490, 0x0000}, // index 81 +{0x4492, 0x0937}, // index 82 +{0x4494, 0x0000}, // index 83 +{0x4496, 0x5b30}, // index 84 +{0x4498, 0x0000}, // index 85 +{0x449a, 0x0416}, // index 86 +{0x449c, 0x0000}, // index 87 +{0x449e, 0x0436}, // index 88 +{0x44a0, 0x0000}, // index 89 +{0x44a2, 0x041f}, // index 90 +{0x44a4, 0x0000}, // index 91 +{0x44a6, 0x0417}, // index 92 +{0x44a8, 0x0000}, // index 93 +{0x44aa, 0x037e}, // index 94 +{0x44ac, 0x0000}, // index 95 +{0x44ae, 0x7b30}, // index 96 +{0x44b0, 0x0000}, // index 97 +{0x44b2, 0x01ff}, // index 98 +{0x44b4, 0x0000}, // index 99 +{0x44b6, 0x5c44}, // index 100 +{0x44b8, 0x0000}, // index 101 +{0x44ba, 0x5845}, // index 102 +{0x44bc, 0x0000}, // index 103 +{0x44be, 0x0416}, // index 104 +{0x44c0, 0x0000}, // index 105 +{0x44c2, 0x0277}, // index 106 +{0x44c4, 0x0000}, // index 107 +{0x44c6, 0x01a7}, // index 108 +{0x44c8, 0x0000}, // index 109 +{0x44ca, 0x043f}, // index 110 +{0x44cc, 0x0000}, // index 111 +{0x44ce, 0x0647}, // index 112 +{0x44d0, 0x0004}, // index 113 +{0x44d2, 0xfd36}, // index 114 +{0x44d4, 0x0000}, // index 115 +{0x44d6, 0x0427}, // index 116 +{0x44d8, 0x0000}, // index 117 +{0x44da, 0x5863}, // index 118 +{0x44dc, 0x0000}, // index 119 +{0x44de, 0x017e}, // index 120 +{0x44e0, 0x0007}, // index 121 +{0x44e2, 0xfd41}, // index 122 +{0x44e4, 0x0000}, // index 123 +{0x44e6, 0x5864}, // index 124 +{0x44e8, 0x0000}, // index 125 +{0x44ea, 0x017e}, // index 126 +{0x44ec, 0x0007}, // index 127 +{0x44ee, 0xfd41}, // index 128 +{0x44f0, 0x0000}, // index 129 +{0x44f2, 0x5865}, // index 130 +{0x44f4, 0x0000}, // index 131 +{0x44f6, 0x017e}, // index 132 +{0x44f8, 0x0007}, // index 133 +{0x44fa, 0xfd41}, // index 134 +{0x44fc, 0x0000}, // index 135 +{0x44fe, 0x0607}, // index 136 +{0x4500, 0x0002}, // index 137 +{0x4502, 0x05e7}, // index 138 +{0x4504, 0x0000}, // index 139 +{0x4506, 0x0627}, // index 140 +{0x4508, 0x0000}, // index 141 +{0x450a, 0x5c29}, // index 142 +{0x450c, 0x000e}, // index 143 +{0x450e, 0x0100}, // index 144 +{0x4510, 0x0000}, // index 145 +{0x4512, 0x00f7}, // index 146 +{0x4514, 0x0000}, // index 147 +{0x4516, 0x0407}, // index 148 +{0x4518, 0x0000}, // index 149 +{0x451a, 0x0077}, // index 150 +{0x451c, 0x0002}, // index 151 +{0x451e, 0x05ed}, // index 152 +}; + + +void adcm2650_wait( int ms ) +{ + mdelay( ms ); +} + + +/***************************************************************************** +* * +* I2C Management * +* * +*****************************************************************************/ +int i2c_init(void) +{ + return i2c_adcm2650_init(); +} + +int i2c_deinit(void) +{ + return i2c_adcm2650_cleanup(); +} + +int adcm2650_pipeline_read( u16 reg_addr, u16 * reg_value) +{ + adcm2650_read (reg_addr, reg_value); + return ADCM_ERR_TIMEOUT; +} + +int adcm2650_pipeline_write( u16 reg_addr, u16 reg_value) +{ + return adcm2650_write(reg_addr, reg_value); +} + +int adcm2650_pipeline_read_rl( u16 reg_addr, u16 mask, u16 field ) +{ + u16 reg_value; + int retry = adcm2650__TIMEOUT; + + while( retry-- ) + { + adcm2650_pipeline_read( reg_addr, ®_value ); + if( (reg_value & mask) == field ) + { + return ADCM_ERR_NONE; + } + + adcm2650_wait( 1 ); + } + + return ADCM_ERR_TIMEOUT; + +} + +int adcm2650_pipeline_write_wa( u16 reg_addr, u16 mask ) +{ + u16 reg_value; + + adcm2650_pipeline_read( reg_addr, ®_value ); + reg_value &= mask; + adcm2650_pipeline_write( reg_addr, reg_value ); + + return ADCM_ERR_NONE; +} + + +int adcm2650_pipeline_write_wo( u16 reg_addr, u16 field ) +{ + u16 reg_value; + + adcm2650_pipeline_read( reg_addr, ®_value ); + reg_value |= field; + adcm2650_pipeline_write( reg_addr, reg_value ); + + return ADCM_ERR_NONE; +} + + +///////////////////////////////////////////////////////////////////////// + + +int adcm2650_sensor_read_rs( u8 reg_addr, u8 * reg_value ) +{ + u16 value; + int retry = adcm2650__TIMEOUT; + + // Loads SENSOR_ADDRESS (0x004a) with address 0x55 and register + // address for a read operation. + value = SENSOR_SLAVE_ADDR | (reg_addr << 8); + adcm2650_pipeline_write( SENSOR_ADDRESS, value ); + + // Loads SENSOR_CTRL (0x0050) with a read request + value = SENSOR_CTRL_RW | SENSOR_CTRL_GO; + adcm2650_pipeline_write( SENSOR_CTRL, value ); + + // Checks if the request is complete and if data is available to read + while( --retry ) + { + // Reads the SENSOR_CTRL (0x0050) address for status + value = 0xFF; + adcm2650_pipeline_read( SENSOR_CTRL, &value ); + + if (!(value & SENSOR_CTRL_GO)) + { + adcm2650_pipeline_read( SENSOR_DATA_1, &value ); + *reg_value = value & 0xFF; + return ADCM_ERR_NONE; + } + adcm2650_wait( 1 ); + } + return ADCM_ERR_TIMEOUT; +} + +int adcm2650_sensor_write_ws( u8 reg_addr, u8 reg_value ) +{ + u16 value; + int retry = adcm2650__TIMEOUT; + + // Loads SENSOR_ADDRESS (0x004a) with address 0x55 and register + // a write operation. + value = SENSOR_SLAVE_ADDR | (reg_addr << 8); + adcm2650_pipeline_write( SENSOR_ADDRESS, reg_value ); + + // Loads SENSOR_DATA0 (0x004c) + value = reg_value; + adcm2650_pipeline_write( SENSOR_DATA_1, reg_value ); + + // Loads SENSOR_CTRL (0x0050) with a write request + value = SENSOR_CTRL_GO; + adcm2650_pipeline_write( SENSOR_CTRL, value | 0x1); // write one byte + + while( --retry ) + { + value = 0xFF; + adcm2650_pipeline_read( SENSOR_CTRL, &value ); + + if (!(value & SENSOR_CTRL_GO)) + { + return ADCM_ERR_NONE; + } + adcm2650_wait( 1 ); + } + return ADCM_ERR_TIMEOUT; +} + + +/////////////////////////////////////////////////////////////// +// +// Programming Guide Chapter 1: Basic Programming +// +/////////////////////////////////////////////////////////////// + +int adcm2650_power_on( u8 clk ) +{ + // 1 Turn on M_VCC and wait for 20 ms. + // 2 Turn on M_CLK using xx MHz and wait for 150 ms. + // 3 Complete auto configuration + adcm2650_auto_config_complete(); + + // 4 Load the firmware upgrade + //adcm2650_FirmwareUpgrade(); + + // 5 Program the master clock + adcm2650_master_clock( clk ); + + // 6 Select the sensor voltage mode + //adcm2650_SensorVoltage( VOLTS_28 ); + + // 7 Change factory overwrite + adcm2650_factory_overwrite(); + + /* The following are optional steps that may be required to meet + specifications. + // 8 Configure gamma correction + // 9 Configure color correction + // 10 Configure flare correction + // 11 Configure for JPEG quality for still frame mode + */ + + return ADCM_ERR_NONE; +} + +int adcm2650_power_off(void) +{ + // 1 Turn off M_CLK and wait for 30 ms. + // 2 Turn off M_VCC and wait for 30 ms. + + return ADCM_ERR_NONE; +} + +int adcm2650_change_viewfinder_mode(adcm_window_size * input_win, adcm_window_size *vf_output_win, adcm_window_size *sf_output_win ) +{ + // 1 Turn viewfinder off + adcm2650_viewfinder_off(); + + // 2 Set the input size for viewfinder and still frame modes + adcm2650_viewfinder_input_size( input_win ); + adcm2650_stillframe_input_size( input_win ); + + // 3 Set the output size for viewfinder mode + adcm2650_viewfinder_output_size( vf_output_win ); + + // 4 Set the output size for still frame mode + adcm2650_stillframe_output_size( sf_output_win ); + + // 5 Turn the viewfinder back on + adcm2650_viewfinder_on(); + + return ADCM_ERR_NONE; +} + + +int adcm2650_jpeg_slow_still_frame(void) +{ + return 0; +} + +int adcm2650_jpeg_fast_still_frame(void) +{ + return 0; +} + +int adcm2650_pll(void) +{ + return 0; +} + + + +///////////////////////////////////////////////////////////////////////////////////// +// +// Programming Guide Chapter 2: Configuration Methods +// +///////////////////////////////////////////////////////////////////////////////////// + +int adcm2650_auto_config_complete(void) +{ + int ret; + ret = RL( CMD_2, CMD_2_ACS|CMD_2_AVT|CMD_2_AST, 0x00 ); + return ret; +} + +int adcm2650_firmware_upgrade(void) +{ + int i; + firm_update_t *pfu; + + pfu = firm_update; + for( i = 0; i < 152; i++ ) + { + W(pfu->addr, pfu->value); + pfu++; + } + + return ADCM_ERR_NONE; +} + + +int adcm2650_version_revision(u16 * cm_revision, u8 *sensor_revision) +{ + // Camera module version is 0x0600 + R( REV, cm_revision ); + + // Image sensor version is 0x60 + RS( IDENT, sensor_revision ); + + return ADCM_ERR_NONE; +} + +int adcm2650_viewfinder_on(void) +{ + // Enables the viewfinder bits in VE + WO( CMD_1, CMD_1_VE ); + + // Once the viewfinder is turned on, wait until the camera + // module is ready to produce quality images. + RL( AF_STATUS, AF_STATUS_CC, AF_STATUS_CC ); + + return ADCM_ERR_NONE; +} + + +int adcm2650_viewfinder_off(void) +{ + // Turns the viewfinder off + WA( CMD_1, ~CMD_1_VE ); + + // Resets the converged bit + WA( AF_STATUS, ~AF_STATUS_CC ); + + return ADCM_ERR_NONE; +} + + +int adcm2650_power_low(void) +{ + // Enables low power mode bit LPE + WO( CMD_1, CMD_1_LPE ); + + return ADCM_ERR_NONE; +} + +int adcm2650_power_normal(void) +{ + // Returns to normal power mode + WA( CMD_1, ~CMD_1_LPE ); + + return ADCM_ERR_NONE; +} + + +int adcm2650_suspend(void) +{ + // 1 Stops viewfinder mode + W( CMD_1, 0x0000 ); + + // 2 turn off M_CLK Turns off the clock + + // 3 for 150 ms + WAIT( 150 ); + + return ADCM_ERR_NONE; +} + + +int adcm2650_wakeup(void) +{ + // 1 Turns on the clock M_CLK + + // 2 Waits for 150 ms + WAIT( 150 ); + + // 3 Turns on viewfinder mode + W( CMD_1, 0x0001 ); + + return ADCM_ERR_NONE; +} + + +int adcm2650_abort_image(void) +{ + // 1 Aborts the progressive viewfinder image bit IA + WO( CMD_2, CMD_2_IA ); + + // 2 Waits until the IA bit is auto cleared + RL( CMD_2, CMD_2_IA, 0x0000 ); + + return ADCM_ERR_NONE; +} + + +int adcm2650_snapshot_trigger(void) +{ + // Requests a capture snapshot + WO( CMD_2, CMD_2_SNAP ); + return ADCM_ERR_NONE; +} + +int adcm2650_snapshot_complete(void) +{ + // Waits until the snapshot is complete + RL( CMD_2, CMD_2_SNAP, 0x0000 ); + return ADCM_ERR_NONE; +} + +int adcm2650_snapshot_status(u16 *status) +{ + R( CMD_2, status ); + return ADCM_ERR_NONE; +} + +int adcm2650_image_flip_v(u16 mode) +{ + if( mode == VIEWFINDER_MODE ) + { + WO( VIDEO_CONFIG, VIDEO_CONFIG_V_MIRROR ); + } + else if( mode == STILLFRAME_MODE ) + { + WO( STILL_CONFIG, STILL_CONFIG_V_MIRROR ); + } + else + { + return ADCM_ERR_PARAMETER; + } + + return ADCM_ERR_NONE; +} + + +int adcm2650_image_flip_h(u16 mode) +{ + if( mode == VIEWFINDER_MODE ) + { + WO( VIDEO_CONFIG, VIDEO_CONFIG_H_MIRROR ); + } + else if( mode == STILLFRAME_MODE ) + { + WO( STILL_CONFIG, STILL_CONFIG_H_MIRROR ); + } + else + { + return ADCM_ERR_PARAMETER; + } + + return ADCM_ERR_NONE; +} + + +int adcm2650_image_flip_vh(u16 mode) +{ + if( mode == VIEWFINDER_MODE ) + { + WO( VIDEO_CONFIG, VIDEO_CONFIG_H_MIRROR|VIDEO_CONFIG_V_MIRROR ); + } + else if( mode == STILLFRAME_MODE ) + { + WO( STILL_CONFIG, STILL_CONFIG_H_MIRROR|STILL_CONFIG_V_MIRROR ); + } + else + { + return ADCM_ERR_PARAMETER; + } + + return ADCM_ERR_NONE; +} + + +int adcm2650_pll_active(void) +{ + // Starts PLL request + WO( CMD_2, CMD_2_PLL_ON | CMD_2_UCGS ); + + // Checks PLL request is complete + RL( CMD_2, CMD_2_PLL_ON | CMD_2_UCGS, 0x0 ); + + return ADCM_ERR_NONE; +} + + +int adcm2650_pll_deactive(void) +{ + // Disables PLL + WO( CMD_2, CMD_2_PLL_OFF ); + + // Checks PLL disable request is complete + RL( CMD_2, CMD_2_PLL_OFF, 0x0 ); + + return ADCM_ERR_NONE; +} + + +int adcm2650_pll_configuration(void) +{ + return ADCM_ERR_NONE; +} + +int adcm2650_pll_synchronized(void) +{ + // Waits until normal operation + RL( S_PLL_CTRL_0, S_PLL_CTRL_0_S_PLL_SE, 0x00 ); + + return ADCM_ERR_NONE; +} + +int adcm2650_factory_overwrite(void) +{ + // Disable the packet code + WO( BYPASS_CTRL, BYPASS_CTRL_PCD ); + + // Free running VCLK + W( OUTPUT_CTRL, 0x0 ); + + return ADCM_ERR_NONE; +} + +int adcm2650_master_clock( u8 clk ) +{ + W( MASTER_CLK_FREQ, ((u16)clk) << 8 ); + return ADCM_ERR_NONE; +} + + +int adcm2650_sensor_voltage( u8 vltg ) +{ + // Changes the sensor voltage to 2.8 //???? + WS( 0x001c, 0xa0 ); + return ADCM_ERR_NONE; +} + +int adcm2650_viewfinder_input_size( adcm_window_size * window ) +{ + // Input size width - viewfinder mode + W( SZR_IN_W_VID, window->width ); + + // Input size height - viewfinder mode + W( SZR_IN_H_VID, window->height ); + + return ADCM_ERR_NONE; +} + +int adcm2650_stillframe_input_size( adcm_window_size * window ) +{ + // Input size width - still frame mode + W( SZR_IN_W_STL, window->width ); + + // Input size height - still frame mode + W( SZR_IN_H_STL, window->height ); + + return ADCM_ERR_NONE; +} + + +int adcm2650_viewfinder_output_size(adcm_window_size * window) +{ + // Output size width + W( SZR_OUT_W_VID, window->width ); + + // Output size height + W( SZR_OUT_H_VID, window->height); + + return ADCM_ERR_NONE; +} + +int adcm2650_stillframe_output_size(adcm_window_size * window) +{ + // Output size width + W( SZR_OUT_W_STL, window->width ); + + // Output size height + W( SZR_OUT_H_STL, window->height); + + return ADCM_ERR_NONE; +} + +int adcm2650_flicker_rate(u16 rate) +{ + if( rate == AEF_50HZ ) + { + // Clears the AE_F bit and sets it to 50 Hz + WA( 0x2022, 0xfffd ); + } + else if( rate == AEF_60HZ ) + { + // Sets the AE_F bit to 60 Hz + WO( 0x2022, 0x0002 ); + } + else + { + return ADCM_ERR_PARAMETER; + } + + return ADCM_ERR_NONE; +} + + +int adcm2650_low_light(void) +{ + // Sets the bit to 60 Hz; sample duration is 25 + WS( 0x0e, 0x19); + return ADCM_ERR_NONE; +} + +int adcm2650_normal_light(void) +{ + // Sets the bit back to normal mode; sample duration is 4 + WS( 0x0e, 0x04 ); + return ADCM_ERR_NONE; +} + +int adcm2650_discard_viewfinder_image_data(void) +{ + // Discards the current image viewfinder data + W( 0x0004, 0x1000 ); + + // Waits until the image abort is complete + RL(0x0004, 0x1000, 0x0000 ); + + return ADCM_ERR_NONE; +} + +int adcm2650_stillframe_cfg_output(u16 format) +{ + //Clear bits 8..11 + WA( STILL_CONFIG, ~STILL_CONFIG_O_FORMAT_MASK ); + + // Where format is bits 8..11 + WO( STILL_CONFIG, format << STILL_CONFIG_O_FORMAT_SHIFT ); + return ADCM_ERR_NONE; +} + +int adcm2650_viewfinder_cfg_output(u16 format) +{ + //Clear bits 8..11 + WA( VIDEO_CONFIG, ~VIDEO_CONFIG_O_FORMAT_MASK ); + + // Where format is bits 8..11 + WO( VIDEO_CONFIG, format << VIDEO_CONFIG_O_FORMAT_SHIFT ); + return ADCM_ERR_NONE; +} + + +int adcm2650_switch_to_jpeg(u16 mode) +{ + if( mode == STILLFRAME_MODE ) + { + // Switches to JPEG output mode + WO( STILL_CONFIG, STILL_CONFIG_JPEG_MASK ); + } + else if( mode == VIEWFINDER_MODE ) + { + //Changes still frame output mode to JPEG + WO( VIDEO_CONFIG, VIDEO_CONFIG_JPEG_MASK ); + } + else + { + return ADCM_ERR_PARAMETER; + } + + return ADCM_ERR_NONE; +} + +int adcm2650_switch_to_normal(u16 mode) +{ + if( mode == STILLFRAME_MODE ) + { + // Switches to Normal output mode + WA( STILL_CONFIG, ~STILL_CONFIG_JPEG_MASK ); + } + else if( mode == VIEWFINDER_MODE ) + { + //Changes still frame output mode to NORMAL + WA( VIDEO_CONFIG, ~VIDEO_CONFIG_JPEG_MASK ); + } + else + { + return ADCM_ERR_PARAMETER; + } + + return ADCM_ERR_NONE; +} + +int adcm2650_gamma_correction(void) +{ + return ADCM_ERR_NONE; +} + +int adcm2650_get_output_frame_rate(u16 * pfps) +{ + u16 fps; + + // Resets the frame counter + W( OUT_FRAME_CNT, 0x0000 ); + + // Waits for one second + WAIT( 1000 ); + + // Number of frames outputting the lower 8 bits + R( OUT_FRAME_CNT, &fps ); + + *pfps = fps & 0xFF; + + return ADCM_ERR_NONE; +} + +int adcm2650_detect_camera_mode(u16 *mode) +{ + u16 status; + // Note!! Document is wrong. 0x0076 shall be 0x0074 instead. + // Reads the Status_flag + R( STATUS_FLAGS, &status ); + + if( (status & 0x08) == 0x08 ) + { + *mode = STILLFRAME_MODE; + } + else + { + *mode = VIEWFINDER_MODE; + } + + return ADCM_ERR_NONE; +} + +int adcm2650_wait_till_in_stillframe_mode(void) +{ + // Note!! Document is wrong. 0x0076 shall be 0x0074 instead. + RL( STATUS_FLAGS, 0x0008, 0x0008 ); + return ADCM_ERR_NONE; +} + +int adcm2650_camera_ready(void) +{ + RL(AF_STATUS, 0x0008, 0x0008 ); + return ADCM_ERR_NONE; +} + +int adcm2650_halt_video_output(void) +{ + // Halts the viewfinder output + W( UART_CREDITS, 0x0000 ); + return ADCM_ERR_NONE; +} + +int adcm2650_get_single_image(void) +{ + //Outputs a single frame + W( UART_CREDITS, 0x001 ); + return ADCM_ERR_NONE; +} + +int adcm2650_resume_to_full_output_mode(void) +{ + // Output still frames continuously + W( UART_CREDITS, 0x0100 ); + return ADCM_ERR_NONE; +} + + +int adcm2650_manual_q_table(void) +{ + return ADCM_ERR_NONE; +} + +void adcm2650_dump_pipeline_register(u16 startRegAddr, u16 endRegAddr, u16* buffer) +{ + u16 addr; + for(addr=startRegAddr; addr<=endRegAddr; addr+=2) + adcm2650_pipeline_read( addr, buffer++ ); +} + +void adcm2650_dump_sensor_register(u16 startRegAddr, u16 endRegAddr, u8* buffer) +{ + u16 addr; + for(addr=startRegAddr; addr<=endRegAddr; addr++) + adcm2650_sensor_read_rs((u8) addr, buffer++ ); +} diff -uNr a/drivers/media/video/adcm2650_hw.h b/drivers/media/video/adcm2650_hw.h --- a/drivers/media/video/adcm2650_hw.h 1970-01-01 08:00:00.000000000 +0800 +++ b/drivers/media/video/adcm2650_hw.h 2005-01-27 09:56:48.000000000 +0800 @@ -0,0 +1,470 @@ +/* + adcm2650_hw - ADCM 2650 CMOS sensor driver + + Copyright (C) 2003, Intel Corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _ADCM_2650_HW_H_ +#define _ADCM_2650_HW_H_ + + +/*********************************************************************** + * + * Constants & Structures + * + ***********************************************************************/ +typedef struct { + u16 addr; + u16 value; +} firm_update_t; + +typedef struct { + u16 width; + u16 height; +} adcm_window_size; +//ADCM_WINDOWSIZE; + +// I2C address +#define PIPE_SLAVE_ADDR 0x0052 +#define SENSOR_SLAVE_ADDR 0x0055 + +// Revision constants +#define PIPE_REV 0x0600 +#define SENSOR_REV 0x60 + +// Calculating the Module Block Number +#define BLOCK(a) (u8)((a) >> 7) // register's module block address. +#define OFFSET(a) (u8)((a) & 0x7F ) // register's offset to this block. + +// Return codes +#define ADCM_ERR_NONE 0x00 +#define ADCM_ERR_TIMEOUT -1 +#define ADCM_ERR_PARAMETER -2 + +// Auto Exposure Frequency +#define AEF_50HZ 0x20 +#define AEF_60HZ 0x40 + +// Non JEPG Output Format +#define O_FORMAT_888RGB 0 //0b0000 // 888 RGB (1 pixel in 3 bytes ) +#define O_FORMAT_666_A_RGB 1 //0b0001 // 666 A RGB (tight pack, 4 pixels in 9 bytes) +#define O_FORMAT_666_B_RGB 2 //0b0010 // 666 B RGB (loose pack, 1 pixel in 3 bytes,left or right justified) +#define O_FORMAT_565_RGB 3 //0b0011 // 565 RGB (1 pixel in 2 bytes) +#define O_FORMAT_444_A_RGB 4 //0b0100 // 444 A RGB (tight pack, 2 pixels per 3 bytes, RG BR GB) +#define O_FORMAT_444_B_RGB 5 //0b0101 // 444 B RGB (loose pack, 1 pixel per 2 bytes,RG B0 or 0R GB) +#define O_FORMAT_444_C_RGV 6 //0b0110 // 444 C RGB (sparse pack, 1 pixel per three bytes,R0 G0 B0 or 0R 0G 0B) +#define O_FORMAT_332_RGB 7 //0b0111 // 332 RGB (1 pixel in 1 byte) +#define O_FORMAT_422_A_YCbYCr 8 //0b1000 // 4:2:2 A YCbYCr (Y1 Cb12 Y2 CRL2 order) +#define O_FORMAT_422_B_YCbYCr 9 //0b1001 // 4:2:2 B YCbYCr (Cb12 Y1 CRL2 Y2 order) +#define O_FORMAT_422_C_YCbYCr 10 //0b1010 // 4:2:2 C YCbYCr (Y1 CRL2 Y2 Cb12 order) +#define O_FORMAT_422_D_YCbYCr 11 //0b1011 // 4:2:2 D YCbYCr (CRL2 Y1 Cb12 Y2 order) +#define O_FORMAT_444_YCbYCr 12 //0b1100 // 4:4:4 YCbCr (1 pixels per 3 bytes) +#define O_FORMAT_400_B_YCbYCr 13 //0b1101 // 4:0:0 YCbCr (Greyscale, 1 pixel per 1 byte) +#define O_FORMAT_RAWBPA 14 //0b1110 // RAWBPA (with AWB and BPA) +#define O_FORMAT_RAW 15 //0b1111 // RAW (without AWB and BPA) + +// Camera Mode +#define VIEWFINDER_MODE 0x10 +#define STILLFRAME_MODE 0x20 + +// Others +#define adcm2650__TIMEOUT 1000 // ms to timeout. +#define BLOCK_SWITCH_CMD ((u8)0xFE) // Block Switch Code: 0x7F, CMD = Code << 1 +#define CLOCK_13M 13 +#define VOLTS_28 0x28 + + +/*********************************************************************** + * + * Pipeline Register Offset + * + ***********************************************************************/ +// Image Processor Register List +// Page 414 +#define REV 0x0000 +#define CMD_1 0x0002 +#define CMD_2 0x0004 +#define VIDEO_CONFIG 0x000c +#define STILL_CONFIG 0x000e +#define TM_SELECT 0x0010 +#define BYPASS_CTRL 0x0012 +#define PROCESS_CTRL 0x0014 +#define OUTPUT_CTRL 0x0016 +#define CCIR_TIMING_1 0x0018 +#define Y_MAX_MIN 0x001a +#define CbCr_MAX_MIN 0x001c +#define BAD_FRAME_DIS 0x001e +#define UART_PCKT_SIZE 0x0020 +#define UART_CRDT_ADD 0x0022 +#define UART_CREDITS 0x0024 +#define SZR_IN_W_VID 0x0026 +#define SZR_IN_H_VID 0x0028 +#define SZR_OUT_W_VID 0x002a +#define SZR_OUT_H_VID 0x002c +#define SZR_IN_W_STL 0x002e +#define SZR_IN_H_STL 0x0030 +#define SZR_OUT_W_STL 0x0032 +#define SZR_OUT_H_STL 0x0034 +#define QTABLE_SELECT 0x0036 + +// Page 415 +#define QTABLE_MAX_MIN 0x0038 +#define UFL_LIMIT_VID 0x003a +#define UFL_TARGET_VID 0x003c +#define OFL_TARGET_VID 0x003e +#define OFL_LIMIT_VID 0x0040 +#define UFL_LIMIT_STL 0x0042 +#define UFL_TARGET_STL 0x0044 +#define OFL_TARGET_STL 0x0046 +#define OFL_LIMIT_STL 0x0048 +#define SENSOR_ADDRESS 0x004a +#define SENSOR_DATA_1 0x004c +#define SENSOR_DATA_2 0x004e +#define SENSOR_CTRL 0x0050 +#define PLL_CTRL_0 0x0052 +#define PLL_CTRL_1 0x0054 +#define PLL_CTRL_2 0x0056 +#define DIVBY_UART 0x0058 +#define EXT_DIVBY_VID 0x005a +#define EXT_DIVBY_STL 0x005c +#define STAT_CAP_CTRL 0x005e +#define STAT_MODE_CTRL 0x0060 +#define GREEN1_SUM 0x0062 +#define READ_SUM 0x0064 +#define BLUE_SUM 0x0066 +#define GREEN2_SUM 0x0068 +#define NEG_CLIP_CNT 0x006a +#define POS_CLIP_CNT 0x006c +#define PEAK_DATA 0x006e +#define I_WIDTH 0x0070 +#define I_HEIGHT 0x0072 + +// Page 416 +#define STATUS_FLAGS 0x0074 +#define STATUS_REG 0x0076 +#define PLL_DIVBY_VID 0x0078 +#define PLL_DIVBY_STL 0x007a +#define OUTPUT_CTRL_2 0x007c +#define CC_COEF_00 0x0080 +#define CC_COEF_01 0x0082 +#define CC_COEF_02 0x0084 +#define CC_COEF_10 0x0086 +#define CC_COEF_11 0x0088 +#define CC_COEF_12 0x008a +#define CC_COEF_20 0x008c +#define CC_COEF_21 0x008e +#define CC_COEF_22 0x0090 +#define CC_OS_0 0x0092 +#define CC_OS_1 0x0094 +#define CC_OS_2 0x0096 +#define CSC_COEF_00V 0x0098 +#define CSC_COEF_01V 0x009a +#define CSC_COEF_02V 0x009c +#define CSC_COEF_10V 0x009e +#define CSC_COEF_11V 0x00a0 +#define CSC_COEF_12V 0x00a2 +#define CSC_COEF_20V 0x00a4 +#define CSC_COEF_21V 0x00a6 +#define CSC_COEF_22V 0x00a8 +#define CSC_OS_0V 0x00aa +#define CSC_OS_1V 0x00ac +#define CSC_OS_2V 0x00ae + +// Page 417 +#define CSC_COEF_00S 0x00b0 +#define CSC_COEF_01S 0x00b2 +#define CSC_COEF_02S 0x00b4 +#define CSC_COEF_10S 0x00b6 +#define CSC_COEF_11S 0x00b8 +#define CSC_COEF_12S 0x00ba +#define CSC_COEF_20S 0x00bc +#define CSC_COEF_21S 0x00be +#define CSC_COEF_22S 0x00c0 +#define CSC_OS_0S 0x00c2 +#define CSC_OS_1S 0x00c4 +#define CSC_OS_2S 0x00c6 +#define APS_COEF_GRN1 0x0100 +#define APS_COEF_RED 0x0102 +#define APS_COEF_BLUE 0x0104 +#define APS_COEF_GRN2 0x0106 +#define T_DGEN_M 0x0108 +#define CCIR_TIMING_2 0x010a +#define CCIR_TIMING_3 0x010c +#define SERIAL_CTRL 0x010e +#define DM_COEF_GRN1 0x0110 +#define DM_COEF_RED 0x0112 +#define DM_COEF_BLUE 0x0114 +#define DM_COEF_GRN2 0x0116 +#define SSC_TIMING 0x0118 +#define SSC_PERIOD 0x011a +#define JPEG_RESTART 0x011c + +// Page 418 +#define HSYNC_PER_VID 0x011e +#define HSYNC_PER_STL 0x0120 +#define G1_G2_THRESH 0x0124 +#define S_PLL_CTRL_0 0x0126 +#define S_PLL_CTRL_1 0x0128 +#define S_PLL_CTRL_2 0x012a +#define S_DIVBY_UART 0x012c +#define S_EXT_DIVBY_VID 0x012e +#define S_EXT_DIVBY_STL 0x0130 +#define BAD_FRAME_CNT_1 0x0132 +#define BAD_FRAME_CNT_2 0x0134 +#define BAD_FRAME_CNT_3 0x0136 +#define BAD_FRAME_CNT_4 0x0138 +#define OUT_FRAME_CNT 0x013a +#define DROP_FRAME_CNT 0x013c +#define BPA_BADPIX_CNT 0x013e +#define APS_FRAME_CNT 0x0140 +#define BPA_OUTL_PED 0x0142 +#define BPA_SCALE 0x0144 +#define S_PLL_DIVBY_VID 0x0146 +#define S_PLL_DIVBY_STL 0x0148 + +#define TCTRL_VID 0x2000 +#define HBLANK_VID 0x2002 +#define VBLANK_VID 0x2004 +#define MIN_MAX_F_VID 0x2006 +#define RPT_VID 0x2008 + +// Page 419 +#define TCTRL_STL 0x2010 +#define HBLANK_STL 0x2012 +#define VBLANK_STL 0x2014 +#define MIN_MAX_F_STL 0x2016 +#define RPT_STL 0x2018 +#define AF_CTRL_1 0x2020 +#define AF_CTRL_2 0x2022 +#define AF_STATUS 0x2024 +#define MASTER_CLK_FREQ 0x2026 +#define AE_GAIN_MIN 0x2028 +#define AE_GAIN_MIN_P 0x202a +#define AE_GAIN_MAX 0x202c +#define AE_GAIN_DFLT 0x202e +#define AE2_ETIME_MIN 0x2030 +#define AE2_ETIME_MAX 0x2032 +#define AE2_ETIME_DFLT 0x2034 +#define AE_TARGET 0x2036 +#define AE_TOL_ACQ 0x2038 +#define AE_TOL_MON 0x203a +#define AE_MARGIN 0x203c +#define AE_DOE_FACTOR 0x203e +#define AE_DOE_MARGIN 0x2040 +#define AWB_RED_MIN 0x2044 +#define AWB_RED_MAX 0x2046 + +// Page 420 +#define AWB_RED_DFLT 0x2048 +#define AWB_BLUE_MIN 0x204a +#define AWB_BLUE_MAX 0x204c +#define AWB_BLUE_DFLT 0x204e +#define AWB_TOL_ACQ 0x2050 +#define AWB_TOL_MON 0x2052 +#define FLICK_1 0x2054 +#define FLICK_2 0x2056 +#define ERROR_FLAGS 0x205e +#define NACC_TABLE 0x2060 + + +/*********************************************************************** + * + * Sensor Register Offset + * + ***********************************************************************/ +#define IDENT 0x00 +#define STATUS 0x01 +#define ICTRL 0x05 +#define ITMG 0x06 +#define FWROW 0x0a +#define FWCOL 0x0b +#define LWROW 0x0c +#define LWCOL 0x0d +#define TCTRL 0x0e +#define ERECPGA 0x0f +#define EROCPGA 0x10 +#define ORECPGA 0x11 +#define OROCPGA 0x12 +#define ROWEXPL 0x13 +#define ROWEXPH 0x14 +#define SROWEXP 0x15 +#define ERROR 0x16 +#define HBLANK 0x19 +#define VBLANK 0x1a +#define CONFIG 0x1b +#define CONTROL 0x1c +#define TEST0 0x1d +#define TEST1 0x1e +#define TEST2 0x1f +#define TEST3 0x20 +#define TEST4 0x24 +#define TEST5 0x25 +#define CONFIG_2 0x27 + + +/*********************************************************************** + * + * Bit/Field definitions + * + ***********************************************************************/ +#define CMD_1_VE 0x0001 +#define CMD_1_LPE 0x0002 + +#define CMD_2_SNAP 0x0001 +#define CMD_2_ACS 0x0002 +#define CMD_2_AVT 0x0004 +#define CMD_2_AST 0x0008 +#define CMD_2_SGO 0x0010 +#define CMD_2_P_RESET 0x0020 +#define CMD_2_S_RESET 0x0040 +#define CMD_2_U_RESET 0x0080 +#define CMD_2_PLL_ON 0x0100 +#define CMD_2_UCGS 0x0200 +#define CMD_2_PLL_OFF 0x0400 +#define CMD_2_IA 0x1000 +#define CMD_2_UART_F 0x2000 +#define CMD_2_SVWC 0x4000 +#define CMD_2_SVHC 0x8000 + +#define BYPASS_CTRL_BPA 0x0001 +#define BYPASS_CTRL_G1G2 0x0002 +#define BYPASS_CTRL_SB 0x0004 +#define BYPASS_CTRL_AQA 0x0008 +#define BYPASS_CTRL_PCD 0x0010 +#define BYPASS_CTRL_QT_HEAD 0x0020 +#define BYPASS_CTRL_WGF 0x0040 +#define BYPASS_CTRL_OLP 0x0080 +#define BYPASS_CTRL_CAM_CON 0x0100 +#define BYPASS_CTRL_DDFF 0x0200 + +#define VIDEO_CONFIG_SS 0x0004 +#define VIDEO_CONFIG_SHP 0x0008 +#define VIDEO_CONFIG_H_MIRROR 0x0010 +#define VIDEO_CONFIG_V_MIRROR 0x0020 +#define VIDEO_CONFIG_O_FORMAT_SHIFT 8 +#define VIDEO_CONFIG_O_FORMAT_MASK (0xF< + +#define VID_HARDWARE_PXA_CAMERA 50 /* subject to change */ + +#define STATUS_FAILURE (0) +#define STATUS_SUCCESS (1) +#define STATUS_WRONG_PARAMETER -1 + + + +/* +Macros +*/ +/* +Sensor type +*/ +#define CAMERA_TYPE_ADCM_2650 01 +#define CAMERA_TYPE_ADCM_2670 02 +#define CAMERA_TYPE_ADCM_2700 03 +#define CAMERA_TYPE_OMNIVISION_9640 04 +#define CAMERA_TYPE_MAX CAMERA_TYPE_OMNIVISION_9640 + +/* +Image format definition +*/ +#define CAMERA_IMAGE_FORMAT_MAX CAMERA_IMAGE_FORMAT_YCBCR444_PLANAR + +// Interrupt mask +#define CAMERA_INTMASK_FIFO_OVERRUN 0x0001 +#define CAMERA_INTMASK_END_OF_FRAME 0x0002 +#define CAMERA_INTMASK_START_OF_FRAME 0x0004 +#define CAMERA_INTMASK_CI_DISABLE_DONE 0x0008 +#define CAMERA_INTMASK_CI_QUICK_DISABLE 0x0010 +#define CAMERA_INTMASK_PARITY_ERROR 0x0020 +#define CAMERA_INTMASK_END_OF_LINE 0x0040 +#define CAMERA_INTMASK_FIFO_EMPTY 0x0080 +#define CAMERA_INTMASK_RCV_DATA_AVALIBLE 0x0100 +#define CAMERA_INTMASK_TIME_OUT 0x0200 +#define CAMERA_INTMASK_END_OF_DMA 0x0400 + +// Interrupt status +#define CAMERA_INTSTATUS_FIFO_OVERRUN_0 0x00000001 +#define CAMERA_INTSTATUS_FIFO_OVERRUN_1 0x00000002 +#define CAMERA_INTSTATUS_FIFO_OVERRUN_2 0x00000004 +#define CAMERA_INTSTATUS_END_OF_FRAME 0x00000008 +#define CAMERA_INTSTATUS_START_OF_FRAME 0x00000010 +#define CAMERA_INTSTATUS_CI_DISABLE_DONE 0x00000020 +#define CAMERA_INTSTATUS_CI_QUICK_DISABLE 0x00000040 +#define CAMERA_INTSTATUS_PARITY_ERROR 0x00000080 +#define CAMERA_INTSTATUS_END_OF_LINE 0x00000100 +#define CAMERA_INTSTATUS_FIFO_EMPTY_0 0x00000200 +#define CAMERA_INTSTATUS_FIFO_EMPTY_1 0x00000400 +#define CAMERA_INTSTATUS_FIFO_EMPTY_2 0x00000800 +#define CAMERA_INTSTATUS_RCV_DATA_AVALIBLE_0 0x00001000 +#define CAMERA_INTSTATUS_RCV_DATA_AVALIBLE_1 0x00002000 +#define CAMERA_INTSTATUS_RCV_DATA_AVALIBLE_2 0x00004000 +#define CAMERA_INTSTATUS_TIME_OUT 0x00008000 +#define CAMERA_INTSTATUS_END_OF_DMA 0x00010000 + +// Capture status +#define CAMERA_STATUS_VIDEO_CAPTURE_IN_PROCESS 0x0001 +#define CAMERA_STATUS_RING_BUFFER_FULL 0x0002 + +/* +Structures +*/ +typedef struct camera_context_s camera_context_t, *p_camera_context_t; + +typedef struct { + int (*init)(p_camera_context_t context); + int (*deinit)(p_camera_context_t); + int (*set_capture_format)(p_camera_context_t); + int (*start_capture)(p_camera_context_t, unsigned int frames); + int (*stop_capture)(p_camera_context_t); +} camera_function_t, *p_camera_function_t; + +// context +struct camera_context_s { + // syncronization stuff + atomic_t refcount; + + /* + DRIVER FILLED PARAMTER + */ + // sensor info + unsigned int sensor_type; + + // capture image info + unsigned int capture_width; + unsigned int capture_height; + unsigned int capture_input_format; + unsigned int capture_output_format; + + // frame rate control + unsigned int frame_rate; + + // ring buffers + // note: must pass in 8 bytes aligned address + void *buffer_virtual; + void *buffer_physical; + unsigned int buf_size; + + // memory for dma descriptors, layout: + // dma descriptor chain 0, + // dma descriptor chain 1, + // ... + void *dma_descriptors_virtual; + void *dma_descriptors_physical; + unsigned int dma_descriptors_size; + + // os mapped register address + unsigned int clk_reg_base; + unsigned int ost_reg_base; + unsigned int gpio_reg_base; + unsigned int ci_reg_base; + unsigned int board_reg_base; + + // function dispatch table + p_camera_function_t camera_functions; + + /* + FILLED PARAMTER + */ + int dma_channels[3]; + unsigned int capture_status; + + /* + INTERNALLY USED: DON'T TOUCH! + */ + unsigned int block_number, block_size; + unsigned int block_header, block_tail; + unsigned int fifo0_descriptors_virtual, fifo0_descriptors_physical; + unsigned int fifo1_descriptors_virtual, fifo1_descriptors_physical; + unsigned int fifo2_descriptors_virtual, fifo2_descriptors_physical; + unsigned int fifo0_num_descriptors; + unsigned int fifo1_num_descriptors; + unsigned int fifo2_num_descriptors; + unsigned int fifo0_transfer_size; + unsigned int fifo1_transfer_size; + unsigned int fifo2_transfer_size; +}; + + + + + +/* +Prototypes +*/ +/*********************************************************************** + * + * Init/Deinit APIs + * + ***********************************************************************/ +// Setup the sensor type, configure image capture format (RGB, yuv 444, yuv 422, yuv 420, packed | planar, MJPEG) regardless +// of current operating mode (i.e. sets mode for both still capture and video capture) +int camera_init( p_camera_context_t camera_context ); + +// Power off sensor +int camera_deinit( p_camera_context_t camera_context ); + + +/*********************************************************************** + * + * Capture APIs + * + ***********************************************************************/ +// Set the image format +int camera_set_capture_format( p_camera_context_t camera_context ); + +// take a picture and copy it into the ring buffer +int camera_capture_still_image( p_camera_context_t camera_context, unsigned int block_id ); + +// capture motion video and copy it the ring buffer +int camera_start_video_capture( p_camera_context_t camera_context, unsigned int block_id ); + +// disable motion video image capture +void camera_stop_video_capture( p_camera_context_t camera_context ); + + +/*********************************************************************** + * + * Flow Control APIs + * + ***********************************************************************/ +// continue capture image to next available buffer +// Returns the continued buffer id, -1 means buffer full and no transfer started +void camera_continue_transfer( p_camera_context_t camera_context ); + +// Return 1: there is available buffer, 0: buffer is full +int camera_next_buffer_available( p_camera_context_t camera_context ); + +// Application supplies the FrameBufferID to the driver to tell it that the application has completed processing of the +// given frame buffer, and that buffer is now available for re-use. +void camera_release_frame_buffer( p_camera_context_t camera_context, unsigned int frame_buffer_id ); + +// Returns the FrameBufferID for the first filled frame +// Note: -1 represents buffer empty +int camera_get_first_frame_buffer_id( p_camera_context_t camera_context ); + +/* +Returns the FrameBufferID for the last filled frame, this would be used if we were polling for image completion data, +or we wanted to make sure there were no frames waiting for us to process. +Note: -1 represents buffer empty +*/ +int camera_get_last_frame_buffer_id( p_camera_context_t camera_context ); + + +/*********************************************************************** + * + * Buffer Info APIs + * + ***********************************************************************/ +// Return: the number of frame buffers allocated for use. +unsigned int camera_get_num_frame_buffers( p_camera_context_t camera_context ); + +/* +FrameBufferID is a number between 0 and N-1, where N is the total number of frame buffers in use. +Returns the address of the given frame buffer. +The application will call this once for each frame buffer at application initialization only. +*/ +void* camera_get_frame_buffer_addr( p_camera_context_t camera_context, unsigned int frame_buffer_id ); + +// Return the block id +int camera_get_frame_buffer_id( p_camera_context_t camera_context, void* address ); + +/*********************************************************************** + * + * Frame rate APIs + * + ***********************************************************************/ +// Set desired frame rate +void camera_set_capture_frame_rate( p_camera_context_t camera_context ); + +// return current setting +void camera_get_capture_frame_rate( p_camera_context_t camera_context ); + + +/*********************************************************************** + * + * Interrupt APIs + * + ***********************************************************************/ +// set interrupt mask +void camera_set_int_mask( p_camera_context_t camera_context, unsigned int mask ); + +// get interrupt mask +unsigned int camera_get_int_mask( p_camera_context_t camera_context ); + +// clear interrupt status +void camera_clear_int_status( p_camera_context_t camera_context, unsigned int status ); + +// gpio init +void camera_gpio_init(void); + +#endif diff -uNr a/drivers/media/video/ci.h b/drivers/media/video/ci.h --- a/drivers/media/video/ci.h 1970-01-01 08:00:00.000000000 +0800 +++ b/drivers/media/video/ci.h 2005-01-27 09:56:48.000000000 +0800 @@ -0,0 +1,299 @@ +/* + ci - header file for pxa capture interface + + Copyright (C) 2003, Intel Corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _CI_H_ +#define _CI_H_ + +//--------------------------------------------------------------------------- +// Register definitions +//--------------------------------------------------------------------------- + +enum CI_REGBITS_CICR0 { + CI_CICR0_FOM = 0x00000001, + CI_CICR0_EOFM = 0x00000002, + CI_CICR0_SOFM = 0x00000004, + CI_CICR0_CDM = 0x00000008, + CI_CICR0_QDM = 0x00000010, + CI_CICR0_PERRM = 0x00000020, + CI_CICR0_EOLM = 0x00000040, + CI_CICR0_FEM = 0x00000080, + CI_CICR0_RDAVM = 0x00000100, + CI_CICR0_TOM = 0x00000200, + CI_CICR0_RESERVED = 0x03FFFC00, + CI_CICR0_SIM_SHIFT = 24, + CI_CICR0_SIM_SMASK = 0x7, + CI_CICR0_DIS = 0x08000000, + CI_CICR0_ENB = 0x10000000, + CI_CICR0_SL_CAP_EN = 0x20000000, + CI_CICR0_PAR_EN = 0x40000000, + CI_CICR0_DMA_EN = 0x80000000, + CI_CICR0_INTERRUPT_MASK = 0x3FF +}; + +enum CI_REGBITS_CICR1 { + CI_CICR1_DW_SHIFT = 0, + CI_CICR1_DW_SMASK = 0x7, + CI_CICR1_COLOR_SP_SHIFT = 3, + CI_CICR1_COLOR_SP_SMASK = 0x3, + CI_CICR1_RAW_BPP_SHIFT = 5, + CI_CICR1_RAW_BPP_SMASK = 0x3, + CI_CICR1_RGB_BPP_SHIFT = 7, + CI_CICR1_RGB_BPP_SMASK = 0x7, + CI_CICR1_YCBCR_F = 0x00000400, + CI_CICR1_RBG_F = 0x00000800, + CI_CICR1_RGB_CONV_SHIFT = 12, + CI_CICR1_RGB_CONV_SMASK = 0x7, + CI_CICR1_PPL_SHIFT = 15, + CI_CICR1_PPL_SMASK = 0x7FF, + CI_CICR1_RESERVED = 0x1C000000, + CI_CICR1_RGBT_CONV_SHIFT= 29, + CI_CICR1_RGBT_CONV_SMASK= 0x3, + CI_CICR1_TBIT = 0x80000000 +}; + +enum CI_REGBITS_CICR2 { + CI_CICR2_FSW_SHIFT = 0, + CI_CICR2_FSW_SMASK = 0x3, + CI_CICR2_BFPW_SHIFT= 3, + CI_CICR2_BFPW_SMASK= 0x3F, + CI_CICR2_RESERVED = 0x00000200, + CI_CICR2_HSW_SHIFT = 10, + CI_CICR2_HSW_SMASK = 0x3F, + CI_CICR2_ELW_SHIFT = 16, + CI_CICR2_ELW_SMASK = 0xFF, + CI_CICR2_BLW_SHIFT = 24, + CI_CICR2_BLW_SMASK = 0xFF +}; + +enum CI_REGBITS_CICR3 { + CI_CICR3_LPF_SHIFT = 0, + CI_CICR3_LPF_SMASK = 0x7FF, + CI_CICR3_VSW_SHIFT = 11, + CI_CICR3_VSW_SMASK = 0x1F, + CI_CICR3_EFW_SHIFT = 16, + CI_CICR3_EFW_SMASK = 0xFF, + CI_CICR3_BFW_SHIFT = 24, + CI_CICR3_BFW_SMASK = 0xFF +}; + +enum CI_REGBITS_CICR4 { + CI_CICR4_DIV_SHIFT = 0, + CI_CICR4_DIV_SMASK = 0xFF, + CI_CICR4_FR_RATE_SHIFT = 8, + CI_CICR4_FR_RATE_SMASK = 0x7, + CI_CICR4_RESERVED1 = 0x0007F800, + CI_CICR4_MCLK_EN = 0x00080000, + CI_CICR4_VSP = 0x00100000, + CI_CICR4_HSP = 0x00200000, + CI_CICR4_PCP = 0x00400000, + CI_CICR4_PCLK_EN = 0x00800000, + CI_CICR4_RESERVED2 = 0xFF000000, + CI_CICR4_RESERVED = CI_CICR4_RESERVED1 | CI_CICR4_RESERVED2 +}; + +enum CI_REGBITS_CISR { + CI_CISR_IFO_0 = 0x00000001, + CI_CISR_IFO_1 = 0x00000002, + CI_CISR_IFO_2 = 0x00000004, + CI_CISR_EOF = 0x00000008, + CI_CISR_SOF = 0x00000010, + CI_CISR_CDD = 0x00000020, + CI_CISR_CQD = 0x00000040, + CI_CISR_PAR_ERR = 0x00000080, + CI_CISR_EOL = 0x00000100, + CI_CISR_FEMPTY_0 = 0x00000200, + CI_CISR_FEMPTY_1 = 0x00000400, + CI_CISR_FEMPTY_2 = 0x00000800, + CI_CISR_RDAV_0 = 0x00001000, + CI_CISR_RDAV_1 = 0x00002000, + CI_CISR_RDAV_2 = 0x00004000, + CI_CISR_FTO = 0x00008000, + CI_CISR_RESERVED = 0xFFFF0000 +}; + +enum CI_REGBITS_CIFR { + CI_CIFR_FEN0 = 0x00000001, + CI_CIFR_FEN1 = 0x00000002, + CI_CIFR_FEN2 = 0x00000004, + CI_CIFR_RESETF = 0x00000008, + CI_CIFR_THL_0_SHIFT= 4, + CI_CIFR_THL_0_SMASK= 0x3, + CI_CIFR_RESERVED1 = 0x000000C0, + CI_CIFR_FLVL0_SHIFT= 8, + CI_CIFR_FLVL0_SMASK= 0xFF, + CI_CIFR_FLVL1_SHIFT= 16, + CI_CIFR_FLVL1_SMASK= 0x7F, + CI_CIFR_FLVL2_SHIFT= 23, + CI_CIFR_FLVL2_SMASK= 0x7F, + CI_CIFR_RESERVED2 = 0xC0000000, + CI_CIFR_RESERVED = CI_CIFR_RESERVED1 | CI_CIFR_RESERVED2 +}; + +//--------------------------------------------------------------------------- +// Parameter Type definitions +//--------------------------------------------------------------------------- +typedef enum { + CI_RAW8 = 0, //RAW + CI_RAW9, + CI_RAW10, + CI_YCBCR422, //YCBCR + CI_YCBCR422_PLANAR, //YCBCR Planaried + CI_RGB444, //RGB + CI_RGB555, + CI_RGB565, + CI_RGB666, + CI_RGB888, + CI_RGBT555_0, //RGB+Transparent bit 0 + CI_RGBT888_0, + CI_RGBT555_1, //RGB+Transparent bit 1 + CI_RGBT888_1, + CI_RGB666_PACKED, //RGB Packed + CI_RGB888_PACKED, + CI_INVALID_FORMAT = 0xFF +} CI_IMAGE_FORMAT; + +typedef enum { + CI_INTSTATUS_IFO_0 = 0x00000001, + CI_INTSTATUS_IFO_1 = 0x00000002, + CI_INTSTATUS_IFO_2 = 0x00000004, + CI_INTSTATUS_EOF = 0x00000008, + CI_INTSTATUS_SOF = 0x00000010, + CI_INTSTATUS_CDD = 0x00000020, + CI_INTSTATUS_CQD = 0x00000040, + CI_INTSTATUS_PAR_ERR = 0x00000080, + CI_INTSTATUS_EOL = 0x00000100, + CI_INTSTATUS_FEMPTY_0 = 0x00000200, + CI_INTSTATUS_FEMPTY_1 = 0x00000400, + CI_INTSTATUS_FEMPTY_2 = 0x00000800, + CI_INTSTATUS_RDAV_0 = 0x00001000, + CI_INTSTATUS_RDAV_1 = 0x00002000, + CI_INTSTATUS_RDAV_2 = 0x00004000, + CI_INTSTATUS_FTO = 0x00008000, + CI_INTSTATUS_ALL = 0x0000FFFF +} CI_INTERRUPT_STATUS; + +typedef enum { + CI_INT_IFO = 0x00000001, + CI_INT_EOF = 0x00000002, + CI_INT_SOF = 0x00000004, + CI_INT_CDD = 0x00000008, + CI_INT_CQD = 0x00000010, + CI_INT_PAR_ERR = 0x00000020, + CI_INT_EOL = 0x00000040, + CI_INT_FEMPTY = 0x00000080, + CI_INT_RDAV = 0x00000100, + CI_INT_FTO = 0x00000200, + CI_INT_ALL = 0x000003FF +} CI_INTERRUPT_MASK; +#define CI_INT_MAX 10 + +typedef enum CI_MODE { + CI_MODE_MP, // Master-Parallel + CI_MODE_SP, // Slave-Parallel + CI_MODE_MS, // Master-Serial + CI_MODE_EP, // Embedded-Parallel + CI_MODE_ES // Embedded-Serial +} CI_MODE; + + +typedef enum { + CI_FR_ALL = 0, // Capture all incoming frames + CI_FR_1_2, // Capture 1 out of every 2 frames + CI_FR_1_3, // Capture 1 out of every 3 frames + CI_FR_1_4, + CI_FR_1_5, + CI_FR_1_6, + CI_FR_1_7, + CI_FR_1_8 +} CI_FRAME_CAPTURE_RATE; + + +typedef enum { + CI_FIFO_THL_32 = 0, + CI_FIFO_THL_64, + CI_FIFO_THL_96 +} CI_FIFO_THRESHOLD; + +typedef struct { + unsigned int BFW; + unsigned int BLW; +} CI_MP_TIMING, CI_MS_TIMING; + +typedef struct { + unsigned int BLW; + unsigned int ELW; + unsigned int HSW; + unsigned int BFPW; + unsigned int FSW; + unsigned int BFW; + unsigned int EFW; + unsigned int VSW; +} CI_SP_TIMING; + +typedef enum { + CI_DATA_WIDTH4 = 0x0, + CI_DATA_WIDTH5 = 0x1, + CI_DATA_WIDTH8 = 0x2, + CI_DATA_WIDTH9 = 0x3, + CI_DATA_WIDTH10= 0x4 +} CI_DATA_WIDTH; + +//------------------------------------------------------------------------------------------------------- +// Configuration APIs +//------------------------------------------------------------------------------------------------------- + +void ci_set_frame_rate(CI_FRAME_CAPTURE_RATE frate); +CI_FRAME_CAPTURE_RATE ci_get_frame_rate(void); +void ci_set_image_format(CI_IMAGE_FORMAT input_format, CI_IMAGE_FORMAT output_format); +void ci_set_mode(CI_MODE mode, CI_DATA_WIDTH data_width); +void ci_configure_mp(unsigned int PPL, unsigned int LPF, CI_MP_TIMING* timing); +void ci_configure_sp(unsigned int PPL, unsigned int LPF, CI_SP_TIMING* timing); +void ci_configure_ms(unsigned int PPL, unsigned int LPF, CI_MS_TIMING* timing); +void ci_configure_ep(int parity_check); +void ci_configure_es(int parity_check); +void ci_set_clock(unsigned int clk_regs_base, int pclk_enable, int mclk_enable, unsigned int mclk_khz); +void ci_set_polarity(int pclk_sample_falling, int hsync_active_low, int vsync_active_low); +void ci_set_fifo( unsigned int timeout, CI_FIFO_THRESHOLD threshold, int fifo1_enable, + int fifo2_enable); +void ci_set_int_mask( unsigned int mask); +void ci_clear_int_status( unsigned int status); +void ci_set_reg_value( unsigned int reg_offset, unsigned int value); +int ci_get_reg_value(unsigned int reg_offset); + +void ci_reset_fifo(void); +unsigned int ci_get_int_mask(void); +unsigned int ci_get_int_status(void); +void ci_slave_capture_enable(void); +void ci_slave_capture_disable(void); + +//------------------------------------------------------------------------------------------------------- +// Control APIs +//------------------------------------------------------------------------------------------------------- +int ci_init(void); +void ci_deinit(void); +void ci_enable( int dma_en); +int ci_disable(int quick); + +//debug +void ci_dump(void); +// IRQ +irqreturn_t pxa_camera_irq(int irq, void *dev_id, struct pt_regs *regs); + +#endif diff -uNr a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig --- a/drivers/media/video/Kconfig 2004-10-19 05:55:18.000000000 +0800 +++ b/drivers/media/video/Kconfig 2005-01-27 09:56:48.000000000 +0800 @@ -306,5 +306,8 @@ To compile this driver as a module, choose M here: the module will be called ovcamchip +config PXA_CAMERA_MS + tristate "Camera Interface for Intel(R) PXA27x Processor Developer Kit" + depends on VIDEO_DEV && I2C endmenu diff -uNr a/drivers/media/video/Makefile b/drivers/media/video/Makefile --- a/drivers/media/video/Makefile 2004-10-19 05:53:50.000000000 +0800 +++ b/drivers/media/video/Makefile 2005-01-27 09:56:48.000000000 +0800 @@ -7,6 +7,8 @@ zoran-objs := zr36120.o zr36120_i2c.o zr36120_mem.o zr36067-objs := zoran_procfs.o zoran_device.o \ zoran_driver.o zoran_card.o +ms-camera-objs := pxa_camera.o adcm2650.o adcm2650_hw.o + obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o v4l1-compat.o @@ -47,3 +49,4 @@ obj-$(CONFIG_VIDEO_TUNER) += tuner.o tda9887.o obj-$(CONFIG_VIDEO_BUF) += video-buf.o obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o +obj-$(CONFIG_PXA_CAMERA_MS) += ms-camera.o diff -uNr a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c --- a/drivers/media/video/pxa_camera.c 1970-01-01 08:00:00.000000000 +0800 +++ b/drivers/media/video/pxa_camera.c 2005-01-27 09:56:48.000000000 +0800 @@ -0,0 +1,2071 @@ +/* + pxa_camera - main file for camera driver + + Copyright (C) 2003, Intel Corporation. + + 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. + + Code Status: + 2004/10/19: Yan Yin + - Ported to 2.6 kernel + - Made camera driver a loadable module +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "camera.h" +#include "ci.h" + +#ifdef CONFIG_MACH_MAINSTONE +#define ADCM2650 +#else +#define OV9640 +#endif + +#ifdef ADCM2650 +#include "adcm2650.h" +#include "adcm2650_hw.h" +#endif + +#ifdef OV9640 +#include "ov9640.h" +#include "ov9640_hw.h" +#endif + +#define CIBR0_PHY (0x50000000 + 0x28) +#define CIBR1_PHY (0x50000000 + 0x30) +#define CIBR2_PHY (0x50000000 + 0x38) + +#ifdef ADCM2650 +#define MAX_WIDTH 480 +#define MAX_HEIGHT 640 +#define MIN_WIDTH 72 +#define MIN_HEIGHT 72 +#endif + +#ifdef OV9640 +#define MAX_WIDTH 640 +#define MAX_HEIGHT 480 +#define MIN_WIDTH 88 +#define MIN_HEIGHT 72 +#endif + +#define WIDTH_DEFT 176 +#define HEIGHT_DEFT 144 +#define FRAMERATE_DEFT 0x0 +#define BUF_SIZE_DEFT 0xE1000 +#define SINGLE_DESC_TRANS_MAX 8000 + +#define MAX_DESC_NUM 0x400 +#define MAX_BLOCK_NUM 20 +/* + * Buffer Size Calculate Formula + * Buffer_Size = Page_Align (Max(window_size(VGA), window_size(CIF)*3) * Max(BPP)) + * Buffer_Size = Page_Align (Max ((640 * 480), (352 * 288 *3)) * 3) = 0xE1000 + */ + +static camera_context_t *g_camera_context = NULL; +struct device *g_camera_device; + +#ifdef ADCM2650 +static camera_function_t adcm2650_func; +#endif +#ifdef OV9640 +static camera_function_t ov9640_func; +#endif + +wait_queue_head_t camera_wait_q; + +/* /dev/videoX registration number */ +static int minor = 0; +int ci_dma_y; +int ci_dma_cb; +int ci_dma_cr; +volatile int task_waiting = 0; +static int still_image_mode = 0; +static int still_image_rdy = 0; +static int first_video_frame = 0; + +void pxa_ci_dma_irq_y(int channel, void *data, struct pt_regs *regs); +void pxa_ci_dma_irq_cb(int channel, void *data, struct pt_regs *regs); +void pxa_ci_dma_irq_cr(int channel, void *data, struct pt_regs *regs); + +static unsigned long ci_regs_base = 0; /* for CI registers IOMEM mapping */ +#define CI_REG(x) (* (volatile u32*)(x) ) +#define CI_REG_SIZE 0x40 /* 0x5000_0000 --- 0x5000_0038 * 64K */ +#define CI_REGS_PHYS 0x50000000 /* Start phyical address of CI registers */ + + +/*********************************************************************** + * + * Declarations + * + ***********************************************************************/ + +// map of camera image format (camera.h) ==> capture interface format (ci.h) +static const CI_IMAGE_FORMAT FORMAT_MAPPINGS[] = { + CI_RAW8, //RAW + CI_RAW9, + CI_RAW10, + + CI_RGB444, //RGB + CI_RGB555, + CI_RGB565, + CI_RGB666_PACKED, //RGB Packed + CI_RGB666, + CI_RGB888_PACKED, + CI_RGB888, + CI_RGBT555_0, //RGB+Transparent bit 0 + CI_RGBT888_0, + CI_RGBT555_1, //RGB+Transparent bit 1 + CI_RGBT888_1, + + CI_INVALID_FORMAT, + CI_YCBCR422, //YCBCR + CI_YCBCR422_PLANAR, //YCBCR Planaried + CI_INVALID_FORMAT, + CI_INVALID_FORMAT +}; + +static int update_dma_chain( p_camera_context_t camera_context ); +static void start_dma_transfer( p_camera_context_t camera_context, unsigned block_id ); +static void stop_dma_transfer( p_camera_context_t camera_context ); +static int start_capture( p_camera_context_t camera_context, unsigned int block_id, unsigned int frames ); +void pxa_dma_repeat(camera_context_t *cam_ctx); +void pxa_dma_continue(camera_context_t *cam_ctx); + +/*********************************************************************** + * + * Private functions + * + ***********************************************************************/ +/* +Generate dma descriptors +Pre-condition: these variables must be set properly + block_number, fifox_transfer_size + dma_descriptors_virtual, dma_descriptors_physical, dma_descirptors_size +Post-condition: these variables will be set + fifox_descriptors_virtual, fifox_descriptors_physical + fifox_num_descriptors +*/ +int update_dma_chain( p_camera_context_t camera_context ) +{ + pxa_dma_desc *cur_des_virtual, *cur_des_physical, *last_des_virtual = NULL; + int des_transfer_size, remain_size; + unsigned int i,j; + + int target_physical; + int target_virtual; + + // clear descriptor pointers + camera_context->fifo0_descriptors_virtual = camera_context->fifo0_descriptors_physical = 0; + camera_context->fifo1_descriptors_virtual = camera_context->fifo1_descriptors_physical = 0; + camera_context->fifo2_descriptors_virtual = camera_context->fifo2_descriptors_physical = 0; + + // calculate how many descriptors are needed per frame + camera_context->fifo0_num_descriptors = ( camera_context->fifo0_transfer_size + SINGLE_DESC_TRANS_MAX -1 ) + / SINGLE_DESC_TRANS_MAX; + camera_context->fifo1_num_descriptors = ( camera_context->fifo1_transfer_size + SINGLE_DESC_TRANS_MAX -1 ) + / SINGLE_DESC_TRANS_MAX; + camera_context->fifo2_num_descriptors = ( camera_context->fifo2_transfer_size + SINGLE_DESC_TRANS_MAX -1 ) + / SINGLE_DESC_TRANS_MAX; + // check if enough memory to generate descriptors + if ( (camera_context->fifo0_num_descriptors + camera_context->fifo1_num_descriptors + + camera_context->fifo2_num_descriptors) * camera_context->block_number + > camera_context->dma_descriptors_size) + return -1; + + // generate fifo0 dma chains + camera_context->fifo0_descriptors_virtual = (unsigned)camera_context->dma_descriptors_virtual; + camera_context->fifo0_descriptors_physical = (unsigned)camera_context->dma_descriptors_physical; + cur_des_virtual = (pxa_dma_desc *)camera_context->fifo0_descriptors_virtual; + cur_des_physical = (pxa_dma_desc *)camera_context->fifo0_descriptors_physical; + + for(i=0; iblock_number; i++) { + // in each iteration, generate one dma chain for one frame + remain_size = camera_context->fifo0_transfer_size; + + // assume the blocks are stored consecutively + target_physical = (unsigned)camera_context->buffer_physical + camera_context->block_size * i; + target_virtual = (unsigned)camera_context->buffer_virtual + camera_context->block_size * i; + + for(j=0; jfifo0_num_descriptors; j++) { + // set descriptor + if (remain_size > SINGLE_DESC_TRANS_MAX) + des_transfer_size = SINGLE_DESC_TRANS_MAX; + else + des_transfer_size = remain_size; + cur_des_virtual->ddadr = (unsigned)cur_des_physical + sizeof(pxa_dma_desc); + cur_des_virtual->dsadr = CIBR0_PHY; // FIFO0 physical address + cur_des_virtual->dtadr = target_physical; + cur_des_virtual->dcmd = des_transfer_size | DCMD_FLOWSRC | DCMD_INCTRGADDR | DCMD_BURST32; + + // advance pointers + remain_size -= des_transfer_size; + cur_des_virtual++; + cur_des_physical++; + target_physical += des_transfer_size; + target_virtual += des_transfer_size; + } + + // stop the dma transfer on one frame captured + last_des_virtual = cur_des_virtual - 1; + //last_des_virtual->ddadr |= 0x1; + } + + last_des_virtual->ddadr = ((unsigned)camera_context->fifo0_descriptors_physical); + + // generate fifo1 dma chains + if (camera_context->fifo1_transfer_size) { + // record fifo1 descriptors' start address + camera_context->fifo1_descriptors_virtual = (unsigned)cur_des_virtual; + camera_context->fifo1_descriptors_physical = (unsigned)cur_des_physical; + + for(i=0; iblock_number; i++) { + // in each iteration, generate one dma chain for one frame + remain_size = camera_context->fifo1_transfer_size; + + // assume the blocks are stored consecutively + target_physical = (unsigned)camera_context->buffer_physical + camera_context->block_size * i + + camera_context->fifo0_transfer_size; + target_virtual = (unsigned)camera_context->buffer_virtual + camera_context->block_size * i + + camera_context->fifo0_transfer_size; + + for(j=0; jfifo1_num_descriptors; j++) { + // set descriptor + if (remain_size > SINGLE_DESC_TRANS_MAX) + des_transfer_size = SINGLE_DESC_TRANS_MAX; + else + des_transfer_size = remain_size; + cur_des_virtual->ddadr = (unsigned)cur_des_physical + sizeof(pxa_dma_desc); + cur_des_virtual->dsadr = CIBR1_PHY; // FIFO1 physical address + cur_des_virtual->dtadr = target_physical; + cur_des_virtual->dcmd = des_transfer_size | DCMD_FLOWSRC | DCMD_INCTRGADDR | DCMD_BURST32; + + // advance pointers + remain_size -= des_transfer_size; + cur_des_virtual++; + cur_des_physical++; + target_physical += des_transfer_size; + target_virtual += des_transfer_size; + } + + // stop the dma transfer on one frame captured + last_des_virtual = cur_des_virtual - 1; + //last_des_virtual->ddadr |= 0x1; + } + last_des_virtual->ddadr = ((unsigned)camera_context->fifo1_descriptors_physical); + } + + // generate fifo2 dma chains + if (camera_context->fifo2_transfer_size) { + // record fifo1 descriptors' start address + camera_context->fifo2_descriptors_virtual = (unsigned)cur_des_virtual; + camera_context->fifo2_descriptors_physical = (unsigned)cur_des_physical; + + for(i=0; iblock_number; i++) { + // in each iteration, generate one dma chain for one frame + remain_size = camera_context->fifo2_transfer_size; + + // assume the blocks are stored consecutively + target_physical = (unsigned)camera_context->buffer_physical + camera_context->block_size * i + + camera_context->fifo0_transfer_size + camera_context->fifo1_transfer_size; + + for(j=0; jfifo2_num_descriptors; j++) { + // set descriptor + if (remain_size > SINGLE_DESC_TRANS_MAX) + des_transfer_size = SINGLE_DESC_TRANS_MAX; + else + des_transfer_size = remain_size; + cur_des_virtual->ddadr = (unsigned)cur_des_physical + sizeof(pxa_dma_desc); + cur_des_virtual->dsadr = CIBR2_PHY; // FIFO2 physical address + cur_des_virtual->dtadr = target_physical; + cur_des_virtual->dcmd = des_transfer_size | DCMD_FLOWSRC | DCMD_INCTRGADDR | DCMD_BURST32; + + // advance pointers + remain_size -= des_transfer_size; + cur_des_virtual++; + cur_des_physical++; + target_physical += des_transfer_size; + } + + // stop the dma transfer on one frame captured + last_des_virtual = cur_des_virtual - 1; + //last_des_virtual->ddadr |= 0x1; + } + last_des_virtual->ddadr = ((unsigned)camera_context->fifo2_descriptors_physical); + } + return 0; +} + +void start_dma_transfer( p_camera_context_t camera_context, unsigned block_id ) +{ + pxa_dma_desc *des_virtual, *des_physical; + + if (block_id >= camera_context->block_number) + return; + + // start channel 0 + des_virtual = (pxa_dma_desc *)camera_context->fifo0_descriptors_virtual + + block_id * camera_context->fifo0_num_descriptors; + des_physical = (pxa_dma_desc *)camera_context->fifo0_descriptors_physical + + block_id * camera_context->fifo0_num_descriptors; + + DDADR(camera_context->dma_channels[0]) = (int) des_physical; + DCSR(camera_context->dma_channels[0]) |= DCSR_RUN; + + // start channel 1 + if ( camera_context->fifo1_descriptors_virtual ) { + des_virtual = (pxa_dma_desc *)camera_context->fifo1_descriptors_virtual + + block_id * camera_context->fifo1_num_descriptors; + des_physical = (pxa_dma_desc *)camera_context->fifo1_descriptors_physical + + block_id * camera_context->fifo1_num_descriptors; + DDADR(camera_context->dma_channels[1]) = (int) des_physical; + DCSR(camera_context->dma_channels[1]) |= DCSR_RUN; + } + + // start channel 2 + if ( camera_context->fifo2_descriptors_virtual ) { + des_virtual = (pxa_dma_desc *)camera_context->fifo2_descriptors_virtual + + block_id * camera_context->fifo2_num_descriptors; + des_physical = (pxa_dma_desc *)camera_context->fifo2_descriptors_physical + + block_id * camera_context->fifo2_num_descriptors; + DDADR(camera_context->dma_channels[2]) = (int) des_physical; + DCSR(camera_context->dma_channels[2]) |= DCSR_RUN; + } +} + +void stop_dma_transfer( p_camera_context_t camera_context ) +{ + int ch0, ch1, ch2; + + ch0 = camera_context->dma_channels[0]; + ch1 = camera_context->dma_channels[1]; + ch2 = camera_context->dma_channels[2]; + DCSR(ch0) &= ~DCSR_RUN; + DCSR(ch1) &= ~DCSR_RUN; + DCSR(ch2) &= ~DCSR_RUN; + return; +} + +int start_capture( p_camera_context_t camera_context, unsigned int block_id, unsigned int frames ) +{ + int status; + + // clear ci fifo + ci_reset_fifo(); + ci_clear_int_status(0xFFFFFFFF); + + // start dma + start_dma_transfer(camera_context, block_id); + + // start capture + status = camera_context->camera_functions->start_capture(camera_context, frames); + return status; +} + +/*********************************************************************** + * + * Init/Deinit APIs + * + ***********************************************************************/ +int camera_init( p_camera_context_t camera_context ) +{ + int ret = 0; + int i; + +// parameter check + if (camera_context->buffer_virtual == NULL || camera_context->buffer_physical == NULL || camera_context->buf_size == 0) + return STATUS_WRONG_PARAMETER; + if (camera_context->dma_descriptors_virtual == NULL || camera_context->dma_descriptors_physical == NULL || + camera_context->dma_descriptors_size == 0) + return STATUS_WRONG_PARAMETER; + if (camera_context->sensor_type > CAMERA_TYPE_MAX) + return STATUS_WRONG_PARAMETER; + if (camera_context->capture_input_format > CAMERA_IMAGE_FORMAT_MAX || + camera_context->capture_output_format > CAMERA_IMAGE_FORMAT_MAX) + return STATUS_WRONG_PARAMETER; + // check the function dispatch table according to the sensor type + if ( !camera_context->camera_functions ) + return STATUS_WRONG_PARAMETER; + if ( !camera_context->camera_functions->init || + !camera_context->camera_functions->deinit || + !camera_context->camera_functions->set_capture_format || + !camera_context->camera_functions->start_capture || + !camera_context->camera_functions->stop_capture ) + return STATUS_WRONG_PARAMETER; + + // init context status + for(i=0; i<3; i++) + camera_context->dma_channels[i] = 0xFF; + (int)camera_context->fifo0_descriptors_virtual = NULL; + (int)camera_context->fifo1_descriptors_virtual = NULL; + (int)camera_context->fifo2_descriptors_virtual = NULL; + (int)camera_context->fifo0_descriptors_physical = NULL; + (int)camera_context->fifo1_descriptors_physical = NULL; + (int)camera_context->fifo2_descriptors_physical = NULL; + + camera_context->fifo0_num_descriptors = 0; + camera_context->fifo1_num_descriptors = 0; + camera_context->fifo2_num_descriptors = 0; + + camera_context->fifo0_transfer_size = 0; + camera_context->fifo1_transfer_size = 0; + camera_context->fifo2_transfer_size = 0; + + camera_context->block_number = 0; + camera_context->block_size = 0; + camera_context->block_header = 0; + camera_context->block_tail = 0; + + // Enable hardware + camera_gpio_init(); + + // capture interface init + ci_init(); + + // sensor init + ret = camera_context->camera_functions->init(camera_context); + if (ret) + goto camera_init_err; + + + camera_context->dma_channels[0] = ci_dma_y; + camera_context->dma_channels[1] = ci_dma_cb; + camera_context->dma_channels[2] = ci_dma_cr; + + // set capture format + ret = camera_set_capture_format(camera_context); + if (ret) + goto camera_init_err; + + // set frame rate + camera_set_capture_frame_rate(camera_context); + + return 0; + +camera_init_err: + camera_deinit(camera_context); + return -1; +} + +void camera_gpio_init() +{ + pxa_gpio_mode( 27 | GPIO_ALT_FN_3_IN); /* CIF_DD[0] */ + pxa_gpio_mode( 114 | GPIO_ALT_FN_1_IN); /* CIF_DD[1] */ + pxa_gpio_mode( 116 | GPIO_ALT_FN_1_IN); /* CIF_DD[2] */ + pxa_gpio_mode( 115 | GPIO_ALT_FN_2_IN); /* CIF_DD[3] */ + pxa_gpio_mode( 90 | GPIO_ALT_FN_3_IN); /* CIF_DD[4] */ + pxa_gpio_mode( 91 | GPIO_ALT_FN_3_IN); /* CIF_DD[5] */ + pxa_gpio_mode( 17 | GPIO_ALT_FN_2_IN); /* CIF_DD[6] */ + pxa_gpio_mode( 12 | GPIO_ALT_FN_2_IN); /* CIF_DD[7] */ + pxa_gpio_mode( 23 | GPIO_ALT_FN_1_OUT); /* CIF_MCLK */ + pxa_gpio_mode( 26 | GPIO_ALT_FN_2_IN); /* CIF_PCLK */ + pxa_gpio_mode( 25 | GPIO_ALT_FN_1_IN); /* CIF_LV */ + pxa_gpio_mode( 24 | GPIO_ALT_FN_1_IN); /* CIF_FV */ +#ifdef OV9640 + pxa_gpio_mode( 120 | GPIO_OUT); /* CAMERA_PWR */ +#endif + + return; +} + +int camera_deinit( p_camera_context_t camera_context ) +{ + int ret; + + // deinit sensor + ret = camera_context->camera_functions->deinit(camera_context); + + // capture interface deinit + ci_deinit(); + + return ret; +} + + +/*********************************************************************** + * + * Capture APIs + * + ***********************************************************************/ +// Set the image format +int camera_set_capture_format( p_camera_context_t camera_context ) +{ + int ret; + unsigned int frame_size; + unsigned int block_number = 0; + CI_IMAGE_FORMAT ci_input_format, ci_output_format; + CI_MP_TIMING timing; + + // set capture interface + if (camera_context->capture_input_format > CAMERA_IMAGE_FORMAT_MAX || + camera_context->capture_output_format > CAMERA_IMAGE_FORMAT_MAX ) + return STATUS_WRONG_PARAMETER; + ci_input_format = FORMAT_MAPPINGS[camera_context->capture_input_format]; + ci_output_format = FORMAT_MAPPINGS[camera_context->capture_output_format]; + if (ci_input_format == CI_INVALID_FORMAT || ci_output_format == CI_INVALID_FORMAT) + return STATUS_WRONG_PARAMETER; + ci_set_image_format(ci_input_format, ci_output_format); + timing.BFW = timing.BLW = 0; + ci_configure_mp(camera_context->capture_width-1, camera_context->capture_height-1, &timing); + + // set sensor setting + ret = camera_context->camera_functions->set_capture_format(camera_context); + if (ret) + return ret; + + // ring buffer init + switch(camera_context->capture_output_format) { + case CAMERA_IMAGE_FORMAT_RGB565: + frame_size = camera_context->capture_width * camera_context->capture_height * 2; + camera_context->fifo0_transfer_size = frame_size; + camera_context->fifo1_transfer_size = 0; + camera_context->fifo2_transfer_size = 0; + break; + case CAMERA_IMAGE_FORMAT_YCBCR422_PACKED: + frame_size = camera_context->capture_width * camera_context->capture_height * 2; + camera_context->fifo0_transfer_size = frame_size; + camera_context->fifo1_transfer_size = 0; + camera_context->fifo2_transfer_size = 0; + break; + case CAMERA_IMAGE_FORMAT_YCBCR422_PLANAR: + frame_size = camera_context->capture_width * camera_context->capture_height * 2; + camera_context->fifo0_transfer_size = frame_size / 2; + camera_context->fifo1_transfer_size = frame_size / 4; + camera_context->fifo2_transfer_size = frame_size / 4; + break; + case CAMERA_IMAGE_FORMAT_RGB666_PLANAR: + frame_size = camera_context->capture_width * camera_context->capture_height * 4; + camera_context->fifo0_transfer_size = frame_size; + camera_context->fifo1_transfer_size = 0; + camera_context->fifo2_transfer_size = 0; + break; + case CAMERA_IMAGE_FORMAT_RGB666_PACKED: + frame_size = camera_context->capture_width * camera_context->capture_height * 3; + camera_context->fifo0_transfer_size = frame_size; + camera_context->fifo1_transfer_size = 0; + camera_context->fifo2_transfer_size = 0; + break; + case CAMERA_IMAGE_FORMAT_RGB888_PLANAR: + frame_size = camera_context->capture_width * camera_context->capture_height * 4; + camera_context->fifo0_transfer_size = frame_size; + camera_context->fifo1_transfer_size = 0; + camera_context->fifo2_transfer_size = 0; + break; + + default: + return STATUS_WRONG_PARAMETER; + break; + } + camera_context->block_size = frame_size; + block_number = camera_context->buf_size / frame_size; + camera_context->block_number = block_number > MAX_BLOCK_NUM ? MAX_BLOCK_NUM : block_number; + camera_context->block_header = camera_context->block_tail = 0; + + // generate dma descriptor chain + ret = update_dma_chain(camera_context); + if (ret) + return -1; + return 0; +} + +// take a picture and copy it into the ring buffer +int camera_capture_still_image( p_camera_context_t camera_context, unsigned int block_id ) +{ + int status; + + // init buffer status & capture + camera_context->block_header = camera_context->block_tail = block_id; + camera_context->capture_status = 0; + status = start_capture( camera_context, block_id, 1 ); + return status; +} + +// capture motion video and copy it to the ring buffer +int camera_start_video_capture( p_camera_context_t camera_context, unsigned int block_id ) +{ + int status; + + // init buffer status & capture + camera_context->block_header = camera_context->block_tail = block_id; + camera_context->capture_status = CAMERA_STATUS_VIDEO_CAPTURE_IN_PROCESS; + status = start_capture( camera_context, block_id, 0 ); + return status; +} + +// disable motion video image capture +void camera_stop_video_capture( p_camera_context_t camera_context ) +{ + int status; + + // stop capture + status = camera_context->camera_functions->stop_capture(camera_context); + + // stop dma + stop_dma_transfer(camera_context); + + // update the flag + if ( !(camera_context->capture_status & CAMERA_STATUS_RING_BUFFER_FULL) ) + camera_context->capture_status &= ~CAMERA_STATUS_VIDEO_CAPTURE_IN_PROCESS; + return; +} + + +/*********************************************************************** + * + * Flow Control APIs + * + ***********************************************************************/ +// continue capture image to next available buffer +void camera_continue_transfer( p_camera_context_t camera_context ) +{ + // don't think we need this either. JR + // continue transfer on next block + start_dma_transfer( camera_context, camera_context->block_tail ); +} + +// Return 1: there is available buffer, 0: buffer is full +int camera_next_buffer_available( p_camera_context_t camera_context ) +{ + camera_context->block_header = (camera_context->block_header + 1) % camera_context->block_number; + if (((camera_context->block_header + 1) % camera_context->block_number) != camera_context->block_tail) + { + return 1; + } + camera_context->capture_status |= CAMERA_STATUS_RING_BUFFER_FULL; + + return 0; +} + +// Application supplies the FrameBufferID to the driver to tell it that the application has completed processing of +// the given frame buffer, and that buffer is now available for re-use. +void camera_release_frame_buffer( p_camera_context_t camera_context, unsigned int frame_buffer_id ) +{ + + camera_context->block_tail = (camera_context->block_tail + 1) % camera_context->block_number; + + // restart video capture only if video capture is in progress and space is available for image capture + if( (camera_context->capture_status & CAMERA_STATUS_RING_BUFFER_FULL ) && + (camera_context->capture_status & CAMERA_STATUS_VIDEO_CAPTURE_IN_PROCESS)) + { + if (((camera_context->block_header + 2) % camera_context->block_number) != camera_context->block_tail) + { + camera_context->capture_status &= ~CAMERA_STATUS_RING_BUFFER_FULL; + start_capture( camera_context, camera_context->block_tail, 0 ); + } + } +} + +// Returns the FrameBufferID for the first filled frame +// Note: -1 represents buffer empty +int camera_get_first_frame_buffer_id( p_camera_context_t camera_context ) +{ + // check whether buffer is empty + if ( (camera_context->block_header == camera_context->block_tail) && + !(camera_context->capture_status & CAMERA_STATUS_RING_BUFFER_FULL) ) + return -1; + + // return the block header + return camera_context->block_header; +} + +// Returns the FrameBufferID for the last filled frame, this would be used if we were polling for image completion data, +// or we wanted to make sure there were no frames waiting for us to process. +// Note: -1 represents buffer empty +int camera_get_last_frame_buffer_id( p_camera_context_t camera_context ) +{ + int ret; + + // check whether buffer is empty + if ( (camera_context->block_header == camera_context->block_tail) && + !(camera_context->capture_status & CAMERA_STATUS_RING_BUFFER_FULL) ) + return -1; + + // return the block before the block_tail + ret = ( camera_context->block_tail + camera_context->block_number -1 ) % camera_context->block_number; + return ret; +} + + +/*********************************************************************** + * + * Buffer Info APIs + * + ***********************************************************************/ +// Return: the number of frame buffers allocated for use. +unsigned int camera_get_num_frame_buffers( p_camera_context_t camera_context ) +{ + return camera_context->block_number; +} + +// FrameBufferID is a number between 0 and N-1, where N is the total number of frame buffers in use. Returns the address of +// the given frame buffer. The application will call this once for each frame buffer at application initialization only. +void* camera_get_frame_buffer_addr( p_camera_context_t camera_context, unsigned int frame_buffer_id ) +{ + return (void*)((unsigned)camera_context->buffer_virtual + camera_context->block_size * frame_buffer_id); +} + +// Return the block id +int camera_get_frame_buffer_id( p_camera_context_t camera_context, void* address ) +{ + if ( ((unsigned)address >= (unsigned)camera_context->buffer_virtual) && + ((unsigned)address <= (unsigned)camera_context->buffer_virtual + camera_context->buf_size )) + return ((unsigned)address - (unsigned)camera_context->buffer_virtual) / camera_context->block_size; + return -1; +} + + +/*********************************************************************** + * + * Frame rate APIs + * + ***********************************************************************/ +// Set desired frame rate +void camera_set_capture_frame_rate( p_camera_context_t camera_context ) +{ + ci_set_frame_rate(camera_context->frame_rate); + return; +} + +// return current setting +void camera_get_capture_frame_rate( p_camera_context_t camera_context ) +{ + camera_context->frame_rate = ci_get_frame_rate(); + return; +} + + +/*********************************************************************** + * + * Interrupt APIs + * + ***********************************************************************/ +// set interrupt mask +void camera_set_int_mask( p_camera_context_t camera_context, unsigned int mask ) +{ + pxa_dma_desc *end_des_virtual; + int dma_interrupt_on; + unsigned int i; + + // set CI interrupt + ci_set_int_mask( mask & CI_CICR0_INTERRUPT_MASK ); + + // set dma end interrupt + if ( mask & CAMERA_INTMASK_END_OF_DMA ) + dma_interrupt_on = 1; + else + dma_interrupt_on = 0; + + // set fifo0 dma chains' flag + end_des_virtual = (pxa_dma_desc*)camera_context->fifo0_descriptors_virtual + + camera_context->fifo0_num_descriptors - 1; + for(i=0; iblock_number; i++) { + if (dma_interrupt_on) + end_des_virtual->dcmd |= DCMD_ENDIRQEN; + else + end_des_virtual->dcmd &= ~DCMD_ENDIRQEN; + end_des_virtual += camera_context->fifo0_num_descriptors; + } +} + +// get interrupt mask +unsigned int camera_get_int_mask( p_camera_context_t camera_context ) +{ + pxa_dma_desc *end_des_virtual; + unsigned int ret; + + // get CI mask + ret = ci_get_int_mask(); + + // get dma end mask + end_des_virtual = (pxa_dma_desc *)camera_context->fifo0_descriptors_virtual + camera_context->fifo0_num_descriptors - 1; + if (end_des_virtual->dcmd & DCMD_ENDIRQEN) + ret |= CAMERA_INTMASK_END_OF_DMA; + + return ret; +} + +// clear interrupt status +void camera_clear_int_status( p_camera_context_t camera_context, unsigned int status ) +{ + ci_clear_int_status( (status & 0xFFFF) ); +} + +/*********************************************************************************** +* Application interface * +***********************************************************************************/ +static int pxa_camera_open(struct inode *inode, struct file *file) +{ + int status = -1; + camera_context_t *cam_ctx; + + cam_ctx = g_camera_context; + + if (atomic_read(&cam_ctx->refcount)) + return -EBUSY; + + atomic_inc(&cam_ctx->refcount); + init_waitqueue_head(&camera_wait_q); + +#ifdef ADCM2650 + cam_ctx->sensor_type = CAMERA_TYPE_ADCM_2650; +#endif +#ifdef OV9640 + cam_ctx->sensor_type = CAMERA_TYPE_OMNIVISION_9640; +#endif + cam_ctx->capture_width = WIDTH_DEFT; + cam_ctx->capture_height = HEIGHT_DEFT; + cam_ctx->capture_input_format = CAMERA_IMAGE_FORMAT_YCBCR422_PLANAR; + cam_ctx->capture_output_format = CAMERA_IMAGE_FORMAT_YCBCR422_PLANAR; + cam_ctx->frame_rate = FRAMERATE_DEFT; + + // init function dispatch table +#ifdef ADCM2650 + adcm2650_func.init = camera_func_adcm2650_init; + adcm2650_func.deinit = camera_func_adcm2650_deinit; + adcm2650_func.set_capture_format = camera_func_adcm2650_set_capture_format; + adcm2650_func.start_capture = camera_func_adcm2650_start_capture; + adcm2650_func.stop_capture = camera_func_adcm2650_stop_capture; + cam_ctx->camera_functions = &adcm2650_func; +#endif + +#ifdef OV9640 + ov9640_func.init = camera_func_ov9640_init; + ov9640_func.deinit = camera_func_ov9640_deinit; + ov9640_func.set_capture_format = camera_func_ov9640_set_capture_format; + ov9640_func.start_capture = camera_func_ov9640_start_capture; + ov9640_func.stop_capture = camera_func_ov9640_stop_capture; + cam_ctx->camera_functions = &ov9640_func; +#endif + + cam_ctx->ost_reg_base = 0; + cam_ctx->gpio_reg_base = 0; + cam_ctx->ci_reg_base = 0; + cam_ctx->board_reg_base = 0; + + cam_ctx->dma_descriptors_virtual = dma_alloc_writecombine( g_camera_device, + MAX_DESC_NUM * sizeof(pxa_dma_desc), + (void *)&cam_ctx->dma_descriptors_physical, + GFP_KERNEL); + if (!cam_ctx->dma_descriptors_virtual) + goto open_error; + + cam_ctx->buf_size = BUF_SIZE_DEFT; + cam_ctx->buffer_virtual = dma_alloc_writecombine( g_camera_device, + cam_ctx->buf_size, + (void *)&cam_ctx->buffer_physical, + GFP_KERNEL); + if (!cam_ctx->buffer_virtual) + goto open_error; + cam_ctx->dma_descriptors_size = MAX_DESC_NUM; + + status = camera_init(cam_ctx); + printk("PXA_CAMERA: pxa_camera_open, status = %d \n", status); + return status; + +open_error: + if (cam_ctx->dma_descriptors_virtual) + dma_free_writecombine (g_camera_device, + MAX_DESC_NUM * sizeof (pxa_dma_desc), + cam_ctx->dma_descriptors_virtual, + (int)cam_ctx->dma_descriptors_physical); + + return -1; +} + +static int pxa_camera_close(struct inode *inode, struct file *file) +{ + camera_context_t *cam_ctx = g_camera_context; + + if(still_image_mode==0) + camera_stop_video_capture(cam_ctx); + + camera_deinit(cam_ctx); + if (cam_ctx->dma_descriptors_virtual) + dma_free_writecombine (g_camera_device, + MAX_DESC_NUM * sizeof (pxa_dma_desc), + cam_ctx->dma_descriptors_virtual, + (int)cam_ctx->dma_descriptors_physical); + if (cam_ctx->buffer_virtual) + dma_free_writecombine (g_camera_device, + cam_ctx->buf_size, + cam_ctx->buffer_virtual, + (int)cam_ctx->buffer_physical); + + atomic_dec(&cam_ctx->refcount); + printk("PXA_CAMERA: pxa_camera_close\n"); + return 0; +} + + +static ssize_t pxa_camera_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + camera_context_t *cam_ctx = g_camera_context; + int offset = 0; + + if (still_image_mode == 1 && still_image_rdy == 1) { + if (copy_to_user(buf, cam_ctx->buffer_virtual + offset, cam_ctx->block_size)) + return -EFAULT; + still_image_rdy = 0; + return cam_ctx->block_size; + } + + if (first_video_frame == 1) + first_video_frame = 0; + else if (still_image_mode == 0) + cam_ctx->block_tail = (cam_ctx->block_tail + 1) % cam_ctx->block_number; + + offset = cam_ctx->block_tail * cam_ctx->block_size; + if (cam_ctx->block_header == cam_ctx->block_tail) { + task_waiting = 1; + interruptible_sleep_on (&camera_wait_q); + } + + if (copy_to_user(buf, cam_ctx->buffer_virtual + offset, cam_ctx->block_size)) + return -EFAULT; + + return cam_ctx->block_size; +} + +struct reg_set_s { + int val1; + int val2; +}; + +static int pxa_camera_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long param) +{ + int retval = 0; + camera_context_t *cam_ctx = g_camera_context; + + switch (cmd) { +/* V4L Standard IOCTL. */ + case VIDIOCGCAP: + { + struct video_capability vc; + strcpy (vc.name, "Bulverde Camera"); + vc.maxwidth = MAX_WIDTH; + vc.maxheight = MAX_HEIGHT; + vc.minwidth = MIN_WIDTH; + vc.minheight = MIN_HEIGHT; + if (copy_to_user((int __user*)param, &vc, sizeof(struct video_capability))) + return -EFAULT; + break; + } + + /* get capture size */ + case VIDIOCGWIN: + { + struct video_window vw; + vw.width = cam_ctx->capture_width; + vw.height = cam_ctx->capture_height; + if (copy_to_user((int __user*)param, &vw, sizeof(struct video_window))) + retval = -EFAULT; + break; + } + + /* set capture size. */ + case VIDIOCSWIN: + { + struct video_window vw; + if (copy_from_user(&vw, (int __user*)param, sizeof(vw))) { + retval = -EFAULT; + break; + } + if (vw.width > MAX_WIDTH || vw.height > MAX_HEIGHT + || vw.width < MIN_WIDTH || vw.height < MIN_HEIGHT) { + retval = -EFAULT; + break; + } + + cam_ctx->capture_width = vw.width; + cam_ctx->capture_height = vw.height; + camera_set_capture_format(cam_ctx); + break; + } + + case VIDIOCSPICT: + { + struct video_picture vp; + if (copy_from_user(&vp, (int __user*)param, sizeof(vp))) { + retval = -EFAULT; + break; + } + cam_ctx->capture_output_format = vp.palette; + retval = camera_set_capture_format(cam_ctx); + break; + } + case VIDIOCGPICT: + { + struct video_picture vp; + vp.palette = cam_ctx->capture_output_format; + if (copy_to_user((int __user*)param, &vp, sizeof(struct video_picture))) + retval = -EFAULT; + break; + } + + case VIDIOCCAPTURE: + { + int capture_flag; + + capture_flag = (int)param; +/* Still Image Capture */ + if (capture_flag == STILL_IMAGE) { + camera_set_int_mask(cam_ctx, 0x3f9 | 0x0400); + cam_ctx->block_header = 0; + cam_ctx->block_tail = 0; + still_image_mode = 1; + camera_capture_still_image(cam_ctx, 0); + break; + } +/* Video Capture Start */ + else if (capture_flag == VIDEO_START) { + camera_set_int_mask(cam_ctx, 0x3f9 | 0x0400); + cam_ctx->block_header = 0; + cam_ctx->block_tail = 0; + still_image_mode = 0; + first_video_frame = 1; + camera_start_video_capture (cam_ctx, 0); + break; + } +/* Video Capture Stop */ + else if (capture_flag == VIDEO_STOP) { + camera_stop_video_capture(cam_ctx); + break; + } + else { + retval = -EFAULT; + break; + } + } + +/* mmap interface */ + case VIDIOCGMBUF: + { + struct video_mbuf vm; + int i; + + memset(&vm, 0, sizeof(vm)); + vm.size = cam_ctx->buf_size; + vm.frames = cam_ctx->block_number; + for (i = 0; i < vm.frames; i++) + vm.offsets[i] = cam_ctx->block_size * i; + + if (copy_to_user((int __user*)param, (void *)&vm, sizeof(vm))) + retval = -EFAULT; + break; + } + +/* Application extended IOCTL. */ +/* Register access interface */ + case WCAM_VIDIOCSINFOR: + { + struct reg_set_s reg_s; + if (copy_from_user(®_s, (int __user*)param, sizeof(int) * 2)) { + retval = -EFAULT; + break; + } + cam_ctx->capture_input_format = reg_s.val1; + cam_ctx->capture_output_format = reg_s.val2; + + retval = camera_set_capture_format(cam_ctx); + break; + } + + case WCAM_VIDIOCGINFOR: + { + struct reg_set_s reg_s; + reg_s.val1 = cam_ctx->capture_input_format; + reg_s.val2 = cam_ctx->capture_output_format; + if (copy_to_user((int __user*)param, ®_s, sizeof(int) * 2)) + retval = -EFAULT; + break; + } + + case WCAM_VIDIOCGCIREG: + { + struct reg_set_s reg_s; + if (copy_from_user(®_s, (int __user*)param, sizeof(int) * 2)) { + retval = -EFAULT; + break; + } + + reg_s.val2 = ci_get_reg_value (reg_s.val1); + if (copy_to_user((int __user*)param, ®_s, sizeof(int) * 2)) + retval = -EFAULT; + break; + } + + case WCAM_VIDIOCSCIREG: + { + struct reg_set_s reg_s; + if (copy_from_user(®_s, (int __user*)param, sizeof(int) * 2)) { + retval = -EFAULT; + break; + } + ci_set_reg_value (reg_s.val1, reg_s.val2); + break; + } +#ifdef ADCM2650 + case WCAM_VIDIOCGCAMREG: + { + struct reg_set_s reg_s; + if (copy_from_user(®_s, (int __user*)param, sizeof(int) * 2)) { + retval = -EFAULT; + break; + } + adcm2650_pipeline_read((u16)reg_s.val1, (u16 *)®_s.val2); + if (copy_to_user((int __user*)param, ®_s, sizeof(int) * 2)) + retval = -EFAULT; + break; + } + + case WCAM_VIDIOCSCAMREG: + { + struct reg_set_s reg_s; + if (copy_from_user(®_s, (int __user*)param, sizeof(int) * 2)) { + retval = -EFAULT; + break; + } + adcm2650_pipeline_write ((u16)reg_s.val1, (u16)reg_s.val2); + break; + } +#endif + default: + { + printk ("Invalid ioctl parameters.\n"); + retval = -ENOIOCTLCMD; + break; + } + + } + + return retval; +} + +#define pte_offset(dir, addr) ((pte_t *)pmd_page(*(dir)) + __pte_index(addr)) + +static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) +{ + unsigned long ret = 0UL; + pmd_t *pmd; + pte_t *ptep, pte; + + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, adr); + if (!pmd_none(*pmd)) { + ptep = pte_offset(pmd, adr); + pte = *ptep; + if (pte_present(pte)) { + ret = (unsigned long) page_address(pte_page(pte)); + ret |= (adr & (PAGE_SIZE-1)); + } + } + } + return ret; +} + +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long va, kva, ret; + + va=(unsigned long) (adr); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = __pa(kva); + return ret; +} + +static int pxa_camera_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long start = (unsigned long)vma->vm_start; + unsigned long end = (unsigned long)vma->vm_end; + unsigned long size=end-start; + + unsigned long page, pos; + camera_context_t *cam_ctx = g_camera_context; + + pos = (unsigned long)(cam_ctx->buffer_virtual); + page = kvirt_to_pa(pos); + + if (remap_page_range(vma, start, page, size, PAGE_SHARED)) { + return -EFAULT; + } + return 0; +} + +unsigned int pxa_camera_poll(struct file *file, poll_table *wait) +{ + camera_context_t *cam_ctx = g_camera_context; + static int waited = 0; + + poll_wait(file, &camera_wait_q, wait); + + if (still_image_mode == 1 && still_image_rdy == 1) { + still_image_rdy = 0; + return POLLIN | POLLRDNORM; + } + if (first_video_frame == 1) + first_video_frame = 0; + else if (still_image_mode == 0 && waited != 1) + cam_ctx->block_tail = (cam_ctx->block_tail + 1) % cam_ctx->block_number; + + if (cam_ctx->block_header == cam_ctx->block_tail) { + task_waiting = 1; + waited = 1; + return 0; + } + else waited = 0; + + return POLLIN | POLLRDNORM; +} + +/* + * Suspend the Camera Module. + */ +static int pxa_camera_suspend(struct device * dev, u32 state, u32 level) +{ + switch(level){ + case SUSPEND_POWER_DOWN: + printk(KERN_INFO "pxa_camera: camera suspend\n"); + disable_irq(IRQ_CAMERA); + } + + return 0; +} + +/* + * Resume the Camera Module. + */ +static int pxa_camera_resume(struct device * dev, u32 level) +{ + switch(level){ + case RESUME_POWER_ON: + printk(KERN_INFO "pxa_camera: camera resume\n"); + enable_irq(IRQ_CAMERA); + + DRCMR68 = ci_dma_y | DRCMR_MAPVLD; + DRCMR69 = ci_dma_cb | DRCMR_MAPVLD; + DRCMR70 = ci_dma_cr | DRCMR_MAPVLD; + } + + return 0; +} + +int pxa_camera_video_init(struct video_device *vdev) +{ + int status = -1; + + if (! (g_camera_context = kmalloc (sizeof(struct camera_context_s), GFP_KERNEL) ) ) + printk( "PXA_CAMERA: Cann't allocate buffer for camera control structure \n"); + else + status = 0; + + g_camera_context->dma_channels[0] = ci_dma_y; + g_camera_context->dma_channels[1] = ci_dma_cb; + g_camera_context->dma_channels[2] = ci_dma_cr; + + return status; +} + +static void pxa_camera_release(struct video_device *dev) +{ +#if 0 + /*FIXME: add specific release function here*/ + printk(KERN_WARNING "videodev: has no release callback. " + "Please fix your driver for proper sysfs support, see " + "http://lwn.net/Articles/36850/\n"); +#endif +} + +static struct file_operations pxa_camera_fops = +{ + .owner = THIS_MODULE, + .open = pxa_camera_open, + .release = pxa_camera_close, + .ioctl = pxa_camera_ioctl, + .read = pxa_camera_read, + .mmap = pxa_camera_mmap, + .poll = pxa_camera_poll, + .llseek = no_llseek, +}; + +static struct video_device vd = +{ + .name = "PXA Camera", + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_PXA_CAMERA, /* FIXME */ + .fops = &pxa_camera_fops, + .release = pxa_camera_release, + .minor = -1, +}; + +static int pxa_camera_probe(struct device *dev) +{ + + /* 1. mapping CI registers, so that we can access the CI */ + if (request_irq(IRQ_CAMERA, pxa_camera_irq, 0, "PXA Camera", &vd)) { + printk ("Camera interrupt register failed \n"); + goto init_error_1; + } + ci_dma_y = -1; + ci_dma_cb = -1; + ci_dma_cr = -1; + + ci_dma_y = pxa_request_dma("CI_Y",DMA_PRIO_HIGH, pxa_ci_dma_irq_y, &vd); + if (ci_dma_y < 0) { + printk( "PXA_CAMERA: Cann't request DMA for Y\n"); + goto init_error_2; + } + + ci_dma_cb = pxa_request_dma("CI_Cb",DMA_PRIO_HIGH, pxa_ci_dma_irq_cb, &vd); + if (ci_dma_cb < 0) { + printk( "PXA_CAMERA: Cann't request DMA for Cb\n"); + goto init_error_2; + } + + ci_dma_cr = pxa_request_dma("CI_Cr",DMA_PRIO_HIGH, pxa_ci_dma_irq_cr, &vd); + if (ci_dma_cr < 0) { + printk( "PXA_CAMERA: Cann't request DMA for Cr\n"); + goto init_error_2; + } + DRCMR68 = ci_dma_y | DRCMR_MAPVLD; + DRCMR69 = ci_dma_cb | DRCMR_MAPVLD; + DRCMR70 = ci_dma_cr | DRCMR_MAPVLD; + + if (video_register_device(&vd, VFL_TYPE_GRABBER, minor) < 0) { + printk("PXA_CAMERA: video_register_device failed\n"); + goto init_error_2; + } else { + printk("PXA_CAMERA: PXA Camera driver loaded for: /dev/video%d \n",minor); + } + + if(pxa_camera_video_init(&vd)<0){ + goto init_error_2; + } + + return 0; + +init_error_2: + if (ci_dma_y >= 0) + pxa_free_dma(ci_dma_y); + if (ci_dma_cb >= 0) + pxa_free_dma(ci_dma_cb); + if (ci_dma_cr >= 0) + pxa_free_dma(ci_dma_cr); + if (g_camera_context) + kfree(g_camera_context); + free_irq (IRQ_CAMERA, &vd); + +init_error_1: + return -EIO; +} + +static int pxa_camera_remove(struct device *dev) +{ + camera_context_t *cam_ctx = g_camera_context; + + if (cam_ctx->dma_channels[0]) { + pxa_free_dma(cam_ctx->dma_channels[0]); + cam_ctx->dma_channels[0] = 0; + } + if (cam_ctx->dma_channels[1]) { + pxa_free_dma(cam_ctx->dma_channels[1]); + cam_ctx->dma_channels[1] = 0; + } + if (cam_ctx->dma_channels[2]) { + pxa_free_dma(cam_ctx->dma_channels[2]); + cam_ctx->dma_channels[2] = 0; + } + + free_irq(IRQ_CAMERA, &vd); + kfree(g_camera_context); + video_unregister_device(&vd); + + printk("PXA_CAMERA: PXA Camera driver unloaded.\n"); + return 0; +} + +static struct device_driver pxa_camera_driver = { + .name = "pxa2xx-camera", + .bus = &platform_bus_type, + .probe = pxa_camera_probe, + .remove = pxa_camera_remove, + .suspend = pxa_camera_suspend, + .resume = pxa_camera_resume, +}; + +static int __devinit pxa_camera_init(void) +{ + return driver_register(&pxa_camera_driver); +} + +static void __exit pxa_camera_exit(void) +{ + return driver_unregister(&pxa_camera_driver); +} + +//------------------------------------------------------------------------------------------------------- +// Configuration APIs +//------------------------------------------------------------------------------------------------------- +void ci_set_frame_rate(CI_FRAME_CAPTURE_RATE frate) +{ + unsigned int value; + + // write cicr4 + value = CICR4; + value &= ~(CI_CICR4_FR_RATE_SMASK << CI_CICR4_FR_RATE_SHIFT); + value |= (unsigned)frate << CI_CICR4_FR_RATE_SHIFT; + CICR4 = value; +} + +CI_FRAME_CAPTURE_RATE ci_get_frame_rate(void) +{ + unsigned int value; + value = CICR4; + return (CI_FRAME_CAPTURE_RATE)((value >> CI_CICR4_FR_RATE_SHIFT) & CI_CICR4_FR_RATE_SMASK); +} + +void ci_set_image_format(CI_IMAGE_FORMAT input_format, CI_IMAGE_FORMAT output_format) +{ + unsigned int value, tbit, rgbt_conv, rgb_conv, rgb_f, ycbcr_f, rgb_bpp, raw_bpp, cspace; + + // write cicr1: preserve ppl value and data width value + value = CICR1; + value &= ( (CI_CICR1_PPL_SMASK << CI_CICR1_PPL_SHIFT) | ((CI_CICR1_DW_SMASK) << CI_CICR1_DW_SHIFT)); + tbit = rgbt_conv = rgb_conv = rgb_f = ycbcr_f = rgb_bpp = raw_bpp = cspace = 0; + switch(input_format) { + case CI_RAW8: + cspace = 0; + raw_bpp = 0; + break; + case CI_RAW9: + cspace = 0; + raw_bpp = 1; + break; + case CI_RAW10: + cspace = 0; + raw_bpp = 2; + break; + case CI_YCBCR422: + case CI_YCBCR422_PLANAR: + cspace = 2; + if (output_format == CI_YCBCR422_PLANAR) { + ycbcr_f = 1; + } + break; + case CI_RGB444: + cspace = 1; + rgb_bpp = 0; + break; + case CI_RGB555: + cspace = 1; + rgb_bpp = 1; + if (output_format == CI_RGBT555_0) { + rgbt_conv = 2; + tbit = 0; + } + else if (output_format == CI_RGBT555_1) { + rgbt_conv = 2; + tbit = 1; + } + break; + case CI_RGB565: + cspace = 1; + rgb_bpp = 2; + rgb_f = 1; + break; + case CI_RGB666: + cspace = 1; + rgb_bpp = 3; + if (output_format == CI_RGB666_PACKED) { + rgb_f = 1; + } + break; + case CI_RGB888: + case CI_RGB888_PACKED: + cspace = 1; + rgb_bpp = 4; + switch(output_format) { + case CI_RGB888_PACKED: + rgb_f = 1; + break; + case CI_RGBT888_0: + rgbt_conv = 1; + tbit = 0; + break; + case CI_RGBT888_1: + rgbt_conv = 1; + tbit = 1; + break; + case CI_RGB666: + rgb_conv = 1; + break; + case CI_RGB666_PACKED: + rgb_conv = 1; + rgb_f = 1; + break; + case CI_RGB565: + rgb_conv = 2; + break; + case CI_RGB555: + rgb_conv = 3; + break; + case CI_RGB444: + rgb_conv = 4; + break; + default: + break; + } + break; + default: + break; + } + value |= (tbit==1) ? CI_CICR1_TBIT : 0; + value |= rgbt_conv << CI_CICR1_RGBT_CONV_SHIFT; + value |= rgb_conv << CI_CICR1_RGB_CONV_SHIFT; + value |= (rgb_f==1) ? CI_CICR1_RBG_F : 0; + value |= (ycbcr_f==1) ? CI_CICR1_YCBCR_F : 0; + value |= rgb_bpp << CI_CICR1_RGB_BPP_SHIFT; + value |= raw_bpp << CI_CICR1_RAW_BPP_SHIFT; + value |= cspace << CI_CICR1_COLOR_SP_SHIFT; + CICR1 = value; + + return; +} + +void ci_set_mode(CI_MODE mode, CI_DATA_WIDTH data_width) +{ + unsigned int value; + + // write mode field in cicr0 + value = CICR0; + value &= ~(CI_CICR0_SIM_SMASK << CI_CICR0_SIM_SHIFT); + value |= (unsigned int)mode << CI_CICR0_SIM_SHIFT; + CICR0 = value; + + // write data width cicr1 + value = CICR1; + value &= ~(CI_CICR1_DW_SMASK << CI_CICR1_DW_SHIFT); + value |= ((unsigned)data_width) << CI_CICR1_DW_SHIFT; + CICR1 = value; + return; +} + +void ci_configure_mp(unsigned int ppl, unsigned int lpf, CI_MP_TIMING* timing) +{ + unsigned int value; + + // write ppl field in cicr1 + value = CICR1; + value &= ~(CI_CICR1_PPL_SMASK << CI_CICR1_PPL_SHIFT); + value |= (ppl & CI_CICR1_PPL_SMASK) << CI_CICR1_PPL_SHIFT; + CICR1 = value; + + // write BLW, ELW in cicr2 + value = CICR2; + value &= ~(CI_CICR2_BLW_SMASK << CI_CICR2_BLW_SHIFT | CI_CICR2_ELW_SMASK << CI_CICR2_ELW_SHIFT ); + value |= (timing->BLW & CI_CICR2_BLW_SMASK) << CI_CICR2_BLW_SHIFT; + CICR2 = value; + + // write BFW, LPF in cicr3 + value = CICR3; + value &= ~(CI_CICR3_BFW_SMASK << CI_CICR3_BFW_SHIFT | CI_CICR3_LPF_SMASK << CI_CICR3_LPF_SHIFT ); + value |= (timing->BFW & CI_CICR3_BFW_SMASK) << CI_CICR3_BFW_SHIFT; + value |= (lpf & CI_CICR3_LPF_SMASK) << CI_CICR3_LPF_SHIFT; + CICR3 = value; + return; +} + +void ci_configure_sp(unsigned int ppl, unsigned int lpf, CI_SP_TIMING* timing) +{ + unsigned int value; + + // write ppl field in cicr1 + value = CICR1; + value &= ~(CI_CICR1_PPL_SMASK << CI_CICR1_PPL_SHIFT); + value |= (ppl & CI_CICR1_PPL_SMASK) << CI_CICR1_PPL_SHIFT; + CICR1 = value; + + // write cicr2 + value = CICR2; + value |= (timing->BLW & CI_CICR2_BLW_SMASK) << CI_CICR2_BLW_SHIFT; + value |= (timing->ELW & CI_CICR2_ELW_SMASK) << CI_CICR2_ELW_SHIFT; + value |= (timing->HSW & CI_CICR2_HSW_SMASK) << CI_CICR2_HSW_SHIFT; + value |= (timing->BFPW & CI_CICR2_BFPW_SMASK) << CI_CICR2_BFPW_SHIFT; + value |= (timing->FSW & CI_CICR2_FSW_SMASK) << CI_CICR2_FSW_SHIFT; + CICR2 = value; + + // write cicr3 + value = CICR3; + value |= (timing->BFW & CI_CICR3_BFW_SMASK) << CI_CICR3_BFW_SHIFT; + value |= (timing->EFW & CI_CICR3_EFW_SMASK) << CI_CICR3_EFW_SHIFT; + value |= (timing->VSW & CI_CICR3_VSW_SMASK) << CI_CICR3_VSW_SHIFT; + value |= (lpf & CI_CICR3_LPF_SMASK) << CI_CICR3_LPF_SHIFT; + CICR3 = value; + return; +} + +void ci_configure_ms(unsigned int ppl, unsigned int lpf, CI_MS_TIMING* timing) +{ + // the operation is same as Master-Parallel + ci_configure_mp(ppl, lpf, (CI_MP_TIMING*)timing); +} + +void ci_configure_ep(int parity_check) +{ + unsigned int value; + + // write parity_enable field in cicr0 + value = CICR0; + if (parity_check) { + value |= CI_CICR0_PAR_EN; + } + else { + value &= ~CI_CICR0_PAR_EN; + } + CICR0 = value; + return; +} + +void ci_configure_es(int parity_check) +{ + // the operationi is same as Embedded-Parallel + ci_configure_ep(parity_check); +} + +void ci_set_clock(unsigned int clk_regs_base, int pclk_enable, int mclk_enable, unsigned int mclk_khz) +{ + unsigned int ciclk = 0, value, div, cccr_l; + + // determine the LCLK frequency programmed into the CCCR. + cccr_l = (CCCR & 0x0000001F); + + if (cccr_l < 8) // L = [2 - 7] + ciclk = (13 * cccr_l) * 100; + else if (cccr_l < 17) // L = [8 - 16] + ciclk = ((13 * cccr_l) * 100) >> 1; + else if (cccr_l < 32) // L = [17 - 31] + ciclk = ((13 * cccr_l) * 100) >> 2; + + // want a divisor that gives us a clock rate as close to, but not more than the given mclk. + div = (ciclk / (2 * mclk_khz)); + div = div - 1; + + // write cicr4 + value = CICR4; + value &= ~(CI_CICR4_PCLK_EN | CI_CICR4_MCLK_EN | CI_CICR4_DIV_SMASK< 0 ) { + value = CISR; + if ( value & mask ) { + CISR = mask; + return 0; + } + mdelay(10); + } + + return -1; +} + +void ci_slave_capture_enable() +{ + unsigned int value; + + // write mask in cicr0 + value = CICR0; + value |= CI_CICR0_SL_CAP_EN; + CICR0 = value; + return; +} + +void ci_slave_capture_disable() +{ + unsigned int value; + + // write mask in cicr0 + value = CICR0; + value &= ~CI_CICR0_SL_CAP_EN; + CICR0 = value; + return; +} + +void pxa_ci_dma_irq_y(int channel, void *data, struct pt_regs *regs) +{ + int dcsr; + static int dma_repeated=0; + camera_context_t *cam_ctx = g_camera_context; + + dcsr = DCSR(channel); + DCSR(channel) = dcsr & ~DCSR_STOPIRQEN; + + if (still_image_mode == 1) { + if (task_waiting == 1) { + wake_up_interruptible (&camera_wait_q); + task_waiting = 0; + } + else { + still_image_rdy = 1; + } + } + else if (dma_repeated == 0 + && (cam_ctx->block_tail == ((cam_ctx->block_header + 2) % cam_ctx->block_number))) { + dma_repeated = 1; + pxa_dma_repeat(cam_ctx); + cam_ctx->block_header = (cam_ctx->block_header + 1) % cam_ctx->block_number; + } + else if (dma_repeated == 1 && + (cam_ctx->block_tail != ((cam_ctx->block_header + 1) % cam_ctx->block_number)) + && (cam_ctx->block_tail != ((cam_ctx->block_header + 2) % cam_ctx->block_number))) { + pxa_dma_continue(cam_ctx); + dma_repeated = 0; + } + else if (dma_repeated == 0) { + cam_ctx->block_header = (cam_ctx->block_header + 1) % cam_ctx->block_number; + } + + if (task_waiting == 1 && !(cam_ctx->block_header == cam_ctx->block_tail)) { + wake_up_interruptible (&camera_wait_q); + task_waiting = 0; + } + return; +} + +void pxa_ci_dma_irq_cb(int channel, void *data, struct pt_regs *regs) +{ + return; +} + +void pxa_ci_dma_irq_cr(int channel, void *data, struct pt_regs *regs) +{ + return; +} + +inline static void pxa_ci_dma_stop(camera_context_t *cam_ctx) +{ + int ch0, ch1, ch2; + + ch0 = cam_ctx->dma_channels[0]; + ch1 = cam_ctx->dma_channels[1]; + ch2 = cam_ctx->dma_channels[2]; + DCSR(ch0) &= ~DCSR_RUN; + DCSR(ch1) &= ~DCSR_RUN; + DCSR(ch2) &= ~DCSR_RUN; +} + + +void pxa_dma_start(camera_context_t *cam_ctx) +{ + unsigned char cnt_blk; + pxa_dma_desc *cnt_desc; + + cam_ctx->block_header = (cam_ctx->block_header + 1) % cam_ctx->block_number; + cnt_blk = (unsigned char)cam_ctx->block_header; + + cnt_desc = (pxa_dma_desc *)cam_ctx->fifo0_descriptors_physical + cnt_blk * cam_ctx->fifo0_num_descriptors; + + DDADR(cam_ctx->dma_channels[0]) = (int) cnt_desc; + DCSR(cam_ctx->dma_channels[0]) |= DCSR_RUN; + + if (cam_ctx->fifo1_num_descriptors) { + cnt_desc = (pxa_dma_desc *)cam_ctx->fifo1_descriptors_physical + cnt_blk * cam_ctx->fifo1_num_descriptors; + DDADR(cam_ctx->dma_channels[1]) = (int) cnt_desc; + DCSR(cam_ctx->dma_channels[1]) |= DCSR_RUN; + } + + if (cam_ctx->fifo2_num_descriptors) { + cnt_desc = (pxa_dma_desc *)cam_ctx->fifo2_descriptors_physical + cnt_blk * cam_ctx->fifo2_num_descriptors; + DDADR(cam_ctx->dma_channels[2]) = (int) cnt_desc; + DCSR(cam_ctx->dma_channels[2]) |= DCSR_RUN; + } + + return; +} + + +irqreturn_t pxa_camera_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + int cisr; + static int dma_started=0; + + disable_irq(IRQ_CAMERA); + cisr = CISR; + if (cisr & CI_CISR_SOF) { + if (dma_started == 0) { + dma_started = 1; + } + CISR |= CI_CISR_SOF; + } + if (cisr & CI_CISR_EOF) { + CISR |= CI_CISR_EOF; + } + + enable_irq(IRQ_CAMERA); + return IRQ_HANDLED; +} + +void pxa_dma_repeat(camera_context_t *cam_ctx) +{ + pxa_dma_desc *cnt_head, *cnt_tail; + int cnt_block; + + cnt_block = (cam_ctx->block_header + 1) % cam_ctx->block_number; +// FIFO0 + (pxa_dma_desc *)cnt_head = (pxa_dma_desc *)cam_ctx->fifo0_descriptors_virtual + cnt_block * cam_ctx->fifo0_num_descriptors; + cnt_tail = cnt_head + cam_ctx->fifo0_num_descriptors - 1; + cnt_tail->ddadr = cnt_head->ddadr - sizeof(pxa_dma_desc); +// FIFO1 + if (cam_ctx->fifo1_transfer_size) { + cnt_head = (pxa_dma_desc *)cam_ctx->fifo1_descriptors_virtual + cnt_block * cam_ctx->fifo1_num_descriptors; + cnt_tail = cnt_head + cam_ctx->fifo1_num_descriptors - 1; + cnt_tail->ddadr = cnt_head->ddadr - sizeof(pxa_dma_desc); + } +// FIFO2 + if (cam_ctx->fifo2_transfer_size) { + cnt_head = (pxa_dma_desc *)cam_ctx->fifo2_descriptors_virtual + cnt_block * cam_ctx->fifo2_num_descriptors; + cnt_tail = cnt_head + cam_ctx->fifo2_num_descriptors - 1; + cnt_tail->ddadr = cnt_head->ddadr - sizeof(pxa_dma_desc); + } + return; +} + +void pxa_dma_continue(camera_context_t *cam_ctx) +{ + pxa_dma_desc *cnt_head, *cnt_tail; + pxa_dma_desc *next_head; + int cnt_block, next_block; + + cnt_block = cam_ctx->block_header; + next_block = (cnt_block + 1) % cam_ctx->block_number; +// FIFO0 + cnt_head = (pxa_dma_desc *)cam_ctx->fifo0_descriptors_virtual + cnt_block * cam_ctx->fifo0_num_descriptors; + cnt_tail = cnt_head + cam_ctx->fifo0_num_descriptors - 1; + next_head = (pxa_dma_desc *)cam_ctx->fifo0_descriptors_virtual + next_block * cam_ctx->fifo0_num_descriptors; + cnt_tail->ddadr = next_head->ddadr - sizeof(pxa_dma_desc); +// FIFO1 + if (cam_ctx->fifo1_transfer_size) { + cnt_head = (pxa_dma_desc *)cam_ctx->fifo1_descriptors_virtual + cnt_block * cam_ctx->fifo1_num_descriptors; + cnt_tail = cnt_head + cam_ctx->fifo1_num_descriptors - 1; + next_head = (pxa_dma_desc *)cam_ctx->fifo1_descriptors_virtual + next_block * cam_ctx->fifo1_num_descriptors; + cnt_tail->ddadr = next_head->ddadr - sizeof(pxa_dma_desc); + } +// FIFO2 + if (cam_ctx->fifo2_transfer_size) { + cnt_head = (pxa_dma_desc *)cam_ctx->fifo2_descriptors_virtual + cnt_block * cam_ctx->fifo2_num_descriptors; + cnt_tail = cnt_head + cam_ctx->fifo2_num_descriptors - 1; + next_head = (pxa_dma_desc *)cam_ctx->fifo2_descriptors_virtual + next_block * cam_ctx->fifo2_num_descriptors; + cnt_tail->ddadr = next_head->ddadr - sizeof(pxa_dma_desc); + } + return; +} + +module_init(pxa_camera_init); +module_exit(pxa_camera_exit); + +MODULE_DESCRIPTION("Bulverde Camera Interface driver"); +MODULE_LICENSE("GPL"); diff -uNr a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c --- a/drivers/mtd/chips/cfi_cmdset_0001.c 2004-10-19 05:53:45.000000000 +0800 +++ b/drivers/mtd/chips/cfi_cmdset_0001.c 2005-01-27 09:56:48.000000000 +0800 @@ -365,8 +365,13 @@ * The L18 flash memory array is divided * into multiple 8-Mbit partitions. */ - numparts = 1 << (cfi->cfiq->DevSize - 20); - partshift = 20 + __ffs(cfi->interleave); + + /* + * FIXME: Use 16-Mbit partitions for 2x32Mb flash; won't work + * for L18 with 8-Mbit partitions + */ + numparts = 1 << (cfi->cfiq->DevSize - 21); + partshift = 21 + __ffs(cfi->interleave); numvirtchips = cfi->numchips * numparts; newcfi = kmalloc(sizeof(struct cfi_private) + numvirtchips * sizeof(struct flchip), GFP_KERNEL); @@ -1754,14 +1759,12 @@ case FL_STATUS: case FL_CFI_QUERY: case FL_JEDEC_QUERY: - if (chip->oldstate == FL_READY) { - chip->oldstate = chip->state; - chip->state = FL_PM_SUSPENDED; - /* No need to wake_up() on this state change - - * as the whole point is that nobody can do anything - * with the chip now anyway. - */ - } + chip->oldstate = chip->state; + chip->state = FL_PM_SUSPENDED; + /* No need to wake_up() on this state change - + * as the whole point is that nobody can do anything + * with the chip now anyway. + */ break; default: ret = -EAGAIN; diff -uNr a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig --- a/drivers/mtd/maps/Kconfig 2004-10-19 05:55:28.000000000 +0800 +++ b/drivers/mtd/maps/Kconfig 2005-01-27 09:56:48.000000000 +0800 @@ -595,5 +595,12 @@ help Map driver for Dy-4 SVME/DMV-182 board. +config MTD_PXA27x + tristate "CFI Flash device mapped on Intel PXA27x-based boards" + depends on PXA27x && MTD_CFI_INTELEXT && MTD_PARTITIONS + help + This provides a driver for the on-chip flash of the Intel + PXA27x-based boards. + endmenu diff -uNr a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile --- a/drivers/mtd/maps/Makefile 2004-10-19 05:53:05.000000000 +0800 +++ b/drivers/mtd/maps/Makefile 2005-01-27 09:56:48.000000000 +0800 @@ -65,3 +65,4 @@ obj-$(CONFIG_MTD_IXP2000) += ixp2000.o obj-$(CONFIG_MTD_WRSBC8260) += wr_sbc82xx_flash.o obj-$(CONFIG_MTD_DMV182) += dmv182.o +obj-$(CONFIG_MTD_PXA27x) += pxa27x-flash.o diff -uNr a/drivers/mtd/maps/pxa27x-flash.c b/drivers/mtd/maps/pxa27x-flash.c --- a/drivers/mtd/maps/pxa27x-flash.c 1970-01-01 08:00:00.000000000 +0800 +++ b/drivers/mtd/maps/pxa27x-flash.c 2005-01-27 09:56:48.000000000 +0800 @@ -0,0 +1,230 @@ +/* + * Map driver for the PXA27x-based developer platform. + * + * Modified from linux/drivers/mtd/maps/lubbock-flash.c + * + * Copyright (C) 2004, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define WINDOW_SIZE 64*1024*1024 + +struct pxa27x_flash_info { + struct mtd_info *mtd; + struct mtd_partition * part; + struct map_info *map; + int nparts; +}; + +static void pxa27x_map_inval_cache(struct map_info *map, unsigned long from, ssize_t len) +{ + consistent_sync((char *)map->cached + from, len, DMA_FROM_DEVICE); +} + +static struct map_info pxa27x_map = { + .size = WINDOW_SIZE, + .phys = 0x00000000, + .inval_cache = pxa27x_map_inval_cache, +}; + +static struct mtd_partition pxa27x_partitions[] = { + { + .name = "Bootloader", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_WRITEABLE /* force read-only */ + },{ + .name = "Kernel", + .size = 0x00200000, + .offset = 0x00040000, + },{ + .name = "Filesystem", + .size = 0x01000000, + .offset = 0x00240000 + } +}; + +static struct mtd_info *mymtd; +static struct mtd_partition *parsed_parts; +static int nr_parsed_part; + +static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; + +static int pxa27x_flash_probe(struct device *dev) +{ + int ret = 0, i; + int err = 0; + struct pxa27x_flash_info *info; + + info = kmalloc(sizeof(struct pxa27x_flash_info), GFP_KERNEL); + if(!info) { + err = -ENOMEM; + goto Error; + } + memzero(info, sizeof(struct pxa27x_flash_info)); + + dev_set_drvdata(dev, info); + + pxa27x_map.bankwidth = (BOOT_DEF & 1) ? 2 : 4; + + pxa27x_map.name = "Intel PXA27x On-Chip ROM"; + + pxa27x_map.virt = (unsigned long)ioremap(pxa27x_map.phys, WINDOW_SIZE); + if (!pxa27x_map.virt) { + printk(KERN_WARNING "Failed to ioremap %s\n", pxa27x_map.name); + err = -ENOMEM; + goto Error; + } + pxa27x_map.cached = __ioremap(pxa27x_map.phys, WINDOW_SIZE, L_PTE_CACHEABLE, 1); + if (!pxa27x_map.cached) + printk(KERN_WARNING "Failed to ioremap cached %s\n", pxa27x_map.name); + + simple_map_init(&pxa27x_map); + + printk(KERN_NOTICE "Probing %s at physical address 0x%08lx (%d-bit bankwidth)\n", + pxa27x_map.name, pxa27x_map.phys, pxa27x_map.bankwidth * 8); + + mymtd = do_map_probe("cfi_probe", &pxa27x_map); + + if (!mymtd) { + iounmap((void *)pxa27x_map.virt); + if (pxa27x_map.cached) + iounmap(pxa27x_map.cached); + err = -EIO; + goto Error; + } + + /* Unlock the flash device. */ + for (i = 0; i < mymtd->numeraseregions; i++) { + int j; + for( j = 0; j < mymtd->eraseregions[i].numblocks; j++) { + mymtd->unlock(mymtd, mymtd->eraseregions[i].offset + + j * mymtd->eraseregions[i].erasesize, + mymtd->eraseregions[i].erasesize); + } + } + + mymtd->owner = THIS_MODULE; + + ret = parse_mtd_partitions(mymtd, probes, &parsed_parts, 0); + + if (ret > 0) nr_parsed_part = ret; + + if (nr_parsed_part) { + add_mtd_partitions(mymtd, parsed_parts, nr_parsed_part); + info->part = parsed_parts; + info->nparts = nr_parsed_part; + } else { + printk("Using static partitions on %s\n", pxa27x_map.name); + add_mtd_partitions(mymtd, pxa27x_partitions, ARRAY_SIZE(pxa27x_partitions)); + info->nparts = ARRAY_SIZE(pxa27x_partitions); + info->part = pxa27x_partitions; + } + info->mtd = mymtd; + return 0; +Error: + if (info) + kfree(info); + return err; +} + +static int pxa27x_flash_remove(struct device *dev) +{ + struct pxa27x_flash_info *info = dev_get_drvdata(dev); + + dev_set_drvdata(dev, NULL); + + if (!mymtd) return 0; + + del_mtd_partitions(mymtd); + map_destroy(mymtd); + + iounmap((void *)pxa27x_map.virt); + + if (pxa27x_map.cached) iounmap(pxa27x_map.cached); + + if (parsed_parts) kfree(parsed_parts); + if (info) kfree(info); + return 0; +} + +static int pxa27x_flash_suspend(struct device *dev, u32 state, u32 level) +{ + struct pxa27x_flash_info *info = dev_get_drvdata(dev); + struct mtd_info *mtd = info->mtd; + int ret = 0; + + if (!mtd) + return 0; + if (level == SUSPEND_POWER_DOWN) { + if (mtd->suspend) + ret = mtd->suspend(mtd); + } + return ret; +} + +static int pxa27x_flash_resume(struct device *dev, u32 level) +{ + struct pxa27x_flash_info *info = dev_get_drvdata(dev); + struct mtd_info *mtd = info->mtd; + int i, j; + + if (!mtd) + return 0; + if (level == RESUME_POWER_ON) { + if (mtd->resume) + mtd->resume(mtd); + for (i = 0; i < mtd->numeraseregions; i++) { + for (j = 0; j < mtd->eraseregions[i].numblocks; j++) { + mtd->unlock(mtd, mtd->eraseregions[i].offset + + j * mtd->eraseregions[i].erasesize, + mtd->eraseregions[i].erasesize); + } + } + } + + return 0; +} + +static struct device_driver pxa27x_flash_driver = { + .name = "pxa27x-flash", + .bus = &platform_bus_type, + .probe = pxa27x_flash_probe, + .remove = pxa27x_flash_remove, + .suspend = pxa27x_flash_suspend, + .resume = pxa27x_flash_resume, +}; + +static void __exit cleanup_pxa27x(void) +{ + driver_unregister(&pxa27x_flash_driver); +} +static int __init init_pxa27x(void) +{ + return driver_register(&pxa27x_flash_driver); +} + +module_init(init_pxa27x); +module_exit(cleanup_pxa27x); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yu Tang "); +MODULE_DESCRIPTION("MTD map driver for Intel PXA27x On-Chip ROM"); diff -uNr a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c --- a/drivers/mtd/mtd_blkdevs.c 2004-10-19 05:53:06.000000000 +0800 +++ b/drivers/mtd/mtd_blkdevs.c 2005-01-27 09:56:48.000000000 +0800 @@ -81,7 +81,7 @@ struct request_queue *rq = tr->blkcore_priv->rq; /* we might get involved when memory gets low, so use PF_MEMALLOC */ - current->flags |= PF_MEMALLOC; + current->flags |= PF_MEMALLOC | PF_NOFREEZE; daemonize("%sd", tr->name); diff -uNr a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig --- a/drivers/net/irda/Kconfig 2004-10-19 05:54:40.000000000 +0800 +++ b/drivers/net/irda/Kconfig 2005-01-27 09:56:49.000000000 +0800 @@ -383,6 +383,12 @@ tristate "SA1100 Internal IR" depends on ARCH_SA1100 && IRDA +config PXA_IRDA + tristate "PXA IrDA" + depends on (PXA27x || ARCH_PXA) && IRDA + help + Say Y if you want to support IrDA driver of PXA + config VIA_FIR tristate "VIA VT8231/VT1211 SIR/MIR/FIR" depends on IRDA && ISA && PCI diff -uNr a/drivers/net/irda/Makefile b/drivers/net/irda/Makefile --- a/drivers/net/irda/Makefile 2004-10-19 05:54:07.000000000 +0800 +++ b/drivers/net/irda/Makefile 2005-01-27 09:56:49.000000000 +0800 @@ -18,6 +18,7 @@ obj-$(CONFIG_ALI_FIR) += ali-ircc.o obj-$(CONFIG_VLSI_FIR) += vlsi_ir.o obj-$(CONFIG_VIA_FIR) += via-ircc.o +obj-$(CONFIG_PXA_IRDA) += pxa_ir.o # Old dongle drivers for old SIR drivers obj-$(CONFIG_ESI_DONGLE_OLD) += esi.o obj-$(CONFIG_TEKRAM_DONGLE_OLD) += tekram.o diff -uNr a/drivers/net/irda/pxa_ir.c b/drivers/net/irda/pxa_ir.c --- a/drivers/net/irda/pxa_ir.c 1970-01-01 08:00:00.000000000 +0800 +++ b/drivers/net/irda/pxa_ir.c 2005-01-27 09:56:49.000000000 +0800 @@ -0,0 +1,1213 @@ +/* + * linux/drivers/net/irda/pxa_ir.c + * + * Copyright (C) 2004 Intel Corporation + * + * 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. + * + * Infra-red SIR and FIR driver for the PXA 210/250 embedded microprocessors + * Based on linux/drivers/net/irda/sa1100_ir.c + * 06/30/2002 add support to PXA2XX/Lubbock by Hao Wu (hao.h.wu@intel.com) + * 07/06/2003 add support to Bulverde/Mainstone by Mingzhi Lai (mingzhi.lai@intel.com) + * 09/10/2004 add support to kernel 2.6 by Fengwei Yin (fengwei.yin@intel.com) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +#ifdef CONFIG_MACH_MAINSTONE +/* If you use Bulverde, you can unremark following definition to enable LED for debug purpose */ +/* #define DEBUG_LED_ON */ +#define DEBUG_LED_ON +#define LUB_MISC_WR_IRDA_MODE (1<<4) +#endif /* */ + +#define IrSR_RXPL_NEG_IS_ZERO (1<<4) +#define IrSR_RXPL_POS_IS_ZERO 0x0 +#define IrSR_TXPL_NEG_IS_ZERO (1<<3) +#define IrSR_TXPL_POS_IS_ZERO 0x0 +#define IrSR_XMODE_PULSE_1_6 (1<<2) +#define IrSR_XMODE_PULSE_3_16 0x0 +#define IrSR_RCVEIR_IR_MODE (1<<1) +#define IrSR_RCVEIR_UART_MODE 0x0 +#define IrSR_XMITIR_IR_MODE (1<<0) +#define IrSR_XMITIR_UART_MODE 0x0 + +#define IrSR_IR_RECEIVE_ON (\ + IrSR_RXPL_NEG_IS_ZERO | \ + IrSR_TXPL_POS_IS_ZERO | \ + IrSR_XMODE_PULSE_3_16 | \ + IrSR_RCVEIR_IR_MODE | \ + IrSR_XMITIR_UART_MODE) + +#define IrSR_IR_TRANSMIT_ON (\ + IrSR_RXPL_NEG_IS_ZERO | \ + IrSR_TXPL_POS_IS_ZERO | \ + IrSR_XMODE_PULSE_3_16 | \ + IrSR_RCVEIR_UART_MODE | \ + IrSR_XMITIR_IR_MODE) + +#define IS_FIR(si) ((si)->speed >= 4000000) +#define IRDA_FRAME_SIZE_LIMIT 2047 + +#define DEBUG_LED_RX D21 +#define DEBUG_LED_TX D22 + +#define BOARD_IRDA_SIR 0 +#define BOARD_IRDA_FIR 1 + +#define IRDA_DMA_ALIGN 8 +#define MEMRES_LENGTH 0xFFFF + +//#define DEBUG +#undef DEBUG +#ifdef DEBUG +#define dbg(format, arg...) printk("<1>" __FILE__ ":" format "\n",##arg) +#else /* */ +#define dbg(format, arg...) +#endif /* */ + + +struct pxa_irda { + struct device *dev; + struct net_device *netdev; + unsigned char open; + int speed; + int newspeed; + int mtt; + dma_addr_t dma_rx_buff_phy; + dma_addr_t dma_tx_buff_phy; + unsigned int dma_tx_buff_len; + int txdma; + int rxdma; + struct sk_buff *rxskb; + struct sk_buff *txskb; + struct net_device_stats stats; + struct irlap_cb *irlap; + struct qos_info qos; + iobuff_t tx_buff; + iobuff_t rx_buff; +}; + + +/* + * Allocate and map the receive buffer, unless it is already allocated. + */ +static int pxa_irda_rx_alloc(struct pxa_irda *si) +{ + if (si->rxskb) + return 0; + + si->rxskb = alloc_skb(IRDA_FRAME_SIZE_LIMIT + IRDA_DMA_ALIGN, GFP_ATOMIC); + if (!si->rxskb) { + printk(KERN_ERR "sa1100_ir: out of memory for RX SKB\n"); + return -ENOMEM; + } + + /* + * Align any IP headers that may be contained + * within the frame. + * In Bulverde,It must be align on 8 bytes -- by Fengwei Yin. + */ + + skb_reserve(si->rxskb, (IRDA_DMA_ALIGN - ((ulong) si->rxskb->data & IRDA_DMA_ALIGN))); + si->dma_rx_buff_phy = dma_map_single(si->dev, si->rxskb->data, IRDA_FRAME_SIZE_LIMIT, DMA_FROM_DEVICE); + return 0; +} + +static inline void board_irda_select(int irda_mode) +{ + if (machine_is_lubbock()) { + if (irda_mode == BOARD_IRDA_SIR) { + + /* select SIR on LUBBOCK */ + LUB_MISC_WR &= ~LUB_MISC_WR_IRDA_MODE; + } else { + + /* select FIR on LUBBOCK */ + LUB_MISC_WR |= LUB_MISC_WR_IRDA_MODE; + } + } + + if (machine_is_mainstone()) { + if (irda_mode == BOARD_IRDA_SIR) { + + /* select SIR on Mainstone */ + MST_MSCWR1 &= ~(1 << 4); + } else { + + /* select FIR on Mainstone */ + MST_MSCWR1 |= (1 << 4); + } + } +} + +static void empty_rx_FIFO(void) +{ + volatile unsigned data; + + while (ICSR1 & ICSR1_RNE) { + data = ICDR; + } +} + +static inline void pxa_irda_fir_dma_rx_start(struct pxa_irda *si) +{ + unsigned long flags; + + if (!si->rxskb) { + printk(KERN_ERR "pxa_ir: rx buffer went missing!\n"); + return; + } + + /* + * First empty receive FIFO + */ + + empty_rx_FIFO(); + local_irq_save(flags); + DCSR(si->rxdma) = DCSR_NODESC; + DSADR(si->rxdma) = __PREG(ICDR); + DTADR(si->rxdma) = si->dma_rx_buff_phy; + DCMD(si->rxdma) = DCMD_INCTRGADDR | DCMD_FLOWSRC | DCMD_WIDTH1 | DCMD_BURST32 | IRDA_FRAME_SIZE_LIMIT; + DCSR(si->rxdma) |= DCSR_RUN; + local_irq_restore(flags); +} + +static inline void pxa_irda_fir_dma_tx_start(struct pxa_irda *si) +{ + unsigned long flags; + + local_irq_save(flags); + DCSR(si->txdma) = DCSR_NODESC; + DSADR(si->txdma) = si->dma_tx_buff_phy; + DTADR(si->txdma) = __PREG(ICDR); + DCMD(si->txdma) = DCMD_INCSRCADDR | DCMD_FLOWTRG | DCMD_ENDIRQEN | DCMD_WIDTH1 | DCMD_BURST32 | si->txskb->len; + DCSR(si->txdma) |= DCSR_RUN; + local_irq_restore(flags); +} + +/* + * Set the IrDA communications speed. + */ +static int pxa_irda_set_speed(struct pxa_irda *si, int speed) +{ + unsigned long flags; + int divisor, ret = -EINVAL; + +#ifdef DEBUG_LED_ON + if (speed == 9600) + MST_LEDDAT1 = 0x9600; + else if (speed == 19200) + MST_LEDDAT1 = 0x19200; + else if (speed == 38400) + MST_LEDDAT1 = 0x38400; + else if (speed == 57600) + MST_LEDDAT1 = 0x57600; + else if (speed == 115200) + MST_LEDDAT1 = 0x115200; + else if (speed == 4000000) + MST_LEDDAT1 = 0x4000000; +#endif /* */ + + switch (speed) { + case 9600: + case 19200: + case 38400: + case 57600: + case 115200: + + /* refer to Bulverde Developer's Manual 10-7 */ + /* BaudRate = 14.7456 MHz / (16*Divisor) */ + divisor = 14745600 / (16 * speed); + local_irq_save(flags); + if (IS_FIR(si)) { + + /* stop RX DMA */ + DCSR(si->rxdma) &= ~DCSR_RUN; + + /* disable FICP */ + ICCR0 &= ~ICCR0_ITR; + CKEN &= ~CKEN13_FICP; + board_irda_select(BOARD_IRDA_SIR); + + /* configure GPIO46/47 */ + pxa_gpio_mode(GPIO46_STRXD_MD); + pxa_gpio_mode(GPIO47_STTXD_MD); + + /* enable the STUART clock */ + CKEN |= CKEN5_STUART; + } + + /* disable STUART first */ + STIER &= ~IER_UUE; + + /* access DLL & DLH */ + STLCR |= LCR_DLAB; + STDLL = divisor; + STDLH = 0; + + /* restore to access THR, RBR & IER */ + STLCR &= ~LCR_DLAB; + si->speed = speed; + STISR = IrSR_IR_RECEIVE_ON | IrSR_XMODE_PULSE_1_6; + STIER = IER_UUE | IER_RLSE | IER_RAVIE | IER_RTIOE; + local_irq_restore(flags); + ret = 0; + break; + case 4000000: + local_irq_save(flags); + + /* disable STUART */ + STIER = 0; + STISR = 0; + CKEN &= ~CKEN5_STUART; + + /* disable FICP first */ + ICCR0 &= ~ICCR0_ITR; + board_irda_select(BOARD_IRDA_FIR); + + /* configure GPIO46/47 */ + pxa_gpio_mode(GPIO46_ICPRXD_MD); + pxa_gpio_mode(GPIO47_ICPTXD_MD); + + /* enable the FICP clock */ + CKEN |= CKEN13_FICP; + si->speed = speed; + pxa_irda_rx_alloc(si); + pxa_irda_fir_dma_rx_start(si); + ICCR0 = ICCR0_ITR | ICCR0_RXE; + local_irq_restore(flags); + ret = 0; + break; + + default: + break; + } + return ret; +} + + +/* SIR interrupt service routine. */ +static irqreturn_t pxa_irda_sir_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = dev_id; + struct pxa_irda *si = dev->priv; + volatile unsigned long iir, lsr, data; + + iir = STIIR; + switch (iir & 0x0F) { + case 0x06: + + /* Receiver Line Status */ + lsr = STLSR; + while (lsr & LSR_FIFOE) { + data = STRBR; + if (lsr & (LSR_OE | LSR_PE | LSR_FE | LSR_BI)) { + printk(KERN_DEBUG "pxa_ir: sir receiving error\n"); + si->stats.rx_errors++; + if (lsr & LSR_FE) + si->stats.rx_frame_errors++; + if (lsr & LSR_OE) + si->stats.rx_fifo_errors++; + } else { + async_unwrap_char(dev, &si->stats, &si->rx_buff, data); + } + lsr = STLSR; + } + break; + case 0x04: + + /* Received Data Available */ + /* forth through */ + case 0x0C: + + /* Character Timeout Indication */ + do { + async_unwrap_char(dev, &si->stats, &si->rx_buff, STRBR); + } while (STLSR & LSR_DR); + dev->last_rx = jiffies; + break; + case 0x02: + + /* Transmit FIFO Data Request */ + while ((si->tx_buff.len) && (STLSR & LSR_TDRQ)) { + STTHR = *si->tx_buff.data++; + si->tx_buff.len -= 1; + } + if (si->tx_buff.len == 0) { + si->stats.tx_packets++; + si->stats.tx_bytes += si->tx_buff.data - si->tx_buff.head; + + /* + * We need to ensure that the transmitter has + * finished. + */ + while ((STLSR & LSR_TEMT) == 0) { + rmb(); + } + + /* + * Ok, we've finished transmitting. Now enable + * the receiver. Sometimes we get a receive IRQ + * immediately after a transmit... + */ +#ifdef DEBUG_LED_ON + DISCRETE_LED_ON(DEBUG_LED_RX); + DISCRETE_LED_OFF(DEBUG_LED_TX); + +#endif /* */ + if (si->newspeed) { + pxa_irda_set_speed(si, si->newspeed); + si->newspeed = 0; + } else { + + /* enable IR Receiver, disable IR Transmitter */ + STISR = IrSR_IR_RECEIVE_ON | IrSR_XMODE_PULSE_1_6; + + /* enable STUART and receive interrupts */ + STIER = IER_UUE | IER_RLSE | IER_RAVIE | IER_RTIOE; + } + + /* I'm hungry! */ + netif_wake_queue(dev); + } + break; + case 0: + + /* Modem Status */ + break; + + default: + break; + } + return IRQ_HANDLED; +} + + +/* FIR Receive DMA interrupt handler */ +static void pxa_irda_fir_dma_rx_irq(int channel, void *data, struct pt_regs *regs) +{ + int dcsr = DCSR(channel); + + DCSR(channel) &= ~DCSR_RUN; + DCSR(channel) |= DCSR_STARTINTR | DCSR_ENDINTR | DCSR_BUSERR; + if (dcsr & DCSR_BUSERR) { + printk(KERN_DEBUG "pxa_ir: fir rx dma bus error.\n"); + } +} + + +/* FIR Transmit DMA interrupt handler */ +static void pxa_irda_fir_dma_tx_irq(int channel, void *data, struct pt_regs *regs) +{ + struct pxa_irda *si = data; + struct net_device *netdev = si->netdev; + struct sk_buff *skb = si->txskb; + int dcsr; + + dcsr = DCSR(channel); + DCSR(channel) &= ~DCSR_RUN; + if (dcsr & DCSR_ENDINTR) { + DCSR(channel) |= DCSR_ENDINTR; + si->txskb = NULL; + while (ICSR1 & ICSR1_TBY) { + rmb(); + } + +#ifdef DEBUG_LED_ON + DISCRETE_LED_ON(DEBUG_LED_RX); + DISCRETE_LED_OFF(DEBUG_LED_TX); + +#endif /* */ + /* minimal turn-around time delay */ + if (si->mtt) + udelay(si->mtt); + if (si->newspeed) { + pxa_irda_set_speed(si, si->newspeed); + si->newspeed = 0; + } else { + pxa_irda_fir_dma_rx_start(si); + ICCR0 = ICCR0_ITR | ICCR0_RXE; + } + if (skb) { + dma_unmap_single(si->dev, si->dma_tx_buff_phy, skb->len, DMA_TO_DEVICE); + si->stats.tx_packets++; + si->stats.tx_bytes += skb->len; + dev_kfree_skb(skb); + } + netif_wake_queue(netdev); + } +} + + +/* EIF(Error in FIFO/End in Frame) handler for FIR */ +static void pxa_irda_fir_irq_eif(struct pxa_irda *si, struct net_device *dev) +{ + unsigned int len; + volatile unsigned long stat, data; + struct sk_buff *skb = si->rxskb; + + if (!skb) { + printk(KERN_ERR "pxa_ir: SKB is NULL!\n"); + return; + } + + /* Get the current data position. */ + len = DTADR(si->rxdma) - si->dma_rx_buff_phy; + + if (len > IRDA_FRAME_SIZE_LIMIT) + len = IRDA_FRAME_SIZE_LIMIT; + + dma_unmap_single(si->dev, si->dma_rx_buff_phy, len, DMA_FROM_DEVICE); + + do { + /* Read Status, and then Data. */ + stat = ICSR1; + rmb(); + data = ICDR; + if (stat & (ICSR1_CRE | ICSR1_ROR)) { + si->stats.rx_errors++; + if (stat & ICSR1_CRE) { + printk(KERN_DEBUG "pxa_ir: fir receive CRC error\n"); + si->stats.rx_crc_errors++; + } + if (stat & ICSR1_ROR) { + printk(KERN_DEBUG "pxa_ir: fir receive overrun\n"); + si->stats.rx_frame_errors++; + } + } else { + skb->data[len++] = data; + } + + /* If we hit the end of frame, there's no point in continuing. */ + if (stat & ICSR1_EOF) { + break; + } + } while (ICSR0 & ICSR0_EIF); + + if (stat & ICSR1_EOF) { + + /* end of frame. */ + si->rxskb = NULL; + skb_put(skb, len); + + /* Feed it to IrLAP */ + skb->dev = dev; + skb->mac.raw = skb->data; + skb->protocol = htons(ETH_P_IRDA); + netif_rx(skb); + si->stats.rx_packets++; + si->stats.rx_bytes += len; + pxa_irda_rx_alloc(si); + dev->last_rx = jiffies; + } +} + +#ifdef CONFIG_PXA27x_E23 +static void read_E23_data(struct pxa_irda *si) +{ + volatile unsigned long data; + unsigned int len; + + /* Get the current data position. */ + len = DTADR(si->rxdma) - si->dma_rx_buff_phy; + + if (len > IRDA_FRAME_SIZE_LIMIT) + len = IRDA_FRAME_SIZE_LIMIT; + + dma_unmap_single(si->dev, si->dma_rx_buff_phy, len, DMA_FROM_DEVICE); + + while ( !(ICSR1 & ICSR1_EOF)) + data = ICDR; +} +#endif + +/* FIR interrupt handler */ +static irqreturn_t pxa_irda_fir_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = dev_id; + struct pxa_irda *si = dev->priv; + volatile unsigned long icsr0; + + /* stop RX DMA */ + DCSR(si->rxdma) &= ~DCSR_RUN; + icsr0 = ICSR0; + + if (icsr0 & ICSR0_FRE) { + printk(KERN_DEBUG "pxa_ir: fir receive frame error\n"); + si->stats.rx_frame_errors++; + ICSR0 |= ICSR0_FRE; + } + + if (icsr0 & ICSR0_RAB) { + /* FIXME: PXA27x E23 */ +#ifdef CONFIG_PXA27x_E23 + if ( !(icsr0 & ICSR0_EIF)) { + read_E23_data(si); + ICSR0 |= ICSR0_RAB; + goto next_skb; + } else +#endif + { + printk(KERN_DEBUG "pxa_ir: fir receive abort\n"); + si->stats.rx_errors++; + ICSR0 |= ICSR0_RAB; + } + } + + if (icsr0 & ICSR0_EIF) { + /* An error in FIFO occues, or there is a end of frame */ + pxa_irda_fir_irq_eif(si, dev); + } + +next_skb: + pxa_irda_fir_dma_rx_start(si); + return IRQ_HANDLED; +} + + +/* hard_xmit interface of irda device */ +static int pxa_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct pxa_irda *si = dev->priv; + int speed = irda_get_next_speed(skb); + + /* + * Does this packet contain a request to change the interface + * speed? If so, remember it until we complete the transmission + * of this frame. + */ + if (speed != si->speed && speed != -1) + si->newspeed = speed; + + /* + * If this is an empty frame, we can bypass a lot. + */ + if (skb->len == 0) { + if (si->newspeed) { + si->newspeed = 0; + pxa_irda_set_speed(si, speed); + } + dev_kfree_skb(skb); + return 0; + } + + if (!IS_FIR(si)) { + netif_stop_queue(dev); + si->tx_buff.data = si->tx_buff.head; + si->tx_buff.len = async_wrap_skb(skb, si->tx_buff.data, si->tx_buff.truesize); + +#ifdef DEBUG_LED_ON + DISCRETE_LED_OFF(DEBUG_LED_RX); + DISCRETE_LED_ON(DEBUG_LED_TX); + +#endif /* */ + + /* disable IR Receiver, enable IR Transmitter */ + STISR = IrSR_IR_TRANSMIT_ON | IrSR_XMODE_PULSE_1_6; + + /* It is said when GPIO47 switch from normal mode to SIR mode, + we must force GPIO low to avoid false start bit. + However it seems also ok when skip it */ + /* GPCR1 |= 0x00008000; */ + /*GPCR(47) |= GPIO_bit(47); */ + + /* enable STUART and transmit interrupts */ + STIER = IER_UUE | IER_TIE; + + /* reset Transmitter FIFO to fire the interrupt */ + /* STFCR |= FCR_RESETTF; */ + /* but it doesn't alway work. it seems only when FIFO drops, + * the interrupt will occur. so we copy the codes + * in the interrupt handler here + */ + disable_irq(IRQ_STUART); + while ((si->tx_buff.len) && (!(STLSR & LSR_TEMT) == 0)) { + STTHR = *si->tx_buff.data++; + si->tx_buff.len -= 1; + }; + + enable_irq(IRQ_STUART); + if (si->tx_buff.len == 0) { + si->stats.tx_packets++; + si->stats.tx_bytes += si->tx_buff.data - si->tx_buff.head; + + /* + * We need to ensure that the transmitter has + * finished. + */ + while ((STLSR & LSR_TEMT) == 0) { + rmb(); + } + + /* + * Ok, we've finished transmitting. Now enable + * the receiver. Sometimes we get a receive IRQ + * immediately after a transmit... + */ +#ifdef DEBUG_LED_ON + DISCRETE_LED_ON(DEBUG_LED_RX); + DISCRETE_LED_OFF(DEBUG_LED_TX); + +#endif /* */ + if (si->newspeed) { + pxa_irda_set_speed(si, si->newspeed); + si->newspeed = 0; + } else { + + /* enable IR Receiver, disable IR Transmitter */ + STISR = IrSR_IR_RECEIVE_ON | IrSR_XMODE_PULSE_1_6; + + /* enable STUART and receive interrupts */ + STIER = IER_UUE | IER_RLSE | IER_RAVIE | IER_RTIOE; + } + + /* I'm hungry! */ + netif_wake_queue(dev); + } + dev_kfree_skb(skb); + } else { + si->mtt = irda_get_mtt(skb); + if (si->txskb) + BUG(); + netif_stop_queue(dev); + +#ifdef DEBUG_LED_ON + DISCRETE_LED_OFF(DEBUG_LED_RX); + DISCRETE_LED_ON(DEBUG_LED_TX); + +#endif /* */ + + si->txskb = skb; + si->dma_tx_buff_phy = dma_map_single(si->dev, skb->data, skb->len, DMA_TO_DEVICE); + pxa_irda_fir_dma_tx_start(si); + if (si->mtt) + udelay(si->mtt); + ICCR0 = ICCR0_ITR | ICCR0_TXE; + } + dev->trans_start = jiffies; + return 0; +} + + +static int pxa_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd) +{ + struct if_irda_req *rq = (struct if_irda_req *) ifreq; + struct pxa_irda *si = dev->priv; + int ret = -EOPNOTSUPP; + + switch (cmd) { + case SIOCSBANDWIDTH: + if (capable(CAP_NET_ADMIN)) { + + /* + * We are unable to set the speed if the + * device is not running. + */ + if (si->open) { + ret = pxa_irda_set_speed(si, rq->ifr_baudrate); + } else { + printk(KERN_INFO "pxa_ir: SIOCSBANDWIDTH: !netif_running\n"); + ret = 0; + } + } + break; + case SIOCSMEDIABUSY: + ret = -EPERM; + if (capable(CAP_NET_ADMIN)) { + irda_device_set_media_busy(dev, TRUE); + ret = 0; + } + break; + case SIOCGRECEIVING: + rq->ifr_receiving = IS_FIR(si) ? 0 : si->rx_buff.state != OUTSIDE_FRAME; + break; + default: + break; + } + return ret; +} + + +static struct net_device_stats *pxa_irda_stats(struct net_device *dev) +{ + struct pxa_irda *si = dev->priv; + return &si->stats; +} + + +static int pxa_irda_startup(struct pxa_irda *si) +{ + int ret; + + /* enable STUART interrupt to the processor */ + STMCR = MCR_OUT2; + + /* configure SIR frame format: StartBit - Data 7 ... Data 0 - Stop Bit */ + STLCR = LCR_WLS0 | LCR_WLS1; + + /* enable FIFO, we use FIFO to improve performance */ + STFCR = FCR_TRFIFOE | FCR_ITL_32; + + /* configure FICP ICCR2 */ + ICCR2 = ICCR2_TXP | ICCR2_TRIG_32; + + /* configure DMAC */ + DRCMR17 = si->rxdma | DRCMR_MAPVLD; + DRCMR18 = si->txdma | DRCMR_MAPVLD; + + /* we start from SIR 9600 baudrate */ + /* disable FICP */ + ICCR0 &= ~ICCR0_ITR; + CKEN &= ~CKEN13_FICP; + board_irda_select(BOARD_IRDA_SIR); + if (machine_is_mainstone()) + MST_MSCWR1 &= ~(3 << 4); + + /* configure GPIO46/47 */ + pxa_gpio_mode(GPIO46_STRXD_MD); + pxa_gpio_mode(GPIO47_STTXD_MD); + + /* enable the STUART clock */ + CKEN |= CKEN5_STUART; + ret = pxa_irda_set_speed(si, si->speed = 9600); + if (ret) + return ret; + +#ifdef DEBUG_LED_ON + DISCRETE_LED_ON(DEBUG_LED_RX); + DISCRETE_LED_OFF(DEBUG_LED_TX); + +#endif /* */ + printk(KERN_INFO "pxa_ir: irda startup\n"); + return 0; +} + +static void pxa_irda_shutdown(struct pxa_irda *si) +{ + + /* disable the STUART clock */ + CKEN &= ~CKEN5_STUART; + + /* disable STUART and interrupt */ + STIER = 0; + + /* disable STUART SIR mode */ + STISR = 0; + + /* disable the FICP clock */ + CKEN &= ~CKEN13_FICP; + + /* disable FICP */ + ICCR0 = 0; + +#ifdef DEBUG_LED_ON + DISCRETE_LED_OFF(DEBUG_LED_RX); + DISCRETE_LED_OFF(DEBUG_LED_TX); + +#endif /* */ + printk(KERN_INFO "pxa_ir: irda shutdown\n"); +} + +static int pxa_irda_start(struct net_device *dev) +{ + struct pxa_irda *si = dev->priv; + int err; + + si->speed = 9600; + err = request_irq(IRQ_STUART, pxa_irda_sir_irq, 0, dev->name, dev); + if (err) + goto err_irq1; + err = request_irq(IRQ_ICP, pxa_irda_fir_irq, 0, dev->name, dev); + if (err) + goto err_irq2; + + /* + * The interrupt must remain disabled for now. + */ + disable_irq(IRQ_STUART); + disable_irq(IRQ_ICP); + si->rxdma = pxa_request_dma("FICP_RX", DMA_PRIO_LOW, pxa_irda_fir_dma_rx_irq, si); + if (si->rxdma < 0) + goto err_rx_dma; + si->txdma = pxa_request_dma("FICP_TX", DMA_PRIO_LOW, pxa_irda_fir_dma_tx_irq, si); + if (si->txdma < 0) + goto err_tx_dma; + + /* + * Setup the serial port for the specified speed. + */ + err = pxa_irda_startup(si); + if (err) + goto err_startup; + + /* + * Open a new IrLAP layer instance. + */ + si->irlap = irlap_open(dev, &si->qos, "pxa"); + err = -ENOMEM; + if (!si->irlap) + goto err_irlap; + + /* + * Now enable the interrupt and start the queue + */ + si->open = 1; + enable_irq(IRQ_STUART); + enable_irq(IRQ_ICP); + netif_start_queue(dev); + printk(KERN_INFO "pxa_ir: irda driver opened\n"); + return 0; + +err_irlap: + si->open = 0; + pxa_irda_shutdown(si); +err_startup: + pxa_free_dma(si->txdma); +err_tx_dma: + pxa_free_dma(si->rxdma); +err_rx_dma: + free_irq(IRQ_ICP, dev); +err_irq2: + free_irq(IRQ_STUART, dev); +err_irq1: + return err; +} + +static int pxa_irda_stop(struct net_device *dev) +{ + struct pxa_irda *si = dev->priv; + + disable_irq(IRQ_STUART); + disable_irq(IRQ_ICP); + pxa_irda_shutdown(si); + + /* + * If we have been doing DMA receive, make sure we + * tidy that up cleanly. + */ + if (si->rxskb) { + dma_unmap_single(si->dev, si->dma_rx_buff_phy, IRDA_FRAME_SIZE_LIMIT, DMA_FROM_DEVICE); + dev_kfree_skb(si->rxskb); + si->rxskb = NULL; + } + + /* Stop IrLAP */ + if (si->irlap) { + irlap_close(si->irlap); + si->irlap = NULL; + } + netif_stop_queue(dev); + si->open = 0; + free_irq(IRQ_STUART, dev); + free_irq(IRQ_ICP, dev); + pxa_free_dma(si->rxdma); + pxa_free_dma(si->txdma); + printk(KERN_INFO "pxa_ir: irda driver closed\n"); + return 0; +} + + +#ifdef CONFIG_PM +/* + * Suspend the IrDA interface. + */ +static int pxa_irda_suspend(struct device *_dev, u32 state, u32 level) +{ + struct net_device *dev = dev_get_drvdata(_dev); + struct pxa_irda *si = dev->priv; + + if (!dev || level != SUSPEND_DISABLE) + return 0; + printk(KERN_INFO "pxa_ir: irda suspend\n"); + if (si && si->open) { + /* + * Stop the transmit queue + */ + netif_device_detach(dev); + disable_irq(IRQ_STUART); + disable_irq(IRQ_ICP); + pxa_irda_shutdown(si); + + /* add here: shutdown IR transceiver on the board */ + } + return 0; +} + + +/* + * Resume the IrDA interface. + */ +static int pxa_irda_resume(struct device *_dev, u32 level) +{ + struct net_device *dev = dev_get_drvdata(_dev); + struct pxa_irda *si = dev->priv; + + if (!dev || level != RESUME_ENABLE) + return 0; + printk(KERN_INFO "pxa_ir: irda resume\n"); + if (si && si->open) { + + /* + * If we missed a speed change, initialise at the new speed + * directly. It is debatable whether this is actually + * required, but in the interests of continuing from where + * we left off it is desireable. The converse argument is + * that we should re-negotiate at 9600 baud again. + */ + if (si->newspeed) { + si->speed = si->newspeed; + si->newspeed = 0; + } + + /* add here: restart IR transceiver on the board */ + pxa_irda_startup(si); + enable_irq(IRQ_STUART); + enable_irq(IRQ_ICP); + + /* + * This automatically wakes up the queue + */ + netif_device_attach(dev); + } + return 0; +} + + +#else /* */ +#define pxa_irda_suspend NULL +#define pxa_irda_resume NULL +#endif /* */ + +static int pxa_irda_init_iobuf(iobuff_t * io, int size) +{ + io->head = kmalloc(size, GFP_KERNEL | GFP_DMA); + if (io->head != NULL) { + io->truesize = size; + io->in_frame = FALSE; + io->state = OUTSIDE_FRAME; + io->data = io->head; + } + return io->head ? 0 : -ENOMEM; +} + +static int pxa_irda_net_init(struct net_device *dev) +{ + struct pxa_irda *si = dev->priv; + unsigned int baudrate_mask; + int err = -ENOMEM; + + /* + * Initialise the SIR buffers + */ + err = pxa_irda_init_iobuf(&si->rx_buff, 14384); + if (err) + goto out; + + err = pxa_irda_init_iobuf(&si->tx_buff, 4000); + if (err) + goto out_free_rx; + + /* + * Request the regions. + */ + err = request_mem_region(__PREG(FICP), MEMRES_LENGTH, "irda") ? 0 : -EBUSY; + if (err) + goto mem_out1; + + err = request_mem_region(__PREG(STUART), MEMRES_LENGTH, "irda") ? 0 : -EBUSY; + if (err) + goto mem_out2; + + dev->hard_start_xmit = pxa_irda_hard_xmit; + dev->open = pxa_irda_start; + dev->stop = pxa_irda_stop; + dev->do_ioctl = pxa_irda_ioctl; + dev->get_stats = pxa_irda_stats; + dev->irq = IRQ_ICP; + irda_init_max_qos_capabilies(&si->qos); + + /* + * We support original IRDA up to 115k2. (we don't currently + * support 4Mbps). Min Turn Time set to 1ms or greater. + */ + baudrate_mask = IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200; + baudrate_mask |= IR_4000000 << 8; + si->qos.baud_rate.bits &= baudrate_mask; + si->qos.min_turn_time.bits = 7; /* 1ms or more */ + irda_qos_bits_to_value(&si->qos); + printk(KERN_INFO "pxa_ir: irda driver inited\n"); + return 0; + +mem_out2: + release_mem_region(__PREG(FICP), MEMRES_LENGTH); +mem_out1: + kfree(si->tx_buff.head); +out_free_rx: + kfree(si->rx_buff.head); +out: + return err; +} + + +/* + * Remove all traces of this driver module from the kernel, so we can't be + * called. Note that the device has already been stopped, so we don't have + * to worry about interrupts or dma. + */ +static void pxa_irda_net_uninit(struct net_device *dev) +{ + struct pxa_irda *si = dev->priv; + + dev->hard_start_xmit = NULL; + dev->open = NULL; + dev->stop = NULL; + dev->do_ioctl = NULL; + dev->get_stats = NULL; + dev->priv = NULL; + + release_mem_region(__PREG(STUART), MEMRES_LENGTH); + release_mem_region(__PREG(FICP), MEMRES_LENGTH); + + kfree(si->tx_buff.head); + kfree(si->rx_buff.head); + printk(KERN_INFO "pxa_ir: irda driver uninited\n"); +} + +static int pxa_irda_probe(struct device *_dev) +{ + struct platform_device *pdev = to_platform_device(_dev); + struct net_device *dev; + struct pxa_irda *si; + int err; + + dev = alloc_irdadev(sizeof(struct pxa_irda)); + if (!dev) + return -ENOMEM; + + si = dev->priv; + si->dev = &pdev->dev; + si->netdev = dev; + dev->init = pxa_irda_net_init; + dev->uninit = pxa_irda_net_uninit; + + err = register_netdev(dev); + if (err == 0) + dev_set_drvdata(&pdev->dev, dev); + + else + free_netdev(dev); + + return err; +} + +static int pxa_irda_remove(struct device *_dev) +{ + struct net_device *dev = dev_get_drvdata(_dev); + + if (dev) { + unregister_netdev(dev); + free_netdev(dev); + } + return 0; +} + +static struct resource pxa_irda_resources[] = { + [0] = { // fast irda mem resource + .start = 0x40800000, + .end = 0x4080ffff, + .flags = IORESOURCE_MEM,}, + [1] = { // stuart mem resource + .start = 0x40700000, + .end = 0x4070ffff, + .flags = IORESOURCE_MEM,}, + [2] = { // fast irda irq resource + .start = IRQ_ICP, + .end = IRQ_ICP, + .flags = IORESOURCE_IRQ,}, + [3] = { // stuart irq resource + .start = IRQ_STUART, + .end = IRQ_STUART, + .flags = IORESOURCE_IRQ,}, +}; + +static void pxa_irda_dev_release(struct device *pdev) +{ + return; +} + +static u64 irda_dma_mask = ~(u64)0; + +static struct platform_device pxa_irda_device = { + .name = "pxa-irda", + .id = 0, + .resource = pxa_irda_resources, + .num_resources = ARRAY_SIZE(pxa_irda_resources), + .dev = { + .dma_mask = &irda_dma_mask, + .coherent_dma_mask = 0xffffffff, + .release = pxa_irda_dev_release, + }, +}; + +static struct device_driver pxa_irda_driver = { + .name = "pxa-irda", + .bus = &platform_bus_type, + .probe = pxa_irda_probe, + .remove = pxa_irda_remove, + .suspend = pxa_irda_suspend, + .resume = pxa_irda_resume, +}; + +static int __init pxa_irda_init(void) +{ + int ret; + + ret = platform_device_register(&pxa_irda_device); + if (!ret) { + ret = driver_register(&pxa_irda_driver); + if (ret) + platform_device_unregister(&pxa_irda_device); + } + + return ret; +} + +static void __exit pxa_irda_exit(void) +{ + driver_unregister(&pxa_irda_driver); + platform_device_unregister(&pxa_irda_device); +} + +module_init(pxa_irda_init); +module_exit(pxa_irda_exit); +MODULE_DESCRIPTION("Bulverde SIR/FIR driver"); +MODULE_LICENSE("GPL"); diff -uNr a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/pcmcia/3c574_cs.c --- a/drivers/net/pcmcia/3c574_cs.c 2004-10-19 05:53:43.000000000 +0800 +++ b/drivers/net/pcmcia/3c574_cs.c 2005-01-27 09:56:49.000000000 +0800 @@ -592,6 +592,7 @@ link->state |= DEV_SUSPEND; /* Fall through... */ case CS_EVENT_RESET_PHYSICAL: + disable_irq(dev->irq); if (link->state & DEV_CONFIG) { if (link->open) netif_device_detach(dev); @@ -602,6 +603,7 @@ link->state &= ~DEV_SUSPEND; /* Fall through... */ case CS_EVENT_CARD_RESET: + enable_irq(dev->irq); if (link->state & DEV_CONFIG) { pcmcia_request_configuration(link->handle, &link->conf); if (link->open) { diff -uNr a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c --- a/drivers/net/pcmcia/xirc2ps_cs.c 2004-10-19 05:53:51.000000000 +0800 +++ b/drivers/net/pcmcia/xirc2ps_cs.c 2005-01-27 09:56:49.000000000 +0800 @@ -1215,6 +1215,7 @@ link->state |= DEV_SUSPEND; /* Fall through... */ case CS_EVENT_RESET_PHYSICAL: + disable_irq(dev->irq); if (link->state & DEV_CONFIG) { if (link->open) { netif_device_detach(dev); @@ -1227,6 +1228,7 @@ link->state &= ~DEV_SUSPEND; /* Fall through... */ case CS_EVENT_CARD_RESET: + enable_irq(dev->irq); if (link->state & DEV_CONFIG) { pcmcia_request_configuration(link->handle, &link->conf); if (link->open) { diff -uNr a/drivers/net/smc91x.h b/drivers/net/smc91x.h --- a/drivers/net/smc91x.h 2004-10-19 05:54:54.000000000 +0800 +++ b/drivers/net/smc91x.h 2005-01-27 09:56:49.000000000 +0800 @@ -204,6 +204,7 @@ */ #include #include +#include #ifdef SMC_insl #undef SMC_insl diff -uNr a/drivers/serial/pxa.c b/drivers/serial/pxa.c --- a/drivers/serial/pxa.c 2004-10-19 05:55:21.000000000 +0800 +++ b/drivers/serial/pxa.c 2005-01-27 09:56:49.000000000 +0800 @@ -48,6 +48,9 @@ #include +#ifdef CONFIG_PXA27x +#include +#endif struct uart_pxa_port { struct uart_port port; @@ -254,6 +257,23 @@ wake_up_interruptible(&up->port.info->delta_msr_wait); } +#ifdef CONFIG_PXA27x_E20 +static inline void disable_timeout_int(struct uart_pxa_port *up) +{ + up->ier &= ~UART_IER_RTOIE; + serial_out(up, UART_IER, up->ier); +} + +static inline void enable_timeout_int(struct uart_pxa_port *up) +{ + up->ier |= UART_IER_RTOIE; + serial_out(up, UART_IER, up->ier); +} +#else +static inline void disable_timeout_int(struct uart_pxa_port *up) {} +static inline void enable_timeout_int(struct uart_pxa_port *up) {} +#endif + /* * This handles the interrupt from one port. */ @@ -267,8 +287,12 @@ if (iir & UART_IIR_NO_INT) return IRQ_NONE; lsr = serial_in(up, UART_LSR); - if (lsr & UART_LSR_DR) + if (lsr & UART_LSR_DR) { +/*FIXME: PXA27x E20 */ + disable_timeout_int(up); receive_chars(up, &lsr, regs); + enable_timeout_int(up); + } check_modem_status(up); if (lsr & UART_LSR_THRE) transmit_chars(up); @@ -387,6 +411,19 @@ if (retval) return retval; +/* This enable the BTUART. FFUART has been enabled in bootloader. + * STUART will be enabled in IrDA driver. Put these codes after + * request irq successful. + */ + if ( IRQ_BTUART == up->port.irq) { + MST_MSCWR1 |= (0x03<<7); + CKEN |= CKEN7_BTUART; + pxa_gpio_mode(GPIO42_BTRXD_MD); + pxa_gpio_mode(GPIO43_BTTXD_MD); + pxa_gpio_mode(GPIO44_BTCTS_MD); + pxa_gpio_mode(GPIO45_BTRTS_MD); + } + /* * Clear the FIFO buffers and disable them. * (they will be reenabled in set_termios()) diff -uNr a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c --- a/drivers/usb/gadget/ether.c 2004-10-19 05:55:28.000000000 +0800 +++ b/drivers/usb/gadget/ether.c 2005-01-27 09:56:49.000000000 +0800 @@ -231,6 +231,9 @@ #define DEV_CONFIG_CDC #endif +#ifdef CONFIG_USB_GADGET_PXA27X +//#define DEV_CONFIG_CDC +#endif /* For CDC-incapable hardware, choose the simple cdc subset. * Anything that talks bulk (without notable bugs) can do this. @@ -238,6 +241,10 @@ #ifdef CONFIG_USB_GADGET_PXA2XX #define DEV_CONFIG_SUBSET #endif + +#ifdef CONFIG_USB_GADGET_PXA27X +#define DEV_CONFIG_SUBSET +#endif #ifdef CONFIG_USB_GADGET_SH #define DEV_CONFIG_SUBSET @@ -252,6 +259,10 @@ #define DEV_CONFIG_SUBSET #endif +#ifdef CONFIG_USB_PXA27X +extern struct usb_ep* pxa27x_ep_config(struct usb_gadget *gadget, + struct usb_endpoint_descriptor *desc,int config,int interface,int alt); +#endif /*-------------------------------------------------------------------------*/ @@ -2334,6 +2345,9 @@ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0208); } else if (gadget_is_lh7a40x(gadget)) { device_desc.bcdDevice = __constant_cpu_to_le16 (0x0209); + } else if (gadget_is_pxa27x(gadget)) { + device_desc.bcdDevice = __constant_cpu_to_le16 (0x0110); + zlp = 0; } else { /* can't assume CDC works. don't want to default to * anything less functional on CDC-capable hardware, @@ -2391,7 +2405,27 @@ /* all we really need is bulk IN/OUT */ usb_ep_autoconfig_reset (gadget); +#ifdef CONFIG_USB_PXA27X +#ifdef CONFIG_USB_ETH_RNDIS + ep = pxa27x_ep_config (gadget, &fs_source_desc, + DEV_RNDIS_CONFIG_VALUE, + (int)rndis_data_intf.bInterfaceNumber, + (int)rndis_data_intf.bAlternateSetting); +#elif defined(DEV_CONFIG_CDC) + ep = pxa27x_ep_config (gadget, &fs_source_desc, + DEV_CONFIG_VALUE, + (int)data_intf.bInterfaceNumber, + (int)data_intf.bAlternateSetting); +#elif defined(DEV_CONFIG_SUBSET) + ep = pxa27x_ep_config (gadget, &fs_source_desc, + DEV_CONFIG_VALUE, + (int)subset_data_intf.bInterfaceNumber, + (int)subset_data_intf.bAlternateSetting); + +#endif //CONFIG_USB_ETH_RNDIS +#else ep = usb_ep_autoconfig (gadget, &fs_source_desc); +#endif //CONFIG_USB_PXA27X if (!ep) { autoconf_fail: dev_err (&gadget->dev, @@ -2401,8 +2435,26 @@ } EP_IN_NAME = ep->name; ep->driver_data = ep; /* claim */ - +#ifdef CONFIG_USB_PXA27X +#ifdef CONFIG_USB_ETH_RNDIS + ep = pxa27x_ep_config (gadget, &fs_sink_desc, + DEV_RNDIS_CONFIG_VALUE, + (int)rndis_data_intf.bInterfaceNumber, + (int)rndis_data_intf.bAlternateSetting); +#elif defined(DEV_CONFIG_CDC) + ep = pxa27x_ep_config (gadget, &fs_sink_desc, + DEV_CONFIG_VALUE, + (int)data_intf.bInterfaceNumber, + (int)data_intf.bAlternateSetting); +#elif defined(DEV_CONFIG_SUBSET) + ep = pxa27x_ep_config (gadget, &fs_sink_desc, + DEV_CONFIG_VALUE, + (int)subset_data_intf.bInterfaceNumber, + (int)subset_data_intf.bAlternateSetting); +#endif //CONFIG_USB_ETH_RNDIS +#else ep = usb_ep_autoconfig (gadget, &fs_sink_desc); +#endif //CONFIG_USB_PXA27X if (!ep) goto autoconf_fail; EP_OUT_NAME = ep->name; @@ -2413,7 +2465,22 @@ * Since some hosts expect one, try to allocate one anyway. */ if (cdc || rndis) { - ep = usb_ep_autoconfig (gadget, &fs_status_desc); +#ifdef CONFIG_USB_PXA27X +#ifdef CONFIG_USB_ETH_RNDIS + ep = pxa27x_ep_config (gadget, &fs_status_desc, + DEV_RNDIS_CONFIG_VALUE, + (int)rndis_control_intf.bInterfaceNumber, + (int)rndis_control_intf.bAlternateSetting); +#elif defined(DEV_CONFIG_CDC) + ep = pxa27x_ep_config (gadget, &fs_status_desc, + DEV_CONFIG_VALUE, + (int)control_intf.bInterfaceNumber, + (int)control_intf.bAlternateSetting); + +#endif //CONFIG_USB_ETH_RNDIS +#else + ep = usb_ep_autoconfig (gadget, &fs_status_desc); +#endif //CONFIG_USB_PXA27X if (ep) { EP_STATUS_NAME = ep->name; ep->driver_data = ep; /* claim */ @@ -2422,10 +2489,13 @@ "can't run RNDIS on %s\n", gadget->name); return -ENODEV; - } else if (cdc) { + } +#ifndef CONFIG_USB_PXA27X + else if (cdc) { control_intf.bNumEndpoints = 0; /* FIXME remove endpoint from descriptor list */ } +#endif //CONFIG_USB_PXA27X } #endif diff -uNr a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h --- a/drivers/usb/gadget/gadget_chips.h 2004-10-19 05:53:05.000000000 +0800 +++ b/drivers/usb/gadget/gadget_chips.h 2005-01-27 09:56:49.000000000 +0800 @@ -62,6 +62,12 @@ #define gadget_is_omap(g) 0 #endif +#ifdef CONFIG_USB_GADGET_PXA27X +#define gadget_is_pxa27x(g) !strcmp("pxa27x_udc", (g)->name) +#else +#define gadget_is_pxa27x(g) 0 +#endif + // CONFIG_USB_GADGET_AT91RM9200 // CONFIG_USB_GADGET_SX2 // CONFIG_USB_GADGET_AU1X00 diff -uNr a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig --- a/drivers/usb/gadget/Kconfig 2004-10-19 05:53:46.000000000 +0800 +++ b/drivers/usb/gadget/Kconfig 2005-01-27 09:56:49.000000000 +0800 @@ -99,6 +99,15 @@ default y if USB_ZERO default y if USB_ETH default y if USB_G_SERIAL + +config USB_GADGET_PXA27X + depends on ARCH_PXA && PXA27x + bool "PXA27x" + +config USB_PXA27X + bool + depends on USB_GADGET_PXA27X + default USB_GADGET config USB_GADGET_GOKU boolean "Toshiba TC86C001 'Goku-S'" diff -uNr a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile --- a/drivers/usb/gadget/Makefile 2004-10-19 05:53:13.000000000 +0800 +++ b/drivers/usb/gadget/Makefile 2005-01-27 09:56:49.000000000 +0800 @@ -7,6 +7,7 @@ obj-$(CONFIG_USB_GOKU) += goku_udc.o obj-$(CONFIG_USB_OMAP) += omap_udc.o obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o +obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o # # USB gadget drivers diff -uNr a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c --- a/drivers/usb/gadget/pxa27x_udc.c 1970-01-01 08:00:00.000000000 +0800 +++ b/drivers/usb/gadget/pxa27x_udc.c 2005-01-27 09:56:49.000000000 +0800 @@ -0,0 +1,2342 @@ +/* + * linux/drivers/usb/gadget/pxa27x_udc.c + * Intel PXA2xx and IXP4xx on-chip full speed USB device controllers + * + * Copyright (C) 2002 Intrinsyc, Inc. (Frank Becker) + * Copyright (C) 2003 Robert Schwebel, Pengutronix + * Copyright (C) 2003 Benedikt Spranger, Pengutronix + * Copyright (C) 2003 David Brownell + * Copyright (C) 2003 Joshua Wise + * Copyright (C) 2004 Intel Corporation + * + * 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 + * + */ + + #undef DEBUG + //#define DEBUG + //#define VERBOSE DBG_VERBOSE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + + +/* + * This driver handles the USB Device Controller (UDC) in Intel's PXA 27777777x + * series processors. + * Such controller drivers work with a gadget driver. The gadget driver + * returns descriptors, implements configuration and data protocols used + * by the host to interact with this device, and allocates endpoints to + * the different protocol interfaces. The controller driver virtualizes + * usb hardware so that the gadget drivers will be more portable. + * + * This UDC hardware wants to implement a bit too much USB protocol, so + * it constrains the sorts of USB configuration change events that work. + * The errata for these chips are misleading; some "fixed" bugs from + * pxa250 a0/a1 b0/b1/b2 sure act like they're still there. + */ + +#define DRIVER_VERSION "23-Sep-2004" +#define DRIVER_DESC "PXA 27x USB Device Controller driver" + + +static const char driver_name [] = "pxa27x_udc"; + +static const char ep0name [] = "ep0"; + + +#define USE_DMA +//#define DISABLE_TEST_MODE + +#ifdef CONFIG_PROC_FS +#define UDC_PROC_FILE +#endif + +#include "pxa27x_udc.h" + +#ifdef CONFIG_EMBEDDED +/* few strings, and little code to use them */ +#undef DEBUG +#undef UDC_PROC_FILE +#endif + +#ifdef USE_DMA +static int use_dma = 1; +module_param(use_dma, bool, 0); +MODULE_PARM_DESC (use_dma, "true to use dma"); + +static void dma_nodesc_handler (int dmach, void *_ep, struct pt_regs *r); +static void kick_dma(struct pxa27x_ep *ep, struct pxa27x_request *req); + +#define DMASTR " (dma support)" + +#else /* !USE_DMA */ +#define DMASTR " (pio only)" +#endif + +#ifdef CONFIG_USB_PXA27X_SMALL +#define SIZE_STR " (small)" +#else +#define SIZE_STR "" +#endif + +#ifdef DISABLE_TEST_MODE +/* (mode == 0) == no undocumented chip tweaks + * (mode & 1) == double buffer bulk IN + * (mode & 2) == double buffer bulk OUT + * ... so mode = 3 (or 7, 15, etc) does it for both + */ +static ushort fifo_mode = 0; +module_param(fifo_mode, ushort, 0); +MODULE_PARM_DESC (fifo_mode, "pxa27x udc fifo mode"); +#endif + +#define UDCISR0_IR0 0x3 +#define UDCISR_INT_MASK (UDC_INT_FIFOERROR | UDC_INT_PACKETCMP) +#define UDCICR_INT_MASK UDCISR_INT_MASK + +#define UDCCSR_MASK (UDCCSR_FST | UDCCSR_DME) +/* --------------------------------------------------------------------------- + * endpoint related parts of the api to the usb controller hardware, + * used by gadget driver; and the inner talker-to-hardware core. + * --------------------------------------------------------------------------- + */ + +static void pxa27x_ep_fifo_flush (struct usb_ep *ep); +static void nuke (struct pxa27x_ep *, int status); + +static void pio_irq_enable(int ep_num) +{ + if (ep_num < 16) + UDCICR0 |= 3 << (ep_num * 2); + else { + ep_num -= 16; + UDCICR1 |= 3 << (ep_num * 2); + } +} + +static void pio_irq_disable(int ep_num) +{ + ep_num &= 0xf; + if (ep_num < 16) + UDCICR0 &= ~(3 << (ep_num * 2)); + else { + ep_num -= 16; + UDCICR1 &= ~(3 << (ep_num * 2)); + } +} + +/* The UDCCR reg contains mask and interrupt status bits, + * so using '|=' isn't safe as it may ack an interrupt. + */ +#define UDCCR_MASK_BITS (UDCCR_OEN | UDCCR_UDE) + +static inline void udc_set_mask_UDCCR(int mask) +{ + UDCCR = (UDCCR & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS); +} + +static inline void udc_clear_mask_UDCCR(int mask) +{ + UDCCR = (UDCCR & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS); +} + +static inline void udc_ack_int_UDCCR(int mask) +{ + /* udccr contains the bits we dont want to change */ + __u32 udccr = UDCCR & UDCCR_MASK_BITS; + + UDCCR = udccr | (mask & ~UDCCR_MASK_BITS); +} + +/* + * endpoint enable/disable + * + * we need to verify the descriptors used to enable endpoints. since pxa27x + * endpoint configurations are fixed, and are pretty much always enabled, + * there's not a lot to manage here. + * + * because pxa27x can't selectively initialize bulk (or interrupt) endpoints, + * (resetting endpoint halt and toggle), SET_INTERFACE is unusable except + * for a single interface (with only the default altsetting) and for gadget + * drivers that don't halt endpoints (not reset by set_interface). that also + * means that if you use ISO, you must violate the USB spec rule that all + * iso endpoints must be in non-default altsettings. + */ +static int pxa27x_ep_enable (struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct pxa27x_ep *ep; + struct pxa27x_udc *dev; + + ep = container_of (_ep, struct pxa27x_ep, ep); + if (!_ep || !desc || _ep->name == ep0name + || desc->bDescriptorType != USB_DT_ENDPOINT + || ep->fifo_size < le16_to_cpu(desc->wMaxPacketSize)) { + DMSG("%s, bad ep or descriptor\n", __FUNCTION__); + return -EINVAL; + } + + /* xfer types must match, except that interrupt ~= bulk */ + if( ep->ep_type != USB_ENDPOINT_XFER_BULK + && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { + DMSG("%s, %s type mismatch\n", __FUNCTION__, _ep->name); + return -EINVAL; + } + + /* hardware _could_ do smaller, but driver doesn't */ + if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK + && le16_to_cpu (desc->wMaxPacketSize) + != BULK_FIFO_SIZE) + || !desc->wMaxPacketSize) { + DMSG("%s, bad %s maxpacket\n", __FUNCTION__, _ep->name); + return -ERANGE; + } + + dev = ep->dev; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { + DMSG("%s, bogus device state\n", __FUNCTION__); + return -ESHUTDOWN; + } + + ep->desc = desc; + ep->dma = -1; + ep->stopped = 0; + ep->pio_irqs = ep->dma_irqs = 0; + ep->ep.maxpacket = le16_to_cpu (desc->wMaxPacketSize); + + /* flush fifo (mostly for OUT buffers) */ + pxa27x_ep_fifo_flush (_ep); + + /* ... reset halt state too, if we could ... */ + +#ifdef USE_DMA + /* for (some) bulk and ISO endpoints, try to get a DMA channel and + * bind it to the endpoint. otherwise use PIO. + */ + DMSG("%s: called attributes=%d\n", __FUNCTION__, ep->ep_type); + switch (ep->ep_type) { + case USB_ENDPOINT_XFER_ISOC: + if (le16_to_cpu(desc->wMaxPacketSize) % 32) + break; + // fall through + case USB_ENDPOINT_XFER_BULK: + if (!use_dma || !ep->reg_drcmr) + break; + ep->dma = pxa_request_dma ((char *)_ep->name, + (le16_to_cpu (desc->wMaxPacketSize) > 64) + ? DMA_PRIO_MEDIUM /* some iso */ + : DMA_PRIO_LOW, + dma_nodesc_handler, ep); + if (ep->dma >= 0) { + *ep->reg_drcmr = DRCMR_MAPVLD | ep->dma; + DMSG("%s using dma%d\n", _ep->name, ep->dma); + } + default: + break; + } +#endif + DBG(DBG_VERBOSE, "enabled %s\n", _ep->name); + return 0; +} + +static int pxa27x_ep_disable (struct usb_ep *_ep) +{ + struct pxa27x_ep *ep; + + ep = container_of (_ep, struct pxa27x_ep, ep); + if (!_ep || !ep->desc) { + DMSG("%s, %s not enabled\n", __FUNCTION__, + _ep ? ep->ep.name : NULL); + return -EINVAL; + } + nuke (ep, -ESHUTDOWN); + +#ifdef USE_DMA + if (ep->dma >= 0) { + *ep->reg_drcmr = 0; + pxa_free_dma (ep->dma); + ep->dma = -1; + } +#endif + + /* flush fifo (mostly for IN buffers) */ + pxa27x_ep_fifo_flush (_ep); + + ep->desc = 0; + ep->stopped = 1; + + DBG(DBG_VERBOSE, "%s disabled\n", _ep->name); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* for the pxa27x, these can just wrap kmalloc/kfree. gadget drivers + * must still pass correctly initialized endpoints, since other controller + * drivers may care about how it's currently set up (dma issues etc). + */ + +/* + * pxa27x_ep_alloc_request - allocate a request data structure + */ +static struct usb_request * +pxa27x_ep_alloc_request (struct usb_ep *_ep, int gfp_flags) +{ + struct pxa27x_request *req; + + req = kmalloc (sizeof *req, gfp_flags); + if (!req) + return 0; + + memset (req, 0, sizeof *req); + INIT_LIST_HEAD (&req->queue); + return &req->req; +} + + +/* + * pxa27x_ep_free_request - deallocate a request data structure + */ +static void +pxa27x_ep_free_request (struct usb_ep *_ep, struct usb_request *_req) +{ + struct pxa27x_request *req; + + req = container_of(_req, struct pxa27x_request, req); + WARN_ON (!list_empty (&req->queue)); + kfree(req); +} + + +/* PXA cache needs flushing with DMA I/O (it's dma-incoherent), but there's + * no device-affinity and the heap works perfectly well for i/o buffers. + * It wastes much less memory than dma_alloc_coherent() would, and even + * prevents cacheline (32 bytes wide) sharing problems. + */ +static void * +pxa27x_ep_alloc_buffer(struct usb_ep *_ep, unsigned bytes, + dma_addr_t *dma, int gfp_flags) +{ + char *retval; + + retval = kmalloc (bytes, gfp_flags & ~(__GFP_DMA|__GFP_HIGHMEM)); + if (retval) + *dma = virt_to_bus (retval); + return retval; +} + +static void +pxa27x_ep_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma, + unsigned bytes) +{ + kfree (buf); +} + +/*-------------------------------------------------------------------------*/ + +/* + * done - retire a request; caller blocked irqs + */ +static void done(struct pxa27x_ep *ep, struct pxa27x_request *req, int status) +{ + list_del_init(&req->queue); + if (likely (req->req.status == -EINPROGRESS)) + req->req.status = status; + else + status = req->req.status; + + if (status && status != -ESHUTDOWN) + DBG(DBG_VERBOSE, "complete %s req %p stat %d len %u/%u\n", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + + /* don't modify queue heads during completion callback */ + req->req.complete(&ep->ep, &req->req); +} + + +static inline void ep0_idle (struct pxa27x_udc *dev) +{ + dev->ep0state = EP0_IDLE; + LED_EP0_OFF; +} + +static int +write_packet(volatile u32 *uddr, struct pxa27x_request *req, unsigned max) +{ + u32 *buf; + int length, count, remain; + + buf = (u32*)(req->req.buf + req->req.actual); + prefetch(buf); + + /* how big will this packet be? */ + length = min(req->req.length - req->req.actual, max); + req->req.actual += length; + + remain = length & 0x3; + count = length & ~(0x3); + + while (likely(count)) { + *uddr = *buf++; + count -= 4; + } + + if (remain) { + volatile u8* reg=(u8*)uddr; + char *rd =(u8*)buf; + + while (remain--) { + *reg=*rd++; + } + } + + return length; +} + +/* + * write to an IN endpoint fifo, as many packets as possible. + * irqs will use this to write the rest later. + * caller guarantees at least one packet buffer is ready (or a zlp). + */ +static int +write_fifo (struct pxa27x_ep *ep, struct pxa27x_request *req) +{ + unsigned max; + + max = le16_to_cpu(ep->desc->wMaxPacketSize); + do { + int count; + int is_last, is_short; + + count = write_packet(ep->reg_udcdr, req, max); + + /* last packet is usually short (or a zlp) */ + if (unlikely (count != max)) + is_last = is_short = 1; + else { + if (likely(req->req.length != req->req.actual) + || req->req.zero) + is_last = 0; + else + is_last = 1; + /* interrupt/iso maxpacket may not fill the fifo */ + is_short = unlikely (max < ep->fifo_size); + } + + DMSG("wrote %s count:%d bytes%s%s %d left %p\n", + ep->ep.name, count, + is_last ? "/L" : "", is_short ? "/S" : "", + req->req.length - req->req.actual, &req->req); + + /* let loose that packet. maybe try writing another one, + * double buffering might work. TSP, TPC, and TFS + * bit values are the same for all normal IN endpoints. + */ + *ep->reg_udccsr = UDCCSR_PC; + if (is_short) + *ep->reg_udccsr = UDCCSR_SP; + + /* requests complete when all IN data is in the FIFO */ + if (is_last) { + done (ep, req, 0); + if (list_empty(&ep->queue) || unlikely(ep->dma >= 0)) { + pio_irq_disable (ep->ep_num); +#ifdef USE_DMA + /* unaligned data and zlps couldn't use dma */ + if (unlikely(!list_empty(&ep->queue))) { + req = list_entry(ep->queue.next, + struct pxa27x_request, queue); + kick_dma(ep,req); + return 0; + } +#endif + } + return 1; + } + + // TODO experiment: how robust can fifo mode tweaking be? + // double buffering is off in the default fifo mode, which + // prevents TFS from being set here. + + } while (*ep->reg_udccsr & UDCCSR_FS); + return 0; +} + +/* caller asserts req->pending (ep0 irq status nyet cleared); starts + * ep0 data stage. these chips want very simple state transitions. + */ +static inline +void ep0start(struct pxa27x_udc *dev, u32 flags, const char *tag) +{ + UDCCSR0 = flags|UDCCSR0_SA|UDCCSR0_OPC; + UDCISR0 = UDCICR_INT(0, UDC_INT_FIFOERROR | UDC_INT_PACKETCMP); + dev->req_pending = 0; + DBG(DBG_VERY_NOISY, "%s %s, %02x/%02x\n", + __FUNCTION__, tag, UDCCSR0, flags); +} + +static int +write_ep0_fifo (struct pxa27x_ep *ep, struct pxa27x_request *req) +{ + unsigned count; + int is_short; + + count = write_packet(&UDCDR0, req, EP0_FIFO_SIZE); + ep->dev->stats.write.bytes += count; + + /* last packet "must be" short (or a zlp) */ + is_short = (count != EP0_FIFO_SIZE); + + DBG(DBG_VERY_NOISY, "ep0in %d bytes %d left %p\n", count, + req->req.length - req->req.actual, &req->req); + + if (unlikely (is_short)) { + if (ep->dev->req_pending) + ep0start(ep->dev, UDCCSR0_IPR, "short IN"); + else + UDCCSR0 = UDCCSR0_IPR; + + count = req->req.length; + done (ep, req, 0); + ep0_idle(ep->dev); +#if 0 + /* This seems to get rid of lost status irqs in some cases: + * host responds quickly, or next request involves config + * change automagic, or should have been hidden, or ... + * + * FIXME get rid of all udelays possible... + */ + if (count >= EP0_FIFO_SIZE) { + count = 100; + do { + if ((UDCCSR0 & UDCCSR0_OPC) != 0) { + /* clear OPC, generate ack */ + UDCCSR0 = UDCCSR0_OPC; + break; + } + count--; + udelay(1); + } while (count); + } +#endif + } else if (ep->dev->req_pending) + ep0start(ep->dev, 0, "IN"); + return is_short; +} + + +/* + * read_fifo - unload packet(s) from the fifo we use for usb OUT + * transfers and put them into the request. caller should have made + * sure there's at least one packet ready. + * + * returns true if the request completed because of short packet or the + * request buffer having filled (and maybe overran till end-of-packet). + */ +static int +read_fifo (struct pxa27x_ep *ep, struct pxa27x_request *req) +{ + for (;;) { + u32 *buf; + int bufferspace, count, is_short; + + /* make sure there's a packet in the FIFO.*/ + if (unlikely ((*ep->reg_udccsr & UDCCSR_PC) == 0)) + break; + buf =(u32*) (req->req.buf + req->req.actual); + prefetchw(buf); + bufferspace = req->req.length - req->req.actual; + + /* read all bytes from this packet */ + if (likely (*ep->reg_udccsr & UDCCSR_BNE)) { + count = 0x3ff & *ep->reg_udcbcr; + req->req.actual += min (count, bufferspace); + } else /* zlp */ + count = 0; + + is_short = (count < ep->ep.maxpacket); + DMSG("read %s udccsr:%02x, count:%d bytes%s req %p %d/%d\n", + ep->ep.name, *ep->reg_udccsr, count, + is_short ? "/S" : "", + &req->req, req->req.actual, req->req.length); + +// dump_regs(ep->ep_num ); + count = min(count, bufferspace); + while (likely (count > 0)) { + *buf++ = *ep->reg_udcdr; + count -= 4; + } + DMSG("Buf:0x%p\n", req->req.buf); + + *ep->reg_udccsr = UDCCSR_PC; + /* RPC/RSP/RNE could now reflect the other packet buffer */ + + /* completion */ + if (is_short || req->req.actual == req->req.length) { + done (ep, req, 0); + if (list_empty(&ep->queue)) + pio_irq_disable (ep->ep_num); + return 1; + } + + /* finished that packet. the next one may be waiting... */ + } + return 0; +} + +/* + * special ep0 version of the above. no UBCR0 or double buffering; status + * handshaking is magic. most device protocols don't need control-OUT. + * CDC vendor commands (and RNDIS), mass storage CB/CBI, and some other + * protocols do use them. + */ +static int +read_ep0_fifo (struct pxa27x_ep *ep, struct pxa27x_request *req) +{ + u32 *buf, word; + unsigned bufferspace; + + buf = (u32*) (req->req.buf + req->req.actual); + bufferspace = req->req.length - req->req.actual; + + while (UDCCSR0 & UDCCSR0_RNE) { + word = UDCDR0; + + if (unlikely (bufferspace == 0)) { + /* this happens when the driver's buffer + * is smaller than what the host sent. + * discard the extra data. + */ + if (req->req.status != -EOVERFLOW) + DMSG("%s overflow\n", ep->ep.name); + req->req.status = -EOVERFLOW; + } else { + *buf++ = word; + req->req.actual += 4; + bufferspace -= 4; + } + } + + UDCCSR0 = UDCCSR0_OPC ; + + /* completion */ + if (req->req.actual >= req->req.length) + return 1; + + /* finished that packet. the next one may be waiting... */ + return 0; +} + +#ifdef USE_DMA + +#define MAX_IN_DMA ((DCMD_LENGTH + 1) - BULK_FIFO_SIZE) +static void kick_dma(struct pxa27x_ep *ep, struct pxa27x_request *req) +{ + u32 dcmd = 0; + u32 len = req->req.length; + u32 buf = req->req.dma; + u32 fifo = io_v2p ((u32)ep->reg_udcdr); + + buf += req->req.actual; + len -= req->req.actual; + ep->dma_con = 0; + + DMSG("%s: req:0x%p length:%d, actual:%d dma:%d\n", + __FUNCTION__, &req->req, req->req.length, + req->req.actual,ep->dma); + + /* no-descriptor mode can be simple for bulk-in, iso-in, iso-out */ + DCSR(ep->dma) = DCSR_NODESC; + if (buf & 0x3) + DALGN |= 1 << ep->dma; + else + DALGN &= ~(1 << ep->dma); + + if (ep->dir_in) { + DSADR(ep->dma) = buf; + DTADR(ep->dma) = fifo; + if (len > MAX_IN_DMA) { + len= MAX_IN_DMA; + ep->dma_con =1 ; + } else if (len >= ep->ep.maxpacket) { + if ((ep->dma_con = (len % ep->ep.maxpacket) != 0)) + len = ep->ep.maxpacket; + } + dcmd = len | DCMD_BURST32 | DCMD_WIDTH4 | DCMD_ENDIRQEN + | DCMD_FLOWTRG | DCMD_INCSRCADDR; + } else { + DSADR(ep->dma) = fifo; + DTADR(ep->dma) = buf; + dcmd = len | DCMD_BURST32 | DCMD_WIDTH4 | DCMD_ENDIRQEN + | DCMD_FLOWSRC | DCMD_INCTRGADDR; + } + *ep->reg_udccsr = UDCCSR_DME; + DCMD(ep->dma) = dcmd; + DCSR(ep->dma) = DCSR_NODESC | DCSR_EORIRQEN \ + | ((ep->dir_in) ? DCSR_STOPIRQEN : 0); + *ep->reg_drcmr = ep->dma | DRCMR_MAPVLD; + DCSR(ep->dma) |= DCSR_RUN; +} + +static void cancel_dma(struct pxa27x_ep *ep) +{ + struct pxa27x_request *req; + u32 tmp; + + if (DCSR(ep->dma) == 0 || list_empty(&ep->queue)) + return; + + DMSG("hehe dma:%d,dcsr:0x%x\n", ep->dma, DCSR(ep->dma)); + DCSR(ep->dma) = 0; + while ((DCSR(ep->dma) & DCSR_STOPSTATE) == 0) + cpu_relax(); + + req = list_entry(ep->queue.next, struct pxa27x_request, queue); + tmp = DCMD(ep->dma) & DCMD_LENGTH; + req->req.actual = req->req.length - tmp; + + /* the last tx packet may be incomplete, so flush the fifo. + * FIXME correct req.actual if we can + */ + *ep->reg_udccsr = UDCCSR_FEF; +} + +static void dma_nodesc_handler(int dmach, void *_ep, struct pt_regs *r) +{ + struct pxa27x_ep *ep = _ep; + struct pxa27x_request *req, *req_next; + u32 dcsr, tmp, completed; + + local_irq_disable(); + + req = list_entry(ep->queue.next, struct pxa27x_request, queue); + + DMSG("%s, buf:0x%p\n",__FUNCTION__, req->req.buf); + + ep->dma_irqs++; + ep->dev->stats.irqs++; + HEX_DISPLAY(ep->dev->stats.irqs); + + completed = 0; + + dcsr = DCSR(dmach); + DCSR(ep->dma) &= ~DCSR_RUN; + + if (dcsr & DCSR_BUSERR) { + DCSR(dmach) = DCSR_BUSERR; + printk(KERN_ERR " Buss Error\n"); + req->req.status = -EIO; + completed = 1; + } else if (dcsr & DCSR_ENDINTR) { + DCSR(dmach) = DCSR_ENDINTR; + if (ep->dir_in) { + tmp = req->req.length - req->req.actual; + /* Last packet is a short one*/ + if ( tmp < ep->ep.maxpacket) { + int count = 0; + + *ep->reg_udccsr = UDCCSR_SP | \ + (*ep->reg_udccsr & UDCCSR_MASK); + /*Wait for packet out */ + while( (count++ < 10000) && \ + !(*ep->reg_udccsr & UDCCSR_FS)); + if (count >= 10000) + DMSG("Failed to send packet\n"); + else + DMSG("%s: short packet sent len:%d," + "length:%d,actual:%d\n", __FUNCTION__, + tmp, req->req.length, req->req.actual); + req->req.actual = req->req.length; + completed = 1; + /* There are still packets to transfer */ + } else if ( ep->dma_con) { + DMSG("%s: more packets,length:%d,actual:%d\n", + __FUNCTION__,req->req.length, + req->req.actual); + req->req.actual += ep->ep.maxpacket; + completed = 0; + } else { + DMSG("%s: no more packets,length:%d," + "actual:%d\n", __FUNCTION__, + req->req.length, req->req.actual); + req->req.actual = req->req.length; + completed = 1; + } + } else { + req->req.actual = req->req.length; + completed = 1; + } + } else if (dcsr & DCSR_EORINTR) { //Only happened in OUT DMA + int remain,udccsr ; + + DCSR(dmach) = DCSR_EORINTR; + remain = DCMD(dmach) & DCMD_LENGTH; + req->req.actual = req->req.length - remain; + + udccsr = *ep->reg_udccsr; + if (udccsr & UDCCSR_SP) { + *ep->reg_udccsr = UDCCSR_PC | (udccsr & UDCCSR_MASK); + completed = 1; + } + DMSG("%s: length:%d actual:%d\n", + __FUNCTION__, req->req.length, req->req.actual); + } else + DMSG("%s: Others dma:%d DCSR:0x%x DCMD:0x%x\n", + __FUNCTION__, dmach, DCSR(dmach), DCMD(dmach)); + + if (likely(completed)) { + if (req->queue.next != &ep->queue) { + req_next = list_entry(req->queue.next, + struct pxa27x_request, queue); + kick_dma(ep, req_next); + } + done(ep, req, 0); + } else { + kick_dma(ep, req); + } + + local_irq_enable(); +} + +#endif +/*-------------------------------------------------------------------------*/ + +static int +pxa27x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags) +{ + struct pxa27x_ep *ep; + struct pxa27x_request *req; + struct pxa27x_udc *dev; + unsigned long flags; + + req = container_of(_req, struct pxa27x_request, req); + if (unlikely (!_req || !_req->complete || !_req->buf|| + !list_empty(&req->queue))) { + DMSG("%s, bad params\n", __FUNCTION__); + return -EINVAL; + } + + ep = container_of(_ep, struct pxa27x_ep, ep); + if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) { + DMSG("%s, bad ep\n", __FUNCTION__); + return -EINVAL; + } + + DMSG("%s, ep point %d is queue\n", __FUNCTION__, ep->ep_num); + + dev = ep->dev; + if (unlikely (!dev->driver + || dev->gadget.speed == USB_SPEED_UNKNOWN)) { + DMSG("%s, bogus device state\n", __FUNCTION__); + return -ESHUTDOWN; + } + + /* iso is always one packet per request, that's the only way + * we can report per-packet status. that also helps with dma. + */ + if (unlikely (ep->ep_type == USB_ENDPOINT_XFER_ISOC + && req->req.length > le16_to_cpu + (ep->desc->wMaxPacketSize))) + return -EMSGSIZE; + +#ifdef USE_DMA + // FIXME caller may already have done the dma mapping + if (ep->dma >= 0) { + _req->dma = dma_map_single(dev->dev, _req->buf, _req->length, + (ep->dir_in) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + } +#endif + + DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n", + _ep->name, _req, _req->length, _req->buf); + + local_irq_save(flags); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + /* kickstart this i/o queue? */ + if (list_empty(&ep->queue) && !ep->stopped) { + if (ep->desc == 0 /* ep0 */) { + unsigned length = _req->length; + + switch (dev->ep0state) { + case EP0_IN_DATA_PHASE: + dev->stats.write.ops++; + if (write_ep0_fifo(ep, req)) + req = 0; + break; + + case EP0_OUT_DATA_PHASE: + dev->stats.read.ops++; + if (dev->req_pending) + ep0start(dev, UDCCSR0_IPR, "OUT"); + if (length == 0 || ((UDCCSR0 & UDCCSR0_RNE) != 0 + && read_ep0_fifo(ep, req))) { + ep0_idle(dev); + done(ep, req, 0); + req = 0; + } + break; + case EP0_NO_ACTION: + ep0_idle(dev); + req=0; + break; + default: + DMSG("ep0 i/o, odd state %d\n", dev->ep0state); + local_irq_restore (flags); + return -EL2HLT; + } +#ifdef USE_DMA + /* either start dma or prime pio pump */ + } else if (ep->dma >= 0) { + kick_dma(ep, req); +#endif + /* can the FIFO can satisfy the request immediately? */ + } else if (ep->dir_in + && (*ep->reg_udccsr & UDCCSR_FS) != 0 + && write_fifo(ep, req)) { + req = 0; + } else if ((*ep->reg_udccsr & UDCCSR_FS) != 0 + && read_fifo(ep, req)) { + req = 0; + } + DMSG("req:%p,ep->desc:%p,ep->dma:%d\n", req, ep->desc, ep->dma); + if (likely (req && ep->desc) && ep->dma < 0) + pio_irq_enable(ep->ep_num); + } + + /* pio or dma irq handler advances the queue. */ + if (likely (req != 0)) + list_add_tail(&req->queue, &ep->queue); + local_irq_restore(flags); + + return 0; +} + + +/* + * nuke - dequeue ALL requests + */ +static void nuke(struct pxa27x_ep *ep, int status) +{ + struct pxa27x_request *req; + + /* called with irqs blocked */ +#ifdef USE_DMA + if (ep->dma >= 0 && !ep->stopped) + cancel_dma(ep); +#endif + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct pxa27x_request, queue); + done(ep, req, status); + } + if (ep->desc) + pio_irq_disable (ep->ep_num); +} + + +/* dequeue JUST ONE request */ +static int pxa27x_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct pxa27x_ep *ep; + struct pxa27x_request *req; + unsigned long flags; + + ep = container_of(_ep, struct pxa27x_ep, ep); + if (!_ep || ep->ep.name == ep0name) + return -EINVAL; + + local_irq_save(flags); + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry (req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + local_irq_restore(flags); + return -EINVAL; + } + +#ifdef USE_DMA + if (ep->dma >= 0 && ep->queue.next == &req->queue && !ep->stopped) { + cancel_dma(ep); + done(ep, req, -ECONNRESET); + /* restart i/o */ + if (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct pxa27x_request, queue); + kick_dma(ep, req); + } + } else +#endif + done(ep, req, -ECONNRESET); + + local_irq_restore(flags); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int pxa27x_ep_set_halt(struct usb_ep *_ep, int value) +{ + struct pxa27x_ep *ep; + unsigned long flags; + + DMSG("%s is called\n", __FUNCTION__); + ep = container_of(_ep, struct pxa27x_ep, ep); + if (unlikely (!_ep + || (!ep->desc && ep->ep.name != ep0name)) + || ep->ep_type == USB_ENDPOINT_XFER_ISOC) { + DMSG("%s, bad ep\n", __FUNCTION__); + return -EINVAL; + } + if (value == 0) { + /* this path (reset toggle+halt) is needed to implement + * SET_INTERFACE on normal hardware. but it can't be + * done from software on the PXA UDC, and the hardware + * forgets to do it as part of SET_INTERFACE automagic. + */ + DMSG("only host can clear %s halt\n", _ep->name); + return -EROFS; + } + + local_irq_save(flags); + + if (ep->dir_in && ((*ep->reg_udccsr & UDCCSR_FS) == 0 + || !list_empty(&ep->queue))) { + local_irq_restore(flags); + return -EAGAIN; + } + + /* FST bit is the same for control, bulk in, bulk out, interrupt in */ + *ep->reg_udccsr = UDCCSR_FST|UDCCSR_FEF; + + /* ep0 needs special care */ + if (!ep->desc) { + start_watchdog(ep->dev); + ep->dev->req_pending = 0; + ep->dev->ep0state = EP0_STALL; + LED_EP0_OFF; + + /* and bulk/intr endpoints like dropping stalls too */ + } else { + unsigned i; + for (i = 0; i < 1000; i += 20) { + if (*ep->reg_udccsr & UDCCSR_SST) + break; + udelay(20); + } + } + local_irq_restore(flags); + + DBG(DBG_VERBOSE, "%s halt\n", _ep->name); + return 0; +} + +static int pxa27x_ep_fifo_status(struct usb_ep *_ep) +{ + struct pxa27x_ep *ep; + + ep = container_of(_ep, struct pxa27x_ep, ep); + if (!_ep) { + DMSG("%s, bad ep\n", __FUNCTION__); + return -ENODEV; + } + /* pxa can't report unclaimed bytes from IN fifos */ + if (ep->dir_in) + return -EOPNOTSUPP; + if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN + || (*ep->reg_udccsr & UDCCSR_FS) == 0) + return 0; + else + return (*ep->reg_udcbcr & 0xfff) + 1; +} + +static void pxa27x_ep_fifo_flush(struct usb_ep *_ep) +{ + struct pxa27x_ep *ep; + + ep = container_of(_ep, struct pxa27x_ep, ep); + if (!_ep || ep->ep.name == ep0name || !list_empty(&ep->queue)) { + DMSG("%s, bad ep\n", __FUNCTION__); + return; + } + + /* toggle and halt bits stay unchanged */ + + /* for OUT, just read and discard the FIFO contents. */ + if (!ep->dir_in) { + while (((*ep->reg_udccsr) & UDCCSR_BNE) != 0) + (void) *ep->reg_udcdr; + return; + } + + /* most IN status is the same, but ISO can't stall */ + *ep->reg_udccsr = UDCCSR_PC|UDCCSR_FST|UDCCSR_TRN + | (ep->ep_type == USB_ENDPOINT_XFER_ISOC) + ? 0 : UDCCSR_SST; +} + + +static struct usb_ep_ops pxa27x_ep_ops = { + .enable = pxa27x_ep_enable, + .disable = pxa27x_ep_disable, + + .alloc_request = pxa27x_ep_alloc_request, + .free_request = pxa27x_ep_free_request, + + .alloc_buffer = pxa27x_ep_alloc_buffer, + .free_buffer = pxa27x_ep_free_buffer, + + .queue = pxa27x_ep_queue, + .dequeue = pxa27x_ep_dequeue, + + .set_halt = pxa27x_ep_set_halt, + .fifo_status = pxa27x_ep_fifo_status, + .fifo_flush = pxa27x_ep_fifo_flush, +}; + + +/* --------------------------------------------------------------------------- + * device-scoped parts of the api to the usb controller hardware + * --------------------------------------------------------------------------- + */ + +static int pxa27x_udc_get_frame(struct usb_gadget *_gadget) +{ + return (UDCFNR & 0x3FF); +} + +static int pxa27x_udc_wakeup(struct usb_gadget *_gadget) +{ + /* host may not have enabled remote wakeup */ + if ((UDCCR & UDCCR_DWRE) == 0) + return -EHOSTUNREACH; + udc_set_mask_UDCCR(UDCCR_UDR); + return 0; +} + +static const struct usb_gadget_ops pxa27x_udc_ops = { + .get_frame = pxa27x_udc_get_frame, + .wakeup = pxa27x_udc_wakeup, + // current versions must always be self-powered +}; + + +/*-------------------------------------------------------------------------*/ + +#ifdef UDC_PROC_FILE + +static const char proc_node_name [] = "driver/udc"; + +static int +udc_proc_read(char *page, char **start, off_t off, int count, + int *eof, void *_dev) +{ + char *buf = page; + struct pxa27x_udc *dev = _dev; + char *next = buf; + unsigned size = count; + unsigned long flags; + int i, t; + u32 tmp; + + if (off != 0) + return 0; + + local_irq_save(flags); + + /* basic device status */ + t = scnprintf(next, size, DRIVER_DESC "\n" + "%s version: %s\nGadget driver: %s\n", + driver_name, DRIVER_VERSION SIZE_STR DMASTR, + dev->driver ? dev->driver->driver.name : "(none)"); + size -= t; + next += t; + + /* registers for device and ep0 */ + t = scnprintf(next, size, + "uicr %02X.%02X, usir %02X.%02x, ufnr %02X\n", + UDCICR1, UDCICR0, UDCISR1, UDCISR0, UDCFNR); + size -= t; + next += t; + + tmp = UDCCR; + t = scnprintf(next, size,"udccr %02X =%s%s%s%s%s%s%s%s%s%s, con=%d,inter=%d,altinter=%d\n", tmp, + (tmp & UDCCR_OEN) ? " oen":"", + (tmp & UDCCR_AALTHNP) ? " aalthnp":"", + (tmp & UDCCR_AHNP) ? " rem" : "", + (tmp & UDCCR_BHNP) ? " rstir" : "", + (tmp & UDCCR_DWRE) ? " dwre" : "", + (tmp & UDCCR_SMAC) ? " smac" : "", + (tmp & UDCCR_EMCE) ? " emce" : "", + (tmp & UDCCR_UDR) ? " udr" : "", + (tmp & UDCCR_UDA) ? " uda" : "", + (tmp & UDCCR_UDE) ? " ude" : "", + (tmp & UDCCR_ACN) >> UDCCR_ACN_S, + (tmp & UDCCR_AIN) >> UDCCR_AIN_S, + (tmp & UDCCR_AAISN)>> UDCCR_AAISN_S ); + + size -= t; + next += t; + + tmp = UDCCSR0; + t = scnprintf(next, size, + "udccsr0 %02X =%s%s%s%s%s%s%s\n", tmp, + (tmp & UDCCSR0_SA) ? " sa" : "", + (tmp & UDCCSR0_RNE) ? " rne" : "", + (tmp & UDCCSR0_FST) ? " fst" : "", + (tmp & UDCCSR0_SST) ? " sst" : "", + (tmp & UDCCSR0_DME) ? " dme" : "", + (tmp & UDCCSR0_IPR) ? " ipr" : "", + (tmp & UDCCSR0_OPC) ? " opc" : ""); + size -= t; + next += t; + + if (!dev->driver) + goto done; + + t = scnprintf(next, size, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n", + dev->stats.write.bytes, dev->stats.write.ops, + dev->stats.read.bytes, dev->stats.read.ops, + dev->stats.irqs); + size -= t; + next += t; + + /* dump endpoint queues */ + for (i = 0; i < UDC_EP_NUM; i++) { + struct pxa27x_ep *ep = &dev->ep [i]; + struct pxa27x_request *req; + int t; + + if (i != 0) { + const struct usb_endpoint_descriptor *d; + + d = ep->desc; + if (!d) + continue; + tmp = *dev->ep [i].reg_udccsr; + t = scnprintf(next, size, + "%s max %d %s udccs %02x udccr:0x%x\n", + ep->ep.name, le16_to_cpu (d->wMaxPacketSize), + (ep->dma >= 0) ? "dma" : "pio", tmp, + *dev->ep[i].reg_udccr); + /* TODO translate all five groups of udccs bits! */ + + } else /* ep0 should only have one transfer queued */ + t = scnprintf(next, size, "ep0 max 16 pio irqs %lu\n", + ep->pio_irqs); + if (t <= 0 || t > size) + goto done; + size -= t; + next += t; + + if (list_empty(&ep->queue)) { + t = scnprintf(next, size, "\t(nothing queued)\n"); + if (t <= 0 || t > size) + goto done; + size -= t; + next += t; + continue; + } + list_for_each_entry(req, &ep->queue, queue) { +#ifdef USE_DMA + if (ep->dma >= 0 && req->queue.prev == &ep->queue) + t = scnprintf(next, size, + "\treq %p len %d/%d " + "buf %p (dma%d dcmd %08x)\n", + &req->req, req->req.actual, + req->req.length, req->req.buf, + ep->dma, DCMD(ep->dma) + // low 13 bits == bytes-to-go + ); + else +#endif + t = scnprintf(next, size, + "\treq %p len %d/%d buf %p\n", + &req->req, req->req.actual, + req->req.length, req->req.buf); + if (t <= 0 || t > size) + goto done; + size -= t; + next += t; + } + } + +done: + local_irq_restore(flags); + *eof = 1; + return count - size; +} + +#define create_proc_files() \ + create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev) +#define remove_proc_files() \ + remove_proc_entry(proc_node_name, NULL) + +#else /* !UDC_PROC_FILE */ +#define create_proc_files() do {} while (0) +#define remove_proc_files() do {} while (0) + +#endif /* UDC_PROC_FILE */ + +/* "function" sysfs attribute */ +static ssize_t +show_function (struct device *_dev, char *buf) +{ + struct pxa27x_udc *dev = dev_get_drvdata (_dev); + + if (!dev->driver + || !dev->driver->function + || strlen (dev->driver->function) > PAGE_SIZE) + return 0; + return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function); +} +static DEVICE_ATTR (function, S_IRUGO, show_function, NULL); + +/*-------------------------------------------------------------------------*/ + +/* + * udc_disable - disable USB device controller + */ +static void udc_disable(struct pxa27x_udc *dev) +{ + UDCICR0 = UDCICR1 = 0x00000000; + + udc_clear_mask_UDCCR(UDCCR_UDE); + + /* Disable clock for USB device */ + pxa_set_cken(CKEN11_USB, 0); + + ep0_idle (dev); + dev->gadget.speed = USB_SPEED_UNKNOWN; + LED_CONNECTED_OFF; +} + + +/* + * udc_reinit - initialize software state + */ +static void udc_reinit(struct pxa27x_udc *dev) +{ + u32 i; + + dev->ep0state = EP0_IDLE; + + /* basic endpoint records init */ + for (i = 0; i < UDC_EP_NUM; i++) { + struct pxa27x_ep *ep = &dev->ep[i]; + + ep->stopped = 0; + ep->pio_irqs = ep->dma_irqs = 0; + } + dev->configuration = 0; + dev->interface = 0; + dev->alternate = 0; + /* the rest was statically initialized, and is read-only */ +} + +/* until it's enabled, this UDC should be completely invisible + * to any USB host. + */ +static void udc_enable (struct pxa27x_udc *dev) +{ + udc_clear_mask_UDCCR(UDCCR_UDE); + + MST_MSCWR2 &= ~(MST_MSCWR2_nUSBC_SC); + + /* Enable clock for USB device */ + pxa_set_cken(CKEN11_USB, 1); + + UDCICR0 = UDCICR1 = 0; + + ep0_idle(dev); + dev->gadget.speed = USB_SPEED_FULL; + dev->stats.irqs = 0; + + udc_set_mask_UDCCR(UDCCR_UDE); + udelay (2); + if (UDCCR & UDCCR_EMCE) + { + printk(KERN_ERR ": There are error in configuration, udc disabled\n"); + } + + /* caller must be able to sleep in order to cope + * with startup transients. + */ + msleep(100); + + /* enable suspend/resume and reset irqs */ + UDCICR1 = UDCICR1_IECC | UDCICR1_IERU | UDCICR1_IESU | UDCICR1_IERS; + + /* enable ep0 irqs */ + UDCICR0 = UDCICR_INT(0,UDCICR_INT_MASK); +#if 0 + for(i=1; i < UDC_EP_NUM; i++) { + if (dev->ep[i].assigned) + pio_irq_enable(i); + } +#endif +} + + +/* when a driver is successfully registered, it will receive + * control requests including set_configuration(), which enables + * non-control requests. then usb traffic follows until a + * disconnect is reported. then a host may connect again, or + * the driver might get unbound. + */ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct pxa27x_udc *dev = the_controller; + int retval; +#if 0 + DMSG("dev=0x%x, driver=0x%x, speed=%d," + "bind=0x%x, unbind=0x%x, disconnect=0x%x, setup=0x%x\n", + (unsigned)dev, (unsigned)driver, driver->speed, + (unsigned)driver->bind, (unsigned)driver->unbind, + (unsigned)driver->disconnect, (unsigned)driver->setup); +#endif + if (!driver || driver->speed != USB_SPEED_FULL + || !driver->bind + || !driver->unbind + || !driver->disconnect + || !driver->setup) + return -EINVAL; + if (!dev) + return -ENODEV; + if (dev->driver) + return -EBUSY; + + /* first hook up the driver ... */ + dev->driver = driver; + dev->gadget.dev.driver = &driver->driver; + + device_add (&dev->gadget.dev); + retval = driver->bind(&dev->gadget); + if (retval) { + DMSG("bind to driver %s --> error %d\n", + driver->driver.name, retval); + device_del (&dev->gadget.dev); + + dev->driver = 0; + dev->gadget.dev.driver = 0; + return retval; + } + device_create_file(dev->dev, &dev_attr_function); + + /* ... then enable host detection and ep0; and we're ready + * for set_configuration as well as eventual disconnect. + * NOTE: this shouldn't power up until later. + */ + DMSG("registered gadget driver '%s'\n", driver->driver.name); + udc_enable(dev); + dump_state(dev); + return 0; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + +static void +stop_activity(struct pxa27x_udc *dev, struct usb_gadget_driver *driver) +{ + int i; + + DMSG("Trace path 1\n"); + /* don't disconnect drivers more than once */ + if (dev->gadget.speed == USB_SPEED_UNKNOWN) + driver = 0; + dev->gadget.speed = USB_SPEED_UNKNOWN; + + /* prevent new request submissions, kill any outstanding requests */ + for (i = 0; i < UDC_EP_NUM; i++) { + struct pxa27x_ep *ep = &dev->ep[i]; + + ep->stopped = 1; + nuke(ep, -ESHUTDOWN); + } + del_timer_sync(&dev->timer); + + /* report disconnect; the driver is already quiesced */ + if (driver) + driver->disconnect(&dev->gadget); + + /* re-init driver-visible data structures */ + udc_reinit(dev); +} + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct pxa27x_udc *dev = the_controller; + + if (!dev) + return -ENODEV; + if (!driver || driver != dev->driver) + return -EINVAL; + + local_irq_disable(); + udc_disable(dev); + stop_activity(dev, driver); + local_irq_enable(); + + driver->unbind(&dev->gadget); + dev->driver = 0; + + device_del (&dev->gadget.dev); + device_remove_file(dev->dev, &dev_attr_function); + + DMSG("unregistered gadget driver '%s'\n", driver->driver.name); + dump_state(dev); + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +#ifndef enable_disconnect_irq +#define enable_disconnect_irq() do {} while (0) +#define disable_disconnect_irq() do {} while (0) +#endif + + +/*-------------------------------------------------------------------------*/ + +static inline void clear_ep_state (struct pxa27x_udc *dev) +{ + unsigned i; + + /* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint + * fifos, and pending transactions mustn't be continued in any case. + */ + for (i = 1; i < UDC_EP_NUM; i++) + nuke(&dev->ep[i], -ECONNABORTED); +} + +static void udc_watchdog(unsigned long _dev) +{ + struct pxa27x_udc *dev = (void *)_dev; + + local_irq_disable(); + if (dev->ep0state == EP0_STALL + && (UDCCSR0 & UDCCSR0_FST) == 0 + && (UDCCSR0 & UDCCSR0_SST) == 0) { + UDCCSR0 = UDCCSR0_FST|UDCCSR0_FTF; + DBG(DBG_VERBOSE, "ep0 re-stall\n"); + start_watchdog(dev); + } + local_irq_enable(); +} + +static void handle_ep0 (struct pxa27x_udc *dev) +{ + u32 udccsr0 = UDCCSR0; + struct pxa27x_ep *ep = &dev->ep [0]; + struct pxa27x_request *req; + union { + struct usb_ctrlrequest r; + u8 raw [8]; + u32 word [2]; + } u; + + if (list_empty(&ep->queue)) + req = 0; + else + req = list_entry(ep->queue.next, struct pxa27x_request, queue); + + /* clear stall status */ + if (udccsr0 & UDCCSR0_SST) { + nuke(ep, -EPIPE); + UDCCSR0 = UDCCSR0_SST; + del_timer(&dev->timer); + ep0_idle(dev); + } + + /* previous request unfinished? non-error iff back-to-back ... */ + if ((udccsr0 & UDCCSR0_SA) != 0 && dev->ep0state != EP0_IDLE) { + nuke(ep, 0); + del_timer(&dev->timer); + ep0_idle(dev); + } + + switch (dev->ep0state) { + case EP0_NO_ACTION: + printk(KERN_INFO"%s: Busy\n", __FUNCTION__); + /*Fall through */ + case EP0_IDLE: + /* late-breaking status? */ + udccsr0 = UDCCSR0; + + /* start control request? */ + if (likely((udccsr0 & (UDCCSR0_OPC|UDCCSR0_SA|UDCCSR0_RNE)) + == (UDCCSR0_OPC|UDCCSR0_SA|UDCCSR0_RNE))) { + int i; + + nuke (ep, -EPROTO); + /* read SETUP packet */ + for (i = 0; i < 2; i++) { + if (unlikely(!(UDCCSR0 & UDCCSR0_RNE))) { +bad_setup: + DMSG("SETUP %d!\n", i); + goto stall; + } + u.word [i] = UDCDR0; + } + if (unlikely((UDCCSR0 & UDCCSR0_RNE) != 0)) + goto bad_setup; + + le16_to_cpus (&u.r.wValue); + le16_to_cpus (&u.r.wIndex); + le16_to_cpus (&u.r.wLength); + + LED_EP0_ON; + + DBG(DBG_VERBOSE, "SETUP %02x.%02x v%04x i%04x l%04x\n", + u.r.bRequestType, u.r.bRequest, + u.r.wValue, u.r.wIndex, u.r.wLength); + /* cope with automagic for some standard requests. */ + dev->req_std = (u.r.bRequestType & USB_TYPE_MASK) + == USB_TYPE_STANDARD; + dev->req_config = 0; + dev->req_pending = 1; +#if 0 + switch (u.r.bRequest) { + /* hardware was supposed to hide this */ + case USB_REQ_SET_CONFIGURATION: + case USB_REQ_SET_INTERFACE: + case USB_REQ_SET_ADDRESS: + printk(KERN_ERR "Should not come here\n"); + break; + } + +#endif + if (u.r.bRequestType & USB_DIR_IN) + dev->ep0state = EP0_IN_DATA_PHASE; + else + dev->ep0state = EP0_OUT_DATA_PHASE; + i = dev->driver->setup(&dev->gadget, &u.r); + + if (i < 0) { + /* hardware automagic preventing STALL... */ + if (dev->req_config) { + /* hardware sometimes neglects to tell + * tell us about config change events, + * so later ones may fail... + */ + WARN("config change %02x fail %d?\n", + u.r.bRequest, i); + return; + /* TODO experiment: if has_cfr, + * hardware didn't ACK; maybe we + * could actually STALL! + */ + } + DBG(DBG_VERBOSE, "protocol STALL, " + "%02x err %d\n", UDCCSR0, i); +stall: + /* the watchdog timer helps deal with cases + * where udc seems to clear FST wrongly, and + * then NAKs instead of STALLing. + */ + ep0start(dev, UDCCSR0_FST|UDCCSR0_FTF, "stall"); + start_watchdog(dev); + dev->ep0state = EP0_STALL; + LED_EP0_OFF; + + /* deferred i/o == no response yet */ + } else if (dev->req_pending) { + if (likely(dev->ep0state == EP0_IN_DATA_PHASE + || dev->req_std || u.r.wLength)) + ep0start(dev, 0, "defer"); + else + ep0start(dev, UDCCSR0_IPR, "defer/IPR"); + } + + /* expect at least one data or status stage irq */ + return; + + } else { + /* some random early IRQ: + * - we acked FST + * - IPR cleared + * - OPC got set, without SA (likely status stage) + */ + UDCCSR0 = udccsr0 & (UDCCSR0_SA|UDCCSR0_OPC); + } + break; + case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ + if (udccsr0 & UDCCSR0_OPC) { + UDCCSR0 = UDCCSR0_OPC|UDCCSR0_FTF; + DBG(DBG_VERBOSE, "ep0in premature status\n"); + if (req) + done(ep, req, 0); + ep0_idle(dev); + } else /* irq was IPR clearing */ { + if (req) { + /* this IN packet might finish the request */ + (void) write_ep0_fifo(ep, req); + } /* else IN token before response was written */ + } + break; + case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */ + if (udccsr0 & UDCCSR0_OPC) { + if (req) { + /* this OUT packet might finish the request */ + if (read_ep0_fifo(ep, req)) + done(ep, req, 0); + /* else more OUT packets expected */ + } /* else OUT token before read was issued */ + } else /* irq was IPR clearing */ { + DBG(DBG_VERBOSE, "ep0out premature status\n"); + if (req) + done(ep, req, 0); + ep0_idle(dev); + } + break; + case EP0_STALL: + UDCCSR0 = UDCCSR0_FST; + break; + } + UDCISR0 = UDCISR_INT(0, UDCISR_INT_MASK); +} + + +static void handle_ep(struct pxa27x_ep *ep) +{ + struct pxa27x_request *req; + int completed; + u32 udccsr=0; + + DMSG("%s is called\n", __FUNCTION__); + do { + completed = 0; + if (likely (!list_empty(&ep->queue))) { + req = list_entry(ep->queue.next, + struct pxa27x_request, queue); + } else + req = 0; + +// udccsr = *ep->reg_udccsr; + DMSG("%s: req:%p, udcisr0:0x%x udccsr %p:0x%x\n", __FUNCTION__, + req, UDCISR0, ep->reg_udccsr, *ep->reg_udccsr); + if (unlikely(ep->dir_in)) { + udccsr = (UDCCSR_SST | UDCCSR_TRN) & *ep->reg_udccsr; + if (unlikely (udccsr)) + *ep->reg_udccsr = udccsr; + + if (req && likely ((*ep->reg_udccsr & UDCCSR_FS) != 0)) + completed = write_fifo(ep, req); + + } else { + udccsr = (UDCCSR_SST | UDCCSR_TRN) & *ep->reg_udccsr; + if (unlikely(udccsr)) + *ep->reg_udccsr = udccsr; + + /* fifos can hold packets, ready for reading... */ + if (likely(req)) { + completed = read_fifo(ep, req); + } else { + pio_irq_disable (ep->ep_num); + *ep->reg_udccsr = UDCCSR_FEF; + DMSG("%s: no req for out data\n", + __FUNCTION__); + } + } + ep->pio_irqs++; + } while (completed); +} + +static void pxa27x_change_configuration (struct pxa27x_udc *dev) +{ + struct usb_ctrlrequest req ; + + req.bRequestType = 0; + req.bRequest = USB_REQ_SET_CONFIGURATION; + req.wValue = dev->configuration; + req.wIndex = 0; + req.wLength = 0; + + dev->ep0state = EP0_NO_ACTION; + dev->driver->setup(&dev->gadget, &req); + +} + +static void pxa27x_change_interface (struct pxa27x_udc *dev) +{ + struct usb_ctrlrequest req; + + req.bRequestType = USB_RECIP_INTERFACE; + req.bRequest = USB_REQ_SET_INTERFACE; + req.wValue = dev->alternate; + req.wIndex = dev->interface; + req.wLength = 0; + + dev->ep0state = EP0_NO_ACTION; + dev->driver->setup(&dev->gadget, &req); +} + +/* + * pxa27x_udc_irq - interrupt handler + * + * avoid delays in ep0 processing. the control handshaking isn't always + * under software control (pxa250c0 and the pxa255 are better), and delays + * could cause usb protocol errors. + */ +static irqreturn_t +pxa27x_udc_irq(int irq, void *_dev, struct pt_regs *r) +{ + struct pxa27x_udc *dev = _dev; + int handled; + + dev->stats.irqs++; + HEX_DISPLAY(dev->stats.irqs); + +// printk("\n"); + DBG(DBG_VERBOSE, "Interrupt, UDCISR0:0x%08x, UDCISR1:0x%08x, " + "UDCCR:0x%08x\n", UDCISR0, UDCISR1, UDCCR); + do { + u32 udcir = UDCISR1 & 0xF8000000; + + handled = 0; + + /* SUSpend Interrupt Request */ + if (unlikely(udcir & UDCISR1_IRSU)) { + UDCISR1 = UDCISR1_IRSU; + handled = 1; + DBG(DBG_VERBOSE, "USB suspend\n"); + if (dev->gadget.speed != USB_SPEED_UNKNOWN + && dev->driver + && dev->driver->suspend) + dev->driver->suspend(&dev->gadget); + ep0_idle (dev); + } + + /* RESume Interrupt Request */ + if (unlikely(udcir & UDCISR1_IRRU)) { + UDCISR1 = UDCISR1_IRRU; + handled = 1; + DBG(DBG_VERBOSE, "USB resume\n"); + + if (dev->gadget.speed != USB_SPEED_UNKNOWN + && dev->driver + && dev->driver->resume) + dev->driver->resume(&dev->gadget); + } + + if (unlikely(udcir & UDCISR1_IRCC)) { + unsigned config, interface, alternate; + + handled = 1; + DBG(DBG_VERBOSE, "USB SET_CONFIGURATION or " + "SET_INTERFACE command received\n"); + + UDCCR |= UDCCR_SMAC; + + config = (UDCCR & UDCCR_ACN) >> UDCCR_ACN_S; + + if (dev->configuration != config) { + dev->configuration = config; + pxa27x_change_configuration(dev) ; + } + + interface = (UDCCR & UDCCR_AIN) >> UDCCR_AIN_S; + alternate = (UDCCR & UDCCR_AAISN) >> UDCCR_AAISN_S; + + if ( (dev->configuration != interface) || \ + (dev->alternate != alternate)){ + dev->interface = config; + dev->alternate = alternate; + pxa27x_change_interface(dev); + } + + UDCISR1 = UDCISR1_IRCC; + DMSG("%s: con:%d,inter:%d,alt:%d\n", + __FUNCTION__, config,interface, alternate); + } + + /* ReSeT Interrupt Request - USB reset */ + if (unlikely(udcir & UDCISR1_IRRS)) { + UDCISR1 = UDCISR1_IRRS; + handled = 1; + + if ((UDCCR & UDCCR_UDA) == 0) { + DBG(DBG_VERBOSE, "USB reset start\n"); + + /* reset driver and endpoints, + * in case that's not yet done + */ + stop_activity (dev, dev->driver); + + } + INFO("USB reset\n"); + dev->gadget.speed = USB_SPEED_FULL; + memset(&dev->stats, 0, sizeof dev->stats); + + } else { + u32 udcisr0 = UDCISR0 ; + u32 udcisr1 = UDCISR1 & 0xFFFF; + int i; + + if (unlikely (!udcisr0 && !udcisr1)) + continue; + + DBG(DBG_VERY_NOISY, "irq %02x.%02x\n", udcisr1,udcisr0); + + /* control traffic */ + if (udcisr0 & UDCISR0_IR0) { + dev->ep[0].pio_irqs++; + handle_ep0(dev); + handled = 1; + } + + udcisr0 >>= 2; + /* endpoint data transfers */ + for (i = 1; udcisr0!=0 && i < 16; udcisr0>>=2,i++) { + UDCISR0 = UDCISR_INT(i, UDCISR_INT_MASK); + + if (udcisr0 & UDC_INT_FIFOERROR) + printk(KERN_ERR" Endpoint %d Fifo error\n", i); + if (udcisr0 & UDC_INT_PACKETCMP) { + handle_ep(&dev->ep[i]); + handled = 1; + } + + } + + for (i = 0; udcisr1!=0 && i < 8; udcisr1 >>= 2, i++) { + UDCISR1 = UDCISR_INT(i, UDCISR_INT_MASK); + + if (udcisr1 & UDC_INT_FIFOERROR) { + printk(KERN_ERR" Endpoint %d fifo error\n", (i+16)); + } + + if (udcisr1 & UDC_INT_PACKETCMP) { + handle_ep(&dev->ep[i+16]); + handled = 1; + } + } + } + + /* we could also ask for 1 msec SOF (SIR) interrupts */ + + } while (handled); + return IRQ_HANDLED; +} + +static inline void validate_fifo_size(struct pxa27x_ep *pxa_ep, u8 bmAttributes) +{ + switch (bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_CONTROL: + pxa_ep->fifo_size = EP0_FIFO_SIZE; + break; + case USB_ENDPOINT_XFER_ISOC: + pxa_ep->fifo_size = ISO_FIFO_SIZE; + break; + case USB_ENDPOINT_XFER_BULK: + pxa_ep->fifo_size = BULK_FIFO_SIZE; + break; + case USB_ENDPOINT_XFER_INT: + pxa_ep->fifo_size = INT_FIFO_SIZE; + break; + default: + break; + } +} + +static void udc_init_ep(struct pxa27x_udc *dev) +{ + int i; + + INIT_LIST_HEAD (&dev->gadget.ep_list); + INIT_LIST_HEAD (&dev->gadget.ep0->ep_list); + + for (i = 0; i < UDC_EP_NUM; i++) { + struct pxa27x_ep *ep = &dev->ep[i]; + + ep->dma = -1; + if (i != 0) { + memset(ep, 0, sizeof(*ep)); + } + INIT_LIST_HEAD (&ep->queue); + } +} +#define NAME_SIZE 18 + +struct usb_ep* pxa27x_ep_config( + struct usb_gadget *gadget, + struct usb_endpoint_descriptor *desc, + int config, int interface, int alt +) +{ + u32 tmp ; + unsigned i; + char* name; + struct usb_ep * ep = NULL; + struct pxa27x_ep *pxa_ep = NULL; + struct pxa27x_udc *dev = the_controller; + + DMSG("pxa27x_config_ep is called\n"); + DMSG(" usb endpoint descriptor is:\n" + " bLength:%d\n" + " bDescriptorType:%x\n" + " bEndpointAddress:%x\n" + " bmAttributes:%x\n" + " wMaxPacketSize:%d\n", + desc->bLength, + desc->bDescriptorType,desc->bEndpointAddress, + desc->bmAttributes,desc->wMaxPacketSize); + + for (i = 1; i < UDC_EP_NUM; i++) { + if(!dev->ep[i].assigned) { + pxa_ep = &dev->ep[i]; + pxa_ep->assigned = 1; + pxa_ep->ep_num = i; + break; + } + } + if (unlikely(i == UDC_EP_NUM)) { + printk(KERN_ERR __FILE__ ": Failed to find a spare endpoint\n"); + return ep; + } + + + ep = &pxa_ep->ep; + + pxa_ep->dev = dev; + pxa_ep->desc = desc; + pxa_ep->pio_irqs = pxa_ep->dma_irqs = 0; + pxa_ep->dma = -1; + + if (!(desc->bEndpointAddress & 0xF)) + desc->bEndpointAddress |= i; + + if (!(desc->wMaxPacketSize)) { + validate_fifo_size(pxa_ep, desc->bmAttributes); + desc->wMaxPacketSize = pxa_ep->fifo_size; + } else + pxa_ep->fifo_size = desc->wMaxPacketSize; + + pxa_ep->dir_in = (desc->bEndpointAddress & USB_DIR_IN) ? 1 : 0; + pxa_ep->ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + pxa_ep->stopped = 1; + pxa_ep->dma_con = 0; + pxa_ep->config = config; + pxa_ep->interface = interface; + pxa_ep->aisn = alt; + + pxa_ep->reg_udccsr = &UDCCSR0 + i; + pxa_ep->reg_udcbcr = &UDCBCR0 + i; + pxa_ep->reg_udcdr = &UDCDR0 + i ; + pxa_ep->reg_udccr = &UDCCRA - 1 + i; +#ifdef USE_DMA + pxa_ep->reg_drcmr = &DRCMR24 + i; +#endif + +#if 0 + DMSG("udccsr=0x%8x, udcbcr=0x%8x, udcdr=0x%8x," + "udccr0=0x%8x\n", + (unsigned)pxa_ep->reg_udccsr, + (unsigned)pxa_ep->reg_udcbcr, + (unsigned)pxa_ep->reg_udcdr, + (unsigned)pxa_ep->reg_udccr); +#endif + /* Configure UDCCR */ + tmp = 0; + tmp |= (pxa_ep->config << UDCCONR_CN_S) & UDCCONR_CN; + tmp |= (pxa_ep->interface << UDCCONR_IN_S) & UDCCONR_IN; + tmp |= (pxa_ep->aisn << UDCCONR_AISN_S) & UDCCONR_AISN; + tmp |= (desc->bEndpointAddress << UDCCONR_EN_S) & UDCCONR_EN; + tmp |= (pxa_ep->ep_type << UDCCONR_ET_S) & UDCCONR_ET; + tmp |= (pxa_ep->dir_in) ? UDCCONR_ED : 0; + tmp |= (min(pxa_ep->fifo_size, (unsigned)desc->wMaxPacketSize) \ + << UDCCONR_MPS_S ) & UDCCONR_MPS; + tmp |= UDCCONR_DE | UDCCONR_EE; +// tmp |= UDCCONR_EE; + + *pxa_ep->reg_udccr = tmp; + +#ifdef USE_DMA + /* Only BULK use DMA */ + if ((pxa_ep->ep_type & USB_ENDPOINT_XFERTYPE_MASK)\ + == USB_ENDPOINT_XFER_BULK) + *pxa_ep->reg_udccsr = UDCCSR_DME; +#endif + + DMSG("UDCCR: 0x%p is 0x%x\n", pxa_ep->reg_udccr,*pxa_ep->reg_udccr); + + /* Fill ep name*/ + name = kmalloc(NAME_SIZE, GFP_KERNEL); + if (!name) { + printk(KERN_ERR "%s: Error\n", __FUNCTION__); + return NULL; + } + + switch (pxa_ep->ep_type) { + case USB_ENDPOINT_XFER_BULK: + sprintf(name, "Bulk-%s-%d", (pxa_ep->dir_in ? "in":"out"), i); + break; + case USB_ENDPOINT_XFER_INT: + sprintf(name, "Interrupt-%s-%d", (pxa_ep->dir_in ? \ + "in":"out"), i); + break; + default: + sprintf(name, "endpoint-%s-%d", (pxa_ep->dir_in ? \ + "in":"out"), i); + break; + } + ep->name = name; + + ep->ops = &pxa27x_ep_ops; + ep->maxpacket = min((ushort)pxa_ep->fifo_size, desc->wMaxPacketSize); + + list_add_tail (&ep->ep_list, &gadget->ep_list); + return ep; +} + +EXPORT_SYMBOL(pxa27x_ep_config); + +/*-------------------------------------------------------------------------*/ + +static void nop_release (struct device *dev) +{ + DMSG("%s %s\n", __FUNCTION__, dev->bus_id); +} + +/* this uses load-time allocation and initialization (instead of + * doing it at run-time) to save code, eliminate fault paths, and + * be more obviously correct. + */ +static struct pxa27x_udc memory = { + .gadget = { + .ops = &pxa27x_udc_ops, + .ep0 = &memory.ep[0].ep, + .name = driver_name, + .dev = { + .bus_id = "gadget", + .release = nop_release, + }, + }, + + /* control endpoint */ + .ep[0] = { + .ep = { + .name = ep0name, + .ops = &pxa27x_ep_ops, + .maxpacket = EP0_FIFO_SIZE, + }, + .dev = &memory, + .reg_udccsr = &UDCCSR0, + .reg_udcdr = &UDCDR0, + } +}; + +#define CP15R0_VENDOR_MASK 0xffffe000 + +#define CP15R0_XSCALE_VALUE 0x69054000 /* intel/arm/xscale */ + +/* + * probe - binds to the platform device + */ +static int __init pxa27x_udc_probe(struct device *_dev) +{ + struct pxa27x_udc *dev = &memory; + int retval; + u32 chiprev; + + /* insist on Intel/ARM/XScale */ + asm("mrc%? p15, 0, %0, c0, c0" : "=r" (chiprev)); + if ((chiprev & CP15R0_VENDOR_MASK) != CP15R0_XSCALE_VALUE) { + printk(KERN_ERR "%s: not XScale!\n", driver_name); + return -ENODEV; + } + /* other non-static parts of init */ + dev->dev = _dev; + dev->mach = _dev->platform_data; + + init_timer(&dev->timer); + dev->timer.function = udc_watchdog; + dev->timer.data = (unsigned long) dev; + + device_initialize(&dev->gadget.dev); + dev->gadget.dev.parent = _dev; + dev->gadget.dev.dma_mask = _dev->dma_mask; + + the_controller = dev; + dev_set_drvdata(_dev, dev); + + udc_disable(dev); + udc_init_ep(dev); + udc_reinit(dev); + + /* irq setup after old hardware state is cleaned up */ + retval = request_irq(IRQ_USB, pxa27x_udc_irq, + SA_INTERRUPT, driver_name, dev); + if (retval != 0) { + printk(KERN_ERR "%s: can't get irq %i, err %d\n", + driver_name, IRQ_USB, retval); + return -EBUSY; + } + dev->got_irq = 1; + + create_proc_files(); + + return 0; +} + +static int __exit pxa27x_udc_remove(struct device *_dev) +{ + struct pxa27x_udc *dev = _dev->driver_data; + + udc_disable(dev); + remove_proc_files(); + usb_gadget_unregister_driver(dev->driver); + + if (dev->got_irq) { + free_irq(IRQ_USB, dev); + dev->got_irq = 0; + } + if (machine_is_lubbock() && dev->got_disc) { + free_irq(LUBBOCK_USB_DISC_IRQ, dev); + dev->got_disc = 0; + } + dev_set_drvdata(_dev, 0); + the_controller = 0; + return 0; +} + +#ifdef CONFIG_PM +static int pxa27x_udc_suspend(struct device *_dev, u32 state, u32 level) +{ + int i; + struct pxa27x_udc *dev = (struct pxa27x_udc*)dev_get_drvdata(_dev); + + DMSG("%s is called\n", __FUNCTION__); + if (level == SUSPEND_POWER_DOWN) { + DMSG("%s will go into SUSPEND_POWER_DOWN\n", __FUNCTION__); + dev->udccsr0 = UDCCSR0; + for(i=1; (iep[i].assigned) { + struct pxa27x_ep *ep = &dev->ep[i]; + + ep->udccsr_value = *ep->reg_udccsr; + ep->udccr_value = *ep->reg_udccr; + DMSG("EP%d, udccsr:0x%x, udccr:0x%x\n", + i, *ep->reg_udccsr, *ep->reg_udccr); + } + } + + udc_clear_mask_UDCCR(UDCCR_UDE); + pxa_set_cken(CKEN11_USB, 0); + MST_MSCWR2 |= MST_MSCWR2_nUSBC_SC; + } + + return 0; +} + +static int pxa27x_udc_resume(struct device *_dev, u32 level) +{ + int i; + struct pxa27x_udc *dev = (struct pxa27x_udc*)dev_get_drvdata(_dev); + + DMSG("%s is called\n", __FUNCTION__); + if (level == RESUME_POWER_ON) { + DMSG("%s: udc resume\n", __FUNCTION__); + + UDCCSR0 = dev->udccsr0 & (UDCCSR0_FST | UDCCSR0_DME); + for (i=1; i < UDC_EP_NUM; i++) { + if (dev->ep[i].assigned) { + struct pxa27x_ep *ep = &dev->ep[i]; + + *ep->reg_udccsr = ep->udccsr_value; + *ep->reg_udccr = ep->udccr_value; + DMSG("EP%d, udccsr:0x%x, udccr:0x%x\n", + i, *ep->reg_udccsr, *ep->reg_udccr); + } + } + udc_enable(dev); + /* OTGPH bit is set when sleep mode is entered. + * it indicates that OTG pad is retaining its state. + * Upon exit from sleep mode and before clearing OTGPH, + * Software must configure the USB OTG pad, UDC, and UHC + * to the state they were in before entering sleep mode.*/ + PSSR |= PSSR_OTGPH; + } + return 0; +} +#endif + +/*-------------------------------------------------------------------------*/ + +static struct device_driver udc_driver = { + .name = "pxa2xx-udc", + .bus = &platform_bus_type, + .probe = pxa27x_udc_probe, + .remove = __exit_p(pxa27x_udc_remove), + +#ifdef CONFIG_PM + // FIXME power management support + .suspend = pxa27x_udc_suspend, + .resume = pxa27x_udc_resume +#endif +}; + +static int __init udc_init(void) +{ + printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION); + return driver_register(&udc_driver); +} +module_init(udc_init); + +static void __exit udc_exit(void) +{ + driver_unregister(&udc_driver); +} +module_exit(udc_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Frank Becker, Robert Schwebel, David Brownell"); +MODULE_LICENSE("GPL"); + diff -uNr a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/pxa27x_udc.h --- a/drivers/usb/gadget/pxa27x_udc.h 1970-01-01 08:00:00.000000000 +0800 +++ b/drivers/usb/gadget/pxa27x_udc.h 2005-01-27 09:56:49.000000000 +0800 @@ -0,0 +1,332 @@ +/* + * linux/drivers/usb/gadget/pxa27x_udc.h + * Intel PXA27x on-chip full speed USB device controller + * + * Copyright (C) 2003 Robert Schwebel , Pengutronix + * Copyright (C) 2003 David Brownell + * Copyright (C) 2004 Intel Corporation + * + * 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 __LINUX_USB_GADGET_PXA27X_H +#define __LINUX_USB_GADGET_PXA27X_H + +#include + +struct pxa27x_udc; + +struct pxa27x_ep { + struct usb_ep ep; + struct pxa27x_udc *dev; + + const struct usb_endpoint_descriptor *desc; + struct list_head queue; + unsigned long pio_irqs; + unsigned long dma_irqs; + + int dma; + unsigned fifo_size; + unsigned ep_num; + unsigned ep_type; + + unsigned stopped : 1; + unsigned dma_con : 1; + unsigned dir_in : 1; + unsigned assigned : 1; + + unsigned config; + unsigned interface; + unsigned aisn; + /* UDCCSR = UDC Control/Status Register for this EP + * UBCR = UDC Byte Count Remaining (contents of OUT fifo) + * UDCDR = UDC Endpoint Data Register (the fifo) + * UDCCR = UDC Endpoint Configuration Registers + * DRCM = DMA Request Channel Map + */ + volatile u32 *reg_udccsr; + volatile u32 *reg_udcbcr; + volatile u32 *reg_udcdr; + volatile u32 *reg_udccr; +#ifdef USE_DMA + volatile u32 *reg_drcmr; +#define drcmr(n) .reg_drcmr = & DRCMR ## n , +#else +#define drcmr(n) +#endif + +#ifdef CONFIG_PM + unsigned udccsr_value; + unsigned udccr_value; +#endif +}; + +struct pxa27x_request { + struct usb_request req; + struct list_head queue; +}; + +enum ep0_state { + EP0_IDLE, + EP0_IN_DATA_PHASE, + EP0_OUT_DATA_PHASE, +// EP0_END_XFER, + EP0_STALL, + EP0_NO_ACTION +}; + +#define EP0_FIFO_SIZE ((unsigned)16) +#define BULK_FIFO_SIZE ((unsigned)64) +#define ISO_FIFO_SIZE ((unsigned)256) +#define INT_FIFO_SIZE ((unsigned)8) + +struct udc_stats { + struct ep0stats { + unsigned long ops; + unsigned long bytes; + } read, write; + unsigned long irqs; +}; + +#ifdef CONFIG_USB_PXA27X_SMALL +/* when memory's tight, SMALL config saves code+data. */ +//#undef USE_DMA +//#define UDC_EP_NUM 3 +#endif + +#ifndef UDC_EP_NUM +#define UDC_EP_NUM 24 +#endif + +struct pxa27x_udc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + + enum ep0_state ep0state; + struct udc_stats stats; + unsigned got_irq : 1, + got_disc : 1, + has_cfr : 1, + req_pending : 1, + req_std : 1, + req_config : 1; + +#define start_watchdog(dev) mod_timer(&dev->timer, jiffies + (HZ/200)) + struct timer_list timer; + + struct device *dev; + struct pxa27x_udc_mach_info *mach; + u64 dma_mask; + struct pxa27x_ep ep [UDC_EP_NUM]; + + unsigned configuration, + interface, + alternate; +#ifdef CONFIG_PM + unsigned udccsr0; +#endif +}; + +/*-------------------------------------------------------------------------*/ +#if 0 +#ifdef DEBUG +#define HEX_DISPLAY(n) do { \ + if (machine_is_mainstone())\ + { MST_LEDDAT1 = (n); } \ + } while(0) + +#define HEX_DISPLAY1(n) HEX_DISPLAY(n) + +#define HEX_DISPLAY2(n) do { \ + if (machine_is_mainstone()) \ + { MST_LEDDAT2 = (n); } \ + } while(0) + +#endif /* DEBUG */ +#endif +/*-------------------------------------------------------------------------*/ + +/* LEDs are only for debug */ +#ifndef HEX_DISPLAY +#define HEX_DISPLAY(n) do {} while(0) +#endif + +#ifndef LED_CONNECTED_ON +#define LED_CONNECTED_ON do {} while(0) +#define LED_CONNECTED_OFF do {} while(0) +#endif +#ifndef LED_EP0_ON +#define LED_EP0_ON do {} while (0) +#define LED_EP0_OFF do {} while (0) +#endif + +static struct pxa27x_udc *the_controller; + +#if 0 +/*-------------------------------------------------------------------------*/ + + +/* one GPIO should be used to detect host disconnect */ +static inline int is_usb_connected(void) +{ + if (!the_controller->mach->udc_is_connected) + return 1; + return the_controller->mach->udc_is_connected(); +} + +/* one GPIO should force the host to see this device (or not) */ +static inline void make_usb_disappear(void) +{ + if (!the_controller->mach->udc_command) + return; + the_controller->mach->udc_command(PXA27X_UDC_CMD_DISCONNECT); +} + +static inline void let_usb_appear(void) +{ + if (!the_controller->mach->udc_command) + return; + the_controller->mach->udc_command(PXA2XX_UDC_CMD_CONNECT); +} +#endif + +/*-------------------------------------------------------------------------*/ + +/* + * Debugging support vanishes in non-debug builds. DBG_NORMAL should be + * mostly silent during normal use/testing, with no timing side-effects. + */ +#define DBG_NORMAL 1 /* error paths, device state transitions */ +#define DBG_VERBOSE 2 /* add some success path trace info */ +#define DBG_NOISY 3 /* ... even more: request level */ +#define DBG_VERY_NOISY 4 /* ... even more: packet level */ + +#ifdef DEBUG + +static const char *state_name[] = { + "EP0_IDLE", + "EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE", + "EP0_END_XFER", "EP0_STALL" +}; + +#define DMSG(stuff...) printk(KERN_ERR "udc: " stuff) + +#ifdef VERBOSE +# define UDC_DEBUG DBG_VERBOSE +#else +# define UDC_DEBUG DBG_NORMAL +#endif + +static void __attribute__ ((__unused__)) +dump_udccr(const char *label) +{ + u32 udccr = UDCCR; + DMSG("%s 0x%08x =%s%s%s%s%s%s%s%s%s%s, con=%d,inter=%d,altinter=%d\n", + label, udccr, + (udccr & UDCCR_OEN) ? " oen":"", + (udccr & UDCCR_AALTHNP) ? " aalthnp":"", + (udccr & UDCCR_AHNP) ? " rem" : "", + (udccr & UDCCR_BHNP) ? " rstir" : "", + (udccr & UDCCR_DWRE) ? " dwre" : "", + (udccr & UDCCR_SMAC) ? " smac" : "", + (udccr & UDCCR_EMCE) ? " emce" : "", + (udccr & UDCCR_UDR) ? " udr" : "", + (udccr & UDCCR_UDA) ? " uda" : "", + (udccr & UDCCR_UDE) ? " ude" : "", + (udccr & UDCCR_ACN) >> UDCCR_ACN_S, + (udccr & UDCCR_AIN) >> UDCCR_AIN_S, + (udccr & UDCCR_AAISN)>> UDCCR_AAISN_S ); +} + +static void __attribute__ ((__unused__)) +dump_udccsr0(const char *label) +{ + u32 udccsr0 = UDCCSR0; + + DMSG("%s %s 0x%08x =%s%s%s%s%s%s%s\n", + label, state_name[the_controller->ep0state], udccsr0, + (udccsr0 & UDCCSR0_SA) ? " sa" : "", + (udccsr0 & UDCCSR0_RNE) ? " rne" : "", + (udccsr0 & UDCCSR0_FST) ? " fst" : "", + (udccsr0 & UDCCSR0_SST) ? " sst" : "", + (udccsr0 & UDCCSR0_DME) ? " dme" : "", + (udccsr0 & UDCCSR0_IPR) ? " ipr" : "", + (udccsr0 & UDCCSR0_OPC) ? " opr" : ""); +} + +static void __attribute__ ((__unused__)) +dump_state(struct pxa27x_udc *dev) +{ + unsigned i; + + DMSG("%s, udcicr %02X.%02X, udcsir %02X.%02x, udcfnr %02X\n", + state_name[dev->ep0state], + UDCICR1, UDCICR0, UDCISR1, UDCISR0, UDCFNR); + dump_udccr("udccr"); + + if (!dev->driver) { + DMSG("no gadget driver bound\n"); + return; + } else + DMSG("ep0 driver '%s'\n", dev->driver->driver.name); + + + dump_udccsr0 ("udccsr0"); + DMSG("ep0 IN %lu/%lu, OUT %lu/%lu\n", + dev->stats.write.bytes, dev->stats.write.ops, + dev->stats.read.bytes, dev->stats.read.ops); + + for (i = 1; i < UDC_EP_NUM; i++) { + if (dev->ep [i].desc == 0) + continue; + DMSG ("udccs%d = %02x\n", i, *dev->ep->reg_udccsr); + } +} + +#if 0 +static void dump_regs(u8 ep) +{ + DMSG("EP:%d UDCCSR:0x%08x UDCBCR:0x%08x\n UDCCR:0x%08x\n", + ep,UDCCSN(ep), UDCBCN(ep), UDCCN(ep)); +} +static void dump_req (struct pxa27x_request *req) +{ + struct usb_request *r = &req->req; + + DMSG("%s: buf:0x%08x length:%d dma:0x%08x actual:%d\n", + __FUNCTION__, (unsigned)r->buf, r->length, + r->dma, r->actual); +} +#endif + +#else + +#define DMSG(stuff...) do{}while(0) + +#define dump_udccr(x) do{}while(0) +#define dump_udccsr0(x) do{}while(0) +#define dump_state(x) do{}while(0) + +#define UDC_DEBUG ((unsigned)0) + +#endif + +#define DBG(lvl, stuff...) do{if ((lvl) <= UDC_DEBUG) DMSG(stuff);}while(0) + +#define WARN(stuff...) printk(KERN_WARNING "udc: " stuff) +#define INFO(stuff...) printk(KERN_INFO "udc: " stuff) + + +#endif /* __LINUX_USB_GADGET_PXA27X_H */ diff -uNr a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c --- a/drivers/usb/gadget/serial.c 2004-10-19 05:54:55.000000000 +0800 +++ b/drivers/usb/gadget/serial.c 2005-01-27 09:56:49.000000000 +0800 @@ -1249,6 +1249,39 @@ } } +#ifdef CONFIG_USB_PXA27X +static int gs_ep_config(struct usb_gadget *gadget) +{ + struct usb_ep *ep = NULL; + + ep = pxa27x_ep_config(gadget, &gs_fullspeed_in_desc, \ + gs_config_desc.bConfigurationValue, \ + gs_interface_desc.bInterfaceNumber, \ + gs_interface_desc.bAlternateSetting); + if (unlikely(!ep)) { + printk(KERN_ERR "%s: Failed to config endpoint\n", + __FUNCTION__); + return -1; + } + EP_IN_NAME = ep->name; + ep->driver_data = ep; + + ep = pxa27x_ep_config(gadget, &gs_fullspeed_out_desc, \ + gs_config_desc.bConfigurationValue, \ + gs_interface_desc.bInterfaceNumber, \ + gs_interface_desc.bAlternateSetting); + if (unlikely(!ep)) { + printk(KERN_ERR "%s: Failed to config endpoint\n", + __FUNCTION__); + return -1; + } + EP_OUT_NAME = ep->name; + ep->driver_data = ep; + + return 0; +} +#endif + /* Gadget Driver */ /* @@ -1264,7 +1297,9 @@ struct gs_dev *dev; usb_ep_autoconfig_reset(gadget); - +#ifdef CONFIG_USB_PXA27X + gs_ep_config(gadget); +#else ep = usb_ep_autoconfig(gadget, &gs_fullspeed_in_desc); if (!ep) goto autoconf_fail; @@ -1276,6 +1311,7 @@ goto autoconf_fail; EP_OUT_NAME = ep->name; ep->driver_data = ep; /* claim the endpoint */ +#endif /* device specific bcdDevice value in device descriptor */ if (gadget_is_net2280(gadget)) { diff -uNr a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c --- a/drivers/usb/host/ohci-hcd.c 2004-10-19 05:53:12.000000000 +0800 +++ b/drivers/usb/host/ohci-hcd.c 2005-01-27 09:56:49.000000000 +0800 @@ -810,10 +810,15 @@ #include "ohci-lh7a404.c" #endif +#ifdef CONFIG_PXA27x +#include "ohci-pxa.c" +#endif + #if !(defined(CONFIG_PCI) \ || defined(CONFIG_SA1111) \ || defined(CONFIG_ARCH_OMAP) \ || defined (CONFIG_ARCH_LH7A404) \ + || defined (CONFIG_PXA27x) \ ) #error "missing bus glue for ohci-hcd" #endif diff -uNr a/drivers/usb/host/ohci-pxa.c b/drivers/usb/host/ohci-pxa.c --- a/drivers/usb/host/ohci-pxa.c 1970-01-01 08:00:00.000000000 +0800 +++ b/drivers/usb/host/ohci-pxa.c 2005-01-27 09:56:49.000000000 +0800 @@ -0,0 +1,505 @@ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2002 David Brownell + * (C) Copyright 2002 Hewlett-Packard Company + * (C) Copyright 2004 Intel Company + * + * PXA Bus Glue + * + * Written by Christopher Hoover + * Based on fragments of previous driver by Rusell King et al. + * + * Modified for PXA27x from ohci-sa1111.c by Intel. + * + * This file is licenced under the GPL. + */ + +#include +#include +#include + +extern int usb_disabled(void); + +#define PMM_NPS_MODE 1 +#define PMM_GLOBAL_MODE 2 +#define PMM_PERPORT_MODE 3 + +#define PXA_UHC_MAX_PORTNUM 3 + +static int pxa_ohci_pmm_state; + +/*-------------------------------------------------------------------------*/ +static void pxa_hcd_set_gpio(void) +{ + pxa_gpio_mode(GPIO88_USBH1_PWR_MD); + pxa_gpio_mode(GPIO89_USBH1_PEN_MD); +} + +static int pxa_hcd_select_pmm( int mode ) +{ + pxa_ohci_pmm_state = mode; + switch ( mode ) { + case PMM_NPS_MODE: + UHCRHDA |= RH_A_NPS; + break; + case PMM_GLOBAL_MODE: + UHCRHDA &= ~(RH_A_NPS & RH_A_PSM); + break; + case PMM_PERPORT_MODE: + UHCRHDA &= ~(RH_A_NPS); + UHCRHDA |= RH_A_PSM; + + /* Set port power control mask bits, only 3 ports. */ + UHCRHDB |= (0x7<<17); + break; + default: + printk( KERN_ERR + "Invalid mode %d, set to non-power switch mode.\n", + mode ); + + UHCRHDA |= RH_A_NPS; + } + + return 0; +} + +static int pxa_hcd_set_port_power( int port ) +{ + if ((pxa_ohci_pmm_state == PMM_PERPORT_MODE) + && (port > 0) && (port < PXA_UHC_MAX_PORTNUM) ) { + UHCRHPS(port) |= 0x100; + return 0; + } + return -1; +} + +/* + If you select PMM_PERPORT_MODE, you should set the port power +*/ +static int pxa_hcd_clear_port_power( int port ) +{ + if ((pxa_ohci_pmm_state == PMM_PERPORT_MODE) + && (port > 0) && (port < PXA_UHC_MAX_PORTNUM) ) { + UHCRHPS(port) |= 0x200; + return 0; + } + + return -1; +} + +static void pxa_start_hc(struct platform_device *dev) +{ + printk(KERN_DEBUG"OHCI_PXA: starting PXA OHCI USB Controller\n"); + + pxa_hcd_set_gpio(); + + CKEN |= CKEN10_USBHOST; + + UHCHR |= UHCHR_FHR; + udelay(11); + UHCHR &= ~UHCHR_FHR; + + UHCHR |= UHCHR_FSBIR; + while ( UHCHR & UHCHR_FSBIR ) + ; + + if (machine_is_mainstone()) + UHCHR |= (UHCHR_PCPL | UHCHR_PSPL); + + UHCHR &= ~UHCHR_SSEP3; + UHCHR &= ~UHCHR_SSEP2; + UHCHR &= ~UHCHR_SSEP1; + UHCHR &= ~UHCHR_SSE; +} + +static void pxa_stop_hc(struct platform_device *dev) +{ + printk(KERN_DEBUG"OHCI_PXA: stopping PXA OHCI USB Controller\n"); + + /* + * Put the USB host controller into reset. + */ + UHCHR |= (UHCHR_FHR|UHCHR_FSBIR); + + /* + * Stop the USB clock. + */ + CKEN &= ~CKEN10_USBHOST; +} + +/*-------------------------------------------------------------------------*/ +static irqreturn_t usb_hcd_pxa_hcim_irq (int irq, void *__hcd, struct pt_regs * r) +{ + struct usb_hcd *hcd = __hcd; + + return usb_hcd_irq(irq, hcd, r); +} + +/*-------------------------------------------------------------------------*/ + +void usb_hcd_pxa_remove (struct usb_hcd *, struct platform_device *); + +/* configure so an HC device and id are always provided */ +/* always called with process context; sleeping is OK */ + + +/** + * usb_hcd_pxa_probe - initialize PXA-based HCDs + * Context: !in_interrupt() + * + * Allocates basic resources for this USB host controller, and + * then invokes the start() method for the HCD associated with it + * through the hotplug entry's driver_data. + * + * Store this function in the HCD's struct pci_driver as probe(). + */ +int usb_hcd_pxa_probe (const struct hc_driver *driver, + struct usb_hcd **hcd_out, + struct platform_device *dev) +{ + int retval; + struct usb_hcd *hcd = 0; + + if (!request_mem_region(dev->resource[0].start, + dev->resource[0].end - + dev->resource[0].start + 1, hcd_name)) { + dbg("request_mem_region failed"); + return -EBUSY; + } + + pxa_start_hc(dev); + + pxa_hcd_select_pmm( PMM_PERPORT_MODE ); + if ( pxa_hcd_set_port_power( 1 )<0 ) { + printk(KERN_ERR "Setting port 1 power failed.\n"); + } + + if ( pxa_hcd_clear_port_power( 2 )<0 ) { + printk(KERN_ERR "Setting port 2 power failed.\n"); + } + + if ( pxa_hcd_clear_port_power( 3 )<0 ) { + printk(KERN_ERR "Setting port 3 power failed.\n"); + } + + hcd = driver->hcd_alloc (); + if (hcd == NULL){ + dbg ("hcd_alloc failed"); + retval = -ENOMEM; + goto err1; + } + + hcd->driver = (struct hc_driver *) driver; + hcd->description = driver->description; + hcd->irq = dev->resource[1].start; + hcd->regs = (void*) io_p2v(dev->resource[0].start); + hcd->self.controller = &dev->dev; + + retval = hcd_buffer_create (hcd); + if (retval != 0) { + dbg ("pool alloc fail"); + goto err1; + } + + retval = request_irq (hcd->irq, usb_hcd_pxa_hcim_irq, SA_INTERRUPT, + hcd->description, hcd); + if (retval != 0) { + dbg("request_irq failed"); + retval = -EBUSY; + goto err2; + } + + info ("%s (PXA) at 0x%p, irq %d\n", + hcd->description, hcd->regs, hcd->irq); + + usb_bus_init (&hcd->self); + hcd->self.op = &usb_hcd_operations; + hcd->self.hcpriv = (void *) hcd; + hcd->self.bus_name = "pxa27x"; + hcd->product_desc = "PXA27x OHCI"; + + INIT_LIST_HEAD (&hcd->dev_list); + + usb_register_bus (&hcd->self); + + if ((retval = driver->start (hcd)) < 0) + { + usb_hcd_pxa_remove(hcd, dev); + return retval; + } + + *hcd_out = hcd; + return 0; + + err2: + hcd_buffer_destroy (hcd); + if (hcd) + driver->hcd_free(hcd); + err1: + pxa_stop_hc(dev); + release_mem_region(dev->resource[0].start, + dev->resource[0].end - dev->resource[0].start + 1); + return retval; +} + + +/* may be called without controller electrically present */ +/* may be called with controller, bus, and devices active */ + +/** + * usb_hcd_pxa_remove - shutdown processing for PXA-based HCDs + * @dev: USB Host Controller being removed + * Context: !in_interrupt() + * + * Reverses the effect of usb_hcd_pxa_probe(), first invoking + * the HCD's stop() method. It is always called from a thread + * context, normally "rmmod", "apmd", or something similar. + * + */ +void usb_hcd_pxa_remove (struct usb_hcd *hcd, struct platform_device *dev) +{ + void *base; + info ("remove: %s, state %x", hcd->self.bus_name, hcd->state); + + if (in_interrupt ()) + BUG (); + + hcd->state = USB_STATE_QUIESCING; + + dbg ("%s: roothub graceful disconnect", hcd->self.bus_name); + usb_disconnect (&hcd->self.root_hub); + + hcd->driver->stop (hcd); + hcd->state = USB_STATE_HALT; + + free_irq (hcd->irq, hcd); + hcd_buffer_destroy (hcd); + + usb_deregister_bus (&hcd->self); + + base = hcd->regs; + hcd->driver->hcd_free (hcd); + + pxa_stop_hc(dev); + release_mem_region(dev->resource[0].start, + dev->resource[0].end - dev->resource[0].start + 1); +} + +/*-------------------------------------------------------------------------*/ + +static int __devinit +ohci_pxa_start (struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + int ret; + + ohci->hcca = dma_alloc_coherent (hcd->self.controller, + sizeof *ohci->hcca, &ohci->hcca_dma, 0); + if (!ohci->hcca) + return -ENOMEM; + + memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); + if ((ret = ohci_mem_init (ohci)) < 0) { + ohci_stop (hcd); + return ret; + } + ohci->regs = hcd->regs; + + if (hc_reset (ohci) < 0) { + ohci_stop (hcd); + return -ENODEV; + } + + if (hc_start (ohci) < 0) { + err ("can't start %s", ohci->hcd.self.bus_name); + ohci_stop (hcd); + return -EBUSY; + } + create_debug_files (ohci); + +#ifdef DEBUG + ohci_dump (ohci, 1); +#endif + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static const struct hc_driver ohci_pxa_hc_driver = { + .description = hcd_name, + + /* + * generic hardware linkage + */ + .irq = ohci_irq, + .flags = HCD_USB11, + + /* + * basic lifecycle operations + */ + .start = ohci_pxa_start, +#ifdef CONFIG_PM + /* suspend: ohci_pxa_suspend, -- tbd */ + /* resume: ohci_pxa_resume, -- tbd */ +#endif + .stop = ohci_stop, + + /* + * memory lifecycle (except per-request) + */ + .hcd_alloc = ohci_hcd_alloc, + .hcd_free = ohci_hcd_free, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ohci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, +#ifdef CONFIG_USB_SUSPEND + .hub_suspend = ohci_hub_suspend, + .hub_resume = ohci_hub_resume, +#endif +}; + +/*-------------------------------------------------------------------------*/ + +static int ohci_hcd_pxa_drv_probe(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = NULL; + int ret; + + if (usb_disabled()) + return -ENODEV; + + ret = usb_hcd_pxa_probe(&ohci_pxa_hc_driver, &hcd, pdev); + + if (ret == 0) + dev_set_drvdata(dev, hcd); + + return ret; +} + +static int ohci_hcd_pxa_drv_remove(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = dev_get_drvdata(dev); + + usb_hcd_pxa_remove(hcd, pdev); + + dev_set_drvdata(dev, NULL); + + return 0; +} + +static int ohci_hcd_pxa_drv_suspend(struct device * dev, u32 state, u32 level) +{ +// printk(KERN_ERR"state:%d, level:%d\n", state, level); + if ( level == SUSPEND_POWER_DOWN) + pxa_stop_hc(NULL); + return 0; +} + +static int ohci_hcd_pxa_drv_resume(struct device * dev, u32 level) +{ +// printk(KERN_ERR"level:%d\n", level); + if (level == RESUME_POWER_ON) { + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct ohci_hcd * ohci = hcd_to_ohci(hcd); + + pxa_start_hc(NULL); + if (hc_reset (ohci) < 0) { + ohci_stop (hcd); + return -ENODEV; + } + + if (hc_start (ohci) < 0) { + err ("can't start %s", ohci->hcd.self.bus_name); + ohci_stop (hcd); + return -EBUSY; + } + } + return 0; +} + +static u64 ohci_hcd_pxa_dmamask = 0xffffffffUL; + +static struct resource pxa_ohci_resources[] = { + [0] = { + .start= 0x4C000000, + .end= 0x4C000fff, + .flags= IORESOURCE_MEM, + }, + [1] = { + .start= IRQ_USBH1, + .end= IRQ_USBH1, + .flags= IORESOURCE_IRQ, + }, +}; + +static void ohci_hcd_pxa_device_release(struct device *dev) +{ + /* Keep this function empty. */ +} + +static struct platform_device ohci_hcd_pxa_device = { + .name = "pxa-ohci", + .id = -1, + .dev = { + .dma_mask = &ohci_hcd_pxa_dmamask, + .coherent_dma_mask = 0xffffffff, + .release = ohci_hcd_pxa_device_release, + }, + + .num_resources = ARRAY_SIZE(pxa_ohci_resources), + .resource = pxa_ohci_resources, +}; + +static struct device_driver ohci_hcd_pxa_driver = { + .name = "pxa-ohci", + .bus = &platform_bus_type, + .probe = ohci_hcd_pxa_drv_probe, + .remove = ohci_hcd_pxa_drv_remove, + .suspend = ohci_hcd_pxa_drv_suspend, + .resume = ohci_hcd_pxa_drv_resume, +}; + +static int __init ohci_hcd_pxa_init (void) +{ + int ret; + + dbg (DRIVER_INFO " (PXA-27x)"); + dbg ("block sizes: ed %d td %d", + sizeof (struct ed), sizeof (struct td)); + + ret = platform_device_register(&ohci_hcd_pxa_device); + if (!ret) { + ret = driver_register(&ohci_hcd_pxa_driver); + if (ret) + platform_device_unregister(&ohci_hcd_pxa_device); + } + + return ret; +} +static void __exit ohci_hcd_pxa_cleanup (void) +{ + driver_unregister(&ohci_hcd_pxa_driver); + platform_device_unregister(&ohci_hcd_pxa_device); +} + +module_init (ohci_hcd_pxa_init); +module_exit (ohci_hcd_pxa_cleanup); diff -uNr a/drivers/usb/Kconfig b/drivers/usb/Kconfig --- a/drivers/usb/Kconfig 2004-10-19 05:53:37.000000000 +0800 +++ b/drivers/usb/Kconfig 2005-01-27 09:56:49.000000000 +0800 @@ -7,7 +7,7 @@ # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. config USB tristate "Support for Host-side USB" - depends on PCI || SA1111 || ARCH_OMAP1510 || ARCH_OMAP1610 || ARCH_LH7A404 + depends on PCI || SA1111 || ARCH_OMAP1510 || ARCH_OMAP1610 || ARCH_LH7A404 || PXA27x ---help--- Universal Serial Bus (USB) is a specification for a serial bus subsystem which offers higher speeds and more features than the diff -uNr a/drivers/video/Kconfig b/drivers/video/Kconfig --- a/drivers/video/Kconfig 2004-10-19 05:53:07.000000000 +0800 +++ b/drivers/video/Kconfig 2005-01-27 09:56:49.000000000 +0800 @@ -970,6 +970,24 @@ If unsure, say N. +choice + prompt "PXA LCD type" + depends on FB_PXA + +config FB_PXA_LCD_QVGA + bool "QVGA(320x240)" + +config FB_PXA_LCD_VGA + bool "VGA (640x480)" + +endchoice + +config FB_PXA_OVERLAY + tristate "PXA LCD overlay support" + depends on FB_PXA + ---help--- + Frame buffer overlay driver for PXA27x + config FB_PXA_PARAMETERS bool "PXA LCD command line parameters" default n diff -uNr a/drivers/video/Makefile b/drivers/video/Makefile --- a/drivers/video/Makefile 2004-10-19 05:53:21.000000000 +0800 +++ b/drivers/video/Makefile 2005-01-27 09:56:49.000000000 +0800 @@ -89,6 +89,7 @@ obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o cfbfillrect.o cfbimgblt.o cfbcopyarea.o obj-$(CONFIG_FB_ASILIANT) += asiliantfb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o obj-$(CONFIG_FB_PXA) += pxafb.o cfbimgblt.o cfbcopyarea.o cfbfillrect.o +obj-$(CONFIG_FB_PXA_OVERLAY) += pxafb_overlay.o # Platform or fallback drivers go here obj-$(CONFIG_FB_VESA) += vesafb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o diff -uNr a/drivers/video/pxafb.c b/drivers/video/pxafb.c --- a/drivers/video/pxafb.c 2004-10-19 05:53:51.000000000 +0800 +++ b/drivers/video/pxafb.c 2005-01-27 09:56:49.000000000 +0800 @@ -58,11 +58,19 @@ #define LCCR0_INVALID_CONFIG_MASK (LCCR0_OUM|LCCR0_BM|LCCR0_QDM|LCCR0_DIS|LCCR0_EFM|LCCR0_IUM|LCCR0_SFM|LCCR0_LDM|LCCR0_ENB) #define LCCR3_INVALID_CONFIG_MASK (LCCR3_HSP|LCCR3_VSP|LCCR3_PCD|LCCR3_BPP) +wait_queue_head_t fcs_wait_eof; +int fcs_in_eof = 0; +static DECLARE_MUTEX(fcs_lcd_sem); +#ifdef CONFIG_FAST_DVFM +unsigned int fcs_clkcfg = 0; +unsigned int fcs_lcd_disable = 0; +#endif + static void (*pxafb_backlight_power)(int); static void (*pxafb_lcd_power)(int); static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *); -static void set_ctrlr_state(struct pxafb_info *fbi, u_int state); +void set_ctrlr_state(struct pxafb_info *fbi, u_int state); #ifdef CONFIG_FB_PXA_PARAMETERS #define PXAFB_OPTIONS_SIZE 256 @@ -190,6 +198,10 @@ case 4: ret = LCCR3_4BPP; break; case 8: ret = LCCR3_8BPP; break; case 16: ret = LCCR3_16BPP; break; + case 18: ret = LCCR3_18BPP; break; + case 19: ret = LCCR3_19BPP; break; + case 24: ret = LCCR3_24BPP; break; + case 25: ret = LCCR3_25BPP; break; } return ret; } @@ -245,18 +257,34 @@ * The pixel packing format is described on page 7-11 of the * PXA2XX Developer's Manual. */ - if (var->bits_per_pixel == 16) { - var->red.offset = 11; var->red.length = 5; - var->green.offset = 5; var->green.length = 6; - var->blue.offset = 0; var->blue.length = 5; - var->transp.offset = var->transp.length = 0; - } else { - var->red.offset = var->green.offset = var->blue.offset = var->transp.offset = 0; - var->red.length = 8; - var->green.length = 8; - var->blue.length = 8; - var->transp.length = 0; - } + switch (var->bits_per_pixel) { + case 16: + /* 2 pixels per line */ + var->red = def_rgb_16.red; + var->green = def_rgb_16.green; + var->blue = def_rgb_16.blue; + var->transp = def_rgb_16.transp; + break; + case 18: + case 19: + var->red = def_rgb_18.red; + var->green = def_rgb_18.green; + var->blue = def_rgb_18.blue; + var->transp = def_rgb_18.transp; + break; + case 24: + case 25: + var->red = def_rgb_24.red; + var->green = def_rgb_24.green; + var->blue = def_rgb_24.blue; + var->transp = def_rgb_24.transp; + break; + default: + var->red = def_rgb_8.red; + var->green = def_rgb_8.green; + var->blue = def_rgb_8.blue; + var->transp = def_rgb_8.transp; + } #ifdef CONFIG_CPU_FREQ DPRINTK("dma period = %d ps, clock = %d kHz\n", @@ -285,7 +313,8 @@ DPRINTK("set_par\n"); - if (var->bits_per_pixel == 16) + if (var->bits_per_pixel == 16 || var->bits_per_pixel == 18 ||var->bits_per_pixel == 19 + || var->bits_per_pixel == 24 || var->bits_per_pixel == 25) fbi->fb.fix.visual = FB_VISUAL_TRUECOLOR; else if (!fbi->cmap_static) fbi->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; @@ -298,12 +327,25 @@ fbi->fb.fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR; } - fbi->fb.fix.line_length = var->xres_virtual * - var->bits_per_pixel / 8; - if (var->bits_per_pixel == 16) - fbi->palette_size = 0; - else - fbi->palette_size = var->bits_per_pixel == 1 ? 4 : 1 << var->bits_per_pixel; + switch (var->bits_per_pixel) { + case 16: + fbi->fb.fix.line_length = var->xres_virtual * 2; + fbi->palette_size = 0; + break; + case 18: + case 19: + fbi->fb.fix.line_length = var->xres_virtual * 3; + fbi->palette_size = 0; + break; + case 24: + case 25: + fbi->fb.fix.line_length = var->xres_virtual * 4; + fbi->palette_size = 0; + break; + default: + fbi->fb.fix.line_length = var->xres_virtual * var->bits_per_pixel / 8; + fbi->palette_size = var->bits_per_pixel == 1 ? 4 : 1 << var->bits_per_pixel; + } palette_mem_size = fbi->palette_size * sizeof(u16); @@ -317,7 +359,8 @@ */ pxafb_set_truecolor(fbi->fb.fix.visual == FB_VISUAL_TRUECOLOR); - if (fbi->fb.var.bits_per_pixel == 16) + if (fbi->fb.var.bits_per_pixel == 16 || fbi->fb.var.bits_per_pixel == 18 ||fbi->fb.var.bits_per_pixel == 19 + || fbi->fb.var.bits_per_pixel == 24 || fbi->fb.var.bits_per_pixel == 25) fb_dealloc_cmap(&fbi->fb.cmap); else fb_alloc_cmap(&fbi->fb.cmap, 1<fb.var.bits_per_pixel, 0); @@ -363,7 +406,7 @@ * 16 bpp mode does not really use the palette, so this will not * blank the display in all modes. */ -static int pxafb_blank(int blank, struct fb_info *info) +int pxafb_blank(int blank, struct fb_info *info) { struct pxafb_info *fbi = (struct pxafb_info *)info; int i; @@ -379,7 +422,7 @@ for (i = 0; i < fbi->palette_size; i++) pxafb_setpalettereg(i, 0, 0, 0, 0, info); - pxafb_schedule_work(fbi, C_DISABLE); + pxafb_schedule_work(fbi, C_BLANK); //TODO if (pxafb_blank_helper) pxafb_blank_helper(blank); break; @@ -388,10 +431,11 @@ if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR || fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) fb_set_cmap(&fbi->fb.cmap, info); - pxafb_schedule_work(fbi, C_ENABLE); + pxafb_schedule_work(fbi, C_UNBLANK); } return 0; } +EXPORT_SYMBOL(pxafb_blank); static struct fb_ops pxafb_ops = { .owner = THIS_MODULE, @@ -444,9 +488,19 @@ * speeds */ pcd = (unsigned long long)get_lcdclk_frequency_10khz() * pixclock; - pcd /= 100000000 * 2; + pcd /= 100000000; /* no need for this, since we should subtract 1 anyway. they cancel */ /* pcd += 1; */ /* make up for integer math truncations */ + /* FIXME: for lcd clock(10khz) equals 10400 or 5200, special + PCD value is used. If we use the formula to calculate the + PCD value, the LCD will flicker when DVFM. + */ +#ifdef CONFIG_FB_PXA_LCD_QVGA + if ( get_lcdclk_frequency_10khz() == 10400) + pcd = 16; + else if ( get_lcdclk_frequency_10khz() == 5200) + pcd = 9; +#endif return (unsigned int)pcd; } @@ -481,6 +535,10 @@ case 4: case 8: case 16: + case 18: + case 19: + case 24: + case 25: break; default: printk(KERN_ERR "%s: invalid bit depth %d\n", @@ -512,7 +570,10 @@ new_regs.lccr0 = fbi->lccr0 | (LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | - LCCR0_QDM | LCCR0_BM | LCCR0_OUM); + #ifdef CONFIG_PXA27x /* Enable overlay for PXA27x */ + LCCR0_OUC | LCCR0_CMDIM | LCCR0_RDSTM | + #endif + LCCR0_QDM | LCCR0_BM ); new_regs.lccr1 = LCCR1_DisWdth(var->xres) + @@ -571,13 +632,14 @@ fbi->dmadesc_fbhigh_cpu->fsadr = fbi->screen_dma; fbi->dmadesc_fbhigh_cpu->fidr = 0; - fbi->dmadesc_fbhigh_cpu->ldcmd = BYTES_PER_PANEL; + fbi->dmadesc_fbhigh_cpu->ldcmd = BYTES_PER_PANEL | LDCMD_EOFINT; fbi->dmadesc_palette_cpu->fsadr = fbi->palette_dma; fbi->dmadesc_palette_cpu->fidr = 0; fbi->dmadesc_palette_cpu->ldcmd = (fbi->palette_size * 2) | LDCMD_PAL; - if (var->bits_per_pixel == 16) { + if (var->bits_per_pixel == 16 || var->bits_per_pixel == 18 ||var->bits_per_pixel == 19 + || var->bits_per_pixel == 24 || var->bits_per_pixel == 25) { /* palette shouldn't be loaded in true-color mode */ fbi->dmadesc_fbhigh_cpu->fdadr = fbi->dmadesc_fbhigh_dma; fbi->fdadr0 = fbi->dmadesc_fbhigh_dma; /* no pal just fbhigh */ @@ -690,17 +752,32 @@ GAFR2_L = (GAFR2_L & ~(0xff << 20)) | (0xaa << 20); } - /* 16 bit interface */ - else if ((lccr0 & LCCR0_CMS) == LCCR0_Color && - ((lccr0 & LCCR0_SDS) == LCCR0_Dual || (lccr0 & LCCR0_PAS) == LCCR0_Act)) - { - // bits 58-77 - GPDR1 |= (0x3f << 26); - GPDR2 |= 0x00003fff; - - GAFR1_U = (GAFR1_U & ~(0xfff << 20)) | (0xaaa << 20); - GAFR2_L = (GAFR2_L & 0xf0000000) | 0x0aaaaaaa; - } + else if ((lccr0 & LCCR0_CMS) == LCCR0_Color && + ((lccr0 & LCCR0_SDS) == LCCR0_Dual || (lccr0 & LCCR0_PAS) == LCCR0_Act)) + { + switch (fbi->max_bpp) { + case 16: + // bits 58-77 + GPDR1 |= (0x3f << 26); + GPDR2 |= 0x00003fff; + + GAFR1_U = (GAFR1_U & ~(0xfff << 20)) | (0xaaa << 20); + GAFR2_L = (GAFR2_L & 0xf0000000) | 0x0aaaaaaa; + break; + case 18: + case 19: + case 24: + case 25: + // bits 58-77 and 86, 87 + GPDR1 |= (0x3f << 26); + GPDR2 |= 0x00c03fff; + + GAFR1_U = (GAFR1_U & ~(0xfff << 20)) | (0xaaa << 20); + GAFR2_L = (GAFR2_L & 0xf0000000) | 0x0aaaaaaa; + GAFR2_U = (GAFR2_U & 0xffff0fff) | 0xa000; + break; + } + } else { printk(KERN_ERR "pxafb_setup_gpio: unable to determine bits per pixel\n"); @@ -716,6 +793,37 @@ DPRINTK("reg_lccr1 0x%08x\n", (unsigned int) fbi->reg_lccr1); DPRINTK("reg_lccr2 0x%08x\n", (unsigned int) fbi->reg_lccr2); DPRINTK("reg_lccr3 0x%08x\n", (unsigned int) fbi->reg_lccr3); + + down(&fcs_lcd_sem); +#ifdef CONFIG_PXA27x_E17 + /* FIXME:PXA27X E17: Overlay1 is not enabled intermittently after re-enabling LCD. */ + + OVL1C2 = 0; + OVL1C1 = 0; + OVL2C2 = 0; + OVL2C1 = 0; + CCR = 0; + + LCCR3 = fbi->reg_lccr3; + LCCR2 = fbi->reg_lccr2; + LCCR1 = fbi->reg_lccr1; + LCCR0 = fbi->reg_lccr0 & ~LCCR0_ENB; + + LCCR0 |= LCCR0_ENB; + LCCR0 |= LCCR0_DIS; + + LCCR3 = fbi->reg_lccr3; + LCCR2 = fbi->reg_lccr2; + LCCR1 = fbi->reg_lccr1; + LCCR0 = fbi->reg_lccr0 & ~LCCR0_ENB; + + LCCR0 |= LCCR0_ENB; + + FDADR0 = fbi->fdadr0; + FDADR1 = fbi->fdadr1; + + LCCR0 |= LCCR0_ENB; +#else /* Sequence from 11.7.10 */ LCCR3 = fbi->reg_lccr3; @@ -726,6 +834,8 @@ FDADR0 = fbi->fdadr0; FDADR1 = fbi->fdadr1; LCCR0 |= LCCR0_ENB; +#endif + up(&fcs_lcd_sem); DPRINTK("FDADR0 0x%08x\n", (unsigned int) FDADR0); DPRINTK("FDADR1 0x%08x\n", (unsigned int) FDADR1); @@ -741,6 +851,7 @@ DPRINTK("Disabling LCD controller\n"); + down(&fcs_lcd_sem); add_wait_queue(&fbi->ctrlr_wait, &wait); set_current_state(TASK_UNINTERRUPTIBLE); @@ -750,6 +861,7 @@ schedule_timeout(20 * HZ / 1000); remove_wait_queue(&fbi->ctrlr_wait, &wait); + up(&fcs_lcd_sem); } /* @@ -759,11 +871,33 @@ { struct pxafb_info *fbi = dev_id; unsigned int lcsr = LCSR; + static unsigned int ou_count; if (lcsr & LCSR_LDD) { LCCR0 |= LCCR0_LDM; wake_up(&fbi->ctrlr_wait); } + if (lcsr & LCSR_EOF && fcs_in_eof) { + LCCR0 |= LCCR0_EFM; +#ifdef CONFIG_FAST_DVFM + int pcd, lccr3; + pxa27x_setspeed(fcs_clkcfg); + pcd = get_pcd(fbi->fb.var.pixclock); + + lccr3 = fbi->reg_lccr3; + fbi->reg_lccr3 = (fbi->reg_lccr3 & ~0xff) | LCCR3_PixClkDiv(pcd); + if (lccr3 != fbi->reg_lccr3) + LCCR3 = fbi->reg_lccr3; + +#endif + fcs_in_eof = 0; + wake_up(&fcs_wait_eof); + } + if (lcsr & LCSR_OU) + { + ou_count++; + } + //MST_LEDDAT1 = ou_count; LCSR = lcsr; return IRQ_HANDLED; @@ -774,7 +908,7 @@ * sleep when disabling the LCD controller, or if we get two contending * processes trying to alter state. */ -static void set_ctrlr_state(struct pxafb_info *fbi, u_int state) +void set_ctrlr_state(struct pxafb_info *fbi, u_int state) { u_int old_state; @@ -797,6 +931,8 @@ if (old_state != C_DISABLE && old_state != C_DISABLE_PM) { fbi->state = state; //TODO __pxafb_lcd_power(fbi, 0); + if(fbi->set_overlay_ctrlr_state) + fbi->set_overlay_ctrlr_state(fbi, C_DISABLE); pxafb_disable_controller(fbi); } break; @@ -810,6 +946,8 @@ fbi->state = state; __pxafb_backlight_power(fbi, 0); __pxafb_lcd_power(fbi, 0); + if(fbi->set_overlay_ctrlr_state) + fbi->set_overlay_ctrlr_state(fbi, C_DISABLE); if (old_state != C_DISABLE_CLKCHANGE) pxafb_disable_controller(fbi); } @@ -824,6 +962,8 @@ fbi->state = C_ENABLE; pxafb_enable_controller(fbi); //TODO __pxafb_lcd_power(fbi, 1); + if(fbi->set_overlay_ctrlr_state) + fbi->set_overlay_ctrlr_state(fbi, C_ENABLE); } break; @@ -834,9 +974,13 @@ * registers. */ if (old_state == C_ENABLE) { + if(fbi->set_overlay_ctrlr_state) + fbi->set_overlay_ctrlr_state(fbi, C_DISABLE); pxafb_disable_controller(fbi); pxafb_setup_gpio(fbi); pxafb_enable_controller(fbi); + if(fbi->set_overlay_ctrlr_state) + fbi->set_overlay_ctrlr_state(fbi, C_ENABLE); } break; @@ -861,11 +1005,46 @@ pxafb_enable_controller(fbi); __pxafb_lcd_power(fbi, 1); __pxafb_backlight_power(fbi, 1); + if(fbi->set_overlay_ctrlr_state) + fbi->set_overlay_ctrlr_state(fbi, C_ENABLE); } break; + + case C_BLANK: + /* + * Disable controller, blank overlays if exist. + */ + if ((old_state != C_DISABLE) && (old_state != C_BLANK)) { + fbi->state = state; + __pxafb_backlight_power(fbi, 0); + __pxafb_lcd_power(fbi, 0); + if(fbi->set_overlay_ctrlr_state) + fbi->set_overlay_ctrlr_state(fbi, C_BLANK); + if (old_state != C_DISABLE_CLKCHANGE) + pxafb_disable_controller(fbi); + } + break; + + case C_UNBLANK: + /* + * Power up the LCD screen, enable controller, and + * turn on the backlight, unblank overlays if exist. + */ + if ((old_state != C_ENABLE) && (old_state != C_UNBLANK)) { + fbi->state = C_UNBLANK; + pxafb_setup_gpio(fbi); + pxafb_enable_controller(fbi); + __pxafb_lcd_power(fbi, 1); + __pxafb_backlight_power(fbi, 1); + if(fbi->set_overlay_ctrlr_state) + fbi->set_overlay_ctrlr_state(fbi, C_UNBLANK); + } + break; + } up(&fbi->ctrlr_sem); } +EXPORT_SYMBOL(set_ctrlr_state); /* * Our LCD controller task (which is called when we blank or unblank) @@ -880,6 +1059,52 @@ } #ifdef CONFIG_CPU_FREQ + +#ifdef CONFIG_FAST_DVFM +void pxafb_wait_for_eof(unsigned int clkcfg) +#else +void pxafb_wait_for_eof() +#endif +{ + unsigned long flags; + DECLARE_WAITQUEUE(wait, current); +#ifdef CONFIG_FAST_DVFM + fcs_clkcfg = clkcfg; +#endif + down(&fcs_lcd_sem); + add_wait_queue(&fcs_wait_eof, &wait); + set_current_state(TASK_UNINTERRUPTIBLE); + /* if LCD controller is disabled or is disabling, we do not need wait + eof. directly change frequency. + */ + local_irq_save(flags); + if ((LCCR0 & LCCR0_DIS) || !(LCCR0 & LCCR0_ENB)) { + fcs_in_eof = 0; + local_irq_restore(flags); +#ifdef CONFIG_FAST_DVFM + fcs_lcd_disable = 1; + pxa27x_setspeed(fcs_clkcfg); +#endif + current->state = TASK_RUNNING; + remove_wait_queue(&fcs_wait_eof, &wait); + up(&fcs_lcd_sem); + return ; + } + +#ifdef CONFIG_FAST_DVFM + fcs_lcd_disable = 0; +#endif + fcs_in_eof = 1; + /* enable end of frame interrupt */ + LCCR0 &= ~LCCR0_EFM; + local_irq_restore(flags); + schedule_timeout(20 * HZ / 1000); + current->state = TASK_RUNNING; + remove_wait_queue(&fcs_wait_eof, &wait); + up(&fcs_lcd_sem); +} +EXPORT_SYMBOL(pxafb_wait_for_eof); + /* * CPU clock speed change handler. We need to adjust the LCD timing * parameters when the CPU clock is adjusted by the power management @@ -891,18 +1116,32 @@ pxafb_freq_transition(struct notifier_block *nb, unsigned long val, void *data) { struct pxafb_info *fbi = TO_INF(nb, freq_transition); - //TODO struct cpufreq_freqs *f = data; + struct cpufreq_freqs *clkinfo; u_int pcd; + u_int lccr3; switch (val) { case CPUFREQ_PRECHANGE: - set_ctrlr_state(fbi, C_DISABLE_CLKCHANGE); break; case CPUFREQ_POSTCHANGE: + clkinfo = (struct cpufreq_freqs *)data; + if ((clkinfo->old == 13000)) + break; + +#ifdef CONFIG_FAST_DVFM + if (fcs_lcd_disable) { + pcd = get_pcd(fbi->fb.var.pixclock); + fbi->reg_lccr3 = (fbi->reg_lccr3 & ~0xff) | LCCR3_PixClkDiv(pcd); + } +#else pcd = get_pcd(fbi->fb.var.pixclock); + + lccr3 = fbi->reg_lccr3; fbi->reg_lccr3 = (fbi->reg_lccr3 & ~0xff) | LCCR3_PixClkDiv(pcd); - set_ctrlr_state(fbi, C_ENABLE_CLKCHANGE); + if (lccr3 != fbi->reg_lccr3 && !((LCCR0 & LCCR0_DIS) || !(LCCR0 & LCCR0_ENB))) + LCCR3 = fbi->reg_lccr3; +#endif break; } return 0; @@ -947,8 +1186,9 @@ { struct pxafb_info *fbi = dev_get_drvdata(dev); - if (level == SUSPEND_DISABLE || level == SUSPEND_POWER_DOWN) + if (level == SUSPEND_DISABLE || level == SUSPEND_POWER_DOWN) { set_ctrlr_state(fbi, C_DISABLE_PM); + } return 0; } @@ -956,8 +1196,11 @@ { struct pxafb_info *fbi = dev_get_drvdata(dev); - if (level == RESUME_ENABLE) + if (level == RESUME_ENABLE) { set_ctrlr_state(fbi, C_ENABLE_PM); + LCCR4 |= (1 << 31); + LCCR4 |= (5 << 17); + } return 0; } #else @@ -1072,8 +1315,16 @@ fbi->lccr3 = inf->lccr3; fbi->state = C_STARTUP; fbi->task_state = (u_char)-1; - fbi->fb.fix.smem_len = fbi->max_xres * fbi->max_yres * - fbi->max_bpp / 8; + if ( fbi->max_bpp <= 16 ) { /* 8, 16 bpp */ + fbi->fb.fix.smem_len = fbi->max_xres * fbi->max_yres * fbi->max_bpp / 8; + } else if ( fbi->max_bpp > 19 ) { /* 24, 25 bpp */ + fbi->fb.fix.smem_len = fbi->max_xres * fbi->max_yres * 4; + } else { /* 18, 19 bpp */ + /* packed format */ + fbi->fb.fix.smem_len = fbi->max_xres * fbi->max_yres * 3; + } + + fbi->set_overlay_ctrlr_state = NULL; init_waitqueue_head(&fbi->ctrlr_wait); INIT_WORK(&fbi->task, pxafb_task, fbi); @@ -1141,6 +1392,10 @@ case 4: case 8: case 16: + case 18: + case 19: + case 24: + case 25: inf->bpp = bpp; dev_info(dev, "overriding bit depth: %d\n", bpp); break; @@ -1340,6 +1595,9 @@ * Ok, now enable the LCD controller */ set_ctrlr_state(fbi, C_ENABLE); + LCCR4 |= (1 << 31); + LCCR4 |= (5 << 17); + init_waitqueue_head(&fcs_wait_eof); return 0; diff -uNr a/drivers/video/pxafb.h b/drivers/video/pxafb.h --- a/drivers/video/pxafb.h 2004-10-19 05:54:07.000000000 +0800 +++ b/drivers/video/pxafb.h 2005-01-27 09:56:49.000000000 +0800 @@ -29,6 +29,116 @@ unsigned int lccr3; }; +struct pxafb_rgb { + struct fb_bitfield red; + struct fb_bitfield green; + struct fb_bitfield blue; + struct fb_bitfield transp; +}; + +static struct pxafb_rgb def_rgb_8 = { + red: { offset: 0, length: 8, }, + green: { offset: 0, length: 8, }, + blue: { offset: 0, length: 8, }, + transp: { offset: 0, length: 0, }, +}; + +static struct pxafb_rgb def_rgb_16 = { + red: { offset: 11, length: 5, }, + green: { offset: 5, length: 6, }, + blue: { offset: 0, length: 5, }, + transp: { offset: 0, length: 0, }, +}; + +static struct pxafb_rgb def_rgb_18 = { + red: { offset: 12, length: 6, }, + green: { offset: 6, length: 6, }, + blue: { offset: 0, length: 6, }, + transp: { offset: 0, length: 0, }, +}; + +static struct pxafb_rgb def_rgb_24 = { + red: { offset: 16, length: 8, }, + green: { offset: 8, length: 8, }, + blue: { offset: 0, length: 8, }, + transp: { offset: 0, length: 0, }, +}; + +#ifdef CONFIG_PXA27x +static struct pxafb_rgb def_rgbt_16 = { + red: { offset: 10, length: 5, }, + green: { offset: 5, length: 5, }, + blue: { offset: 0, length: 5, }, + transp: { offset: 15, length: 1, }, +}; + +static struct pxafb_rgb def_rgbt_19 = { + red: { offset: 12, length: 6, }, + green: { offset: 6, length: 6, }, + blue: { offset: 0, length: 6, }, + transp: { offset: 18, length: 1, }, +}; + +static struct pxafb_rgb def_rgbt_24 = { + red: { offset: 16, length: 7, }, + green: { offset: 8, length: 8, }, + blue: { offset: 0, length: 8, }, + transp: { offset: 0, length: 0, }, +}; + +static struct pxafb_rgb def_rgbt_25 = { + red: { offset: 16, length: 8, }, + green: { offset: 8, length: 8, }, + blue: { offset: 0, length: 8, }, + transp: { offset: 24, length: 1, }, +}; + +/* PXA Overlay Framebuffer Support */ +struct overlayfb_info +{ + struct fb_info fb; + + struct fb_var_screeninfo old_var; + + struct semaphore mutex; + unsigned long refcount; + + struct pxafb_info *basefb; + + unsigned long map_cpu; + unsigned long screen_cpu; + unsigned long palette_cpu; + unsigned long map_size; + unsigned long palette_size; + + dma_addr_t screen_dma; + dma_addr_t map_dma; + dma_addr_t palette_dma; + + volatile u_char state; + + /* overlay specific info */ + unsigned long xpos; /* screen position (x, y)*/ + unsigned long ypos; + unsigned long format; + + /* additional */ + union { + struct pxafb_dma_descriptor *dma0; + struct pxafb_dma_descriptor *dma1; + struct { + struct pxafb_dma_descriptor *dma2; + struct pxafb_dma_descriptor *dma3; + struct pxafb_dma_descriptor *dma4; + }; + struct { + struct pxafb_dma_descriptor *dma5_pal; + struct pxafb_dma_descriptor *dma5_frame; + }; + }; +}; +#endif + /* PXA LCD DMA descriptor */ struct pxafb_dma_descriptor { unsigned int fdadr; @@ -89,6 +199,14 @@ wait_queue_head_t ctrlr_wait; struct work_struct task; +#ifdef CONFIG_PXA27x + /* PXA Overlay Framebuffer Support */ + struct overlayfb_info *overlay1fb; + struct overlayfb_info *overlay2fb; + struct overlayfb_info *cursorfb; + void (*set_overlay_ctrlr_state)(struct pxafb_info *, u_int); +#endif + #ifdef CONFIG_CPU_FREQ struct notifier_block freq_transition; struct notifier_block freq_policy; @@ -108,6 +226,9 @@ #define C_DISABLE_PM (5) #define C_ENABLE_PM (6) #define C_STARTUP (7) +#define C_BLANK (8) +#define C_UNBLANK (9) + #define PXA_NAME "PXA" diff -uNr a/drivers/video/pxafb_overlay.c b/drivers/video/pxafb_overlay.c --- a/drivers/video/pxafb_overlay.c 1970-01-01 08:00:00.000000000 +0800 +++ b/drivers/video/pxafb_overlay.c 2005-01-27 09:56:49.000000000 +0800 @@ -0,0 +1,1668 @@ +/* + * linux/drivers/video/pxafb_overlay.c + * + * Copyright (c) 2004, Intel Corporation + * + * Code Status: + * 2004/10/28: + * - Ported to 2.6 kernel + * - Made overlay driver a loadable module + * - Merged overlay optimized patch + * 2004/03/10: + * - Fixed Bugs + * - Added workaround for overlay1&2 + * 2003/08/27: + * - Added Overlay 1 & Overlay2 & Hardware Cursor support + * + * + * This software program is licensed subject to the GNU Lesser General + * Public License (LGPL). Version 2.1, February 1999, available at + * http://www.gnu.org/copyleft/lesser.html + * + * Intel PXA27x LCD Controller Frame Buffer Overlay Driver + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "pxafb.h" + +/* LCD enhancement : Overlay 1 & 2 & Hardware Cursor */ + +/* + * LCD enhancement : Overlay 1 + * + * Features: + * - support 16bpp (No palette) + */ +/* + * debugging? + */ +#define DEBUG 0 + +#ifdef DEBUG +#define dbg(fmt,arg...) printk(KERN_ALERT "%s(): " fmt "\n", __FUNCTION__, ##arg) +#else +#define dbg(fmt,arg...) +#endif + +static int overlay1fb_enable(struct fb_info *info); +static int overlay2fb_enable(struct fb_info *info); +static int cursorfb_enable(struct fb_info *info); + +static int overlay1fb_disable(struct fb_info *info); +static int overlay2fb_disable(struct fb_info *info); +static int cursorfb_disable(struct fb_info *info); + +static int overlay1fb_blank(int blank, struct fb_info *info); +static int overlay2fb_blank(int blank, struct fb_info *info); +static int cursorfb_blank(int blank, struct fb_info *info); + +extern void set_ctrlr_state(struct pxafb_info *fbi, u_int state); +extern int pxafb_blank(int blank, struct fb_info *info); + +#define CLEAR_LCD_INTR(reg, intr) do { \ + reg = (intr); \ +}while(0) + +#define WAIT_FOR_LCD_INTR(reg,intr,timeout) ({ \ + int __done =0; \ + int __t = timeout; \ + while (__t) { \ + __done = (reg) & (intr); \ + if (__done) break; \ + mdelay(10); \ + __t--; \ + } \ + if (!__t) dbg("wait " #intr " timeount");\ + __done; \ +}) + +#define DISABLE_OVERLAYS(fbi) do { \ + if (fbi->overlay1fb && (fbi->overlay1fb->state == C_ENABLE)) { \ + overlay1fb_disable((struct fb_info*)fbi->overlay1fb); \ + } \ + if (fbi->overlay2fb && (fbi->overlay2fb->state == C_ENABLE)) { \ + overlay2fb_disable((struct fb_info*)fbi->overlay2fb); \ + } \ + if (fbi->cursorfb && (fbi->cursorfb->state == C_ENABLE)) { \ + cursorfb_disable((struct fb_info*)fbi->cursorfb); \ + } \ +}while(0) + +#define ENABLE_OVERLAYS(fbi) do { \ + if (fbi->overlay1fb && (fbi->overlay1fb->state == C_DISABLE)){ \ + overlay1fb_enable((struct fb_info*)fbi->overlay1fb); \ + } \ + if (fbi->overlay2fb && (fbi->overlay2fb->state == C_DISABLE)){ \ + overlay2fb_unblank_workaroundYUV420((struct fb_info*)fbi->overlay2fb); \ + overlay2fb_enable((struct fb_info*)fbi->overlay2fb); \ + } \ + if (fbi->cursorfb && (fbi->cursorfb->state == C_DISABLE)){ \ + cursorfb_enable((struct fb_info*)fbi->cursorfb); \ + } \ +}while(0) + +#define BLANK_OVERLAYS(fbi) do { \ + if (fbi->overlay1fb && (fbi->overlay1fb->state == C_ENABLE)) { \ + overlay1fb_disable((struct fb_info*)fbi->overlay1fb); \ + fbi->overlay1fb->state = C_BLANK; \ + } \ + if (fbi->overlay2fb && (fbi->overlay2fb->state == C_ENABLE)) { \ + overlay2fb_disable((struct fb_info*)fbi->overlay2fb); \ + fbi->overlay2fb->state = C_BLANK; \ + } \ + if (fbi->cursorfb && (fbi->cursorfb->state == C_ENABLE)) { \ + cursorfb_disable((struct fb_info*)fbi->cursorfb); \ + fbi->cursorfb->state = C_BLANK; \ + } \ +}while(0) + +#define UNBLANK_OVERLAYS(fbi) do { \ + if (fbi->overlay1fb && (fbi->overlay1fb->state == C_BLANK)){ \ + overlay1fb_enable((struct fb_info*)fbi->overlay1fb); \ + fbi->overlay1fb->state = C_ENABLE; \ + } \ + if (fbi->overlay2fb && (fbi->overlay2fb->state == C_BLANK)){ \ + overlay2fb_unblank_workaroundYUV420((struct fb_info*)fbi->overlay2fb); \ + overlay2fb_enable((struct fb_info*)fbi->overlay2fb); \ + fbi->overlay2fb->state = C_ENABLE; \ + } \ + if (fbi->cursorfb && (fbi->cursorfb->state == C_BLANK)){ \ + cursorfb_enable((struct fb_info*)fbi->cursorfb); \ + fbi->cursorfb->state = C_ENABLE; \ + } \ +}while(0) + +static int overlay1fb_open(struct fb_info *info, int user) +{ + struct overlayfb_info *fbi = (struct overlayfb_info*) info; + int ret = 0; + +/* If basefb is disable, enable fb. */ + if (fbi->basefb && fbi->basefb->state != C_ENABLE) + pxafb_blank(VESA_NO_BLANKING, (struct fb_info *)(fbi->basefb)); + + down(&fbi->mutex); + + if (fbi->refcount) + ret = -EACCES; + else + fbi->refcount ++; + + up(&fbi->mutex); + + /* Initialize the variables in overlay1 framebuffer. */ + fbi->fb.var.xres = fbi->fb.var.yres = 0; + fbi->fb.var.bits_per_pixel = 0; + + return ret; +} + +static int overlay1fb_release(struct fb_info *info, int user) +{ + struct overlayfb_info *fbi = (struct overlayfb_info*) info; + down(&fbi->mutex); + + if (fbi->refcount) + fbi->refcount --; + + up(&fbi->mutex); + /* disable overlay when released */ + overlay1fb_blank(1, info); + + return 0; +} + +static int overlay1fb_map_video_memory(struct fb_info *info) +{ + struct overlayfb_info *fbi = (struct overlayfb_info*) info; + + if (fbi->map_cpu) + dma_free_writecombine(NULL, fbi->map_size, (void*)fbi->map_cpu, fbi->map_dma); + fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + PAGE_SIZE); + + fbi->map_cpu = (unsigned long)dma_alloc_writecombine(NULL, fbi->map_size, + &fbi->map_dma, GFP_KERNEL ); + + if (!fbi->map_cpu) return -ENOMEM; + + fbi->screen_cpu = fbi->map_cpu + PAGE_SIZE; + fbi->screen_dma = fbi->map_dma + PAGE_SIZE; + + fbi->fb.fix.smem_start = fbi->screen_dma; + + /* setup dma descriptor */ + fbi->dma1 = (struct pxafb_dma_descriptor*) + (fbi->screen_cpu - sizeof(struct pxafb_dma_descriptor)); + + fbi->dma1->fdadr = (fbi->screen_dma - sizeof(struct pxafb_dma_descriptor)); + fbi->dma1->fsadr = fbi->screen_dma; + fbi->dma1->fidr = 0; + fbi->dma1->ldcmd = fbi->fb.fix.smem_len; + + return 0; +} + +static int overlay1fb_enable(struct fb_info *info) +{ + struct overlayfb_info *fbi = (struct overlayfb_info*) info; + unsigned long bpp1; + + if (!fbi->map_cpu) return -EINVAL; + + switch(fbi->fb.var.bits_per_pixel){ + case 16: + bpp1 = 0x4; + break; + case 18: + bpp1 = 0x6; + break; + case 19: + bpp1 = 0x8; + break; + case 24: + bpp1 = 0x9; + break; + case 25: + bpp1 = 0xa; + break; + default: + return -EINVAL; + } + + /* disable branch/start/end of frame interrupt */ + LCCR5 |= (LCCR5_IUM1 | LCCR5_BSM1 | LCCR5_EOFM1 | LCCR5_SOFM1); + + if (fbi->state == C_DISABLE || fbi->state == C_BLANK) + FDADR1 = (fbi->dma1->fdadr); + else + FBR1 = fbi->dma1->fdadr | 0x1; + + /* enable overlay 1 window */ + OVL1C2 = (fbi->ypos << 10) | fbi->xpos; + OVL1C1 = OVL1C1_O1EN | (bpp1 << 20) | ((fbi->fb.var.yres-1)<<10) | (fbi->fb.var.xres-1); + + fbi->state = C_ENABLE; + + return 0; +} + +static int overlay1fb_disable(struct fb_info *info) +{ + struct overlayfb_info *fbi = (struct overlayfb_info*)info; + int done; + + if ((fbi->state == C_DISABLE) || (fbi->state == C_BLANK)) + return 0; + + fbi->state = C_DISABLE; + + /* clear O1EN */ + OVL1C1 &= ~OVL1C1_O1EN; + + CLEAR_LCD_INTR(LCSR1, LCSR1_BS1); + FBR1 = 0x3; + done = WAIT_FOR_LCD_INTR(LCSR1, LCSR1_BS1, 100); + + if (!done) { + DPRINTK(KERN_INFO "%s: timeout\n", __FUNCTION__); + return -1; + } + return 0; +} + +static int overlay1fb_blank(int blank, struct fb_info *info) +{ + struct overlayfb_info *fbi = (struct overlayfb_info*) info; + int err=0; + + switch(blank) + { + case 0: + err = overlay1fb_enable(info); + if (err) { + fbi->state = C_DISABLE; + set_ctrlr_state(fbi->basefb, C_REENABLE); + } + break; + case 1: + err = overlay1fb_disable(info); + if (err) { + fbi->state = C_DISABLE; + set_ctrlr_state(fbi->basefb, C_REENABLE); + } + break; + default: + break; + } + + return err; +} + +static int overlay1fb_check_var( struct fb_var_screeninfo *var, struct fb_info *info) +{ + int xpos, ypos; + struct overlayfb_info *fbi=(struct overlayfb_info*)info; + + /* must in base frame */ + xpos = (var->nonstd & 0x3ff); + ypos = ((var->nonstd>>10) & 0x3ff); + + if ( (xpos + var->xres) > fbi->basefb->fb.var.xres ) + return -EINVAL; + + if ( (ypos + var->yres) > fbi->basefb->fb.var.yres ) + return -EINVAL; + + switch (var->bits_per_pixel) { + case 16: + if ( var->xres & 0x1 ) { + printk("xres should be a multiple of 2 pixels!\n"); + return -EINVAL; + } + break; + case 18: + case 19: + if ( var->xres & 0x7 ) { + printk("xres should be a multiple of 8 pixels!\n"); + return -EINVAL; + } + break; + default: + break; + } + + fbi->old_var=*var; + + var->activate=FB_ACTIVATE_NOW; + + return 0; +} + + +static int overlay1fb_set_par(struct fb_info *info) +{ + int nbytes=0, err=0, pixels_per_line=0; + + struct overlayfb_info *fbi=(struct overlayfb_info*)info; + struct fb_var_screeninfo *var = &fbi->fb.var; + + info->flags &= ~FBINFO_MISC_MODECHANGEUSER; + + if (fbi->state == C_BLANK) + return 0; + + if (fbi->state == C_DISABLE) + goto out1; + + /* only xpos & ypos change */ + if ( (var->xres == fbi->old_var.xres) && + (var->yres == fbi->old_var.yres) && + (var->bits_per_pixel == fbi->old_var.bits_per_pixel) ) + goto out2; + + out1: + switch(var->bits_per_pixel) { + case 16: + /* 2 pixels per line */ + pixels_per_line = (fbi->fb.var.xres + 0x1) & (~0x1); + nbytes = 2; + + var->red = def_rgbt_16.red; + var->green = def_rgbt_16.green; + var->blue = def_rgbt_16.blue; + var->transp = def_rgbt_16.transp; + + break; + case 18: + /* 8 pixels per line */ + pixels_per_line = (fbi->fb.var.xres + 0x7 ) & (~0x7); + nbytes = 3; + + var->red = def_rgb_18.red; + var->green = def_rgb_18.green; + var->blue = def_rgb_18.blue; + var->transp = def_rgb_18.transp; + + break; + case 19: + /* 8 pixels per line */ + pixels_per_line = (fbi->fb.var.xres + 0x7 ) & (~0x7); + nbytes = 3; + + var->red = def_rgbt_19.red; + var->green = def_rgbt_19.green; + var->blue = def_rgbt_19.blue; + var->transp = def_rgbt_19.transp; + + break; + case 24: + pixels_per_line = fbi->fb.var.xres; + nbytes = 4; + + var->red = def_rgbt_24.red; + var->green = def_rgbt_24.green; + var->blue = def_rgbt_24.blue; + var->transp = def_rgbt_24.transp; + + break; + case 25: + pixels_per_line = fbi->fb.var.xres; + nbytes = 4; + + var->red = def_rgbt_25.red; + var->green = def_rgbt_25.green; + var->blue = def_rgbt_25.blue; + var->transp = def_rgbt_25.transp; + + break; + } + + fbi->fb.fix.line_length = nbytes * pixels_per_line; + fbi->fb.fix.smem_len = fbi->fb.fix.line_length * fbi->fb.var.yres; + + err= overlay1fb_map_video_memory((struct fb_info*)fbi); + + if (err) return err; + +out2: + fbi->xpos = var->nonstd & 0x3ff; + fbi->ypos = (var->nonstd>>10) & 0x3ff; + + overlay1fb_enable(info); + + return 0; + +} + +static struct fb_ops overlay1fb_ops = { + .owner = THIS_MODULE, + .fb_open = overlay1fb_open, + .fb_release = overlay1fb_release, + .fb_check_var = overlay1fb_check_var, + .fb_set_par = overlay1fb_set_par, + .fb_blank = overlay1fb_blank, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_cursor = soft_cursor, +}; + + /* + * LCD enhancement : Overlay 2 + * + * Features: + * - support planar YCbCr420/YCbCr422/YCbCr444; + */ +static int overlay2fb_open(struct fb_info *info, int user) +{ + struct overlayfb_info *fbi = (struct overlayfb_info*) info; + int ret = 0; + + /* if basefb is disable, enable fb. */ + if (fbi->basefb && fbi->basefb->state != C_ENABLE) + pxafb_blank(VESA_NO_BLANKING, (struct fb_info *)(fbi->basefb)); + + down(&fbi->mutex); + + if (fbi->refcount) + ret = -EACCES; + else + fbi->refcount ++; + + up(&fbi->mutex); + fbi->fb.var.xres = fbi->fb.var.yres = 0; + + return ret; +} + +static int overlay2fb_release(struct fb_info *info, int user) +{ + struct overlayfb_info *fbi = (struct overlayfb_info*) info; + + down(&fbi->mutex); + + if (fbi->refcount) + fbi->refcount --; + + up(&fbi->mutex); + + /* disable overlay when released */ + overlay2fb_blank(1, info); + + return 0; +} + +static int overlay2fb_map_YUV_memory( struct fb_info *info) +{ + struct overlayfb_info *fbi = (struct overlayfb_info*) info; + unsigned int ylen, cblen, crlen, aylen, acblen, acrlen; + unsigned int yoff, cboff, croff; + unsigned int xres,yres; + unsigned int nbytes; + + ylen = cblen = crlen = aylen = acblen = acrlen = 0; + yoff = cboff = croff = 0; + + if (fbi->map_cpu) + dma_free_writecombine(NULL, fbi->map_size, (void*)fbi->map_cpu, fbi->map_dma); + + yres = fbi->fb.var.yres; + + switch(fbi->format) { + case 0x4: /* YCbCr 4:2:0 planar */ + DPRINTK("420 planar\n"); + /* 16 pixels per line */ + xres = (fbi->fb.var.xres + 0xf) & (~0xf); + fbi->fb.fix.line_length = xres; + + nbytes = xres * yres; + ylen = nbytes; + cblen = crlen = (nbytes/4); + + break; + case 0x3: /* YCbCr 4:2:2 planar */ + /* 8 pixles per line */ + DPRINTK("422 planar\n"); + xres = (fbi->fb.var.xres + 0x7) & (~0x7); + fbi->fb.fix.line_length = xres; + + nbytes = xres * yres; + ylen = nbytes; + cblen = crlen = (nbytes/2); + + break; + case 0x2: /* YCbCr 4:4:4 planar */ + /* 4 pixels per line */ + DPRINTK("444 planar\n"); + xres = (fbi->fb.var.xres + 0x3) & (~0x3); + fbi->fb.fix.line_length = xres; + + nbytes = xres * yres; + ylen = cblen = crlen = nbytes; + break; + } + + /* 16-bytes alignment for DMA */ + aylen = (ylen + 0xf) & (~0xf); + acblen = (cblen + 0xf) & (~0xf); + acrlen = (crlen + 0xf) & (~0xf); + + fbi->fb.fix.smem_len = aylen + acblen + acrlen; + + /* alloc memory */ + + fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + PAGE_SIZE); + fbi->map_cpu = (unsigned long)dma_alloc_writecombine(NULL, fbi->map_size, + &fbi->map_dma, GFP_KERNEL ); + + if (!fbi->map_cpu) return -ENOMEM; + + fbi->screen_cpu = fbi->map_cpu + PAGE_SIZE; + fbi->screen_dma = fbi->map_dma + PAGE_SIZE; + + fbi->fb.fix.smem_start = fbi->screen_dma; + + /* setup dma for Planar format */ + fbi->dma2 = (struct pxafb_dma_descriptor*) + (fbi->screen_cpu - sizeof(struct pxafb_dma_descriptor)); + fbi->dma3 = fbi->dma2 - 1; + fbi->dma4 = fbi->dma3 - 1; + + /* offset */ + yoff = 0; + cboff = aylen; + croff = cboff + acblen; + + /* Y vector */ + fbi->dma2->fdadr = (fbi->screen_dma - sizeof(struct pxafb_dma_descriptor)); + fbi->dma2->fsadr = fbi->screen_dma + yoff; + fbi->dma2->fidr = 0; + fbi->dma2->ldcmd = ylen; + + /* Cb vector */ + fbi->dma3->fdadr = (fbi->dma2->fdadr - sizeof(struct pxafb_dma_descriptor)); + fbi->dma3->fsadr = (fbi->screen_dma + cboff); + fbi->dma3->fidr = 0; + fbi->dma3->ldcmd = cblen; + + /* Cr vector */ + + fbi->dma4->fdadr = (fbi->dma3->fdadr - sizeof(struct pxafb_dma_descriptor)); + fbi->dma4->fsadr = (fbi->screen_dma + croff); + fbi->dma4->fidr = 0; + fbi->dma4->ldcmd = crlen; + + /* adjust for user */ + fbi->fb.var.red.length = ylen; + fbi->fb.var.red.offset = yoff; + fbi->fb.var.green.length = cblen; + fbi->fb.var.green.offset = cboff; + fbi->fb.var.blue.length = crlen; + fbi->fb.var.blue.offset = croff; + + return 0; +}; + +static int overlay2fb_map_RGB_memory( struct fb_info *info) +{ + struct overlayfb_info *fbi = (struct overlayfb_info*) info; + struct fb_var_screeninfo *var = &fbi->fb.var; + int pixels_per_line=0 , nbytes=0; + + if (fbi->map_cpu) + dma_free_writecombine(NULL, fbi->map_size, (void*)fbi->map_cpu, fbi->map_dma); + + switch(var->bits_per_pixel) { + case 16: + /* 2 pixels per line */ + pixels_per_line = (fbi->fb.var.xres + 0x1) & (~0x1); + nbytes = 2; + + var->red = def_rgbt_16.red; + var->green = def_rgbt_16.green; + var->blue = def_rgbt_16.blue; + var->transp = def_rgbt_16.transp; + break; + + case 18: + /* 8 pixels per line */ + pixels_per_line = (fbi->fb.var.xres + 0x7 ) & (~0x7); + nbytes = 3; + + var->red = def_rgb_18.red; + var->green = def_rgb_18.green; + var->blue = def_rgb_18.blue; + var->transp = def_rgb_18.transp; + + break; + case 19: + /* 8 pixels per line */ + pixels_per_line = (fbi->fb.var.xres + 0x7 ) & (~0x7); + nbytes = 3; + + var->red = def_rgbt_19.red; + var->green = def_rgbt_19.green; + var->blue = def_rgbt_19.blue; + var->transp = def_rgbt_19.transp; + + break; + case 24: + pixels_per_line = fbi->fb.var.xres; + nbytes = 4; + + var->red = def_rgbt_24.red; + var->green = def_rgbt_24.green; + var->blue = def_rgbt_24.blue; + var->transp = def_rgbt_24.transp; + + break; + + case 25: + pixels_per_line = fbi->fb.var.xres; + nbytes = 4; + + var->red = def_rgbt_25.red; + var->green = def_rgbt_25.green; + var->blue = def_rgbt_25.blue; + var->transp = def_rgbt_25.transp; + + break; + } + + fbi->fb.fix.line_length = nbytes * pixels_per_line ; + fbi->fb.fix.smem_len = fbi->fb.fix.line_length * fbi->fb.var.yres ; + + fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + PAGE_SIZE); + fbi->map_cpu = (unsigned long)dma_alloc_writecombine(NULL, fbi->map_size, + &fbi->map_dma, GFP_KERNEL ); + + if (!fbi->map_cpu) return -ENOMEM; + + fbi->screen_cpu = fbi->map_cpu + PAGE_SIZE; + fbi->screen_dma = fbi->map_dma + PAGE_SIZE; + + fbi->fb.fix.smem_start = fbi->screen_dma; + + /* setup dma descriptor */ + fbi->dma2 = (struct pxafb_dma_descriptor*) + (fbi->screen_cpu - sizeof(struct pxafb_dma_descriptor)); + + fbi->dma2->fdadr = (fbi->screen_dma - sizeof(struct pxafb_dma_descriptor)); + fbi->dma2->fsadr = fbi->screen_dma; + fbi->dma2->fidr = 0; + fbi->dma2->ldcmd = fbi->fb.fix.smem_len; + + return 0; +} + +#ifdef CONFIG_PXA27x_E25 +/* set xpos, ypos, PPL and LP to 0 */ +static int overlay2fb_YUV420_workaround(struct fb_info *info) +{ + struct overlayfb_info *fbi = (struct overlayfb_info*)info; + struct pxafb_dma_descriptor *dma; + u32 map_dma, map_cpu; + int done, ret=0; + + map_cpu = (u32)dma_alloc_writecombine(NULL, PAGE_SIZE, &map_dma, GFP_KERNEL ); + + if (!map_cpu) return -1; + + dma = (struct pxafb_dma_descriptor*)((map_cpu + PAGE_SIZE) - sizeof(struct pxafb_dma_descriptor)); + dma->fdadr = map_dma + PAGE_SIZE - sizeof(struct pxafb_dma_descriptor); + dma->fsadr = map_dma; + dma->fidr = 0; + dma->ldcmd = LDCMD_EOFINT | 128; + + /* step 2.a - enable overlay 2 with RGB mode + * + * - (xpos,ypos) = (0,0); + * - 64 pixels, 16bpp + */ + LCCR5 |= (LCCR5_IUM4 | LCCR5_IUM3 | LCCR5_IUM2 | + LCCR5_BSM4 | LCCR5_BSM3 | LCCR5_BSM2 | + LCCR5_EOFM4 | LCCR5_EOFM3 | LCCR5_EOFM2 | + LCCR5_SOFM4 | LCCR5_SOFM3 | LCCR5_SOFM2); + OVL2C2 = 0; + OVL2C1 = OVL2C1_O2EN | 0x04 <<20 | (63); + + CLEAR_LCD_INTR(LCSR1, LCSR1_EOF2); + if (fbi->state == C_DISABLE || fbi->state == C_BLANK) + FDADR2 = dma->fdadr; + else + FBR2 = dma->fdadr | 0x1; + + /* step 2.b - run at least 1 frame with overlay 2 */ + done = WAIT_FOR_LCD_INTR(LCSR1, LCSR1_EOF2, 100); + if (!done) goto err; + + /* step 2.c - disable overlay 2 */ + OVL2C1 &= ~OVL2C1_O2EN; + + CLEAR_LCD_INTR(LCSR1, LCSR1_BS2); + FBR2 = 0x3; + + done = WAIT_FOR_LCD_INTR(LCSR1, LCSR1_BS2, 100); + if (!done) goto err; + + /* step 2.d - Wait for base EOF interrupts */ + CLEAR_LCD_INTR(LCSR0, LCSR_EOF); + done = WAIT_FOR_LCD_INTR(LCSR0, LCSR_EOF, 100); + + goto out; +err: + ret = -1; +out: + /* free buffer allocated */ + dma_free_writecombine(NULL, PAGE_SIZE, (void*)map_cpu, map_dma); + + fbi->state == C_DISABLE; + return ret; +} + +static void overlay2fb_unblank_workaroundYUV420(struct fb_info *info) +{ + struct overlayfb_info *fbi=(struct overlayfb_info*)info; + int format = fbi->format; + + if ( (format == 0x4) && (fbi->state == C_BLANK) ) + overlay2fb_YUV420_workaround(info); +} +#else +static void overlay2fb_unblank_workaroundYUV420(struct fb_info *info) {} +#endif + +#ifdef CONFIG_PXA27x_OV2_FASTBUS_WORKAROUND +/* workaround for overlay2 fast bus issue */ +static int fastbus_set = 0; + +static void overlay2fb_set_fastbus( void ) +{ + unsigned long flags; + + local_irq_save(flags); + if ( fastbus_set ) { + __asm__ __volatile__ ("\n\ + mrc p14, 0, r0, c6, c0, 0 \n\ + orr r0, r0, #0x8 \n\ + mcr p14, 0, r0, c6, c0, 0 \n\ + mrc p14, 0, r0, c6, c0, 0 \n\ + mov r0, r0 \n\ + nop \n\ + nop" : : : "r0" ); + } + local_irq_restore(flags); +} + +static void overlay2fb_clear_fastbus( void ) +{ + unsigned int cclkcfg; + unsigned int unused; + unsigned int flags; + + local_irq_save(flags); + __asm__ __volatile__("\ + mrc p14, 0, %0, c6, c0, 0 \n\ + nop \n\ + nop \n\ + " + : "=r" (cclkcfg)); + printk("cclkcfg = 0x%08x\n", cclkcfg); + + fastbus_set = 0; + /* If fast bus bit is set, clear it and set a flag */ + if ( cclkcfg | 0x8 ) { + cclkcfg &= ~(0x8); + __asm__ __volatile__ ( "\n\ + mcr p14, 0, %1, c6, c0, 0 \n\ + nop \n\ + nop \n\ + " : "=&r" (unused) : "r" (cclkcfg) ); + + fastbus_set = 1; + } + local_irq_restore(flags); +} +#endif + +static int overlay2fb_enable(struct fb_info *info) +{ + struct overlayfb_info *fbi = (struct overlayfb_info*) info; + unsigned long bpp2; + unsigned int xres, yres; + + if (!fbi->map_cpu) return -EINVAL; + + switch(fbi->fb.var.bits_per_pixel) { + case 16: + bpp2 = 0x4; + break; + case 18: + bpp2 = 0x6; + break; + case 19: + bpp2 = 0x8; + break; + case 24: + bpp2 = 0x9; + break; + case 25: + bpp2 = 0xa; + break; + default: + return -EINVAL; + } + + /* disable branch/start/end of frame interrupt */ + LCCR5 |= (LCCR5_IUM4 | LCCR5_IUM3 | LCCR5_IUM2 | + LCCR5_BSM4 | LCCR5_BSM3 | LCCR5_BSM2 | + LCCR5_EOFM4 | LCCR5_EOFM3 | LCCR5_EOFM2 | + LCCR5_SOFM4 | LCCR5_SOFM3 | LCCR5_SOFM2); + + if (fbi->format == 0) { + /* overlay2 RGB resolution, RGB and YUV have different xres value*/ + xres = fbi->fb.var.xres; + yres = fbi->fb.var.yres; + + OVL2C2 = (fbi->format << 20) | (fbi->ypos << 10) | fbi->xpos; + OVL2C1 = OVL2C1_O2EN | (bpp2 << 20) | ((yres-1)<<10) | (xres-1); + /* setup RGB DMA */ + if (fbi->state == C_DISABLE || fbi->state == C_BLANK) + FDADR2 = fbi->dma2->fdadr; + else + FBR2 = fbi->dma2->fdadr | 0x1; + } else { + /* overlay2 YUV resolution */ + xres = fbi->fb.fix.line_length; + yres = fbi->fb.var.yres; + + OVL2C2 = (fbi->format << 20) | (fbi->ypos << 10) | fbi->xpos; + OVL2C1 = OVL2C1_O2EN | (bpp2 << 20) | ((yres-1)<<10) | (xres-1); +#ifdef CONFIG_PXA27x_E25 + /* FIXME PXA27x E25 */ + if (fbi->format == 4){ + /* FIXME */ + /* Wait util fifo emtpy */ + CLEAR_LCD_INTR(LCSR1, LCSR1_IU2); + WAIT_FOR_LCD_INTR(LCSR1, LCSR1_IU2, 100); + } +#endif + if (fbi->state == C_DISABLE || fbi->state == C_BLANK) { + FDADR2 = fbi->dma2->fdadr; + FDADR3 = fbi->dma3->fdadr; + FDADR4 = fbi->dma4->fdadr; + } else { + FBR2 = fbi->dma2->fdadr | 0x01; + FBR3 = fbi->dma3->fdadr | 0x01; + FBR4 = fbi->dma4->fdadr | 0x01; + } + } + + fbi->state = C_ENABLE; + return 0; +} + +static int overlay2fb_disable(struct fb_info *info) +{ + struct overlayfb_info *fbi = (struct overlayfb_info*)info; + int done; + + if (fbi->state == C_DISABLE) + return 0; + if (fbi->state == C_BLANK) { + fbi->state = C_DISABLE; + return 0; + } + + fbi->state = C_DISABLE; + + /* clear O2EN */ + OVL2C1 &= ~OVL2C1_O2EN; + +/* This sentence was lost in opt.patch. + * That make overlay2 can't disable/enable + * correctly sometimes. + */ + CLEAR_LCD_INTR(LCSR1, LCSR1_BS2); + + if (fbi->format == 0) + FBR2 = 0x3; + else { + FBR2 = 0x3; + FBR3 = 0x3; + FBR4 = 0x3; + } + + done = WAIT_FOR_LCD_INTR(LCSR1, LCSR1_BS2, 100); + + if (!done) { + DPRINTK(KERN_INFO "%s: timeout\n", __FUNCTION__); + return -1; + } + return 0; +} + +static int overlay2fb_blank(int blank, struct fb_info *info) +{ + struct overlayfb_info *fbi = (struct overlayfb_info*) info; + int err=0; + + switch(blank) + { + case 0: + err = overlay2fb_enable(info); + if (err) { + fbi->state = C_DISABLE; + set_ctrlr_state(fbi->basefb, C_REENABLE); + } + break; + case 1: + err = overlay2fb_disable(info); + if (err) { + fbi->state = C_DISABLE; + set_ctrlr_state(fbi->basefb, C_REENABLE); + } + break; + default: + /* reserved */ + break; + } + + return err; +} + + +static int overlay2fb_check_var( struct fb_var_screeninfo *var, struct fb_info *info) +{ + int xpos, ypos, xres, yres; + int format; + struct overlayfb_info *fbi=(struct overlayfb_info*)info; + + xres=yres=0; + + xpos = (var->nonstd & 0x3ff); + ypos = (var->nonstd >> 10) & 0x3ff; + format = (var->nonstd >>20) & 0x7; + + + /* Palnar YCbCr444, YCbCr422, YCbCr420 */ + if ( (format != 0x4) && (format != 0x3) && (format != 0x2) && (format !=0x0)) + return -EINVAL; + + /* dummy pixels */ + switch(format) { + case 0x0: /* RGB */ + xres = var->xres; + break; + case 0x2: /* 444 */ + xres = (var->xres + 0x3) & ~(0x3); + break; + case 0x3: /* 422 */ + xres = (var->xres + 0x7) & ~(0x7); + break; + case 0x4: /* 420 */ + xres = (var->xres + 0xf) & ~(0xf); + break; + } + yres = var->yres; + + if ( (xpos + xres) > fbi->basefb->fb.var.xres ) + return -EINVAL; + + if ( (ypos + yres) > fbi->basefb->fb.var.yres ) + return -EINVAL; + + fbi->old_var=*var; + + var->activate=FB_ACTIVATE_NOW; + + return 0; + +} + + +/* + * overlay2fb_set_var() + * + * var.nonstd is used as YCbCr format. + * var.red/green/blue is used as (Y/Cb/Cr) vector + */ + +static int overlay2fb_set_par(struct fb_info *info) +{ + unsigned int xpos, ypos; + int format, err; + + struct overlayfb_info *fbi=(struct overlayfb_info*)info; + struct fb_var_screeninfo *var = &fbi->fb.var; + + info->flags &= ~FBINFO_MISC_MODECHANGEUSER; + + if (fbi->state == C_BLANK) + return 0; + + if (fbi->state == C_DISABLE) + goto out1; + + if ( (var->xres == fbi->old_var.xres) && + (var->yres == fbi->old_var.yres) && + (var->bits_per_pixel == fbi->old_var.bits_per_pixel) && + (((var->nonstd>>20) & 0x7) == fbi->format) ) + goto out2; + +out1: + xpos = var->nonstd & 0x3ff; + ypos = (var->nonstd>>10) & 0x3ff; + format = (var->nonstd>>20) & 0x7; + + + fbi->format = format; + if ( fbi->format==0 ) + err = overlay2fb_map_RGB_memory(info); + else + err = overlay2fb_map_YUV_memory(info); + + if (err) return err; + +out2: +#ifdef CONFIG_PXA27x_E25 + /* FIXME PXA27x E25 */ + if (C_ENABLE == fbi->state) + overlay2fb_disable(info); + + if ((format == 0x4 ) && + ((fbi->state == C_DISABLE) || (fbi->format != format)) ) + overlay2fb_YUV420_workaround(info); +#endif + + /* position */ + fbi->xpos = var->nonstd & 0x3ff; + fbi->ypos = (var->nonstd>>10) & 0x3ff; + + overlay2fb_enable(info); + + return 0; +} + +static struct fb_ops overlay2fb_ops = { + .owner = THIS_MODULE, + .fb_open = overlay2fb_open, + .fb_release = overlay2fb_release, + .fb_check_var = overlay2fb_check_var, + .fb_set_par = overlay2fb_set_par, + .fb_blank = overlay2fb_blank, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_cursor = soft_cursor, +}; + +/* Hardware cursor */ + +/* Bulverde Cursor Modes */ +struct cursorfb_mode{ + int xres; + int yres; + int bpp; +}; + +static struct cursorfb_mode cursorfb_modes[]={ + { 32, 32, 2}, + { 32, 32, 2}, + { 32, 32, 2}, + { 64, 64, 2}, + { 64, 64, 2}, + { 64, 64, 2}, + {128, 128, 1}, + {128, 128, 1} +}; + +static int cursorfb_enable(struct fb_info *info) +{ + struct overlayfb_info *fbi = (struct overlayfb_info*) info; + + if (!fbi->map_cpu) return -EINVAL; + + CCR &= ~CCR_CEN; + + /* set palette format + * + * FIXME: if only cursor uses palette + */ + LCCR4 = (LCCR4 & (~(0x3<<15))) | (0x1<<15); + + /* disable branch/start/end of frame interrupt */ + LCCR5 |= (LCCR5_IUM5 | LCCR5_BSM5 | LCCR5_EOFM5 | LCCR5_SOFM5); + + /* load palette and frame data */ + if (fbi->state == C_DISABLE) { + FDADR5 = fbi->dma5_pal->fdadr; + udelay(1); + FDADR5 = fbi->dma5_frame->fdadr; + udelay(1); + + } + else { + FBR5 = fbi->dma5_pal->fdadr | 0x1; + udelay(1); + FBR5 = fbi->dma5_frame->fdadr | 0x1; + udelay(1); + } + + CCR = CCR_CEN | (fbi->ypos << 15) | (fbi->xpos << 5) | (fbi->format); + + fbi->state = C_ENABLE; + + return 0; +} + +static int cursorfb_disable(struct fb_info *info) +{ + struct overlayfb_info *fbi = (struct overlayfb_info*)info; + int done, ret = 0; + + fbi->state = C_DISABLE; + + done = WAIT_FOR_LCD_INTR(LCSR1, LCSR1_BS5, 100); + if (!done) ret = -1; + + CCR &= ~CCR_CEN; + + return ret; +} + +static int cursorfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int trans, struct fb_info *info) +{ + struct overlayfb_info *fbi = (struct overlayfb_info *)info; + u_int val, ret = 1; + u_int *pal=(u_int*) fbi->palette_cpu; + + /* 25bit with Transparcy for 16bpp format */ + if (regno < fbi->palette_size) { + val = ((trans << 24) & 0x1000000); + val |= ((red << 16) & 0x0ff0000); + val |= ((green << 8 ) & 0x000ff00); + val |= ((blue << 0) & 0x00000ff); + + pal[regno] = val; + ret = 0; + } + return ret; +} + +int cursorfb_blank(int blank, struct fb_info *info) +{ + switch(blank) + { + case 0: + cursorfb_enable(info); + break; + case 1: + cursorfb_disable(info); + break; + default: + /* reserved */ + break; + } + return 0; +} + +static int cursorfb_check_var( struct fb_var_screeninfo *var, struct fb_info *info) +{ + int xpos, ypos, xres, yres; + int mode; + struct cursorfb_mode *cursor; + struct overlayfb_info *fbi=(struct overlayfb_info*)info; + + mode = var->nonstd & 0x7; + xpos = (var->nonstd>>5) & 0x3ff; + ypos = (var->nonstd>>15) & 0x3ff; + + if (mode>7 || mode <0 ) + return -EINVAL; + + cursor = cursorfb_modes + mode; + + xres = cursor->xres; + yres = cursor->yres; + + if ( (xpos + xres) > fbi->basefb->fb.var.xres ) + return -EINVAL; + + if ( (ypos + yres) > fbi->basefb->fb.var.yres ) + return -EINVAL; + + return 0; + +} + +static int cursorfb_set_par(struct fb_info *info) +{ + struct overlayfb_info *fbi = (struct overlayfb_info*) info; + struct fb_var_screeninfo *var = &fbi->fb.var; + struct cursorfb_mode *cursor; + int mode, xpos, ypos; + int err; + + info->flags &= ~FBINFO_MISC_MODECHANGEUSER; + + mode = var->nonstd & 0x7; + xpos = (var->nonstd>>5) & 0x3ff; + ypos = (var->nonstd>>15) & 0x3ff; + + if (mode != fbi->format) { + cursor = cursorfb_modes + mode; + + /* update "var" info */ + fbi->fb.var.xres = cursor->xres; + fbi->fb.var.yres = cursor->yres; + fbi->fb.var.bits_per_pixel = cursor->bpp; + + /* alloc video memory + * + * 4k is engouh for 128x128x1 cursor, + * - 2k for cursor pixels, + * - 2k for palette data, plus 2 dma descriptor + */ + if (!fbi->map_cpu) { + fbi->map_size = PAGE_SIZE; + fbi->map_cpu = (unsigned long)dma_alloc_writecombine(NULL, fbi->map_size, + &fbi->map_dma, GFP_KERNEL ); + if (!fbi->map_cpu) return -ENOMEM; + } + + cursor = cursorfb_modes + mode; + + /* update overlay & fix "info" */ + fbi->screen_cpu = fbi->map_cpu; + fbi->palette_cpu = fbi->map_cpu + (PAGE_SIZE/2); + fbi->screen_dma = fbi->map_dma; + fbi->palette_dma = fbi->map_dma + (PAGE_SIZE/2); + + fbi->format = mode; + fbi->palette_size = (1<bpp) ; + fbi->fb.fix.smem_start = fbi->screen_dma; + fbi->fb.fix.smem_len = cursor->xres * cursor->yres * cursor->bpp / 8; + fbi->fb.fix.line_length = cursor->xres * cursor->bpp / 8 ; + + fbi->dma5_pal = (struct pxafb_dma_descriptor*)(fbi->map_cpu + PAGE_SIZE - 16 ); + fbi->dma5_pal->fdadr = (fbi->map_dma + PAGE_SIZE - 16); + fbi->dma5_pal->fsadr = fbi->palette_dma; + fbi->dma5_pal->fidr = 0; + fbi->dma5_pal->ldcmd = (fbi->palette_size<<2) | LDCMD_PAL; + + fbi->dma5_frame = (struct pxafb_dma_descriptor*)(fbi->map_cpu + PAGE_SIZE - 32 ); + fbi->dma5_frame->fdadr = (fbi->map_dma + PAGE_SIZE - 32); + fbi->dma5_frame->fsadr = fbi->screen_dma; + fbi->dma5_frame->fidr = 0; + fbi->dma5_frame->ldcmd = fbi->fb.fix.smem_len; + + /* alloc & set default cmap */ + err = fb_alloc_cmap(&fbi->fb.cmap, fbi->palette_size, 0); + if (err) return err; + err = fb_set_cmap(&fbi->fb.cmap, info); + if (err) return err; + } + + /* update overlay info */ + if( (xpos != fbi->xpos) || (ypos != fbi->ypos) ) { + fbi->xpos = xpos; + fbi->ypos = ypos; + } + + cursorfb_enable(info); + set_ctrlr_state(fbi->basefb, C_REENABLE); + + return 0; +} + +static struct fb_ops cursorfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = cursorfb_check_var, + .fb_set_par = cursorfb_set_par, + .fb_blank = cursorfb_blank, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_setcolreg = cursorfb_setcolreg, +}; + +static struct overlayfb_info * __init overlay1fb_init_fbinfo(void) +{ + struct overlayfb_info *fbi; + + fbi = kmalloc(sizeof(struct overlayfb_info) + sizeof(u16) * 16, GFP_KERNEL); + if (!fbi) + return NULL; + + memset(fbi, 0, sizeof(struct overlayfb_info) ); + + fbi->refcount = 0; + init_MUTEX(&fbi->mutex); + + strcpy(fbi->fb.fix.id, "overlay1"); + + fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS; + fbi->fb.fix.type_aux = 0; + fbi->fb.fix.xpanstep = 0; + fbi->fb.fix.ypanstep = 0; + fbi->fb.fix.ywrapstep = 0; + fbi->fb.fix.accel = FB_ACCEL_NONE; + + fbi->fb.var.nonstd = 0; + fbi->fb.var.activate = FB_ACTIVATE_NOW; + fbi->fb.var.height = -1; + fbi->fb.var.width = -1; + fbi->fb.var.accel_flags = 0; + fbi->fb.var.vmode = FB_VMODE_NONINTERLACED; + + + fbi->fb.fbops = &overlay1fb_ops; + fbi->fb.flags = FBINFO_FLAG_DEFAULT; + fbi->fb.node = -1; + fbi->fb.pseudo_palette = NULL; + + fbi->xpos = 0; + fbi->ypos = 0; + fbi->format = -1; + fbi->state = C_DISABLE; + + return fbi; +} + +static struct overlayfb_info * __init overlay2fb_init_fbinfo(void) +{ + struct overlayfb_info *fbi; + + fbi = kmalloc(sizeof(struct overlayfb_info) + sizeof(u16) * 16, GFP_KERNEL); + if (!fbi) + return NULL; + + memset(fbi, 0, sizeof(struct overlayfb_info) ); + + fbi->refcount = 0; + init_MUTEX(&fbi->mutex); + + strcpy(fbi->fb.fix.id, "overlay2"); + + fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS; + fbi->fb.fix.type_aux = 0; + fbi->fb.fix.xpanstep = 0; + fbi->fb.fix.ypanstep = 0; + fbi->fb.fix.ywrapstep = 0; + fbi->fb.fix.accel = FB_ACCEL_NONE; + + fbi->fb.var.nonstd = 0; + fbi->fb.var.activate = FB_ACTIVATE_NOW; + fbi->fb.var.height = -1; + fbi->fb.var.width = -1; + fbi->fb.var.accel_flags = 0; + fbi->fb.var.vmode = FB_VMODE_NONINTERLACED; + + fbi->fb.fbops = &overlay2fb_ops; + fbi->fb.flags = FBINFO_FLAG_DEFAULT; + fbi->fb.node = -1; + fbi->fb.pseudo_palette = NULL; + + fbi->xpos = 0; + fbi->ypos = 0; + fbi->format = -1; + fbi->state = C_DISABLE; + + return fbi; +} + +static struct overlayfb_info * __init cursorfb_init_fbinfo(void) +{ + struct overlayfb_info *fbi; + + fbi = kmalloc(sizeof(struct overlayfb_info) + sizeof(u16) * 16, GFP_KERNEL); + if (!fbi) + return NULL; + + memset(fbi, 0, sizeof(struct overlayfb_info) ); + + fbi->refcount = 0; + init_MUTEX(&fbi->mutex); + + strcpy(fbi->fb.fix.id, "cursor"); + + fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS; + fbi->fb.fix.type_aux = 0; + fbi->fb.fix.xpanstep = 0; + fbi->fb.fix.ypanstep = 0; + fbi->fb.fix.ywrapstep = 0; + fbi->fb.fix.accel = FB_ACCEL_NONE; + + fbi->fb.var.nonstd = 0; + fbi->fb.var.activate = FB_ACTIVATE_NOW; + fbi->fb.var.height = -1; + fbi->fb.var.width = -1; + fbi->fb.var.accel_flags = 0; + fbi->fb.var.vmode = FB_VMODE_NONINTERLACED; + + fbi->fb.fbops = &cursorfb_ops; + fbi->fb.flags = FBINFO_FLAG_DEFAULT; + fbi->fb.node = -1; + fbi->fb.pseudo_palette = NULL; + + + fbi->xpos = 0; + fbi->ypos = 0; + fbi->format = -1; + fbi->state = C_DISABLE; + + return fbi; +} + + +void pxa_set_overlay_ctrlr_state(struct pxafb_info *fbi, u_int state) +{ + switch (state) { + case C_DISABLE: + DISABLE_OVERLAYS(fbi); + break; + case C_ENABLE: + ENABLE_OVERLAYS(fbi); + break; + case C_BLANK: + BLANK_OVERLAYS(fbi); + break; + case C_UNBLANK: + UNBLANK_OVERLAYS(fbi); + break; + default: + break; + } +} + +struct callback_data_t{ + char *name; + struct device *dev; +}; + + +static int find_dev(struct device *dev, void *callback_data) +{ + int found=0; + struct callback_data_t * data=(struct callback_data_t *) callback_data; + + found = (strncmp(dev->kobj.name, data->name, KOBJ_NAME_LEN) == 0); + if(found == 1){ + data->dev = dev; + } + + return found; +} +struct device* find_bus_device(struct bus_type *bus, char *name) +{ + struct callback_data_t callback_data; + + callback_data.name = name; + callback_data.dev = NULL; + bus_for_each_dev(bus, NULL, &callback_data, find_dev); + + return callback_data.dev; +} + +static int __devinit pxafb_overlay_init(void) +{ + int ret; + struct overlayfb_info *overlay1fb, *overlay2fb, *cursorfb; + struct pxafb_info *fbi; + struct device *dev; + + ret = -1; + overlay1fb = overlay2fb = cursorfb = NULL; + fbi=NULL; + + dev=find_bus_device(&platform_bus_type, "pxa2xx-fb"); + if(dev ==NULL){ + printk(KERN_INFO "Base framebuffer not exists, failed to load overlay driver!\n"); + return ret; + } + + fbi = dev_get_drvdata(dev); + if(fbi ==NULL ){ + printk(KERN_INFO "Base framebuffer not initialized, failed to load overlay driver!\n"); + return ret; + } + + + /* Overlay 1 windows */ + overlay1fb = overlay1fb_init_fbinfo(); + + if(!overlay1fb) { + ret = -ENOMEM; + printk("overlay1fb_init_fbinfo failed\n"); + goto failed; + } + + + ret = register_framebuffer(&overlay1fb->fb); + if (ret<0) goto failed; + + /* Overlay 2 window */ + overlay2fb = overlay2fb_init_fbinfo(); + + if(!overlay2fb) { + ret = -ENOMEM; + printk("overlay2fb_init_fbinfo failed\n"); + goto failed; + } + + ret = register_framebuffer(&overlay2fb->fb); + if (ret<0) goto failed; + + /* Hardware cursor window */ + cursorfb = cursorfb_init_fbinfo(); + + if(!cursorfb) { + ret = -ENOMEM; + printk("cursorfb_init_fbinfo failed\n"); + goto failed; + } + + ret = register_framebuffer(&cursorfb->fb); + if (ret<0) goto failed; + + + /* set refernce to Overlays */ + fbi->overlay1fb = overlay1fb; + fbi->overlay2fb = overlay2fb; + fbi->cursorfb = cursorfb; + fbi->set_overlay_ctrlr_state=pxa_set_overlay_ctrlr_state; + + /* set refernce to BaseFrame */ + overlay1fb->basefb = fbi; + overlay2fb->basefb = fbi; + cursorfb->basefb = fbi; + + printk(KERN_INFO "Load PXA Overlay driver successfully!\n"); + + return 0; + +failed: + if (overlay1fb) + kfree(overlay1fb); + if (overlay2fb) + kfree(overlay2fb); + if (cursorfb) + kfree(cursorfb); + printk(KERN_INFO "Load PXA Overlay driver failed!\n"); + return ret; +} + +static void __exit pxafb_overlay_exit(void) +{ + struct pxafb_info *fbi; + struct device *dev; + + dev=find_bus_device(&platform_bus_type, "pxa2xx-fb"); + if(dev ==NULL){ + return ; + } + + fbi = dev_get_drvdata(dev); + if(fbi ==NULL ){ + return ; + } + + if (fbi->overlay1fb) { + unregister_framebuffer(&(fbi->overlay1fb->fb)); + kfree(fbi->overlay1fb); + fbi->overlay1fb = NULL; + } + + if (fbi->overlay2fb) { + unregister_framebuffer(&(fbi->overlay2fb->fb)); + kfree(fbi->overlay2fb); + fbi->overlay2fb = NULL; + } + + if (fbi->cursorfb) { + unregister_framebuffer(&(fbi->cursorfb->fb)); + kfree(fbi->cursorfb); + fbi->cursorfb = NULL; + } + + fbi->set_overlay_ctrlr_state = NULL; + + printk(KERN_INFO "Unload PXA Overlay driver successfully!\n"); + return ; +} + + +module_init(pxafb_overlay_init); +module_exit(pxafb_overlay_exit); + +MODULE_DESCRIPTION("Loadable framebuffer overlay driver for PXA"); +MODULE_LICENSE("GPL"); + diff -uNr a/include/asm-arm/arch-pxa/hardware.h b/include/asm-arm/arch-pxa/hardware.h --- a/include/asm-arm/arch-pxa/hardware.h 2004-10-19 05:53:06.000000000 +0800 +++ b/include/asm-arm/arch-pxa/hardware.h 2005-01-27 09:56:49.000000000 +0800 @@ -74,6 +74,11 @@ #ifndef __ASSEMBLY__ +#ifdef CONFIG_ARCH_PXA +#include "sram.h" +#endif + + /* * Handy routine to set GPIO alternate functions */ @@ -92,4 +97,8 @@ #endif +#ifdef CONFIG_MACH_MAINSTONE +#include "mainstone.h" +#endif + #endif /* _ASM_ARCH_HARDWARE_H */ diff -uNr a/include/asm-arm/arch-pxa/i2c-pxa.h b/include/asm-arm/arch-pxa/i2c-pxa.h --- a/include/asm-arm/arch-pxa/i2c-pxa.h 1970-01-01 08:00:00.000000000 +0800 +++ b/include/asm-arm/arch-pxa/i2c-pxa.h 2005-01-27 09:56:49.000000000 +0800 @@ -0,0 +1,76 @@ +/* + * i2c_pxa.h + * + * Copyright (C) 2002 Intrinsyc Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#ifndef _I2C_PXA_H_ +#define _I2C_PXA_H_ + +struct i2c_algo_pxa_data +{ + void (*write_byte) (u8 value); + u8 (*read_byte) (void); + void (*start) (void); + void (*repeat_start) (void); + void (*stop) (void); + void (*abort) (void); + int (*wait_bus_not_busy) (void); + int (*wait_for_interrupt) (int wait_type); + void (*transfer) (int lastbyte, int receive, int midbyte); + void (*reset) (void); + + int udelay; + int timeout; +}; + +#define DEF_TIMEOUT 3 +#define BUS_ERROR (-EREMOTEIO) +#define ACK_DELAY 0 /* time to delay before checking bus error */ +#define MAX_MESSAGES 65536 /* maximum number of messages to send */ + +#define I2C_SLEEP_TIMEOUT 2 /* time to sleep for on i2c transactions */ +#define I2C_RETRY (-2000) /* an error has occurred retry transmit */ +#define I2C_TRANSMIT 1 +#define I2C_RECEIVE 0 +#define I2C_PXA_SLAVE_ADDR 0x1 /* slave pxa unit address */ +#define I2C_ICR_INIT (ICR_BEIE | ICR_IRFIE | ICR_ITEIE | ICR_GCD | ICR_SCLE) /* ICR initialization value */ +/* ICR initialize bit values +* +* 15. FM 0 (100 Khz operation) +* 14. UR 0 (No unit reset) +* 13. SADIE 0 (Disables the unit from interrupting on slave addresses +* matching its slave address) +* 12. ALDIE 0 (Disables the unit from interrupt when it loses arbitration +* in master mode) +* 11. SSDIE 0 (Disables interrupts from a slave stop detected, in slave mode) +* 10. BEIE 1 (Enable interrupts from detected bus errors, no ACK sent) +* 9. IRFIE 1 (Enable interrupts from full buffer received) +* 8. ITEIE 1 (Enables the I2C unit to interrupt when transmit buffer empty) +* 7. GCD 1 (Disables i2c unit response to general call messages as a slave) +* 6. IUE 0 (Disable unit until we change settings) +* 5. SCLE 1 (Enables the i2c clock output for master mode (drives SCL) +* 4. MA 0 (Only send stop with the ICR stop bit) +* 3. TB 0 (We are not transmitting a byte initially) +* 2. ACKNAK 0 (Send an ACK after the unit receives a byte) +* 1. STOP 0 (Do not send a STOP) +* 0. START 0 (Do not send a START) +* +*/ + +#define I2C_ISR_INIT 0x7FF /* status register init */ +/* I2C status register init values + * + * 10. BED 1 (Clear bus error detected) + * 9. SAD 1 (Clear slave address detected) + * 7. IRF 1 (Clear IDBR Receive Full) + * 6. ITE 1 (Clear IDBR Transmit Empty) + * 5. ALD 1 (Clear Arbitration Loss Detected) + * 4. SSD 1 (Clear Slave Stop Detected) + */ + +#endif diff -uNr a/include/asm-arm/arch-pxa/mainstone.h b/include/asm-arm/arch-pxa/mainstone.h --- a/include/asm-arm/arch-pxa/mainstone.h 2004-10-19 05:53:43.000000000 +0800 +++ b/include/asm-arm/arch-pxa/mainstone.h 2005-01-27 09:56:49.000000000 +0800 @@ -117,4 +117,41 @@ #define MST_PCMCIA_PWR_VCC_33 0x8 /* voltage VCC = 3.3V */ #define MST_PCMCIA_PWR_VCC_50 0x4 /* voltage VCC = 5.0V */ +/* + * LED macros + */ + +#define LEDS_BASE MST_LEDCTRL + +// 8 discrete leds available for general use: + +#define D28 0x1 +#define D27 0x2 +#define D26 0x4 +#define D25 0x8 +#define D24 0x10 +#define D23 0x20 +#define D22 0x40 +#define D21 0x80 + +/* Note: bits [15-8] are used to enable/blank the 8 7 segment hex displays so + * be sure to not monkey with them here. + */ + +#define HEARTBEAT_LED D28 +#define SYS_BUSY_LED D27 +#define HEXLEDS_BASE MST_HEXLED + +#define HEARTBEAT_LED_ON (LEDS_BASE &= ~HEARTBEAT_LED) +#define HEARTBEAT_LED_OFF (LEDS_BASE |= HEARTBEAT_LED) +#define SYS_BUSY_LED_OFF (LEDS_BASE |= SYS_BUSY_LED) +#define SYS_BUSY_LED_ON (LEDS_BASE &= ~SYS_BUSY_LED) + +//use x = D26-D21 for these, please... +#define DISCRETE_LED_ON(x) (LEDS_BASE &= ~(x)) +#define DISCRETE_LED_OFF(x) (LEDS_BASE |= (x)) + +#define BUMP_COUNTER (HEXLEDS_BASE = hexled_val++) +#define DEC_COUNTER (HEXLEDS_BASE = hexled_val--) + #endif diff -uNr a/include/asm-arm/arch-pxa/pxa-regs.h b/include/asm-arm/arch-pxa/pxa-regs.h --- a/include/asm-arm/arch-pxa/pxa-regs.h 2004-10-19 05:54:37.000000000 +0800 +++ b/include/asm-arm/arch-pxa/pxa-regs.h 2005-01-27 09:56:49.000000000 +0800 @@ -5,6 +5,8 @@ * Created: Jun 15, 2001 * Copyright: MontaVista Software Inc. * + * Copyright (C) 2004, Intel Corporation. + * * 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. @@ -100,7 +102,7 @@ #define DCSR_SETCMPST (1 << 25) /* Set Descriptor Compare Status */ #define DCSR_CLRCMPST (1 << 24) /* Clear Descriptor Compare Status */ #define DCSR_CMPST (1 << 10) /* The Descriptor Compare Status */ -#define DCSR_ENRINTR (1 << 9) /* The end of Receive */ +#define DCSR_EORINTR (1 << 9) /* The end of Receive */ #endif #define DCSR_REQPEND (1 << 8) /* Request Pending (read-only) */ #define DCSR_STOPSTATE (1 << 3) /* Stop State (read-only) */ @@ -108,6 +110,7 @@ #define DCSR_STARTINTR (1 << 1) /* Start Interrupt (read / write) */ #define DCSR_BUSERR (1 << 0) /* Bus Error Interrupt (read / write) */ +#define DALGN __REG(0x400000a0) /* DMA Alignment Register */ #define DINT __REG(0x400000f0) /* DMA Interrupt Register */ #define DRCMR(n) __REG2(0x40000100, (n)<<2) @@ -739,11 +742,11 @@ #define UDCISR0 __REG(0x4060000C) /* UDC Interrupt Status Register 0 */ #define UDCISR1 __REG(0x40600010) /* UDC Interrupt Status Register 1 */ #define UDCISR_INT(n,intr) (((intr) & 0x03) << (((n) & 0x0F) * 2)) -#define UDCISR1_IECC (1 << 31) /* IntEn - Configuration Change */ -#define UDCISR1_IESOF (1 << 30) /* IntEn - Start of Frame */ -#define UDCISR1_IERU (1 << 29) /* IntEn - Resume */ -#define UDCISR1_IESU (1 << 28) /* IntEn - Suspend */ -#define UDCISR1_IERS (1 << 27) /* IntEn - Reset */ +#define UDCISR1_IRCC (1 << 31) /* IntEn - Configuration Change */ +#define UDCISR1_IRSOF (1 << 30) /* IntEn - Start of Frame */ +#define UDCISR1_IRRU (1 << 29) /* IntEn - Resume */ +#define UDCISR1_IRSU (1 << 28) /* IntEn - Suspend */ +#define UDCISR1_IRRS (1 << 27) /* IntEn - Reset */ #define UDCFNR __REG(0x40600014) /* UDC Frame Number Register */ @@ -1308,6 +1311,7 @@ #define GPIO43_BTTXD_MD (43 | GPIO_ALT_FN_2_OUT) #define GPIO44_BTCTS_MD (44 | GPIO_ALT_FN_1_IN) #define GPIO45_BTRTS_MD (45 | GPIO_ALT_FN_2_OUT) +#define GPIO45_SYSCLK_AC97_MD (45 | GPIO_ALT_FN_1_OUT) #define GPIO46_ICPRXD_MD (46 | GPIO_ALT_FN_1_IN) #define GPIO46_STRXD_MD (46 | GPIO_ALT_FN_2_IN) #define GPIO47_ICPTXD_MD (47 | GPIO_ALT_FN_2_OUT) @@ -1358,6 +1362,8 @@ #define GPIO79_pSKTSEL_MD (79 | GPIO_ALT_FN_1_OUT) #define GPIO80_nCS_4_MD (80 | GPIO_ALT_FN_2_OUT) #define GPIO85_nPCE_1_MD (85 | GPIO_ALT_FN_1_OUT) +#define GPIO88_USBH1_PWR_MD (88 | GPIO_ALT_FN_1_IN) +#define GPIO89_USBH1_PEN_MD (89 | GPIO_ALT_FN_2_OUT) #define GPIO92_MMCDAT0_MD (92 | GPIO_ALT_FN_1_OUT) #define GPIO109_MMCDAT1_MD (109 | GPIO_ALT_FN_1_OUT) #define GPIO110_MMCDAT2_MD (110 | GPIO_ALT_FN_1_OUT) @@ -1366,6 +1372,8 @@ #define GPIO110_MMCCS1_MD (111 | GPIO_ALT_FN_1_OUT) #define GPIO112_MMCCMD_MD (112 | GPIO_ALT_FN_1_OUT) #define GPIO113_AC97_RESET_N_MD (113 | GPIO_ALT_FN_2_OUT) +#define GPIO117_I2CSCL_MD (117 | GPIO_ALT_FN_1_OUT) +#define GPIO118_I2CSDA_MD (118 | GPIO_ALT_FN_1_IN) /* @@ -1436,7 +1444,7 @@ #define PVCR_CommandDelay (0xf80) #define PCFR_PI2C_EN (0x1 << 6) -#define PSSR_OTGPH (1 << 7) /* OTG Peripheral control Hold */ +#define PSSR_OTGPH (1 << 6) /* OTG Peripheral control Hold */ #define PSSR_RDH (1 << 5) /* Read Disable Hold */ #define PSSR_PH (1 << 4) /* Peripheral Control Hold */ #define PSSR_VFS (1 << 2) /* VDD Fault Status */ @@ -1538,6 +1546,7 @@ #define CCCR_M_MASK 0x0060 /* Memory Frequency to Run Mode Frequency Multiplier */ #define CCCR_L_MASK 0x001f /* Crystal Frequency to Memory Frequency Multiplier */ +#define CKEN31_AC97 (1 << 31) #define CKEN24_CAMERA (1 << 24) /* Camera Interface Clock Enable */ #define CKEN23_SSP1 (1 << 23) /* SSP1 Unit Clock Enable */ #define CKEN22_MEMC (1 << 22) /* Memory Controller Clock Enable */ @@ -1580,6 +1589,8 @@ #define DFBR0 __REG(0x44000020) /* DMA Channel 0 Frame Branch Register */ #define DFBR1 __REG(0x44000024) /* DMA Channel 1 Frame Branch Register */ #define LCSR __REG(0x44000038) /* LCD Controller Status Register */ +#define LCSR0 __REG(0x44000038) /* LCD Controller Status Register */ +#define LCSR1 __REG(0x44000034) /* LCD Controller Status Register */ #define LIIDR __REG(0x4400003C) /* LCD Controller Interrupt ID Register */ #define TMEDRGBR __REG(0x44000040) /* TMED RGB Seed Register */ #define TMEDCR __REG(0x44000044) /* TMED Control Register */ @@ -1589,6 +1600,10 @@ #define LCCR3_4BPP (2 << 24) #define LCCR3_8BPP (3 << 24) #define LCCR3_16BPP (4 << 24) +#define LCCR3_18BPP (6 << 24) /* packed pixel format */ +#define LCCR3_19BPP (8 << 24) /* packed pixel format */ +#define LCCR3_24BPP (9 << 24) +#define LCCR3_25BPP (10<< 24) #define FDADR0 __REG(0x44000200) /* DMA Channel 0 Frame Descriptor Address Register */ #define FSADR0 __REG(0x44000204) /* DMA Channel 0 Frame Source Address Register */ @@ -1627,6 +1642,9 @@ #define LCCR0_PDD_S 12 #define LCCR0_BM (1 << 20) /* Branch mask */ #define LCCR0_OUM (1 << 21) /* Output FIFO underrun mask */ +#define LCCR0_LCDT (1 << 22) /* LCD Panel Type */ +#define LCCR0_RDSTM (1 << 23) /* Read Status Interrupt Mask */ +#define LCCR0_CMDIM (1 << 24) /* Command Interrupt Mask */ #define LCCR1_PPL Fld (10, 0) /* Pixels Per Line - 1 */ #define LCCR1_DisWdth(Pixel) /* Display Width [1..800 pix.] */ \ @@ -1748,6 +1766,106 @@ #define LDCMD_PAL (1 << 26) /* instructs DMA to load palette buffer */ +/* Overlay1 & Overlay2 & Hardware Cursor */ +#define LCSR1_SOF1 (1 << 0) +#define LCSR1_SOF2 (1 << 1) +#define LCSR1_SOF3 (1 << 2) +#define LCSR1_SOF4 (1 << 3) +#define LCSR1_SOF5 (1 << 4) +#define LCSR1_SOF6 (1 << 5) + +#define LCSR1_EOF1 (1 << 8) +#define LCSR1_EOF2 (1 << 9) +#define LCSR1_EOF3 (1 << 10) +#define LCSR1_EOF4 (1 << 11) +#define LCSR1_EOF5 (1 << 12) +#define LCSR1_EOF6 (1 << 13) + +#define LCSR1_BS1 (1 << 16) +#define LCSR1_BS2 (1 << 17) +#define LCSR1_BS3 (1 << 18) +#define LCSR1_BS4 (1 << 19) +#define LCSR1_BS5 (1 << 20) +#define LCSR1_BS6 (1 << 21) + +#define LCSR1_IU2 (1 << 25) +#define LCSR1_IU3 (1 << 26) +#define LCSR1_IU4 (1 << 27) +#define LCSR1_IU5 (1 << 28) +#define LCSR1_IU6 (1 << 29) + +#define LDCMD_SOFINT (1 << 22) +#define LDCMD_EOFINT (1 << 21) + +#define LCCR0_LDDALT (1<<26) /* LDD Alternate mapping bit when base pixel is RGBT16 */ +#define LCCR0_OUC (1<<25) /* Overlay Underlay Control Bit */ + +#define LCCR5_SOFM1 (1<<0) /* Start Of Frame Mask for Overlay 1 (channel 1) */ +#define LCCR5_SOFM2 (1<<1) /* Start Of Frame Mask for Overlay 2 (channel 2) */ +#define LCCR5_SOFM3 (1<<2) /* Start Of Frame Mask for Overlay 2 (channel 3) */ +#define LCCR5_SOFM4 (1<<3) /* Start Of Frame Mask for Overlay 2 (channel 4) */ +#define LCCR5_SOFM5 (1<<4) /* Start Of Frame Mask for cursor (channel 5) */ +#define LCCR5_SOFM6 (1<<5) /* Start Of Frame Mask for command data (channel 6) */ + +#define LCCR5_EOFM1 (1<<8) /* End Of Frame Mask for Overlay 1 (channel 1) */ +#define LCCR5_EOFM2 (1<<9) /* End Of Frame Mask for Overlay 2 (channel 2) */ +#define LCCR5_EOFM3 (1<<10) /* End Of Frame Mask for Overlay 2 (channel 3) */ +#define LCCR5_EOFM4 (1<<11) /* End Of Frame Mask for Overlay 2 (channel 4) */ +#define LCCR5_EOFM5 (1<<12) /* End Of Frame Mask for cursor (channel 5) */ +#define LCCR5_EOFM6 (1<<13) /* End Of Frame Mask for command data (channel 6) */ + +#define LCCR5_BSM1 (1<<16) /* Branch mask for Overlay 1 (channel 1) */ +#define LCCR5_BSM2 (1<<17) /* Branch mask for Overlay 2 (channel 2) */ +#define LCCR5_BSM3 (1<<18) /* Branch mask for Overlay 2 (channel 3) */ +#define LCCR5_BSM4 (1<<19) /* Branch mask for Overlay 2 (channel 4) */ +#define LCCR5_BSM5 (1<<20) /* Branch mask for cursor (channel 5) */ +#define LCCR5_BSM6 (1<<21) /* Branch mask for data command (channel 6) */ + +#define LCCR5_IUM1 (1<<24) /* Input FIFO Underrun Mask for Overlay 1 */ +#define LCCR5_IUM2 (1<<25) /* Input FIFO Underrun Mask for Overlay 2 */ +#define LCCR5_IUM3 (1<<26) /* Input FIFO Underrun Mask for Overlay 2 */ +#define LCCR5_IUM4 (1<<27) /* Input FIFO Underrun Mask for Overlay 2 */ +#define LCCR5_IUM5 (1<<28) /* Input FIFO Underrun Mask for cursor */ +#define LCCR5_IUM6 (1<<29) /* Input FIFO Underrun Mask for data command */ + +#define OVL1C1_O1EN (1<<31) /* Enable bit for Overlay 1 */ +#define OVL2C1_O2EN (1<<31) /* Enable bit for Overlay 2 */ +#define CCR_CEN (1<<31) /* Enable bit for Cursor */ + +/* LCD registers */ +#define LCCR4 __REG(0x44000010) /* LCD Controller Control Register 4 */ +#define LCCR5 __REG(0x44000014) /* LCD Controller Control Register 5 */ +#define FBR0 __REG(0x44000020) /* DMA Channel 0 Frame Branch Register */ +#define FBR1 __REG(0x44000024) /* DMA Channel 1 Frame Branch Register */ +#define FBR2 __REG(0x44000028) /* DMA Channel 2 Frame Branch Register */ +#define FBR3 __REG(0x4400002C) /* DMA Channel 3 Frame Branch Register */ +#define FBR4 __REG(0x44000030) /* DMA Channel 4 Frame Branch Register */ +#define FDADR2 __REG(0x44000220) /* DMA Channel 2 Frame Descriptor Address Register */ +#define FSADR2 __REG(0x44000224) /* DMA Channel 2 Frame Source Address Register */ +#define FIDR2 __REG(0x44000228) /* DMA Channel 2 Frame ID Register */ +#define LDCMD2 __REG(0x4400022C) /* DMA Channel 2 Command Register */ +#define FDADR3 __REG(0x44000230) /* DMA Channel 3 Frame Descriptor Address Register */ +#define FSADR3 __REG(0x44000234) /* DMA Channel 3 Frame Source Address Register */ +#define FIDR3 __REG(0x44000238) /* DMA Channel 3 Frame ID Register */ +#define LDCMD3 __REG(0x4400023C) /* DMA Channel 3 Command Register */ +#define FDADR4 __REG(0x44000240) /* DMA Channel 4 Frame Descriptor Address Register */ +#define FSADR4 __REG(0x44000244) /* DMA Channel 4 Frame Source Address Register */ +#define FIDR4 __REG(0x44000248) /* DMA Channel 4 Frame ID Register */ +#define LDCMD4 __REG(0x4400024C) /* DMA Channel 4 Command Register */ +#define FDADR5 __REG(0x44000250) /* DMA Channel 5 Frame Descriptor Address Register */ +#define FSADR5 __REG(0x44000254) /* DMA Channel 5 Frame Source Address Register */ +#define FIDR5 __REG(0x44000258) /* DMA Channel 5 Frame ID Register */ +#define LDCMD5 __REG(0x4400025C) /* DMA Channel 5 Command Register */ + +#define OVL1C1 __REG(0x44000050) /* Overlay 1 Control Register 1 */ +#define OVL1C2 __REG(0x44000060) /* Overlay 1 Control Register 2 */ +#define OVL2C1 __REG(0x44000070) /* Overlay 2 Control Register 1 */ +#define OVL2C2 __REG(0x44000080) /* Overlay 2 Control Register 2 */ +#define CCR __REG(0x44000090) /* Cursor Control Register */ + +#define FBR5 __REG(0x44000110) /* DMA Channel 5 Frame Branch Register */ +#define FBR6 __REG(0x44000114) /* DMA Channel 6 Frame Branch Register */ + /* * Memory controller */ @@ -1800,6 +1918,18 @@ #ifdef CONFIG_PXA27x +#define ARB_CNTRL __REG(0x48000048) /* Arbiter Control Register */ + +#define ARB_DMA_SLV_PARK (1<<31) /* Be parked with DMA slave when idle */ +#define ARB_CI_PARK (1<<30) /* Be parked with Camera Interface when idle */ +#define ARB_EX_MEM_PARK (1<<29) /* Be parked with external MEMC when idle */ +#define ARB_INT_MEM_PARK (1<<28) /* Be parked with internal MEMC when idle */ +#define ARB_USB_PARK (1<<27) /* Be parked with USB when idle */ +#define ARB_LCD_PARK (1<<26) /* Be parked with LCD when idle */ +#define ARB_DMA_PARK (1<<25) /* Be parked with DMA when idle */ +#define ARB_CORE_PARK (1<<24) /* Be parked with core when idle */ +#define ARB_LOCK_FLAG (1<<23) /* Only Locking masters gain access to the bus */ + /* * Keypad */ @@ -1884,6 +2014,7 @@ #define UHCRHPS1 __REG(0x4C000054) /* UHC Root Hub Port 1 Status */ #define UHCRHPS2 __REG(0x4C000058) /* UHC Root Hub Port 2 Status */ #define UHCRHPS3 __REG(0x4C00005C) /* UHC Root Hub Port 3 Status */ +#define UHCRHPS(x) __REG2(0x4C000050, (x)<<2) #define UHCSTAT __REG(0x4C000060) /* UHC Status Register */ #define UHCSTAT_UPS3 (1 << 16) /* USB Power Sense Port3 */ diff -uNr a/include/asm-arm/arch-pxa/sram.h b/include/asm-arm/arch-pxa/sram.h --- a/include/asm-arm/arch-pxa/sram.h 1970-01-01 08:00:00.000000000 +0800 +++ b/include/asm-arm/arch-pxa/sram.h 2005-01-27 09:56:49.000000000 +0800 @@ -0,0 +1,10 @@ +#ifndef _ASM_ARCH_SRAM_H +#define _ASM_ARCH_SRAM_H + +extern void * xscale_sram_malloc(u32); +extern void * xscale_sram_malloc_user(u32, u32, u32); +extern void xscale_sram_free(void *); +extern void xscale_sram_free_user(void *, u32); +extern void xscale_sram_free_all_pid(u32); + +#endif diff -uNr a/include/asm-arm/arch-pxa/xscale-pmu.h b/include/asm-arm/arch-pxa/xscale-pmu.h --- a/include/asm-arm/arch-pxa/xscale-pmu.h 1970-01-01 08:00:00.000000000 +0800 +++ b/include/asm-arm/arch-pxa/xscale-pmu.h 2005-01-27 09:56:49.000000000 +0800 @@ -0,0 +1,63 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef _PMU_H_ +#define _PMU_H_ + +#include + +/* + * Different types of events that can be counted by the XScale PMU + */ + +#define EVT_ICACHE_MISS 0x00 +#define EVT_ICACHE_NO_DELIVER 0x01 +#define EVT_DATA_STALL 0x02 +#define EVT_ITLB_MISS 0x03 +#define EVT_DTLB_MISS 0x04 +#define EVT_BRANCH 0x05 +#define EVT_BRANCH_MISS 0x06 +#define EVT_INSTRUCTION 0x07 +#define EVT_DCACHE_FULL_STALL 0x08 +#define EVT_DCACHE_FULL_STALL_CONTIG 0x09 +#define EVT_DCACHE_ACCESS 0x0A +#define EVT_DCACHE_MISS 0x0B +#define EVT_DCACE_WRITE_BACK 0x0C +#define EVT_PC_CHANGED 0x0D +#define EVT_BCU_REQUEST 0x10 +#define EVT_BCU_FULL 0x11 +#define EVT_BCU_DRAIN 0x12 +#define EVT_BCU_ECC_NO_ELOG 0x14 +#define EVT_BCU_1_BIT_ERR 0x15 +#define EVT_RMW 0x16 + + +struct pmu_results +{ + u32 ccnt_of; + u32 ccnt; /* Clock Counter Register */ + u32 pmn0_of; + u32 pmn0; /* Performance Counter Register 0 */ + u32 pmn1_of; + u32 pmn1; /* Performance Counter Register 1 */ + u32 pmn2_of; + u32 pmn2; /* Performance Counter Register 2 */ + u32 pmn3_of; + u32 pmn3; /* Performance Counter Register 3 */ +}; + +extern struct pmu_results results; + +int pmu_claim(void); /* Claim PMU for usage */ +int pmu_start(u32, u32, u32, u32); /* Start PMU execution */ +int pmu_stop(struct pmu_results *); /* Stop perfmon unit */ +int pmu_release(int); /* Release PMU */ + +#endif /* _PMU_H_ */ + + diff -uNr a/include/linux/ac97_codec.h b/include/linux/ac97_codec.h --- a/include/linux/ac97_codec.h 2004-10-19 05:53:09.000000000 +0800 +++ b/include/linux/ac97_codec.h 2005-01-27 09:56:49.000000000 +0800 @@ -290,6 +290,7 @@ #define AC97_DELUDED_MODEM 1 /* Audio codec reports its a modem */ #define AC97_NO_PCM_VOLUME 2 /* Volume control is missing */ +#define AC97_DEFAULT_POWER_OFF 4 /* Needs warm reset to power up */ }; extern int ac97_read_proc (char *page_out, char **start, off_t off, diff -uNr a/include/linux/i2c-id.h b/include/linux/i2c-id.h --- a/include/linux/i2c-id.h 2004-10-19 05:53:10.000000000 +0800 +++ b/include/linux/i2c-id.h 2005-01-27 09:56:49.000000000 +0800 @@ -196,6 +196,7 @@ #define I2C_ALGO_OCP_IOP3XX 0x140000 /* XSCALE IOP3XX On-chip I2C alg */ #define I2C_ALGO_PCA 0x150000 /* PCA 9564 style adapters */ +#define I2C_ALGO_PXA 0x200000 /* Intel PXA I2C algorithm */ #define I2C_ALGO_EXP 0x800000 /* experimental */ #define I2C_ALGO_MASK 0xff0000 /* Mask for algorithms */ diff -uNr a/include/linux/miscdevice.h b/include/linux/miscdevice.h --- a/include/linux/miscdevice.h 2004-10-19 05:54:32.000000000 +0800 +++ b/include/linux/miscdevice.h 2005-01-27 09:56:49.000000000 +0800 @@ -11,6 +11,7 @@ #define SUN_MOUSE_MINOR 6 #define APOLLO_MOUSE_MINOR 7 #define PC110PAD_MINOR 9 +#define IPMC_MINOR 90 /*#define ADB_MOUSE_MINOR 10 FIXME OBSOLETE */ #define WATCHDOG_MINOR 130 /* Watchdog timer */ #define TEMP_MINOR 131 /* Temperature Sensor */ diff -uNr a/include/linux/pxa_camera.h b/include/linux/pxa_camera.h --- a/include/linux/pxa_camera.h 1970-01-01 08:00:00.000000000 +0800 +++ b/include/linux/pxa_camera.h 2005-01-27 09:56:49.000000000 +0800 @@ -0,0 +1,73 @@ +/* + pxa_camera - PXA camera driver header file + + Copyright (C) 2003, Intel Corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __LINUX_PXA_CAMERA_H_ +#define __LINUX_PXA_CAMERA_H_ + +#define WCAM_VIDIOCSCAMREG _IOW('v', 211, int) +#define WCAM_VIDIOCGCAMREG _IOR('v', 212, int) +#define WCAM_VIDIOCSCIREG _IOW('v', 213, int) +#define WCAM_VIDIOCGCIREG _IOR('v', 214, int) +#define WCAM_VIDIOCSINFOR _IOW('v', 215, int) +#define WCAM_VIDIOCGINFOR _IOR('v', 216, int) + +/* +Image format definition +*/ +#define CAMERA_IMAGE_FORMAT_RAW8 0 +#define CAMERA_IMAGE_FORMAT_RAW9 1 +#define CAMERA_IMAGE_FORMAT_RAW10 2 + +#define CAMERA_IMAGE_FORMAT_RGB444 3 +#define CAMERA_IMAGE_FORMAT_RGB555 4 +#define CAMERA_IMAGE_FORMAT_RGB565 5 +#define CAMERA_IMAGE_FORMAT_RGB666_PACKED 6 +#define CAMERA_IMAGE_FORMAT_RGB666_PLANAR 7 +#define CAMERA_IMAGE_FORMAT_RGB888_PACKED 8 +#define CAMERA_IMAGE_FORMAT_RGB888_PLANAR 9 +#define CAMERA_IMAGE_FORMAT_RGBT555_0 10 //RGB+Transparent bit 0 +#define CAMERA_IMAGE_FORMAT_RGBT888_0 11 +#define CAMERA_IMAGE_FORMAT_RGBT555_1 12 //RGB+Transparent bit 1 +#define CAMERA_IMAGE_FORMAT_RGBT888_1 13 + +#define CAMERA_IMAGE_FORMAT_YCBCR400 14 +#define CAMERA_IMAGE_FORMAT_YCBCR422_PACKED 15 +#define CAMERA_IMAGE_FORMAT_YCBCR422_PLANAR 16 +#define CAMERA_IMAGE_FORMAT_YCBCR444_PACKED 17 +#define CAMERA_IMAGE_FORMAT_YCBCR444_PLANAR 18 + +/* +Bpp definition +*/ + +#define YUV422_BPP 16 +#define RGB565_BPP 16 +#define RGB666_UNPACKED_BPP 32 +#define RGB666_PACKED_BPP 24 + +/* +VIDIOCCAPTURE Arguments +*/ +#define STILL_IMAGE 1 +#define VIDEO_START 0 +#define VIDEO_STOP -1 + + +#endif /* __LINUX_PXA_CAMERA_H_ */ diff -uNr a/kernel/power/power.h b/kernel/power/power.h --- a/kernel/power/power.h 2004-10-19 05:55:43.000000000 +0800 +++ b/kernel/power/power.h 2005-01-27 09:56:49.000000000 +0800 @@ -6,7 +6,7 @@ so bad things might happen. */ #if defined(CONFIG_VT) && defined(CONFIG_VT_CONSOLE) -#define SUSPEND_CONSOLE (MAX_NR_CONSOLES-1) +//#define SUSPEND_CONSOLE (MAX_NR_CONSOLES-1) #endif diff -uNr a/Makefile b/Makefile --- a/Makefile 2004-10-19 05:54:38.000000000 +0800 +++ b/Makefile 2005-01-27 09:56:48.000000000 +0800 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 9 -EXTRAVERSION = +EXTRAVERSION = -intc1 NAME=Zonked Quokka # *DOCUMENTATION* diff -uNr a/sound/oss/ac97_codec.c b/sound/oss/ac97_codec.c --- a/sound/oss/ac97_codec.c 2004-10-19 05:53:21.000000000 +0800 +++ b/sound/oss/ac97_codec.c 2005-01-27 09:56:49.000000000 +0800 @@ -30,6 +30,9 @@ ************************************************************************** * * History + * Feb 25, 2004 Liam Girdwood + * Added support for codecs that require a warm reset to power up. + * Support for WM9713 * May 02, 2003 Liam Girdwood * Removed non existant WM9700 * Added support for WM9705, WM9708, WM9709, WM9710, WM9711 @@ -70,6 +73,7 @@ static int wolfson_init04(struct ac97_codec * codec); static int wolfson_init05(struct ac97_codec * codec); static int wolfson_init11(struct ac97_codec * codec); +static int wolfson_init13(struct ac97_codec * codec); static int tritech_init(struct ac97_codec * codec); static int tritech_maestro_init(struct ac97_codec * codec); static int sigmatel_9708_init(struct ac97_codec *codec); @@ -106,6 +110,7 @@ static struct ac97_ops wolfson_ops04 = { wolfson_init04, NULL, NULL }; static struct ac97_ops wolfson_ops05 = { wolfson_init05, NULL, NULL }; static struct ac97_ops wolfson_ops11 = { wolfson_init11, NULL, NULL }; +static struct ac97_ops wolfson_ops13 = { wolfson_init13, NULL, NULL }; static struct ac97_ops tritech_ops = { tritech_init, NULL, NULL }; static struct ac97_ops tritech_m_ops = { tritech_maestro_init, NULL, NULL }; static struct ac97_ops sigmatel_9708_ops = { sigmatel_9708_init, NULL, NULL }; @@ -149,6 +154,7 @@ {0x43525931, "Cirrus Logic CS4299 rev A", &crystal_digital_ops}, {0x43525933, "Cirrus Logic CS4299 rev C", &crystal_digital_ops}, {0x43525934, "Cirrus Logic CS4299 rev D", &crystal_digital_ops}, + {0x4352594d, "Cirrus Logic CS4201", &null_ops}, {0x43585442, "CXT66", &default_ops, AC97_DELUDED_MODEM }, {0x44543031, "Diamond Technology DT0893", &default_ops}, {0x45838308, "ESS Allegro ES1988", &null_ops}, @@ -156,6 +162,7 @@ {0x4e534331, "National Semiconductor LM4549", &null_ops}, {0x53494c22, "Silicon Laboratory Si3036", &null_ops}, {0x53494c23, "Silicon Laboratory Si3038", &null_ops}, + {0x50534304, "Philips UCB1400", &default_ops}, {0x545200FF, "TriTech TR?????", &tritech_m_ops}, {0x54524102, "TriTech TR28022", &null_ops}, {0x54524103, "TriTech TR28023", &null_ops}, @@ -167,6 +174,7 @@ {0x574D4C05, "Wolfson WM9705/WM9710", &wolfson_ops05}, {0x574D4C09, "Wolfson WM9709", &null_ops}, {0x574D4C12, "Wolfson WM9711/9712", &wolfson_ops11}, + {0x574D4C13, "Wolfson WM9713", &wolfson_ops13, AC97_DEFAULT_POWER_OFF}, {0x83847600, "SigmaTel STAC????", &null_ops}, {0x83847604, "SigmaTel STAC9701/3/4/5", &null_ops}, {0x83847605, "SigmaTel STAC9704", &null_ops}, @@ -793,6 +801,9 @@ * Currently codec_wait is used to wait for AC97 codec * reset to complete. * + * Some codecs will power down when a register reset is + * performed. We now check for such codecs. + * * Returns 1 (true) on success, or 0 (false) on failure. */ @@ -806,34 +817,17 @@ struct list_head *l; struct ac97_driver *d; - /* probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should - * be read zero. - * - * FIXME: is the following comment outdated? -jgarzik - * Probing of AC97 in this way is not reliable, it is not even SAFE !! - */ - codec->codec_write(codec, AC97_RESET, 0L); - - /* also according to spec, we wait for codec-ready state */ + /* wait for codec-ready state */ if (codec->codec_wait) codec->codec_wait(codec); else udelay(10); - - if ((audio = codec->codec_read(codec, AC97_RESET)) & 0x8000) { - printk(KERN_ERR "ac97_codec: %s ac97 codec not present\n", - (codec->id & 0x2) ? (codec->id&1 ? "4th" : "Tertiary") - : (codec->id&1 ? "Secondary": "Primary")); - return 0; - } - - /* probe for Modem Codec */ - codec->modem = ac97_check_modem(codec); - codec->name = NULL; - codec->codec_ops = &default_ops; - + + /* will the codec power down if register reset ? */ id1 = codec->codec_read(codec, AC97_VENDOR_ID1); id2 = codec->codec_read(codec, AC97_VENDOR_ID2); + codec->name = NULL; + codec->codec_ops = &null_ops; for (i = 0; i < ARRAY_SIZE(ac97_codec_ids); i++) { if (ac97_codec_ids[i].id == ((id1 << 16) | id2)) { codec->type = ac97_codec_ids[i].id; @@ -845,9 +839,34 @@ } codec->model = (id1 << 16) | id2; + if ((codec->flags & AC97_DEFAULT_POWER_OFF) == 0) { + /* reset codec and wait for the ready bit before we continue */ + codec->codec_write(codec, AC97_RESET, 0L); + if (codec->codec_wait) + codec->codec_wait(codec); + else + udelay(10); + } + + /* probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should + * be read zero. + * + * FIXME: is the following comment outdated? -jgarzik + * Probing of AC97 in this way is not reliable, it is not even SAFE !! + */ + if ((audio = codec->codec_read(codec, AC97_RESET)) & 0x8000) { + printk(KERN_ERR "ac97_codec: %s ac97 codec not present\n", + (codec->id & 0x2) ? (codec->id&1 ? "4th" : "Tertiary") + : (codec->id&1 ? "Secondary": "Primary")); + return 0; + } + /* probe for Modem Codec */ + codec->modem = ac97_check_modem(codec); + + /* enable SPDIF */ f = codec->codec_read(codec, AC97_EXTENDED_STATUS); - if(f & 4) + if((codec->codec_ops == &null_ops) && (f & 4)) codec->codec_ops = &default_digital_ops; /* A device which thinks its a modem but isnt */ @@ -916,11 +935,6 @@ codec->recmask_io = ac97_recmask_io; codec->mixer_ioctl = ac97_mixer_ioctl; - /* codec specific initialization for 4-6 channel output or secondary codec stuff */ - if (codec->codec_ops->init != NULL) { - codec->codec_ops->init(codec); - } - /* initialize mixer channel volumes */ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { struct mixer_defaults *md = &mixer_defaults[i]; @@ -930,6 +944,11 @@ continue; ac97_set_mixer(codec, md->mixer, md->value); } + + /* codec specific initialization for 4-6 channel output or secondary codec stuff */ + if (codec->codec_ops->init != NULL) { + codec->codec_ops->init(codec); + } /* * Volume is MUTE only on this device. We have to initialise @@ -1086,6 +1105,19 @@ return 0; } +/* WM9713 */ +static int wolfson_init13(struct ac97_codec * codec) +{ + codec->codec_write(codec, AC97_RECORD_GAIN, 0x00a0); + codec->codec_write(codec, AC97_POWER_CONTROL, 0x0000); + codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0xDA00); + codec->codec_write(codec, AC97_EXTEND_MODEM_STAT, 0x3810); + codec->codec_write(codec, AC97_PHONE_VOL, 0x0808); + codec->codec_write(codec, AC97_PCBEEP_VOL, 0x0808); + + return 0; +} + static int tritech_init(struct ac97_codec * codec) { codec->codec_write(codec, 0x26, 0x0300); diff -uNr a/sound/oss/Kconfig b/sound/oss/Kconfig --- a/sound/oss/Kconfig 2004-10-19 05:54:55.000000000 +0800 +++ b/sound/oss/Kconfig 2005-01-27 09:56:49.000000000 +0800 @@ -1096,3 +1096,21 @@ int " DAC channel" default "1" depends on SOUND_SH_DAC_AUDIO + +config SOUND_PXA + tristate "PXA Audio" + depends on SOUND_PRIME!=n && ARCH_PXA + +config SOUND_PXA_AC97 + tristate "PXA AC97 audio support" + depends on SOUND_PRIME!=n && SOUND_PXA + help + Say Y hear or M if you want to include support for AC97 audio. + +config SOUND_UCB1X00 + tristate "UCB1X00 core support" + depends on SOUND_PRIME!=n && SOUND_PXA + +config SOUND_UCB1X00_TS + tristate "UCB1X00 touchscreen plugin" + depends on SOUND_UCB1X00 diff -uNr a/sound/oss/Makefile b/sound/oss/Makefile --- a/sound/oss/Makefile 2004-10-19 05:53:45.000000000 +0800 +++ b/sound/oss/Makefile 2005-01-27 09:56:49.000000000 +0800 @@ -80,6 +80,11 @@ obj-$(CONFIG_SOUND_AD1980) += ac97_plugin_ad1980.o obj-$(CONFIG_SOUND_WM97XX) += ac97_plugin_wm97xx.o +obj-$(CONFIG_SOUND_PXA) += pxa-audio.o +obj-$(CONFIG_SOUND_PXA_AC97) += pxa-ac97.o ac97_codec.o +obj-$(CONFIG_SOUND_UCB1X00) += ucb1x00-core.o +obj-$(CONFIG_SOUND_UCB1X00_TS) += ucb1x00-ts.o + ifeq ($(CONFIG_MIDI_EMU10K1),y) obj-$(CONFIG_SOUND_EMU10K1) += sound.o endif diff -uNr a/sound/oss/pxa-ac97.c b/sound/oss/pxa-ac97.c --- a/sound/oss/pxa-ac97.c 1970-01-01 08:00:00.000000000 +0800 +++ b/sound/oss/pxa-ac97.c 2005-01-27 09:56:49.000000000 +0800 @@ -0,0 +1,675 @@ +/* + * linux/drivers/sound/pxa-ac97.c -- AC97 interface for the Cotula chip + * + * Author: Nicolas Pitre + * Created: Aug 15, 2001 + * Copyright: MontaVista Software Inc. + * + * Forward ported to 2.6 by Ian Molton 15/09/2003 + * + * 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. + * + * Sep 1st 2004: Liam Girdwood + * Changed codec initialisation sequence to perform a cold reset + * followed by a warm reset. This allows a greater range of + * codecs to be used on the XScale. + * Added codec init failure error detection, fast codec GPIO + * for PXA25x and PXA27x, new PXA27x AC97 read/write and + * shared the AC97 interrupt for use with codec specifig GPIO's. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "pxa-audio.h" + +/* We need a irq tag to request/free our shared irq handler */ +#define AC97_IRQ_TAG 0xac97c0de +//#define DEBUG 1 + +static DECLARE_MUTEX(CAR_mutex); + +/* + * PXA27x has a different AC97 IO flow than PXA25x. + */ +#ifdef CONFIG_PXA27x + +static int pxa27x_read_wait(void) +{ + /* XLLP: timeout in reading and writing codec registers through AC link is 200us */ + int timeout = 20; + + while ( (!(GSR & GSR_SDONE)) && (timeout--) ) { + udelay(10); + } + + GSR = GSR_SDONE | GSR_CDONE; + + if (timeout < 0) { + printk(KERN_CRIT "%s: read codec register timeout.\n", __FUNCTION__); + return 0; + } +#ifdef DEBUG + else{ + printk("\n<%s>: finish in %d us. \n",__FUNCTION__, (20-timeout)*10); + } +#endif + + return 1; +} + +static int pxa27x_write_wait(void) +{ + /* XLLP: timeout in reading and writing codec registers through AC link is 200us */ + int timeout = 20; + + while ( (!(GSR & GSR_CDONE)) && (timeout--) ) { + udelay(10); + } + + GSR = GSR_CDONE; + + if (timeout < 0) { + printk(KERN_CRIT "%s: write codec register timeout.\n", __FUNCTION__); + return 0; + } +#ifdef DEBUG + else{ + printk("\n<%s>: finish in %d us. \n",__FUNCTION__, (20-timeout)*10); + } +#endif + + return 1; +} + +static u16 pxa_ac97_read(struct ac97_codec *codec, u8 reg) +{ + u16 val =-1; + volatile u32 *reg_addr; + + down(&CAR_mutex); + + /* set up primary or secondary space */ + if (codec->id & 0x1) + reg_addr = (u32 *)&SAC_REG_BASE + (reg >> 1); + else + reg_addr = (u32 *)&PAC_REG_BASE + (reg >> 1); + + /* No fast codec GPIO reads on PXA27x */ + (void)*reg_addr; // dummy read + if (pxa27x_read_wait()) { + val = *reg_addr; + if (!pxa27x_read_wait()) + val = -1; + } + + up(&CAR_mutex); + return val; +} + +static void pxa_ac97_write(struct ac97_codec *codec, u8 reg, u16 val) +{ + volatile u32 *reg_addr; + down(&CAR_mutex); + + /* Status reg (0x54) writes are fast, others need to wait */ + if (reg == AC97_GPIO_STATUS) { + + /* set up primary or secondary modem space */ + if (codec->id & 0x1) + reg_addr = (u32 *)&SMC_REG_BASE + (reg >> 1); + else + reg_addr = (u32 *)&PMC_REG_BASE + (reg >> 1); + + *reg_addr = val; + udelay(50); + } else { + /* set up primary or secondary codec space */ + if (codec->id & 0x1) + reg_addr = (u32 *)&SAC_REG_BASE + (reg >> 1); + else + reg_addr = (u32 *)&PAC_REG_BASE + (reg >> 1); + + *reg_addr = val; + pxa27x_write_wait(); + } + up(&CAR_mutex); +} + +#else + +static struct completion CAR_completion; +static int waitingForMask; + +static u16 pxa_ac97_read(struct ac97_codec *codec, u8 reg) +{ + u16 val = -1; + + down(&CAR_mutex); + if (!(CAR & CAR_CAIP)) { + volatile u32 *reg_addr; + + /* Status reg (0x54) reads are fast, others need to wait */ + if (reg == AC97_GPIO_STATUS) { + /* set up primary or secondary modem space */ + if (codec->id & 0x1) + reg_addr = (u32 *)&SMC_REG_BASE + (reg >> 1); + else + reg_addr = (u32 *)&PMC_REG_BASE + (reg >> 1); + + val = *reg_addr; + up(&CAR_mutex); + return val; + } + + /* set up primary or secondary codec space */ + if (codec->id & 0x1) + reg_addr = (u32 *)&SAC_REG_BASE + (reg >> 1); + else + reg_addr = (u32 *)&PAC_REG_BASE + (reg >> 1); + + waitingForMask=GSR_SDONE; + init_completion(&CAR_completion); + (void)*reg_addr; //start read access across the ac97 link + wait_for_completion(&CAR_completion); + + if (GSR & GSR_RDCS) { + GSR = GSR_RDCS; //write a 1 to clear + printk(KERN_CRIT "%s: read codec register timeout.\n", __FUNCTION__); + } + + init_completion(&CAR_completion); + val = *reg_addr; //valid data now but we've just started another cycle... + wait_for_completion(&CAR_completion); + + } else { + printk(KERN_CRIT"%s: CAR_CAIP already set\n", __FUNCTION__); + } + up(&CAR_mutex); + + return val; +} + +static void pxa_ac97_write(struct ac97_codec *codec, u8 reg, u16 val) +{ + down(&CAR_mutex); + if (!(CAR & CAR_CAIP)) { + volatile u32 *reg_addr; + + /* Status reg (0x54) writes are fast, others need to wait */ + if (reg == AC97_GPIO_STATUS) { + /* set up primary or secondary modem space */ + if (codec->id & 0x1) + reg_addr = (u32 *)&SMC_REG_BASE + (reg >> 1); + else + reg_addr = (u32 *)&PMC_REG_BASE + (reg >> 1); + + *reg_addr = val; + up(&CAR_mutex); + return; + } + + /* set up primary or secondary codec space */ + if (codec->id & 0x1) + reg_addr = (u32 *)&SAC_REG_BASE + (reg >> 1); + else + reg_addr = (u32 *)&PAC_REG_BASE + (reg >> 1); + + waitingForMask=GSR_CDONE; + init_completion(&CAR_completion); + *reg_addr = val; + wait_for_completion(&CAR_completion); + } else { + printk(KERN_CRIT "%s: CAR_CAIP already set\n", __FUNCTION__); + } + up(&CAR_mutex); +} + +static irqreturn_t pxa_ac97_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + int gsr = GSR; + + /* we only handle CDONE and SDONE */ + if (gsr & (GSR_SDONE|GSR_CDONE)) { + GSR = gsr & (GSR_SDONE|GSR_CDONE); //write a 1 to clear + if (gsr & waitingForMask) + complete(&CAR_completion); + return IRQ_HANDLED; + } else + return IRQ_NONE; +} +#endif + +static struct ac97_codec pxa_ac97_codec = { + codec_read: pxa_ac97_read, + codec_write: pxa_ac97_write, +}; + +static int pxa_ac97_cold_reset(void) +{ + int timeout = 50; + + GCR = 0; + udelay(100); /* XLLP: holdtime for keeping nReset signal active(low) in AC link is 100us */ +#ifdef CONFIG_PXA27x + GCR = GCR_COLD_RST; +#else + GCR = GCR_COLD_RST|GCR_CDONE_IE|GCR_SDONE_IE; +#endif + + /* XLLP: timeout in waiting for codec's ready signal during setup process is 500us */ + while (!(GSR & GSR_PCR) && timeout--) { + udelay(10); + } + if (timeout < 0) { + printk(KERN_CRIT "%s: cold reset codec timeout.\n", __FUNCTION__); + return 0; + } +#ifdef DEBUG + else{ + printk("\n<%s>: finish in %d us. \n",__FUNCTION__, (50-timeout)*10); + } +#endif + + return 1; +} + +static int pxa_ac97_warm_reset(void) +{ + int timeout = 50; + +#ifdef CONFIG_PXA27x + GCR |= GCR_WARM_RST; +#else + GCR |= GCR_WARM_RST|GCR_CDONE_IE|GCR_SDONE_IE; +#endif + + /* XLLP: timeout in waiting for codec's ready signal during setup process is 500us */ + while (!(GSR & GSR_PCR) && timeout--) { + udelay(10); + } + if (timeout < 0) { + printk(KERN_CRIT "%s: warm reset codec timeout.\n", __FUNCTION__); + return 0; + } +#ifdef DEBUG + else{ + printk("\n<%s>: finish in %d us. \n",__FUNCTION__, (50-timeout)*10); + } +#endif + + return 1; +} + +static DECLARE_MUTEX(pxa_ac97_mutex); +static int pxa_ac97_refcount; + +int pxa_ac97_get(struct ac97_codec **codec) +{ + int ret; + + *codec = NULL; + down(&pxa_ac97_mutex); + + if (!pxa_ac97_refcount) { +#ifndef CONFIG_PXA27x + ret = request_irq(IRQ_AC97, pxa_ac97_irq, SA_SHIRQ, "AC97", AC97_IRQ_TAG); + if (ret) + return ret; +#endif + pxa_set_cken(CKEN2_AC97, 1); + + /* + * On Mainstone, we enable the on board audio amp and + * route AC97_SYSCLK via GPIO45 to the audio daughter card + */ +#ifdef CONFIG_MACH_MAINSTONE + MST_MSCWR2 &= 0xffff000b; + pxa_gpio_mode(GPIO45_SYSCLK_AC97_MD); +#endif + + pxa_gpio_mode(GPIO31_SYNC_AC97_MD); + pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); + pxa_gpio_mode(GPIO28_BITCLK_AC97_MD); + pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); + + /* + * Use GPIO 113 as AC97 Reset on Bulverde + */ +#ifdef CONFIG_PXA27x + pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); +#endif + + /* + * We try a cold reset followed by a warm reset. If the codec doesn't + * respond after this then something is wrong. + */ + if (pxa_ac97_cold_reset() == 0) + if (pxa_ac97_warm_reset() == 0) + goto ac97_fail; + + ret = ac97_probe_codec(&pxa_ac97_codec); + if (ret != 1) + goto ac97_fail; + } + + pxa_ac97_refcount++; + up(&pxa_ac97_mutex); + *codec = &pxa_ac97_codec; + return 0; + +ac97_fail: +#ifdef CONFIG_MACH_MAINSTONE + MST_MSCWR2 &= ~0xc0; + pxa_gpio_mode(GPIO45_BTRTS_MD); +#endif + free_irq(IRQ_AC97, (void*)AC97_IRQ_TAG); + GCR = GCR_ACLINK_OFF; + pxa_set_cken(CKEN2_AC97, 0); + printk(KERN_ERR "could not initialise AC97 codec."); + return -ENODEV; +} + +void pxa_ac97_put(void) +{ + down(&pxa_ac97_mutex); + pxa_ac97_refcount--; + if (!pxa_ac97_refcount) { +#ifdef CONFIG_MACH_MAINSTONE + MST_MSCWR2 &= ~0xc0; + pxa_gpio_mode(GPIO45_BTRTS_MD); +#endif + GCR = GCR_ACLINK_OFF; + pxa_set_cken(CKEN2_AC97, 0); +#ifndef CONFIG_PXA27x + free_irq(IRQ_AC97, AC97_IRQ_TAG); +#endif + } + up(&pxa_ac97_mutex); +} + +EXPORT_SYMBOL(pxa_ac97_get); +EXPORT_SYMBOL(pxa_ac97_put); + + +/* + * Audio Mixer stuff + */ + +static audio_state_t ac97_audio_state; +static audio_stream_t ac97_audio_in; + +static int mixer_ioctl( struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret; + + ret = pxa_ac97_codec.mixer_ioctl(&pxa_ac97_codec, cmd, arg); + if (ret) + return ret; + + /* We must snoop for some commands to provide our own extra processing */ + switch (cmd) { + case SOUND_MIXER_WRITE_RECSRC: + /* + * According to the PXA250 spec, mic-in should use different + * DRCMR and different AC97 FIFO. + * Unfortunately current UCB1400 versions (up to ver 2A) don't + * produce slot 6 for the audio input frame, therefore the PXA + * AC97 mic-in FIFO is always starved. + */ +#if 0 + ret = get_user(val, (int *)arg); + if (ret) + return ret; + pxa_audio_clear_buf(&ac97_audio_in); + *ac97_audio_in.drcmr = 0; + if (val & (1 << SOUND_MIXER_MIC)) { + ac97_audio_in.dcmd = DCMD_RXMCDR; + ac97_audio_in.drcmr = &DRCMRRXMCDR; + ac97_audio_in.dev_addr = __PREG(MCDR); + } else { + ac97_audio_in.dcmd = DCMD_RXPCDR; + ac97_audio_in.drcmr = &DRCMRRXPCDR; + ac97_audio_in.dev_addr = __PREG(PCDR); + } + if (ac97_audio_state.rd_ref) + *ac97_audio_in.drcmr = + ac97_audio_in.dma_ch | DRCMR_MAPVLD; +#endif + break; + } + return 0; +} + +static struct file_operations mixer_fops = { + ioctl: mixer_ioctl, + llseek: no_llseek, + owner: THIS_MODULE +}; + +/* + * AC97 codec ioctls + */ + +static int codec_adc_rate = 48000; +static int codec_dac_rate = 48000; + +static int ac97_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret; + long val=0; + + switch(cmd) { + case SNDCTL_DSP_STEREO: + ret = get_user(val, (int *) arg); + if (ret) + return ret; + /* FIXME: do we support mono? */ + ret = (val == 0) ? -EINVAL : 1; + return put_user(ret, (int *) arg); + + case SNDCTL_DSP_CHANNELS: + case SOUND_PCM_READ_CHANNELS: + /* FIXME: do we support mono? */ + return put_user(2, (long *) arg); + + case SNDCTL_DSP_SPEED: + ret = get_user(val, (long *) arg); + if (ret) + return ret; + if (file->f_mode & FMODE_READ) + codec_adc_rate = ac97_set_adc_rate(&pxa_ac97_codec, val); + if (file->f_mode & FMODE_WRITE) + codec_dac_rate = ac97_set_dac_rate(&pxa_ac97_codec, val); + /* fall through */ + case SOUND_PCM_READ_RATE: + if (file->f_mode & FMODE_READ) + val = codec_adc_rate; + if (file->f_mode & FMODE_WRITE) + val = codec_dac_rate; + return put_user(val, (long *) arg); + + case SNDCTL_DSP_SETFMT: + case SNDCTL_DSP_GETFMTS: + /* FIXME: can we do other fmts? */ + return put_user(AFMT_S16_LE, (long *) arg); + + default: + /* Maybe this is meant for the mixer (As per OSS Docs) */ + return mixer_ioctl(inode, file, cmd, arg); + } + return 0; +} + + +/* + * Audio stuff + */ + +static audio_stream_t ac97_audio_out = { + name: "AC97 audio out", + dcmd: DCMD_TXPCDR, + drcmr: &DRCMRTXPCDR, + dev_addr: __PREG(PCDR), +}; + +static audio_stream_t ac97_audio_in = { + name: "AC97 audio in", + dcmd: DCMD_RXPCDR, + drcmr: &DRCMRRXPCDR, + dev_addr: __PREG(PCDR), +}; + +static audio_state_t ac97_audio_state = { + output_stream: &ac97_audio_out, + input_stream: &ac97_audio_in, + client_ioctl: ac97_ioctl, + sem: __MUTEX_INITIALIZER(ac97_audio_state.sem), +}; + +static int ac97_audio_open(struct inode *inode, struct file *file) +{ + return pxa_audio_attach(inode, file, &ac97_audio_state); +} + +/* + * Missing fields of this structure will be patched with the call + * to pxa_audio_attach(). + */ + +static struct file_operations ac97_audio_fops = { + open: ac97_audio_open, + owner: THIS_MODULE +}; + + +static int pxa_ac97_probe(struct device *dev) +{ + int ret; + struct ac97_codec *dummy; + + ret = pxa_ac97_get(&dummy); + if (ret) + return ret; + + ac97_audio_state.dev_dsp = register_sound_dsp(&ac97_audio_fops, -1); + pxa_ac97_codec.dev_mixer = register_sound_mixer(&mixer_fops, -1); + + return 0; +} + +static int pxa_ac97_remove(struct device *dev) +{ + unregister_sound_dsp(ac97_audio_state.dev_dsp); + unregister_sound_mixer(pxa_ac97_codec.dev_mixer); + pxa_ac97_put(); + + return 0; +} + +/* + * Suspend & Resume the PXA AC97 Controller. + */ +static int pxa_ac97_suspend(struct device * dev, u32 state, u32 level) +{ + if (level == SUSPEND_POWER_DOWN) { + /* initiate power-down of codec */ + unsigned int pr; + int i = 0; + + printk("AC97 suspend\n"); + pr = pxa_ac97_read(&pxa_ac97_codec, AC97_POWER_CONTROL); + pr |= AC97_PWR_PR4; + pxa_ac97_write(&pxa_ac97_codec, AC97_POWER_CONTROL, pr); + GCR |= GCR_ACLINK_OFF; + pxa_set_cken(CKEN31_AC97, 1); + while (!(GSR & (1 << 3)) && i < 99999) + i++; + pxa_set_cken(CKEN31_AC97, 0); + udelay(20); + pxa_set_cken(CKEN2_AC97, 0); + } + return 0; +} + +static int pxa_ac97_resume(struct device * dev, u32 level) +{ + if (level == RESUME_POWER_ON) { + printk("ac97 resume\n"); + pxa_set_cken(CKEN2_AC97, 1); +#ifdef CONFIG_MACH_MAINSTONE + MST_MSCWR2 &= 0xffff000b; +#endif + + /* cold reset the codec */ + GCR = 0; + pxa_set_cken(CKEN31_AC97, 1); + udelay(120); + pxa_set_cken(CKEN31_AC97, 0); + GCR = GCR_COLD_RST; + udelay(30); + + /* XLLP: timeout in waiting for codec's ready signal during setup process is 500us */ + int timeout=50; + while (!(GSR & GSR_PCR) && timeout--) { + udelay(10); + } + + if (timeout < 0) { + printk(KERN_CRIT "%s: resume codec timeout.\n", __FUNCTION__); + } +#ifdef DEBUG + else{ + printk("\n<%s>: finish in %d us. \n",__FUNCTION__, (50-timeout)*10); + } +#endif + } + return 0; +} + +static struct device_driver pxa_ac97_driver = { + .name = "pxa2xx-ac97", + .probe = pxa_ac97_probe, + .remove = pxa_ac97_remove, + .bus = &platform_bus_type, + .suspend = pxa_ac97_suspend, + .resume = pxa_ac97_resume, +}; + +static int __devinit pxa_ac97_init(void) +{ + return driver_register(&pxa_ac97_driver); +} + +static void __exit pxa_ac97_exit(void) +{ + return driver_unregister(&pxa_ac97_driver); +} + +module_init(pxa_ac97_init); +module_exit(pxa_ac97_exit); +MODULE_LICENSE("GPL"); + + diff -uNr a/sound/oss/pxa-audio.c b/sound/oss/pxa-audio.c --- a/sound/oss/pxa-audio.c 1970-01-01 08:00:00.000000000 +0800 +++ b/sound/oss/pxa-audio.c 2005-01-27 09:56:49.000000000 +0800 @@ -0,0 +1,856 @@ +/* + * linux/drivers/sound/pxa-audio.c -- audio interface for the Cotula chip + * + * Author: Nicolas Pitre + * Created: Aug 15, 2001 + * Copyright: MontaVista Software 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "pxa-audio.h" + + +#define AUDIO_NBFRAGS_DEFAULT 8 +#define AUDIO_FRAGSIZE_DEFAULT 8192 + +#define MAX_DMA_SIZE 4096 +#define DMA_DESC_SIZE sizeof(pxa_dma_desc) + + +/* + * This function frees all buffers + */ +#define audio_clear_buf pxa_audio_clear_buf + +void pxa_audio_clear_buf(audio_stream_t * s) +{ + DECLARE_WAITQUEUE(wait, current); + int frag; + + if (!s->buffers) + return; + + /* Ensure DMA isn't running */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&s->stop_wq, &wait); + DCSR(s->dma_ch) = DCSR_STOPIRQEN; + schedule(); + remove_wait_queue(&s->stop_wq, &wait); + + /* free DMA buffers */ + for (frag = 0; frag < s->nbfrags; frag++) { + audio_buf_t *b = &s->buffers[frag]; + if (!b->master) + continue; + dma_free_writecombine(NULL, b->master, b->data, b->dma_desc->dsadr); + } + + /* free descriptor ring */ + if (s->buffers->dma_desc) + dma_free_writecombine(NULL, s->nbfrags * s->descs_per_frag * DMA_DESC_SIZE, + s->buffers->dma_desc, s->dma_desc_phys); + + /* free buffer structure array */ + kfree(s->buffers); + s->buffers = NULL; +} + +/* + * This function allocates the DMA descriptor array and buffer data space + * according to the current number of fragments and fragment size. + */ +static int audio_setup_buf(audio_stream_t * s) +{ + pxa_dma_desc *dma_desc; + dma_addr_t dma_desc_phys; + int nb_desc, frag, i, buf_size = 0; + char *dma_buf = NULL; + dma_addr_t dma_buf_phys = 0; + + if (s->buffers) + return -EBUSY; + + /* Our buffer structure array */ + s->buffers = kmalloc(sizeof(audio_buf_t) * s->nbfrags, GFP_KERNEL); + if (!s->buffers) + goto err; + memzero(s->buffers, sizeof(audio_buf_t) * s->nbfrags); + + /* + * Our DMA descriptor array: + * for Each fragment we have one checkpoint descriptor plus one + * descriptor per MAX_DMA_SIZE byte data blocks. + */ + nb_desc = (1 + (s->fragsize + MAX_DMA_SIZE - 1)/MAX_DMA_SIZE) * s->nbfrags; + dma_desc = dma_alloc_writecombine(NULL, nb_desc * DMA_DESC_SIZE, + &dma_desc_phys, GFP_KERNEL); + + if (!dma_desc) + goto err; + s->descs_per_frag = nb_desc / s->nbfrags; + s->buffers->dma_desc = dma_desc; + s->dma_desc_phys = dma_desc_phys; + for (i = 0; i < nb_desc - 1; i++) + dma_desc[i].ddadr = dma_desc_phys + (i + 1) * DMA_DESC_SIZE; + dma_desc[i].ddadr = dma_desc_phys; + + /* Our actual DMA buffers */ + for (frag = 0; frag < s->nbfrags; frag++) { + audio_buf_t *b = &s->buffers[frag]; + + /* + * Let's allocate non-cached memory for DMA buffers. + * We try to allocate all memory at once. + * If this fails (a common reason is memory fragmentation), + * then we'll try allocating smaller buffers. + */ + if (!buf_size) { + buf_size = (s->nbfrags - frag) * s->fragsize; + do { + dma_buf = dma_alloc_writecombine(NULL, buf_size, + &dma_buf_phys, + GFP_KERNEL); + if (!dma_buf) + buf_size -= s->fragsize; + } while (!dma_buf && buf_size); + if (!dma_buf) + goto err; + b->master = buf_size; + memzero(dma_buf, buf_size); + } + + /* + * Set up our checkpoint descriptor. Since the count + * is always zero, we'll abuse the dsadr and dtadr fields + * just in case this one is picked up by the hardware + * while processing SOUND_DSP_GETPTR. + */ + dma_desc->dsadr = dma_buf_phys; + dma_desc->dtadr = dma_buf_phys; + dma_desc->dcmd = DCMD_ENDIRQEN; + if (s->output && !s->mapped) + dma_desc->ddadr |= DDADR_STOP; + b->dma_desc = dma_desc++; + + /* set up the actual data descriptors */ + for (i = 0; (i * MAX_DMA_SIZE) < s->fragsize; i++) { + dma_desc[i].dsadr = (s->output) ? + (dma_buf_phys + i*MAX_DMA_SIZE) : s->dev_addr; + dma_desc[i].dtadr = (s->output) ? + s->dev_addr : (dma_buf_phys + i*MAX_DMA_SIZE); + dma_desc[i].dcmd = s->dcmd | + ((s->fragsize < MAX_DMA_SIZE) ? + s->fragsize : MAX_DMA_SIZE); + } + dma_desc += i; + + /* handle buffer pointers */ + b->data = dma_buf; + dma_buf += s->fragsize; + dma_buf_phys += s->fragsize; + buf_size -= s->fragsize; + } + + s->usr_frag = s->dma_frag = 0; + s->bytecount = 0; + s->fragcount = 0; + sema_init(&s->sem, (s->output) ? s->nbfrags : 0); + return 0; + +err: + printk("pxa-audio: unable to allocate audio memory\n "); + audio_clear_buf(s); + return -ENOMEM; +} + +/* + * Our DMA interrupt handler + */ +static void audio_dma_irq(int ch, void *dev_id, struct pt_regs *regs) +{ + audio_stream_t *s = dev_id; + u_int dcsr; + + dcsr = DCSR(ch); + DCSR(ch) = dcsr & ~DCSR_STOPIRQEN; + + if (!s->buffers) { + printk("Audio DMA: wow... received IRQ for channel %d but no buffer exists\n", ch); + return; + } + + if (dcsr & DCSR_BUSERR) + printk("Audio DMA: bus error interrupt on channel %d\n", ch); + + if (dcsr & DCSR_ENDINTR) { + u_long cur_dma_desc; + u_int cur_dma_frag; + + /* + * Find out which DMA desc is current. Note that DDADR + * points to the next desc, not the current one. + */ + cur_dma_desc = DDADR(ch) - s->dma_desc_phys - DMA_DESC_SIZE; + + /* + * Let the compiler nicely optimize constant divisors into + * multiplications for the common cases which is much faster. + * Common cases: x = 1 + (1 << y) for y = [0..3] + */ + switch (s->descs_per_frag) { + case 2: cur_dma_frag = cur_dma_desc / (2*DMA_DESC_SIZE); break; + case 3: cur_dma_frag = cur_dma_desc / (3*DMA_DESC_SIZE); break; + case 5: cur_dma_frag = cur_dma_desc / (5*DMA_DESC_SIZE); break; + case 9: cur_dma_frag = cur_dma_desc / (9*DMA_DESC_SIZE); break; + default: cur_dma_frag = + cur_dma_desc / (s->descs_per_frag * DMA_DESC_SIZE); + } + + /* Account for possible wrap back of cur_dma_desc above */ + if (cur_dma_frag >= s->nbfrags) + cur_dma_frag = s->nbfrags - 1; + + while (s->dma_frag != cur_dma_frag) { + if (!s->mapped) { + /* + * This fragment is done - set the checkpoint + * descriptor to STOP until it is gets + * processed by the read or write function. + */ + s->buffers[s->dma_frag].dma_desc->ddadr |= DDADR_STOP; + up(&s->sem); + } + if (++s->dma_frag >= s->nbfrags) + s->dma_frag = 0; + + /* Accounting */ + s->bytecount += s->fragsize; + s->fragcount++; + } + + /* ... and for polling processes */ + wake_up(&s->frag_wq); + } + + if ((dcsr & DCSR_STOPIRQEN) && (dcsr & DCSR_STOPSTATE)) + wake_up(&s->stop_wq); +} + +/* + * Validate and sets up buffer fragments, etc. + */ +static int audio_set_fragments(audio_stream_t *s, int val) +{ + if (s->mapped || DCSR(s->dma_ch) & DCSR_RUN) + return -EBUSY; + if (s->buffers) + audio_clear_buf(s); + s->nbfrags = (val >> 16) & 0x7FFF; + val &= 0xffff; + if (val < 5) + val = 5; + if (val > 15) + val = 15; + s->fragsize = 1 << val; + if (s->nbfrags < 2) + s->nbfrags = 2; + if (s->nbfrags * s->fragsize > 256 * 1024) + s->nbfrags = 256 * 1024 / s->fragsize; + if (audio_setup_buf(s)) + return -ENOMEM; + return val|(s->nbfrags << 16); +} + + +/* + * The fops functions + */ + +static int audio_write(struct file *file, const char *buffer, + size_t count, loff_t * ppos) +{ + const char *buffer0 = buffer; + audio_state_t *state = (audio_state_t *)file->private_data; + audio_stream_t *s = state->output_stream; + int chunksize, ret = 0; + if (*ppos != file->f_pos) + return -ESPIPE; + if (s->mapped) + return -ENXIO; + if (!s->buffers && audio_setup_buf(s)) + return -ENOMEM; + while (count > 0) { + audio_buf_t *b = &s->buffers[s->usr_frag]; + + /* Grab a fragment */ + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + if (down_trylock(&s->sem)) + break; + } else { + ret = -ERESTARTSYS; + if (down_interruptible(&s->sem)) + break; + } + + /* Feed the current buffer */ + chunksize = s->fragsize - b->offset; + if (chunksize > count) + chunksize = count; + if (copy_from_user(b->data + b->offset, buffer, chunksize)) { + up(&s->sem); + return -EFAULT; + } + b->offset += chunksize; + buffer += chunksize; + count -= chunksize; + if (b->offset < s->fragsize) { + ret = 0; + up(&s->sem); + break; + } + + /* + * Activate DMA on current buffer. + * We unlock this fragment's checkpoint descriptor and + * kick DMA if it is idle. Using checkpoint descriptors + * allows for control operations without the need for + * stopping the DMA channel if it is already running. + */ + b->offset = 0; + b->dma_desc->ddadr &= ~DDADR_STOP; + if (DCSR(s->dma_ch) & DCSR_STOPSTATE) { + DDADR(s->dma_ch) = b->dma_desc->ddadr; + DCSR(s->dma_ch) = DCSR_RUN; + } + + /* move the index to the next fragment */ + if (++s->usr_frag >= s->nbfrags) + s->usr_frag = 0; + } + + if ((buffer - buffer0)) + ret = buffer - buffer0; + return ret; +} + + +static int audio_read(struct file *file, char *buffer, + size_t count, loff_t * ppos) +{ + char *buffer0 = buffer; + audio_state_t *state = file->private_data; + audio_stream_t *s = state->input_stream; + int chunksize, ret = 0; + + if (*ppos != file->f_pos) + return -ESPIPE; + if (s->mapped) + return -ENXIO; + if (!s->buffers && audio_setup_buf(s)) + return -ENOMEM; + + while (count > 0) { + audio_buf_t *b = &s->buffers[s->usr_frag]; + + /* prime DMA */ + if (DCSR(s->dma_ch) & DCSR_STOPSTATE) { + DDADR(s->dma_ch) = + s->buffers[s->dma_frag].dma_desc->ddadr; + DCSR(s->dma_ch) = DCSR_RUN; + } + + /* Wait for a buffer to become full */ + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + if (down_trylock(&s->sem)) + break; + } else { + ret = -ERESTARTSYS; + if (down_interruptible(&s->sem)) + break; + } + + /* Grab data from current buffer */ + chunksize = s->fragsize - b->offset; + if (chunksize > count) + chunksize = count; + if (copy_to_user(buffer, b->data + b->offset, chunksize)) { + up(&s->sem); + return -EFAULT; + } + b->offset += chunksize; + buffer += chunksize; + count -= chunksize; + if (b->offset < s->fragsize) { + ret = 0; + up(&s->sem); + break; + } + + /* + * Make this buffer available for DMA again. + * We unlock this fragment's checkpoint descriptor and + * kick DMA if it is idle. Using checkpoint descriptors + * allows for control operations without the need for + * stopping the DMA channel if it is already running. + */ + b->offset = 0; + b->dma_desc->ddadr &= ~DDADR_STOP; + + /* move the index to the next fragment */ + if (++s->usr_frag >= s->nbfrags) + s->usr_frag = 0; + } + + if ((buffer - buffer0)) + ret = buffer - buffer0; + return ret; +} + + +static int audio_sync(struct file *file) +{ + audio_state_t *state = file->private_data; + audio_stream_t *s = state->output_stream; + audio_buf_t *b; + pxa_dma_desc *final_desc; + u_long dcmd_save = 0; + + DECLARE_WAITQUEUE(wait, current); + + if (!(file->f_mode & FMODE_WRITE) || !s->buffers || s->mapped) + return 0; + + /* + * Send current buffer if it contains data. Be sure to send + * a full sample count. + */ + final_desc = NULL; + b = &s->buffers[s->usr_frag]; + if (b->offset &= ~3) { + final_desc = &b->dma_desc[1 + b->offset/MAX_DMA_SIZE]; + b->offset &= (MAX_DMA_SIZE-1); + dcmd_save = final_desc->dcmd; + final_desc->dcmd = b->offset | s->dcmd | DCMD_ENDIRQEN; + final_desc->ddadr |= DDADR_STOP; + b->offset = 0; + b->dma_desc->ddadr &= ~DDADR_STOP; + if (DCSR(s->dma_ch) & DCSR_STOPSTATE) { + DDADR(s->dma_ch) = b->dma_desc->ddadr; + DCSR(s->dma_ch) = DCSR_RUN; + } + } + + /* Wait for DMA to complete. */ + set_current_state(TASK_INTERRUPTIBLE); +#if 0 + /* + * The STOPSTATE IRQ never seem to occur if DCSR_STOPIRQEN is set + * along wotj DCSR_RUN. Silicon bug? + */ + add_wait_queue(&s->stop_wq, &wait); + DCSR(s->dma_ch) |= DCSR_STOPIRQEN; + schedule(); +#else + add_wait_queue(&s->frag_wq, &wait); + while ((DCSR(s->dma_ch) & DCSR_RUN) && !signal_pending(current)) { + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } +#endif + set_current_state(TASK_RUNNING); + remove_wait_queue(&s->frag_wq, &wait); + + /* Restore the descriptor chain. */ + if (final_desc) { + final_desc->dcmd = dcmd_save; + final_desc->ddadr &= ~DDADR_STOP; + b->dma_desc->ddadr |= DDADR_STOP; + } + return 0; +} + + +static unsigned int audio_poll(struct file *file, + struct poll_table_struct *wait) +{ + audio_state_t *state = file->private_data; + audio_stream_t *is = state->input_stream; + audio_stream_t *os = state->output_stream; + unsigned int mask = 0; + + if (file->f_mode & FMODE_READ) { + /* Start audio input if not already active */ + if (!is->buffers && audio_setup_buf(is)) + return -ENOMEM; + if (DCSR(is->dma_ch) & DCSR_STOPSTATE) { + DDADR(is->dma_ch) = + is->buffers[is->dma_frag].dma_desc->ddadr; + DCSR(is->dma_ch) = DCSR_RUN; + } + poll_wait(file, &is->frag_wq, wait); + } + + if (file->f_mode & FMODE_WRITE) { + if (!os->buffers && audio_setup_buf(os)) + return -ENOMEM; + poll_wait(file, &os->frag_wq, wait); + } + + if (file->f_mode & FMODE_READ) + if (( is->mapped && is->bytecount > 0) || + (!is->mapped && atomic_read(&is->sem.count) > 0)) + mask |= POLLIN | POLLRDNORM; + + if (file->f_mode & FMODE_WRITE) + if (( os->mapped && os->bytecount > 0) || + (!os->mapped && atomic_read(&os->sem.count) > 0)) + mask |= POLLOUT | POLLWRNORM; + + return mask; +} + + +static int audio_ioctl( struct inode *inode, struct file *file, + uint cmd, ulong arg) +{ + audio_state_t *state = file->private_data; + audio_stream_t *os = state->output_stream; + audio_stream_t *is = state->input_stream; + long val; + + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) + return put_user(os->fragsize, (int *)arg); + else + return put_user(is->fragsize, (int *)arg); + + case SNDCTL_DSP_GETCAPS: + val = DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP; + if (is && os) + val |= DSP_CAP_DUPLEX; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (long *) arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + int ret = audio_set_fragments(is, val); + if (ret < 0) + return ret; + ret = put_user(ret, (int *)arg); + if (ret) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + int ret = audio_set_fragments(os, val); + if (ret < 0) + return ret; + ret = put_user(ret, (int *)arg); + if (ret) + return ret; + } + return 0; + + case SNDCTL_DSP_SYNC: + return audio_sync(file); + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && DCSR(is->dma_ch) & DCSR_RUN) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && DCSR(os->dma_ch) & DCSR_RUN) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!is->buffers && audio_setup_buf(is)) + return -ENOMEM; + if (!(DCSR(is->dma_ch) & DCSR_RUN)) { + audio_buf_t *b = &is->buffers[is->dma_frag]; + DDADR(is->dma_ch) = b->dma_desc->ddadr; + DCSR(is->dma_ch) = DCSR_RUN; + } + } else { + DCSR(is->dma_ch) = 0; + } + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!os->buffers && audio_setup_buf(os)) + return -ENOMEM; + if (!(DCSR(os->dma_ch) & DCSR_RUN)) { + audio_buf_t *b = &os->buffers[os->dma_frag]; + DDADR(os->dma_ch) = b->dma_desc->ddadr; + DCSR(os->dma_ch) = DCSR_RUN; + } + } else { + DCSR(os->dma_ch) = 0; + } + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + case SNDCTL_DSP_GETISPACE: + { + audio_buf_info inf = { 0, }; + audio_stream_t *s = (cmd == SNDCTL_DSP_GETOSPACE) ? os : is; + + if ((s == is && !(file->f_mode & FMODE_READ)) || + (s == os && !(file->f_mode & FMODE_WRITE))) + return -EINVAL; + if (!s->buffers && audio_setup_buf(s)) + return -ENOMEM; + inf.bytes = atomic_read(&s->sem.count) * s->fragsize; + inf.bytes -= s->buffers[s->usr_frag].offset; + inf.fragments = inf.bytes / s->fragsize; + inf.fragsize = s->fragsize; + inf.fragstotal = s->nbfrags; + return copy_to_user((void *)arg, &inf, sizeof(inf)); + } + + case SNDCTL_DSP_GETOPTR: + case SNDCTL_DSP_GETIPTR: + { + count_info inf = { 0, }; + audio_stream_t *s = (cmd == SNDCTL_DSP_GETOPTR) ? os : is; + dma_addr_t ptr; + int bytecount, offset; + unsigned long flags; + + if ((s == is && !(file->f_mode & FMODE_READ)) || + (s == os && !(file->f_mode & FMODE_WRITE))) + return -EINVAL; + local_irq_save(flags); + if (DCSR(s->dma_ch) & DCSR_RUN) { + audio_buf_t *b; + ptr = (s->output) ? DSADR(s->dma_ch) : DTADR(s->dma_ch); + b = &s->buffers[s->dma_frag]; + offset = ptr - b->dma_desc->dsadr; + if (offset >= s->fragsize) + offset = s->fragsize - 4; + } else { + offset = 0; + } + inf.ptr = s->dma_frag * s->fragsize + offset; + bytecount = s->bytecount + offset; + s->bytecount = -offset; + inf.blocks = s->fragcount; + s->fragcount = 0; + local_irq_restore(flags); + if (bytecount < 0) + bytecount = 0; + inf.bytes = bytecount; + return copy_to_user((void *)arg, &inf, sizeof(inf)); + } + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) + audio_clear_buf(os); + if (file->f_mode & FMODE_READ) + audio_clear_buf(is); + return 0; + + default: + return state->client_ioctl ? + state->client_ioctl(inode, file, cmd, arg) : -EINVAL; + } + + return 0; +} + + +static int audio_mmap(struct file *file, struct vm_area_struct *vma) +{ + audio_state_t *state = file->private_data; + audio_stream_t *s; + unsigned long size, vma_addr; + int i, ret; + + if (vma->vm_pgoff != 0) + return -EINVAL; + + if (vma->vm_flags & VM_WRITE) { + if (!state->wr_ref) + return -EINVAL;; + s = state->output_stream; + } else if (vma->vm_flags & VM_READ) { + if (!state->rd_ref) + return -EINVAL; + s = state->input_stream; + } else return -EINVAL; + + if (s->mapped) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size != s->fragsize * s->nbfrags) + return -EINVAL; + if (!s->buffers && audio_setup_buf(s)) + return -ENOMEM; + vma_addr = vma->vm_start; + for (i = 0; i < s->nbfrags; i++) { + audio_buf_t *buf = &s->buffers[i]; + if (!buf->master) + continue; + ret = remap_page_range(vma, vma->vm_start, buf->dma_desc->dsadr, + buf->master, vma->vm_page_prot); + if (ret) + return ret; + vma_addr += buf->master; + } + for (i = 0; i < s->nbfrags; i++) + s->buffers[i].dma_desc->ddadr &= ~DDADR_STOP; + s->mapped = 1; + return 0; +} + + +static int audio_release(struct inode *inode, struct file *file) +{ + audio_state_t *state = file->private_data; + + down(&state->sem); + + if (file->f_mode & FMODE_READ) { + audio_clear_buf(state->input_stream); + *state->input_stream->drcmr = 0; + pxa_free_dma(state->input_stream->dma_ch); + state->rd_ref = 0; + } + + if (file->f_mode & FMODE_WRITE) { + audio_sync(file); + audio_clear_buf(state->output_stream); + *state->output_stream->drcmr = 0; + pxa_free_dma(state->output_stream->dma_ch); + state->wr_ref = 0; + } + + up(&state->sem); + return 0; +} + + +int pxa_audio_attach(struct inode *inode, struct file *file, + audio_state_t *state) +{ + audio_stream_t *is = state->input_stream; + audio_stream_t *os = state->output_stream; + int err; + + down(&state->sem); + + /* access control */ + err = -ENODEV; + if ((file->f_mode & FMODE_WRITE) && !os) + goto out; + if ((file->f_mode & FMODE_READ) && !is) + goto out; + err = -EBUSY; + if ((file->f_mode & FMODE_WRITE) && state->wr_ref) + goto out; + if ((file->f_mode & FMODE_READ) && state->rd_ref) + goto out; + + /* request DMA channels */ + if (file->f_mode & FMODE_WRITE) { + err = pxa_request_dma(os->name, DMA_PRIO_LOW, + audio_dma_irq, os); + if (err < 0) + goto out; + os->dma_ch = err; + } + if (file->f_mode & FMODE_READ) { + err = pxa_request_dma(is->name, DMA_PRIO_LOW, + audio_dma_irq, is); + if (err < 0) { + if (file->f_mode & FMODE_WRITE) { + *os->drcmr = 0; + pxa_free_dma(os->dma_ch); + } + goto out; + } + is->dma_ch = err; + } + + file->private_data = state; + file->f_op->release = audio_release; + file->f_op->write = audio_write; + file->f_op->read = audio_read; + file->f_op->mmap = audio_mmap; + file->f_op->poll = audio_poll; + file->f_op->ioctl = audio_ioctl; + file->f_op->llseek = no_llseek; + + if ((file->f_mode & FMODE_WRITE)) { + state->wr_ref = 1; + os->fragsize = AUDIO_FRAGSIZE_DEFAULT; + os->nbfrags = AUDIO_NBFRAGS_DEFAULT; + os->output = 1; + os->mapped = 0; + init_waitqueue_head(&os->frag_wq); + init_waitqueue_head(&os->stop_wq); + *os->drcmr = os->dma_ch | DRCMR_MAPVLD; + } + if (file->f_mode & FMODE_READ) { + state->rd_ref = 1; + is->fragsize = AUDIO_FRAGSIZE_DEFAULT; + is->nbfrags = AUDIO_NBFRAGS_DEFAULT; + is->output = 0; + is->mapped = 0; + init_waitqueue_head(&is->frag_wq); + init_waitqueue_head(&is->stop_wq); + *is->drcmr = is->dma_ch | DRCMR_MAPVLD; + } + + err = 0; + +out: + up(&state->sem); + return err; +} + +EXPORT_SYMBOL(pxa_audio_attach); +EXPORT_SYMBOL(pxa_audio_clear_buf); + +MODULE_AUTHOR("Nicolas Pitre, MontaVista Software Inc."); +MODULE_DESCRIPTION("Audio interface for PXA25x, PXA26x and PXA27x"); +MODULE_LICENSE("GPL"); diff -uNr a/sound/oss/pxa-audio.h b/sound/oss/pxa-audio.h --- a/sound/oss/pxa-audio.h 1970-01-01 08:00:00.000000000 +0800 +++ b/sound/oss/pxa-audio.h 2005-01-27 09:56:49.000000000 +0800 @@ -0,0 +1,55 @@ +/* + * linux/drivers/sound/pxa-audio.h -- audio interface for the Cotula chip + * + * Author: Nicolas Pitre + * Created: Aug 15, 2001 + * Copyright: MontaVista Software 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. + */ + +typedef struct { + int offset; /* current buffer position */ + char *data; /* actual buffer */ + pxa_dma_desc *dma_desc; /* pointer to the starting desc */ + int master; /* owner for buffer allocation, contain size whn true */ +} audio_buf_t; + +typedef struct { + char *name; /* stream identifier */ + audio_buf_t *buffers; /* pointer to audio buffer array */ + u_int usr_frag; /* user fragment index */ + u_int dma_frag; /* DMA fragment index */ + u_int fragsize; /* fragment size */ + u_int nbfrags; /* number of fragments */ + u_int dma_ch; /* DMA channel number */ + dma_addr_t dma_desc_phys; /* phys addr of descriptor ring */ + u_int descs_per_frag; /* nbr descriptors per fragment */ + int bytecount; /* nbr of processed bytes */ + int fragcount; /* nbr of fragment transitions */ + struct semaphore sem; /* account for fragment usage */ + wait_queue_head_t frag_wq; /* for poll(), etc. */ + wait_queue_head_t stop_wq; /* for users of DCSR_STOPIRQEN */ + u_long dcmd; /* DMA descriptor dcmd field */ + volatile u32 *drcmr; /* the DMA request channel to use */ + u_long dev_addr; /* device physical address for DMA */ + int mapped:1; /* mmap()'ed buffers */ + int output:1; /* 0 for input, 1 for output */ +} audio_stream_t; + +typedef struct { + audio_stream_t *output_stream; + audio_stream_t *input_stream; + int dev_dsp; /* audio device handle */ + int rd_ref:1; /* open reference for recording */ + int wr_ref:1; /* open reference for playback */ + int (*client_ioctl)(struct inode *, struct file *, uint, ulong); + struct semaphore sem; /* prevent races in attach/release */ +} audio_state_t; + +extern int pxa_audio_attach(struct inode *inode, struct file *file, + audio_state_t *state); +extern void pxa_audio_clear_buf(audio_stream_t *s); + diff -uNr a/sound/oss/ucb1x00-core.c b/sound/oss/ucb1x00-core.c --- a/sound/oss/ucb1x00-core.c 1970-01-01 08:00:00.000000000 +0800 +++ b/sound/oss/ucb1x00-core.c 2005-01-27 09:56:49.000000000 +0800 @@ -0,0 +1,866 @@ +/* + * linux/drivers/misc/ucb1x00-core.c + * + * Copyright (C) 2001 Russell King, 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. + * + * The UCB1x00 core driver provides basic services for handling IO, + * the ADC, interrupts, and accessing registers. It is designed + * such that everything goes through this layer, thereby providing + * a consistent locking methodology, as well as allowing the drivers + * to be used on other non-MCP-enabled hardware platforms. + * + * Note that all locks are private to this file. Nothing else may + * touch them. + * + * Code Status: + * 2004/10/26 Yan Yin + * - Port to 2.6 kernel + * - Remove dependency of MCP abstraction layer + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef CONFIG_ARCH_SA1100 +#include +#include +#endif + +#include + + +#include "ucb1x00.h" + +/** + * ucb1x00_io_set_dir - set IO direction + * @ucb: UCB1x00 structure describing chip + * @in: bitfield of IO pins to be set as inputs + * @out: bitfield of IO pins to be set as outputs + * + * Set the IO direction of the ten general purpose IO pins on + * the UCB1x00 chip. The @in bitfield has priority over the + * @out bitfield, in that if you specify a pin as both input + * and output, it will end up as an input. + * + * ucb1x00_enable must have been called to enable the comms + * before using this function. + * + * This function takes a spinlock, disabling interrupts. + */ +void ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsigned int in, unsigned int out) +{ + unsigned long flags; + + spin_lock_irqsave(&ucb->io_lock, flags); + ucb->io_dir |= out; + ucb->io_dir &= ~in; + + ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); + spin_unlock_irqrestore(&ucb->io_lock, flags); +} + +/** + * ucb1x00_io_write - set or clear IO outputs + * @ucb: UCB1x00 structure describing chip + * @set: bitfield of IO pins to set to logic '1' + * @clear: bitfield of IO pins to set to logic '0' + * + * Set the IO output state of the specified IO pins. The value + * is retained if the pins are subsequently configured as inputs. + * The @clear bitfield has priority over the @set bitfield - + * outputs will be cleared. + * + * ucb1x00_enable must have been called to enable the comms + * before using this function. + * + * This function takes a spinlock, disabling interrupts. + */ +void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int set, unsigned int clear) +{ + unsigned long flags; + + spin_lock_irqsave(&ucb->io_lock, flags); + ucb->io_out |= set; + ucb->io_out &= ~clear; + + ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); + spin_unlock_irqrestore(&ucb->io_lock, flags); +} + +/** + * ucb1x00_io_read - read the current state of the IO pins + * @ucb: UCB1x00 structure describing chip + * + * Return a bitfield describing the logic state of the ten + * general purpose IO pins. + * + * ucb1x00_enable must have been called to enable the comms + * before using this function. + * + * This function does not take any semaphores or spinlocks. + */ +unsigned int ucb1x00_io_read(struct ucb1x00 *ucb) +{ + return ucb1x00_reg_read(ucb, UCB_IO_DATA); +} + +/* + * UCB1300 data sheet says we must: + * 1. enable ADC => 5us (including reference startup time) + * 2. select input => 51*tsibclk => 4.3us + * 3. start conversion => 102*tsibclk => 8.5us + * (tsibclk = 1/11981000) + * Period between SIB 128-bit frames = 10.7us + */ + +/** + * ucb1x00_adc_enable - enable the ADC converter + * @ucb: UCB1x00 structure describing chip + * + * Enable the ucb1x00 and ADC converter on the UCB1x00 for use. + * Any code wishing to use the ADC converter must call this + * function prior to using it. + * + * This function takes the ADC semaphore to prevent two or more + * concurrent uses, and therefore may sleep. As a result, it + * can only be called from process context, not interrupt + * context. + * + * You should release the ADC as soon as possible using + * ucb1x00_adc_disable. + */ +void ucb1x00_adc_enable(struct ucb1x00 *ucb) +{ + down(&ucb->adc_sem); + + ucb->adc_cr |= UCB_ADC_ENA; + + ucb1x00_enable(ucb); + ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr); +} + +/** + * ucb1x00_adc_read - read the specified ADC channel + * @ucb: UCB1x00 structure describing chip + * @adc_channel: ADC channel mask + * @sync: wait for syncronisation pulse. + * + * Start an ADC conversion and wait for the result. Note that + * synchronised ADC conversions (via the ADCSYNC pin) must wait + * until the trigger is asserted and the conversion is finished. + * + * This function currently spins waiting for the conversion to + * complete (2 frames max without sync). + * + * If called for a synchronised ADC conversion, it may sleep + * with the ADC semaphore held. + * + * See ucb1x00.h for definition of the UCB_ADC_DAT macro. It + * addresses a bug in the ucb1200/1300 which, of course, Philips + * decided to finally fix in the ucb1400 ;-) -jws + */ +unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync) +{ + unsigned int val; + + if (sync) + adc_channel |= UCB_ADC_SYNC_ENA; + + ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr | adc_channel); + ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr | adc_channel | UCB_ADC_START); + + for (;;) { + val = ucb1x00_reg_read(ucb, UCB_ADC_DATA); + if (val & UCB_ADC_DAT_VAL) + break; + /* yield to other processes */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + } + + return UCB_ADC_DAT(val); +} + +/** + * ucb1x00_adc_disable - disable the ADC converter + * @ucb: UCB1x00 structure describing chip + * + * Disable the ADC converter and release the ADC semaphore. + */ +void ucb1x00_adc_disable(struct ucb1x00 *ucb) +{ + ucb->adc_cr &= ~UCB_ADC_ENA; + ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr); + ucb1x00_disable(ucb); + + up(&ucb->adc_sem); +} + + +/* + * UCB1x00 Interrupt handling. + * + * The UCB1x00 can generate interrupts when the SIBCLK is stopped. + * Since we need to read an internal register, we must re-enable + * SIBCLK to talk to the chip. We leave the clock running until + * we have finished processing all interrupts from the chip. + * + * A restriction with interrupts exists when using the ucb1400, as + * the codec read/write routines may sleep while waiting for codec + * access completion and uses semaphores for access control to the + * AC97 bus. A complete codec read cycle could take anywhere from + * 60 to 100uSec so we *definitely* don't want to spin inside the + * interrupt handler waiting for codec access. So, we handle the + * interrupt by scheduling a RT kernel thread to run in process + * context instead of interrupt context. + */ + +static int ucb1x00_thread(void *_ucb) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + struct ucb1x00 *ucb = _ucb; + struct ucb1x00_irq *irq; + unsigned int isr, i; + + tsk->flags |= PF_NOFREEZE; + ucb->rtask = tsk; + + daemonize("kUCB1x00d"); + tsk->policy = SCHED_FIFO; + tsk->rt_priority = 1; + strcpy(tsk->comm, "kUCB1x00d"); + + /* only want to receive SIGKILL */ + allow_signal(SIGKILL); + + add_wait_queue(&ucb->irq_wait, &wait); + set_task_state(tsk, TASK_INTERRUPTIBLE); + complete(&ucb->complete); + + for (;;) { + if (signal_pending(tsk)) + break; + enable_irq(ucb->irq); + schedule(); + + ucb1x00_enable(ucb); + isr = ucb1x00_reg_read(ucb, UCB_IE_STATUS); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); + + for (i = 0, irq = ucb->irq_handler; + i < 16 && isr; + i++, isr >>= 1, irq++) + if (isr & 1 && irq->fn) + irq->fn(i, irq->devid); + ucb1x00_disable(ucb); + + set_task_state(tsk, TASK_INTERRUPTIBLE); + } + + remove_wait_queue(&ucb->irq_wait, &wait); + ucb->rtask = NULL; + complete_and_exit(&ucb->complete, 0); +} + +static irqreturn_t ucb1x00_irq(int irqnr, void *devid, struct pt_regs *regs) +{ + struct ucb1x00 *ucb = devid; + disable_irq(irqnr); + wake_up(&ucb->irq_wait); + return IRQ_HANDLED; +} + +/** + * ucb1x00_hook_irq - hook a UCB1x00 interrupt + * @ucb: UCB1x00 structure describing chip + * @idx: interrupt index + * @fn: function to call when interrupt is triggered + * @devid: device id to pass to interrupt handler + * + * Hook the specified interrupt. You can only register one handler + * for each interrupt source. The interrupt source is not enabled + * by this function; use ucb1x00_enable_irq instead. + * + * Interrupt handlers will be called with other interrupts enabled. + * + * Returns zero on success, or one of the following errors: + * -EINVAL if the interrupt index is invalid + * -EBUSY if the interrupt has already been hooked + */ +int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid) +{ + struct ucb1x00_irq *irq; + int ret = -EINVAL; + + if (idx < 16) { + irq = ucb->irq_handler + idx; + ret = -EBUSY; + + spin_lock_irq(&ucb->lock); + if (irq->fn == NULL) { + irq->devid = devid; + irq->fn = fn; + ret = 0; + } + spin_unlock_irq(&ucb->lock); + } + return ret; +} + +/** + * ucb1x00_enable_irq - enable an UCB1x00 interrupt source + * @ucb: UCB1x00 structure describing chip + * @idx: interrupt index + * @edges: interrupt edges to enable + * + * Enable the specified interrupt to trigger on %UCB_RISING, + * %UCB_FALLING or both edges. The interrupt should have been + * hooked by ucb1x00_hook_irq. + */ +void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges) +{ + unsigned long flags; + + if (idx < 16) { + spin_lock_irqsave(&ucb->lock, flags); + + ucb1x00_enable(ucb); + + /* This prevents spurious interrupts on the UCB1400 */ + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 1 << idx); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); + + if (edges & UCB_RISING) { + ucb->irq_ris_enbl |= 1 << idx; + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); + } + if (edges & UCB_FALLING) { + ucb->irq_fal_enbl |= 1 << idx; + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); + } + ucb1x00_disable(ucb); + spin_unlock_irqrestore(&ucb->lock, flags); + } +} + +/** + * ucb1x00_disable_irq - disable an UCB1x00 interrupt source + * @ucb: UCB1x00 structure describing chip + * @edges: interrupt edges to disable + * + * Disable the specified interrupt triggering on the specified + * (%UCB_RISING, %UCB_FALLING or both) edges. + */ +void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges) +{ + unsigned long flags; + + if (idx < 16) { + spin_lock_irqsave(&ucb->lock, flags); + + ucb1x00_enable(ucb); + if (edges & UCB_RISING) { + ucb->irq_ris_enbl &= ~(1 << idx); + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); + } + if (edges & UCB_FALLING) { + ucb->irq_fal_enbl &= ~(1 << idx); + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); + } + ucb1x00_disable(ucb); + spin_unlock_irqrestore(&ucb->lock, flags); + } +} + +/** + * ucb1x00_free_irq - disable and free the specified UCB1x00 interrupt + * @ucb: UCB1x00 structure describing chip + * @idx: interrupt index + * @devid: device id. + * + * Disable the interrupt source and remove the handler. devid must + * match the devid passed when hooking the interrupt. + * + * Returns zero on success, or one of the following errors: + * -EINVAL if the interrupt index is invalid + * -ENOENT if devid does not match + */ +int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid) +{ + struct ucb1x00_irq *irq; + int ret; + + if (idx >= 16) + goto bad; + + irq = ucb->irq_handler + idx; + ret = -ENOENT; + + spin_lock_irq(&ucb->lock); + if (irq->devid == devid) { + ucb->irq_ris_enbl &= ~(1 << idx); + ucb->irq_fal_enbl &= ~(1 << idx); + + ucb1x00_enable(ucb); + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); + ucb1x00_disable(ucb); + + irq->fn = NULL; + irq->devid = NULL; + ret = 0; + } + spin_unlock_irq(&ucb->lock); + return ret; + +bad: + printk(KERN_ERR "%s: freeing bad irq %d\n", __FUNCTION__, idx); + return -EINVAL; +} + +/* + * Try to probe our interrupt, rather than relying on lots of + * hard-coded machine dependencies. For reference, the expected + * IRQ mappings are: + * + * Machine Default IRQ + * adsbitsy IRQ_GPCIN4 + * cerf IRQ_GPIO_UCB1200_IRQ + * flexanet IRQ_GPIO_GUI + * freebird IRQ_GPIO_FREEBIRD_UCB1300_IRQ + * graphicsclient ADS_EXT_IRQ(8) + * graphicsmaster ADS_EXT_IRQ(8) + * lart LART_IRQ_UCB1200 + * omnimeter IRQ_GPIO23 + * pfs168 IRQ_GPIO_UCB1300_IRQ + * simpad IRQ_GPIO_UCB1300_IRQ + * shannon SHANNON_IRQ_GPIO_IRQ_CODEC + * yopy IRQ_GPIO_UCB1200_IRQ + */ +static int __init ucb1x00_detect_irq(struct ucb1x00 *ucb) +{ + unsigned long mask; + + mask = probe_irq_on(); + if (!mask) + return NO_IRQ; + + /* + * Enable the ADC interrupt. + */ + ucb1x00_reg_write(ucb, UCB_IE_RIS, UCB_IE_ADC); + ucb1x00_reg_write(ucb, UCB_IE_FAL, UCB_IE_ADC); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0xffff); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); + + /* + * Cause an ADC interrupt. + */ + ucb1x00_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA); + ucb1x00_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START); + + /* + * Wait for the conversion to complete. + */ + while ((ucb1x00_reg_read(ucb, UCB_ADC_DATA) & UCB_ADC_DAT_VAL) == 0); + ucb1x00_reg_write(ucb, UCB_ADC_CR, 0); + + /* + * Disable and clear interrupt. + */ + ucb1x00_reg_write(ucb, UCB_IE_RIS, 0); + ucb1x00_reg_write(ucb, UCB_IE_FAL, 0); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0xffff); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); + + /* + * Read triggered interrupt. + */ + return probe_irq_off(mask); +} + +/* + * This configures the UCB1x00 layer depending on the machine type + * we're running on. The UCB1x00 drivers should not contain any + * machine dependencies. + * + * We can get rid of some of these dependencies by using existing + * facilities provided by the kernel - namely IRQ probing. The + * machine specific files are expected to setup the IRQ levels on + * initialisation. With any luck, we'll get rid of all the + * machine dependencies here. + */ +static int __init ucb1x00_configure(struct ucb1x00 *ucb) +{ + unsigned int irq_gpio_pin = 0; + int irq, default_irq = NO_IRQ; + +#ifdef CONFIG_ARCH_SA1100 + if (machine_is_adsbitsy()) + default_irq = IRQ_GPCIN4; + +// if (machine_is_assabet()) +// default_irq = IRQ_GPIO23; + +#ifdef CONFIG_SA1100_CERF + if (machine_is_cerf()) + default_irq = IRQ_GPIO_UCB1200_IRQ; +#endif +#ifdef CONFIG_SA1100_FREEBIRD + if (machine_is_freebird()) + default_irq = IRQ_GPIO_FREEBIRD_UCB1300_IRQ; +#endif +#if defined(CONFIG_SA1100_GRAPHICSCLIENT) || defined(CONFIG_SA1100_GRAPICSMASTER) + if (machine_is_graphicsclient() || machine_is_graphicsmaster()) + default_irq = ADS_EXT_IRQ(8); +#endif +#ifdef CONFIG_SA1100_LART + if (machine_is_lart()) { + default_irq = LART_IRQ_UCB1200; + irq_gpio_pin = LART_GPIO_UCB1200; + } +#endif + if (machine_is_omnimeter()) + default_irq = IRQ_GPIO23; + +#ifdef CONFIG_SA1100_PFS168 + if (machine_is_pfs168()) + default_irq = IRQ_GPIO_UCB1300_IRQ; +#endif +#ifdef CONFIG_SA1100_SIMPAD + if (machine_is_simpad()) + default_irq = IRQ_GPIO_UCB1300_IRQ; +#endif +#ifdef CONFIG_SA1100_SIMPUTER + if (machine_is_simputer()) { + default_irq = IRQ_GPIO_UCB1300_IRQ; + irq_gpio_pin = GPIO_UCB1300_IRQ; + } +#endif + if (machine_is_shannon()) + default_irq = SHANNON_IRQ_GPIO_IRQ_CODEC; +#ifdef CONFIG_SA1100_YOPY + if (machine_is_yopy()) + default_irq = IRQ_GPIO_UCB1200_IRQ; +#endif +#ifdef CONFIG_SA1100_ACCELENT + if (machine_is_accelent_sa()) { + ucb->irq = IRQ_GPIO_UCB1200_IRQ; + irq_gpio_pin = GPIO_UCB1200_IRQ; + } +#endif + +#endif /* CONFIG_ARCH_SA1100 */ + +#ifdef CONFIG_ARCH_PXA_IDP + if (machine_is_pxa_idp()) { + default_irq = TOUCH_PANEL_IRQ; + irq_gpio_pin = IRQ_TO_GPIO_2_80(TOUCH_PANEL_IRQ); + GPDR(irq_gpio_pin) &= ~GPIO_bit(irq_gpio_pin); + } +#endif + +#ifdef CONFIG_ARCH_TRIZEPS2 + if (machine_is_trizeps2()) { + default_irq = TOUCH_PANEL_IRQ; + irq_gpio_pin = IRQ_TO_GPIO_2_80(TOUCH_PANEL_IRQ); + GPDR(irq_gpio_pin) &= ~GPIO_bit(irq_gpio_pin); + } +#endif + +#ifdef CONFIG_MACH_MAINSTONE + if (machine_is_mainstone()) { + ucb->irq = MAINSTONE_AC97_IRQ; + } +#endif + +#ifdef CONFIG_PXA_CERF_PDA + if (machine_is_pxa_cerf()) { + irq_gpio_pin = CERF_GPIO_UCB1400_IRQ; + } +#endif + + /* + * Eventually, this will disappear. + */ + if (irq_gpio_pin) +#ifdef CONFIG_ARCH_PXA_IDP + set_irq_type(irq_gpio_pin, IRQT_FALLING); +#else + set_irq_type(irq_gpio_pin, IRQT_RISING); +#endif + irq = ucb1x00_detect_irq(ucb); + if (irq != NO_IRQ) { + if (default_irq != NO_IRQ && irq != default_irq) + printk(KERN_ERR "UCB1x00: probed IRQ%d != default IRQ%d\n", + irq, default_irq); + if (irq == default_irq) + printk(KERN_ERR "UCB1x00: probed IRQ%d correctly. " + "Please remove machine dependencies from " + "ucb1x00-core.c\n", irq); + ucb->irq = irq; + } else { + printk(KERN_ERR "UCB1x00: IRQ probe failed, using IRQ%d\n", + default_irq); + ucb->irq = default_irq; + } + + return ucb->irq == NO_IRQ ? -ENODEV : 0; +} + +struct ucb1x00 *my_ucb; + +extern int pxa_ac97_get(struct ac97_codec **codec); +static int ucb1x00_init_helper(void) +{ + u16 id; + int ret = -ENODEV; + + struct ac97_codec *codec; + if (pxa_ac97_get(&codec) < 0) + return ret; + + id = codec->codec_read(codec, UCB_ID); + + if (id != UCB_ID_1200 && id != UCB_ID_1300 && id != UCB_ID_1400) { + printk(KERN_WARNING "UCB1x00 ID not found: %04x\n", id); + goto out; + } + + /* distinguish between UCB1400 revs 1B and 2A */ + if (id == UCB_ID_1400 && codec->codec_read(codec, 0x00) == 0x002a) + id = UCB_ID_1400_BUGGY; + + if (id == UCB_ID_1400) { + /*for audio set */ + codec->codec_write(codec,AC97_EXTENDED_STATUS,1); + codec->codec_write(codec, 0x6a, 0x0050); + codec->codec_write(codec, 0x6c, 0x0030); + } + + my_ucb = kmalloc(sizeof(struct ucb1x00), GFP_KERNEL); + ret = -ENOMEM; + if (!my_ucb) + goto out; + +#ifdef CONFIG_ARCH_SA1100 + if (machine_is_shannon()) { + /* reset the codec */ + GPDR |= SHANNON_GPIO_CODEC_RESET; + GPCR = SHANNON_GPIO_CODEC_RESET; + GPSR = SHANNON_GPIO_CODEC_RESET; + } +#endif + + memset(my_ucb, 0, sizeof(struct ucb1x00)); + + spin_lock_init(&my_ucb->lock); + spin_lock_init(&my_ucb->io_lock); + sema_init(&my_ucb->adc_sem, 1); + + my_ucb->id = id; + my_ucb->codec = codec; + + ret = ucb1x00_configure(my_ucb); + if (ret) + goto out; + + init_waitqueue_head(&my_ucb->irq_wait); + ret = request_irq(my_ucb->irq, ucb1x00_irq, 0, "UCB1x00", my_ucb); + if (ret) { + printk(KERN_ERR "ucb1x00: unable to grab irq%d: %d\n", + my_ucb->irq, ret); + goto irq_err; + } + + init_completion(&my_ucb->complete); + ret = kernel_thread(ucb1x00_thread, my_ucb, CLONE_FS | CLONE_FILES); + if (ret >= 0) { + wait_for_completion(&my_ucb->complete); + ret = 0; + goto out; + } + + free_irq(my_ucb->irq, my_ucb); +irq_err: + kfree(my_ucb); + my_ucb = NULL; +out: + return ret; +} + + +/** + * ucb1x00_get - get the UCB1x00 structure describing a chip + * @ucb: UCB1x00 structure describing chip + * + * Return the UCB1x00 structure describing a chip. + * + * FIXME: Currently very noddy indeed, which currently doesn't + * matter since we only support one chip. + */ +struct ucb1x00 *ucb1x00_get(void) +{ + if( !my_ucb) ucb1x00_init_helper(); + + return my_ucb; +} + +static int ucb1x00_probe(struct device *dev) +{ + /* check if driver is already initialized */ + if( my_ucb) return 0; + + return ucb1x00_init_helper(); +} + +extern void pxa_ac97_put(void); +static int ucb1x00_remove(struct device *dev) +{ + send_sig(SIGKILL, my_ucb->rtask, 1); + wait_for_completion(&my_ucb->complete); + free_irq(my_ucb->irq, my_ucb); + kfree(my_ucb); + my_ucb = 0; + pxa_ac97_put(); + + return 0; +} + + +/* + * Suspend & Resume the UCB1x00 Controller. + */ +static void ucb1x00_regs(struct ucb1x00 *ucb, u32 save) +{ + static unsigned int master_vol; + static unsigned int mic_vol; + static unsigned int record_gain; + + if (save) { + master_vol = ucb1x00_reg_read(ucb, AC97_MASTER_VOL_STEREO); + mic_vol = ucb1x00_reg_read(ucb, AC97_MIC_VOL); + record_gain = ucb1x00_reg_read(ucb, AC97_RECORD_GAIN); + } + else { + ucb1x00_reg_write(ucb, AC97_MASTER_VOL_STEREO, master_vol); + ucb1x00_reg_write(ucb, AC97_MIC_VOL, mic_vol); + ucb1x00_reg_write(ucb, AC97_RECORD_GAIN, record_gain); + } +} + + +static int ucb1x00_suspend(struct device * dev, u32 state, u32 level) +{ + if (level == SUSPEND_POWER_DOWN) { + struct ucb1x00 *ucb; + + ucb = ucb1x00_get(); + if (!ucb) { + printk("no valid ucb\n"); + return 0; + } + /* power down ucb1400 except AC-Link*/ + if (ucb->id == UCB_ID_1400) { + ucb1x00_enable(ucb); + ucb1x00_regs(ucb, 1); + ucb1x00_reg_write(ucb, AC97_POWER_CONTROL, 0x4b00); + ucb1x00_disable(ucb); + } + } + + return 0; +} + +static int ucb1x00_resume(struct device * dev, u32 level) +{ + struct ucb1x00 *ucb; + + ucb = ucb1x00_get(); + if (!ucb) { + printk("no valid ucb\n"); + return 0; + } + if(level == RESUME_POWER_ON) { + unsigned int isr; + + ucb1x00_enable(ucb); + /* power on UCB1400 */ + if (ucb->id == UCB_ID_1400) { + ucb1x00_reg_write(ucb, AC97_POWER_CONTROL, 0x0); + ucb1x00_regs(ucb, 0); + } + isr = ucb1x00_reg_read(ucb, UCB_IE_STATUS); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); + /* for audio set */ + /* default set VRA to get variable sample rate */ + ucb1x00_reg_write(ucb,AC97_EXTENDED_STATUS,1); + ucb1x00_reg_write(ucb, 0x6a, 0x0050); + ucb1x00_reg_write(ucb, 0x6c, 0x0030); + ucb1x00_disable(ucb); + } + return 0; +} + +static struct device_driver ucb1x00_driver = { + .name = "ucb1x00-core", + .probe = ucb1x00_probe, + .remove = ucb1x00_remove, + .bus = &platform_bus_type, + .suspend = ucb1x00_suspend, + .resume = ucb1x00_resume, +}; + +static struct platform_device *ucb1x00_device = NULL; + +static int __devinit ucb1x00_init(void) +{ + ucb1x00_device = platform_device_register_simple("ucb1x00-core", -1, NULL, 0); + return driver_register(&ucb1x00_driver); +} + +static void __exit ucb1x00_exit(void) +{ + driver_unregister(&ucb1x00_driver); + platform_device_unregister(ucb1x00_device); +} + + +module_init(ucb1x00_init); +module_exit(ucb1x00_exit); + +EXPORT_SYMBOL(ucb1x00_get); + +EXPORT_SYMBOL(ucb1x00_io_set_dir); +EXPORT_SYMBOL(ucb1x00_io_write); +EXPORT_SYMBOL(ucb1x00_io_read); + +EXPORT_SYMBOL(ucb1x00_adc_enable); +EXPORT_SYMBOL(ucb1x00_adc_read); +EXPORT_SYMBOL(ucb1x00_adc_disable); + +EXPORT_SYMBOL(ucb1x00_hook_irq); +EXPORT_SYMBOL(ucb1x00_free_irq); +EXPORT_SYMBOL(ucb1x00_enable_irq); +EXPORT_SYMBOL(ucb1x00_disable_irq); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("UCB1x00 core driver"); +MODULE_LICENSE("GPL"); diff -uNr a/sound/oss/ucb1x00.h b/sound/oss/ucb1x00.h --- a/sound/oss/ucb1x00.h 1970-01-01 08:00:00.000000000 +0800 +++ b/sound/oss/ucb1x00.h 2005-01-27 09:56:49.000000000 +0800 @@ -0,0 +1,265 @@ +/* + * linux/drivers/misc/ucb1x00.h + * + * Copyright (C) 2001 Russell King, 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. + */ +#ifndef UCB1200_H +#define UCB1200_H + +#include + +#ifdef CONFIG_ARCH_PXA + +/* ucb1400 aclink register mappings: */ + +#define UCB_IO_DATA 0x5a +#define UCB_IO_DIR 0x5c +#define UCB_IE_RIS 0x5e +#define UCB_IE_FAL 0x60 +#define UCB_IE_STATUS 0x62 +#define UCB_IE_CLEAR 0x62 +#define UCB_TS_CR 0x64 +#define UCB_ADC_CR 0x66 +#define UCB_ADC_DATA 0x68 +#define UCB_ID 0x7e /* 7c is mfr id, 7e part id (from aclink spec) */ + +#define UCB_ADC_DAT(x) ((x) & 0x3ff) + +#else + +/* ucb1x00 SIB register mappings: */ + +#define UCB_IO_DATA 0x00 +#define UCB_IO_DIR 0x01 +#define UCB_IE_RIS 0x02 +#define UCB_IE_FAL 0x03 +#define UCB_IE_STATUS 0x04 +#define UCB_IE_CLEAR 0x04 +#define UCB_TC_A 0x05 +#define UCB_TC_B 0x06 +#define UCB_AC_A 0x07 +#define UCB_AC_B 0x08 +#define UCB_TS_CR 0x09 +#define UCB_ADC_CR 0x0a +#define UCB_ADC_DATA 0x0b +#define UCB_ID 0x0c +#define UCB_MODE 0x0d + +#define UCB_ADC_DAT(x) (((x) & 0x7fe0) >> 5) + +#endif + + +#define UCB_IO_0 (1 << 0) +#define UCB_IO_1 (1 << 1) +#define UCB_IO_2 (1 << 2) +#define UCB_IO_3 (1 << 3) +#define UCB_IO_4 (1 << 4) +#define UCB_IO_5 (1 << 5) +#define UCB_IO_6 (1 << 6) +#define UCB_IO_7 (1 << 7) +#define UCB_IO_8 (1 << 8) +#define UCB_IO_9 (1 << 9) + +#define UCB_IE_ADC (1 << 11) +#define UCB_IE_TSPX (1 << 12) +#define UCB_IE_TSMX (1 << 13) +#define UCB_IE_TCLIP (1 << 14) +#define UCB_IE_ACLIP (1 << 15) + +#define UCB_IRQ_TSPX 12 + +#define UCB_TC_A_LOOP (1 << 7) /* UCB1200 */ +#define UCB_TC_A_AMPL (1 << 7) /* UCB1300 */ + +#define UCB_TC_B_VOICE_ENA (1 << 3) +#define UCB_TC_B_CLIP (1 << 4) +#define UCB_TC_B_ATT (1 << 6) +#define UCB_TC_B_SIDE_ENA (1 << 11) +#define UCB_TC_B_MUTE (1 << 13) +#define UCB_TC_B_IN_ENA (1 << 14) +#define UCB_TC_B_OUT_ENA (1 << 15) + +#define UCB_AC_B_LOOP (1 << 8) +#define UCB_AC_B_MUTE (1 << 13) +#define UCB_AC_B_IN_ENA (1 << 14) +#define UCB_AC_B_OUT_ENA (1 << 15) + +#define UCB_TS_CR_TSMX_POW (1 << 0) +#define UCB_TS_CR_TSPX_POW (1 << 1) +#define UCB_TS_CR_TSMY_POW (1 << 2) +#define UCB_TS_CR_TSPY_POW (1 << 3) +#define UCB_TS_CR_TSMX_GND (1 << 4) +#define UCB_TS_CR_TSPX_GND (1 << 5) +#define UCB_TS_CR_TSMY_GND (1 << 6) +#define UCB_TS_CR_TSPY_GND (1 << 7) +#define UCB_TS_CR_MODE_INT (0 << 8) +#define UCB_TS_CR_MODE_PRES (1 << 8) +#define UCB_TS_CR_MODE_POS (2 << 8) +#define UCB_TS_CR_BIAS_ENA (1 << 11) +#define UCB_TS_CR_TSPX_LOW (1 << 12) +#define UCB_TS_CR_TSMX_LOW (1 << 13) + +#define UCB_ADC_SYNC_ENA (1 << 0) +#define UCB_ADC_VREFBYP_CON (1 << 1) +#define UCB_ADC_INP_TSPX (0 << 2) +#define UCB_ADC_INP_TSMX (1 << 2) +#define UCB_ADC_INP_TSPY (2 << 2) +#define UCB_ADC_INP_TSMY (3 << 2) +#define UCB_ADC_INP_AD0 (4 << 2) +#define UCB_ADC_INP_AD1 (5 << 2) +#define UCB_ADC_INP_AD2 (6 << 2) +#define UCB_ADC_INP_AD3 (7 << 2) +#define UCB_ADC_EXT_REF (1 << 5) +#define UCB_ADC_START (1 << 7) +#define UCB_ADC_ENA (1 << 15) + +#define UCB_ADC_DAT_VAL (1 << 15) + +#define UCB_ID_1X00 0x5053 +#define UCB_ID_1200 0x1004 +#define UCB_ID_1300 0x1005 +#define UCB_ID_1400 0x4304 +#define UCB_ID_1400_BUGGY 0x4303 /* fake ID */ + +#define UCB_MODE_DYN_VFLAG_ENA (1 << 12) +#define UCB_MODE_AUD_OFF_CAN (1 << 13) + +struct ucb1x00; + +struct ucb1x00_irq { + void *devid; + void (*fn)(int, void *); +}; + +struct ucb1x00 { + spinlock_t lock; + struct ac97_codec *codec; + unsigned int irq; + struct semaphore adc_sem; + spinlock_t io_lock; + wait_queue_head_t irq_wait; + struct completion complete; + struct task_struct *rtask; + u16 id; + u16 io_dir; + u16 io_out; + u16 adc_cr; + u16 irq_fal_enbl; + u16 irq_ris_enbl; + struct ucb1x00_irq irq_handler[16]; +}; + +/** + * ucb1x00_clkrate - return the UCB1x00 SIB clock rate + * @ucb: UCB1x00 structure describing chip + * + * Return the SIB clock rate in Hz. + */ +static inline unsigned int ucb1x00_clkrate(struct ucb1x00 *ucb) +{ + return 0; /* FIXME */ +} + +/** + * ucb1x00_enable - enable the UCB1x00 SIB clock + * @ucb: UCB1x00 structure describing chip + * + * Enable the SIB clock. This can be called multiple times. + */ +static inline void ucb1x00_enable(struct ucb1x00 *ucb) +{ + /* + * Should we do something here to make sure the aclink + * codec is alive??? + * A: not for now --NP + */ +} + +/** + * ucb1x00_disable - disable the UCB1x00 SIB clock + * @ucb: UCB1x00 structure describing chip + * + * Disable the SIB clock. The SIB clock will only be disabled + * when the number of ucb1x00_enable calls match the number of + * ucb1x00_disable calls. + */ +static inline void ucb1x00_disable(struct ucb1x00 *ucb) +{ +} + +/** + * ucb1x00_reg_write - write a UCB1x00 register + * @ucb: UCB1x00 structure describing chip + * @reg: UCB1x00 4-bit register index to write + * @val: UCB1x00 16-bit value to write + * + * Write the UCB1x00 register @reg with value @val. The SIB + * clock must be running for this function to return. + */ +static inline void ucb1x00_reg_write(struct ucb1x00 *ucb, unsigned int reg, unsigned int val) +{ + struct ac97_codec *codec = ucb->codec; + codec->codec_write(codec, reg, val); +} + +/** + * ucb1x00_reg_read - read a UCB1x00 register + * @ucb: UCB1x00 structure describing chip + * @reg: UCB1x00 4-bit register index to write + * + * Read the UCB1x00 register @reg and return its value. The SIB + * clock must be running for this function to return. + */ +static inline unsigned int ucb1x00_reg_read(struct ucb1x00 *ucb, unsigned int reg) +{ + struct ac97_codec *codec = ucb->codec; + return codec->codec_read(codec, reg); +} +/** + * ucb1x00_set_audio_divisor - + * @ucb: UCB1x00 structure describing chip + * @div: SIB clock divisor + */ +static inline void ucb1x00_set_audio_divisor(struct ucb1x00 *ucb, unsigned int div) +{ +} + +/** + * ucb1x00_set_telecom_divisor - + * @ucb: UCB1x00 structure describing chip + * @div: SIB clock divisor + */ +static inline void ucb1x00_set_telecom_divisor(struct ucb1x00 *ucb, unsigned int div) +{ +} + +struct ucb1x00 *ucb1x00_get(void); + +void ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsigned int, unsigned int); +void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int, unsigned int); +unsigned int ucb1x00_io_read(struct ucb1x00 *ucb); + +#define UCB_NOSYNC (0) +#define UCB_SYNC (1) + +unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync); +void ucb1x00_adc_enable(struct ucb1x00 *ucb); +void ucb1x00_adc_disable(struct ucb1x00 *ucb); + +/* + * Which edges of the IRQ do you want to control today? + */ +#define UCB_RISING (1 << 0) +#define UCB_FALLING (1 << 1) + +int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid); +void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges); +void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges); +int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid); + +#endif diff -uNr a/sound/oss/ucb1x00-ts.c b/sound/oss/ucb1x00-ts.c --- a/sound/oss/ucb1x00-ts.c 1970-01-01 08:00:00.000000000 +0800 +++ b/sound/oss/ucb1x00-ts.c 2005-01-27 09:56:49.000000000 +0800 @@ -0,0 +1,542 @@ +/* + * linux/drivers/misc/ucb1x00-ts.c + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 21-Jan-2002 : + * + * Added support for synchronous A/D mode. This mode is useful to + * avoid noise induced in the touchpanel by the LCD, provided that + * the UCB1x00 has a valid LCD sync signal routed to its ADCSYNC pin. + * It is important to note that the signal connected to the ADCSYNC + * pin should provide pulses even when the LCD is blanked, otherwise + * a pen touch needed to unblank the LCD will never be read. + * + * Code Status: + * 2004/10/26 Yan Yin + * - Port to 2.6 kernel + * - Remove dependency of MCP abstraction layer + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ucb1x00.h" + +struct ucb1x00_ts { + struct input_dev idev; + struct ucb1x00 *ucb; + struct semaphore irq_wait; + struct semaphore sem; + struct completion init_exit; + struct task_struct *rtask; + int use_count; + u16 x_res; + u16 y_res; + + int restart:1; + int adcsync:1; +}; + +struct ucb1x00_ts ucbts; +static int adcsync = UCB_NOSYNC; + +static int ucb1x00_ts_startup(struct ucb1x00_ts *ts); +static void ucb1x00_ts_shutdown(struct ucb1x00_ts *ts); +extern int pm_updatetimer(int); + +#define ucb1x00_ts_evt_clear(ts) do { } while (0) + +static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y) +{ + input_report_abs(&ts->idev, ABS_X, x); + input_report_abs(&ts->idev, ABS_Y, y); + input_report_abs(&ts->idev, ABS_PRESSURE, pressure); +} + +static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts) +{ + input_report_abs(&ts->idev, ABS_PRESSURE, 0); +} + +static int ucb1x00_ts_open(struct input_dev *idev) +{ + struct ucb1x00_ts *ts = (struct ucb1x00_ts *)idev; + + return ucb1x00_ts_startup(ts); +} + +static void ucb1x00_ts_close(struct input_dev *idev) +{ + struct ucb1x00_ts *ts = (struct ucb1x00_ts *)idev; + + down(&ts->sem); + ucb1x00_ts_shutdown(ts); + up(&ts->sem); +} + +static inline int ucb1x00_ts_register(struct ucb1x00_ts *ts) +{ + ts->idev.name = "Touchscreen panel"; + ts->idev.open = ucb1x00_ts_open; + ts->idev.close = ucb1x00_ts_close; + + __set_bit(EV_ABS, ts->idev.evbit); + __set_bit(ABS_X, ts->idev.absbit); + __set_bit(ABS_Y, ts->idev.absbit); + __set_bit(ABS_PRESSURE, ts->idev.absbit); + + input_register_device(&ts->idev); + + return 0; +} + +static inline void ucb1x00_ts_deregister(struct ucb1x00_ts *ts) +{ + input_unregister_device(&ts->idev); +} + + +/* + * Switch to interrupt mode. + */ +static inline void ucb1x00_ts_mode_int(struct ucb1x00_ts *ts) +{ + if (ts->ucb->id == UCB_ID_1400_BUGGY) + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_INT); + else + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_INT); +} + +/* + * Switch to pressure mode, and read pressure. We don't need to wait + * here, since both plates are being driven. + */ +static inline unsigned int ucb1x00_ts_read_pressure(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); +} + +/* + * Switch to X position mode and measure Y plate. We switch the plate + * configuration in pressure mode, then switch to position mode. This + * gives a faster response time. Even so, we need to wait about 55us + * for things to stabilise. + */ +static inline unsigned int ucb1x00_ts_read_xpos(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + + udelay(55); + + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); +} + +/* + * Switch to Y position mode and measure X plate. We switch the plate + * configuration in pressure mode, then switch to position mode. This + * gives a faster response time. Even so, we need to wait about 55us + * for things to stabilise. + */ +static inline unsigned int ucb1x00_ts_read_ypos(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + + udelay(55); + + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync); +} + +/* + * Switch to X plate resistance mode. Set MX to ground, PX to + * supply. Measure current. + */ +static inline unsigned int ucb1x00_ts_read_xres(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync); +} + +/* + * Switch to Y plate resistance mode. Set MY to ground, PY to + * supply. Measure current. + */ +static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync); +} + +/* + * This is a RT kernel thread that handles the ADC accesses + * (mainly so we can use semaphores in the UCB1200 core code + * to serialise accesses to the ADC). The UCB1400 access + * functions are expected to be able to sleep as well. + */ +static int ucb1x00_thread(void *_ts) +{ + struct ucb1x00_ts *ts = _ts; + struct task_struct *tsk = current; + int valid; + + tsk->flags |= PF_NOFREEZE; + ts->rtask = tsk; + + /* set up thread context */ + daemonize("ktsd"); + strcpy(tsk->comm, "ktsd"); + + /* + * We could run as a real-time thread. However, thus far + * this doesn't seem to be necessary. + */ +// tsk->policy = SCHED_FIFO; +// tsk->rt_priority = 1; + + /* only want to receive SIGKILL */ + allow_signal(SIGKILL); + + /* init is complete */ + complete(&ts->init_exit); + + valid = 0; + + for (;;) { + unsigned int x, y, p, val; + + ts->restart = 0; + + ucb1x00_adc_enable(ts->ucb); + + x = ucb1x00_ts_read_xpos(ts); + y = ucb1x00_ts_read_ypos(ts); + p = ucb1x00_ts_read_pressure(ts); + + /* + * Switch back to interrupt mode. + */ + ucb1x00_ts_mode_int(ts); + ucb1x00_adc_disable(ts->ucb); + + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 100); + if (signal_pending(tsk)) + break; + + ucb1x00_enable(ts->ucb); + val = ucb1x00_reg_read(ts->ucb, UCB_TS_CR); + + if (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW)) { + ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING); + ucb1x00_disable(ts->ucb); + + /* + * If we spat out a valid sample set last time, + * spit out a "pen off" sample here. + */ + if (valid) { + ucb1x00_ts_event_release(ts); + valid = 0; + } + + /* + * Since ucb1x00_enable_irq() might sleep due + * to the way the UCB1400 regs are accessed, we + * can't use set_task_state() before that call, + * and not changing state before enabling the + * interrupt is racy. A semaphore solves all + * those issues quite nicely. + */ + down_interruptible(&ts->irq_wait); + } else { + ucb1x00_disable(ts->ucb); + + /* + * Filtering is policy. Policy belongs in user + * space. We therefore leave it to user space + * to do any filtering they please. + */ + if (!ts->restart) { + ucb1x00_ts_evt_add(ts, p, x, y); + valid = 1; + } + + set_task_state(tsk, TASK_INTERRUPTIBLE); + schedule_timeout(HZ / 100); + } + + if (signal_pending(tsk)) + break; + } + + ts->rtask = NULL; + ucb1x00_ts_evt_clear(ts); + complete_and_exit(&ts->init_exit, 0); +} + +/* + * We only detect touch screen _touches_ with this interrupt + * handler, and even then we just schedule our task. + */ +static void ucb1x00_ts_irq(int idx, void *id) +{ + struct ucb1x00_ts *ts = id; + ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING); +#ifdef CONFIG_PM + pm_updatetimer(0); +#endif + up(&ts->irq_wait); +} + +static int ucb1x00_ts_startup(struct ucb1x00_ts *ts) +{ + int ret = 0; + + if (down_interruptible(&ts->sem)) + return -EINTR; + + if (ts->use_count++ != 0) + goto out; + + if (ts->rtask) + panic("ucb1x00: rtask running?"); + + sema_init(&ts->irq_wait, 0); + ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts); + if (ret < 0) + goto out; + + /* + * If we do this at all, we should allow the user to + * measure and read the X and Y resistance at any time. + */ + ucb1x00_adc_enable(ts->ucb); + ts->x_res = ucb1x00_ts_read_xres(ts); + ts->y_res = ucb1x00_ts_read_yres(ts); + ucb1x00_adc_disable(ts->ucb); + + init_completion(&ts->init_exit); + ret = kernel_thread(ucb1x00_thread, ts, 0); + if (ret >= 0) { + wait_for_completion(&ts->init_exit); + ret = 0; + } else { + ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); + } + + out: + if (ret) + ts->use_count--; + up(&ts->sem); + return ret; +} + +/* + * Release touchscreen resources. Disable IRQs. + */ +static void ucb1x00_ts_shutdown(struct ucb1x00_ts *ts) +{ + if (--ts->use_count == 0) { + if (ts->rtask) { + send_sig(SIGKILL, ts->rtask, 1); + wait_for_completion(&ts->init_exit); + } + + ucb1x00_enable(ts->ucb); + ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0); + ucb1x00_disable(ts->ucb); + } +} + + +/* + * Initialisation. + */ +static int ucb1x00_ac97_probe(struct ac97_codec *codec, struct ac97_driver *driver) +{ + struct ucb1x00_ts *ts = &ucbts; + + ts->ucb = ucb1x00_get(); + if (!ts->ucb) + return -ENODEV; + + ts->adcsync = adcsync; + init_MUTEX(&ts->sem); + + return ucb1x00_ts_register(ts); +} + +static void ucb1x00_ac97_remove(struct ac97_codec *codec, struct ac97_driver *driver) +{ + struct ucb1x00_ts *ts = &ucbts; + + ucb1x00_ts_deregister(ts); +} + +/* AC97 registration info */ +static struct ac97_driver ucb1x00_ac97_driver = { + .codec_id = (UCB_ID_1X00 << 16) | UCB_ID_1400, /* FIXME: UCB_ID_1200/UCB_ID_1300? */ + .codec_mask = 0xFFFFFFFF, + .name = "UCB1x00 Touchscreen", + .probe = ucb1x00_ac97_probe, + .remove = __devexit_p(ucb1x00_ac97_remove), +}; + +static int ucb1x00_ts_probe(struct device *dev) +{ + printk(KERN_INFO "UCB1x00 Touchscreen Controller Driver.\n"); + + /* register with the AC97 layer */ + ac97_register_driver(&ucb1x00_ac97_driver); + + return 0; +} + +static int ucb1x00_ts_remove(struct device *dev) +{ + /* Unregister from the AC97 layer */ + ac97_unregister_driver(&ucb1x00_ac97_driver); + + return 0; +} + +/* + * Suspend & Resume the UCB1x00 Controller. + */ +static int ucb1x00_ts_suspend(struct device * dev, u32 state, u32 level) +{ + switch(level){ + case SUSPEND_POWER_DOWN: + break; + } + + return 0; +} + +static int ucb1x00_ts_resume(struct device * dev, u32 level) +{ + switch(level){ + case RESUME_POWER_ON: + printk(KERN_INFO "ucb1x00: ucb1x00 touchscreen resume\n"); + struct ucb1x00_ts *ts = &ucbts; + + if (ts->rtask != NULL) { + /* + * Restart the TS thread to ensure the + * TS interrupt mode is set up again + * after sleep. + */ + ts->restart = 1; + up(&ts->irq_wait); + } + break; + } + + return 0; +} + +static struct device_driver ucb1x00_ts_driver = { + .name = "ucb1x00-ts", + .probe = ucb1x00_ts_probe, + .remove = ucb1x00_ts_remove, + .bus = &platform_bus_type, + .suspend = ucb1x00_ts_suspend, + .resume = ucb1x00_ts_resume, +}; + +static struct platform_device *ucb1x00_ts_device = NULL; + +static int __devinit ucb1x00_ts_init(void) +{ + ucb1x00_ts_device = platform_device_register_simple("ucb1x00-ts", -1, NULL, 0); + return driver_register(&ucb1x00_ts_driver); +} + +static void __exit ucb1x00_ts_exit(void) +{ + driver_unregister(&ucb1x00_ts_driver); + platform_device_unregister(ucb1x00_ts_device); +} + +#ifndef MODULE + +/* + * Parse kernel command-line options. + * + * syntax : ucbts=[sync|nosync],... + */ +static int __init ucb1x00_ts_setup(char *str) +{ + char *p; + + while ((p = strsep(&str, ",")) != NULL) { + if (strcmp(p, "sync") == 0) + adcsync = UCB_SYNC; + } + + return 1; +} + +__setup("ucbts=", ucb1x00_ts_setup); + +#else + +MODULE_PARM(adcsync, "i"); +MODULE_PARM_DESC(adcsync, "Enable use of ADCSYNC signal"); + +#endif + + +module_init(ucb1x00_ts_init); +module_exit(ucb1x00_ts_exit); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("UCB1x00 touchscreen driver"); +MODULE_LICENSE("GPL");