1
0
mirror of git://projects.qi-hardware.com/openwrt-xburst.git synced 2024-11-28 14:57:31 +02:00

Add 2.6.31 patches and files.

This commit is contained in:
Lars-Peter Clausen 2009-08-20 11:38:17 +02:00 committed by Xiangfu Liu
parent f58d88c0f1
commit 7d446f792e
82 changed files with 27649 additions and 103429 deletions

View File

@ -0,0 +1,422 @@
CONFIG_32BIT=y
# CONFIG_64BIT is not set
# CONFIG_ALCHEMY_GPIO_INDIRECT is not set
# CONFIG_AR7 is not set
# CONFIG_ARCH_HAS_ILOG2_U32 is not set
# CONFIG_ARCH_HAS_ILOG2_U64 is not set
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
CONFIG_ARCH_POPULATES_NODE_MAP=y
CONFIG_ARCH_REQUIRE_GPIOLIB=y
# CONFIG_ARCH_SUPPORTS_MSI is not set
CONFIG_ARCH_SUPPORTS_OPROFILE=y
CONFIG_ARCH_SUSPEND_POSSIBLE=y
# CONFIG_ARPD is not set
# CONFIG_BACKTRACE_SELF_TEST is not set
CONFIG_BASE_SMALL=0
# CONFIG_BCM47XX is not set
# CONFIG_BINARY_PRINTF is not set
CONFIG_BITREVERSE=y
# CONFIG_BLK_DEV_INITRD is not set
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_COUNT=2
CONFIG_BLK_DEV_RAM_SIZE=4096
CONFIG_BLK_DEV_SD=y
# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set
CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0
# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
# CONFIG_BOOT_PRINTK_DELAY is not set
# CONFIG_BRIDGE is not set
# CONFIG_BSD_PROCESS_ACCT is not set
# CONFIG_CAVIUM_OCTEON_REFERENCE_BOARD is not set
# CONFIG_CAVIUM_OCTEON_SIMULATOR is not set
# CONFIG_CFG80211_DEBUGFS is not set
# CONFIG_CGROUP_SCHED is not set
CONFIG_CMDLINE="mem=32M console=ttyS0,57600n8 rootfstype=ext2 root=/dev/mmcblk0p1 rw rootdelay=2"
CONFIG_CONSOLE_TRANSLATIONS=y
CONFIG_CONSTRUCTORS=y
# CONFIG_CPU_BIG_ENDIAN is not set
# CONFIG_CPU_CAVIUM_OCTEON is not set
CONFIG_CPU_FREQ=y
# CONFIG_CPU_FREQ_DEBUG is not set
# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set
CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_JZ=y
CONFIG_CPU_FREQ_STAT=y
# CONFIG_CPU_FREQ_STAT_DETAILS is not set
CONFIG_CPU_FREQ_TABLE=y
CONFIG_CPU_HAS_LLSC=y
CONFIG_CPU_HAS_PREFETCH=y
CONFIG_CPU_HAS_SYNC=y
CONFIG_CPU_LITTLE_ENDIAN=y
# CONFIG_CPU_LOONGSON2 is not set
CONFIG_CPU_MIPS32=y
CONFIG_CPU_MIPS32_R1=y
# CONFIG_CPU_MIPS32_R2 is not set
# CONFIG_CPU_MIPS64_R1 is not set
# CONFIG_CPU_MIPS64_R2 is not set
CONFIG_CPU_MIPSR1=y
# CONFIG_CPU_NEVADA is not set
# CONFIG_CPU_R10000 is not set
# CONFIG_CPU_R3000 is not set
# CONFIG_CPU_R4300 is not set
# CONFIG_CPU_R4X00 is not set
# CONFIG_CPU_R5000 is not set
# CONFIG_CPU_R5432 is not set
# CONFIG_CPU_R5500 is not set
# CONFIG_CPU_R6000 is not set
# CONFIG_CPU_R8000 is not set
# CONFIG_CPU_RM7000 is not set
# CONFIG_CPU_RM9000 is not set
# CONFIG_CPU_SB1 is not set
CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y
CONFIG_CPU_SUPPORTS_HIGHMEM=y
# CONFIG_CPU_TX39XX is not set
# CONFIG_CPU_TX49XX is not set
# CONFIG_CPU_VR41XX is not set
CONFIG_CRC16=y
CONFIG_CRYPTO_AEAD2=y
CONFIG_CRYPTO_BLKCIPHER=y
CONFIG_CRYPTO_BLKCIPHER2=y
CONFIG_CRYPTO_CBC=y
CONFIG_CRYPTO_DEFLATE=y
CONFIG_CRYPTO_DES=y
CONFIG_CRYPTO_HASH=y
CONFIG_CRYPTO_HASH2=y
CONFIG_CRYPTO_HW=y
CONFIG_CRYPTO_LZO=y
CONFIG_CRYPTO_MANAGER=y
CONFIG_CRYPTO_MANAGER2=y
CONFIG_CRYPTO_MD5=y
CONFIG_CRYPTO_RNG2=y
CONFIG_CRYPTO_WORKQUEUE=y
# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
# CONFIG_DEBUG_DEVRES is not set
# CONFIG_DEBUG_DRIVER is not set
# CONFIG_DEBUG_GPIO is not set
# CONFIG_DEBUG_INFO is not set
CONFIG_DEBUG_KERNEL=y
# CONFIG_DEBUG_KOBJECT is not set
# CONFIG_DEBUG_LIST is not set
# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
# CONFIG_DEBUG_LOCK_ALLOC is not set
# CONFIG_DEBUG_MUTEXES is not set
# CONFIG_DEBUG_NOTIFIERS is not set
# CONFIG_DEBUG_OBJECTS is not set
CONFIG_DEBUG_PREEMPT=y
# CONFIG_DEBUG_RT_MUTEXES is not set
# CONFIG_DEBUG_SG is not set
# CONFIG_DEBUG_SHIRQ is not set
# CONFIG_DEBUG_SLAB is not set
# CONFIG_DEBUG_SPINLOCK is not set
# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
# CONFIG_DEBUG_STACK_USAGE is not set
# CONFIG_DEBUG_VM is not set
# CONFIG_DEBUG_WRITECOUNT is not set
CONFIG_DEFAULT_AS=y
# CONFIG_DEFAULT_DEADLINE is not set
CONFIG_DEFAULT_IOSCHED="anticipatory"
CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
CONFIG_DEFAULT_TCP_CONG="cubic"
CONFIG_DETECT_HUNG_TASK=y
CONFIG_DETECT_SOFTLOCKUP=y
CONFIG_DMA_NEED_PCI_MAP_STATE=y
CONFIG_DMA_NONCOHERENT=y
CONFIG_DNOTIFY=y
CONFIG_DUMMY_CONSOLE=y
CONFIG_EARLY_PRINTK=y
CONFIG_ELF_CORE=y
CONFIG_ENABLE_MUST_CHECK=y
CONFIG_EXT2_FS=y
# CONFIG_EXT2_FS_POSIX_ACL is not set
# CONFIG_EXT2_FS_SECURITY is not set
CONFIG_EXT2_FS_XATTR=y
CONFIG_FAT_FS=y
# CONFIG_FAULT_INJECTION is not set
CONFIG_FB=y
# CONFIG_FB_BACKLIGHT is not set
# CONFIG_FB_BOOT_VESA_SUPPORT is not set
# CONFIG_FB_BROADSHEET is not set
CONFIG_FB_CFB_COPYAREA=y
CONFIG_FB_CFB_FILLRECT=y
CONFIG_FB_CFB_IMAGEBLIT=y
# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
# CONFIG_FB_DDC is not set
# CONFIG_FB_FOREIGN_ENDIAN is not set
# CONFIG_FB_JZ4740_SLCD is not set
CONFIG_FB_JZLCD_4730_4740=y
CONFIG_FB_JZSOC=y
# CONFIG_FB_MACMODES is not set
# CONFIG_FB_MB862XX is not set
# CONFIG_FB_METRONOME is not set
# CONFIG_FB_MODE_HELPERS is not set
# CONFIG_FB_S1D13XXX is not set
# CONFIG_FB_SVGALIB is not set
# CONFIG_FB_SYS_COPYAREA is not set
# CONFIG_FB_SYS_FILLRECT is not set
# CONFIG_FB_SYS_FOPS is not set
# CONFIG_FB_SYS_IMAGEBLIT is not set
# CONFIG_FB_TILEBLITTING is not set
# CONFIG_FB_VIRTUAL is not set
# CONFIG_FIRMWARE_EDID is not set
CONFIG_FONTS=y
# CONFIG_FONT_10x18 is not set
# CONFIG_FONT_6x11 is not set
# CONFIG_FONT_7x14 is not set
CONFIG_FONT_8x16=y
CONFIG_FONT_8x8=y
# CONFIG_FONT_ACORN_8x8 is not set
# CONFIG_FONT_MINI_4x6 is not set
# CONFIG_FONT_PEARL_8x8 is not set
# CONFIG_FONT_SUN12x22 is not set
# CONFIG_FONT_SUN8x16 is not set
CONFIG_FORCE_MAX_ZONEORDER=12
CONFIG_FRAMEBUFFER_CONSOLE=y
# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
CONFIG_FSNOTIFY=y
CONFIG_FS_MBCACHE=y
CONFIG_FS_POSIX_ACL=y
# CONFIG_FW_LOADER is not set
# CONFIG_GCOV_KERNEL is not set
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
CONFIG_GENERIC_CMOS_UPDATE=y
CONFIG_GENERIC_FIND_LAST_BIT=y
CONFIG_GENERIC_FIND_NEXT_BIT=y
CONFIG_GENERIC_GPIO=y
CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
CONFIG_GPIOLIB=y
CONFIG_GPIO_SYSFS=y
CONFIG_GROUP_SCHED=y
# CONFIG_HAMRADIO is not set
CONFIG_HARDWARE_WATCHPOINTS=y
CONFIG_HAS_DMA=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
CONFIG_HAVE_ARCH_KGDB=y
# CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
CONFIG_HAVE_IDE=y
CONFIG_HAVE_MLOCK=y
CONFIG_HAVE_MLOCKED_PAGE_BIT=y
CONFIG_HAVE_OPROFILE=y
CONFIG_HID=y
CONFIG_HID_SUPPORT=y
# CONFIG_HIGH_RES_TIMERS is not set
CONFIG_HW_CONSOLE=y
# CONFIG_HW_RANDOM is not set
# CONFIG_I2C is not set
# CONFIG_IEEE802154 is not set
CONFIG_INET_DIAG=m
CONFIG_INET_TCP_DIAG=m
CONFIG_INOTIFY=y
CONFIG_INOTIFY_USER=y
CONFIG_INPUT=y
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_KEYBOARD=y
# CONFIG_INPUT_MISC is not set
CONFIG_IOSCHED_AS=y
CONFIG_IOSCHED_CFQ=y
# CONFIG_IP_ADVANCED_ROUTER is not set
# CONFIG_IP_MULTICAST is not set
CONFIG_IP_PNP=y
CONFIG_IP_PNP_BOOTP=y
CONFIG_IP_PNP_DHCP=y
# CONFIG_IP_PNP_RARP is not set
# CONFIG_ISDN is not set
CONFIG_JFFS2_FS_DEBUG=1
CONFIG_JZ4740_QI_LB60=y
# CONFIG_JZLCD_AUO_A030FL01_V1 is not set
# CONFIG_JZLCD_CSTN_320x240 is not set
# CONFIG_JZLCD_CSTN_800x600 is not set
CONFIG_JZLCD_FOXCONN_PT035TN01=y
# CONFIG_JZLCD_HYNIX_HT10X21 is not set
# CONFIG_JZLCD_INNOLUX_AT080TN42 is not set
# CONFIG_JZLCD_INNOLUX_PT035TN01_SERIAL is not set
# CONFIG_JZLCD_MSTN_240x128 is not set
# CONFIG_JZLCD_MSTN_320x240 is not set
# CONFIG_JZLCD_MSTN_480x320 is not set
# CONFIG_JZLCD_SAMSUNG_LTP400WQF01 is not set
# CONFIG_JZLCD_SAMSUNG_LTP400WQF02 is not set
# CONFIG_JZLCD_SAMSUNG_LTS350Q1 is not set
# CONFIG_JZLCD_SAMSUNG_LTV350QVF04 is not set
# CONFIG_JZLCD_SHARP_LQ035Q7 is not set
# CONFIG_JZLCD_TOSHIBA_LTM084P363 is not set
# CONFIG_JZLCD_TRULY_TFTG240320UTSW_63W_E is not set
# CONFIG_JZLCD_TRULY_TFTG320240DTSW is not set
# CONFIG_JZLCD_TRULY_TFTG320240DTSW_SERIAL is not set
CONFIG_JZRISC=y
CONFIG_JZSOC=y
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
# CONFIG_KEYBOARD_ATKBD is not set
# CONFIG_KEYBOARD_GPIO is not set
# CONFIG_KEYBOARD_LKKBD is not set
CONFIG_KEYBOARD_MATRIX=y
# CONFIG_KEYBOARD_NEWTON is not set
# CONFIG_KEYBOARD_STOWAWAY is not set
# CONFIG_KEYBOARD_SUNKBD is not set
# CONFIG_KEYBOARD_XTKBD is not set
# CONFIG_KGDB is not set
# CONFIG_KMEMCHECK is not set
# CONFIG_LBDAF is not set
CONFIG_LEGACY_PTYS=y
CONFIG_LEGACY_PTY_COUNT=2
# CONFIG_LEMOTE_FULONG is not set
CONFIG_LOCALVERSION_AUTO=y
CONFIG_LOCKD=y
CONFIG_LOCK_KERNEL=y
# CONFIG_LOCK_STAT is not set
CONFIG_LOGO=y
CONFIG_LOGO_LINUX_CLUT224=y
CONFIG_LOGO_LINUX_MONO=y
CONFIG_LOGO_LINUX_VGA16=y
CONFIG_LZO_COMPRESS=y
CONFIG_LZO_DECOMPRESS=y
# CONFIG_MAC80211_DEFAULT_PS is not set
CONFIG_MAC80211_DEFAULT_PS_VALUE=0
# CONFIG_MACH_ALCHEMY is not set
# CONFIG_MACH_DECSTATION is not set
# CONFIG_MACH_JAZZ is not set
# CONFIG_MACH_TX39XX is not set
# CONFIG_MACH_TX49XX is not set
# CONFIG_MACH_VR41XX is not set
CONFIG_MAGIC_SYSRQ=y
# CONFIG_MEDIA_SUPPORT is not set
# CONFIG_MIKROTIK_RB532 is not set
CONFIG_MIPS=y
# CONFIG_MIPS_COBALT is not set
# CONFIG_MIPS_FPU_EMU is not set
CONFIG_MIPS_L1_CACHE_SHIFT=5
# CONFIG_MIPS_MACHINE is not set
# CONFIG_MIPS_MALTA is not set
CONFIG_MIPS_MT_DISABLED=y
# CONFIG_MIPS_MT_SMP is not set
# CONFIG_MIPS_MT_SMTC is not set
# CONFIG_MIPS_SIM is not set
CONFIG_MMC=y
CONFIG_MMC_BLOCK=y
CONFIG_MMC_BLOCK_BOUNCE=y
# CONFIG_MMC_DEBUG is not set
# CONFIG_MMC_JZ is not set
# CONFIG_MMC_SDHCI is not set
# CONFIG_MMC_UNSAFE_RESUME is not set
CONFIG_MODULE_SRCVERSION_ALL=y
CONFIG_MODVERSIONS=y
CONFIG_MSDOS_FS=y
CONFIG_MTD_CONCAT=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_JZ4740=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_UBI=y
CONFIG_MTD_UBI_BEB_RESERVE=1
# CONFIG_MTD_UBI_DEBUG is not set
# CONFIG_MTD_UBI_GLUEBI is not set
CONFIG_MTD_UBI_WL_THRESHOLD=4096
# CONFIG_NETDEVICES is not set
# CONFIG_NETFILTER is not set
# CONFIG_NET_SCHED is not set
# CONFIG_NEW_LEDS is not set
CONFIG_NFS_ACL_SUPPORT=y
CONFIG_NFS_FS=y
CONFIG_NFS_V3_ACL=y
# CONFIG_NFS_V4_1 is not set
CONFIG_NLS=y
CONFIG_NLS_ASCII=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_CODEPAGE_936=y
CONFIG_NLS_ISO8859_1=y
# CONFIG_NO_IOPORT is not set
# CONFIG_NXP_STB220 is not set
# CONFIG_NXP_STB225 is not set
# CONFIG_PACKET_MMAP is not set
CONFIG_PAGEFLAGS_EXTENDED=y
# CONFIG_PAGE_POISONING is not set
CONFIG_PCSPKR_PLATFORM=y
# CONFIG_PMC_MSP is not set
# CONFIG_PMC_YOSEMITE is not set
# CONFIG_PNX8550_JBS is not set
# CONFIG_PNX8550_STB810 is not set
# CONFIG_PPS is not set
CONFIG_PREEMPT=y
# CONFIG_PREEMPT_NONE is not set
# CONFIG_PROM_EMU is not set
# CONFIG_PROVE_LOCKING is not set
# CONFIG_RCU_TORTURE_TEST is not set
CONFIG_RELAY=y
CONFIG_ROOT_NFS=y
CONFIG_RPCSEC_GSS_KRB5=y
# CONFIG_RT_GROUP_SCHED is not set
# CONFIG_RT_MUTEX_TESTER is not set
# CONFIG_RUNTIME_DEBUG is not set
# CONFIG_SCHEDSTATS is not set
CONFIG_SCHED_DEBUG=y
CONFIG_SCHED_OMIT_FRAME_POINTER=y
CONFIG_SCSI=y
# CONFIG_SCSI_MULTI_LUN is not set
# CONFIG_SDIO_UART is not set
CONFIG_SECCOMP=y
# CONFIG_SERIAL_8250_EXTENDED is not set
CONFIG_SERIAL_8250_NR_UARTS=1
CONFIG_SERIAL_8250_RUNTIME_UARTS=1
CONFIG_SERIO=y
# CONFIG_SERIO_I8042 is not set
CONFIG_SERIO_LIBPS2=y
# CONFIG_SERIO_RAW is not set
CONFIG_SERIO_SERPORT=y
# CONFIG_SGI_IP22 is not set
# CONFIG_SGI_IP27 is not set
# CONFIG_SGI_IP28 is not set
# CONFIG_SGI_IP32 is not set
# CONFIG_SIBYTE_BIGSUR is not set
# CONFIG_SIBYTE_CARMEL is not set
# CONFIG_SIBYTE_CRHINE is not set
# CONFIG_SIBYTE_CRHONE is not set
# CONFIG_SIBYTE_LITTLESUR is not set
# CONFIG_SIBYTE_RHONE is not set
# CONFIG_SIBYTE_SENTOSA is not set
# CONFIG_SIBYTE_SWARM is not set
# CONFIG_SLOW_WORK is not set
CONFIG_SOC_JZ4740=y
CONFIG_SOUND=y
CONFIG_SOUND_OSS_CORE=y
CONFIG_SOUND_PRIME=y
CONFIG_SUNRPC=y
CONFIG_SUNRPC_GSS=y
# CONFIG_SYN_COOKIES is not set
CONFIG_SYSFS_DEPRECATED=y
CONFIG_SYSFS_DEPRECATED_V2=y
CONFIG_SYS_HAS_CPU_MIPS32_R1=y
CONFIG_SYS_HAS_EARLY_PRINTK=y
CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y
CONFIG_SYS_SUPPORTS_ARBIT_HZ=y
CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y
# CONFIG_TCP_CONG_ADVANCED is not set
CONFIG_TCP_CONG_CUBIC=y
# CONFIG_TIMER_STATS is not set
CONFIG_TRACING_SUPPORT=y
CONFIG_TRAD_SIGNALS=y
CONFIG_UBIFS_FS=y
# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set
# CONFIG_UBIFS_FS_DEBUG is not set
CONFIG_UBIFS_FS_LZO=y
# CONFIG_UBIFS_FS_XATTR is not set
CONFIG_UBIFS_FS_ZLIB=y
CONFIG_USER_SCHED=y
CONFIG_VFAT_FS=y
# CONFIG_VGA_CONSOLE is not set
# CONFIG_VLAN_8021Q is not set
CONFIG_VM_EVENT_COUNTERS=y
CONFIG_VT=y
CONFIG_VT_CONSOLE=y
CONFIG_VT_HW_CONSOLE_BINDING=y
CONFIG_ZONE_DMA_FLAG=0

View File

@ -0,0 +1,42 @@
#
# linux/arch/mips/boot/compressed/Makefile
#
# create a compressed zImage from the original vmlinux
#
targets := zImage vmlinuz vmlinux.bin.gz head.o misc.o piggy.o dummy.o
OBJS := $(obj)/head.o $(obj)/misc.o
LD_ARGS := -T $(obj)/ld.script -Ttext 0x80600000 -Bstatic
OBJCOPY_ARGS := -O elf32-tradlittlemips
ENTRY := $(obj)/../tools/entry
FILESIZE := $(obj)/../tools/filesize
drop-sections = .reginfo .mdebug .comment .note .pdr .options .MIPS.options
strip-flags = $(addprefix --remove-section=,$(drop-sections))
$(obj)/vmlinux.bin.gz: vmlinux
rm -f $(obj)/vmlinux.bin.gz
$(OBJCOPY) -O binary $(strip-flags) vmlinux $(obj)/vmlinux.bin
gzip -v9f $(obj)/vmlinux.bin
$(obj)/head.o: $(obj)/head.S $(obj)/vmlinux.bin.gz vmlinux
$(CC) $(KBUILD_AFLAGS) \
-DIMAGESIZE=$(shell sh $(FILESIZE) $(obj)/vmlinux.bin.gz) \
-DKERNEL_ENTRY=$(shell sh $(ENTRY) $(NM) vmlinux ) \
-DLOADADDR=$(loadaddr) \
-c -o $(obj)/head.o $<
$(obj)/vmlinuz: $(OBJS) $(obj)/ld.script $(obj)/vmlinux.bin.gz $(obj)/dummy.o
$(OBJCOPY) \
--add-section=.image=$(obj)/vmlinux.bin.gz \
--set-section-flags=.image=contents,alloc,load,readonly,data \
$(obj)/dummy.o $(obj)/piggy.o
$(LD) $(LD_ARGS) -o $@ $(OBJS) $(obj)/piggy.o
$(OBJCOPY) $(OBJCOPY_ARGS) $@ $@ -R .comment -R .stab -R .stabstr -R .initrd -R .sysmap
zImage: $(obj)/vmlinuz
$(OBJCOPY) -O binary $(obj)/vmlinuz $(obj)/zImage

View File

@ -0,0 +1,4 @@
int main(void)
{
return 0;
}

View File

@ -0,0 +1,85 @@
/*
* linux/arch/mips/boot/compressed/head.S
*
* Copyright (C) 2005-2008 Ingenic Semiconductor Inc.
*/
#include <asm/asm.h>
#include <asm/cacheops.h>
#include <asm/cachectl.h>
#include <asm/regdef.h>
#define IndexInvalidate_I 0x00
#define IndexWriteBack_D 0x01
.set noreorder
LEAF(startup)
startup:
move s0, a0 /* Save the boot loader transfered args */
move s1, a1
move s2, a2
move s3, a3
la a0, _edata
la a1, _end
1: sw zero, 0(a0) /* Clear BSS section */
bne a1, a0, 1b
addu a0, 4
la sp, (.stack + 8192)
la a0, __image_begin
la a1, IMAGESIZE
la a2, LOADADDR
la ra, 1f
la k0, decompress_kernel
jr k0
nop
1:
move a0, s0
move a1, s1
move a2, s2
move a3, s3
li k0, KERNEL_ENTRY
jr k0
nop
2:
b 32
END(startup)
LEAF(flushcaches)
la t0, 1f
la t1, 0xa0000000
or t0, t0, t1
jr t0
nop
1:
li k0, 0x80000000 # start address
li k1, 0x80004000 # end address (16KB I-Cache)
subu k1, 128
2:
.set mips3
cache IndexWriteBack_D, 0(k0)
cache IndexWriteBack_D, 32(k0)
cache IndexWriteBack_D, 64(k0)
cache IndexWriteBack_D, 96(k0)
cache IndexInvalidate_I, 0(k0)
cache IndexInvalidate_I, 32(k0)
cache IndexInvalidate_I, 64(k0)
cache IndexInvalidate_I, 96(k0)
.set mips0
bne k0, k1, 2b
addu k0, k0, 128
la t0, 3f
jr t0
nop
3:
jr ra
nop
END(flushcaches)
.comm .stack,4096*2,4

View File

@ -0,0 +1,151 @@
OUTPUT_ARCH(mips)
ENTRY(startup)
SECTIONS
{
/* Read-only sections, merged into text segment: */
.init : { *(.init) } =0
.text :
{
_ftext = . ;
*(.text)
*(.rodata)
*(.rodata1)
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
} =0
.kstrtab : { *(.kstrtab) }
. = ALIGN(16); /* Exception table */
__start___ex_table = .;
__ex_table : { *(__ex_table) }
__stop___ex_table = .;
__start___dbe_table = .; /* Exception table for data bus errors */
__dbe_table : { *(__dbe_table) }
__stop___dbe_table = .;
__start___ksymtab = .; /* Kernel symbol table */
__ksymtab : { *(__ksymtab) }
__stop___ksymtab = .;
_etext = .;
. = ALIGN(8192);
.data.init_task : { *(.data.init_task) }
/* Startup code */
. = ALIGN(4096);
__init_begin = .;
.text.init : { *(.text.init) }
.data.init : { *(.data.init) }
. = ALIGN(16);
__setup_start = .;
.setup.init : { *(.setup.init) }
__setup_end = .;
__initcall_start = .;
.initcall.init : { *(.initcall.init) }
__initcall_end = .;
. = ALIGN(4096); /* Align double page for init_task_union */
__init_end = .;
. = ALIGN(4096);
.data.page_aligned : { *(.data.idt) }
. = ALIGN(32);
.data.cacheline_aligned : { *(.data.cacheline_aligned) }
.fini : { *(.fini) } =0
.reginfo : { *(.reginfo) }
/* Adjust the address for the data segment. We want to adjust up to
the same address within the page on the next page up. It would
be more correct to do this:
. = .;
The current expression does not correctly handle the case of a
text segment ending precisely at the end of a page; it causes the
data segment to skip a page. The above expression does not have
this problem, but it will currently (2/95) cause BFD to allocate
a single segment, combining both text and data, for this case.
This will prevent the text segment from being shared among
multiple executions of the program; I think that is more
important than losing a page of the virtual address space (note
that no actual memory is lost; the page which is skipped can not
be referenced). */
. = .;
.data :
{
_fdata = . ;
*(.data)
/* Put the compressed image here, so bss is on the end. */
__image_begin = .;
*(.image)
__image_end = .;
/* Align the initial ramdisk image (INITRD) on page boundaries. */
. = ALIGN(4096);
__ramdisk_begin = .;
*(.initrd)
__ramdisk_end = .;
. = ALIGN(4096);
CONSTRUCTORS
}
.data1 : { *(.data1) }
_gp = . + 0x8000;
.lit8 : { *(.lit8) }
.lit4 : { *(.lit4) }
.ctors : { *(.ctors) }
.dtors : { *(.dtors) }
.got : { *(.got.plt) *(.got) }
.dynamic : { *(.dynamic) }
/* We want the small data sections together, so single-instruction offsets
can access them all, and initialized data all before uninitialized, so
we can shorten the on-disk segment size. */
.sdata : { *(.sdata) }
. = ALIGN(4);
_edata = .;
PROVIDE (edata = .);
__bss_start = .;
_fbss = .;
.sbss : { *(.sbss) *(.scommon) }
.bss :
{
*(.dynbss)
*(.bss)
*(COMMON)
. = ALIGN(4);
_end = . ;
PROVIDE (end = .);
}
/* Sections to be discarded */
/DISCARD/ :
{
*(.text.exit)
*(.data.exit)
*(.exitcall.exit)
}
/* This is the MIPS specific mdebug section. */
.mdebug : { *(.mdebug) }
/* These are needed for ELF backends which have not yet been
converted to the new style linker. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
/* DWARF debug sections.
Symbols in the .debug DWARF section are relative to the beginning of the
section so we begin .debug at 0. It's not clear yet what needs to happen
for the others. */
.debug 0 : { *(.debug) }
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
.debug_sfnames 0 : { *(.debug_sfnames) }
.line 0 : { *(.line) }
/* These must appear regardless of . */
.gptab.sdata : { *(.gptab.data) *(.gptab.sdata) }
.gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) }
.comment : { *(.comment) }
.note : { *(.note) }
}

View File

@ -0,0 +1,242 @@
/*
* linux/arch/mips/boot/compressed/misc.c
*
* This is a collection of several routines from gzip-1.0.3
* adapted for Linux.
*
* malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
*
* Adapted for JZSOC by Peter Wei, 2008
*
*/
#define size_t int
#define NULL 0
/*
* gzip declarations
*/
#define OF(args) args
#define STATIC static
#undef memset
#undef memcpy
#define memzero(s, n) memset ((s), 0, (n))
typedef unsigned char uch;
typedef unsigned short ush;
typedef unsigned long ulg;
#define WSIZE 0x8000 /* Window size must be at least 32k, */
/* and a power of two */
static uch *inbuf; /* input buffer */
static uch window[WSIZE]; /* Sliding window buffer */
static unsigned insize = 0; /* valid bytes in inbuf */
static unsigned inptr = 0; /* index of next byte to be processed in inbuf */
static unsigned outcnt = 0; /* bytes in output buffer */
/* gzip flag byte */
#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */
#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
#define COMMENT 0x10 /* bit 4 set: file comment present */
#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */
#define RESERVED 0xC0 /* bit 6,7: reserved */
#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf())
/* Diagnostic functions */
#ifdef DEBUG
# define Assert(cond,msg) {if(!(cond)) error(msg);}
# define Trace(x) fprintf x
# define Tracev(x) {if (verbose) fprintf x ;}
# define Tracevv(x) {if (verbose>1) fprintf x ;}
# define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
#else
# define Assert(cond,msg)
# define Trace(x)
# define Tracev(x)
# define Tracevv(x)
# define Tracec(c,x)
# define Tracecv(c,x)
#endif
static int fill_inbuf(void);
static void flush_window(void);
static void error(char *m);
static void gzip_mark(void **);
static void gzip_release(void **);
void* memset(void* s, int c, size_t n);
void* memcpy(void* __dest, __const void* __src, size_t __n);
extern void flushcaches(void); /* defined in head.S */
char *input_data;
int input_len;
static long bytes_out = 0;
static uch *output_data;
static unsigned long output_ptr = 0;
static void *malloc(int size);
static void free(void *where);
static void error(char *m);
static void gzip_mark(void **);
static void gzip_release(void **);
static void puts(const char *str)
{
}
extern unsigned char _end[];
static unsigned long free_mem_ptr;
static unsigned long free_mem_end_ptr;
#define HEAP_SIZE 0x10000
#include "../../../../lib/inflate.c"
static void *malloc(int size)
{
void *p;
if (size <0) error("Malloc error\n");
if (free_mem_ptr == 0) error("Memory error\n");
free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */
p = (void *)free_mem_ptr;
free_mem_ptr += size;
if (free_mem_ptr >= free_mem_end_ptr)
error("\nOut of memory\n");
return p;
}
static void free(void *where)
{ /* Don't care */
}
static void gzip_mark(void **ptr)
{
*ptr = (void *) free_mem_ptr;
}
static void gzip_release(void **ptr)
{
free_mem_ptr = (long) *ptr;
}
void* memset(void* s, int c, size_t n)
{
int i;
char *ss = (char*)s;
for (i=0;i<n;i++) ss[i] = c;
return s;
}
void* memcpy(void* __dest, __const void* __src, size_t __n)
{
int i = 0;
unsigned char *d = (unsigned char *)__dest, *s = (unsigned char *)__src;
for (i = __n >> 3; i > 0; i--) {
*d++ = *s++;
*d++ = *s++;
*d++ = *s++;
*d++ = *s++;
*d++ = *s++;
*d++ = *s++;
*d++ = *s++;
*d++ = *s++;
}
if (__n & 1 << 2) {
*d++ = *s++;
*d++ = *s++;
*d++ = *s++;
*d++ = *s++;
}
if (__n & 1 << 1) {
*d++ = *s++;
*d++ = *s++;
}
if (__n & 1)
*d++ = *s++;
return __dest;
}
/* ===========================================================================
* Fill the input buffer. This is called only when the buffer is empty
* and at least one byte is really needed.
*/
static int fill_inbuf(void)
{
if (insize != 0) {
error("ran out of input data\n");
}
inbuf = input_data;
insize = input_len;
inptr = 1;
return inbuf[0];
}
/* ===========================================================================
* Write the output window window[0..outcnt-1] and update crc and bytes_out.
* (Used for the decompressed data only.)
*/
static void flush_window(void)
{
ulg c = crc; /* temporary variable */
unsigned n;
uch *in, *out, ch;
in = window;
out = &output_data[output_ptr];
for (n = 0; n < outcnt; n++) {
ch = *out++ = *in++;
c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8);
}
crc = c;
bytes_out += (ulg)outcnt;
output_ptr += (ulg)outcnt;
outcnt = 0;
}
static void error(char *x)
{
puts("\n\n");
puts(x);
puts("\n\n -- System halted");
while(1); /* Halt */
}
void decompress_kernel(unsigned int imageaddr, unsigned int imagesize, unsigned int loadaddr)
{
input_data = (char *)imageaddr;
input_len = imagesize;
output_ptr = 0;
output_data = (uch *)loadaddr;
free_mem_ptr = (unsigned long)_end;
free_mem_end_ptr = free_mem_ptr + HEAP_SIZE;
makecrc();
puts("Uncompressing Linux...");
gunzip();
flushcaches();
puts("Ok, booting the kernel.");
}

View File

@ -0,0 +1,12 @@
#!/bin/sh
# grab the kernel_entry address from the vmlinux elf image
entry=`$1 $2 | grep kernel_entry`
fs=`echo $entry | grep ffffffff` # check toolchain output
if [ -n "$fs" ]; then
echo "0x"`$1 $2 | grep kernel_entry | cut -c9- | awk '{print $1}'`
else
echo "0x"`$1 $2 | grep kernel_entry | cut -c1- | awk '{print $1}'`
fi

View File

@ -0,0 +1,7 @@
#!/bin/sh
HOSTNAME=`uname`
if [ "$HOSTNAME" = "Linux" ]; then
echo `ls -l $1 | awk '{print $5}'`
else
echo `ls -l $1 | awk '{print $6}'`
fi

View File

@ -0,0 +1,26 @@
/*
* linux/include/asm-mips/jzsoc.h
*
* Ingenic's JZXXXX SoC common include.
*
* Copyright (C) 2006 - 2008 Ingenic Semiconductor Inc.
*
* Author: <jlwei@ingenic.cn>
*
* 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 __ASM_JZSOC_H__
#define __ASM_JZSOC_H__
/*
* SoC include
*/
#ifdef CONFIG_SOC_JZ4740
#include <asm/mach-jz4740/jz4740.h>
#endif
#endif /* __ASM_JZSOC_H__ */

View File

@ -0,0 +1,69 @@
/*
* linux/include/asm-mips/mach-jz4740/board-dipper.h
*
* JZ4725-based (16bit) Dipper board ver 1.x definition.
*
* Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc.
*
* Author: <lhhuang@ingenic.cn>
*
* 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 __ASM_JZ4725_DIPPER_H__
#define __ASM_JZ4725_DIPPER_H__
/*======================================================================
* Frequencies of on-board oscillators
*/
#define JZ_EXTAL 12000000 /* Main extal freq: 12 MHz */
#define JZ_EXTAL2 32768 /* RTC extal freq: 32.768 KHz */
/*======================================================================
* GPIO JZ4725
*/
#define GPIO_SD_VCC_EN_N 85 /* GPC21 */
#define GPIO_SD_CD_N 91 /* GPC27 */
#define GPIO_SD_WP 112 /* GPD16 */
#define GPIO_USB_DETE 124 /* GPD28 */
#define GPIO_DC_DETE_N 103 /* GPD7 */
#define GPIO_CHARG_STAT_N 86 /* GPC22 */
#define GPIO_DISP_OFF_N 118 /* GPD22 */
#define GPIO_UDC_HOTPLUG GPIO_USB_DETE
/*======================================================================
* MMC/SD
*/
#define MSC_WP_PIN GPIO_SD_WP
#define MSC_HOTPLUG_PIN GPIO_SD_CD_N
#define MSC_HOTPLUG_IRQ (IRQ_GPIO_0 + GPIO_SD_CD_N)
#define __msc_init_io() \
do { \
__gpio_as_output(GPIO_SD_VCC_EN_N); \
__gpio_as_input(GPIO_SD_CD_N); \
} while (0)
#define __msc_enable_power() \
do { \
__gpio_clear_pin(GPIO_SD_VCC_EN_N); \
} while (0)
#define __msc_disable_power() \
do { \
__gpio_set_pin(GPIO_SD_VCC_EN_N); \
} while (0)
#define __msc_card_detected(s) \
({ \
int detected = 1; \
if (__gpio_get_pin(GPIO_SD_CD_N)) \
detected = 0; \
detected; \
})
#endif /* __ASM_JZ4740_DIPPER_H__ */

View File

@ -0,0 +1,56 @@
#ifndef __ASM_JZ4740_LEO_H__
#define __ASM_JZ4740_LEO_H__
/*
* Define your board specific codes here !!!
*/
/*======================================================================
* Frequencies of on-board oscillators
*/
#define JZ_EXTAL 12000000 /* Main extal freq: 12 MHz */
#define JZ_EXTAL2 32768 /* RTC extal freq: 32.768 KHz */
/*======================================================================
* GPIO
*/
#define GPIO_DISP_OFF_N 100
#define GPIO_SD_VCC_EN_N 119
#define GPIO_SD_CD_N 120
#define GPIO_SD_WP 111
/*======================================================================
* MMC/SD
*/
#define MSC_WP_PIN GPIO_SD_WP
#define MSC_HOTPLUG_PIN GPIO_SD_CD_N
#define MSC_HOTPLUG_IRQ (IRQ_GPIO_0 + GPIO_SD_CD_N)
#define __msc_init_io() \
do { \
__gpio_as_output(GPIO_SD_VCC_EN_N); \
__gpio_as_input(GPIO_SD_CD_N); \
} while (0)
#define __msc_enable_power() \
do { \
__gpio_clear_pin(GPIO_SD_VCC_EN_N); \
} while (0)
#define __msc_disable_power() \
do { \
__gpio_set_pin(GPIO_SD_VCC_EN_N); \
} while (0)
#define __msc_card_detected(s) \
({ \
int detected = 1; \
__gpio_as_input(GPIO_SD_CD_N); \
if (__gpio_get_pin(GPIO_SD_CD_N)) \
detected = 0; \
detected; \
})
#endif /* __ASM_JZ4740_BOARD_LEO_H__ */

View File

@ -0,0 +1,70 @@
/*
* linux/include/asm-mips/mach-jz4740/board-lyra.h
*
* JZ4740-based LYRA board ver 2.x definition.
*
* Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc.
*
* Author: <lhhuang@ingenic.cn>
*
* 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 __ASM_JZ4740_LYRA_H__
#define __ASM_JZ4740_LYRA_H__
/*======================================================================
* Frequencies of on-board oscillators
*/
#define JZ_EXTAL 12000000 /* Main extal freq: 12 MHz */
#define JZ_EXTAL2 32768 /* RTC extal freq: 32.768 KHz */
/*======================================================================
* GPIO
*/
#define GPIO_SD_VCC_EN_N 113 /* GPD17 */
#define GPIO_SD_CD_N 110 /* GPD14 */
#define GPIO_SD_WP 112 /* GPD16 */
#define GPIO_USB_DETE 102 /* GPD6 */
#define GPIO_DC_DETE_N 103 /* GPD7 */
#define GPIO_CHARG_STAT_N 111 /* GPD15 */
#define GPIO_DISP_OFF_N 118 /* GPD22 */
#define GPIO_LED_EN 124 /* GPD28 */
#define GPIO_UDC_HOTPLUG GPIO_USB_DETE
/*======================================================================
* MMC/SD
*/
#define MSC_WP_PIN GPIO_SD_WP
#define MSC_HOTPLUG_PIN GPIO_SD_CD_N
#define MSC_HOTPLUG_IRQ (IRQ_GPIO_0 + GPIO_SD_CD_N)
#define __msc_init_io() \
do { \
__gpio_as_output(GPIO_SD_VCC_EN_N); \
__gpio_as_input(GPIO_SD_CD_N); \
} while (0)
#define __msc_enable_power() \
do { \
__gpio_clear_pin(GPIO_SD_VCC_EN_N); \
} while (0)
#define __msc_disable_power() \
do { \
__gpio_set_pin(GPIO_SD_VCC_EN_N); \
} while (0)
#define __msc_card_detected(s) \
({ \
int detected = 1; \
if (!(__gpio_get_pin(GPIO_SD_CD_N))) \
detected = 0; \
detected; \
})
#endif /* __ASM_JZ4740_LYRA_H__ */

View File

@ -0,0 +1,70 @@
/*
* linux/include/asm-mips/mach-jz4740/board-pavo.h
*
* JZ4730-based PAVO board ver 2.x definition.
*
* Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc.
*
* Author: <lhhuang@ingenic.cn>
*
* 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 __ASM_JZ4740_PAVO_H__
#define __ASM_JZ4740_PAVO_H__
/*======================================================================
* Frequencies of on-board oscillators
*/
#define JZ_EXTAL 12000000 /* Main extal freq: 12 MHz */
#define JZ_EXTAL2 32768 /* RTC extal freq: 32.768 KHz */
/*======================================================================
* GPIO
*/
#define GPIO_SD_VCC_EN_N 113 /* GPD17 */
#define GPIO_SD_CD_N 110 /* GPD14 */
#define GPIO_SD_WP 112 /* GPD16 */
#define GPIO_USB_DETE 102 /* GPD6 */
#define GPIO_DC_DETE_N 103 /* GPD7 */
#define GPIO_CHARG_STAT_N 111 /* GPD15 */
#define GPIO_DISP_OFF_N 118 /* GPD22 */
#define GPIO_LED_EN 124 /* GPD28 */
#define GPIO_UDC_HOTPLUG GPIO_USB_DETE
/*======================================================================
* MMC/SD
*/
#define MSC_WP_PIN GPIO_SD_WP
#define MSC_HOTPLUG_PIN GPIO_SD_CD_N
#define MSC_HOTPLUG_IRQ (IRQ_GPIO_0 + GPIO_SD_CD_N)
#define __msc_init_io() \
do { \
__gpio_as_output(GPIO_SD_VCC_EN_N); \
__gpio_as_input(GPIO_SD_CD_N); \
} while (0)
#define __msc_enable_power() \
do { \
__gpio_clear_pin(GPIO_SD_VCC_EN_N); \
} while (0)
#define __msc_disable_power() \
do { \
__gpio_set_pin(GPIO_SD_VCC_EN_N); \
} while (0)
#define __msc_card_detected(s) \
({ \
int detected = 1; \
if (__gpio_get_pin(GPIO_SD_CD_N)) \
detected = 0; \
detected; \
})
#endif /* __ASM_JZ4740_PAVO_H__ */

View File

@ -0,0 +1,67 @@
/*
* linux/include/asm-mips/mach-jz4740/board-virgo.h
*
* JZ4720-based VIRGO board ver 1.x definition.
*
* Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc.
*
* Author: <lhhuang@ingenic.cn>
*
* 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 __ASM_JZ4720_VIRGO_H__
#define __ASM_JZ4720_VIRGO_H__
/*======================================================================
* Frequencies of on-board oscillators
*/
#define JZ_EXTAL 12000000 /* Main extal freq: 12 MHz */
#define JZ_EXTAL2 32768 /* RTC extal freq: 32.768 KHz */
/*======================================================================
* GPIO VIRGO(JZ4720)
*/
#define GPIO_SD_VCC_EN_N 115 /* GPD19 */
#define GPIO_SD_CD_N 116 /* GPD20 */
#define GPIO_USB_DETE 114 /* GPD18 */
#define GPIO_DC_DETE_N 120 /* GPD24 */
#define GPIO_DISP_OFF_N 118 /* GPD22 */
#define GPIO_LED_EN 117 /* GPD21 */
#define GPIO_UDC_HOTPLUG GPIO_USB_DETE
/*======================================================================
* MMC/SD
*/
#define MSC_HOTPLUG_PIN GPIO_SD_CD_N
#define MSC_HOTPLUG_IRQ (IRQ_GPIO_0 + GPIO_SD_CD_N)
#define __msc_init_io() \
do { \
__gpio_as_output(GPIO_SD_VCC_EN_N); \
__gpio_as_input(GPIO_SD_CD_N); \
} while (0)
#define __msc_enable_power() \
do { \
__gpio_clear_pin(GPIO_SD_VCC_EN_N); \
} while (0)
#define __msc_disable_power() \
do { \
__gpio_set_pin(GPIO_SD_VCC_EN_N); \
} while (0)
#define __msc_card_detected(s) \
({ \
int detected = 1; \
if (__gpio_get_pin(GPIO_SD_CD_N)) \
detected = 0; \
detected; \
})
#endif /* __ASM_JZ4720_VIRGO_H__ */

View File

@ -0,0 +1,173 @@
/*
* linux/include/asm-mips/mach-jz4740/clock.h
*
* JZ4740 clocks definition.
*
* Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc.
*
* Author: <lhhuang@ingenic.cn>
*
* 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 __ASM_JZ4740_CLOCK_H__
#define __ASM_JZ4740_CLOCK_H__
#ifndef JZ_EXTAL
//#define JZ_EXTAL 3686400 /* 3.6864 MHz */
#define JZ_EXTAL 12000000 /* 3.6864 MHz */
#endif
#ifndef JZ_EXTAL2
#define JZ_EXTAL2 32768 /* 32.768 KHz */
#endif
/*
* JZ4740 clocks structure
*/
typedef struct {
unsigned int cclk; /* CPU clock */
unsigned int hclk; /* System bus clock */
unsigned int pclk; /* Peripheral bus clock */
unsigned int mclk; /* Flash/SRAM/SDRAM clock */
unsigned int lcdclk; /* LCDC module clock */
unsigned int pixclk; /* LCD pixel clock */
unsigned int i2sclk; /* AIC module clock */
unsigned int usbclk; /* USB module clock */
unsigned int mscclk; /* MSC module clock */
unsigned int extalclk; /* EXTAL clock for UART,I2C,SSI,TCU,USB-PHY */
unsigned int rtcclk; /* RTC clock for CPM,INTC,RTC,TCU,WDT */
} jz_clocks_t;
extern jz_clocks_t jz_clocks;
/* PLL output frequency */
static __inline__ unsigned int __cpm_get_pllout(void)
{
unsigned long m, n, no, pllout;
unsigned long cppcr = REG_CPM_CPPCR;
unsigned long od[4] = {1, 2, 2, 4};
if ((cppcr & CPM_CPPCR_PLLEN) && !(cppcr & CPM_CPPCR_PLLBP)) {
m = __cpm_get_pllm() + 2;
n = __cpm_get_plln() + 2;
no = od[__cpm_get_pllod()];
pllout = ((JZ_EXTAL) / (n * no)) * m;
} else
pllout = JZ_EXTAL;
return pllout;
}
/* PLL output frequency for MSC/I2S/LCD/USB */
static __inline__ unsigned int __cpm_get_pllout2(void)
{
if (REG_CPM_CPCCR & CPM_CPCCR_PCS)
return __cpm_get_pllout();
else
return __cpm_get_pllout()/2;
}
/* CPU core clock */
static __inline__ unsigned int __cpm_get_cclk(void)
{
int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32};
return __cpm_get_pllout() / div[__cpm_get_cdiv()];
}
/* AHB system bus clock */
static __inline__ unsigned int __cpm_get_hclk(void)
{
int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32};
return __cpm_get_pllout() / div[__cpm_get_hdiv()];
}
/* Memory bus clock */
static __inline__ unsigned int __cpm_get_mclk(void)
{
int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32};
return __cpm_get_pllout() / div[__cpm_get_mdiv()];
}
/* APB peripheral bus clock */
static __inline__ unsigned int __cpm_get_pclk(void)
{
int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32};
return __cpm_get_pllout() / div[__cpm_get_pdiv()];
}
/* LCDC module clock */
static __inline__ unsigned int __cpm_get_lcdclk(void)
{
return __cpm_get_pllout2() / (__cpm_get_ldiv() + 1);
}
/* LCD pixel clock */
static __inline__ unsigned int __cpm_get_pixclk(void)
{
return __cpm_get_pllout2() / (__cpm_get_pixdiv() + 1);
}
/* I2S clock */
static __inline__ unsigned int __cpm_get_i2sclk(void)
{
if (REG_CPM_CPCCR & CPM_CPCCR_I2CS) {
return __cpm_get_pllout2() / (__cpm_get_i2sdiv() + 1);
}
else {
return JZ_EXTAL;
}
}
/* USB clock */
static __inline__ unsigned int __cpm_get_usbclk(void)
{
if (REG_CPM_CPCCR & CPM_CPCCR_UCS) {
return __cpm_get_pllout2() / (__cpm_get_udiv() + 1);
}
else {
return JZ_EXTAL;
}
}
/* MSC clock */
static __inline__ unsigned int __cpm_get_mscclk(void)
{
return __cpm_get_pllout2() / (__cpm_get_mscdiv() + 1);
}
/* EXTAL clock for UART,I2C,SSI,TCU,USB-PHY */
static __inline__ unsigned int __cpm_get_extalclk(void)
{
return JZ_EXTAL;
}
/* RTC clock for CPM,INTC,RTC,TCU,WDT */
static __inline__ unsigned int __cpm_get_rtcclk(void)
{
return JZ_EXTAL2;
}
/*
* Output 24MHz for SD and 16MHz for MMC.
*/
static inline void __cpm_select_msc_clk(int sd)
{
unsigned int pllout2 = __cpm_get_pllout2();
unsigned int div = 0;
if (sd) {
div = pllout2 / 24000000;
}
else {
div = pllout2 / 16000000;
}
REG_CPM_MSCCDR = div - 1;
}
#endif /* __ASM_JZ4740_CLOCK_H__ */

View File

@ -0,0 +1,265 @@
/*
* linux/include/asm-mips/mach-jz4740/dma.h
*
* JZ4740 DMA definition.
*
* Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc.
*
* Author: <lhhuang@ingenic.cn>
*
* 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 __ASM_JZ4740_DMA_H__
#define __ASM_JZ4740_DMA_H__
#include <linux/interrupt.h>
#include <asm/io.h> /* need byte IO */
#include <linux/spinlock.h> /* And spinlocks */
#include <linux/delay.h>
#include <asm/system.h>
/*
* Descriptor structure for JZ4740 DMA engine
* Note: this structure must always be aligned to a 16-bytes boundary.
*/
typedef struct {
volatile u32 dcmd; /* DCMD value for the current transfer */
volatile u32 dsadr; /* DSAR value for the current transfer */
volatile u32 dtadr; /* DTAR value for the current transfer */
volatile u32 ddadr; /* Points to the next descriptor + transfer count */
} jz_dma_desc;
/* DMA Device ID's follow */
enum {
DMA_ID_UART0_TX = 0,
DMA_ID_UART0_RX,
DMA_ID_SSI_TX,
DMA_ID_SSI_RX,
DMA_ID_AIC_TX,
DMA_ID_AIC_RX,
DMA_ID_MSC_TX,
DMA_ID_MSC_RX,
DMA_ID_TCU_OVERFLOW,
DMA_ID_AUTO,
DMA_ID_RAW_SET,
DMA_ID_MAX
};
/* DMA modes, simulated by sw */
#define DMA_MODE_READ 0x0 /* I/O to memory, no autoinit, increment, single mode */
#define DMA_MODE_WRITE 0x1 /* memory to I/O, no autoinit, increment, single mode */
#define DMA_AUTOINIT 0x2
#define DMA_MODE_MASK 0x3
struct jz_dma_chan {
int dev_id; /* DMA ID: this channel is allocated if >=0, free otherwise */
unsigned int io; /* DMA channel number */
const char *dev_str; /* string describes the DMA channel */
int irq; /* DMA irq number */
void *irq_dev; /* DMA private device structure */
unsigned int fifo_addr; /* physical fifo address of the requested device */
unsigned int cntl; /* DMA controll */
unsigned int mode; /* DMA configuration */
unsigned int source; /* DMA request source */
};
extern struct jz_dma_chan jz_dma_table[];
#define DMA_8BIT_RX_CMD \
DMAC_DCMD_DAI | \
DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | \
DMAC_DCMD_DS_8BIT | DMAC_DCMD_RDIL_IGN
#define DMA_8BIT_TX_CMD \
DMAC_DCMD_SAI | \
DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | \
DMAC_DCMD_DS_8BIT | DMAC_DCMD_RDIL_IGN
#define DMA_16BIT_RX_CMD \
DMAC_DCMD_DAI | \
DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_32 | \
DMAC_DCMD_DS_16BIT | DMAC_DCMD_RDIL_IGN
#define DMA_16BIT_TX_CMD \
DMAC_DCMD_SAI | \
DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_16 | \
DMAC_DCMD_DS_16BIT | DMAC_DCMD_RDIL_IGN
#define DMA_32BIT_RX_CMD \
DMAC_DCMD_DAI | \
DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | \
DMAC_DCMD_DS_32BIT | DMAC_DCMD_RDIL_IGN
#define DMA_32BIT_TX_CMD \
DMAC_DCMD_SAI | \
DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | \
DMAC_DCMD_DS_32BIT | DMAC_DCMD_RDIL_IGN
#define DMA_16BYTE_RX_CMD \
DMAC_DCMD_DAI | \
DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | \
DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN
#define DMA_16BYTE_TX_CMD \
DMAC_DCMD_SAI | \
DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | \
DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN
#define DMA_32BYTE_RX_CMD \
DMAC_DCMD_DAI | \
DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | \
DMAC_DCMD_DS_32BYTE | DMAC_DCMD_RDIL_IGN
#define DMA_32BYTE_TX_CMD \
DMAC_DCMD_SAI | \
DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | \
DMAC_DCMD_DS_32BYTE | DMAC_DCMD_RDIL_IGN
#define DMA_AIC_32_16BYTE_TX_CMD \
DMAC_DCMD_SAI | \
DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | \
DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN
#define DMA_AIC_32_16BYTE_RX_CMD \
DMAC_DCMD_DAI | \
DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | \
DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN
#define DMA_AIC_16BIT_TX_CMD \
DMAC_DCMD_SAI | \
DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_16 | \
DMAC_DCMD_DS_16BIT | DMAC_DCMD_RDIL_IGN
#define DMA_AIC_16BIT_RX_CMD \
DMAC_DCMD_DAI | \
DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_16 | \
DMAC_DCMD_DS_16BIT | DMAC_DCMD_RDIL_IGN
#define DMA_AIC_16BYTE_RX_CMD \
DMAC_DCMD_DAI | \
DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_16 | \
DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN
#define DMA_AIC_16BYTE_TX_CMD \
DMAC_DCMD_SAI | \
DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_16 | \
DMAC_DCMD_DS_16BYTE | DMAC_DCMD_RDIL_IGN
extern int jz_request_dma(int dev_id,
const char *dev_str,
irqreturn_t (*irqhandler)(int, void *),
unsigned long irqflags,
void *irq_dev_id);
extern void jz_free_dma(unsigned int dmanr);
extern int jz_dma_read_proc(char *buf, char **start, off_t fpos,
int length, int *eof, void *data);
extern void dump_jz_dma_channel(unsigned int dmanr);
extern void enable_dma(unsigned int dmanr);
extern void disable_dma(unsigned int dmanr);
extern void set_dma_addr(unsigned int dmanr, unsigned int phyaddr);
extern void set_dma_count(unsigned int dmanr, unsigned int bytecnt);
extern void set_dma_mode(unsigned int dmanr, unsigned int mode);
extern void jz_set_oss_dma(unsigned int dmanr, unsigned int mode, unsigned int audio_fmt);
extern void jz_set_alsa_dma(unsigned int dmanr, unsigned int mode, unsigned int audio_fmt);
extern unsigned int get_dma_residue(unsigned int dmanr);
extern spinlock_t dma_spin_lock;
static __inline__ unsigned long claim_dma_lock(void)
{
unsigned long flags;
spin_lock_irqsave(&dma_spin_lock, flags);
return flags;
}
static __inline__ void release_dma_lock(unsigned long flags)
{
spin_unlock_irqrestore(&dma_spin_lock, flags);
}
/* Clear the 'DMA Pointer Flip Flop'.
* Write 0 for LSB/MSB, 1 for MSB/LSB access.
*/
#define clear_dma_ff(channel)
static __inline__ struct jz_dma_chan *get_dma_chan(unsigned int dmanr)
{
if (dmanr > MAX_DMA_NUM
|| jz_dma_table[dmanr].dev_id < 0)
return NULL;
return &jz_dma_table[dmanr];
}
static __inline__ int dma_halted(unsigned int dmanr)
{
struct jz_dma_chan *chan = get_dma_chan(dmanr);
if (!chan)
return 1;
return __dmac_channel_transmit_halt_detected(dmanr) ? 1 : 0;
}
static __inline__ unsigned int get_dma_mode(unsigned int dmanr)
{
struct jz_dma_chan *chan = get_dma_chan(dmanr);
if (!chan)
return 0;
return chan->mode;
}
static __inline__ void clear_dma_done(unsigned int dmanr)
{
struct jz_dma_chan *chan = get_dma_chan(dmanr);
if (!chan)
return;
REG_DMAC_DCCSR(chan->io) &= ~(DMAC_DCCSR_HLT | DMAC_DCCSR_TT | DMAC_DCCSR_AR);
}
static __inline__ void clear_dma_halt(unsigned int dmanr)
{
struct jz_dma_chan *chan = get_dma_chan(dmanr);
if (!chan)
return;
REG_DMAC_DCCSR(chan->io) &= ~(DMAC_DCCSR_HLT);
REG_DMAC_DMACR &= ~(DMAC_DMACR_HLT);
}
static __inline__ void clear_dma_flag(unsigned int dmanr)
{
struct jz_dma_chan *chan = get_dma_chan(dmanr);
if (!chan)
return;
REG_DMAC_DCCSR(chan->io) &= ~(DMAC_DCCSR_HLT | DMAC_DCCSR_TT | DMAC_DCCSR_AR);
REG_DMAC_DMACR &= ~(DMAC_DMACR_HLT | DMAC_DMACR_AR);
}
static __inline__ void set_dma_page(unsigned int dmanr, char pagenr)
{
}
static __inline__ unsigned int get_dma_done_status(unsigned int dmanr)
{
unsigned long dccsr;
struct jz_dma_chan *chan = get_dma_chan(dmanr);
if (!chan)
return 0;
dccsr = REG_DMAC_DCCSR(chan->io);
return dccsr & (DMAC_DCCSR_HLT | DMAC_DCCSR_TT | DMAC_DCCSR_AR);
}
static __inline__ int get_dma_done_irq(unsigned int dmanr)
{
struct jz_dma_chan *chan = get_dma_chan(dmanr);
if (!chan)
return -1;
return chan->irq;
}
#endif /* __ASM_JZ4740_DMA_H__ */

View File

@ -0,0 +1,56 @@
/*
* linux/include/asm-mips/mach-jz4740/jz4740.h
*
* JZ4740 common definition.
*
* Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc.
*
* Author: <lhhuang@ingenic.cn>
*
* 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 __ASM_JZ4740_H__
#define __ASM_JZ4740_H__
#include <asm/mach-jz4740/regs.h>
#include <asm/mach-jz4740/ops.h>
#include <asm/mach-jz4740/dma.h>
#include <asm/mach-jz4740/misc.h>
/*------------------------------------------------------------------
* Platform definitions
*/
#ifdef CONFIG_JZ4740_PAVO
#include <asm/mach-jz4740/board-pavo.h>
#endif
#ifdef CONFIG_JZ4740_LEO
#include <asm/mach-jz4740/board-leo.h>
#endif
#ifdef CONFIG_JZ4740_LYRA
#include <asm/mach-jz4740/board-lyra.h>
#endif
#ifdef CONFIG_JZ4725_DIPPER
#include <asm/mach-jz4740/board-dipper.h>
#endif
#ifdef CONFIG_JZ4720_VIRGO
#include <asm/mach-jz4740/board-virgo.h>
#endif
/* Add other platform definition here ... */
/*------------------------------------------------------------------
* Follows are related to platform definitions
*/
#include <asm/mach-jz4740/clock.h>
#include <asm/mach-jz4740/serial.h>
#endif /* __ASM_JZ4740_H__ */

View File

@ -0,0 +1,43 @@
/*
* linux/include/asm-mips/mach-jz4740/misc.h
*
* Ingenic's JZ4740 common include.
*
* Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc.
*
* Author: <yliu@ingenic.cn>
*
* 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 __ASM_JZ4740_MISC_H__
#define __ASM_JZ4740_MISC_H__
/*==========================================================
* I2C
*===========================================================*/
#define I2C_EEPROM_DEV 0xA /* b'1010 */
#define I2C_RTC_DEV 0xD /* b'1101 */
#define DIMM0_SPD_ADDR 0
#define DIMM1_SPD_ADDR 1
#define DIMM2_SPD_ADDR 2
#define DIMM3_SPD_ADDR 3
#define JZ_HCI_ADDR 7
#define DIMM_SPD_LEN 128
#define JZ_HCI_LEN 512 /* 4K bits E2PROM */
#define I2C_RTC_LEN 16
#define HCI_MAC_OFFSET 64
extern void i2c_open(void);
extern void i2c_close(void);
extern void i2c_setclk(unsigned int i2cclk);
extern int i2c_read(unsigned char device, unsigned char *buf,
unsigned char address, int count);
extern int i2c_write(unsigned char device, unsigned char *buf,
unsigned char address, int count);
#endif /* __ASM_JZ4740_MISC_H__ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,30 @@
/*
* linux/include/asm-mips/mach-jz4740/serial.h
*
* Ingenic's JZ4740 common include.
*
* Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc.
*
* Author: <yliu@ingenic.cn>
*
* 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 __ASM_BOARD_SERIAL_H__
#define __ASM_BOARD_SERIAL_H__
#ifndef CONFIG_SERIAL_MANY_PORTS
#undef RS_TABLE_SIZE
#define RS_TABLE_SIZE 1
#endif
#define JZ_BASE_BAUD (12000000/16)
#define JZ_SERIAL_PORT_DEFNS \
{ .baud_base = JZ_BASE_BAUD, .irq = IRQ_UART0, \
.flags = STD_COM_FLAGS, .iomem_base = (u8 *)UART0_BASE, \
.iomem_reg_shift = 2, .io_type = SERIAL_IO_MEM },
#endif /* __ASM_BORAD_SERIAL_H__ */

View File

@ -0,0 +1,25 @@
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2002, 2004, 2007 by Ralf Baechle <ralf@linux-mips.org>
*/
#ifndef __ASM_MIPS_MACH_JZ4740_WAR_H
#define __ASM_MIPS_MACH_JZ4740_WAR_H
#define R4600_V1_INDEX_ICACHEOP_WAR 0
#define R4600_V1_HIT_CACHEOP_WAR 0
#define R4600_V2_HIT_CACHEOP_WAR 0
#define R5432_CP0_INTERRUPT_WAR 0
#define BCM1250_M3_WAR 0
#define SIBYTE_1956_WAR 0
#define MIPS4K_ICACHE_REFILL_WAR 0
#define MIPS_CACHE_SYNC_WAR 0
#define TX49XX_ICACHE_INDEX_INV_WAR 0
#define RM9000_CDEX_SMP_WAR 0
#define ICACHE_REFILLS_WORKAROUND_WAR 0
#define R10000_LLSC_WAR 0
#define MIPS34K_MISSED_ITLB_WAR 0
#endif /* __ASM_MIPS_MACH_JZ4740_WAR_H */

View File

@ -0,0 +1,26 @@
#
# Makefile for the Ingenic JZ4740.
#
# Object file lists.
obj-y += prom.o irq.o time.o reset.o setup.o dma.o \
platform.o i2c.o
obj-$(CONFIG_PROC_FS) += proc.o
# board specific support
obj-$(CONFIG_JZ4740_PAVO) += board-pavo.o
obj-$(CONFIG_JZ4740_LEO) += board-leo.o
obj-$(CONFIG_JZ4740_LYRA) += board-lyra.o
obj-$(CONFIG_JZ4725_DIPPER) += board-dipper.o
obj-$(CONFIG_JZ4720_VIRGO) += board-virgo.o
# PM support
obj-$(CONFIG_PM) +=pm.o
# CPU Frequency scaling support
obj-$(CONFIG_CPU_FREQ_JZ) +=cpufreq.o

View File

@ -0,0 +1,602 @@
/*
* linux/arch/mips/jz4740/cpufreq.c
*
* cpufreq driver for JZ4740
*
* Copyright (c) 2006-2007 Ingenic Semiconductor Inc.
* Author: <lhhuang@ingenic.cn>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cpufreq.h>
#include <asm/jzsoc.h>
#include <asm/processor.h>
#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
"cpufreq-jz4740", msg)
#undef CHANGE_PLL
#define PLL_UNCHANGED 0
#define PLL_GOES_UP 1
#define PLL_GOES_DOWN 2
#define PLL_WAIT_500NS (500*(__cpm_get_cclk()/1000000000))
/* Saved the boot-time parameters */
static struct {
/* SDRAM parameters */
unsigned int mclk; /* memory clock, KHz */
unsigned int tras; /* RAS pulse width, cycles of mclk */
unsigned int rcd; /* RAS to CAS Delay, cycles of mclk */
unsigned int tpc; /* RAS Precharge time, cycles of mclk */
unsigned int trwl; /* Write Precharge Time, cycles of mclk */
unsigned int trc; /* RAS Cycle Time, cycles of mclk */
unsigned int rtcor; /* Refresh Time Constant */
unsigned int sdram_initialized;
/* LCD parameters */
unsigned int lcd_clk; /* LCD clock, Hz */
unsigned int lcdpix_clk; /* LCD Pixel clock, Hz */
unsigned int lcd_clks_initialized;
} boot_config;
struct jz4740_freq_percpu_info {
struct cpufreq_frequency_table table[7];
};
static struct jz4740_freq_percpu_info jz4740_freq_table;
/*
* This contains the registers value for an operating point.
* If only part of a register needs to change then there is
* a mask value for that register.
* When going to a new operating point the current register
* value is ANDed with the ~mask and ORed with the new value.
*/
struct dpm_regs {
u32 cpccr; /* Clock Freq Control Register */
u32 cpccr_mask; /* Clock Freq Control Register mask */
u32 cppcr; /* PLL1 Control Register */
u32 cppcr_mask; /* PLL1 Control Register mask */
u32 pll_up_flag; /* New PLL freq is higher than current or not */
};
extern jz_clocks_t jz_clocks;
static void jz_update_clocks(void)
{
/* Next clocks must be updated if we have changed
* the PLL or divisors.
*/
jz_clocks.cclk = __cpm_get_cclk();
jz_clocks.hclk = __cpm_get_hclk();
jz_clocks.mclk = __cpm_get_mclk();
jz_clocks.pclk = __cpm_get_pclk();
jz_clocks.lcdclk = __cpm_get_lcdclk();
jz_clocks.pixclk = __cpm_get_pixclk();
jz_clocks.i2sclk = __cpm_get_i2sclk();
jz_clocks.usbclk = __cpm_get_usbclk();
jz_clocks.mscclk = __cpm_get_mscclk();
}
static void
jz_init_boot_config(void)
{
if (!boot_config.lcd_clks_initialized) {
/* the first time to scale pll */
boot_config.lcd_clk = __cpm_get_lcdclk();
boot_config.lcdpix_clk = __cpm_get_pixclk();
boot_config.lcd_clks_initialized = 1;
}
if (!boot_config.sdram_initialized) {
/* the first time to scale frequencies */
unsigned int dmcr, rtcor;
unsigned int tras, rcd, tpc, trwl, trc;
dmcr = REG_EMC_DMCR;
rtcor = REG_EMC_RTCOR;
tras = (dmcr >> 13) & 0x7;
rcd = (dmcr >> 11) & 0x3;
tpc = (dmcr >> 8) & 0x7;
trwl = (dmcr >> 5) & 0x3;
trc = (dmcr >> 2) & 0x7;
boot_config.mclk = __cpm_get_mclk() / 1000;
boot_config.tras = tras + 4;
boot_config.rcd = rcd + 1;
boot_config.tpc = tpc + 1;
boot_config.trwl = trwl + 1;
boot_config.trc = trc * 2 + 1;
boot_config.rtcor = rtcor;
boot_config.sdram_initialized = 1;
}
}
static void jz_update_dram_rtcor(unsigned int new_mclk)
{
unsigned int rtcor;
new_mclk /= 1000;
rtcor = boot_config.rtcor * new_mclk / boot_config.mclk;
rtcor--;
if (rtcor < 1) rtcor = 1;
if (rtcor > 255) rtcor = 255;
REG_EMC_RTCOR = rtcor;
REG_EMC_RTCNT = rtcor;
}
static void jz_update_dram_dmcr(unsigned int new_mclk)
{
unsigned int dmcr;
unsigned int tras, rcd, tpc, trwl, trc;
unsigned int valid_time, new_time; /* ns */
new_mclk /= 1000;
tras = boot_config.tras * new_mclk / boot_config.mclk;
rcd = boot_config.rcd * new_mclk / boot_config.mclk;
tpc = boot_config.tpc * new_mclk / boot_config.mclk;
trwl = boot_config.trwl * new_mclk / boot_config.mclk;
trc = boot_config.trc * new_mclk / boot_config.mclk;
/* Validation checking */
valid_time = (boot_config.tras * 1000000) / boot_config.mclk;
new_time = (tras * 1000000) / new_mclk;
if (new_time < valid_time) tras += 1;
valid_time = (boot_config.rcd * 1000000) / boot_config.mclk;
new_time = (rcd * 1000000) / new_mclk;
if (new_time < valid_time) rcd += 1;
valid_time = (boot_config.tpc * 1000000) / boot_config.mclk;
new_time = (tpc * 1000000) / new_mclk;
if (new_time < valid_time) tpc += 1;
valid_time = (boot_config.trwl * 1000000) / boot_config.mclk;
new_time = (trwl * 1000000) / new_mclk;
if (new_time < valid_time) trwl += 1;
valid_time = (boot_config.trc * 1000000) / boot_config.mclk;
new_time = (trc * 1000000) / new_mclk;
if (new_time < valid_time) trc += 2;
tras = (tras < 4) ? 4: tras;
tras = (tras > 11) ? 11: tras;
tras -= 4;
rcd = (rcd < 1) ? 1: rcd;
rcd = (rcd > 4) ? 4: rcd;
rcd -= 1;
tpc = (tpc < 1) ? 1: tpc;
tpc = (tpc > 8) ? 8: tpc;
tpc -= 1;
trwl = (trwl < 1) ? 1: trwl;
trwl = (trwl > 4) ? 4: trwl;
trwl -= 1;
trc = (trc < 1) ? 1: trc;
trc = (trc > 15) ? 15: trc;
trc /= 2;
dmcr = REG_EMC_DMCR;
dmcr &= ~(EMC_DMCR_TRAS_MASK | EMC_DMCR_RCD_MASK | EMC_DMCR_TPC_MASK | EMC_DMCR_TRWL_MASK | EMC_DMCR_TRC_MASK);
dmcr |= ((tras << EMC_DMCR_TRAS_BIT) | (rcd << EMC_DMCR_RCD_BIT) | (tpc << EMC_DMCR_TPC_BIT) | (trwl << EMC_DMCR_TRWL_BIT) | (trc << EMC_DMCR_TRC_BIT));
REG_EMC_DMCR = dmcr;
}
static void jz_update_dram_prev(unsigned int cur_mclk, unsigned int new_mclk)
{
/* No risk, no fun: run with interrupts on! */
if (new_mclk > cur_mclk) {
/* We're going FASTER, so first update TRAS, RCD, TPC, TRWL
* and TRC of DMCR before changing the frequency.
*/
jz_update_dram_dmcr(new_mclk);
} else {
/* We're going SLOWER: first update RTCOR value
* before changing the frequency.
*/
jz_update_dram_rtcor(new_mclk);
}
}
static void jz_update_dram_post(unsigned int cur_mclk, unsigned int new_mclk)
{
/* No risk, no fun: run with interrupts on! */
if (new_mclk > cur_mclk) {
/* We're going FASTER, so update RTCOR
* after changing the frequency
*/
jz_update_dram_rtcor(new_mclk);
} else {
/* We're going SLOWER: so update TRAS, RCD, TPC, TRWL
* and TRC of DMCR after changing the frequency.
*/
jz_update_dram_dmcr(new_mclk);
}
}
static void jz_scale_divisors(struct dpm_regs *regs)
{
unsigned int cpccr;
unsigned int cur_mclk, new_mclk;
int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32};
unsigned int tmp = 0, wait = PLL_WAIT_500NS;
cpccr = REG_CPM_CPCCR;
cpccr &= ~((unsigned long)regs->cpccr_mask);
cpccr |= regs->cpccr;
cpccr |= CPM_CPCCR_CE; /* update immediately */
cur_mclk = __cpm_get_mclk();
new_mclk = __cpm_get_pllout() / div[(cpccr & CPM_CPCCR_MDIV_MASK) >> CPM_CPCCR_MDIV_BIT];
/* Update some DRAM parameters before changing frequency */
jz_update_dram_prev(cur_mclk, new_mclk);
/* update register to change the clocks.
* align this code to a cache line.
*/
__asm__ __volatile__(
".set noreorder\n\t"
".align 5\n"
"sw %1,0(%0)\n\t"
"li %3,0\n\t"
"1:\n\t"
"bne %3,%2,1b\n\t"
"addi %3, 1\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
".set reorder\n\t"
:
: "r" (CPM_CPCCR), "r" (cpccr), "r" (wait), "r" (tmp));
/* Update some other DRAM parameters after changing frequency */
jz_update_dram_post(cur_mclk, new_mclk);
}
#ifdef CHANGE_PLL
/* Maintain the LCD clock and pixel clock */
static void jz_scale_lcd_divisors(struct dpm_regs *regs)
{
unsigned int new_pll, new_lcd_div, new_lcdpix_div;
unsigned int cpccr;
unsigned int tmp = 0, wait = PLL_WAIT_500NS;
if (!boot_config.lcd_clks_initialized) return;
new_pll = __cpm_get_pllout();
new_lcd_div = new_pll / boot_config.lcd_clk;
new_lcdpix_div = new_pll / boot_config.lcdpix_clk;
if (new_lcd_div < 1)
new_lcd_div = 1;
if (new_lcd_div > 16)
new_lcd_div = 16;
if (new_lcdpix_div < 1)
new_lcdpix_div = 1;
if (new_lcdpix_div > 512)
new_lcdpix_div = 512;
// REG_CPM_CPCCR2 = new_lcdpix_div - 1;
cpccr = REG_CPM_CPCCR;
cpccr &= ~CPM_CPCCR_LDIV_MASK;
cpccr |= ((new_lcd_div - 1) << CPM_CPCCR_LDIV_BIT);
cpccr |= CPM_CPCCR_CE; /* update immediately */
/* update register to change the clocks.
* align this code to a cache line.
*/
__asm__ __volatile__(
".set noreorder\n\t"
".align 5\n"
"sw %1,0(%0)\n\t"
"li %3,0\n\t"
"1:\n\t"
"bne %3,%2,1b\n\t"
"addi %3, 1\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
".set reorder\n\t"
:
: "r" (CPM_CPCCR), "r" (cpccr), "r" (wait), "r" (tmp));
}
static void jz_scale_pll(struct dpm_regs *regs)
{
unsigned int cppcr;
unsigned int cur_mclk, new_mclk, new_pll;
int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32};
int od[] = {1, 2, 2, 4};
cppcr = REG_CPM_CPPCR;
cppcr &= ~(regs->cppcr_mask | CPM_CPPCR_PLLS | CPM_CPPCR_PLLEN | CPM_CPPCR_PLLST_MASK);
regs->cppcr &= ~CPM_CPPCR_PLLEN;
cppcr |= (regs->cppcr | 0xff);
/* Update some DRAM parameters before changing frequency */
new_pll = JZ_EXTAL * ((cppcr>>23)+2) / ((((cppcr>>18)&0x1f)+2) * od[(cppcr>>16)&0x03]);
cur_mclk = __cpm_get_mclk();
new_mclk = new_pll / div[(REG_CPM_CPCCR>>CPM_CPCCR_MDIV_BIT) & 0xf];
/*
* Update some SDRAM parameters
*/
jz_update_dram_prev(cur_mclk, new_mclk);
/*
* Update PLL, align code to cache line.
*/
cppcr |= CPM_CPPCR_PLLEN;
__asm__ __volatile__(
".set noreorder\n\t"
".align 5\n"
"sw %1,0(%0)\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
".set reorder\n\t"
:
: "r" (CPM_CPPCR), "r" (cppcr));
/* Update some other DRAM parameters after changing frequency */
jz_update_dram_post(cur_mclk, new_mclk);
}
#endif
static void jz4740_transition(struct dpm_regs *regs)
{
/*
* Get and save some boot-time conditions.
*/
jz_init_boot_config();
#ifdef CHANGE_PLL
/*
* Disable LCD before scaling pll.
* LCD and LCD pixel clocks should not be changed even if the PLL
* output frequency has been changed.
*/
REG_LCD_CTRL &= ~LCD_CTRL_ENA;
/*
* Stop module clocks before scaling PLL
*/
__cpm_stop_eth();
__cpm_stop_aic(1);
__cpm_stop_aic(2);
#endif
/* ... add more as necessary */
if (regs->pll_up_flag == PLL_GOES_UP) {
/* the pll frequency is going up, so change dividors first */
jz_scale_divisors(regs);
#ifdef CHANGE_PLL
jz_scale_pll(regs);
#endif
}
else if (regs->pll_up_flag == PLL_GOES_DOWN) {
/* the pll frequency is going down, so change pll first */
#ifdef CHANGE_PLL
jz_scale_pll(regs);
#endif
jz_scale_divisors(regs);
}
else {
/* the pll frequency is unchanged, so change divisors only */
jz_scale_divisors(regs);
}
#ifdef CHANGE_PLL
/*
* Restart module clocks before scaling PLL
*/
__cpm_start_eth();
__cpm_start_aic(1);
__cpm_start_aic(2);
/* ... add more as necessary */
/* Scale the LCD divisors after scaling pll */
if (regs->pll_up_flag != PLL_UNCHANGED) {
jz_scale_lcd_divisors(regs);
}
/* Enable LCD controller */
REG_LCD_CTRL &= ~LCD_CTRL_DIS;
REG_LCD_CTRL |= LCD_CTRL_ENA;
#endif
/* Update system clocks */
jz_update_clocks();
}
extern unsigned int idle_times;
static unsigned int jz4740_freq_get(unsigned int cpu)
{
return (__cpm_get_cclk() / 1000);
}
static unsigned int index_to_divisor(unsigned int index, struct dpm_regs *regs)
{
int n2FR[33] = {
0, 0, 1, 2, 3, 0, 4, 0, 5, 0, 0, 0, 6, 0, 0, 0,
7, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0,
9
};
int div[4] = {1, 2, 2, 2}; /* divisors of I:S:P:M */
unsigned int div_of_cclk, new_freq, i;
regs->pll_up_flag = PLL_UNCHANGED;
regs->cpccr_mask = CPM_CPCCR_CDIV_MASK | CPM_CPCCR_HDIV_MASK | CPM_CPCCR_PDIV_MASK | CPM_CPCCR_MDIV_MASK;
new_freq = jz4740_freq_table.table[index].frequency;
do {
div_of_cclk = __cpm_get_pllout() / (1000 * new_freq);
} while (div_of_cclk==0);
if(div_of_cclk == 1 || div_of_cclk == 2 || div_of_cclk == 4) {
for(i = 1; i<4; i++) {
div[i] = 3;
}
} else {
for(i = 1; i<4; i++) {
div[i] = 2;
}
}
for(i = 0; i<4; i++) {
div[i] *= div_of_cclk;
}
dprintk("divisors of I:S:P:M = %d:%d:%d:%d\n", div[0], div[1], div[2], div[3]);
regs->cpccr =
(n2FR[div[0]] << CPM_CPCCR_CDIV_BIT) |
(n2FR[div[1]] << CPM_CPCCR_HDIV_BIT) |
(n2FR[div[2]] << CPM_CPCCR_PDIV_BIT) |
(n2FR[div[3]] << CPM_CPCCR_MDIV_BIT);
return div_of_cclk;
}
static void jz4740_set_cpu_divider_index(unsigned int cpu, unsigned int index)
{
unsigned long divisor, old_divisor;
struct cpufreq_freqs freqs;
struct dpm_regs regs;
old_divisor = __cpm_get_pllout() / __cpm_get_cclk();
divisor = index_to_divisor(index, &regs);
freqs.old = __cpm_get_cclk() / 1000;
freqs.new = __cpm_get_pllout() / (1000 * divisor);
freqs.cpu = cpu;
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
if (old_divisor != divisor)
jz4740_transition(&regs);
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
}
static int jz4740_freq_target(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
{
unsigned int new_index = 0;
if (cpufreq_frequency_table_target(policy,
&jz4740_freq_table.table[0],
target_freq, relation, &new_index))
return -EINVAL;
jz4740_set_cpu_divider_index(policy->cpu, new_index);
dprintk("new frequency is %d KHz (REG_CPM_CPCCR:0x%x)\n", __cpm_get_cclk() / 1000, REG_CPM_CPCCR);
return 0;
}
static int jz4740_freq_verify(struct cpufreq_policy *policy)
{
return cpufreq_frequency_table_verify(policy,
&jz4740_freq_table.table[0]);
}
static int __init jz4740_cpufreq_driver_init(struct cpufreq_policy *policy)
{
struct cpufreq_frequency_table *table = &jz4740_freq_table.table[0];
unsigned int MAX_FREQ;
dprintk(KERN_INFO "Jz4740 cpufreq driver\n");
if (policy->cpu != 0)
return -EINVAL;
policy->cur = MAX_FREQ = __cpm_get_cclk() / 1000; /* in kHz. Current and max frequency is determined by u-boot */
policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
policy->cpuinfo.min_freq = MAX_FREQ/8;
policy->cpuinfo.max_freq = MAX_FREQ;
policy->cpuinfo.transition_latency = 100000; /* in 10^(-9) s = nanoseconds */
table[0].index = 0;
table[0].frequency = MAX_FREQ/8;
table[1].index = 1;
table[1].frequency = MAX_FREQ/6;
table[2].index = 2;
table[2].frequency = MAX_FREQ/4;
table[3].index = 3;
table[3].frequency = MAX_FREQ/3;
table[4].index = 4;
table[4].frequency = MAX_FREQ/2;
table[5].index = 5;
table[5].frequency = MAX_FREQ;
table[6].index = 6;
table[6].frequency = CPUFREQ_TABLE_END;
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
cpufreq_frequency_table_get_attr(table, policy->cpu); /* for showing /sys/devices/system/cpu/cpuX/cpufreq/stats/ */
#endif
return cpufreq_frequency_table_cpuinfo(policy, table);
}
static struct cpufreq_driver cpufreq_jz4740_driver = {
// .flags = CPUFREQ_STICKY,
.init = jz4740_cpufreq_driver_init,
.verify = jz4740_freq_verify,
.target = jz4740_freq_target,
.get = jz4740_freq_get,
.name = "jz4740",
};
static int __init jz4740_cpufreq_init(void)
{
return cpufreq_register_driver(&cpufreq_jz4740_driver);
}
static void __exit jz4740_cpufreq_exit(void)
{
cpufreq_unregister_driver(&cpufreq_jz4740_driver);
}
module_init(jz4740_cpufreq_init);
module_exit(jz4740_cpufreq_exit);
MODULE_AUTHOR("Regen <lhhuang@ingenic.cn>");
MODULE_DESCRIPTION("cpufreq driver for Jz4740");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,768 @@
/*
* linux/arch/mips/jz4740/dma.c
*
* Support functions for the JZ4740 internal DMA channels.
* No-descriptor transfer only.
* Descriptor transfer should also call jz_request_dma() to get a free
* channel and call jz_free_dma() to free the channel. And driver should
* build the DMA descriptor and setup the DMA channel by itself.
*
* Copyright (C) 2006 Ingenic Semiconductor Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/soundcard.h>
#include <asm/system.h>
#include <asm/addrspace.h>
#include <asm/jzsoc.h>
/*
* A note on resource allocation:
*
* All drivers needing DMA channels, should allocate and release them
* through the public routines `jz_request_dma()' and `jz_free_dma()'.
*
* In order to avoid problems, all processes should allocate resources in
* the same sequence and release them in the reverse order.
*
* So, when allocating DMAs and IRQs, first allocate the DMA, then the IRQ.
* When releasing them, first release the IRQ, then release the DMA. The
* main reason for this order is that, if you are requesting the DMA buffer
* done interrupt, you won't know the irq number until the DMA channel is
* returned from jz_request_dma().
*/
struct jz_dma_chan jz_dma_table[MAX_DMA_NUM] = {
{dev_id:-1,},
{dev_id:-1,},
{dev_id:-1,},
{dev_id:-1,},
{dev_id:-1,},
{dev_id:-1,},
};
// Device FIFO addresses and default DMA modes
static const struct {
unsigned int fifo_addr;
unsigned int dma_mode;
unsigned int dma_source;
} dma_dev_table[DMA_ID_MAX] = {
{CPHYSADDR(UART0_TDR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_UART0OUT},
{CPHYSADDR(UART0_RDR), DMA_8BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_UART0IN},
{CPHYSADDR(SSI_DR), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_SSIOUT},
{CPHYSADDR(SSI_DR), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_SSIIN},
{CPHYSADDR(AIC_DR), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_AICOUT},
{CPHYSADDR(AIC_DR), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_AICIN},
{CPHYSADDR(MSC_TXFIFO), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_MSCOUT},
{CPHYSADDR(MSC_RXFIFO), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_MSCIN},
{0, DMA_AUTOINIT, DMAC_DRSR_RS_TCU},
{0, DMA_AUTOINIT, DMAC_DRSR_RS_AUTO},
{},
};
int jz_dma_read_proc(char *buf, char **start, off_t fpos,
int length, int *eof, void *data)
{
int i, len = 0;
struct jz_dma_chan *chan;
for (i = 0; i < MAX_DMA_NUM; i++) {
if ((chan = get_dma_chan(i)) != NULL) {
len += sprintf(buf + len, "%2d: %s\n",
i, chan->dev_str);
}
}
if (fpos >= len) {
*start = buf;
*eof = 1;
return 0;
}
*start = buf + fpos;
if ((len -= fpos) > length)
return length;
*eof = 1;
return len;
}
void dump_jz_dma_channel(unsigned int dmanr)
{
struct jz_dma_chan *chan;
if (dmanr > MAX_DMA_NUM)
return;
chan = &jz_dma_table[dmanr];
printk("DMA%d Registers:\n", dmanr);
printk(" DMACR = 0x%08x\n", REG_DMAC_DMACR);
printk(" DSAR = 0x%08x\n", REG_DMAC_DSAR(dmanr));
printk(" DTAR = 0x%08x\n", REG_DMAC_DTAR(dmanr));
printk(" DTCR = 0x%08x\n", REG_DMAC_DTCR(dmanr));
printk(" DRSR = 0x%08x\n", REG_DMAC_DRSR(dmanr));
printk(" DCCSR = 0x%08x\n", REG_DMAC_DCCSR(dmanr));
printk(" DCMD = 0x%08x\n", REG_DMAC_DCMD(dmanr));
printk(" DDA = 0x%08x\n", REG_DMAC_DDA(dmanr));
printk(" DMADBR = 0x%08x\n", REG_DMAC_DMADBR);
}
/**
* jz_request_dma - dynamically allcate an idle DMA channel to return
* @dev_id: the specified dma device id or DMA_ID_RAW_SET
* @dev_str: the specified dma device string name
* @irqhandler: the irq handler, or NULL
* @irqflags: the irq handler flags
* @irq_dev_id: the irq handler device id for shared irq
*
* Finds a free channel, and binds the requested device to it.
* Returns the allocated channel number, or negative on error.
* Requests the DMA done IRQ if irqhandler != NULL.
*
*/
/*int jz_request_dma(int dev_id, const char *dev_str,
void (*irqhandler)(int, void *, struct pt_regs *),
unsigned long irqflags,
void *irq_dev_id)
*/
int jz_request_dma(int dev_id, const char *dev_str,
irqreturn_t (*irqhandler)(int, void *),
unsigned long irqflags,
void *irq_dev_id)
{
struct jz_dma_chan *chan;
int i, ret;
if (dev_id < 0 || dev_id >= DMA_ID_MAX)
return -EINVAL;
for (i = 0; i < MAX_DMA_NUM; i++) {
if (jz_dma_table[i].dev_id < 0)
break;
}
if (i == MAX_DMA_NUM) /* no free channel */
return -ENODEV;
/* we got a free channel */
chan = &jz_dma_table[i];
if (irqhandler) {
chan->irq = IRQ_DMA_0 + i; // allocate irq number
chan->irq_dev = irq_dev_id;
if ((ret = request_irq(chan->irq, irqhandler, irqflags,
dev_str, chan->irq_dev))) {
chan->irq = -1;
chan->irq_dev = NULL;
return ret;
}
} else {
chan->irq = -1;
chan->irq_dev = NULL;
}
// fill it in
chan->io = i;
chan->dev_id = dev_id;
chan->dev_str = dev_str;
chan->fifo_addr = dma_dev_table[dev_id].fifo_addr;
chan->mode = dma_dev_table[dev_id].dma_mode;
chan->source = dma_dev_table[dev_id].dma_source;
return i;
}
void jz_free_dma(unsigned int dmanr)
{
struct jz_dma_chan *chan = get_dma_chan(dmanr);
if (!chan) {
printk("Trying to free DMA%d\n", dmanr);
return;
}
disable_dma(dmanr);
if (chan->irq)
free_irq(chan->irq, chan->irq_dev);
chan->irq = -1;
chan->irq_dev = NULL;
chan->dev_id = -1;
}
void jz_set_dma_dest_width(int dmanr, int nbit)
{
struct jz_dma_chan *chan = get_dma_chan(dmanr);
if (!chan)
return;
chan->mode &= ~DMAC_DCMD_DWDH_MASK;
switch (nbit) {
case 8:
chan->mode |= DMAC_DCMD_DWDH_8;
break;
case 16:
chan->mode |= DMAC_DCMD_DWDH_16;
break;
case 32:
chan->mode |= DMAC_DCMD_DWDH_32;
break;
}
}
void jz_set_dma_src_width(int dmanr, int nbit)
{
struct jz_dma_chan *chan = get_dma_chan(dmanr);
if (!chan)
return;
chan->mode &= ~DMAC_DCMD_SWDH_MASK;
switch (nbit) {
case 8:
chan->mode |= DMAC_DCMD_SWDH_8;
break;
case 16:
chan->mode |= DMAC_DCMD_SWDH_16;
break;
case 32:
chan->mode |= DMAC_DCMD_SWDH_32;
break;
}
}
void jz_set_dma_block_size(int dmanr, int nbyte)
{
struct jz_dma_chan *chan = get_dma_chan(dmanr);
if (!chan)
return;
chan->mode &= ~DMAC_DCMD_DS_MASK;
switch (nbyte) {
case 1:
chan->mode |= DMAC_DCMD_DS_8BIT;
break;
case 2:
chan->mode |= DMAC_DCMD_DS_16BIT;
break;
case 4:
chan->mode |= DMAC_DCMD_DS_32BIT;
break;
case 16:
chan->mode |= DMAC_DCMD_DS_16BYTE;
break;
case 32:
chan->mode |= DMAC_DCMD_DS_32BYTE;
break;
}
}
unsigned int jz_get_dma_command(int dmanr)
{
struct jz_dma_chan *chan = get_dma_chan(dmanr);
return chan->mode;
}
/**
* jz_set_dma_mode - do the raw settings for the specified DMA channel
* @dmanr: the specified DMA channel
* @mode: dma operate mode, DMA_MODE_READ or DMA_MODE_WRITE
* @dma_mode: dma raw mode
* @dma_source: dma raw request source
* @fifo_addr: dma raw device fifo address
*
* Ensure call jz_request_dma(DMA_ID_RAW_SET, ...) first, then call
* jz_set_dma_mode() rather than set_dma_mode() if you work with
* and external request dma device.
*
* NOTE: Don not dynamically allocate dma channel if one external request
* dma device will occupy this channel.
*/
int jz_set_dma_mode(unsigned int dmanr, unsigned int mode,
unsigned int dma_mode, unsigned int dma_source,
unsigned int fifo_addr)
{
int dev_id, i;
struct jz_dma_chan *chan;
if (dmanr > MAX_DMA_NUM)
return -ENODEV;
for (i = 0; i < MAX_DMA_NUM; i++) {
if (jz_dma_table[i].dev_id < 0)
break;
}
if (i == MAX_DMA_NUM)
return -ENODEV;
chan = &jz_dma_table[dmanr];
dev_id = chan->dev_id;
if (dev_id > 0) {
printk(KERN_DEBUG "%s sets the allocated DMA channel %d!\n",
__FUNCTION__, dmanr);
return -ENODEV;
}
/* clone it from the dynamically allocated. */
if (i != dmanr) {
chan->irq = jz_dma_table[i].irq;
chan->irq_dev = jz_dma_table[i].irq_dev;
chan->dev_str = jz_dma_table[i].dev_str;
jz_dma_table[i].irq = 0;
jz_dma_table[i].irq_dev = NULL;
jz_dma_table[i].dev_id = -1;
}
chan->dev_id = DMA_ID_RAW_SET;
chan->io = dmanr;
chan->fifo_addr = fifo_addr;
chan->mode = dma_mode;
chan->source = dma_source;
set_dma_mode(dmanr, dma_mode);
return dmanr;
}
void enable_dma(unsigned int dmanr)
{
struct jz_dma_chan *chan = get_dma_chan(dmanr);
if (!chan)
return;
REG_DMAC_DCCSR(dmanr) &= ~(DMAC_DCCSR_HLT | DMAC_DCCSR_TT | DMAC_DCCSR_AR);
REG_DMAC_DCCSR(dmanr) |= DMAC_DCCSR_NDES; /* No-descriptor transfer */
__dmac_enable_channel(dmanr);
if (chan->irq)
__dmac_channel_enable_irq(dmanr);
}
#define DMA_DISABLE_POLL 0x10000
void disable_dma(unsigned int dmanr)
{
int i;
struct jz_dma_chan *chan = get_dma_chan(dmanr);
if (!chan)
return;
if (!__dmac_channel_enabled(dmanr))
return;
for (i = 0; i < DMA_DISABLE_POLL; i++)
if (__dmac_channel_transmit_end_detected(dmanr))
break;
#if 0
if (i == DMA_DISABLE_POLL)
printk(KERN_INFO "disable_dma: poll expired!\n");
#endif
__dmac_disable_channel(dmanr);
if (chan->irq)
__dmac_channel_disable_irq(dmanr);
}
/* Note: DMA_MODE_MASK is simulated by sw */
void set_dma_mode(unsigned int dmanr, unsigned int mode)
{
struct jz_dma_chan *chan = get_dma_chan(dmanr);
if (!chan)
return;
chan->mode |= mode & ~(DMAC_DCMD_SAI | DMAC_DCMD_DAI);
mode &= DMA_MODE_MASK;
if (mode == DMA_MODE_READ) {
chan->mode |= DMAC_DCMD_DAI;
chan->mode &= ~DMAC_DCMD_SAI;
} else if (mode == DMA_MODE_WRITE) {
chan->mode |= DMAC_DCMD_SAI;
chan->mode &= ~DMAC_DCMD_DAI;
} else {
printk(KERN_DEBUG "set_dma_mode() just supports DMA_MODE_READ or DMA_MODE_WRITE!\n");
}
REG_DMAC_DCMD(chan->io) = chan->mode & ~DMA_MODE_MASK;
REG_DMAC_DRSR(chan->io) = chan->source;
}
void set_dma_addr(unsigned int dmanr, unsigned int phyaddr)
{
unsigned int mode;
struct jz_dma_chan *chan = get_dma_chan(dmanr);
if (!chan)
return;
mode = chan->mode & DMA_MODE_MASK;
if (mode == DMA_MODE_READ) {
REG_DMAC_DSAR(chan->io) = chan->fifo_addr;
REG_DMAC_DTAR(chan->io) = phyaddr;
} else if (mode == DMA_MODE_WRITE) {
REG_DMAC_DSAR(chan->io) = phyaddr;
REG_DMAC_DTAR(chan->io) = chan->fifo_addr;
} else
printk(KERN_DEBUG "Driver should call set_dma_mode() ahead set_dma_addr()!\n");
}
void set_dma_count(unsigned int dmanr, unsigned int bytecnt)
{
struct jz_dma_chan *chan = get_dma_chan(dmanr);
int dma_ds[] = {4, 1, 2, 16, 32};
unsigned int ds;
if (!chan)
return;
ds = (chan->mode & DMAC_DCMD_DS_MASK) >> DMAC_DCMD_DS_BIT;
REG_DMAC_DTCR(chan->io) = bytecnt / dma_ds[ds]; // transfer count
}
unsigned int get_dma_residue(unsigned int dmanr)
{
unsigned int count, ds;
int dma_ds[] = {4, 1, 2, 16, 32};
struct jz_dma_chan *chan = get_dma_chan(dmanr);
if (!chan)
return 0;
ds = (chan->mode & DMAC_DCMD_DS_MASK) >> DMAC_DCMD_DS_BIT;
count = REG_DMAC_DTCR(chan->io);
count = count * dma_ds[ds];
return count;
}
void jz_set_oss_dma(unsigned int dmanr, unsigned int mode, unsigned int audio_fmt)
{
struct jz_dma_chan *chan = get_dma_chan(dmanr);
if (!chan)
return;
switch (audio_fmt) {
case AFMT_U8:
/* burst mode : 32BIT */
break;
case AFMT_S16_LE:
/* burst mode : 16BYTE */
if (mode == DMA_MODE_READ) {
chan->mode = DMA_AIC_32_16BYTE_RX_CMD | DMA_MODE_READ;
chan->mode |= mode & ~(DMAC_DCMD_SAI | DMAC_DCMD_DAI);
mode &= DMA_MODE_MASK;
chan->mode |= DMAC_DCMD_DAI;
chan->mode &= ~DMAC_DCMD_SAI;
} else if (mode == DMA_MODE_WRITE) {
chan->mode = DMA_AIC_32_16BYTE_TX_CMD | DMA_MODE_WRITE;
chan->mode |= mode & ~(DMAC_DCMD_SAI | DMAC_DCMD_DAI);
mode &= DMA_MODE_MASK;
chan->mode |= DMAC_DCMD_SAI;
chan->mode &= ~DMAC_DCMD_DAI;
} else
printk("oss_dma_burst_mode() just supports DMA_MODE_READ or DMA_MODE_WRITE!\n");
REG_DMAC_DCMD(chan->io) = chan->mode & ~DMA_MODE_MASK;
REG_DMAC_DRSR(chan->io) = chan->source;
break;
}
}
void jz_set_alsa_dma(unsigned int dmanr, unsigned int mode, unsigned int audio_fmt)
{
struct jz_dma_chan *chan = get_dma_chan(dmanr);
if (!chan)
return;
switch (audio_fmt) {
case 8:
/* SNDRV_PCM_FORMAT_S8 burst mode : 32BIT */
break;
case 16:
/* SNDRV_PCM_FORMAT_S16_LE burst mode : 16BYTE */
if (mode == DMA_MODE_READ) {
chan->mode = DMA_AIC_16BYTE_RX_CMD | DMA_MODE_READ;
chan->mode |= mode & ~(DMAC_DCMD_SAI | DMAC_DCMD_DAI);
mode &= DMA_MODE_MASK;
chan->mode |= DMAC_DCMD_DAI;
chan->mode &= ~DMAC_DCMD_SAI;
} else if (mode == DMA_MODE_WRITE) {
chan->mode = DMA_AIC_16BYTE_TX_CMD | DMA_MODE_WRITE;
chan->mode |= mode & ~(DMAC_DCMD_SAI | DMAC_DCMD_DAI);
mode &= DMA_MODE_MASK;
chan->mode |= DMAC_DCMD_SAI;
chan->mode &= ~DMAC_DCMD_DAI;
} else
printk("alsa_dma_burst_mode() just supports DMA_MODE_READ or DMA_MODE_WRITE!\n");
REG_DMAC_DCMD(chan->io) = chan->mode & ~DMA_MODE_MASK;
REG_DMAC_DRSR(chan->io) = chan->source;
break;
}
}
#undef JZ4740_DMAC_TEST_ENABLE
#ifdef JZ4740_DMAC_TEST_ENABLE
/*
* DMA test: external address <--> external address
*/
#define TEST_DMA_SIZE 16*1024
static jz_dma_desc *dma_desc;
static int dma_chan;
static dma_addr_t dma_desc_phys_addr;
static unsigned int dma_src_addr, dma_src_phys_addr, dma_dst_addr, dma_dst_phys_addr;
static int dma_check_result(void *src, void *dst, int size)
{
unsigned int addr1, addr2, i, err = 0;
addr1 = (unsigned int)src;
addr2 = (unsigned int)dst;
for (i = 0; i < size; i += 4) {
if (*(volatile unsigned int *)addr1 != *(volatile unsigned int *)addr2) {
err++;
printk("wrong data at 0x%08x: src 0x%08x dst 0x%08x\n", addr2, *(volatile unsigned int *)addr1, *(volatile unsigned int *)addr2);
}
addr1 += 4;
addr2 += 4;
}
printk("check DMA result err=%d\n", err);
return err;
}
static void jz4740_dma_irq(int irq, void *dev_id, struct pt_regs *regs)
{
printk("jz4740_dma_irq %d\n", irq);
REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */
if (__dmac_channel_transmit_halt_detected(dma_chan)) {
printk("DMA HALT\n");
__dmac_channel_clear_transmit_halt(dma_chan);
}
if (__dmac_channel_address_error_detected(dma_chan)) {
printk("DMA ADDR ERROR\n");
__dmac_channel_clear_address_error(dma_chan);
}
if (__dmac_channel_descriptor_invalid_detected(dma_chan)) {
printk("DMA DESC INVALID\n");
__dmac_channel_clear_descriptor_invalid(dma_chan);
}
if (__dmac_channel_count_terminated_detected(dma_chan)) {
printk("DMA CT\n");
__dmac_channel_clear_count_terminated(dma_chan);
}
if (__dmac_channel_transmit_end_detected(dma_chan)) {
printk("DMA TT\n");
__dmac_channel_clear_transmit_end(dma_chan);
dump_jz_dma_channel(dma_chan);
dma_check_result((void *)dma_src_addr, (void *)dma_dst_addr, TEST_DMA_SIZE);
}
/* free buffers */
printk("free DMA buffers\n");
free_pages(dma_src_addr, 2);
free_pages(dma_dst_addr, 2);
if (dma_desc)
free_pages((unsigned int)dma_desc, 0);
/* free dma */
jz_free_dma(dma_chan);
}
void dma_nodesc_test(void)
{
unsigned int addr, i;
printk("dma_nodesc_test\n");
/* Request DMA channel and setup irq handler */
dma_chan = jz_request_dma(DMA_ID_AUTO, "auto", jz4740_dma_irq,
SA_INTERRUPT, NULL);
if (dma_chan < 0) {
printk("Setup irq failed\n");
return;
}
printk("Requested DMA channel = %d\n", dma_chan);
/* Allocate DMA buffers */
dma_src_addr = __get_free_pages(GFP_KERNEL, 2); /* 16KB */
dma_dst_addr = __get_free_pages(GFP_KERNEL, 2); /* 16KB */
dma_src_phys_addr = CPHYSADDR(dma_src_addr);
dma_dst_phys_addr = CPHYSADDR(dma_dst_addr);
printk("Buffer addresses: 0x%08x 0x%08x 0x%08x 0x%08x\n",
dma_src_addr, dma_src_phys_addr, dma_dst_addr, dma_dst_phys_addr);
/* Prepare data for source buffer */
addr = (unsigned int)dma_src_addr;
for (i = 0; i < TEST_DMA_SIZE; i += 4) {
*(volatile unsigned int *)addr = addr;
addr += 4;
}
dma_cache_wback((unsigned long)dma_src_addr, TEST_DMA_SIZE);
/* Init target buffer */
memset((void *)dma_dst_addr, 0, TEST_DMA_SIZE);
dma_cache_wback((unsigned long)dma_dst_addr, TEST_DMA_SIZE);
/* Init DMA module */
printk("Starting DMA\n");
REG_DMAC_DMACR = 0;
REG_DMAC_DCCSR(dma_chan) = 0;
REG_DMAC_DRSR(dma_chan) = DMAC_DRSR_RS_AUTO;
REG_DMAC_DSAR(dma_chan) = dma_src_phys_addr;
REG_DMAC_DTAR(dma_chan) = dma_dst_phys_addr;
REG_DMAC_DTCR(dma_chan) = 512;
REG_DMAC_DCMD(dma_chan) = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BYTE | DMAC_DCMD_TIE;
REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_NDES | DMAC_DCCSR_EN;
REG_DMAC_DMACR = DMAC_DMACR_DMAE; /* global DMA enable bit */
printk("DMA started. IMR=%08x\n", REG_INTC_IMR);
}
void dma_desc_test(void)
{
unsigned int next, addr, i;
static jz_dma_desc *desc;
printk("dma_desc_test\n");
/* Request DMA channel and setup irq handler */
dma_chan = jz_request_dma(DMA_ID_AUTO, "auto", jz4740_dma_irq,
SA_INTERRUPT, NULL);
if (dma_chan < 0) {
printk("Setup irq failed\n");
return;
}
printk("Requested DMA channel = %d\n", dma_chan);
/* Allocate DMA buffers */
dma_src_addr = __get_free_pages(GFP_KERNEL, 2); /* 16KB */
dma_dst_addr = __get_free_pages(GFP_KERNEL, 2); /* 16KB */
dma_src_phys_addr = CPHYSADDR(dma_src_addr);
dma_dst_phys_addr = CPHYSADDR(dma_dst_addr);
printk("Buffer addresses: 0x%08x 0x%08x 0x%08x 0x%08x\n",
dma_src_addr, dma_src_phys_addr, dma_dst_addr, dma_dst_phys_addr);
/* Prepare data for source buffer */
addr = (unsigned int)dma_src_addr;
for (i = 0; i < TEST_DMA_SIZE; i += 4) {
*(volatile unsigned int *)addr = addr;
addr += 4;
}
dma_cache_wback((unsigned long)dma_src_addr, TEST_DMA_SIZE);
/* Init target buffer */
memset((void *)dma_dst_addr, 0, TEST_DMA_SIZE);
dma_cache_wback((unsigned long)dma_dst_addr, TEST_DMA_SIZE);
/* Allocate DMA descriptors */
dma_desc = (jz_dma_desc *)__get_free_pages(GFP_KERNEL, 0);
dma_desc_phys_addr = CPHYSADDR((unsigned long)dma_desc);
printk("DMA descriptor address: 0x%08x 0x%08x\n", (u32)dma_desc, dma_desc_phys_addr);
/* Setup DMA descriptors */
desc = dma_desc;
next = (dma_desc_phys_addr + (sizeof(jz_dma_desc))) >> 4;
desc->dcmd = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BYTE | DMAC_DCMD_TM | DMAC_DCMD_DES_V | DMAC_DCMD_DES_VM | DMAC_DCMD_DES_VIE | DMAC_DCMD_TIE | DMAC_DCMD_LINK;
desc->dsadr = dma_src_phys_addr; /* DMA source address */
desc->dtadr = dma_dst_phys_addr; /* DMA target address */
desc->ddadr = (next << 24) + 128; /* size: 128*32 bytes = 4096 bytes */
desc++;
next = (dma_desc_phys_addr + 2*(sizeof(jz_dma_desc))) >> 4;
desc->dcmd = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_16BYTE | DMAC_DCMD_DES_V | DMAC_DCMD_DES_VM | DMAC_DCMD_DES_VIE | DMAC_DCMD_TIE | DMAC_DCMD_LINK;
desc->dsadr = dma_src_phys_addr + 4096; /* DMA source address */
desc->dtadr = dma_dst_phys_addr + 4096; /* DMA target address */
desc->ddadr = (next << 24) + 256; /* size: 256*16 bytes = 4096 bytes */
desc++;
next = (dma_desc_phys_addr + 3*(sizeof(jz_dma_desc))) >> 4;
desc->dcmd = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_16BYTE | DMAC_DCMD_DES_V | DMAC_DCMD_DES_VM | DMAC_DCMD_DES_VIE | DMAC_DCMD_TIE | DMAC_DCMD_LINK;
desc->dsadr = dma_src_phys_addr + 8192; /* DMA source address */
desc->dtadr = dma_dst_phys_addr + 8192; /* DMA target address */
desc->ddadr = (next << 24) + 256; /* size: 256*16 bytes = 4096 bytes */
desc++;
next = (dma_desc_phys_addr + 4*(sizeof(jz_dma_desc))) >> 4;
desc->dcmd = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT | DMAC_DCMD_DES_V | DMAC_DCMD_DES_VM | DMAC_DCMD_DES_VIE | DMAC_DCMD_TIE;
desc->dsadr = dma_src_phys_addr + 12*1024; /* DMA source address */
desc->dtadr = dma_dst_phys_addr + 12*1024; /* DMA target address */
desc->ddadr = (next << 24) + 1024; /* size: 1024*4 bytes = 4096 bytes */
dma_cache_wback((unsigned long)dma_desc, 4*(sizeof(jz_dma_desc)));
/* Setup DMA descriptor address */
REG_DMAC_DDA(dma_chan) = dma_desc_phys_addr;
/* Setup request source */
REG_DMAC_DRSR(dma_chan) = DMAC_DRSR_RS_AUTO;
/* Setup DMA channel control/status register */
REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_EN; /* descriptor transfer, clear status, start channel */
/* Enable DMA */
REG_DMAC_DMACR = DMAC_DMACR_DMAE;
/* DMA doorbell set -- start DMA now ... */
REG_DMAC_DMADBSR = 1 << dma_chan;
printk("DMA started. IMR=%08x\n", REG_INTC_IMR);
}
#endif
//EXPORT_SYMBOL_NOVERS(jz_dma_table);
EXPORT_SYMBOL(jz_dma_table);
EXPORT_SYMBOL(jz_request_dma);
EXPORT_SYMBOL(jz_free_dma);
EXPORT_SYMBOL(jz_set_dma_src_width);
EXPORT_SYMBOL(jz_set_dma_dest_width);
EXPORT_SYMBOL(jz_set_dma_block_size);
EXPORT_SYMBOL(jz_set_dma_mode);
EXPORT_SYMBOL(set_dma_mode);
EXPORT_SYMBOL(jz_set_oss_dma);
EXPORT_SYMBOL(jz_set_alsa_dma);
EXPORT_SYMBOL(set_dma_addr);
EXPORT_SYMBOL(set_dma_count);
EXPORT_SYMBOL(get_dma_residue);
EXPORT_SYMBOL(enable_dma);
EXPORT_SYMBOL(disable_dma);
EXPORT_SYMBOL(dump_jz_dma_channel);

View File

@ -0,0 +1,297 @@
/*
* Copyright (C) 2009, Lars-Peter Clausen <lars@metafoo.de>
* JZ74xx platform GPIO support
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/bitops.h>
#include <asm/mach-jz4740/regs.h>
#define JZ_GPIO_BASE_A (32*0)
#define JZ_GPIO_BASE_B (32*1)
#define JZ_GPIO_BASE_C (32*2)
#define JZ_GPIO_BASE_D (32*3)
#define JZ_GPIO_NUM_A 32
#define JZ_GPIO_NUM_B 32
#define JZ_GPIO_NUM_C 31
#define JZ_GPIO_NUM_D 28
#define JZ_IRQ_GPIO_BASE_A JZ_IRQ_GPIO(0) + JZ_GPIO_BASE_A
#define JZ_IRQ_GPIO_BASE_B JZ_IRQ_GPIO(0) + JZ_GPIO_BASE_B
#define JZ_IRQ_GPIO_BASE_C JZ_IRQ_GPIO(0) + JZ_GPIO_BASE_C
#define JZ_IRQ_GPIO_BASE_D JZ_IRQ_GPIO(0) + JZ_GPIO_BASE_D
#define JZ_IRQ_GPIO_A(num) (num < JZ_GPIO_NUM_A ? JZ_IRQ_GPIO_BASE_A + num : -EINVAL)
#define JZ_IRQ_GPIO_B(num) (num < JZ_GPIO_NUM_B ? JZ_IRQ_GPIO_BASE_B + num : -EINVAL)
#define JZ_IRQ_GPIO_C(num) (num < JZ_GPIO_NUM_C ? JZ_IRQ_GPIO_BASE_C + num : -EINVAL)
#define JZ_IRQ_GPIO_D(num) (num < JZ_GPIO_NUM_D ? JZ_IRQ_GPIO_BASE_D + num : -EINVAL)
#define CHIP_TO_REG(chip, reg) (jz_gpio_base + (((chip)->base) << 3) + reg)
#define CHIP_TO_PIN_REG(chip) CHIP_TO_REG(chip, 0x00)
#define CHIP_TO_DATA_REG(chip) CHIP_TO_REG(chip, 0x10)
#define CHIP_TO_DATA_SET_REG(chip) CHIP_TO_REG(chip, 0x14)
#define CHIP_TO_DATA_CLEAR_REG(chip) CHIP_TO_REG(chip, 0x18)
#define CHIP_TO_PULL_REG(chip) CHIP_TO_REG(chip, 0x30)
#define CHIP_TO_PULL_SET_REG(chip) CHIP_TO_REG(chip, 0x34)
#define CHIP_TO_PULL_CLEAR_REG(chip) CHIP_TO_REG(chip, 0x38)
#define CHIP_TO_DATA_SELECT_REG(chip) CHIP_TO_REG(chip, 0x50)
#define CHIP_TO_DATA_SELECT_SET_REG(chip) CHIP_TO_REG(chip, 0x54)
#define CHIP_TO_DATA_SELECT_CLEAR_REG(chip) CHIP_TO_REG(chip, 0x58)
#define CHIP_TO_DATA_DIRECION_REG(chip) CHIP_TO_REG(chip, 0x60)
#define CHIP_TO_DATA_DIRECTION_SET_REG(chip) CHIP_TO_REG(chip, 0x64)
#define CHIP_TO_DATA_DIRECTION_CLEAR_REG(chip) CHIP_TO_REG(chip, 0x68)
#define GPIO_TO_REG(gpio, reg) (jz_gpio_base + ((gpio >> 5) << 8) + reg)
#define GPIO_TO_PULL_REG(gpio) GPIO_TO_REG(gpio, 0x30)
#define GPIO_TO_PULL_SET_REG(gpio) GPIO_TO_REG(gpio, 0x34)
#define GPIO_TO_PULL_CLEAR_REG(gpio) GPIO_TO_REG(gpio, 0x38)
static void __iomem *jz_gpio_base;
static spinlock_t jz_gpio_lock;
struct jz_gpio_chip {
unsigned int irq_base;
struct gpio_chip gpio_chip;
struct irq_chip irq_chip;
};
void jz_gpio_enable_pullup(unsigned gpio)
{
writel(BIT(gpio & 0x1f), GPIO_TO_PULL_CLEAR_REG(gpio));
}
EXPORT_SYMBOL_GPL(jz_gpio_enable_pullup);
void jz_gpio_disable_pullup(unsigned gpio)
{
writel(BIT(gpio & 0x1f), GPIO_TO_PULL_SET_REG(gpio));
}
EXPORT_SYMBOL_GPL(jz_gpio_disable_pullup);
void jz_gpio_set_function(unsigned gpio, unsigned function)
{
}
EXPORT_SYMBOL_GPL(jz_gpio_set_function);
static int jz_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
{
return !!(readl(CHIP_TO_PIN_REG(chip)) & BIT(gpio));
}
static void jz_gpio_set_value(struct gpio_chip *chip, unsigned gpio, int value)
{
uint32_t __iomem *reg = CHIP_TO_DATA_SET_REG(chip) + ((!value) << 2);
writel(BIT(gpio), reg);
}
static int jz_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, int value)
{
writel(BIT(gpio), CHIP_TO_DATA_DIRECTION_SET_REG(chip));
jz_gpio_set_value(chip, gpio, value);
return 0;
}
static int jz_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
{
writel(BIT(gpio), CHIP_TO_DATA_DIRECTION_CLEAR_REG(chip));
return 0;
}
#define IRQ_TO_GPIO(irq) (irq - JZ_IRQ_GPIO(0))
#define IRQ_TO_BIT(irq) BIT(IRQ_TO_GPIO(irq) & 0x1f)
#define IRQ_TO_REG(irq, reg) GPIO_TO_REG(IRQ_TO_GPIO(irq), reg)
#define IRQ_TO_MASK_REG(irq) IRQ_TO_REG(irq, 0x20)
#define IRQ_TO_MASK_SET_REG(irq) IRQ_TO_REG(irq, 0x24)
#define IRQ_TO_MASK_CLEAR_REG(irq) IRQ_TO_REG(irq, 0x28)
#define IRQ_TO_SELECT_REG(irq) IRQ_TO_REG(irq, 0x50)
#define IRQ_TO_SELECT_SET_REG(irq) IRQ_TO_REG(irq, 0x54)
#define IRQ_TO_SELECT_CLEAR_REG(irq) IRQ_TO_REG(irq, 0x58)
#define IRQ_TO_DIRECTION_REG(irq) IRQ_TO_REG(irq, 0x60)
#define IRQ_TO_DIRECTION_SET_REG(irq) IRQ_TO_REG(irq, 0x64)
#define IRQ_TO_DIRECTION_CLEAR_REG(irq) IRQ_TO_REG(irq, 0x68)
#define IRQ_TO_TRIGGER_REG(irq) IRQ_TO_REG(irq, 0x70)
#define IRQ_TO_TRIGGER_SET_REG(irq) IRQ_TO_REG(irq, 0x74)
#define IRQ_TO_TRIGGER_CLEAR_REG(irq) IRQ_TO_REG(irq, 0x78)
#define IRQ_TO_FLAG_REG(irq) IRQ_TO_REG(irq, 0x80)
#define IRQ_TO_FLAG_CLEAR_REG(irq) IRQ_TO_REG(irq, 0x14)
static void jz_gpio_irq_demux_handler(unsigned int irq, struct irq_desc *desc)
{
uint32_t flag;
unsigned int gpio_irq;
unsigned int gpio_bank;
gpio_bank = IRQ_GPIO0 - irq;
flag = readl(jz_gpio_base + (gpio_bank << 8) + 0x80);
gpio_irq = ffs(flag);
gpio_irq += (gpio_bank << 5) + JZ_IRQ_GPIO(0) - 1;
generic_handle_irq(gpio_irq);
};
/* TODO: Check if function is gpio */
static unsigned int jz_gpio_irq_startup(unsigned int irq)
{
writel(IRQ_TO_BIT(irq), IRQ_TO_SELECT_SET_REG(irq));
spin_lock(&jz_gpio_lock);
writel(IRQ_TO_BIT(irq), IRQ_TO_MASK_CLEAR_REG(irq));
spin_unlock(&jz_gpio_lock);
return 0;
}
static void jz_gpio_irq_shutdown(unsigned int irq)
{
spin_lock(&jz_gpio_lock);
writel(IRQ_TO_BIT(irq), IRQ_TO_MASK_SET_REG(irq));
spin_unlock(&jz_gpio_lock);
/* Set direction to input */
writel(IRQ_TO_BIT(irq), IRQ_TO_DIRECTION_CLEAR_REG(irq));
writel(IRQ_TO_BIT(irq), IRQ_TO_SELECT_CLEAR_REG(irq));
}
static void jz_gpio_irq_mask(unsigned int irq)
{
writel(IRQ_TO_BIT(irq), IRQ_TO_MASK_SET_REG(irq));
};
static void jz_gpio_irq_unmask(unsigned int irq)
{
writel(IRQ_TO_BIT(irq), IRQ_TO_MASK_CLEAR_REG(irq));
};
static void jz_gpio_irq_ack(unsigned int irq)
{
writel(IRQ_TO_BIT(irq), IRQ_TO_FLAG_CLEAR_REG(irq));
};
static int jz_gpio_irq_set_type(unsigned int irq, unsigned int flow_type)
{
uint32_t mask;
spin_lock(&jz_gpio_lock);
mask = readl(IRQ_TO_MASK_REG(irq));
writel(IRQ_TO_BIT(irq), IRQ_TO_MASK_CLEAR_REG(irq));
switch(flow_type) {
case IRQ_TYPE_EDGE_RISING:
case IRQ_TYPE_EDGE_BOTH:
writel(IRQ_TO_BIT(irq), IRQ_TO_DIRECTION_SET_REG(irq));
writel(IRQ_TO_BIT(irq), IRQ_TO_TRIGGER_SET_REG(irq));
break;
case IRQ_TYPE_EDGE_FALLING:
writel(IRQ_TO_BIT(irq), IRQ_TO_DIRECTION_CLEAR_REG(irq));
writel(IRQ_TO_BIT(irq), IRQ_TO_TRIGGER_SET_REG(irq));
break;
case IRQ_TYPE_LEVEL_HIGH:
writel(IRQ_TO_BIT(irq), IRQ_TO_DIRECTION_SET_REG(irq));
writel(IRQ_TO_BIT(irq), IRQ_TO_TRIGGER_CLEAR_REG(irq));
break;
case IRQ_TYPE_LEVEL_LOW:
writel(IRQ_TO_BIT(irq), IRQ_TO_DIRECTION_CLEAR_REG(irq));
writel(IRQ_TO_BIT(irq), IRQ_TO_TRIGGER_CLEAR_REG(irq));
break;
default:
return -EINVAL;
}
writel(mask, IRQ_TO_MASK_SET_REG(irq));
spin_unlock(&jz_gpio_lock);
return 0;
}
int gpio_to_irq(unsigned gpio)
{
return JZ_IRQ_GPIO(0) + gpio;
}
EXPORT_SYMBOL_GPL(gpio_to_irq);
int irq_to_gpio(unsigned gpio)
{
return IRQ_TO_GPIO(gpio);
}
EXPORT_SYMBOL_GPL(irq_to_gpio);
#define JZ_GPIO_CHIP(_bank) { \
.irq_base = JZ_IRQ_GPIO_BASE_ ## _bank, \
.gpio_chip = { \
.label = "Bank " # _bank, \
.owner = THIS_MODULE, \
.set = jz_gpio_set_value, \
.get = jz_gpio_get_value, \
.direction_output = jz_gpio_direction_output, \
.direction_input = jz_gpio_direction_input, \
.base = JZ_GPIO_BASE_ ## _bank, \
.ngpio = JZ_GPIO_NUM_ ## _bank, \
}, \
.irq_chip = { \
.name = "GPIO Bank " # _bank, \
.mask = jz_gpio_irq_mask, \
.unmask = jz_gpio_irq_unmask, \
.ack = jz_gpio_irq_ack, \
.startup = jz_gpio_irq_startup, \
.shutdown = jz_gpio_irq_shutdown, \
.set_type = jz_gpio_irq_set_type, \
}, \
}
static struct jz_gpio_chip jz_gpio_chips[] = {
JZ_GPIO_CHIP(A),
JZ_GPIO_CHIP(B),
JZ_GPIO_CHIP(C),
JZ_GPIO_CHIP(D),
};
__init int jz_gpiolib_init(void)
{
struct jz_gpio_chip *chip = jz_gpio_chips;
int i, irq;
jz_gpio_base = ioremap(0x10010000, 0x400);
for (i = 0; i < ARRAY_SIZE(jz_gpio_chips); ++i, ++chip) {
gpiochip_add(&chip->gpio_chip);
enable_irq(JZ_IRQ_INTC_GPIO(i));
set_irq_chained_handler(JZ_IRQ_INTC_GPIO(i), jz_gpio_irq_demux_handler);
for (irq = chip->irq_base; irq < chip->irq_base + chip->gpio_chip.ngpio;
++irq)
set_irq_chip_and_handler(irq, &chip->irq_chip, handle_level_irq);
}
printk("JZ GPIO initalized\n");
return 0;
}

View File

@ -0,0 +1,273 @@
/*
* linux/arch/mips/jz4740/i2c.c
*
* Jz4740 I2C routines.
*
* Copyright (C) 2005,2006 Ingenic Semiconductor Inc.
* Author: <lhhuang@ingenic.cn>
*
* This program is free software; you can distribute it and/or modify it
* under the terms of the GNU General Public License (Version 2) as
* published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/addrspace.h>
#include <asm/jzsoc.h>
/* I2C protocol */
#define I2C_READ 1
#define I2C_WRITE 0
#define TIMEOUT 1000
/*
* I2C bus protocol basic routines
*/
static int i2c_put_data(unsigned char data)
{
unsigned int timeout = TIMEOUT*10;
__i2c_write(data);
__i2c_set_drf();
while (__i2c_check_drf() != 0);
while (!__i2c_transmit_ended());
while (!__i2c_received_ack() && timeout)
timeout--;
if (timeout)
return 0;
else
return -ETIMEDOUT;
}
#ifdef CONFIG_JZ_TPANEL_ATA2508
static int i2c_put_data_nack(unsigned char data)
{
unsigned int timeout = TIMEOUT*10;
__i2c_write(data);
__i2c_set_drf();
while (__i2c_check_drf() != 0);
while (!__i2c_transmit_ended());
while (timeout--);
return 0;
}
#endif
static int i2c_get_data(unsigned char *data, int ack)
{
int timeout = TIMEOUT*10;
if (!ack)
__i2c_send_nack();
else
__i2c_send_ack();
while (__i2c_check_drf() == 0 && timeout)
timeout--;
if (timeout) {
if (!ack)
__i2c_send_stop();
*data = __i2c_read();
__i2c_clear_drf();
return 0;
} else
return -ETIMEDOUT;
}
/*
* I2C interface
*/
void i2c_open(void)
{
__i2c_set_clk(jz_clocks.extalclk, 10000); /* default 10 KHz */
__i2c_enable();
}
void i2c_close(void)
{
udelay(300); /* wait for STOP goes over. */
__i2c_disable();
}
void i2c_setclk(unsigned int i2cclk)
{
__i2c_set_clk(jz_clocks.extalclk, i2cclk);
}
int i2c_lseek(unsigned char device, unsigned char offset)
{
__i2c_send_nack(); /* Master does not send ACK, slave sends it */
__i2c_send_start();
if (i2c_put_data( (device << 1) | I2C_WRITE ) < 0)
goto device_err;
if (i2c_put_data(offset) < 0)
goto address_err;
return 0;
device_err:
printk(KERN_DEBUG "No I2C device (0x%02x) installed.\n", device);
__i2c_send_stop();
return -ENODEV;
address_err:
printk(KERN_DEBUG "No I2C device (0x%02x) response.\n", device);
__i2c_send_stop();
return -EREMOTEIO;
}
int i2c_read(unsigned char device, unsigned char *buf,
unsigned char address, int count)
{
int cnt = count;
int timeout = 5;
L_try_again:
if (timeout < 0)
goto L_timeout;
__i2c_send_nack(); /* Master does not send ACK, slave sends it */
__i2c_send_start();
if (i2c_put_data( (device << 1) | I2C_WRITE ) < 0)
goto device_werr;
if (i2c_put_data(address) < 0)
goto address_err;
__i2c_send_start();
if (i2c_put_data( (device << 1) | I2C_READ ) < 0)
goto device_rerr;
__i2c_send_ack(); /* Master sends ACK for continue reading */
while (cnt) {
if (cnt == 1) {
if (i2c_get_data(buf, 0) < 0)
break;
} else {
if (i2c_get_data(buf, 1) < 0)
break;
}
cnt--;
buf++;
}
__i2c_send_stop();
return count - cnt;
device_rerr:
device_werr:
address_err:
timeout --;
__i2c_send_stop();
goto L_try_again;
L_timeout:
__i2c_send_stop();
printk("Read I2C device 0x%2x failed.\n", device);
return -ENODEV;
}
int i2c_write(unsigned char device, unsigned char *buf,
unsigned char address, int count)
{
int cnt = count;
int cnt_in_pg;
int timeout = 5;
unsigned char *tmpbuf;
unsigned char tmpaddr;
__i2c_send_nack(); /* Master does not send ACK, slave sends it */
W_try_again:
if (timeout < 0)
goto W_timeout;
cnt = count;
tmpbuf = (unsigned char *)buf;
tmpaddr = address;
start_write_page:
cnt_in_pg = 0;
__i2c_send_start();
if (i2c_put_data( (device << 1) | I2C_WRITE ) < 0)
goto device_err;
#ifdef CONFIG_JZ_TPANEL_ATA2508
if (address == 0xff) {
if (i2c_put_data_nack(tmpaddr) < 0)
goto address_err;
while (cnt) {
if (++cnt_in_pg > 8) {
__i2c_send_stop();
mdelay(1);
tmpaddr += 8;
goto start_write_page;
}
if (i2c_put_data_nack(*tmpbuf) < 0)
break;
cnt--;
tmpbuf++;
}
}
else {
if (i2c_put_data(tmpaddr) < 0)
goto address_err;
while (cnt) {
if (++cnt_in_pg > 8) {
__i2c_send_stop();
mdelay(1);
tmpaddr += 8;
goto start_write_page;
}
if (i2c_put_data(*tmpbuf) < 0)
break;
cnt--;
tmpbuf++;
}
}
#else
if (i2c_put_data(tmpaddr) < 0)
goto address_err;
while (cnt) {
if (++cnt_in_pg > 8) {
__i2c_send_stop();
mdelay(1);
tmpaddr += 8;
goto start_write_page;
}
if (i2c_put_data(*tmpbuf) < 0)
break;
cnt--;
tmpbuf++;
}
#endif
__i2c_send_stop();
return count - cnt;
device_err:
address_err:
timeout--;
__i2c_send_stop();
goto W_try_again;
W_timeout:
printk(KERN_DEBUG "Write I2C device 0x%2x failed.\n", device);
__i2c_send_stop();
return -ENODEV;
}
EXPORT_SYMBOL(i2c_open);
EXPORT_SYMBOL(i2c_close);
EXPORT_SYMBOL(i2c_setclk);
EXPORT_SYMBOL(i2c_read);
EXPORT_SYMBOL(i2c_write);

View File

@ -0,0 +1,150 @@
/*
* linux/arch/mips/jz4740/irq.c
*
* JZ4740 interrupt routines.
*
* Copyright (c) 2006-2007 Ingenic Semiconductor Inc.
* Author: <lhhuang@ingenic.cn>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/kernel_stat.h>
#include <linux/module.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/timex.h>
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/delay.h>
#include <linux/bitops.h>
#include <asm/bootinfo.h>
#include <asm/io.h>
#include <asm/mipsregs.h>
#include <asm/system.h>
#include <asm/jzsoc.h>
static void __iomem *jz_intc_base;
#define JZ_REG_BASE_INTC 0x10001000
#define JZ_REG_INTC_STATUS 0x00
#define JZ_REG_INTC_MASK 0x04
#define JZ_REG_INTC_SET_MASK 0x08
#define JZ_REG_INTC_CLEAR_MASK 0x0c
#define JZ_REG_INTC_PENDING 0x10
/*
* INTC irq type
*/
static void intc_irq_unmask(unsigned int irq)
{
writel(BIT(irq), jz_intc_base + JZ_REG_INTC_CLEAR_MASK);
}
static void intc_irq_mask(unsigned int irq)
{
writel(BIT(irq), jz_intc_base + JZ_REG_INTC_SET_MASK);
}
static void intc_irq_ack(unsigned int irq)
{
writel(BIT(irq), jz_intc_base + JZ_REG_INTC_PENDING);
}
static void intc_irq_end(unsigned int irq)
{
if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) {
intc_irq_unmask(irq);
}
}
static struct irq_chip intc_irq_type = {
.name = "INTC",
.mask = intc_irq_mask,
.unmask = intc_irq_unmask,
.ack = intc_irq_ack,
.end = intc_irq_end,
};
/*
* DMA irq type
*/
static void enable_dma_irq(unsigned int irq)
{
__intc_unmask_irq(IRQ_DMAC);
__dmac_channel_enable_irq(irq - IRQ_DMA_0);
}
static void disable_dma_irq(unsigned int irq)
{
__dmac_channel_disable_irq(irq - IRQ_DMA_0);
}
static void mask_and_ack_dma_irq(unsigned int irq)
{
__intc_ack_irq(IRQ_DMAC);
__dmac_channel_disable_irq(irq - IRQ_DMA_0);
}
static void end_dma_irq(unsigned int irq)
{
if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) {
enable_dma_irq(irq);
}
}
static struct irq_chip dma_irq_type = {
.name = "DMA",
.unmask = enable_dma_irq,
.mask = disable_dma_irq,
.ack = mask_and_ack_dma_irq,
.end = end_dma_irq,
};
//----------------------------------------------------------------------
void __init arch_init_irq(void)
{
int i;
clear_c0_status(0xff04); /* clear ERL */
set_c0_status(0x0400); /* set IP2 */
jz_intc_base = ioremap(JZ_REG_BASE_INTC, 0x14);
for (i = 0; i < 32; i++) {
intc_irq_mask(i);
set_irq_chip_and_handler(i, &intc_irq_type, handle_level_irq);
}
/* Set up DMAC irq
*/
for (i = 0; i < NUM_DMA; i++) {
disable_dma_irq(IRQ_DMA_0 + i);
set_irq_chip_and_handler(IRQ_DMA_0 + i, &dma_irq_type, handle_level_irq);
}
}
asmlinkage void plat_irq_dispatch(void)
{
uint32_t irq_reg;
irq_reg = readl(jz_intc_base + JZ_REG_INTC_PENDING);
if (irq_reg)
do_IRQ(ffs(irq_reg) - 1);
}

View File

@ -0,0 +1,340 @@
/*
* Platform device support for Jz4740 SoC.
*
* Copyright 2007, <yliu@ingenic.cn>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/resource.h>
#include <linux/mtd/jz4740_nand.h>
#include <linux/input/matrix_keypad.h>
#include <asm/jzsoc.h>
/* OHCI (USB full speed host controller) */
static struct resource jz_usb_ohci_resources[] = {
[0] = {
.start = CPHYSADDR(UHC_BASE), // phys addr for ioremap
.end = CPHYSADDR(UHC_BASE) + 0x10000 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_UHC,
.end = IRQ_UHC,
.flags = IORESOURCE_IRQ,
},
};
/* The dmamask must be set for OHCI to work */
static u64 ohci_dmamask = ~(u32)0;
static struct platform_device jz_usb_ohci_device = {
.name = "jz-ohci",
.id = 0,
.dev = {
.dma_mask = &ohci_dmamask,
.coherent_dma_mask = 0xffffffff,
},
.num_resources = ARRAY_SIZE(jz_usb_ohci_resources),
.resource = jz_usb_ohci_resources,
};
/*** LCD controller ***/
static struct resource jz_lcd_resources[] = {
[0] = {
.start = CPHYSADDR(LCD_BASE),
.end = CPHYSADDR(LCD_BASE) + 0x10000 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_LCD,
.end = IRQ_LCD,
.flags = IORESOURCE_IRQ,
}
};
static u64 jz_lcd_dmamask = ~(u32)0;
static struct platform_device jz_lcd_device = {
.name = "jz-lcd",
.id = 0,
.dev = {
.dma_mask = &jz_lcd_dmamask,
.coherent_dma_mask = 0xffffffff,
},
.num_resources = ARRAY_SIZE(jz_lcd_resources),
.resource = jz_lcd_resources,
};
/* UDC (USB gadget controller) */
static struct resource jz_usb_gdt_resources[] = {
[0] = {
.start = CPHYSADDR(UDC_BASE),
.end = CPHYSADDR(UDC_BASE) + 0x10000 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_UDC,
.end = IRQ_UDC,
.flags = IORESOURCE_IRQ,
},
};
static u64 udc_dmamask = ~(u32)0;
static struct platform_device jz_usb_gdt_device = {
.name = "jz-udc",
.id = 0,
.dev = {
.dma_mask = &udc_dmamask,
.coherent_dma_mask = 0xffffffff,
},
.num_resources = ARRAY_SIZE(jz_usb_gdt_resources),
.resource = jz_usb_gdt_resources,
};
/** MMC/SD controller **/
static struct resource jz_mmc_resources[] = {
[0] = {
.start = CPHYSADDR(MSC_BASE),
.end = CPHYSADDR(MSC_BASE) + 0x10000 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_MSC,
.end = IRQ_MSC,
.flags = IORESOURCE_IRQ,
}
};
static u64 jz_mmc_dmamask = ~(u32)0;
static struct platform_device jz_mmc_device = {
.name = "jz-mmc",
.id = 0,
.dev = {
.dma_mask = &jz_mmc_dmamask,
.coherent_dma_mask = 0xffffffff,
},
.num_resources = ARRAY_SIZE(jz_mmc_resources),
.resource = jz_mmc_resources,
};
/** I2C controller **/
static struct resource jz_i2c_resources[] = {
[0] = {
.start = CPHYSADDR(I2C_BASE),
.end = CPHYSADDR(I2C_BASE) + 0x10000 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_I2C,
.end = IRQ_I2C,
.flags = IORESOURCE_IRQ,
}
};
static u64 jz_i2c_dmamask = ~(u32)0;
static struct platform_device jz_i2c_device = {
.name = "jz_i2c",
.id = 0,
.dev = {
.dma_mask = &jz_i2c_dmamask,
.coherent_dma_mask = 0xffffffff,
},
.num_resources = ARRAY_SIZE(jz_i2c_resources),
.resource = jz_i2c_resources,
};
static struct resource jz_nand_resources[] = {
[0] = {
.start = CPHYSADDR(EMC_BASE),
.end = CPHYSADDR(EMC_BASE) + 0x10000 - 1,
.flags = IORESOURCE_MEM,
},
};
static struct nand_ecclayout qi_lb60_ecclayout = {
.eccbytes = 36,
.eccpos = {
6, 7, 8, 9, 10, 11, 12, 13,
14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37,
38, 39, 40, 41},
.oobfree = {
{.offset = 0,
.length = 6},
{.offset = 42,
.length = 22}}
};
static struct mtd_partition qi_lb60_partitions[] = {
{ .name = "NAND BOOT partition",
.offset = 0 * 0x100000,
.size = 4 * 0x100000,
},
{ .name = "NAND KERNEL partition",
.offset = 4 * 0x100000,
.size = 4 * 0x100000,
},
{ .name = "NAND ROOTFS partition",
.offset = 8 * 0x100000,
.size = 20 * 0x100000,
},
{ .name = "NAND DATA partition",
.offset = 100 * 0x100000,
.size = 20 * 0x100000,
},
};
static struct jz_nand_platform_data jz_nand_platform_data = {
.num_partitions = ARRAY_SIZE(qi_lb60_partitions),
.partitions = qi_lb60_partitions,
.ecc_layout = &qi_lb60_ecclayout,
.busy_gpio = 94,
};
static struct platform_device jz_nand_device = {
.name = "jz4740-nand",
.num_resources = ARRAY_SIZE(jz_nand_resources),
.resource = jz_nand_resources,
.dev = {
.platform_data = &jz_nand_platform_data,
}
};
#define KEEP_UART_ALIVE
#define KEY_QI_QI KEY_F9
#define KEY_QI_UPBIG KEY_F10
#define KEY_QI_DOWNBIG KEY_F11
#define KEY_QI_UPRED KEY_F12
#define KEY_QI_VOLUP KEY_F13
#define KEY_QI_VOLDOWN KEY_F14
static const uint32_t qi_lb60_keymap[] = {
KEY(0, 0, KEY_F1), /* S2 */
KEY(0, 1, KEY_F2), /* S3 */
KEY(0, 2, KEY_F3), /* S4 */
KEY(0, 3, KEY_F4), /* S5 */
KEY(0, 4, KEY_F5), /* S6 */
KEY(0, 5, KEY_F6), /* S7 */
KEY(0, 6, KEY_F7), /* S8 */
KEY(1, 0, KEY_Q), /* S10 */
KEY(1, 1, KEY_W), /* S11 */
KEY(1, 2, KEY_E), /* S12 */
KEY(1, 3, KEY_R), /* S13 */
KEY(1, 4, KEY_T), /* S14 */
KEY(1, 5, KEY_Y), /* S15 */
KEY(1, 6, KEY_U), /* S16 */
KEY(1, 7, KEY_I), /* S17 */
KEY(2, 0, KEY_A), /* S18 */
KEY(2, 1, KEY_S), /* S19 */
KEY(2, 2, KEY_D), /* S20 */
KEY(2, 3, KEY_F), /* S21 */
KEY(2, 4, KEY_G), /* S22 */
KEY(2, 5, KEY_H), /* S23 */
KEY(2, 6, KEY_J), /* S24 */
KEY(2, 7, KEY_K), /* S25 */
KEY(3, 0, KEY_ESC), /* S26 */
KEY(3, 1, KEY_Z), /* S27 */
KEY(3, 2, KEY_X), /* S28 */
KEY(3, 3, KEY_C), /* S29 */
KEY(3, 4, KEY_V), /* S30 */
KEY(3, 5, KEY_B), /* S31 */
KEY(3, 6, KEY_N), /* S32 */
KEY(3, 7, KEY_M), /* S33 */
KEY(4, 0, KEY_TAB), /* S34 */
KEY(4, 1, KEY_QI_DOWNBIG), /* S35 */
KEY(4, 2, KEY_BACKSLASH), /* S36 */
KEY(4, 3, KEY_APOSTROPHE), /* S37 */
KEY(4, 4, KEY_COMMA), /* S38 */
KEY(4, 5, KEY_DOT), /* S39 */
KEY(4, 6, KEY_SLASH), /* S40 */
KEY(4, 7, KEY_UP), /* S41 */
KEY(5, 0, KEY_O), /* S42 */
KEY(5, 1, KEY_L), /* S43 */
KEY(5, 2, KEY_EQUAL), /* S44 */
KEY(5, 3, KEY_QI_UPRED), /* S45 */
KEY(5, 4, KEY_SPACE), /* S46 */
KEY(5, 5, KEY_QI_QI), /* S47 */
KEY(5, 6, KEY_LEFTCTRL), /* S48 */
KEY(5, 7, KEY_LEFT), /* S49 */
KEY(6, 0, KEY_F8), /* S50 */
KEY(6, 1, KEY_P), /* S51 */
KEY(6, 2, KEY_BACKSPACE),/* S52 */
KEY(6, 3, KEY_ENTER), /* S53 */
KEY(6, 4, KEY_QI_VOLUP), /* S54 */
KEY(6, 5, KEY_QI_VOLDOWN), /* S55 */
KEY(6, 6, KEY_DOWN), /* S56 */
KEY(6, 7, KEY_RIGHT), /* S57 */
#ifndef KEEP_UART_ALIVE
KEY(7, 0, KEY_QI_UPBIG), /* S58 */
KEY(7, 1, KEY_LEFTALT), /* S59 */
KEY(7, 2, KEY_FN), /* S60 */
#endif
};
static const struct matrix_keymap_data qi_lb60_keymap_data = {
.keymap = qi_lb60_keymap,
.keymap_size = ARRAY_SIZE(qi_lb60_keymap),
};
static const unsigned int qi_lb60_keypad_cols[] = {
74, 75, 76, 77, 78, 79, 80, 81,
};
static const unsigned int qi_lb60_keypad_rows[] = {
114, 115, 116, 117, 118, 119, 120,
#ifndef KEEP_UART_ALIVE
122,
#endif
};
static struct matrix_keypad_platform_data qi_lb60_pdata = {
.keymap_data = &qi_lb60_keymap_data,
.col_gpios = qi_lb60_keypad_cols,
.row_gpios = qi_lb60_keypad_rows,
.num_col_gpios = ARRAY_SIZE(qi_lb60_keypad_cols),
.num_row_gpios = ARRAY_SIZE(qi_lb60_keypad_rows),
.col_scan_delay_us = 10,
.debounce_ms = 10,
.wakeup = 1,
.active_low = 1,
};
static struct platform_device qi_lb60_keypad = {
.name = "matrix-keypad",
.id = -1,
.dev = {
.platform_data = &qi_lb60_pdata,
},
};
/* All */
static struct platform_device *jz_platform_devices[] __initdata = {
&jz_usb_ohci_device,
&jz_lcd_device,
&jz_usb_gdt_device,
&jz_mmc_device,
&jz_nand_device,
&jz_i2c_device,
&qi_lb60_keypad,
};
static int __init jz_platform_init(void)
{
return platform_add_devices(jz_platform_devices, ARRAY_SIZE(jz_platform_devices));
}
arch_initcall(jz_platform_init);

View File

@ -0,0 +1,410 @@
/*
* linux/arch/mips/jz4740/common/pm.c
*
* JZ4740 Power Management Routines
*
* Copyright (C) 2006 Ingenic Semiconductor Inc.
* Author: <jlwei@ingenic.cn>
*
* This program is free software; you can distribute it and/or modify it
* under the terms of the GNU General Public License (Version 2) as
* published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*
*/
#include <linux/init.h>
#include <linux/pm.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include <linux/sysctl.h>
#include <linux/suspend.h>
#include <asm/cacheops.h>
#include <asm/jzsoc.h>
#undef DEBUG
//#define DEBUG
#ifdef DEBUG
#define dprintk(x...) printk(x)
#else
#define dprintk(x...)
#endif
#define GPIO_WAKEUP 125 /* set SW7(GPIO 125) as WAKEUP key */
/*
* __gpio_as_sleep set all pins to pull-disable, and set all pins as input
* except sdram, nand flash pins and the pins which can be used as CS1_N
* to CS4_N for chip select.
*/
#define __gpio_as_sleep() \
do { \
REG_GPIO_PXFUNC(1) = ~0x9ff9ffff; \
REG_GPIO_PXSELC(1) = ~0x9ff9ffff; \
REG_GPIO_PXDIRC(1) = ~0x9ff9ffff; \
REG_GPIO_PXPES(1) = 0xffffffff; \
REG_GPIO_PXFUNC(2) = ~0x37000000; \
REG_GPIO_PXSELC(2) = ~0x37000000; \
REG_GPIO_PXDIRC(2) = ~0x37000000; \
REG_GPIO_PXPES(2) = 0xffffffff; \
REG_GPIO_PXFUNC(3) = 0xffffffff; \
REG_GPIO_PXSELC(3) = 0xffffffff; \
REG_GPIO_PXDIRC(3) = 0xffffffff; \
REG_GPIO_PXPES(3) = 0xffffffff; \
} while (0)
static int jz_pm_do_hibernate(void)
{
/* Mask all interrupts */
REG_INTC_IMSR = 0xffffffff;
/*
* RTC Wakeup or 1Hz interrupt can be enabled or disabled
* through RTC driver's ioctl (linux/driver/char/rtc_jz.c).
*/
/* Set minimum wakeup_n pin low-level assertion time for wakeup: 100ms */
while (!(REG_RTC_RCR & RTC_RCR_WRDY));
REG_RTC_HWFCR = (100 << RTC_HWFCR_BIT);
/* Set reset pin low-level assertion time after wakeup: must > 60ms */
while (!(REG_RTC_RCR & RTC_RCR_WRDY));
REG_RTC_HRCR = (60 << RTC_HRCR_BIT); /* 60 ms */
/* Scratch pad register to be reserved */
while (!(REG_RTC_RCR & RTC_RCR_WRDY));
REG_RTC_HSPR = 0x12345678;
/* clear wakeup status register */
while (!(REG_RTC_RCR & RTC_RCR_WRDY));
REG_RTC_HWRSR = 0x0;
/* Put CPU to power down mode */
while (!(REG_RTC_RCR & RTC_RCR_WRDY));
REG_RTC_HCR = RTC_HCR_PD;
while (!(REG_RTC_RCR & RTC_RCR_WRDY));
while(1);
/* We can't get here */
return 0;
}
/* NOTES:
* 1: Pins that are floated (NC) should be set as input and pull-enable.
* 2: Pins that are pull-up or pull-down by outside should be set as input
* and pull-disable.
* 3: Pins that are connected to a chip except sdram and nand flash
* should be set as input and pull-disable, too.
*/
static void jz_board_do_sleep(unsigned long *ptr)
{
unsigned char i;
/* Print messages of GPIO registers for debug */
for(i=0;i<4;i++) {
dprintk("run dat:%x pin:%x fun:%x sel:%x dir:%x pull:%x msk:%x trg:%x\n", \
REG_GPIO_PXDAT(i),REG_GPIO_PXPIN(i),REG_GPIO_PXFUN(i),REG_GPIO_PXSEL(i), \
REG_GPIO_PXDIR(i),REG_GPIO_PXPE(i),REG_GPIO_PXIM(i),REG_GPIO_PXTRG(i));
}
/* Save GPIO registers */
for(i = 1; i < 4; i++) {
*ptr++ = REG_GPIO_PXFUN(i);
*ptr++ = REG_GPIO_PXSEL(i);
*ptr++ = REG_GPIO_PXDIR(i);
*ptr++ = REG_GPIO_PXPE(i);
*ptr++ = REG_GPIO_PXIM(i);
*ptr++ = REG_GPIO_PXDAT(i);
*ptr++ = REG_GPIO_PXTRG(i);
}
/*
* Set all pins to pull-disable, and set all pins as input except
* sdram, nand flash pins and the pins which can be used as CS1_N
* to CS4_N for chip select.
*/
__gpio_as_sleep();
/*
* Set proper status for GPB25 to GPB28 which can be used as CS1_N to CS4_N.
* Keep the pins' function used for chip select(CS) here according to your
* system to avoid chip select crashing with sdram when resuming from sleep mode.
*/
#if defined(CONFIG_JZ4740_PAVO)
/* GPB25/CS1_N is used as chip select for nand flash, shouldn't be change. */
/* GPB26/CS2_N is connected to nand flash, needn't be changed. */
/* GPB27/CS3_N is used as EXT_INT for CS8900 on debug board, it should be set as input.*/
__gpio_as_input(32+27);
/* GPB28/CS4_N is used as cs8900's chip select, shouldn't be changed. */
#endif
/*
* Enable pull for NC pins here according to your system
*/
#if defined(CONFIG_JZ4740_PAVO)
/* GPB30-27 <-> J1: WE_N RD_N CS4_N EXT_INT */
for(i=27;i<31;i++) {
__gpio_enable_pull(32+i);
}
/* GPC27<-> WAIT_N */
__gpio_enable_pull(32*2+27);
/* GPD16<->SD_WP; GPD13-10<->MSC_D0-3; GPD9<->MSC_CMD; GPD8<->MSC_CLK */
__gpio_enable_pull(32*3+16);
for(i=8;i<14;i++) {
__gpio_enable_pull(32*3+i);
}
#endif
/*
* If you must set some GPIOs as output to high level or low level,
* you can set them here, using:
* __gpio_as_output(n);
* __gpio_set_pin(n); or __gpio_clear_pin(n);
*/
#if defined(CONFIG_JZ4740_PAVO)
/* GPD16 which is used as AMPEN_N should be set to high to disable audio amplifier */
__gpio_set_pin(32*3+4);
#endif
#ifdef DEBUG
/* Keep uart0 function for printing debug message */
__gpio_as_uart0();
/* Print messages of GPIO registers for debug */
for(i=0;i<4;i++) {
dprintk("sleep dat:%x pin:%x fun:%x sel:%x dir:%x pull:%x msk:%x trg:%x\n", \
REG_GPIO_PXDAT(i),REG_GPIO_PXPIN(i),REG_GPIO_PXFUN(i),REG_GPIO_PXSEL(i), \
REG_GPIO_PXDIR(i),REG_GPIO_PXPE(i),REG_GPIO_PXIM(i),REG_GPIO_PXTRG(i));
}
#endif
}
static void jz_board_do_resume(unsigned long *ptr)
{
unsigned char i;
/* Restore GPIO registers */
for(i = 1; i < 4; i++) {
REG_GPIO_PXFUNS(i) = *ptr;
REG_GPIO_PXFUNC(i) = ~(*ptr++);
REG_GPIO_PXSELS(i) = *ptr;
REG_GPIO_PXSELC(i) = ~(*ptr++);
REG_GPIO_PXDIRS(i) = *ptr;
REG_GPIO_PXDIRC(i) = ~(*ptr++);
REG_GPIO_PXPES(i) = *ptr;
REG_GPIO_PXPEC(i) = ~(*ptr++);
REG_GPIO_PXIMS(i)=*ptr;
REG_GPIO_PXIMC(i)=~(*ptr++);
REG_GPIO_PXDATS(i)=*ptr;
REG_GPIO_PXDATC(i)=~(*ptr++);
REG_GPIO_PXTRGS(i)=*ptr;
REG_GPIO_PXTRGC(i)=~(*ptr++);
}
/* Print messages of GPIO registers for debug */
for(i=0;i<4;i++) {
dprintk("resume dat:%x pin:%x fun:%x sel:%x dir:%x pull:%x msk:%x trg:%x\n", \
REG_GPIO_PXDAT(i),REG_GPIO_PXPIN(i),REG_GPIO_PXFUN(i),REG_GPIO_PXSEL(i), \
REG_GPIO_PXDIR(i),REG_GPIO_PXPE(i),REG_GPIO_PXIM(i),REG_GPIO_PXTRG(i));
}
}
static int jz_pm_do_sleep(void)
{
unsigned long delta;
unsigned long nfcsr = REG_EMC_NFCSR;
unsigned long scr = REG_CPM_SCR;
unsigned long imr = REG_INTC_IMR;
unsigned long sadc = REG_SADC_ENA;
unsigned long sleep_gpio_save[7*3];
/* Preserve current time */
delta = xtime.tv_sec - REG_RTC_RSR;
/* Disable nand flash */
REG_EMC_NFCSR = ~0xff;
/* stop sadc */
REG_SADC_ENA &= ~0x7;
while((REG_SADC_ENA & 0x7) != 0);
udelay(100);
/*stop udc and usb*/
REG_CPM_SCR &= ~( 1<<6 | 1<<7);
REG_CPM_SCR |= 0<<6 | 1<<7;
/* Sleep on-board modules */
jz_board_do_sleep(sleep_gpio_save);
/* Mask all interrupts */
REG_INTC_IMSR = 0xffffffff;
/* Just allow following interrupts to wakeup the system.
* Note: modify this according to your system.
*/
/* enable RTC alarm */
__intc_unmask_irq(IRQ_RTC);
#if 0
/* make system wake up after n seconds by RTC alarm */
unsigned int v, n;
n = 10;
while (!__rtc_write_ready());
__rtc_enable_alarm();
while (!__rtc_write_ready());
__rtc_enable_alarm_irq();
while (!__rtc_write_ready());
v = __rtc_get_second();
while (!__rtc_write_ready());
__rtc_set_alarm_second(v+n);
#endif
/* WAKEUP key */
__gpio_as_irq_rise_edge(GPIO_WAKEUP);
__gpio_unmask_irq(GPIO_WAKEUP);
__intc_unmask_irq(IRQ_GPIO3); /* IRQ_GPIOn depends on GPIO_WAKEUP */
/* Enter SLEEP mode */
REG_CPM_LCR &= ~CPM_LCR_LPM_MASK;
REG_CPM_LCR |= CPM_LCR_LPM_SLEEP;
__asm__(".set\tmips3\n\t"
"wait\n\t"
".set\tmips0");
/* Restore to IDLE mode */
REG_CPM_LCR &= ~CPM_LCR_LPM_MASK;
REG_CPM_LCR |= CPM_LCR_LPM_IDLE;
/* Restore nand flash control register */
REG_EMC_NFCSR = nfcsr;
/* Restore interrupts */
REG_INTC_IMSR = imr;
REG_INTC_IMCR = ~imr;
/* Restore sadc */
REG_SADC_ENA = sadc;
/* Resume on-board modules */
jz_board_do_resume(sleep_gpio_save);
/* Restore sleep control register */
REG_CPM_SCR = scr;
/* Restore current time */
xtime.tv_sec = REG_RTC_RSR + delta;
return 0;
}
/* Put CPU to HIBERNATE mode */
int jz_pm_hibernate(void)
{
printk("Put CPU into hibernate mode.\n");
return jz_pm_do_hibernate();
}
#ifndef CONFIG_JZ_POWEROFF
static irqreturn_t pm_irq_handler (int irq, void *dev_id)
{
return IRQ_HANDLED;
}
#endif
/* Put CPU to SLEEP mode */
int jz_pm_sleep(void)
{
int retval;
#ifndef CONFIG_JZ_POWEROFF
if ((retval = request_irq (IRQ_GPIO_0 + GPIO_WAKEUP, pm_irq_handler, IRQF_DISABLED,
"PM", NULL))) {
printk ("PM could not get IRQ for GPIO_WAKEUP\n");
return retval;
}
#endif
printk("Put CPU into sleep mode.\n");
retval = jz_pm_do_sleep();
#ifndef CONFIG_JZ_POWEROFF
free_irq (IRQ_GPIO_0 + GPIO_WAKEUP, NULL);
#endif
return retval;
}
/*
* valid states, only support standby(sleep) and mem(hibernate)
*/
static int jz_pm_valid(suspend_state_t state)
{
switch (state) {
case PM_SUSPEND_STANDBY:
case PM_SUSPEND_MEM:
return 1;
default:
return 0;
}
}
/*
* Jz CPU enter save power mode
*/
static int jz_pm_enter(suspend_state_t state)
{
if (state == PM_SUSPEND_STANDBY)
jz_pm_sleep();
else
jz_pm_hibernate();
return 0;
}
static struct platform_suspend_ops jz_pm_ops = {
.valid = jz_pm_valid,
.enter = jz_pm_enter,
};
/*
* Initialize power interface
*/
int __init jz_pm_init(void)
{
printk("Power Management for JZ\n");
suspend_set_ops(&jz_pm_ops);
return 0;
}
//module_init(jz_pm_init);

View File

@ -0,0 +1,873 @@
/*
* linux/arch/mips/jz4740/proc.c
*
* /proc/jz/ procfs for jz4740 on-chip modules.
*
* Copyright (C) 2006 Ingenic Semiconductor Inc.
* Author: <jlwei@ingenic.cn>
*
* This program is free software; you can distribute it and/or modify it
* under the terms of the GNU General Public License (Version 2) as
* published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/page-flags.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/jzsoc.h>
//#define DEBUG 1
#undef DEBUG
struct proc_dir_entry *proc_jz_root;
/*
* EMC Modules
*/
static int emc_read_proc (char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len = 0;
len += sprintf (page+len, "SMCR(0-5): 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", REG_EMC_SMCR0, REG_EMC_SMCR1, REG_EMC_SMCR2, REG_EMC_SMCR3, REG_EMC_SMCR4);
len += sprintf (page+len, "SACR(0-5): 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", REG_EMC_SACR0, REG_EMC_SACR1, REG_EMC_SACR2, REG_EMC_SACR3, REG_EMC_SACR4);
len += sprintf (page+len, "DMCR: 0x%08x\n", REG_EMC_DMCR);
len += sprintf (page+len, "RTCSR: 0x%04x\n", REG_EMC_RTCSR);
len += sprintf (page+len, "RTCOR: 0x%04x\n", REG_EMC_RTCOR);
return len;
}
/*
* Power Manager Module
*/
static int pmc_read_proc (char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len = 0;
unsigned long lcr = REG_CPM_LCR;
unsigned long clkgr = REG_CPM_CLKGR;
len += sprintf (page+len, "Low Power Mode : %s\n",
((lcr & CPM_LCR_LPM_MASK) == (CPM_LCR_LPM_IDLE)) ?
"IDLE" : (((lcr & CPM_LCR_LPM_MASK) == (CPM_LCR_LPM_SLEEP)) ?
"SLEEP" : "HIBERNATE"));
len += sprintf (page+len, "Doze Mode : %s\n",
(lcr & CPM_LCR_DOZE_ON) ? "on" : "off");
if (lcr & CPM_LCR_DOZE_ON)
len += sprintf (page+len, " duty : %d\n", (int)((lcr & CPM_LCR_DOZE_DUTY_MASK) >> CPM_LCR_DOZE_DUTY_BIT));
len += sprintf (page+len, "IPU : %s\n",
(clkgr & CPM_CLKGR_IPU) ? "stopped" : "running");
len += sprintf (page+len, "DMAC : %s\n",
(clkgr & CPM_CLKGR_DMAC) ? "stopped" : "running");
len += sprintf (page+len, "UHC : %s\n",
(clkgr & CPM_CLKGR_UHC) ? "stopped" : "running");
len += sprintf (page+len, "UDC : %s\n",
(clkgr & CPM_CLKGR_UDC) ? "stopped" : "running");
len += sprintf (page+len, "LCD : %s\n",
(clkgr & CPM_CLKGR_LCD) ? "stopped" : "running");
len += sprintf (page+len, "CIM : %s\n",
(clkgr & CPM_CLKGR_CIM) ? "stopped" : "running");
len += sprintf (page+len, "SADC : %s\n",
(clkgr & CPM_CLKGR_SADC) ? "stopped" : "running");
len += sprintf (page+len, "MSC : %s\n",
(clkgr & CPM_CLKGR_MSC) ? "stopped" : "running");
len += sprintf (page+len, "AIC1 : %s\n",
(clkgr & CPM_CLKGR_AIC1) ? "stopped" : "running");
len += sprintf (page+len, "AIC2 : %s\n",
(clkgr & CPM_CLKGR_AIC2) ? "stopped" : "running");
len += sprintf (page+len, "SSI : %s\n",
(clkgr & CPM_CLKGR_SSI) ? "stopped" : "running");
len += sprintf (page+len, "I2C : %s\n",
(clkgr & CPM_CLKGR_I2C) ? "stopped" : "running");
len += sprintf (page+len, "RTC : %s\n",
(clkgr & CPM_CLKGR_RTC) ? "stopped" : "running");
len += sprintf (page+len, "TCU : %s\n",
(clkgr & CPM_CLKGR_TCU) ? "stopped" : "running");
len += sprintf (page+len, "UART1 : %s\n",
(clkgr & CPM_CLKGR_UART1) ? "stopped" : "running");
len += sprintf (page+len, "UART0 : %s\n",
(clkgr & CPM_CLKGR_UART0) ? "stopped" : "running");
return len;
}
static int pmc_write_proc(struct file *file, const char *buffer, unsigned long count, void *data)
{
REG_CPM_CLKGR = simple_strtoul(buffer, 0, 16);
return count;
}
/*
* Clock Generation Module
*/
#define TO_MHZ(x) (x/1000000),(x%1000000)/10000
#define TO_KHZ(x) (x/1000),(x%1000)/10
static int cgm_read_proc (char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len = 0;
unsigned int cppcr = REG_CPM_CPPCR; /* PLL Control Register */
unsigned int cpccr = REG_CPM_CPCCR; /* Clock Control Register */
unsigned int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32};
unsigned int od[4] = {1, 2, 2, 4};
len += sprintf (page+len, "CPPCR : 0x%08x\n", cppcr);
len += sprintf (page+len, "CPCCR : 0x%08x\n", cpccr);
len += sprintf (page+len, "PLL : %s\n",
(cppcr & CPM_CPPCR_PLLEN) ? "ON" : "OFF");
len += sprintf (page+len, "m:n:o : %d:%d:%d\n",
__cpm_get_pllm() + 2,
__cpm_get_plln() + 2,
od[__cpm_get_pllod()]
);
len += sprintf (page+len, "C:H:M:P : %d:%d:%d:%d\n",
div[__cpm_get_cdiv()],
div[__cpm_get_hdiv()],
div[__cpm_get_mdiv()],
div[__cpm_get_pdiv()]
);
len += sprintf (page+len, "PLL Freq : %3d.%02d MHz\n", TO_MHZ(__cpm_get_pllout()));
len += sprintf (page+len, "CCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_cclk()));
len += sprintf (page+len, "HCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_hclk()));
len += sprintf (page+len, "MCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_mclk()));
len += sprintf (page+len, "PCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_pclk()));
len += sprintf (page+len, "LCDCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_lcdclk()));
len += sprintf (page+len, "PIXCLK : %3d.%02d KHz\n", TO_KHZ(__cpm_get_pixclk()));
len += sprintf (page+len, "I2SCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_i2sclk()));
len += sprintf (page+len, "USBCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_usbclk()));
len += sprintf (page+len, "MSCCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_mscclk()));
len += sprintf (page+len, "EXTALCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_extalclk()));
len += sprintf (page+len, "RTCCLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_rtcclk()));
return len;
}
static int cgm_write_proc(struct file *file, const char *buffer, unsigned long count, void *data)
{
REG_CPM_CPCCR = simple_strtoul(buffer, 0, 16);
return count;
}
/* USAGE:
* echo n > /proc/jz/ipu // n = [1,...,9], alloc mem, 2^n pages.
* echo FF > /proc/jz/ipu // 255, free all buffer
* echo xxxx > /proc/jz/ipu // free buffer which addr is xxxx
* echo llll > /proc/jz/ipu // add_wired_entry(l,l,l,l)
* echo 0 > /proc/jz/ipu // debug, print ipu_buf
* od -X /proc/jz/ipu // read mem addr
*/
typedef struct _ipu_buf {
unsigned int addr; /* phys addr */
unsigned int page_shift;
} ipu_buf_t;
#define IPU_BUF_MAX 4 /* 4 buffers */
static struct _ipu_buf ipu_buf[IPU_BUF_MAX];
static int ipu_buf_cnt = 0;
static unsigned char g_asid=0;
extern void local_flush_tlb_all(void);
/* CP0 hazard avoidance. */
#define BARRIER __asm__ __volatile__(".set noreorder\n\t" \
"nop; nop; nop; nop; nop; nop;\n\t" \
".set reorder\n\t")
void show_tlb(void)
{
#define ASID_MASK 0xFF
unsigned long flags;
unsigned int old_ctx;
unsigned int entry;
unsigned int entrylo0, entrylo1, entryhi;
unsigned int pagemask;
local_irq_save(flags);
/* Save old context */
old_ctx = (read_c0_entryhi() & 0xff);
printk("TLB content:\n");
entry = 0;
while(entry < 32) {
write_c0_index(entry);
BARRIER;
tlb_read();
BARRIER;
entryhi = read_c0_entryhi();
entrylo0 = read_c0_entrylo0();
entrylo1 = read_c0_entrylo1();
pagemask = read_c0_pagemask();
printk("%02d: ASID=%02d%s VA=0x%08x ", entry, entryhi & ASID_MASK, (entrylo0 & entrylo1 & 1) ? "(G)" : " ", entryhi & ~ASID_MASK);
printk("PA0=0x%08x C0=%x %s%s%s\n", (entrylo0>>6)<<12, (entrylo0>>3) & 7, (entrylo0 & 4) ? "Dirty " : "", (entrylo0 & 2) ? "Valid " : "Invalid ", (entrylo0 & 1) ? "Global" : "");
printk("\t\t\t PA1=0x%08x C1=%x %s%s%s\n", (entrylo1>>6)<<12, (entrylo1>>3) & 7, (entrylo1 & 4) ? "Dirty " : "", (entrylo1 & 2) ? "Valid " : "Invalid ", (entrylo1 & 1) ? "Global" : "");
printk("\t\tpagemask=0x%08x", pagemask);
printk("\tentryhi=0x%08x\n", entryhi);
printk("\t\tentrylo0=0x%08x", entrylo0);
printk("\tentrylo1=0x%08x\n", entrylo1);
entry++;
}
BARRIER;
write_c0_entryhi(old_ctx);
local_irq_restore(flags);
}
static void ipu_add_wired_entry(unsigned long pid,
unsigned long entrylo0, unsigned long entrylo1,
unsigned long entryhi, unsigned long pagemask)
{
unsigned long flags;
unsigned long wired;
unsigned long old_pagemask;
unsigned long old_ctx;
struct task_struct *g, *p;
/* We will lock an 4MB page size entry to map the 4MB reserved IPU memory */
wired = read_c0_wired();
if (wired) return;
do_each_thread(g, p) {
if (p->pid == pid )
g_asid = p->mm->context[0];
} while_each_thread(g, p);
local_irq_save(flags);
entrylo0 = entrylo0 >> 6; /* PFN */
entrylo0 |= 0x6 | (0 << 3); /* Write-through cacheable, dirty, valid */
/* Save old context and create impossible VPN2 value */
old_ctx = read_c0_entryhi() & 0xff;
old_pagemask = read_c0_pagemask();
write_c0_wired(wired + 1);
write_c0_index(wired);
BARRIER;
entryhi &= ~0xff; /* new add, 20070906 */
entryhi |= g_asid; /* new add, 20070906 */
// entryhi |= old_ctx; /* new add, 20070906 */
write_c0_pagemask(pagemask);
write_c0_entryhi(entryhi);
write_c0_entrylo0(entrylo0);
write_c0_entrylo1(entrylo1);
BARRIER;
tlb_write_indexed();
BARRIER;
write_c0_entryhi(old_ctx);
BARRIER;
write_c0_pagemask(old_pagemask);
local_flush_tlb_all();
local_irq_restore(flags);
#if defined(DEBUG)
printk("\nold_ctx=%03d\n", old_ctx);
show_tlb();
#endif
}
static void ipu_del_wired_entry( void )
{
unsigned long flags;
unsigned long wired;
/* Free all lock entry */
local_irq_save(flags);
wired = read_c0_wired();
if (wired)
write_c0_wired(0);
local_irq_restore(flags);
}
static inline void ipu_buf_get( unsigned int page_shift )
{
unsigned char * virt_addr;
int i;
for ( i=0; i< IPU_BUF_MAX; ++i ) {
if ( ipu_buf[i].addr == 0 ) {
break;
}
}
if ( (ipu_buf_cnt = i) == IPU_BUF_MAX ) {
printk("Error, no free ipu buffer.\n");
return ;
}
virt_addr = (unsigned char *)__get_free_pages(GFP_KERNEL, page_shift);
if ( virt_addr ) {
ipu_buf[ipu_buf_cnt].addr = (unsigned int)virt_to_phys((void *)virt_addr);
ipu_buf[ipu_buf_cnt].page_shift = page_shift;
for (i = 0; i < (1<<page_shift); i++) {
SetPageReserved(virt_to_page(virt_addr));
virt_addr += PAGE_SIZE;
}
}
else {
printk("get memory Failed.\n");
}
}
static inline void ipu_buf_free( unsigned int phys_addr )
{
unsigned char * virt_addr, *addr;
int cnt, i;
if ( phys_addr == 0 )
return ;
for ( cnt=0; cnt<IPU_BUF_MAX; ++cnt )
if ( phys_addr == ipu_buf[cnt].addr )
break;
if ( cnt == IPU_BUF_MAX ) { /* addr not in the ipu buffers */
printk("Invalid addr:0x%08x\n", (unsigned int)phys_addr);
}
virt_addr = (unsigned char *)phys_to_virt(ipu_buf[cnt].addr);
addr = virt_addr;
for (i = 0; i < (1<<ipu_buf[cnt].page_shift); i++) {
ClearPageReserved(virt_to_page(addr));
addr += PAGE_SIZE;
}
if ( cnt == 0 )
ipu_del_wired_entry();
free_pages((unsigned long )virt_addr, ipu_buf[cnt].page_shift);
ipu_buf[cnt].addr = 0;
ipu_buf[cnt].page_shift = 0;
}
static int ipu_read_proc (char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len = 0;
/* read as binary */
unsigned int * pint;
pint = (unsigned int *) (page+len);
if ( ipu_buf_cnt >= IPU_BUF_MAX ) { /* failed alloc mem, rturn 0 */
printk("no free buffer.\n");
*pint = 0;
}
else
*pint = (unsigned int )ipu_buf[ipu_buf_cnt].addr; /* phys addr */
len += sizeof(unsigned int);
#if defined(DEBUG)
show_tlb();
#endif
return len;
}
static int ipu_write_proc(struct file *file, const char *buffer, unsigned long count, void *data)
{
unsigned int val ;
int cnt,i;
char buf[12];
unsigned long pid, entrylo0, entrylo1, entryhi, pagemask;
#if defined(DEBUG)
printk("ipu write count=%u\n", count);
#endif
if (count == (8*5+1)) {
for (i=0;i<12;i++) buf[i]=0;
strncpy(buf, buffer+8*0, 8);
pid = simple_strtoul(buf, 0, 16);
for (i=0;i<12;i++) buf[i]=0;
strncpy(buf, buffer+8*1, 8);
entrylo0 = simple_strtoul(buf, 0, 16);
for (i=0;i<12;i++) buf[i]=0;
strncpy(buf, buffer+8*2, 8);
entrylo1 = simple_strtoul(buf, 0, 16);
for (i=0;i<12;i++) buf[i]=0;
strncpy(buf, buffer+8*3, 8);
entryhi = simple_strtoul(buf, 0, 16);
for (i=0;i<12;i++) buf[i]=0;
strncpy(buf, buffer+8*4, 8);
pagemask = simple_strtoul(buf, 0, 16);
#if defined(DEBUG)
printk("pid=0x%08x, entrylo0=0x%08x, entrylo1=0x%08x, entryhi=0x%08x, pagemask=0x%08x\n",
pid, entrylo0, entrylo1, entryhi, pagemask);
#endif
ipu_add_wired_entry( pid, entrylo0, entrylo1, entryhi, pagemask);
return 41;
} else if ( count <= 8+1 ) {
for (i=0;i<12;i++) buf[i]=0;
strncpy(buf, buffer, 8);
val = simple_strtoul(buf, 0, 16);
} else if (count == 44) {
for (i = 0; i < 12; i++)
buf[i] = 0;
strncpy(buf, buffer, 10);
pid = simple_strtoul(buf, 0, 16);
for (i = 0; i < 12; i++)
buf[i] = 0;
strncpy(buf, buffer + 11, 10);
entryhi = simple_strtoul(buf, 0, 16);//vaddr
for (i = 0; i < 12; i++)
buf[i] = 0;
strncpy(buf, buffer + 22, 10);
entrylo0 = simple_strtoul(buf, 0, 16);//paddr
for (i = 0; i < 12; i++)
buf[i] = 0;
strncpy(buf, buffer + 33, 10);
pagemask = simple_strtoul(buf, 0, 16);
pagemask = 0x3ff << 13; /* Fixed to 4MB page size */
ipu_add_wired_entry(pid, entrylo0, 0, entryhi, pagemask);
return 44;
} else {
printk("ipu write count error, count=%d\n.", (unsigned int)count);
return -1;
}
/* val: 1-9, page_shift, val>= 10: ipu_buf.addr */
if ( val == 0 ) { /* debug, print ipu_buf info */
for ( cnt=0; cnt<IPU_BUF_MAX; ++cnt)
printk("ipu_buf[%d]: addr=0x%08x, page_shift=%d\n",
cnt, ipu_buf[cnt].addr, ipu_buf[cnt].page_shift );
#if defined(DEBUG)
show_tlb();
#endif
}
else if ( 0< val && val < 10 ) {
ipu_buf_get(val);
}
else if ( val == 0xff ) { /* 255: free all ipu_buf */
for ( cnt=0; cnt<IPU_BUF_MAX; ++cnt ) {
ipu_buf_free(ipu_buf[cnt].addr);
}
}
else {
ipu_buf_free(val);
}
return count;
}
/*
* UDC hotplug
*/
#ifdef CONFIG_JZ_UDC_HOTPLUG
extern int jz_udc_active; /* defined in drivers/char/jzchar/jz_udc_hotplug.c */
#endif
#ifndef GPIO_UDC_HOTPLUG
#define GPIO_UDC_HOTPLUG 86
#endif
static int udc_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len = 0;
if (__gpio_get_pin(GPIO_UDC_HOTPLUG)) {
#ifdef CONFIG_JZ_UDC_HOTPLUG
/* Cable has connected, wait for disconnection. */
__gpio_as_irq_fall_edge(GPIO_UDC_HOTPLUG);
if (jz_udc_active)
len += sprintf (page+len, "CONNECT_CABLE\n");
else
len += sprintf (page+len, "CONNECT_POWER\n");
#else
len += sprintf (page+len, "CONNECT\n");
#endif
}
else {
#ifdef CONFIG_JZ_UDC_HOTPLUG
/* Cable has disconnected, wait for connection. */
__gpio_as_irq_rise_edge(GPIO_UDC_HOTPLUG);
#endif
len += sprintf (page+len, "REMOVE\n");
}
return len;
}
/*
* MMC/SD hotplug
*/
#ifndef MSC_HOTPLUG_PIN
#define MSC_HOTPLUG_PIN 90
#endif
static int mmc_read_proc (char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len = 0;
#if defined(CONFIG_JZ4740_LYRA)
if (!(__gpio_get_pin(MSC_HOTPLUG_PIN)))
#else
if (__gpio_get_pin(MSC_HOTPLUG_PIN))
#endif
len += sprintf (page+len, "REMOVE\n");
else
len += sprintf (page+len, "INSERT\n");
return len;
}
/***********************************************************************
* IPU memory management (used by mplayer and other apps)
*
* We reserved 4MB memory for IPU
* The memory base address is jz_ipu_framebuf
*/
/* Usage:
*
* echo n > /proc/jz/imem // n = [0,...,10], allocate memory, 2^n pages
* echo xxxxxxxx > /proc/jz/imem // free buffer which addr is xxxxxxxx
* echo FF > /proc/jz/ipu // FF, free all buffers
* od -X /proc/jz/imem // return the allocated buffer address and the max order of free buffer
*/
//#define DEBUG_IMEM 1
#define IMEM_MAX_ORDER 10 /* max 2^10 * 4096 = 4MB */
static unsigned int jz_imem_base; /* physical base address of ipu memory */
static unsigned int allocated_phys_addr = 0;
/*
* Allocated buffer list
*/
typedef struct imem_list {
unsigned int phys_start; /* physical start addr */
unsigned int phys_end; /* physical end addr */
struct imem_list *next;
} imem_list_t;
static struct imem_list *imem_list_head = NULL; /* up sorted by phys_start */
#ifdef DEBUG_IMEM
static void dump_imem_list(void)
{
struct imem_list *imem;
printk("*** dump_imem_list 0x%x ***\n", (u32)imem_list_head);
imem = imem_list_head;
while (imem) {
printk("imem=0x%x phys_start=0x%x phys_end=0x%x next=0x%x\n", (u32)imem, imem->phys_start, imem->phys_end, (u32)imem->next);
imem = imem->next;
}
}
#endif
/* allocate 2^order pages inside the 4MB memory */
static int imem_alloc(unsigned int order)
{
int alloc_ok = 0;
unsigned int start, end;
unsigned int size = (1 << order) * PAGE_SIZE;
struct imem_list *imem, *imemn, *imemp;
allocated_phys_addr = 0;
start = jz_imem_base;
end = start + (1 << IMEM_MAX_ORDER) * PAGE_SIZE;
imem = imem_list_head;
while (imem) {
if ((imem->phys_start - start) >= size) {
/* we got a valid address range */
alloc_ok = 1;
break;
}
start = imem->phys_end + 1;
imem = imem->next;
}
if (!alloc_ok) {
if ((end - start) >= size)
alloc_ok = 1;
}
if (alloc_ok) {
end = start + size - 1;
allocated_phys_addr = start;
/* add to imem_list, up sorted by phys_start */
imemn = kmalloc(sizeof(struct imem_list), GFP_KERNEL);
if (!imemn) {
return -ENOMEM;
}
imemn->phys_start = start;
imemn->phys_end = end;
imemn->next = NULL;
if (!imem_list_head)
imem_list_head = imemn;
else {
imem = imemp = imem_list_head;
while (imem) {
if (start < imem->phys_start) {
break;
}
imemp = imem;
imem = imem->next;
}
if (imem == imem_list_head) {
imem_list_head = imemn;
imemn->next = imem;
}
else {
imemn->next = imemp->next;
imemp->next = imemn;
}
}
}
#ifdef DEBUG_IMEM
dump_imem_list();
#endif
return 0;
}
static void imem_free(unsigned int phys_addr)
{
struct imem_list *imem, *imemp;
imem = imemp = imem_list_head;
while (imem) {
if (phys_addr == imem->phys_start) {
if (imem == imem_list_head) {
imem_list_head = imem->next;
}
else {
imemp->next = imem->next;
}
kfree(imem);
break;
}
imemp = imem;
imem = imem->next;
}
#ifdef DEBUG_IMEM
dump_imem_list();
#endif
}
static void imem_free_all(void)
{
struct imem_list *imem;
imem = imem_list_head;
while (imem) {
kfree(imem);
imem = imem->next;
}
imem_list_head = NULL;
allocated_phys_addr = 0;
#ifdef DEBUG_IMEM
dump_imem_list();
#endif
}
/*
* Return the allocated buffer address and the max order of free buffer
*/
static int imem_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len = 0;
unsigned int start_addr, end_addr, max_order, max_size;
struct imem_list *imem;
unsigned int *tmp = (unsigned int *)(page + len);
start_addr = jz_imem_base;
end_addr = start_addr + (1 << IMEM_MAX_ORDER) * PAGE_SIZE;
if (!imem_list_head)
max_size = end_addr - start_addr;
else {
max_size = 0;
imem = imem_list_head;
while (imem) {
if (max_size < (imem->phys_start - start_addr))
max_size = imem->phys_start - start_addr;
start_addr = imem->phys_end + 1;
imem = imem->next;
}
if (max_size < (end_addr - start_addr))
max_size = end_addr - start_addr;
}
if (max_size > 0) {
max_order = get_order(max_size);
if (((1 << max_order) * PAGE_SIZE) > max_size)
max_order--;
}
else {
max_order = 0xffffffff; /* No any free buffer */
}
*tmp++ = allocated_phys_addr; /* address allocated by 'echo n > /proc/jz/imem' */
*tmp = max_order; /* max order of current free buffers */
len += 2 * sizeof(unsigned int);
return len;
}
static int imem_write_proc(struct file *file, const char *buffer, unsigned long count, void *data)
{
unsigned int val;
val = simple_strtoul(buffer, 0, 16);
if (val == 0xff) {
/* free all memory */
imem_free_all();
ipu_del_wired_entry();
}
else if ((val >= 0) && (val <= IMEM_MAX_ORDER)) {
/* allocate 2^val pages */
imem_alloc(val);
}
else {
/* free buffer which phys_addr is val */
imem_free(val);
}
return count;
}
/*
* /proc/jz/xxx entry
*
*/
static int __init jz_proc_init(void)
{
struct proc_dir_entry *res;
unsigned int virt_addr, i;
proc_jz_root = proc_mkdir("jz", 0);
/* External Memory Controller */
res = create_proc_entry("emc", 0644, proc_jz_root);
if (res) {
res->read_proc = emc_read_proc;
res->write_proc = NULL;
res->data = NULL;
}
/* Power Management Controller */
res = create_proc_entry("pmc", 0644, proc_jz_root);
if (res) {
res->read_proc = pmc_read_proc;
res->write_proc = pmc_write_proc;
res->data = NULL;
}
/* Clock Generation Module */
res = create_proc_entry("cgm", 0644, proc_jz_root);
if (res) {
res->read_proc = cgm_read_proc;
res->write_proc = cgm_write_proc;
res->data = NULL;
}
/* Image process unit */
res = create_proc_entry("ipu", 0644, proc_jz_root);
if (res) {
res->read_proc = ipu_read_proc;
res->write_proc = ipu_write_proc;
res->data = NULL;
}
/* udc hotplug */
res = create_proc_entry("udc", 0644, proc_jz_root);
if (res) {
res->read_proc = udc_read_proc;
res->write_proc = NULL;
res->data = NULL;
}
/* mmc hotplug */
res = create_proc_entry("mmc", 0644, proc_jz_root);
if (res) {
res->read_proc = mmc_read_proc;
res->write_proc = NULL;
res->data = NULL;
}
/*
* Reserve a 4MB memory for IPU on JZ4740.
*/
jz_imem_base = (unsigned int)__get_free_pages(GFP_KERNEL, IMEM_MAX_ORDER);
if (jz_imem_base) {
/* imem (IPU memory management) */
res = create_proc_entry("imem", 0644, proc_jz_root);
if (res) {
res->read_proc = imem_read_proc;
res->write_proc = imem_write_proc;
res->data = NULL;
}
/* Set page reserved */
virt_addr = jz_imem_base;
for (i = 0; i < (1 << IMEM_MAX_ORDER); i++) {
SetPageReserved(virt_to_page((void *)virt_addr));
virt_addr += PAGE_SIZE;
}
/* Convert to physical address */
jz_imem_base = virt_to_phys((void *)jz_imem_base);
printk("Total %dMB memory at 0x%x was reserved for IPU\n",
(unsigned int)((1 << IMEM_MAX_ORDER) * PAGE_SIZE)/1000000, jz_imem_base);
}
return 0;
}
__initcall(jz_proc_init);

View File

@ -0,0 +1,198 @@
/*
*
* BRIEF MODULE DESCRIPTION
* PROM library initialisation code, supports YAMON and U-Boot.
*
* Copyright 2000, 2001, 2006 MontaVista Software Inc.
* Author: MontaVista Software, Inc.
* ppopov@mvista.com or source@mvista.com
*
* This file was derived from Carsten Langgaard's
* arch/mips/mips-boards/xx files.
*
* Carsten Langgaard, carstenl@mips.com
* Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
#include <asm/bootinfo.h>
#include <asm/jzsoc.h>
/* #define DEBUG_CMDLINE */
int prom_argc;
char **prom_argv, **prom_envp;
char * prom_getcmdline(void)
{
return &(arcs_cmdline[0]);
}
void prom_init_cmdline(void)
{
char *cp;
int actr;
actr = 1; /* Always ignore argv[0] */
cp = &(arcs_cmdline[0]);
while(actr < prom_argc) {
strcpy(cp, prom_argv[actr]);
cp += strlen(prom_argv[actr]);
*cp++ = ' ';
actr++;
}
if (cp != &(arcs_cmdline[0])) /* get rid of trailing space */
--cp;
if (prom_argc > 1)
*cp = '\0';
}
char *prom_getenv(char *envname)
{
#if 0
/*
* Return a pointer to the given environment variable.
* YAMON uses "name", "value" pairs, while U-Boot uses "name=value".
*/
char **env = prom_envp;
int i = strlen(envname);
int yamon = (*env && strchr(*env, '=') == NULL);
while (*env) {
if (yamon) {
if (strcmp(envname, *env++) == 0)
return *env;
} else {
if (strncmp(envname, *env, i) == 0 && (*env)[i] == '=')
return *env + i + 1;
}
env++;
}
#endif
return NULL;
}
inline unsigned char str2hexnum(unsigned char c)
{
if(c >= '0' && c <= '9')
return c - '0';
if(c >= 'a' && c <= 'f')
return c - 'a' + 10;
if(c >= 'A' && c <= 'F')
return c - 'A' + 10;
return 0; /* foo */
}
inline void str2eaddr(unsigned char *ea, unsigned char *str)
{
int i;
for(i = 0; i < 6; i++) {
unsigned char num;
if((*str == '.') || (*str == ':'))
str++;
num = str2hexnum(*str++) << 4;
num |= (str2hexnum(*str++));
ea[i] = num;
}
}
int get_ethernet_addr(char *ethernet_addr)
{
char *ethaddr_str;
ethaddr_str = prom_getenv("ethaddr");
if (!ethaddr_str) {
printk("ethaddr not set in boot prom\n");
return -1;
}
str2eaddr(ethernet_addr, ethaddr_str);
#if 0
{
int i;
printk("get_ethernet_addr: ");
for (i=0; i<5; i++)
printk("%02x:", (unsigned char)*(ethernet_addr+i));
printk("%02x\n", *(ethernet_addr+i));
}
#endif
return 0;
}
void __init prom_free_prom_memory(void)
{
}
void __init prom_init(void)
{
unsigned char *memsize_str;
unsigned long memsize;
prom_argc = (int) fw_arg0;
prom_argv = (char **) fw_arg1;
prom_envp = (char **) fw_arg2;
mips_machtype = MACH_INGENIC_JZ4740;
prom_init_cmdline();
memsize_str = prom_getenv("memsize");
if (!memsize_str) {
memsize = 0x04000000;
} else {
memsize = simple_strtol(memsize_str, NULL, 0);
}
add_memory_region(0, memsize, BOOT_MEM_RAM);
}
/* used by early printk */
void prom_putchar(char c)
{
volatile u8 *uart_lsr = (volatile u8 *)(UART0_BASE + OFF_LSR);
volatile u8 *uart_tdr = (volatile u8 *)(UART0_BASE + OFF_TDR);
/* Wait for fifo to shift out some bytes */
while ( !((*uart_lsr & (UARTLSR_TDRQ | UARTLSR_TEMT)) == 0x60) );
*uart_tdr = (u8)c;
}
const char *get_system_type(void)
{
return "JZ4740";
}
EXPORT_SYMBOL(prom_getcmdline);
EXPORT_SYMBOL(get_ethernet_addr);
EXPORT_SYMBOL(str2eaddr);

View File

@ -0,0 +1,46 @@
/*
* linux/arch/mips/jz4740/reset.c
*
* JZ4740 reset routines.
*
* Copyright (c) 2006-2007 Ingenic Semiconductor Inc.
* Author: <yliu@ingenic.cn>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/sched.h>
#include <linux/mm.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
#include <asm/reboot.h>
#include <asm/system.h>
#include <asm/jzsoc.h>
void jz_restart(char *command)
{
printk("Restarting after 4 ms\n");
REG_WDT_TCSR = WDT_TCSR_PRESCALE4 | WDT_TCSR_EXT_EN;
REG_WDT_TCNT = 0;
REG_WDT_TDR = JZ_EXTAL/1000; /* reset after 4ms */
REG_TCU_TSCR = TCU_TSSR_WDTSC; /* enable wdt clock */
REG_WDT_TCER = WDT_TCER_TCEN; /* wdt start */
while (1);
}
void jz_halt(void)
{
printk(KERN_NOTICE "\n** You can safely turn off the power\n");
while (1)
__asm__(".set\tmips3\n\t"
"wait\n\t"
".set\tmips0");
}
void jz_power_off(void)
{
jz_halt();
}

View File

@ -0,0 +1,188 @@
/*
* linux/arch/mips/jz4740/common/setup.c
*
* JZ4740 common setup routines.
*
* Copyright (C) 2006 Ingenic Semiconductor Inc.
*
* This program is free software; you can distribute it and/or modify it
* under the terms of the GNU General Public License (Version 2) as
* published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*
*/
#include <linux/init.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/ioport.h>
#include <linux/tty.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/serial_8250.h>
#include <asm/cpu.h>
#include <asm/bootinfo.h>
#include <asm/irq.h>
#include <asm/mipsregs.h>
#include <asm/reboot.h>
#include <asm/pgtable.h>
#include <asm/time.h>
#include <asm/jzsoc.h>
#ifdef CONFIG_PM
#include <asm/suspend.h>
#endif
#ifdef CONFIG_PC_KEYB
#include <asm/keyboard.h>
#endif
jz_clocks_t jz_clocks;
extern char * __init prom_getcmdline(void);
extern void __init jz_board_setup(void);
extern void jz_restart(char *);
extern void jz_halt(void);
extern void jz_power_off(void);
extern void jz_time_init(void);
static void __init sysclocks_setup(void)
{
#ifndef CONFIG_MIPS_JZ_EMURUS /* FPGA */
jz_clocks.cclk = __cpm_get_cclk();
jz_clocks.hclk = __cpm_get_hclk();
jz_clocks.pclk = __cpm_get_pclk();
jz_clocks.mclk = __cpm_get_mclk();
jz_clocks.lcdclk = __cpm_get_lcdclk();
jz_clocks.pixclk = __cpm_get_pixclk();
jz_clocks.i2sclk = __cpm_get_i2sclk();
jz_clocks.usbclk = __cpm_get_usbclk();
jz_clocks.mscclk = __cpm_get_mscclk();
jz_clocks.extalclk = __cpm_get_extalclk();
jz_clocks.rtcclk = __cpm_get_rtcclk();
#else
#define FPGACLK 8000000
jz_clocks.cclk = FPGACLK;
jz_clocks.hclk = FPGACLK;
jz_clocks.pclk = FPGACLK;
jz_clocks.mclk = FPGACLK;
jz_clocks.lcdclk = FPGACLK;
jz_clocks.pixclk = FPGACLK;
jz_clocks.i2sclk = FPGACLK;
jz_clocks.usbclk = FPGACLK;
jz_clocks.mscclk = FPGACLK;
jz_clocks.extalclk = FPGACLK;
jz_clocks.rtcclk = FPGACLK;
#endif
printk("CPU clock: %dMHz, System clock: %dMHz, Peripheral clock: %dMHz, Memory clock: %dMHz\n",
(jz_clocks.cclk + 500000) / 1000000,
(jz_clocks.hclk + 500000) / 1000000,
(jz_clocks.pclk + 500000) / 1000000,
(jz_clocks.mclk + 500000) / 1000000);
}
static void __init soc_cpm_setup(void)
{
/* Start all module clocks
*/
__cpm_start_all();
/* Enable CKO to external memory */
__cpm_enable_cko();
/* CPU enters IDLE mode when executing 'wait' instruction */
__cpm_idle_mode();
/* Setup system clocks */
sysclocks_setup();
}
static void __init soc_harb_setup(void)
{
// __harb_set_priority(0x00); /* CIM>LCD>DMA>ETH>PCI>USB>CBB */
// __harb_set_priority(0x03); /* LCD>CIM>DMA>ETH>PCI>USB>CBB */
// __harb_set_priority(0x0a); /* ETH>LCD>CIM>DMA>PCI>USB>CBB */
}
static void __init soc_emc_setup(void)
{
}
static void __init soc_dmac_setup(void)
{
__dmac_enable_module();
}
static void __init jz_soc_setup(void)
{
soc_cpm_setup();
soc_harb_setup();
soc_emc_setup();
soc_dmac_setup();
}
static void __init jz_serial_setup(void)
{
#ifdef CONFIG_SERIAL_8250
struct uart_port s;
REG8(UART0_FCR) |= UARTFCR_UUE; /* enable UART module */
memset(&s, 0, sizeof(s));
s.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST;
s.iotype = SERIAL_IO_MEM;
s.regshift = 2;
s.uartclk = jz_clocks.extalclk ;
s.line = 0;
s.membase = (u8 *)UART0_BASE;
s.irq = IRQ_UART0;
if (early_serial_setup(&s) != 0) {
printk(KERN_ERR "Serial ttyS0 setup failed!\n");
}
s.line = 1;
s.membase = (u8 *)UART1_BASE;
s.irq = IRQ_UART1;
if (early_serial_setup(&s) != 0) {
printk(KERN_ERR "Serial ttyS1 setup failed!\n");
}
#endif
}
void __init plat_mem_setup(void)
{
char *argptr;
argptr = prom_getcmdline();
/* IO/MEM resources. Which will be the addtion value in `inX' and
* `outX' macros defined in asm/io.h */
set_io_port_base(0);
ioport_resource.start = 0x00000000;
ioport_resource.end = 0xffffffff;
iomem_resource.start = 0x00000000;
iomem_resource.end = 0xffffffff;
_machine_restart = jz_restart;
_machine_halt = jz_halt;
pm_power_off = jz_power_off;
#ifdef CONFIG_PM
jz_pm_init();
#endif
jz_soc_setup();
jz_serial_setup();
jz_board_setup();
}

View File

@ -0,0 +1,159 @@
/*
* linux/arch/mips/jz4740/time.c
*
* Setting up the clock on the JZ4740 boards.
*
* Copyright (C) 2008 Ingenic Semiconductor Inc.
* Author: <jlwei@ingenic.cn>
*
* This program is free software; you can distribute it and/or modify it
* under the terms of the GNU General Public License (Version 2) as
* published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*
*/
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/clockchips.h>
#include <asm/time.h>
#include <asm/jzsoc.h>
/* This is for machines which generate the exact clock. */
#define JZ_TIMER_CHAN 0
#define JZ_TIMER_IRQ IRQ_TCU0
#define JZ_TIMER_CLOCK (JZ_EXTAL>>4) /* Jz timer clock frequency */
static struct clocksource clocksource_jz; /* Jz clock source */
static struct clock_event_device jz_clockevent_device; /* Jz clock event */
void (*jz_timer_callback)(void);
static irqreturn_t jz_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *cd = dev_id;
REG_TCU_TFCR = 1 << JZ_TIMER_CHAN; /* ACK timer */
if (jz_timer_callback)
jz_timer_callback();
cd->event_handler(cd);
return IRQ_HANDLED;
}
static struct irqaction jz_irqaction = {
.handler = jz_timer_interrupt,
.flags = IRQF_DISABLED | IRQF_PERCPU | IRQF_TIMER,
.name = "jz-timerirq",
};
cycle_t jz_get_cycles(void)
{
/* convert jiffes to jz timer cycles */
return (cycle_t)( jiffies*((JZ_TIMER_CLOCK)/HZ) + REG_TCU_TCNT(JZ_TIMER_CHAN));
}
static struct clocksource clocksource_jz = {
.name = "jz_clocksource",
.rating = 300,
.read = jz_get_cycles,
.mask = 0xFFFF,
.shift = 10,
.flags = CLOCK_SOURCE_WATCHDOG,
};
static int __init jz_clocksource_init(void)
{
clocksource_jz.mult = clocksource_hz2mult(JZ_TIMER_CLOCK, clocksource_jz.shift);
clocksource_register(&clocksource_jz);
return 0;
}
static int jz_set_next_event(unsigned long evt,
struct clock_event_device *unused)
{
return 0;
}
static void jz_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
break;
case CLOCK_EVT_MODE_ONESHOT:
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_SHUTDOWN:
break;
case CLOCK_EVT_MODE_RESUME:
break;
}
}
static struct clock_event_device jz_clockevent_device = {
.name = "jz-clockenvent",
.features = CLOCK_EVT_FEAT_PERIODIC,
// .features = CLOCK_EVT_FEAT_ONESHOT, /* Jz4740 not support dynamic clock now */
/* .mult, .shift, .max_delta_ns and .min_delta_ns left uninitialized */
.mult = 1,
.rating = 300,
.irq = JZ_TIMER_IRQ,
.set_mode = jz_set_mode,
.set_next_event = jz_set_next_event,
};
static void __init jz_clockevent_init(void)
{
struct clock_event_device *cd = &jz_clockevent_device;
unsigned int cpu = smp_processor_id();
cd->cpumask = cpumask_of(cpu);
clockevents_register_device(cd);
}
static void __init jz_timer_setup(void)
{
jz_clocksource_init(); /* init jz clock source */
jz_clockevent_init(); /* init jz clock event */
/*
* Make irqs happen for the system timer
*/
jz_irqaction.dev_id = &jz_clockevent_device;
setup_irq(JZ_TIMER_IRQ, &jz_irqaction);
}
void __init plat_time_init(void)
{
unsigned int latch;
/* Init timer */
latch = ( JZ_TIMER_CLOCK + (HZ>>1)) / HZ;
REG_TCU_TCSR(JZ_TIMER_CHAN) = TCU_TCSR_PRESCALE16 | TCU_TCSR_EXT_EN;
REG_TCU_TCNT(JZ_TIMER_CHAN) = 0;
REG_TCU_TDHR(JZ_TIMER_CHAN) = 0;
REG_TCU_TDFR(JZ_TIMER_CHAN) = latch;
REG_TCU_TMSR = (1 << (JZ_TIMER_CHAN + 16)); /* mask half irq */
REG_TCU_TMCR = (1 << JZ_TIMER_CHAN); /* unmask full irq */
REG_TCU_TSCR = (1 << JZ_TIMER_CHAN); /* enable timer clock */
REG_TCU_TESR = (1 << JZ_TIMER_CHAN); /* start counting up */
jz_timer_setup();
}

View File

@ -0,0 +1,995 @@
/*
* linux/drivers/mmc/jz_mmc.c - JZ SD/MMC driver
*
* Copyright (C) 2005 - 2008 Ingenic Semiconductor Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/dma-mapping.h>
#include <linux/mmc/host.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
#include <linux/mmc/sdio.h>
#include <linux/mm.h>
#include <linux/signal.h>
#include <linux/pm.h>
#include <linux/scatterlist.h>
#include <asm/io.h>
#include <asm/scatterlist.h>
#include <asm/sizes.h>
#include <asm/jzsoc.h>
#include "jz_mmc.h"
#define DRIVER_NAME "jz-mmc"
#define NR_SG 1
#if defined(CONFIG_SOC_JZ4725) || defined(CONFIG_SOC_JZ4720)
#undef USE_DMA
#else
#define USE_DMA
#endif
struct jz_mmc_host {
struct mmc_host *mmc;
spinlock_t lock;
struct {
int len;
int dir;
} dma;
struct {
int index;
int offset;
int len;
} pio;
int irq;
unsigned int clkrt;
unsigned int cmdat;
unsigned int imask;
unsigned int power_mode;
struct jz_mmc_platform_data *pdata;
struct mmc_request *mrq;
struct mmc_command *cmd;
struct mmc_data *data;
dma_addr_t sg_dma;
struct jzsoc_dma_desc *sg_cpu;
unsigned int dma_len;
unsigned int dma_dir;
struct pm_dev *pmdev;
};
static int r_type = 0;
#define MMC_IRQ_MASK() \
do { \
REG_MSC_IMASK = 0xff; \
REG_MSC_IREG = 0xff; \
} while (0)
static int rxdmachan = 0;
static int txdmachan = 0;
static int mmc_slot_enable = 0;
/* Stop the MMC clock and wait while it happens */
static inline int jz_mmc_stop_clock(void)
{
int timeout = 1000;
REG_MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_STOP;
while (timeout && (REG_MSC_STAT & MSC_STAT_CLK_EN)) {
timeout--;
if (timeout == 0)
return 0;
udelay(1);
}
return MMC_NO_ERROR;
}
/* Start the MMC clock and operation */
static inline int jz_mmc_start_clock(void)
{
REG_MSC_STRPCL =
MSC_STRPCL_CLOCK_CONTROL_START | MSC_STRPCL_START_OP;
return MMC_NO_ERROR;
}
static inline u32 jz_mmc_calc_clkrt(int is_sd, u32 rate)
{
u32 clkrt;
u32 clk_src = is_sd ? 24000000 : 20000000;
clkrt = 0;
while (rate < clk_src) {
clkrt++;
clk_src >>= 1;
}
return clkrt;
}
/* Select the MMC clock frequency */
static int jz_mmc_set_clock(u32 rate)
{
int clkrt;
jz_mmc_stop_clock();
__cpm_select_msc_clk(1); /* select clock source from CPM */
clkrt = jz_mmc_calc_clkrt(1, rate);
REG_MSC_CLKRT = clkrt;
return MMC_NO_ERROR;
}
static void jz_mmc_enable_irq(struct jz_mmc_host *host, unsigned int mask)
{
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
host->imask &= ~mask;
REG_MSC_IMASK = host->imask;
spin_unlock_irqrestore(&host->lock, flags);
}
static void jz_mmc_disable_irq(struct jz_mmc_host *host, unsigned int mask)
{
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
host->imask |= mask;
REG_MSC_IMASK = host->imask;
spin_unlock_irqrestore(&host->lock, flags);
}
void jz_set_dma_block_size(int dmanr, int nbyte);
#ifdef USE_DMA
static inline void
jz_mmc_start_dma(int chan, unsigned long phyaddr, int count, int mode)
{
unsigned long flags;
flags = claim_dma_lock();
disable_dma(chan);
clear_dma_ff(chan);
jz_set_dma_block_size(chan, 32);
set_dma_mode(chan, mode);
set_dma_addr(chan, phyaddr);
set_dma_count(chan, count + 31);
enable_dma(chan);
release_dma_lock(flags);
}
static irqreturn_t jz_mmc_dma_rx_callback(int irq, void *devid)
{
int chan = rxdmachan;
disable_dma(chan);
if (__dmac_channel_address_error_detected(chan)) {
printk(KERN_DEBUG "%s: DMAC address error.\n",
__FUNCTION__);
__dmac_channel_clear_address_error(chan);
}
if (__dmac_channel_transmit_end_detected(chan)) {
__dmac_channel_clear_transmit_end(chan);
}
return IRQ_HANDLED;
}
static irqreturn_t jz_mmc_dma_tx_callback(int irq, void *devid)
{
int chan = txdmachan;
disable_dma(chan);
if (__dmac_channel_address_error_detected(chan)) {
printk(KERN_DEBUG "%s: DMAC address error.\n",
__FUNCTION__);
__dmac_channel_clear_address_error(chan);
}
if (__dmac_channel_transmit_end_detected(chan)) {
__dmac_channel_clear_transmit_end(chan);
}
return IRQ_HANDLED;
}
/* Prepare DMA to start data transfer from the MMC card */
static void jz_mmc_rx_setup_data(struct jz_mmc_host *host,
struct mmc_data *data)
{
unsigned int nob = data->blocks;
int channelrx = rxdmachan;
int i;
u32 size;
if (data->flags & MMC_DATA_STREAM)
nob = 0xffff;
REG_MSC_NOB = nob;
REG_MSC_BLKLEN = data->blksz;
size = nob * data->blksz;
if (data->flags & MMC_DATA_READ) {
host->dma.dir = DMA_FROM_DEVICE;
} else {
host->dma.dir = DMA_TO_DEVICE;
}
host->dma.len =
dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
host->dma.dir);
for (i = 0; i < host->dma.len; i++) {
host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]);
host->sg_cpu[i].dcmd = sg_dma_len(&data->sg[i]);
dma_cache_wback_inv((unsigned long)
CKSEG0ADDR(sg_dma_address(data->sg)) +
data->sg->offset,
host->sg_cpu[i].dcmd);
jz_mmc_start_dma(channelrx, host->sg_cpu[i].dtadr,
host->sg_cpu[i].dcmd, DMA_MODE_READ);
}
}
/* Prepare DMA to start data transfer from the MMC card */
static void jz_mmc_tx_setup_data(struct jz_mmc_host *host,
struct mmc_data *data)
{
unsigned int nob = data->blocks;
int channeltx = txdmachan;
int i;
u32 size;
if (data->flags & MMC_DATA_STREAM)
nob = 0xffff;
REG_MSC_NOB = nob;
REG_MSC_BLKLEN = data->blksz;
size = nob * data->blksz;
if (data->flags & MMC_DATA_READ) {
host->dma.dir = DMA_FROM_DEVICE;
} else {
host->dma.dir = DMA_TO_DEVICE;
}
host->dma.len =
dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
host->dma.dir);
for (i = 0; i < host->dma.len; i++) {
host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]);
host->sg_cpu[i].dcmd = sg_dma_len(&data->sg[i]);
dma_cache_wback_inv((unsigned long)
CKSEG0ADDR(sg_dma_address(data->sg)) +
data->sg->offset,
host->sg_cpu[i].dcmd);
jz_mmc_start_dma(channeltx, host->sg_cpu[i].dtadr,
host->sg_cpu[i].dcmd, DMA_MODE_WRITE);
}
}
#else
static void jz_mmc_receive_pio(struct jz_mmc_host *host)
{
struct mmc_data *data = 0;
int sg_len = 0, max = 0, count = 0;
u32 *buf = 0;
struct scatterlist *sg;
unsigned int nob;
data = host->mrq->data;
nob = data->blocks;
REG_MSC_NOB = nob;
REG_MSC_BLKLEN = data->blksz;
max = host->pio.len;
if (host->pio.index < host->dma.len) {
sg = &data->sg[host->pio.index];
buf = sg_virt(sg) + host->pio.offset;
/* This is the space left inside the buffer */
sg_len = sg_dma_len(&data->sg[host->pio.index]) - host->pio.offset;
/* Check to if we need less then the size of the sg_buffer */
if (sg_len < max) max = sg_len;
}
max = max / 4;
for(count = 0; count < max; count++) {
while (REG_MSC_STAT & MSC_STAT_DATA_FIFO_EMPTY)
;
*buf++ = REG_MSC_RXFIFO;
}
host->pio.len -= count;
host->pio.offset += count;
if (sg_len && count == sg_len) {
host->pio.index++;
host->pio.offset = 0;
}
}
static void jz_mmc_send_pio(struct jz_mmc_host *host)
{
struct mmc_data *data = 0;
int sg_len, max, count = 0;
u32 *wbuf = 0;
struct scatterlist *sg;
unsigned int nob;
data = host->mrq->data;
nob = data->blocks;
REG_MSC_NOB = nob;
REG_MSC_BLKLEN = data->blksz;
/* This is the pointer to the data buffer */
sg = &data->sg[host->pio.index];
wbuf = sg_virt(sg) + host->pio.offset;
/* This is the space left inside the buffer */
sg_len = data->sg[host->pio.index].length - host->pio.offset;
/* Check to if we need less then the size of the sg_buffer */
max = (sg_len > host->pio.len) ? host->pio.len : sg_len;
max = max / 4;
for(count = 0; count < max; count++ ) {
while (REG_MSC_STAT & MSC_STAT_DATA_FIFO_FULL)
;
REG_MSC_TXFIFO = *wbuf++;
}
host->pio.len -= count;
host->pio.offset += count;
if (count == sg_len) {
host->pio.index++;
host->pio.offset = 0;
}
}
static int
jz_mmc_prepare_data(struct jz_mmc_host *host, struct mmc_data *data)
{
int datalen = data->blocks * data->blksz;
host->dma.dir = DMA_BIDIRECTIONAL;
host->dma.len = dma_map_sg(mmc_dev(host->mmc), data->sg,
data->sg_len, host->dma.dir);
if (host->dma.len == 0)
return -ETIMEDOUT;
host->pio.index = 0;
host->pio.offset = 0;
host->pio.len = datalen;
return 0;
}
#endif
static int jz_mmc_cmd_done(struct jz_mmc_host *host, unsigned int stat);
static void jz_mmc_finish_request(struct jz_mmc_host *host, struct mmc_request *mrq)
{
jz_mmc_stop_clock();
host->mrq = NULL;
host->cmd = NULL;
host->data = NULL;
mmc_request_done(host->mmc, mrq);
}
static void jz_mmc_start_cmd(struct jz_mmc_host *host,
struct mmc_command *cmd, unsigned int cmdat)
{
u32 timeout = 0x3fffff;
unsigned int stat;
struct jz_mmc_host *hst = host;
WARN_ON(host->cmd != NULL);
host->cmd = cmd;
/* stop MMC clock */
jz_mmc_stop_clock();
/* mask interrupts */
REG_MSC_IMASK = 0xff;
/* clear status */
REG_MSC_IREG = 0xff;
if (cmd->flags & MMC_RSP_BUSY)
cmdat |= MSC_CMDAT_BUSY;
#define RSP_TYPE(x) ((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE))
switch (RSP_TYPE(mmc_resp_type(cmd))) {
case RSP_TYPE(MMC_RSP_R1): /* r1,r1b, r6, r7 */
cmdat |= MSC_CMDAT_RESPONSE_R1;
r_type = 1;
break;
case RSP_TYPE(MMC_RSP_R3):
cmdat |= MSC_CMDAT_RESPONSE_R3;
r_type = 1;
break;
case RSP_TYPE(MMC_RSP_R2):
cmdat |= MSC_CMDAT_RESPONSE_R2;
r_type = 2;
break;
default:
break;
}
REG_MSC_CMD = cmd->opcode;
/* Set argument */
#ifdef CONFIG_JZ_MMC_BUS_1
if (cmd->opcode == 6) {
/* set 1 bit sd card bus*/
if (cmd->arg ==2)
REG_MSC_ARG = 0;
/* set 1 bit mmc card bus*/
if (cmd->arg == 0x3b70101)
REG_MSC_ARG = 0x3b70001;
} else
REG_MSC_ARG = cmd->arg;
#else
REG_MSC_ARG = cmd->arg;
#endif
/* Set command */
REG_MSC_CMDAT = cmdat;
/* Send command */
jz_mmc_start_clock();
while (timeout-- && !(REG_MSC_STAT & MSC_STAT_END_CMD_RES))
;
REG_MSC_IREG = MSC_IREG_END_CMD_RES; /* clear irq flag */
if (cmd->opcode == 12) {
while (timeout-- && !(REG_MSC_IREG & MSC_IREG_PRG_DONE))
;
REG_MSC_IREG = MSC_IREG_PRG_DONE; /* clear status */
}
if (!mmc_slot_enable) {
/* It seems that MSC can't report the MSC_STAT_TIME_OUT_RES when
* card was removed. We force to return here.
*/
cmd->error = -ETIMEDOUT;
jz_mmc_finish_request(hst, hst->mrq);
return;
}
if (SD_IO_SEND_OP_COND == cmd->opcode) {
/*
* Don't support SDIO card currently.
*/
cmd->error = -ETIMEDOUT;
jz_mmc_finish_request(hst, hst->mrq);
return;
}
/* Check for status */
stat = REG_MSC_STAT;
jz_mmc_cmd_done(hst, stat);
if (host->data) {
if (cmd->opcode == MMC_WRITE_BLOCK || cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK)
#ifdef USE_DMA
jz_mmc_tx_setup_data(host, host->data);
#else
jz_mmc_send_pio(host);
else
jz_mmc_receive_pio(host);
#endif
}
}
static int jz_mmc_cmd_done(struct jz_mmc_host *host, unsigned int stat)
{
struct mmc_command *cmd = host->cmd;
int i, temp[16];
u8 *buf;
u32 data, v, w1, w2;
if (!cmd)
return 0;
host->cmd = NULL;
buf = (u8 *) temp;
switch (r_type) {
case 1:
{
data = REG_MSC_RES;
buf[0] = (data >> 8) & 0xff;
buf[1] = data & 0xff;
data = REG_MSC_RES;
buf[2] = (data >> 8) & 0xff;
buf[3] = data & 0xff;
data = REG_MSC_RES;
buf[4] = data & 0xff;
cmd->resp[0] =
buf[1] << 24 | buf[2] << 16 | buf[3] << 8 |
buf[4];
break;
}
case 2:
{
data = REG_MSC_RES;
v = data & 0xffff;
for (i = 0; i < 4; i++) {
data = REG_MSC_RES;
w1 = data & 0xffff;
data = REG_MSC_RES;
w2 = data & 0xffff;
cmd->resp[i] = v << 24 | w1 << 8 | w2 >> 8;
v = w2;
}
break;
}
case 0:
break;
}
if (stat & MSC_STAT_TIME_OUT_RES) {
printk("MSC_STAT_TIME_OUT_RES\n");
cmd->error = -ETIMEDOUT;
} else if (stat & MSC_STAT_CRC_RES_ERR && cmd->flags & MMC_RSP_CRC) {
printk("MSC_STAT_CRC\n");
if (cmd->opcode == MMC_ALL_SEND_CID ||
cmd->opcode == MMC_SEND_CSD ||
cmd->opcode == MMC_SEND_CID) {
/* a bogus CRC error can appear if the msb of
the 15 byte response is a one */
if ((cmd->resp[0] & 0x80000000) == 0)
cmd->error = -EILSEQ;
}
}
/*
* Did I mention this is Sick. We always need to
* discard the upper 8 bits of the first 16-bit word.
*/
if (host->data && cmd->error == 0)
jz_mmc_enable_irq(host, MSC_IMASK_DATA_TRAN_DONE);
else
jz_mmc_finish_request(host, host->mrq);
return 1;
}
static int jz_mmc_data_done(struct jz_mmc_host *host, unsigned int stat)
{
struct mmc_data *data = host->data;
if (!data)
return 0;
REG_MSC_IREG = MSC_IREG_DATA_TRAN_DONE; /* clear status */
jz_mmc_stop_clock();
dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len,
host->dma_dir);
if (stat & MSC_STAT_TIME_OUT_READ) {
printk("MMC/SD timeout, MMC_STAT 0x%x\n", stat);
data->error = -ETIMEDOUT;
} else if (REG_MSC_STAT &
(MSC_STAT_CRC_READ_ERROR | MSC_STAT_CRC_WRITE_ERROR)) {
printk("MMC/SD CRC error, MMC_STAT 0x%x\n", stat);
data->error = -EILSEQ;
}
/*
* There appears to be a hardware design bug here. There seems to
* be no way to find out how much data was transferred to the card.
* This means that if there was an error on any block, we mark all
* data blocks as being in error.
*/
if (data->error == 0)
data->bytes_xfered = data->blocks * data->blksz;
else
data->bytes_xfered = 0;
jz_mmc_disable_irq(host, MSC_IMASK_DATA_TRAN_DONE);
host->data = NULL;
if (host->mrq->stop) {
jz_mmc_stop_clock();
jz_mmc_start_cmd(host, host->mrq->stop, 0);
} else {
jz_mmc_finish_request(host, host->mrq);
}
return 1;
}
static void jz_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct jz_mmc_host *host = mmc_priv(mmc);
unsigned int cmdat;
/* stop MMC clock */
jz_mmc_stop_clock();
/* Save current request for the future processing */
host->mrq = mrq;
host->data = mrq->data;
cmdat = host->cmdat;
host->cmdat &= ~MSC_CMDAT_INIT;
if (mrq->data) {
cmdat &= ~MSC_CMDAT_BUSY;
#ifdef USE_DMA
if ((mrq->cmd->opcode == 51) | (mrq->cmd->opcode == 8) | (mrq->cmd->opcode == 6))
cmdat |=
MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN |
MSC_CMDAT_DMA_EN;
else {
#ifdef CONFIG_JZ_MMC_BUS_1
cmdat &= ~MSC_CMDAT_BUS_WIDTH_4BIT;
cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN |
MSC_CMDAT_DMA_EN;
#else
cmdat |= MSC_CMDAT_DATA_EN | MSC_CMDAT_DMA_EN;
#endif
}
if (mrq->data->flags & MMC_DATA_WRITE)
cmdat |= MSC_CMDAT_WRITE;
if (mrq->data->flags & MMC_DATA_STREAM)
cmdat |= MSC_CMDAT_STREAM_BLOCK;
if (mrq->cmd->opcode != MMC_WRITE_BLOCK
&& mrq->cmd->opcode != MMC_WRITE_MULTIPLE_BLOCK)
jz_mmc_rx_setup_data(host, mrq->data);
#else /*USE_DMA*/
if ((mrq->cmd->opcode == 51) | (mrq->cmd->opcode == 8) | (mrq->cmd->opcode == 6))
cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN;
else {
#ifdef CONFIG_JZ_MMC_BUS_1
cmdat &= ~MSC_CMDAT_BUS_WIDTH_4BIT;
cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN;
#else
cmdat |= MSC_CMDAT_DATA_EN;
#endif
}
if (mrq->data->flags & MMC_DATA_WRITE)
cmdat |= MSC_CMDAT_WRITE;
if (mrq->data->flags & MMC_DATA_STREAM)
cmdat |= MSC_CMDAT_STREAM_BLOCK;
jz_mmc_prepare_data(host, host->data);
#endif /*USE_DMA*/
}
jz_mmc_start_cmd(host, mrq->cmd, cmdat);
}
static irqreturn_t jz_mmc_irq(int irq, void *devid)
{
struct jz_mmc_host *host = devid;
unsigned int ireg;
int handled = 0;
ireg = REG_MSC_IREG;
if (ireg) {
unsigned stat = REG_MSC_STAT;
if (ireg & MSC_IREG_DATA_TRAN_DONE)
handled |= jz_mmc_data_done(host, stat);
}
return IRQ_RETVAL(handled);
}
/* Returns true if MMC slot is empty */
static int jz_mmc_slot_is_empty(int slot)
{
int empty;
empty = (__msc_card_detected(slot) == 0) ? 1 : 0;
if (empty) {
/* wait for card insertion */
#ifdef CONFIG_MIPS_JZ4740_LYRA
__gpio_as_irq_rise_edge(MSC_HOTPLUG_PIN);
#else
__gpio_as_irq_fall_edge(MSC_HOTPLUG_PIN);
#endif
} else {
/* wait for card removal */
#ifdef CONFIG_MIPS_JZ4740_LYRA
__gpio_as_irq_fall_edge(MSC_HOTPLUG_PIN);
#else
__gpio_as_irq_rise_edge(MSC_HOTPLUG_PIN);
#endif
}
return empty;
}
static irqreturn_t jz_mmc_detect_irq(int irq, void *devid)
{
struct jz_mmc_host *host = (struct jz_mmc_host *) devid;
if (jz_mmc_slot_is_empty(0)) {
mmc_slot_enable = 0;
mmc_detect_change(host->mmc, 50);
} else {
mmc_slot_enable = 1;
mmc_detect_change(host->mmc, 50);
}
return IRQ_HANDLED;
}
static int jz_mmc_get_ro(struct mmc_host *mmc)
{
struct jz_mmc_host *host = mmc_priv(mmc);
if (host->pdata && host->pdata->get_ro)
return host->pdata->get_ro(mmc_dev(mmc));
/* Host doesn't support read only detection so assume writeable */
return 0;
}
/* set clock and power */
static void jz_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct jz_mmc_host *host = mmc_priv(mmc);
if (ios->clock)
jz_mmc_set_clock(ios->clock);
else
jz_mmc_stop_clock();
if (host->power_mode != ios->power_mode) {
host->power_mode = ios->power_mode;
if (ios->power_mode == MMC_POWER_ON)
host->cmdat |= CMDAT_INIT;
}
if ((ios->bus_width == MMC_BUS_WIDTH_4) || (ios->bus_width == MMC_BUS_WIDTH_8))
host->cmdat |= MSC_CMDAT_BUS_WIDTH_4BIT;
else
host->cmdat &= ~MSC_CMDAT_BUS_WIDTH_4BIT;
}
static const struct mmc_host_ops jz_mmc_ops = {
.request = jz_mmc_request,
.get_ro = jz_mmc_get_ro,
.set_ios = jz_mmc_set_ios,
};
static int jz_mmc_probe(struct platform_device *pdev)
{
int retval;
struct mmc_host *mmc;
struct jz_mmc_host *host = NULL;
int irq;
struct resource *r;
__gpio_as_msc();
__msc_init_io();
__msc_enable_power();
__msc_reset();
/* On reset, stop MMC clock */
jz_mmc_stop_clock();
MMC_IRQ_MASK();
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
if (!r || irq < 0)
return -ENXIO;
r = request_mem_region(r->start, SZ_4K, DRIVER_NAME);
if (!r)
return -EBUSY;
mmc = mmc_alloc_host(sizeof(struct jz_mmc_host), &pdev->dev);
if (!mmc) {
retval = -ENOMEM;
goto out;
}
mmc->ops = &jz_mmc_ops;
mmc->f_min = MMC_CLOCK_SLOW;
mmc->f_max = SD_CLOCK_FAST;
/*
* We can do SG-DMA, but we don't because we never know how much
* data we successfully wrote to the card.
*/
mmc->max_phys_segs = NR_SG;
/*
* Our hardware DMA can handle a maximum of one page per SG entry.
*/
mmc->max_seg_size = PAGE_SIZE;
/*
* Block length register is 10 bits.
*/
mmc->max_blk_size = 1023;
/*
* Block count register is 16 bits.
*/
mmc->max_blk_count = 65535;
host = mmc_priv(mmc);
host->mmc = mmc;
host->pdata = pdev->dev.platform_data;
mmc->ocr_avail = host->pdata ?
host->pdata->ocr_mask : MMC_VDD_32_33 | MMC_VDD_33_34;
host->mmc->caps =
MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED
| MMC_CAP_MMC_HIGHSPEED;
/*
*MMC_CAP_4_BIT_DATA (1 << 0) The host can do 4 bit transfers
*
*/
host->sg_cpu =
dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma,
GFP_KERNEL);
if (!host->sg_cpu) {
retval = -ENOMEM;
goto out;
}
spin_lock_init(&host->lock);
host->irq = IRQ_MSC;
host->imask = 0xff;
/*
* Ensure that the host controller is shut down, and setup
* with our defaults.
*/
retval = request_irq(IRQ_MSC, jz_mmc_irq, 0, "MMC/SD", host);
if (retval) {
printk(KERN_ERR "MMC/SD: can't request MMC/SD IRQ\n");
return retval;
}
jz_mmc_slot_is_empty(0);
/* Request card detect interrupt */
retval = request_irq(MSC_HOTPLUG_IRQ, jz_mmc_detect_irq, 0, //SA_INTERRUPT,
"MMC card detect", host);
if (retval) {
printk(KERN_ERR "MMC/SD: can't request card detect IRQ\n");
goto err1;
}
#ifdef USE_DMA
/* Request MMC Rx DMA channel */
rxdmachan =
jz_request_dma(DMA_ID_MSC_RX, "MMC Rx", jz_mmc_dma_rx_callback,
0, host);
if (rxdmachan < 0) {
printk(KERN_ERR "jz_request_dma failed for MMC Rx\n");
goto err2;
}
/* Request MMC Tx DMA channel */
txdmachan =
jz_request_dma(DMA_ID_MSC_TX, "MMC Tx", jz_mmc_dma_tx_callback,
0, host);
if (txdmachan < 0) {
printk(KERN_ERR "jz_request_dma failed for MMC Tx\n");
goto err3;
}
#endif
platform_set_drvdata(pdev, mmc);
mmc_add_host(mmc);
printk("JZ SD/MMC card driver registered\n");
/* Detect card during initialization */
#ifdef CONFIG_SOC_JZ4740
if (!jz_mmc_slot_is_empty(0)) {
mmc_slot_enable = 1;
mmc_detect_change(host->mmc, 0);
}
#endif
return 0;
err1:free_irq(IRQ_MSC, &host);
#ifdef USE_DMA
err2:jz_free_dma(rxdmachan);
err3:jz_free_dma(txdmachan);
#endif
out:
if (host) {
if (host->sg_cpu)
dma_free_coherent(&pdev->dev, PAGE_SIZE,
host->sg_cpu, host->sg_dma);
}
if (mmc)
mmc_free_host(mmc);
return -1;
}
static int jz_mmc_remove(struct platform_device *pdev)
{
struct mmc_host *mmc = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
if (mmc) {
struct jz_mmc_host *host = mmc_priv(mmc);
if (host->pdata && host->pdata->exit)
host->pdata->exit(&pdev->dev, mmc);
mmc_remove_host(mmc);
jz_mmc_stop_clock();
__msc_disable_power();
jz_free_dma(rxdmachan);
jz_free_dma(txdmachan);
free_irq(IRQ_MSC, host);
mmc_free_host(mmc);
}
return 0;
}
#ifdef CONFIG_PM
pm_message_t state;
static int jz_mmc_suspend(struct platform_device *dev, pm_message_t state)
{
struct mmc_host *mmc = platform_get_drvdata(dev);
int ret = 0;
__msc_disable_power();
if (mmc)
ret = mmc_suspend_host(mmc, state);
return ret;
}
static int jz_mmc_resume(struct platform_device *dev)
{
struct mmc_host *mmc = platform_get_drvdata(dev);
int ret = 0;
#if 0
/*for sandisk BB0807011816D and other strange cards*/
int i;
for(i = 104; i < 110; i++)
__gpio_as_input(i);
/* perhaps you should mdelay more */
mdelay(1000);
__gpio_as_msc();
#endif
__msc_init_io();
__msc_enable_power();
__msc_reset();
if (!jz_mmc_slot_is_empty(0)) {
mmc_slot_enable = 1;
mmc_detect_change(mmc, 10);
}
if (mmc)
ret = mmc_resume_host(mmc);
return ret;
}
#else
#define jz_mmc_suspend NULL
#define jz_mmc_resume NULL
#endif
static struct platform_driver jz_mmc_driver = {
.probe = jz_mmc_probe,
.remove = jz_mmc_remove,
.suspend = jz_mmc_suspend,
.resume = jz_mmc_resume,
.driver = {
.name = DRIVER_NAME,
},
};
static int __init jz_mmc_init(void)
{
return platform_driver_register(&jz_mmc_driver);
}
static void __exit jz_mmc_exit(void)
{
platform_driver_unregister(&jz_mmc_driver);
}
module_init(jz_mmc_init);
module_exit(jz_mmc_exit);
MODULE_DESCRIPTION("JZ47XX SD/Multimedia Card Interface Driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,65 @@
#ifndef __JZ_MMC_H__
#define __JZ_MMC_H__
#define MMC_CLOCK_SLOW 400000 /* 400 kHz for initial setup */
#define MMC_CLOCK_FAST 20000000 /* 20 MHz for maximum for normal operation */
#define SD_CLOCK_FAST 24000000 /* 24 MHz for SD Cards */
#define MMC_NO_ERROR 0
/* Extra MMC commands for state control */
/* Use negative numbers to disambiguate */
#define MMC_CIM_RESET -1
#define MMC_SET_CLOCK 100
typedef struct jzsoc_dma_desc {
volatile u32 ddadr; /* Points to the next descriptor + flags */
volatile u32 dsadr; /* DSADR value for the current transfer */
volatile u32 dtadr; /* DTADR value for the current transfer */
volatile u32 dcmd; /* DCMD value for the current transfer */
} jzsoc_dma_desc;
#include <linux/interrupt.h>
struct device;
struct mmc_host;
struct jz_mmc_platform_data {
unsigned int ocr_mask; /* available voltages */
unsigned long detect_delay; /* delay in jiffies before detecting cards after interrupt */
int (*init)(struct device *, irq_handler_t , void *);
int (*get_ro)(struct device *);
void (*setpower)(struct device *, unsigned int);
void (*exit)(struct device *, void *);
};
//extern void pxa_set_mci_info(struct pxamci_platform_data *info);
#define SZ_1K 0x00000400
#define SZ_4K 0x00001000
#define SZ_8K 0x00002000
#define SZ_16K 0x00004000
#define SZ_64K 0x00010000
#define SZ_128K 0x00020000
#define SZ_256K 0x00040000
#define SZ_512K 0x00080000
#define SZ_1M 0x00100000
#define SZ_2M 0x00200000
#define SZ_4M 0x00400000
#define SZ_8M 0x00800000
#define SZ_16M 0x01000000
#define SZ_32M 0x02000000
#define SZ_64M 0x04000000
#define SZ_128M 0x08000000
#define SZ_256M 0x10000000
#define SZ_512M 0x20000000
#define SZ_1G 0x40000000
#define SZ_2G 0x80000000
#endif /* __JZ_MMC_H__ */

View File

@ -0,0 +1,413 @@
/*
* Copyright (C) 2009, Lars-Peter Clausen <lars@metafoo.de>
* JZ4720/JZ4740 SoC NAND controller driver
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/jz4740_nand.h>
#include <linux/gpio.h>
#define JZ_REG_NAND_CTRL 0x50
#define JZ_REG_NAND_ECC_CTRL 0x100
#define JZ_REG_NAND_DATA 0x104
#define JZ_REG_NAND_PAR0 0x108
#define JZ_REG_NAND_PAR1 0x10C
#define JZ_REG_NAND_PAR2 0x110
#define JZ_REG_NAND_IRQ_STAT 0x114
#define JZ_REG_NAND_IRQ_CTRL 0x118
#define JZ_REG_NAND_ERR(x) (0x11C + (x << 2))
#define JZ_NAND_ECC_CTRL_PAR_READY BIT(4)
#define JZ_NAND_ECC_CTRL_ENCODING BIT(3)
#define JZ_NAND_ECC_CTRL_RS BIT(2)
#define JZ_NAND_ECC_CTRL_RESET BIT(1)
#define JZ_NAND_ECC_CTRL_ENABLE BIT(0)
#define JZ_NAND_STATUS_ERR_COUNT (BIT(31) | BIT(30) | BIT(29))
#define JZ_NAND_STATUS_PAD_FINISH BIT(4)
#define JZ_NAND_STATUS_DEC_FINISH BIT(3)
#define JZ_NAND_STATUS_ENC_FINISH BIT(2)
#define JZ_NAND_STATUS_UNCOR_ERROR BIT(1)
#define JZ_NAND_STATUS_ERROR BIT(0)
#define JZ_NAND_CTRL_ENABLE_CHIP(x) BIT(x << 1)
#define JZ_NAND_CTRL_ASSERT_CHIP(x) BIT((x << 1) + 1)
#define JZ_NAND_DATA_ADDR ((void __iomem *)0xB8000000)
#define JZ_NAND_CMD_ADDR (JZ_NAND_DATA_ADDR + 0x8000)
#define JZ_NAND_ADDR_ADDR (JZ_NAND_DATA_ADDR + 0x10000)
struct jz_nand {
struct mtd_info mtd;
struct nand_chip chip;
void __iomem *base;
struct resource *mem;
struct jz_nand_platform_data *pdata;
};
static inline struct jz_nand *mtd_to_jz_nand(struct mtd_info *mtd)
{
return container_of(mtd, struct jz_nand, mtd);
}
static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
struct jz_nand *nand = mtd_to_jz_nand(mtd);
struct nand_chip *chip = mtd->priv;
uint32_t reg;
if (ctrl & NAND_CTRL_CHANGE) {
BUG_ON((ctrl & NAND_ALE) && (ctrl & NAND_CLE));
if (ctrl & NAND_ALE)
chip->IO_ADDR_W = JZ_NAND_ADDR_ADDR;
else if (ctrl & NAND_CLE)
chip->IO_ADDR_W = JZ_NAND_CMD_ADDR;
else
chip->IO_ADDR_W = JZ_NAND_DATA_ADDR;
reg = readl(nand->base + JZ_REG_NAND_CTRL);
if ( ctrl & NAND_NCE )
reg |= JZ_NAND_CTRL_ASSERT_CHIP(0);
else
reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(0);
writel(reg, nand->base + JZ_REG_NAND_CTRL);
}
if (dat != NAND_CMD_NONE)
writeb(dat, chip->IO_ADDR_W);
}
static int jz_nand_dev_ready(struct mtd_info *mtd)
{
struct jz_nand *nand = mtd_to_jz_nand(mtd);
return gpio_get_value_cansleep(nand->pdata->busy_gpio);
}
static void jz_nand_hwctl(struct mtd_info *mtd, int mode)
{
struct jz_nand *nand = mtd_to_jz_nand(mtd);
uint32_t reg;
writel(0, nand->base + JZ_REG_NAND_IRQ_STAT);
reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL);
reg |= JZ_NAND_ECC_CTRL_RESET;
reg |= JZ_NAND_ECC_CTRL_ENABLE;
reg |= JZ_NAND_ECC_CTRL_RS;
switch(mode) {
case NAND_ECC_READ:
reg &= ~JZ_NAND_ECC_CTRL_ENCODING;
break;
case NAND_ECC_WRITE:
reg |= JZ_NAND_ECC_CTRL_ENCODING;
break;
default:
break;
}
writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL);
}
static int jz_nand_calculate_ecc_rs(struct mtd_info* mtd, const uint8_t* dat,
uint8_t *ecc_code)
{
struct jz_nand *nand = mtd_to_jz_nand(mtd);
uint32_t reg, status;
int i;
do {
status = readl(nand->base + JZ_REG_NAND_IRQ_STAT);
} while(!(status & JZ_NAND_STATUS_ENC_FINISH));
reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL);
reg &= ~JZ_NAND_ECC_CTRL_ENABLE;
writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL);
for (i = 0; i < 9; ++i) {
ecc_code[i] = readb(nand->base + JZ_REG_NAND_PAR0 + i);
}
return 0;
}
static void correct_data(uint8_t *dat, int index, int mask)
{
int offset = index & 0x7;
uint16_t data;
printk("correct: ");
index += (index >> 3);
data = dat[index];
data |= dat[index+1] << 8;
printk("0x%x -> ", data);
mask ^= (data >> offset) & 0x1ff;
data &= ~(0x1ff << offset);
data |= (mask << offset);
printk("0x%x\n", data);
dat[index] = data & 0xff;
dat[index+1] = (data >> 8) & 0xff;
}
static int jz_nand_correct_ecc_rs(struct mtd_info* mtd, uint8_t *dat,
uint8_t *read_ecc, uint8_t *calc_ecc)
{
struct jz_nand *nand = mtd_to_jz_nand(mtd);
int i, error_count, index;
uint32_t reg, status, error;
for(i = 0; i < 9; ++i) {
if (read_ecc[i] != 0xff)
break;
}
if (i == 9) {
for (i = 0; i < nand->chip.ecc.size; ++i) {
if (dat[i] != 0xff)
break;
}
if (i == nand->chip.ecc.size)
return 0;
}
for(i = 0; i < 9; ++i)
writeb(read_ecc[i], nand->base + JZ_REG_NAND_PAR0 + i);
reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL);
reg |= JZ_NAND_ECC_CTRL_PAR_READY;
writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL);
do {
status = readl(nand->base + JZ_REG_NAND_IRQ_STAT);
} while (!(status & JZ_NAND_STATUS_DEC_FINISH));
reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL);
reg &= ~JZ_NAND_ECC_CTRL_ENABLE;
writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL);
if (status & JZ_NAND_STATUS_ERROR) {
if (status & JZ_NAND_STATUS_UNCOR_ERROR) {
printk("uncorreclteble ecc:");
for(i = 0; i < 9; ++i)
printk(" 0x%x", read_ecc[i]);
printk("\n");
printk("uncorreclteble data:");
for(i = 0; i < 32; ++i)
printk(" 0x%x", dat[i]);
printk("\n");
return -1;
}
error_count = (status & JZ_NAND_STATUS_ERR_COUNT) >> 29;
printk("error_count: %d %x\n", error_count, status);
for(i = 0;i < error_count; ++i) {
error = readl(nand->base + JZ_REG_NAND_ERR(i));
index = ((error >> 16) & 0x1ff) - 1;
if (index >= 0 && index < 512) {
correct_data(dat, index, error & 0x1ff);
}
}
return error_count;
}
return 0;
}
#ifdef CONFIG_MTD_CMDLINE_PARTS
static const char *part_probes[] = {"cmdline", NULL};
#endif
static int __devinit jz_nand_probe(struct platform_device *pdev)
{
int ret;
struct jz_nand *nand;
struct nand_chip *chip;
struct mtd_info *mtd;
struct jz_nand_platform_data *pdata = pdev->dev.platform_data;
#ifdef CONFIG_MTD_PARTITIONS
struct mtd_partition *partition_info;
int num_partitions = 0;
#endif
nand = kzalloc(sizeof(*nand), GFP_KERNEL);
if (!nand) {
dev_err(&pdev->dev, "Failed to allocate device structure.\n");
return -ENOMEM;
}
nand->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!nand->mem) {
dev_err(&pdev->dev, "Failed to get platform mmio memory\n");
ret = -ENOENT;
goto err_free;
}
nand->mem = request_mem_region(nand->mem->start, resource_size(nand->mem),
pdev->name);
if (!nand->mem) {
dev_err(&pdev->dev, "Failed to request mmio memory region\n");
ret = -EBUSY;
goto err_free;
}
nand->base = ioremap(nand->mem->start, resource_size(nand->mem));
if (!nand->base) {
dev_err(&pdev->dev, "Faild to ioremap mmio memory region\n");
ret = -EBUSY;
goto err_release_mem;
}
if (pdata && gpio_is_valid(pdata->busy_gpio)) {
ret = gpio_request(pdata->busy_gpio, "jz nand busy line");
if (ret) {
dev_err(&pdev->dev, "Failed to request busy gpio %d: %d\n",
pdata->busy_gpio, ret);
goto err_iounmap;
}
}
mtd = &nand->mtd;
chip = &nand->chip;
mtd->priv = chip;
mtd->owner = THIS_MODULE;
mtd->name = "jz4740-nand";
chip->ecc.hwctl = jz_nand_hwctl;
chip->ecc.calculate = jz_nand_calculate_ecc_rs;
chip->ecc.correct = jz_nand_correct_ecc_rs;
chip->ecc.mode = NAND_ECC_HW;
chip->ecc.size = 512;
chip->ecc.bytes = 9;
if (pdata)
chip->ecc.layout = pdata->ecc_layout;
chip->chip_delay = 50;
chip->cmd_ctrl = jz_nand_cmd_ctrl;
if (pdata && gpio_is_valid(pdata->busy_gpio))
chip->dev_ready = jz_nand_dev_ready;
chip->IO_ADDR_R = JZ_NAND_DATA_ADDR;
chip->IO_ADDR_W = JZ_NAND_DATA_ADDR;
nand->pdata = pdata;
platform_set_drvdata(pdev, nand);
ret = nand_scan(mtd, 1);
if (ret) {
dev_err(&pdev->dev, "Failed to scan nand\n");
goto err_gpio_free;
}
#ifdef CONFIG_MTD_PARTITIONS
#ifdef CONFIG_MTD_CMDLINE_PARTS
num_partitions = parse_mtd_partitions(mtd, part_probes,
&partition_info, 0);
if (num_partitions <= 0 && pdata) {
num_partitions = pdata->num_partitions;
partition_info = pdata->partitions;
}
#else
if (pdata) {
num_partitions = pdata->num_partitions;
partition_info = pdata->partitions;
}
#endif
if (num_partitions > 0)
ret = add_mtd_partitions(mtd, partition_info, num_partitions);
else
#endif
ret = add_mtd_device(mtd);
if (ret) {
dev_err(&pdev->dev, "Failed to add mtd device\n");
goto err_nand_release;
}
dev_info(&pdev->dev, "Successfully registered JZ4740 NAND driver\n");
return 0;
err_nand_release:
nand_release(&nand->mtd);
err_gpio_free:
platform_set_drvdata(pdev, NULL);
gpio_free(pdata->busy_gpio);
err_iounmap:
iounmap(nand->base);
err_release_mem:
release_mem_region(nand->mem->start, resource_size(nand->mem));
err_free:
kfree(nand);
return ret;
}
static void __devexit jz_nand_remove(struct platform_device *pdev)
{
struct jz_nand *nand = platform_get_drvdata(pdev);
nand_release(&nand->mtd);
iounmap(nand->base);
release_mem_region(nand->mem->start, resource_size(nand->mem));
platform_set_drvdata(pdev, NULL);
kfree(nand);
}
struct platform_driver jz_nand_driver = {
.probe = jz_nand_probe,
.remove = __devexit_p(jz_nand_probe),
.driver = {
.name = "jz4740-nand",
.owner = THIS_MODULE,
},
};
static int __init jz_nand_init(void)
{
return platform_driver_register(&jz_nand_driver);
}
module_init(jz_nand_init);
static void __exit jz_nand_exit(void)
{
platform_driver_unregister(&jz_nand_driver);
}
module_exit(jz_nand_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("NAND controller driver for JZ4720/JZ4740 SoC");
MODULE_ALIAS("platform:jz4740-nand");
MODULE_ALIAS("platform:jz4720-nand");

View File

@ -0,0 +1,298 @@
/*
* linux/drivers/power/jz_battery
*
* Battery measurement code for Ingenic JZ SOC.
*
* based on tosa_battery.c
*
* Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/power_supply.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <asm/jzsoc.h>
#ifdef CONFIG_POWER_SUPPLY_DEBUG
#define dprintk(x...) printk(x)
#else
#define dprintk(x...) while(0){}
#endif
#define JZ_BAT_MAX_VOLTAGE 4200000 // uV
#define JZ_BAT_MIN_VOLTAGE 3600000
static DEFINE_MUTEX(bat_lock);
struct workqueue_struct *monitor_wqueue;
struct delayed_work bat_work;
struct mutex work_lock;
int bat_status = POWER_SUPPLY_STATUS_DISCHARGING;
extern unsigned int jz_read_battery(void);
/*********************************************************************
* Power
*********************************************************************/
static int jz_get_power_prop(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
if (psy->type == POWER_SUPPLY_TYPE_MAINS)
val->intval = !__gpio_get_pin(GPIO_DC_DETE_N);
else
val->intval = __gpio_get_pin(GPIO_USB_DETE);
break;
default:
return -EINVAL;
}
return 0;
}
static enum power_supply_property jz_power_props[] = {
POWER_SUPPLY_PROP_ONLINE,
};
static struct power_supply jz_ac = {
.name = "ac",
.type = POWER_SUPPLY_TYPE_MAINS,
.properties = jz_power_props,
.num_properties = ARRAY_SIZE(jz_power_props),
.get_property = jz_get_power_prop,
};
static struct power_supply jz_usb = {
.name = "usb",
.type = POWER_SUPPLY_TYPE_USB,
.properties = jz_power_props,
.num_properties = ARRAY_SIZE(jz_power_props),
.get_property = jz_get_power_prop,
};
/*********************************************************************
* Battery properties
*********************************************************************/
static unsigned long jz_read_bat(struct power_supply *bat_ps)
{
unsigned long val;
if (CFG_PBAT_DIV == 1)
val = (((unsigned long long)jz_read_battery() * 7500000)) >> 12;
else
val = (((unsigned long long)jz_read_battery() * CFG_PBAT_DIV * 2500000)) >> 12;
dprintk("--raw_batter_vol=%d uV\n", val);
return val;
}
static int jz_bat_get_property(struct power_supply *bat_ps,
enum power_supply_property psp,
union power_supply_propval *val)
{
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
val->intval = bat_status;
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO;
break;
case POWER_SUPPLY_PROP_HEALTH:
if(jz_read_bat(bat_ps) < 3600000) {
dprintk("--battery dead\n");
val->intval = POWER_SUPPLY_HEALTH_DEAD;
} else {
dprintk("--battery good\n");
val->intval = POWER_SUPPLY_HEALTH_GOOD;
}
break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = (jz_read_bat(bat_ps) - 3600000) * 100 / (4200000 - 3600000);
if (val->intval > 100)
val->intval = 100;
dprintk("--battery_capacity=%d\%\n",val->intval);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
val->intval = jz_read_bat(bat_ps);
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
val->intval = JZ_BAT_MAX_VOLTAGE;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
val->intval = JZ_BAT_MIN_VOLTAGE;
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = 1;
break;
case POWER_SUPPLY_PROP_TEMP:
case POWER_SUPPLY_PROP_VOL:
val->intval = 0; // reading TEMP and VOL aren't supported
break;
default:
return -EINVAL;
}
return 0;
}
static void jz_bat_external_power_changed(struct power_supply *bat_ps)
{
cancel_delayed_work(&bat_work);
queue_delayed_work(monitor_wqueue, &bat_work, HZ/10);
}
static char *status_text[] = {
[POWER_SUPPLY_STATUS_UNKNOWN] = "Unknown",
[POWER_SUPPLY_STATUS_CHARGING] = "Charging",
[POWER_SUPPLY_STATUS_DISCHARGING] = "Discharging",
[POWER_SUPPLY_STATUS_NOT_CHARGING] = "Not charging",
};
static void jz_bat_update(struct power_supply *bat_ps)
{
int old_status = bat_status;
static unsigned long old_batt_vol = 0;
unsigned long batt_vol = jz_read_bat(bat_ps);
mutex_lock(&work_lock);
if(!__gpio_get_pin(GPIO_CHARG_STAT_N))
bat_status = POWER_SUPPLY_STATUS_CHARGING;
else {
bat_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
}
dprintk("--battery status=%s\n", status_text[bat_status]);
if ((old_status != bat_status) ||
(old_batt_vol - batt_vol > 50000)) {
pr_debug("%s %s -> %s\n", bat_ps->name,
status_text[old_status],
status_text[bat_status]);
power_supply_changed(bat_ps);
}
old_batt_vol = batt_vol;
mutex_unlock(&work_lock);
}
static enum power_supply_property jz_bat_main_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_CAPACITY, /* in percents! */
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_VOL,
};
struct power_supply bat_ps = {
.name = "battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.properties = jz_bat_main_props,
.num_properties = ARRAY_SIZE(jz_bat_main_props),
.get_property = jz_bat_get_property,
.external_power_changed = jz_bat_external_power_changed,
.use_for_apm = 1,
};
static void jz_bat_work(struct work_struct *work)
{
const int interval = HZ * 6;
jz_bat_update(&bat_ps);
queue_delayed_work(monitor_wqueue, &bat_work, interval);
}
#ifdef CONFIG_PM
static int jz_bat_suspend(struct platform_device *dev, pm_message_t state)
{
bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
return 0;
}
static int jz_bat_resume(struct platform_device *dev)
{
bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
cancel_delayed_work(&bat_work);
queue_delayed_work(monitor_wqueue, &bat_work, HZ/10);
return 0;
}
#else
#define jz_bat_suspend NULL
#define jz_bat_resume NULL
#endif
static int __devinit jz_bat_probe(struct platform_device *dev)
{
int ret = 0;
printk("JZ battery init.\n");
mutex_init(&work_lock);
INIT_DELAYED_WORK(&bat_work, jz_bat_work);
__gpio_disable_pull(GPIO_USB_DETE);
power_supply_register(&dev->dev, &jz_ac);
power_supply_register(&dev->dev, &jz_usb);
ret = power_supply_register(&dev->dev, &bat_ps);
if (!ret) {
monitor_wqueue = create_singlethread_workqueue("jz_battery");
if (!monitor_wqueue) {
return -ESRCH;
}
queue_delayed_work(monitor_wqueue, &bat_work, HZ * 1);
}
return ret;
}
static int __devexit jz_bat_remove(struct platform_device *dev)
{
power_supply_unregister(&bat_ps);
return 0;
}
static struct platform_driver jz_bat_driver = {
.driver.name = "jz-battery",
.driver.owner = THIS_MODULE,
.probe = jz_bat_probe,
.remove = __devexit_p(jz_bat_remove),
.suspend = jz_bat_suspend,
.resume = jz_bat_resume,
};
static int __init jz_bat_init(void)
{
platform_device_register_simple("jz-battery", 0, NULL, 0);
return platform_driver_register(&jz_bat_driver);
}
static void __exit jz_bat_exit(void)
{
platform_driver_unregister(&jz_bat_driver);
}
module_init(jz_bat_init);
module_exit(jz_bat_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
MODULE_DESCRIPTION("Palm T|X battery driver");

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,121 @@
/*
* linux/drivers/usb/gadget/jz4740_udc.h
*
* Ingenic JZ4740 on-chip high speed USB device controller
*
* Copyright (C) 2006 Ingenic Semiconductor Inc.
* Author: <jlwei@ingenic.cn>
*
* 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 __USB_GADGET_JZ4740_H__
#define __USB_GADGET_JZ4740_H__
/*-------------------------------------------------------------------------*/
// Max packet size
#define EP0_MAXPACKETSIZE 64
#define EPBULK_MAXPACKETSIZE 512
#define EPINTR_MAXPACKETSIZE 64
#define UDC_MAX_ENDPOINTS 4
/*-------------------------------------------------------------------------*/
typedef enum ep_type {
ep_control, ep_bulk_in, ep_bulk_out, ep_interrupt
} ep_type_t;
struct jz4740_ep {
struct usb_ep ep;
struct jz4740_udc *dev;
const struct usb_endpoint_descriptor *desc;
struct list_head queue;
unsigned long pio_irqs;
u8 stopped;
u8 bEndpointAddress;
u8 bmAttributes;
ep_type_t ep_type;
u32 fifo;
u32 csr;
u32 reg_addr;
};
struct jz4740_request {
struct usb_request req;
struct list_head queue;
};
enum ep0state {
WAIT_FOR_SETUP, /* between STATUS ack and SETUP report */
DATA_STATE_XMIT, /* data tx stage */
DATA_STATE_NEED_ZLP, /* data tx zlp stage */
WAIT_FOR_OUT_STATUS, /* status stages */
DATA_STATE_RECV, /* data rx stage */
};
/* For function binding with UDC Disable - Added by River */
typedef enum {
UDC_STATE_ENABLE = 0,
UDC_STATE_DISABLE,
}udc_state_t;
struct jz4740_udc {
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
struct device *dev;
spinlock_t lock;
enum ep0state ep0state;
struct jz4740_ep ep[UDC_MAX_ENDPOINTS];
unsigned char usb_address;
/* UDC state - Added by River */
udc_state_t state;
};
extern struct jz4740_udc *the_controller;
#define ep_is_in(EP) (((EP)->bEndpointAddress&USB_DIR_IN)==USB_DIR_IN)
#define ep_maxpacket(EP) ((EP)->ep.maxpacket)
#define ep_index(EP) ((EP)->bEndpointAddress&0xF)
#define usb_set_index(i) (REG8(USB_REG_INDEX) = (i))
/*-------------------------------------------------------------------------*/
/* 2.5 stuff that's sometimes missing in 2.4 */
#ifndef container_of
#define container_of list_entry
#endif
#ifndef likely
#define likely(x) (x)
#define unlikely(x) (x)
#endif
#ifndef BUG_ON
#define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0)
#endif
#ifndef WARN_ON
#define WARN_ON(x) do { } while (0)
#endif
#ifndef IRQ_NONE
typedef void irqreturn_t;
#define IRQ_NONE
#define IRQ_HANDLED
#define IRQ_RETVAL(x)
#endif
#endif /* __USB_GADGET_JZ4740_H__ */

View File

@ -0,0 +1,50 @@
/*
* Ingenic USB Device Contoller Hotplug External Interfaces
*/
#ifndef __UDC_HOTPLUG_H__
#define __UDC_HOTPLUG_H__
#include <linux/notifier.h>
typedef enum {
BROADCAST_TYPE_STATE = 0,
BROADCAST_TYPE_EVENT,
}udc_hotplug_broadcast_type_t;
typedef enum {
EVENT_STATE_OFFLINE = 0,
EVENT_STATE_ONLINE,
}udc_hotplug_event_state_t;
typedef enum {
EVENT_TYPE_USB = 0,
EVENT_TYPE_CABLE,
}udc_hotplug_event_type_t;
enum {
EVENT_FLAG_UDC_PHY_TOUCHED = 0,
};
typedef struct {
udc_hotplug_event_type_t type;
udc_hotplug_event_state_t state;
unsigned long flags;
}udc_hotplug_event_t;
/* Register notifier */
int udc_hotplug_register_notifier(struct notifier_block *n, int request_state);
/* Unregister notifier */
int udc_hotplug_unregister_notifier(struct notifier_block *n);
/* Start keep alive */
int udc_hotplug_start_keep_alive(unsigned long timer_interval_in_jiffies, unsigned long counter_limit);
/* Do keep alive */
void udc_hotplug_do_keep_alive(void);
/* Stop keep alive */
void udc_hotplug_stop_keep_alive(void);
#endif /* Define __UDC_HOTPLUG_H__ */

View File

@ -0,0 +1,836 @@
/*
* Ingenic USB Device Controller Hotplug Core Function
* Detection mechanism and code are based on the old version of udc_hotplug.c
*/
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/wait.h>
#include <linux/kthread.h>
#include <linux/timer.h>
#include <asm/jzsoc.h>
#include "udc_hotplug.h"
#define PFX "jz_hotplug_udc"
#define D(msg, fmt...) \
// printk(KERN_ERR PFX": %s(): "msg, __func__, ##fmt);
/* HAVE_DETECT_SYNC
Provide a lock like seqlock keep the synchronization between the start and the end of a detection,
If the lock seems not synchronous(new interrupt comes, when doing our detection) in the end of a detection,
the result of the detection is discarded. No event will be broadcast, and the detection will be restarted.
Use to filter out more significant events when the interrupt is too noisy.
*/
//#define HAVE_DETECT_SYNC 1
#if defined (HAVE_DETECT_SYNC)
#define NR_RESTART_TIMES 3
#define NR_JIFFIES_SLEEP_BEFORE_RESTART 7
#endif
#define NR_GPIO_STABLE_TIMES 50
#define NR_JIFFIES_USB_DETECT_WAIT 11
#define DEFAULT_KEEP_ALIVE_TIMER_INTERVAL (2 * HZ)
#define DEFAULT_KEEP_ALIVE_COUNTER_LIMIT 2
#define UDC_HOTPLUG_PIN GPIO_UDC_HOTPLUG
#define UDC_HOTPLUG_IRQ (IRQ_GPIO_0 + UDC_HOTPLUG_PIN)
/* UDC State bits */
enum {
/* Online state. */
BIT_CABLE_ONLINE = 0,
BIT_USB_ONLINE,
/* State changed ?*/
BIT_CABLE_CHANGE,
BIT_USB_CHANGE,
/* What detection will be done ? */
BIT_DO_CABLE_DETECT,
BIT_DO_USB_DETECT,
/* What detection is requested ? */
BIT_REQUEST_CABLE_DETECT,
BIT_REQUEST_USB_DETECT,
/* Indicate whether a detection is finisned. */
BIT_USB_DETECT_DONE,
BIT_CABLE_DETECT_DONE,
BIT_UDC_PHY_TOUCHED,
/* Keep alive */
BIT_KEEP_ALIVE,
BIT_KEEP_ALIVE_TIMEOUT,
};
struct uh_data {
/* Notifier */
struct blocking_notifier_head notifier_head;
/* Thread */
struct task_struct *kthread;
/* Wait queue */
wait_queue_head_t kthread_wq; /* Kernel thread sleep here. */
wait_queue_head_t wq; /* Others sleep here. */
/* UDC State */
unsigned long state;
/* Current Event */
udc_hotplug_event_t cur_uh_event;
#if defined (HAVE_DETECT_SYNC)
/* Sync seq */
unsigned long irq_sync_seq;
unsigned long our_sync_seq;
#endif
/* Keep alive */
struct timer_list keep_alive_timer;
unsigned long keep_alive_counter_limit;
unsigned long keep_alive_timer_interval;
unsigned long keep_alive_counter;
};
static struct uh_data *g_puh_data = NULL;
#if defined (HAVE_DETECT_SYNC)
/* Seq sync function */
static inline int is_seq_sync(struct uh_data *uh)
{
return (uh->our_sync_seq == uh->irq_sync_seq);
}
static inline void reset_seq(struct uh_data *uh)
{
uh->our_sync_seq = uh->irq_sync_seq = 0;
return;
}
static inline void sync_seq(struct uh_data *uh)
{
uh->our_sync_seq = uh->irq_sync_seq;
return;
}
#endif
/* Call kernel thread to detect. */
static inline void start_detect(struct uh_data *uh)
{
D("called.\n");
#if defined (HAVE_DETECT_SYNC)
uh->irq_sync_seq ++;
#endif
wake_up_process(uh->kthread);
return;
}
static void wait_gpio_pin_stable(struct uh_data *uh)
{
unsigned long pin = 0;
int i = 1;
pin = __gpio_get_pin(UDC_HOTPLUG_PIN);
while (i < NR_GPIO_STABLE_TIMES) {
if (__gpio_get_pin(UDC_HOTPLUG_PIN) != pin) {
pin = __gpio_get_pin(UDC_HOTPLUG_PIN);
i = 1;
}else
i++;
sleep_on_timeout(&uh->wq, 1);
}
return;
}
/* Do cable detection */
static void cable_detect(struct uh_data *uh)
{
D("Wait pin stable.\n");
/* Wait GPIO pin stable first. */
wait_gpio_pin_stable(uh);
if (__gpio_get_pin(UDC_HOTPLUG_PIN)) {
D("Cable online.\n");
if (!test_and_set_bit(BIT_CABLE_ONLINE, &uh->state)) {
D("Cable state change to online.\n");
set_bit(BIT_CABLE_CHANGE, &uh->state);
}
}else {
D("Cable offline.\n");
/* Clear keep alive bit. */
clear_bit(BIT_KEEP_ALIVE, &uh->state);
if (test_and_clear_bit(BIT_CABLE_ONLINE, &uh->state)) {
D("Cable state change to offline.\n");
set_bit(BIT_CABLE_CHANGE, &uh->state);
}
}
set_bit(BIT_CABLE_DETECT_DONE, &uh->state);
return;
}
/* Really do USB detection */
static int do_usb_detect(struct uh_data *uh)
{
u32 intr_usb;
int rv;
D("called.\n");
__intc_mask_irq(IRQ_UDC);
/* Now enable PHY to start detect */
#ifdef CONFIG_SOC_JZ4740
REG_CPM_SCR |= CPM_SCR_USBPHY_ENABLE;
#elif defined(CONFIG_SOC_JZ4750) || defined(CONFIG_SOC_JZ4750D)
REG_CPM_OPCR |= CPM_OPCR_UDCPHY_ENABLE;
#endif
/* Clear IRQs */
REG16(USB_REG_INTRINE) = 0;
REG16(USB_REG_INTROUTE) = 0;
REG8(USB_REG_INTRUSBE) = 0;
/* disable UDC IRQs first */
REG16(USB_REG_INTRINE) = 0;
REG16(USB_REG_INTROUTE) = 0;
REG8(USB_REG_INTRUSBE) = 0;
/* Disable DMA */
REG32(USB_REG_CNTL1) = 0;
REG32(USB_REG_CNTL2) = 0;
/* Enable HS Mode */
REG8(USB_REG_POWER) |= USB_POWER_HSENAB;
/* Enable soft connect */
REG8(USB_REG_POWER) |= USB_POWER_SOFTCONN;
D("enable phy! %x %x %x %x %x\n",
REG8(USB_REG_POWER),
REG_CPM_OPCR,
REG16(USB_REG_INTRINE),
REG16(USB_REG_INTROUTE),
REG8(USB_REG_INTRUSBE));
/* Wait a moment. */
sleep_on_timeout(&uh->wq, NR_JIFFIES_USB_DETECT_WAIT);
intr_usb = REG8(USB_REG_INTRUSB);
if ((intr_usb & USB_INTR_RESET) ||
(intr_usb & USB_INTR_RESUME) ||
(intr_usb & USB_INTR_SUSPEND))
{
rv = 1;
}
else
{
rv = 0;
}
/* Detect finish ,clean every thing */
/* Disconnect from usb */
REG8(USB_REG_POWER) &= ~USB_POWER_SOFTCONN;
/* Disable the USB PHY */
#ifdef CONFIG_SOC_JZ4740
REG_CPM_SCR &= ~CPM_SCR_USBPHY_ENABLE;
#elif defined(CONFIG_SOC_JZ4750) || defined(CONFIG_SOC_JZ4750D)
REG_CPM_OPCR &= ~CPM_OPCR_UDCPHY_ENABLE;
#endif
/* Clear IRQs */
REG16(USB_REG_INTRINE) = 0;
REG16(USB_REG_INTROUTE) = 0;
REG8(USB_REG_INTRUSBE) = 0;
__intc_ack_irq(IRQ_UDC);
__intc_unmask_irq(IRQ_UDC);
mdelay(1);
return rv;
}
/* Do USB bus protocol detection */
static void usb_detect(struct uh_data *uh)
{
int rv = 0;
D("Called.\n");
/* If the cable has already been offline, we just pass the real USB detection. */
if (test_bit(BIT_CABLE_ONLINE, &uh->state)) {
D("Do real detection.\n");
rv = do_usb_detect(uh);
set_bit(BIT_UDC_PHY_TOUCHED, &uh->state);
}else{
clear_bit(BIT_UDC_PHY_TOUCHED, &uh->state);
D("No need to do real detection.\n");
}
if (rv) {
if (!test_and_set_bit(BIT_USB_ONLINE, &uh->state))
set_bit(BIT_USB_CHANGE, &uh->state);
}else{
/* Clear keep alive bit. */
clear_bit(BIT_KEEP_ALIVE, &uh->state);
if (test_and_clear_bit(BIT_USB_ONLINE, &uh->state))
set_bit(BIT_USB_CHANGE, &uh->state);
}
set_bit(BIT_USB_DETECT_DONE, &uh->state);
return;
}
/* USB is active ? */
static int usb_is_active(void)
{
unsigned long tmp;
tmp = REG16(USB_REG_FRAME);
mdelay(2); /* USB 1.1 Frame length is 1ms, USB 2.0 HS Frame length is 125us */
rmb();
return tmp == REG16(USB_REG_FRAME) ? 0 : 1;
}
/* Broadcast event to notifier */
static void do_broadcast_event(struct uh_data *uh)
{
udc_hotplug_event_t *e = &uh->cur_uh_event;
/* Collect Information */
if (test_and_clear_bit(BIT_CABLE_CHANGE, &uh->state)) {
e->type = EVENT_TYPE_CABLE;
e->state = (test_bit(BIT_CABLE_ONLINE, &uh->state)) ? EVENT_STATE_ONLINE: EVENT_STATE_OFFLINE;
e->flags = 0;
D("Broadcast cable event -> State: %s.\n", (e->state == EVENT_STATE_ONLINE ? "Online" : "Offline"));
/* Kick chain. */
blocking_notifier_call_chain(&uh->notifier_head, BROADCAST_TYPE_EVENT, e);
}
if (test_and_clear_bit(BIT_USB_CHANGE, &uh->state)) {
e->type = EVENT_TYPE_USB;
e->state = (test_bit(BIT_USB_ONLINE, &uh->state)) ? EVENT_STATE_ONLINE : EVENT_STATE_OFFLINE;
e->flags = 0;
if (test_bit(BIT_UDC_PHY_TOUCHED, &uh->state)) {
set_bit(EVENT_FLAG_UDC_PHY_TOUCHED, &e->flags);
}
D("Broadcast USB event -> State: %s.\n", (e->state == EVENT_STATE_ONLINE ? "Online" : "Offline"));
/* Kick chain. */
blocking_notifier_call_chain(&uh->notifier_head, BROADCAST_TYPE_EVENT, e);
}
return;
}
/* Handle pending request */
static inline void handle_request(struct uh_data *uh)
{
if (test_and_clear_bit(BIT_REQUEST_CABLE_DETECT, &uh->state))
set_bit(BIT_DO_CABLE_DETECT, &uh->state);
if (test_and_clear_bit(BIT_REQUEST_USB_DETECT, &uh->state))
set_bit(BIT_DO_USB_DETECT, &uh->state);
return;
}
/* Have pending request ? */
static inline int pending_request(struct uh_data *uh)
{
if (test_bit(BIT_REQUEST_CABLE_DETECT, &uh->state) || test_bit(BIT_REQUEST_USB_DETECT, &uh->state))
return 1;
else
return 0;
}
#if defined (HAVE_DETECT_SYNC)
static void prepare_restart(struct uh_data *uh, wait_queue_head_t *wq)
{
D("Called.\n");
if (test_bit(BIT_CABLE_DETECT_DONE, &uh->state))
set_bit(BIT_DO_CABLE_DETECT, &uh->state);
if (test_bit(BIT_USB_DETECT_DONE, &uh->state))
set_bit(BIT_DO_USB_DETECT, &uh->state);
sleep_on_timeout(wq, NR_JIFFIES_SLEEP_BEFORE_RESTART);
sync_seq(uh);
return;
}
/* Called from kernel thread */
static void udc_pnp_detect(struct uh_data *uh)
{
int nr_restart = 0;
D("Do UDC detection.\n");
while (nr_restart != NR_RESTART_TIMES) {
/* Do cable detection ? */
if (test_bit(BIT_DO_CABLE_DETECT, &uh->state)) {
D("Do cable detection.\n");
cable_detect(uh);
}
/* Need restart ? */
if (!is_seq_sync(uh)) {
nr_restart ++;
prepare_restart(uh, &uh->wq);
continue;
}
/* Do USB detection ? */
if (test_bit(BIT_DO_USB_DETECT, &uh->state)) {
D("Do USB detection.\n");
usb_detect(uh);
}
/* Need restart ? */
if (!is_seq_sync(uh)) {
nr_restart ++;
prepare_restart(uh, &uh->wq);
continue;
}
/* Done */
D("Done.\n");
clear_bit(BIT_DO_CABLE_DETECT, &uh->state);
clear_bit(BIT_DO_USB_DETECT, &uh->state);
break;
}
return;
}
static inline void broadcast_event(struct uh_data *uh)
{
/* Sync ? */
if (is_seq_sync(uh)) {
D("Sync -> Broadcast event.\n");
do_broadcast_event(uh);
}else{
D("Not sync -> Prepare restarting.\n");
prepare_restart(uh, &uh->kthread_wq);
}
}
static inline void udc_pnp_thread_sleep(struct uh_data *uh)
{
/* Sync ? -> Sleep. */
if ( !pending_request(uh) || is_seq_sync(uh)) {
D("Sleep.\n");
sleep_on(&uh->kthread_wq);
}
return;
}
#else /* !HAVE_DETECT_SYNC */
/* Called from kernel thread */
static void udc_pnp_detect(struct uh_data *uh)
{
D("Do UDC detection.\n");
/* Do cable detection ? */
if (test_bit(BIT_DO_CABLE_DETECT, &uh->state)) {
D("Do cable detection.\n");
cable_detect(uh);
}
/* Do USB detection ? */
if (test_bit(BIT_DO_USB_DETECT, &uh->state)) {
D("Do USB detection.\n");
usb_detect(uh);
}
/* Done */
D("Done.\n");
clear_bit(BIT_DO_CABLE_DETECT, &uh->state);
clear_bit(BIT_DO_USB_DETECT, &uh->state);
return;
}
static inline void broadcast_event(struct uh_data *uh)
{
D("Broadcast event.\n");
do_broadcast_event(uh);
return;
}
static inline void udc_pnp_thread_sleep(struct uh_data *uh)
{
if (!pending_request(uh)) {
D("Sleep.\n");
sleep_on(&uh->kthread_wq);
}
return;
}
#endif /* HAVE_DETECT_SYNC */
/* Kernel thread */
static int udc_pnp_thread(void *data)
{
struct uh_data *uh = (struct uh_data *)data;
while (!kthread_should_stop()) {
/* Sleep. */
udc_pnp_thread_sleep(uh);
D("Running.\n");
if (kthread_should_stop())
break;
#if defined (HAVE_DETECT_SYNC)
/* Sync */
sync_seq(uh);
#endif
D("Will do UDC detection.\n");
handle_request(uh);
/* Do detect */
udc_pnp_detect(uh);
D("Done.\n");
/* Broadcast event. */
broadcast_event(uh);
}
D("Exit.\n");
return 0;
}
static irqreturn_t udc_pnp_irq(int irq, void *dev_id)
{
struct uh_data *uh = (struct uh_data *)dev_id;
D("called.\n");
/* clear interrupt pending status */
__gpio_ack_irq(UDC_HOTPLUG_PIN);
set_bit(BIT_REQUEST_CABLE_DETECT, &uh->state);
set_bit(BIT_REQUEST_USB_DETECT, &uh->state);
start_detect(uh);
return IRQ_HANDLED;
}
static void __init init_gpio(struct uh_data *uh)
{
/* get current pin level */
__gpio_disable_pull(UDC_HOTPLUG_PIN);
__gpio_as_input(UDC_HOTPLUG_PIN);
udelay(1);
cable_detect(uh);
/* Because of every plug IN/OUT action will casue more than one interrupt,
So whether rising trigger or falling trigger method can both start the detection.
*/
__gpio_as_irq_rise_edge(UDC_HOTPLUG_PIN);
if (test_bit(BIT_CABLE_ONLINE, &uh->state)) {
D("Cable Online -> Do start detection.\n");
set_bit(BIT_REQUEST_CABLE_DETECT, &uh->state);
set_bit(BIT_REQUEST_USB_DETECT, &uh->state);
start_detect(uh);
}else{
D("Cable Offline.\n");
}
return;
}
/* ---------------------------------------------------------------------------------- */
/* Export routines */
static void udc_hotplug_keep_alive_timer_func(unsigned long data)
{
struct uh_data *uh = (struct uh_data *)data;
D("Timer running.\n");
/* Decrease the counter. */
if (test_bit(BIT_KEEP_ALIVE, &uh->state) && !(--uh->keep_alive_counter)) {
if (!usb_is_active()) {
D("Timeout.\n");
set_bit(BIT_KEEP_ALIVE_TIMEOUT, &uh->state);
clear_bit(BIT_USB_ONLINE, &uh->state);
set_bit(BIT_USB_CHANGE, &uh->state);
/* No detection needed. We just want to broadcast our event. */
start_detect(uh);
}
}
/* Set next active time. */
if (test_bit(BIT_KEEP_ALIVE, &uh->state) && !test_bit(BIT_KEEP_ALIVE_TIMEOUT, &uh->state))
mod_timer(&uh->keep_alive_timer, uh->keep_alive_timer_interval + jiffies);
else
D("Timer will stop.\n");
return;
}
int udc_hotplug_register_notifier(struct notifier_block *n, int request_state)
{
struct uh_data *uh = g_puh_data;
udc_hotplug_event_t e;
D("Register notifier: 0x%p.\n", (void *)n);
/* Notifer will be registered is requesting current state. */
if (request_state) {
BUG_ON(!n->notifier_call);
/* Cable State */
e.type = EVENT_TYPE_CABLE;
e.state = (test_bit(BIT_CABLE_ONLINE, &uh->state)) ? EVENT_STATE_ONLINE: EVENT_STATE_OFFLINE;
n->notifier_call(n, BROADCAST_TYPE_STATE, &e);
/* USB State */
e.type = EVENT_TYPE_USB;
e.state = (test_bit(BIT_CABLE_ONLINE, &uh->state)) ? EVENT_STATE_ONLINE: EVENT_STATE_OFFLINE;
n->notifier_call(n, BROADCAST_TYPE_STATE, &e);
}
return blocking_notifier_chain_register(&uh->notifier_head, n);
}EXPORT_SYMBOL(udc_hotplug_register_notifier);
int udc_hotplug_unregister_notifier(struct notifier_block *n)
{
struct uh_data *uh = g_puh_data;
D("Unregister notifier: 0x%p.\n", (void *)n);
return blocking_notifier_chain_unregister(&uh->notifier_head, n);
}EXPORT_SYMBOL(udc_hotplug_unregister_notifier);
/* Start keep alive, 0 - Use default value */
int udc_hotplug_start_keep_alive(unsigned long timer_interval_in_jiffies, unsigned long counter_limit)
{
struct uh_data *uh = g_puh_data;
/* Already started. */
if (test_and_set_bit(BIT_KEEP_ALIVE, &uh->state))
return 0;
if (timer_interval_in_jiffies)
uh->keep_alive_timer_interval = timer_interval_in_jiffies;
else
uh->keep_alive_timer_interval = DEFAULT_KEEP_ALIVE_TIMER_INTERVAL;
if (counter_limit)
uh->keep_alive_counter_limit = counter_limit;
else
uh->keep_alive_counter_limit = DEFAULT_KEEP_ALIVE_COUNTER_LIMIT;
uh->keep_alive_counter = uh->keep_alive_counter_limit;
/* Active our timer. */
return mod_timer(&uh->keep_alive_timer, 3 + jiffies);
}EXPORT_SYMBOL(udc_hotplug_start_keep_alive);
void udc_hotplug_do_keep_alive(void)
{
struct uh_data *uh = g_puh_data;
D("Keep alive.\n");
/* Reset counter */
uh->keep_alive_counter = uh->keep_alive_counter_limit;
/* We are alive again. */
if (test_and_clear_bit(BIT_KEEP_ALIVE_TIMEOUT, &uh->state)) {
D("Reactive timer.\n");
/* Active timer. */
set_bit(BIT_KEEP_ALIVE, &uh->state);
mod_timer(&uh->keep_alive_timer, 3 + jiffies);
}
return;
}EXPORT_SYMBOL(udc_hotplug_do_keep_alive);
void udc_hotplug_stop_keep_alive(void)
{
struct uh_data *uh = g_puh_data;
clear_bit(BIT_KEEP_ALIVE, &uh->state);
return;
}EXPORT_SYMBOL(udc_hotplug_stop_keep_alive);
/* ----------------------------------------------------------------------------- */
/*
* Module init and exit
*/
static int __init udc_hotplug_init(void)
{
struct uh_data *uh;
unsigned long status = 0;
int rv;
g_puh_data = (struct uh_data *)kzalloc(sizeof(struct uh_data), GFP_KERNEL);
if (!g_puh_data) {
printk(KERN_ERR PFX": Failed to allocate memory.\n");
return -ENOMEM;
}
uh = g_puh_data;
set_bit(1, &status);
BLOCKING_INIT_NOTIFIER_HEAD(&uh->notifier_head);
init_waitqueue_head(&uh->kthread_wq);
init_waitqueue_head(&uh->wq);
init_timer(&uh->keep_alive_timer);
uh->keep_alive_timer.function = udc_hotplug_keep_alive_timer_func;
uh->keep_alive_timer.expires = jiffies - 1; /* Add a stopped timer */
uh->keep_alive_timer.data = (unsigned long)uh;
add_timer(&uh->keep_alive_timer);
#if defined (HAVE_DETECT_SYNC)
reset_seq(uh);
#endif
/* Create pnp thread and register IRQ */
uh->kthread = kthread_run(udc_pnp_thread, uh, "kudcd");
if (IS_ERR(uh->kthread)) {
printk(KERN_ERR PFX": Failed to create system monitor thread.\n");
rv = PTR_ERR(uh->kthread);
goto err;
}
set_bit(2, &status);
rv = request_irq(UDC_HOTPLUG_IRQ, udc_pnp_irq, IRQF_DISABLED, "udc_pnp", uh);
if (rv) {
printk(KERN_ERR PFX": Could not get udc hotplug irq %d\n", UDC_HOTPLUG_IRQ);
goto err;
}
init_gpio(uh);
#if defined (HAVE_DETECT_SYNC)
printk(KERN_ERR PFX": Registered(HAVE_DETECT_SYNC).\n");
#else
printk(KERN_ERR PFX": Registered.\n");
#endif
return 0;
err:
if (test_bit(2, &status)) {
kthread_stop(uh->kthread);
}
if (test_bit(1, &status)) {
kfree(g_puh_data);
}
return rv;
}
static void __exit udc_hotplug_exit(void)
{
free_irq(UDC_HOTPLUG_IRQ, g_puh_data);
kthread_stop(g_puh_data->kthread);
kfree(g_puh_data);
return;
}
module_init(udc_hotplug_init);
module_exit(udc_hotplug_exit);
MODULE_AUTHOR("River Wang <zwang@ingenic.cn>");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,376 @@
/*
* linux/drivers/video/jzslcd.h -- Ingenic On-Chip SLCD frame buffer device
*
* Copyright (C) 2005-2007, Ingenic Semiconductor Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#ifndef __JZSLCD_H__
#define __JZSLCD_H__
#define UINT16 unsigned short
#define UINT32 unsigned int
#define NR_PALETTE 256
/* Jz LCDFB supported I/O controls. */
#define FBIOSETBACKLIGHT 0x4688
#define FBIODISPON 0x4689
#define FBIODISPOFF 0x468a
#define FBIORESET 0x468b
#define FBIOPRINT_REG 0x468c
#define FBIO_REFRESH_ALWAYS 0x468d
#define FBIO_REFRESH_EVENTS 0x468e
#define FBIO_DO_REFRESH 0x468f
#define FBIO_SET_REG 0x4690
#ifdef CONFIG_JZ_SLCD_LGDP4551
#define PIN_CS_N (32*2+18) /* Chip select :SLCD_WR: GPC18 */
#define PIN_RESET_N (32*2+21) /* LCD reset :SLCD_RST: GPC21*/
#define PIN_RS_N (32*2+19)
#define __slcd_special_pin_init() \
do { \
__gpio_as_output(PIN_CS_N); \
__gpio_as_output(PIN_RESET_N); \
__gpio_clear_pin(PIN_CS_N); /* Clear CS */\
mdelay(100); \
} while(0)
#define __slcd_special_on() \
do { /* RESET# */ \
__gpio_set_pin(PIN_RESET_N); \
mdelay(10); \
__gpio_clear_pin(PIN_RESET_N); \
mdelay(10); \
__gpio_set_pin(PIN_RESET_N); \
mdelay(100); \
Mcupanel_RegSet(0x0015,0x0050); \
Mcupanel_RegSet(0x0011,0x0000); \
Mcupanel_RegSet(0x0010,0x3628); \
Mcupanel_RegSet(0x0012,0x0002); \
Mcupanel_RegSet(0x0013,0x0E47); \
udelay(100); \
Mcupanel_RegSet(0x0012,0x0012); \
udelay(100); \
Mcupanel_RegSet(0x0010,0x3620); \
Mcupanel_RegSet(0x0013,0x2E47); \
udelay(50); \
Mcupanel_RegSet(0x0030,0x0000); \
Mcupanel_RegSet(0x0031,0x0502); \
Mcupanel_RegSet(0x0032,0x0307); \
Mcupanel_RegSet(0x0033,0x0304); \
Mcupanel_RegSet(0x0034,0x0004); \
Mcupanel_RegSet(0x0035,0x0401); \
Mcupanel_RegSet(0x0036,0x0707); \
Mcupanel_RegSet(0x0037,0x0303); \
Mcupanel_RegSet(0x0038,0x1E02); \
Mcupanel_RegSet(0x0039,0x1E02); \
Mcupanel_RegSet(0x0001,0x0000); \
Mcupanel_RegSet(0x0002,0x0300); \
if (jzfb.bpp == 16) \
Mcupanel_RegSet(0x0003,0x10B8); /*8-bit system interface two transfers
up:0x10B8 down:0x1088 left:0x1090 right:0x10a0*/ \
else \
if (jzfb.bpp == 32)\
Mcupanel_RegSet(0x0003,0xD0B8);/*8-bit system interface three transfers,666
up:0xD0B8 down:0xD088 left:0xD090 right:0xD0A0*/ \
Mcupanel_RegSet(0x0008,0x0204);\
Mcupanel_RegSet(0x000A,0x0008);\
Mcupanel_RegSet(0x0060,0x3100);\
Mcupanel_RegSet(0x0061,0x0001);\
Mcupanel_RegSet(0x0090,0x0052);\
Mcupanel_RegSet(0x0092,0x000F);\
Mcupanel_RegSet(0x0093,0x0001);\
Mcupanel_RegSet(0x009A,0x0008);\
Mcupanel_RegSet(0x00A3,0x0010);\
Mcupanel_RegSet(0x0050,0x0000);\
Mcupanel_RegSet(0x0051,0x00EF);\
Mcupanel_RegSet(0x0052,0x0000);\
Mcupanel_RegSet(0x0053,0x018F);\
/*===Display_On_Function=== */ \
Mcupanel_RegSet(0x0007,0x0001);\
Mcupanel_RegSet(0x0007,0x0021);\
Mcupanel_RegSet(0x0007,0x0023);\
Mcupanel_RegSet(0x0007,0x0033);\
Mcupanel_RegSet(0x0007,0x0133);\
Mcupanel_Command(0x0022);/*Write Data to GRAM */ \
udelay(1); \
Mcupanel_SetAddr(0,0); \
mdelay(100); \
} while (0)
#define __slcd_special_off() \
do { \
} while(0)
#endif /*CONFIG_JZ_SLCD_LGDP4551_xxBUS*/
#ifdef CONFIG_JZ_SLCD_SPFD5420A
//#define PIN_CS_N (32*2+18) // Chip select //GPC18;
#define PIN_CS_N (32*2+22) // Chip select //GPC18;
#define PIN_RESET_N (32*1+18) // LCD reset //GPB18;
#define PIN_RS_N (32*2+19) // LCD RS //GPC19;
#define PIN_POWER_N (32*3+0) //Power off //GPD0;
#define PIN_FMARK_N (32*3+1) //fmark //GPD1;
#define GAMMA() \
do { \
Mcupanel_RegSet(0x0300,0x0101); \
Mcupanel_RegSet(0x0301,0x0b27); \
Mcupanel_RegSet(0x0302,0x132a); \
Mcupanel_RegSet(0x0303,0x2a13); \
Mcupanel_RegSet(0x0304,0x270b); \
Mcupanel_RegSet(0x0305,0x0101); \
Mcupanel_RegSet(0x0306,0x1205); \
Mcupanel_RegSet(0x0307,0x0512); \
Mcupanel_RegSet(0x0308,0x0005); \
Mcupanel_RegSet(0x0309,0x0003); \
Mcupanel_RegSet(0x030a,0x0f04); \
Mcupanel_RegSet(0x030b,0x0f00); \
Mcupanel_RegSet(0x030c,0x000f); \
Mcupanel_RegSet(0x030d,0x040f); \
Mcupanel_RegSet(0x030e,0x0300); \
Mcupanel_RegSet(0x030f,0x0500); \
/*** secorrect gamma2 ***/ \
Mcupanel_RegSet(0x0400,0x3500); \
Mcupanel_RegSet(0x0401,0x0001); \
Mcupanel_RegSet(0x0404,0x0000); \
Mcupanel_RegSet(0x0500,0x0000); \
Mcupanel_RegSet(0x0501,0x0000); \
Mcupanel_RegSet(0x0502,0x0000); \
Mcupanel_RegSet(0x0503,0x0000); \
Mcupanel_RegSet(0x0504,0x0000); \
Mcupanel_RegSet(0x0505,0x0000); \
Mcupanel_RegSet(0x0600,0x0000); \
Mcupanel_RegSet(0x0606,0x0000); \
Mcupanel_RegSet(0x06f0,0x0000); \
Mcupanel_RegSet(0x07f0,0x5420); \
Mcupanel_RegSet(0x07f3,0x288a); \
Mcupanel_RegSet(0x07f4,0x0022); \
Mcupanel_RegSet(0x07f5,0x0001); \
Mcupanel_RegSet(0x07f0,0x0000); \
} while(0)
#define __slcd_special_on() \
do { \
__gpio_set_pin(PIN_RESET_N); \
mdelay(10); \
__gpio_clear_pin(PIN_RESET_N); \
mdelay(10); \
__gpio_set_pin(PIN_RESET_N); \
mdelay(100); \
if (jzfb.bus == 18) {\
Mcupanel_RegSet(0x0606,0x0000); \
udelay(10); \
Mcupanel_RegSet(0x0007,0x0001); \
udelay(10); \
Mcupanel_RegSet(0x0110,0x0001); \
udelay(10); \
Mcupanel_RegSet(0x0100,0x17b0); \
Mcupanel_RegSet(0x0101,0x0147); \
Mcupanel_RegSet(0x0102,0x019d); \
Mcupanel_RegSet(0x0103,0x8600); \
Mcupanel_RegSet(0x0281,0x0010); \
udelay(10); \
Mcupanel_RegSet(0x0102,0x01bd); \
udelay(10); \
/************initial************/\
Mcupanel_RegSet(0x0000,0x0000); \
Mcupanel_RegSet(0x0001,0x0000); \
Mcupanel_RegSet(0x0002,0x0400); \
Mcupanel_RegSet(0x0003,0x1288); /*up:0x1288 down:0x12B8 left:0x1290 right:0x12A0*/ \
Mcupanel_RegSet(0x0006,0x0000); \
Mcupanel_RegSet(0x0008,0x0503); \
Mcupanel_RegSet(0x0009,0x0001); \
Mcupanel_RegSet(0x000b,0x0010); \
Mcupanel_RegSet(0x000c,0x0000); \
Mcupanel_RegSet(0x000f,0x0000); \
Mcupanel_RegSet(0x0007,0x0001); \
Mcupanel_RegSet(0x0010,0x0010); \
Mcupanel_RegSet(0x0011,0x0202); \
Mcupanel_RegSet(0x0012,0x0300); \
Mcupanel_RegSet(0x0020,0x021e); \
Mcupanel_RegSet(0x0021,0x0202); \
Mcupanel_RegSet(0x0022,0x0100); \
Mcupanel_RegSet(0x0090,0x0000); \
Mcupanel_RegSet(0x0092,0x0000); \
Mcupanel_RegSet(0x0100,0x16b0); \
Mcupanel_RegSet(0x0101,0x0147); \
Mcupanel_RegSet(0x0102,0x01bd); \
Mcupanel_RegSet(0x0103,0x2c00); \
Mcupanel_RegSet(0x0107,0x0000); \
Mcupanel_RegSet(0x0110,0x0001); \
Mcupanel_RegSet(0x0210,0x0000); \
Mcupanel_RegSet(0x0211,0x00ef); \
Mcupanel_RegSet(0x0212,0x0000); \
Mcupanel_RegSet(0x0213,0x018f); \
Mcupanel_RegSet(0x0280,0x0000); \
Mcupanel_RegSet(0x0281,0x0001); \
Mcupanel_RegSet(0x0282,0x0000); \
GAMMA(); \
Mcupanel_RegSet(0x0007,0x0173); \
} else { \
Mcupanel_RegSet(0x0600, 0x0001); /*soft reset*/ \
mdelay(10); \
Mcupanel_RegSet(0x0600, 0x0000); /*soft reset*/ \
mdelay(10); \
Mcupanel_RegSet(0x0606, 0x0000); /*i80-i/F Endian Control*/ \
/*===User setting=== */ \
Mcupanel_RegSet(0x0001, 0x0000);/* Driver Output Control-----0x0100 SM(bit10) | 0x400*/ \
Mcupanel_RegSet(0x0002, 0x0100); /*LCD Driving Wave Control 0x0100 */ \
if (jzfb.bpp == 16) \
Mcupanel_RegSet(0x0003, 0x50A8);/*Entry Mode 0x1030*/ \
else /*bpp = 18*/ \
Mcupanel_RegSet(0x0003, 0x1010 | 0xC8); /*Entry Mode 0x1030*/ \
/*#endif */ \
Mcupanel_RegSet(0x0006, 0x0000); /*Outline Sharpening Control*/\
Mcupanel_RegSet(0x0008, 0x0808); /*Sets the number of lines for front/back porch period*/\
Mcupanel_RegSet(0x0009, 0x0001); /*Display Control 3 */\
Mcupanel_RegSet(0x000B, 0x0010); /*Low Power Control*/\
Mcupanel_RegSet(0x000C, 0x0000); /*External Display Interface Control 1 /*0x0001*/\
Mcupanel_RegSet(0x000F, 0x0000); /*External Display Interface Control 2 */\
Mcupanel_RegSet(0x0400, 0xB104);/*Base Image Number of Line---GS(bit15) | 0x8000*/ \
Mcupanel_RegSet(0x0401, 0x0001); /*Base Image Display 0x0001*/\
Mcupanel_RegSet(0x0404, 0x0000); /*Base Image Vertical Scroll Control 0x0000*/\
Mcupanel_RegSet(0x0500, 0x0000); /*Partial Image 1: Display Position*/\
Mcupanel_RegSet(0x0501, 0x0000); /*RAM Address (Start Line Address) */\
Mcupanel_RegSet(0x0502, 0x018f); /*RAM Address (End Line Address) */ \
Mcupanel_RegSet(0x0503, 0x0000); /*Partial Image 2: Display Position RAM Address*/\
Mcupanel_RegSet(0x0504, 0x0000); /*RAM Address (Start Line Address) */\
Mcupanel_RegSet(0x0505, 0x0000); /*RAM Address (End Line Address)*/\
/*Panel interface control===*/\
Mcupanel_RegSet(0x0010, 0x0011); /*Division Ratio,Clocks per Line 14 */\
mdelay(10); \
Mcupanel_RegSet(0x0011, 0x0202); /*Division Ratio,Clocks per Line*/\
Mcupanel_RegSet(0x0012, 0x0300); /*Sets low power VCOM drive period. */\
mdelay(10); \
Mcupanel_RegSet(0x0020, 0x021e); /*Panel Interface Control 4 */\
Mcupanel_RegSet(0x0021, 0x0202); /*Panel Interface Control 5 */\
Mcupanel_RegSet(0x0022, 0x0100); /*Panel Interface Control 6*/\
Mcupanel_RegSet(0x0090, 0x0000); /*Frame Marker Control */\
Mcupanel_RegSet(0x0092, 0x0000); /*MDDI Sub-display Control */\
/*===Gamma setting=== */\
Mcupanel_RegSet(0x0300, 0x0101); /*γ Control*/\
Mcupanel_RegSet(0x0301, 0x0000); /*γ Control*/\
Mcupanel_RegSet(0x0302, 0x0016); /*γ Control*/\
Mcupanel_RegSet(0x0303, 0x2913); /*γ Control*/\
Mcupanel_RegSet(0x0304, 0x260B); /*γ Control*/\
Mcupanel_RegSet(0x0305, 0x0101); /*γ Control*/\
Mcupanel_RegSet(0x0306, 0x1204); /*γ Control*/\
Mcupanel_RegSet(0x0307, 0x0415); /*γ Control*/\
Mcupanel_RegSet(0x0308, 0x0205); /*γ Control*/\
Mcupanel_RegSet(0x0309, 0x0303); /*γ Control*/\
Mcupanel_RegSet(0x030a, 0x0E05); /*γ Control*/\
Mcupanel_RegSet(0x030b, 0x0D01); /*γ Control*/\
Mcupanel_RegSet(0x030c, 0x010D); /*γ Control*/\
Mcupanel_RegSet(0x030d, 0x050E); /*γ Control*/\
Mcupanel_RegSet(0x030e, 0x0303); /*γ Control*/\
Mcupanel_RegSet(0x030f, 0x0502); /*γ Control*/\
/*===Power on sequence===*/\
Mcupanel_RegSet(0x0007, 0x0001); /*Display Control 1*/\
Mcupanel_RegSet(0x0110, 0x0001); /*Power supply startup enable bit*/\
Mcupanel_RegSet(0x0112, 0x0060); /*Power Control 7*/\
Mcupanel_RegSet(0x0100, 0x16B0); /*Power Control 1 */\
Mcupanel_RegSet(0x0101, 0x0115); /*Power Control 2*/\
Mcupanel_RegSet(0x0102, 0x0119); /*Starts VLOUT3,Sets the VREG1OUT.*/\
mdelay(50); \
Mcupanel_RegSet(0x0103, 0x2E00); /*set the amplitude of VCOM*/\
mdelay(50);\
Mcupanel_RegSet(0x0282, 0x0093);/*0x008E);/*0x0093); /*VCOMH voltage*/\
Mcupanel_RegSet(0x0281, 0x000A); /*Selects the factor of VREG1OUT to generate VCOMH. */\
Mcupanel_RegSet(0x0102, 0x01BE); /*Starts VLOUT3,Sets the VREG1OUT.*/\
mdelay(10);\
/*Address */\
Mcupanel_RegSet(0x0210, 0x0000); /*Window Horizontal RAM Address Start*/\
Mcupanel_RegSet(0x0211, 0x00ef); /*Window Horizontal RAM Address End*/\
Mcupanel_RegSet(0x0212, 0x0000); /*Window Vertical RAM Address Start*/\
Mcupanel_RegSet(0x0213, 0x018f); /*Window Vertical RAM Address End */\
Mcupanel_RegSet(0x0200, 0x0000); /*RAM Address Set (Horizontal Address)*/\
Mcupanel_RegSet(0x0201, 0x018f); /*RAM Address Set (Vertical Address)*/ \
/*===Display_On_Function===*/\
Mcupanel_RegSet(0x0007, 0x0021); /*Display Control 1 */\
mdelay(50); /*40*/\
Mcupanel_RegSet(0x0007, 0x0061); /*Display Control 1 */\
mdelay(50); /*100*/\
Mcupanel_RegSet(0x0007, 0x0173); /*Display Control 1 */\
mdelay(50); /*300*/\
}\
Mcupanel_Command(0x0202); /*Write Data to GRAM */ \
udelay(10);\
Mcupanel_SetAddr(0,0);\
udelay(100);\
} while(0)
#define __slcd_special_pin_init() \
do { \
__gpio_as_output(PIN_CS_N); \
__gpio_as_output(PIN_RESET_N); \
__gpio_clear_pin(PIN_CS_N); /* Clear CS */ \
__gpio_as_output(PIN_POWER_N); \
mdelay(100); \
} while(0)
#endif /*CONFIG_JZ_SLCD_SPFD5420A*/
#ifndef __slcd_special_pin_init
#define __slcd_special_pin_init()
#endif
#ifndef __slcd_special_on
#define __slcd_special_on()
#endif
#ifndef __slcd_special_off
#define __slcd_special_off()
#endif
/*
* Platform specific definition
*/
#if defined(CONFIG_SOC_JZ4740)
#if defined(CONFIG_JZ4740_PAVO)
#define GPIO_PWM 123 /* GP_D27 */
#define PWM_CHN 4 /* pwm channel */
#define PWM_FULL 101
/* 100 level: 0,1,...,100 */
#define __slcd_set_backlight_level(n)\
do { \
__gpio_as_output(32*3+27); \
__gpio_set_pin(32*3+27); \
} while (0)
#define __slcd_close_backlight() \
do { \
__gpio_as_output(GPIO_PWM); \
__gpio_clear_pin(GPIO_PWM); \
} while (0)
#else
#define __slcd_set_backlight_level(n)
#define __slcd_close_backlight()
#endif /* #if defined(CONFIG_MIPS_JZ4740_PAVO) */
#define __slcd_display_pin_init() \
do { \
__slcd_special_pin_init(); \
} while (0)
#define __slcd_display_on() \
do { \
__slcd_special_on(); \
__slcd_set_backlight_level(80); \
} while (0)
#define __slcd_display_off() \
do { \
__slcd_special_off(); \
__slcd_close_backlight(); \
} while (0)
#endif /* CONFIG_SOC_JZ4740 */
#endif /*__JZSLCD_H__*/

View File

@ -0,0 +1,129 @@
/*
* linux/drivers/video/jz_auo_a043fl01v2.h -- Ingenic LCD driver
*/
#ifndef __JZ_AUO_A043FL01V2_H__
#define __JZ_AUO_A043FL01V2_H__
#if defined(CONFIG_JZ4750_APUS) /* board pavo */
#define SPEN (32*3+29) /*LCD_CS*/
#define SPCK (32*3+26) /*LCD_SCL*/
#define SPDA (32*3+27) /*LCD_SDA*/
#define LCD_DISP_N (32*4+25) /*LCD_DISP_N use for lcd reset*/
#elif defined(CONFIG_JZ4750_FUWA) /* board fuwa */
#define SPEN (32*3+29) /*LCD_CS*/
#define SPCK (32*3+26) /*LCD_SCL*/
#define SPDA (32*3+27) /*LCD_SDA*/
#define LCD_DISP_N (32*5+2) /*LCD_DISP_N use for lcd reset*/
#elif defined(CONFIG_JZ4750D_CETUS) /* board cetus */
#define SPEN (32*5+13) /*LCD_CS*/
#define SPCK (32*5+10) /*LCD_SCL*/
#define SPDA (32*5+11) /*LCD_SDA*/
#define LCD_DISP_N (32*4+18) /*LCD_DISP_N use for lcd reset*/
#else
#error "driver/video/Jzlcd.h, please define SPI pins on your board."
#endif
#define __spi_write_reg(reg, val) \
do { \
unsigned char no; \
unsigned short value; \
unsigned char a=0; \
unsigned char b=0; \
__gpio_as_output(SPEN); /* use SPDA */ \
__gpio_as_output(SPCK); /* use SPCK */ \
__gpio_as_output(SPDA); /* use SPDA */ \
a=reg; \
b=val; \
__gpio_set_pin(SPEN); \
__gpio_clear_pin(SPCK); \
udelay(50); \
__gpio_clear_pin(SPDA); \
__gpio_clear_pin(SPEN); \
udelay(50); \
value=((a<<8)|(b&0xFF)); \
for(no=0;no<16;no++) \
{ \
if((value&0x8000)==0x8000){ \
__gpio_set_pin(SPDA);} \
else{ \
__gpio_clear_pin(SPDA); } \
udelay(50); \
__gpio_set_pin(SPCK); \
value=(value<<1); \
udelay(50); \
__gpio_clear_pin(SPCK); \
} \
__gpio_set_pin(SPEN); \
udelay(400); \
} while (0)
#define __spi_read_reg(reg,val) \
do{ \
unsigned char no; \
unsigned short value; \
__gpio_as_output(SPEN); /* use SPDA */ \
__gpio_as_output(SPCK); /* use SPCK */ \
__gpio_as_output(SPDA); /* use SPDA */ \
value = ((reg << 0) | (1 << 7)); \
val = 0; \
__gpio_as_output(SPDA); \
__gpio_set_pin(SPEN); \
__gpio_clear_pin(SPCK); \
udelay(50); \
__gpio_clear_pin(SPDA); \
__gpio_clear_pin(SPEN); \
udelay(50); \
for (no = 0; no < 16; no++ ) { \
udelay(50); \
if(no < 8) \
{ \
if (value & 0x80) /* send data */ \
__gpio_set_pin(SPDA); \
else \
__gpio_clear_pin(SPDA); \
udelay(50); \
__gpio_set_pin(SPCK); \
value = (value << 1); \
udelay(50); \
__gpio_clear_pin(SPCK); \
if(no == 7) \
__gpio_as_input(SPDA); \
} \
else \
{ \
udelay(100); \
__gpio_set_pin(SPCK); \
udelay(50); \
val = (val << 1); \
val |= __gpio_get_pin(SPDA); \
__gpio_clear_pin(SPCK); \
} \
} \
__gpio_as_output(SPDA); \
__gpio_set_pin(SPEN); \
udelay(400); \
} while(0)
#define __lcd_special_pin_init() \
do { \
__gpio_as_output(SPEN); /* use SPDA */ \
__gpio_as_output(SPCK); /* use SPCK */ \
__gpio_as_output(SPDA); /* use SPDA */ \
__gpio_as_output(LCD_DISP_N); \
__gpio_clear_pin(LCD_DISP_N); \
} while (0)
#define __lcd_special_on() \
do { \
udelay(50);\
__gpio_clear_pin(LCD_DISP_N); \
udelay(100); \
__gpio_set_pin(LCD_DISP_N); \
} while (0)
#define __lcd_special_off() \
do { \
__gpio_clear_pin(LCD_DISP_N); \
} while (0)
#endif /* __JZ_AUO_A043FL01V2_H__ */

View File

@ -0,0 +1,157 @@
#ifndef __JZ_TOPPOLY_TD043MGEB1_H__
#define __JZ_TOPPOLY_TD043MGEB1_H__
#include <asm/jzsoc.h>
#if defined(CONFIG_JZ4750_LCD_TOPPOLY_TD043MGEB1) || defined(CONFIG_JZ4750_ANDROID_LCD_TOPPOLY_TD043MGEB1)
#if defined(CONFIG_JZ4750_APUS) /* board FuWa */
#define SPEN (32*3+29) /*LCD_CS*/
#define SPCK (32*3+26) /*LCD_SCL*/
#define SPDA (32*3+27) /*LCD_SDA*/
#define LCD_RET (32*4+23) /*LCD_DISP_N use for lcd reset*/
#define LCD_STBY (32*4+25) /*LCD_STBY, use for lcd standby*/
#else
#error "driver/video/jz_toppoly_td043mgeb1.h, please define SPI pins on your board."
#endif
#define __spi_write_reg(reg, val) \
do { \
unsigned char no; \
unsigned short value; \
unsigned char a=0; \
unsigned char b=0; \
__gpio_as_output(SPEN); /* use SPDA */ \
__gpio_as_output(SPCK); /* use SPCK */ \
__gpio_as_output(SPDA); /* use SPDA */ \
a=reg; \
b=val; \
__gpio_set_pin(SPEN); \
__gpio_clear_pin(SPCK); \
udelay(500); \
__gpio_clear_pin(SPDA); \
__gpio_clear_pin(SPEN); \
udelay(500); \
value=((a<<10)|(b&0xFF)); \
for(no=0;no<16;no++) \
{ \
if((value&0x8000)==0x8000){ \
__gpio_set_pin(SPDA);} \
else{ \
__gpio_clear_pin(SPDA); } \
udelay(500); \
__gpio_set_pin(SPCK); \
value=(value<<1); \
udelay(500); \
__gpio_clear_pin(SPCK); \
} \
__gpio_set_pin(SPEN); \
udelay(4000); \
} while (0)
#define __spi_read_reg(reg,val) \
do{ \
unsigned char no; \
unsigned short value; \
__gpio_as_output(SPEN); /* use SPDA */ \
__gpio_as_output(SPCK); /* use SPCK */ \
__gpio_as_output(SPDA); /* use SPDA */ \
value = ((reg << 2) | (1 << 1)); \
val = 0; \
__gpio_as_output(SPDA); \
__gpio_set_pin(SPEN); \
__gpio_clear_pin(SPCK); \
udelay(50); \
__gpio_clear_pin(SPDA); \
__gpio_clear_pin(SPEN); \
udelay(50); \
for (no = 0; no < 16; no++ ) { \
udelay(50); \
if(no < 8) \
{ \
if (value & 0x80) /* send data */ \
__gpio_set_pin(SPDA); \
else \
__gpio_clear_pin(SPDA); \
udelay(50); \
__gpio_set_pin(SPCK); \
value = (value << 1); \
udelay(50); \
__gpio_clear_pin(SPCK); \
if(no == 7) \
__gpio_as_input(SPDA); \
} \
else \
{ \
udelay(100); \
__gpio_set_pin(SPCK); \
udelay(50); \
val = (val << 1); \
val |= __gpio_get_pin(SPDA); \
__gpio_clear_pin(SPCK); \
} \
} \
__gpio_as_output(SPDA); \
__gpio_set_pin(SPEN); \
udelay(400); \
} while(0)
#define __lcd_special_pin_init() \
do { \
__gpio_as_output(SPEN); /* use SPDA */ \
__gpio_as_output(SPCK); /* use SPCK */ \
__gpio_as_output(SPDA); /* use SPDA */ \
__gpio_as_output(LCD_STBY); \
__gpio_as_output(LCD_RET); \
udelay(500); \
__gpio_clear_pin(LCD_RET); \
udelay(1000); \
__gpio_set_pin(LCD_RET); \
udelay(1000); \
} while (0)
#define __lcd_special_on() \
do { \
__gpio_set_pin(LCD_STBY); \
udelay(1000); \
__spi_write_reg(0x02, 0x07); \
__spi_write_reg(0x03, 0x5F); \
__spi_write_reg(0x04, 0x17); \
__spi_write_reg(0x05, 0x20); \
__spi_write_reg(0x06, 0x08); \
__spi_write_reg(0x07, 0x20); \
__spi_write_reg(0x08, 0x20); \
__spi_write_reg(0x09, 0x20); \
__spi_write_reg(0x0A, 0x20); \
__spi_write_reg(0x0B, 0x20); \
__spi_write_reg(0x0C, 0x20); \
__spi_write_reg(0x0D, 0x22); \
__spi_write_reg(0x0E, 0x2F); \
__spi_write_reg(0x0F, 0x2f); \
__spi_write_reg(0x10, 0x2F); \
__spi_write_reg(0x11, 0x15); \
__spi_write_reg(0x12, 0xaa); \
__spi_write_reg(0x13, 0xFF); \
__spi_write_reg(0x14, 0x86); \
__spi_write_reg(0x15, 0x8e); \
__spi_write_reg(0x16, 0xd6); \
__spi_write_reg(0x17, 0xfe); \
__spi_write_reg(0x18, 0x28); \
__spi_write_reg(0x19, 0x52); \
__spi_write_reg(0x1A, 0x7c); \
__spi_write_reg(0x1B, 0xe9); \
__spi_write_reg(0x1C, 0x42); \
__spi_write_reg(0x1D, 0x88); \
__spi_write_reg(0x1E, 0xb8); \
__spi_write_reg(0x1F, 0xff); \
__spi_write_reg(0x20, 0xf0); \
__spi_write_reg(0x21, 0xf0); \
__spi_write_reg(0x22, 0x08); \
} while (0)
#define __lcd_special_off() \
do { \
__gpio_clear_pin(LCD_STBY); \
} while (0)
#endif /* LCD_TOPPOLY_TD043MGEB1 */
#endif /* __JZ_TOPPOLY_TD043MGEB1_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,791 @@
/*
* linux/drivers/video/jzlcd.h -- Ingenic On-Chip LCD frame buffer device
*
* Copyright (C) 2005-2007, Ingenic Semiconductor Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#ifndef __JZLCD_H__
#define __JZLCD_H__
#include <asm/io.h>
#define NR_PALETTE 256
struct lcd_desc{
unsigned int next_desc; /* LCDDAx */
unsigned int databuf; /* LCDSAx */
unsigned int frame_id; /* LCDFIDx */
unsigned int cmd; /* LCDCMDx */
};
#define MODE_MASK 0x0f
#define MODE_TFT_GEN 0x00
#define MODE_TFT_SHARP 0x01
#define MODE_TFT_CASIO 0x02
#define MODE_TFT_SAMSUNG 0x03
#define MODE_CCIR656_NONINT 0x04
#define MODE_CCIR656_INT 0x05
#define MODE_STN_COLOR_SINGLE 0x08
#define MODE_STN_MONO_SINGLE 0x09
#define MODE_STN_COLOR_DUAL 0x0a
#define MODE_STN_MONO_DUAL 0x0b
#define MODE_8BIT_SERIAL_TFT 0x0c
#define MODE_TFT_18BIT (1<<7)
#define STN_DAT_PIN1 (0x00 << 4)
#define STN_DAT_PIN2 (0x01 << 4)
#define STN_DAT_PIN4 (0x02 << 4)
#define STN_DAT_PIN8 (0x03 << 4)
#define STN_DAT_PINMASK STN_DAT_PIN8
#define STFT_PSHI (1 << 15)
#define STFT_CLSHI (1 << 14)
#define STFT_SPLHI (1 << 13)
#define STFT_REVHI (1 << 12)
#define SYNC_MASTER (0 << 16)
#define SYNC_SLAVE (1 << 16)
#define DE_P (0 << 9)
#define DE_N (1 << 9)
#define PCLK_P (0 << 10)
#define PCLK_N (1 << 10)
#define HSYNC_P (0 << 11)
#define HSYNC_N (1 << 11)
#define VSYNC_P (0 << 8)
#define VSYNC_N (1 << 8)
#define DATA_NORMAL (0 << 17)
#define DATA_INVERSE (1 << 17)
/* Jz LCDFB supported I/O controls. */
#define FBIOSETBACKLIGHT 0x4688
#define FBIODISPON 0x4689
#define FBIODISPOFF 0x468a
#define FBIORESET 0x468b
#define FBIOPRINT_REGS 0x468c
#define FBIOGETBUFADDRS 0x468d
#define FBIOROTATE 0x46a0 /* rotated fb */
struct jz_lcd_buffer_addrs_t {
int fb_num;
unsigned int lcd_desc_phys_addr;
unsigned int fb_phys_addr[CONFIG_JZLCD_FRAMEBUFFER_MAX];
};
/*
* LCD panel specific definition
*/
#if defined(CONFIG_JZLCD_TRULY_TFTG320240DTSW)
#if defined(CONFIG_JZ4730_PMP)
#define LCD_RESET_PIN 63
#endif
#define __lcd_special_on() \
do { \
__gpio_set_pin(LCD_RESET_PIN); \
__gpio_as_output(LCD_RESET_PIN); \
__gpio_clear_pin(LCD_RESET_PIN); \
udelay(100); \
__gpio_set_pin(LCD_RESET_PIN); \
} while (0)
#endif /* CONFIG_JZLCD_TRULY_TFTG320240DTSW */
#if defined(CONFIG_JZLCD_SAMSUNG_LTV350QVF04)
#if defined(CONFIG_JZ4730_FPRINT)
#define PortSDI 60
#define PortSCL 61
#define PortCS 62
#define PortRST 63
#define PortSht 64
#endif
#if defined(CONFIG_JZ4730_GPS)
#define PortSDI 74
#define PortSCL 72
#define PortCS 73
#define PortRST 60
#define PortSht 59
#endif
#ifndef PortSDI
#define PortSDI 0
#endif
#ifndef PortSCL
#define PortSCL 0
#endif
#ifndef PortCS
#define PortCS 0
#endif
#ifndef PortRST
#define PortRST 0
#endif
#ifndef PortSht
#define PortSht 0
#endif
#define __lcd_special_pin_init() \
do { \
__gpio_as_output(PortSDI); /* SDI */\
__gpio_as_output(PortSCL); /* SCL */ \
__gpio_as_output(PortCS); /* CS */ \
__gpio_as_output(PortRST); /* Reset */ \
__gpio_as_output(PortSht); /* Shut Down # */ \
__gpio_set_pin(PortCS); \
__gpio_set_pin(PortSCL); \
__gpio_set_pin(PortSDI); \
} while (0)
#define __spi_out(val) \
do { \
int __i__; \
unsigned int _t_ = (val); \
__gpio_clear_pin(PortCS); \
udelay(25); \
for (__i__ = 0; __i__ < 24; __i__++ ) { \
__gpio_clear_pin(PortSCL); \
if (_t_ & 0x800000) \
__gpio_set_pin(PortSDI); \
else \
__gpio_clear_pin(PortSDI); \
_t_ <<= 1; \
udelay(25); \
__gpio_set_pin(PortSCL); \
udelay(25); \
} \
__gpio_set_pin(PortCS); \
udelay(25); \
__gpio_set_pin(PortSDI); \
udelay(25); \
__gpio_set_pin(PortSCL); \
} while (0)
#define __spi_id_op_data(rs, rw, val) \
__spi_out((0x1d<<18)|((rs)<<17)|((rw)<<16)|(val))
#define __spi_write_reg(reg, val) \
do { \
__spi_id_op_data(0, 0, (reg)); \
__spi_id_op_data(1, 0, (val)); \
} while (0)
#define __lcd_special_on() \
do { \
__gpio_set_pin(PortSht); \
__gpio_clear_pin(PortRST); \
mdelay(10); \
__gpio_set_pin(PortRST); \
mdelay(1); \
__spi_write_reg(0x09, 0); \
mdelay(10); \
__spi_write_reg(0x09, 0x4000); \
__spi_write_reg(0x0a, 0x2000); \
mdelay(40); \
__spi_write_reg(0x09, 0x4055); \
mdelay(50); \
__spi_write_reg(0x01, 0x409d); \
__spi_write_reg(0x02, 0x0204); \
__spi_write_reg(0x03, 0x0100); \
__spi_write_reg(0x04, 0x3000); \
__spi_write_reg(0x05, 0x4003); \
__spi_write_reg(0x06, 0x000a); \
__spi_write_reg(0x07, 0x0021); \
__spi_write_reg(0x08, 0x0c00); \
__spi_write_reg(0x10, 0x0103); \
__spi_write_reg(0x11, 0x0301); \
__spi_write_reg(0x12, 0x1f0f); \
__spi_write_reg(0x13, 0x1f0f); \
__spi_write_reg(0x14, 0x0707); \
__spi_write_reg(0x15, 0x0307); \
__spi_write_reg(0x16, 0x0707); \
__spi_write_reg(0x17, 0x0000); \
__spi_write_reg(0x18, 0x0004); \
__spi_write_reg(0x19, 0x0000); \
mdelay(60); \
__spi_write_reg(0x09, 0x4a55); \
__spi_write_reg(0x05, 0x5003); \
} while (0)
#define __lcd_special_off() \
do { \
__spi_write_reg(0x09, 0x4055); \
__spi_write_reg(0x05, 0x4003); \
__spi_write_reg(0x0a, 0x0000); \
mdelay(10); \
__spi_write_reg(0x09, 0x4000); \
__gpio_clear_pin(PortSht); \
} while (0)
#endif /* CONFIG_JZLCD_SAMSUNG_LTV350QVF04 */
#if defined(CONFIG_JZLCD_AUO_A030FL01_V1)
#if defined(CONFIG_JZ4740_PAVO) /* board pavo */
#define SPEN (32*1+18) /*LCD_CS*/
#define SPCK (32*1+17) /*LCD_SCL*/
#define SPDA (32*2+12) /*LCD_SDA*/
#define LCD_RET (32*2+23) /*use for lcd reset*/
#elif defined(CONFIG_JZ4740_LYRA) /* board lyra */
#define SPEN (32*3+19) //LCD_CS
#define SPCK (32*3+18) //LCD_SCL
#define SPDA (32*3+20) //LCD_SDA
#define LCD_RET (32*3+31) //use for lcd reset
#else
#error "driver/video/Jzlcd.h, please define SPI pins on your board."
#endif
#define __spi_write_reg(reg, val) \
do { \
unsigned char no; \
unsigned short value; \
unsigned char a=0; \
unsigned char b=0; \
__gpio_as_output(SPEN); /* use SPDA */ \
__gpio_as_output(SPCK); /* use SPCK */ \
__gpio_as_output(SPDA); /* use SPDA */ \
a=reg; \
b=val; \
__gpio_set_pin(SPEN); \
__gpio_clear_pin(SPCK); \
udelay(50); \
__gpio_clear_pin(SPDA); \
__gpio_clear_pin(SPEN); \
udelay(50); \
value=((a<<8)|(b&0xFF)); \
for(no=0;no<16;no++) \
{ \
if((value&0x8000)==0x8000){ \
__gpio_set_pin(SPDA);} \
else{ \
__gpio_clear_pin(SPDA); } \
udelay(400); \
__gpio_set_pin(SPCK); \
value=(value<<1); \
udelay(50); \
__gpio_clear_pin(SPCK); \
} \
__gpio_set_pin(SPEN); \
udelay(400); \
} while (0)
#define __spi_read_reg(reg,val) \
do{ \
unsigned char no; \
unsigned short value; \
__gpio_as_output(SPEN); /* use SPDA */ \
__gpio_as_output(SPCK); /* use SPCK */ \
__gpio_as_output(SPDA); /* use SPDA */ \
value = ((reg << 0) | (1 << 7)); \
val = 0; \
__gpio_as_output(SPDA); \
__gpio_set_pin(SPEN); \
__gpio_set_pin(SPCK); \
udelay(1); \
__gpio_clear_pin(SPDA); \
__gpio_clear_pin(SPEN); \
udelay(1); \
for (no = 0; no < 16; no++ ) { \
__gpio_clear_pin(SPCK); \
udelay(1); \
if(no < 8) \
{ \
if (value & 0x80) /* send data */ \
__gpio_set_pin(SPDA); \
else \
__gpio_clear_pin(SPDA); \
value = (value << 1); \
udelay(1); \
__gpio_set_pin(SPCK); \
udelay(1); \
} \
else \
{ \
__gpio_as_input(SPDA); \
udelay(1); \
__gpio_set_pin(SPCK); \
udelay(1); \
val = (val << 1); \
val |= __gpio_get_pin(SPDA); \
udelay(1); \
} \
udelay(400); \
} \
__gpio_as_output(SPDA); \
__gpio_set_pin(SPEN); \
udelay(400); \
} while(0)
#define __lcd_special_pin_init() \
do { \
__gpio_as_output(SPEN); /* use SPDA */ \
__gpio_as_output(SPCK); /* use SPCK */ \
__gpio_as_output(SPDA); /* use SPDA */ \
__gpio_as_output(LCD_RET); \
udelay(50); \
__gpio_clear_pin(LCD_RET); \
udelay(100); \
__gpio_set_pin(LCD_RET); \
} while (0)
#define __lcd_special_on() \
do { \
udelay(50); \
__gpio_clear_pin(LCD_RET); \
udelay(100); \
__gpio_set_pin(LCD_RET); \
__spi_write_reg(0x0D, 0x44); \
__spi_write_reg(0x0D, 0x4D); \
__spi_write_reg(0x0B, 0x06); \
__spi_write_reg(0x40, 0xC0); \
__spi_write_reg(0x42, 0x43); \
__spi_write_reg(0x44, 0x28); \
__spi_write_reg(0x0D, 0x4F); \
} while (0)
#define __lcd_special_off() \
do { \
__spi_write_reg(0x04, 0x4C); \
} while (0)
#endif /* CONFIG_JZLCD_AUO_A030FL01_V1 */
//#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01)
#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01) || defined(CONFIG_JZLCD_INNOLUX_PT035TN01_SERIAL)
#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01) /* board pmp */
#define MODE 0xcd /* 24bit parellel RGB */
#endif
#if defined(CONFIG_JZLCD_INNOLUX_PT035TN01_SERIAL)
#define MODE 0xc9 /* 8bit serial RGB */
#endif
#if defined(CONFIG_JZ4730_PMP)
#define SPEN 60 //LCD_SPL
#define SPCK 61 //LCD_CLS
#define SPDA 62 //LCD_PS
#define LCD_RET 63 //LCD_REV //use for lcd reset
#elif defined(CONFIG_JZ4740_LEO) /* board leo */
#define SPEN (32*1+18) //LCD_SPL
#define SPCK (32*1+17) //LCD_CLS
#define SPDA (32*2+22) //LCD_PS
#define LCD_RET (32*2+23) //LCD_REV //use for lcd reset
#elif defined(CONFIG_JZ4740_PAVO) /* board pavo */
#define SPEN (32*1+18) //LCD_SPL
#define SPCK (32*1+17) //LCD_CLS
#define SPDA (32*2+12) //LCD_D12
#define LCD_RET (32*2+23) //LCD_REV, GPC23
#if 0 /*old driver*/
#define SPEN (32*1+18) //LCD_SPL
#define SPCK (32*1+17) //LCD_CLS
#define SPDA (32*2+12) //LCD_D12
#define LCD_RET (32*3+27) //PWM4 //use for lcd reset
#endif
#else
#error "driver/video/Jzlcd.h, please define SPI pins on your board."
#endif
#define __spi_write_reg1(reg, val) \
do { \
unsigned char no;\
unsigned short value;\
unsigned char a=0;\
unsigned char b=0;\
a=reg;\
b=val;\
__gpio_set_pin(SPEN);\
__gpio_set_pin(SPCK);\
__gpio_clear_pin(SPDA);\
__gpio_clear_pin(SPEN);\
udelay(25);\
value=((a<<8)|(b&0xFF));\
for(no=0;no<16;no++)\
{\
__gpio_clear_pin(SPCK);\
if((value&0x8000)==0x8000)\
__gpio_set_pin(SPDA);\
else\
__gpio_clear_pin(SPDA);\
udelay(25);\
__gpio_set_pin(SPCK);\
value=(value<<1); \
udelay(25);\
}\
__gpio_set_pin(SPEN);\
udelay(100);\
} while (0)
#define __spi_write_reg(reg, val) \
do {\
__spi_write_reg1((reg<<2|2), val); \
udelay(100); \
}while(0)
#define __lcd_special_pin_init() \
do { \
__gpio_as_output(SPEN); /* use SPDA */\
__gpio_as_output(SPCK); /* use SPCK */\
__gpio_as_output(SPDA); /* use SPDA */\
__gpio_as_output(LCD_RET);\
udelay(50);\
__gpio_clear_pin(LCD_RET);\
mdelay(150);\
__gpio_set_pin(LCD_RET);\
} while (0)
#define __lcd_special_on() \
do { \
udelay(50);\
__gpio_clear_pin(LCD_RET);\
mdelay(150);\
__gpio_set_pin(LCD_RET);\
mdelay(10);\
__spi_write_reg(0x00, 0x03); \
__spi_write_reg(0x01, 0x40); \
__spi_write_reg(0x02, 0x11); \
__spi_write_reg(0x03, MODE); /* mode */ \
__spi_write_reg(0x04, 0x32); \
__spi_write_reg(0x05, 0x0e); \
__spi_write_reg(0x07, 0x03); \
__spi_write_reg(0x08, 0x08); \
__spi_write_reg(0x09, 0x32); \
__spi_write_reg(0x0A, 0x88); \
__spi_write_reg(0x0B, 0xc6); \
__spi_write_reg(0x0C, 0x20); \
__spi_write_reg(0x0D, 0x20); \
} while (0) //reg 0x0a is control the display direction:DB0->horizontal level DB1->vertical level
/* __spi_write_reg(0x02, 0x03); \
__spi_write_reg(0x06, 0x40); \
__spi_write_reg(0x0a, 0x11); \
__spi_write_reg(0x0e, 0xcd); \
__spi_write_reg(0x12, 0x32); \
__spi_write_reg(0x16, 0x0e); \
__spi_write_reg(0x1e, 0x03); \
__spi_write_reg(0x22, 0x08); \
__spi_write_reg(0x26, 0x40); \
__spi_write_reg(0x2a, 0x88); \
__spi_write_reg(0x2e, 0x88); \
__spi_write_reg(0x32, 0x20); \
__spi_write_reg(0x36, 0x20); \
*/
// } while (0) //reg 0x0a is control the display direction:DB0->horizontal level DB1->vertical level
#define __lcd_special_off() \
do { \
__spi_write_reg(0x00, 0x03); \
} while (0)
#endif /* CONFIG_JZLCD_FOXCONN_PT035TN01 or CONFIG_JZLCD_INNOLUX_PT035TN01_SERIAL */
#ifndef __lcd_special_pin_init
#define __lcd_special_pin_init()
#endif
#ifndef __lcd_special_on
#define __lcd_special_on()
#endif
#ifndef __lcd_special_off
#define __lcd_special_off()
#endif
/*
* Platform specific definition
*/
#if defined(CONFIG_JZ4730_GPS)
#define __lcd_set_backlight_level(n) \
do { \
; \
} while (0)
#define __lcd_display_pin_init() \
do { \
__lcd_special_pin_init(); \
__gpio_as_output(94); /* PWM0 pin */ \
__gpio_as_output(95); /* PWM1 pin */ \
} while (0)
#define __lcd_display_on() \
do { \
__lcd_special_on(); \
__gpio_set_pin(94); /* PWM0 pin */ \
__gpio_set_pin(95); /* PWM1 pin */ \
__lcd_set_backlight_level(8); \
} while (0)
#define __lcd_display_off() \
do { \
__lcd_special_off(); \
} while (0)
#endif /* CONFIG_JZ4730_GPS */
#if defined(CONFIG_JZ4730_FPRINT)
#define __lcd_set_backlight_level(n) \
do { \
REG_PWM_DUT(0) = n; \
REG_PWM_PER(0) = 7; \
REG_PWM_CTR(0) = 0x81; \
} while (0)
#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01)
#define __lcd_display_pin_init() \
do { \
__lcd_special_pin_init();\
__gpio_as_pwm();\
__lcd_set_backlight_level(8);\
} while (0)
#define __lcd_display_on() \
do { \
__lcd_set_backlight_level(8); \
__lcd_special_on();\
} while (0)
#define __lcd_display_off() \
do { \
__lcd_set_backlight_level(0); \
__lcd_special_off();\
} while (0)
#else
#define __lcd_display_pin_init() \
do { \
__gpio_as_output(GPIO_DISP_OFF_N); \
__gpio_as_pwm(); \
__lcd_set_backlight_level(8); \
} while (0)
#define __lcd_display_on() \
do { \
__lcd_set_backlight_level(8); \
__gpio_set_pin(GPIO_DISP_OFF_N); \
} while (0)
#define __lcd_display_off() \
do { \
__lcd_set_backlight_level(0); \
__gpio_clear_pin(GPIO_DISP_OFF_N); \
} while (0)
#endif
#endif /* CONFIG_JZ4730_FPRINT */
#if defined(CONFIG_JZ4730_LIBRA)
#define __lcd_set_backlight_level(n) \
do { \
} while (0)
#define __lcd_display_pin_init() \
do { \
__lcd_special_pin_init(); \
__gpio_clear_pin(100); \
__gpio_as_output(100); \
__gpio_as_output(94); \
__gpio_as_output(95); \
__lcd_set_backlight_level(8); \
} while (0)
#define __lcd_display_on() \
do { \
__lcd_special_on(); \
__gpio_set_pin(100); \
__gpio_set_pin(94); \
__gpio_set_pin(95); \
} while (0)
#define __lcd_display_off() \
do { \
__lcd_special_off(); \
__gpio_clear_pin(100); \
__gpio_clear_pin(94); \
__gpio_clear_pin(95); \
} while (0)
#endif /* CONFIG_JZ4730_LIBRA */
#if defined(CONFIG_JZ4730_PMP)
#define __lcd_set_backlight_level(n) \
do { \
REG_PWM_DUT(0) = n; \
REG_PWM_PER(0) = 7; \
REG_PWM_CTR(0) = 0x81; \
} while (0)
#define __lcd_display_pin_init() \
do { \
__gpio_as_output(GPIO_DISP_OFF_N); \
__gpio_as_pwm(); \
__lcd_set_backlight_level(10); \
__lcd_special_pin_init(); \
} while (0)
#define __lcd_display_on() \
do { \
__lcd_special_on(); \
__lcd_set_backlight_level(8); \
__gpio_set_pin(GPIO_DISP_OFF_N); \
} while (0)
#define __lcd_display_off() \
do { \
__lcd_special_off(); \
__lcd_set_backlight_level(0); \
__gpio_clear_pin(GPIO_DISP_OFF_N); \
} while (0)
#endif /* CONFIG_JZ4730_PMP */
/*#if defined(CONFIG_JZ4740_LEO) || defined(CONFIG_JZ4740_PAVO)*/
#if defined(CONFIG_SOC_JZ4740)
#if defined(CONFIG_JZ4740_PAVO) || defined(CONFIG_JZ4740_LYRA)
#define GPIO_PWM 123 /* GP_D27 */
#define PWM_CHN 4 /* pwm channel */
#define PWM_FULL 101
/* 100 level: 0,1,...,100 */
#define __lcd_set_backlight_level(n)\
do { \
__gpio_as_output(32*3+27); \
__gpio_set_pin(32*3+27); \
} while (0)
#define __lcd_close_backlight() \
do { \
__gpio_as_output(GPIO_PWM); \
__gpio_clear_pin(GPIO_PWM); \
} while (0)
#elif defined(CONFIG_JZ4720_VIRGO)
#define GPIO_PWM 119 /* GP_D23 */
#define PWM_CHN 0 /* pwm channel */
#define PWM_FULL 101
/* 100 level: 0,1,...,100 */
/*#define __lcd_set_backlight_level(n) \
do { \
__gpio_as_pwm(0); \
__tcu_disable_pwm_output(PWM_CHN); \
__tcu_stop_counter(PWM_CHN); \
__tcu_init_pwm_output_high(PWM_CHN); \
__tcu_set_pwm_output_shutdown_abrupt(PWM_CHN); \
__tcu_select_clk_div1(PWM_CHN); \
__tcu_mask_full_match_irq(PWM_CHN); \
__tcu_mask_half_match_irq(PWM_CHN); \
__tcu_set_count(PWM_CHN,0); \
__tcu_set_full_data(PWM_CHN,__cpm_get_extalclk()/1000); \
__tcu_set_half_data(PWM_CHN,__cpm_get_extalclk()/1000*n/100); \
__tcu_enable_pwm_output(PWM_CHN); \
__tcu_select_extalclk(PWM_CHN); \
__tcu_start_counter(PWM_CHN); \
} while (0)
*/
#define __lcd_set_backlight_level(n) \
do { \
__gpio_as_output(GPIO_PWM); \
__gpio_set_pin(GPIO_PWM); \
} while (0)
#define __lcd_close_backlight() \
do { \
__gpio_as_output(GPIO_PWM); \
__gpio_clear_pin(GPIO_PWM); \
} while (0)
#else
#define __lcd_set_backlight_level(n)
#define __lcd_close_backlight()
#endif /* #if defined(CONFIG_MIPS_JZ4740_PAVO) */
#define __lcd_display_pin_init() \
do { \
__gpio_as_output(GPIO_DISP_OFF_N); \
__cpm_start_tcu(); \
__lcd_special_pin_init(); \
} while (0)
/* __lcd_set_backlight_level(100); \*/
#define __lcd_display_on() \
do { \
__gpio_set_pin(GPIO_DISP_OFF_N); \
__lcd_special_on(); \
__lcd_set_backlight_level(80); \
} while (0)
#define __lcd_display_off() \
do { \
__lcd_special_off(); \
__lcd_close_backlight(); \
__gpio_clear_pin(GPIO_DISP_OFF_N); \
} while (0)
#endif /* CONFIG_MIPS_JZ4740_LEO */
#if defined(CONFIG_JZLCD_MSTN_240x128)
#if 0 /* The final version does not use software emulation of VCOM. */
#define GPIO_VSYNC 59
#define GPIO_VCOM 90
#define REG_VCOM REG_GPIO_GPDR((GPIO_VCOM>>5))
#define VCOM_BIT (1 << (GPIO_VCOM & 0x1f))
static unsigned int vcom_static;
static void vsync_irq(int irq, void *dev_id, struct pt_regs *reg)
{
vcom_static = REG_VCOM;
vcom_static ^= VCOM_BIT;
REG_VCOM = vcom_static;
}
#define __lcd_display_pin_init() \
__gpio_as_irq_rise_edge(GPIO_VSYNC); \
__gpio_as_output(GPIO_VCOM); \
{ \
static int inited = 0; \
if (!inited) { \
inited = 1; \
if (request_irq(IRQ_GPIO_0 + GPIO_VSYNC, vsync_irq, SA_INTERRUPT, \
"vsync", 0)) { \
err = -EBUSY; \
goto failed; \
}}}
#endif
/* We uses AC BIAs pin to generate VCOM signal, so above code should be removed.
*/
#endif
/*****************************************************************************
* LCD display pin dummy macros
*****************************************************************************/
#ifndef __lcd_display_pin_init
#define __lcd_display_pin_init()
#endif
#ifndef __lcd_display_on
#define __lcd_display_on()
#endif
#ifndef __lcd_display_off
#define __lcd_display_off()
#endif
#ifndef __lcd_set_backlight_level
#define __lcd_set_backlight_level(n)
#endif
#endif /* __JZLCD_H__ */

View File

@ -0,0 +1,31 @@
/*
* Copyright (C) 2009, Lars-Peter Clausen <lars@metafoo.de>
* JZ4720/JZ4740 SoC NAND controller driver
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifndef __JZ_NAND_H__
#define __JZ_NAND_H__
#include <linux/mtd/partitions.h>
#include <linux/mtd/nand.h>
struct jz_nand_platform_data {
int num_partitions;
struct mtd_partition *partitions;
struct nand_ecclayout *ecc_layout;
unsigned int busy_gpio;
};
#endif

View File

@ -0,0 +1,725 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include "../jz4740/jz4740-pcm.h"
#include "jzcodec.h"
#define AUDIO_NAME "jzcodec"
#define JZCODEC_VERSION "1.0"
/*
* Debug
*/
#define JZCODEC_DEBUG 0
#ifdef JZCODEC_DEBUG
#define dbg(format, arg...) \
printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
#else
#define dbg(format, arg...) do {} while (0)
#endif
#define err(format, arg...) \
printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
#define info(format, arg...) \
printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
#define warn(format, arg...) \
printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
struct snd_soc_codec_device soc_codec_dev_jzcodec;
/* codec private data */
struct jzcodec_priv {
unsigned int sysclk;
};
/*
* jzcodec register cache
*/
static u32 jzcodec_reg[JZCODEC_CACHEREGNUM / 2];
/*
* codec register is 16 bits width in ALSA, so we define array to store 16 bits configure paras
*/
static u16 jzcodec_reg_LH[JZCODEC_CACHEREGNUM];
/*
* read jzcodec register cache
*/
static inline unsigned int jzcodec_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
{
u16 *cache = codec->reg_cache;
if (reg >= JZCODEC_CACHEREGNUM)
return -1;
return cache[reg];
}
/*
* write jzcodec register cache
*/
static inline void jzcodec_write_reg_cache(struct snd_soc_codec *codec,
unsigned int reg, u16 value)
{
u16 *cache = codec->reg_cache;
u32 reg_val;
if (reg >= JZCODEC_CACHEREGNUM) {
return;
}
cache[reg] = value;
/* update internal codec register value */
switch (reg) {
case 0:
case 1:
reg_val = cache[0] & 0xffff;
reg_val = reg_val | (cache[1] << 16);
jzcodec_reg[0] = reg_val;
break;
case 2:
case 3:
reg_val = cache[2] & 0xffff;
reg_val = reg_val | (cache[3] << 16);
jzcodec_reg[1] = reg_val;
break;
}
}
/*
* write to the jzcodec register space
*/
static int jzcodec_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
jzcodec_write_reg_cache(codec, reg, value);
if(codec->hw_write)
codec->hw_write(&value, NULL, reg);
return 0;
}
static int jzcodec_reset(struct snd_soc_codec *codec)
{
u16 val;
val = jzcodec_read_reg_cache(codec, ICODEC_1_LOW);
val = val | 0x1;
jzcodec_write(codec, ICODEC_1_LOW, val);
mdelay(1);
val = jzcodec_read_reg_cache(codec, ICODEC_1_LOW);
val = val & ~0x1;
jzcodec_write(codec, ICODEC_1_LOW, val);
mdelay(1);
return 0;
}
static const struct snd_kcontrol_new jzcodec_snd_controls[] = {
//SOC_DOUBLE_R("Master Playback Volume", 1, 1, 0, 3, 0),
SOC_DOUBLE_R("Master Playback Volume", ICODEC_2_LOW, ICODEC_2_LOW, 0, 3, 0),
//SOC_DOUBLE_R("MICBG", ICODEC_2_LOW, ICODEC_2_LOW, 4, 3, 0),
//SOC_DOUBLE_R("Line", 2, 2, 0, 31, 0),
SOC_DOUBLE_R("Line", ICODEC_2_HIGH, ICODEC_2_HIGH, 0, 31, 0),
};
/* add non dapm controls */
static int jzcodec_add_controls(struct snd_soc_codec *codec)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(jzcodec_snd_controls); i++) {
if ((err = snd_ctl_add(codec->card,
snd_soc_cnew(&jzcodec_snd_controls[i], codec, NULL))) < 0)
return err;
}
return 0;
}
static const struct snd_soc_dapm_widget jzcodec_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("LOUT"),
SND_SOC_DAPM_OUTPUT("LHPOUT"),
SND_SOC_DAPM_OUTPUT("ROUT"),
SND_SOC_DAPM_OUTPUT("RHPOUT"),
SND_SOC_DAPM_INPUT("MICIN"),
SND_SOC_DAPM_INPUT("RLINEIN"),
SND_SOC_DAPM_INPUT("LLINEIN"),
};
static const char *intercon[][3] = {
/* output mixer */
{"Output Mixer", "Line Bypass Switch", "Line Input"},
{"Output Mixer", "HiFi Playback Switch", "DAC"},
{"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
/* outputs */
{"RHPOUT", NULL, "Output Mixer"},
{"ROUT", NULL, "Output Mixer"},
{"LHPOUT", NULL, "Output Mixer"},
{"LOUT", NULL, "Output Mixer"},
/* input mux */
{"Input Mux", "Line In", "Line Input"},
{"Input Mux", "Mic", "Mic Bias"},
{"ADC", NULL, "Input Mux"},
/* inputs */
{"Line Input", NULL, "LLINEIN"},
{"Line Input", NULL, "RLINEIN"},
{"Mic Bias", NULL, "MICIN"},
/* terminator */
{NULL, NULL, NULL},
};
static int jzcodec_add_widgets(struct snd_soc_codec *codec)
{
int i,cnt;
cnt = ARRAY_SIZE(jzcodec_dapm_widgets);
for(i = 0; i < ARRAY_SIZE(jzcodec_dapm_widgets); i++) {
snd_soc_dapm_new_control(codec, &jzcodec_dapm_widgets[i]);
}
#if 1
/* set up audio path interconnects */
for(i = 0; intercon[i][0] != NULL; i++) {
snd_soc_dapm_connect_input(codec, intercon[i][0],
intercon[i][1], intercon[i][2]);
}
#endif
snd_soc_dapm_new_widgets(codec);
return 0;
}
static int jzcodec_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
u16 reg_val = jzcodec_read_reg_cache(codec, ICODEC_2_LOW);
#if 0
/* bit size. codec side */
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
break;
}
#endif
/* sample rate */
reg_val = reg_val & ~(0xf << 8);
switch (params_rate(params)) {
case 8000:
reg_val |= (0x0 << 8);
break;
case 11025:
reg_val |= (0x1 << 8);
break;
case 12000:
reg_val |= (0x2 << 8);
break;
case 16000:
reg_val |= (0x3 << 8);
break;
case 22050:
reg_val |= (0x4 << 8);
break;
case 24000:
reg_val |= (0x5 << 8);
break;
case 32000:
reg_val |= (0x6 << 8);
break;
case 44100:
reg_val |= (0x7 << 8);
break;
case 48000:
reg_val |= (0x8 << 8);
break;
default:
printk(" invalid rate :0x%08x\n",params_rate(params));
}
jzcodec_write(codec, ICODEC_2_LOW, reg_val);
return 0;
}
static int jzcodec_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
int ret = 0;
u16 val;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
//case SNDRV_PCM_TRIGGER_RESUME:
//case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
val = 0x7302;
jzcodec_write(codec, ICODEC_1_LOW, val);
val = 0x0003;
jzcodec_write(codec, ICODEC_1_HIGH, val);
mdelay(2);
val = 0x6000;
jzcodec_write(codec, ICODEC_1_LOW, val);
val = 0x0300;
jzcodec_write(codec, ICODEC_1_HIGH, val);
mdelay(2);
val = 0x2000;
jzcodec_write(codec, ICODEC_1_LOW, val);
val = 0x0300;
jzcodec_write(codec, ICODEC_1_HIGH, val);
} else {
val = 0x4300;
jzcodec_write(codec, ICODEC_1_LOW, val);
val = 0x1402;
jzcodec_write(codec, ICODEC_1_HIGH, val);
}
break;
case SNDRV_PCM_TRIGGER_STOP:
//case SNDRV_PCM_TRIGGER_SUSPEND:
//case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
val = 0x3300;
jzcodec_write(codec, ICODEC_1_LOW, val);
val = 0x0003;
jzcodec_write(codec, ICODEC_1_HIGH, val);
} else {
val = 0x3300;
jzcodec_write(codec, ICODEC_1_LOW, val);
val = 0x0003;
jzcodec_write(codec, ICODEC_1_HIGH, val);
}
break;
default:
ret = -EINVAL;
}
return ret;
}
static int jzcodec_pcm_prepare(struct snd_pcm_substream *substream)
{
/*struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec; */
return 0;
}
static void jzcodec_shutdown(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
/* deactivate */
if (!codec->active) {
udelay(50);
}
}
static int jzcodec_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
u16 reg_val = jzcodec_read_reg_cache(codec, ICODEC_1_LOW);
if (mute != 0)
mute = 1;
if (mute)
reg_val = reg_val | (0x1 << 14);
else
reg_val = reg_val & ~(0x1 << 14);
jzcodec_write(codec, ICODEC_1_LOW, reg_val);
return 0;
}
static int jzcodec_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct jzcodec_priv *jzcodec = codec->private_data;
jzcodec->sysclk = freq;
return 0;
}
/*
* Set's ADC and Voice DAC format. called by pavo_hw_params() in pavo.c
*/
static int jzcodec_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
/* set master/slave audio interface. codec side */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
/* set master mode for codec */
break;
case SND_SOC_DAIFMT_CBS_CFS:
/* set slave mode for codec */
break;
default:
return -EINVAL;
}
/* interface format . set some parameter for codec side */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
/* set I2S mode for codec */
break;
case SND_SOC_DAIFMT_RIGHT_J:
/* set right J mode */
break;
case SND_SOC_DAIFMT_LEFT_J:
/* set left J mode */
break;
case SND_SOC_DAIFMT_DSP_A:
/* set dsp A mode */
break;
case SND_SOC_DAIFMT_DSP_B:
/* set dsp B mode */
break;
default:
return -EINVAL;
}
/* clock inversion. codec side */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_IB_IF:
break;
case SND_SOC_DAIFMT_IB_NF:
break;
case SND_SOC_DAIFMT_NB_IF:
break;
default:
return -EINVAL;
}
/* jzcodec_write(codec, 0, val); */
return 0;
}
#if 1
static int jzcodec_dapm_event(struct snd_soc_codec *codec, int event)
{
/* u16 reg_val; */
switch (event) {
case SNDRV_CTL_POWER_D0: /* full On */
/* vref/mid, osc on, dac unmute */
/* u16 reg_val = jzcodec_read_reg_cache(codec, ICODEC_1_LOW); */
/* jzcodec_write(codec, 0, val); */
break;
case SNDRV_CTL_POWER_D1: /* partial On */
case SNDRV_CTL_POWER_D2: /* partial On */
break;
case SNDRV_CTL_POWER_D3hot: /* Off, with power */
/* everything off except vref/vmid, */
/*reg_val = 0x0800;
jzcodec_write_reg_cache(codec, ICODEC_1_LOW, reg_val);
reg_val = 0x0017;
jzcodec_write_reg_cache(codec, ICODEC_1_HIGH, reg_val);
REG_ICDC_CDCCR1 = jzcodec_reg[0];
mdelay(2);
reg_val = 0x2102;
jzcodec_write_reg_cache(codec, ICODEC_1_LOW, reg_val);
reg_val = 0x001f;
jzcodec_write_reg_cache(codec, ICODEC_1_HIGH, reg_val);
REG_ICDC_CDCCR1 = jzcodec_reg[0];
mdelay(2);
reg_val = 0x3302;
jzcodec_write_reg_cache(codec, ICODEC_1_LOW, reg_val);
reg_val = 0x0003;
jzcodec_write_reg_cache(codec, ICODEC_1_HIGH, reg_val);
REG_ICDC_CDCCR1 = jzcodec_reg[0];*/
break;
case SNDRV_CTL_POWER_D3cold: /* Off, without power */
/* everything off, dac mute, inactive */
/*reg_val = 0x2302;
jzcodec_write(codec, ICODEC_1_LOW, reg_val);
reg_val = 0x001b;
jzcodec_write(codec, ICODEC_1_HIGH, reg_val);
mdelay(1);
reg_val = 0x2102;
jzcodec_write(codec, ICODEC_1_LOW, reg_val);
reg_val = 0x001b;
jzcodec_write(codec, ICODEC_1_HIGH, reg_val);*/
break;
}
//codec->dapm_state = event;
return 0;
}
#endif
#define JZCODEC_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
SNDRV_PCM_RATE_48000)
#define JZCODEC_FORMATS (SNDRV_PCM_FORMAT_S8 | SNDRV_PCM_FMTBIT_S16_LE)
struct snd_soc_dai jzcodec_dai = {
.name = "JZCODEC",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = JZCODEC_RATES,
.formats = JZCODEC_FORMATS,},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = JZCODEC_RATES,
.formats = JZCODEC_FORMATS,},
.ops = {
.trigger = jzcodec_pcm_trigger,
.prepare = jzcodec_pcm_prepare,
.hw_params = jzcodec_hw_params,
.shutdown = jzcodec_shutdown,
},
.dai_ops = {
.digital_mute = jzcodec_mute,
.set_sysclk = jzcodec_set_dai_sysclk,
.set_fmt = jzcodec_set_dai_fmt,
}
};
EXPORT_SYMBOL_GPL(jzcodec_dai);
#ifdef CONFIG_PM
static u16 jzcodec_reg_pm[JZCODEC_CACHEREGNUM];
static int jzcodec_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
jzcodec_reg_pm[ICODEC_1_LOW] = jzcodec_read_reg_cache(codec, ICODEC_1_LOW);
jzcodec_reg_pm[ICODEC_1_HIGH] = jzcodec_read_reg_cache(codec, ICODEC_1_HIGH);
jzcodec_reg_pm[ICODEC_2_LOW] = jzcodec_read_reg_cache(codec, ICODEC_2_LOW);
jzcodec_reg_pm[ICODEC_2_HIGH] = jzcodec_read_reg_cache(codec, ICODEC_2_HIGH);
jzcodec_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
return 0;
}
static int jzcodec_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
u16 reg_val;
jzcodec_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
reg_val = jzcodec_reg_pm[ICODEC_1_LOW];
jzcodec_write(codec, ICODEC_1_LOW, reg_val);
reg_val = jzcodec_reg_pm[ICODEC_1_HIGH];
jzcodec_write(codec, ICODEC_1_HIGH, reg_val);
reg_val = jzcodec_reg_pm[ICODEC_2_LOW];
jzcodec_write(codec, ICODEC_2_LOW, reg_val);
reg_val = jzcodec_reg_pm[ICODEC_2_HIGH];
jzcodec_write(codec, ICODEC_2_HIGH, reg_val);
jzcodec_dapm_event(codec, codec->suspend_dapm_state);
return 0;
}
#else
#define jzcodec_suspend NULL
#define jzcodec_resume NULL
#endif
/*
* initialise the JZCODEC driver
* register the mixer and dsp interfaces with the kernel
*/
static int jzcodec_init(struct snd_soc_device *socdev)
{
struct snd_soc_codec *codec = socdev->codec;
int reg, ret = 0;
u16 reg_val;
for (reg = 0; reg < JZCODEC_CACHEREGNUM / 2; reg++) {
switch (reg) {
case 0:
jzcodec_reg[reg] = REG_ICDC_CDCCR1;
jzcodec_reg_LH[ICODEC_1_LOW] = jzcodec_reg[reg] & 0xffff;
jzcodec_reg_LH[ICODEC_1_HIGH] = (jzcodec_reg[reg] & 0xffff0000) >> 16;
break;
case 1:
jzcodec_reg[reg] = REG_ICDC_CDCCR2;
jzcodec_reg_LH[ICODEC_2_LOW] = jzcodec_reg[reg] & 0xffff;
jzcodec_reg_LH[ICODEC_2_HIGH] = (jzcodec_reg[reg] & 0xffff0000) >> 16;
break;
}
}
codec->name = "JZCODEC";
codec->owner = THIS_MODULE;
codec->read = jzcodec_read_reg_cache;
codec->write = jzcodec_write;
//codec->dapm_event = jzcodec_dapm_event;
codec->dai = &jzcodec_dai;
codec->num_dai = 1;
codec->reg_cache_size = sizeof(jzcodec_reg_LH);
codec->reg_cache = kmemdup(jzcodec_reg_LH, sizeof(jzcodec_reg_LH), GFP_KERNEL);
if (codec->reg_cache == NULL)
return -ENOMEM;
jzcodec_reset(codec);
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
printk(KERN_ERR "jzcodec: failed to create pcms\n");
goto pcm_err;
}
/* power on device */
jzcodec_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
/* clear suspend bit of jz4740 internal codec */
reg_val = jzcodec_read_reg_cache(codec, ICODEC_1_LOW);
reg_val = reg_val & ~(0x2);
jzcodec_write(codec, ICODEC_1_LOW, reg_val);
/* set vol bits */
reg_val = jzcodec_read_reg_cache(codec, ICODEC_2_LOW);
reg_val = reg_val | 0x3;
jzcodec_write(codec, ICODEC_2_LOW, reg_val);
/* set line in capture gain bits */
reg_val = jzcodec_read_reg_cache(codec, ICODEC_2_HIGH);
reg_val = reg_val | 0x1f;
jzcodec_write(codec, ICODEC_2_HIGH, reg_val);
/* set mic boost gain bits */
reg_val = jzcodec_read_reg_cache(codec, ICODEC_2_LOW);
reg_val = reg_val | (0x3 << 4);
jzcodec_write(codec, ICODEC_2_LOW, reg_val);
mdelay(5);
reg_val = 0x3300;
jzcodec_write(codec, ICODEC_1_LOW, reg_val);
reg_val = 0x0003;
jzcodec_write(codec, ICODEC_1_HIGH, reg_val);
jzcodec_add_controls(codec);
jzcodec_add_widgets(codec);
ret = snd_soc_register_card(socdev);
if (ret < 0) {
printk(KERN_ERR "jzcodec: failed to register card\n");
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
kfree(codec->reg_cache);
return ret;
}
static struct snd_soc_device *jzcodec_socdev;
static int write_codec_reg(u16 * add, char * name, int reg)
{
switch (reg) {
case 0:
case 1:
REG_ICDC_CDCCR1 = jzcodec_reg[0];
break;
case 2:
case 3:
REG_ICDC_CDCCR2 = jzcodec_reg[1];
break;
}
return 0;
}
static int jzcodec_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
struct jzcodec_priv *jzcodec;
int ret = 0;
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
jzcodec = kzalloc(sizeof(struct jzcodec_priv), GFP_KERNEL);
if (jzcodec == NULL) {
kfree(codec);
return -ENOMEM;
}
codec->private_data = jzcodec;
socdev->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
jzcodec_socdev = socdev;
/* Add other interfaces here ,no I2C connection */
codec->hw_write = (hw_write_t)write_codec_reg;
ret = jzcodec_init(jzcodec_socdev);
if (ret < 0) {
codec = jzcodec_socdev->codec;
err("failed to initialise jzcodec\n");
kfree(codec);
}
return ret;
}
/* power down chip */
static int jzcodec_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
if (codec->control_data)
jzcodec_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
kfree(codec->private_data);
kfree(codec);
return 0;
}
struct snd_soc_codec_device soc_codec_dev_jzcodec = {
.probe = jzcodec_probe,
.remove = jzcodec_remove,
.suspend = jzcodec_suspend,
.resume = jzcodec_resume,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_jzcodec);
MODULE_DESCRIPTION("ASoC JZCODEC driver");
MODULE_AUTHOR("Richard");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,22 @@
/*
* 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 _ICODEC_H
#define _ICODEC_H
/* jzcodec register space */
#define ICODEC_1_LOW 0x00 /* bit0 -- bit15 in CDCCR1 */
#define ICODEC_1_HIGH 0x01 /* bit16 -- bit31 in CDCCR1 */
#define ICODEC_2_LOW 0x02 /* bit0 -- bit16 in CDCCR2 */
#define ICODEC_2_HIGH 0x03 /* bit16 -- bit31 in CDCCR2 */
#define JZCODEC_CACHEREGNUM 4
#define JZCODEC_SYSCLK 0
extern struct snd_soc_dai jzcodec_dai;
extern struct snd_soc_codec_device soc_codec_dev_jzcodec;
#endif

View File

@ -0,0 +1,981 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include "../jz4750/jz4750-pcm.h"
#include "jzdlv.h"
#define AUDIO_NAME "jzdlv"
#define JZDLV_VERSION "1.0"
/*
* Debug
*/
#define JZDLV_DEBUG 0
#ifdef JZDLV_DEBUG
#define dbg(format, arg...) \
printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
#else
#define dbg(format, arg...) do {} while (0)
#endif
#define err(format, arg...) \
printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
#define info(format, arg...) \
printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
#define warn(format, arg...) \
printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
struct snd_soc_codec_device soc_codec_dev_jzdlv;
/* codec private data */
struct jzdlv_priv {
unsigned int sysclk;
};
/*
* jzdlv register cache
*/
static u16 jzdlv_reg[JZDLV_CACHEREGNUM];
int read_codec_file(int addr)
{
while (__icdc_rgwr_ready());
__icdc_set_addr(addr);
mdelay(1);
return(__icdc_get_value());
}
static void printk_codec_files(void)
{
int cnt, val;
//printk("\n");
#if 0
printk("REG_CPM_I2SCDR=0x%08x\n",REG_CPM_I2SCDR);
printk("REG_CPM_CLKGR=0x%08x\n",REG_CPM_CLKGR);
printk("REG_CPM_CPCCR=0x%08x\n",REG_CPM_CPCCR);
printk("REG_AIC_FR=0x%08x\n",REG_AIC_FR);
printk("REG_AIC_CR=0x%08x\n",REG_AIC_CR);
printk("REG_AIC_I2SCR=0x%08x\n",REG_AIC_I2SCR);
printk("REG_AIC_SR=0x%08x\n",REG_AIC_SR);
printk("REG_ICDC_RGDATA=0x%08x\n",REG_ICDC_RGDATA);
#endif
for (cnt = 0; cnt < JZDLV_CACHEREGNUM ; cnt++) {
val = read_codec_file(cnt);
jzdlv_reg[cnt] = val;
//printk(" ( %d : 0x%x ) ",cnt ,jzdlv_reg[cnt]);
}
//printk("\n");
}
void write_codec_file(int addr, int val)
{
while (__icdc_rgwr_ready());
__icdc_set_addr(addr);
__icdc_set_cmd(val); /* write */
mdelay(1);
__icdc_set_rgwr();
mdelay(1);
//jzdlv_reg[addr] = val;
}
int write_codec_file_bit(int addr, int bitval, int mask_bit)
{
int val;
while (__icdc_rgwr_ready());
__icdc_set_addr(addr);
mdelay(1);
val = __icdc_get_value(); /* read */
val &= ~(1 << mask_bit);
if (bitval == 1)
val |= 1 << mask_bit;
#if 0
while (__icdc_rgwr_ready());
__icdc_set_addr(addr);
__icdc_set_cmd(val); /* write */
mdelay(1);
__icdc_set_rgwr();
mdelay(1);
#else
write_codec_file(addr, val);
#endif
while (__icdc_rgwr_ready());
__icdc_set_addr(addr);
val = __icdc_get_value(); /* read */
if (((val >> mask_bit) & bitval) == bitval)
return 1;
else
return 0;
}
/*
* read jzdlv register cache
*/
static inline unsigned int jzdlv_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
{
u16 *cache = codec->reg_cache;
if (reg >= JZDLV_CACHEREGNUM)
return -1;
return cache[reg];
}
static inline unsigned int jzdlv_read(struct snd_soc_codec *codec,
unsigned int reg)
{
u8 data;
data = reg;
if (codec->hw_write(codec->control_data, &data, 1) != 1)
return -EIO;
if (codec->hw_read(codec->control_data, &data, 1) != 1)
return -EIO;
return data;
}
/*
* write jzdlv register cache
*/
static inline void jzdlv_write_reg_cache(struct snd_soc_codec *codec,
unsigned int reg, u16 value)
{
u16 *cache = codec->reg_cache;
if (reg >= JZDLV_CACHEREGNUM) {
return;
}
cache[reg] = value;
}
/*
* write to the jzdlv register space
*/
static int jzdlv_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
jzdlv_write_reg_cache(codec, reg, value);
if(codec->hw_write)
codec->hw_write(&value, NULL, reg);
return 0;
}
static const char *jzdlv_input_select[] = {"Line In", "Mic"};
static const char *jzdlv_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
static const struct soc_enum jzdlv_enum[] = {
SOC_ENUM_SINGLE(0x04, 2, 2, jzdlv_input_select),
SOC_ENUM_SINGLE(0x05, 1, 4, jzdlv_deemph),
};
/* set Audio data replay */
void set_audio_data_replay(void)
{
write_codec_file(9, 0xff);
write_codec_file(8, 0x20);// only CCMC
mdelay(10);
/* DAC path */
write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0
write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1
write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1
write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
mdelay(100);
write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
write_codec_file_bit(5, 0, 7);//PMR1.SB_DAC->0
write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
mdelay(100);
write_codec_file_bit(1, 0, 5);//DAC_MUTE->0
}
/* unset Audio data replay */
void unset_audio_data_replay(void)
{
write_codec_file_bit(1, 1, 5);//DAC_MUTE->1
mdelay(200);
write_codec_file_bit(5, 1, 6);//SB_OUT->1
write_codec_file_bit(5, 1, 7);//SB_DAC->1
write_codec_file_bit(5, 1, 4);//SB_MIX->1
write_codec_file_bit(6, 1, 0);//SB_SLEEP->1
write_codec_file_bit(6, 1, 1);//SB->1
write_codec_file(9, 0xff);
write_codec_file(8, 0x3f);
}
/* set Record MIC input audio without playback */
static void set_record_mic_input_audio_without_playback(void)
{
/* ADC path for MIC IN */
write_codec_file_bit(1, 1, 2);
write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0
//write_codec_file_bit(1, 1, 6);//CR1.MONO->1
write_codec_file(22, 0x40);//mic 1
write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0
write_codec_file_bit(3, 1, 7);//CR1.HP_DIS->1
write_codec_file_bit(5, 1, 3);//PMR1.SB_LIN->1
write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1
write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0
write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0
write_codec_file_bit(6, 1, 3);// gain set
write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0
mdelay(100);
write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0
write_codec_file(1, 0x4);
}
/* unset Record MIC input audio without playback */
static void unset_record_mic_input_audio_without_playback(void)
{
/* ADC path for MIC IN */
write_codec_file_bit(5, 1, 4);//SB_ADC->1
write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1
write_codec_file(22, 0xc0);//CR3.SB_MIC1
}
#if 0
static irqreturn_t aic_codec_irq(int irq, void *dev_id)
{
u8 file_9 = read_codec_file(9);
u8 file_8 = read_codec_file(8);
//printk("--- 8:0x%x 9:0x%x ---\n",file_8,file_9);
if ((file_9 & 0x1f) == 0x10) {
// have hp short circuit
write_codec_file(8, 0x3f);//mask all interrupt
write_codec_file_bit(5, 1, 6);//SB_OUT->1
mdelay(300);
while ((read_codec_file(9) & 0x4) != 0x4);
while ((read_codec_file(9) & 0x10) == 0x10) {
write_codec_file(9, 0x10);
}
write_codec_file_bit(5, 0, 6);//SB_OUT->0
mdelay(300);
while ((read_codec_file(9) & 0x8) != 0x8);
write_codec_file(9, file_9);
write_codec_file(8, file_8);
return IRQ_HANDLED;
}
if (file_9 & 0x8)
ramp_up_end = jiffies;
else if (file_9 & 0x4)
ramp_down_end = jiffies;
else if (file_9 & 0x2)
gain_up_end = jiffies;
else if (file_9 & 0x1)
gain_down_end = jiffies;
write_codec_file(9, file_9);
if (file_9 & 0xf)
wake_up(&pop_wait_queue);
while (REG_ICDC_RGDATA & 0x100);
return IRQ_HANDLED;
}
#else
static irqreturn_t aic_codec_irq(int irq, void *dev_id)
{
u8 file_9 = read_codec_file(9);
u8 file_8 = read_codec_file(8);
//printk("--- 1 8:0x%x 9:0x%x ---\n",file_8,file_9);
if ((file_9 & 0x1f) == 0x10) {
write_codec_file(8, 0x3f);
write_codec_file_bit(5, 1, 6);//SB_OUT->1
mdelay(300);
while ((read_codec_file(9) & 0x4) != 0x4);
while ((read_codec_file(9) & 0x10) == 0x10) {
write_codec_file(9, 0x10);
}
write_codec_file_bit(5, 0, 6);//SB_OUT->0
mdelay(300);
while ((read_codec_file(9) & 0x8) != 0x8);
write_codec_file(9, file_9);
write_codec_file(8, file_8);
return IRQ_HANDLED;
}
/*if (file_9 & 0x8)
ramp_up_end = jiffies;
else if (file_9 & 0x4)
ramp_down_end = jiffies;
else if (file_9 & 0x2)
gain_up_end = jiffies;
else if (file_9 & 0x1)
gain_down_end = jiffies;*/
write_codec_file(9, file_9);
/*if (file_9 & 0xf)
wake_up(&pop_wait_queue);*/
while (REG_ICDC_RGDATA & 0x100);
return IRQ_HANDLED;
}
#endif
static int jzdlv_reset(struct snd_soc_codec *codec)
{
/* reset DLV codec. from hibernate mode to sleep mode */
write_codec_file(0, 0xf);
write_codec_file_bit(6, 0, 0);
write_codec_file_bit(6, 0, 1);
mdelay(200);
//write_codec_file(0, 0xf);
write_codec_file_bit(5, 0, 7);//PMR1.SB_DAC->0
write_codec_file_bit(5, 0, 4);//PMR1.SB_ADC->0
mdelay(10);//wait for stability
return 0;
}
#if 0
static int jzdlv_sync(struct snd_soc_codec *codec)
{
u16 *cache = codec->reg_cache;
int i, r = 0;
for (i = 0; i < JZDLV_CACHEREGNUM; i++)
r |= jzdlv_write(codec, i, cache[i]);
return r;
};
#endif
static const struct snd_kcontrol_new jzdlv_snd_controls[] = {
//SOC_DOUBLE_R("Master Playback Volume", 1, 1, 0, 3, 0),
SOC_DOUBLE_R("Master Playback Volume", DLV_CGR8, DLV_CGR9, 0, 31, 0),
//SOC_DOUBLE_R("MICBG", ICODEC_2_LOW, ICODEC_2_LOW, 4, 3, 0),
//SOC_DOUBLE_R("Line", 2, 2, 0, 31, 0),
SOC_DOUBLE_R("Line", DLV_CGR10, DLV_CGR10, 0, 15, 0),
};
/* add non dapm controls */
static int jzdlv_add_controls(struct snd_soc_codec *codec)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(jzdlv_snd_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&jzdlv_snd_controls[i], codec, NULL));
if (err < 0)
return err;
}
return 0;
}
/* Output Mixer */
static const struct snd_kcontrol_new jzdlv_output_mixer_controls[] = {
SOC_DAPM_SINGLE("Line Bypass Switch", 0x04, 3, 1, 0),
SOC_DAPM_SINGLE("Mic Sidetone Switch", 0x04, 5, 1, 0),
SOC_DAPM_SINGLE("HiFi Playback Switch", 0x04, 4, 1, 0),
};
/* Input mux */
static const struct snd_kcontrol_new jzdlv_input_mux_controls =
SOC_DAPM_ENUM("Input Select", jzdlv_enum[0]);
static const struct snd_soc_dapm_widget jzdlv_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Output Mixer", 0x06, 4, 1,
&jzdlv_output_mixer_controls[0],
ARRAY_SIZE(jzdlv_output_mixer_controls)),
//SND_SOC_DAPM_DAC("DAC", "HiFi Playback", 0x06, 3, 1),
SND_SOC_DAPM_OUTPUT("LOUT"),
SND_SOC_DAPM_OUTPUT("LHPOUT"),
SND_SOC_DAPM_OUTPUT("ROUT"),
SND_SOC_DAPM_OUTPUT("RHPOUT"),
//SND_SOC_DAPM_ADC("ADC", "HiFi Capture", 0x06, 2, 1),
SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &jzdlv_input_mux_controls),
SND_SOC_DAPM_PGA("Line Input", 0x06, 0, 1, NULL, 0),
SND_SOC_DAPM_MICBIAS("Mic Bias", 0x06, 1, 1),
SND_SOC_DAPM_INPUT("MICIN"),
SND_SOC_DAPM_INPUT("RLINEIN"),
SND_SOC_DAPM_INPUT("LLINEIN"),
};
static const struct snd_soc_dapm_route intercon[] = {
/* output mixer */
{"Output Mixer", "Line Bypass Switch", "Line Input"},
{"Output Mixer", "HiFi Playback Switch", "DAC"},
{"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
/* outputs */
{"RHPOUT", NULL, "Output Mixer"},
{"ROUT", NULL, "Output Mixer"},
{"LHPOUT", NULL, "Output Mixer"},
{"LOUT", NULL, "Output Mixer"},
/* input mux */
{"Input Mux", "Line In", "Line Input"},
{"Input Mux", "Mic", "Mic Bias"},
{"ADC", NULL, "Input Mux"},
/* inputs */
{"Line Input", NULL, "LLINEIN"},
{"Line Input", NULL, "RLINEIN"},
{"Mic Bias", NULL, "MICIN"},
};
static void init_codec(void)
{
/* reset DLV codec. from hibernate mode to sleep mode */
write_codec_file(0, 0xf);
write_codec_file_bit(6, 0, 0);
write_codec_file_bit(6, 0, 1);
mdelay(200);
//write_codec_file(0, 0xf);
write_codec_file_bit(5, 0, 7);//PMR1.SB_DAC->0
write_codec_file_bit(5, 0, 4);//PMR1.SB_ADC->0
mdelay(10);//wait for stability
}
static int jzdlv_add_widgets(struct snd_soc_codec *codec)
{
snd_soc_dapm_new_controls(codec, jzdlv_dapm_widgets,
ARRAY_SIZE(jzdlv_dapm_widgets));
snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
snd_soc_dapm_new_widgets(codec);
return 0;
}
static int jzdlv_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
int speed = 0;
int val = 0;
/* sample channel */
switch (params_channels(params)) {
case 1:
write_codec_file_bit(1, 1, 6);//CR1.MONO->1 for Mono
break;
case 2:
write_codec_file_bit(1, 0, 6);//CR1.MONO->0 for Stereo
break;
}
/* sample rate */
switch (params_rate(params)) {
case 8000:
speed = 10;
break;
case 9600:
speed = 9;
break;
case 11025:
speed = 8;
break;
case 12000:
speed = 7;
break;
case 16000:
speed = 6;
break;
case 22050:
speed = 5;
break;
case 24000:
speed = 4;
break;
case 32000:
speed = 3;
break;
case 44100:
speed = 2;
break;
case 48000:
speed = 1;
break;
case 96000:
speed = 0;
break;
default:
printk(" invalid rate :0x%08x\n",params_rate(params));
}
val = (speed << 4) | speed;
jzdlv_write(codec, DLV_CCR2, val);
return 0;
}
static int jzdlv_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
int ret = 0;
//struct snd_soc_pcm_runtime *rtd = substream->private_data;
//struct snd_soc_device *socdev = rtd->socdev;
//struct snd_soc_codec *codec = socdev->codec;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
//case SNDRV_PCM_TRIGGER_RESUME:
//case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
init_codec();
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
REG_AIC_I2SCR = 0x10;
mdelay(1);
set_audio_data_replay();
mdelay(5);
write_codec_file_bit(5, 0, 7);//PMR1.SB_DAC->0
__aic_flush_fifo();
} else {
set_record_mic_input_audio_without_playback();
mdelay(10);
REG_AIC_I2SCR = 0x10;
mdelay(20);
__aic_flush_fifo();
write_codec_file_bit(5, 1, 7);
}
break;
case SNDRV_PCM_TRIGGER_STOP:
//case SNDRV_PCM_TRIGGER_SUSPEND:
//case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
unset_audio_data_replay();
} else {
unset_record_mic_input_audio_without_playback();
}
break;
default:
ret = -EINVAL;
}
return ret;
}
static int jzdlv_pcm_prepare(struct snd_pcm_substream *substream)
{
/*struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec; */
return 0;
}
static void jzdlv_shutdown(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
/* deactivate */
if (!codec->active) {
udelay(50);
}
}
static int jzdlv_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
u16 reg_val = jzdlv_read_reg_cache(codec, 2/*DLV_1_LOW*/);
if (mute != 0)
mute = 1;
if (mute)
reg_val = reg_val | (0x1 << 14);
else
reg_val = reg_val & ~(0x1 << 14);
//jzdlv_write(codec, DLV_1_LOW, reg_val);
return 0;
}
static int jzdlv_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct jzdlv_priv *jzdlv = codec->private_data;
jzdlv->sysclk = freq;
return 0;
}
/*
* Set's ADC and Voice DAC format. called by apus_hw_params() in apus.c
*/
static int jzdlv_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
/* struct snd_soc_codec *codec = codec_dai->codec; */
/* set master/slave audio interface. codec side */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
/* set master mode for codec */
break;
case SND_SOC_DAIFMT_CBS_CFS:
/* set slave mode for codec */
break;
default:
return -EINVAL;
}
/* interface format . set some parameter for codec side */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
/* set I2S mode for codec */
break;
case SND_SOC_DAIFMT_RIGHT_J:
/* set right J mode */
break;
case SND_SOC_DAIFMT_LEFT_J:
/* set left J mode */
break;
case SND_SOC_DAIFMT_DSP_A:
/* set dsp A mode */
break;
case SND_SOC_DAIFMT_DSP_B:
/* set dsp B mode */
break;
default:
return -EINVAL;
}
/* clock inversion. codec side */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_IB_IF:
break;
case SND_SOC_DAIFMT_IB_NF:
break;
case SND_SOC_DAIFMT_NB_IF:
break;
default:
return -EINVAL;
}
/* jzcodec_write(codec, 0, val); */
return 0;
}
static int jzdlv_dapm_event(struct snd_soc_codec *codec, int event)
{
/* u16 reg_val; */
switch (event) {
case SNDRV_CTL_POWER_D0: /* full On */
/* vref/mid, osc on, dac unmute */
/* u16 reg_val = jzcodec_read_reg_cache(codec, ICODEC_1_LOW); */
/* jzcodec_write(codec, 0, val); */
break;
case SNDRV_CTL_POWER_D1: /* partial On */
case SNDRV_CTL_POWER_D2: /* partial On */
break;
case SNDRV_CTL_POWER_D3hot: /* Off, with power */
/* everything off except vref/vmid, */
/*reg_val = 0x0800;
jzcodec_write_reg_cache(codec, ICODEC_1_LOW, reg_val);
reg_val = 0x0017;
jzcodec_write_reg_cache(codec, ICODEC_1_HIGH, reg_val);
REG_ICDC_CDCCR1 = jzcodec_reg[0];
mdelay(2);
reg_val = 0x2102;
jzcodec_write_reg_cache(codec, ICODEC_1_LOW, reg_val);
reg_val = 0x001f;
jzcodec_write_reg_cache(codec, ICODEC_1_HIGH, reg_val);
REG_ICDC_CDCCR1 = jzcodec_reg[0];
mdelay(2);
reg_val = 0x3302;
jzcodec_write_reg_cache(codec, ICODEC_1_LOW, reg_val);
reg_val = 0x0003;
jzcodec_write_reg_cache(codec, ICODEC_1_HIGH, reg_val);
REG_ICDC_CDCCR1 = jzcodec_reg[0];*/
break;
case SNDRV_CTL_POWER_D3cold: /* Off, without power */
/* everything off, dac mute, inactive */
/*reg_val = 0x2302;
jzcodec_write(codec, ICODEC_1_LOW, reg_val);
reg_val = 0x001b;
jzcodec_write(codec, ICODEC_1_HIGH, reg_val);
mdelay(1);
reg_val = 0x2102;
jzcodec_write(codec, ICODEC_1_LOW, reg_val);
reg_val = 0x001b;
jzcodec_write(codec, ICODEC_1_HIGH, reg_val);*/
break;
}
//codec->dapm_state = event;
return 0;
}
#define JZDLV_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_12000 |\
SNDRV_PCM_RATE_24000)
#define JZDLV_FORMATS (SNDRV_PCM_FORMAT_S8 | SNDRV_PCM_FMTBIT_S16_LE)
struct snd_soc_dai jzdlv_dai = {
.name = "JZDLV",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = JZDLV_RATES,
.formats = JZDLV_FORMATS,},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = JZDLV_RATES,
.formats = JZDLV_FORMATS,},
.ops = {
.trigger = jzdlv_pcm_trigger,
.prepare = jzdlv_pcm_prepare,
.hw_params = jzdlv_hw_params,
.shutdown = jzdlv_shutdown,
},
.dai_ops = {
.digital_mute = jzdlv_mute,
.set_sysclk = jzdlv_set_dai_sysclk,
.set_fmt = jzdlv_set_dai_fmt,
}
};
EXPORT_SYMBOL_GPL(jzdlv_dai);
#ifdef CONFIG_PM
static int jzdlv_suspend(struct platform_device *pdev, pm_message_t state)
{
#if 0
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
jzcodec_reg_pm[ICODEC_1_LOW] = jzcodec_read_reg_cache(codec, ICODEC_1_LOW);
jzcodec_reg_pm[ICODEC_1_HIGH] = jzcodec_read_reg_cache(codec, ICODEC_1_HIGH);
jzcodec_reg_pm[ICODEC_2_LOW] = jzcodec_read_reg_cache(codec, ICODEC_2_LOW);
jzcodec_reg_pm[ICODEC_2_HIGH] = jzcodec_read_reg_cache(codec, ICODEC_2_HIGH);
jzcodec_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
#endif
return 0;
}
static int jzdlv_resume(struct platform_device *pdev)
{
#if 0
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
u16 reg_val;
jzcodec_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
reg_val = jzcodec_reg_pm[ICODEC_1_LOW];
jzcodec_write(codec, ICODEC_1_LOW, reg_val);
reg_val = jzcodec_reg_pm[ICODEC_1_HIGH];
jzcodec_write(codec, ICODEC_1_HIGH, reg_val);
reg_val = jzcodec_reg_pm[ICODEC_2_LOW];
jzcodec_write(codec, ICODEC_2_LOW, reg_val);
reg_val = jzcodec_reg_pm[ICODEC_2_HIGH];
jzcodec_write(codec, ICODEC_2_HIGH, reg_val);
jzcodec_dapm_event(codec, codec->suspend_dapm_state);
#endif
return 0;
}
#else
#define jzdlv_suspend NULL
#define jzdlv_resume NULL
#endif
/*
* initialise the JZDLV driver
* register the mixer and dsp interfaces with the kernel
*/
static int jzdlv_init(struct snd_soc_device *socdev)
{
struct snd_soc_codec *codec = socdev->codec;
int ret = 0, retval;
/*REG_CPM_CPCCR &= ~(1 << 31);
REG_CPM_CPCCR &= ~(1 << 30);*/
write_codec_file(0, 0xf);
REG_AIC_I2SCR = 0x10;
__i2s_internal_codec();
__i2s_as_slave();
__i2s_select_i2s();
__aic_select_i2s();
__aic_reset();
mdelay(10);
REG_AIC_I2SCR = 0x10;
mdelay(20);
/* power on DLV */
write_codec_file(8, 0x3f);
write_codec_file(9, 0xff);
mdelay(10);
__cpm_start_idct();
__cpm_start_db();
__cpm_start_me();
__cpm_start_mc();
__cpm_start_ipu();
codec->name = "JZDLV";
codec->owner = THIS_MODULE;
codec->read = jzdlv_read_reg_cache;
codec->write = jzdlv_write;
//codec->dapm_event = jzdlv_dapm_event;
codec->dai = &jzdlv_dai;
codec->num_dai = 1;
codec->reg_cache_size = sizeof(jzdlv_reg);
codec->reg_cache = kmemdup(jzdlv_reg, sizeof(jzdlv_reg), GFP_KERNEL);
if (codec->reg_cache == NULL)
return -ENOMEM;
jzdlv_reset(codec);
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
printk(KERN_ERR "jzdlv: failed to create pcms\n");
goto pcm_err;
}
/* power on device */
jzdlv_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
jzdlv_add_controls(codec);
jzdlv_add_widgets(codec);
ret = snd_soc_register_card(socdev);
if (ret < 0) {
printk(KERN_ERR "jzcodec: failed to register card\n");
goto card_err;
}
mdelay(10);
REG_AIC_I2SCR = 0x10;
mdelay(20);
/* power on DLV */
write_codec_file(9, 0xff);
write_codec_file(8, 0x3f);
retval = request_irq(IRQ_AIC, aic_codec_irq, IRQF_DISABLED, "aic_codec_irq", NULL);
if (retval) {
printk("Could not get aic codec irq %d\n", IRQ_AIC);
return retval;
}
printk_codec_files();
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
kfree(codec->reg_cache);
return ret;
}
static struct snd_soc_device *jzdlv_socdev;
static int write_codec_reg(u16 * add, char * name, int reg)
{
write_codec_file(reg, *add);
return 0;
}
static int jzdlv_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
struct jzdlv_priv *jzdlv;
int ret = 0;
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
jzdlv = kzalloc(sizeof(struct jzdlv_priv), GFP_KERNEL);
if (jzdlv == NULL) {
kfree(codec);
return -ENOMEM;
}
codec->private_data = jzdlv;
socdev->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
jzdlv_socdev = socdev;
/* Add other interfaces here ,no I2C connection */
codec->hw_write = (hw_write_t)write_codec_reg;
//codec->hw_read = (hw_read_t)read_codec_reg;
ret = jzdlv_init(jzdlv_socdev);
if (ret < 0) {
codec = jzdlv_socdev->codec;
err("failed to initialise jzdlv\n");
kfree(codec);
}
return ret;
}
/* power down chip */
static int jzdlv_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
if (codec->control_data)
jzdlv_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
kfree(codec->private_data);
kfree(codec);
return 0;
}
struct snd_soc_codec_device soc_codec_dev_jzdlv = {
.probe = jzdlv_probe,
.remove = jzdlv_remove,
.suspend = jzdlv_suspend,
.resume = jzdlv_resume,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_jzdlv);
MODULE_DESCRIPTION("ASoC JZDLV driver");
MODULE_AUTHOR("Richard");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,50 @@
/*
* 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 _DLV_H
#define _DLV_H
/* jzdlv register space */
#define DLV_AICR 0x00
#define DLV_CR1 0x01
#define DLV_CR2 0x02
#define DLV_CCR1 0x03
#define DLV_CCR2 0x04
#define DLV_PMR1 0x05
#define DLV_PMR2 0x06
#define DLV_CRR 0x07
#define DLV_ICR 0x08
#define DLV_IFR 0x09
#define DLV_CGR1 0x0a
#define DLV_CGR2 0x0b
#define DLV_CGR3 0x0c
#define DLV_CGR4 0x0d
#define DLV_CGR5 0x0e
#define DLV_CGR6 0x0f
#define DLV_CGR7 0x10
#define DLV_CGR8 0x11
#define DLV_CGR9 0x12
#define DLV_CGR10 0x13
#define DLV_TR1 0x14
#define DLV_TR2 0x15
#define DLV_CR3 0x16
#define DLV_AGC1 0x17
#define DLV_AGC2 0x18
#define DLV_AGC3 0x19
#define DLV_AGC4 0x1a
#define DLV_AGC5 0x1b
#define JZDLV_CACHEREGNUM (DLV_AGC5+1)
#define JZDLV_SYSCLK 0
int read_codec_file(int addr);
int write_codec_file_bit(int addr, int bitval, int mask_bit);
void write_codec_file(int addr, int val);
extern struct snd_soc_dai jzdlv_dai;
extern struct snd_soc_codec_device soc_codec_dev_jzdlv;
#endif

View File

@ -0,0 +1,34 @@
config SND_JZ4740_SOC
tristate "SoC Audio for Ingenic jz4740 chip"
depends on (JZ4740_PAVO || JZ4725_DIPPER || JZ4720_VIRGO) && SND_SOC
help
Say Y or M if you want to add support for codecs attached to
the Jz4740 AC97, I2S or SSP interface. You will also need
to select the audio interfaces to support below.
config SND_JZ4740_SOC_PAVO
tristate "SoC Audio support for Ingenic Jz4740 PAVO board"
depends on SND_JZ4740_SOC
help
Say Y if you want to add support for SoC audio of internal codec on Ingenic Jz4740 PAVO board.
config SND_JZ4740_AC97
tristate "select AC97 protocol and AC97 codec pcm core support"
depends on SND_JZ4740_SOC && SND_JZ4740_SOC_PAVO
select SND_AC97_CODEC
help
Say Y if you want to add AC97 protocol support for pcm core.
config SND_JZ4740_SOC_AC97
tristate "SoC Audio (AC97 protocol) for Ingenic jz4740 chip"
depends on SND_JZ4740_SOC && SND_JZ4740_AC97 && SND_JZ4740_SOC_PAVO
select AC97_BUS
select SND_SOC_AC97_BUS
help
Say Y if you want to use AC97 protocol and ac97 codec on Ingenic Jz4740 PAVO board.
config SND_JZ4740_SOC_I2S
depends on SND_JZ4740_SOC && SND_JZ4740_SOC_PAVO
tristate "SoC Audio (I2S protocol) for Ingenic jz4740 chip"
help
Say Y if you want to use I2S protocol and I2S codec on Ingenic Jz4740 PAVO board.

View File

@ -0,0 +1,15 @@
#
# Jz4740 Platform Support
#
snd-soc-jz4740-objs := jz4740-pcm.o
snd-soc-jz4740-ac97-objs := jz4740-ac97.o
snd-soc-jz4740-i2s-objs := jz4740-i2s.o
obj-$(CONFIG_SND_JZ4740_SOC) += snd-soc-jz4740.o
obj-$(CONFIG_SND_JZ4740_SOC_AC97) += snd-soc-jz4740-ac97.o
obj-$(CONFIG_SND_JZ4740_SOC_I2S) += snd-soc-jz4740-i2s.o
# Jz4740 Machine Support
snd-soc-pavo-objs := pavo.o
obj-$(CONFIG_SND_JZ4740_SOC_PAVO) += snd-soc-pavo.o

View File

@ -0,0 +1,261 @@
/*
* linux/sound/jz4740-ac97.c -- AC97 support for the Ingenic jz4740 chip.
*
* Author: Richard
* Created: Dec 02, 2007
* Copyright: Ingenic Semiconductor Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <asm/irq.h>
#include <linux/mutex.h>
#include <asm/hardware.h>
#include <asm/arch/audio.h>
#include "jz4740-pcm.h"
#include "jz4740-ac97.h"
static DEFINE_MUTEX(car_mutex);
static DECLARE_WAIT_QUEUE_HEAD(gsr_wq);
static volatile long gsr_bits;
static unsigned short jz4740_ac97_read(struct snd_ac97 *ac97,
unsigned short reg)
{
unsigned short val = -1;
volatile u32 *reg_addr;
mutex_lock(&car_mutex);
out: mutex_unlock(&car_mutex);
return val;
}
static void jz4740_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
unsigned short val)
{
volatile u32 *reg_addr;
mutex_lock(&car_mutex);
mutex_unlock(&car_mutex);
}
static void jz4740_ac97_warm_reset(struct snd_ac97 *ac97)
{
gsr_bits = 0;
}
static void jz4740_ac97_cold_reset(struct snd_ac97 *ac97)
{
}
static irqreturn_t jz4740_ac97_irq(int irq, void *dev_id)
{
long status;
return IRQ_NONE;
}
struct snd_ac97_bus_ops soc_ac97_ops = {
.read = jz4740_ac97_read,
.write = jz4740_ac97_write,
.warm_reset = jz4740_ac97_warm_reset,
.reset = jz4740_ac97_cold_reset,
};
static struct jz4740_pcm_dma_params jz4740_ac97_pcm_stereo_out = {
.name = "AC97 PCM Stereo out",
.dev_addr = __PREG(PCDR),
.drcmr = &DRCMRTXPCDR,
.dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
DCMD_BURST32 | DCMD_WIDTH4,
};
static struct jz4740_pcm_dma_params jz4740_ac97_pcm_stereo_in = {
.name = "AC97 PCM Stereo in",
.dev_addr = __PREG(PCDR),
.drcmr = &DRCMRRXPCDR,
.dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
DCMD_BURST32 | DCMD_WIDTH4,
};
static struct jz4740_pcm_dma_params jz4740_ac97_pcm_aux_mono_out = {
.name = "AC97 Aux PCM (Slot 5) Mono out",
.dev_addr = __PREG(MODR),
.drcmr = &DRCMRTXMODR,
.dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
DCMD_BURST16 | DCMD_WIDTH2,
};
static struct jz4740_pcm_dma_params jz4740_ac97_pcm_aux_mono_in = {
.name = "AC97 Aux PCM (Slot 5) Mono in",
.dev_addr = __PREG(MODR),
.drcmr = &DRCMRRXMODR,
.dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
DCMD_BURST16 | DCMD_WIDTH2,
};
static struct jz4740_pcm_dma_params jz4740_ac97_pcm_mic_mono_in = {
.name = "AC97 Mic PCM (Slot 6) Mono in",
.dev_addr = __PREG(MCDR),
.drcmr = &DRCMRRXMCDR,
.dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
DCMD_BURST16 | DCMD_WIDTH2,
};
#ifdef CONFIG_PM
static int jz4740_ac97_suspend(struct platform_device *pdev,
struct snd_soc_cpu_dai *dai)
{
return 0;
}
static int jz4740_ac97_resume(struct platform_device *pdev,
struct snd_soc_cpu_dai *dai)
{
return 0;
}
#else
#define jz4740_ac97_suspend NULL
#define jz4740_ac97_resume NULL
#endif
static int jz4740_ac97_probe(struct platform_device *pdev)
{
int ret;
return 0;
}
static void jz4740_ac97_remove(struct platform_device *pdev)
{
}
static int jz4740_ac97_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
cpu_dai->dma_data = &jz4740_ac97_pcm_stereo_out;
else
cpu_dai->dma_data = &jz4740_ac97_pcm_stereo_in;
return 0;
}
static int jz4740_ac97_hw_aux_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
cpu_dai->dma_data = &jz4740_ac97_pcm_aux_mono_out;
else
cpu_dai->dma_data = &jz4740_ac97_pcm_aux_mono_in;
return 0;
}
static int jz4740_ac97_hw_mic_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
return -ENODEV;
else
cpu_dai->dma_data = &jz4740_ac97_pcm_mic_mono_in;
return 0;
}
#define JZ4740_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000)
struct snd_soc_cpu_dai jz4740_ac97_dai[] = {
{
.name = "jz4740-ac97",
.id = 0,
.type = SND_SOC_DAI_AC97,
.probe = jz4740_ac97_probe,
.remove = jz4740_ac97_remove,
.suspend = jz4740_ac97_suspend,
.resume = jz4740_ac97_resume,
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 2,
.channels_max = 2,
.rates = JZ4740_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.capture = {
.stream_name = "AC97 Capture",
.channels_min = 2,
.channels_max = 2,
.rates = JZ4740_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.ops = {
.hw_params = jz4740_ac97_hw_params,},
},
{
.name = "jz4740-ac97-aux",
.id = 1,
.type = SND_SOC_DAI_AC97,
.playback = {
.stream_name = "AC97 Aux Playback",
.channels_min = 1,
.channels_max = 1,
.rates = JZ4740_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.capture = {
.stream_name = "AC97 Aux Capture",
.channels_min = 1,
.channels_max = 1,
.rates = JZ4740_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.ops = {
.hw_params = jz4740_ac97_hw_aux_params,},
},
{
.name = "jz4740-ac97-mic",
.id = 2,
.type = SND_SOC_DAI_AC97,
.capture = {
.stream_name = "AC97 Mic Capture",
.channels_min = 1,
.channels_max = 1,
.rates = JZ4740_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.ops = {
.hw_params = jz4740_ac97_hw_mic_params,},
},
};
EXPORT_SYMBOL_GPL(jz4740_ac97_dai);
EXPORT_SYMBOL_GPL(soc_ac97_ops);
MODULE_AUTHOR("Richard");
MODULE_DESCRIPTION("AC97 driver for the Ingenic jz4740 chip");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,21 @@
/*
* linux/sound/soc/jz4740/jz4740-ac97.h
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _JZ4740_AC97_H
#define _JZ4740_AC97_H
#define JZ4740_DAI_AC97_HIFI 0
#define JZ4740_DAI_AC97_AUX 1
#define JZ4740_DAI_AC97_MIC 2
extern struct snd_soc_cpu_dai jz4740_ac97_dai[3];
/* platform data */
extern struct snd_ac97_bus_ops jz4740_ac97_ops;
#endif

View File

@ -0,0 +1,297 @@
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include "jz4740-pcm.h"
#include "jz4740-i2s.h"
static struct jz4740_dma_client jz4740_dma_client_out = {
.name = "I2S PCM Stereo out"
};
static struct jz4740_dma_client jz4740_dma_client_in = {
.name = "I2S PCM Stereo in"
};
static struct jz4740_pcm_dma_params jz4740_i2s_pcm_stereo_out = {
.client = &jz4740_dma_client_out,
.channel = DMA_ID_AIC_TX,
.dma_addr = AIC_DR,
.dma_size = 2,
};
static struct jz4740_pcm_dma_params jz4740_i2s_pcm_stereo_in = {
.client = &jz4740_dma_client_in,
.channel = DMA_ID_AIC_RX,
.dma_addr = AIC_DR,
.dma_size = 2,
};
static int jz4740_i2s_startup(struct snd_pcm_substream *substream)
{
/*struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;*/
return 0;
}
static int jz4740_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
/* 1 : ac97 , 0 : i2s */
break;
case SND_SOC_DAIFMT_LEFT_J:
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
/* 0 : slave */
break;
case SND_SOC_DAIFMT_CBM_CFS:
/* 1 : master */
break;
default:
break;
}
return 0;
}
/*
* Set Jz4740 Clock source
*/
static int jz4740_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int dir)
{
return 0;
}
static void jz4740_snd_tx_ctrl(int on)
{
if (on) {
/* enable replay */
__i2s_enable_transmit_dma();
__i2s_enable_replay();
__i2s_enable();
} else {
/* disable replay & capture */
__i2s_disable_replay();
__i2s_disable_record();
__i2s_disable_receive_dma();
__i2s_disable_transmit_dma();
__i2s_disable();
}
}
static void jz4740_snd_rx_ctrl(int on)
{
if (on) {
/* enable capture */
__i2s_enable_receive_dma();
__i2s_enable_record();
__i2s_enable();
} else {
/* disable replay & capture */
__i2s_disable_replay();
__i2s_disable_record();
__i2s_disable_receive_dma();
__i2s_disable_transmit_dma();
__i2s_disable();
}
}
static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
//struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
int channels = params_channels(params);
jz4740_snd_rx_ctrl(0);
jz4740_snd_rx_ctrl(0);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
//cpu_dai->dma_data = &jz4740_i2s_pcm_stereo_out;
rtd->dai->cpu_dai->dma_data = &jz4740_i2s_pcm_stereo_out;
if (channels == 1)
__aic_enable_mono2stereo();
else
__aic_disable_mono2stereo();
} else
rtd->dai->cpu_dai->dma_data = &jz4740_i2s_pcm_stereo_in;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
__i2s_set_transmit_trigger(4);
__i2s_set_receive_trigger(3);
__i2s_set_oss_sample_size(8);
__i2s_set_iss_sample_size(8);
break;
case SNDRV_PCM_FORMAT_S16_LE:
/* playback sample:16 bits, burst:16 bytes */
__i2s_set_transmit_trigger(4);
/* capture sample:16 bits, burst:16 bytes */
__i2s_set_receive_trigger(3);
__i2s_set_oss_sample_size(16);
__i2s_set_iss_sample_size(16);
break;
}
return 0;
}
static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
{
int ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
jz4740_snd_rx_ctrl(1);
else
jz4740_snd_tx_ctrl(1);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
jz4740_snd_rx_ctrl(0);
else
jz4740_snd_tx_ctrl(0);
break;
default:
ret = -EINVAL;
}
return ret;
}
static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream)
{
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
} else {
}
return;
}
static int jz4740_i2s_probe(struct platform_device *pdev)
{
__i2s_internal_codec();
__i2s_as_slave();
__i2s_select_i2s();
__aic_select_i2s();
mdelay(2);
__i2s_disable();
__i2s_reset();
mdelay(2);
__i2s_disable();
__i2s_internal_codec();
__i2s_as_slave();
__i2s_select_i2s();
__aic_select_i2s();
__i2s_set_oss_sample_size(16);
__i2s_set_iss_sample_size(16);
__aic_play_lastsample();
__i2s_disable_record();
__i2s_disable_replay();
__i2s_disable_loopback();
__i2s_set_transmit_trigger(7);
__i2s_set_receive_trigger(7);
jz4740_snd_tx_ctrl(0);
jz4740_snd_rx_ctrl(0);
return 0;
}
#ifdef CONFIG_PM
static int jz4740_i2s_suspend(struct platform_device *dev,
struct snd_soc_dai *dai)
{
if (!dai->active)
return 0;
return 0;
}
static int jz4740_i2s_resume(struct platform_device *pdev,
struct snd_soc_dai *dai)
{
if (!dai->active)
return 0;
return 0;
}
#else
#define jz4740_i2s_suspend NULL
#define jz4740_i2s_resume NULL
#endif
#define JZ4740_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
SNDRV_PCM_RATE_12000 | SNDRV_PCM_RATE_16000 |\
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_24000 |\
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
SNDRV_PCM_RATE_48000)
struct snd_soc_dai jz4740_i2s_dai = {
.name = "jz4740-i2s",
.id = 0,
.type = SND_SOC_DAI_I2S,
.probe = jz4740_i2s_probe,
.suspend = jz4740_i2s_suspend,
.resume = jz4740_i2s_resume,
.playback = {
.channels_min = 1,
.channels_max = 2,
.rates = JZ4740_I2S_RATES,
.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
.capture = {
.channels_min = 1,
.channels_max = 2,
.rates = JZ4740_I2S_RATES,
.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
.ops = {
.startup = jz4740_i2s_startup,
.shutdown = jz4740_i2s_shutdown,
.trigger = jz4740_i2s_trigger,
.hw_params = jz4740_i2s_hw_params,},
.dai_ops = {
.set_fmt = jz4740_i2s_set_dai_fmt,
.set_sysclk = jz4740_i2s_set_dai_sysclk,
},
};
EXPORT_SYMBOL_GPL(jz4740_i2s_dai);
/* Module information */
MODULE_AUTHOR("Richard, cjfeng@ingenic.cn, www.ingenic.cn");
MODULE_DESCRIPTION("jz4740 I2S SoC Interface");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,18 @@
/*
* 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 _JZ4740_I2S_H
#define _JZ4740_I2S_H
/* jz4740 DAI ID's */
#define JZ4740_DAI_I2S 0
/* I2S clock */
#define JZ4740_I2S_SYSCLK 0
extern struct snd_soc_dai jz4740_i2s_dai;
#endif

View File

@ -0,0 +1,689 @@
/*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <asm/io.h>
#include "jz4740-pcm.h"
static long sum_bytes = 0;
static int first_transfer = 0;
static int printk_flag = 0;
static int tran_bit = 0;
#ifdef CONFIG_SND_OSSEMUL
static int hw_params_cnt = 0;
#endif
struct jz4740_dma_buf_aic {
struct jz4740_dma_buf_aic *next;
int size; /* buffer size in bytes */
dma_addr_t data; /* start of DMA data */
dma_addr_t ptr; /* where the DMA got to [1] */
void *id; /* client's id */
};
struct jz4740_runtime_data {
spinlock_t lock;
int state;
int aic_dma_flag; /* start dma transfer or not */
unsigned int dma_loaded;
unsigned int dma_limit;
unsigned int dma_period;
dma_addr_t dma_start;
dma_addr_t dma_pos;
dma_addr_t dma_end;
struct jz4740_pcm_dma_params *params;
dma_addr_t user_cur_addr; /* user current write buffer start address */
unsigned int user_cur_len; /* user current write buffer length */
/* buffer list and information */
struct jz4740_dma_buf_aic *curr; /* current dma buffer */
struct jz4740_dma_buf_aic *next; /* next buffer to load */
struct jz4740_dma_buf_aic *end; /* end of queue */
};
/* identify hardware playback capabilities */
static const struct snd_pcm_hardware jz4740_pcm_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_U16_LE |
SNDRV_PCM_FMTBIT_U8 |
SNDRV_PCM_FMTBIT_S8,
.rates = SNDRV_PCM_RATE_8000_48000/*0x3fe*/,
.rate_min = 8000,
.rate_min = 48000,
.channels_min = 1,//2
.channels_max = 2,
.buffer_bytes_max = 128 * 1024,//16 * 1024
.period_bytes_min = PAGE_SIZE,
.period_bytes_max = PAGE_SIZE * 2,
.periods_min = 2,
.periods_max = 128,//16,
.fifo_size = 32,
};
/* jz4740__dma_buf_enqueue
*
* queue an given buffer for dma transfer.
*
* data the physical address of the buffer data
* size the size of the buffer in bytes
*
*/
static int jz4740_dma_buf_enqueue(struct jz4740_runtime_data *prtd, dma_addr_t data, int size)
{
struct jz4740_dma_buf_aic *aic_buf;
aic_buf = kzalloc(sizeof(struct jz4740_dma_buf_aic), GFP_KERNEL);
if (aic_buf == NULL) {
printk("aic buffer allocate failed,no memory!\n");
return -ENOMEM;
}
aic_buf->next = NULL;
aic_buf->data = aic_buf->ptr = data;
aic_buf->size = size;
if( prtd->curr == NULL) {
prtd->curr = aic_buf;
prtd->end = aic_buf;
prtd->next = NULL;
} else {
if (prtd->end == NULL)
printk("prtd->end is NULL\n");
prtd->end->next = aic_buf;
prtd->end = aic_buf;
}
/* if necessary, update the next buffer field */
if (prtd->next == NULL)
prtd->next = aic_buf;
return 0;
}
void audio_start_dma(struct jz4740_runtime_data *prtd, int mode)
{
unsigned long flags;
struct jz4740_dma_buf_aic *aic_buf;
int channel;
printk("%s:%s:%d\n",__FILE__,__FUNCTION__,__LINE__);
switch (mode) {
case DMA_MODE_WRITE:
/* free cur aic_buf */
if (first_transfer == 1) {
first_transfer = 0;
} else {
aic_buf = prtd->curr;
if (aic_buf != NULL) {
prtd->curr = aic_buf->next;
prtd->next = aic_buf->next;
aic_buf->next = NULL;
kfree(aic_buf);
aic_buf = NULL;
}
}
aic_buf = prtd->next;
channel = prtd->params->channel;
if (aic_buf) {
flags = claim_dma_lock();
disable_dma(channel);
jz_set_alsa_dma(channel, mode, tran_bit);
set_dma_addr(channel, aic_buf->data);
set_dma_count(channel, aic_buf->size);
enable_dma(channel);
release_dma_lock(flags);
prtd->aic_dma_flag |= AIC_START_DMA;
} else {
printk("next buffer is NULL for playback\n");
prtd->aic_dma_flag &= ~AIC_START_DMA;
return;
}
break;
case DMA_MODE_READ:
/* free cur aic_buf */
if (first_transfer == 1) {
first_transfer = 0;
} else {
aic_buf = prtd->curr;
if (aic_buf != NULL) {
prtd->curr = aic_buf->next;
prtd->next = aic_buf->next;
aic_buf->next = NULL;
kfree(aic_buf);
aic_buf = NULL;
}
}
aic_buf = prtd->next;
channel = prtd->params->channel;
if (aic_buf) {
flags = claim_dma_lock();
disable_dma(channel);
jz_set_alsa_dma(channel, mode, tran_bit);
set_dma_addr(channel, aic_buf->data);
set_dma_count(channel, aic_buf->size);
enable_dma(channel);
release_dma_lock(flags);
prtd->aic_dma_flag |= AIC_START_DMA;
} else {
printk("next buffer is NULL for capture\n");
prtd->aic_dma_flag &= ~AIC_START_DMA;
return;
}
break;
}
/* dump_jz_dma_channel(channel); */
}
/*
* place a dma buffer onto the queue for the dma system to handle.
*/
static void jz4740_pcm_enqueue(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4740_runtime_data *prtd = runtime->private_data;
/*struct snd_dma_buffer *buf = &substream->dma_buffer;*/
dma_addr_t pos = prtd->dma_pos;
int ret;
while (prtd->dma_loaded < prtd->dma_limit) {
unsigned long len = prtd->dma_period;
if ((pos + len) > prtd->dma_end) {
len = prtd->dma_end - pos;
}
ret = jz4740_dma_buf_enqueue(prtd, pos, len);
if (ret == 0) {
prtd->dma_loaded++;
pos += prtd->dma_period;
if (pos >= prtd->dma_end)
pos = prtd->dma_start;
} else
break;
}
prtd->dma_pos = pos;
}
/*
* call the function:jz4740_pcm_dma_irq() after DMA has transfered the current buffer
*/
static irqreturn_t jz4740_pcm_dma_irq(int dma_ch, void *dev_id)
{
struct snd_pcm_substream *substream = dev_id;
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4740_runtime_data *prtd = runtime->private_data;
/*struct jz4740_dma_buf_aic *aic_buf = prtd->curr;*/
int channel = prtd->params->channel;
unsigned long flags;
disable_dma(channel);
prtd->aic_dma_flag &= ~AIC_START_DMA;
/* must clear TT bit in DCCSR to avoid interrupt again */
if (__dmac_channel_transmit_end_detected(channel)) {
__dmac_channel_clear_transmit_end(channel);
}
if (__dmac_channel_transmit_halt_detected(channel)) {
__dmac_channel_clear_transmit_halt(channel);
}
if (__dmac_channel_address_error_detected(channel)) {
__dmac_channel_clear_address_error(channel);
}
if (substream)
snd_pcm_period_elapsed(substream);
spin_lock(&prtd->lock);
prtd->dma_loaded--;
if (prtd->state & ST_RUNNING) {
jz4740_pcm_enqueue(substream);
}
spin_unlock(&prtd->lock);
local_irq_save(flags);
if (prtd->state & ST_RUNNING) {
if (prtd->dma_loaded) {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
audio_start_dma(prtd, DMA_MODE_WRITE);
else
audio_start_dma(prtd, DMA_MODE_READ);
}
}
local_irq_restore(flags);
return IRQ_HANDLED;
}
/* some parameter about DMA operation */
static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4740_runtime_data *prtd = runtime->private_data;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct jz4740_pcm_dma_params *dma = rtd->dai->cpu_dai->dma_data;
size_t totbytes = params_buffer_bytes(params);
int ret;
#ifdef CONFIG_SND_OSSEMUL
if (hw_params_cnt)
return 0;
else
hw_params_cnt++ ;
#endif
if (!dma)
return 0;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
tran_bit = 8;
break;
case SNDRV_PCM_FORMAT_S16_LE:
tran_bit = 16;
break;
}
/* prepare DMA */
prtd->params = dma;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
ret = jz_request_dma(DMA_ID_AIC_TX, prtd->params->client->name,
jz4740_pcm_dma_irq, IRQF_DISABLED, substream);
if (ret < 0)
return ret;
prtd->params->channel = ret;
} else {
ret = jz_request_dma(DMA_ID_AIC_RX, prtd->params->client->name,
jz4740_pcm_dma_irq, IRQF_DISABLED, substream);
if (ret < 0)
return ret;
prtd->params->channel = ret;
}
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
runtime->dma_bytes = totbytes;
spin_lock_irq(&prtd->lock);
prtd->dma_loaded = 0;
prtd->aic_dma_flag = 0;
prtd->dma_limit = runtime->hw.periods_min;
prtd->dma_period = params_period_bytes(params);
prtd->dma_start = runtime->dma_addr;
prtd->dma_pos = prtd->dma_start;
prtd->dma_end = prtd->dma_start + totbytes;
prtd->curr = NULL;
prtd->next = NULL;
prtd->end = NULL;
sum_bytes = 0;
first_transfer = 1;
printk_flag = 0;
__dmac_disable_descriptor(prtd->params->channel);
__dmac_channel_disable_irq(prtd->params->channel);
spin_unlock_irq(&prtd->lock);
return ret;
}
static int jz4740_pcm_hw_free(struct snd_pcm_substream *substream)
{
struct jz4740_runtime_data *prtd = substream->runtime->private_data;
snd_pcm_set_runtime_buffer(substream, NULL);
if (prtd->params) {
jz_free_dma(prtd->params->channel);
prtd->params = NULL;
}
return 0;
}
/* set some dma para for playback/capture */
static int jz4740_dma_ctrl(int channel)
{
disable_dma(channel);
/* must clear TT bit in DCCSR to avoid interrupt again */
if (__dmac_channel_transmit_end_detected(channel)) {
__dmac_channel_clear_transmit_end(channel);
}
if (__dmac_channel_transmit_halt_detected(channel)) {
__dmac_channel_clear_transmit_halt(channel);
}
if (__dmac_channel_address_error_detected(channel)) {
__dmac_channel_clear_address_error(channel);
}
return 0;
}
static int jz4740_pcm_prepare(struct snd_pcm_substream *substream)
{
struct jz4740_runtime_data *prtd = substream->runtime->private_data;
int ret = 0;
/* return if this is a bufferless transfer e.g */
if (!prtd->params)
return 0;
/* flush the DMA channel and DMA channel bit check */
jz4740_dma_ctrl(prtd->params->channel);
prtd->dma_loaded = 0;
prtd->dma_pos = prtd->dma_start;
/* enqueue dma buffers */
jz4740_pcm_enqueue(substream);
return ret;
}
static int jz4740_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4740_runtime_data *prtd = runtime->private_data;
int ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
prtd->state |= ST_RUNNING;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
audio_start_dma(prtd, DMA_MODE_WRITE);
} else {
audio_start_dma(prtd, DMA_MODE_READ);
}
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
prtd->state &= ~ST_RUNNING;
break;
case SNDRV_PCM_TRIGGER_RESUME:
printk(" RESUME \n");
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
printk(" RESTART \n");
break;
default:
ret = -EINVAL;
}
return ret;
}
static snd_pcm_uframes_t
jz4740_pcm_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4740_runtime_data *prtd = runtime->private_data;
struct jz4740_dma_buf_aic *aic_buf = prtd->curr;
long count,res;
dma_addr_t ptr;
snd_pcm_uframes_t x;
int channel = prtd->params->channel;
spin_lock(&prtd->lock);
#if 1
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
count = get_dma_residue(channel);
count = aic_buf->size - count;
ptr = aic_buf->data + count;
res = ptr - prtd->dma_start;
} else {
count = get_dma_residue(channel);
count = aic_buf->size - count;
ptr = aic_buf->data + count;
res = ptr - prtd->dma_start;
}
# else
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
if ((prtd->aic_dma_flag & AIC_START_DMA) == 0) {
count = get_dma_residue(channel);
count = aic_buf->size - count;
ptr = aic_buf->data + count;
REG_DMAC_DSAR(channel) = ptr;
res = ptr - prtd->dma_start;
} else {
ptr = REG_DMAC_DSAR(channel);
if (ptr == 0x0)
printk("\ndma address is 00000000 in running!\n");
res = ptr - prtd->dma_start;
}
} else {
if ((prtd->aic_dma_flag & AIC_START_DMA) == 0) {
count = get_dma_residue(channel);
count = aic_buf->size - count;
ptr = aic_buf->data + count;
REG_DMAC_DTAR(channel) = ptr;
res = ptr - prtd->dma_start;
} else {
ptr = REG_DMAC_DTAR(channel);
if (ptr == 0x0)
printk("\ndma address is 00000000 in running!\n");
res = ptr - prtd->dma_start;
}
}
#endif
spin_unlock(&prtd->lock);
x = bytes_to_frames(runtime, res);
if (x == runtime->buffer_size)
x = 0;
return x;
}
static int jz4740_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4740_runtime_data *prtd;
#ifdef CONFIG_SND_OSSEMUL
hw_params_cnt = 0;
#endif
snd_soc_set_runtime_hwparams(substream, &jz4740_pcm_hardware);
prtd = kzalloc(sizeof(struct jz4740_runtime_data), GFP_KERNEL);
if (prtd == NULL)
return -ENOMEM;
spin_lock_init(&prtd->lock);
runtime->private_data = prtd;
REG_AIC_I2SCR = 0x10;
return 0;
}
static int jz4740_pcm_close(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4740_runtime_data *prtd = runtime->private_data;
struct jz4740_dma_buf_aic *aic_buf = NULL;
#ifdef CONFIG_SND_OSSEMUL
hw_params_cnt = 0;
#endif
if (prtd)
aic_buf = prtd->curr;
while (aic_buf != NULL) {
prtd->curr = aic_buf->next;
prtd->next = aic_buf->next;
aic_buf->next = NULL;
kfree(aic_buf);
aic_buf = NULL;
aic_buf = prtd->curr;
}
if (prtd) {
prtd->curr = NULL;
prtd->next = NULL;
prtd->end = NULL;
kfree(prtd);
}
return 0;
}
static int jz4740_pcm_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)//include/linux/mm.h
{
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned long start;
unsigned long off;
u32 len;
int ret = -ENXIO;
off = vma->vm_pgoff << PAGE_SHIFT;
start = runtime->dma_addr;
len = PAGE_ALIGN((start & ~PAGE_MASK) + runtime->dma_bytes);
start &= PAGE_MASK;
if ((vma->vm_end - vma->vm_start + off) > len) {
return -EINVAL;
}
off += start;
vma->vm_pgoff = off >> PAGE_SHIFT;
vma->vm_flags |= VM_IO;
#if defined(CONFIG_MIPS32)
pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK;
pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED;
/* pgprot_val(vma->vm_page_prot) |= _CACHE_CACHABLE_NONCOHERENT; */
#endif
ret = io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
vma->vm_end - vma->vm_start,
vma->vm_page_prot);
return ret;
}
struct snd_pcm_ops jz4740_pcm_ops = {
.open = jz4740_pcm_open,
.close = jz4740_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = jz4740_pcm_hw_params,
.hw_free = jz4740_pcm_hw_free,
.prepare = jz4740_pcm_prepare,
.trigger = jz4740_pcm_trigger,
.pointer = jz4740_pcm_pointer,
.mmap = jz4740_pcm_mmap,
};
static int jz4740_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
{
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
struct snd_dma_buffer *buf = &substream->dma_buffer;
size_t size = jz4740_pcm_hardware.buffer_bytes_max;
buf->dev.type = SNDRV_DMA_TYPE_DEV;
buf->dev.dev = pcm->card->dev;
buf->private_data = NULL;
/*buf->area = dma_alloc_coherent(pcm->card->dev, size,
&buf->addr, GFP_KERNEL);*/
buf->area = dma_alloc_noncoherent(pcm->card->dev, size,
&buf->addr, GFP_KERNEL);
if (!buf->area)
return -ENOMEM;
buf->bytes = size;
return 0;
}
static void jz4740_pcm_free_dma_buffers(struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
struct snd_dma_buffer *buf;
int stream;
for (stream = 0; stream < 2; stream++) {
substream = pcm->streams[stream].substream;
if (!substream)
continue;
buf = &substream->dma_buffer;
if (!buf->area)
continue;
dma_free_noncoherent(pcm->card->dev, buf->bytes,
buf->area, buf->addr);
buf->area = NULL;
}
}
static u64 jz4740_pcm_dmamask = DMA_32BIT_MASK;
int jz4740_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
{
int ret = 0;
if (!card->dev->dma_mask)
card->dev->dma_mask = &jz4740_pcm_dmamask;
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = DMA_32BIT_MASK;
if (dai->playback.channels_min) {
ret = jz4740_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_PLAYBACK);
if (ret)
goto out;
}
if (dai->capture.channels_min) {
ret = jz4740_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_CAPTURE);
if (ret)
goto out;
}
out:
return ret;
}
struct snd_soc_platform jz4740_soc_platform = {
.name = "jz4740-audio",
.pcm_ops = &jz4740_pcm_ops,
.pcm_new = jz4740_pcm_new,
.pcm_free = jz4740_pcm_free_dma_buffers,
};
EXPORT_SYMBOL_GPL(jz4740_soc_platform);
MODULE_AUTHOR("Richard");
MODULE_DESCRIPTION("Ingenic Jz4740 PCM DMA module");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,33 @@
/*
*
* 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 _JZ4740_PCM_H
#define _JZ4740_PCM_H
#include <asm/jzsoc.h>
#define ST_RUNNING (1<<0)
#define ST_OPENED (1<<1)
#define AIC_START_DMA (1<<0)
#define AIC_END_DMA (1<<1)
struct jz4740_dma_client {
char *name;
};
struct jz4740_pcm_dma_params {
struct jz4740_dma_client *client; /* stream identifier */
int channel; /* Channel ID */
dma_addr_t dma_addr;
int dma_size; /* Size of the DMA transfer */
};
/* platform data */
extern struct snd_soc_platform jz4740_soc_platform;
#endif

View File

@ -0,0 +1,364 @@
/*
* pavo.c -- SoC audio for PAVO
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include "../codecs/jzcodec.h"
#include "jz4740-pcm.h"
#include "jz4740-i2s.h"
#define PAVO_HP 0
#define PAVO_MIC 1
#define PAVO_LINE 2
#define PAVO_HEADSET 3
#define PAVO_HP_OFF 4
#define PAVO_SPK_ON 0
#define PAVO_SPK_OFF 1
/* audio clock in Hz - rounded from 12.235MHz */
#define PAVO_AUDIO_CLOCK 12288000
static int pavo_jack_func;
static int pavo_spk_func;
unsigned short set_scoop_gpio(struct device *dev, unsigned short bit)
{
unsigned short gpio_bit = 0;
return gpio_bit;
}
unsigned short reset_scoop_gpio(struct device *dev, unsigned short bit)
{
unsigned short gpio_bit = 0;
return gpio_bit;
}
static void pavo_ext_control(struct snd_soc_codec *codec)
{
int spk = 0, mic = 0, line = 0, hp = 0, hs = 0;
/* set up jack connection */
switch (pavo_jack_func) {
case PAVO_HP:
hp = 1;
/* set = unmute headphone */
//set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
//set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
break;
case PAVO_MIC:
mic = 1;
/* reset = mute headphone */
//reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
//reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
break;
case PAVO_LINE:
line = 1;
//reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
//reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
break;
case PAVO_HEADSET:
hs = 1;
mic = 1;
//reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
//set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
break;
}
if (pavo_spk_func == PAVO_SPK_ON)
spk = 1;
#if 0
/* set the enpoints to their new connetion states */
snd_soc_dapm_set_endpoint(codec, "Ext Spk", spk);
snd_soc_dapm_set_endpoint(codec, "Mic Jack", mic);
snd_soc_dapm_set_endpoint(codec, "Line Jack", line);
snd_soc_dapm_set_endpoint(codec, "Headphone Jack", hp);
snd_soc_dapm_set_endpoint(codec, "Headset Jack", hs);
/* signal a DAPM event */
snd_soc_dapm_sync_endpoints(codec);
#endif
}
static int pavo_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->socdev->codec;
/* check the jack status at stream startup */
pavo_ext_control(codec);
return 0;
}
/* we need to unmute the HP at shutdown as the mute burns power on pavo */
static void pavo_shutdown(struct snd_pcm_substream *substream)
{
/*struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->socdev->codec;*/
return;
}
static int pavo_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
int ret = 0;
/* set codec DAI configuration */
ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set the codec system clock for DAC and ADC */
ret = codec_dai->dai_ops.set_sysclk(codec_dai, JZCODEC_SYSCLK, 111,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
/* set the I2S system clock as input (unused) */
ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, JZ4740_I2S_SYSCLK, 0,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
return 0;
}
static struct snd_soc_ops pavo_ops = {
.startup = pavo_startup,
.hw_params = pavo_hw_params,
.shutdown = pavo_shutdown,
};
static int pavo_get_jack(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = pavo_jack_func;
return 0;
}
static int pavo_set_jack(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
if (pavo_jack_func == ucontrol->value.integer.value[0])
return 0;
pavo_jack_func = ucontrol->value.integer.value[0];
pavo_ext_control(codec);
return 1;
}
static int pavo_get_spk(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = pavo_spk_func;
return 0;
}
static int pavo_set_spk(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
if (pavo_spk_func == ucontrol->value.integer.value[0])
return 0;
pavo_spk_func = ucontrol->value.integer.value[0];
pavo_ext_control(codec);
return 1;
}
static int pavo_amp_event(struct snd_soc_dapm_widget *w, int event)
{
if (SND_SOC_DAPM_EVENT_ON(event))
//set_scoop_gpio(&corgiscoop_device.dev, PAVO_SCP_APM_ON);
;
else
//reset_scoop_gpio(&corgiscoop_device.dev, PAVO_SCP_APM_ON);
;
return 0;
}
static int pavo_mic_event(struct snd_soc_dapm_widget *w, int event)
{
if (SND_SOC_DAPM_EVENT_ON(event))
//set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MIC_BIAS);
;
else
//reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MIC_BIAS);
;
return 0;
}
/* pavo machine dapm widgets */
static const struct snd_soc_dapm_widget jzcodec_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Mic Jack",pavo_mic_event),
SND_SOC_DAPM_SPK("Ext Spk", pavo_amp_event),
SND_SOC_DAPM_LINE("Line Jack", NULL),
SND_SOC_DAPM_HP("Headset Jack", NULL),
};
/* pavo machine audio map (connections to the codec pins) */
static const char *audio_map[][3] = {
/* headset Jack - in = micin, out = LHPOUT*/
{"Headset Jack", NULL, "LHPOUT"},
/* headphone connected to LHPOUT1, RHPOUT1 */
{"Headphone Jack", NULL, "LHPOUT"},
{"Headphone Jack", NULL, "RHPOUT"},
/* speaker connected to LOUT, ROUT */
{"Ext Spk", NULL, "ROUT"},
{"Ext Spk", NULL, "LOUT"},
/* mic is connected to MICIN (via right channel of headphone jack) */
{"MICIN", NULL, "Mic Jack"},
/* Same as the above but no mic bias for line signals */
{"MICIN", NULL, "Line Jack"},
{NULL, NULL, NULL},
};
static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
"Off"};
static const char *spk_function[] = {"On", "Off"};
static const struct soc_enum pavo_enum[] = {
SOC_ENUM_SINGLE_EXT(5, jack_function),
SOC_ENUM_SINGLE_EXT(2, spk_function),
};
static const struct snd_kcontrol_new jzcodec_pavo_controls[] = {
SOC_ENUM_EXT("Jack Function", pavo_enum[0], pavo_get_jack,
pavo_set_jack),
SOC_ENUM_EXT("Speaker Function", pavo_enum[1], pavo_get_spk,
pavo_set_spk),
};
/*
* Pavo for a jzcodec as connected on jz4740 Device
*/
static int pavo_jzcodec_init(struct snd_soc_codec *codec)
{
int i, err;
#if 0
snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0);
snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0);
#endif
/* Add pavo specific controls */
for (i = 0; i < ARRAY_SIZE(jzcodec_pavo_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&jzcodec_pavo_controls[i],codec, NULL));
if (err < 0)
return err;
}
/* Add pavo specific widgets */
for(i = 0; i < ARRAY_SIZE(jzcodec_dapm_widgets); i++) {
snd_soc_dapm_new_control(codec, &jzcodec_dapm_widgets[i]);
}
#if 0
/* Set up pavo specific audio path audio_map */
for(i = 0; audio_map[i][0] != NULL; i++) {
snd_soc_dapm_connect_input(codec, audio_map[i][0],
audio_map[i][1], audio_map[i][2]);
}
snd_soc_dapm_sync_endpoints(codec);
#endif
return 0;
}
/* pavo digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link pavo_dai = {
.name = "JZCODEC",
.stream_name = "JZCODEC",
.cpu_dai = &jz4740_i2s_dai,
.codec_dai = &jzcodec_dai,
.init = pavo_jzcodec_init,
.ops = &pavo_ops,
};
/* pavo audio machine driver */
static struct snd_soc_machine snd_soc_machine_pavo = {
.name = "Pavo",
.dai_link = &pavo_dai,
.num_links = 1,
};
/* pavo audio subsystem */
static struct snd_soc_device pavo_snd_devdata = {
.machine = &snd_soc_machine_pavo,
.platform = &jz4740_soc_platform,
.codec_dev = &soc_codec_dev_jzcodec,
//.codec_data
};
static struct platform_device *pavo_snd_device;
static int __init pavo_init(void)
{
int ret;
pavo_snd_device = platform_device_alloc("soc-audio", -1);
if (!pavo_snd_device)
return -ENOMEM;
platform_set_drvdata(pavo_snd_device, &pavo_snd_devdata);
pavo_snd_devdata.dev = &pavo_snd_device->dev;
ret = platform_device_add(pavo_snd_device);
if (ret)
platform_device_put(pavo_snd_device);
return ret;
}
static void __exit pavo_exit(void)
{
platform_device_unregister(pavo_snd_device);
}
module_init(pavo_init);
module_exit(pavo_exit);
/* Module information */
MODULE_AUTHOR("Richard");
MODULE_DESCRIPTION("ALSA SoC Pavo");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,34 @@
config SND_JZ4750_SOC
tristate "SoC Audio for Ingenic jz4750 chip"
depends on (JZ4750_APUS || JZ4750_FUWA || JZ4750D_CETUS) && SND_SOC
help
Say Y or M if you want to add support for codecs attached to
the Jz4750 AC97, I2S or SSP interface. You will also need
to select the audio interfaces to support below.
config SND_JZ4750_SOC_APUS
tristate "SoC Audio support for Ingenic Jz4750 APUS board"
depends on SND_JZ4750_SOC
help
Say Y if you want to add support for SoC audio of internal codec on Ingenic Jz4750 APUS board.
config SND_JZ4750_AC97
tristate "select AC97 protocol and AC97 codec pcm core support"
depends on SND_JZ4750_SOC && SND_JZ4750_SOC_APUS
select SND_AC97_CODEC
help
Say Y if you want to add AC97 protocol support for pcm core.
config SND_JZ4750_SOC_AC97
tristate "SoC Audio (AC97 protocol) for Ingenic jz4750 chip"
depends on SND_JZ4750_SOC && SND_JZ4750_AC97 && SND_JZ4750_SOC_APUS
select AC97_BUS
select SND_SOC_AC97_BUS
help
Say Y if you want to use AC97 protocol and ac97 codec on Ingenic Jz4750 APUS board.
config SND_JZ4750_SOC_I2S
depends on SND_JZ4750_SOC && SND_JZ4750_SOC_APUS
tristate "SoC Audio (I2S protocol) for Ingenic jz4750 chip"
help
Say Y if you want to use I2S protocol and I2S codec on Ingenic Jz4750 APUS board.

View File

@ -0,0 +1,15 @@
#
# Jz4750 Platform Support
#
snd-soc-jz4750-objs := jz4750-pcm.o
snd-soc-jz4750-ac97-objs := jz4750-ac97.o
snd-soc-jz4750-i2s-objs := jz4750-i2s.o
obj-$(CONFIG_SND_JZ4750_SOC) += snd-soc-jz4750.o
obj-$(CONFIG_SND_JZ4750_SOC_AC97) += snd-soc-jz4750-ac97.o
obj-$(CONFIG_SND_JZ4750_SOC_I2S) += snd-soc-jz4750-i2s.o
# Jz4750 Machine Support
snd-soc-apus-objs := apus.o
obj-$(CONFIG_SND_JZ4750_SOC_APUS) += snd-soc-apus.o

View File

@ -0,0 +1,405 @@
/*
* apus.c -- SoC audio for APUS
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/tlv.h>
#include "../codecs/jzdlv.h"
#include "jz4750-pcm.h"
#include "jz4750-i2s.h"
//static struct snd_soc_machine apus;
#define APUS_HP 0
#define APUS_MIC 1
#define APUS_LINE 2
#define APUS_HEADSET 3
#define APUS_HP_OFF 4
#define APUS_SPK_ON 0
#define APUS_SPK_OFF 1
static int apus_jack_func;
static int apus_spk_func;
unsigned short set_scoop_gpio(struct device *dev, unsigned short bit)
{
unsigned short gpio_bit = 0;
return gpio_bit;
}
unsigned short reset_scoop_gpio(struct device *dev, unsigned short bit)
{
unsigned short gpio_bit = 0;
return gpio_bit;
}
static void apus_ext_control(struct snd_soc_codec *codec)
{
int spk = 0, mic = 0, line = 0, hp = 0, hs = 0;
/* set up jack connection */
switch (apus_jack_func) {
case APUS_HP:
hp = 1;
snd_soc_dapm_disable_pin(codec, "Mic Jack");
snd_soc_dapm_disable_pin(codec, "Line Jack");
snd_soc_dapm_enable_pin(codec, "Headphone Jack");
snd_soc_dapm_disable_pin(codec, "Headset Jack");
break;
case APUS_MIC:
mic = 1;
snd_soc_dapm_enable_pin(codec, "Mic Jack");
snd_soc_dapm_disable_pin(codec, "Line Jack");
snd_soc_dapm_disable_pin(codec, "Headphone Jack");
snd_soc_dapm_disable_pin(codec, "Headset Jack");
break;
case APUS_LINE:
line = 1;
snd_soc_dapm_disable_pin(codec, "Mic Jack");
snd_soc_dapm_enable_pin(codec, "Line Jack");
snd_soc_dapm_disable_pin(codec, "Headphone Jack");
snd_soc_dapm_disable_pin(codec, "Headset Jack");
break;
case APUS_HEADSET:
hs = 1;
mic = 1;
snd_soc_dapm_enable_pin(codec, "Mic Jack");
snd_soc_dapm_disable_pin(codec, "Line Jack");
snd_soc_dapm_disable_pin(codec, "Headphone Jack");
snd_soc_dapm_enable_pin(codec, "Headset Jack");
break;
}
if (apus_spk_func == APUS_SPK_ON)
spk = 1;
if (apus_spk_func == APUS_SPK_ON)
snd_soc_dapm_enable_pin(codec, "Ext Spk");
else
snd_soc_dapm_disable_pin(codec, "Ext Spk");
/* signal a DAPM event */
snd_soc_dapm_sync(codec);
}
static int apus_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->socdev->codec;
/* check the jack status at stream startup */
apus_ext_control(codec);
return 0;
}
/* we need to unmute the HP at shutdown as the mute burns power on apus */
static int apus_shutdown(struct snd_pcm_substream *substream)
{
/*struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->socdev->codec;*/
return 0;
}
static int apus_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
int ret = 0;
/* set codec DAI configuration */
/*ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);*/
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
/*ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);*/
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set the codec system clock for DAC and ADC */
/*ret = codec_dai->dai_ops.set_sysclk(codec_dai, JZDLV_SYSCLK, 111,
SND_SOC_CLOCK_IN);*/
ret = snd_soc_dai_set_sysclk(codec_dai, JZDLV_SYSCLK, 111,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
/* set the I2S system clock as input (unused) */
/*ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, JZ4750_I2S_SYSCLK, 0,
SND_SOC_CLOCK_IN);*/
ret = snd_soc_dai_set_sysclk(cpu_dai, JZ4750_I2S_SYSCLK, 0,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
return 0;
}
static struct snd_soc_ops apus_ops = {
.startup = apus_startup,
.hw_params = apus_hw_params,
.shutdown = apus_shutdown,
};
static int apus_get_jack(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = apus_jack_func;
return 0;
}
static int apus_set_jack(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
if (apus_jack_func == ucontrol->value.integer.value[0])
return 0;
apus_jack_func = ucontrol->value.integer.value[0];
apus_ext_control(codec);
return 1;
}
static int apus_get_spk(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = apus_spk_func;
return 0;
}
static int apus_set_spk(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
if (apus_spk_func == ucontrol->value.integer.value[0])
return 0;
apus_spk_func = ucontrol->value.integer.value[0];
apus_ext_control(codec);
return 1;
}
static int apus_amp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event)
{
if (SND_SOC_DAPM_EVENT_ON(event))
//set_scoop_gpio(&corgiscoop_device.dev, APUS_SCP_APM_ON);
;
else
//reset_scoop_gpio(&corgiscoop_device.dev, APUS_SCP_APM_ON);
;
return 0;
}
static int apus_mic_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event)
{
if (SND_SOC_DAPM_EVENT_ON(event))
//set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MIC_BIAS);
;
else
//reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MIC_BIAS);
;
return 0;
}
static int jzdlv_get_reg(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
//int reg = kcontrol->private_value & 0xFF;
//int shift = (kcontrol->private_value >> 8) & 0x0F;
//int mask = (kcontrol->private_value >> 16) & 0xFF;
//ucontrol->value.integer.value[0] = (lm4857_regs[reg] >> shift) & mask;
return 0;
}
static int jzdlv_set_reg(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
//int reg = kcontrol->private_value & 0xFF;
//int shift = (kcontrol->private_value >> 8) & 0x0F;
//int mask = (kcontrol->private_value >> 16) & 0xFF;
/*if (((lm4857_regs[reg] >> shift) & mask) ==
ucontrol->value.integer.value[0])
return 0;
lm4857_regs[reg] &= ~(mask << shift);
lm4857_regs[reg] |= ucontrol->value.integer.value[0] << shift;
lm4857_write_regs();*/
return 1;
}
/* apus machine dapm widgets */
static const struct snd_soc_dapm_widget jzdlv_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Mic Jack",apus_mic_event),
SND_SOC_DAPM_SPK("Ext Spk", apus_amp_event),
SND_SOC_DAPM_LINE("Line Jack", NULL),
SND_SOC_DAPM_HP("Headset Jack", NULL),
};
/* apus machine audio map (connections to the codec pins) */
static const struct snd_soc_dapm_route audio_map[] = {
/* headset Jack - in = micin, out = LHPOUT*/
{"Headset Jack", NULL, "LHPOUT"},
/* headphone connected to LHPOUT1, RHPOUT1 */
{"Headphone Jack", NULL, "LHPOUT"},
{"Headphone Jack", NULL, "RHPOUT"},
/* speaker connected to LOUT, ROUT */
{"Ext Spk", NULL, "ROUT"},
{"Ext Spk", NULL, "LOUT"},
/* mic is connected to MICIN (via right channel of headphone jack) */
{"MICIN", NULL, "Mic Jack"},
/* Same as the above but no mic bias for line signals */
{"MICIN", NULL, "Line Jack"},
};
static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
"Off"};
static const char *spk_function[] = {"On", "Off"};
static const struct soc_enum apus_enum[] = {
SOC_ENUM_SINGLE_EXT(5, jack_function),
SOC_ENUM_SINGLE_EXT(2, spk_function),
};
static const DECLARE_TLV_DB_SCALE(stereo_tlv, -4050, 150, 0);
//static const DECLARE_TLV_DB_SCALE(mono_tlv, -3450, 150, 0);
static const struct snd_kcontrol_new jzdlv_apus_controls[] = {
/* SOC_SINGLE_EXT_TLV("Amp Left Playback Volume", 1, 0, 31, 0,
jzdlv_get_reg, jzdlv_set_reg, stereo_tlv),*/
SOC_SINGLE_EXT_TLV("PCM", 1, 0, 31, 0,
jzdlv_get_reg, jzdlv_set_reg, stereo_tlv),
SOC_SINGLE_EXT_TLV("Capture", 2, 0, 31, 0,
jzdlv_get_reg, jzdlv_set_reg, stereo_tlv),
/* SOC_SINGLE_EXT_TLV("Amp Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
lm4857_get_reg, lm4857_set_reg, mono_tlv),*/
SOC_ENUM_EXT("Jack Function", apus_enum[0], apus_get_jack,apus_set_jack),
SOC_ENUM_EXT("Speaker Function", apus_enum[1], apus_get_spk,apus_set_spk),
};
/*
* Apus for a jzdlv as connected on jz4750 Device
*/
static int apus_jzdlv_init(struct snd_soc_codec *codec)
{
int i, err;
snd_soc_dapm_disable_pin(codec, "LLINEIN");
snd_soc_dapm_disable_pin(codec, "RLINEIN");
/* Add apus specific controls */
for (i = 0; i < ARRAY_SIZE(jzdlv_apus_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&jzdlv_apus_controls[i],codec, NULL));
if (err < 0)
return err;
}
/* Add apus specific widgets */
snd_soc_dapm_new_controls(codec, jzdlv_dapm_widgets,
ARRAY_SIZE(jzdlv_dapm_widgets));
/* Set up apus specific audio path audio_map */
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_sync(codec);
return 0;
}
/* apus digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link apus_dai = {
.name = "JZDLV",
.stream_name = "JZDLV",
.cpu_dai = &jz4750_i2s_dai,
.codec_dai = &jzdlv_dai,
.init = apus_jzdlv_init,
.ops = &apus_ops,
};
/* apus audio machine driver */
static struct snd_soc_machine snd_soc_machine_apus = {
.name = "Apus",
.dai_link = &apus_dai,
.num_links = 1,
};
/* apus audio subsystem */
static struct snd_soc_device apus_snd_devdata = {
.machine = &snd_soc_machine_apus,
.platform = &jz4750_soc_platform,
.codec_dev = &soc_codec_dev_jzdlv,
//.codec_data
};
static struct platform_device *apus_snd_device;
static int __init apus_init(void)
{
int ret;
apus_snd_device = platform_device_alloc("soc-audio", -1);
if (!apus_snd_device)
return -ENOMEM;
platform_set_drvdata(apus_snd_device, &apus_snd_devdata);
apus_snd_devdata.dev = &apus_snd_device->dev;
ret = platform_device_add(apus_snd_device);
if (ret)
platform_device_put(apus_snd_device);
return ret;
}
static void __exit apus_exit(void)
{
platform_device_unregister(apus_snd_device);
}
module_init(apus_init);
module_exit(apus_exit);
/* Module information */
MODULE_AUTHOR("Richard");
MODULE_DESCRIPTION("ALSA SoC Apus");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,261 @@
/*
* linux/sound/jz4740-ac97.c -- AC97 support for the Ingenic jz4740 chip.
*
* Author: Richard
* Created: Dec 02, 2007
* Copyright: Ingenic Semiconductor Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <asm/irq.h>
#include <linux/mutex.h>
#include <asm/hardware.h>
#include <asm/arch/audio.h>
#include "jz4740-pcm.h"
#include "jz4740-ac97.h"
static DEFINE_MUTEX(car_mutex);
static DECLARE_WAIT_QUEUE_HEAD(gsr_wq);
static volatile long gsr_bits;
static unsigned short jz4740_ac97_read(struct snd_ac97 *ac97,
unsigned short reg)
{
unsigned short val = -1;
volatile u32 *reg_addr;
mutex_lock(&car_mutex);
out: mutex_unlock(&car_mutex);
return val;
}
static void jz4740_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
unsigned short val)
{
volatile u32 *reg_addr;
mutex_lock(&car_mutex);
mutex_unlock(&car_mutex);
}
static void jz4740_ac97_warm_reset(struct snd_ac97 *ac97)
{
gsr_bits = 0;
}
static void jz4740_ac97_cold_reset(struct snd_ac97 *ac97)
{
}
static irqreturn_t jz4740_ac97_irq(int irq, void *dev_id)
{
long status;
return IRQ_NONE;
}
struct snd_ac97_bus_ops soc_ac97_ops = {
.read = jz4740_ac97_read,
.write = jz4740_ac97_write,
.warm_reset = jz4740_ac97_warm_reset,
.reset = jz4740_ac97_cold_reset,
};
static struct jz4740_pcm_dma_params jz4740_ac97_pcm_stereo_out = {
.name = "AC97 PCM Stereo out",
.dev_addr = __PREG(PCDR),
.drcmr = &DRCMRTXPCDR,
.dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
DCMD_BURST32 | DCMD_WIDTH4,
};
static struct jz4740_pcm_dma_params jz4740_ac97_pcm_stereo_in = {
.name = "AC97 PCM Stereo in",
.dev_addr = __PREG(PCDR),
.drcmr = &DRCMRRXPCDR,
.dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
DCMD_BURST32 | DCMD_WIDTH4,
};
static struct jz4740_pcm_dma_params jz4740_ac97_pcm_aux_mono_out = {
.name = "AC97 Aux PCM (Slot 5) Mono out",
.dev_addr = __PREG(MODR),
.drcmr = &DRCMRTXMODR,
.dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
DCMD_BURST16 | DCMD_WIDTH2,
};
static struct jz4740_pcm_dma_params jz4740_ac97_pcm_aux_mono_in = {
.name = "AC97 Aux PCM (Slot 5) Mono in",
.dev_addr = __PREG(MODR),
.drcmr = &DRCMRRXMODR,
.dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
DCMD_BURST16 | DCMD_WIDTH2,
};
static struct jz4740_pcm_dma_params jz4740_ac97_pcm_mic_mono_in = {
.name = "AC97 Mic PCM (Slot 6) Mono in",
.dev_addr = __PREG(MCDR),
.drcmr = &DRCMRRXMCDR,
.dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
DCMD_BURST16 | DCMD_WIDTH2,
};
#ifdef CONFIG_PM
static int jz4740_ac97_suspend(struct platform_device *pdev,
struct snd_soc_cpu_dai *dai)
{
return 0;
}
static int jz4740_ac97_resume(struct platform_device *pdev,
struct snd_soc_cpu_dai *dai)
{
return 0;
}
#else
#define jz4740_ac97_suspend NULL
#define jz4740_ac97_resume NULL
#endif
static int jz4740_ac97_probe(struct platform_device *pdev)
{
int ret;
return 0;
}
static void jz4740_ac97_remove(struct platform_device *pdev)
{
}
static int jz4740_ac97_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
cpu_dai->dma_data = &jz4740_ac97_pcm_stereo_out;
else
cpu_dai->dma_data = &jz4740_ac97_pcm_stereo_in;
return 0;
}
static int jz4740_ac97_hw_aux_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
cpu_dai->dma_data = &jz4740_ac97_pcm_aux_mono_out;
else
cpu_dai->dma_data = &jz4740_ac97_pcm_aux_mono_in;
return 0;
}
static int jz4740_ac97_hw_mic_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
return -ENODEV;
else
cpu_dai->dma_data = &jz4740_ac97_pcm_mic_mono_in;
return 0;
}
#define JZ4740_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000)
struct snd_soc_cpu_dai jz4740_ac97_dai[] = {
{
.name = "jz4740-ac97",
.id = 0,
.type = SND_SOC_DAI_AC97,
.probe = jz4740_ac97_probe,
.remove = jz4740_ac97_remove,
.suspend = jz4740_ac97_suspend,
.resume = jz4740_ac97_resume,
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 2,
.channels_max = 2,
.rates = JZ4740_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.capture = {
.stream_name = "AC97 Capture",
.channels_min = 2,
.channels_max = 2,
.rates = JZ4740_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.ops = {
.hw_params = jz4740_ac97_hw_params,},
},
{
.name = "jz4740-ac97-aux",
.id = 1,
.type = SND_SOC_DAI_AC97,
.playback = {
.stream_name = "AC97 Aux Playback",
.channels_min = 1,
.channels_max = 1,
.rates = JZ4740_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.capture = {
.stream_name = "AC97 Aux Capture",
.channels_min = 1,
.channels_max = 1,
.rates = JZ4740_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.ops = {
.hw_params = jz4740_ac97_hw_aux_params,},
},
{
.name = "jz4740-ac97-mic",
.id = 2,
.type = SND_SOC_DAI_AC97,
.capture = {
.stream_name = "AC97 Mic Capture",
.channels_min = 1,
.channels_max = 1,
.rates = JZ4740_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.ops = {
.hw_params = jz4740_ac97_hw_mic_params,},
},
};
EXPORT_SYMBOL_GPL(jz4740_ac97_dai);
EXPORT_SYMBOL_GPL(soc_ac97_ops);
MODULE_AUTHOR("Richard");
MODULE_DESCRIPTION("AC97 driver for the Ingenic jz4740 chip");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,21 @@
/*
* linux/sound/soc/jz4750/jz4750-ac97.h
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _JZ4750_AC97_H
#define _JZ4750_AC97_H
#define JZ4750_DAI_AC97_HIFI 0
#define JZ4750_DAI_AC97_AUX 1
#define JZ4750_DAI_AC97_MIC 2
extern struct snd_soc_cpu_dai jz4750_ac97_dai[3];
/* platform data */
extern struct snd_ac97_bus_ops jz4750_ac97_ops;
#endif

View File

@ -0,0 +1,311 @@
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include "jz4750-pcm.h"
#include "jz4750-i2s.h"
#include "../codecs/jzdlv.h"
static struct jz4750_dma_client jz4750_dma_client_out = {
.name = "I2S PCM Stereo out"
};
static struct jz4750_dma_client jz4750_dma_client_in = {
.name = "I2S PCM Stereo in"
};
static struct jz4750_pcm_dma_params jz4750_i2s_pcm_stereo_out = {
.client = &jz4750_dma_client_out,
.channel = DMA_ID_AIC_TX,
.dma_addr = AIC_DR,
.dma_size = 2,
};
static struct jz4750_pcm_dma_params jz4750_i2s_pcm_stereo_in = {
.client = &jz4750_dma_client_in,
.channel = DMA_ID_AIC_RX,
.dma_addr = AIC_DR,
.dma_size = 2,
};
static int jz4750_i2s_startup(struct snd_pcm_substream *substream)
{
/*struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;*/
return 0;
}
static int jz4750_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
/* 1 : ac97 , 0 : i2s */
break;
case SND_SOC_DAIFMT_LEFT_J:
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
/* 0 : slave */
break;
case SND_SOC_DAIFMT_CBM_CFS:
/* 1 : master */
break;
default:
break;
}
return 0;
}
/*
* Set Jz4750 Clock source
*/
static int jz4750_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int dir)
{
return 0;
}
static void jz4750_snd_tx_ctrl(int on)
{
if (on) {
/* enable replay */
__i2s_enable_transmit_dma();
__i2s_enable_replay();
__i2s_enable();
} else {
/* disable replay & capture */
__i2s_disable_replay();
__i2s_disable_record();
__i2s_disable_receive_dma();
__i2s_disable_transmit_dma();
__i2s_disable();
}
}
static void jz4750_snd_rx_ctrl(int on)
{
if (on) {
/* enable capture */
__i2s_enable_receive_dma();
__i2s_enable_record();
__i2s_enable();
} else {
/* disable replay & capture */
__i2s_disable_replay();
__i2s_disable_record();
__i2s_disable_receive_dma();
__i2s_disable_transmit_dma();
__i2s_disable();
}
}
static int jz4750_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
//struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
int channels = params_channels(params);
jz4750_snd_rx_ctrl(0);
jz4750_snd_rx_ctrl(0);
write_codec_file_bit(5, 0, 7);//PMR1.SB_DAC->0
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
//cpu_dai->dma_data = &jz4750_i2s_pcm_stereo_out;
rtd->dai->cpu_dai->dma_data = &jz4750_i2s_pcm_stereo_out;
if (channels == 1)
__aic_enable_mono2stereo();
else
__aic_disable_mono2stereo();
} else
rtd->dai->cpu_dai->dma_data = &jz4750_i2s_pcm_stereo_in;
#if 1
switch (channels) {
case 1:
write_codec_file_bit(1, 1, 6);//CR1.MONO->1 for Mono
break;
case 2:
write_codec_file_bit(1, 0, 6);//CR1.MONO->0 for Stereo
break;
}
#endif
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
__i2s_set_transmit_trigger(4);
__i2s_set_receive_trigger(3);
__i2s_set_oss_sample_size(8);
__i2s_set_iss_sample_size(8);
break;
case SNDRV_PCM_FORMAT_S16_LE:
/* playback sample:16 bits, burst:16 bytes */
__i2s_set_transmit_trigger(4);
/* capture sample:16 bits, burst:16 bytes */
__i2s_set_receive_trigger(3);
__i2s_set_oss_sample_size(16);
__i2s_set_iss_sample_size(16);
/* DAC path and ADC path */
write_codec_file(2, 0x00);
break;
}
return 0;
}
static int jz4750_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
{
int ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
jz4750_snd_rx_ctrl(1);
else
jz4750_snd_tx_ctrl(1);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
jz4750_snd_rx_ctrl(0);
else
jz4750_snd_tx_ctrl(0);
break;
default:
ret = -EINVAL;
}
return ret;
}
static void jz4750_i2s_shutdown(struct snd_pcm_substream *substream)
{
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
} else {
}
return;
}
static int jz4750_i2s_probe(struct platform_device *pdev)
{
__i2s_internal_codec();
__i2s_as_slave();
__i2s_select_i2s();
__aic_select_i2s();
mdelay(2);
__i2s_disable();
mdelay(2);
REG_AIC_I2SCR = 0x10;
__i2s_disable();
__i2s_internal_codec();
__i2s_as_slave();
__i2s_select_i2s();
__aic_select_i2s();
__i2s_set_oss_sample_size(16);
__i2s_set_iss_sample_size(16);
__aic_play_lastsample();
__i2s_disable_record();
__i2s_disable_replay();
__i2s_disable_loopback();
__i2s_set_transmit_trigger(7);
__i2s_set_receive_trigger(7);
jz4750_snd_tx_ctrl(0);
jz4750_snd_rx_ctrl(0);
return 0;
}
#ifdef CONFIG_PM
static int jz4750_i2s_suspend(struct platform_device *dev,
struct snd_soc_dai *dai)
{
if (!dai->active)
return 0;
return 0;
}
static int jz4750_i2s_resume(struct platform_device *pdev,
struct snd_soc_dai *dai)
{
if (!dai->active)
return 0;
return 0;
}
#else
#define jz4750_i2s_suspend NULL
#define jz4750_i2s_resume NULL
#endif
#define JZ4750_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
SNDRV_PCM_RATE_12000 | SNDRV_PCM_RATE_16000 |\
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_24000 |\
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
SNDRV_PCM_RATE_48000)
struct snd_soc_dai jz4750_i2s_dai = {
.name = "jz4750-i2s",
.id = 0,
.type = SND_SOC_DAI_I2S,
.probe = jz4750_i2s_probe,
.suspend = jz4750_i2s_suspend,
.resume = jz4750_i2s_resume,
.playback = {
.channels_min = 1,
.channels_max = 2,
.rates = JZ4750_I2S_RATES,
.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
.capture = {
.channels_min = 1,
.channels_max = 2,
.rates = JZ4750_I2S_RATES,
.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
.ops = {
.startup = jz4750_i2s_startup,
.shutdown = jz4750_i2s_shutdown,
.trigger = jz4750_i2s_trigger,
.hw_params = jz4750_i2s_hw_params,},
.dai_ops = {
.set_fmt = jz4750_i2s_set_dai_fmt,
.set_sysclk = jz4750_i2s_set_dai_sysclk,
},
};
EXPORT_SYMBOL_GPL(jz4750_i2s_dai);
/* Module information */
MODULE_AUTHOR("Richard, cjfeng@ingenic.cn, www.ingenic.cn");
MODULE_DESCRIPTION("jz4750 I2S SoC Interface");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,18 @@
/*
* 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 _JZ4750_I2S_H
#define _JZ4750_I2S_H
/* jz4750 DAI ID's */
#define JZ4750_DAI_I2S 0
/* I2S clock */
#define JZ4750_I2S_SYSCLK 0
extern struct snd_soc_dai jz4750_i2s_dai;
#endif

View File

@ -0,0 +1,687 @@
/*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <asm/io.h>
#include "jz4750-pcm.h"
static long sum_bytes = 0;
static int first_transfer = 0;
static int printk_flag = 0;
static int tran_bit = 0;
#ifdef CONFIG_SND_OSSEMUL
static int hw_params_cnt = 0;
#endif
struct jz4750_dma_buf_aic {
struct jz4750_dma_buf_aic *next;
int size; /* buffer size in bytes */
dma_addr_t data; /* start of DMA data */
dma_addr_t ptr; /* where the DMA got to [1] */
void *id; /* client's id */
};
struct jz4750_runtime_data {
spinlock_t lock;
int state;
int aic_dma_flag; /* start dma transfer or not */
unsigned int dma_loaded;
unsigned int dma_limit;
unsigned int dma_period;
dma_addr_t dma_start;
dma_addr_t dma_pos;
dma_addr_t dma_end;
struct jz4750_pcm_dma_params *params;
dma_addr_t user_cur_addr; /* user current write buffer start address */
unsigned int user_cur_len; /* user current write buffer length */
/* buffer list and information */
struct jz4750_dma_buf_aic *curr; /* current dma buffer */
struct jz4750_dma_buf_aic *next; /* next buffer to load */
struct jz4750_dma_buf_aic *end; /* end of queue */
};
/* identify hardware playback capabilities */
static const struct snd_pcm_hardware jz4750_pcm_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_U16_LE |
SNDRV_PCM_FMTBIT_U8 |
SNDRV_PCM_FMTBIT_S8,
.rates = SNDRV_PCM_RATE_8000_96000/*0x3fe*/,
.rate_min = 8000,
.rate_max = 96000,
.channels_min = 1,//2
.channels_max = 2,
.buffer_bytes_max = 128 * 1024,//16 * 1024
.period_bytes_min = PAGE_SIZE,
.period_bytes_max = PAGE_SIZE * 2,
.periods_min = 2,
.periods_max = 128,//16,
.fifo_size = 32,
};
/* jz4750__dma_buf_enqueue
*
* queue an given buffer for dma transfer.
*
* data the physical address of the buffer data
* size the size of the buffer in bytes
*
*/
static int jz4750_dma_buf_enqueue(struct jz4750_runtime_data *prtd, dma_addr_t data, int size)
{
struct jz4750_dma_buf_aic *aic_buf;
aic_buf = kzalloc(sizeof(struct jz4750_dma_buf_aic), GFP_KERNEL);
if (aic_buf == NULL) {
printk("aic buffer allocate failed,no memory!\n");
return -ENOMEM;
}
aic_buf->next = NULL;
aic_buf->data = aic_buf->ptr = data;
aic_buf->size = size;
if( prtd->curr == NULL) {
prtd->curr = aic_buf;
prtd->end = aic_buf;
prtd->next = NULL;
} else {
if (prtd->end == NULL)
printk("prtd->end is NULL\n");
prtd->end->next = aic_buf;
prtd->end = aic_buf;
}
/* if necessary, update the next buffer field */
if (prtd->next == NULL)
prtd->next = aic_buf;
return 0;
}
void audio_start_dma(struct jz4750_runtime_data *prtd, int mode)
{
unsigned long flags;
struct jz4750_dma_buf_aic *aic_buf;
int channel;
switch (mode) {
case DMA_MODE_WRITE:
/* free cur aic_buf */
if (first_transfer == 1) {
first_transfer = 0;
} else {
aic_buf = prtd->curr;
if (aic_buf != NULL) {
prtd->curr = aic_buf->next;
prtd->next = aic_buf->next;
aic_buf->next = NULL;
kfree(aic_buf);
aic_buf = NULL;
}
}
aic_buf = prtd->next;
channel = prtd->params->channel;
if (aic_buf) {
flags = claim_dma_lock();
disable_dma(channel);
jz_set_alsa_dma(channel, mode, tran_bit);
set_dma_addr(channel, aic_buf->data);
set_dma_count(channel, aic_buf->size);
enable_dma(channel);
release_dma_lock(flags);
prtd->aic_dma_flag |= AIC_START_DMA;
} else {
printk("next buffer is NULL for playback\n");
prtd->aic_dma_flag &= ~AIC_START_DMA;
return;
}
break;
case DMA_MODE_READ:
/* free cur aic_buf */
if (first_transfer == 1) {
first_transfer = 0;
} else {
aic_buf = prtd->curr;
if (aic_buf != NULL) {
prtd->curr = aic_buf->next;
prtd->next = aic_buf->next;
aic_buf->next = NULL;
kfree(aic_buf);
aic_buf = NULL;
}
}
aic_buf = prtd->next;
channel = prtd->params->channel;
if (aic_buf) {
flags = claim_dma_lock();
disable_dma(channel);
jz_set_alsa_dma(channel, mode, tran_bit);
set_dma_addr(channel, aic_buf->data);
set_dma_count(channel, aic_buf->size);
enable_dma(channel);
release_dma_lock(flags);
prtd->aic_dma_flag |= AIC_START_DMA;
} else {
printk("next buffer is NULL for capture\n");
prtd->aic_dma_flag &= ~AIC_START_DMA;
return;
}
break;
}
}
/*
* place a dma buffer onto the queue for the dma system to handle.
*/
static void jz4750_pcm_enqueue(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4750_runtime_data *prtd = runtime->private_data;
/*struct snd_dma_buffer *buf = &substream->dma_buffer;*/
dma_addr_t pos = prtd->dma_pos;
int ret;
while (prtd->dma_loaded < prtd->dma_limit) {
unsigned long len = prtd->dma_period;
if ((pos + len) > prtd->dma_end) {
len = prtd->dma_end - pos;
}
ret = jz4750_dma_buf_enqueue(prtd, pos, len);
if (ret == 0) {
prtd->dma_loaded++;
pos += prtd->dma_period;
if (pos >= prtd->dma_end)
pos = prtd->dma_start;
} else
break;
}
prtd->dma_pos = pos;
}
/*
* call the function:jz4750_pcm_dma_irq() after DMA has transfered the current buffer
*/
static irqreturn_t jz4750_pcm_dma_irq(int dma_ch, void *dev_id)
{
struct snd_pcm_substream *substream = dev_id;
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4750_runtime_data *prtd = runtime->private_data;
/*struct jz4750_dma_buf_aic *aic_buf = prtd->curr;*/
int channel = prtd->params->channel;
unsigned long flags;
disable_dma(channel);
prtd->aic_dma_flag &= ~AIC_START_DMA;
/* must clear TT bit in DCCSR to avoid interrupt again */
if (__dmac_channel_transmit_end_detected(channel)) {
__dmac_channel_clear_transmit_end(channel);
}
if (__dmac_channel_transmit_halt_detected(channel)) {
__dmac_channel_clear_transmit_halt(channel);
}
if (__dmac_channel_address_error_detected(channel)) {
__dmac_channel_clear_address_error(channel);
}
if (substream)
snd_pcm_period_elapsed(substream);
spin_lock(&prtd->lock);
prtd->dma_loaded--;
if (prtd->state & ST_RUNNING) {
jz4750_pcm_enqueue(substream);
}
spin_unlock(&prtd->lock);
local_irq_save(flags);
if (prtd->state & ST_RUNNING) {
if (prtd->dma_loaded) {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
audio_start_dma(prtd, DMA_MODE_WRITE);
else
audio_start_dma(prtd, DMA_MODE_READ);
}
}
local_irq_restore(flags);
return IRQ_HANDLED;
}
/* some parameter about DMA operation */
static int jz4750_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4750_runtime_data *prtd = runtime->private_data;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct jz4750_pcm_dma_params *dma = rtd->dai->cpu_dai->dma_data;
size_t totbytes = params_buffer_bytes(params);
int ret;
#ifdef CONFIG_SND_OSSEMUL
if (hw_params_cnt)
return 0;
else
hw_params_cnt++ ;
#endif
if (!dma)
return 0;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
tran_bit = 8;
break;
case SNDRV_PCM_FORMAT_S16_LE:
tran_bit = 16;
break;
}
/* prepare DMA */
prtd->params = dma;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
ret = jz_request_dma(DMA_ID_AIC_TX, prtd->params->client->name,
jz4750_pcm_dma_irq, IRQF_DISABLED, substream);
if (ret < 0)
return ret;
prtd->params->channel = ret;
} else {
ret = jz_request_dma(DMA_ID_AIC_RX, prtd->params->client->name,
jz4750_pcm_dma_irq, IRQF_DISABLED, substream);
if (ret < 0)
return ret;
prtd->params->channel = ret;
}
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
runtime->dma_bytes = totbytes;
spin_lock_irq(&prtd->lock);
prtd->dma_loaded = 0;
prtd->aic_dma_flag = 0;
prtd->dma_limit = runtime->hw.periods_min;
prtd->dma_period = params_period_bytes(params);
prtd->dma_start = runtime->dma_addr;
prtd->dma_pos = prtd->dma_start;
prtd->dma_end = prtd->dma_start + totbytes;
prtd->curr = NULL;
prtd->next = NULL;
prtd->end = NULL;
sum_bytes = 0;
first_transfer = 1;
printk_flag = 0;
__dmac_disable_descriptor(prtd->params->channel);
__dmac_channel_disable_irq(prtd->params->channel);
spin_unlock_irq(&prtd->lock);
return ret;
}
static int jz4750_pcm_hw_free(struct snd_pcm_substream *substream)
{
struct jz4750_runtime_data *prtd = substream->runtime->private_data;
snd_pcm_set_runtime_buffer(substream, NULL);
if (prtd->params) {
jz_free_dma(prtd->params->channel);
prtd->params = NULL;
}
return 0;
}
/* set some dma para for playback/capture */
static int jz4750_dma_ctrl(int channel)
{
disable_dma(channel);
/* must clear TT bit in DCCSR to avoid interrupt again */
if (__dmac_channel_transmit_end_detected(channel)) {
__dmac_channel_clear_transmit_end(channel);
}
if (__dmac_channel_transmit_halt_detected(channel)) {
__dmac_channel_clear_transmit_halt(channel);
}
if (__dmac_channel_address_error_detected(channel)) {
__dmac_channel_clear_address_error(channel);
}
return 0;
}
static int jz4750_pcm_prepare(struct snd_pcm_substream *substream)
{
struct jz4750_runtime_data *prtd = substream->runtime->private_data;
int ret = 0;
/* return if this is a bufferless transfer e.g */
if (!prtd->params)
return 0;
/* flush the DMA channel and DMA channel bit check */
jz4750_dma_ctrl(prtd->params->channel);
prtd->dma_loaded = 0;
prtd->dma_pos = prtd->dma_start;
/* enqueue dma buffers */
jz4750_pcm_enqueue(substream);
return ret;
}
static int jz4750_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4750_runtime_data *prtd = runtime->private_data;
int ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
prtd->state |= ST_RUNNING;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
audio_start_dma(prtd, DMA_MODE_WRITE);
} else {
audio_start_dma(prtd, DMA_MODE_READ);
}
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
prtd->state &= ~ST_RUNNING;
break;
case SNDRV_PCM_TRIGGER_RESUME:
printk(" RESUME \n");
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
printk(" RESTART \n");
break;
default:
ret = -EINVAL;
}
return ret;
}
static snd_pcm_uframes_t
jz4750_pcm_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4750_runtime_data *prtd = runtime->private_data;
struct jz4750_dma_buf_aic *aic_buf = prtd->curr;
long count,res;
dma_addr_t ptr;
snd_pcm_uframes_t x;
int channel = prtd->params->channel;
spin_lock(&prtd->lock);
#if 1
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
count = get_dma_residue(channel);
count = aic_buf->size - count;
ptr = aic_buf->data + count;
res = ptr - prtd->dma_start;
} else {
count = get_dma_residue(channel);
count = aic_buf->size - count;
ptr = aic_buf->data + count;
res = ptr - prtd->dma_start;
}
# else
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
if ((prtd->aic_dma_flag & AIC_START_DMA) == 0) {
count = get_dma_residue(channel);
count = aic_buf->size - count;
ptr = aic_buf->data + count;
REG_DMAC_DSAR(channel) = ptr;
res = ptr - prtd->dma_start;
} else {
ptr = REG_DMAC_DSAR(channel);
if (ptr == 0x0)
printk("\ndma address is 00000000 in running!\n");
res = ptr - prtd->dma_start;
}
} else {
if ((prtd->aic_dma_flag & AIC_START_DMA) == 0) {
count = get_dma_residue(channel);
count = aic_buf->size - count;
ptr = aic_buf->data + count;
REG_DMAC_DTAR(channel) = ptr;
res = ptr - prtd->dma_start;
} else {
ptr = REG_DMAC_DTAR(channel);
if (ptr == 0x0)
printk("\ndma address is 00000000 in running!\n");
res = ptr - prtd->dma_start;
}
}
#endif
spin_unlock(&prtd->lock);
x = bytes_to_frames(runtime, res);
if (x == runtime->buffer_size)
x = 0;
return x;
}
static int jz4750_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4750_runtime_data *prtd;
#ifdef CONFIG_SND_OSSEMUL
hw_params_cnt = 0;
#endif
REG_DMAC_DMACKE(0) = 0x3f;
REG_DMAC_DMACKE(1) = 0x3f;
snd_soc_set_runtime_hwparams(substream, &jz4750_pcm_hardware);
prtd = kzalloc(sizeof(struct jz4750_runtime_data), GFP_KERNEL);
if (prtd == NULL)
return -ENOMEM;
spin_lock_init(&prtd->lock);
runtime->private_data = prtd;
return 0;
}
static int jz4750_pcm_close(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4750_runtime_data *prtd = runtime->private_data;
struct jz4750_dma_buf_aic *aic_buf = NULL;
#ifdef CONFIG_SND_OSSEMUL
hw_params_cnt = 0;
#endif
if (prtd)
aic_buf = prtd->curr;
while (aic_buf != NULL) {
prtd->curr = aic_buf->next;
prtd->next = aic_buf->next;
aic_buf->next = NULL;
kfree(aic_buf);
aic_buf = NULL;
aic_buf = prtd->curr;
}
if (prtd) {
prtd->curr = NULL;
prtd->next = NULL;
prtd->end = NULL;
kfree(prtd);
}
return 0;
}
static int jz4750_pcm_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)//include/linux/mm.h
{
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned long start;
unsigned long off;
u32 len;
int ret = -ENXIO;
off = vma->vm_pgoff << PAGE_SHIFT;
start = runtime->dma_addr;
len = PAGE_ALIGN((start & ~PAGE_MASK) + runtime->dma_bytes);
start &= PAGE_MASK;
if ((vma->vm_end - vma->vm_start + off) > len) {
return -EINVAL;
}
off += start;
vma->vm_pgoff = off >> PAGE_SHIFT;
vma->vm_flags |= VM_IO;
#if defined(CONFIG_MIPS32)
pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK;
pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED;
/* pgprot_val(vma->vm_page_prot) |= _CACHE_CACHABLE_NONCOHERENT; */
#endif
ret = io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
vma->vm_end - vma->vm_start,
vma->vm_page_prot);
return ret;
}
struct snd_pcm_ops jz4750_pcm_ops = {
.open = jz4750_pcm_open,
.close = jz4750_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = jz4750_pcm_hw_params,
.hw_free = jz4750_pcm_hw_free,
.prepare = jz4750_pcm_prepare,
.trigger = jz4750_pcm_trigger,
.pointer = jz4750_pcm_pointer,
.mmap = jz4750_pcm_mmap,
};
static int jz4750_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
{
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
struct snd_dma_buffer *buf = &substream->dma_buffer;
size_t size = jz4750_pcm_hardware.buffer_bytes_max;
buf->dev.type = SNDRV_DMA_TYPE_DEV;
buf->dev.dev = pcm->card->dev;
buf->private_data = NULL;
/*buf->area = dma_alloc_coherent(pcm->card->dev, size,
&buf->addr, GFP_KERNEL);*/
buf->area = dma_alloc_noncoherent(pcm->card->dev, size,
&buf->addr, GFP_KERNEL);
if (!buf->area)
return -ENOMEM;
buf->bytes = size;
return 0;
}
static void jz4750_pcm_free_dma_buffers(struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
struct snd_dma_buffer *buf;
int stream;
for (stream = 0; stream < 2; stream++) {
substream = pcm->streams[stream].substream;
if (!substream)
continue;
buf = &substream->dma_buffer;
if (!buf->area)
continue;
dma_free_noncoherent(pcm->card->dev, buf->bytes,
buf->area, buf->addr);
buf->area = NULL;
}
}
static u64 jz4750_pcm_dmamask = DMA_32BIT_MASK;
int jz4750_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
{
int ret = 0;
if (!card->dev->dma_mask)
card->dev->dma_mask = &jz4750_pcm_dmamask;
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = DMA_32BIT_MASK;
if (dai->playback.channels_min) {
ret = jz4750_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_PLAYBACK);
if (ret)
goto out;
}
if (dai->capture.channels_min) {
ret = jz4750_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_CAPTURE);
if (ret)
goto out;
}
out:
return ret;
}
struct snd_soc_platform jz4750_soc_platform = {
.name = "jz4750-audio",
.pcm_ops = &jz4750_pcm_ops,
.pcm_new = jz4750_pcm_new,
.pcm_free = jz4750_pcm_free_dma_buffers,
};
EXPORT_SYMBOL_GPL(jz4750_soc_platform);
MODULE_AUTHOR("Richard");
MODULE_DESCRIPTION("Ingenic Jz4750 PCM DMA module");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,33 @@
/*
*
* 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 _JZ4750_PCM_H
#define _JZ4750_PCM_H
#include <asm/jzsoc.h>
#define ST_RUNNING (1<<0)
#define ST_OPENED (1<<1)
#define AIC_START_DMA (1<<0)
#define AIC_END_DMA (1<<1)
struct jz4750_dma_client {
char *name;
};
struct jz4750_pcm_dma_params {
struct jz4750_dma_client *client; /* stream identifier */
int channel; /* Channel ID */
dma_addr_t dma_addr;
int dma_size; /* Size of the DMA transfer */
};
/* platform data */
extern struct snd_soc_platform jz4750_soc_platform;
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,726 @@
From ac50b482e2278cdff65e67c8a31079bf9c5ae289 Mon Sep 17 00:00:00 2001
From: Lars-Peter Clausen <lars@metafoo.de>
Date: Wed, 19 Aug 2009 14:47:08 +0200
Subject: [PATCH] core
---
arch/mips/Kconfig | 48 +++++
arch/mips/Makefile | 18 ++
arch/mips/boot/Makefile | 23 ++-
arch/mips/include/asm/bootinfo.h | 6 +
arch/mips/include/asm/cpu.h | 13 ++-
arch/mips/include/asm/mach-generic/irq.h | 2 +-
arch/mips/include/asm/r4kcache.h | 231 ++++++++++++++++++++++
arch/mips/include/asm/suspend.h | 3 +
arch/mips/kernel/cpu-probe.c | 21 ++
arch/mips/kernel/signal.c | 6 +
arch/mips/kernel/unaligned.c | 5 +
arch/mips/mm/c-r4k.c | 30 +++
arch/mips/mm/cache.c | 2 +
arch/mips/mm/tlbex.c | 5 +
include/linux/suspend.h | 315 ------------------------------
15 files changed, 409 insertions(+), 319 deletions(-)
delete mode 100644 include/linux/suspend.h
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 3ca0fe1..e8f8027 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -671,6 +671,29 @@ source "arch/mips/cavium-octeon/Kconfig"
endmenu
+#####################################################
+# Ingenic SOC series
+#####################################################
+
+config SOC_JZ4720
+ bool
+ select JZSOC
+
+config SOC_JZ4740
+ bool
+ select JZSOC
+
+config JZSOC
+ bool
+ select JZRISC
+ select SYS_HAS_CPU_MIPS32_R1
+ select SYS_SUPPORTS_32BIT_KERNEL
+
+config JZRISC
+ bool
+
+####################################################
+
config RWSEM_GENERIC_SPINLOCK
bool
default y
@@ -1893,6 +1916,14 @@ config NR_CPUS
source "kernel/time/Kconfig"
+# the value of (max order + 1)
+config FORCE_MAX_ZONEORDER
+ prompt "MAX_ZONEORDER"
+ int
+ default "12"
+ help
+ The max memory that can be allocated = 4KB * 2^(CONFIG_FORCE_MAX_ZONEORDER - 1)
+
#
# Timer Interrupt Frequency Configuration
#
@@ -2164,6 +2195,23 @@ config BINFMT_ELF32
endmenu
+menu "CPU Frequency scaling"
+
+config CPU_FREQ_JZ
+ tristate "CPUfreq driver for JZ CPUs"
+ depends on JZSOC
+ default n
+ help
+ This enables the CPUfreq driver for JZ CPUs.
+
+ If in doubt, say N.
+
+if (CPU_FREQ_JZ)
+source "drivers/cpufreq/Kconfig"
+endif
+
+endmenu
+
menu "Power management options"
config ARCH_HIBERNATION_POSSIBLE
diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index 861da51..14a6f9a 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -180,6 +180,14 @@ cflags-$(CONFIG_AR7) += -I$(srctree)/arch/mips/include/asm/mach-ar7
load-$(CONFIG_AR7) += 0xffffffff94100000
#
+# Commond Ingenic JZ4740 series
+#
+
+core-$(CONFIG_SOC_JZ4740) += arch/mips/jz4740/
+cflags-$(CONFIG_SOC_JZ4740) += -I$(srctree)/arch/mips/include/asm/mach-jz4740
+load-$(CONFIG_SOC_JZ4740) += 0xffffffff80010000
+
+#
# Acer PICA 61, Mips Magnum 4000 and Olivetti M700.
#
core-$(CONFIG_MACH_JAZZ) += arch/mips/jazz/
@@ -711,6 +719,12 @@ makeboot =$(Q)$(MAKE) $(build)=arch/mips/boot VMLINUX=$(vmlinux-32) $(1)
all: $(all-y)
+uImage: $(vmlinux-32)
+ +@$(call makeboot,$@)
+
+zImage: $(vmlinux-32)
+ +@$(call makeboot,$@)
+
vmlinux.bin: $(vmlinux-32)
+@$(call makeboot,$@)
@@ -740,6 +754,7 @@ install:
archclean:
@$(MAKE) $(clean)=arch/mips/boot
+ @$(MAKE) $(clean)=arch/mips/boot/compressed
@$(MAKE) $(clean)=arch/mips/lasat
define archhelp
@@ -747,6 +762,9 @@ define archhelp
echo ' vmlinux.ecoff - ECOFF boot image'
echo ' vmlinux.bin - Raw binary boot image'
echo ' vmlinux.srec - SREC boot image'
+ echo ' uImage - u-boot format image (arch/$(ARCH)/boot/uImage)'
+ echo ' zImage - Compressed binary image (arch/$(ARCH)/boot/compressed/zImage)'
+ echo ' vmlinux.bin - Uncompressed binary image (arch/$(ARCH)/boot/vmlinux.bin)'
echo
echo ' These will be default as apropriate for a configured platform.'
endef
diff --git a/arch/mips/boot/Makefile b/arch/mips/boot/Makefile
index 2a209d7..1cfce3e 100644
--- a/arch/mips/boot/Makefile
+++ b/arch/mips/boot/Makefile
@@ -7,6 +7,9 @@
# Copyright (C) 2004 Maciej W. Rozycki
#
+# This one must match the LOADADDR in arch/mips/Makefile!
+LOADADDR=0x80010000
+
#
# Some DECstations need all possible sections of an ECOFF executable
#
@@ -25,7 +28,7 @@ strip-flags = $(addprefix --remove-section=,$(drop-sections))
VMLINUX = vmlinux
-all: vmlinux.ecoff vmlinux.srec addinitrd
+all: vmlinux.ecoff vmlinux.srec addinitrd uImage zImage
vmlinux.ecoff: $(obj)/elf2ecoff $(VMLINUX)
$(obj)/elf2ecoff $(VMLINUX) vmlinux.ecoff $(E2EFLAGS)
@@ -42,8 +45,24 @@ vmlinux.srec: $(VMLINUX)
$(obj)/addinitrd: $(obj)/addinitrd.c
$(HOSTCC) -o $@ $^
+uImage: $(VMLINUX) vmlinux.bin
+ rm -f $(obj)/vmlinux.bin.gz
+ gzip -9 $(obj)/vmlinux.bin
+ mkimage -A mips -O linux -T kernel -C gzip \
+ -a $(LOADADDR) -e $(shell sh ./$(obj)/tools/entry $(NM) $(VMLINUX) ) \
+ -n 'Linux-$(KERNELRELEASE)' \
+ -d $(obj)/vmlinux.bin.gz $(obj)/uImage
+ @echo ' Kernel: arch/mips/boot/$@ is ready'
+
+zImage:
+ $(Q)$(MAKE) $(build)=$(obj)/compressed loadaddr=$(LOADADDR) $@
+ @echo ' Kernel: arch/mips/boot/compressed/$@ is ready'
+
clean-files += addinitrd \
elf2ecoff \
vmlinux.bin \
vmlinux.ecoff \
- vmlinux.srec
+ vmlinux.srec \
+ vmlinux.bin.gz \
+ uImage \
+ zImage
diff --git a/arch/mips/include/asm/bootinfo.h b/arch/mips/include/asm/bootinfo.h
index 610fe3a..8451d28 100644
--- a/arch/mips/include/asm/bootinfo.h
+++ b/arch/mips/include/asm/bootinfo.h
@@ -57,6 +57,12 @@
#define MACH_MIKROTIK_RB532 0 /* Mikrotik RouterBoard 532 */
#define MACH_MIKROTIK_RB532A 1 /* Mikrotik RouterBoard 532A */
+/*
+ * Valid machtype for group INGENIC
+ */
+#define MACH_INGENIC_JZ4720 0 /* JZ4730 SOC */
+#define MACH_INGENIC_JZ4740 1 /* JZ4740 SOC */
+
#define CL_SIZE COMMAND_LINE_SIZE
extern char *system_type;
diff --git a/arch/mips/include/asm/cpu.h b/arch/mips/include/asm/cpu.h
index 3bdc0e3..904c574 100644
--- a/arch/mips/include/asm/cpu.h
+++ b/arch/mips/include/asm/cpu.h
@@ -34,7 +34,7 @@
#define PRID_COMP_LSI 0x080000
#define PRID_COMP_LEXRA 0x0b0000
#define PRID_COMP_CAVIUM 0x0d0000
-
+#define PRID_COMP_INGENIC 0xd00000
/*
* Assigned values for the product ID register. In order to detect a
@@ -127,6 +127,12 @@
#define PRID_IMP_CAVIUM_CN52XX 0x0700
/*
+ * These are the PRID's for when 23:16 == PRID_COMP_INGENIC
+ */
+
+#define PRID_IMP_JZRISC 0x0200
+
+/*
* Definitions for 7:0 on legacy processors
*/
@@ -217,6 +223,11 @@ enum cpu_type_enum {
CPU_5KC, CPU_20KC, CPU_25KF, CPU_SB1, CPU_SB1A, CPU_LOONGSON2,
CPU_CAVIUM_OCTEON,
+ /*
+ * Ingenic class processors
+ */
+ CPU_JZRISC, CPU_XBURST,
+
CPU_LAST
};
diff --git a/arch/mips/include/asm/mach-generic/irq.h b/arch/mips/include/asm/mach-generic/irq.h
index 70d9a25..73b7a83 100644
--- a/arch/mips/include/asm/mach-generic/irq.h
+++ b/arch/mips/include/asm/mach-generic/irq.h
@@ -9,7 +9,7 @@
#define __ASM_MACH_GENERIC_IRQ_H
#ifndef NR_IRQS
-#define NR_IRQS 128
+#define NR_IRQS 256
#endif
#ifdef CONFIG_I8259
diff --git a/arch/mips/include/asm/r4kcache.h b/arch/mips/include/asm/r4kcache.h
index 387bf59..b500056 100644
--- a/arch/mips/include/asm/r4kcache.h
+++ b/arch/mips/include/asm/r4kcache.h
@@ -17,6 +17,58 @@
#include <asm/cpu-features.h>
#include <asm/mipsmtregs.h>
+#ifdef CONFIG_JZRISC
+
+#define K0_TO_K1() \
+do { \
+ unsigned long __k0_addr; \
+ \
+ __asm__ __volatile__( \
+ "la %0, 1f\n\t" \
+ "or %0, %0, %1\n\t" \
+ "jr %0\n\t" \
+ "nop\n\t" \
+ "1: nop\n" \
+ : "=&r"(__k0_addr) \
+ : "r" (0x20000000) ); \
+} while(0)
+
+#define K1_TO_K0() \
+do { \
+ unsigned long __k0_addr; \
+ __asm__ __volatile__( \
+ "nop;nop;nop;nop;nop;nop;nop\n\t" \
+ "la %0, 1f\n\t" \
+ "jr %0\n\t" \
+ "nop\n\t" \
+ "1: nop\n" \
+ : "=&r" (__k0_addr)); \
+} while (0)
+
+#define INVALIDATE_BTB() \
+do { \
+ unsigned long tmp; \
+ __asm__ __volatile__( \
+ ".set mips32\n\t" \
+ "mfc0 %0, $16, 7\n\t" \
+ "nop\n\t" \
+ "ori %0, 2\n\t" \
+ "mtc0 %0, $16, 7\n\t" \
+ "nop\n\t" \
+ : "=&r" (tmp)); \
+} while (0)
+
+#define SYNC_WB() __asm__ __volatile__ ("sync")
+
+#else /* CONFIG_JZRISC */
+
+#define K0_TO_K1() do { } while (0)
+#define K1_TO_K0() do { } while (0)
+#define INVALIDATE_BTB() do { } while (0)
+#define SYNC_WB() do { } while (0)
+
+#endif /* CONFIG_JZRISC */
+
/*
* This macro return a properly sign-extended address suitable as base address
* for indexed cache operations. Two issues here:
@@ -144,6 +196,7 @@ static inline void flush_icache_line_indexed(unsigned long addr)
{
__iflush_prologue
cache_op(Index_Invalidate_I, addr);
+ INVALIDATE_BTB();
__iflush_epilogue
}
@@ -151,6 +204,7 @@ static inline void flush_dcache_line_indexed(unsigned long addr)
{
__dflush_prologue
cache_op(Index_Writeback_Inv_D, addr);
+ SYNC_WB();
__dflush_epilogue
}
@@ -163,6 +217,7 @@ static inline void flush_icache_line(unsigned long addr)
{
__iflush_prologue
cache_op(Hit_Invalidate_I, addr);
+ INVALIDATE_BTB();
__iflush_epilogue
}
@@ -170,6 +225,7 @@ static inline void flush_dcache_line(unsigned long addr)
{
__dflush_prologue
cache_op(Hit_Writeback_Inv_D, addr);
+ SYNC_WB();
__dflush_epilogue
}
@@ -177,6 +233,7 @@ static inline void invalidate_dcache_line(unsigned long addr)
{
__dflush_prologue
cache_op(Hit_Invalidate_D, addr);
+ SYNC_WB();
__dflush_epilogue
}
@@ -209,6 +266,7 @@ static inline void flush_scache_line(unsigned long addr)
static inline void protected_flush_icache_line(unsigned long addr)
{
protected_cache_op(Hit_Invalidate_I, addr);
+ INVALIDATE_BTB();
}
/*
@@ -220,6 +278,7 @@ static inline void protected_flush_icache_line(unsigned long addr)
static inline void protected_writeback_dcache_line(unsigned long addr)
{
protected_cache_op(Hit_Writeback_Inv_D, addr);
+ SYNC_WB();
}
static inline void protected_writeback_scache_line(unsigned long addr)
@@ -396,8 +455,10 @@ static inline void blast_##pfx##cache##lsize##_page_indexed(unsigned long page)
__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 16)
__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 16)
__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 16)
+#ifndef CONFIG_JZRISC
__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 32)
__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 32)
+#endif
__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 32)
__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 64)
__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 64)
@@ -405,12 +466,122 @@ __BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 64)
__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 128)
__BUILD_BLAST_CACHE(inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 16)
+#ifndef CONFIG_JZRISC
__BUILD_BLAST_CACHE(inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 32)
+#endif
__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 16)
__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 32)
__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 64)
__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 128)
+#ifdef CONFIG_JZRISC
+
+static inline void blast_dcache32(void)
+{
+ unsigned long start = INDEX_BASE;
+ unsigned long end = start + current_cpu_data.dcache.waysize;
+ unsigned long ws_inc = 1UL << current_cpu_data.dcache.waybit;
+ unsigned long ws_end = current_cpu_data.dcache.ways <<
+ current_cpu_data.dcache.waybit;
+ unsigned long ws, addr;
+
+ for (ws = 0; ws < ws_end; ws += ws_inc)
+ for (addr = start; addr < end; addr += 0x400)
+ cache32_unroll32(addr|ws,Index_Writeback_Inv_D);
+
+ SYNC_WB();
+}
+
+static inline void blast_dcache32_page(unsigned long page)
+{
+ unsigned long start = page;
+ unsigned long end = page + PAGE_SIZE;
+
+ do {
+ cache32_unroll32(start,Hit_Writeback_Inv_D);
+ start += 0x400;
+ } while (start < end);
+
+ SYNC_WB();
+}
+
+static inline void blast_dcache32_page_indexed(unsigned long page)
+{
+ unsigned long indexmask = current_cpu_data.dcache.waysize - 1;
+ unsigned long start = INDEX_BASE + (page & indexmask);
+ unsigned long end = start + PAGE_SIZE;
+ unsigned long ws_inc = 1UL << current_cpu_data.dcache.waybit;
+ unsigned long ws_end = current_cpu_data.dcache.ways <<
+ current_cpu_data.dcache.waybit;
+ unsigned long ws, addr;
+
+ for (ws = 0; ws < ws_end; ws += ws_inc)
+ for (addr = start; addr < end; addr += 0x400)
+ cache32_unroll32(addr|ws,Index_Writeback_Inv_D);
+
+ SYNC_WB();
+}
+
+static inline void blast_icache32(void)
+{
+ unsigned long start = INDEX_BASE;
+ unsigned long end = start + current_cpu_data.icache.waysize;
+ unsigned long ws_inc = 1UL << current_cpu_data.icache.waybit;
+ unsigned long ws_end = current_cpu_data.icache.ways <<
+ current_cpu_data.icache.waybit;
+ unsigned long ws, addr;
+
+ K0_TO_K1();
+
+ for (ws = 0; ws < ws_end; ws += ws_inc)
+ for (addr = start; addr < end; addr += 0x400)
+ cache32_unroll32(addr|ws,Index_Invalidate_I);
+
+ INVALIDATE_BTB();
+
+ K1_TO_K0();
+}
+
+static inline void blast_icache32_page(unsigned long page)
+{
+ unsigned long start = page;
+ unsigned long end = page + PAGE_SIZE;
+
+ K0_TO_K1();
+
+ do {
+ cache32_unroll32(start,Hit_Invalidate_I);
+ start += 0x400;
+ } while (start < end);
+
+ INVALIDATE_BTB();
+
+ K1_TO_K0();
+}
+
+static inline void blast_icache32_page_indexed(unsigned long page)
+{
+ unsigned long indexmask = current_cpu_data.icache.waysize - 1;
+ unsigned long start = INDEX_BASE + (page & indexmask);
+ unsigned long end = start + PAGE_SIZE;
+ unsigned long ws_inc = 1UL << current_cpu_data.icache.waybit;
+ unsigned long ws_end = current_cpu_data.icache.ways <<
+ current_cpu_data.icache.waybit;
+ unsigned long ws, addr;
+
+ K0_TO_K1();
+
+ for (ws = 0; ws < ws_end; ws += ws_inc)
+ for (addr = start; addr < end; addr += 0x400)
+ cache32_unroll32(addr|ws,Index_Invalidate_I);
+
+ INVALIDATE_BTB();
+
+ K1_TO_K0();
+}
+
+#endif /* CONFIG_JZRISC */
+
/* build blast_xxx_range, protected_blast_xxx_range */
#define __BUILD_BLAST_CACHE_RANGE(pfx, desc, hitop, prot) \
static inline void prot##blast_##pfx##cache##_range(unsigned long start, \
@@ -432,13 +603,73 @@ static inline void prot##blast_##pfx##cache##_range(unsigned long start, \
__##pfx##flush_epilogue \
}
+#ifndef CONFIG_JZRISC
__BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D, protected_)
+#endif
__BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD, protected_)
+#ifndef CONFIG_JZRISC
__BUILD_BLAST_CACHE_RANGE(i, icache, Hit_Invalidate_I, protected_)
__BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D, )
+#endif
__BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD, )
/* blast_inv_dcache_range */
__BUILD_BLAST_CACHE_RANGE(inv_d, dcache, Hit_Invalidate_D, )
__BUILD_BLAST_CACHE_RANGE(inv_s, scache, Hit_Invalidate_SD, )
+#ifdef CONFIG_JZRISC
+
+static inline void protected_blast_dcache_range(unsigned long start,
+ unsigned long end)
+{
+ unsigned long lsize = cpu_dcache_line_size();
+ unsigned long addr = start & ~(lsize - 1);
+ unsigned long aend = (end - 1) & ~(lsize - 1);
+
+ while (1) {
+ protected_cache_op(Hit_Writeback_Inv_D, addr);
+ if (addr == aend)
+ break;
+ addr += lsize;
+ }
+ SYNC_WB();
+}
+
+static inline void protected_blast_icache_range(unsigned long start,
+ unsigned long end)
+{
+ unsigned long lsize = cpu_icache_line_size();
+ unsigned long addr = start & ~(lsize - 1);
+ unsigned long aend = (end - 1) & ~(lsize - 1);
+
+ K0_TO_K1();
+
+ while (1) {
+ protected_cache_op(Hit_Invalidate_I, addr);
+ if (addr == aend)
+ break;
+ addr += lsize;
+ }
+ INVALIDATE_BTB();
+
+ K1_TO_K0();
+}
+
+static inline void blast_dcache_range(unsigned long start,
+ unsigned long end)
+{
+ unsigned long lsize = cpu_dcache_line_size();
+ unsigned long addr = start & ~(lsize - 1);
+ unsigned long aend = (end - 1) & ~(lsize - 1);
+
+ while (1) {
+ cache_op(Hit_Writeback_Inv_D, addr);
+ if (addr == aend)
+ break;
+ addr += lsize;
+ }
+ SYNC_WB();
+}
+
+#endif /* CONFIG_JZRISC */
+
#endif /* _ASM_R4KCACHE_H */
diff --git a/arch/mips/include/asm/suspend.h b/arch/mips/include/asm/suspend.h
index 294cdb6..94dc597 100644
--- a/arch/mips/include/asm/suspend.h
+++ b/arch/mips/include/asm/suspend.h
@@ -2,6 +2,9 @@
#define __ASM_SUSPEND_H
static inline int arch_prepare_suspend(void) { return 0; }
+#if defined(CONFIG_PM) && defined(CONFIG_JZSOC)
+extern int jz_pm_init(void);
+#endif
/* References to section boundaries */
extern const void __nosave_begin, __nosave_end;
diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c
index 1abe990..4535ae7 100644
--- a/arch/mips/kernel/cpu-probe.c
+++ b/arch/mips/kernel/cpu-probe.c
@@ -160,6 +160,7 @@ void __init check_wait(void)
case CPU_PR4450:
case CPU_BCM3302:
case CPU_CAVIUM_OCTEON:
+ case CPU_JZRISC:
cpu_wait = r4k_wait;
break;
@@ -888,6 +889,23 @@ static inline void cpu_probe_cavium(struct cpuinfo_mips *c, unsigned int cpu)
}
}
+static inline void cpu_probe_ingenic(struct cpuinfo_mips *c, unsigned int cpu)
+{
+ decode_configs(c);
+ c->options &= ~MIPS_CPU_COUNTER; /* JZRISC does not implement the CP0 counter. */
+ switch (c->processor_id & 0xff00) {
+ case PRID_IMP_JZRISC:
+ c->cputype = CPU_JZRISC;
+ c->isa_level = MIPS_CPU_ISA_M32R1;
+ c->tlbsize = 32;
+ __cpu_name[cpu] = "Ingenic JZRISC";
+ break;
+ default:
+ panic("Unknown Ingenic Processor ID!");
+ break;
+ }
+}
+
const char *__cpu_name[NR_CPUS];
__cpuinit void cpu_probe(void)
@@ -925,6 +943,9 @@ __cpuinit void cpu_probe(void)
case PRID_COMP_CAVIUM:
cpu_probe_cavium(c, cpu);
break;
+ case PRID_COMP_INGENIC:
+ cpu_probe_ingenic(c, cpu);
+ break;
}
BUG_ON(!__cpu_name[cpu]);
diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c
index 67bd626..e26ebe0 100644
diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c
index 6721ee2..dd4b70b 100644
--- a/arch/mips/mm/c-r4k.c
+++ b/arch/mips/mm/c-r4k.c
@@ -928,6 +928,36 @@ static void __cpuinit probe_pcache(void)
c->dcache.waybit = 0;
break;
+ case CPU_JZRISC:
+ config1 = read_c0_config1();
+ config1 = (config1 >> 22) & 0x07;
+ if (config1 == 0x07)
+ config1 = 10;
+ else
+ config1 = config1 + 11;
+ config1 += 2;
+ icache_size = (1 << config1);
+ c->icache.linesz = 32;
+ c->icache.ways = 4;
+ c->icache.waybit = __ffs(icache_size / c->icache.ways);
+
+ config1 = read_c0_config1();
+ config1 = (config1 >> 13) & 0x07;
+ if (config1 == 0x07)
+ config1 = 10;
+ else
+ config1 = config1 + 11;
+ config1 += 2;
+ dcache_size = (1 << config1);
+ c->dcache.linesz = 32;
+ c->dcache.ways = 4;
+ c->dcache.waybit = __ffs(dcache_size / c->dcache.ways);
+
+ c->dcache.flags = 0;
+ c->options |= MIPS_CPU_PREFETCH;
+
+ break;
+
default:
if (!(config & MIPS_CONF_M))
panic("Don't know how to probe P-caches on this cpu.");
diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c
index 694d51f..4b2bc95 100644
--- a/arch/mips/mm/cache.c
+++ b/arch/mips/mm/cache.c
@@ -52,6 +52,8 @@ void (*_dma_cache_wback)(unsigned long start, unsigned long size);
void (*_dma_cache_inv)(unsigned long start, unsigned long size);
EXPORT_SYMBOL(_dma_cache_wback_inv);
+EXPORT_SYMBOL(_dma_cache_wback);
+EXPORT_SYMBOL(_dma_cache_inv);
#endif /* CONFIG_DMA_NONCOHERENT */
diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
index 9a17bf8..9b80053 100644
--- a/arch/mips/mm/tlbex.c
+++ b/arch/mips/mm/tlbex.c
@@ -385,6 +385,11 @@ static void __cpuinit build_tlb_write_entry(u32 **p, struct uasm_label **l,
tlbw(p);
break;
+ case CPU_JZRISC:
+ tlbw(p);
+ uasm_i_nop(p);
+ break;
+
default:
panic("No TLB refill handler yet (CPU type: %d)",
current_cpu_data.cputype);
--
1.5.6.5

View File

@ -0,0 +1,156 @@
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -420,4 +420,10 @@ config MTD_NAND_SH_FLCTL
Several Renesas SuperH CPU has FLCTL. This option enables support
for NAND Flash using FLCTL. This driver support SH7723.
+config MTD_NAND_JZ4740
+ tristate "Support NAND Flash device on Jz4740 board"
+ depends on SOC_JZ4740
+ help
+ Support NAND Flash device on Jz4740 board
+
endif # MTD_NAND
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -40,5 +40,6 @@ obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_
obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o
obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o
obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
+obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
nand-objs := nand_base.o nand_bbt.o
diff --git a/arch/mips/jz4740/platform.c b/arch/mips/jz4740/platform.c
index 4ce8e3a..ee5c90d 100644
--- a/arch/mips/jz4740/platform.c
+++ b/arch/mips/jz4740/platform.c
@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/resource.h>
+#include <linux/mtd/jz4740_nand.h>
#include <asm/jzsoc.h>
@@ -152,12 +153,70 @@ static struct platform_device jz_i2c_device = {
.resource = jz_i2c_resources,
};
+static struct resource jz_nand_resources[] = {
+ [0] = {
+ .start = CPHYSADDR(EMC_BASE),
+ .end = CPHYSADDR(EMC_BASE) + 0x10000 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct nand_ecclayout qi_lb60_ecclayout = {
+ .eccbytes = 36,
+ .eccpos = {
+ 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37,
+ 38, 39, 40, 41},
+ .oobfree = {
+ {.offset = 0,
+ .length = 6},
+ {.offset = 42,
+ .length = 22}}
+};
+
+static struct mtd_partition qi_lb60_partitions[] = {
+ { .name = "NAND BOOT partition",
+ .offset = 0 * 0x100000,
+ .size = 4 * 0x100000,
+ },
+ { .name = "NAND KERNEL partition",
+ .offset = 4 * 0x100000,
+ .size = 4 * 0x100000,
+ },
+ { .name = "NAND ROOTFS partition",
+ .offset = 8 * 0x100000,
+ .size = 20 * 0x100000,
+ },
+ { .name = "NAND DATA partition",
+ .offset = 100 * 0x100000,
+ .size = 20 * 0x100000,
+ },
+};
+
+static struct jz_nand_platform_data jz_nand_platform_data = {
+ .num_partitions = ARRAY_SIZE(qi_lb60_partitions),
+ .partitions = qi_lb60_partitions,
+ .ecc_layout = &qi_lb60_ecclayout,
+};
+
+static struct platform_device jz_nand_device = {
+ .name = "jz4740-nand",
+ .num_resources = ARRAY_SIZE(jz_nand_resources),
+ .resource = jz_nand_resources,
+ .dev = {
+ .platform_data = &jz_nand_platform_data,
+ }
+};
+
/* All */
static struct platform_device *jz_platform_devices[] __initdata = {
&jz_usb_ohci_device,
&jz_lcd_device,
&jz_usb_gdt_device,
&jz_mmc_device,
+ &jz_nand_device,
&jz_i2c_device,
};
diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c
index 92f0afe..08a7b37 100644
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 0a9c9cd..3870dcc 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -896,29 +896,22 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *ecc_calc = chip->buffers->ecccalc;
uint8_t *ecc_code = chip->buffers->ecccode;
uint32_t *eccpos = chip->ecc.layout->eccpos;
-
- for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- chip->ecc.hwctl(mtd, NAND_ECC_READ);
- chip->read_buf(mtd, p, eccsize);
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
- }
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ int stat;
for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
- eccsteps = chip->ecc.steps;
- p = buf;
-
- for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- int stat;
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ chip->ecc.hwctl(mtd, NAND_ECC_READ);
+ chip->read_buf(mtd, p, eccsize);
stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
if (stat < 0)
mtd->ecc_stats.failed++;
else
mtd->ecc_stats.corrected += stat;
}
+
return 0;
}
@@ -1068,6 +1061,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
bufpoi = aligned ? buf : chip->buffers->databuf;
if (likely(sndcmd)) {
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0x00, page);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
sndcmd = 0;
}

View File

@ -0,0 +1,36 @@
From e8e05da0df412b041b9bb7d7d0dc30931a3e1344 Mon Sep 17 00:00:00 2001
From: Lars-Peter Clausen <lars@metafoo.de>
Date: Wed, 19 Aug 2009 14:47:33 +0200
Subject: [PATCH] battery
---
drivers/power/Kconfig | 5 +++++
drivers/power/Makefile | 1 +
2 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index bdbc4f7..3942136 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -103,4 +103,9 @@ config CHARGER_PCF50633
help
Say Y to include support for NXP PCF50633 Main Battery Charger.
+config BATTERY_JZ
+ tristate "JZ battery"
+ help
+ Say Y to enable support for the battery in JZ SOC.
+
endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 380d17c..4eebbf5 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -28,3 +28,4 @@ obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
+obj-$(CONFIG_BATTERY_JZ) += jz_battery.o
--
1.5.6.5

View File

@ -0,0 +1,45 @@
From 18cd35fce85884d15b1c600dd6e132ed25d6954b Mon Sep 17 00:00:00 2001
From: Lars-Peter Clausen <lars@metafoo.de>
Date: Wed, 19 Aug 2009 14:47:20 +0200
Subject: [PATCH] mmc
---
drivers/mmc/host/Kconfig | 9 +++++++++
drivers/mmc/host/Makefile | 1 +
2 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 891ef18..11e35bd 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -66,6 +66,15 @@ config MMC_RICOH_MMC
useless. It is safe to select this driver even if you don't
have a Ricoh based card reader.
+config MMC_JZ
+ tristate "JZ SD/Multimedia Card Interface support"
+ depends on SOC_JZ4720 || SOC_JZ4740
+ help
+ This selects the Ingenic JZ4720/JZ4740 SD/Multimedia card Interface.
+ If you have abIngenic platform with a Multimedia Card slot,
+ say Y or M here.
+
+ If unsure, say N.
To compile this driver as a module, choose M here:
the module will be called ricoh_mmc.
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index cf153f6..5396cd6 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -6,6 +6,7 @@ ifeq ($(CONFIG_MMC_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
endif
+obj-$(CONFIG_MMC_JZ) += jz_mmc.o
obj-$(CONFIG_MMC_ARMMMCI) += mmci.o
obj-$(CONFIG_MMC_PXA) += pxamci.o
obj-$(CONFIG_MMC_IMX) += imxmmc.o
--
1.5.6.5

View File

@ -0,0 +1,151 @@
From 2623194f73c9d272245263c8a54c7b1a63766e8e Mon Sep 17 00:00:00 2001
From: Lars-Peter Clausen <lars@metafoo.de>
Date: Wed, 19 Aug 2009 14:49:48 +0200
Subject: [PATCH] video
---
drivers/video/Kconfig | 113 ++++++++++++++++++++++++++++++++++++++++++++++++
drivers/video/Makefile | 3 +
2 files changed, 116 insertions(+), 0 deletions(-)
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 3b54b39..3d5d623 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -237,6 +237,119 @@ config FB_TILEBLITTING
comment "Frame buffer hardware drivers"
depends on FB
+/************************************************************/
+config FB_JZSOC
+ tristate "JZSOC LCD controller support"
+ depends on FB && JZSOC
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ ---help---
+ JZSOC LCD Controller and Smart LCD Controller driver support.
+
+config FB_JZ4740_SLCD
+ tristate "JZ4740 Smart LCD controller support"
+ depends on FB_JZSOC && SOC_JZ4740
+ default n
+ ---help---
+ This is the frame buffer device driver for the JZ4740 Smart LCD controller.
+ If select this, please set <JZ4740 LCD controller support> to <n>.
+
+choice
+ depends on FB_JZ4740_SLCD
+ prompt "SLCD Panel"
+ default JZ_SLCD_LGDP4551_8BUS
+
+config JZ_SLCD_LGDP4551
+ bool "LG LGDP4551 Smart LCD panel"
+ ---help---
+ Driver for Smart LCD LGDP4551, 8-bit sytem interface, 16BPP.
+
+config JZ_SLCD_SPFD5420A
+ bool "SPFD5420A Smart LCD panel"
+ ---help---
+ Driver for Smart LCD SPFD5420A 18-bit sytem interface, 18BPP.
+
+config JZ_SLCD_TRULY
+ bool "TRULY Smart LCD panel (MAX Pixels 400x240)"
+ ---help---
+
+endchoice
+
+config FB_JZLCD_4730_4740
+ tristate "JZ4730 JZ4740 LCD controller support"
+ depends on FB_JZSOC && (SOC_JZ4730 || SOC_JZ4740)
+ help
+ This is the frame buffer device driver for the JZ4730 and JZ4740 LCD controller.
+choice
+ depends on FB_JZLCD_4730_4740
+ prompt "LCD Panel"
+ default JZLCD_SAMSUNG_LTP400WQF01
+
+config JZLCD_SHARP_LQ035Q7
+ bool "SHARP LQ035Q7 TFT panel (240x320)"
+
+config JZLCD_SAMSUNG_LTS350Q1
+ bool "SAMSUNG LTS350Q1 TFT panel (240x320)"
+
+config JZLCD_SAMSUNG_LTV350QVF04
+ bool "SAMSUNG LTV350QV_F04 TFT panel (320x240)"
+
+config JZLCD_SAMSUNG_LTP400WQF01
+ bool "SAMSUNG LTP400WQF01 TFT panel (480x272)(16bits)"
+
+config JZLCD_SAMSUNG_LTP400WQF02
+ bool "SAMSUNG LTP400WQF02 TFT panel (480x272)(18bits)"
+
+config JZLCD_AUO_A030FL01_V1
+ bool "AUO A030FL01_V1 TFT panel (480x272)"
+
+config JZLCD_TRULY_TFTG320240DTSW
+ bool "TRULY TFTG320240DTSW TFT panel (320x240)"
+
+config JZLCD_TRULY_TFTG320240DTSW_SERIAL
+ bool "TRULY TFTG320240DTSW TFT panel (320x240)(8bit-serial mode)"
+
+config JZLCD_TRULY_TFTG240320UTSW_63W_E
+ bool "TRULY TFTG240320UTSW-63W-E TFT panel (240x320,2.5in)"
+
+config JZLCD_FOXCONN_PT035TN01
+ bool "FOXCONN PT035TN01 TFT panel (320x240)"
+
+config JZLCD_INNOLUX_PT035TN01_SERIAL
+ bool "INNOLUX PT035TN01 TFT panel (320x240,3.5in)(8bit-serial mode)"
+
+config JZLCD_TOSHIBA_LTM084P363
+ bool "Toshiba LTM084P363 TFT panel (800x600)"
+
+config JZLCD_HYNIX_HT10X21
+ bool "Hynix HT10X21_300 TFT panel (1024x768)"
+
+config JZLCD_INNOLUX_AT080TN42
+ bool "INNOLUX AT080TN42 TFT panel (800x600)"
+
+config JZLCD_CSTN_800x600
+ bool "800x600 colorDSTN panel"
+
+config JZLCD_CSTN_320x240
+ bool "320x240 colorSTN panel"
+
+config JZLCD_MSTN_480x320
+ bool "480x320 monoSTN panel"
+
+config JZLCD_MSTN_320x240
+ bool "320x240 monoSTN panel"
+
+config JZLCD_MSTN_240x128
+ bool "240x128 monoSTN panel"
+
+config JZLCD_MSTN_INVERSE
+ bool "Use an inverse color display."
+ depends on (JZLCD_MSTN_480x320 || JZLCD_MSTN_240x128)
+
+endchoice
+
+
config FB_CIRRUS
tristate "Cirrus Logic support"
depends on FB && (ZORRO || PCI)
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 01a819f..289b6e9 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -28,6 +28,9 @@ obj-$(CONFIG_FB_DDC) += fb_ddc.o
obj-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o
# Hardware specific drivers go first
+obj-$(CONFIG_FB_JZLCD_4720_4740) += jzlcd.o
+obj-$(CONFIG_FB_JZ4740_SLCD) += jz4740_slcd.o
+
obj-$(CONFIG_FB_AMIGA) += amifb.o c2p_planar.o
obj-$(CONFIG_FB_ARC) += arcfb.o
obj-$(CONFIG_FB_CLPS711X) += clps711xfb.o
--
1.5.6.5

View File

@ -0,0 +1,178 @@
From 35d6fcb8cc04d368abef00b00e6af04b8c71ba46 Mon Sep 17 00:00:00 2001
From: Lars-Peter Clausen <lars@metafoo.de>
Date: Wed, 19 Aug 2009 14:54:02 +0200
Subject: [PATCH] serial
---
drivers/serial/8250.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 103 insertions(+), 1 deletions(-)
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index fb867a9..d8784c3 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -194,7 +194,7 @@ static const struct serial8250_config uart_config[] = {
[PORT_16550A] = {
.name = "16550A",
.fifo_size = 16,
- .tx_loadsz = 16,
+ .tx_loadsz = 8,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
.flags = UART_CAP_FIFO,
},
@@ -401,6 +401,10 @@ static unsigned int mem_serial_in(struct uart_port *p, int offset)
static void mem_serial_out(struct uart_port *p, int offset, int value)
{
offset = map_8250_out_reg(p, offset) << p->regshift;
+#if defined(CONFIG_JZSOC)
+ if (offset == (UART_FCR << p->regshift))
+ value |= 0x10; /* set FCR.UUE */
+#endif
writeb(value, p->membase + offset);
}
@@ -2213,6 +2217,83 @@ static void serial8250_shutdown(struct uart_port *port)
serial_unlink_irq_chain(up);
}
+#if defined(CONFIG_JZSOC) && !defined(CONFIG_SOC_JZ4730)
+static unsigned short quot1[3] = {0}; /* quot[0]:baud_div, quot[1]:umr, quot[2]:uacr */
+static unsigned short * serial8250_get_divisor(struct uart_port *port, unsigned int baud)
+{
+ int err, sum, i, j;
+ int a[12], b[12];
+ unsigned short div, umr, uacr;
+ unsigned short umr_best, div_best, uacr_best;
+ long long t0, t1, t2, t3;
+
+ sum = 0;
+ umr_best = div_best = uacr_best = 0;
+ div = 1;
+
+ if ((port->uartclk % (16 * baud)) == 0) {
+ quot1[0] = port->uartclk / (16 * baud);
+ quot1[1] = 16;
+ quot1[2] = 0;
+ return quot1;
+ }
+
+ while (1) {
+ umr = port->uartclk / (baud * div);
+ if (umr > 32) {
+ div++;
+ continue;
+ }
+ if (umr < 4) {
+ break;
+ }
+ for (i = 0; i < 12; i++) {
+ a[i] = umr;
+ b[i] = 0;
+ sum = 0;
+ for (j = 0; j <= i; j++) {
+ sum += a[j];
+ }
+
+ /* the precision could be 1/2^(36) due to the value of t0 */
+ t0 = 0x1000000000LL;
+ t1 = (i + 1) * t0;
+ t2 = (sum * div) * t0;
+ t3 = div * t0;
+ do_div(t1, baud);
+ do_div(t2, port->uartclk);
+ do_div(t3, (2 * port->uartclk));
+ err = t1 - t2 - t3;
+
+ if (err > 0) {
+ a[i] += 1;
+ b[i] = 1;
+ }
+ }
+
+ uacr = 0;
+ for (i = 0; i < 12; i++) {
+ if (b[i] == 1) {
+ uacr |= 1 << i;
+ }
+ }
+
+ /* the best value of umr should be near 16, and the value of uacr should better be smaller */
+ if (abs(umr - 16) < abs(umr_best - 16) || (abs(umr - 16) == abs(umr_best - 16) && uacr_best > uacr)) {
+ div_best = div;
+ umr_best = umr;
+ uacr_best = uacr;
+ }
+ div++;
+ }
+
+ quot1[0] = div_best;
+ quot1[1] = umr_best;
+ quot1[2] = uacr_best;
+
+ return quot1;
+}
+#else
static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud)
{
unsigned int quot;
@@ -2232,6 +2313,7 @@ static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int
return quot;
}
+#endif
static void
serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
@@ -2241,6 +2323,9 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
unsigned char cval, fcr = 0;
unsigned long flags;
unsigned int baud, quot;
+#if defined(CONFIG_JZSOC) && !defined(CONFIG_SOC_JZ4730)
+ unsigned short *quot1;
+#endif
switch (termios->c_cflag & CSIZE) {
case CS5:
@@ -2273,7 +2358,12 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
* Ask the core to calculate the divisor for us.
*/
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+#if defined(CONFIG_JZSOC) && !defined(CONFIG_SOC_JZ4730)
+ quot1 = serial8250_get_divisor(port, baud);
+ quot = quot1[0]; /* not usefull, just let gcc happy */
+#else
quot = serial8250_get_divisor(port, baud);
+#endif
/*
* Oxford Semi 952 rev B workaround
@@ -2351,6 +2441,10 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
if (up->capabilities & UART_CAP_UUE)
up->ier |= UART_IER_UUE | UART_IER_RTOIE;
+#ifdef CONFIG_JZSOC
+ up->ier |= UART_IER_RTOIE; /* Set this flag, or very slow */
+#endif
+
serial_out(up, UART_IER, up->ier);
if (up->capabilities & UART_CAP_EFR) {
@@ -2385,7 +2479,15 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
}
+#if defined(CONFIG_JZSOC) && !defined(CONFIG_SOC_JZ4730)
+#define UART_UMR 9
+#define UART_UACR 10
+ serial_dl_write(up, quot1[0]);
+ serial_outp(up, UART_UMR, quot1[1]);
+ serial_outp(up, UART_UACR, quot1[2]);
+#else
serial_dl_write(up, quot);
+#endif
/*
* LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR
--
1.5.6.5

View File

@ -0,0 +1,183 @@
From d6e1e759473e3b62bb2a7b906bbb27f1f1f2aff2 Mon Sep 17 00:00:00 2001
From: Lars-Peter Clausen <lars@metafoo.de>
Date: Wed, 19 Aug 2009 14:54:18 +0200
Subject: [PATCH] usb
---
drivers/usb/Kconfig | 1 +
drivers/usb/core/hub.c | 43 +++++++++++++++++++++++++++++++++++++
drivers/usb/gadget/Kconfig | 18 ++++++++++++++-
drivers/usb/gadget/Makefile | 3 ++
drivers/usb/gadget/gadget_chips.h | 9 +++++++
5 files changed, 73 insertions(+), 1 deletions(-)
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index dcd49f1..a520bed 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -44,6 +44,7 @@ config USB_ARCH_HAS_OHCI
default y if PPC_MPC52xx
# MIPS:
default y if SOC_AU1X00
+ default y if JZSOC
# SH:
default y if CPU_SUBTYPE_SH7720
default y if CPU_SUBTYPE_SH7721
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 71f86c6..d3f9344 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1857,6 +1857,25 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
{
int i, status;
+#ifdef CONFIG_SOC_JZ4730
+ /*
+ * On Jz4730, we assume that the first USB port was used as device.
+ * If not, please comment next lines.
+ */
+ if (port1 == 1) {
+ return 0;
+ }
+#endif
+
+#if defined(CONFIG_SOC_JZ4740) || defined(CONFIG_SOC_JZ4750) || defined(CONFIG_SOC_JZ4750D)
+ /*
+ * On Jz4740 and Jz4750, the second USB port was used as device.
+ */
+ if (port1 == 2) {
+ return 0;
+ }
+#endif
+
/* Block EHCI CF initialization during the port reset.
* Some companion controllers don't like it when they mix.
*/
@@ -2818,11 +2837,35 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
le16_to_cpu(hub->descriptor->wHubCharacteristics);
struct usb_device *udev;
int status, i;
+#ifdef CONFIG_JZSOC
+ static char jzhub = 1; /* the hub first to be initialized is jzsoc on-chip hub */
+#endif
dev_dbg (hub_dev,
"port %d, status %04x, change %04x, %s\n",
port1, portstatus, portchange, portspeed (portstatus));
+#ifdef CONFIG_SOC_JZ4730
+ /*
+ * On Jz4730, we assume that the first USB port was used as device.
+ * If not, please comment next lines.
+ */
+ if ((port1 == 1) && (jzhub)) {
+ jzhub = 0;
+ return;
+ }
+#endif
+
+#if defined(CONFIG_SOC_JZ4740) || defined(CONFIG_SOC_JZ4750) || defined(CONFIG_SOC_JZ4750D)
+ /*
+ * On Jz4740 and Jz4750, the second USB port was used as device.
+ */
+ if ((port1 == 2) && (jzhub)) {
+ jzhub = 0;
+ return;
+ }
+#endif
+
if (hub->has_indicators) {
set_port_led(hub, port1, HUB_LED_AUTO);
hub->indicator[port1-1] = INDICATOR_AUTO;
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 7f8e83a..a973ce7 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -121,11 +121,24 @@ choice
#
# Integrated controllers
#
+config USB_GADGET_JZ4740
+ boolean "JZ4740 UDC"
+ depends on SOC_JZ4740
+ select USB_GADGET_DUALSPEED
+ help
+ Select this to support the Ingenic JZ4740 processor
+ high speed USB device controller.
+
+config USB_JZ4740
+ tristate
+ depends on USB_GADGET_JZ4740
+ default USB_GADGET
config USB_GADGET_AT91
boolean "Atmel AT91 USB Device Port"
depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9
select USB_GADGET_SELECTED
+
help
Many Atmel AT91 processors (such as the AT91RM2000) have a
full speed USB Device Port with support for five configurable
@@ -534,6 +547,10 @@ config USB_DUMMY_HCD
endchoice
+config USB_JZ_UDC_HOTPLUG
+ boolean "Ingenic USB Device Controller Hotplug Support"
+ depends on USB_GADGET_JZ4750
+
config USB_GADGET_DUALSPEED
bool
depends on USB_GADGET
@@ -541,7 +558,6 @@ config USB_GADGET_DUALSPEED
help
Means that gadget drivers should include extra descriptors
and code to handle dual-speed controllers.
-
#
# USB Gadget Drivers
#
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index e6017e6..b758686 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -27,6 +27,9 @@ obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o
obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o
obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o
+obj-$(CONFIG_USB_JZ4740) += jz4740_udc.o
+
+obj-$(CONFIG_USB_JZ_UDC_HOTPLUG)+= udc_hotplug_core.o
#
# USB gadget drivers
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index 8e0e9a0..d9c0990 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -15,6 +15,12 @@
#ifndef __GADGET_CHIPS_H
#define __GADGET_CHIPS_H
+#ifdef CONFIG_USB_GADGET_JZ4740
+#define gadget_is_jz4740(g) !strcmp("ingenic_hsusb", (g)->name)
+#else
+#define gadget_is_jz4740(g) 0
+#endif
+
#ifdef CONFIG_USB_GADGET_NET2280
#define gadget_is_net2280(g) !strcmp("net2280", (g)->name)
#else
@@ -239,6 +245,9 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
return 0x23;
else if (gadget_is_langwell(gadget))
return 0x24;
+ else if (gadget_is_jz4740(gadget))
+ return 0x25;
+
return -ENOENT;
}
--
1.5.6.5

View File

@ -0,0 +1,268 @@
From 1a6fa6adbd597171648c7cb308cc9e3efe488668 Mon Sep 17 00:00:00 2001
From: Lars-Peter Clausen <lars@metafoo.de>
Date: Wed, 19 Aug 2009 14:54:29 +0200
Subject: [PATCH] sound
---
include/sound/pcm.h | 26 ++++++------
sound/core/pcm_lib.c | 94 +++++++++++++++++++++++++++++++++++++++++++++
sound/core/pcm_native.c | 15 ++++++-
sound/soc/Kconfig | 1 +
sound/soc/Makefile | 1 +
sound/soc/codecs/Kconfig | 12 ++++++
sound/soc/codecs/Makefile | 2 +
7 files changed, 135 insertions(+), 16 deletions(-)
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 2389352..24dcb18 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -113,23 +113,23 @@ struct snd_pcm_ops {
#define SNDRV_PCM_RATE_5512 (1<<0) /* 5512Hz */
#define SNDRV_PCM_RATE_8000 (1<<1) /* 8000Hz */
#define SNDRV_PCM_RATE_11025 (1<<2) /* 11025Hz */
-#define SNDRV_PCM_RATE_16000 (1<<3) /* 16000Hz */
-#define SNDRV_PCM_RATE_22050 (1<<4) /* 22050Hz */
-#define SNDRV_PCM_RATE_32000 (1<<5) /* 32000Hz */
-#define SNDRV_PCM_RATE_44100 (1<<6) /* 44100Hz */
-#define SNDRV_PCM_RATE_48000 (1<<7) /* 48000Hz */
-#define SNDRV_PCM_RATE_64000 (1<<8) /* 64000Hz */
-#define SNDRV_PCM_RATE_88200 (1<<9) /* 88200Hz */
-#define SNDRV_PCM_RATE_96000 (1<<10) /* 96000Hz */
-#define SNDRV_PCM_RATE_176400 (1<<11) /* 176400Hz */
-#define SNDRV_PCM_RATE_192000 (1<<12) /* 192000Hz */
+#define SNDRV_PCM_RATE_12000 (1<<3) /* 12000Hz */
+#define SNDRV_PCM_RATE_16000 (1<<4) /* 16000Hz */
+#define SNDRV_PCM_RATE_22050 (1<<5) /* 22050Hz */
+#define SNDRV_PCM_RATE_24000 (1<<6) /* 24000Hz */
+#define SNDRV_PCM_RATE_32000 (1<<7) /* 32000Hz */
+#define SNDRV_PCM_RATE_44100 (1<<8) /* 44100Hz */
+#define SNDRV_PCM_RATE_48000 (1<<9) /* 48000Hz */
+#define SNDRV_PCM_RATE_64000 (1<<10) /* 64000Hz */
+#define SNDRV_PCM_RATE_88200 (1<<11) /* 88200Hz */
+#define SNDRV_PCM_RATE_96000 (1<<12) /* 96000Hz */
+#define SNDRV_PCM_RATE_176400 (1<<13) /* 176400Hz */
+#define SNDRV_PCM_RATE_192000 (1<<14) /* 192000Hz */
#define SNDRV_PCM_RATE_CONTINUOUS (1<<30) /* continuous range */
#define SNDRV_PCM_RATE_KNOT (1<<31) /* supports more non-continuos rates */
-#define SNDRV_PCM_RATE_8000_44100 (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_11025|\
- SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_22050|\
- SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100)
+#define SNDRV_PCM_RATE_8000_44100 (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_11025|SNDRV_PCM_RATE_12000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_22050|SNDRV_PCM_RATE_24000|SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100)
#define SNDRV_PCM_RATE_8000_48000 (SNDRV_PCM_RATE_8000_44100|SNDRV_PCM_RATE_48000)
#define SNDRV_PCM_RATE_8000_96000 (SNDRV_PCM_RATE_8000_48000|SNDRV_PCM_RATE_64000|\
SNDRV_PCM_RATE_88200|SNDRV_PCM_RATE_96000)
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 72cfd47..5ae0a2d 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -2076,6 +2076,7 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
}
+#if 0
snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __user *buf, snd_pcm_uframes_t size)
{
struct snd_pcm_runtime *runtime;
@@ -2091,6 +2092,99 @@ snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __u
return -EINVAL;
return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer);
}
+#else
+snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __user *buf, snd_pcm_uframes_t size)
+{
+ struct snd_pcm_runtime *runtime;
+ int nonblock;
+
+ snd_pcm_sframes_t tmp_frames;
+ snd_pcm_sframes_t final_frames;
+ int channels;
+
+ snd_assert(substream != NULL, return -ENXIO);
+ runtime = substream->runtime;
+ snd_assert(runtime != NULL, return -ENXIO);
+ snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);
+ if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
+
+ nonblock = !!(substream->f_flags & O_NONBLOCK);
+ if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED)
+ return -EINVAL;
+
+ /*
+ * mono capture process for no mono codec
+ * function codec such as ipcood and dlv
+ */
+
+ tmp_frames = snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer);
+
+ channels = runtime->channels;
+
+ if (channels == 1) {
+ short *tmpbuf = kcalloc(tmp_frames, sizeof(short), GFP_KERNEL);
+ short *src, *dst, *end;
+
+ memcpy(tmpbuf, buf, frames_to_bytes(runtime, tmp_frames));
+
+ src = (short *)buf;
+ dst = (short *)tmpbuf;
+ end = dst + tmp_frames - 1;
+
+ src++;
+ dst++;
+ dst++;
+ final_frames = 1;
+ while (dst <= end) {
+ *src = *dst;
+ final_frames++;
+ src++;
+ dst++;
+ dst++;
+ }
+ tmp_frames = final_frames;
+ kfree(tmpbuf);
+
+#if 0
+ /* when i have time, i will try the code, no kcalloc */
+ snd_assert(runtime->dma_area, return -EFAULT);
+ if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames)))
+ return -EFAULT;
+
+ unsigned int up_bytes = frames_to_bytes(runtime, frames);
+
+ int while_cnt = 4;
+ int while_all = up_bytes - 2;
+
+ while (while_cnt <= while_all) {
+ //printk("[%d = %d]\n",(while_cnt/2),while_cnt);
+ buf[(while_cnt/2)] = buf[while_cnt];
+ //printk("[%d = %d]\n",((while_cnt/2)+1),(while_cnt+1));
+ buf[((while_cnt/2)+1)] = buf[(while_cnt+1)];
+ while_cnt += 4;
+#if 0
+ buf[2] = buf[4];
+ buf[3] = buf[5];
+
+ buf[4] = buf[8];
+ buf[5] = buf[9];
+
+ buf[6] = buf[12];
+ buf[7] = buf[13];
+
+ buf[8] = buf[16];
+ buf[9] = buf[17];
+#endif
+ }
+ /* when i have time, i will try the code, no kcalloc */
+#endif
+
+ }
+
+ return tmp_frames;
+}
+#endif
EXPORT_SYMBOL(snd_pcm_lib_read);
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index ac2150e..2a57ab7 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -1748,12 +1748,13 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params,
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
}
-#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12
+#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 14
#error "Change this table"
#endif
-static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100,
- 48000, 64000, 88200, 96000, 176400, 192000 };
+static unsigned int rates[] = { 5512, 8000, 11025, 12000, 16000, 22050, 24000,
+ 32000, 44100, 48000, 64000, 88200, 96000,
+ 176400, 192000 };
const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = {
.count = ARRAY_SIZE(rates),
@@ -1764,9 +1765,17 @@ static int snd_pcm_hw_rule_rate(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_pcm_hardware *hw = rule->private;
+#if 0
return snd_interval_list(hw_param_interval(params, rule->var),
snd_pcm_known_rates.count,
snd_pcm_known_rates.list, hw->rates);
+#else
+ //printk("hw->rates=0x%08x\n",hw->rates);//0x3b6
+ hw->rates = 0x3fe;//12KHz and 24KHz bits are all zero,you need set 1
+ return snd_interval_list(hw_param_interval(params, rule->var),
+ snd_pcm_known_rates.count,
+ snd_pcm_known_rates.list, hw->rates);
+#endif
}
static int snd_pcm_hw_rule_buffer_bytes_max(struct snd_pcm_hw_params *params,
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index d3e786a..a5335f4 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -35,6 +35,7 @@ source "sound/soc/s3c24xx/Kconfig"
source "sound/soc/s6000/Kconfig"
source "sound/soc/sh/Kconfig"
source "sound/soc/txx9/Kconfig"
+source "sound/soc/jz4740/Kconfig"
# Supported codecs
source "sound/soc/codecs/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 6f1e28d..132772d 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_SND_SOC) += s3c24xx/
obj-$(CONFIG_SND_SOC) += s6000/
obj-$(CONFIG_SND_SOC) += sh/
obj-$(CONFIG_SND_SOC) += txx9/
+obj-$(CONFIG_SND_SOC) += jz4740/
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index bbc97fd..493e216 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -176,3 +176,15 @@ config SND_SOC_WM9712
config SND_SOC_WM9713
tristate
+
+config SND_SOC_ICODEC
+ tristate "Jz4740 internal codec"
+ depends on SND_SOC && SND_JZ4740_SOC_PAVO && SND_JZ4740_SOC_I2S
+ help
+ Say Y if you want to use internal codec on Ingenic Jz4740 PAVO board.
+
+config SND_SOC_DLV
+ tristate "Jz4750 internal codec"
+ depends on SND_SOC && SND_JZ4750_SOC_APUS && SND_JZ4750_SOC_I2S
+ help
+ Say Y if you want to use internal codec on Ingenic Jz4750 APUS board.
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 8b75305..f053c15 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -34,6 +34,7 @@ snd-soc-wm9081-objs := wm9081.o
snd-soc-wm9705-objs := wm9705.o
snd-soc-wm9712-objs := wm9712.o
snd-soc-wm9713-objs := wm9713.o
+snd-soc-jzcodec-objs := jzcodec.o
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
@@ -71,3 +72,4 @@ obj-$(CONFIG_SND_SOC_WM9081) += snd-soc-wm9081.o
obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
+obj-$(CONFIG_SND_SOC_ICODEC) += snd-soc-jzcodec.o
--
1.5.6.5

View File

@ -0,0 +1,319 @@
--- /dev/null
+++ b/arch/mips/jz4740/board-qi_lb60.c
@@ -0,0 +1,110 @@
+/*
+ * linux/arch/mips/jz4740/board-qi_lb60.c
+ *
+ * QI_LB60 setup routines.
+ *
+ * Copyright (c) 2009 Qi Hardware inc.,
+ * Author: Xiangfu Liu <xiangfu@qi-hardware.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+
+#include <asm/cpu.h>
+#include <asm/bootinfo.h>
+#include <asm/mipsregs.h>
+#include <asm/reboot.h>
+
+#include <asm/jzsoc.h>
+
+extern void (*jz_timer_callback)(void);
+
+static void dancing(void)
+{
+ static unsigned int count = 0;
+
+ count ++;
+ count &= 1;
+ if (count)
+ __gpio_set_pin(GPIO_LED_EN);
+ else
+ __gpio_clear_pin(GPIO_LED_EN);
+}
+
+static void pi_timer_callback(void)
+{
+ static unsigned long count = 0;
+
+ if ((++count) % 50 == 0) {
+ dancing();
+ count = 0;
+ }
+}
+
+static void __init board_cpm_setup(void)
+{
+ /* Stop unused module clocks here.
+ * We have started all module clocks at arch/mips/jz4740/setup.c.
+ */
+}
+
+static void __init board_gpio_setup(void)
+{
+ /*
+ * Most of the GPIO pins should have been initialized by the boot-loader
+ */
+
+ /*
+ * Initialize MSC pins
+ */
+ /* __gpio_as_msc(); */
+
+ /*
+ * Initialize LCD pins
+ */
+ /* __gpio_as_lcd_18bit(); */
+
+ /*
+ * Initialize SSI pins
+ */
+ /* __gpio_as_ssi(); */
+
+ /*
+ * Initialize I2C pins
+ */
+ /* __gpio_as_i2c(); */
+
+ /*
+ * Initialize Other pins
+ */
+ __gpio_as_output(GPIO_SD_VCC_EN_N);
+ __gpio_disable_pull(GPIO_SD_VCC_EN_N);
+ __gpio_clear_pin(GPIO_SD_VCC_EN_N);
+
+ __gpio_as_input(GPIO_SD_CD_N);
+ __gpio_disable_pull(GPIO_SD_CD_N);
+
+ __gpio_as_input(GPIO_SD_WP);
+ __gpio_disable_pull(GPIO_SD_WP);
+
+ __gpio_as_input(GPIO_DC_DETE_N);
+ __gpio_as_input(GPIO_CHARG_STAT_N);
+}
+
+void __init jz_board_setup(void)
+{
+ printk("Qi Hardware JZ4740 QI_LB60 setup\n");
+
+ board_cpm_setup();
+ board_gpio_setup();
+
+ jz_timer_callback = pi_timer_callback;
+}
--- a/arch/mips/jz4740/Makefile
+++ b/arch/mips/jz4740/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_JZ4740_LEO) += board-leo.o
obj-$(CONFIG_JZ4740_LYRA) += board-lyra.o
obj-$(CONFIG_JZ4725_DIPPER) += board-dipper.o
obj-$(CONFIG_JZ4720_VIRGO) += board-virgo.o
+obj-$(CONFIG_JZ4740_QI_LB60) += board-qi_lb60.o
# PM support
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -655,6 +655,14 @@ choice
Kodama
Hikari
Say Y here for most Octeon reference boards.
+
+config JZ4740_QI_LB60
+ bool "Ingenic JZ4740 QI_LB60 board"
+ select DMA_NONCOHERENT
+ select SYS_HAS_CPU_MIPS32_R1
+ select SYS_SUPPORTS_32BIT_KERNEL
+ select SYS_SUPPORTS_LITTLE_ENDIAN
+ select SOC_JZ4740
endchoice
--- a/drivers/video/jzlcd.c
+++ b/drivers/video/jzlcd.c
@@ -126,15 +126,18 @@ static struct jzfb_info jzfb = {
MODE_TFT_GEN | HSYNC_N | VSYNC_N | PCLK_N | DE_N,
320, 240, 16, 60, 3, 3, 3, 3, 3, 85 /* 320x240 */
#endif
-#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01) && defined(CONFIG_JZ4740_PAVO)
- MODE_TFT_GEN | HSYNC_N | VSYNC_N | MODE_TFT_18BIT | PCLK_N,
-// 320, 240, 18, 110, 1, 1, 10, 50, 10, 13
- 320, 240, 18, 80, 1, 1, 10, 50, 10, 13
-#endif
-#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01) && !(defined(CONFIG_JZ4740_PAVO))
- MODE_TFT_GEN | HSYNC_N | VSYNC_N | PCLK_N,
- 320, 240, 16, 110, 1, 1, 10, 50, 10, 13
-#endif
+#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01)
+ #if defined(CONFIG_JZ4740_PAVO)
+ MODE_TFT_GEN | HSYNC_N | VSYNC_N | MODE_TFT_18BIT | PCLK_N,
+ 320, 240, 18, 80, 1, 1, 10, 50, 10, 13
+ #elif defined(CONFIG_JZ4740_QI_LB60)
+ MODE_8BIT_SERIAL_TFT | PCLK_N | HSYNC_N | VSYNC_N,
+ 320, 240, 32, 70, 1, 1, 273, 140, 1, 20
+ #else
+ MODE_TFT_GEN | HSYNC_N | VSYNC_N | PCLK_N,
+ 320, 240, 16, 110, 1, 1, 10, 50, 10, 13
+ #endif
+#endif /* CONFIG_JZLCD_FOXCONN_PT035TN01 */
#if defined(CONFIG_JZLCD_INNOLUX_PT035TN01_SERIAL)
MODE_8BIT_SERIAL_TFT | PCLK_N | HSYNC_N | VSYNC_N,
320, 240, 32, 60, 1, 1, 10, 50, 10, 13
@@ -1523,7 +1526,7 @@ static int __init jzfb_init(void)
cfb->pm->data = cfb;
#endif
- __lcd_display_on();
+ __lcd_display_off();
return 0;
--- a/drivers/video/jzlcd.h
+++ b/drivers/video/jzlcd.h
@@ -363,7 +363,11 @@ do { \
#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01) || defined(CONFIG_JZLCD_INNOLUX_PT035TN01_SERIAL)
#if defined(CONFIG_JZLCD_FOXCONN_PT035TN01) /* board pmp */
-#define MODE 0xcd /* 24bit parellel RGB */
+ #if defined(CONFIG_JZ4740_QI_LB60)
+ #define MODE 0xc9
+ #else
+ #define MODE 0xcd /* 24bit parellel RGB */
+ #endif
#endif
#if defined(CONFIG_JZLCD_INNOLUX_PT035TN01_SERIAL)
#define MODE 0xc9 /* 8bit serial RGB */
@@ -384,6 +388,11 @@ do { \
#define SPCK (32*1+17) //LCD_CLS
#define SPDA (32*2+12) //LCD_D12
#define LCD_RET (32*2+23) //LCD_REV, GPC23
+#elif defined(CONFIG_JZ4740_QI_LB60)
+ #define SPEN (32*2+21) //LCD_SPL
+ #define SPCK (32*2+23) //LCD_CLS
+ #define SPDA (32*2+22) //LCD_D12
+ #define LCD_RET (32*3+27)
#if 0 /*old driver*/
#define SPEN (32*1+18) //LCD_SPL
#define SPCK (32*1+17) //LCD_CLS
@@ -655,7 +664,7 @@ do { \
/*#if defined(CONFIG_JZ4740_LEO) || defined(CONFIG_JZ4740_PAVO)*/
#if defined(CONFIG_SOC_JZ4740)
-#if defined(CONFIG_JZ4740_PAVO) || defined(CONFIG_JZ4740_LYRA)
+#if defined(CONFIG_JZ4740_PAVO) || defined(CONFIG_JZ4740_LYRA) || defined(CONFIG_JZ4740_QI_LB60)
#define GPIO_PWM 123 /* GP_D27 */
#define PWM_CHN 4 /* pwm channel */
#define PWM_FULL 101
@@ -725,7 +734,7 @@ do { \
do { \
__gpio_set_pin(GPIO_DISP_OFF_N); \
__lcd_special_on(); \
- __lcd_set_backlight_level(80); \
+ __lcd_set_backlight_level(20); \
} while (0)
#define __lcd_display_off() \
--- /dev/null
+++ b/arch/mips/include/asm/mach-jz4740/board-qi_lb60.h
@@ -0,0 +1,79 @@
+/*
+ * linux/include/asm-mips/mach-jz4740/board-qi_lb60.h
+ *
+ * Copyright (c) 2009 Qi Hardware inc.,
+ * Author: Xiangfu Liu <xiangfu@qi-hardware.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __ASM_JZ4740_QI_LB60_H__
+#define __ASM_JZ4740_QI_LB60_H__
+
+/*
+ * Frequencies of on-board oscillators
+ */
+#define JZ_EXTAL 12000000 /* Main extal freq: 12 MHz */
+#define JZ_EXTAL2 32768 /* RTC extal freq: 32.768 KHz */
+
+/*
+ * GPIO
+ */
+#define GPIO_DC_DETE_N (2 * 32 + 26)
+#define GPIO_CHARG_STAT_N (2 * 32 + 27)
+#define GPIO_LED_EN (2 * 32 + 28)
+#define GPIO_LCD_CS (2 * 32 + 21)
+#define GPIO_DISP_OFF_N (3 * 32 + 21)
+#define GPIO_PWM (3 * 32 + 27)
+
+#define GPIO_AMP_EN (3 * 32 + 4)
+
+#define GPIO_SD_CD_N (3 * 32 + 0)
+#define GPIO_SD_VCC_EN_N (3 * 32 + 2)
+#define GPIO_SD_WP (3 * 32 + 16)
+
+#define GPIO_USB_DETE (3 * 32 + 28)
+#define GPIO_BUZZ_PWM (3 * 32 + 27)
+#define GPIO_UDC_HOTPLUG GPIO_USB_DETE
+
+#define GPIO_AUDIO_POP (1 * 32 + 29)
+#define GPIO_COB_TEST (1 * 32 + 30)
+
+#define GPIO_KEYOUT_BASE (2 * 32 + 10)
+#define GPIO_KEYIN_BASE (3 * 32 + 18)
+#define GPIO_KEYIN_8 (3 * 32 + 26)
+
+/*
+ * MMC/SD
+ */
+#define MSC_WP_PIN GPIO_SD_WP
+#define MSC_HOTPLUG_PIN GPIO_SD_CD_N
+#define MSC_HOTPLUG_IRQ (IRQ_GPIO_0 + GPIO_SD_CD_N)
+
+#define __msc_init_io() \
+do { \
+ __gpio_as_output(GPIO_SD_VCC_EN_N); \
+ __gpio_as_input(GPIO_SD_CD_N); \
+} while (0)
+
+#define __msc_enable_power() \
+do { \
+ __gpio_clear_pin(GPIO_SD_VCC_EN_N); \
+} while (0)
+
+#define __msc_disable_power() \
+do { \
+ __gpio_set_pin(GPIO_SD_VCC_EN_N); \
+} while (0)
+
+#define __msc_card_detected(s) \
+({ \
+ int detected = 1; \
+ if (!__gpio_get_pin(GPIO_SD_CD_N)) \
+ detected = 0; \
+ detected; \
+})
+
+#endif /* __ASM_JZ4740_QI_LB60_H__ */
--- a/arch/mips/include/asm/mach-jz4740/jz4740.h
+++ b/arch/mips/include/asm/mach-jz4740/jz4740.h
@@ -43,6 +43,10 @@
#include <asm/mach-jz4740/board-virgo.h>
#endif
+#ifdef CONFIG_JZ4740_QI_LB60
+#include <asm/mach-jz4740/board-qi_lb60.h>
+#endif
+
/* Add other platform definition here ... */