diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 3ca0fe1..a86cdd5 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -19,6 +19,98 @@ choice prompt "System type" default SGI_IP22 +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 + +config JZ4730_PMP + bool "Ingenic JZ4730 PMP board" + select DMA_NONCOHERENT + select SYS_HAS_CPU_MIPS32_R1 + select SYS_SUPPORTS_32BIT_KERNEL + select SYS_SUPPORTS_LITTLE_ENDIAN + select SOC_JZ4730 + +config JZ4740_PAVO + bool "Ingenic JZ4740 PAVO board" + select DMA_NONCOHERENT + select SYS_HAS_CPU_MIPS32_R1 + select SYS_SUPPORTS_32BIT_KERNEL + select SYS_SUPPORTS_LITTLE_ENDIAN + select SOC_JZ4740 + +config JZ4740_LEO + bool "Ingenic JZ4740 LEO board" + select DMA_NONCOHERENT + select SYS_HAS_CPU_MIPS32_R1 + select SYS_SUPPORTS_32BIT_KERNEL + select SYS_SUPPORTS_LITTLE_ENDIAN + select SOC_JZ4740 + +config JZ4740_LYRA + bool "Ingenic JZ4740 LYRA board" + select DMA_NONCOHERENT + select SYS_HAS_CPU_MIPS32_R1 + select SYS_SUPPORTS_32BIT_KERNEL + select SYS_SUPPORTS_LITTLE_ENDIAN + select SOC_JZ4740 + +config JZ4725_DIPPER + bool "Ingenic JZ4725 DIPPER board" + select DMA_NONCOHERENT + select SYS_HAS_CPU_MIPS32_R1 + select SYS_SUPPORTS_32BIT_KERNEL + select SYS_SUPPORTS_LITTLE_ENDIAN + select SOC_JZ4740 + select SOC_JZ4725 + +config JZ4720_VIRGO + bool "Ingenic JZ4720 VIRGO board" + select DMA_NONCOHERENT + select SYS_HAS_CPU_MIPS32_R1 + select SYS_SUPPORTS_32BIT_KERNEL + select SYS_SUPPORTS_LITTLE_ENDIAN + select SOC_JZ4740 + select SOC_JZ4720 + +config JZ4750_FUWA + bool "Ingenic JZ4750 FUWA board" + select DMA_NONCOHERENT + select SYS_HAS_CPU_MIPS32_R1 + select SYS_SUPPORTS_32BIT_KERNEL + select SYS_SUPPORTS_LITTLE_ENDIAN + select SOC_JZ4750 + select JZ_FPGA + +config JZ4750D_FUWA1 + bool "Ingenic JZ4750d FUWA1 board" + select DMA_NONCOHERENT + select SYS_HAS_CPU_MIPS32_R1 + select SYS_SUPPORTS_32BIT_KERNEL + select SYS_SUPPORTS_LITTLE_ENDIAN + select SOC_JZ4750D + select JZ_FPGA + +config JZ4750_APUS + bool "Ingenic JZ4750 APUS board" + select DMA_NONCOHERENT + select SYS_HAS_CPU_MIPS32_R1 + select SYS_SUPPORTS_32BIT_KERNEL + select SYS_SUPPORTS_LITTLE_ENDIAN + select SOC_JZ4750 + +config JZ4750D_CETUS + bool "Ingenic JZ4750d CETUS board" + select DMA_NONCOHERENT + select SYS_HAS_CPU_MIPS32_R1 + select SYS_SUPPORTS_32BIT_KERNEL + select SYS_SUPPORTS_LITTLE_ENDIAN + select SOC_JZ4750D + config MACH_ALCHEMY bool "Alchemy processor based machines" @@ -671,6 +763,48 @@ source "arch/mips/cavium-octeon/Kconfig" endmenu +##################################################### +# Ingenic SOC series +##################################################### + +config SOC_JZ4730 + bool + select JZSOC + +config SOC_JZ4740 + bool + select JZSOC + +config SOC_JZ4725 + bool + select JZSOC + +config SOC_JZ4720 + bool + select JZSOC + +config SOC_JZ4750 + bool + select JZSOC + +config SOC_JZ4750D + bool + select JZSOC + +config JZ_FPGA + bool + +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 @@ -2055,6 +2189,14 @@ config PCI_DOMAINS source "drivers/pci/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) + # # ISA support is now enabled via select. Too many systems still have the one # or other ISA chip on the board that users don't know about so don't expect @@ -2164,6 +2306,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..91f73c0 100644 --- a/arch/mips/Makefile +++ b/arch/mips/Makefile @@ -180,6 +180,37 @@ cflags-$(CONFIG_AR7) += -I$(srctree)/arch/mips/include/asm/mach-ar7 load-$(CONFIG_AR7) += 0xffffffff94100000 # +# Commond Ingenic JZ4730 series +# +core-$(CONFIG_SOC_JZ4730) += arch/mips/jz4730/ +cflags-$(CONFIG_SOC_JZ4730) += -Iinclude/asm-mips/mach-jz4730 +load-$(CONFIG_SOC_JZ4730) += 0xffffffff80010000 + +# +# Commond Ingenic JZ4740 series +# + +core-$(CONFIG_SOC_JZ4740) += arch/mips/jz4740/ +cflags-$(CONFIG_SOC_JZ4740) += -Iinclude/asm-mips/mach-jz4740 +load-$(CONFIG_SOC_JZ4740) += 0xffffffff80010000 + +# +# Commond Ingenic JZ4750 series +# + +core-$(CONFIG_SOC_JZ4750) += arch/mips/jz4750/ +cflags-$(CONFIG_SOC_JZ4750) += -Iinclude/asm-mips/mach-jz4750 +load-$(CONFIG_SOC_JZ4750) += 0xffffffff80010000 + +# +# Commond Ingenic JZ4750d series +# + +core-$(CONFIG_SOC_JZ4750D) += arch/mips/jz4750d/ +cflags-$(CONFIG_SOC_JZ4750D) += -Iinclude/asm-mips/mach-jz4750d +load-$(CONFIG_SOC_JZ4750D) += 0xffffffff80010000 + +# # Acer PICA 61, Mips Magnum 4000 and Olivetti M700. # core-$(CONFIG_MACH_JAZZ) += arch/mips/jazz/ @@ -711,6 +742,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 +777,7 @@ install: archclean: @$(MAKE) $(clean)=arch/mips/boot + @$(MAKE) $(clean)=arch/mips/boot/compressed @$(MAKE) $(clean)=arch/mips/lasat define archhelp 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/boot/compressed/Makefile b/arch/mips/boot/compressed/Makefile new file mode 100644 index 0000000..8c3ede2 --- /dev/null +++ b/arch/mips/boot/compressed/Makefile @@ -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) -Iinclude \ + -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 diff --git a/arch/mips/boot/compressed/dummy.c b/arch/mips/boot/compressed/dummy.c new file mode 100644 index 0000000..31dbf45 --- /dev/null +++ b/arch/mips/boot/compressed/dummy.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/arch/mips/boot/compressed/head.S b/arch/mips/boot/compressed/head.S new file mode 100644 index 0000000..d9700eb --- /dev/null +++ b/arch/mips/boot/compressed/head.S @@ -0,0 +1,85 @@ +/* + * linux/arch/mips/boot/compressed/head.S + * + * Copyright (C) 2005-2008 Ingenic Semiconductor Inc. + */ + +#include +#include +#include +#include + +#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 diff --git a/arch/mips/boot/compressed/ld.script b/arch/mips/boot/compressed/ld.script new file mode 100644 index 0000000..fcf8ba0 --- /dev/null +++ b/arch/mips/boot/compressed/ld.script @@ -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) } +} diff --git a/arch/mips/boot/compressed/misc.c b/arch/mips/boot/compressed/misc.c new file mode 100644 index 0000000..2309fee --- /dev/null +++ b/arch/mips/boot/compressed/misc.c @@ -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> 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."); +} diff --git a/arch/mips/boot/tools/entry b/arch/mips/boot/tools/entry new file mode 100644 index 0000000..376e822 --- /dev/null +++ b/arch/mips/boot/tools/entry @@ -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 diff --git a/arch/mips/boot/tools/filesize b/arch/mips/boot/tools/filesize new file mode 100644 index 0000000..2142ad5 --- /dev/null +++ b/arch/mips/boot/tools/filesize @@ -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 diff --git a/arch/mips/include/asm/bootinfo.h b/arch/mips/include/asm/bootinfo.h index 610fe3a..8bcf713 100644 --- a/arch/mips/include/asm/bootinfo.h +++ b/arch/mips/include/asm/bootinfo.h @@ -57,6 +57,14 @@ #define MACH_MIKROTIK_RB532 0 /* Mikrotik RouterBoard 532 */ #define MACH_MIKROTIK_RB532A 1 /* Mikrotik RouterBoard 532A */ +/* + * Valid machtype for group INGENIC + */ +#define MACH_INGENIC_JZ4730 0 /* JZ4730 SOC */ +#define MACH_INGENIC_JZ4740 1 /* JZ4740 SOC */ +#define MACH_INGENIC_JZ4750 2 /* JZ4750 SOC */ +#define MACH_INGENIC_JZ4750D 3 /* JZ4750D SOC */ + #define CL_SIZE COMMAND_LINE_SIZE extern char *system_type; diff --git a/arch/mips/jz4730/Makefile b/arch/mips/jz4730/Makefile new file mode 100644 index 0000000..d84b6b6 --- /dev/null +++ b/arch/mips/jz4730/Makefile @@ -0,0 +1,22 @@ +# +# Makefile for the Ingenic JZ4730. +# + +# 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_JZ4730_PMP) += board-pmp.o + +# CPU Frequency scaling support + +obj-$(CONFIG_CPU_FREQ_JZ) +=cpufreq.o + +# PM support + +obj-$(CONFIG_PM_LEGACY) +=pm.o sleep.o diff --git a/arch/mips/jz4730/board-pmp.c b/arch/mips/jz4730/board-pmp.c new file mode 100644 index 0000000..00860c1 --- /dev/null +++ b/arch/mips/jz4730/board-pmp.c @@ -0,0 +1,109 @@ +/* + * linux/arch/mips/jz4730/board-pmp.c + * + * JZ4730 PMP board setup routines. + * + * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +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 pmp_timer_ack(void) +{ + static unsigned int count = 0; + count ++; + if (count % 100 == 0) { + count = 0; + dancing(); + } +} + +static void __init board_cpm_setup(void) +{ + __cpm_start_all(); +} + +static void __init board_gpio_setup(void) +{ + /* + * Most of the gpios have been setup in the bootloader. + */ + + __harb_usb0_uhc(); + __gpio_as_dma(); + __gpio_as_eth(); + __gpio_as_usb(); + __gpio_as_lcd_master(); +#if defined(CONFIG_I2S_AK4642EN) + __gpio_as_scc1(); +#endif +#if defined(CONFIG_I2S_TSC2301) || defined(CONFIG_I2S_TLC320AIC23) + __gpio_as_ssi(); +#endif + //__gpio_as_ac97(); +#if defined(CONFIG_I2S_TSC2301) || defined(CONFIG_I2S_TLC320AIC23) || defined(CONFIG_I2S_CS42L51) + __gpio_as_i2s_slave(); +#endif + __gpio_as_cim(); + __gpio_as_msc(); + + __gpio_as_output(GPIO_LED_EN); + __gpio_set_pin(GPIO_LED_EN); + + __gpio_as_output(GPIO_DISP_OFF_N); + __gpio_set_pin(GPIO_DISP_OFF_N); + __gpio_as_output(GPIO_PWM0); + __gpio_set_pin(GPIO_PWM0); + + __gpio_as_input(GPIO_RTC_IRQ); + __gpio_as_output(GPIO_USB_CLK_EN); + __gpio_set_pin(GPIO_USB_CLK_EN); + + __gpio_as_input(GPIO_CHARG_STAT); + __gpio_disable_pull(GPIO_CHARG_STAT); + + __gpio_as_input(GPIO_UDC_HOTPLUG); + __gpio_disable_pull(GPIO_UDC_HOTPLUG); + __gpio_disable_pull(54); /* fixed ic bug, the pull of gpio pin 86 is as pin 54 */ +} + +void __init jz_board_setup(void) +{ + printk("JZ4730 PMP board setup\n"); + + board_cpm_setup(); + board_gpio_setup(); + + jz_timer_callback = pmp_timer_ack; +} diff --git a/arch/mips/jz4730/cpufreq.c b/arch/mips/jz4730/cpufreq.c new file mode 100644 index 0000000..1bf4713 --- /dev/null +++ b/arch/mips/jz4730/cpufreq.c @@ -0,0 +1,596 @@ + +/* + * linux/arch/mips/jz4730/cpufreq.c + * + * cpufreq driver for JZ4730 + * + * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include + +#include +#include + +#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \ + "cpufreq-jz4730", 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_iclk()/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 jz4730_freq_percpu_info { + struct cpufreq_frequency_table table[7]; +}; + +static struct jz4730_freq_percpu_info jz4730_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 cfcr; /* Clock Freq Control Register */ + u32 cfcr_mask; /* Clock Freq Control Register mask */ + u32 cfcr2; /* Clock Freq Control Register 2 */ + u32 cfcr2_mask; /* Clock Freq Control Register 2 mask */ + u32 plcr1; /* PLL1 Control Register */ + u32 plcr1_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.iclk = __cpm_get_iclk(); + jz_clocks.sclk = __cpm_get_sclk(); + jz_clocks.mclk = __cpm_get_mclk(); + jz_clocks.pclk = __cpm_get_pclk(); + jz_clocks.lcdclk = __cpm_get_lcdclk(); + jz_clocks.pixclk = __cpm_get_pixclk(); +} + +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 cfcr; + 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; + + cfcr = REG_CPM_CFCR; + cfcr &= ~((unsigned long)regs->cfcr_mask); + cfcr |= regs->cfcr; + cfcr |= CPM_CFCR_UPE; /* update immediately */ + + cur_mclk = __cpm_get_mclk(); + new_mclk = __cpm_get_pllout() / div[(cfcr & CPM_CFCR_MFR_MASK) >> CPM_CFCR_MFR_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_CFCR), "r" (cfcr), "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 cfcr; + 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_CFCR2 = new_lcdpix_div - 1; + + cfcr = REG_CPM_CFCR; + cfcr &= ~CPM_CFCR_LFR_MASK; + cfcr |= ((new_lcd_div - 1) << CPM_CFCR_LFR_BIT); + cfcr |= CPM_CFCR_UPE; /* 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_CFCR), "r" (cfcr), "r" (wait), "r" (tmp)); +} + +static void jz_scale_pll(struct dpm_regs *regs) +{ + unsigned int plcr1; + 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}; + + plcr1 = REG_CPM_PLCR1; + plcr1 &= ~(regs->plcr1_mask | CPM_PLCR1_PLL1S | CPM_PLCR1_PLL1EN | CPM_PLCR1_PLL1ST_MASK); + regs->plcr1 &= ~CPM_PLCR1_PLL1EN; + plcr1 |= (regs->plcr1 | 0xff); + + /* Update some DRAM parameters before changing frequency */ + new_pll = JZ_EXTAL * ((plcr1>>23)+2) / ((((plcr1>>18)&0x1f)+2) * od[(plcr1>>16)&0x03]); + cur_mclk = __cpm_get_mclk(); + new_mclk = new_pll / div[(REG_CPM_CFCR>>16) & 0xf]; + + /* + * Update some SDRAM parameters + */ + jz_update_dram_prev(cur_mclk, new_mclk); + + /* + * Update PLL, align code to cache line. + */ + plcr1 |= CPM_PLCR1_PLL1EN; + __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_PLCR1), "r" (plcr1)); + + /* Update some other DRAM parameters after changing frequency */ + jz_update_dram_post(cur_mclk, new_mclk); +} +#endif + +static void jz4730_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; +#endif + /* + * Stop module clocks before scaling PLL + */ + __cpm_stop_eth(); + __cpm_stop_aic_pclk(); + __cpm_stop_aic_bitclk(); + + /* ... 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); + } + + /* + * Restart module clocks before scaling PLL + */ + __cpm_start_eth(); + __cpm_start_aic_pclk(); + __cpm_start_aic_bitclk(); + + /* ... add more as necessary */ + +#ifdef CHANGE_PLL + /* 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 jz4730_freq_get(unsigned int cpu) +{ + return (__cpm_get_iclk() / 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->cfcr_mask = CPM_CFCR_IFR_MASK | CPM_CFCR_SFR_MASK | CPM_CFCR_PFR_MASK | CPM_CFCR_MFR_MASK; + + new_freq = jz4730_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->cfcr = (n2FR[div[0]] << CPM_CFCR_IFR_BIT) | + (n2FR[div[1]] << CPM_CFCR_SFR_BIT) | + (n2FR[div[2]] << CPM_CFCR_PFR_BIT) | + (n2FR[div[3]] << CPM_CFCR_MFR_BIT); + + return div_of_cclk; +} + +static void jz4730_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_iclk(); + divisor = index_to_divisor(index, ®s); + + freqs.old = __cpm_get_iclk() / 1000; + freqs.new = __cpm_get_pllout() / (1000 * divisor); + freqs.cpu = cpu; + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + if (old_divisor != divisor) + jz4730_transition(®s); + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); +} + +static int jz4730_freq_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + unsigned int new_index = 0; + + if (cpufreq_frequency_table_target(policy, + &jz4730_freq_table.table[0], + target_freq, relation, &new_index)) + return -EINVAL; + + jz4730_set_cpu_divider_index(policy->cpu, new_index); + + dprintk("new frequency is %d KHz (REG_CPM_CFCR:0x%x)\n", __cpm_get_iclk() / 1000, REG_CPM_CFCR); + + return 0; +} + +static int jz4730_freq_verify(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, + &jz4730_freq_table.table[0]); +} + +static int __init jz4730_cpufreq_driver_init(struct cpufreq_policy *policy) +{ + + struct cpufreq_frequency_table *table = &jz4730_freq_table.table[0]; + unsigned int MAX_FREQ; + + dprintk(KERN_INFO "Jz4730 cpufreq driver\n"); + + if (policy->cpu != 0) + return -EINVAL; + + policy->cur = MAX_FREQ = __cpm_get_iclk() / 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; + + return cpufreq_frequency_table_cpuinfo(policy, table); +} + +static struct cpufreq_driver cpufreq_jz4730_driver = { +// .flags = CPUFREQ_STICKY, + .init = jz4730_cpufreq_driver_init, + .verify = jz4730_freq_verify, + .target = jz4730_freq_target, + .get = jz4730_freq_get, + .name = "jz4730", +}; + +static int __init jz4730_cpufreq_init(void) +{ + return cpufreq_register_driver(&cpufreq_jz4730_driver); +} + +static void __exit jz4730_cpufreq_exit(void) +{ + cpufreq_unregister_driver(&cpufreq_jz4730_driver); +} + +module_init(jz4730_cpufreq_init); +module_exit(jz4730_cpufreq_exit); + +MODULE_AUTHOR("Regen "); +MODULE_DESCRIPTION("cpufreq driver for Jz4730"); +MODULE_LICENSE("GPL"); + diff --git a/arch/mips/jz4730/dma.c b/arch/mips/jz4730/dma.c new file mode 100644 index 0000000..ca7d549 --- /dev/null +++ b/arch/mips/jz4730/dma.c @@ -0,0 +1,509 @@ +/* + * linux/arch/mips/jz4730/dma.c + * + * JZ4730 DMA PC-like APIs. + * + * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * 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[NUM_DMA] = { + {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[NUM_DMA_DEV] = { + {CPHYSADDR(UART0_BASE), DMA_8bit_TX_CONF|DMA_MODE_WRITE, DMAC_DRSR_RS_UART0OUT}, + {CPHYSADDR(UART0_BASE), DMA_8bit_RX_CONF|DMA_MODE_READ, DMAC_DRSR_RS_UART0IN}, + {CPHYSADDR(UART1_BASE), DMA_8bit_TX_CONF|DMA_MODE_WRITE, DMAC_DRSR_RS_UART1OUT}, + {CPHYSADDR(UART1_BASE), DMA_8bit_RX_CONF|DMA_MODE_READ, DMAC_DRSR_RS_UART1IN}, + {CPHYSADDR(UART2_BASE), DMA_8bit_TX_CONF|DMA_MODE_WRITE, DMAC_DRSR_RS_UART2OUT}, + {CPHYSADDR(UART2_BASE), DMA_8bit_RX_CONF|DMA_MODE_READ, DMAC_DRSR_RS_UART2IN}, + {CPHYSADDR(UART3_BASE), DMA_8bit_TX_CONF|DMA_MODE_WRITE, DMAC_DRSR_RS_UART3OUT}, + {CPHYSADDR(UART3_BASE), DMA_8bit_RX_CONF|DMA_MODE_READ, DMAC_DRSR_RS_UART3IN}, + {CPHYSADDR(SSI_DR), DMA_32bit_TX_CONF|DMA_MODE_WRITE, DMAC_DRSR_RS_SSIOUT}, + {CPHYSADDR(SSI_DR), DMA_32bit_RX_CONF|DMA_MODE_READ, DMAC_DRSR_RS_SSIIN}, + {CPHYSADDR(MSC_TXFIFO), DMA_32bit_TX_CONF|DMA_MODE_WRITE, DMAC_DRSR_RS_MSCOUT}, + {CPHYSADDR(MSC_RXFIFO), DMA_32bit_RX_CONF|DMA_MODE_READ, DMAC_DRSR_RS_MSCIN}, + {CPHYSADDR(AIC_DR), DMA_32bit_TX_CONF|DMA_MODE_WRITE, DMAC_DRSR_RS_AICOUT}, + {CPHYSADDR(AIC_DR), DMA_32bit_RX_CONF|DMA_MODE_READ, DMAC_DRSR_RS_AICIN}, + {0, DMA_AUTOINIT, 0}, +}; + + +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 < NUM_DMA; 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 > NUM_DMA) + return; + chan = &jz_dma_table[dmanr]; + + printk(KERN_INFO "DMA%d Register Dump:\n", dmanr); + printk(KERN_INFO " DMACR= 0x%08x\n", REG_DMAC_DMACR); + printk(KERN_INFO " DSAR = 0x%08x\n", REG_DMAC_DSAR(dmanr)); + printk(KERN_INFO " DDAR = 0x%08x\n", REG_DMAC_DDAR(dmanr)); + printk(KERN_INFO " DTCR = 0x%08x\n", REG_DMAC_DTCR(dmanr)); + printk(KERN_INFO " DRSR = 0x%08x\n", REG_DMAC_DRSR(dmanr)); + printk(KERN_INFO " DCCSR = 0x%08x\n", REG_DMAC_DCCSR(dmanr)); +} + + +/** + * jz_request_dma - dynamically allcate an idle DMA channel to return + * @dev_id: the specified dma device id or DMA_ID_RAW_REQ + * @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, + 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 >= NUM_DMA_DEV) + return -EINVAL; + + for (i = 0; i < NUM_DMA; i++) { + if (jz_dma_table[i].dev_id < 0) + break; + } + if (i == NUM_DMA) + return -ENODEV; + + chan = &jz_dma_table[i]; + + if (irqhandler) { + chan->irq = IRQ_DMA_0 + i; // see intc.h + chan->irq_dev = irq_dev_id; + if ((ret = request_irq(chan->irq, irqhandler, irqflags, + dev_str, chan->irq_dev))) { + chan->irq = 0; + chan->irq_dev = NULL; + return ret; + } + } else { + chan->irq = 0; + 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 = 0; + 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_DCCSR_DWDH_MASK; + switch (nbit) { + case 8: + chan->mode |= DMAC_DCCSR_DWDH_8; + break; + case 16: + chan->mode |= DMAC_DCCSR_DWDH_16; + break; + case 32: + chan->mode |= DMAC_DCCSR_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_DCCSR_SWDH_MASK; + switch (nbit) { + case 8: + chan->mode |= DMAC_DCCSR_SWDH_8; + break; + case 16: + chan->mode |= DMAC_DCCSR_SWDH_16; + break; + case 32: + chan->mode |= DMAC_DCCSR_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_DCCSR_DS_MASK; + switch (nbyte) { + case 1: + chan->mode |= DMAC_DCCSR_DS_8b; + break; + case 2: + chan->mode |= DMAC_DCCSR_DS_16b; + break; + case 4: + chan->mode |= DMAC_DCCSR_DS_32b; + break; + case 16: + chan->mode |= DMAC_DCCSR_DS_16B; + break; + case 32: + chan->mode |= DMAC_DCCSR_DS_32B; + break; + } +} + +/** + * 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_REQ, ...) 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 > NUM_DMA) + return -ENODEV; + for (i = 0; i < NUM_DMA; i++) { + if (jz_dma_table[i].dev_id < 0) + break; + } + if (i == NUM_DMA) + 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(chan->io) &= ~(DMAC_DCCSR_HLT | DMAC_DCCSR_TC | DMAC_DCCSR_AR); + __dmac_enable_channel(dmanr); + if (chan->irq) + __dmac_channel_enable_irq(dmanr); +} + +#define DMA_DISABLE_POLL 0x5000 + +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, DCCSR_MODE_MASK mask hw bits */ +void set_dma_mode(unsigned int dmanr, unsigned int mode) +{ + struct jz_dma_chan *chan = get_dma_chan(dmanr); + if (!chan) + return; + mode &= ~(DMAC_DCCSR_TC | DMAC_DCCSR_AR); + chan->mode |= mode & ~(DMAC_DCCSR_SAM | DMAC_DCCSR_EACKM | DMAC_DCCSR_DAM); + mode &= DMA_MODE_MASK; + if (mode == DMA_MODE_READ) { + chan->mode |= DMAC_DCCSR_DAM; + chan->mode &= ~DMAC_DCCSR_SAM; + } else if (mode == DMA_MODE_WRITE) { + chan->mode |= DMAC_DCCSR_SAM | DMAC_DCCSR_EACKM; + chan->mode &= ~DMAC_DCCSR_DAM; + } else { + printk(KERN_DEBUG "set_dma_mode() support DMA_MODE_READ or DMA_MODE_WRITE!\n"); + } + REG_DMAC_DCCSR(chan->io) = chan->mode & ~DMA_MODE_MASK; + REG_DMAC_DRSR(chan->io) = chan->source; +} + +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: + /* SNDRV_PCM_FORMAT_S8 burst mode : 32BIT */ + break; + case AFMT_S16_LE: + /* SNDRV_PCM_FORMAT_S16_LE burst mode : 16BYTE */ + if (mode == DMA_MODE_READ) { + mode &= ~(DMAC_DCCSR_TC | DMAC_DCCSR_AR); + chan->mode = DMA_AIC_32_16BYTE_RX_CMD | DMA_MODE_READ; + chan->mode |= mode & ~(DMAC_DCCSR_SAM | DMAC_DCCSR_EACKM | DMAC_DCCSR_DAM); + mode &= DMA_MODE_MASK; + chan->mode |= DMAC_DCCSR_DAM; + chan->mode &= ~DMAC_DCCSR_SAM; + } else if (mode == DMA_MODE_WRITE) { + mode &= ~(DMAC_DCCSR_TC | DMAC_DCCSR_AR); + chan->mode = DMA_AIC_32_16BYTE_TX_CMD | DMA_MODE_WRITE; + chan->mode |= mode & ~(DMAC_DCCSR_SAM | DMAC_DCCSR_EACKM |DMAC_DCCSR_DAM); + mode &= DMA_MODE_MASK; + chan->mode |= DMAC_DCCSR_SAM | DMAC_DCCSR_EACKM; + chan->mode &= ~DMAC_DCCSR_DAM; + } else + printk("jz_set_oss_dma() just supports DMA_MODE_READ or DMA_MODE_WRITE!\n"); + + REG_DMAC_DCCSR(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) { + mode &= ~(DMAC_DCCSR_TC | DMAC_DCCSR_AR); + chan->mode = DMA_AIC_16BYTE_RX_CMD | DMA_MODE_READ; + chan->mode |= mode & ~(DMAC_DCCSR_SAM | DMAC_DCCSR_EACKM | DMAC_DCCSR_DAM); + mode &= DMA_MODE_MASK; + chan->mode |= DMAC_DCCSR_DAM; + chan->mode &= ~DMAC_DCCSR_SAM; + } else if (mode == DMA_MODE_WRITE) { + mode &= ~(DMAC_DCCSR_TC | DMAC_DCCSR_AR); + chan->mode = DMA_AIC_16BYTE_TX_CMD | DMA_MODE_WRITE; + chan->mode |= mode & ~(DMAC_DCCSR_SAM | DMAC_DCCSR_EACKM | DMAC_DCCSR_DAM); + mode &= DMA_MODE_MASK; + chan->mode |= DMAC_DCCSR_SAM | DMAC_DCCSR_EACKM; + chan->mode &= ~DMAC_DCCSR_DAM; + } else + printk("jz_set_alsa_dma() just supports DMA_MODE_READ or DMA_MODE_WRITE!\n"); + + REG_DMAC_DCCSR(chan->io) = chan->mode & ~DMA_MODE_MASK; + REG_DMAC_DRSR(chan->io) = chan->source; + break; + } +} + +void set_dma_addr(unsigned int dmanr, unsigned int a) +{ + 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_DDAR(chan->io) = a; + } else if (mode == DMA_MODE_WRITE) { + REG_DMAC_DSAR(chan->io) = a; + REG_DMAC_DDAR(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 count) +{ + unsigned int mode; + int dma_ds[] = {4, 1, 2, 16, 32}; + struct jz_dma_chan *chan = get_dma_chan(dmanr); + if (!chan) + return; + mode = (chan->mode & DMAC_DCCSR_DS_MASK) >> DMAC_DCCSR_DS_BIT; + count = count / dma_ds[mode]; + REG_DMAC_DTCR(chan->io) = count; +} + +int get_dma_residue(unsigned int dmanr) +{ + int count; + unsigned int mode; + int dma_ds[] = {4, 1, 2, 16, 32}; + struct jz_dma_chan *chan = get_dma_chan(dmanr); + if (!chan) + return 0; + + mode = (chan->mode & DMAC_DCCSR_DS_MASK) >> DMAC_DCCSR_DS_BIT; + count = REG_DMAC_DTCR(chan->io); + count = count * dma_ds[mode]; + + return count; +} + +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); diff --git a/arch/mips/jz4730/i2c.c b/arch/mips/jz4730/i2c.c new file mode 100644 index 0000000..91f55a7 --- /dev/null +++ b/arch/mips/jz4730/i2c.c @@ -0,0 +1,214 @@ +/* + * linux/arch/mips/jz4730/i2c.c + * + * JZ4730 I2C APIs. + * + * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include + +/* 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; +} + +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.devclk, 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.devclk, 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; + 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++; + } + __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); diff --git a/arch/mips/jz4730/irq.c b/arch/mips/jz4730/irq.c new file mode 100644 index 0000000..658596f --- /dev/null +++ b/arch/mips/jz4730/irq.c @@ -0,0 +1,266 @@ +/* + * linux/arch/mips/jz4730/irq.c + * + * JZ4730 interrupt routines. + * + * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * INTC irq type + */ + +static void enable_intc_irq(unsigned int irq) +{ + __intc_unmask_irq(irq); +} + +static void disable_intc_irq(unsigned int irq) +{ + __intc_mask_irq(irq); +} + +static void mask_and_ack_intc_irq(unsigned int irq) +{ + __intc_mask_irq(irq); + __intc_ack_irq(irq); +} + +static void end_intc_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { + enable_intc_irq(irq); + } +} + +static unsigned int startup_intc_irq(unsigned int irq) +{ + enable_intc_irq(irq); + return 0; +} + +static void shutdown_intc_irq(unsigned int irq) +{ + disable_intc_irq(irq); +} + +static struct irq_chip intc_irq_type = { + .typename = "INTC", + .startup = startup_intc_irq, + .shutdown = shutdown_intc_irq, + .enable = enable_intc_irq, + .disable = disable_intc_irq, + .ack = mask_and_ack_intc_irq, + .end = end_intc_irq, +}; + +/* + * GPIO irq type + */ + +static void enable_gpio_irq(unsigned int irq) +{ + unsigned int intc_irq; + + if (irq < (IRQ_GPIO_0 + 32)) { + intc_irq = IRQ_GPIO0; + } + else if (irq < (IRQ_GPIO_0 + 64)) { + intc_irq = IRQ_GPIO1; + } + else if (irq < (IRQ_GPIO_0 + 96)) { + intc_irq = IRQ_GPIO2; + } + else { + intc_irq = IRQ_GPIO3; + } + + enable_intc_irq(intc_irq); + __gpio_unmask_irq(irq - IRQ_GPIO_0); +} + +static void disable_gpio_irq(unsigned int irq) +{ + __gpio_mask_irq(irq - IRQ_GPIO_0); +} + +static void mask_and_ack_gpio_irq(unsigned int irq) +{ + __gpio_mask_irq(irq - IRQ_GPIO_0); + __gpio_ack_irq(irq - IRQ_GPIO_0); +} + +static void end_gpio_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { + enable_gpio_irq(irq); + } +} + +static unsigned int startup_gpio_irq(unsigned int irq) +{ + enable_gpio_irq(irq); + return 0; +} + +static void shutdown_gpio_irq(unsigned int irq) +{ + disable_gpio_irq(irq); +} + +static struct irq_chip gpio_irq_type = { + .typename = "GPIO", + .startup = startup_gpio_irq, + .shutdown = shutdown_gpio_irq, + .enable = enable_gpio_irq, + .disable = disable_gpio_irq, + .ack = mask_and_ack_gpio_irq, + .end = end_gpio_irq, +}; + +/* + * 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 unsigned int startup_dma_irq(unsigned int irq) +{ + enable_dma_irq(irq); + return 0; +} + +static void shutdown_dma_irq(unsigned int irq) +{ + disable_dma_irq(irq); +} + +static struct irq_chip dma_irq_type = { + .typename = "DMA", + .startup = startup_dma_irq, + .shutdown = shutdown_dma_irq, + .enable = enable_dma_irq, + .disable = 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 */ + + /* Set up INTC irq + */ + for (i = 0; i < 32; i++) { + disable_intc_irq(i); + irq_desc[i].chip = &intc_irq_type; + } + + /* Set up DMAC irq + */ + for (i = 0; i < NUM_DMA; i++) { + disable_dma_irq(IRQ_DMA_0 + i); + irq_desc[IRQ_DMA_0 + i].chip = &dma_irq_type; + } + + /* Set up GPIO irq + */ + for (i = 0; i < NUM_GPIO; i++) { + disable_gpio_irq(IRQ_GPIO_0 + i); + irq_desc[IRQ_GPIO_0 + i].chip = &gpio_irq_type; + } +} + +static int plat_real_irq(int irq) +{ + switch (irq) { + case IRQ_GPIO0: + irq = __gpio_group_irq(0) + IRQ_GPIO_0; + break; + case IRQ_GPIO1: + irq = __gpio_group_irq(1) + IRQ_GPIO_0 + 32; + break; + case IRQ_GPIO2: + irq = __gpio_group_irq(2) + IRQ_GPIO_0 + 64; + break; + case IRQ_GPIO3: + irq = __gpio_group_irq(3) + IRQ_GPIO_0 + 96; + break; + case IRQ_DMAC: + irq = __dmac_get_irq() + IRQ_DMA_0; + break; + } + + return irq; +} + +asmlinkage void plat_irq_dispatch(void) +{ + int irq = 0; + static unsigned long intc_ipr = 0; + + intc_ipr |= REG_INTC_IPR; + + if (!intc_ipr) return; + + irq = ffs(intc_ipr) - 1; + intc_ipr &= ~(1< + * + * 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 +#include +#include +#include +#include + +#include + +/* 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, +}; + +/* All */ +static struct platform_device *jz_platform_devices[] __initdata = { + &jz_usb_ohci_device, + &jz_lcd_device, + &jz_usb_gdt_device, + &jz_mmc_device, +}; + +static int __init jz_platform_init(void) +{ + return platform_add_devices(jz_platform_devices, ARRAY_SIZE(jz_platform_devices)); +} + +arch_initcall(jz_platform_init); diff --git a/arch/mips/jz4730/pm.c b/arch/mips/jz4730/pm.c new file mode 100644 index 0000000..a46e9c3 --- /dev/null +++ b/arch/mips/jz4730/pm.c @@ -0,0 +1,1098 @@ +/* + * linux/arch/mips/jz4730/pm.c + * + * Jz4730 Power Management Routines + * + * Copyright 2005 Ingenic Semiconductor + * Wei Jianli + * Huang Lihong + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +extern void jz_cpu_suspend(void); +extern void jz_cpu_resume(void); + +static void jz_board_pm_suspend(void); + +#define SAVE(x,s) sleep_save[SLEEP_SAVE_##x] = REG##s(x) +#define RESTORE(x,s) REG##s(x) = sleep_save[SLEEP_SAVE_##x] + +/* + * List of global jz4730 peripheral registers to preserve. + * More ones like core register and general purpose register values + * are preserved with the stack pointer in sleep.S. + */ +enum { SLEEP_SAVE_START = 0, + + /* CPM */ + SLEEP_SAVE_CPM_MSCR, SLEEP_SAVE_CPM_PLCR1, + + /* WDT */ + SLEEP_SAVE_WDT_WTCNT, SLEEP_SAVE_WDT_WTCSR, + + /* OST */ + SLEEP_SAVE_OST_TER, + SLEEP_SAVE_OST_TCSR0, SLEEP_SAVE_OST_TCSR1, SLEEP_SAVE_OST_TCSR2, + SLEEP_SAVE_OST_TRDR0, SLEEP_SAVE_OST_TRDR1, SLEEP_SAVE_OST_TRDR2, + SLEEP_SAVE_OST_TCNT0, SLEEP_SAVE_OST_TCNT1, SLEEP_SAVE_OST_TCNT2, + + /* HARB */ + SLEEP_SAVE_HARB_HAPOR, SLEEP_SAVE_HARB_HMCTR, SLEEP_SAVE_HARB_HMLTR, + + /* EMC */ + SLEEP_SAVE_EMC_SMCR0, SLEEP_SAVE_EMC_SMCR1, SLEEP_SAVE_EMC_SMCR2, SLEEP_SAVE_EMC_SMCR3, + SLEEP_SAVE_EMC_SMCR4, SLEEP_SAVE_EMC_SMCR5, + + /* GPIO */ + SLEEP_SAVE_GPIO_GPDR0, SLEEP_SAVE_GPIO_GPDR1, SLEEP_SAVE_GPIO_GPDR2, SLEEP_SAVE_GPIO_GPDR3, + SLEEP_SAVE_GPIO_GPDIR0, SLEEP_SAVE_GPIO_GPDIR1, SLEEP_SAVE_GPIO_GPDIR2, SLEEP_SAVE_GPIO_GPDIR3, + SLEEP_SAVE_GPIO_GPODR0, SLEEP_SAVE_GPIO_GPODR1, SLEEP_SAVE_GPIO_GPODR2, SLEEP_SAVE_GPIO_GPODR3, + SLEEP_SAVE_GPIO_GPPUR0, SLEEP_SAVE_GPIO_GPPUR1, SLEEP_SAVE_GPIO_GPPUR2, SLEEP_SAVE_GPIO_GPPUR3, + SLEEP_SAVE_GPIO_GPALR0, SLEEP_SAVE_GPIO_GPALR1, SLEEP_SAVE_GPIO_GPALR2, SLEEP_SAVE_GPIO_GPALR3, + SLEEP_SAVE_GPIO_GPAUR0, SLEEP_SAVE_GPIO_GPAUR1, SLEEP_SAVE_GPIO_GPAUR2, SLEEP_SAVE_GPIO_GPAUR3, + SLEEP_SAVE_GPIO_GPIDLR0, SLEEP_SAVE_GPIO_GPIDLR1, SLEEP_SAVE_GPIO_GPIDLR2, SLEEP_SAVE_GPIO_GPIDLR3, + SLEEP_SAVE_GPIO_GPIDUR0, SLEEP_SAVE_GPIO_GPIDUR1, SLEEP_SAVE_GPIO_GPIDUR2, SLEEP_SAVE_GPIO_GPIDUR3, + SLEEP_SAVE_GPIO_GPIER0, SLEEP_SAVE_GPIO_GPIER1, SLEEP_SAVE_GPIO_GPIER2, SLEEP_SAVE_GPIO_GPIER3, + SLEEP_SAVE_GPIO_GPIMR0, SLEEP_SAVE_GPIO_GPIMR1, SLEEP_SAVE_GPIO_GPIMR2, SLEEP_SAVE_GPIO_GPIMR3, + SLEEP_SAVE_GPIO_GPFR0, SLEEP_SAVE_GPIO_GPFR1, SLEEP_SAVE_GPIO_GPFR2, SLEEP_SAVE_GPIO_GPFR3, + + /* UART(0-3) */ + SLEEP_SAVE_UART0_IER, SLEEP_SAVE_UART0_LCR, SLEEP_SAVE_UART0_MCR, SLEEP_SAVE_UART0_SPR, SLEEP_SAVE_UART0_DLLR, SLEEP_SAVE_UART0_DLHR, + SLEEP_SAVE_UART1_IER, SLEEP_SAVE_UART1_LCR, SLEEP_SAVE_UART1_MCR, SLEEP_SAVE_UART1_SPR, SLEEP_SAVE_UART1_DLLR, SLEEP_SAVE_UART1_DLHR, + SLEEP_SAVE_UART2_IER, SLEEP_SAVE_UART2_LCR, SLEEP_SAVE_UART2_MCR, SLEEP_SAVE_UART2_SPR, SLEEP_SAVE_UART2_DLLR, SLEEP_SAVE_UART2_DLHR, + SLEEP_SAVE_UART3_IER, SLEEP_SAVE_UART3_LCR, SLEEP_SAVE_UART3_MCR, SLEEP_SAVE_UART3_SPR, SLEEP_SAVE_UART3_DLLR, SLEEP_SAVE_UART3_DLHR, + + /* DMAC */ + SLEEP_SAVE_DMAC_DMACR, + SLEEP_SAVE_DMAC_DSAR0, SLEEP_SAVE_DMAC_DSAR1, SLEEP_SAVE_DMAC_DSAR2, SLEEP_SAVE_DMAC_DSAR3, SLEEP_SAVE_DMAC_DSAR4, SLEEP_SAVE_DMAC_DSAR5, SLEEP_SAVE_DMAC_DSAR6, SLEEP_SAVE_DMAC_DSAR7, + SLEEP_SAVE_DMAC_DDAR0, SLEEP_SAVE_DMAC_DDAR1, SLEEP_SAVE_DMAC_DDAR2, SLEEP_SAVE_DMAC_DDAR3, SLEEP_SAVE_DMAC_DDAR4, SLEEP_SAVE_DMAC_DDAR5, SLEEP_SAVE_DMAC_DDAR6, SLEEP_SAVE_DMAC_DDAR7, + SLEEP_SAVE_DMAC_DTCR0, SLEEP_SAVE_DMAC_DTCR1, SLEEP_SAVE_DMAC_DTCR2, SLEEP_SAVE_DMAC_DTCR3, SLEEP_SAVE_DMAC_DTCR4, SLEEP_SAVE_DMAC_DTCR5, SLEEP_SAVE_DMAC_DTCR6, SLEEP_SAVE_DMAC_DTCR7, + SLEEP_SAVE_DMAC_DRSR0, SLEEP_SAVE_DMAC_DRSR1, SLEEP_SAVE_DMAC_DRSR2, SLEEP_SAVE_DMAC_DRSR3, SLEEP_SAVE_DMAC_DRSR4, SLEEP_SAVE_DMAC_DRSR5, SLEEP_SAVE_DMAC_DRSR6, SLEEP_SAVE_DMAC_DRSR7, + SLEEP_SAVE_DMAC_DCCSR0, SLEEP_SAVE_DMAC_DCCSR1, SLEEP_SAVE_DMAC_DCCSR2, SLEEP_SAVE_DMAC_DCCSR3, SLEEP_SAVE_DMAC_DCCSR4, SLEEP_SAVE_DMAC_DCCSR5, SLEEP_SAVE_DMAC_DCCSR6, SLEEP_SAVE_DMAC_DCCSR7, + + /* INTC */ + SLEEP_SAVE_INTC_IPR, SLEEP_SAVE_INTC_ISR, SLEEP_SAVE_INTC_IMR, + + /* Checksum */ + SLEEP_SAVE_CKSUM, + + SLEEP_SAVE_SIZE +}; + +static unsigned long sleep_save[SLEEP_SAVE_SIZE]; + +static int jz_pm_do_suspend(void) +{ + unsigned long checksum = 0; + unsigned long imr = REG_INTC_IMR; + int i; + + printk("Put cpu into suspend mode.\n"); + + /* Mask all interrupts */ + REG_INTC_IMSR = 0xffffffff; + + /* Preserve current time */ + REG_RTC_RSR = xtime.tv_sec; + + REG_CPM_OCR |= CPM_OCR_SUSPEND_PHY0; /* suspend USB PHY 0 */ + REG_CPM_OCR |= CPM_OCR_SUSPEND_PHY1; /* suspend USB PHY 1 */ + REG_CPM_OCR |= CPM_OCR_EXT_RTC_CLK; /* select the external RTC clock (32.768KHz) */ + + /* Disable NAND ctroller */ + REG_EMC_NFCSR &= ~(EMC_NFCSR_NFE | EMC_NFCSR_FCE); + + /* + * Temporary solution. This won't be necessary once + * we move this support into the device drivers. + * Save the on-chip modules + */ + SAVE(UART0_LCR, 8); SAVE(UART0_MCR, 8); SAVE(UART0_SPR, 8); + REG8(UART0_LCR) |= UARTLCR_DLAB; /* Access to DLLR/DLHR */ + SAVE(UART0_DLLR, 8); SAVE(UART0_DLHR, 8); + REG8(UART0_LCR) &= ~UARTLCR_DLAB; /* Access to IER */ + SAVE(UART0_IER, 8); + + SAVE(UART1_LCR, 8); SAVE(UART1_MCR, 8); SAVE(UART1_SPR, 8); + REG8(UART1_LCR) |= UARTLCR_DLAB; /* Access to DLLR/DLHR */ + SAVE(UART1_DLLR, 8); SAVE(UART1_DLHR, 8); + REG8(UART1_LCR) &= ~UARTLCR_DLAB; /* Access to IER */ + SAVE(UART1_IER, 8); + + SAVE(UART2_LCR, 8); SAVE(UART2_MCR, 8); SAVE(UART2_SPR, 8); + REG8(UART2_LCR) |= UARTLCR_DLAB; /* Access to DLLR/DLHR */ + SAVE(UART2_DLLR, 8); SAVE(UART2_DLHR, 8); + REG8(UART2_LCR) &= ~UARTLCR_DLAB; /* Access to IER */ + SAVE(UART2_IER, 8); + + SAVE(UART3_LCR, 8); SAVE(UART3_MCR, 8); SAVE(UART3_SPR, 8); + REG8(UART3_LCR) |= UARTLCR_DLAB; /* Access to DLLR/DLHR */ + SAVE(UART3_DLLR, 8); SAVE(UART3_DLHR, 8); + REG8(UART3_LCR) &= ~UARTLCR_DLAB; /* Access to IER */ + SAVE(UART3_IER, 8); + + /* Save vital registers */ + + SAVE(OST_TER, 8); + SAVE(OST_TCSR0, 16); SAVE(OST_TCSR1, 16); SAVE(OST_TCSR2, 16); + SAVE(OST_TRDR0, 32); SAVE(OST_TRDR1, 32); SAVE(OST_TRDR2, 32); + SAVE(OST_TCNT0, 32); SAVE(OST_TCNT1, 32); SAVE(OST_TCNT2, 32); + + SAVE(HARB_HAPOR, 32); SAVE(HARB_HMCTR, 32); SAVE(HARB_HMLTR, 32); + + SAVE(EMC_SMCR0, 32); SAVE(EMC_SMCR1, 32); SAVE(EMC_SMCR2, 32); SAVE(EMC_SMCR3, 32); + SAVE(EMC_SMCR4, 32); SAVE(EMC_SMCR5, 32); + + SAVE(GPIO_GPDR0, 32); SAVE(GPIO_GPDR1, 32); SAVE(GPIO_GPDR2, 32); + SAVE(GPIO_GPDR3, 32); + SAVE(GPIO_GPDIR0, 32); SAVE(GPIO_GPDIR1, 32); SAVE(GPIO_GPDIR2, 32); + SAVE(GPIO_GPDIR3, 32); + SAVE(GPIO_GPODR0, 32); SAVE(GPIO_GPODR1, 32); SAVE(GPIO_GPODR2, 32); + SAVE(GPIO_GPODR3, 32); + SAVE(GPIO_GPPUR0, 32); SAVE(GPIO_GPPUR1, 32); SAVE(GPIO_GPPUR2, 32); + SAVE(GPIO_GPPUR3, 32); + SAVE(GPIO_GPALR0, 32); SAVE(GPIO_GPALR1, 32); SAVE(GPIO_GPALR2, 32); + SAVE(GPIO_GPALR3, 32); + SAVE(GPIO_GPAUR0, 32); SAVE(GPIO_GPAUR1, 32); SAVE(GPIO_GPAUR2, 32); + SAVE(GPIO_GPAUR3, 32); + SAVE(GPIO_GPIDLR0, 32); SAVE(GPIO_GPIDLR1, 32); SAVE(GPIO_GPIDLR2, 32); + SAVE(GPIO_GPIDLR3, 32); + SAVE(GPIO_GPIDUR0, 32); SAVE(GPIO_GPIDUR1, 32); SAVE(GPIO_GPIDUR2, 32); + SAVE(GPIO_GPIDUR3, 32); + SAVE(GPIO_GPIER0, 32); SAVE(GPIO_GPIER1, 32); SAVE(GPIO_GPIER2, 32); + SAVE(GPIO_GPIER3, 32); + SAVE(GPIO_GPIMR0, 32); SAVE(GPIO_GPIMR1, 32); SAVE(GPIO_GPIMR2, 32); + SAVE(GPIO_GPIMR3, 32); + SAVE(GPIO_GPFR0, 32); SAVE(GPIO_GPFR1, 32); SAVE(GPIO_GPFR2, 32); + SAVE(GPIO_GPFR3, 32); + + SAVE(DMAC_DMACR, 32); + SAVE(DMAC_DSAR0, 32); SAVE(DMAC_DSAR1, 32); SAVE(DMAC_DSAR2, 32); SAVE(DMAC_DSAR3, 32); SAVE(DMAC_DSAR4, 32); SAVE(DMAC_DSAR5, 32); SAVE(DMAC_DSAR6, 32); SAVE(DMAC_DSAR7, 32); + SAVE(DMAC_DDAR0, 32); SAVE(DMAC_DDAR1, 32); SAVE(DMAC_DDAR2, 32); SAVE(DMAC_DDAR3, 32); SAVE(DMAC_DDAR4, 32); SAVE(DMAC_DDAR5, 32); SAVE(DMAC_DDAR6, 32); SAVE(DMAC_DDAR7, 32); + SAVE(DMAC_DTCR0, 32); SAVE(DMAC_DTCR1, 32); SAVE(DMAC_DTCR2, 32); SAVE(DMAC_DTCR3, 32); SAVE(DMAC_DTCR4, 32); SAVE(DMAC_DTCR5, 32); SAVE(DMAC_DTCR6, 32); SAVE(DMAC_DTCR7, 32); + SAVE(DMAC_DRSR0, 32); SAVE(DMAC_DRSR1, 32); SAVE(DMAC_DRSR2, 32); SAVE(DMAC_DRSR3, 32); SAVE(DMAC_DRSR4, 32); SAVE(DMAC_DRSR5, 32); SAVE(DMAC_DRSR6, 32); SAVE(DMAC_DRSR7, 32); + SAVE(DMAC_DCCSR0, 32); SAVE(DMAC_DCCSR1, 32); SAVE(DMAC_DCCSR2, 32); SAVE(DMAC_DCCSR3, 32); SAVE(DMAC_DCCSR4, 32); SAVE(DMAC_DCCSR5, 32); SAVE(DMAC_DCCSR6, 32); SAVE(DMAC_DCCSR7, 32); + + SAVE(INTC_IPR, 32);SAVE(INTC_ISR, 32);SAVE(INTC_IMR, 32); + + SAVE(WDT_WTCNT, 32);SAVE(WDT_WTCSR, 8); + + /* Mask all interrupts */ + REG_INTC_IMSR = 0xffffffff; + + /* Save module clocks */ + SAVE(CPM_MSCR, 32); + + /* Save PLL */ + SAVE(CPM_PLCR1, 32); + + /* Stop module clocks */ + __cpm_stop_uart0(); + __cpm_stop_uart1(); + __cpm_stop_uart2(); + __cpm_stop_uart3(); + __cpm_stop_uhc(); + __cpm_stop_udc(); + __cpm_stop_eth(); + __cpm_stop_cim(); + __cpm_stop_kbc(); + __cpm_stop_scc(); + __cpm_stop_ssi(); + __cpm_stop_ost(); + + /* platform-specific pm routine */ + jz_board_pm_suspend(); + + /* Clear previous reset status */ + REG_CPM_RSTR &= ~(CPM_RSTR_HR | CPM_RSTR_WR | CPM_RSTR_SR); + + /* Set resume return address */ + REG_CPM_SPR = virt_to_phys(jz_cpu_resume); + + /* Before sleeping, calculate and save a checksum */ + for (i = 0; i < SLEEP_SAVE_SIZE - 1; i++) + checksum += sleep_save[i]; + sleep_save[SLEEP_SAVE_CKSUM] = checksum; + + /* *** go zzz *** */ + jz_cpu_suspend(); +#if 0 + /* after sleeping, validate the checksum */ + checksum = 0; + for (i = 0; i < SLEEP_SAVE_SIZE - 1; i++) + checksum += sleep_save[i]; + + /* if invalid, display message and wait for a hardware reset */ + if (checksum != sleep_save[SLEEP_SAVE_CKSUM]) { + /** Add platform-specific message display codes here **/ + while (1); + } +#endif + /* Restore PLL */ + RESTORE(CPM_PLCR1, 32); + + /* Restore module clocks */ + RESTORE(CPM_MSCR, 32); + + /* Ensure not to come back here if it wasn't intended */ + REG_CPM_SPR = 0; + + /* Restore registers */ + + RESTORE(GPIO_GPDR0, 32); RESTORE(GPIO_GPDR1, 32); RESTORE(GPIO_GPDR2, 32); + RESTORE(GPIO_GPDR3, 32); + RESTORE(GPIO_GPDIR0, 32); RESTORE(GPIO_GPDIR1, 32); RESTORE(GPIO_GPDIR2, 32); + RESTORE(GPIO_GPDIR3, 32); + RESTORE(GPIO_GPODR0, 32); RESTORE(GPIO_GPODR1, 32); RESTORE(GPIO_GPODR2, 32); + RESTORE(GPIO_GPODR3, 32); + RESTORE(GPIO_GPPUR0, 32); RESTORE(GPIO_GPPUR1, 32); RESTORE(GPIO_GPPUR2, 32); + RESTORE(GPIO_GPPUR3, 32); + RESTORE(GPIO_GPALR0, 32); RESTORE(GPIO_GPALR1, 32); RESTORE(GPIO_GPALR2, 32); + RESTORE(GPIO_GPALR3, 32); + RESTORE(GPIO_GPAUR0, 32); RESTORE(GPIO_GPAUR1, 32); RESTORE(GPIO_GPAUR2, 32); + RESTORE(GPIO_GPAUR3, 32); + RESTORE(GPIO_GPIDLR0, 32);RESTORE(GPIO_GPIDLR1, 32);RESTORE(GPIO_GPIDLR2, 32); + RESTORE(GPIO_GPIDLR3, 32); + RESTORE(GPIO_GPIDUR0, 32);RESTORE(GPIO_GPIDUR1, 32);RESTORE(GPIO_GPIDUR2, 32); + RESTORE(GPIO_GPIDUR3, 32); + RESTORE(GPIO_GPIER0, 32); RESTORE(GPIO_GPIER1, 32); RESTORE(GPIO_GPIER2, 32); + RESTORE(GPIO_GPIER3, 32); + RESTORE(GPIO_GPIMR0, 32); RESTORE(GPIO_GPIMR1, 32); RESTORE(GPIO_GPIMR2, 32); + RESTORE(GPIO_GPIMR3, 32); + RESTORE(GPIO_GPFR0, 32); RESTORE(GPIO_GPFR1, 32); RESTORE(GPIO_GPFR2, 32); + RESTORE(GPIO_GPFR3, 32); + + RESTORE(EMC_SMCR0, 32); RESTORE(EMC_SMCR1, 32); RESTORE(EMC_SMCR2, 32); RESTORE(EMC_SMCR3, 32); + RESTORE(EMC_SMCR4, 32); RESTORE(EMC_SMCR5, 32); + + RESTORE(HARB_HAPOR, 32); RESTORE(HARB_HMCTR, 32); RESTORE(HARB_HMLTR, 32); + + RESTORE(OST_TCNT0, 32); RESTORE(OST_TCNT1, 32); RESTORE(OST_TCNT2, 32); + RESTORE(OST_TRDR0, 32); RESTORE(OST_TRDR1, 32); RESTORE(OST_TRDR2, 32); + RESTORE(OST_TCSR0, 16); RESTORE(OST_TCSR1, 16); RESTORE(OST_TCSR2, 16); + RESTORE(OST_TER, 8); + + RESTORE(DMAC_DMACR, 32); + RESTORE(DMAC_DSAR0, 32); RESTORE(DMAC_DSAR1, 32); RESTORE(DMAC_DSAR2, 32); RESTORE(DMAC_DSAR3, 32); RESTORE(DMAC_DSAR4, 32); RESTORE(DMAC_DSAR5, 32); RESTORE(DMAC_DSAR6, 32); RESTORE(DMAC_DSAR7, 32); + RESTORE(DMAC_DDAR0, 32); RESTORE(DMAC_DDAR1, 32); RESTORE(DMAC_DDAR2, 32); RESTORE(DMAC_DDAR3, 32); RESTORE(DMAC_DDAR4, 32); RESTORE(DMAC_DDAR5, 32); RESTORE(DMAC_DDAR6, 32); RESTORE(DMAC_DDAR7, 32); + RESTORE(DMAC_DTCR0, 32); RESTORE(DMAC_DTCR1, 32); RESTORE(DMAC_DTCR2, 32); RESTORE(DMAC_DTCR3, 32); RESTORE(DMAC_DTCR4, 32); RESTORE(DMAC_DTCR5, 32); RESTORE(DMAC_DTCR6, 32); RESTORE(DMAC_DTCR7, 32); + RESTORE(DMAC_DRSR0, 32); RESTORE(DMAC_DRSR1, 32); RESTORE(DMAC_DRSR2, 32); RESTORE(DMAC_DRSR3, 32); RESTORE(DMAC_DRSR4, 32); RESTORE(DMAC_DRSR5, 32); RESTORE(DMAC_DRSR6, 32); RESTORE(DMAC_DRSR7, 32); + RESTORE(DMAC_DCCSR0, 32); RESTORE(DMAC_DCCSR1, 32); RESTORE(DMAC_DCCSR2, 32); RESTORE(DMAC_DCCSR3, 32); RESTORE(DMAC_DCCSR4, 32); RESTORE(DMAC_DCCSR5, 32); RESTORE(DMAC_DCCSR6, 32); RESTORE(DMAC_DCCSR7, 32); + + RESTORE(INTC_IPR, 32);RESTORE(INTC_ISR, 32);RESTORE(INTC_IMR, 32); + + REG_WDT_WTCNT = 0; RESTORE(WDT_WTCSR, 8); + + /* + * Temporary solution. This won't be necessary once + * we move this support into the device drivers. + * Restore the on-chip modules. + */ + + /* FIFO control reg, write-only */ + REG8(UART0_FCR) = UARTFCR_FE | UARTFCR_RFLS | UARTFCR_TFLS | UARTFCR_UUE; + REG8(UART1_FCR) = UARTFCR_FE | UARTFCR_RFLS | UARTFCR_TFLS | UARTFCR_UUE; + REG8(UART2_FCR) = UARTFCR_FE | UARTFCR_RFLS | UARTFCR_TFLS | UARTFCR_UUE; + REG8(UART3_FCR) = UARTFCR_FE | UARTFCR_RFLS | UARTFCR_TFLS | UARTFCR_UUE; + + REG8(UART0_LCR) |= UARTLCR_DLAB; /* Access to DLLR/DLHR */ + RESTORE(UART0_DLLR, 8); RESTORE(UART0_DLHR, 8); + REG8(UART0_LCR) &= ~UARTLCR_DLAB; /* Access to IER */ + RESTORE(UART0_IER, 8); + RESTORE(UART0_MCR, 8); RESTORE(UART0_SPR, 8); RESTORE(UART0_LCR, 8); + + REG8(UART1_LCR) |= UARTLCR_DLAB; /* Access to DLLR/DLHR */ + RESTORE(UART1_DLLR, 8); RESTORE(UART1_DLHR, 8); + REG8(UART1_LCR) &= ~UARTLCR_DLAB; /* Access to IER */ + RESTORE(UART1_IER, 8); + RESTORE(UART1_MCR, 8); RESTORE(UART1_SPR, 8); RESTORE(UART1_LCR, 8); + + REG8(UART2_LCR) |= UARTLCR_DLAB; /* Access to DLLR/DLHR */ + RESTORE(UART2_DLLR, 8); RESTORE(UART2_DLHR, 8); + REG8(UART2_LCR) &= ~UARTLCR_DLAB; /* Access to IER */ + RESTORE(UART2_IER, 8); + RESTORE(UART2_MCR, 8); RESTORE(UART2_SPR, 8); RESTORE(UART2_LCR, 8); + + REG8(UART3_LCR) |= UARTLCR_DLAB; /* Access to DLLR/DLHR */ + RESTORE(UART3_DLLR, 8); RESTORE(UART3_DLHR, 8); + REG8(UART3_LCR) &= ~UARTLCR_DLAB; /* Access to IER */ + RESTORE(UART3_IER, 8); + RESTORE(UART3_MCR, 8); RESTORE(UART3_SPR, 8); RESTORE(UART3_LCR, 8); + + REG_CPM_OCR &= ~CPM_OCR_SUSPEND_PHY0; /* resume USB PHY 0 */ + REG_CPM_OCR &= ~CPM_OCR_SUSPEND_PHY1; /* resume USB PHY 1 */ +#if 0 + REG_CPM_OCR &= ~CPM_OCR_EXT_RTC_CLK; /* use internal RTC clock (JZ_EXTAL/128 Hz) */ +#else + REG_CPM_OCR |= CPM_OCR_EXT_RTC_CLK; /* use external RTC clock (32.768 KHz) */ +#endif + + /* Enable NAND ctroller */ + REG_EMC_NFCSR |= EMC_NFCSR_NFE; + + /* Restore current time */ + xtime.tv_sec = REG_RTC_RSR; + + /* Restore interrupts */ + REG_INTC_IMSR = imr; + REG_INTC_IMCR = ~imr; + + 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 chipset should be set as pull-disable. + */ +static void jz_board_pm_gpio_setup(void) +{ + /* CIM_D0(IN)/PULL-UP/GP0 */ + __gpio_as_input(0); + __gpio_enable_pull(0); + + /* CIM_D1(IN)/PULL-UP/GP1 */ + __gpio_as_input(1); + __gpio_enable_pull(1); + + /* CIM_D2(IN)/PULL-UP/GP2 */ + __gpio_as_input(2); + __gpio_enable_pull(2); + + /* CIM_D3(IN)/PULL-UP/GP3 */ + __gpio_as_input(3); + __gpio_enable_pull(3); + + /* CIM_D4(IN)/PULL-DOWN/GP4 */ + __gpio_as_input(4); + __gpio_enable_pull(4); + + /* CIM_D5(IN)/PULL-DOWN/GP5 */ + __gpio_as_input(5); + __gpio_enable_pull(5); + + /* CIM_D6(IN)/PULL-DOWN/GP6 */ + __gpio_as_input(6); + __gpio_enable_pull(6); + + /* CIM_D7(IN)/PULL-DOWN/GP7 */ + __gpio_as_input(7); + __gpio_enable_pull(7); + + /* CIM_VSYNC(IN)/PULL-DOWN/GP8 */ + __gpio_as_input(8); + __gpio_enable_pull(8); + + /* CIM_HSYNC(IN)/PULL-UP/GP9 */ + __gpio_as_input(9); + __gpio_enable_pull(9); + + /* CIM_PCLK(IN)/PULL-DOWN/GP10 */ + __gpio_as_input(10); + __gpio_enable_pull(10); + + /* CIM_MCLK(OUT)/PULL-DOWN/GP11 */ + __gpio_as_input(11); + __gpio_enable_pull(11); + + /* DMA_DREQ0(IN)/CHIP_MODE/PULL-UP/GP12 */ + __gpio_as_input(12); + __gpio_enable_pull(12); + + /* DMA_DACK0(OUT)/PULL-UP/GP13 */ /* GPIO13 */ + __gpio_as_input(13); + __gpio_disable_pull(13); + + /* GP14 */ + /* GP15 */ + + /* RXD3(IN)/PULL-UP/GP16 */ + __gpio_as_input(16); + __gpio_enable_pull(16); + + /* CTS3(IN)/PULL-UP/GP17 */ + __gpio_as_input(17); + __gpio_enable_pull(17); + + /* GP18 */ + /* GP19 */ + /* GP20 */ + + /* TXD3(OUT)/PULL-UP/GP21 */ + __gpio_as_input(21); + __gpio_enable_pull(21); + + /* GP22 */ + + /* RTS3(OUT)/PULL-UP/GP23 */ + __gpio_as_input(23); + __gpio_enable_pull(23); + + /* RXD1(IN)/PULL-UP/GP24 */ /* IR_RXD */ + __gpio_as_input(24); + __gpio_enable_pull(24); + + /* TXD1(OUT)/PULL-UP/GP25 */ /* IR_TXD */ + __gpio_disable_pull(25); + __gpio_as_output(25); + __cpm_set_pin(25); + + /* DMA_AEN(OUT)/PULL-UP/GP26 */ /* CIM_PWD_N */ + __gpio_as_input(26); + __gpio_disable_pull(26); + + /* DMA_EOP(OUT)/PULL-UP/GP27 */ /* SW4 */ + __gpio_as_input(27); + __gpio_disable_pull(27); + + /* USB_CLK(IN)/PULL-UP/GP28 */ + __gpio_as_input(28); + __gpio_disable_pull(28); + + /* USB_PPWR0(OUT)/PULL-UP/GP29 */ /* USB_CLK_EN */ + __gpio_disable_pull(29); + __gpio_as_output(29); + __cpm_clear_pin(29); /* disable USB 48MHz clock */ + + /* GP30 */ + /* GP31 */ + + /* PS2_KCLK(IO)/PULL-UP/GP32 */ + __gpio_as_input(32); + __gpio_enable_pull(32); + + /* PS2_KDATA(IO)/PULL-UP/GP33 */ /* CIM_RST */ + __gpio_as_input(33); + __gpio_enable_pull(33); + + /* MSC_D0(IO)/PULL-UP/GP34 */ + __gpio_as_input(34); + __gpio_disable_pull(34); + + /* MSC_D1(IO)/PULL-UP/GP35 */ + __gpio_as_input(35); + __gpio_disable_pull(35); + + /* MSC_D2(IO)/PULL-UP/GP36 */ + __gpio_as_input(36); + __gpio_disable_pull(36); + + /* MSC_D3(IO)/PULL-UP/GP37 */ + __gpio_as_input(37); + __gpio_disable_pull(37); + + /* MSC_CMD(IO)/PULL-UP/GP38 */ + __gpio_as_input(38); + __gpio_disable_pull(38); + + /* MSC_CLK(OUT)/PULL-UP/GP39 */ + __gpio_as_input(39); + __gpio_enable_pull(39); + + /* LCD_D0(OUT)/PULL-UP/GP40 */ + __gpio_as_input(40); + __gpio_enable_pull(40); + + /* LCD_D1(OUT)/PULL-UP/GP41 */ + __gpio_as_input(41); + __gpio_enable_pull(41); + + /* LCD_D2(OUT)/PULL-UP/GP42 */ + __gpio_as_input(42); + __gpio_enable_pull(42); + + /* LCD_D3(OUT)/PULL-UP/GP43 */ + __gpio_as_input(43); + __gpio_enable_pull(43); + + /* LCD_D4(OUT)/PULL-UP/GP44 */ + __gpio_as_input(44); + __gpio_enable_pull(44); + + /* LCD_D5(OUT)/PULL-UP/GP45 */ + __gpio_as_input(45); + __gpio_enable_pull(45); + + /* LCD_D6(OUT)/PULL-UP/GP46 */ + __gpio_as_input(46); + __gpio_enable_pull(46); + + /* LCD_D7(OUT)/PULL-UP/GP47 */ + __gpio_as_input(47); + __gpio_enable_pull(47); + + /* LCD_D8(OUT)/PULL-DOWN/GP48 */ + __gpio_as_input(48); + __gpio_enable_pull(48); + + /* LCD_D9(OUT)/PULL-DOWN/GP49 */ + __gpio_as_input(49); + __gpio_enable_pull(49); + + /* LCD_D10(OUT)/PULL-DOWN/GP50 */ + __gpio_as_input(50); + __gpio_enable_pull(50); + + /* LCD_D11(OUT)/PULL-DOWN/GP51 */ + __gpio_as_input(51); + __gpio_enable_pull(51); + + /* LCD_D12(OUT)/PULL-DOWN/GP52 */ + __gpio_as_input(52); + __gpio_enable_pull(52); + + /* LCD_D13(OUT)/PULL-DOWN/GP53 */ + __gpio_as_input(53); + __gpio_enable_pull(53); + + /* LCD_D14(OUT)/PULL-DOWN/GP54 */ + __gpio_as_input(54); + __gpio_enable_pull(54); + + /* LCD_D15(OUT)/PULL-DOWN/GP55 */ + __gpio_as_input(55); + __gpio_enable_pull(55); + + /* LCD_VSYNC(IN)/PULL-DOWN/GP56 */ + __gpio_as_input(56); + __gpio_enable_pull(56); + + /* LCD_HSYNC(IN)/PULL-UP/GP57 */ + __gpio_as_input(57); + __gpio_enable_pull(57); + + /* LCD_PCLK(IN)/PULL-DOWN/GP58 */ + __gpio_as_input(58); + __gpio_enable_pull(58); + + /* LCD_DE(OUT)/PULL-DOWN/GP59 */ + __gpio_as_input(59); + __gpio_enable_pull(59); + + /* LCD_SPL(OUT)/PULL-UP/GP60 */ + __gpio_as_input(60); + __gpio_disable_pull(60); + + /* LCD_CLS(OUT)/PULL-UP/GP61 */ + __gpio_as_input(61); + __gpio_disable_pull(61); + + /* LCD_PS(OUT)/PULL-UP/GP62 */ + __gpio_as_input(62); + __gpio_disable_pull(62); + + /* LCD_REV(OUT)/PULL-UP/GP63 */ + __gpio_as_input(63); + __gpio_enable_pull(63); + + /* SCC0_DAT(IO)/PULL-UP/GP64 */ /* Keypad */ + __gpio_as_input(64); + __gpio_enable_pull(64); + + /* SCC1_DAT(IO)/PULL-UP/GP65 */ /* SW5 */ + __gpio_as_input(65); + __gpio_disable_pull(65); + + /* SCC0_CLK(OUT)/PULL-UP/GP66 */ /* PW_O */ + __gpio_disable_pull(66); + __gpio_as_output(66); + __cpm_set_pin(66); + + /* SCC1_CLK(OUT)/PULL-UP/GP67 */ /* SW6 */ + __gpio_as_input(67); + __gpio_disable_pull(67); + + /* SYS_CLK(OUT)/PULL-UP/GP68 */ /* I2S_CLK */ + __gpio_disable_pull(68); + + /* ACRESET_N(OUT)/PULL-UP/GP69 */ /* AK4642 PDN */ + __gpio_disable_pull(69); + __gpio_as_output(69); + __cpm_clear_pin(69); + + /* SDATA_OUT(OUT)/PULL-UP/GP70 */ /* I2S_DIN */ + __gpio_disable_pull(70); + + /* SDATA_IN(IN)/PULL-UP/GP71 */ /* I2S_DOUT */ + __gpio_disable_pull(71); + + /* SSI_CLK(OUT)/PULL-UP/GP72 */ /* SSI_CLK */ + __gpio_as_input(72); + __gpio_enable_pull(72); + + /* SSI_CE1_N(OUT)/PULL-UP/GP73 */ /* SSI_CE1_N */ + __gpio_as_input(73); + __gpio_enable_pull(73); + + /* SSI_DT(OUT)/PULL-UP/GP74 */ /* SSI_DT */ + __gpio_as_input(74); + __gpio_enable_pull(74); + + /* SSI_DR(IN)/PULL-UP/GP75 */ /* SSI_DR */ + __gpio_as_input(75); + __gpio_enable_pull(75); + + /* SSI_CE2_N(OUT)/SSI_GPC/PULL-UP/GP76 */ + __gpio_as_input(76); + __gpio_enable_pull(76); + + /* BITCLK_IN(IN)/PULL-UP/GP77 */ /* I2S_BITCLK */ + __gpio_disable_pull(77); + + /* SYNC_IN(IN)/PULL-UP/GP78 */ /* I2S_LRCIN */ + __gpio_disable_pull(78); + + /* FRE_N(OUT)/PULL-UP/GP79 */ + __gpio_enable_pull(79); + __gpio_as_input(79); + + /* FWE_N(OUT)/PULL-UP/GP80 */ + __gpio_enable_pull(80); + __gpio_as_input(80); + + /* FRB_N(IN)/PULL-UP/GP81 */ + __gpio_enable_pull(81); + __gpio_as_input(81); + + /* DCS1_N(OUT)/PULL-UP/GP82 */ /* SD_WP */ + __gpio_as_input(82); + __gpio_enable_pull(82); + + /* CS1_N(OUT)/PULL-UP/GP83 */ /* JACK_PLUG */ + __gpio_as_input(83); + __gpio_disable_pull(83); + + /* CS2_N(OUT)/PULL-UP/GP84 */ /* DC_DETE */ + __gpio_as_input(84); + __gpio_disable_pull(84); + + /* CS3_N(OUT)/PULL-UP/GP85 */ /* NAND CS# */ + __gpio_enable_pull(85); + __gpio_as_input(85); + + /* CS4_N/(OUT)PULL-UP/GP86 */ /* PULL_OFF */ + __gpio_disable_pull(86); + __gpio_as_output(86); +// __cpm_set_pin(86); + __cpm_clear_pin(86); + + /* CS5_N(OUT)/PULL-UP/GP87 */ /* IR_SD */ + __gpio_as_input(87); + __gpio_disable_pull(87); + + /* INPACK_N(IN)/PULL-UP/GP88 */ /* SW7 */ + __gpio_as_input(88); + __gpio_disable_pull(88); + + /* BVD2(IN)/PULL-UP/GP89 */ /* SW8 */ + __gpio_as_input(89); + __gpio_disable_pull(89); + + /* PCE1_N(OUT)/PULL-UP/GP90 */ /* SD_CD_N */ + __gpio_as_input(90); + __gpio_enable_pull(90); + + /* PSKTSEL_N(OUT)/PULL-UP/GP91 */ /* SD_VCC_3V_EN_N */ + __gpio_disable_pull(91); + __gpio_as_output(91); + __cpm_clear_pin(91); + + /* IOIS16_N(IN)/PULL-UP/GP92 */ /* LED_EN */ + __gpio_disable_pull(92); + __gpio_as_output(92); + __cpm_clear_pin(92); + + /* PCE2_N(OUT)/PULL-UP/GP93 */ /* LCD_DISP_OFF_N */ + __gpio_disable_pull(93); + __gpio_as_input(93); + + /* PWM0(OUT)/PULL-UP/GP94 */ /* LCD backlight off */ + __gpio_disable_pull(94); + __gpio_as_output(94); + __cpm_clear_pin(94); + + /* PWM1(OUT)/PULL-UP/GP95 */ + __gpio_disable_pull(95); + __gpio_as_output(95); + __cpm_clear_pin(95); + + /* PRT(OUT)/PULL-UP/GP96 */ /* RTC_IRQ */ + __gpio_as_input(96); + __gpio_disable_pull(96); + + /* PRT(OUT)/PULL-UP/GP97 */ /* PW_I */ + __gpio_as_input(97); + __gpio_disable_pull(97); + + /* PRT(OUT)/PULL-UP/GP98 */ /* Keypad */ + __gpio_as_input(98); + __gpio_disable_pull(98); + + /* PRT(OUT)/PULL-UP/GP99 */ /* Keypad */ + __gpio_as_input(99); + __gpio_disable_pull(99); + + /* PRT(OUT)/PULL-UP/GP100 */ /* Keypad */ + __gpio_as_input(100); + __gpio_disable_pull(100); + + /* PRT(OUT)/PULL-UP/GP101 */ /* Keypad */ + __gpio_as_input(101); + __gpio_disable_pull(101); + + /* PRT(OUT)/PULL-UP/GP102 */ /* Keypad */ + __gpio_as_input(102); + __gpio_disable_pull(102); + + /* PRT(OUT)/PULL-UP/GP103 */ /* Keypad */ + __gpio_as_input(103); + __gpio_enable_pull(103); + + /* PRT(OUT)/PULL-UP/GP104 */ /* Keypad */ + __gpio_as_input(104); + __gpio_enable_pull(104); + + /* PRT(OUT)/PULL-UP/GP105 */ /* Keypad */ + __gpio_as_input(105); + __gpio_enable_pull(105); + + /* PRT(OUT)/PULL-UP/GP106 */ /* 5V_ON */ + __gpio_disable_pull(106); + __gpio_as_output(106); + __cpm_clear_pin(106); + + /* PRT(IN)/PULL-UP/GP107 */ /* GSM_BOOT */ + __gpio_as_input(107); + __gpio_enable_pull(107); + + /* PRT(IN)/PULL-UP/GP108 */ /* GSM_RESET */ + __gpio_as_input(108); + __gpio_enable_pull(108); + + /* PRT(IN)/PULL-UP/GP109 */ /* GSM_EN */ + __gpio_as_input(109); + __gpio_enable_pull(109); + + /* PRT(IN)/PULL-UP/GP110 */ /* GSM_RING */ + __gpio_as_input(110); + __gpio_enable_pull(110); + + /* PRT(IN)/UART2_RXD/PULL-UP/GP111 */ /* Keypad */ + __gpio_as_input(111); + __gpio_enable_pull(111); + + /* MII_TX_EN(OUT)/PULL-UP/GP112 */ + __gpio_as_input(112); + __gpio_enable_pull(112); + + /* MII_RX_DV(IN)/PULL-UP/GP113 */ + __gpio_as_input(113); + __gpio_enable_pull(113); + + /* MII_RX_ER(IN)/PULL-UP/GP114 */ + __gpio_as_input(114); + __gpio_enable_pull(114); + + /* MII_COL(IN)/PULL-UP/GP115 */ + __gpio_as_input(115); + __gpio_enable_pull(115); + + /* MII_CRS(IN)/PULL-UP/GP116 */ + __gpio_as_input(116); + __gpio_enable_pull(116); + + /* MII_TXD0(OUT)/PULL-UP/GP117 */ + __gpio_as_input(117); + __gpio_enable_pull(117); + + /* MII_TXD1(OUT)/PULL-UP/GP118 */ + __gpio_as_input(118); + __gpio_enable_pull(118); + + /* MII_TXD2(OUT)/PULL-UP/GP119 */ + __gpio_as_input(119); + __gpio_enable_pull(119); + + /* MII_TXD3(OUT)/PULL-UP/GP120 */ + __gpio_as_input(120); + __gpio_enable_pull(120); + + /* MII_RXD0(IN)/PULL-UP/GP121 */ + __gpio_as_input(121); + __gpio_enable_pull(121); + + /* MII_RXD1(IN)/PULL-UP/GP122 */ + __gpio_as_input(122); + __gpio_enable_pull(122); + + /* MII_RXD2(IN)/PULL-UP/GP123 */ + __gpio_as_input(123); + __gpio_enable_pull(123); + + /* MII_RXD3(IN)/PULL-UP/GP124 */ + __gpio_as_input(124); + __gpio_enable_pull(124); + + /* UART2_TXD(OUT)/PULL-UP/GP125 */ /* CHARG_STAT */ + __gpio_as_output(125); + __gpio_disable_pull(125); + __cpm_clear_pin(125); + + /* UART0_RXD(IN)/PULL-UP/GP126 */ + __gpio_as_input(126); + __gpio_enable_pull(126); + + /* UART0_TXD(OUT)/PULL-UP/GP127 */ + __gpio_as_input(127); + __gpio_enable_pull(127); +} + +/* + * In order to save power most, all gpio pins should be put to their + * proper states during low power mode. + */ +static void jz_board_pm_suspend(void) +{ + /* Setup the state of all the GPIO pins during low-power mode */ + jz_board_pm_gpio_setup(); + + /* Allow next interrupts to wakeup the system. + */ + REG_CPM_WER = 0; /* Clear all first */ + + /* RTC alarm */ + REG_CPM_WER |= 1 << 0; + REG_CPM_WRER |= 1 << 0; + REG_CPM_WFER |= 1 << 0; + __gpio_as_irq_rise_edge(96); + + /* Power_I key */ + REG_CPM_WER |= 1 << 1; + REG_CPM_WRER |= 1 << 1; + REG_CPM_WFER |= 1 << 1; + __gpio_as_irq_rise_edge(97); + + /* enable INTC irq */ + __intc_unmask_irq(IRQ_GPIO3); + +#if 0 + /* Enable RTC alarm */ + REG_CPM_WER |= CPM_WER_WERTC; + REG_RTC_RGR = 32767; + REG_RTC_RCR &= ~RTC_RCR_AE; + REG_RTC_RSR = 0; + REG_RTC_RSAR = 30; + REG_RTC_RCR = RTC_RCR_AE | RTC_RCR_AIE | RTC_RCR_START; +#endif +} + +/* + * We don't use sleep mode of jz4730 for it has bug, the suspend mode + * implemented by hibernate mode is used instead of it. + */ +static int jz_pm_do_sleep(void) +{ + printk("It was deprecated, please use /proc/sys/pm/suspend.\n"); +#if 0 + unsigned long imr = REG_INTC_IMR; + + /* Preserve current time */ + REG_RTC_RSR = xtime.tv_sec; + + /* Mask all interrupts */ + REG_INTC_IMSR = 0xffffffff; + + /* Just allow next interrupts to wakeup the system. + * Note: modify this according to your system. + */ + /* RTC alarm */ + __gpio_as_irq_fall_edge(96); /* GPIO 96 */ + + /* POWER_I key */ + __gpio_as_irq_rise_edge(97); /* GPIO 97 */ + + /* Enable INTC */ + __intc_unmask_irq(IRQ_GPIO3); + + /* Disable modules e.g. LCD backlight */ + + /* Stop module clocks */ + __cpm_stop_uhc(); + + /* Enter SLEEP mode + * Put SDRAM into self-refresh mode. + */ + REG_CPM_LPCR &= ~CPM_LPCR_LPM_MASK; + REG_CPM_LPCR |= CPM_LPCR_LPM_SLEEP; + + __asm__(".set\tmips3\n\t" + ".set noreorder\n\t" + ".align 5\n\t" + "wait\n\t" + "nop\n\t" + ".set reorder\n\t" + ".set\tmips0"); + + /* Restore to IDLE mode */ + REG_CPM_LPCR &= ~CPM_LPCR_LPM_MASK; + REG_CPM_LPCR |= CPM_LPCR_LPM_IDLE; + + /* Restore clock of usb host */ + __cpm_start_uhc(); + + /* Restore interrupts */ + REG_INTC_IMSR = imr; + REG_INTC_IMCR = ~imr; + + /* Restore current time */ + xtime.tv_sec = REG_RTC_RSR; +#endif + return 0; +} + +#define K0BASE KSEG0 +void jz_flush_cache_all(void) +{ + unsigned long addr; + + /* Clear CP0 TagLo */ + asm volatile ("mtc0 $0, $28\n\t"::); + + for (addr = K0BASE; addr < (K0BASE + 0x4000); addr += 32) { + asm volatile ( + ".set mips3\n\t" + " cache %0, 0(%1)\n\t" + ".set mips2\n\t" + : + : "I" (Index_Writeback_Inv_D), "r"(addr)); + + asm volatile ( + ".set mips3\n\t" + " cache %0, 0(%1)\n\t" + ".set mips2\n\t" + : + : "I" (Index_Store_Tag_I), "r"(addr)); + } + + asm volatile ("sync\n\t"::); + + /* invalidate BTB */ + 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" + ".set mips2\n\t" + : + : "r"(addr)); +} + +/* Put CPU to HIBERNATE mode */ +int jz_pm_suspend(void) +{ + int retval; + + pm_send_all(PM_SUSPEND, (void *)3); + + retval = jz_pm_do_suspend(); + + pm_send_all(PM_RESUME, (void *)0); + + return retval; +} + +#if 0 +/* Put CPU to SLEEP mode */ +int jz_pm_sleep(void) +{ + return jz_pm_do_sleep(); +} + +/* Put CPU to IDLE mode, used for dpm in linux 2.4 */ +void jz_pm_idle(void) +{ + local_irq_disable(); + if (!need_resched()) { + local_irq_enable(); + cpu_wait(); + } +} +#endif + +#ifdef CONFIG_SYSCTL + +/* + * Use a temporary sysctl number. Horrid, but will be cleaned up in 2.6 + * when all the PM interfaces exist nicely. + */ +#define CTL_PM_SUSPEND 1 +#define CTL_PM_HIBERNATE 2 + +/*---------------------------------------------------------------------------- + * Power Management sleep sysctl proc interface + * + * A write to /proc/sys/pm/suspend invokes this function + * which initiates a sleep. + *--------------------------------------------------------------------------*/ +static int sysctl_jz_pm_sleep(void) +{ + return jz_pm_suspend(); +} + +static struct ctl_table pm_table[] = +{ + { + .ctl_name = CTL_UNNUMBERED, + .procname = "suspend", + .data = NULL, + .maxlen = 0, + .mode = 0600, + .proc_handler = &sysctl_jz_pm_sleep, + }, + { .ctl_name = 0} +}; + +static struct ctl_table pm_dir_table[] = +{ + { + .ctl_name = CTL_UNNUMBERED, + .procname = "pm", + .mode = 0555, + .child = pm_table, + }, + { .ctl_name = 0} +}; + +#endif /* CONFIG_SYSCTL */ + +/* + * Initialize power interface + */ +static int __init jz_pm_init(void) +{ + printk("Power Management for JZ\n"); + +#ifdef CONFIG_SYSCTL + register_sysctl_table(pm_dir_table); +#endif + + return 0; +} + +module_init(jz_pm_init); diff --git a/arch/mips/jz4730/proc.c b/arch/mips/jz4730/proc.c new file mode 100644 index 0000000..49585bc --- /dev/null +++ b/arch/mips/jz4730/proc.c @@ -0,0 +1,292 @@ +/* + * linux/arch/mips/jz4730/proc.c + * + * /proc/jz/ procfs for on-chip peripherals. + * + * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +struct proc_dir_entry *proc_jz_root; + +/* + * EMC Module + */ +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, "BCR: 0x%08x\n", REG_EMC_BCR); + len += sprintf (page+len, "SMCR(0-5): 0x%08x 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, REG_EMC_SMCR5); + len += sprintf (page+len, "SACR(0-5): 0x%08x 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, REG_EMC_SACR5); + 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); + len += sprintf (page+len, "DMAR(0-1): 0x%08x 0x%08x\n", REG_EMC_DMAR1, REG_EMC_DMAR2); + 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 lpcr = REG_CPM_LPCR; + unsigned long mscr = REG_CPM_MSCR; + + len += sprintf (page+len, "LPCR : 0x%08lx\n", lpcr); + len += sprintf (page+len, "Low Power Mode : %s\n", + ((lpcr & CPM_LPCR_LPM_MASK) == (CPM_LPCR_LPM_IDLE)) ? + "idle" : (((lpcr & CPM_LPCR_LPM_MASK) == (CPM_LPCR_LPM_SLEEP)) ? "sleep" : "hibernate")); + len += sprintf (page+len, "Doze Mode : %s\n", + (lpcr & CPM_LPCR_DOZE) ? "on" : "off"); + if (lpcr & CPM_LPCR_DOZE) + len += sprintf (page+len, " duty : %d\n", (int)((lpcr & CPM_LPCR_DUTY_MASK) >> CPM_LPCR_DUTY_BIT)); + len += sprintf (page+len, "CKO1 : %s\n", + (REG_CPM_CFCR & CPM_CFCR_CKOEN1) ? "enable" : "disable"); + len += sprintf (page+len, "UART0 : %s\n", + (mscr & CPM_MSCR_MSTP_UART0) ? "stopped" : "running"); + len += sprintf (page+len, "UART1 : %s\n", + (mscr & CPM_MSCR_MSTP_UART1) ? "stopped" : "running"); + len += sprintf (page+len, "UART2 : %s\n", + (mscr & CPM_MSCR_MSTP_UART2) ? "stopped" : "running"); + len += sprintf (page+len, "UART3 : %s\n", + (mscr & CPM_MSCR_MSTP_UART3) ? "stopped" : "running"); + len += sprintf (page+len, "OST : %s\n", + (mscr & CPM_MSCR_MSTP_OST) ? "stopped" : "running"); + len += sprintf (page+len, "DMAC : %s\n", + (mscr & CPM_MSCR_MSTP_DMAC) ? "stopped" : "running"); + len += sprintf (page+len, "ETH : %s\n", + (mscr & CPM_MSCR_MSTP_ETH) ? "stopped" : "running"); + len += sprintf (page+len, "UHC/UDC : %s\n", + (mscr & CPM_MSCR_MSTP_UHC) ? "stopped" : "running"); + len += sprintf (page+len, "PWM0 : %s\n", + (mscr & CPM_MSCR_MSTP_PWM0) ? "stopped" : "running"); + len += sprintf (page+len, "PWM1 : %s\n", + (mscr & CPM_MSCR_MSTP_PWM1) ? "stopped" : "running"); + len += sprintf (page+len, "I2C : %s\n", + (mscr & CPM_MSCR_MSTP_I2C) ? "stopped" : "running"); + len += sprintf (page+len, "SSI : %s\n", + (mscr & CPM_MSCR_MSTP_SSI) ? "stopped" : "running"); + len += sprintf (page+len, "SCC : %s\n", + (mscr & CPM_MSCR_MSTP_SCC) ? "stopped" : "running"); + return len; +} + +static int pmc_write_proc(struct file *file, const char __user *buffer, unsigned long count, void *data) +{ + REG_CPM_MSCR = simple_strtoul(buffer, 0, 16); + return count; +} + +/* + * Clock Generation Module + */ +static int cgm_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0; + unsigned int cfcr = REG_CPM_CFCR; + unsigned int plcr1 = REG_CPM_PLCR1; + 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, "PLCR1 : 0x%08x\n", plcr1); + len += sprintf (page+len, "CFCR : 0x%08x\n", cfcr); + len += sprintf (page+len, "PLL : %s\n", + (plcr1 & CPM_PLCR1_PLL1EN) ? "ON" : "OFF"); + len += sprintf (page+len, "NF:NR:NO : %d:%d:%d\n", + __cpm_plcr1_fd() + 2, + __cpm_plcr1_rd() + 2, + od[__cpm_plcr1_od()] + ); + len += sprintf (page+len, "I:S:M:P : %d:%d:%d:%d\n", + div[(cfcr & CPM_CFCR_IFR_MASK) >> CPM_CFCR_IFR_BIT], + div[(cfcr & CPM_CFCR_SFR_MASK) >> CPM_CFCR_SFR_BIT], + div[(cfcr & CPM_CFCR_MFR_MASK) >> CPM_CFCR_MFR_BIT], + div[(cfcr & CPM_CFCR_PFR_MASK) >> CPM_CFCR_PFR_BIT] + ); + len += sprintf (page+len, "PLL Freq : %d MHz\n", __cpm_get_pllout()/1000000); + len += sprintf (page+len, "ICLK : %d MHz\n", __cpm_get_iclk()/1000000); + len += sprintf (page+len, "SCLK : %d MHz\n", __cpm_get_sclk()/1000000); + len += sprintf (page+len, "MCLK : %d MHz\n", __cpm_get_mclk()/1000000); + len += sprintf (page+len, "PCLK : %d MHz\n", __cpm_get_pclk()/1000000); + len += sprintf (page+len, "DEVCLK : %d MHz\n", __cpm_get_devclk()/1000000); + len += sprintf (page+len, "RTCCLK : %d KHz\n", __cpm_get_rtcclk()/1000); + len += sprintf (page+len, "USBCLK : %d MHz\n", __cpm_get_usbclk()/1000000); +#if defined(CONFIG_FB_JZ) + len += sprintf (page+len, "LCDCLK : %d MHz\n", __cpm_get_lcdclk()/1000000); + len += sprintf (page+len, "PIXCLK : %d MHz\n", __cpm_get_pixclk()/1000000); +#endif + return len; +} + +static int cgm_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) +{ + REG_CPM_CFCR = simple_strtoul(buffer, 0, 16); + return count; +} + +/* + * WDT + */ +static int wdt_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0; + + len += sprintf (page+len, "WDT_WTCSR : 0x%08x\n", REG_WDT_WTCSR); + len += sprintf (page+len, "WDT_WTCNT : 0x%08x\n", REG_WDT_WTCNT); + + return len; +} + +static int wdt_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) +{ + unsigned long cnt = simple_strtoul(buffer, 0, 16); + + REG_WDT_WTCNT = cnt; + REG_WDT_WTCSR = WDT_WTCSR_START; + + return count; +} + +/* + * PWM + */ + +static int proc_jz_pwm_read_byte(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + return sprintf (page, "0x%02x\n", REG8(data)); +} + +static int proc_jz_pwm_read_word(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + return sprintf (page, "0x%04x\n", REG16(data)); +} + +static int proc_jz_pwm_write_byte(struct file *file, const char *buffer, unsigned long count, void *data) +{ + REG8(data) = simple_strtoul(buffer, 0, 16); + return count; +} + +static int proc_jz_pwm_write_word(struct file *file, const char *buffer, unsigned long count, void *data) +{ + REG16(data) = simple_strtoul(buffer, 0, 16); + return count; +} + +#define PWM_NUM 2 + +static int jz_pwm_proc_init(void) +{ + struct proc_dir_entry *proc_jz_pwm, *res; + char name[16]; + unsigned char i; + + for (i = 0; i < PWM_NUM; i++) { + sprintf(name, "pwm%d", i); + proc_jz_pwm = proc_mkdir(name, proc_jz_root); + res = create_proc_entry("control", 0600, proc_jz_pwm); + if ( res) { + res->read_proc = proc_jz_pwm_read_byte; + res->write_proc = proc_jz_pwm_write_byte; + if (i) + res->data = (void * )PWM_CTR(1); + else + res->data = (void * )PWM_CTR(0); + } + res = create_proc_entry("period", 0600, proc_jz_pwm); + if ( res) { + res->read_proc = proc_jz_pwm_read_word; + res->write_proc = proc_jz_pwm_write_word; + if (i) + res->data = (void *)PWM_PER(1); + else + res->data = (void *)PWM_PER(0); + } + res = create_proc_entry("duty", 0600, proc_jz_pwm); + if ( res) { + res->read_proc = proc_jz_pwm_read_word; + res->write_proc = proc_jz_pwm_write_word; + if (i) + res->data = (void * )PWM_DUT(1); + else + res->data = (void * )PWM_DUT(0); + } + } + return 0; +} + +/* + * /proc/jz/xxx entry + * + */ +static int __init jz_proc_init(void) +{ + struct proc_dir_entry *entry; + + /* create /proc/jz */ + proc_jz_root = proc_mkdir("jz", 0); + + /* create /proc/jz/emc */ + entry = create_proc_entry("emc", 0644, proc_jz_root); + if (entry) { + entry->read_proc = emc_read_proc; + entry->write_proc = NULL; + entry->data = NULL; + } + + /* create /proc/jz/pmc */ + entry = create_proc_entry("pmc", 0644, proc_jz_root); + if (entry) { + entry->read_proc = pmc_read_proc; + entry->write_proc = pmc_write_proc; + entry->data = NULL; + } + + /* create /proc/jz/cgm */ + entry = create_proc_entry("cgm", 0644, proc_jz_root); + if (entry) { + entry->read_proc = cgm_read_proc; + entry->write_proc = cgm_write_proc; + entry->data = NULL; + } + + /* create /proc/jz/wdt */ + entry = create_proc_entry("wdt", 0644, proc_jz_root); + if (entry) { + entry->read_proc = wdt_read_proc; + entry->write_proc = wdt_write_proc; + entry->data = NULL; + } + + /* PWM */ + jz_pwm_proc_init(); + + return 0; +} + +__initcall(jz_proc_init); diff --git a/arch/mips/jz4730/prom.c b/arch/mips/jz4730/prom.c new file mode 100644 index 0000000..5a5f6b8 --- /dev/null +++ b/arch/mips/jz4730/prom.c @@ -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 +#include +#include +#include + +#include +#include + +/* #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_JZ4730; + + 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 *)(UART3_BASE + OFF_LSR); + volatile u8 *uart_tdr = (volatile u8 *)(UART3_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 "JZ4730"; +} + +EXPORT_SYMBOL(prom_getcmdline); +EXPORT_SYMBOL(get_ethernet_addr); +EXPORT_SYMBOL(str2eaddr); diff --git a/arch/mips/jz4730/reset.c b/arch/mips/jz4730/reset.c new file mode 100644 index 0000000..4c9e20c --- /dev/null +++ b/arch/mips/jz4730/reset.c @@ -0,0 +1,40 @@ +/* + * linux/arch/mips/jz4730/reset.c + * + * JZ4730 reset routines. + * + * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +void jz_restart(char *command) +{ + __wdt_set_count(0xffffffff-32); /* reset after 1/1024 s */ + __wdt_start(); + while (1); +} + +void jz_halt(void) +{ + __wdt_set_count(0xffffffff-32); /* reset after 1/1024 s */ + __wdt_start(); + while (1); +} + +void jz_power_off(void) +{ + jz_halt(); +} diff --git a/arch/mips/jz4730/setup.c b/arch/mips/jz4730/setup.c new file mode 100644 index 0000000..4594b56 --- /dev/null +++ b/arch/mips/jz4730/setup.c @@ -0,0 +1,182 @@ +/* + * linux/arch/mips/jz4730/setup.c + * + * JZ4730 CPU common setup routines. + * + * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PC_KEYB +#include +#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_JZ4730_URANUS + jz_clocks.iclk = __cpm_get_iclk(); + jz_clocks.sclk = __cpm_get_sclk(); + jz_clocks.mclk = __cpm_get_mclk(); + jz_clocks.pclk = __cpm_get_pclk(); + jz_clocks.devclk = __cpm_get_devclk(); + jz_clocks.rtcclk = __cpm_get_rtcclk(); + jz_clocks.uartclk = __cpm_get_uartclk(); + jz_clocks.lcdclk = __cpm_get_lcdclk(); + jz_clocks.pixclk = __cpm_get_pixclk(); + jz_clocks.usbclk = __cpm_get_usbclk(); + jz_clocks.i2sclk = __cpm_get_i2sclk(); + jz_clocks.mscclk = __cpm_get_mscclk(); +#else /* URANUS FPGA */ + +#define FPGACLK 8000000 + + jz_clocks.iclk = FPGACLK; + jz_clocks.sclk = FPGACLK; + jz_clocks.mclk = FPGACLK; + jz_clocks.devclk = FPGACLK; + jz_clocks.rtcclk = FPGACLK; + jz_clocks.uartclk = FPGACLK; + jz_clocks.pixclk = FPGACLK; + jz_clocks.lcdclk = FPGACLK; + jz_clocks.usbclk = FPGACLK; + jz_clocks.i2sclk = FPGACLK; + jz_clocks.mscclk = FPGACLK; +#endif + + printk("CPU clock: %dMHz, System clock: %dMHz, Memory clock: %dMHz, Peripheral clock: %dMHz\n", + (jz_clocks.iclk + 500000) / 1000000, + (jz_clocks.sclk + 500000) / 1000000, + (jz_clocks.mclk + 500000) / 1000000, + (jz_clocks.pclk + 500000) / 1000000); +} + +static void __init soc_cpm_setup(void) +{ + __cpm_idle_mode(); + __cpm_enable_cko1(); + __cpm_start_all(); + + /* get 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(0x08); /* DMAC>LCD>CIM>ETH>USB>CIM */ +// __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_all_channels(); +} + +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; + + memset(&s, 0, sizeof(s)); + + s.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST; + s.iotype = UPIO_MEM; + s.regshift = 2; + s.uartclk = jz_clocks.uartclk; + + 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"); + } + + s.line = 2; + s.membase = (u8 *)UART2_BASE; + s.irq = IRQ_UART2; + if (early_serial_setup(&s) != 0) { + printk(KERN_ERR "Serial ttyS2 setup failed!\n"); + } + + s.line = 3; + s.membase = (u8 *)UART3_BASE; + s.irq = IRQ_UART3; + if (early_serial_setup(&s) != 0) { + printk(KERN_ERR "Serial ttyS3 setup failed!\n"); + } +#endif +} + +void __init plat_mem_setup(void) +{ + char *argptr; + + argptr = prom_getcmdline(); + + /* IO/MEM resources. */ + 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; + + jz_soc_setup(); /* soc specific setup */ + jz_serial_setup(); /* serial port setup */ + jz_board_setup(); /* board specific setup */ +} diff --git a/arch/mips/jz4730/sleep.S b/arch/mips/jz4730/sleep.S new file mode 100644 index 0000000..9ee9e70 --- /dev/null +++ b/arch/mips/jz4730/sleep.S @@ -0,0 +1,307 @@ +/* + * linux/arch/mips/jz4730/sleep.S + * + * jz4730 Assembler Sleep/WakeUp Management Routines + * + * Copyright (C) 2005 Ingenic Semiconductor + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + + .text + .set noreorder + .set noat + + .extern jz_flush_cache_all + +/* + * jz_cpu_suspend() + * + * Forces CPU into hibernate mode + */ + + .globl jz_cpu_suspend +jz_cpu_suspend: + + /* save hi, lo and general registers except k0($26) and k1($27) (total 32) */ + move k0, sp + addiu k0, k0, -(32*4) + mfhi k1 + sw $0, 0(k0) + sw $1, 4(k0) + sw k1, 120(k0) /* hi */ + mflo k1 + sw $2, 8(k0) + sw $3, 12(k0) + sw k1, 124(k0) /* lo */ + sw $4, 16(k0) + sw $5, 20(k0) + sw $6, 24(k0) + sw $7, 28(k0) + sw $8, 32(k0) + sw $9, 36(k0) + sw $10, 40(k0) + sw $11, 44(k0) + sw $12, 48(k0) + sw $13, 52(k0) + sw $14, 56(k0) + sw $15, 60(k0) + sw $16, 64(k0) + sw $17, 68(k0) + sw $18, 72(k0) + sw $19, 76(k0) + sw $20, 80(k0) + sw $21, 84(k0) + sw $22, 88(k0) + sw $23, 92(k0) + sw $24, 96(k0) + sw $25, 100(k0) + sw $28, 104(k0) + sw $29, 108(k0) /* saved sp */ + sw $30, 112(k0) + sw $31, 116(k0) /* saved ra */ + move sp, k0 + + /* save CP0 registers and sp (total 26) */ + move k0, sp + addiu k0, k0, -(26*4) + + mfc0 $1, CP0_INDEX + mfc0 $2, CP0_RANDOM + mfc0 $3, CP0_ENTRYLO0 + mfc0 $4, CP0_ENTRYLO1 + mfc0 $5, CP0_CONTEXT + mfc0 $6, CP0_PAGEMASK + mfc0 $7, CP0_WIRED + mfc0 $8, CP0_BADVADDR + mfc0 $9, CP0_ENTRYHI + mfc0 $10, CP0_STATUS +/* mfc0 $11, $12, 1*/ /* IntCtl */ + mfc0 $12, CP0_CAUSE + mfc0 $13, CP0_EPC +/* mfc0 $14, $15, 1*/ /* EBase */ + mfc0 $15, CP0_CONFIG +/* mfc0 $16, CP0_CONFIG, 7*/ /* Config 7 */ + mfc0 $17, CP0_LLADDR + mfc0 $18, CP0_WATCHLO + mfc0 $19, CP0_WATCHHI + mfc0 $20, CP0_DEBUG + mfc0 $21, CP0_DEPC + mfc0 $22, CP0_ECC + mfc0 $23, CP0_TAGLO + mfc0 $24, CP0_ERROREPC + mfc0 $25, CP0_DESAVE + + sw $1, 0(k0) + sw $2, 4(k0) + sw $3, 8(k0) + sw $4, 12(k0) + sw $5, 16(k0) + sw $6, 20(k0) + sw $7, 24(k0) + sw $8, 28(k0) + sw $9, 32(k0) + sw $10, 36(k0) + sw $11, 40(k0) + sw $12, 44(k0) + sw $13, 48(k0) + sw $14, 52(k0) + sw $15, 56(k0) + sw $16, 60(k0) + sw $17, 64(k0) + sw $18, 68(k0) + sw $19, 72(k0) + sw $20, 76(k0) + sw $21, 80(k0) + sw $22, 84(k0) + sw $23, 88(k0) + sw $24, 92(k0) + sw $25, 96(k0) + sw $29, 100(k0) /* saved sp */ + move sp, k0 + + /* preserve virtual address of stack */ + la k0, suspend_save_sp + sw sp, 0(k0) + + /* flush caches and write buffers */ + jal jz_flush_cache_all + nop + + /* set new sdram refresh constant */ + li t0, 1 + la t1, EMC_RTCOR + sh t0, 0(t1) + + /* disable PLL */ + la t0, CPM_PLCR1 + sw $0, 0(t0) + + /* put CPU to hibernate mode */ + la t0, CPM_LPCR + lw t1, 0(t0) + li t2, ~CPM_LPCR_LPM_MASK + and t1, t2 + ori t1, CPM_LPCR_LPM_HIBERNATE + + .align 5 + /* align execution to a cache line */ + j 1f + + .align 5 +1: + /* all needed values are now in registers. + * These last instructions should be in cache + */ + nop + nop + + /* set hibernate mode */ + sw t1, 0(t0) + nop + + /* enter hibernate mode */ + .set mips3 + wait + nop + .set mips2 + +2: j 2b /* loop waiting for suspended */ + nop + +/* + * jz_cpu_resume() + * + * entry point from bootloader into kernel during resume + */ + + .align 5 + .globl jz_cpu_resume +jz_cpu_resume: + /* clear SCR.HGP */ + la t0, CPM_SCR + lw t1, 0(t0) + li t2, ~CPM_SCR_HGP + and t1, t2 + sw t1, 0(t0) + + /* restore LPCR.LPM to IDLE mode */ + la t0, CPM_LPCR + lw t1, 0(t0) + li t2, ~CPM_LPCR_LPM_MASK + and t1, t2 + ori t1, CPM_LPCR_LPM_IDLE + sw t1, 0(t0) + + /* restore saved sp */ + la t0, suspend_save_sp + lw sp, 0(t0) + + /* restore CP0 registers */ + move k0, sp + lw $1, 0(k0) + lw $2, 4(k0) + lw $3, 8(k0) + lw $4, 12(k0) + lw $5, 16(k0) + lw $6, 20(k0) + lw $7, 24(k0) + lw $8, 28(k0) + lw $9, 32(k0) + lw $10, 36(k0) + lw $11, 40(k0) + lw $12, 44(k0) + lw $13, 48(k0) + lw $14, 52(k0) + lw $15, 56(k0) + lw $16, 60(k0) + lw $17, 64(k0) + lw $18, 68(k0) + lw $19, 72(k0) + lw $20, 76(k0) + lw $21, 80(k0) + lw $22, 84(k0) + lw $23, 88(k0) + lw $24, 92(k0) + lw $25, 96(k0) + lw $29, 100(k0) /* saved sp */ + + mtc0 $1, CP0_INDEX + mtc0 $2, CP0_RANDOM + mtc0 $3, CP0_ENTRYLO0 + mtc0 $4, CP0_ENTRYLO1 + mtc0 $5, CP0_CONTEXT + mtc0 $6, CP0_PAGEMASK + mtc0 $7, CP0_WIRED + mtc0 $8, CP0_BADVADDR + mtc0 $9, CP0_ENTRYHI + mtc0 $10, CP0_STATUS +/* mtc0 $11, $12, 1*/ /* IntCtl */ + mtc0 $12, CP0_CAUSE + mtc0 $13, CP0_EPC +/* mtc0 $14, $15, 1*/ /* EBase */ + mtc0 $15, CP0_CONFIG +/* mtc0 $16, CP0_CONFIG, 7*/ /* Config 7 */ + mtc0 $17, CP0_LLADDR + mtc0 $18, CP0_WATCHLO + mtc0 $19, CP0_WATCHHI + mtc0 $20, CP0_DEBUG + mtc0 $21, CP0_DEPC + mtc0 $22, CP0_ECC + mtc0 $23, CP0_TAGLO + mtc0 $24, CP0_ERROREPC + mtc0 $25, CP0_DESAVE + + /* restore general registers */ + move k0, sp + lw k1, 120(k0) /* hi */ + lw $0, 0(k0) + lw $1, 4(k0) + mthi k1 + lw k1, 124(k0) /* lo */ + lw $2, 8(k0) + lw $3, 12(k0) + mtlo k1 + lw $4, 16(k0) + lw $5, 20(k0) + lw $6, 24(k0) + lw $7, 28(k0) + lw $8, 32(k0) + lw $9, 36(k0) + lw $10, 40(k0) + lw $11, 44(k0) + lw $12, 48(k0) + lw $13, 52(k0) + lw $14, 56(k0) + lw $15, 60(k0) + lw $16, 64(k0) + lw $17, 68(k0) + lw $18, 72(k0) + lw $19, 76(k0) + lw $20, 80(k0) + lw $21, 84(k0) + lw $22, 88(k0) + lw $23, 92(k0) + lw $24, 96(k0) + lw $25, 100(k0) + lw $28, 104(k0) + lw $29, 108(k0) /* saved sp */ + lw $30, 112(k0) + lw $31, 116(k0) /* saved ra */ + + /* return to caller */ + jr ra + nop + +suspend_save_sp: + .word 0 /* preserve sp here */ + + .set reorder diff --git a/arch/mips/jz4730/time.c b/arch/mips/jz4730/time.c new file mode 100644 index 0000000..806c7fe --- /dev/null +++ b/arch/mips/jz4730/time.c @@ -0,0 +1,129 @@ +/* + * linux/arch/mips/jz4730/time.c + * + * Setting up the clock on the JZ4730 boards. + * + * Copyright (c) 2006-2008 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include + +#include +#include + +#define JZ_TIMER_CHAN 0 +#define JZ_TIMER_IRQ IRQ_OST0 +#define JZ_TIMER_CLOCK JZ_EXTAL + +static unsigned int timer_latch; + +void (*jz_timer_callback)(void); + +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-timer", + .features = CLOCK_EVT_FEAT_PERIODIC, + + /* .mult, .shift, .max_delta_ns and .min_delta_ns left uninitialized */ + + .rating = 300, + .irq = JZ_TIMER_IRQ, + .set_mode = jz_set_mode, +}; + +static irqreturn_t jz_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *cd = dev_id; + + __ost_clear_uf(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, + .name = "jz-timer", +}; + +cycle_t jz_get_cycles(void) +{ + unsigned int jz_timer_cnt; +#if 0 /* clock source use pll, read directly */ + jz_timer_cnt = timer_latch - REG_OST_TCNT(JZ_TIMER_CHAN); +#else /* clock source use RTCClock or Extall Clock, wait read ready */ + jz_timer_cnt = REG_OST_TCNT(JZ_TIMER_CHAN); /* dummy read */ + while ( __ost_is_busy(JZ_TIMER_CHAN) ) ; /* wait read ready */ + jz_timer_cnt = timer_latch - REG_OST_TCRB(JZ_TIMER_CHAN); +#endif + + /* convert jiffes to jz timer cycles */ + return (cycle_t)( jiffies*((JZ_TIMER_CLOCK)/HZ) + jz_timer_cnt); +} + +static struct clocksource clocksource_jz = { + .name = "jz_clocksource", + .rating = 300, + .read = jz_get_cycles, + .mask = 0xFFFFFFFF, + .shift = 10, /* control clocksource.mult's accuracy */ + .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 void __init jz_timer_setup(void) +{ + struct clock_event_device *cd = &jz_clockevent_device; + struct irqaction *action = &jz_irqaction; + unsigned int cpu = smp_processor_id(); + + jz_clocksource_init(); + cd->cpumask = cpumask_of_cpu(cpu); + clockevents_register_device(cd); + action->dev_id = cd; + setup_irq(JZ_TIMER_IRQ, &jz_irqaction); +} + +void __init plat_time_init(void) +{ + /* Init timer, timer clock soure use extal clock */ + timer_latch = (JZ_TIMER_CLOCK + (HZ>>1)) / HZ; + __ost_set_mode(JZ_TIMER_CHAN, OST_TCSR_UIE | OST_TCSR_CKS_EXTAL); + __ost_set_reload(JZ_TIMER_CHAN, timer_latch); + __ost_set_count(JZ_TIMER_CHAN, timer_latch); + __ost_enable_channel(JZ_TIMER_CHAN); + + jz_timer_setup(); +} diff --git a/arch/mips/jz4740/Makefile b/arch/mips/jz4740/Makefile new file mode 100644 index 0000000..d32e7d0 --- /dev/null +++ b/arch/mips/jz4740/Makefile @@ -0,0 +1,27 @@ +# +# 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 +obj-$(CONFIG_JZ4740_QI_LB60) += board-qi_lb60.o + +# PM support + +obj-$(CONFIG_PM_LEGACY) +=pm.o + +# CPU Frequency scaling support + +obj-$(CONFIG_CPU_FREQ_JZ) +=cpufreq.o diff --git a/arch/mips/jz4740/board-dipper.c b/arch/mips/jz4740/board-dipper.c new file mode 100644 index 0000000..ca30225 --- /dev/null +++ b/arch/mips/jz4740/board-dipper.c @@ -0,0 +1,117 @@ +/* + * linux/arch/mips/jz4740/board-dipper.c + * + * JZ4725 Dipper board setup routines. + * + * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +extern void (*jz_timer_callback)(void); + +#if 0 +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); +} +#endif + +static void dipper_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 Smart LCD pins + */ +// __gpio_as_slcd_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_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); + __gpio_as_input(GPIO_USB_DETE); + + __gpio_as_output(GPIO_DISP_OFF_N); + +// __gpio_as_output(GPIO_LED_EN); +} + +void __init jz_board_setup(void) +{ + printk("JZ4725 DIPPER board setup\n"); + + board_cpm_setup(); + board_gpio_setup(); + + jz_timer_callback = dipper_timer_callback; +} diff --git a/arch/mips/jz4740/board-leo.c b/arch/mips/jz4740/board-leo.c new file mode 100644 index 0000000..912636a --- /dev/null +++ b/arch/mips/jz4740/board-leo.c @@ -0,0 +1,67 @@ +/* + * linux/arch/mips/jz4740/board-leo.c + * + * JZ4740 LEO board setup routines. + * + * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +extern void (*jz_timer_callback)(void); + +static void dancing(void) +{ + static unsigned char slash[] = "\\|/-"; + static volatile unsigned char *p = (unsigned char *)0xb6000016; + static unsigned int count = 0; + *p = slash[count++]; + count &= 3; +} + +static void leo_timer_callback(void) +{ + static unsigned long count = 0; + + if ((++count) % 10 == 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) +{ + /* All GPIO pins should have been initialized by the boot-loader */ +} + +void __init jz_board_setup(void) +{ + board_cpm_setup(); + board_gpio_setup(); + printk(" BOARD SETUP"); + jz_timer_callback = leo_timer_callback; +} diff --git a/arch/mips/jz4740/board-lyra.c b/arch/mips/jz4740/board-lyra.c new file mode 100644 index 0000000..ea56626 --- /dev/null +++ b/arch/mips/jz4740/board-lyra.c @@ -0,0 +1,114 @@ +/* + * linux/arch/mips/jz4740/board-lyra.c + * + * JZ4740 LYRA board setup routines. + * + * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +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 lyra_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_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); + __gpio_as_input(GPIO_USB_DETE); + + __gpio_as_output(GPIO_DISP_OFF_N); + + __gpio_as_output(GPIO_LED_EN); +} + +void __init jz_board_setup(void) +{ + printk("JZ4740 LYRA board setup\n"); + + board_cpm_setup(); + board_gpio_setup(); + + jz_timer_callback = lyra_timer_callback; +} diff --git a/arch/mips/jz4740/board-pavo.c b/arch/mips/jz4740/board-pavo.c new file mode 100644 index 0000000..e2a5509 --- /dev/null +++ b/arch/mips/jz4740/board-pavo.c @@ -0,0 +1,114 @@ +/* + * linux/arch/mips/jz4740/board-pavo.c + * + * JZ4740 PAVO board setup routines. + * + * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +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 pavo_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_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); + __gpio_as_input(GPIO_USB_DETE); + + __gpio_as_output(GPIO_DISP_OFF_N); + + __gpio_as_output(GPIO_LED_EN); +} + +void __init jz_board_setup(void) +{ + printk("JZ4740 PAVO board setup\n"); + + board_cpm_setup(); + board_gpio_setup(); + + jz_timer_callback = pavo_timer_callback; +} diff --git a/arch/mips/jz4740/board-virgo.c b/arch/mips/jz4740/board-virgo.c new file mode 100644 index 0000000..1429877 --- /dev/null +++ b/arch/mips/jz4740/board-virgo.c @@ -0,0 +1,114 @@ +/* + * linux/arch/mips/jz4740/board-virgo.c + * + * JZ4720 VIRGO board setup routines. + * + * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +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 virgo_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_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); + __gpio_as_input(GPIO_USB_DETE); + + __gpio_as_output(GPIO_DISP_OFF_N); + +// __gpio_as_output(GPIO_LED_EN); +} + +void __init jz_board_setup(void) +{ + printk("JZ4720 VIRGO board setup\n"); + + board_cpm_setup(); + board_gpio_setup(); + + jz_timer_callback = virgo_timer_callback; +} diff --git a/arch/mips/jz4740/cpufreq.c b/arch/mips/jz4740/cpufreq.c new file mode 100644 index 0000000..d646a1e --- /dev/null +++ b/arch/mips/jz4740/cpufreq.c @@ -0,0 +1,602 @@ +/* + * linux/arch/mips/jz4740/cpufreq.c + * + * cpufreq driver for JZ4740 + * + * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include + +#include +#include + +#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, ®s); + + 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(®s); + + 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 "); +MODULE_DESCRIPTION("cpufreq driver for Jz4740"); +MODULE_LICENSE("GPL"); + diff --git a/arch/mips/jz4740/dma.c b/arch/mips/jz4740/dma.c new file mode 100644 index 0000000..dd5055e --- /dev/null +++ b/arch/mips/jz4740/dma.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * 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); diff --git a/arch/mips/jz4740/i2c.c b/arch/mips/jz4740/i2c.c new file mode 100644 index 0000000..3080fdf --- /dev/null +++ b/arch/mips/jz4740/i2c.c @@ -0,0 +1,273 @@ +/* + * linux/arch/mips/jz4740/i2c.c + * + * Jz4740 I2C routines. + * + * Copyright (C) 2005,2006 Ingenic Semiconductor Inc. + * Author: + * + * 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 +#include +#include +#include +#include + +#include + +/* 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); diff --git a/arch/mips/jz4740/irq.c b/arch/mips/jz4740/irq.c new file mode 100644 index 0000000..98543c2 --- /dev/null +++ b/arch/mips/jz4740/irq.c @@ -0,0 +1,265 @@ +/* + * linux/arch/mips/jz4740/irq.c + * + * JZ4740 interrupt routines. + * + * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * INTC irq type + */ + +static void enable_intc_irq(unsigned int irq) +{ + __intc_unmask_irq(irq); +} + +static void disable_intc_irq(unsigned int irq) +{ + __intc_mask_irq(irq); +} + +static void mask_and_ack_intc_irq(unsigned int irq) +{ + __intc_mask_irq(irq); + __intc_ack_irq(irq); +} + +static void end_intc_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { + enable_intc_irq(irq); + } +} + +static unsigned int startup_intc_irq(unsigned int irq) +{ + enable_intc_irq(irq); + return 0; +} + +static void shutdown_intc_irq(unsigned int irq) +{ + disable_intc_irq(irq); +} + +static struct irq_chip intc_irq_type = { + .typename = "INTC", + .startup = startup_intc_irq, + .shutdown = shutdown_intc_irq, + .enable = enable_intc_irq, + .disable = disable_intc_irq, + .ack = mask_and_ack_intc_irq, + .end = end_intc_irq, +}; + +/* + * GPIO irq type + */ + +static void enable_gpio_irq(unsigned int irq) +{ + unsigned int intc_irq; + + if (irq < (IRQ_GPIO_0 + 32)) { + intc_irq = IRQ_GPIO0; + } + else if (irq < (IRQ_GPIO_0 + 64)) { + intc_irq = IRQ_GPIO1; + } + else if (irq < (IRQ_GPIO_0 + 96)) { + intc_irq = IRQ_GPIO2; + } + else { + intc_irq = IRQ_GPIO3; + } + + enable_intc_irq(intc_irq); + __gpio_unmask_irq(irq - IRQ_GPIO_0); +} + +static void disable_gpio_irq(unsigned int irq) +{ + __gpio_mask_irq(irq - IRQ_GPIO_0); +} + +static void mask_and_ack_gpio_irq(unsigned int irq) +{ + __gpio_mask_irq(irq - IRQ_GPIO_0); + __gpio_ack_irq(irq - IRQ_GPIO_0); +} + +static void end_gpio_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { + enable_gpio_irq(irq); + } +} + +static unsigned int startup_gpio_irq(unsigned int irq) +{ + enable_gpio_irq(irq); + return 0; +} + +static void shutdown_gpio_irq(unsigned int irq) +{ + disable_gpio_irq(irq); +} + +static struct irq_chip gpio_irq_type = { + .typename = "GPIO", + .startup = startup_gpio_irq, + .shutdown = shutdown_gpio_irq, + .enable = enable_gpio_irq, + .disable = disable_gpio_irq, + .ack = mask_and_ack_gpio_irq, + .end = end_gpio_irq, +}; + +/* + * 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 unsigned int startup_dma_irq(unsigned int irq) +{ + enable_dma_irq(irq); + return 0; +} + +static void shutdown_dma_irq(unsigned int irq) +{ + disable_dma_irq(irq); +} + +static struct irq_chip dma_irq_type = { + .typename = "DMA", + .startup = startup_dma_irq, + .shutdown = shutdown_dma_irq, + .enable = enable_dma_irq, + .disable = 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 */ + + /* Set up INTC irq + */ + for (i = 0; i < 32; i++) { + disable_intc_irq(i); + irq_desc[i].chip = &intc_irq_type; + } + + /* Set up DMAC irq + */ + for (i = 0; i < NUM_DMA; i++) { + disable_dma_irq(IRQ_DMA_0 + i); + irq_desc[IRQ_DMA_0 + i].chip = &dma_irq_type; + } + + /* Set up GPIO irq + */ + for (i = 0; i < NUM_GPIO; i++) { + disable_gpio_irq(IRQ_GPIO_0 + i); + irq_desc[IRQ_GPIO_0 + i].chip = &gpio_irq_type; + } +} + +static int plat_real_irq(int irq) +{ + switch (irq) { + case IRQ_GPIO0: + irq = __gpio_group_irq(0) + IRQ_GPIO_0; + break; + case IRQ_GPIO1: + irq = __gpio_group_irq(1) + IRQ_GPIO_0 + 32; + break; + case IRQ_GPIO2: + irq = __gpio_group_irq(2) + IRQ_GPIO_0 + 64; + break; + case IRQ_GPIO3: + irq = __gpio_group_irq(3) + IRQ_GPIO_0 + 96; + break; + case IRQ_DMAC: + irq = __dmac_get_irq() + IRQ_DMA_0; + break; + } + + return irq; +} + +asmlinkage void plat_irq_dispatch(void) +{ + int irq = 0; + static unsigned long intc_ipr = 0; + + intc_ipr |= REG_INTC_IPR; + + if (!intc_ipr) return; + + irq = ffs(intc_ipr) - 1; + intc_ipr &= ~(1< + * + * 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 +#include +#include +#include +#include + +#include + +/* 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, +}; + +/* All */ +static struct platform_device *jz_platform_devices[] __initdata = { + &jz_usb_ohci_device, + &jz_lcd_device, + &jz_usb_gdt_device, + &jz_mmc_device, + &jz_i2c_device, +}; + +static int __init jz_platform_init(void) +{ + return platform_add_devices(jz_platform_devices, ARRAY_SIZE(jz_platform_devices)); +} + +arch_initcall(jz_platform_init); diff --git a/arch/mips/jz4740/pm.c b/arch/mips/jz4740/pm.c new file mode 100644 index 0000000..2c6cd83 --- /dev/null +++ b/arch/mips/jz4740/pm.c @@ -0,0 +1,462 @@ +/* + * linux/arch/mips/jz4740/common/pm.c + * + * JZ4740 Power Management Routines + * + * Copyright (C) 2006 Ingenic Semiconductor Inc. + * Author: + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#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) +{ + printk("Put CPU into hibernate mode.\n"); + + /* 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]; + + printk("Put CPU into sleep mode.\n"); + + /* 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) +{ + 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 + + pm_send_all(PM_SUSPEND, (void *)3); + retval = jz_pm_do_sleep(); + pm_send_all(PM_RESUME, (void *)0); + +#ifndef CONFIG_JZ_POWEROFF + free_irq (IRQ_GPIO_0 + GPIO_WAKEUP, NULL); +#endif + + return retval; +} + +#if 0 +/* Deprecated ,was used by dpm */ +void jz_pm_idle(void) +{ + local_irq_disable(); + if (!need_resched()) { + local_irq_enable(); + cpu_wait(); + } +} +#endif + +#ifdef CONFIG_SYSCTL + +/* + * Use a temporary sysctl number. Horrid, but will be cleaned up in 2.6 + * when all the PM interfaces exist nicely. + */ +#define CTL_PM_SUSPEND 1 +#define CTL_PM_HIBERNATE 2 + +/*---------------------------------------------------------------------------- + * Power Management sleep sysctl proc interface + * + * A write to /proc/sys/pm/suspend invokes this function + * which initiates a sleep. + *--------------------------------------------------------------------------*/ +static int sysctl_jz_pm_sleep(struct ctl_table *ctl, int write, struct file * filp, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + return jz_pm_sleep(); +} + +/*---------------------------------------------------------------------------- + * Power Management sleep sysctl proc interface + * + * A write to /proc/sys/pm/hibernate invokes this function + * which initiates a poweroff. + *--------------------------------------------------------------------------*/ +static int sysctl_jz_pm_hibernate(struct ctl_table *ctl, int write, struct file * filp, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + return jz_pm_hibernate(); +} + +static struct ctl_table pm_table[] = +{ + { + .ctl_name = CTL_UNNUMBERED, + .procname = "suspend", + .data = NULL, + .maxlen = 0, + .mode = 0600, + .proc_handler = &sysctl_jz_pm_sleep, + }, + { + .ctl_name = CTL_UNNUMBERED, + .procname = "hibernate", + .data = NULL, + .maxlen = 0, + .mode = 0600, + .proc_handler = &sysctl_jz_pm_hibernate, + }, + { .ctl_name = 0} +}; + +static struct ctl_table pm_dir_table[] = +{ + { + .ctl_name = CTL_UNNUMBERED, + .procname = "pm", + .mode = 0555, + .child = pm_table, + }, + { .ctl_name = 0} +}; + +#endif /* CONFIG_SYSCTL */ + +/* + * Initialize power interface + */ +static int __init jz_pm_init(void) +{ + printk("Power Management for JZ\n"); + +#ifdef CONFIG_SYSCTL + register_sysctl_table(pm_dir_table); +#endif + + return 0; +} + +module_init(jz_pm_init); + diff --git a/arch/mips/jz4740/proc.c b/arch/mips/jz4740/proc.c new file mode 100644 index 0000000..af48bee --- /dev/null +++ b/arch/mips/jz4740/proc.c @@ -0,0 +1,887 @@ +/* + * linux/arch/mips/jz4740/proc.c + * + * /proc/jz/ procfs for jz4740 on-chip modules. + * + * Copyright (C) 2006 Ingenic Semiconductor Inc. + * Author: + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define DEBUG 1 +#undef DEBUG + +/* Define this to reserve total 4MB contineous physical memory for IPU. + * MPlayer will use IPU to optimize the decoding process. + * + * If you do not want to run the MPlayer, you can comment it. + */ +#define CONFIG_RESERVE_IPU_MEM 1 + +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; +} + + +/* + * 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; +} + +#ifdef CONFIG_RESERVE_IPU_MEM + +/* 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<= 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 /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; +} + +#endif /* CONFIG_RESERVE_IPU_MEM */ + +/* + * /proc/jz/xxx entry + * + */ +static int __init jz_proc_init(void) +{ + struct proc_dir_entry *res; +#ifdef CONFIG_RESERVE_IPU_MEM + unsigned int virt_addr, i; +#endif + + 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; + } + + /* 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; + } + +#ifdef CONFIG_RESERVE_IPU_MEM + /* 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; + } + + /* + * 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); + } +#endif + + return 0; +} + +__initcall(jz_proc_init); diff --git a/arch/mips/jz4740/prom.c b/arch/mips/jz4740/prom.c new file mode 100644 index 0000000..4068939 --- /dev/null +++ b/arch/mips/jz4740/prom.c @@ -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 +#include +#include +#include + +#include +#include + +/* #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); diff --git a/arch/mips/jz4740/reset.c b/arch/mips/jz4740/reset.c new file mode 100644 index 0000000..83577d8 --- /dev/null +++ b/arch/mips/jz4740/reset.c @@ -0,0 +1,46 @@ +/* + * linux/arch/mips/jz4740/reset.c + * + * JZ4740 reset routines. + * + * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +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(); +} diff --git a/arch/mips/jz4740/setup.c b/arch/mips/jz4740/setup.c new file mode 100644 index 0000000..e19f767 --- /dev/null +++ b/arch/mips/jz4740/setup.c @@ -0,0 +1,182 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PC_KEYB +#include +#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; + + jz_soc_setup(); + jz_serial_setup(); + jz_board_setup(); +} + diff --git a/arch/mips/jz4740/time.c b/arch/mips/jz4740/time.c new file mode 100644 index 0000000..a82b17c --- /dev/null +++ b/arch/mips/jz4740/time.c @@ -0,0 +1,158 @@ +/* + * linux/arch/mips/jz4740/time.c + * + * Setting up the clock on the JZ4740 boards. + * + * Copyright (C) 2008 Ingenic Semiconductor Inc. + * Author: + * + * 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 +#include +#include +#include + +#include +#include + +/* 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 */ + .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(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(); +} diff --git a/arch/mips/jz4750/Makefile b/arch/mips/jz4750/Makefile new file mode 100644 index 0000000..a9c6c16 --- /dev/null +++ b/arch/mips/jz4750/Makefile @@ -0,0 +1,23 @@ +# +# Makefile for the Ingenic JZ4750. +# + +# 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_JZ4750_FUWA) += board-fuwa.o +obj-$(CONFIG_JZ4750_APUS) += board-apus.o + +# PM support + +obj-$(CONFIG_PM_LEGACY) +=pm.o + +# CPU Frequency scaling support + +obj-$(CONFIG_CPU_FREQ_JZ) +=cpufreq.o diff --git a/arch/mips/jz4750/board-apus.c b/arch/mips/jz4750/board-apus.c new file mode 100644 index 0000000..7347dfa --- /dev/null +++ b/arch/mips/jz4750/board-apus.c @@ -0,0 +1,64 @@ +/* + * linux/arch/mips/jz4750/board-apus.c + * + * JZ4750 APUS board setup routines. + * + * Copyright (c) 2006-2008 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +extern void (*jz_timer_callback)(void); + +static void dancing(void) +{ +} + +static void apus_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/jz4750/setup.c. + */ +} + +static void __init board_gpio_setup(void) +{ + __gpio_as_pcm(); +} + +void __init jz_board_setup(void) +{ + printk("JZ4750 APUS board setup\n"); + + board_cpm_setup(); + board_gpio_setup(); + + jz_timer_callback = apus_timer_callback; +} diff --git a/arch/mips/jz4750/board-fuwa.c b/arch/mips/jz4750/board-fuwa.c new file mode 100644 index 0000000..c5aae06 --- /dev/null +++ b/arch/mips/jz4750/board-fuwa.c @@ -0,0 +1,105 @@ +/* + * linux/arch/mips/jz4750/board-fuwa.c + * + * JZ4750 FUWA board setup routines. + * + * Copyright (c) 2006-2008 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +extern void (*jz_timer_callback)(void); + +static void dancing(void) +{ + static unsigned char slash[] = "\\|/-"; +// static volatile unsigned char *p = (unsigned char *)0xb6000058; + static volatile unsigned char *p = (unsigned char *)0xb6000016; + static unsigned int count = 0; + *p = slash[count++]; + count &= 3; +} + +static void fuwa_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/jz4750/setup.c. + */ +} + +static void __init board_gpio_setup(void) +{ + /* + * Initialize SDRAM pins + */ + + /* PORT A: D0 ~ D31 */ + REG_GPIO_PXFUNS(0) = 0xffffffff; + REG_GPIO_PXSELC(0) = 0xffffffff; + + /* PORT B: A0 ~ A16, DCS#, RAS#, CAS#, CKE#, RDWE#, CKO#, WE0# */ + REG_GPIO_PXFUNS(1) = 0x81f9ffff; + REG_GPIO_PXSELC(1) = 0x81f9ffff; + + /* PORT C: WE1#, WE2#, WE3# */ + REG_GPIO_PXFUNS(2) = 0x07000000; + REG_GPIO_PXSELC(2) = 0x07000000; + + + /* + * Initialize UART0 pins + */ + + /* PORT D: TXD/RXD */ + REG_GPIO_PXFUNS(3) = 0x06000000; + REG_GPIO_PXSELS(3) = 0x06000000; + + + /* + * Initialize LED pins + */ + __gpio_as_lcd_18bit(); + + /* CS2# */ + REG_GPIO_PXFUNS(1) = 0x04000000; + REG_GPIO_PXSELC(1) = 0x04000000; + + __gpio_as_pcm(); +} + +void __init jz_board_setup(void) +{ + printk("JZ4750 FUWA board setup\n"); + + board_cpm_setup(); + board_gpio_setup(); + + jz_timer_callback = fuwa_timer_callback; +} diff --git a/arch/mips/jz4750/board-slt50.c b/arch/mips/jz4750/board-slt50.c new file mode 100644 index 0000000..e8e20b4 --- /dev/null +++ b/arch/mips/jz4750/board-slt50.c @@ -0,0 +1,64 @@ +/* + * linux/arch/mips/jz4750/board-apus.c + * + * JZ4750 APUS board setup routines. + * + * Copyright (c) 2006-2008 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +extern void (*jz_timer_callback)(void); + +static void dancing(void) +{ +} + +static void apus_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/jz4750/setup.c. + */ +} + +static void __init board_gpio_setup(void) +{ + __gpio_as_pcm(); +} + +void __init jz_board_setup(void) +{ + printk("JZ4750 SLT_50 board setup\n"); + + board_cpm_setup(); + board_gpio_setup(); + + jz_timer_callback = apus_timer_callback; +} diff --git a/arch/mips/jz4750/cpufreq.c b/arch/mips/jz4750/cpufreq.c new file mode 100644 index 0000000..83f98b1 --- /dev/null +++ b/arch/mips/jz4750/cpufreq.c @@ -0,0 +1,601 @@ +/* + * linux/arch/mips/jz4750/cpufreq.c + * + * cpufreq driver for JZ4750 + * + * Copyright (c) 2006-2008 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include + +#include +#include + +#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \ + "cpufreq-jz4750", 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 jz4750_freq_percpu_info { + struct cpufreq_frequency_table table[7]; +}; + +static struct jz4750_freq_percpu_info jz4750_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(0); +} + +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>>16) & 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 jz4750_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 jz4750_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 = jz4750_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 jz4750_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, ®s); + + 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) + jz4750_transition(®s); + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); +} + +static int jz4750_freq_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + unsigned int new_index = 0; + + if (cpufreq_frequency_table_target(policy, + &jz4750_freq_table.table[0], + target_freq, relation, &new_index)) + return -EINVAL; + + jz4750_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 jz4750_freq_verify(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, + &jz4750_freq_table.table[0]); +} + +static int __init jz4750_cpufreq_driver_init(struct cpufreq_policy *policy) +{ + + struct cpufreq_frequency_table *table = &jz4750_freq_table.table[0]; + unsigned int MAX_FREQ; + + dprintk(KERN_INFO "Jz4750 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_jz4750_driver = { +// .flags = CPUFREQ_STICKY, + .init = jz4750_cpufreq_driver_init, + .verify = jz4750_freq_verify, + .target = jz4750_freq_target, + .get = jz4750_freq_get, + .name = "jz4750", +}; + +static int __init jz4750_cpufreq_init(void) +{ + return cpufreq_register_driver(&cpufreq_jz4750_driver); +} + +static void __exit jz4750_cpufreq_exit(void) +{ + cpufreq_unregister_driver(&cpufreq_jz4750_driver); +} + +module_init(jz4750_cpufreq_init); +module_exit(jz4750_cpufreq_exit); + +MODULE_AUTHOR("Regen "); +MODULE_DESCRIPTION("cpufreq driver for Jz4750"); +MODULE_LICENSE("GPL"); diff --git a/arch/mips/jz4750/dma.c b/arch/mips/jz4750/dma.c new file mode 100644 index 0000000..2508111 --- /dev/null +++ b/arch/mips/jz4750/dma.c @@ -0,0 +1,836 @@ +/* + * linux/arch/mips/jz4750/dma.c + * + * Support functions for the JZ4750 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 - 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 as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * 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:DMA_ID_BCH_ENC,}, /* DMAC0 channel 0, reserved for BCH */ + {dev_id:-1,}, /* DMAC0 channel 1 */ + {dev_id:-1,}, /* DMAC0 channel 2 */ + {dev_id:-1,}, /* DMAC0 channel 3 */ + {dev_id:-1,}, /* DMAC0 channel 4 */ + {dev_id:-1,}, /* DMAC0 channel 5 */ + {dev_id:-1,}, /* DMAC1 channel 0 */ + {dev_id:-1,}, /* DMAC1 channel 1 */ + {dev_id:-1,}, /* DMAC1 channel 2 */ + {dev_id:-1,}, /* DMAC1 channel 3 */ + {dev_id:-1,}, /* DMAC1 channel 4 */ + {dev_id:-1,}, /* DMAC1 channel 5 */ +}; + +// 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] = { + {0, DMA_AUTOINIT, DMAC_DRSR_RS_EXT}, /* External request with DREQn */ + {0x18000000, DMA_AUTOINIT, DMAC_DRSR_RS_NAND}, /* NAND request */ + {CPHYSADDR(BCH_DR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_BCH_ENC}, + {CPHYSADDR(BCH_DR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_BCH_DEC}, + {0, DMA_AUTOINIT, DMAC_DRSR_RS_AUTO}, +// {CPHYSADDR(TSSI_FIFO), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_TSSIIN}, + {CPHYSADDR(UART3_TDR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_UART3OUT}, + {CPHYSADDR(UART3_RDR), DMA_8BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_UART3IN}, + {CPHYSADDR(UART2_TDR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_UART2OUT}, + {CPHYSADDR(UART2_RDR), DMA_8BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_UART2IN}, + {CPHYSADDR(UART1_TDR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_UART1OUT}, + {CPHYSADDR(UART1_RDR), DMA_8BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_UART1IN}, + {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(0)), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_SSI0OUT}, + {CPHYSADDR(SSI_DR(0)), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_SSI0IN}, + {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(0)), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_MSC0OUT}, + {CPHYSADDR(MSC_RXFIFO(0)), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_MSC0IN}, + {0, DMA_AUTOINIT, DMAC_DRSR_RS_TCU}, + {SADC_TSDAT, DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_SADC},/* Touch Screen Data Register */ + {CPHYSADDR(MSC_TXFIFO(1)), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_MSC1OUT}, /* SSC1 TX */ + {CPHYSADDR(MSC_RXFIFO(1)), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_MSC1IN}, /* SSC1 RX */ + {CPHYSADDR(SSI_DR(1)), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_SSI1OUT}, + {CPHYSADDR(SSI_DR(1)), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_SSI1IN}, + {CPHYSADDR(PCM_DP), DMA_16BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_PMOUT}, + {CPHYSADDR(PCM_DP), DMA_16BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_PMIN}, + {}, +}; + + +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(chan->io/HALF_DMA_NUM)); + 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(chan->io/HALF_DMA_NUM)); +} + + +/** + * 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, chan0; + + if (dev_id < 0 || dev_id >= DMA_ID_MAX) + return -EINVAL; + + /* Because of a bug in DMA controller of jz4750, which causes auto + request and device request can't be allocated in a same DMA + controller, all device requests should be put in the second DMA + controller + */ + if (dev_id > DMA_ID_AUTO) + chan0 = 6; + else + chan0 = 0; + + for (i = chan0; 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; + + if (i < 6) + REG_DMAC_DMACKE(0) = 1 << i; + else + REG_DMAC_DMACKE(1) = 1 << (i - 6); + + 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 = 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("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; + } +} + +//#define JZ4750_DMAC_TEST_ENABLE +#undef JZ4750_DMAC_TEST_ENABLE + +#ifdef JZ4750_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 irqreturn_t jz4750_dma_irq(int irq, void *dev_id) +{ + printk("jz4750_dma_irq %d\n", irq); + + + if (__dmac_channel_transmit_halt_detected(dma_chan)) { + printk("DMA HALT\n"); + REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + __dmac_channel_clear_transmit_halt(dma_chan); + } + + if (__dmac_channel_address_error_detected(dma_chan)) { + printk("DMA ADDR ERROR\n"); + REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + REG_DMAC_DSAR(dma_chan) = 0; /* clear source address register */ + REG_DMAC_DTAR(dma_chan) = 0; /* clear target address register */ + __dmac_channel_clear_address_error(dma_chan); + } + + if (__dmac_channel_descriptor_invalid_detected(dma_chan)) { + REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + 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)) { + REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + 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); + } + + return IRQ_HANDLED; +} + +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", jz4750_dma_irq, + IRQF_DISABLED, 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(dma_chan/HALF_DMA_NUM) = 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(dma_chan/HALF_DMA_NUM) = DMAC_DMACR_DMAE; /* global DMA enable bit */ + + printk("DMA started. IMR=%08x\n", REG_INTC_IMR); + + /* wait a long time, ensure transfer end */ + printk("wait 3s...\n"); + mdelay(3000); /* wait 3s */ + + REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + /* 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_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", jz4750_dma_irq, + IRQF_DISABLED, 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_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(dma_chan/HALF_DMA_NUM) = DMAC_DMACR_DMAE; + + /* DMA doorbell set -- start DMA now ... */ + REG_DMAC_DMADBSR(dma_chan/HALF_DMA_NUM) = 1 << dma_chan; + + printk("DMA started. IMR=%08x\n", REG_INTC_IMR); + /* wait a long time, ensure transfer end */ + printk("wait 3s...\n"); + mdelay(3000); /* wait 3s */ + + REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + /* 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); +} + +#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); diff --git a/arch/mips/jz4750/i2c.c b/arch/mips/jz4750/i2c.c new file mode 100644 index 0000000..cdb1223 --- /dev/null +++ b/arch/mips/jz4750/i2c.c @@ -0,0 +1,273 @@ +/* + * linux/arch/mips/jz4750/i2c.c + * + * Jz4750 I2C routines. + * + * Copyright (C) 2005,2006 Ingenic Semiconductor Inc. + * Author: + * + * 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 +#include +#include +#include +#include + +#include + +/* 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); diff --git a/arch/mips/jz4750/irq.c b/arch/mips/jz4750/irq.c new file mode 100644 index 0000000..6a9d867 --- /dev/null +++ b/arch/mips/jz4750/irq.c @@ -0,0 +1,299 @@ +/* + * linux/arch/mips/jz4750/irq.c + * + * JZ4750 interrupt routines. + * + * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * INTC irq type + */ + +static void enable_intc_irq(unsigned int irq) +{ + __intc_unmask_irq(irq); +} + +static void disable_intc_irq(unsigned int irq) +{ + __intc_mask_irq(irq); +} + +static void mask_and_ack_intc_irq(unsigned int irq) +{ + __intc_mask_irq(irq); + __intc_ack_irq(irq); +} + +static void end_intc_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { + enable_intc_irq(irq); + } +} + +static unsigned int startup_intc_irq(unsigned int irq) +{ + enable_intc_irq(irq); + return 0; +} + +static void shutdown_intc_irq(unsigned int irq) +{ + disable_intc_irq(irq); +} + +static struct irq_chip intc_irq_type = { + .typename = "INTC", + .startup = startup_intc_irq, + .shutdown = shutdown_intc_irq, + .enable = enable_intc_irq, + .disable = disable_intc_irq, + .ack = mask_and_ack_intc_irq, + .end = end_intc_irq, +}; + +/* + * GPIO irq type + */ + +static void enable_gpio_irq(unsigned int irq) +{ + unsigned int intc_irq; + + if (irq < (IRQ_GPIO_0 + 32)) { + intc_irq = IRQ_GPIO0; + } + else if (irq < (IRQ_GPIO_0 + 64)) { + intc_irq = IRQ_GPIO1; + } + else if (irq < (IRQ_GPIO_0 + 96)) { + intc_irq = IRQ_GPIO2; + } + else if (irq < (IRQ_GPIO_0 + 128)) { + intc_irq = IRQ_GPIO3; + } + else if (irq < (IRQ_GPIO_0 + 160)) { + intc_irq = IRQ_GPIO4; + } + else { + intc_irq = IRQ_GPIO5; + } + + enable_intc_irq(intc_irq); + __gpio_unmask_irq(irq - IRQ_GPIO_0); +} + +static void disable_gpio_irq(unsigned int irq) +{ + __gpio_mask_irq(irq - IRQ_GPIO_0); +} + +static void mask_and_ack_gpio_irq(unsigned int irq) +{ + __gpio_mask_irq(irq - IRQ_GPIO_0); + __gpio_ack_irq(irq - IRQ_GPIO_0); +} + +static void end_gpio_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { + enable_gpio_irq(irq); + } +} + +static unsigned int startup_gpio_irq(unsigned int irq) +{ + enable_gpio_irq(irq); + return 0; +} + +static void shutdown_gpio_irq(unsigned int irq) +{ + disable_gpio_irq(irq); +} + +static struct irq_chip gpio_irq_type = { + .typename = "GPIO", + .startup = startup_gpio_irq, + .shutdown = shutdown_gpio_irq, + .enable = enable_gpio_irq, + .disable = disable_gpio_irq, + .ack = mask_and_ack_gpio_irq, + .end = end_gpio_irq, +}; + +/* + * DMA irq type + */ + +static void enable_dma_irq(unsigned int irq) +{ + unsigned int intc_irq; + + if ( irq < (IRQ_DMA_0 + HALF_DMA_NUM) ) /* DMAC Group 0 irq */ + intc_irq = IRQ_DMAC0; + else if ( irq < (IRQ_DMA_0 + MAX_DMA_NUM) ) /* DMAC Group 1 irq */ + intc_irq = IRQ_DMAC1; + else { + printk("%s, unexpected dma irq #%d\n", __FILE__, irq); + return; + } + __intc_unmask_irq(intc_irq); + __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) +{ + unsigned int intc_irq; + + if ( irq < (IRQ_DMA_0 + HALF_DMA_NUM) ) /* DMAC Group 0 irq */ + intc_irq = IRQ_DMAC0; + else if ( irq < (IRQ_DMA_0 + MAX_DMA_NUM) ) /* DMAC Group 1 irq */ + intc_irq = IRQ_DMAC1; + else { + printk("%s, unexpected dma irq #%d\n", __FILE__, irq); + return ; + } + __intc_ack_irq(intc_irq); + __dmac_channel_ack_irq(irq-IRQ_DMA_0); /* needed?? add 20080506, Wolfgang */ + __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 unsigned int startup_dma_irq(unsigned int irq) +{ + enable_dma_irq(irq); + return 0; +} + +static void shutdown_dma_irq(unsigned int irq) +{ + disable_dma_irq(irq); +} + +static struct irq_chip dma_irq_type = { + .typename = "DMA", + .startup = startup_dma_irq, + .shutdown = shutdown_dma_irq, + .enable = enable_dma_irq, + .disable = 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 */ + + /* Set up INTC irq + */ + for (i = 0; i < 32; i++) { + disable_intc_irq(i); + irq_desc[i].chip = &intc_irq_type; + } + + /* Set up DMAC irq + */ + for (i = 0; i < NUM_DMA; i++) { + disable_dma_irq(IRQ_DMA_0 + i); + irq_desc[IRQ_DMA_0 + i].chip = &dma_irq_type; + } + + /* Set up GPIO irq + */ + for (i = 0; i < NUM_GPIO; i++) { + disable_gpio_irq(IRQ_GPIO_0 + i); + irq_desc[IRQ_GPIO_0 + i].chip = &gpio_irq_type; + } +} + +static int plat_real_irq(int irq) +{ + switch (irq) { + case IRQ_GPIO0: + irq = __gpio_group_irq(0) + IRQ_GPIO_0; + break; + case IRQ_GPIO1: + irq = __gpio_group_irq(1) + IRQ_GPIO_0 + 32; + break; + case IRQ_GPIO2: + irq = __gpio_group_irq(2) + IRQ_GPIO_0 + 64; + break; + case IRQ_GPIO3: + irq = __gpio_group_irq(3) + IRQ_GPIO_0 + 96; + break; + case IRQ_GPIO4: + irq = __gpio_group_irq(4) + IRQ_GPIO_0 + 128; + break; + case IRQ_GPIO5: + irq = __gpio_group_irq(5) + IRQ_GPIO_0 + 160; + break; + case IRQ_DMAC0: + case IRQ_DMAC1: + irq = __dmac_get_irq() + IRQ_DMA_0; + break; + } + + return irq; +} + +asmlinkage void plat_irq_dispatch(void) +{ + int irq = 0; + static unsigned long intc_ipr = 0; + + intc_ipr |= REG_INTC_IPR; + + if (!intc_ipr) return; + + irq = ffs(intc_ipr) - 1; + intc_ipr &= ~(1< + * + * 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 +#include +#include +#include +#include + +#include + +/* 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_MSC0, + .end = IRQ_MSC0, + .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, +}; + +/* All */ +static struct platform_device *jz_platform_devices[] __initdata = { + &jz_usb_ohci_device, + &jz_lcd_device, + &jz_usb_gdt_device, + &jz_mmc_device, +}; + +static int __init jz_platform_init(void) +{ + return platform_add_devices(jz_platform_devices, ARRAY_SIZE(jz_platform_devices)); +} + +arch_initcall(jz_platform_init); diff --git a/arch/mips/jz4750/pm.c b/arch/mips/jz4750/pm.c new file mode 100644 index 0000000..b918cb2 --- /dev/null +++ b/arch/mips/jz4750/pm.c @@ -0,0 +1,463 @@ +/* + * linux/arch/mips/jz4750/common/pm.c + * + * JZ4750 Power Management Routines + * + * Copyright (C) 2006 Ingenic Semiconductor Inc. + * Author: + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#undef DEBUG +//#define DEBUG +#ifdef DEBUG +#define dprintk(x...) printk(x) +#else +#define dprintk(x...) +#endif + +#define GPIO_PORT_NUM 6 + +/* + * __gpio_as_sleep set all pins to pull-disable, and set all pins as input + * except sdram 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) = ~0x03ff7fff; \ + REG_GPIO_PXSELC(1) = ~0x03ff7fff; \ + REG_GPIO_PXDIRC(1) = ~0x03ff7fff; \ + REG_GPIO_PXPES(1) = 0xffffffff; \ + REG_GPIO_PXFUNC(2) = ~0x01e00000; \ + REG_GPIO_PXSELC(2) = ~0x01e00000; \ + REG_GPIO_PXDIRC(2) = ~0x01e00000; \ + 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; \ + REG_GPIO_PXFUNC(4) = 0xffffffff; \ + REG_GPIO_PXSELC(4) = 0xffffffff; \ + REG_GPIO_PXDIRC(4) = 0xffffffff; \ + REG_GPIO_PXPES(4) = 0xffffffff; \ + REG_GPIO_PXFUNC(5) = 0xffffffff; \ + REG_GPIO_PXSELC(5) = 0xffffffff; \ + REG_GPIO_PXDIRC(5) = 0xffffffff; \ + REG_GPIO_PXPES(5) = 0xffffffff; \ +} while (0) + +static int jz_pm_do_hibernate(void) +{ + printk("Put CPU into hibernate mode.\n"); + + /* 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#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, "MSC0 : %s\n", + (clkgr & CPM_CLKGR_MSC0) ? "stopped" : "running"); + len += sprintf (page+len, "MSC1 : %s\n", + (clkgr & CPM_CLKGR_MSC1) ? "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, "SSI0 : %s\n", + (clkgr & CPM_CLKGR_SSI0) ? "stopped" : "running"); + len += sprintf (page+len, "SSI1 : %s\n", + (clkgr & CPM_CLKGR_SSI1) ? "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, "MSC0CLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_mscclk(0))); + len += sprintf (page+len, "MSC1CLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_mscclk(1))); + len += sprintf (page+len, "EXTALCLK0 : %3d.%02d MHz\n", TO_MHZ(__cpm_get_extalclk0())); + len += sprintf (page+len, "EXTALCLK(by CPM): %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 */ + entrylo0 = entrylo0 >> 6; + entrylo0 |= 0x6 | (0 << 3); + /*entrylo0 |= 0x6 | (1 << 3);*/ + + do_each_thread(g, p) { + if (p->pid == pid ) + g_asid = p->mm->context[0]; + } while_each_thread(g, p); + + local_irq_save(flags); + /* Save old context and create impossible VPN2 value */ + old_ctx = read_c0_entryhi() & 0xff; + old_pagemask = read_c0_pagemask(); + wired = read_c0_wired(); + 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; + + 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<= 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 == 41) { + 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 <= 9 ) { + 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 */ + //pagemask = 0xfff << 13; /* Fixed to 16MB page size */ + + ipu_add_wired_entry(pid, entrylo0, 0, entryhi, pagemask); + return 44; + } else if (count == 12) { + printk("\necho release tlb > /proc/jz/ipu\n"); + ipu_del_wired_entry(); + return 12; + } 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 /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 12 /* 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; + } + + /* show tlb */ + res = create_proc_entry("tlb", 0644, proc_jz_root); + if (res) { + res->read_proc = tlb_read_proc; + res->write_proc = NULL; + res->data = NULL; + } + + /* + * Reserve a 4MB memory for IPU on JZ4750. + */ + 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); + } else + printk("NOT enough memory for imem\n"); + + return 0; +} + +__initcall(jz_proc_init); diff --git a/arch/mips/jz4750/prom.c b/arch/mips/jz4750/prom.c new file mode 100644 index 0000000..d04bb3e --- /dev/null +++ b/arch/mips/jz4750/prom.c @@ -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 +#include +#include +#include + +#include +#include + +/* #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_JZ4750; + + 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 "JZ4750"; +} + +EXPORT_SYMBOL(prom_getcmdline); +EXPORT_SYMBOL(get_ethernet_addr); +EXPORT_SYMBOL(str2eaddr); diff --git a/arch/mips/jz4750/reset.c b/arch/mips/jz4750/reset.c new file mode 100644 index 0000000..90b521e --- /dev/null +++ b/arch/mips/jz4750/reset.c @@ -0,0 +1,46 @@ +/* + * linux/arch/mips/jz4750/reset.c + * + * JZ4750 reset routines. + * + * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +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_TSCR_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(); +} diff --git a/arch/mips/jz4750/setup.c b/arch/mips/jz4750/setup.c new file mode 100644 index 0000000..b9b4240 --- /dev/null +++ b/arch/mips/jz4750/setup.c @@ -0,0 +1,197 @@ +/* + * linux/arch/mips/jz4750/common/setup.c + * + * JZ4750 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PC_KEYB +#include +#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(0); + 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(0); + __dmac_enable_module(1); +} + +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"); + } + + s.line = 2; + s.membase = (u8 *)UART2_BASE; + s.irq = IRQ_UART2; + if (early_serial_setup(&s) != 0) { + printk(KERN_ERR "Serial ttyS2 setup failed!\n"); + } + + s.line = 3; + s.membase = (u8 *)UART3_BASE; + s.irq = IRQ_UART3; + if (early_serial_setup(&s) != 0) { + printk(KERN_ERR "Serial ttyS3 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; + + jz_soc_setup(); + jz_serial_setup(); + jz_board_setup(); +} + diff --git a/arch/mips/jz4750/time.c b/arch/mips/jz4750/time.c new file mode 100644 index 0000000..e5d18b5 --- /dev/null +++ b/arch/mips/jz4750/time.c @@ -0,0 +1,156 @@ +/* + * linux/arch/mips/jz4750/time.c + * + * Setting up the clock on the JZ4750 boards. + * + * Copyright (C) 2008 Ingenic Semiconductor Inc. + * Author: + * + * 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 +#include +#include +#include + +#include +#include + +/* This is for machines which generate the exact clock. */ + +#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 = TCU_TFCR_OSTFCL; /* 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_OSTCNT); +} + +static struct clocksource clocksource_jz = { + .name = "jz_clocksource", + .rating = 300, + .read = jz_get_cycles, + .mask = 0xFFFFFFFF, + .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 */ + .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(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_OSTCSR = TCU_OSTCSR_PRESCALE16 | TCU_OSTCSR_EXT_EN; + REG_TCU_OSTCNT = 0; + REG_TCU_OSTDR = latch; + + REG_TCU_TMCR = TCU_TMCR_OSTMCL; /* unmask match irq */ + REG_TCU_TSCR = TCU_TSCR_OSTSC; /* enable timer clock */ + REG_TCU_TESR = TCU_TESR_OSTST; /* start counting up */ + + jz_timer_setup(); +} diff --git a/arch/mips/jz4750d/Makefile b/arch/mips/jz4750d/Makefile new file mode 100644 index 0000000..6bd5189 --- /dev/null +++ b/arch/mips/jz4750d/Makefile @@ -0,0 +1,23 @@ +# +# Makefile for the Ingenic JZ4750D. +# + +# 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_JZ4750D_FUWA1) += board-fuwa1.o +obj-$(CONFIG_JZ4750D_CETUS) += board-cetus.o + +# PM support + +obj-$(CONFIG_PM_LEGACY) +=pm.o + +# CPU Frequency scaling support + +obj-$(CONFIG_CPU_FREQ_JZ) +=cpufreq.o diff --git a/arch/mips/jz4750d/board-cetus.c b/arch/mips/jz4750d/board-cetus.c new file mode 100644 index 0000000..0febbd3 --- /dev/null +++ b/arch/mips/jz4750d/board-cetus.c @@ -0,0 +1,72 @@ +/* + * linux/arch/mips/jz4750d/board-cetus.c + * + * JZ4750D CETUS board setup routines. + * + * Copyright (c) 2006-2008 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +extern void (*jz_timer_callback)(void); + +static void dancing(void) +{ + static unsigned char slash[] = "\\|/-"; +// static volatile unsigned char *p = (unsigned char *)0xb6000058; + static volatile unsigned char *p = (unsigned char *)0xb6000016; + static unsigned int count = 0; + *p = slash[count++]; + count &= 3; +} + +static void cetus_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/jz4750d/setup.c. + */ +} + +static void __init board_gpio_setup(void) +{ + /* + * Initialize SDRAM pins + */ +} + +void __init jz_board_setup(void) +{ + printk("JZ4750D CETUS board setup\n"); + + board_cpm_setup(); + board_gpio_setup(); + + jz_timer_callback = cetus_timer_callback; +} diff --git a/arch/mips/jz4750d/board-fuwa1.c b/arch/mips/jz4750d/board-fuwa1.c new file mode 100644 index 0000000..e9294c3 --- /dev/null +++ b/arch/mips/jz4750d/board-fuwa1.c @@ -0,0 +1,72 @@ +/* + * linux/arch/mips/jz4750d/board-fuwa1.c + * + * JZ4750D FUWA1 board setup routines. + * + * Copyright (c) 2006-2008 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +extern void (*jz_timer_callback)(void); + +static void dancing(void) +{ + static unsigned char slash[] = "\\|/-"; +// static volatile unsigned char *p = (unsigned char *)0xb6000058; + static volatile unsigned char *p = (unsigned char *)0xb6000016; + static unsigned int count = 0; + *p = slash[count++]; + count &= 3; +} + +static void fuwa1_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/jz4750d/setup.c. + */ +} + +static void __init board_gpio_setup(void) +{ + /* + * Initialize SDRAM pins + */ +} + +void __init jz_board_setup(void) +{ + printk("JZ4750D FUWA1 board setup\n"); + + board_cpm_setup(); + board_gpio_setup(); + + jz_timer_callback = fuwa1_timer_callback; +} diff --git a/arch/mips/jz4750d/cpufreq.c b/arch/mips/jz4750d/cpufreq.c new file mode 100644 index 0000000..c0d63ac --- /dev/null +++ b/arch/mips/jz4750d/cpufreq.c @@ -0,0 +1,598 @@ +/* + * linux/arch/mips/jz4750d/cpufreq.c + * + * cpufreq driver for JZ4750D + * + * Copyright (c) 2006-2008 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include + +#include +#include + +#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \ + "cpufreq-jz4750d", 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 lcdpix_clk; /* LCD Pixel clock, Hz */ + unsigned int lcd_clks_initialized; +} boot_config; + +struct jz4750d_freq_percpu_info { + struct cpufreq_frequency_table table[7]; +}; + +static struct jz4750d_freq_percpu_info jz4750d_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.pixclk = __cpm_get_pixclk(); + jz_clocks.i2sclk = __cpm_get_i2sclk(); + jz_clocks.usbclk = __cpm_get_usbclk(); + jz_clocks.mscclk = __cpm_get_mscclk(0); +} + +static void +jz_init_boot_config(void) +{ + if (!boot_config.lcd_clks_initialized) { + /* the first time to scale pll */ + 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>>16) & 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 jz4750d_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 jz4750d_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 = jz4750d_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 jz4750d_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, ®s); + + 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) + jz4750d_transition(®s); + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); +} + +static int jz4750d_freq_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + unsigned int new_index = 0; + + if (cpufreq_frequency_table_target(policy, + &jz4750d_freq_table.table[0], + target_freq, relation, &new_index)) + return -EINVAL; + + jz4750d_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 jz4750d_freq_verify(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, + &jz4750d_freq_table.table[0]); +} + +static int __init jz4750d_cpufreq_driver_init(struct cpufreq_policy *policy) +{ + + struct cpufreq_frequency_table *table = &jz4750d_freq_table.table[0]; + unsigned int MAX_FREQ; + + dprintk(KERN_INFO "Jz4750d 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_jz4750d_driver = { +// .flags = CPUFREQ_STICKY, + .init = jz4750d_cpufreq_driver_init, + .verify = jz4750d_freq_verify, + .target = jz4750d_freq_target, + .get = jz4750d_freq_get, + .name = "jz4750d", +}; + +static int __init jz4750d_cpufreq_init(void) +{ + return cpufreq_register_driver(&cpufreq_jz4750d_driver); +} + +static void __exit jz4750d_cpufreq_exit(void) +{ + cpufreq_unregister_driver(&cpufreq_jz4750d_driver); +} + +module_init(jz4750d_cpufreq_init); +module_exit(jz4750d_cpufreq_exit); + +MODULE_AUTHOR("Regen "); +MODULE_DESCRIPTION("cpufreq driver for Jz4750d"); +MODULE_LICENSE("GPL"); diff --git a/arch/mips/jz4750d/dma.c b/arch/mips/jz4750d/dma.c new file mode 100644 index 0000000..290cc12 --- /dev/null +++ b/arch/mips/jz4750d/dma.c @@ -0,0 +1,822 @@ +/* + * linux/arch/mips/jz4750d/dma.c + * + * Support functions for the JZ4750D 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 - 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 as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * 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:DMA_ID_BCH_ENC,}, /* DMAC0 channel 0, reserved for BCH */ + {dev_id:-1,}, /* DMAC0 channel 1 */ + {dev_id:-1,}, /* DMAC0 channel 2 */ + {dev_id:-1,}, /* DMAC0 channel 3 */ + {dev_id:-1,}, /* DMAC1 channel 0 */ + {dev_id:-1,}, /* DMAC1 channel 1 */ + {dev_id:-1,}, /* DMAC1 channel 2 */ + {dev_id:-1,}, /* DMAC1 channel 3 */ +}; + +// 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] = { + {0, DMA_AUTOINIT, DMAC_DRSR_RS_EXT}, /* External request with DREQn */ + {0x18000000, DMA_AUTOINIT, DMAC_DRSR_RS_NAND}, /* NAND request */ + {CPHYSADDR(BCH_DR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_BCH_ENC}, + {CPHYSADDR(BCH_DR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_BCH_DEC}, + {0, DMA_AUTOINIT, DMAC_DRSR_RS_AUTO}, +// {CPHYSADDR(TSSI_FIFO), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_TSSIIN}, + {CPHYSADDR(UART3_TDR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_UART3OUT}, + {CPHYSADDR(UART3_RDR), DMA_8BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_UART3IN}, + {CPHYSADDR(UART2_TDR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_UART2OUT}, + {CPHYSADDR(UART2_RDR), DMA_8BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_UART2IN}, + {CPHYSADDR(UART1_TDR), DMA_8BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_UART1OUT}, + {CPHYSADDR(UART1_RDR), DMA_8BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_UART1IN}, + {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(0)), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_SSI0OUT}, + {CPHYSADDR(SSI_DR(0)), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_SSI0IN}, + {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(0)), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_MSC0OUT}, + {CPHYSADDR(MSC_RXFIFO(0)), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_MSC0IN}, + {0, DMA_AUTOINIT, DMAC_DRSR_RS_TCU}, + {SADC_TSDAT, DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_SADC},/* Touch Screen Data Register */ + {CPHYSADDR(MSC_TXFIFO(1)), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_MSC1OUT}, /* SSC1 TX */ + {CPHYSADDR(MSC_RXFIFO(1)), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_MSC1IN}, /* SSC1 RX */ + {CPHYSADDR(SSI_DR(1)), DMA_32BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_SSI1OUT}, + {CPHYSADDR(SSI_DR(1)), DMA_32BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_SSI1IN}, + {CPHYSADDR(PCM_DP), DMA_16BIT_TX_CMD | DMA_MODE_WRITE, DMAC_DRSR_RS_PMOUT}, + {CPHYSADDR(PCM_DP), DMA_16BIT_RX_CMD | DMA_MODE_READ, DMAC_DRSR_RS_PMIN}, + {}, +}; + + +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(chan->io/HALF_DMA_NUM)); + 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(chan->io/HALF_DMA_NUM)); +} + + +/** + * 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; + + if (i < HALF_DMA_NUM) + REG_DMAC_DMACKE(0) = 1 << i; + else + REG_DMAC_DMACKE(1) = 1 << (i - HALF_DMA_NUM); + + 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 = 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("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; + } +} + +//#define JZ4750D_DMAC_TEST_ENABLE +#undef JZ4750D_DMAC_TEST_ENABLE + +#ifdef JZ4750D_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 irqreturn_t jz4750d_dma_irq(int irq, void *dev_id) +{ + printk("jz4750d_dma_irq %d\n", irq); + + + if (__dmac_channel_transmit_halt_detected(dma_chan)) { + printk("DMA HALT\n"); + REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + __dmac_channel_clear_transmit_halt(dma_chan); + } + + if (__dmac_channel_address_error_detected(dma_chan)) { + printk("DMA ADDR ERROR\n"); + REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + REG_DMAC_DSAR(dma_chan) = 0; /* clear source address register */ + REG_DMAC_DTAR(dma_chan) = 0; /* clear target address register */ + __dmac_channel_clear_address_error(dma_chan); + } + + if (__dmac_channel_descriptor_invalid_detected(dma_chan)) { + REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + 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)) { + REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + 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); + } + + return IRQ_HANDLED; +} + +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", jz4750d_dma_irq, + IRQF_DISABLED, 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(dma_chan/HALF_DMA_NUM) = 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(dma_chan/HALF_DMA_NUM) = DMAC_DMACR_DMAE; /* global DMA enable bit */ + + printk("DMA started. IMR=%08x\n", REG_INTC_IMR); + + /* wait a long time, ensure transfer end */ + printk("wait 3s...\n"); + mdelay(3000); /* wait 3s */ + + REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + /* 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_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", jz4750d_dma_irq, + IRQF_DISABLED, 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_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(dma_chan/HALF_DMA_NUM) = DMAC_DMACR_DMAE; + + /* DMA doorbell set -- start DMA now ... */ + REG_DMAC_DMADBSR(dma_chan/HALF_DMA_NUM) = 1 << dma_chan; + + printk("DMA started. IMR=%08x\n", REG_INTC_IMR); + /* wait a long time, ensure transfer end */ + printk("wait 3s...\n"); + mdelay(3000); /* wait 3s */ + + REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + /* 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); +} + +#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); diff --git a/arch/mips/jz4750d/i2c.c b/arch/mips/jz4750d/i2c.c new file mode 100644 index 0000000..c12142f --- /dev/null +++ b/arch/mips/jz4750d/i2c.c @@ -0,0 +1,273 @@ +/* + * linux/arch/mips/jz4750d/i2c.c + * + * Jz4750D I2C routines. + * + * Copyright (C) 2005,2006 Ingenic Semiconductor Inc. + * Author: + * + * 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 +#include +#include +#include +#include + +#include + +/* 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); diff --git a/arch/mips/jz4750d/irq.c b/arch/mips/jz4750d/irq.c new file mode 100644 index 0000000..aa43d9b --- /dev/null +++ b/arch/mips/jz4750d/irq.c @@ -0,0 +1,299 @@ +/* + * linux/arch/mips/jz4750d/irq.c + * + * JZ4750D interrupt routines. + * + * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * INTC irq type + */ + +static void enable_intc_irq(unsigned int irq) +{ + __intc_unmask_irq(irq); +} + +static void disable_intc_irq(unsigned int irq) +{ + __intc_mask_irq(irq); +} + +static void mask_and_ack_intc_irq(unsigned int irq) +{ + __intc_mask_irq(irq); + __intc_ack_irq(irq); +} + +static void end_intc_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { + enable_intc_irq(irq); + } +} + +static unsigned int startup_intc_irq(unsigned int irq) +{ + enable_intc_irq(irq); + return 0; +} + +static void shutdown_intc_irq(unsigned int irq) +{ + disable_intc_irq(irq); +} + +static struct irq_chip intc_irq_type = { + .typename = "INTC", + .startup = startup_intc_irq, + .shutdown = shutdown_intc_irq, + .enable = enable_intc_irq, + .disable = disable_intc_irq, + .ack = mask_and_ack_intc_irq, + .end = end_intc_irq, +}; + +/* + * GPIO irq type + */ + +static void enable_gpio_irq(unsigned int irq) +{ + unsigned int intc_irq; + + if (irq < (IRQ_GPIO_0 + 32)) { + intc_irq = IRQ_GPIO0; + } + else if (irq < (IRQ_GPIO_0 + 64)) { + intc_irq = IRQ_GPIO1; + } + else if (irq < (IRQ_GPIO_0 + 96)) { + intc_irq = IRQ_GPIO2; + } + else if (irq < (IRQ_GPIO_0 + 128)) { + intc_irq = IRQ_GPIO3; + } + else if (irq < (IRQ_GPIO_0 + 160)) { + intc_irq = IRQ_GPIO4; + } + else { + intc_irq = IRQ_GPIO5; + } + + enable_intc_irq(intc_irq); + __gpio_unmask_irq(irq - IRQ_GPIO_0); +} + +static void disable_gpio_irq(unsigned int irq) +{ + __gpio_mask_irq(irq - IRQ_GPIO_0); +} + +static void mask_and_ack_gpio_irq(unsigned int irq) +{ + __gpio_mask_irq(irq - IRQ_GPIO_0); + __gpio_ack_irq(irq - IRQ_GPIO_0); +} + +static void end_gpio_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { + enable_gpio_irq(irq); + } +} + +static unsigned int startup_gpio_irq(unsigned int irq) +{ + enable_gpio_irq(irq); + return 0; +} + +static void shutdown_gpio_irq(unsigned int irq) +{ + disable_gpio_irq(irq); +} + +static struct irq_chip gpio_irq_type = { + .typename = "GPIO", + .startup = startup_gpio_irq, + .shutdown = shutdown_gpio_irq, + .enable = enable_gpio_irq, + .disable = disable_gpio_irq, + .ack = mask_and_ack_gpio_irq, + .end = end_gpio_irq, +}; + +/* + * DMA irq type + */ + +static void enable_dma_irq(unsigned int irq) +{ + unsigned int intc_irq; + + if ( irq < (IRQ_DMA_0 + HALF_DMA_NUM) ) /* DMAC Group 0 irq */ + intc_irq = IRQ_DMAC0; + else if ( irq < (IRQ_DMA_0 + MAX_DMA_NUM) ) /* DMAC Group 1 irq */ + intc_irq = IRQ_DMAC1; + else { + printk("%s, unexpected dma irq #%d\n", __FILE__, irq); + return; + } + __intc_unmask_irq(intc_irq); + __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) +{ + unsigned int intc_irq; + + if ( irq < (IRQ_DMA_0 + HALF_DMA_NUM) ) /* DMAC Group 0 irq */ + intc_irq = IRQ_DMAC0; + else if ( irq < (IRQ_DMA_0 + MAX_DMA_NUM) ) /* DMAC Group 1 irq */ + intc_irq = IRQ_DMAC1; + else { + printk("%s, unexpected dma irq #%d\n", __FILE__, irq); + return ; + } + __intc_ack_irq(intc_irq); + __dmac_channel_ack_irq(irq-IRQ_DMA_0); /* needed?? add 20080506, Wolfgang */ + __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 unsigned int startup_dma_irq(unsigned int irq) +{ + enable_dma_irq(irq); + return 0; +} + +static void shutdown_dma_irq(unsigned int irq) +{ + disable_dma_irq(irq); +} + +static struct irq_chip dma_irq_type = { + .typename = "DMA", + .startup = startup_dma_irq, + .shutdown = shutdown_dma_irq, + .enable = enable_dma_irq, + .disable = 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 */ + + /* Set up INTC irq + */ + for (i = 0; i < 32; i++) { + disable_intc_irq(i); + irq_desc[i].chip = &intc_irq_type; + } + + /* Set up DMAC irq + */ + for (i = 0; i < NUM_DMA; i++) { + disable_dma_irq(IRQ_DMA_0 + i); + irq_desc[IRQ_DMA_0 + i].chip = &dma_irq_type; + } + + /* Set up GPIO irq + */ + for (i = 0; i < NUM_GPIO; i++) { + disable_gpio_irq(IRQ_GPIO_0 + i); + irq_desc[IRQ_GPIO_0 + i].chip = &gpio_irq_type; + } +} + +static int plat_real_irq(int irq) +{ + switch (irq) { + case IRQ_GPIO0: + irq = __gpio_group_irq(0) + IRQ_GPIO_0; + break; + case IRQ_GPIO1: + irq = __gpio_group_irq(1) + IRQ_GPIO_0 + 32; + break; + case IRQ_GPIO2: + irq = __gpio_group_irq(2) + IRQ_GPIO_0 + 64; + break; + case IRQ_GPIO3: + irq = __gpio_group_irq(3) + IRQ_GPIO_0 + 96; + break; + case IRQ_GPIO4: + irq = __gpio_group_irq(4) + IRQ_GPIO_0 + 128; + break; + case IRQ_GPIO5: + irq = __gpio_group_irq(5) + IRQ_GPIO_0 + 160; + break; + case IRQ_DMAC0: + case IRQ_DMAC1: + irq = __dmac_get_irq() + IRQ_DMA_0; + break; + } + + return irq; +} + +asmlinkage void plat_irq_dispatch(void) +{ + int irq = 0; + static unsigned long intc_ipr = 0; + + intc_ipr |= REG_INTC_IPR; + + if (!intc_ipr) return; + + irq = ffs(intc_ipr) - 1; + intc_ipr &= ~(1< + * + * 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 +#include +#include +#include +#include + +#include +#if 0 +/* 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, +}; +#endif +/*** 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_MSC0, + .end = IRQ_MSC0, + .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, +}; + +/* All */ +static struct platform_device *jz_platform_devices[] __initdata = { +// &jz_usb_ohci_device, + &jz_lcd_device, + &jz_usb_gdt_device, + &jz_mmc_device, +}; + +static int __init jz_platform_init(void) +{ + return platform_add_devices(jz_platform_devices, ARRAY_SIZE(jz_platform_devices)); +} + +arch_initcall(jz_platform_init); diff --git a/arch/mips/jz4750d/pm.c b/arch/mips/jz4750d/pm.c new file mode 100644 index 0000000..7f58372 --- /dev/null +++ b/arch/mips/jz4750d/pm.c @@ -0,0 +1,461 @@ +/* + * linux/arch/mips/jz4750d/common/pm.c + * + * JZ4750D Power Management Routines + * + * Copyright (C) 2006 Ingenic Semiconductor Inc. + * Author: + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#undef DEBUG +//#define DEBUG +#ifdef DEBUG +#define dprintk(x...) printk(x) +#else +#define dprintk(x...) +#endif + +#define GPIO_PORT_NUM 6 + +/* + * __gpio_as_sleep set all pins to pull-disable, and set all pins as input + * except sdram 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) = ~0x03ff7fff; \ + REG_GPIO_PXSELC(1) = ~0x03ff7fff; \ + REG_GPIO_PXDIRC(1) = ~0x03ff7fff; \ + REG_GPIO_PXPES(1) = 0xffffffff; \ + REG_GPIO_PXFUNC(2) = ~0x01e00000; \ + REG_GPIO_PXSELC(2) = ~0x01e00000; \ + REG_GPIO_PXDIRC(2) = ~0x01e00000; \ + 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; \ + REG_GPIO_PXFUNC(4) = 0xffffffff; \ + REG_GPIO_PXSELC(4) = 0xffffffff; \ + REG_GPIO_PXDIRC(4) = 0xffffffff; \ + REG_GPIO_PXPES(4) = 0xffffffff; \ + REG_GPIO_PXFUNC(5) = 0xffffffff; \ + REG_GPIO_PXSELC(5) = 0xffffffff; \ + REG_GPIO_PXDIRC(5) = 0xffffffff; \ + REG_GPIO_PXPES(5) = 0xffffffff; \ +} while (0) + +static int jz_pm_do_hibernate(void) +{ + printk("Put CPU into hibernate mode.\n"); + + /* 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#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, "AUX_CPU : %s\n", + (clkgr & CPM_CLKGR_AUX_CPU) ? "stopped" : "running"); + len += sprintf (page+len, "AHB1 : %s\n", + (clkgr & CPM_CLKGR_AHB1) ? "stopped" : "running"); + len += sprintf (page+len, "IDCT : %s\n", + (clkgr & CPM_CLKGR_IDCT) ? "stopped" : "running"); + len += sprintf (page+len, "DB : %s\n", + (clkgr & CPM_CLKGR_DB) ? "stopped" : "running"); + len += sprintf (page+len, "ME : %s\n", + (clkgr & CPM_CLKGR_ME) ? "stopped" : "running"); + len += sprintf (page+len, "MC : %s\n", + (clkgr & CPM_CLKGR_MC) ? "stopped" : "running"); + len += sprintf (page+len, "TVE : %s\n", + (clkgr & CPM_CLKGR_TVE) ? "stopped" : "running"); + len += sprintf (page+len, "TSSI : %s\n", + (clkgr & CPM_CLKGR_TSSI) ? "stopped" : "running"); + 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, "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, "MSC0 : %s\n", + (clkgr & CPM_CLKGR_MSC0) ? "stopped" : "running"); + len += sprintf (page+len, "MSC1 : %s\n", + (clkgr & CPM_CLKGR_MSC1) ? "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, "H1CLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_h1clk())); + 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, "MSC0CLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_mscclk(0))); + len += sprintf (page+len, "MSC1CLK : %3d.%02d MHz\n", TO_MHZ(__cpm_get_mscclk(1))); + len += sprintf (page+len, "EXTALCLK0 : %3d.%02d MHz\n", TO_MHZ(__cpm_get_extalclk0())); + len += sprintf (page+len, "EXTALCLK(by CPM): %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(); + wired = read_c0_wired(); + 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; + + local_irq_save(flags); + wired = read_c0_wired(); + if ( wired > 0 ) { + write_c0_wired(wired - 1); + } + 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<= 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 /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(); + } + 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 JZ4750D. + */ + 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); diff --git a/arch/mips/jz4750d/prom.c b/arch/mips/jz4750d/prom.c new file mode 100644 index 0000000..3c173d6 --- /dev/null +++ b/arch/mips/jz4750d/prom.c @@ -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 +#include +#include +#include + +#include +#include + +/* #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_JZ4750D; + + 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 *)(UART1_BASE + OFF_LSR); + volatile u8 *uart_tdr = (volatile u8 *)(UART1_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 "JZ4750D"; +} + +EXPORT_SYMBOL(prom_getcmdline); +EXPORT_SYMBOL(get_ethernet_addr); +EXPORT_SYMBOL(str2eaddr); diff --git a/arch/mips/jz4750d/reset.c b/arch/mips/jz4750d/reset.c new file mode 100644 index 0000000..90b521e --- /dev/null +++ b/arch/mips/jz4750d/reset.c @@ -0,0 +1,46 @@ +/* + * linux/arch/mips/jz4750/reset.c + * + * JZ4750 reset routines. + * + * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +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_TSCR_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(); +} diff --git a/arch/mips/jz4750d/setup.c b/arch/mips/jz4750d/setup.c new file mode 100644 index 0000000..b9b8aab --- /dev/null +++ b/arch/mips/jz4750d/setup.c @@ -0,0 +1,199 @@ +/* + * linux/arch/mips/jz4750d/common/setup.c + * + * JZ4750D 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PC_KEYB +#include +#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.h1clk = __cpm_get_h1clk(); + jz_clocks.pixclk = __cpm_get_pixclk(); + jz_clocks.i2sclk = __cpm_get_i2sclk(); + jz_clocks.usbclk = __cpm_get_usbclk(); + jz_clocks.mscclk = __cpm_get_mscclk(0); + 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.h1clk = 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(0); + __dmac_enable_module(1); +} + +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"); + } + + s.line = 2; + s.membase = (u8 *)UART2_BASE; + s.irq = IRQ_UART2; + + if (early_serial_setup(&s) != 0) { + printk(KERN_ERR "Serial ttyS2 setup failed!\n"); + } +/* + s.line = 3; + s.membase = (u8 *)UART3_BASE; + s.irq = IRQ_UART3; + if (early_serial_setup(&s) != 0) { + printk(KERN_ERR "Serial ttyS3 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; + + jz_soc_setup(); + jz_serial_setup(); + jz_board_setup(); +} + diff --git a/arch/mips/jz4750d/time.c b/arch/mips/jz4750d/time.c new file mode 100644 index 0000000..0c6d647 --- /dev/null +++ b/arch/mips/jz4750d/time.c @@ -0,0 +1,156 @@ +/* + * linux/arch/mips/jz4750d/time.c + * + * Setting up the clock on the JZ4750D boards. + * + * Copyright (C) 2008 Ingenic Semiconductor Inc. + * Author: + * + * 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 +#include +#include +#include + +#include +#include + +/* This is for machines which generate the exact clock. */ + +#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 = TCU_TFCR_OSTFCL; /* 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_OSTCNT); +} + +static struct clocksource clocksource_jz = { + .name = "jz_clocksource", + .rating = 300, + .read = jz_get_cycles, + .mask = 0xFFFFFFFF, + .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 */ + .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(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_OSTCSR = TCU_OSTCSR_PRESCALE16 | TCU_OSTCSR_EXT_EN; + REG_TCU_OSTCNT = 0; + REG_TCU_OSTDR = latch; + + REG_TCU_TMCR = TCU_TMCR_OSTMCL; /* unmask match irq */ + REG_TCU_TSCR = TCU_TSCR_OSTSC; /* enable timer clock */ + REG_TCU_TESR = TCU_TESR_OSTST; /* start counting up */ + + jz_timer_setup(); +} diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index 1abe990..bdcf5b6 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -159,6 +159,7 @@ void __init check_wait(void) case CPU_25KF: case CPU_PR4450: case CPU_BCM3302: + case CPU_JZRISC: case CPU_CAVIUM_OCTEON: cpu_wait = r4k_wait; break; @@ -580,6 +581,14 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) MIPS_CPU_32FPR; c->tlbsize = 64; break; + case PRID_IMP_JZRISC: + c->cputype = CPU_JZRISC; + __cpu_name[cpu] = "Ingenic JZRISC"; + c->isa_level = MIPS_CPU_ISA_M32R1; + /* JZRISC does not implement the CP0 counter. */ + c->options &= ~MIPS_CPU_COUNTER; + c->tlbsize = 32; + break; } } @@ -888,6 +897,23 @@ static inline void cpu_probe_cavium(struct cpuinfo_mips *c, unsigned int cpu) } } +static inline void cpu_probe_ingenic(struct cpuinfo_mips *c) +{ + 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; + __cpu_name[cpu] = "Ingenic JZRISC"; + c->isa_level = MIPS_CPU_ISA_M32R1; + c->tlbsize = 32; + break; + default: + panic("Unknown Ingenic Processor ID!"); + break; + } +} + const char *__cpu_name[NR_CPUS]; __cpuinit void cpu_probe(void) @@ -925,6 +951,9 @@ __cpuinit void cpu_probe(void) case PRID_COMP_CAVIUM: cpu_probe_cavium(c, cpu); break; + case PRID_COMP_INGENIC: + cpu_probe_ingenic(c); + break; } BUG_ON(!__cpu_name[cpu]); diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c index 67bd626..9d6dca1 100644 --- a/arch/mips/kernel/unaligned.c +++ b/arch/mips/kernel/unaligned.c @@ -497,6 +497,11 @@ sigill: force_sig(SIGILL, current); } +#if defined(CONFIG_JZ_TCSM) +#undef user_mode +#define user_mode(regs) ((((regs)->cp0_status & KU_MASK) == KU_USER) || (regs->cp0_badvaddr < 0x80000000)) +#endif + asmlinkage void do_ade(struct pt_regs *regs) { unsigned int __user *pc; @@ -522,8 +527,9 @@ asmlinkage void do_ade(struct pt_regs *regs) * This is all so but ugly ... */ seg = get_fs(); - if (!user_mode(regs)) + if (!user_mode(regs)) { set_fs(KERNEL_DS); + } emulate_load_store_insn(regs, (void __user *)regs->cp0_badvaddr, pc); set_fs(seg); 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); diff --git a/crypto/Makefile b/crypto/Makefile index 673d9f7..7180908 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -77,6 +77,7 @@ obj-$(CONFIG_CRYPTO_DEFLATE) += deflate.o obj-$(CONFIG_CRYPTO_ZLIB) += zlib.o obj-$(CONFIG_CRYPTO_MICHAEL_MIC) += michael_mic.o obj-$(CONFIG_CRYPTO_CRC32C) += crc32c.o +obj-$(CONFIG_CRYPTO_LZO) += lzo.o obj-$(CONFIG_CRYPTO_AUTHENC) += authenc.o obj-$(CONFIG_CRYPTO_LZO) += lzo.o obj-$(CONFIG_CRYPTO_RNG2) += rng.o diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c index d59ba50..217080e 100644 --- a/crypto/tcrypt.c +++ b/crypto/tcrypt.c @@ -680,6 +680,11 @@ static int do_test(int m) ret += tcrypt_test("rfc4309(ccm(aes))"); break; + case 33: + test_comp("lzo", lzo_comp_tv_template, lzo_decomp_tv_template, + LZO_COMP_TEST_VECTORS, LZO_DECOMP_TEST_VECTORS); + break; + case 100: ret += tcrypt_test("hmac(md5)"); break; diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 6a06913..357a6c0 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -831,6 +831,16 @@ config RTC To compile this driver as a module, choose M here: the module will be called rtc. +config RTC_PCF8563 + bool 'Philips PCF8563 Real Time Clock (I2C Bus)' + help + Philips PCF8563 Real Time Clock (I2C Bus) + +config RTC_JZ + bool 'Jz47XX On-Chip Real Time Clock' + help + Jz47XX On-Chip Real Time Clock + config JS_RTC tristate "Enhanced Real Time Clock Support" depends on SPARC32 && PCI @@ -1109,6 +1119,7 @@ config DEVPORT default y source "drivers/s390/char/Kconfig" +source "drivers/char/jzchar/Kconfig" endmenu diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 66f779a..ae98c77 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -98,6 +98,10 @@ obj-$(CONFIG_CS5535_GPIO) += cs5535_gpio.o obj-$(CONFIG_GPIO_TB0219) += tb0219.o obj-$(CONFIG_TELCLOCK) += tlclk.o +obj-$(CONFIG_RTC_PCF8563) += rtc_pcf8563.o +obj-$(CONFIG_RTC_JZ) += rtc_jz.o +obj-$(CONFIG_JZCHAR) += jzchar/ + obj-$(CONFIG_MWAVE) += mwave/ obj-$(CONFIG_AGP) += agp/ obj-$(CONFIG_PCMCIA) += pcmcia/ diff --git a/drivers/char/jzchar/Kconfig b/drivers/char/jzchar/Kconfig new file mode 100644 index 0000000..4f7ff70 --- /dev/null +++ b/drivers/char/jzchar/Kconfig @@ -0,0 +1,70 @@ +# +# JzSOC char devices configuration +# + +menu "JZSOC char device support" + depends on SOC_JZ4740 || SOC_JZ4730 || SOC_JZ4750 || SOC_JZ4750D + +config JZCHAR + tristate 'JzSOC char device support' + +config JZ_CAMERA_SENSOR + bool + +config JZ_CIM + tristate 'JzSOC Camera Interface Module (CIM) support' + depends on JZCHAR + select JZ_CAMERA_SENSOR + +config JZ_TPANEL_ATA2508 + tristate 'JzSOC MPEG4 TOUCH PANEL ATA2508 support' + depends on JZCHAR + +config JZ_TPANEL + tristate 'JzSOC touchpanel driver support' + depends on JZCHAR +# select JZ_SADC if SOC_JZ4740 +# select JZ_TPANEL_AK4182 if SOC_JZ4730 + +choice + prompt "Touch Panel ADC type" + depends on JZ_TPANEL + default JZ_SADC if SOC_JZ4740 || SOC_JZ4750 || SOC_JZ4750D + default JZ_TPANEL_AK4182 if SOC_JZ4730 + +config JZ_SADC + bool 'Select the JZ47XX internal SADC' + +config JZ_TPANEL_AK4182 + bool 'Select the AK4182 codec' + +config JZ_TPANEL_UCB1400 + bool 'Select the UCB1400 codec' + +config JZ_TPANEL_WM9712 + bool 'Select the WM9712 codec' + +endchoice + +config JZ_UDC_HOTPLUG + tristate 'JZ UDC hotplug driver support' + depends on JZCHAR + +config JZ_POWEROFF + tristate 'JZ board poweroff support' + depends on JZCHAR + +config JZ_OW + tristate 'JZ One-wire bus support' + depends on JZCHAR + +config JZ_TCSM + tristate 'JZ TCSM support' + depends on JZCHAR + +config JZ_TSSI + tristate 'JZ MPEG2-TS interface support' + depends on JZCHAR && (SOC_JZ4750 || SOC_JZ4750D) + +endmenu + diff --git a/drivers/char/jzchar/Makefile b/drivers/char/jzchar/Makefile new file mode 100644 index 0000000..b079ffc --- /dev/null +++ b/drivers/char/jzchar/Makefile @@ -0,0 +1,24 @@ +# +# Makefile for jzchar +# +obj-$(CONFIG_JZCHAR) += jzchars.o + +obj-$(CONFIG_JZ_SCC) += scc.o +obj-$(CONFIG_JZ_CIM) += cim.o +obj-$(CONFIG_JZ_TPANEL_ATA2508) += ata2508.o +obj-$(CONFIG_JZ_CAMERA_SENSOR) += sensor.o +obj-$(CONFIG_JZ_I2C_EEPROM) += eeprom.o +obj-$(CONFIG_JZ_EJTAG) += ejtag.o +obj-$(CONFIG_JZ_POWEROFF) += poweroff.o + +obj-$(CONFIG_JZ_TPANEL) += jz_ts.o +obj-$(CONFIG_JZ_TPANEL_UCB1400) += ucb1400.o +obj-$(CONFIG_JZ_TPANEL_WM9712) += wm9712.o +obj-$(CONFIG_JZ_TPANEL_AK4182) += ak4182.o +obj-$(CONFIG_JZ_SADC) += sadc.o + +obj-$(CONFIG_JZ_SMART_LCD) += slcd.o +obj-$(CONFIG_JZ_UDC_HOTPLUG) += udc_hotplug.o +obj-$(CONFIG_JZ_OW) += jz_ow.o +obj-$(CONFIG_JZ_TCSM) += tcsm.o +obj-$(CONFIG_JZ_TSSI) += jz_tssi.o diff --git a/drivers/char/jzchar/ak4182.c b/drivers/char/jzchar/ak4182.c new file mode 100644 index 0000000..c88aa9a --- /dev/null +++ b/drivers/char/jzchar/ak4182.c @@ -0,0 +1,657 @@ +/* + * ak4182.c using national microwire protocol + * + * Touch screen driver interface to the AK4182A . + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "jz_ts.h" +#include "ak4182.h" + +#define TS_PIN GPIO_TS_PENIRQ +#define TS_IRQ (IRQ_GPIO_0 + TS_PIN) + +static int samples = 5; +static int first_time = 0; +static unsigned long last_x, last_y, last_p; + +static int adcsync = 0; + +static struct ak4182 *ak; + +extern unsigned int (*codec_read_battery)(void); + +/*------------------JzSoc SSI configure----------------*/ +static void ak4182_ssi_reset(void) +{ + REG_SSI_CR0 = 0x0000; + REG_SSI_CR1 = 0x00007960; + REG_SSI_SR = 0x00000098; + REG_SSI_ITR = 0x0000; + REG_SSI_ICR = 0x00; + REG_SSI_GR = 0x0000; + + __ssi_disable(); + __ssi_flush_fifo(); + __ssi_clear_errors(); + __ssi_select_ce(); +} + +static void ak4182_ssi_enable(void) +{ + __ssi_enable(); +} + +#ifdef CONFIG_PM +static void ak4182_ssi_disable(void) +{ + __ssi_disable(); +} +#endif + +static void ak4182_ssi_set_trans_mode_format(void) +{ + __ssi_microwire_format(); + __ssi_set_msb(); + __ssi_set_microwire_command_length(8); + __ssi_set_frame_length(12); +} + +static void ak4182_ssi_set_clk_div_ratio(int dev_clk, int ssi_clk) +{ + __ssi_set_clk(dev_clk, ssi_clk); +} + +static void ak4182_ssi_set_normal_mode(void) +{ + __ssi_normal_mode(); +} + +static void ak4182_ssi_set_IRQ(void) +{ + __ssi_disable_tx_intr(); + __ssi_disable_rx_intr(); +} + +/*------------------ AK4182 routines ------------------*/ +static inline void ak4182_reg_write(unsigned short val) +{ + __ssi_transmit_data(val); +} + +static inline unsigned int ak4182_reg_read(void) +{ + unsigned int val; + val = __ssi_receive_data(); + return val; +} + +static unsigned int ak4182_adc_read(int cmd_code, int sync) +{ + unsigned int val, timeout = 10000; + unsigned int status,valid1,valid2,dataentry; + + ak4182_reg_write(cmd_code); + udelay(2);//wait 2 D_CLK + for (;;) { + status =0; + status = REG_SSI_SR; + valid1 = (status>>7) & 1; + valid2 = (status>>6) & 1; + if( valid1==1 && valid2==0 )//SSI transfer is finished + { + //Receive FIFO data entry number + dataentry = val = 0; + dataentry = (status>>8) & 0x1F; + if( dataentry > 5 ) + { + printk("R-FIFO entry=%d,SSI transfer is wrong!\n",dataentry); + while(dataentry > 0) + { + ak4182_reg_read(); + dataentry--; + } + return 0; + } + while(dataentry > 0) + { + val = ak4182_reg_read(); + dataentry--; + } + return val; + } + + if (--timeout == 0) + break; + udelay(1); + } + return 0; +} + + +//enable pen down IRQ +static void ak4182_enable_irq(void) +{ + unsigned long flags; + + spin_lock_irqsave(&ak->lock, flags); + __gpio_unmask_irq(TS_PIN); + spin_unlock_irqrestore(&ak->lock, flags); +} + +//disable pen down IRQ +static void ak4182_disable_irq(void) +{ + unsigned long flags; + + spin_lock_irqsave(&ak->lock, flags); + __gpio_mask_irq(TS_PIN); +// spin_unlock_irqrestore(&ucb->lock, flags); + spin_unlock_irqrestore(&ak->lock, flags); +} +/* + * Switch to X position mode and measure Y plate. We switch the plate + * configuration in pressure mode, then switch to position mode. This + * gives a faster response time. Even so, we need to wait about 55us + * for things to stabilise. + */ +static inline unsigned int ak4182_ts_read_xpos(void) +{ + return ak4182_adc_read(0xD0, adcsync);//X-axis,0xD0 for 12bit,0xD8 for 8bit +} + + +/* + * Switch to pressure mode, and read pressure. We don't need to wait + * here, since both plates are being driven. + */ +static inline unsigned int ak4182_ts_read_pressure(void) +{ + unsigned int z1,z2,xpos,pressureval=0;//300 Om + //Z1 pressure + z1 = ak4182_adc_read(0xB0, adcsync);//0xB0 for 12bit,0xB8 for 8bit + if(z1>0) + { + //Z2 pressure + z2 = ak4182_adc_read(0xC0, adcsync);//0xC0 for 12bit,0xC8 for 8bit + if(z2>z1) + { + xpos = ak4182_ts_read_xpos(); + pressureval = (300*xpos*(z2-z1))/(4096*z1); + } + } + + return pressureval; +} + + +/* + * Switch to Y position mode and measure X plate. We switch the plate + * configuration in pressure mode, then switch to position mode. This + * gives a faster response time. Even so, we need to wait about 55us + * for things to stabilise. + */ +static inline unsigned int ak4182_ts_read_ypos(void) +{ + return ak4182_adc_read(0x90, adcsync);//Y-axis,0x90 for 12bit,0x98 for 8bit +} + +/*------------------------------------------------------------ + * Read the battery voltage + */ + +unsigned int ak4182_read_battery(void) +{ + unsigned int v; + int bat_val[5]; + int total = 0, max_bat, min_bat; + + v = ak4182_adc_read(0xA7, adcsync); + v = ak4182_adc_read(0xA7, adcsync); + for(v = 0;v <= 4;v++) + bat_val[v] = ak4182_adc_read(0xA7, adcsync); + + ak4182_adc_read(0xA4, adcsync); + max_bat = min_bat = bat_val[0]; + for(v = 0;v <= 4;v++) { + total += bat_val[v]; + if(bat_val[v] > max_bat) + max_bat = bat_val[v]; + if(bat_val[v] < min_bat) + min_bat = bat_val[v]; + } + total = total - max_bat - min_bat; + v = total / 3; + return v; +} + +/*------------------ Calibrate samples -------------------*/ + +#define DIFF(a,b) ((a>b)?(a-b):(b-a)) + +static int calibrate_samples(void *xbuf, void *ybuf, void *pbuf, int count) +{ + unsigned long *xp = (unsigned long *)xbuf; + unsigned long *yp = (unsigned long *)ybuf; + unsigned long *pp = (unsigned long *)pbuf; + unsigned long x_cal = 0, y_cal = 0, p_cal = 0, tmp; + int ignored, i, j; + int valid = 0; + + /* throw away the max cases */ + tmp = xp[0]; + ignored = 0; + for (i = 1; i < count; i++) { + if (xp[i] > tmp) { + tmp = xp[i]; + ignored = i; + } + }//find the max val + j = 0; + for (i = 0; i < count; i++) { + if (i == ignored) + continue; + xp[j++] = xp[i]; + }//shift val and delete the max val + + tmp = yp[0]; + ignored = 0; + for (i = 1; i < count; i++) { + if (yp[i] > tmp) { + tmp = yp[i]; + ignored = i; + } + } + j = 0; + for (i = 0; i < count; i++) { + if (i == ignored) + continue; + yp[j++] = yp[i]; + } + + tmp = pp[0]; + ignored = 0; + for (i = 1; i < count; i++) { + if (pp[i] > tmp) { + tmp = pp[i]; + ignored = i; + } + } + j = 0; + for (i = 0; i < count; i++) { + if (i == ignored) + continue; + pp[j++] = pp[i]; + } + + /* throw away the min cases */ + + count -= 1; // decrement by 1 + + tmp = xp[0]; + ignored = 0; + for (i = 1; i < count; i++) { + if (xp[i] < tmp) { + tmp = xp[i]; + ignored = i; + } + } + j = 0; + for (i = 0; i < count; i++) { + if (i == ignored) + continue; + xp[j++] = xp[i]; + } + + tmp = yp[0]; + ignored = 0; + for (i = 1; i < count; i++) { + if (yp[i] < tmp) { + tmp = yp[i]; + ignored = i; + } + } + j = 0; + for (i = 0; i < count; i++) { + if (i == ignored) + continue; + yp[j++] = yp[i]; + } + + tmp = pp[0]; + ignored = 0; + for (i = 1; i < count; i++) { + if (pp[i] < tmp) { + tmp = pp[i]; + ignored = i; + } + } + j = 0; + for (i = 0; i < count; i++) { + if (i == ignored) + continue; + pp[j++] = pp[i]; + } + + count -= 1; // decrement by 1 + + /* calculate the average of the rest */ + for (i = 0; i < count; i++) { + x_cal += xp[i]; + y_cal += yp[i]; + p_cal += pp[i]; + } + x_cal /= count; + y_cal /= count; + p_cal /= count; + + if (first_time) { + first_time = 0; + last_x = x_cal; + last_y = y_cal; + last_p = p_cal; + valid = 1; + } + else { + if ((DIFF(x_cal, last_x) > 100) || + (DIFF(y_cal, last_y) > 100)) + valid = 0; + else + valid = 1; + } + + //printk("x_cal=%d y_cal=%d p_cal=%d valid=%d\n", x_cal, y_cal, p_cal, valid); + + if (valid) { + *xp = last_x = x_cal; + *yp = last_y = y_cal; + *pp = last_p = p_cal; + } + + return valid; +} + + +#define TSMAXX 945 +#define TSMAXY 830 +#define TSMINX 90 +#define TSMINY 105 + +#define SCREEN_X 480 +#define SCREEN_Y 272 + +static unsigned long transform_to_screen_x(struct jz_ts_t *ts, unsigned long x ) +{ + + if (ts->minx) + { + if (x < ts->minx) x = ts->minx; + if (x > ts->maxx) x = ts->maxx; + + return (x - ts->minx) * SCREEN_X / (ts->maxx - ts->minx); + } + else + { + if (x < TSMINX) x = TSMINX; + if (x > TSMAXX) x = TSMAXX; + + return (x - TSMINX) * SCREEN_X / (TSMAXX - TSMINX); + } +} + +static unsigned long transform_to_screen_y(struct jz_ts_t *ts, unsigned long y) +{ + if (ts->miny) + { + if (y < ts->miny) y = ts->miny; + if (y > ts->maxy) y = ts->maxy; + + return (y - ts->miny) * SCREEN_Y / (ts->maxy - ts->miny); + } + else + { + if (y < TSMINY) y = TSMINY; + if (y > TSMAXY) y = TSMAXY; + + return (y - TSMINY) * SCREEN_Y / (TSMAXY - TSMINY); + } +} + +/*------------------ Common routines -------------------*/ + +void ts_enable_irq(void) +{ + /* interrupt mode */ + ak4182_enable_irq(); + enable_irq(TS_IRQ); +} + +void ts_disable_irq(void) +{ + ak4182_disable_irq(); + disable_irq(TS_IRQ); +} + +int ts_request_irq(u32 *irq, + irqreturn_t (*handler)(int, void *), + const char *devname, + void *dev_id) +{ + int retval; + + /* return the irq number */ + *irq = TS_IRQ; + /* initializate ssi for AK4182 */ + ak4182_ssi_reset(); + ak4182_ssi_set_trans_mode_format(); + ak4182_ssi_set_normal_mode(); + ak4182_ssi_set_clk_div_ratio(JZ_EXTAL, 200*1000);//DCLK is 1.5M Hz max + ak4182_ssi_set_IRQ(); + + ak4182_enable_irq(); + + /* enable gpio irq */ + __gpio_as_irq_fall_edge(TS_PIN); + + /* register irq handler */ + retval = request_irq(TS_IRQ, handler, IRQF_DISABLED, devname, dev_id); + ak4182_ssi_enable(); + udelay(10); + return retval; +} + +void ts_free_irq(struct jz_ts_t *ts) +{ + free_irq(ts->pendown_irq, ts); + //Close SSI mode + ak4182_ssi_reset(); +} + +void ts_irq_callback(void) +{ + /* clear interrupt status */ + __gpio_ack_irq(TS_PIN); + first_time = 1; // first time to acquire sample +} + +int PenIsDown(void) +{ + unsigned int p; + p = ak4182_ts_read_pressure(); + return (p > 100) ? 1 : 0; +} + +/* + * Acquire Raw pen coodinate data and compute touch screen + * pressure resistance. Hold spinlock when calling. + */ +int AcquireEvent(struct jz_ts_t *ts, struct ts_event *event) +{ + unsigned int x_raw[8], y_raw[8], p_raw[8]; + int valid, i; + + for (i = 0; i < samples; i++) { + x_raw[i] = ak4182_ts_read_xpos(); + } + for (i = 0; i < samples; i++) { + y_raw[i] = ak4182_ts_read_ypos(); + } + for (i = 0; i < samples; i++) { + p_raw[i] = ak4182_ts_read_pressure(); + } + + valid = calibrate_samples(x_raw, y_raw, p_raw, samples); + + if (valid) { + unsigned int x_scr, y_scr; + + if(ts->filter) { + x_scr = transform_to_screen_x(ts, x_raw[0]); + y_scr = transform_to_screen_y(ts, y_raw[0]); + + if (ts->prints) + printk("filter:x_raw:%d,y_raw:%d,x_tran:%d,y_tran:%d\n", x_raw[0], y_raw[0], x_scr, y_scr); + } + else { + x_scr = x_raw[0]; + y_scr = y_raw[0]; + + if (ts->prints) + printk("no filter:x_raw=%d y_raw=%d \n", x_raw[0], y_raw[0]); + } + + event->x = x_scr; + event->y = y_scr; + event->pressure = (u16)p_raw[0]; + event->status = PENDOWN; + return 1; + } + return 0; +} + +#ifdef CONFIG_PM + +/* + * Suspend the Touch pad. + */ +static int ak4182_suspend(struct ak4182 *ak , int state) +{ + ak4182_ssi_disable(); + + return 0; +} + +/* + * Resume the Touch panel. + */ +static int ak4182_resume(struct ak4182 *ak) +{ + /* initializate ssi for AK4182 */ + ak4182_ssi_reset(); + ak4182_ssi_set_trans_mode_format(); + ak4182_ssi_set_normal_mode(); + ak4182_ssi_set_clk_div_ratio(JZ_EXTAL, 200*1000);//DCLK is 1.5M Hz max + ak4182_ssi_set_IRQ(); + + ak4182_enable_irq(); + + ak4182_ssi_enable(); + + return 0; +} + +static int ak4182_pm_callback(struct pm_dev *pm_dev, pm_request_t rqst, void *data) +{ + int ret; + struct ak4182 *akinfo = pm_dev->data; + + if (!akinfo) + return -EINVAL; + + + switch (rqst) { + case PM_SUSPEND: + ret = ak4182_suspend(akinfo, (int)data); + break; + + case PM_RESUME: + ret = ak4182_resume(akinfo); + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +#endif /* CONFIG_PM */ + + +/* + * Module init and exit + */ + +int __init ak4182_init(void) +{ + ak = kmalloc(sizeof(struct ak4182), GFP_KERNEL); + if (!ak) return -ENOMEM; + + memset(ak, 0, sizeof(struct ak4182)); + + codec_read_battery = ak4182_read_battery; + + spin_lock_init(&ak->lock); + sema_init(&ak->adc_sem, 1); + + //initialize AK4182 register + __gpio_clear_pin(73); + __gpio_as_output(73); + mdelay(2); + __gpio_set_pin(73); + __gpio_as_ssi(); + + ak4182_read_battery(); + +#ifdef CONFIG_PM + ak->pmdev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, ak4182_pm_callback); + if (ak->pmdev) + { + ak->pmdev->data = ak; + } +#endif + + printk("AK4182 touch screen driver initialized\n"); + + return 0; +} + +void ak4182_cleanup(void) +{ +} + +module_init(ak4182_init); +module_exit(ak4182_cleanup); + diff --git a/drivers/char/jzchar/ak4182.h b/drivers/char/jzchar/ak4182.h new file mode 100644 index 0000000..e8b1bf8 --- /dev/null +++ b/drivers/char/jzchar/ak4182.h @@ -0,0 +1,16 @@ +#ifndef __AK4182_H__ +#define __AK4182_H__ + +/* Device data structure */ + +struct ak4182 { + spinlock_t lock; + struct pm_dev *pmdev; + struct semaphore adc_sem; + u16 adc_cr; + u16 irq_fal_enbl; + u16 irq_ris_enbl; + int irq_enabled; +}; + +#endif /* __AK4182_H__ */ diff --git a/drivers/char/jzchar/ata2508.c b/drivers/char/jzchar/ata2508.c new file mode 100644 index 0000000..41a4ff4 --- /dev/null +++ b/drivers/char/jzchar/ata2508.c @@ -0,0 +1,227 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MP4_KEY_RST (32*3+3) +#define MP4_KEY_TINT (32*3+2) +#define MP4_KEY_SCL (32*3+1) +#define MP4_KEY_SDA (32*3+0) +#define MP4_TINT_IRQ (IRQ_GPIO_0 + MP4_KEY_TINT) + +#define ADDR_WARM_RESET 0xFF +#define ATA2508_SENSOR_MASK 0x1F + +const unsigned char init_data_burst[] = {//Address:0x0D-0x3E + 0x04, // BETA + 0x27, // AIC_WAIT + //0x32, // REF_DELAY + 0x16, // REF_DELAY + 0x02, // HYSTERESIS01 + 0x02, // HYSTERESIS1 + 0x02, // HYSTERESIS2 + 0x02, // HYSTERESIS3 + 0x02, // HYSTERESIS4 + 0x02, // HYSTERESIS51 + 0x02, // HYSTERESIS61 + 0x02, // HYSTERESIS7 + 0x02, // HYSTERESIS8 + 0x02, // HYSTERESIS9 + 0x02, // HYSTERESIS10 + 0x02, // HYSTERESIS11 + 0x64, // STRENGTH_THRESHOLD0 + 0x64, // STRENGTH_THRESHOLD1 + 0x64, // STRENGTH_THRESHOLD2 + 0x64, // STRENGTH_THRESHOLD3 + 0x64, // STRENGTH_THRESHOLD4 + 0x64, // STRENGTH_THRESHOLD5 + 0x64, // STRENGTH_THRESHOLD6 + 0x64, // STRENGTH_THRESHOLD7 + 0x64, // STRENGTH_THRESHOLD8 + 0x64, // STRENGTH_THRESHOLD9 + 0x64, // STRENGTH_THRESHOLD10 + 0x64, // STRENGTH_THRESHOLD11 + 0x0f, // Sampling Interval + 0xC8, // INTEGRATION TIME + 0x0f, // IDLE TIME + 0x00, // SIF_SETUP(RESERVED) + 0x01, // MODE + 0x00, // GPIO_REG_L + 0x00, // GPIO_REG_H + 0x00, // GPIO_CONFIGURATION_L + 0x00, // GPIO_CONFIGURATION_H + 0x00, // GPIO_DIR_L + 0x00, // GPIO_DIR_H + 0x0c, // CONTROL + 0x38, // INT_MASK + 0x00, // INT_CLEAR + 0xFF, // INT_edge + 0x02, // CONTROL_2 + 0xAF, // BEEP_TIME + 0x7F, // BEEP_FREQUENCY + 0x30, // CALIBRATION INTERVAL + 0x00, // EINT_ENABLE + 0x00, // EINT_POL + 0x00, // FILTER_PERIOD + 0x00, // FILTER_THRESHOLD +}; +const unsigned char init_data_alpha[] = {//Address:0x00-0x0C + 0x02, // APIS + 0x08, // ALPHA0 + 0x08, // ALPHA1 + 0x08, // ALPHA2 + 0x08, // ALPHA3 + 0x08, // ALPHA4 + 0x28, // ALPHA5 + 0x28, // ALPHA6 + 0x28, // ALPHA7 + 0x28, // ALPHA8 + 0x28, // ALPHA9 + 0x28, // ALPHA10 + 0x28, // ALPHA11 +}; +static unsigned int i2c_addr = 0x58; +static unsigned int i2c_clk = 100000; + +static void write_reg(u8 reg, u8 val) +{ + int ret; + i2c_open(); + i2c_setclk(i2c_clk); + ret = i2c_write(i2c_addr, &val, reg, 1); + i2c_close(); +} + +static u8 read_reg(u8 reg) +{ + u8 val; + + i2c_open(); + i2c_setclk(i2c_clk); + i2c_read(i2c_addr, &val, reg, 1); + i2c_close(); + return val; +} + +/* + * Interrupt handler + */ +static irqreturn_t mp4_tint_irq(int irq, void *dev_id) +{ + int key_num = 0; + u8 value0, value1; + + __gpio_ack_irq(MP4_KEY_TINT); + value0 = read_reg(0x75); + value1 = read_reg(0x76); + value0 &= ATA2508_SENSOR_MASK; + if (value0 == 0) { + printk("\nRelease key!\n"); + return IRQ_HANDLED; + } + while(value0 >> 1){ + value0 >>= 1; + key_num++; + } + + printk("\nPress key %d!\n", key_num); + return IRQ_HANDLED; +} + +static int __init init_ata2508(void) +{ + int i; + unsigned char data1; + int retval; + + __gpio_as_output(MP4_KEY_RST); + __gpio_set_pin(MP4_KEY_RST); + mdelay(100); + __gpio_clear_pin(MP4_KEY_RST); + mdelay(800); + __gpio_set_pin(MP4_KEY_RST); + __gpio_mask_irq(MP4_KEY_TINT); + + /*write registers*/ + for(i=0; i<13; i++) + { + data1 = init_data_alpha[i]; + write_reg(i, data1); + } + + for(i=13; i<63; i++) + { + data1 = init_data_burst[i-13]; + write_reg(i, data1); + } +#if 0 + for (i = 0; i < 63; i++) + { + data1 = read_reg(i); + printk("REG0x%02x = 0x%02x\n", i, data1); + } +#endif + + /* wait for 1 ms*/ + mdelay(1); +#if 0 + while(1) + { + data1 = read_reg(0x68); + printk("REG0x68 = %d\n", data1); + data1 = read_reg(0x75); + printk("REG0x75 = 0x%02x\n", data1); + data1 = read_reg(0x76); + printk("REG0x76 = 0x%02x\n", data1); + mdelay(2000); + } +#endif + data1 = read_reg(0x68); + printk("REG0x68 = %d\n", data1); + + /* to activate all the new settings, give a WARM RESET.*/ + write_reg(ADDR_WARM_RESET, 0x00); //ADDR_WARM_RESET=0xFF + + //printk("REG0x68 = %d\n", data1); + + /* wait for 1 ~ 10 ms.*/ + mdelay(10); + data1 = read_reg(0x68); + + /* Enable INT that connected to ATA2508's TINT.*/ + __gpio_as_irq_rise_edge(MP4_KEY_TINT); + + retval = request_irq(MP4_TINT_IRQ, mp4_tint_irq, + IRQF_DISABLED, "mp4_key_tint", NULL); + if (retval) { + printk("Could not get mp4 key irq %d\n", MP4_TINT_IRQ); + return retval; + } + + printk("MP4 touch panel register!\n"); + + return 0; +} + +static void __exit exit_ata2508(void) +{ + free_irq(MP4_TINT_IRQ, NULL); +} + +module_init(init_ata2508); +module_exit(exit_ata2508); diff --git a/drivers/char/jzchar/cim.c b/drivers/char/jzchar/cim.c new file mode 100644 index 0000000..c320d67 --- /dev/null +++ b/drivers/char/jzchar/cim.c @@ -0,0 +1,366 @@ +/* + * linux/drivers/char/jzchar/cim.c + * + * Camera Interface Module (CIM) driver for JzSOC + * This driver is independent of the camera sensor + * + * Copyright (C) 2005 JunZheng semiconductor + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "jzchars.h" + +#define CIM_NAME "cim" + +MODULE_AUTHOR("Jianli Wei"); +MODULE_DESCRIPTION("JzSOC Camera Interface Module driver"); +MODULE_LICENSE("GPL"); + +/* + * Define the Max Image Size + */ +#define MAX_IMAGE_WIDTH 640 +#define MAX_IMAGE_HEIGHT 480 +#define MAX_IMAGE_BPP 16 +#define MAX_FRAME_SIZE (MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT * MAX_IMAGE_BPP / 8) + +typedef struct +{ + u32 width; + u32 height; + u32 bpp; +} img_param_t; + +typedef struct +{ + u32 cfg; + u32 ctrl; + u32 mclk; +} cim_config_t; + +/* + * IOCTL_XXX commands + */ +#define IOCTL_SET_IMG_PARAM 0 // arg type: img_param_t * +#define IOCTL_CIM_CONFIG 1 // arg type: cim_config_t * + +/* Actual image size, must less than max values */ +static int img_width = MAX_IMAGE_WIDTH, img_height = MAX_IMAGE_HEIGHT, img_bpp = MAX_IMAGE_BPP; + +/* + * CIM DMA descriptor + */ +struct cim_desc { + u32 nextdesc; /* Physical address of next desc */ + u32 framebuf; /* Physical address of frame buffer */ + u32 frameid; /* Frame ID */ + u32 dmacmd; /* DMA command */ +}; + +/* + * CIM device structure + */ +struct cim_device { + unsigned char *framebuf; + unsigned int frame_size; + unsigned int page_order; + wait_queue_head_t wait_queue; + struct cim_desc frame_desc __attribute__ ((aligned (16))); +}; + +// global +static struct cim_device *cim_dev; + +/*========================================================================== + * CIM init routines + *========================================================================*/ + +static void cim_config(cim_config_t *c) +{ + REG_CIM_CFG = c->cfg; + REG_CIM_CTRL = c->ctrl; + // Set the master clock output +#if defined(CONFIG_SOC_JZ4730) + __cim_set_master_clk(__cpm_get_sclk(), c->mclk); +#elif defined(CONFIG_SOC_JZ4740) || defined(CONFIG_SOC_JZ4750) + __cim_set_master_clk(__cpm_get_hclk(), c->mclk); +#else + __cim_set_master_clk(__cpm_get_sclk(), c->mclk); +#endif + // Enable sof, eof and stop interrupts + __cim_enable_sof_intr(); + __cim_enable_eof_intr(); + __cim_enable_stop_intr(); +} + +/*========================================================================== + * CIM start/stop operations + *========================================================================*/ + +static int cim_start_dma(char *ubuf) +{ + __cim_disable(); + + dma_cache_wback((unsigned long)cim_dev->framebuf, (2 ^ (cim_dev->page_order)) * 4096); + + // set the desc addr + __cim_set_da(virt_to_phys(&(cim_dev->frame_desc))); + + __cim_clear_state(); // clear state register + __cim_reset_rxfifo(); // resetting rxfifo + __cim_unreset_rxfifo(); + __cim_enable_dma(); // enable dma + + // start + __cim_enable(); + + // wait for interrupts + interruptible_sleep_on(&cim_dev->wait_queue); + + // copy frame data to user buffer + memcpy(ubuf, cim_dev->framebuf, cim_dev->frame_size); + + return cim_dev->frame_size; +} + +static void cim_stop(void) +{ + __cim_disable(); + __cim_clear_state(); +} + +/*========================================================================== + * Framebuffer allocation and destroy + *========================================================================*/ + +static void cim_fb_destroy(void) +{ + if (cim_dev->framebuf) { + free_pages((unsigned long)(cim_dev->framebuf), cim_dev->page_order); + cim_dev->framebuf = NULL; + } +} + +static int cim_fb_alloc(void) +{ + cim_dev->frame_size = img_width * img_height * (img_bpp/8); + cim_dev->page_order = get_order(cim_dev->frame_size); + + /* frame buffer */ + cim_dev->framebuf = (unsigned char *)__get_free_pages(GFP_KERNEL, cim_dev->page_order); + if ( !(cim_dev->framebuf) ) { + return -ENOMEM; + } + + cim_dev->frame_desc.nextdesc = virt_to_phys(&(cim_dev->frame_desc)); + cim_dev->frame_desc.framebuf = virt_to_phys(cim_dev->framebuf); + cim_dev->frame_desc.frameid = 0x52052018; + cim_dev->frame_desc.dmacmd = CIM_CMD_EOFINT | CIM_CMD_STOP | (cim_dev->frame_size >> 2); // stop after capturing a frame + + dma_cache_wback((unsigned long)(&(cim_dev->frame_desc)), 16); + + return 0; +} + +/*========================================================================== + * File operations + *========================================================================*/ + +static int cim_open(struct inode *inode, struct file *filp); +static int cim_release(struct inode *inode, struct file *filp); +static ssize_t cim_read(struct file *filp, char *buf, size_t size, loff_t *l); +static ssize_t cim_write(struct file *filp, const char *buf, size_t size, loff_t *l); +static int cim_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); + +static struct file_operations cim_fops = +{ + open: cim_open, + release: cim_release, + read: cim_read, + write: cim_write, + ioctl: cim_ioctl +}; + +static int cim_open(struct inode *inode, struct file *filp) +{ + try_module_get(THIS_MODULE); + return 0; +} + +static int cim_release(struct inode *inode, struct file *filp) +{ + cim_stop(); + + module_put(THIS_MODULE); + return 0; +} + +static ssize_t cim_read(struct file *filp, char *buf, size_t size, loff_t *l) +{ + if (size < cim_dev->frame_size) + return -EINVAL; + + return cim_start_dma(buf); +} + +static ssize_t cim_write(struct file *filp, const char *buf, size_t size, loff_t *l) +{ + printk("cim error: write is not implemented\n"); + return -1; +} + +static int cim_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case IOCTL_SET_IMG_PARAM: + { + img_param_t i; + + if (copy_from_user((void *)&i, (void *)arg, sizeof(img_param_t))) + return -EFAULT; + + img_width = i.width; + img_height = i.height; + img_bpp = i.bpp; + + if ((img_width * img_height * img_bpp/8) > MAX_FRAME_SIZE) { + /* realloc the buffer */ + cim_fb_destroy(); + if (cim_fb_alloc() < 0) + return -ENOMEM; + } + + cim_dev->frame_size = img_width * img_height * (img_bpp/8); + + cim_dev->frame_desc.dmacmd = CIM_CMD_EOFINT | CIM_CMD_STOP | (cim_dev->frame_size >> 2); // stop after capturing a frame + + dma_cache_wback((unsigned long)(&(cim_dev->frame_desc)), 16); + + break; + } + case IOCTL_CIM_CONFIG: + { + cim_config_t c; + + if (copy_from_user((void *)&c, (void *)arg, sizeof(cim_config_t))) + return -EFAULT; + + cim_config(&c); + + break; + } + default: + printk("Not supported command: 0x%x\n", cmd); + return -EINVAL; + break; + } + return 0; +} + +/*========================================================================== + * Interrupt handler + *========================================================================*/ + +static irqreturn_t cim_irq_handler(int irq, void *dev_id) +{ + u32 state = REG_CIM_STATE; +#if 0 + if (state & CIM_STATE_DMA_EOF) { + wake_up_interruptible(&cim_dev->wait_queue); + } +#endif + if (state & CIM_STATE_DMA_STOP) { + // Got a frame, wake up wait routine + wake_up_interruptible(&cim_dev->wait_queue); + } + + // clear status flags + REG_CIM_STATE = 0; + return IRQ_HANDLED; +} + +/*========================================================================== + * Module init and exit + *========================================================================*/ + +static int __init cim_init(void) +{ + struct cim_device *dev; + int ret; + + /* allocate device */ + dev = kmalloc(sizeof(struct cim_device), GFP_KERNEL); + if (!dev) return -ENOMEM; + + /* record device */ + cim_dev = dev; + + /* allocate a frame buffer */ + if (cim_fb_alloc() < 0) { + kfree(dev); + return -ENOMEM; + } + + init_waitqueue_head(&dev->wait_queue); + + ret = jz_register_chrdev(CIM_MINOR, CIM_NAME, &cim_fops, dev); + if (ret < 0) { + cim_fb_destroy(); + kfree(dev); + return ret; + } + + if ((ret = request_irq(IRQ_CIM, cim_irq_handler, IRQF_DISABLED, + CIM_NAME, dev))) { + cim_fb_destroy(); + kfree(dev); + printk(KERN_ERR "CIM could not get IRQ"); + return ret; + } + + printk("JzSOC Camera Interface Module (CIM) driver registered\n"); + + return 0; +} + +static void __exit cim_exit(void) +{ + free_irq(IRQ_CIM, cim_dev); + jz_unregister_chrdev(CIM_MINOR, CIM_NAME); + cim_fb_destroy(); + kfree(cim_dev); +} + +module_init(cim_init); +module_exit(cim_exit); diff --git a/drivers/char/jzchar/cim.h b/drivers/char/jzchar/cim.h new file mode 100644 index 0000000..c21431b --- /dev/null +++ b/drivers/char/jzchar/cim.h @@ -0,0 +1,36 @@ +/* + * JzSOC CIM driver + * + * Copyright (C) 2005 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __CIM_H__ +#define __CIM_H__ + +typedef struct +{ + u32 width; + u32 height; + u32 bpp; +} IMG_PARAM; + +/* + * IOCTL_XXX commands + */ +#define IOCTL_SET_IMG_PARAM 0 // arg type: IMG_PARAM * + +#endif /* __CIM_H__ */ diff --git a/drivers/char/jzchar/jz_ow.c b/drivers/char/jzchar/jz_ow.c new file mode 100644 index 0000000..86b3c00 --- /dev/null +++ b/drivers/char/jzchar/jz_ow.c @@ -0,0 +1,497 @@ +/* + * linux/drivers/char/jzchar/jz_ow.c + * + * One Wire Bus test driver + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "jzchars.h" + +#define OW_CPU_READ_ROM 1 +#define OW_INTC_READ_ROM 1 +#define OW_CPU_SEARCH_ROM 0 +#define OW_INTC_SEARCH_ROM 0 + +#define OW_DEBUG 0 +#if OW_DEBUG +#define OWI_MAX 10 +static char CFG[OWI_MAX]; +static char CTL[OWI_MAX]; +static char STS[OWI_MAX]; +static char DAT[OWI_MAX]; +static char DIV[OWI_MAX]; +static void owi_register_dump(int i) +{ + CFG[i]= REG_OWI_CFG; + CTL[i]= REG_OWI_CTL; + STS[i]= REG_OWI_STS; + DAT[i]= REG_OWI_DAT; + DIV[i]= REG_OWI_DIV; +} +static void owi_register_print(int i) +{ + printk(" REG_OWI_CFG: 0x%08x\n", CFG[i]); + printk(" REG_OWI_CTL: 0x%08x\n", CTL[i]); + printk(" REG_OWI_STS: 0x%08x\n", STS[i]); + printk(" REG_OWI_DAT: 0x%08x\n", DAT[i]); + printk(" REG_OWI_DIV: 0x%08x\n", DIV[i]); +} +#endif + +static DECLARE_WAIT_QUEUE_HEAD (ow_wait_queue); + +/* + * fops routines + */ +static int ow_open(struct inode *inode, struct file *filp); +static int ow_release(struct inode *inode, struct file *filp); +static ssize_t ow_read(struct file *filp, char *buf, size_t size, loff_t *l); +static ssize_t ow_write(struct file *filp, const char *buf, size_t size, loff_t *l); +static int ow_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); + +static void do_ow_rddata(void); +static void do_ow_wrdata(void); +static void do_ow_wr1rd(void); +static void do_ow_wr0(void); +static void do_ow_rst(void); + +static void do_interrupt_mode_test(void); +static void do_cpu_mode_test(void); + +static struct file_operations ow_fops = +{ + open: ow_open, + release: ow_release, + read: ow_read, + write: ow_write, + ioctl: ow_ioctl, +}; + +static int ow_open(struct inode *inode, struct file *filp) +{ + try_module_get(THIS_MODULE); + return 0; +} + +static int ow_release(struct inode *inode, struct file *filp) +{ + module_put(THIS_MODULE); + return 0; +} + +static ssize_t ow_read(struct file *filp, char *buf, size_t size, loff_t *l) +{ + printk("OW: read is not implemented\n"); + return -1; +} + +static ssize_t ow_write(struct file *filp, const char *buf, size_t size, loff_t *l) +{ + printk("ow: write is not implemented\n"); + return -1; +} + +static int ow_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + switch (cmd) { + + default: + printk("Not supported command: 0x%x\n", cmd); + return -EINVAL; + break; + } + return ret; +} + +static void do_ow_rddata(void) +{ + __owi_clr_sts(); + __owi_set_rddata(); + __owi_enable_ow_ops(); +} + +static void do_ow_wrdata(void) +{ + __owi_clr_sts(); + __owi_set_wrdata(); + __owi_enable_ow_ops(); +} + +static void do_ow_wr1rd(void) +{ + __owi_clr_sts(); + __owi_set_wr1rd(); + __owi_enable_ow_ops(); +} + +static void do_ow_wr0(void) +{ + __owi_clr_sts(); + __owi_set_wr0(); + __owi_enable_ow_ops(); +} + +static void do_ow_rst(void) +{ + __owi_clr_sts(); + __owi_set_rst(); + __owi_enable_ow_ops(); +} + +static irqreturn_t ow_interrupt(int irq, void *dev_id) +{ + __owi_clr_sts(); + wake_up(&ow_wait_queue); + + return IRQ_HANDLED; +} + +static void ow_intcm_read_rom(char *rom) +{ + int i; + + __owi_select_regular_mode(); + REG_OWI_DIV = 23; + __owi_clr_sts(); + __intc_unmask_irq(IRQ_OWI); + __owi_enable_all_interrupts(); + + do_ow_rst(); + sleep_on(&ow_wait_queue); + + REG_OWI_DAT = 0x33; + do_ow_wrdata(); + sleep_on(&ow_wait_queue); + + for(i=0; i<8; i++){ + do_ow_rddata(); + sleep_on(&ow_wait_queue); + rom[i] = REG_OWI_DAT; + } + __intc_mask_irq(IRQ_OWI); +} + +static void ow_intcm_search_rom(void) +{ + int i, j; + int normal, reverse; +#if 1 + unsigned char rom[8]={0x01, 0xf9, 0x35, 0x53, 0x11, 0x00, 0x00, 0x3e}; +#else + unsigned char rom[8]={0x01, 0xd8, 0x10, 0x02, 0x10, 0x00, 0x00, 0x22}; +#endif + __owi_select_regular_mode(); + REG_OWI_DIV = __cpm_get_extalclk()/1000000 - 1; + __owi_clr_sts(); + __intc_unmask_irq(IRQ_OWI); + __owi_enable_all_interrupts(); + + /* reset */ + do_ow_rst(); + sleep_on(&ow_wait_queue); + + /* send search ROM command */ + REG_OWI_DAT = 0xf0; + do_ow_wrdata(); + sleep_on(&ow_wait_queue); + + for( i=0; i<8; i++){ + for (j=0; j<8; j++){ + do_ow_wr1rd(); + sleep_on(&ow_wait_queue); + normal = ( __owi_get_rdst() !=0); + printk("normal: %d\n",normal); + + do_ow_wr1rd(); + sleep_on(&ow_wait_queue); + reverse = ( __owi_get_rdst() !=0); + printk("reverse: %d\n",reverse); + + if(normal ==1 && reverse ==1){ + printk("Search rom INTC mode: 11 NO device found\n"); + __intc_mask_irq(IRQ_OWI); + return; + } +#if 1 + if ( (rom[i]>>j) & 1 ){ + printk("write 1\n"); + do_ow_wr1rd(); + sleep_on(&ow_wait_queue); + } + else{ + printk("write 0\n"); + do_ow_wr0(); + sleep_on(&ow_wait_queue); + } + +#else + if(normal ==0 && reverse ==0){ + if (!((rom[i]>>j) & 1) ){ + printk("write 1\n"); + do_ow_wr1rd(); + sleep_on(&ow_wait_queue); + } + else{ + printk("write 0\n"); + do_ow_wr0(); + sleep_on(&ow_wait_queue); + } + }else{ + + if(normal ==0){ + printk("write 0\n"); + do_ow_wr0(); + sleep_on(&ow_wait_queue); + } + if(normal ==1){ + printk("write 1\n"); + do_ow_wr1rd(); + sleep_on(&ow_wait_queue); + } + } +#endif + + } + printk("\n\n"); + } + + printk("\nSearch rom INTC mode: device found SUCCESSFULLY\n"); + __intc_mask_irq(IRQ_OWI); + +} + +static void ow_cpum_read_rom(char *rom) +{ + int i; + + __owi_select_regular_mode(); + REG_OWI_DIV = __cpm_get_extalclk()/1000000 - 1; + __owi_clr_sts(); + __owi_disable_all_interrupts(); + + do_ow_rst(); + __owi_wait_ops_rdy(); + + if(!__owi_get_sts_pst()) + printk("read rom no device found\n"); + + REG_OWI_DAT = 0x33; + do_ow_wrdata(); + __owi_wait_ops_rdy(); + + for(i=0; i<8; i++){ + do_ow_rddata(); + __owi_wait_ops_rdy(); + rom[i] = REG_OWI_DAT; + } +} + + +static void ow_comm_bit(unsigned comm) +{ + int i; + for(i=0; i<8; i++){ + if ( comm & (1<>j) & 1 ){ + printk("write 1\n"); + do_ow_wr1rd(); + while(!__owi_get_sts_bit_rdy()) ; + } + else{ + printk("write 0\n"); + do_ow_wr0(); + while(!__owi_get_sts_bit_rdy()) ; + } + +#else + if(normal ==0 && reverse ==0){ + if (!((rom[i]>>j) & 1) ){ + printk("write 1\n"); + do_ow_wr1rd(); + while(!__owi_get_sts_bit_rdy()) ; + } + else{ + printk("write 0\n"); + do_ow_wr0(); + while(!__owi_get_sts_bit_rdy()) ; + } + }else{ + + if(normal ==0){ + printk("write 0\n"); + do_ow_wr0(); + while(!__owi_get_sts_bit_rdy()) ; + } + if(normal ==1){ + printk("write 1\n"); + do_ow_wr1rd(); + while(!__owi_get_sts_bit_rdy()) ; + } + } +#endif + + } + printk("\n\n"); + } + printk("\nSearch rom CPU mode: device found SUCCESSFULLY\n"); +} + +static void do_interrupt_mode_test(void) +{ + int ret, i; + unsigned char rom[8]; + + /* interrupt mode */ + ret = request_irq(IRQ_OWI, ow_interrupt, IRQF_DISABLED, + "JZ_OWI", NULL); + if(ret) + printk("failed irq \n"); + +#if OW_INTC_READ_ROM + ow_intcm_read_rom(rom); + printk("\n\nAfter intc mode read ROM ops: \n"); + printk("ROM: "); + for(i=0; i<8; i++) + printk("0x%02x,",rom[i]); +#endif + +#if OW_INTC_SEARCH_ROM + ow_intcm_search_rom(); +#endif + +} + +static void do_cpu_mode_test(void) +{ + +#if OW_CPU_READ_ROM + int i; + unsigned char rom[8]; + + ow_cpum_read_rom(rom); + printk("\n\nAfter CPU mode read ROM ops: \n"); + printk("ROM: "); + for(i=0; i<8; i++) + printk("0x%02x,",rom[i]); +#endif + +#if OW_CPU_SEARCH_ROM + ow_cpum_search_rom(); +#endif +} + +/* + * Module init and exit + */ +static int __init ow_init(void) +{ + int ret; + + ret = jz_register_chrdev(OW_MINOR, "ow", &ow_fops, NULL); + if (ret < 0) { + return ret; + } + __gpio_as_func1(153); + + REG_OWI_CFG=0; + REG_OWI_CTL=0; + REG_OWI_STS=0; + REG_OWI_DAT=0; + REG_OWI_DIV=0; + + do_interrupt_mode_test(); + do_cpu_mode_test(); + + printk("Ingenic OW driver registered\n"); + + return 0; +} + +static void __exit ow_exit(void) +{ + free_irq(IRQ_OWI, NULL); + jz_unregister_chrdev(OW_MINOR, "ow"); +} + +module_init(ow_init); +module_exit(ow_exit); + +MODULE_AUTHOR("Yurong Tan"); +MODULE_DESCRIPTION("One Wire Bus test Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/jzchar/jz_ts.c b/drivers/char/jzchar/jz_ts.c new file mode 100644 index 0000000..16b46a2 --- /dev/null +++ b/drivers/char/jzchar/jz_ts.c @@ -0,0 +1,443 @@ +/* + * jz_ts.c + * + * Touch screen driver for the Ingenic JZ47XX. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "jz_ts.h" + +MODULE_AUTHOR("Peter Wei "); +MODULE_DESCRIPTION("Ingenic Touch Screen Driver"); +MODULE_LICENSE("GPL"); + +#define TS_NAME "jz-ts" +#define TS_MINOR 16 /* MAJOR: 10, MINOR: 16 */ +#define PFX TS_NAME + +//#define JZ_TS_DEBUG + +#ifdef JZ_TS_DEBUG +#define dbg(format, arg...) printk(KERN_DEBUG PFX ": " format "\n" , ## arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif +#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg) +#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg) +#define warn(format, arg...) printk(KERN_WARNING PFX ": " format "\n" , ## arg) + +static struct jz_ts_t jz_ts; + +unsigned int (*codec_read_battery)(void) = NULL; + +// hold the spinlock before calling. +static void event_add(struct jz_ts_t *ts, struct ts_event *event) +{ + unsigned long flags; + + spin_lock_irqsave(&ts->lock, flags); + + // add this event to the event queue + ts->event_buf[ts->nextIn] = *event; + ts->nextIn = (ts->nextIn + 1) & (EVENT_BUFSIZE - 1); + if (ts->event_count < EVENT_BUFSIZE) { + ts->event_count++; + } else { + // throw out the oldest event + ts->nextOut = (ts->nextOut + 1) & (EVENT_BUFSIZE - 1); + } + + spin_unlock_irqrestore(&ts->lock, flags); + + // async notify + if (ts->fasync) + kill_fasync(&ts->fasync, SIGIO, POLL_IN); + // wake up any read call + if (waitqueue_active(&ts->wait)) + wake_up_interruptible(&ts->wait); +} + +static int event_pull(struct jz_ts_t *ts, struct ts_event *event) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&ts->lock, flags); + ret = ts->event_count; + if (ts->event_count) { + *event = ts->event_buf[ts->nextOut]; + ts->nextOut = (ts->nextOut + 1) & (EVENT_BUFSIZE - 1); + ts->event_count--; + } + spin_unlock_irqrestore(&ts->lock, flags); + + return ret; +} + +static int pen_is_down = 0; + +static irqreturn_t pendown_interrupt(int irq, void * dev_id) +{ + struct jz_ts_t* ts = &jz_ts; + struct ts_event event; + + dbg("pen down"); +#if defined(CONFIG_SOC_JZ4740) + if (ts->sleeping) { + ts->sleeping = 0; + ts_data_ready(); + return IRQ_HANDLED; + } +#endif + spin_lock(&ts->lock); + + if (ts->irq_enabled) { + ts->irq_enabled = 0; + } + else + ts->irq_enabled = 1; + + + if (pen_is_down) + pen_is_down = 0; + else + pen_is_down = 1; + + // callback routine to clear irq status + ts_irq_callback(); + + if ( (pen_is_down == 0)){ + del_timer(&ts->acq_timer); + spin_unlock(&ts->lock); + event.x = event.y = event.pressure = 0; + event.status = PENUP; + ts->first_read = 0; + event_add(ts, &event); + return IRQ_HANDLED; + } + + if ( (pen_is_down == 1)) + { + ts->acq_timer.expires = jiffies + HZ / 100; + del_timer(&ts->acq_timer); + ts->first_read = 1; + add_timer(&ts->acq_timer); + spin_unlock(&ts->lock); + } + return IRQ_HANDLED; +} + + +/* + * Raw X,Y,pressure acquisition timer function. It gets scheduled + * only while pen is down. Its duration between calls is the polling + * rate. + */ +static void +jz_acq_timer(unsigned long data) +{ + struct jz_ts_t *ts = (struct jz_ts_t *)data; + struct ts_event event; + int pen_was_down = ts->pen_is_down; + + spin_lock(&ts->lock); + + if (PenIsDown()) { + + ts->pen_is_down = 1; + + if (AcquireEvent(ts, &event)) // check event is valid or not? + event_add(ts, &event); + + // schedule next acquire + ts->acq_timer.expires = jiffies + HZ / 100; + del_timer(&ts->acq_timer); + add_timer(&ts->acq_timer); + } else { + + if (!ts->irq_enabled) { + ts->irq_enabled = 1; + } + ts->pen_is_down = 0; + if (pen_was_down) { + event.x = event.y = event.pressure = 0; + event.status = PENUP; + event_add(ts, &event); + } + } + + spin_unlock(&ts->lock); +} + +/* +++++++++++++ Read battery voltage routine ++++++++++++++*/ + +unsigned int jz_read_battery(void) +{ + unsigned int v = 0; + struct jz_ts_t *ts = &jz_ts; + + spin_lock(&ts->lock); + + if (codec_read_battery) + v = codec_read_battery(); + + spin_unlock(&ts->lock); + + return v; +} + +/* +++++++++++++ File operations ++++++++++++++*/ + +static int +jz_fasync(int fd, struct file *filp, int mode) +{ + struct jz_ts_t *ts = (struct jz_ts_t *)filp->private_data; + return fasync_helper(fd, filp, mode, &ts->fasync); +} + + +static unsigned int +jz_poll(struct file * filp, poll_table * wait) +{ + struct jz_ts_t* ts = (struct jz_ts_t*)filp->private_data; + poll_wait(filp, &ts->wait, wait); + if (ts->event_count) + return POLLIN | POLLRDNORM; + return 0; +} + +static ssize_t +jz_read(struct file * filp, char * buffer, size_t count, loff_t * ppos) +{ + DECLARE_WAITQUEUE(wait, current); + struct jz_ts_t* ts = (struct jz_ts_t*)filp->private_data; + char *ptr = buffer; + struct ts_event event; + int err = 0; + + dbg("jz_read"); + + add_wait_queue(&ts->wait, &wait); + while (count >= sizeof(struct ts_event)) { + err = -ERESTARTSYS; + if (signal_pending(current)) + break; + + + if (event_pull(ts, &event)) { + err = copy_to_user(ptr, &event, + sizeof(struct ts_event)); + if (err) + break; + ptr += sizeof(struct ts_event); + count -= sizeof(struct ts_event); + } else { + set_current_state(TASK_INTERRUPTIBLE); + err = -EAGAIN; + if (filp->f_flags & O_NONBLOCK) + break; + schedule(); + } + } + + current->state = TASK_RUNNING; + remove_wait_queue(&ts->wait, &wait); + + return ptr == buffer ? err : ptr - buffer; +} + + +static int +jz_open(struct inode * inode, struct file * filp) +{ + struct jz_ts_t *ts; + int retval; + + dbg("open ts device"); + filp->private_data = ts = &jz_ts; + + spin_lock(&ts->lock); + + ts->pen_is_down = 0; // start with pen up + ts->sleeping = 0; + // flush event queue + ts->nextIn = ts->nextOut = ts->event_count = 0; + + // Init acquisition timer function + init_timer(&ts->acq_timer); + ts->acq_timer.function = jz_acq_timer; + ts->acq_timer.data = (unsigned long)ts; + + ts->irq_enabled = 1; + + spin_unlock(&ts->lock); + + /* Since ts interrupt can happen immediately after request_irq, + * we wait until we've completed init of all relevent driver + * state variables. Now we grab the PenDown IRQ + */ + retval = ts_request_irq(&ts->pendown_irq, pendown_interrupt, TS_NAME, ts); + if (retval) { + err("unable to get PenDown IRQ %d", ts->pendown_irq); + return retval; + } + + try_module_get(THIS_MODULE); + return 0; +} + +static int +jz_release(struct inode * inode, struct file * filp) +{ + struct jz_ts_t* ts = (struct jz_ts_t*)filp->private_data; + + ts_free_irq(ts); + jz_fasync(-1, filp, 0); + del_timer_sync(&ts->acq_timer); + + module_put(THIS_MODULE); + return 0; +} + +static int jz_ioctl(struct inode *inode, struct file *file, unsigned int ioctl_num, unsigned long ioctl_param) +{ + struct txy { + int minx; + int miny; + int maxx; + int maxy; + }; + + struct txy ch; + + /* + * Switch according to the ioctl called + */ + switch (ioctl_num) + { + case IOCTL_SET_MSG: + jz_ts.filter=1; + break; + case IOCTL_SET_NUM: + if (copy_from_user((void *)&ch, (void *)ioctl_param, sizeof(ch))) + return -EFAULT; + jz_ts.minx = ch.minx; + jz_ts.miny = ch.miny; + jz_ts.maxx = ch.maxx; + jz_ts.maxy = ch.maxy; + break; + } + + return 0; +} + +static struct file_operations ts_fops = { + owner: THIS_MODULE, + read: jz_read, + poll: jz_poll, + fasync: jz_fasync, + ioctl: jz_ioctl, + open: jz_open, + release: jz_release, +}; + +/* +++++++++++++ End File operations ++++++++++++++*/ + +static int __init minx_setup(char *str) +{ + int i; + + if (get_option(&str,&i)) jz_ts.minx = i; + jz_ts.filter=i; + return 1; +} + +__setup("ts_minx=", minx_setup); + +static int __init miny_setup(char *str) +{ + int i; + if (get_option(&str,&i)) jz_ts.miny = i; + return 1; +} + +__setup("ts_miny=", miny_setup); + +static int __init maxx_setup(char *str) +{ + int i; + if (get_option(&str,&i)) jz_ts.maxx = i; + return 1; +} + +__setup("ts_maxx=", maxx_setup); + +static int __init maxy_setup(char *str) +{ + int i; + if (get_option(&str,&i)) jz_ts.maxy = i; + return 1; +} + +__setup("ts_maxy=", maxy_setup); + +static int __init printraw_setup(char *str) +{ + if (str) + jz_ts.prints = 1; + + return 0; +} + +__setup("ts_debug", printraw_setup); + + +static struct miscdevice jz_ts_dev = { + minor: TS_MINOR, + name: TS_NAME, + fops: &ts_fops, +}; + +static int __init jzts_init_module(void) +{ + struct jz_ts_t *ts = &jz_ts; + int ret; + + if ((ret = misc_register(&jz_ts_dev)) < 0) { + err("can't register misc device"); + return ret; + } + +// memset(ts, 0, sizeof(struct jz_ts_t)); + init_waitqueue_head(&ts->wait); + spin_lock_init(&ts->lock); + + printk("Jz generic touch screen driver registered\n"); + + return 0; +} + +static void jzts_cleanup_module(void) +{ + misc_deregister(&jz_ts_dev); +} + +module_init(jzts_init_module); +module_exit(jzts_cleanup_module); diff --git a/drivers/char/jzchar/jz_ts.h b/drivers/char/jzchar/jz_ts.h new file mode 100644 index 0000000..4285797 --- /dev/null +++ b/drivers/char/jzchar/jz_ts.h @@ -0,0 +1,54 @@ +#ifndef __JZ_TS_H__ +#define __JZ_TS_H__ + +/* + * IOCTL commands + */ +#define IOCTL_SET_MSG 0 +#define IOCTL_SET_NUM 1 + + +/* + * TS Event type + */ +struct ts_event { + u16 status; + u16 x; + u16 y; + u16 pressure; + u16 pad; +}; + +/* TS event status */ +#define PENUP 0x00 +#define PENDOWN 0x01 + +#define EVENT_BUFSIZE 64 // must be power of two + +struct jz_ts_t { + int pendown_irq; // IRQ of pendown interrupt + int pen_is_down; // 1 = pen is down, 0 = pen is up + int irq_enabled; + struct ts_event event_buf[EVENT_BUFSIZE];// The event queue + int nextIn, nextOut; + int event_count; + struct fasync_struct *fasync; // asynch notification + struct timer_list acq_timer; // Timer for triggering acquisitions + wait_queue_head_t wait; // read wait queue + spinlock_t lock; + int minx, miny, maxx, maxy; + int filter, prints; + int sleeping; + int first_read; +}; + +extern void ts_enable_irq(void); +extern void ts_disable_irq(void); +extern int ts_request_irq(u32 *irq,irqreturn_t (*handler)(int, void *), const char *devname, void *dev_id); +extern void ts_free_irq(struct jz_ts_t *ts); +extern int PenIsDown(void); +extern int AcquireEvent(struct jz_ts_t *ts, struct ts_event *event); +extern void ts_irq_callback(void); +extern void ts_data_ready(void); + +#endif /* __JZ_TS_H__ */ diff --git a/drivers/char/jzchar/jz_tssi.c b/drivers/char/jzchar/jz_tssi.c new file mode 100644 index 0000000..fe4978a --- /dev/null +++ b/drivers/char/jzchar/jz_tssi.c @@ -0,0 +1,457 @@ +/* + * jz_tssi.c + * + * MPEG2-TS interface driver for the Ingenic JZ47XX. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "jzchars.h" + +#include "jz_tssi.h" + + +MODULE_AUTHOR("Lucifer Liu "); +MODULE_DESCRIPTION("Ingenic MPEG2-TS interface Driver"); +MODULE_LICENSE("GPL"); + +#define TSSI_NAME "JZ MPEG2-TS SI" +#define TSSI_MINOR 204 /* MAJOR: 10, MINOR: 16 */ +#define TSSI_IRQ IRQ_TSSI +#define PFX TSSI_NAME +#define RING_BUF_NUM 100 + +#define USE_DMA +#define TRIG_PIN ( 32 * 2 + 15 ) +#define DMA_ID_TSSI 5 +//#define JZ_TSSI_DEBUG + +#ifdef JZ_TSSISI_DEBUG +#define dbg(format, arg...) printk(KERN_DEBUG PFX ": " format "\n" , ## arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif +#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg) +#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg) +#define warn(format, arg...) printk(KERN_WARNING PFX ": " format "\n" , ## arg) + +static struct jz_tssi_t jz_tssi_g; +static struct jz_tssi_buf_ring_t jz_tssi_ring_g; +static int tssi_dma_reinit(int dma_chan, unsigned char *dma_buf, int size); + +static void print_reg( void ) +{ + printk("REG_TSSI_ENA %8x \n ", REG8( TSSI_ENA )); + printk("REG_TSSI_CFG %8x \n ", REG16( TSSI_CFG )); + printk("REG_TSSI_CTRL %8x \n ", REG8( TSSI_CTRL )); + printk("REG_TSSI_STAT %8x \n ", REG8( TSSI_STAT )); + printk("REG_TSSI_FIFO %8x \n ", REG32( TSSI_FIFO )); + printk("REG_TSSI_PEN %8x \n ", REG32( TSSI_PEN )); + printk("REG_TSSI_PID0 %8x \n ", REG32( TSSI_PID0 )); + printk("REG_TSSI_PID1 %8x \n ", REG32( TSSI_PID1 )); + printk("REG_TSSI_PID2 %8x \n ", REG32( TSSI_PID2 )); + printk("REG_TSSI_PID3 %8x \n ", REG32( TSSI_PID3 )); + printk("REG_TSSI_PID4 %8x \n ", REG32( TSSI_PID4 )); + printk("REG_TSSI_PID5 %8x \n ", REG32( TSSI_PID5 )); + printk("REG_TSSI_PID6 %8x \n ", REG32( TSSI_PID6 )); + printk("REG_TSSI_PID7 %8x \n ", REG32( TSSI_PID7 )); +} + +void dump_dma_channel(unsigned int dmanr) +{ + printk("DMA%d Registers:\n", dmanr); + printk(" DMACR = 0x%8x\n", REG_DMAC_DMACR(0)); + printk(" DSAR = 0x%8x\n", REG_DMAC_DSAR(dmanr)); + printk(" DTAR = 0x%8x\n", REG_DMAC_DTAR(dmanr)); + printk(" DTCR = 0x%8x\n", REG_DMAC_DTCR(dmanr)); + printk(" DRSR = 0x%8x\n", REG_DMAC_DRSR(dmanr)); + printk(" DCCSR = 0x%8x\n", REG_DMAC_DCCSR(dmanr)); + printk(" DCMD = 0x%8x\n", REG_DMAC_DCMD(dmanr)); + printk(" DDA = 0x%8x\n", REG_DMAC_DDA(dmanr)); + printk(" DMADBR = 0x%8x\n", REG_DMAC_DMADBR(1)); +} + +static int tssi_buf_init( struct jz_tssi_buf_ring_t * ring ) +{ + int i; + struct jz_tssi_buf * bp,* ap, *cp; + + ap = cp = bp = (struct jz_tssi_buf *)kmalloc( sizeof( struct jz_tssi_buf ) ,GFP_KERNEL ); //the first + if ( !bp ) { + printk("Can not malloc buffer! \n"); + return -1; + } + + for ( i = 0; i < RING_BUF_NUM; i ++ ) { + bp = ap; + bp->buf = (unsigned int *) kmalloc(MPEG2_TS_PACHAGE_SIZE / 4 * sizeof(unsigned int) ,GFP_KERNEL); + if ( !bp->buf ) { + printk("Can not malloc buffer! \n"); + return -1; + } + bp->index = i; + bp->pos = 0; + ap = (struct jz_tssi_buf *)kmalloc( sizeof( struct jz_tssi_buf ) ,GFP_KERNEL ); + if ( !ap ) { + printk("Can not malloc buffer! \n"); + return -1; + } + + bp->next = ap; //point to next ! + } + + bp->next = cp; //point loop to first! + ring->front = cp; + ring->rear = cp; + ring->fu_num = 0; + kfree(ap); + return 0; +} + +static void tssi_free_buf( struct jz_tssi_buf_ring_t * ring ) +{ + int i; + struct jz_tssi_buf * ap; + for ( i = 0; i < RING_BUF_NUM; i ++ ) + { + ap = ring->front; + ring->front = ring->front->next; + kfree( ap ); + } +} + +#if 0 +static void tssi_read_fifo(void *dev_id) +{ + struct jz_tssi_t* tssi = ( struct jz_tssi_t* )dev_id; + struct jz_tssi_buf_ring_t * ring = tssi->cur_buf; + struct jz_tssi_buf *buf = ring->rear; + int i; +#if 0 + if ( ring->fu_num > RING_BUF_NUM ) + { + printk("Ring buffer full ! %d \n",ring->fu_num); + return; + } +#endif + + for ( i = 0; i < 8 ; i ++ ) + { + ring->front->buf[ring->front->pos++] = REG_TSSI_FIFO; + } + + if ( ring->front->pos >= MPEG2_TS_PACHAGE_SIZE ) + { + ring->fu_num ++; + ring->front = ring->front->next; + ring->front->pos = 0; + } +} +#endif + +static void tssi_config_filting( void ) +{ + __tssi_soft_reset(); + __gpio_as_tssi(); + __tssi_disable_ovrn_irq(); //use dma ,no need irq + __tssi_disable_trig_irq(); + __tssi_set_tigger_num( 8 ); //trig is 4 word! +// __tssi_filter_enable(); + __tssi_clear_state(); + __tssi_filter_disable(); + __tssi_state_clear_overrun(); +// __tssi_clear_trig_irq_flag(); +#ifdef USE_DMA + __tssi_dma_enable(); +#else + __tssi_dma_disable(); +#endif + + __tssi_enable_ovrn_irq(); +// __tssi_enable_trig_irq(); + + //set config +// __tssi_set_bt_1(); + __tssi_set_wd_1(); + __tssi_set_data_use_data7(); + __tssi_set_data_pola_high(); +// __tssi_select_serail_mode(); + __tssi_select_paral_mode(); + __tssi_select_clk_fast(); + __tssi_select_clk_posi_edge(); + __tssi_select_frm_act_high(); + __tssi_select_str_act_high(); + __tssi_select_fail_act_high(); +// __tssi_select_fail_act_low(); + __tssi_disable_filte_pid0(); //we disable pid0 filter for ever! +} + +static void tssi_add_pid(int pid_num, int pid) +{ + unsigned int addr ; + int n = pid_num / 2, hl = pid_num % 2; + if ( hl ) //use high pid, pid1 + { + addr = TSSI_PID0 + ( n * 4 ); + REG32( addr ) |= ( (pid & 0x1fff) << 16 ); //13bit + REG_TSSI_PEN |= ( 1 << (16 + n) ); + } + else //use low pid, pid0 + { + addr = TSSI_PID0 + ( n * 4 ); + REG32( addr ) |= pid & 0x1fff; //13bit + REG_TSSI_PEN |= ( 1 << n ); + } +} + +static irqreturn_t tssi_dma_irq(int irq, void * dev_id) +{ + struct jz_tssi_t *tssi = (struct jz_tssi_t *)dev_id; + struct jz_tssi_buf_ring_t *buf = tssi->cur_buf; + + REG_DMAC_DCCSR(tssi->dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + + if (__dmac_channel_transmit_end_detected(tssi->dma_chan)) { + __dmac_channel_clear_transmit_end(tssi->dma_chan); + if ( buf->fu_num < RING_BUF_NUM ) + { + buf->front = buf->front->next; + REG_DMAC_DSAR(tssi->dma_chan) = CPHYSADDR(TSSI_FIFO); + REG_DMAC_DTAR(tssi->dma_chan) = CPHYSADDR((unsigned int)buf->front->buf); + REG_DMAC_DTCR(tssi->dma_chan) = MPEG2_TS_PACHAGE_SIZE / 32; + REG_DMAC_DCCSR(tssi->dma_chan) = DMAC_DCCSR_NDES | DMAC_DCCSR_EN; + buf->fu_num ++; + } + __tssi_clear_state(); + } + + if (__dmac_channel_transmit_halt_detected(tssi->dma_chan)) { + printk("DMA HALT\n"); + __dmac_channel_clear_transmit_halt(tssi->dma_chan); + } + + if (__dmac_channel_address_error_detected(tssi->dma_chan)) { + printk("DMA ADDR ERROR\n"); + __dmac_channel_clear_address_error(tssi->dma_chan); + } + + if (__dmac_channel_descriptor_invalid_detected(tssi->dma_chan)) { + printk("DMA DESC INVALID\n"); + __dmac_channel_clear_descriptor_invalid(tssi->dma_chan); + } + + if (__dmac_channel_count_terminated_detected(tssi->dma_chan)) { + printk("DMA CT\n"); + __dmac_channel_clear_count_terminated(tssi->dma_chan); + } + + return IRQ_HANDLED; +} + +static irqreturn_t tssi_interrupt(int irq, void * dev_id) +{ + __intc_mask_irq(TSSI_IRQ); +#if 1 + if ( REG_TSSI_STAT & TSSI_STAT_OVRN ) + { + printk("tssi over run occur! %x\n",REG8( TSSI_STAT )); + __tssi_clear_state(); + printk("clear ! %x\n",REG8( TSSI_STAT )); + } +#endif + if ( REG_TSSI_STAT & TSSI_STAT_TRIG ) + { + printk("tssi trig irq occur! \n"); + tssi_read_fifo( dev_id ); + } + + __intc_ack_irq(TSSI_IRQ); + __intc_unmask_irq(TSSI_IRQ); + return IRQ_HANDLED; +} + +static ssize_t jz_read(struct file * filp, char * buffer, size_t count, loff_t * ppos) +{ + jz_char_dev_t *adev = (jz_char_dev_t *)filp->private_data; + struct jz_tssi_t* tssi = (struct jz_tssi_t*)adev->private; + struct jz_tssi_buf_ring_t* ring = tssi->cur_buf; + + int i; + + count /= MPEG2_TS_PACHAGE_SIZE; + + if ( count > ring->fu_num ) + count = ring->fu_num; + + for ( i = 0; i < count; i ++ ) + { + memcpy( buffer + ( i * MPEG2_TS_PACHAGE_SIZE), + ring->rear->buf, MPEG2_TS_PACHAGE_SIZE ); + ring->rear->pos = 0; + ring->rear = ring->rear->next; + } + ring->fu_num -= count; + return count * MPEG2_TS_PACHAGE_SIZE; +} + +static int tssi_dma_reinit(int dma_chan, unsigned char *dma_buf, int size) +{ + static unsigned int dma_src_phys_addr, dma_dst_phys_addr; + REG_DMAC_DMACKE(0) = 0xff; + dma_src_phys_addr = CPHYSADDR(TSSI_FIFO); + dma_dst_phys_addr = CPHYSADDR((unsigned int)dma_buf); + REG_DMAC_DMACR(dma_chan/HALF_DMA_NUM) = 0; + REG_DMAC_DCCSR(dma_chan) = 0; + REG_DMAC_DRSR(dma_chan) = DMAC_DRSR_RS_TSSIIN; + REG_DMAC_DSAR(dma_chan) = dma_src_phys_addr; + REG_DMAC_DTAR(dma_chan) = dma_dst_phys_addr; + REG_DMAC_DTCR(dma_chan) = size / 32; + REG_DMAC_DCMD(dma_chan) = 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(dma_chan/HALF_DMA_NUM) = DMAC_DMACR_DMAE; /* global DMA enable bit */ + return 0; +} + +static int jz_open(struct inode * inode, struct file * filp) +{ + try_module_get(THIS_MODULE); + + __tssi_soft_reset(); + __intc_mask_irq(TSSI_IRQ); + tssi_config_filting(); + + return 0; +} + +static int jz_release(struct inode * inode, struct file * filp) +{ + __intc_mask_irq(TSSI_IRQ); + module_put(THIS_MODULE); + return 0; +} + +static int jz_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + jz_char_dev_t *adev = (jz_char_dev_t *)file->private_data; + struct jz_tssi_t* tssi = (struct jz_tssi_t*)adev->private; + + switch (cmd) + { + case IOCTL_TSSI_ENABLE : + __intc_ack_irq(TSSI_IRQ); + __intc_unmask_irq(TSSI_IRQ); + __tssi_enable(); + print_reg(); + + break; + case IOCTL_TSSI_DISABLE : + __tssi_disable(); + + break; + case IOCTL_TSSI_SOFTRESET : + __tssi_soft_reset(); + + break; + case IOCTL_TSSI_ENFILTER : + __tssi_filter_enable(); + break; + case IOCTL_TSSI_DEFILTER : + __tssi_filter_disable(); + break; + case IOCTL_TSSI_ADDPID : //add one pid to filter + if ( tssi->pid_num < 15 ) + { + tssi_add_pid(tssi->pid_num, arg); + tssi->pid_num ++ ; + } + break; + + case IOCTL_TSSI_FLUSHPID : //set all filting pid to false + REG_TSSI_PEN = 0x0; + REG_TSSI_PID0 = 0x0; + REG_TSSI_PID1 = 0x0; + REG_TSSI_PID2 = 0x0; + REG_TSSI_PID3 = 0x0; + REG_TSSI_PID4 = 0x0; + REG_TSSI_PID5 = 0x0; + REG_TSSI_PID6 = 0x0; + REG_TSSI_PID7 = 0x0; + break; + + case IOCTL_TSSI_INIT_DMA: + tssi_dma_reinit(tssi->dma_chan, tssi->cur_buf->front->buf, MPEG2_TS_PACHAGE_SIZE); + break; + case IOCTL_TSSI_DISABLE_DMA: + REG_DMAC_DCCSR(tssi->dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + break; + } + + return 0; +} + +static struct file_operations tssi_fops = { + owner: THIS_MODULE, + read: jz_read, + poll: NULL, + fasync: NULL, + ioctl: jz_ioctl, + open: jz_open, + release: jz_release, +}; + +static int __init jztssi_init_module(void) +{ + int retval; + struct jz_tssi_t *tssi = &jz_tssi_g; + + __cpm_start_tssi(); + __cpm_start_dmac(); + tssi_buf_init( &jz_tssi_ring_g ); + tssi->cur_buf = &jz_tssi_ring_g; + tssi->pid_num = 0; + retval = request_irq(TSSI_IRQ, tssi_interrupt, IRQF_DISABLED, TSSI_NAME, &jz_tssi_g); + + if (retval) { + printk("unable to get IRQ %d",TSSI_IRQ); + return retval; + } + + tssi->dma_chan = jz_request_dma(DMA_ID_TSSI, "tssi", tssi_dma_irq, + IRQF_DISABLED, &jz_tssi_g); + if ( tssi->dma_chan < 0 ) + { + printk("MPEG2-TS request irq fail! \n"); + return -1; + } + + jz_register_chrdev(TSSI_MINOR, TSSI_NAME, &tssi_fops, &jz_tssi_g); + + printk("Jz MPEG2-TS interface driver registered %x %d\n",&jz_tssi_g,tssi->dma_chan); + return 0; +} + +static void jztssi_cleanup_module(void) +{ + free_irq(TSSI_IRQ,0); + jz_free_dma(jz_tssi_g.dma_chan); + tssi_free_buf( &jz_tssi_ring_g ); + jz_unregister_chrdev(TSSI_MINOR, TSSI_NAME); +} + +module_init(jztssi_init_module); +module_exit(jztssi_cleanup_module); diff --git a/drivers/char/jzchar/jz_tssi.h b/drivers/char/jzchar/jz_tssi.h new file mode 100644 index 0000000..7ac424d --- /dev/null +++ b/drivers/char/jzchar/jz_tssi.h @@ -0,0 +1,76 @@ +#ifndef __JZ_TSSI_H__ +#define __JZ_TSSI_H__ + +/* + * IOCTL commands + */ +#define IOCTL_TSSI_ENABLE 0x01 +#define IOCTL_TSSI_DISABLE 0x02 +#define IOCTL_TSSI_SOFTRESET 0x03 +#define IOCTL_TSSI_ENFILTER 0x04 +#define IOCTL_TSSI_DEFILTER 0x05 +#define IOCTL_TSSI_ADDPID 0x06 +#define IOCTL_TSSI_FLUSHPID 0x07 +#define IOCTL_TSSI_INIT_DMA 0x08 +#define IOCTL_TSSI_DISABLE_DMA 0x09 + +#if 0 +#define IOCTL_TSSI_SET_CFG 0x06 +#define IOCTL_TSSI_GET_CFG 0x07 +#define IOCTL_TSSI_ENIRQ_TRIG 0x08 +#define IOCTL_TSSI_DEIRQ_TRIG 0x09 +#define IOCTL_TSSI_ENIRQ_OVRN 0x0a +#define IOCTL_TSSI_DEIRQ_OVRN 0x0b +#define IOCTL_TSSI_ENPID0 0x0c +#define IOCTL_TSSI_DEPID0 0x0d +#define IOCTL_TSSI_ENPIDN 0x0e +#define IOCTL_TSSI_DEPIDN 0x0f +#define IOCTL_TSSI_SETPIDN 0x10 +#define IOCTL_TSSI_SET_TRIG 0x11 +#endif + +#define MAX_PID_NUM 15 +#define MPEG2_TS_PACHAGE_SIZE 19200 + +struct jz_tssi_cfg_t +{ + unsigned char wordorder; + unsigned char byteorder; + unsigned char dataploa; + unsigned char use0; + unsigned char clkch; + unsigned char mode; + unsigned char clkpola; + unsigned char frmpola; + unsigned char strpola; + unsigned char failpola; + unsigned char trignum; + + unsigned short pid; + unsigned char pid_index; //0 to 15 +}; + +struct jz_tssi_buf +{ + unsigned int *buf; + unsigned int pos; + unsigned int index; + struct jz_tssi_buf *next; +}; + +struct jz_tssi_buf_ring_t +{ + struct jz_tssi_buf *front; + struct jz_tssi_buf *rear; + unsigned int fu_num; +}; + +struct jz_tssi_t +{ + struct jz_tssi_cfg_t cur_config; + struct jz_tssi_buf_ring_t *cur_buf; + struct semaphore tssi_sem; + int dma_chan, pid_num; +}; + +#endif /* __JZ_TSSI_H__ */ diff --git a/drivers/char/jzchar/jzchars.c b/drivers/char/jzchar/jzchars.c new file mode 100644 index 0000000..1a1b944 --- /dev/null +++ b/drivers/char/jzchar/jzchars.c @@ -0,0 +1,158 @@ +/* + * linux/drivers/char/jzchar/jzchars.c + * + * JzSOC char device family common layer. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "jzchars.h" + +LIST_HEAD(jz_char_devs); + +int jz_register_chrdev(unsigned char minor, const char *name, + struct file_operations *fops, void *private) +{ + struct list_head *p; + jz_char_dev_t *new; + list_for_each(p, &jz_char_devs) { + jz_char_dev_t *dev = (jz_char_dev_t *)p; + if (minor == dev->dev_minor) + return -EBUSY; + } + new = (jz_char_dev_t *)kmalloc(sizeof(jz_char_dev_t), GFP_KERNEL); + new->dev_minor = minor; + new->name = (char *)name; + new->fops = fops; + new->private = private; + list_add_tail((struct list_head *)new, &jz_char_devs); + return 0; +} + +int jz_unregister_chrdev(unsigned char minor, const char *name) +{ + struct list_head *p; + jz_char_dev_t *dev = NULL; + list_for_each(p, &jz_char_devs) { + jz_char_dev_t *one = (jz_char_dev_t *)p; + if (minor == one->dev_minor) { + dev = one; + break; + } + } + if (dev == NULL) + return -EINVAL; + list_del((struct list_head *)dev); + kfree(dev); + return 0; +} + +static ssize_t jz_char_read(struct file *, char *, size_t, loff_t *); +static ssize_t jz_char_write(struct file *, const char *, size_t, loff_t *); +static int jz_char_open(struct inode *, struct file *); +static int jz_char_release(struct inode *, struct file *); +static int jz_char_ioctl(struct inode *, struct file *, + unsigned int, unsigned long); + +static struct file_operations jz_char_fops = +{ + read: jz_char_read, + write: jz_char_write, + ioctl: jz_char_ioctl, + open: jz_char_open, + release: jz_char_release +}; + +static int __init jz_char_family_init(void) +{ + printk("JzSOC: char device family.\n"); + return register_chrdev(JZ_CHAR_MAJOR, "JzChar", &jz_char_fops); +} + +static void __exit jz_char_family_exit(void) +{ + printk("JzSOC: exit char device family.\n"); + unregister_chrdev(JZ_CHAR_MAJOR, "JzChar"); +} + +module_init(jz_char_family_init); +module_exit(jz_char_family_exit); + +static int jz_char_open(struct inode *inode, struct file *filp) +{ + jz_char_dev_t *dev = NULL; + unsigned int minor = iminor(inode); //minor extend to 20bit! + struct list_head *p; + list_for_each(p, &jz_char_devs) { + jz_char_dev_t *one = (jz_char_dev_t *)p; + if (one->dev_minor == minor) { + dev = one; + filp->private_data = dev; + return dev->fops->open(inode, filp); + } + } + printk("JzChar: No such device\n"); + return -EINVAL; +} + +static int jz_char_release(struct inode *inode, struct file *filp) +{ + jz_char_dev_t *dev = (jz_char_dev_t *)filp->private_data; + if (dev->fops->release) + return dev->fops->release(inode, filp); + return 0; +} + +static int jz_char_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + jz_char_dev_t *dev = (jz_char_dev_t *)filp->private_data; + if (dev->fops->ioctl) + return dev->fops->ioctl(inode, filp, cmd, arg); + return 0; +} + +static ssize_t jz_char_read(struct file *filp, char *buf, + size_t count, loff_t *ppos) +{ + jz_char_dev_t *dev = (jz_char_dev_t *)filp->private_data; + if (dev->fops->read) + return dev->fops->read(filp, buf, count, ppos); + return 0; +} + +static ssize_t jz_char_write(struct file *filp, const char *buf, + size_t count, loff_t *ppos) +{ + jz_char_dev_t *dev = (jz_char_dev_t *)filp->private_data; + if (dev->fops->write) + return dev->fops->write(filp, buf, count, ppos); + return 0; +} + +EXPORT_SYMBOL(jz_register_chrdev); +EXPORT_SYMBOL(jz_unregister_chrdev); diff --git a/drivers/char/jzchar/jzchars.h b/drivers/char/jzchar/jzchars.h new file mode 100644 index 0000000..a76f2ae --- /dev/null +++ b/drivers/char/jzchar/jzchars.h @@ -0,0 +1,47 @@ +#ifndef __JZ_CHARS_H__ +#define __JZ_CHARS_H__ + +#include +#include + +#define JZ_CHAR_MAJOR 238 + +#define UPRT_MINOR 0 // Micro printer +#define CIM_MINOR 1 // Camera interface module +#define TPANEL_MINOR 2 // Touchpanel +#define KEYPAD_MINOR 3 // Keypad +#define MEMCARD_MINOR 4 // Memory card +#define MAGCARD_MINOR 5 // Magcard +#define VFD_MINOR 6 // VFD +#define POWERFAIL_MINOR 7 // Powerfail +#define EJTAG_MINOR 8 // EJTAG emulation +#define REMR0_MINOR 9 // Remote output receive 0 +#define REMR1_MINOR 10 // Remote output receive 1 +#define USPI_MINOR 11 // Ultra-speed SPI device +#define SADC_MINOR 12 // SAR-ADC +#define SLCD_MINOR 13 // Smart LCD + +// 32 to 47 are reserved for SCC +#define SCC_MINOR 32 +// 48 to 63 are reserved for Camera sensor +#define SENSOR_MINOR 48 +// 64 to 71 are for EEPROM +#define EEPROM_MINOR_BASE 64 +// 72 for OWI +#define OW_MINOR 72 +// 73 for TCSM_MINOR +#define TCSM_MINOR 73 + +typedef struct { + struct list_head list; + char *name; + struct file_operations *fops; + void *private; + unsigned short dev_minor; +} jz_char_dev_t; + +extern int jz_register_chrdev(unsigned char minor, const char *name, + struct file_operations *fops, void * private); +extern int jz_unregister_chrdev(unsigned char minor, const char *name); + +#endif /* __JZ_CHARS_H__ */ diff --git a/drivers/char/jzchar/poweroff.c b/drivers/char/jzchar/poweroff.c new file mode 100644 index 0000000..a488f5f --- /dev/null +++ b/drivers/char/jzchar/poweroff.c @@ -0,0 +1,396 @@ +/* + * linux/drivers/char/jzchar/poweroff.c + * + * Power off handling. + * + * 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 as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "jzchars.h" + +MODULE_AUTHOR("Jianli Wei "); +MODULE_DESCRIPTION("Poweroff handling"); +MODULE_LICENSE("GPL"); + +#undef DEBUG +//#define DEBUG +#ifdef DEBUG +#define dprintk(x...) printk(x) +#else +#define dprintk(x...) +#endif + +//#define USE_SUSPEND_HOTPLUG + +#ifdef CONFIG_SOC_JZ4730 +#define GPIO_PW_I 97 +#define GPIO_PW_O 66 +#define POWEROFF_PIN_DOWN 1 +#define SET_POWEROFF_PIN_AS_IRQ __gpio_as_irq_rise_edge(POWEROFF_PIN) +#define DO_SHUTDOWN_SYSTEM __gpio_clear_pin(GPIO_PW_O) +#define DO_SUSPEND jz_pm_suspend() + +#define GPIO_DISP_OFF_N 93 +#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_close_backlight() \ +do { \ + __lcd_set_backlight_level(0); \ +} while (0) +#endif + +#ifdef CONFIG_SOC_JZ4740 +#define GPIO_PW_I 125 +#define POWEROFF_PIN_DOWN 0 +#define SET_POWEROFF_PIN_AS_IRQ __gpio_as_irq_fall_edge(POWEROFF_PIN) +#define DO_SHUTDOWN_SYSTEM jz_pm_hibernate() +#define DO_SUSPEND { \ + jz_pm_sleep();\ + suspend_flag = 0;\ + SET_POWEROFF_PIN_AS_IRQ;\ + } + +#define GPIO_DISP_OFF_N 118 +#define GPIO_PWM 123 +#define __lcd_close_backlight() \ +do { \ +__gpio_as_output(GPIO_PWM); \ +__gpio_clear_pin(GPIO_PWM); \ +} while (0) +#endif + +#ifdef CONFIG_SOC_JZ4750 +#define GPIO_PW_I GPIO_WAKEUP +#define POWEROFF_PIN_DOWN 0 +#define SET_POWEROFF_PIN_AS_IRQ __gpio_as_irq_fall_edge(POWEROFF_PIN) +#define DO_SHUTDOWN_SYSTEM jz_pm_hibernate() +#define DO_SUSPEND { \ + jz_pm_sleep();\ + suspend_flag = 0;\ + SET_POWEROFF_PIN_AS_IRQ;\ + } +#endif + +#ifdef CONFIG_SOC_JZ4750D +#define GPIO_PW_I GPIO_WAKEUP +#define POWEROFF_PIN_DOWN 0 +#define SET_POWEROFF_PIN_AS_IRQ __gpio_as_irq_fall_edge(POWEROFF_PIN) +#define DO_SHUTDOWN_SYSTEM jz_pm_hibernate() +#define DO_SUSPEND { \ + jz_pm_sleep();\ + suspend_flag = 0;\ + SET_POWEROFF_PIN_AS_IRQ;\ + } +#endif + + +#define POWEROFF_PIN GPIO_PW_I +#define POWEROFF_IRQ (IRQ_GPIO_0 + POWEROFF_PIN) + +#define POWEROFF_PERIOD 1000 /* unit: ms */ +#define POWEROFF_DELAY 100 /* unit: ms */ + +static struct timer_list poweroff_timer; +static struct timer_list poweroff_delaytimer; +static struct work_struct suspend_work; + +static int poweroff_flag = 0; +static int suspend_flag = 0; +static int num_seconds = 0; + +#ifdef CONFIG_JZ_UDC_HOTPLUG +extern int jz_udc_active; +#endif + +extern void jz_pm_suspend(void); +extern int jz_pm_hibernate(void); +extern int jz_pm_sleep(void); + +static void poweroff_timer_routine(unsigned long dummy) +{ + if (__gpio_get_pin(POWEROFF_PIN) == POWEROFF_PIN_DOWN) { + if (++num_seconds > 3) + { + printk("\nShutdown system now ..\n"); + +#ifndef USE_SUSPEND_HOTPLUG + /* Turn off LCD to inform user that the system is shutting down. + * But the information of shutting down system will be shown + * by userspace program if hotplug is used. + */ + __lcd_close_backlight(); +#endif + + /* + * Wait until the power key is up, or the system will reset with + * power key down after entering hibernate. + */ + while(__gpio_get_pin(POWEROFF_PIN)==POWEROFF_PIN_DOWN); + + poweroff_flag = 1; + schedule_work(&suspend_work); /* inform user to poweroff */ + } + else { + del_timer(&poweroff_timer); + init_timer(&poweroff_timer); + poweroff_timer.expires = jiffies + POWEROFF_PERIOD/10; + poweroff_timer.data = 0; + poweroff_timer.function = poweroff_timer_routine; + add_timer(&poweroff_timer); + } + } + else + { + printk("\nSuspend system now ..\n"); + num_seconds = 0; + suspend_flag = 1; + poweroff_flag = 0; + schedule_work(&suspend_work); /* we are entering suspend */ + } +} + +static void poweroff_delaytimer_routine(unsigned long dummy) +{ + __gpio_as_input(POWEROFF_PIN); + if (__gpio_get_pin(POWEROFF_PIN)==POWEROFF_PIN_DOWN) { + if (suspend_flag) { + suspend_flag = 0; + del_timer(&poweroff_delaytimer); + SET_POWEROFF_PIN_AS_IRQ; + __gpio_unmask_irq(POWEROFF_PIN); + return; + } + del_timer(&poweroff_delaytimer); + del_timer(&poweroff_timer); + init_timer(&poweroff_timer); + poweroff_timer.expires = jiffies + POWEROFF_PERIOD/100; + poweroff_timer.data = 0; + poweroff_timer.function = poweroff_timer_routine; + add_timer(&poweroff_timer); + } + else { + del_timer(&poweroff_delaytimer); + SET_POWEROFF_PIN_AS_IRQ; + __gpio_unmask_irq(POWEROFF_PIN); + + printk("This is a dummy key\n"); + } +} + +/* + * Poweroff pin interrupt handler + */ +static irqreturn_t poweroff_irq(int irq, void *dev_id) +{ + __gpio_ack_irq(POWEROFF_PIN); + __gpio_mask_irq(POWEROFF_PIN); + __gpio_as_input(POWEROFF_PIN); +#ifdef CONFIG_JZ_UDC_HOTPLUG + if (__gpio_get_pin(POWEROFF_PIN)==POWEROFF_PIN_DOWN && jz_udc_active == 0){ +#else + if (__gpio_get_pin(POWEROFF_PIN)==POWEROFF_PIN_DOWN){ +#endif + del_timer(&poweroff_delaytimer); + init_timer(&poweroff_delaytimer); + poweroff_delaytimer.expires = jiffies + POWEROFF_DELAY/10; + poweroff_delaytimer.data = 0; + poweroff_delaytimer.function = poweroff_delaytimer_routine; + add_timer(&poweroff_delaytimer); + } + else { + +/* + * If it reaches here without jz_udc_active == 0, then it indicates POWEROFF_PIN was + * changed to WAKEUP key in pm.c for hand is not able to rise up so quickly, so the + * irq handler entered because of WAKEUP key not POWEROFF_PIN. + */ + +#ifdef CONFIG_JZ_UDC_HOTPLUG + if (jz_udc_active == 1) + printk("\nUSB is working; Operation is denied\n"); +#endif + SET_POWEROFF_PIN_AS_IRQ; + __gpio_unmask_irq(POWEROFF_PIN); + } + + return IRQ_HANDLED; +} + +#ifdef CONFIG_PM + +static struct pm_dev *poweroff_dev; + +static int poweroff_suspend(int *poweroff , int state) +{ + suspend_flag = 1; + poweroff_flag = 0; + + return 0; +} + +static int poweroff_resume(int *poweroff) +{ + suspend_flag = 0; + SET_POWEROFF_PIN_AS_IRQ; + __gpio_unmask_irq(POWEROFF_PIN); + + return 0; +} + +static int poweroff_pm_callback(struct pm_dev *pm_dev, pm_request_t rqst, void *data) +{ + int ret; + int *poweroff_info = pm_dev->data; + + if (!poweroff_info) + return -EINVAL; + + switch (rqst) { + case PM_SUSPEND: + ret = poweroff_suspend(poweroff_info, (int)data); + break; + case PM_RESUME: + ret = poweroff_resume(poweroff_info); + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +#endif /* CONFIG_PM */ + +#ifdef USE_SUSPEND_HOTPLUG +static void run_sbin_hotplug(int state) +{ + int i; + char *argv[3], *envp[8]; + char media[64], slotnum[16]; + if (!uevent_helper[0]) + return; + + i = 0; + argv[i++] = uevent_helper; + //argv[i++] = "home/lhhuang/hotplug"; + + if ( poweroff_flag == 1 ) + argv[i++] = "poweroff"; + else + argv[i++] = "suspend"; + + argv[i] = 0; + + /* minimal command environment */ + i = 0; + envp[i++] = "HOME=/"; + envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + + /* other stuff we want to pass to /sbin/hotplug */ + sprintf(slotnum, "SLOT=0"); + + if ( poweroff_flag == 1 ) + sprintf(media, "MEDIA=poweroff"); + else + sprintf(media, "MEDIA=suspend"); + + envp[i++] = slotnum; + envp[i++] = media; + + if (state) + envp[i++] = "ACTION=enter"; + else + envp[i++] = "ACTION=exit"; + + envp[i] = 0; + + dprintk("SUSPEND: hotplug path=%s state=%d\n", argv[0], state); + + SET_POWEROFF_PIN_AS_IRQ; + __gpio_unmask_irq(POWEROFF_PIN); /* set it because call hotplug with call_usermodehelper() \ + might failed, especially when using nfsroot */ + + call_usermodehelper (argv [0], argv, envp, -1); +} +#endif + +static void suspend_handler(struct work_struct *work) +{ +#ifdef USE_SUSPEND_HOTPLUG + int state = 1; + run_sbin_hotplug(state); +#else + if (poweroff_flag) { + dprintk("DO_SHUTDOWN_SYSTEM\n"); + DO_SHUTDOWN_SYSTEM; + } else { + dprintk("DO_SUSPEND\n"); + DO_SUSPEND; + } +#endif +} + +static int __init poweroff_init(void) +{ + int retval; + + retval = request_irq(POWEROFF_IRQ, poweroff_irq, + IRQF_DISABLED, "poweroff", NULL); + + SET_POWEROFF_PIN_AS_IRQ; + + if (retval) { + printk("Could not get poweroff irq %d\n", POWEROFF_IRQ); + return retval; + } + +#ifdef CONFIG_PM + poweroff_dev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, poweroff_pm_callback); + if (poweroff_dev) { + poweroff_dev->data = &poweroff_dev; + } +#endif + + INIT_WORK(&suspend_work, suspend_handler); + + return 0; +} + +static void __exit poweroff_exit(void) +{ + free_irq(POWEROFF_IRQ, NULL); +} + +module_init(poweroff_init); +module_exit(poweroff_exit); + diff --git a/drivers/char/jzchar/sadc.c b/drivers/char/jzchar/sadc.c new file mode 100644 index 0000000..dc43404 --- /dev/null +++ b/drivers/char/jzchar/sadc.c @@ -0,0 +1,580 @@ +/* + * linux/drivers/char/jzchar/sadc.c + * + * SAR-ADC driver for JZ4740. + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "jzchars.h" +#include "jz_ts.h" + +MODULE_AUTHOR("Jianli Wei"); +MODULE_DESCRIPTION("JZ4740 SADC driver"); +MODULE_LICENSE("GPL"); + +#define SADC_NAME "sadc" +static DECLARE_WAIT_QUEUE_HEAD (sadc_wait_queue); + +struct sadc_device { + int mode; + int dma_chan; + char *ts_buf; + char *pbat_buf; +}; + +static struct sadc_device *sadc_dev; + +static int samples = 3; /* we sample 3 every time */ +static int first_time = 0; +static unsigned long last_x, last_y, last_p; + +typedef struct datasource { + u16 xbuf; + u16 ybuf; + u16 zbuf; +}datasource_t; + +static datasource_t data_s; +static unsigned int p; +static unsigned int old_x, old_y; +extern unsigned int (*codec_read_battery)(void); + +/* + * set adc clock to 12MHz/div. A/D works at freq between 500KHz to 6MHz. + */ +static void sadc_init_clock(int div) +{ + if (div < 2) div = 2; + if (div > 23) div = 23; +#if defined(CONFIG_SOC_JZ4740) + REG_SADC_CFG &= ~SADC_CFG_CLKDIV_MASK; + REG_SADC_CFG |= (div - 1) << SADC_CFG_CLKDIV_BIT; +#endif +#if defined(CONFIG_SOC_JZ4750) + REG_SADC_ADCLK &= ~SADC_ADCLK_CLKDIV_MASK; + REG_SADC_ADCLK |= (div - 1) << SADC_ADCLK_CLKDIV_BIT; + REG_SADC_ADCLK &= ~SADC_ADCLK_CLKDIV_BIT; + REG_SADC_ADCLK |= 39 << SADC_ADCLK_CLKDIV_10_BIT; /* if div ==3,here is 39 */ +#endif +} + +void start_sadcin(void) +{ + REG_SADC_CTRL &= ~SADC_CTRL_SRDYM; /* enable interrupt */ + REG_SADC_ENA |= SADC_ENA_SADCINEN; +} + +void start_pbat_adc(void) +{ + REG_SADC_CFG |= SADC_CFG_PBAT_HIGH ; /* full baterry voltage >= 2.5V */ +// REG_SADC_CFG |= SADC_CFG_PBAT_LOW; /* full baterry voltage < 2.5V */ + + REG_SADC_ENA |= SADC_ENA_PBATEN; /* Enable pbat adc */ +} + +void start_ts_adc(void) +{ + REG_SADC_SAMETIME = 10; /* about 0.1 ms,you can change it */ + REG_SADC_WAITTIME = 2; /* about 0.02 ms,you can change it */ + + REG_SADC_CFG &= ~(SADC_CFG_TS_DMA | SADC_CFG_XYZ_MASK | SADC_CFG_SNUM_MASK); + REG_SADC_CFG |= (SADC_CFG_EXIN | SADC_CFG_XYZ | SADC_CFG_SNUM_3); + REG_SADC_CTRL |= (SADC_CTRL_TSRDYM|SADC_CTRL_PBATRDYM|SADC_CTRL_PENUM |SADC_CTRL_SRDYM); + REG_SADC_CTRL &= ~SADC_CTRL_PENDM; + REG_SADC_ENA |= SADC_ENA_TSEN; +} + +static int jz4740_adc_read(struct jz_ts_t *ts) +{ + struct datasource *ds = &data_s; + u32 xybuf,z; + + if (!(REG_SADC_STATE & SADC_STATE_TSRDY)) { + /* sleep */ + REG_SADC_CTRL &= ~SADC_CTRL_TSRDYM; + ts->sleeping = 1; + sleep_on(&sadc_wait_queue); + } + ts->sleeping = 0; + + xybuf = REG_SADC_TSDAT; + ds->xbuf = (xybuf>>16) & 0x0fff; + ds->ybuf = (xybuf)& 0x0fff; + z = REG_SADC_TSDAT; + ds->zbuf = z& 0x0fff; + REG_SADC_STATE &= ~SADC_STATE_TSRDY; + return 0; +} + +/*------------------------------------------------------------ + * Read the battery voltage + */ +unsigned int jz4740_read_battery(void) +{ + unsigned int v; + unsigned int timeout = 0x3ff; + u16 pbat; + + if(!(REG_SADC_STATE & SADC_STATE_PBATRDY) ==1) + start_pbat_adc(); + + while(!(REG_SADC_STATE & SADC_STATE_PBATRDY) && --timeout) + ; + + pbat = REG_SADC_BATDAT; + v = pbat & 0x0fff; + REG_SADC_STATE = SADC_STATE_PBATRDY; + return v; +} + +/*------------------ Calibrate samples -------------------*/ + +#define DIFF(a,b) (((a)>(b))?((a)-(b)):((b)-(a))) +#define MIN(a,b) (((a)<(b))?(a):(b)) + +#if 0 +#define XM 36 /* XM and YM may be changed for your screen */ +#define YM 20 +static int calibrate_samples(void *xbuf, void *ybuf, void *pbuf, int count) +{ + unsigned long usd0,usd1,usd2; + int xMaxError = XM,yMaxError = YM; + int x_valid = 0,y_valid = 0,valid = 0; + unsigned long x_cal = 0, y_cal = 0, p_cal = 0; + unsigned long *xp = (unsigned long *)xbuf; + unsigned long *yp = (unsigned long *)ybuf; + unsigned long *pp = (unsigned long *)pbuf; + + usd0 = (xp[0] > xp[1]) ? (xp[0] - xp[1]) : (xp[1] - xp[0]); + usd1 = (xp[1] > xp[2]) ? (xp[1] - xp[2]) : (xp[2] - xp[1]); + usd2 = (xp[2] > xp[0]) ? (xp[2] - xp[0]) : (xp[0] - xp[2]); + + if ( usd0 < usd1) + x_cal = xp[0] + ((usd2 < usd0) ? xp[2] : xp[1]); + else + x_cal= xp[2] + ((usd2 < usd1) ? xp[0] : xp[1]); + x_cal >>= 1; + + if ( (usd0 < xMaxError) && (usd1 < xMaxError) && (usd2 < xMaxError) ) + x_valid = 1; + + usd0 = (yp[0] > yp[1]) ? (yp[0] - yp[1]) : (yp[1] - yp[0]); + usd1 = (yp[1] > yp[2]) ? (yp[1] - yp[2]) : (yp[2] - yp[1]); + usd2 = (yp[2] > yp[0]) ? (yp[2] - yp[0]) : (yp[0] - yp[2]); + + if ( usd0 < usd1) + y_cal = yp[0] + ((usd2 < usd0) ? yp[2] : yp[1]); + else + y_cal = yp[2] + ((usd2 < usd1) ? yp[0] : yp[1]); + + y_cal >>= 1; + + if ( (usd0 < yMaxError) && (usd1 < yMaxError) && (usd2 < yMaxError) ) + y_valid = 1; + + if( x_valid && y_valid) + valid = 1; + + usd0 = (pp[0] > pp[1]) ? (pp[0] - pp[1]) : (pp[1] - pp[0]); + usd1 = (pp[1] > pp[2]) ? (pp[1] - pp[2]) : (pp[2] - pp[1]); + usd2 = (pp[2] > pp[0]) ? (pp[2] - pp[0]) : (pp[0] - pp[2]); + + if ( usd0 < usd1) + p_cal = pp[0] + ((usd2 < usd0) ? pp[2] : pp[1]); + else + p_cal= pp[2] + ((usd2 < usd1) ? pp[0] : pp[1]); + + p_cal >>= 1; + + if (first_time) { + first_time = 0; + last_x = x_cal; + last_y = y_cal; + last_p = p_cal; + } + else{ + if ((DIFF(x_cal, last_x) > 50) || + (DIFF(y_cal, last_y) > 50)) + valid = 0; + else + valid = 1; + } + *xp = last_x = x_cal; + *yp = last_y = y_cal; + *pp = last_p = p_cal; + + return valid; +} +#endif + +static int calibrate_samples(void *xbuf, void *ybuf, void *pbuf, int count) +{ + unsigned long *xp = (unsigned long *)xbuf; + unsigned long *yp = (unsigned long *)ybuf; + unsigned long *pp = (unsigned long *)pbuf; + unsigned long x_cal = 0, y_cal = 0, p_cal = 0; + int i; + int valid = 1; + + /* calculate the average of the rest */ + for (i = 0; i < count; i++) { + x_cal += xp[i]; + y_cal += yp[i]; + p_cal += pp[i]; + } + x_cal /= count; + y_cal /= count; + p_cal /= count; + + if (first_time) { + first_time = 0; + last_x = x_cal; + last_y = y_cal; + last_p = p_cal; + } + else { + if ((DIFF(x_cal, last_x) > 50) || + (DIFF(y_cal, last_y) > 50)) + valid = 0; + else + valid = 1; + } + + *xp = last_x = x_cal; + *yp = last_y = y_cal; + *pp = last_p = p_cal; + + return valid; +} + +#define TSMAXX 945 +#define TSMAXY 830 +#define TSMINX 90 +#define TSMINY 105 + +#define SCREEN_X 480 +#define SCREEN_Y 272 + +static unsigned long transform_to_screen_x(struct jz_ts_t *ts, unsigned long x ) +{ + + if (ts->minx) + { + if (x < ts->minx) x = ts->minx; + if (x > ts->maxx) x = ts->maxx; + + return (x - ts->minx) * SCREEN_X / (ts->maxx - ts->minx); + } + else + { + if (x < TSMINX) x = TSMINX; + if (x > TSMAXX) x = TSMAXX; + + return (x - TSMINX) * SCREEN_X / (TSMAXX - TSMINX); + } +} + +static unsigned long transform_to_screen_y(struct jz_ts_t *ts, unsigned long y) +{ + if (ts->minx) + { + if (y < ts->minx) y = ts->miny; + if (y > ts->maxx) y = ts->maxy; + + return (y - ts->miny) * SCREEN_Y / (ts->maxy - ts->miny); + } + else + { + if (y < TSMINX) y = TSMINY; + if (y > TSMAXX) y = TSMAXY; + + return (y - TSMINY) * SCREEN_Y / (TSMAXY - TSMINY); + } +} + +/* + * File operations + */ +static int sadc_open(struct inode *inode, struct file *filp); +static int sadc_release(struct inode *inode, struct file *filp); +static ssize_t sadc_read(struct file *filp, char *buf, size_t size, loff_t *l); +static ssize_t sadc_write(struct file *filp, const char *buf, size_t size, loff_t *l); +static int sadc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); + +static struct file_operations sadc_fops = +{ + open: sadc_open, + release: sadc_release, + read: sadc_read, + write: sadc_write, + ioctl: sadc_ioctl +}; + +static int sadc_open(struct inode *inode, struct file *filp) +{ + try_module_get(THIS_MODULE); + return 0; +} + +static int sadc_release(struct inode *inode, struct file *filp) +{ + module_put(THIS_MODULE); + return 0; +} + +static ssize_t sadc_read(struct file *filp, char *buf, size_t size, loff_t *l) +{ + return size; +} + +static ssize_t sadc_write(struct file *filp, const char *buf, size_t size, loff_t *l) +{ + return size; +} + +static int sadc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + default: + printk("Not supported command: 0x%x\n", cmd); + return -EINVAL; + break; + } + return 0; +} + +/*------------------ Common routines -------------------*/ + +void ts_enable_irq(void) +{ + REG_SADC_CTRL &= ~SADC_CTRL_PENDM; +} + +void ts_disable_irq(void) +{ + REG_SADC_CTRL |= (SADC_CTRL_PENDM | SADC_CTRL_PENUM); +} + +void ts_free_irq(struct jz_ts_t *ts) +{ + free_irq(ts->pendown_irq, ts); +} + +void ts_data_ready(void) +{ + REG_SADC_CTRL |= SADC_CTRL_TSRDYM; + wake_up(&sadc_wait_queue); +} + +/* + * Interrupt handler + */ +void ts_irq_callback(void) +{ + u32 state; + + state = REG_SADC_STATE; + if (!(REG_SADC_CTRL&SADC_CTRL_PENDM)&&(REG_SADC_STATE & SADC_STATE_PEND)) { + REG_SADC_STATE = SADC_STATE_PEND; + REG_SADC_STATE = SADC_STATE_PENU; + REG_SADC_CTRL |= SADC_CTRL_PENDM; + REG_SADC_CTRL &= ~SADC_CTRL_PENUM; + p = 1; + } + + if (!(REG_SADC_CTRL&SADC_CTRL_PENUM)&&(REG_SADC_STATE & SADC_STATE_PENU)) { + REG_SADC_STATE = SADC_STATE_PENU; + REG_SADC_CTRL |= SADC_CTRL_PENUM; + REG_SADC_CTRL &= ~SADC_CTRL_PENDM; + p = 0; + } + + first_time = 1; // first time to acquire sample +} + +int PenIsDown(void) +{ + return p; +} + +int ts_request_irq(u32 *irq, + irqreturn_t (*handler)(int, void *), + const char *devname, + void *dev_id) +{ + int ret; + + /* return the irq number */ + *irq = IRQ_SADC; + ts_disable_irq(); + /* interrupt mode */ + ret = request_irq(IRQ_SADC, handler, IRQF_DISABLED, + devname, dev_id); + if(ret) + printk("failed irq \n"); + + start_ts_adc(); + return ret; +} + +/* + * Acquire Raw pen coodinate data and compute touch screen + * pressure resistance. Hold spinlock when calling. + */ +int AcquireEvent(struct jz_ts_t *ts, struct ts_event *event) +{ + unsigned int x_raw[8], y_raw[8], p_raw[8]; + int valid, i; + unsigned int avl_x, avl_y, diff_x, diff_y; + struct datasource *ds = &data_s; + avl_x = avl_y = 0; + + for (i = 0; i < samples; i++) { + if (jz4740_adc_read(ts)) { + return 0; + } + + x_raw[i] = ds->ybuf; + y_raw[i] = ds->xbuf; + p_raw[i] = ds->zbuf; + avl_x += x_raw[i]; + avl_y += y_raw[i]; +#if 0 + printk("x_raw=%x y_raw=%x z_raw=%x\n",x_raw[i],y_raw[i],p_raw[i]); +#endif + } + + avl_x /= samples; + avl_y /= samples; +#define MAX_DELTA 20 + valid = 1; + + for (i = 1; i < samples; i++) + { + if ((100 * DIFF(x_raw[i],x_raw[i-1])/MIN(x_raw[i],x_raw[i-1])) > MAX_DELTA) { + valid = 0; + break; + } + + if ((100 * DIFF(y_raw[i],y_raw[i-1])/MIN(y_raw[i],y_raw[i-1])) > MAX_DELTA) { + valid = 0; + break; + } + + if ((100 * DIFF(p_raw[i],p_raw[i-1])/MIN(p_raw[i],p_raw[i-1])) > MAX_DELTA) { + valid = 0; + break; + } + } + + if (valid) { + if (ts->first_read) { + ts->first_read = 0; + old_x = avl_x; + old_y = avl_y; + } + diff_x = DIFF(old_x, avl_x); + diff_y = DIFF(old_y, avl_y); + if (diff_x < 100 && diff_y < 100) { + old_x = avl_x; + old_y = avl_y; + } else + valid = 0; + } + if (valid) { + valid = calibrate_samples(x_raw, y_raw, p_raw, samples); + } + + if (valid) { + unsigned int x_scr, y_scr; + + if(ts->filter) { + x_scr = transform_to_screen_x(ts, x_raw[0]); + y_scr = transform_to_screen_y(ts, y_raw[0]); + + if (ts->prints) + printk("x_raw=%d y_raw=%d x_transform=%d y_transform=%d\n", x_raw[0], y_raw[0], x_scr, y_scr); + } + else { + x_scr = x_raw[0]; + y_scr = y_raw[0]; + + if (ts->prints) + printk("x_raw=%d y_raw=%d \n", x_raw[0], y_raw[0]); + } + + event->x = x_scr; + event->y = y_scr; + event->pressure = (u16)p_raw[0]; + event->status = PENDOWN; + return 1; + } + return 0; +} + +/* + * Module init and exit + */ +static int __init sadc_init(void) +{ + struct sadc_device *dev; + int ret; + + /* allocate device */ + dev = kmalloc(sizeof(struct sadc_device), GFP_KERNEL); + if (!dev) return -ENOMEM; + + sadc_dev = dev; + ret = jz_register_chrdev(SADC_MINOR, SADC_NAME, &sadc_fops, dev); + if (ret < 0) { + kfree(dev); + return ret; + } + + codec_read_battery = jz4740_read_battery; + sadc_init_clock(3); + + printk("JZ4740 SAR-ADC driver registered\n"); + return 0; +} + +static void __exit sadc_exit(void) +{ + struct sadc_device *dev = sadc_dev; + + free_irq(IRQ_SADC, dev); + jz_unregister_chrdev(SADC_MINOR, SADC_NAME); + kfree(dev); +} + +module_init(sadc_init); +module_exit(sadc_exit); diff --git a/drivers/char/jzchar/sensor.c b/drivers/char/jzchar/sensor.c new file mode 100644 index 0000000..8123203 --- /dev/null +++ b/drivers/char/jzchar/sensor.c @@ -0,0 +1,182 @@ +/* + * linux/drivers/char/jzchar/sensor.c + * + * Common CMOS Camera Sensor Driver + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "jzchars.h" + +MODULE_AUTHOR("Jianli Wei"); +MODULE_DESCRIPTION("Common CMOS Camera Sensor Driver"); +MODULE_LICENSE("GPL"); + +/* + * ioctl commands + */ +#define IOCTL_SET_ADDR 0 /* set i2c address */ +#define IOCTL_SET_CLK 1 /* set i2c clock */ +#define IOCTL_WRITE_REG 2 /* write sensor register */ +#define IOCTL_READ_REG 3 /* read sensor register */ + +/* + * i2c related + */ +static unsigned int i2c_addr = 0x42; +static unsigned int i2c_clk = 100000; + +static void write_reg(u8 reg, u8 val) +{ + i2c_open(); + i2c_setclk(i2c_clk); + i2c_write((i2c_addr >> 1), &val, reg, 1); + i2c_close(); +} + +static u8 read_reg(u8 reg) +{ + u8 val; + + i2c_open(); + i2c_setclk(i2c_clk); + i2c_read((i2c_addr >> 1), &val, reg, 1); + i2c_close(); + return val; +} + +/* + * fops routines + */ + +static int sensor_open(struct inode *inode, struct file *filp); +static int sensor_release(struct inode *inode, struct file *filp); +static ssize_t sensor_read(struct file *filp, char *buf, size_t size, loff_t *l); +static ssize_t sensor_write(struct file *filp, const char *buf, size_t size, loff_t *l); +static int sensor_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); + +static struct file_operations sensor_fops = +{ + open: sensor_open, + release: sensor_release, + read: sensor_read, + write: sensor_write, + ioctl: sensor_ioctl, +}; + +static int sensor_open(struct inode *inode, struct file *filp) +{ + try_module_get(THIS_MODULE); + return 0; +} + +static int sensor_release(struct inode *inode, struct file *filp) +{ + module_put(THIS_MODULE); + return 0; +} + +static ssize_t sensor_read(struct file *filp, char *buf, size_t size, loff_t *l) +{ + printk("sensor: read is not implemented\n"); + return -1; +} + +static ssize_t sensor_write(struct file *filp, const char *buf, size_t size, loff_t *l) +{ + printk("sensor: write is not implemented\n"); + return -1; +} + +static int sensor_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + + switch (cmd) { + case IOCTL_SET_ADDR: + if (copy_from_user(&i2c_addr, (void *)arg, 4)) + return -EFAULT; + break; + case IOCTL_SET_CLK: + if (copy_from_user(&i2c_clk, (void *)arg, 4)) + return -EFAULT; + break; + case IOCTL_WRITE_REG: + { + u8 regval[2]; + + if (copy_from_user(regval, (void *)arg, 2)) + return -EFAULT; + + write_reg(regval[0], regval[1]); + break; + } + case IOCTL_READ_REG: + { + u8 reg, val; + + if (copy_from_user(®, (void *)arg, 1)) + return -EFAULT; + + val = read_reg(reg); + + if (copy_to_user((void *)(arg + 1), &val, 1)) + return -EFAULT; + break; + } + default: + printk("Not supported command: 0x%x\n", cmd); + return -EINVAL; + break; + } + return ret; +} + +/* + * Module init and exit + */ + +static int __init sensor_init(void) +{ + int ret; + + ret = jz_register_chrdev(SENSOR_MINOR, "sensor", &sensor_fops, NULL); + if (ret < 0) { + return ret; + } + + printk("Ingenic CMOS camera sensor driver registered\n"); + + return 0; +} + +static void __exit sensor_exit(void) +{ + jz_unregister_chrdev(SENSOR_MINOR, "sensor"); +} + +module_init(sensor_init); +module_exit(sensor_exit); diff --git a/drivers/char/jzchar/tcsm.c b/drivers/char/jzchar/tcsm.c new file mode 100644 index 0000000..1941d14 --- /dev/null +++ b/drivers/char/jzchar/tcsm.c @@ -0,0 +1,123 @@ +/* + * linux/drivers/char/jzchar/tcsm.c + * + * Virtual device driver with tricky appoach to manage TCSM + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "jzchars.h" + +MODULE_AUTHOR("Jianli Wei"); +MODULE_DESCRIPTION("Virtual Driver of TCSM"); +MODULE_LICENSE("GPL"); + +/* + * fops routines + */ + +static int tcsm_open(struct inode *inode, struct file *filp); +static int tcsm_release(struct inode *inode, struct file *filp); +static ssize_t tcsm_read(struct file *filp, char *buf, size_t size, loff_t *l); +static ssize_t tcsm_write(struct file *filp, const char *buf, size_t size, loff_t *l); +static int tcsm_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); + +static struct file_operations tcsm_fops = +{ + open: tcsm_open, + release: tcsm_release, + read: tcsm_read, + write: tcsm_write, + ioctl: tcsm_ioctl, +}; + +static int tcsm_open(struct inode *inode, struct file *filp) +{ + struct pt_regs *info = task_pt_regs(current); + + info->cp0_status &= ~0x10;// clear UM bit + info->cp0_status |= 0x08000000; // set RP bit a tricky + + return 0; +} + +static int tcsm_release(struct inode *inode, struct file *filp) +{ + struct pt_regs *info = task_pt_regs(current); + + info->cp0_status |= 0x10;// set UM bit + info->cp0_status &= ~0x08000000; // clear RP bit a tricky + + return 0; +} + +static ssize_t tcsm_read(struct file *filp, char *buf, size_t size, loff_t *l) +{ + printk("tcsm: read is not implemented\n"); + return -1; +} + +static ssize_t tcsm_write(struct file *filp, const char *buf, size_t size, loff_t *l) +{ + printk("tcsm: write is not implemented\n"); + return -1; +} + +static int tcsm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + printk("tcsm: ioctl is not implemented\n"); + return ret; +} + +/* + * Module init and exit + */ + +static int __init tcsm_init(void) +{ + int ret; + + ret = jz_register_chrdev(TCSM_MINOR, "tcsm", &tcsm_fops, NULL); + if (ret < 0) { + return ret; + } + + printk("Virtual Driver of TCSM registered\n"); + return 0; +} + +static void __exit tcsm_exit(void) +{ + jz_unregister_chrdev(TCSM_MINOR, "tcsm"); +} + +module_init(tcsm_init); +module_exit(tcsm_exit); diff --git a/drivers/char/jzchar/ucb1400.c b/drivers/char/jzchar/ucb1400.c new file mode 100644 index 0000000..d5d06f7 --- /dev/null +++ b/drivers/char/jzchar/ucb1400.c @@ -0,0 +1,585 @@ +/* + * ucb1400.c + * + * Touch screen driver interface to the UCB1400 codec. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "jz_ts.h" +#include "ucb1400.h" + +#ifndef GPIO_TS_PENIRQ +#define GPIO_TS_PENIRQ 68 +#endif + +#define TS_PIN GPIO_TS_PENIRQ +#define TS_IRQ (IRQ_GPIO_0 + TS_PIN) + +static int samples = 5; /* we sample 5 every time, and throw away the max and min cases, then use the average of the other 3 samples */ +static int first_time = 0; +static unsigned long last_x, last_y, last_p; + +static int adcsync = 0; + +static unsigned int ucb_id = 0; +static struct ucb1400 *ucb; + +extern struct ac97_codec * find_ac97_codec(void); + +extern unsigned int (*codec_read_battery)(void); + +/*------------------ UCB1400 routines ------------------*/ + +static inline void ucb1400_reg_write(unsigned char reg, unsigned short val) +{ + struct ac97_codec *codec = find_ac97_codec(); + if (!codec) + return; + codec->codec_write(codec, reg, val); +} + +static inline unsigned int ucb1400_reg_read(unsigned char reg) +{ + struct ac97_codec *codec = find_ac97_codec(); + if (!codec) + return 0; + return codec->codec_read(codec, reg); +} + +static void ucb1400_adc_enable(void) +{ + down(&ucb->adc_sem); + + ucb->adc_cr |= UCB_ADC_ENA; + ucb1400_reg_write(UCB_ADC_CR, ucb->adc_cr); +} + +static unsigned int ucb1400_adc_read(int adc_channel, int sync) +{ + unsigned int val, timeout = 10000; + + if (sync) + adc_channel |= UCB_ADC_SYNC_ENA; + + ucb1400_reg_write(UCB_ADC_CR, ucb->adc_cr | adc_channel); + ucb1400_reg_write(UCB_ADC_CR, ucb->adc_cr | adc_channel | UCB_ADC_START); + + for (;;) { + val = ucb1400_reg_read(UCB_ADC_DATA); + if (val & UCB_ADC_DAT_VAL) + break; + if (--timeout == 0) + break; + udelay(1); + } + + return UCB_ADC_DAT(val); +} + +static void ucb1400_adc_disable(void) +{ + ucb->adc_cr &= ~UCB_ADC_ENA; + ucb1400_reg_write(UCB_ADC_CR, ucb->adc_cr); + + up(&ucb->adc_sem); +} + +static void ucb1400_enable_irq(unsigned int idx, int edges) +{ + unsigned long flags; + + if (idx < 16) { + spin_lock_irqsave(&ucb->lock, flags); + + /* This prevents spurious interrupts on the UCB1400 */ + ucb1400_reg_write(UCB_IE_CLEAR, 1 << idx); + ucb1400_reg_write(UCB_IE_CLEAR, 0); + + if (edges & UCB_RISING) { + ucb->irq_ris_enbl |= 1 << idx; + ucb1400_reg_write(UCB_IE_RIS, ucb->irq_ris_enbl); + } + if (edges & UCB_FALLING) { + ucb->irq_fal_enbl |= 1 << idx; + ucb1400_reg_write(UCB_IE_FAL, ucb->irq_fal_enbl); + } + spin_unlock_irqrestore(&ucb->lock, flags); + } +} + +static void ucb1400_disable_irq(unsigned int idx, int edges) +{ + unsigned long flags; + + if (idx < 16) { + spin_lock_irqsave(&ucb->lock, flags); + + if (edges & UCB_RISING) { + ucb->irq_ris_enbl &= ~(1 << idx); + ucb1400_reg_write(UCB_IE_RIS, ucb->irq_ris_enbl); + } + if (edges & UCB_FALLING) { + ucb->irq_fal_enbl &= ~(1 << idx); + ucb1400_reg_write(UCB_IE_FAL, ucb->irq_fal_enbl); + } + spin_unlock_irqrestore(&ucb->lock, flags); + } +} + +/* + * Switch to interrupt mode. + */ +static inline void ucb1400_ts_mode_int(void) +{ + if (!ucb_id) { + ucb_id = ucb1400_reg_read(UCB_ID); + } + + if (ucb_id == UCB_ID_1400_BUGGY) + ucb1400_reg_write(UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_INT); + else + ucb1400_reg_write(UCB_TS_CR, + UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_INT); +} + +/* + * Switch to pressure mode, and read pressure. We don't need to wait + * here, since both plates are being driven. + */ +static inline unsigned int ucb1400_ts_read_pressure(void) +{ + ucb1400_reg_write(UCB_TS_CR, + UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + + return ucb1400_adc_read(UCB_ADC_INP_TSPY, adcsync); +} + +/* + * Switch to X position mode and measure Y plate. We switch the plate + * configuration in pressure mode, then switch to position mode. This + * gives a faster response time. Even so, we need to wait about 55us + * for things to stabilise. + */ +static inline unsigned int ucb1400_ts_read_xpos(void) +{ + ucb1400_reg_write(UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1400_reg_write(UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1400_reg_write(UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + + udelay(55); + + return ucb1400_adc_read(UCB_ADC_INP_TSPY, adcsync); +} + +/* + * Switch to Y position mode and measure X plate. We switch the plate + * configuration in pressure mode, then switch to position mode. This + * gives a faster response time. Even so, we need to wait about 55us + * for things to stabilise. + */ +static inline unsigned int ucb1400_ts_read_ypos(void) +{ + ucb1400_reg_write(UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1400_reg_write(UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1400_reg_write(UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + + udelay(55); + + return ucb1400_adc_read(UCB_ADC_INP_TSPX, adcsync); +} + +/*------------------------------------------------------------ + * Read the battery voltage + */ + +unsigned int ucb1400_read_battery(void) +{ + unsigned int v; + + ucb1400_adc_enable(); + + // read twice to reduce fault value + v = ucb1400_adc_read(UCB_ADC_INP_AD0, adcsync); + v = ucb1400_adc_read(UCB_ADC_INP_AD0, adcsync); + + ucb1400_adc_disable(); + +// printk("ucb1400_read_battery v=%d\n", v); + + return v; +} + +/*------------------ Calibrate samples -------------------*/ + +#define DIFF(a,b) ((a>b)?(a-b):(b-a)) + +static int calibrate_samples(void *xbuf, void *ybuf, void *pbuf, int count) +{ + unsigned long *xp = (unsigned long *)xbuf; + unsigned long *yp = (unsigned long *)ybuf; + unsigned long *pp = (unsigned long *)pbuf; + unsigned long x_cal = 0, y_cal = 0, p_cal = 0, tmp; + int ignored, i, j; + int valid = 0; + + /* throw away the max cases */ + tmp = xp[0]; + ignored = 0; + for (i = 1; i < count; i++) { + if (xp[i] > tmp) { + tmp = xp[i]; + ignored = i; + } + } + j = 0; + for (i = 0; i < count; i++) { + if (i == ignored) + continue; + xp[j++] = xp[i]; + } + + tmp = yp[0]; + ignored = 0; + for (i = 1; i < count; i++) { + if (yp[i] > tmp) { + tmp = yp[i]; + ignored = i; + } + } + j = 0; + for (i = 0; i < count; i++) { + if (i == ignored) + continue; + yp[j++] = yp[i]; + } + + tmp = pp[0]; + ignored = 0; + for (i = 1; i < count; i++) { + if (pp[i] > tmp) { + tmp = pp[i]; + ignored = i; + } + } + j = 0; + for (i = 0; i < count; i++) { + if (i == ignored) + continue; + pp[j++] = pp[i]; + } + + /* throw away the min cases */ + + count -= 1; // decrement by 1 + + tmp = xp[0]; + ignored = 0; + for (i = 1; i < count; i++) { + if (xp[i] < tmp) { + tmp = xp[i]; + ignored = i; + } + } + j = 0; + for (i = 0; i < count; i++) { + if (i == ignored) + continue; + xp[j++] = xp[i]; + } + + tmp = yp[0]; + ignored = 0; + for (i = 1; i < count; i++) { + if (yp[i] < tmp) { + tmp = yp[i]; + ignored = i; + } + } + j = 0; + for (i = 0; i < count; i++) { + if (i == ignored) + continue; + yp[j++] = yp[i]; + } + + tmp = pp[0]; + ignored = 0; + for (i = 1; i < count; i++) { + if (pp[i] < tmp) { + tmp = pp[i]; + ignored = i; + } + } + j = 0; + for (i = 0; i < count; i++) { + if (i == ignored) + continue; + pp[j++] = pp[i]; + } + + count -= 1; // decrement by 1 + + /* calculate the average of the rest */ + for (i = 0; i < count; i++) { + x_cal += xp[i]; + y_cal += yp[i]; + p_cal += pp[i]; + } + x_cal /= count; + y_cal /= count; + p_cal /= count; + + if (first_time) { + first_time = 0; + last_x = x_cal; + last_y = y_cal; + last_p = p_cal; + valid = 1; + } + else { + if ((DIFF(x_cal, last_x) > 50) || + (DIFF(y_cal, last_y) > 50)) + valid = 0; + else + valid = 1; + } + +// printk("x_cal=%d y_cal=%d p_cal=%d valid=%d\n", x_cal, y_cal, p_cal, valid); + + if (valid) { + *xp = last_x = x_cal; + *yp = last_y = y_cal; + *pp = last_p = p_cal; + } + + return valid; +} + + +#define TSMAXX 945 +#define TSMAXY 830 +#define TSMINX 90 +#define TSMINY 105 + +#define SCREEN_X 480 +#define SCREEN_Y 272 + +static unsigned long transform_to_screen_x(struct jz_ts_t *ts, unsigned long x ) +{ + + if (ts->minx) + { + if (x < ts->minx) x = ts->minx; + if (x > ts->maxx) x = ts->maxx; + + return (x - ts->minx) * SCREEN_X / (ts->maxx - ts->minx); + } + else + { + if (x < TSMINX) x = TSMINX; + if (x > TSMAXX) x = TSMAXX; + + return (x - TSMINX) * SCREEN_X / (TSMAXX - TSMINX); + } +} + +static unsigned long transform_to_screen_y(struct jz_ts_t *ts, unsigned long y) +{ + if (ts->minx) + { + if (y < ts->minx) y = ts->miny; + if (y > ts->maxx) y = ts->maxy; + + return (y - ts->miny) * SCREEN_Y / (ts->maxy - ts->miny); + } + else + { + if (y < TSMINX) y = TSMINY; + if (y > TSMAXX) y = TSMAXY; + + return (y - TSMINY) * SCREEN_Y / (TSMAXY - TSMINY); + } +} + +/*------------------ Common routines -------------------*/ + +void ts_enable_irq(void) +{ + /* interrupt mode */ + ucb1400_ts_mode_int(); + ucb1400_enable_irq(UCB_IRQ_TSPX, UCB_FALLING); + + enable_irq(TS_IRQ); +} + +void ts_disable_irq(void) +{ + ucb1400_disable_irq(UCB_IRQ_TSPX, UCB_FALLING); + disable_irq(TS_IRQ); +} + +int ts_request_irq(u32 *irq, + void (*handler)(int, void *, struct pt_regs *), + const char *devname, + void *dev_id) +{ + int retval; + + /* return the irq number */ + *irq = TS_IRQ; + + /* interrupt mode */ + ucb1400_ts_mode_int(); + ucb1400_enable_irq(UCB_IRQ_TSPX, UCB_FALLING); + + /* enable gpio irq */ + __gpio_as_irq_rise_edge(TS_PIN); + + /* register irq handler */ + retval = request_irq(TS_IRQ, handler, SA_INTERRUPT, devname, dev_id); + + return retval; +} + +void ts_free_irq(struct jz_ts_t *ts) +{ + free_irq(ts->pendown_irq, ts); +} + +void ts_irq_callback(void) +{ + /* clear interrupt status */ + ucb1400_reg_write(UCB_IE_CLEAR, ucb1400_reg_read(UCB_IE_STATUS)); + __gpio_ack_irq(TS_PIN); + + first_time = 1; // first time to acquire sample +} + +int PenIsDown(void) +{ + unsigned int p; + + ucb1400_adc_enable(); + p = ucb1400_ts_read_pressure(); + ucb1400_adc_disable(); + + return (p > 100) ? 1 : 0; +} + +/* + * Acquire Raw pen coodinate data and compute touch screen + * pressure resistance. Hold spinlock when calling. + */ +int AcquireEvent(struct jz_ts_t *ts, struct ts_event *event) +{ + unsigned int x_raw[8], y_raw[8], p_raw[8]; + int valid, i; + + /* Enable ADC */ + ucb1400_adc_enable(); + + for (i = 0; i < samples; i++) { + x_raw[i] = ucb1400_ts_read_xpos(); + } + for (i = 0; i < samples; i++) { + y_raw[i] = ucb1400_ts_read_ypos(); + } + for (i = 0; i < samples; i++) { + p_raw[i] = ucb1400_ts_read_pressure(); + } + + /* Disable ADC */ + ucb1400_adc_disable(); + + valid = calibrate_samples(x_raw, y_raw, p_raw, samples); + + if (valid) { + unsigned int x_scr, y_scr; + + if(ts->filter) { + x_scr = transform_to_screen_x(ts, x_raw[0]); + y_scr = transform_to_screen_y(ts, y_raw[0]); + + if (ts->prints) + printk("x_raw=%d y_raw=%d x_transform=%d y_transform=%d\n", x_raw[0], y_raw[0], x_scr, y_scr); + } + else { + x_scr = x_raw[0]; + y_scr = y_raw[0]; + + if (ts->prints) + printk("x_raw=%d y_raw=%d \n", x_raw[0], y_raw[0]); + } + + event->x = x_scr; + event->y = y_scr; + event->pressure = (u16)p_raw[0]; + event->status = PENDOWN; + return 1; + } + return 0; +} + +/* + * Module init and exit + */ + +int __init ucb1400_init(void) +{ + ucb = kmalloc(sizeof(struct ucb1400), GFP_KERNEL); + if (!ucb) return -ENOMEM; + + memset(ucb, 0, sizeof(struct ucb1400)); + + codec_read_battery = ucb1400_read_battery; + + spin_lock_init(&ucb->lock); + sema_init(&ucb->adc_sem, 1); + + return 0; +} + +void ucb1400_cleanup(void) +{ + kfree(ucb); +} + +module_init(ucb1400_init); +module_exit(ucb1400_cleanup); diff --git a/drivers/char/jzchar/ucb1400.h b/drivers/char/jzchar/ucb1400.h new file mode 100644 index 0000000..318c004 --- /dev/null +++ b/drivers/char/jzchar/ucb1400.h @@ -0,0 +1,113 @@ +#ifndef __UCB1400_H__ +#define __UCB1400_H__ + +/* ucb1400 aclink register mappings */ + +#define UCB_IO_DATA 0x5a +#define UCB_IO_DIR 0x5c +#define UCB_IE_RIS 0x5e +#define UCB_IE_FAL 0x60 +#define UCB_IE_STATUS 0x62 +#define UCB_IE_CLEAR 0x62 +#define UCB_TS_CR 0x64 +#define UCB_ADC_CR 0x66 +#define UCB_ADC_DATA 0x68 +#define UCB_ID 0x7e /* 7c is mfr id, 7e part id (from aclink spec) */ + +#define UCB_ADC_DAT(x) ((x) & 0x3ff) + +/* register bits */ + +#define UCB_IO_0 (1 << 0) +#define UCB_IO_1 (1 << 1) +#define UCB_IO_2 (1 << 2) +#define UCB_IO_3 (1 << 3) +#define UCB_IO_4 (1 << 4) +#define UCB_IO_5 (1 << 5) +#define UCB_IO_6 (1 << 6) +#define UCB_IO_7 (1 << 7) +#define UCB_IO_8 (1 << 8) +#define UCB_IO_9 (1 << 9) + +#define UCB_IE_ADC (1 << 11) +#define UCB_IE_TSPX (1 << 12) +#define UCB_IE_TSMX (1 << 13) +#define UCB_IE_TCLIP (1 << 14) +#define UCB_IE_ACLIP (1 << 15) + +#define UCB_IRQ_TSPX 12 + +#define UCB_TC_A_LOOP (1 << 7) /* UCB1200 */ +#define UCB_TC_A_AMPL (1 << 7) /* UCB1300 */ + +#define UCB_TC_B_VOICE_ENA (1 << 3) +#define UCB_TC_B_CLIP (1 << 4) +#define UCB_TC_B_ATT (1 << 6) +#define UCB_TC_B_SIDE_ENA (1 << 11) +#define UCB_TC_B_MUTE (1 << 13) +#define UCB_TC_B_IN_ENA (1 << 14) +#define UCB_TC_B_OUT_ENA (1 << 15) + +#define UCB_AC_B_LOOP (1 << 8) +#define UCB_AC_B_MUTE (1 << 13) +#define UCB_AC_B_IN_ENA (1 << 14) +#define UCB_AC_B_OUT_ENA (1 << 15) + +#define UCB_TS_CR_TSMX_POW (1 << 0) +#define UCB_TS_CR_TSPX_POW (1 << 1) +#define UCB_TS_CR_TSMY_POW (1 << 2) +#define UCB_TS_CR_TSPY_POW (1 << 3) +#define UCB_TS_CR_TSMX_GND (1 << 4) +#define UCB_TS_CR_TSPX_GND (1 << 5) +#define UCB_TS_CR_TSMY_GND (1 << 6) +#define UCB_TS_CR_TSPY_GND (1 << 7) +#define UCB_TS_CR_MODE_INT (0 << 8) +#define UCB_TS_CR_MODE_PRES (1 << 8) +#define UCB_TS_CR_MODE_POS (2 << 8) +#define UCB_TS_CR_BIAS_ENA (1 << 11) +#define UCB_TS_CR_TSPX_LOW (1 << 12) +#define UCB_TS_CR_TSMX_LOW (1 << 13) + +#define UCB_ADC_SYNC_ENA (1 << 0) +#define UCB_ADC_VREFBYP_CON (1 << 1) +#define UCB_ADC_INP_TSPX (0 << 2) +#define UCB_ADC_INP_TSMX (1 << 2) +#define UCB_ADC_INP_TSPY (2 << 2) +#define UCB_ADC_INP_TSMY (3 << 2) +#define UCB_ADC_INP_AD0 (4 << 2) +#define UCB_ADC_INP_AD1 (5 << 2) +#define UCB_ADC_INP_AD2 (6 << 2) +#define UCB_ADC_INP_AD3 (7 << 2) +#define UCB_ADC_EXT_REF (1 << 5) +#define UCB_ADC_START (1 << 7) +#define UCB_ADC_ENA (1 << 15) + +#define UCB_ADC_DAT_VAL (1 << 15) + +#define UCB_ID_1200 0x1004 +#define UCB_ID_1300 0x1005 +#define UCB_ID_1400 0x4304 +#define UCB_ID_1400_BUGGY 0x4303 /* fake ID */ + +#define UCB_MODE_DYN_VFLAG_ENA (1 << 12) +#define UCB_MODE_AUD_OFF_CAN (1 << 13) + +/* + * Which edges of the IRQ do you want to control today? + */ +#define UCB_RISING (1 << 0) +#define UCB_FALLING (1 << 1) + +/* Device data structure */ + +struct ucb1400 { + spinlock_t lock; + struct pm_dev *pmdev; + struct semaphore adc_sem; + u16 adc_cr; + u16 irq_fal_enbl; + u16 irq_ris_enbl; + int irq_enabled; +}; + +#endif /* __UCB1400_H__ */ diff --git a/drivers/char/jzchar/udc_hotplug.c b/drivers/char/jzchar/udc_hotplug.c new file mode 100644 index 0000000..1c3e576 --- /dev/null +++ b/drivers/char/jzchar/udc_hotplug.c @@ -0,0 +1,451 @@ +/* + * linux/drivers/char/jzchar/udc_hotplug.c + * + * New UDC hotplug driver. + * + * Copyright (C) 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 as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "jzchars.h" + +#ifndef GPIO_UDC_HOTPLUG +#define GPIO_UDC_HOTPLUG 86 +#endif + +#define UDC_HOTPLUG_PIN GPIO_UDC_HOTPLUG +#define UDC_HOTPLUG_IRQ (IRQ_GPIO_0 + UDC_HOTPLUG_PIN) + +#define dprintk(x,...) + +//simple meaning define +#define NOT_CONNECT 0 +#define YES_CONNECT 1 +#define MAX_GPIO_TIME 50 + +#define EVENT_USB_ADD 1 +#define EVENT_USB_REMOVE 2 +#define EVENT_POWER_ADD 3 +#define EVENT_POWER_REMOVE 4 +#define EVENT_POWER_TO_USB 5 +#define EVENT_USB_SUSPEND_POWER 6 + +struct udc_pnp_stat +{ + char cable_stat, old_cable_stat; + char protl_stat, old_protl_stat; + char object_stat1; + char object_stat2; +}; + +static struct udc_pnp_stat cur_pnp_stat; + +static struct file_operations cable_fops = { + owner: THIS_MODULE, +}; + +static struct miscdevice cable_dev= +{ + 231, + "udc_cable", + &cable_fops +}; + +static struct file_operations power_fops = { + owner: THIS_MODULE, +}; + +static struct miscdevice power_dev= +{ + 232, + "power_cable", + &power_fops +}; + +int jz_udc_active = 0; /* 0: Have no actions; 1: Have actions */ + +static int udc_pin_level; +static int udc_old_state; +static int udc_pin_time; + +static struct timer_list udc_long_timer, udc_gpio_timer; + +/* Kernel thread to deliver event to user space */ +static struct task_struct *kudcd_task; + +static void udc_gpio_timer_routine(unsigned long data) +{ + wake_up_process(kudcd_task); +} + +static void udc_long_timer_routine(unsigned long data) +{ + dprintk("udc_timer\n"); + if (jz_udc_active) + udc_old_state = 1; + if (!jz_udc_active && udc_old_state) //udc irq timeout! do suspend + { + dprintk("udc suspend!\n"); + udc_old_state = 0; + cur_pnp_stat.protl_stat = NOT_CONNECT; + del_timer(&udc_long_timer); + wake_up_process(kudcd_task); + return; + } + jz_udc_active = 0; + udc_long_timer.expires = jiffies + 3 * HZ; /* about 3 s */ + add_timer(&udc_long_timer); +} + +static int udc_get_pnp_stat(void) +{ + udc_pin_level = __gpio_get_pin(UDC_HOTPLUG_PIN); + udc_pin_time = 1; + + init_timer(&udc_gpio_timer); + del_timer(&udc_gpio_timer); + udc_gpio_timer.function = udc_gpio_timer_routine; + udc_gpio_timer.expires = jiffies + 1; /* about 10 ms */ + add_timer(&udc_gpio_timer); + + while(1) + { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + + if (__gpio_get_pin(UDC_HOTPLUG_PIN) != udc_pin_level) + { + udc_pin_level = __gpio_get_pin(UDC_HOTPLUG_PIN); + udc_pin_time = 1; + dprintk("udc gpio detect restart! \n"); + } + + udc_pin_time ++; + if (udc_pin_time > MAX_GPIO_TIME) + break; + + del_timer(&udc_gpio_timer); + udc_gpio_timer.function = udc_gpio_timer_routine; + udc_gpio_timer.expires = jiffies + 1; /* about 10 ms */ + add_timer(&udc_gpio_timer); + } + + del_timer(&udc_gpio_timer); + if (__gpio_get_pin(UDC_HOTPLUG_PIN)) + return YES_CONNECT; + else + return NOT_CONNECT; +} + +static void udc_get_cable(void) +{ + u32 intr_usb; + + __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; + + dprintk("enable phy! %x %x %x %x %x\n", + REG8(USB_REG_POWER), + REG_CPM_SCR, + REG16(USB_REG_INTRINE), + REG16(USB_REG_INTROUTE), + REG8(USB_REG_INTRUSBE)); + + init_timer(&udc_gpio_timer); + del_timer(&udc_gpio_timer); + udc_gpio_timer.function = udc_gpio_timer_routine; + udc_gpio_timer.expires = jiffies + 11; /* about 100 ms */ + add_timer(&udc_gpio_timer); + /* Sleep a short time to see result */ + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + + del_timer(&udc_gpio_timer); + intr_usb = REG8(USB_REG_INTRUSB); + if ((intr_usb & USB_INTR_RESET) || + (intr_usb & USB_INTR_RESUME) || + (intr_usb & USB_INTR_SUSPEND)) + { + cur_pnp_stat.protl_stat = YES_CONNECT; + dprintk("cable is usb! \n"); + } + else + { + cur_pnp_stat.protl_stat = NOT_CONNECT; + dprintk("cable is power! \n"); + } + + /* 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); +} + +static void send_event_udev(int event) +{ + dprintk("Send udev message: cable=%d old=%d protl=%d old=%d \n", + cur_pnp_stat.cable_stat, + cur_pnp_stat.old_cable_stat, + cur_pnp_stat.protl_stat, + cur_pnp_stat.old_protl_stat); + + switch (event) + { + case EVENT_USB_ADD: + printk("usb cable insert! \n"); + misc_register(&cable_dev); + kobject_uevent(&cable_dev.this_device->kobj, KOBJ_ADD); + init_timer(&udc_long_timer); + del_timer(&udc_long_timer); + udc_long_timer.function = udc_long_timer_routine; + udc_long_timer.expires = jiffies + 3 * HZ; /* about 3 s */ + add_timer(&udc_long_timer); + break; + case EVENT_USB_REMOVE: + printk("usb cable remove! \n"); + kobject_uevent(&cable_dev.this_device->kobj, KOBJ_REMOVE); + misc_deregister(&cable_dev); + del_timer(&udc_long_timer); + break; + case EVENT_POWER_ADD: + printk("power cable insert! \n"); + misc_register(&power_dev); + kobject_uevent(&power_dev.this_device->kobj, KOBJ_ADD); + break; + case EVENT_POWER_REMOVE: + printk("power cable remove! \n"); + kobject_uevent(&power_dev.this_device->kobj, KOBJ_REMOVE); + misc_deregister(&power_dev); + break; + case EVENT_POWER_TO_USB: + printk("change power cable to usb! \n"); + kobject_uevent(&power_dev.this_device->kobj, KOBJ_REMOVE); + misc_deregister(&power_dev); + misc_register(&cable_dev); + kobject_uevent(&cable_dev.this_device->kobj, KOBJ_ADD); + break; + case EVENT_USB_SUSPEND_POWER: + printk("usb cable suspend! \n"); + printk("as power cable insert! \n"); + kobject_uevent(&cable_dev.this_device->kobj, KOBJ_REMOVE); + misc_deregister(&cable_dev); + misc_register(&power_dev); + kobject_uevent(&power_dev.this_device->kobj, KOBJ_ADD); + break; + }; +} + +static void udc_pnp_detect(void) +{ + if (cur_pnp_stat.cable_stat == YES_CONNECT) /* already connected! */ + { + if (udc_get_pnp_stat() == NOT_CONNECT) + { + dprintk("cable real out! \n"); + cur_pnp_stat.cable_stat = NOT_CONNECT; + cur_pnp_stat.protl_stat = NOT_CONNECT; + /* Deliver this event to user space in udev model */ + if (cur_pnp_stat.old_protl_stat) + send_event_udev(EVENT_USB_REMOVE); + else + send_event_udev(EVENT_POWER_REMOVE); + cur_pnp_stat.old_cable_stat = cur_pnp_stat.cable_stat; + cur_pnp_stat.old_protl_stat = cur_pnp_stat.protl_stat; + } + else + { + if (cur_pnp_stat.old_protl_stat != cur_pnp_stat.protl_stat) + { + send_event_udev(EVENT_USB_SUSPEND_POWER); + cur_pnp_stat.old_cable_stat = cur_pnp_stat.cable_stat; + cur_pnp_stat.old_protl_stat = cur_pnp_stat.protl_stat; + } + else //change power to cable + { +#if 0 //not support yet! + udc_get_cable(); + if (cur_pnp_stat.old_protl_stat != cur_pnp_stat.protl_stat) + send_event_udev(EVENT_POWER_TO_USB); + cur_pnp_stat.old_cable_stat = cur_pnp_stat.cable_stat; + cur_pnp_stat.old_protl_stat = cur_pnp_stat.protl_stat; +#endif + } + } + } + else + { + if (udc_get_pnp_stat() == YES_CONNECT) + { + dprintk("cable real in! \n"); + cur_pnp_stat.cable_stat = YES_CONNECT; + udc_get_cable(); + /* Deliver this event to user space in udev model */ + if (cur_pnp_stat.protl_stat) + send_event_udev(EVENT_USB_ADD); + else + send_event_udev(EVENT_POWER_ADD); + cur_pnp_stat.old_cable_stat = cur_pnp_stat.cable_stat; + cur_pnp_stat.old_protl_stat = cur_pnp_stat.protl_stat; + } + else + dprintk("cable false in! \n"); + + } +} + +static void udc_pnp_set_gpio(void) +{ + if (cur_pnp_stat.cable_stat == YES_CONNECT) + __gpio_as_irq_fall_edge(UDC_HOTPLUG_PIN); + else + __gpio_as_irq_rise_edge(UDC_HOTPLUG_PIN); + + /* clear interrupt pending status */ + __gpio_ack_irq(UDC_HOTPLUG_PIN); + /* unmask interrupt */ + __gpio_unmask_irq(UDC_HOTPLUG_PIN); +} + +static int udc_pnp_thread(void *unused) +{ + printk(KERN_NOTICE "UDC starting pnp monitor thread\n"); + + while(1) + { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + + dprintk("pnp thread wake up! \n"); + /* wake up here */ + udc_pnp_detect(); + /* Reset gpio state last */ + udc_pnp_set_gpio(); + } +} + +static irqreturn_t udc_pnp_irq(int irq, void *dev_id) +{ + printk("udc_pnp_irq----\n"); + /* clear interrupt pending status */ + __gpio_ack_irq(UDC_HOTPLUG_PIN); + /* mask interrupt */ + __gpio_mask_irq(UDC_HOTPLUG_PIN); + /* wake up pnp detect thread */ + wake_up_process(kudcd_task); + + return IRQ_HANDLED; +} + +/* + * Module init and exit + */ +static int __init udc_hotplug_init(void) +{ + int retval; + /* Init pnp stat first */ + cur_pnp_stat.cable_stat = NOT_CONNECT; + cur_pnp_stat.protl_stat = NOT_CONNECT; + cur_pnp_stat.old_cable_stat = NOT_CONNECT; + cur_pnp_stat.old_protl_stat = NOT_CONNECT; + cur_pnp_stat.object_stat1 = NOT_CONNECT; + cur_pnp_stat.object_stat2 = NOT_CONNECT; + udc_old_state = 0; + + /* create pnp thread and register IRQ */ + kudcd_task = kthread_run(udc_pnp_thread, NULL, "kudcd"); + if (IS_ERR(kudcd_task)) { + printk(KERN_ERR "jz_udc_hotplug: Failed to create system monitor thread.\n"); + return PTR_ERR(kudcd_task); + } + + retval = request_irq(UDC_HOTPLUG_IRQ, udc_pnp_irq, + IRQF_DISABLED, "udc_pnp", NULL); + if (retval) { + printk("Could not get udc hotplug irq %d\n", UDC_HOTPLUG_IRQ); + return retval; + } + + /* get current pin level */ + __gpio_disable_pull(UDC_HOTPLUG_PIN); + __gpio_as_input(UDC_HOTPLUG_PIN); + udelay(1); + udc_pin_level = __gpio_get_pin(UDC_HOTPLUG_PIN); + + if (udc_pin_level) { + dprintk("Cable already in! \n"); + /* Post a event */ + wake_up_process(kudcd_task); + } + else { + __gpio_as_irq_rise_edge(UDC_HOTPLUG_PIN); + dprintk("Cable not in! \n"); + } + + printk("JZ UDC hotplug driver registered\n"); + + return 0; +} + +static void __exit udc_hotplug_exit(void) +{ + free_irq(UDC_HOTPLUG_IRQ, NULL); +} + +module_init(udc_hotplug_init); +module_exit(udc_hotplug_exit); + +EXPORT_SYMBOL(jz_udc_active); + +MODULE_AUTHOR("Lucifer "); +MODULE_DESCRIPTION("JzSOC OnChip udc hotplug driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/jzchar/wm9712.c b/drivers/char/jzchar/wm9712.c new file mode 100644 index 0000000..de75435 --- /dev/null +++ b/drivers/char/jzchar/wm9712.c @@ -0,0 +1,334 @@ +/* + * wm9712.c + * + * Touch screen driver interface to the Wolfson WM9712 codec. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "jz_ts.h" +#include "wm9712.h" + +#define POLL_TIMES 10 + +static int samples = 1; +static int inited = 0, started = 0; + +extern struct ac97_codec * find_ac97_codec(void); +extern int PenIsDown(void); + + +static inline void wm9712_reg_write(unsigned int reg, unsigned int val) +{ + struct ac97_codec *codec = find_ac97_codec(); + if (!codec) + return; + codec->codec_write(codec, reg, val); +} + +static inline unsigned int wm9712_reg_read(unsigned int reg) +{ + struct ac97_codec *codec = find_ac97_codec(); + if (!codec) + return 0; + return codec->codec_read(codec, reg); +} + +static unsigned int wm9712_adc_read(int adc_channel) +{ + unsigned int val; + + if (!PenIsDown()) + return 0; + + val = wm9712_reg_read(DIGI_REG1); + wm9712_reg_write(DIGI_REG1, val|adc_channel|DIGI_REG1_POLL); + + for (;;) { + if (wm9712_reg_read(0x54) & (1 << 12)) { + val = wm9712_reg_read(DIGI_READBACK); + break; + } + } + + /* stop the measure */ + wm9712_reg_write(DIGI_REG1, 0); + + return (val & 0x0fff); +} + +static struct timer_list pndn_timer; +static void (*irq_handler)(int, void *, struct pt_regs *) = NULL; + +void ts_irq_callback(void) +{ +#ifdef TS_IRQ + __gpio_ack_irq(TS_IRQ); +#else +#endif +} + +void ts_enable_irq(void) +{ + if (!inited) + return; +#ifdef TS_IRQ + enable_irq(TS_IRQ); +#else + pndn_timer.expires = jiffies + HZ/POLL_TIMES; + add_timer(&pndn_timer); +#endif +} + +void ts_disable_irq(void) +{ + if (!inited) + return; +#ifdef TS_IRQ + disable_irq(TS_IRQ); +#endif +} + +#ifndef TS_IRQ +static void pndn_detect(unsigned long data) +{ + if (PenIsDown()) { + if (!started) + return; + if (irq_handler) + irq_handler(NULL, data, NULL); + } else { + pndn_timer.expires = jiffies + HZ/POLL_TIMES; + add_timer(&pndn_timer); + } +} +#endif + +void ts_free_irq(struct jz_ts_t *ts) +{ +#ifdef TS_IRQ + free_irq(ts->pendown_irq, ts); +#else + started = 0; + del_timer_sync(&pndn_timer); +#endif +} + +int ts_request_irq(u32 *irq, + void (*handler)(int, void *, struct pt_regs *), + const char *devname, + void *dev_id) +{ + /* 4wire, Ip=400uA, Rpu=64Kohm/64, wake-up on pendown without + * reset, meassure on pen down. Do not use wait mode. + */ + started = 1; + if (!inited) { + wm9712_reg_write(DIGI_REG2, + DIGI_REG2_WIRE_4 | + DIGI_REG2_PIL_200uA | + (31 << DIGI_REG2_RPU_BIT) | + DIGI_REG2_PRP_ALLON | + DIGI_REG2_RPR_NWOR); + /* Polling mode and no measurement */ + wm9712_reg_write(DIGI_REG1, 0); + } + +#ifdef TS_IRQ + /* Generate irq request on PENDOWN pin, pendown cause the level high */ + wm9712_reg_write(0x56, wm9712_reg_read(0x56) & ~(1 << 3)); + wm9712_reg_write(0x4c, wm9712_reg_read(0x4c) & ~(1 << 3)); + + *irq = TS_IRQ; + return request_irq(TS_IRQ, handler, SA_INTERRUPT, devname, dev_id); +#else + if (!inited) { + irq_handler = handler; + init_timer(&pndn_timer); + pndn_timer.function = pndn_detect; + pndn_timer.data = (unsigned long)dev_id; + pndn_timer.expires = jiffies + HZ/POLL_TIMES; + add_timer(&pndn_timer); + inited = 1; + } else { + pndn_timer.expires = jiffies + HZ/POLL_TIMES; + add_timer(&pndn_timer); + } + return 0; +#endif +} + +int PenIsDown(void) +{ + if (wm9712_reg_read(DIGI_READBACK) & DIGI_READBACK_PNDN) + return 1; + return 0; +} + +#if defined(CONFIG_MIPS_JZ4730_GPS) +#define adj_data(r1, r2, r3, s) \ +do { \ + if (r1 < 0x90) \ + r1 = 0x90; \ + if (r2 < 0xed) \ + r2 = 0xed; \ + r1 = ((r1 - 0x90) * 240) / 3354; \ + r2 = ((r2 - 0xed) * 320) / 3671; \ + if (r1 > 239) \ + r1 = 239; \ + if (r2 > 319) \ + r2 = 319; \ + \ + *s = r2; \ + *(s+1) = 239 - r1; \ + *(s+2) = z_raw; \ +} while (0) +#endif + +#ifndef adj_data +#define adj_data(r1, r2, r3, s) +#endif + +static int read_adc(unsigned int *sdata) +{ + unsigned long x_raw=0, y_raw=0, z_raw=0, t, fail = 0; + int i; + + for (i=0; i 1) { + x_raw = (x_raw + (samples>>1)) / samples; + y_raw = (y_raw + (samples>>1)) / samples; + z_raw = (z_raw + (samples>>1)) / samples; + } + + adj_data (x_raw, y_raw, z_raw, sdata); + + return 1; +} + + +#define TSMAXX 945 +#define TSMAXY 830 +#define TSMINX 90 +#define TSMINY 105 + +#define SCREEN_X 480 +#define SCREEN_Y 272 + +static unsigned long transform_to_screen_x(struct jz_ts_t *ts, unsigned long x ) +{ + + if (ts->minx) + { + if (x < ts->minx) x = ts->minx; + if (x > ts->maxx) x = ts->maxx; + + return (x - ts->minx) * SCREEN_X / (ts->maxx - ts->minx); + } + else + { + if (x < TSMINX) x = TSMINX; + if (x > TSMAXX) x = TSMAXX; + + return (x - TSMINX) * SCREEN_X / (TSMAXX - TSMINX); + } +} + +static unsigned long transform_to_screen_y(struct jz_ts_t *ts, unsigned long y) +{ + if (ts->minx) + { + if (y < ts->minx) y = ts->miny; + if (y > ts->maxx) y = ts->maxy; + + return (y - ts->miny) * SCREEN_Y / (ts->maxy - ts->miny); + } + else + { + if (y < TSMINX) y = TSMINY; + if (y > TSMAXX) y = TSMAXY; + + return (y - TSMINY) * SCREEN_Y / (TSMAXY - TSMINY); + } +} + + +/* + * Acquire Raw pen coodinate data and compute touch screen + * pressure resistance. Hold spinlock when calling. + */ +int AcquireEvent(struct jz_ts_t *ts, struct ts_event *event) +{ + unsigned int s[3]; + unsigned int x_scr, y_scr; + if (!read_adc(s)) + return 0; + if(ts->filter) { + x_scr = transform_to_screen_x(ts, s[0]); + y_scr = transform_to_screen_y(ts, s[1]); + + if (ts->prints) + printk("x_raw=%d y_raw=%d x_transform=%d y_transform=%d\n", s[0], s[1], x_scr, y_scr); } + else { + x_scr = s[0]; + y_scr = s[1]; + + if (ts->prints) + printk("x_raw=%d y_raw=%d \n", s[0], s[1]); + } + event->x = x_scr; + event->y = y_scr; + event->pressure = (u16)s[2]; + event->status = PENDOWN; + return 1; +#if 0 + do_gettimeofday(&event->stamp); +#endif +} + +int __init wm9712_init(void) +{ + return 0; +} + +void wm9712_cleanup(void) +{ +} + +module_init(wm9712_init); +module_exit(wm9712_cleanup); + diff --git a/drivers/char/jzchar/wm9712.h b/drivers/char/jzchar/wm9712.h new file mode 100644 index 0000000..7cd9e5c --- /dev/null +++ b/drivers/char/jzchar/wm9712.h @@ -0,0 +1,58 @@ +#ifndef __WM9712_H__ +#define __WM9712_H__ + +#define DIGI_REG1 0x76 +#define DIGI_REG2 0x78 +#define DIGI_READBACK 0x7A + +#define ADCSEL_BIT 12 +#define ADCSEL_MASK (7 << ADCSEL_BIT) +#define ADCSEL_NONE (0 << ADCSEL_BIT) +#define ADCSEL_XPOS (1 << ADCSEL_BIT) +#define ADCSEL_YPOS (2 << ADCSEL_BIT) +#define ADCSEL_PRESSURE (3 << ADCSEL_BIT) +#define ADCSEL_COMP1 (4 << ADCSEL_BIT) +#define ADCSEL_COMP2 (5 << ADCSEL_BIT) +#define ADCSEL_BMON (6 << ADCSEL_BIT) +#define ADCSEL_WIPER (7 << ADCSEL_BIT) + +#define DIGI_REG1_CTC (1 << 10) +#define DIGI_REG1_POLL (1 << 15) +#define DIGI_REG1_CR_BIT 8 +#define DIGI_REG1_CR_MASK (3 << DIGI_REG1_CR_BIT) +#define DIGI_REG1_COO (1 << 11) +#define DIGI_REG1_SLEN (1 << 3) +#define DIGI_REG1_SLT_BIT 0 +#define DIGI_REG1_SLT_MASK (7 << DIGI_REG1_SLT_BIT) +#define DIGI_REG1_SLT_5 (0 << DIGI_REG1_SLT_BIT) +#define DIGI_REG1_SLT_6 (1 << DIGI_REG1_SLT_BIT) +#define DIGI_REG1_SLT_7 (2 << DIGI_REG1_SLT_BIT) +#define DIGI_REG1_SLT_8 (3 << DIGI_REG1_SLT_BIT) +#define DIGI_REG1_SLT_9 (4 << DIGI_REG1_SLT_BIT) +#define DIGI_REG1_SLT_10 (5 << DIGI_REG1_SLT_BIT) +#define DIGI_REG1_SLT_11 (6 << DIGI_REG1_SLT_BIT) +#define DIGI_REG1_SLT_RES (7 << DIGI_REG1_SLT_BIT) +#define DIGI_REG1_DEL_BIT 4 +#define DIGI_REG1_DEL_MASK (0x0f << DIGI_REG1_DEL_BIT) + +#define DIGI_REG2_WIRE_5 (1 << 12) +#define DIGI_REG2_WIRE_4 (0 << 12) +#define DIGI_REG2_RPU_BIT 0 +#define DIGI_REG2_RPU_MASK (0x3f << DIGI_REG2_RPU_BIT) +#define DIGI_REG2_PIL_400uA (1 << 8) +#define DIGI_REG2_PIL_200uA (0 << 8) +#define DIGI_REG2_PRP_BIT 14 +#define DIGI_REG2_PRP_MASK (3 << DIGI_REG2_PRP_BIT) +#define DIGI_REG2_PRP_ALLOFF (0 << DIGI_REG2_PRP_BIT) +#define DIGI_REG2_PRP_WOP (1 << DIGI_REG2_PRP_BIT) +#define DIGI_REG2_PRP_NWOP (2 << DIGI_REG2_PRP_BIT) +#define DIGI_REG2_PRP_ALLON (3 << DIGI_REG2_PRP_BIT) +#define DIGI_REG2_RPR_WOR (0 << 13) +#define DIGI_REG2_RPR_NWOR (1 << 13) +#define DIGI_REG2_PDEN (1 << 11) +#define DIGI_REG2_WAIT (1 << 9) + +#define DIGI_READBACK_PNDN (1 << 15) + +#endif /* __WM9712_H__ */ + diff --git a/drivers/char/rtc_jz.c b/drivers/char/rtc_jz.c new file mode 100644 index 0000000..55da710 --- /dev/null +++ b/drivers/char/rtc_jz.c @@ -0,0 +1,503 @@ +/* + * Jz OnChip Real Time Clock interface for Linux + * + * NOTE: we need to wait rtc write ready before read or write RTC registers. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* get the user-level API */ +#include +#include + +#include "rtc_jz.h" + + +char sbin_rtc_alarm_handler_path[] = "/sbin/rtcalarm"; +//call_usermodehelper(char *path, char **argv, char **envp, int wait) +//extern int call_usermodehelper(char *path, char **argv, char **envp); + +extern spinlock_t rtc_lock; + +static int rtc_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + + +static void get_rtc_time (struct rtc_time *rtc_tm); +static int set_rtc_time (struct rtc_time *rtc_tm); +static void get_rtc_alm_time (struct rtc_time *alm_tm); +static int set_rtc_alm_time (struct rtc_time *alm_tm); + +static void set_rtc_irq_bit(int bit); +static void mask_rtc_irq_bit(int bit); + +static unsigned int rtc_status = 0; +static unsigned int epoch = 1900; + +static void get_rtc_time(struct rtc_time *rtc_tm) +{ + unsigned long lval; + struct rtc_time ltm; + + spin_lock_irq(&rtc_lock); + while ( !__rtc_write_ready() ) ; + lval = REG_RTC_RSR; + rtc_time_to_tm(lval, <m); + if(rtc_valid_tm(<m) == 0) { + /* is valid */ + rtc_tm->tm_sec = ltm.tm_sec; + rtc_tm->tm_min = ltm.tm_min; + rtc_tm->tm_hour = ltm.tm_hour; + rtc_tm->tm_mday = ltm.tm_mday; + rtc_tm->tm_wday = ltm.tm_wday; + rtc_tm->tm_mon = ltm.tm_mon; + rtc_tm->tm_year = ltm.tm_year; + } else { + printk("invlaid data / time!\n"); + } + spin_unlock_irq(&rtc_lock); +} + +static int set_rtc_time(struct rtc_time *rtc_tm) +{ + unsigned long lval; + + rtc_tm_to_time(rtc_tm, &lval); + + spin_lock_irq(&rtc_lock); + while ( !__rtc_write_ready() ) ; + REG_RTC_RSR = lval; + + spin_unlock_irq(&rtc_lock); + + return 0; + +} + +static void get_rtc_alm_time(struct rtc_time *alm_tm) +{ + unsigned long lval; + struct rtc_time altm; + + spin_lock_irq(&rtc_lock); + while ( !__rtc_write_ready() ) ; + lval = REG_RTC_RSAR; + rtc_time_to_tm(lval, &altm); + if(rtc_valid_tm(&altm) == 0) { + /* is valid */ + alm_tm->tm_sec = altm.tm_sec; + alm_tm->tm_min = altm.tm_min; + alm_tm->tm_hour = altm.tm_hour; + alm_tm->tm_mday = altm.tm_mday; + alm_tm->tm_wday = altm.tm_wday; + alm_tm->tm_mon = altm.tm_mon; + alm_tm->tm_year = altm.tm_year; + } else { + printk("invlaid data / time in Line:%d!\n",__LINE__); + } + spin_unlock_irq(&rtc_lock); +} + +static int set_rtc_alm_time(struct rtc_time *alm_tm) +{ + unsigned long lval; + + rtc_tm_to_time(alm_tm, &lval); + + spin_lock_irq(&rtc_lock); + while ( !__rtc_write_ready() ) ; + REG_RTC_RSAR = lval; + + while ( !__rtc_write_ready() ) ; /* set alarm function */ + if ( !((REG_RTC_RCR>>2) & 0x1) ) { + while ( !__rtc_write_ready() ) ; + __rtc_enable_alarm(); + } + + while ( !__rtc_write_ready() ) ; + if ( !(REG_RTC_RCR & RTC_RCR_AIE) ) { /* Enable alarm irq */ + __rtc_enable_alarm_irq(); + } + + spin_unlock_irq(&rtc_lock); + + return 0; +} + +static void get_rtc_wakeup_alarm(struct rtc_wkalrm *wkalm) +{ + int enabled, pending; + + get_rtc_alm_time(&wkalm->time); + + spin_lock_irq(&rtc_lock); + while ( !__rtc_write_ready() ) ; + enabled = (REG_RTC_HWCR & 0x1); + pending = 0; + if ( enabled ) { + if ( (u32)REG_RTC_RSAR > (u32)REG_RTC_RSR ) /* 32bit val */ + pending = 1; + } + + wkalm->enabled = enabled; + wkalm->pending = pending; + spin_unlock_irq(&rtc_lock); +} + +static int set_rtc_wakeup_alarm(struct rtc_wkalrm *wkalm) +{ + int enabled; + //int pending; + + enabled = wkalm->enabled; + //pending = wkalm->pending; /* Fix me, what's pending mean??? */ + + while ( !__rtc_write_ready() ) ; /* set wakeup alarm enable */ + if ( enabled != (REG_RTC_HWCR & 0x1) ) { + while ( !__rtc_write_ready() ) ; + REG_RTC_HWCR = (REG_RTC_HWCR & ~0x1) | enabled; + } + while ( !__rtc_write_ready() ) ; /* set alarm function */ + if ( enabled != ((REG_RTC_RCR>>2) & 0x1) ) { + while ( !__rtc_write_ready() ) ; + REG_RTC_RCR = (REG_RTC_RCR & ~(1<<2)) | (enabled<<2); + } + + if ( !enabled ) /* if disabled wkalrm, rturn. */ + { + return 0; + } + + while ( !__rtc_write_ready() ) ; + if ( !(REG_RTC_RCR & RTC_RCR_AIE) ) { /* Enable alarm irq */ + __rtc_enable_alarm_irq(); + } + + set_rtc_alm_time(&wkalm->time); + + return 0; +} + + +static void set_rtc_irq_bit( int bit ) +{ + spin_lock_irq(&rtc_lock); + + while ( !__rtc_write_ready() ) ; + REG_RTC_RCR |= (1<= 0xc0 + * means "don't care" or "match all". Only the tm_hour, + * tm_min, and tm_sec values are filled in. + */ + + get_rtc_alm_time(&wtime); + return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0; + + case RTC_ALM_SET: /* Store a time into the alarm */ + { + struct rtc_time alm_tm; + + if (copy_from_user(&alm_tm, (struct rtc_time*)arg, + sizeof(struct rtc_time))) + return -EFAULT; + if(rtc_valid_tm(&alm_tm) != 0) { + printk("invalid time set in Line:%d! \n",__LINE__); + return -EFAULT; + } + + return set_rtc_alm_time(&alm_tm); + } + case RTC_RD_TIME: /* Read the time/date from RTC */ + get_rtc_time(&wtime); + return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0; + case RTC_SET_TIME: /* Set the RTC */ + { + struct rtc_time rtc_tm; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, + sizeof(struct rtc_time))) + return -EFAULT; + if(rtc_valid_tm(&rtc_tm) != 0) { + printk("invalid time set in Line:%d! \n",__LINE__); + return -EFAULT; + } + + return set_rtc_time(&rtc_tm); + } + case RTC_EPOCH_READ: /* Read the epoch. */ + return put_user (epoch, (unsigned long *)arg); + case RTC_EPOCH_SET: /* Set the epoch. */ + /* + * There were no RTC clocks before 1900. + */ + if (arg < 1900) + return -EINVAL; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + epoch = arg; + return 0; + case RTC_WKALM_SET: /* Wake alarm set. */ + { + struct rtc_wkalrm wkalrm; + + if (copy_from_user(&wkalrm, (struct rtc_wkalrm*)arg, + sizeof(struct rtc_wkalrm))) + return -EFAULT; + return set_rtc_wakeup_alarm(&wkalrm); + } + case RTC_WKALM_RD: /* Wake alarm read. */ + { + struct rtc_wkalrm wkalrm; + get_rtc_wakeup_alarm(&wkalrm); + return copy_to_user((void *)arg, &wkalrm, sizeof(struct rtc_wkalrm)) ? -EFAULT : 0; + } + /* set power down: shut down the machine. */ + case RTC_POWER_DOWN: /* enter HIBERNATE mode */ + dprintk("Power down. Bye....\n"); + while ( !__rtc_write_ready() ) ; + REG_RTC_HCR = 0x1; + return 0; +#ifdef DEBUG + case RTC_PRINT_REG: /* Print RTC registers */ + print_rtc_registers(); + return 0; +#endif + default: + return -EINVAL; + } + return 0; +} + +/* + * We enforce only one user at a time here with the open/close. + * Also clear the previous interrupt data on an open, and clean + * up things on a close. + */ + +/* We use rtc_lock to protect against concurrent opens. So the BKL is not + * needed here. Or anywhere else in this driver. */ +static int rtc_open(struct inode *inode, struct file *file) +{ + spin_lock_irq (&rtc_lock); + + if(rtc_status) + goto out_busy; + + rtc_status = 1; + + spin_unlock_irq (&rtc_lock); + return 0; + +out_busy: + return -EBUSY; +} + +static int rtc_release(struct inode *inode, struct file *file) +{ + + rtc_status = 0; + /* No need for locking -- nobody else can do anything until this rmw is + * committed, and no timer is running. */ + return 0; +} + +/* + * The various file operations we support. + */ + +static struct file_operations rtc_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: rtc_ioctl, + open: rtc_open, + release: rtc_release, +}; + + +static void run_sbin_rtc_alarm( void ) +{ + int i; + char *argv[2], *envp[3]; + + if (!sbin_rtc_alarm_handler_path[0]) + return; + + print_dbg(": sbin_rtc_alarm_handler_path=%s\n", sbin_rtc_alarm_handler_path); + + i = 0; + argv[i++] = sbin_rtc_alarm_handler_path; + argv[i] = 0; + + /* minimal command environment */ + i = 0; + envp[i++] = "HOME=/"; + envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + + /* other stuff we want to pass to /sbin/hotplug */ + + envp[i] = 0; + + call_usermodehelper (argv [0], argv, envp, 0); +} + +static void rtc_alarm_task_handler(struct work_struct *work) +{ + run_sbin_rtc_alarm(); +} + +static struct work_struct rtc_alarm_task; + +static irqreturn_t jz_rtc_interrupt(int irq, void *dev_id) +{ + REG_RTC_HCR = 0x0; + printk("%s:%s:%d\n",__FILE__,__FUNCTION__,__LINE__); + spin_lock_irq(&rtc_lock); + + if ( __rtc_get_1Hz_flag() ) { + while ( !__rtc_write_ready() ) ; + __rtc_clear_1Hz_flag(); + dprintk("RTC 1Hz interrupt occur.\n"); + } + + if ( __rtc_get_alarm_flag() ) { /* rtc alarm interrupt */ + while ( !__rtc_write_ready() ) ; + __rtc_clear_alarm_flag(); + dprintk("RTC alarm interrupt occur.\n"); + //schedule_task( &rtc_alarm_task ); + schedule_work( &rtc_alarm_task ); + } + spin_unlock_irq(&rtc_lock); + + return IRQ_HANDLED; +} + + +#define RTC_MINOR 135 + +static struct miscdevice rtc_dev= +{ + RTC_MINOR, + "rtc", + &rtc_fops +}; + +int __init Jz_rtc_init(void) +{ + + INIT_WORK(&rtc_alarm_task, rtc_alarm_task_handler); + + /* Enabled rtc function, enable rtc alarm function */ + while ( !__rtc_write_ready() ) ; /* need we wait for WRDY??? */ + if ( !(REG_RTC_RCR & RTC_RCR_RTCE) || !(REG_RTC_RCR &RTC_RCR_AE) ) { + REG_RTC_RCR |= RTC_RCR_AE | RTC_RCR_RTCE; + } + /* clear irq flags */ + __rtc_clear_1Hz_flag(); + /* In a alarm reset, we expect a alarm interrupt. + * We can do something in the interrupt handler. + * So, do not clear alarm flag. + */ +/* __rtc_clear_alarm_flag(); */ + + if (request_irq(IRQ_RTC, jz_rtc_interrupt, 0, "rtc", NULL) < 0) + return -EBUSY; + + misc_register(&rtc_dev); + + printk("JzSOC onchip RTC installed !!!\n"); + return 0; + +} + +void __exit Jz_rtc_exit (void) +{ + misc_deregister(&rtc_dev); + free_irq (IRQ_RTC, NULL); +} + +module_init(Jz_rtc_init); +module_exit(Jz_rtc_exit); + diff --git a/drivers/char/rtc_jz.h b/drivers/char/rtc_jz.h new file mode 100644 index 0000000..6c754d6 --- /dev/null +++ b/drivers/char/rtc_jz.h @@ -0,0 +1,74 @@ +#ifndef __RTC_JZ_H__ +#define __RTC_JZ_H__ + +//#define DEBUG 1 +#undef DEBUG + +#ifdef DEBUG +#define dprintk(x...) printk(x) +#define print_dbg(f, arg...) \ + printk("%s, %s[%d]:" f , __FUNCTION__, __FILE__, __LINE__ , ##arg ) +#else +#define dprintk(x...) +#define print_dbg(n, arg...) +#endif + + +#ifdef DEBUG + +static void print_rtc_time( struct rtc_time * tm ) +{ + printk("%02d%02d-%02d:%02d:%02d-%d\n", tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_year); + printk("sec:\t%d\n", tm->tm_sec); + printk("min:\t%d\n", tm->tm_min); + printk("hour:\t%d\n", tm->tm_hour); + printk("mday:\t%d\n", tm->tm_mday); + printk("mon:\t%d\n", tm->tm_mon); + printk("year:\t%d\n", tm->tm_year); + printk("wday:\t%d\n", tm->tm_wday); + printk("yday:\t%d\n", tm->tm_yday); + printk("isdst:\t%d\n", tm->tm_isdst); + +} + +static void print_rtc_registers( void ) +{ + while ( !__rtc_write_ready() ) ; + printk("REG_RTC_RCR:\t 0x%8.8x\n", REG_RTC_RCR ); + printk("REG_RTC_RSR:\t 0x%8.8x\n", REG_RTC_RSR ); + printk("REG_RTC_RSAR:\t 0x%8.8x\n", REG_RTC_RSAR ); + printk("REG_RTC_RGR:\t 0x%8.8x\n", REG_RTC_RGR ); + printk("REG_RTC_HCR:\t 0x%8.8x\n", REG_RTC_HCR ); + printk("REG_RTC_HWFCR:\t 0x%8.8x\n", REG_RTC_HWFCR ); + printk("REG_RTC_HRCR:\t 0x%8.8x\n", REG_RTC_HRCR ); + printk("REG_RTC_HWCR:\t 0x%8.8x\n", REG_RTC_HWCR ); + printk("REG_RTC_HWRSR:\t 0x%8.8x\n", REG_RTC_HWRSR ); + printk("REG_RTC_HSPR:\t 0x%8.8x\n", REG_RTC_HSPR ); +} + +#define RTC_PRINT_REG _IOR('p', 0x12, unsigned long)/* Set power down */ +#endif /* #ifdef DEBUG */ + + +/* + * JZSOC ioctl calls that are permitted to the /dev/rtc interface + */ + +#define RTC_ENABLED _IO('p', 0x11) /* enable rtc */ +#define RTC_DISABLED _IO('p', 0x12) /* disable rtc */ +#define RTC_ALM_ON _IO('p', 0x13) /* enable rtc */ +#define RTC_ALM_OFF _IO('p', 0x14) /* disable rtc */ +#define RTC_1HZIE_ON _IO('p', 0x15) /* 1Hz int. enable on */ +#define RTC_1HZIE_OFF _IO('p', 0x16) /* ... off */ + +#define RTC_POWER_DOWN _IOR('p', 0x11, unsigned long)/* Set power down */ + +/* Registers define */ +/* RTC Control register */ +#define RTC_AIE 3 /* jz4740_06_rtc_spec.pdf, RTC Control Register */ +#define RTC_1HZIE 5 /* ... */ +#define RTC_ALM_EN 2 /* ... */ +#define RTC_EN 0 /* ... */ + +#endif /* #define __RTC_JZ_H__ */ diff --git a/drivers/char/rtc_pcf8563.c b/drivers/char/rtc_pcf8563.c new file mode 100644 index 0000000..08dc30b --- /dev/null +++ b/drivers/char/rtc_pcf8563.c @@ -0,0 +1,448 @@ +/* + * PCF8563 Real Time Clock interface for Linux + * + * It only support 24Hour Mode, And the stored values are in BCD format. + * The alarm register is start at minute reg, no second alarm register. + */ + +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* get the user-level API */ +#include +#include + +/********************************************************************** + * register summary + **********************************************************************/ +#define RTC_SECONDS 2 +#define RTC_MINUTES 3 +#define RTC_HOURS 4 +#define RTC_DAY_OF_MONTH 5 +#define RTC_DAY_OF_WEEK 6 +#define RTC_MONTH 7 +#define RTC_YEAR 8 + +#define RTC_MINUTES_ALARM 9 +#define RTC_HOURS_ALARM 0x0a +#define RTC_DAY_ALARM 0x0b +#define RTC_WEEKDAY_ALARM 0x0c + +/* control registers - Moto names + */ +#define RTC_CONTROL 0x00 +#define RTC_STATUS 0x01 +#define RTC_CLKOUT 0x0d +#define RTC_TIMERCTL 0x0e +#define RTC_TIMERCOUNTDOWN 0x0f + + +/* example: !(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) + * determines if the following two #defines are needed + */ +#ifndef BCD2BIN +#define BCD2BIN(val) (((val) & 0x0f) + ((val) >> 4) * 10) +#endif + +#ifndef BIN2BCD +#define BIN2BCD(val) ((((val) / 10) << 4) + (val) % 10) +#endif + +extern spinlock_t rtc_lock; +extern void i2c_open(void); +extern void i2c_close(void); +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); +/* + * We sponge a minor off of the misc major. No need slurping + * up another valuable major dev number for this. If you add + * an ioctl, make sure you don't conflict with SPARC's RTC + * ioctls. + */ + +static int rtc_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + + +static void get_rtc_time (struct rtc_time *rtc_tm); +static void get_rtc_alm_time (struct rtc_time *alm_tm); + +/* + * rtc_status is never changed by rtc_interrupt, and ioctl/open/close is + * protected by the big kernel lock. However, ioctl can still disable the timer + * in rtc_status and then with del_timer after the interrupt has read + * rtc_status but before mod_timer is called, which would then reenable the + * timer (but you would need to have an awful timing before you'd trip on it) + */ +static unsigned long rtc_status = 0; /* bitmapped status byte. */ + +/* + * If this driver ever becomes modularised, it will be really nice + * to make the epoch retain its value across module reload... + */ +static unsigned int epoch = 1900; +static const unsigned char days_in_mo[] = +{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +static unsigned char rtcframe[16]; + +static void read_rtcframe(void) +{ + i2c_open(); + i2c_read(0x51, rtcframe, 0, 16); + i2c_close(); +} + +static void write_rtcframe(void) +{ + i2c_open(); + i2c_write(0x51, rtcframe, 0, 16); + i2c_close(); +} + +static void write_rtc(unsigned char addr, unsigned char val) +{ + volatile unsigned char v = val; + i2c_open(); + i2c_write(0x51, (unsigned char *)&v, addr, 1); + i2c_close(); +} + +static unsigned char read_rtc(unsigned char addr) +{ + volatile unsigned char v; + i2c_open(); + i2c_read(0x51, (unsigned char *)&v, addr, 1); + i2c_close(); + return v; +} + +static void CMOS_WRITE(unsigned char addr, unsigned char val) +{ + rtcframe[addr] = val; +} + +static unsigned char CMOS_READ(unsigned char addr) +{ + return rtcframe[addr]; +} + +static void get_rtc_time(struct rtc_time *rtc_tm) +{ + unsigned char sec,mon,mday,wday,year,hour,min; + + /* + * Only the values that we read from the RTC are set. We leave + * tm_wday, tm_yday and tm_isdst untouched. Even though the + * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated + * by the RTC when initially set to a non-zero value. + */ + + spin_lock_irq(&rtc_lock); + read_rtcframe(); + sec = CMOS_READ(RTC_SECONDS) & ~0x80; + min = CMOS_READ(RTC_MINUTES) & ~0x80; + hour = CMOS_READ(RTC_HOURS) & ~0xc0; + mday = CMOS_READ(RTC_DAY_OF_MONTH) & ~0xc0; + wday = CMOS_READ(RTC_DAY_OF_WEEK) & ~0xf8; + mon = CMOS_READ(RTC_MONTH) & ~0xe0; + year = CMOS_READ(RTC_YEAR) ; + + rtc_tm->tm_sec = BCD2BIN(sec); + rtc_tm->tm_min = BCD2BIN(min); + rtc_tm->tm_hour = BCD2BIN(hour); + rtc_tm->tm_mday = BCD2BIN(mday); + rtc_tm->tm_wday = wday; + /* Don't use centry, but start from year 1970 */ + rtc_tm->tm_mon = BCD2BIN(mon); + year = BCD2BIN(year); + if ((year += (epoch - 1900)) <= 69) + year += 100; + rtc_tm->tm_year = year; + + spin_unlock_irq(&rtc_lock); + + + /* + * Account for differences between how the RTC uses the values + * and how they are defined in a struct rtc_time; + */ + rtc_tm->tm_mon--; +} + +static void get_rtc_alm_time(struct rtc_time *alm_tm) +{ + unsigned char sec, min, hour; + + /* + * Only the values that we read from the RTC are set. That + * means only tm_hour, tm_min, and tm_sec. + */ + spin_lock_irq(&rtc_lock); + read_rtcframe(); + sec = 0; + min = CMOS_READ(RTC_MINUTES_ALARM); + hour = CMOS_READ(RTC_HOURS_ALARM); + + alm_tm->tm_sec = sec;//not set sec + alm_tm->tm_min = BCD2BIN(min); + alm_tm->tm_hour = BCD2BIN(hour); + + spin_unlock_irq(&rtc_lock); +} + +static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct rtc_time wtime; + switch (cmd) { + case RTC_ALM_READ: /* Read the present alarm time */ + { + /* + * This returns a struct rtc_time. Reading >= 0xc0 + * means "don't care" or "match all". Only the tm_hour, + * tm_min, and tm_sec values are filled in. + */ + + get_rtc_alm_time(&wtime); + break; + } + case RTC_ALM_SET: /* Store a time into the alarm */ + { + unsigned char hrs, min, sec; + struct rtc_time alm_tm; + + if (copy_from_user(&alm_tm, (struct rtc_time*)arg, + sizeof(struct rtc_time))) + return -EFAULT; + + hrs = alm_tm.tm_hour; + min = alm_tm.tm_min; + sec = alm_tm.tm_sec; + + + + if (hrs >= 24) + return -EINVAL; + + hrs = BIN2BCD(hrs); + + if (min >= 60) + return -EINVAL; + + min = BIN2BCD(min); + + if (sec >= 60) + return -EINVAL; + + spin_lock_irq(&rtc_lock); + read_rtcframe(); + CMOS_WRITE(RTC_HOURS_ALARM, hrs | 0x80); + CMOS_WRITE(RTC_MINUTES_ALARM, min | 0x80); + + CMOS_WRITE(RTC_DAY_ALARM, CMOS_READ(RTC_DAY_ALARM) | 0x80); + CMOS_WRITE(RTC_WEEKDAY_ALARM, CMOS_READ(RTC_WEEKDAY_ALARM) | 0x80); + CMOS_WRITE(RTC_STATUS, CMOS_READ(RTC_STATUS) | 0x02);/*open alarm int*/ + write_rtcframe(); + spin_unlock_irq(&rtc_lock); + break; + } + case RTC_RD_TIME: /* Read the time/date from RTC */ + { + get_rtc_time(&wtime); + break; + } + case RTC_SET_TIME: /* Set the RTC */ + { + struct rtc_time rtc_tm; + unsigned char mon, day, hrs, min, sec, leap_yr, date; + unsigned int yrs; +// unsigned char ctr; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, + sizeof(struct rtc_time))) + return -EFAULT; + + + yrs = rtc_tm.tm_year + 1900; + mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */ + day = rtc_tm.tm_wday; + date = rtc_tm.tm_mday; + hrs = rtc_tm.tm_hour; + min = rtc_tm.tm_min; + sec = rtc_tm.tm_sec; + + + if (yrs < 1970) + return -EINVAL; + + leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400)); + + if ((mon > 12) || (date == 0)) + return -EINVAL; + + if (date > (days_in_mo[mon] + ((mon == 2) && leap_yr))) + return -EINVAL; + + if ((hrs >= 24) || (min >= 60) || (sec >= 60)) + return -EINVAL; + + if ((yrs -= epoch) > 255) /* They are unsigned */ + return -EINVAL; + + spin_lock_irq(&rtc_lock); + /* These limits and adjustments are independant of + * whether the chip is in binary mode or not. + */ + if (yrs > 169) { + spin_unlock_irq(&rtc_lock); + return -EINVAL; + } + + if (yrs >= 100) + yrs -= 100; + + min = BIN2BCD(min); + sec = BIN2BCD(sec); + hrs = BIN2BCD(hrs); + mon = BIN2BCD(mon); + yrs = BIN2BCD(yrs); + date = BIN2BCD(date); + + read_rtcframe(); + CMOS_WRITE(RTC_SECONDS, sec ); + CMOS_WRITE(RTC_MINUTES, min); + CMOS_WRITE(RTC_HOURS, hrs); + CMOS_WRITE(RTC_DAY_OF_MONTH, date); + CMOS_WRITE(RTC_DAY_OF_WEEK, day); + CMOS_WRITE(RTC_MONTH, mon); + CMOS_WRITE(RTC_YEAR, yrs); + write_rtcframe(); + + spin_unlock_irq(&rtc_lock); + return 0; + } + case RTC_EPOCH_READ: /* Read the epoch. */ + { + return put_user (epoch, (unsigned long *)arg); + } + case RTC_EPOCH_SET: /* Set the epoch. */ + { + /* + * There were no RTC clocks before 1900. + */ + if (arg < 1900) + return -EINVAL; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + epoch = arg; + return 0; + } + default: + return -EINVAL; + } + return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0; +} + +/* + * We enforce only one user at a time here with the open/close. + * Also clear the previous interrupt data on an open, and clean + * up things on a close. + */ + +/* We use rtc_lock to protect against concurrent opens. So the BKL is not + * needed here. Or anywhere else in this driver. */ +static int rtc_open(struct inode *inode, struct file *file) +{ + spin_lock_irq (&rtc_lock); + + if(rtc_status) + goto out_busy; + + rtc_status = 1; + + spin_unlock_irq (&rtc_lock); + return 0; + +out_busy: + spin_unlock_irq (&rtc_lock); + return -EBUSY; +} + +static int rtc_release(struct inode *inode, struct file *file) +{ + + + /* No need for locking -- nobody else can do anything until this rmw is + * committed, and no timer is running. */ + rtc_status = 0; + return 0; +} + +/* + * The various file operations we support. + */ + +static struct file_operations rtc_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: rtc_ioctl, + open: rtc_open, + release: rtc_release, +}; + +#define RTC_MINOR 135 + +static struct miscdevice rtc_dev= +{ + RTC_MINOR, + "rtc", + &rtc_fops +}; + +int __init pcf_rtc_init(void) +{ + int r; + unsigned char ctr; + r = misc_register(&rtc_dev); + + ctr = read_rtc(RTC_CONTROL); + write_rtc(RTC_CONTROL,0x00 ); + + read_rtcframe(); + CMOS_WRITE(RTC_STATUS, 0x00); + CMOS_WRITE(RTC_CLKOUT, 0x80); + /* RTC clock out, 32.768k */ + + CMOS_WRITE(RTC_TIMERCTL, 0x00); + CMOS_WRITE(RTC_TIMERCOUNTDOWN, 0x00); + write_rtcframe(); + + printk("PCF8563 RTC installed !!!\n"); + return 0; + +} + +void __exit pcf_rtc_exit (void) +{ + misc_deregister(&rtc_dev); +} + +module_init(pcf_rtc_init); +module_exit(pcf_rtc_exit); diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 8206442..d704743 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -7,6 +7,14 @@ menu "I2C Hardware Bus support" comment "PC SMBus host controller drivers" depends on PCI +config I2C_JZ47XX + tristate "JZ47XX I2C Interface support" + depends on SOC_JZ4730 || SOC_JZ4740 + help + If you have devices in the Ingenic JZ4730/JZ4740 I2C bus, say yes to + this option. This driver can also be built as a module. If so, the + module will be called i2c-jz47xx. + config I2C_ALI1535 tristate "ALI 1535" depends on PCI diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index e654263..73960cd 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o obj-$(CONFIG_I2C_IMX) += i2c-imx.o obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o obj-$(CONFIG_I2C_IXP2000) += i2c-ixp2000.o +obj-$(CONFIG_I2C_JZ47XX) += i2c-jz47xx.o obj-$(CONFIG_I2C_MPC) += i2c-mpc.o obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o diff --git a/drivers/i2c/busses/i2c-jz47xx.c b/drivers/i2c/busses/i2c-jz47xx.c new file mode 100644 index 0000000..16f947e --- /dev/null +++ b/drivers/i2c/busses/i2c-jz47xx.c @@ -0,0 +1,330 @@ +/* + * i2c_jz47xx.c + * I2C adapter for the INGENIC I2C bus access. + * + * Copyright (C) 2006 - 2008 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "i2c-jz47xx.h" + +/* I2C protocol */ +#define I2C_READ 1 +#define I2C_WRITE 0 + +#define TIMEOUT 1000 +unsigned short sub_addr = 0; +int addr_val = 0; +struct jz_i2c { + spinlock_t lock; + wait_queue_head_t wait; + struct i2c_msg *msg; + unsigned int msg_num; + unsigned int slave_addr; + struct i2c_adapter adap; + struct clk *clk; +}; + +/* + * 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; +} + +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_jz_setclk(unsigned int i2cclk) +{ + __i2c_set_clk(jz_clocks.extalclk, i2cclk); +} + +static int xfer_read(unsigned char device, struct i2c_adapter *adap, unsigned char *buf, int length) +{ + int cnt = length; + int timeout = 5; + + device = (0xa << 3) | ((sub_addr & 0x0700) >> 8); + sub_addr = sub_addr & 0xff; + +L_try_again: + + if (timeout < 0) + goto L_timeout; + + __i2c_send_nack(); /* Master does not send ACK, slave sends it */ + + if (addr_val) { + __i2c_send_start(); + if (i2c_put_data( (device << 1) | I2C_WRITE ) < 0) + goto device_werr; + if (i2c_put_data(sub_addr) < 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 */ + __i2c_send_start(); + + while (cnt) { + if (cnt == 1) { + if (i2c_get_data(buf, 0) < 0) + break; + } else { + if (i2c_get_data(buf, 1) < 0) + break; + } + cnt--; + buf++; + } + addr_val = 0; + return length - 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; +} + +static int xfer_write(unsigned char device, struct i2c_adapter *adap, unsigned char *buf, int length) +{ + int cnt = length; + int cnt_in_pg; + int timeout = 5; + unsigned char *tmpbuf; + unsigned char tmpaddr; + + device = (0xa << 3) | ((sub_addr & 0x0700) >> 8); + sub_addr = sub_addr & 0xff; + + __i2c_send_nack(); /* Master does not send ACK, slave sends it */ + + W_try_again: + if (timeout < 0) + goto W_timeout; + + cnt = length; + tmpbuf = (unsigned char *)buf; + tmpaddr = device; + start_write_page: + cnt_in_pg = 0; + __i2c_send_start(); + if (i2c_put_data( (device << 1) | I2C_WRITE ) < 0) + goto device_err; + if (addr_val) { + if (i2c_put_data(sub_addr) < 0) + goto address_err; + } + while (cnt) { + if (++cnt_in_pg > 8) { + __i2c_send_stop(); + mdelay(1); + sub_addr += 8; + goto start_write_page; + } + if (i2c_put_data(*tmpbuf) < 0) + break; + cnt--; + tmpbuf++; + } + __i2c_send_stop(); + addr_val = 0; + return length - 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; +} + +static int i2c_jz_xfer(struct i2c_adapter *adap, struct i2c_msg *pmsg, int num) +{ + int ret, i; + + dev_dbg(&adap->dev, "jz47xx_xfer: processing %d messages:\n", num); + for (i = 0; i < num; i++) { + dev_dbg(&adap->dev, " #%d: %sing %d byte%s %s 0x%02x\n", i, + pmsg->flags & I2C_M_RD ? "read" : "writ", + pmsg->len, pmsg->len > 1 ? "s" : "", + pmsg->flags & I2C_M_RD ? "from" : "to", pmsg->addr); + if (pmsg->len && pmsg->buf) { /* sanity check */ + if (pmsg->flags & I2C_M_RD) + ret = xfer_read(pmsg->addr, adap, pmsg->buf, pmsg->len); + else + ret = xfer_write(pmsg->addr, adap, pmsg->buf, pmsg->len); + + if (ret) + return ret; + /* Wait until transfer is finished */ + } + dev_dbg(&adap->dev, "transfer complete\n"); + pmsg++; /* next message */ + } + return i; +} + +static u32 i2c_jz_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm i2c_jz_algorithm = { + .master_xfer = i2c_jz_xfer, + .functionality = i2c_jz_functionality, +}; + +static int i2c_jz_probe(struct platform_device *dev) +{ + struct jz_i2c *i2c; + struct i2c_jz_platform_data *plat = dev->dev.platform_data; + int ret; + + __i2c_set_clk(jz_clocks.extalclk, 10000); /* default 10 KHz */ + __i2c_enable(); + + i2c = kzalloc(sizeof(struct jz_i2c), GFP_KERNEL); + if (!i2c) { + printk("There is no enough memory\n"); + ret = -ENOMEM; + goto emalloc; + } + + i2c->adap.owner = THIS_MODULE; + i2c->adap.algo = &i2c_jz_algorithm; + i2c->adap.retries = 5; + spin_lock_init(&i2c->lock); + init_waitqueue_head(&i2c->wait); + sprintf(i2c->adap.name, "jz_i2c-i2c.%u", dev->id); + i2c->adap.algo_data = i2c; + i2c->adap.dev.parent = &dev->dev; + + if (plat) { + i2c->adap.class = plat->class; + } + + /* + * If "dev->id" is negative we consider it as zero. + * The reason to do so is to avoid sysfs names that only make + * sense when there are multiple adapters. + */ + i2c->adap.nr = dev->id != -1 ? dev->id : 0; + /* ret = i2c_add_adapter(&i2c->adap); */ + ret = i2c_add_numbered_adapter(&i2c->adap); + if (ret < 0) { + printk(KERN_INFO "I2C: Failed to add bus\n"); + goto eadapt; + } + + platform_set_drvdata(dev, i2c); + dev_info(&dev->dev, "JZ47xx i2c bus driver.\n"); + return 0; +eadapt: + __i2c_disable(); +emalloc: + return ret; +} + +static int i2c_jz_remove(struct platform_device *dev) +{ + struct i2c_adapter *adapter = platform_get_drvdata(dev); + int rc; + + rc = i2c_del_adapter(adapter); + platform_set_drvdata(dev, NULL); + return rc; +} + +static struct platform_driver i2c_jz_driver = { + .probe = i2c_jz_probe, + .remove = i2c_jz_remove, + .driver = { + .name = "jz_i2c", + }, +}; + +static int __init i2c_adap_jz_init(void) +{ + return platform_driver_register(&i2c_jz_driver); +} + +static void __exit i2c_adap_jz_exit(void) +{ + return platform_driver_unregister(&i2c_jz_driver); +} + +MODULE_LICENSE("GPL"); + +module_init(i2c_adap_jz_init); +module_exit(i2c_adap_jz_exit); diff --git a/drivers/i2c/busses/i2c-jz47xx.h b/drivers/i2c/busses/i2c-jz47xx.h new file mode 100644 index 0000000..eb86110 --- /dev/null +++ b/drivers/i2c/busses/i2c-jz47xx.h @@ -0,0 +1,20 @@ +/* + * i2c_jz47xx.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 _I2C_JZ_H_ +#define _I2C_JZ_H_ + +struct i2c_slave_client; + +struct i2c_jz_platform_data { + unsigned int slave_addr; + struct i2c_slave_client *slave; + unsigned int class; +}; + +extern void jz_set_i2c_info(struct i2c_jz_platform_data *info); +#endif diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 7e13d2d..0b0986b 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -38,8 +38,9 @@ #include #include +extern unsigned short sub_addr; +extern int addr_val; static struct i2c_driver i2cdev_driver; - /* * An i2c_dev represents an i2c_adapter ... an I2C or SMBus master, not a * slave (i2c_client) with which messages will be exchanged. It's coupled @@ -428,6 +429,14 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) */ client->adapter->timeout = msecs_to_jiffies(arg * 10); break; + case I2C_SET_SUB_ADDRESS: + addr_val = 1; + sub_addr = arg; + break; + case I2C_SET_CLOCK: + i2c_jz_setclk(arg); + break; + default: /* NOTE: returning a fault code here could cause trouble * in buggy userspace code. Some old kernel bugs returned diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index a6b989a..a2c9ff9 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -146,8 +146,8 @@ config KEYBOARD_EP93XX module will be called ep93xx_keypad. config KEYBOARD_GPIO - tristate "GPIO Buttons" - depends on GENERIC_GPIO + tristate "JZ GPIO Buttons support" +# depends on GENERIC_GPIO help This driver implements support for buttons connected to GPIO pins of various CPUs (and some other chips). diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index efed0c9..4c8d358 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -1,7 +1,13 @@ /* - * Driver for keys on GPIO lines capable of generating interrupts. + * linux/drivers/input/keyboard/gpio_keys.c * - * Copyright 2005 Phil Blundell + * JZ GPIO Buttons driver for JZ4740 PAVO + * + * User applications can access to this device via /dev/input/eventX. + * + * Copyright (c) 2005 - 2008 Ingenic Semiconductor Inc. + * + * Author: Richard * * 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 @@ -225,7 +231,7 @@ static int gpio_keys_suspend(struct platform_device *pdev, pm_message_t state) for (i = 0; i < pdata->nbuttons; i++) { struct gpio_keys_button *button = &pdata->buttons[i]; if (button->wakeup) { - int irq = gpio_to_irq(button->gpio); + int irq = button->gpio + IRQ_GPIO_0; enable_irq_wake(irq); } } @@ -243,7 +249,7 @@ static int gpio_keys_resume(struct platform_device *pdev) for (i = 0; i < pdata->nbuttons; i++) { struct gpio_keys_button *button = &pdata->buttons[i]; if (button->wakeup) { - int irq = gpio_to_irq(button->gpio); + int irq = button->gpio + IRQ_GPIO_0; disable_irq_wake(irq); } } @@ -269,11 +275,13 @@ static struct platform_driver gpio_keys_device_driver = { static int __init gpio_keys_init(void) { + pavo_board_init(); return platform_driver_register(&gpio_keys_device_driver); } static void __exit gpio_keys_exit(void) { + platform_device_unregister(&pavo_button_device); platform_driver_unregister(&gpio_keys_device_driver); } diff --git a/drivers/input/keyboard/jz_keypad.c b/drivers/input/keyboard/jz_keypad.c new file mode 100644 index 0000000..3e6e309 --- /dev/null +++ b/drivers/input/keyboard/jz_keypad.c @@ -0,0 +1,357 @@ +/* + * linux/drivers/input/keyboard/jz_keypad.c + * + * JZ Keypad Driver + * + * Copyright (c) 2005 - 2008 Ingenic Semiconductor Inc. + * + * Author: Richard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define KB_ROWS 3 +#define KB_COLS 3 + +#define SCAN_INTERVAL (10) + +static unsigned short col[KB_COLS] = {85,87,91}; +static unsigned short row[KB_ROWS] = {60,61,62}; +static unsigned short s0[KB_COLS]; +static unsigned short s1[KB_COLS]={7,7,7}; +static unsigned short precol,prerow; + +static const unsigned int jz_kbd_keycode[KB_COLS * KB_ROWS] = { + KEY_1, KEY_4, KEY_7, + KEY_2, KEY_5, 0, + KEY_3, KEY_6, 0, +}; + +struct jz_kbd { + unsigned int keycode[ARRAY_SIZE(jz_kbd_keycode)]; + struct input_dev *input; + char phys[32]; + + spinlock_t lock; + struct timer_list timer; + + unsigned int suspended; + unsigned long suspend_jiffies; +}; + +static struct jz_kbd g_jz_kbd; + +static inline void jz_scan_kbd(unsigned short *s) +{ + int i; + + if (!s) + return; + + for (i = 0; i < KB_COLS; i++) { + + __gpio_as_input(85); /* row */ + __gpio_as_input(87); /* row */ + __gpio_as_input(91); /* row */ + + __gpio_as_input(60); /* col */ + __gpio_as_input(61); /* col */ + __gpio_as_input(62); /* col */ + + __gpio_clear_pin(col[i]); + __gpio_as_output(col[i]); + + udelay(1000); + s[i]=(__gpio_get_pin(60) << 0) | (__gpio_get_pin(61) << 1) | + (__gpio_get_pin(62) << 2); + } +} + +static void jz_kbd_scankeyboard(struct jz_kbd *kbd_data) +{ + unsigned int row,col; + unsigned long flags; + unsigned int num_pressed; + + if (kbd_data->suspended) + return; + + spin_lock_irqsave(&kbd_data->lock, flags); + + num_pressed = 0; + jz_scan_kbd(s0); + + /* look for key if pressed down on not, col & row */ + if (s0[0] == 7 && s0[1] == 7 && s0[2] == 7) { + if (s1[0] != 7 || s1[1] != 7 || s1[2] != 7) { + /* up */ + input_report_key(kbd_data->input, kbd_data->keycode[prerow * KB_COLS + precol], 0); + input_sync(kbd_data->input); + } + precol = prerow = -1; + s1[0] = s1[1] = s1[2] = 7; + spin_unlock_irqrestore(&kbd_data->lock, flags); + return; + } + + if (s0[0] == 6 && s0[1] == 7 && s0[2] == 7) { + row = 0;//K7 + col = 2; + goto find_row_col; + } + if (s0[0] == 7 && s0[1] == 3 && s0[2] == 7) { + row = 2;//k6 + col = 1; + goto find_row_col; + } + if (s0[0] == 7 && s0[1] == 5 && s0[2] == 7) { + row = 1;//k5 + col = 1; + goto find_row_col; + } + if (s0[0] == 7 && s0[1] == 6 && s0[2] == 7) { + row = 0;//k4 + col = 1; + goto find_row_col; + } + if (s0[0] == 7 && s0[1] == 7 && s0[2] == 3) { + row = 2;//k3 + col = 0; + goto find_row_col; + } + if (s0[0] == 7 && s0[1] == 7 && s0[2] == 5) { + row = 1;//k2 + col = 0; + goto find_row_col; + } + if (s0[0] == 7 && s0[1] == 7 && s0[2] == 6) { + row = 0;//k1 + col = 0; + goto find_row_col; + } + /* 2 or 3 buttons are pressed */ + s0[0] = s0[1] = s0[2] = 7; + s1[0] = s1[1] = s1[2] = 7; + prerow = precol = -1; + spin_unlock_irqrestore(&kbd_data->lock, flags); + return; +find_row_col: + if (s1[0] == 7 && s1[1] == 7 && s1[2] == 7) { + /* down */ + input_report_key(kbd_data->input, kbd_data->keycode[row * KB_COLS + col], 1); + input_sync(kbd_data->input); + s1[0] = s0[0]; + s1[1] = s0[1]; + s1[2] = s0[2]; + + precol = col; + prerow = row; + spin_unlock_irqrestore(&kbd_data->lock, flags); + return; + } + if (s1[0] != 7 || s1[1] != 7 || s1[2] != 7) { + /* is the same as the preview key */ + if (s0[0] == s1[0] && s0[1] == s1[1] && s0[2] == s1[2]) { + input_report_key(kbd_data->input, kbd_data->keycode[row * KB_COLS + col], 1); + input_sync(kbd_data->input); + s1[0] = s0[0]; + s1[1] = s0[1]; + s1[2] = s0[2]; + + precol = col; + prerow = row; + spin_unlock_irqrestore(&kbd_data->lock, flags); + return; + } else { + /* the preview key is up and other key is down */ + if (s0[0] != s1[0] || s0[1] != s1[1] || s0[2] != s1[2]) { + input_report_key(kbd_data->input, kbd_data->keycode[prerow * KB_COLS + precol], 0); + input_sync(kbd_data->input); + input_report_key(kbd_data->input, kbd_data->keycode[row * KB_COLS + col], 1); + input_sync(kbd_data->input); + s1[0] = s0[0]; + s1[1] = s0[1]; + s1[2] = s0[2]; + precol = col; + prerow = row; + spin_unlock_irqrestore(&kbd_data->lock, flags); + return; + } + } + } +} + +static void jz_kbd_timer_callback(unsigned long data) +{ + jz_kbd_scankeyboard(&g_jz_kbd); + mod_timer(&g_jz_kbd.timer, jiffies + SCAN_INTERVAL); +} + +#ifdef CONFIG_PM +static int jz_kbd_suspend(struct platform_device *dev, pm_message_t state) +{ + struct jz_kbd *jz_kbd = platform_get_drvdata(dev); + jz_kbd->suspended = 1; + + return 0; +} + +static int jz_kbd_resume(struct platform_device *dev) +{ + struct jz_kbd *jz_kbd = platform_get_drvdata(dev); + + jz_kbd->suspend_jiffies = jiffies; + jz_kbd->suspended = 0; + + return 0; +} +#else +#define jz_kbd_suspend NULL +#define jz_kbd_resume NULL +#endif + +static int __init jz_kbd_probe(struct platform_device *dev) +{ + struct input_dev *input_dev; + int i, error; + + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + platform_set_drvdata(dev, &g_jz_kbd); + + strcpy(g_jz_kbd.phys, "input/kbd0"); + + spin_lock_init(&g_jz_kbd.lock); + + g_jz_kbd.suspend_jiffies = jiffies; + g_jz_kbd.input = input_dev; + + input_dev->private = &g_jz_kbd; + input_dev->name = "JZ Keypad"; + input_dev->phys = g_jz_kbd.phys; + input_dev->cdev.dev = &dev->dev; + + input_dev->id.bustype = BUS_PARPORT; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_SYN); + input_dev->keycode = g_jz_kbd.keycode; /* keycode array address */ + input_dev->keycodesize = sizeof(unsigned int); + input_dev->keycodemax = ARRAY_SIZE(jz_kbd_keycode); + + memcpy(g_jz_kbd.keycode, jz_kbd_keycode, sizeof(g_jz_kbd.keycode)); + + for (i = 0; i < ARRAY_SIZE(jz_kbd_keycode); i++) + set_bit(g_jz_kbd.keycode[i], input_dev->keybit); + + //clear_bit(0, input_dev->keybit); + + __gpio_as_input(85); + __gpio_as_input(87); + __gpio_as_input(91); + +#if 0 + __gpio_as_input(60); + __gpio_as_input(61); + __gpio_as_input(62); +#endif + + /* Init Keyboard rescan timer */ + init_timer(&g_jz_kbd.timer); + g_jz_kbd.timer.function = jz_kbd_timer_callback; + g_jz_kbd.timer.data = (unsigned long)&g_jz_kbd; + mod_timer(&g_jz_kbd.timer, jiffies + SCAN_INTERVAL); + + error = input_register_device(input_dev); + if (error) { + pr_err("gpio-keys: Unable to register input device, " + "error: %d\n", error); + } + printk("input: JZ Keypad Registered\n"); + + return 0; +} + +static int jz_kbd_remove(struct platform_device *dev) +{ + struct jz_kbd *jz_kbd = platform_get_drvdata(dev); + + del_timer_sync(&jz_kbd->timer); + + __gpio_as_input(85); + __gpio_as_input(87); + __gpio_as_input(91); + + /* These pins is conficting with cs8900a's CS RD WE pins on JZ4740-PAVO board */ + __gpio_as_input(60); + __gpio_as_input(61); + __gpio_as_input(62); + + input_unregister_device(jz_kbd->input); + + return 0; +} + +static struct platform_driver jz_kbd_driver = { + .probe = jz_kbd_probe, + .remove = jz_kbd_remove, + .suspend = jz_kbd_suspend, + .resume = jz_kbd_resume, + .driver = { + .name = "jz-keypad", + }, +}; + +/* + * Jz Keyboard Device + */ +static struct platform_device jzkbd_device = { + .name = "jz-keypad", + .id = -1, +}; + +static int __init jz_kbd_init(void) +{ + platform_device_register(&jzkbd_device); + return platform_driver_register(&jz_kbd_driver); +} + +static void __exit jz_kbd_exit(void) +{ + platform_device_unregister(&jzkbd_device); + platform_driver_unregister(&jz_kbd_driver); +} + +module_init(jz_kbd_init); +module_exit(jz_kbd_exit); + +MODULE_AUTHOR("Richard"); +MODULE_DESCRIPTION("JZ keypad driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/keyboard/jz_keypad_5x5.c b/drivers/input/keyboard/jz_keypad_5x5.c new file mode 100644 index 0000000..eb1502b --- /dev/null +++ b/drivers/input/keyboard/jz_keypad_5x5.c @@ -0,0 +1,329 @@ +/* + * JZ Keypad ( 5 x 5 ) Driver + * + * Copyright (c) 2005 - 2008 Ingenic Semiconductor Inc. + * + * Author: Jason 20090210 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define KB_ROWS 5 +#define KB_COLS 5 + +#define SCAN_INTERVAL (10) + +#define ROW_KEYBIT_MASK 0xFFE0 + +#define SET_GPIOS_AS_INPUT() \ +do { \ + unsigned short i; \ + \ + for (i = 0; i < KB_ROWS; i++) { \ + __gpio_as_input(jz_row_gpios[i]); \ + __gpio_as_input(jz_col_gpios[i]); \ + } \ +} while (0) + + +#define GET_ROW_GPIO_PINS() \ +({ \ + unsigned short _pins = 0, i; \ + for (i = 0; \ + i < KB_ROWS; \ + _pins |= __gpio_get_pin(jz_row_gpios[i]) << i, i++) \ + ; \ + _pins; \ +}) + +#define CHECK_IF_KEY_PRESSED(s) \ +({ \ + unsigned short i; \ + for (i = 0; i < KB_COLS && s[i] == 0x1F ; i++) \ + ; \ + i != KB_ROWS; \ +}) + +#define CLEAN_SCAN_RESULT(s) \ +do { \ + unsigned short i; \ + for (i = 0; i < KB_COLS; s[i++] = 0x1F) \ + ; \ +} while (0) + + +static const unsigned short jz_col_gpios[KB_ROWS] = {76, 75, 74, 73, 72}; +static const unsigned short jz_row_gpios[KB_COLS] = {181, 182, 79, 78, 77}; + +static const unsigned int jz_kbd_keycode[KB_ROWS * KB_COLS] = { + KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, + KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, + KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, + KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, + KEY_LEFTSHIFT, KEY_CAPSLOCK, KEY_SPACE, KEY_BACKSPACE, KEY_Y +}; + +struct jz_kbd { + unsigned int keycode[ARRAY_SIZE(jz_kbd_keycode)]; + struct input_dev *input; + char phys[32]; + + spinlock_t lock; + struct timer_list timer; + + unsigned int suspended; + unsigned long suspend_jiffies; +}; +static struct jz_kbd g_jz_kbd; + +static unsigned short scan_result[KB_COLS]; +static unsigned short pre_scan_result[KB_COLS] = {0x1F, 0x1F, 0x1F, 0x1F, 0x1F}; +static unsigned short pre_col, pre_row; + +/** + * Scan keypad by reading GPIO pins. + */ +static inline void jz_do_scan(unsigned short *s) +{ + unsigned short i; + + if (!s) + return ; + + for (i = 0; i < KB_COLS; i++) { + + SET_GPIOS_AS_INPUT(); + __gpio_clear_pin(jz_col_gpios[i]); + __gpio_as_output(jz_col_gpios[i]); + + udelay(1000); + + s[i] = GET_ROW_GPIO_PINS(); + } +} + +/** + * Call scan function and handle 'GPIO event'(like key down, key up), + * and report it to upper layer of input subsystem ... if necessary + */ +static void jz_kbd_scan(struct jz_kbd *kbd_data) +{ + unsigned short row, col, i; + unsigned long flags; + + if (kbd_data->suspended) + return; + + spin_lock_irqsave(&kbd_data->lock, flags); + + jz_do_scan(scan_result); + + /* check if any key was pressed or not */ + if (!CHECK_IF_KEY_PRESSED(scan_result)) { + + /* key up */ + if (CHECK_IF_KEY_PRESSED(pre_scan_result)) { + input_report_key(kbd_data->input, kbd_data->keycode[pre_row * KB_COLS + pre_col], 0); + input_sync(kbd_data->input); + } + pre_col = pre_row = 0xFFFF; + CLEAN_SCAN_RESULT(pre_scan_result); + + spin_unlock_irqrestore(&kbd_data->lock, flags); + return; + } + + /* find the key */ + for (row = 0; row < KB_ROWS; row++) { + for (i = scan_result[row], col = 0; col < KB_COLS; col++) { + if ( !(i & 0x01) ) + break; + i >>= 1; + } + if (col != KB_COLS) + break; + } + + //printk("[DRIVER] row = %d, col = %d, key code: 0x%02X\n", row, col, kbd_data->keycode[row * KB_COLS + col]); + + /* the same as the preview one || new key */ + if ( (col == pre_col && row == pre_row) + || (pre_col == 0xFFFF && pre_row == 0xFFFF) ) { + + input_report_key(kbd_data->input, kbd_data->keycode[row * KB_COLS + col], 1); + input_sync(kbd_data->input); + + } else { + /* the preview key is up and other key is down */ + input_report_key(kbd_data->input, kbd_data->keycode[pre_row * KB_COLS + col], 0); + input_sync(kbd_data->input); + input_report_key(kbd_data->input, kbd_data->keycode[row * KB_COLS + col], 1); + input_sync(kbd_data->input); + } + + for (i = 0; i < KB_ROWS; i++) { + pre_scan_result[i] = scan_result[i]; + } + + pre_col = col; + pre_row = row; + + spin_unlock_irqrestore(&kbd_data->lock, flags); + + return; +} + +static void jz_kbd_timer_callback(unsigned long data) +{ + jz_kbd_scan(&g_jz_kbd); + mod_timer(&g_jz_kbd.timer, jiffies + SCAN_INTERVAL); +} + +#ifdef CONFIG_PM +static int jz_kbd_suspend(struct platform_device *dev, pm_message_t state) +{ + struct jz_kbd *jz_kbd = platform_get_drvdata(dev); + jz_kbd->suspended = 1; + + return 0; +} + +static int jz_kbd_resume(struct platform_device *dev) +{ + struct jz_kbd *jz_kbd = platform_get_drvdata(dev); + + jz_kbd->suspend_jiffies = jiffies; + jz_kbd->suspended = 0; + + return 0; +} + +#else +#define jz_kbd_suspend NULL +#define jz_kbd_resume NULL +#endif + +/** + * Driver init + */ +static int __init jz_kbd_probe(struct platform_device *dev) +{ + struct input_dev *input_dev; + int i, error; + + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + platform_set_drvdata(dev, &g_jz_kbd); + + strcpy(g_jz_kbd.phys, "input/kbd0"); + + spin_lock_init(&g_jz_kbd.lock); + + g_jz_kbd.suspend_jiffies = jiffies; + g_jz_kbd.input = input_dev; + + input_dev->private = &g_jz_kbd; + input_dev->name = "JZ 5x5 Keypad"; + input_dev->phys = g_jz_kbd.phys; + input_dev->cdev.dev = &dev->dev; + + input_dev->id.bustype = BUS_PARPORT; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_SYN); + input_dev->keycode = g_jz_kbd.keycode; + input_dev->keycodesize = sizeof(unsigned int); + input_dev->keycodemax = ARRAY_SIZE(jz_kbd_keycode); + + memcpy(g_jz_kbd.keycode, jz_kbd_keycode, sizeof(g_jz_kbd.keycode)); + + for ( i = 0; i < ARRAY_SIZE(jz_kbd_keycode); i++) + set_bit(g_jz_kbd.keycode[i], input_dev->keybit); + + init_timer(&g_jz_kbd.timer); + g_jz_kbd.timer.function = jz_kbd_timer_callback; + g_jz_kbd.timer.data = (unsigned long)&g_jz_kbd; + mod_timer(&g_jz_kbd.timer, jiffies + SCAN_INTERVAL); + + error = input_register_device(input_dev); + if (error) { + pr_err("gpio-keys: Unable to register input device, " + "error: %d\n", error); + } + printk("input: JZ 5x5 Keypad Registered.\n"); + + return 0; +} + +static int jz_kbd_remove(struct platform_device *dev) +{ + struct jz_kbd *kbd = platform_get_drvdata(dev); + + del_timer_sync(&kbd->timer); + + SET_GPIOS_AS_INPUT(); + + input_unregister_device(kbd->input); + + return 0; +} + +static struct platform_driver jz_kbd_driver = { + .probe = jz_kbd_probe, + .remove = jz_kbd_remove, + .suspend= jz_kbd_suspend, + .resume = jz_kbd_resume, + .driver = { + .name = "jz-5x5-keypad", + }, +}; + +static struct platform_device jzkbd_device = { + .name = "jz-5x5-keypad", + .id = -1, +}; + +static int __init jz_kbd_init(void) +{ + platform_device_register(&jzkbd_device); + return platform_driver_register(&jz_kbd_driver); +} + +static void __exit jz_kbd_exit(void) +{ + platform_device_unregister(&jzkbd_device); + platform_driver_unregister(&jz_kbd_driver); +} + +module_init(jz_kbd_init); +module_exit(jz_kbd_exit); + +MODULE_AUTHOR("Jason "); +MODULE_DESCRIPTION("JZ 5x5 keypad driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 84b6fc1..16c0f2e 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -615,6 +615,24 @@ config VIDEO_VINO Say Y here to build in support for the Vino video input system found on SGI Indy machines. +config VIDEO_JZ4730_CIM + tristate 'JzSOC Camera Interface Module (CIM) support' + depends on VIDEO_V4L2 && FB_JZSOC && SOC_JZ4730 + select VIDEO_JZ_SENSOR + +config VIDEO_JZ4740_CIM + tristate 'JzSOC Camera Interface Module (CIM) support' + depends on VIDEO_V4L2 && FB_JZSOC && SOC_JZ4740 + select VIDEO_JZ_SENSOR + +config VIDEO_JZ4750_CIM + tristate 'JzSOC Camera Interface Module (CIM) support' + depends on VIDEO_V4L2 && FB_JZSOC && (SOC_JZ4750 || SOC_JZ4750D) + select VIDEO_JZ_SENSOR + +config VIDEO_JZ_SENSOR + tristate "Jz generic camera sensor driver" + config VIDEO_STRADIS tristate "Stradis 4:2:2 MPEG-2 video driver (EXPERIMENTAL)" depends on EXPERIMENTAL && PCI && VIDEO_V4L1 && VIRT_TO_BUS diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 9f2e321..77a2944 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -80,6 +80,11 @@ obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o # And now the v4l2 drivers: +obj-$(CONFIG_VIDEO_JZ4730_CIM) += jz4730_cim.o +obj-$(CONFIG_VIDEO_JZ4740_CIM) += jz4740_cim.o +obj-$(CONFIG_VIDEO_JZ4750_CIM) += jz4750_cim.o +obj-$(CONFIG_VIDEO_JZ_SENSOR) += jz_sensor.o + obj-$(CONFIG_VIDEO_BT848) += bt8xx/ obj-$(CONFIG_VIDEO_ZORAN) += zoran/ obj-$(CONFIG_VIDEO_CQCAM) += c-qcam.o diff --git a/drivers/media/video/jz4730_cim.c b/drivers/media/video/jz4730_cim.c new file mode 100644 index 0000000..4ea0594 --- /dev/null +++ b/drivers/media/video/jz4730_cim.c @@ -0,0 +1,622 @@ +/* + * linux/drivers/char/jzchar/cim.c + * + * Camera Interface Module (CIM) driver for JzSOC + * This driver is independent of the camera sensor + * + * Copyright (C) 2005 JunZheng semiconductor + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define CIM_NAME "cim" + +MODULE_AUTHOR("Jianli Wei"); +MODULE_DESCRIPTION("JzSOC Camera Interface Module driver"); +MODULE_LICENSE("GPL"); + +#undef DEBUG +//#define DEBUG +#ifdef DEBUG +#define dprintk(x...) printk(x) +#else +#define dprintk(x...) +#endif +/* + * Define the Max Image Size + */ +#define MAX_IMAGE_WIDTH 2048 +#define MAX_IMAGE_HEIGHT 2048 +#define MAX_IMAGE_BPP 16 +#define MAX_FRAME_SIZE (MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT * MAX_IMAGE_BPP / 8) +#define CIM_RAM_ADDR (CIM_BASE + 0x1000) + +typedef struct +{ + u32 width; + u32 height; + u32 bpp; +} img_param_t; + +typedef struct +{ + u32 cfg; + u32 ctrl; + u32 mclk; +} cim_config_t; + +/* + * IOCTL_XXX commands + */ +#define IOCTL_SET_IMG_PARAM 0 // arg type: img_param_t * +#define IOCTL_CIM_CONFIG 1 // arg type: cim_config_t * +#define IOCTL_STOP_CIM 2 // arg type: void +#define IOCTL_GET_IMG_PARAM 3 // arg type: img_param_t * +#define IOCTL_GET_CIM_CONFIG 4 // arg type: cim_config_t * +#define IOCTL_TEST_CIM_RAM 5 // no arg type * + +/* + * CIM DMA descriptor + */ +struct cim_desc { + u32 nextdesc; /* Physical address of next desc */ + u32 framebuf; /* Physical address of frame buffer */ + u32 frameid; /* Frame ID */ + u32 dmacmd; /* DMA command */ + u32 pagenum; +}; + +/* + * CIM device structure + */ +struct cim_device { + struct video_device *jz_cim; + unsigned char *framebuf; + unsigned int frame_size; + unsigned int page_order; + wait_queue_head_t wait_queue; + struct cim_desc *frame_desc __attribute__ ((aligned (16))); +}; + +/* global*/ +static struct cim_device *cim_dev; + +/*========================================================================== + * CIM init routines + *========================================================================*/ +#if defined(CONFIG_SOC_JZ4750) +static void cim_image_area(img_param_t *c) { + /*set the image data area start 0, 0, lines_per_frame and pixels_per_line*/ + REG_CIM_SIZE = 0; + REG_CIM_OFFSET = 0; + if (REG_CIM_CTRL & CIM_CTRL_SIZEEN_MASK) { + REG_CIM_SIZE = (c->height << CIM_SIZE_LPF_BIT) | (c->width << CIM_SIZE_PPL_BIT); + REG_CIM_OFFSET = (0 << CIM_OFFSET_V_BIT) | (0 << CIM_OFFSET_H_BIT); +// REG_CIM_OFFSET = (100 << CIM_OFFSET_V_BIT) | (50 << CIM_OFFSET_H_BIT); + } +} +#endif + +static void cim_config(cim_config_t *c) +{ + REG_CIM_CFG = c->cfg; + REG_CIM_CTRL = c->ctrl; + + /*Set the master clock output*/ +#if defined(CONFIG_SOC_JZ4730) + __cim_set_master_clk(__cpm_get_sclk(), c->mclk); +#elif defined(CONFIG_SOC_JZ4740) + __cim_set_master_clk(__cpm_get_hclk(), c->mclk); +#elif defined(CONFIG_SOC_JZ4750) + __cim_set_master_clk(__cpm_get_hclk(), c->mclk); +#else + __cim_set_master_clk(__cpm_get_hclk(), c->mclk); +#endif + /* Enable sof, eof and stop interrupts*/ + __cim_enable_sof_intr(); + __cim_enable_eof_intr(); + __cim_enable_stop_intr(); +} + +/*========================================================================== + * CIM start/stop operations + *========================================================================*/ +static int cim_start_dma(char *ubuf) +{ + struct cim_desc *jz_frame_desc; + int cim_frame_size = 0; + jz_frame_desc = cim_dev->frame_desc; + dprintk("framedesc = %x\n", (u32) jz_frame_desc); + __cim_disable(); + dprintk("__cim_disable\n"); + __cim_set_da(virt_to_phys(cim_dev->frame_desc)); + __cim_clear_state(); // clear state register + __cim_reset_rxfifo(); // resetting rxfifo + __cim_unreset_rxfifo(); + __cim_enable_dma(); // enable dma + __cim_enable(); + + dprintk("__cim_enable\n"); +// while(1) { +// mdelay(10); +// dprintk("REG_CIM_DA = 0x%08x\n", REG_CIM_DA); +// dprintk("REG_CIM_FA = 0x%08x\n", REG_CIM_FA); +// dprintk("REG_CIM_FID = 0x%08x\n", REG_CIM_FID); +// dprintk("REG_CIM_CMD = 0x%08x\n", REG_CIM_CMD); +// dprintk("REG_CIM_CFG = 0x%08x\n", REG_CIM_CFG); +// dprintk("REG_CIM_STATE = 0x%08x\n", REG_CIM_STATE); +// dprintk("REG_CIM_CTRL = 0x%08x\n", REG_CIM_CTRL); +// dprintk("REG_CIM_SIZE = 0x%08x\n", REG_CIM_SIZE); +// dprintk("REG_CIM_OFFSET = 0x%08x\n", REG_CIM_OFFSET); +// mdelay(100); +// } + // wait for interrupts + interruptible_sleep_on(&cim_dev->wait_queue); + dprintk("interruptible_sleep_on\n"); + dprintk("REG_CIM_DA = 0x%08x\n", REG_CIM_DA); + dprintk("REG_CIM_FA = 0x%08x\n", REG_CIM_FA); + dprintk("REG_CIM_FID = 0x%08x\n", REG_CIM_FID); + dprintk("REG_CIM_CMD = 0x%08x\n", REG_CIM_CMD); + dprintk("REG_CIM_CFG = 0x%08x\n", REG_CIM_CFG); + dprintk("REG_CIM_STATE = 0x%08x\n", REG_CIM_STATE); + dprintk("REG_CIM_CTRL = 0x%08x\n", REG_CIM_CTRL); + dprintk("REG_CIM_SIZE = 0x%08x\n", REG_CIM_SIZE); + dprintk("REG_CIM_OFFSET = 0x%08x\n", REG_CIM_OFFSET); + dprintk("REG_CIM_CMD_3 = %x\n", REG_CIM_CMD); + dprintk("REG_CIM_FA = %x\n", REG_CIM_FA); + /* copy frame data to user buffer */ + jz_frame_desc = cim_dev->frame_desc; + + while(jz_frame_desc != NULL) + { + dprintk("ubuf = %x, framebuf = %x,frame_size= %d\n", (u32)ubuf,(u32) jz_frame_desc->framebuf, jz_frame_desc->dmacmd & 0xffffff); + memcpy(ubuf, phys_to_virt(jz_frame_desc->framebuf), ((jz_frame_desc->dmacmd & CIM_CMD_LEN_MASK) * 4)); + ubuf += (jz_frame_desc->dmacmd & CIM_CMD_LEN_MASK) * 4; + cim_frame_size += (jz_frame_desc->dmacmd & CIM_CMD_LEN_MASK) * 4; + jz_frame_desc = (struct cim_desc *)phys_to_virt(jz_frame_desc->nextdesc); + } + return cim_dev->frame_size; +} +static void cim_stop(void) +{ + __cim_disable(); + __cim_clear_state(); +} + +/*========================================================================== + * Framebuffer allocation and destroy + *========================================================================*/ +static void cim_fb_destroy(void) +{ + int pages; + struct cim_desc *jz_frame_desc, *p_desc; + if (cim_dev->frame_desc == NULL) { + printk("Original memory is NULL\n"); + return; + } + jz_frame_desc = cim_dev->frame_desc; + while (jz_frame_desc != NULL) { + dprintk("framebuf = %x,thisdesc = %x,frame_size= %d\n", (u32) jz_frame_desc->framebuf, (unsigned int)jz_frame_desc, (jz_frame_desc->dmacmd & 0xffffff) * 4); + p_desc = (struct cim_desc *)phys_to_virt(jz_frame_desc->nextdesc); + pages = jz_frame_desc->pagenum; + dprintk("page_order = %d\n", pages); + free_pages((unsigned long)phys_to_virt(jz_frame_desc->framebuf), pages); + kfree(jz_frame_desc); + jz_frame_desc = p_desc; + } + cim_dev->frame_desc = NULL; +} + +static struct cim_desc *get_desc_list(int page_order) +{ + int num, page_nums = 0; + unsigned char *p_buf; + struct cim_desc *desc_list_head __attribute__ ((aligned (16))); + struct cim_desc *desc_list_tail __attribute__ ((aligned (16))); + struct cim_desc *p_desc; +// num = page_order - 1; + num = page_order; + desc_list_head = desc_list_tail = NULL; + + while(page_nums < (1 << page_order)) { + p_desc = (struct cim_desc *)kmalloc(sizeof(struct cim_desc), GFP_KERNEL); + if (NULL == p_desc) + return NULL; + //return -ENOMEM; + cim_realloc_pages: + p_buf = (unsigned char *)__get_free_pages(GFP_KERNEL, num); + if ( !(p_buf) && num != 0) { + num --; + goto cim_realloc_pages; + } + else if ( !(p_buf) && num == 0) { + printk("No memory can be alloc!\n"); + //return -ENOMEM; + return NULL; + } + else { + if (desc_list_head == NULL) { + dprintk("Page_list_head\n"); + desc_list_head = p_desc; + } + + else + desc_list_tail->nextdesc = virt_to_phys(p_desc); + + desc_list_tail = p_desc; + desc_list_tail->framebuf = virt_to_phys(p_buf); + dprintk("framebuf addr is 0x%08x\n", (u32)desc_list_tail->framebuf); + dprintk("frame_desc addr is 0x%08x\n",(u32)virt_to_phys(desc_list_tail)); + + desc_list_tail->frameid = 0x52052018; + desc_list_tail->pagenum = num; + if ((page_nums + (1<< num)) < (1 << page_order)) { + desc_list_tail->dmacmd = ((1 << num) * 4096) >> 2 ; + } + else + desc_list_tail->dmacmd = + (cim_dev->frame_size - page_nums * 4096) >> 2 ; + dprintk("the desc_list_tail->dmacmd is 0x%08x\n", desc_list_tail->dmacmd); + page_nums += (1 << num); + dprintk("the pages_num is %d\n", page_nums); + } + } + + desc_list_tail->nextdesc = virt_to_phys(NULL); + /* stop after capturing a frame */ + desc_list_tail->dmacmd |= (CIM_CMD_STOP | CIM_CMD_EOFINT); + dprintk("the desc_list_tail->dmacmd is 0x%08x\n", desc_list_tail->dmacmd); + + return desc_list_head; +} + +static int cim_fb_alloc(int img_width, int img_height, int img_bpp) +{ +#if defined(CONFIG_SOC_JZ4750) + if ((REG_CIM_CFG & (CIM_CFG_DF_MASK | CIM_CFG_BYPASS_MASK)) == 0) + cim_dev->frame_size = img_width * (img_height-1) * (img_bpp/8); + else + cim_dev->frame_size = img_width * img_height * (img_bpp/8); +#else + cim_dev->frame_size = img_width * img_height * (img_bpp/8); +#endif + cim_dev->page_order = get_order(cim_dev->frame_size); + dprintk("cim_dev->page_order=%d\n", cim_dev->page_order); + /* frame buffer ?? need large mem ??*/ + cim_dev->frame_desc = get_desc_list(cim_dev->page_order); + if (cim_dev->frame_desc == NULL) + return -ENOMEM; + dma_cache_wback((unsigned long)(cim_dev->frame_desc), 16); + return 0; +} + +/*========================================================================== + * File operations + *========================================================================*/ + +static int cim_open(struct inode *inode, struct file *filp); +static int cim_release(struct inode *inode, struct file *filp); +static ssize_t cim_read(struct file *filp, char *buf, size_t size, loff_t *l); +static ssize_t cim_write(struct file *filp, const char *buf, size_t size, loff_t *l); +static int cim_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); +static int cim_mmap(struct file *file, struct vm_area_struct *vma); + +static struct file_operations cim_fops = +{ + open: cim_open, + release: cim_release, + read: cim_read, + write: cim_write, + ioctl: cim_ioctl, + compat_ioctl: v4l_compat_ioctl32, + mmap: cim_mmap +}; + +static struct video_device jz_v4l_device = { + .name = "jz cim", + //.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE | + // VID_TYPE_CLIPPING | VID_TYPE_SCALES, VID_TYPE_OVERLAY + .fops = &cim_fops, + .minor = -1, + .owner = THIS_MODULE, + .release = video_device_release, +}; + +static int cim_open(struct inode *inode, struct file *filp) +{ + + try_module_get(THIS_MODULE); + return 0; +} + +static int cim_release(struct inode *inode, struct file *filp) +{ + cim_fb_destroy(); + cim_stop(); + + module_put(THIS_MODULE); + return 0; +} + +static ssize_t cim_read(struct file *filp, char *buf, size_t size, loff_t *l) +{ + if (size < cim_dev->frame_size) + return -EINVAL; + dprintk("read cim\n"); + return cim_start_dma(buf); +} + +static ssize_t cim_write(struct file *filp, const char *buf, size_t size, loff_t *l) +{ + printk("cim error: write is not implemented\n"); + return -1; +} + +static int cim_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + switch (cmd) { + case IOCTL_GET_IMG_PARAM: + { + img_param_t i; + return copy_to_user(argp, &i, sizeof(img_param_t)) ? -EFAULT : 0; + } + case IOCTL_SET_IMG_PARAM: + { + img_param_t i; + int img_width, img_height, img_bpp; + if (copy_from_user((void *)&i, (void *)arg, sizeof(img_param_t))) + return -EFAULT; +#if defined(CONFIG_SOC_JZ4750) + cim_image_area(&i); +#endif + img_width = i.width; + img_height = i.height; + img_bpp = i.bpp; + dprintk("ioctl_set_cim_param\n"); + if ((img_width * img_height * img_bpp/8) > MAX_FRAME_SIZE){ + printk("ERROR! Image is too large!\n"); + return -EINVAL; + } + /* allocate frame buffers */ + if (cim_dev->frame_desc == NULL){ + if (cim_fb_alloc(img_width, img_height, img_bpp) < 0){ + printk("ERROR! Init & alloc cim fail!\n"); + return -ENOMEM; + } + } + else + if ((img_width * img_height * img_bpp/8) > cim_dev->frame_size){ + /* realloc the buffer */ + cim_fb_destroy(); + if (cim_fb_alloc(img_width, img_height, img_bpp) < 0){ + printk("ERRROR! Init & alloc cim fail!\n"); + return -ENOMEM; + } + } + break; + } + case IOCTL_CIM_CONFIG: + { + cim_config_t c; + + if (copy_from_user((void *)&c, (void *)arg, sizeof(cim_config_t))) + return -EFAULT; + + cim_config(&c); + + break; + } + case IOCTL_TEST_CIM_RAM: + { + + int i; + volatile unsigned int *ptr; + ptr = (volatile unsigned int *)(CIM_RAM_ADDR); + printk("RAM test!\n"); + printk("CIM_RAM_ADDR = 0x%08x\n", CIM_RAM_ADDR); + for (i = 0; i < 1024; ptr++, i++) + *ptr = i; + ptr = (volatile unsigned int *)(CIM_RAM_ADDR); + dma_cache_wback((unsigned long)CIM_RAM_ADDR,0xffc); + + for (i = 0; i < 1024; i++) { + if (i != *ptr) + printk("*ptr!=i, *ptr=%d, i=%d\n", *ptr, i); + if (i%32 == 0) { + if (i%128 == 0) + printk("\n"); + printk("*ptr=%04d, i=%04d | ", *ptr, i); + } + ptr++; + } + printk("\n"); + break; + } + default: + printk("Not supported command: 0x%x\n", cmd); + return -EINVAL; + break; + } + return 0; +} + +/* Use mmap /dev/fb can only get a non-cacheable Virtual Address. */ +static int cim_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long start; + unsigned long off; + u32 len; + + off = vma->vm_pgoff << PAGE_SHIFT; + //fb->fb_get_fix(&fix, PROC_CONSOLE(info), info); + + /* frame buffer memory */ + start = cim_dev->frame_desc->framebuf; + len = PAGE_ALIGN((start & ~PAGE_MASK) + (cim_dev->frame_desc->dmacmd & CIM_CMD_LEN_MASK)); + 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_CACHABLE_NO_WA; /* WT cachable */ + pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED; +#endif + + if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + vma->vm_flags |= VM_IO; + return -EAGAIN; + + return 0; +} +/*========================================================================== + * Interrupt handler + *========================================================================*/ + +static irqreturn_t cim_irq_handler(int irq, void *dev_id) +{ + u32 state = REG_CIM_STATE; + dprintk("REG_CIM_STATE = %x\n", REG_CIM_STATE); + dprintk("REG_CIM_CTRL = %x\n", REG_CIM_CTRL); +#if 1 + if (state & CIM_STATE_RXF_OF) { + dprintk("OverFlow interrupt!\n"); + } +#endif + if (state & CIM_STATE_DMA_EOF) { + dprintk("EOF interrupt!\n"); + __cim_disable_dma(); + __cim_disable(); + wake_up_interruptible(&cim_dev->wait_queue); + dprintk("EOF interrupt wake up!\n"); + } + + if (state & CIM_STATE_DMA_STOP) { + // Got a frame, wake up wait routine + __cim_disable_dma(); + __cim_disable(); + dprintk("Stop interrupt!\n"); + wake_up_interruptible(&cim_dev->wait_queue); + } +#if 1 + if (state & CIM_STATE_RXF_TRIG) { + dprintk("Trig!\n"); + } +#endif + + /* clear status flags*/ + REG_CIM_STATE = 0; + return IRQ_HANDLED; +} + +static int v4l_device_init(void) +{ + cim_dev = kzalloc(sizeof(struct cim_device), GFP_KERNEL); + if (!cim_dev) return -ENOMEM; + cim_dev->jz_cim = video_device_alloc(); + if (!cim_dev->jz_cim) { + return -ENOMEM; + } + memcpy(cim_dev->jz_cim, &jz_v4l_device, sizeof(struct video_device)); + cim_dev->frame_desc = NULL; + cim_dev->frame_size = 0; + cim_dev->page_order = 0; + return 0; +} +/*========================================================================== + * Module init and exit + *========================================================================*/ + +static int __init jz_cim_init(void) +{ + struct cim_device *dev; + int ret; + /* allocate device */ + ret = v4l_device_init(); + if (ret) + return ret; + /* record device */ + dev = cim_dev; + init_waitqueue_head(&dev->wait_queue); + + ret = video_register_device(dev->jz_cim, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + printk(KERN_ERR "CIM Video4Linux-device " + "registration failed\n"); + return -EINVAL; + } + + if (ret < 0) { + cim_fb_destroy(); + kfree(dev); + return ret; + } + + if ((ret = request_irq(IRQ_CIM, cim_irq_handler, IRQF_DISABLED, + CIM_NAME, dev))) { + printk(KERN_ERR "request_irq return error, ret=%d\n", ret); + cim_fb_destroy(); + kfree(dev); + printk(KERN_ERR "CIM could not get IRQ\n"); + return ret; + } + + printk("JzSOC Camera Interface Module (CIM) driver registered\n"); + + return 0; +} + +static void __exit jz_cim_exit(void) +{ + free_irq(IRQ_CIM, cim_dev); + kfree(cim_dev); + video_unregister_device(cim_dev->jz_cim); +} + +module_init(jz_cim_init); +module_exit(jz_cim_exit); diff --git a/drivers/media/video/jz4740_cim.c b/drivers/media/video/jz4740_cim.c new file mode 100644 index 0000000..4ea0594 --- /dev/null +++ b/drivers/media/video/jz4740_cim.c @@ -0,0 +1,622 @@ +/* + * linux/drivers/char/jzchar/cim.c + * + * Camera Interface Module (CIM) driver for JzSOC + * This driver is independent of the camera sensor + * + * Copyright (C) 2005 JunZheng semiconductor + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define CIM_NAME "cim" + +MODULE_AUTHOR("Jianli Wei"); +MODULE_DESCRIPTION("JzSOC Camera Interface Module driver"); +MODULE_LICENSE("GPL"); + +#undef DEBUG +//#define DEBUG +#ifdef DEBUG +#define dprintk(x...) printk(x) +#else +#define dprintk(x...) +#endif +/* + * Define the Max Image Size + */ +#define MAX_IMAGE_WIDTH 2048 +#define MAX_IMAGE_HEIGHT 2048 +#define MAX_IMAGE_BPP 16 +#define MAX_FRAME_SIZE (MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT * MAX_IMAGE_BPP / 8) +#define CIM_RAM_ADDR (CIM_BASE + 0x1000) + +typedef struct +{ + u32 width; + u32 height; + u32 bpp; +} img_param_t; + +typedef struct +{ + u32 cfg; + u32 ctrl; + u32 mclk; +} cim_config_t; + +/* + * IOCTL_XXX commands + */ +#define IOCTL_SET_IMG_PARAM 0 // arg type: img_param_t * +#define IOCTL_CIM_CONFIG 1 // arg type: cim_config_t * +#define IOCTL_STOP_CIM 2 // arg type: void +#define IOCTL_GET_IMG_PARAM 3 // arg type: img_param_t * +#define IOCTL_GET_CIM_CONFIG 4 // arg type: cim_config_t * +#define IOCTL_TEST_CIM_RAM 5 // no arg type * + +/* + * CIM DMA descriptor + */ +struct cim_desc { + u32 nextdesc; /* Physical address of next desc */ + u32 framebuf; /* Physical address of frame buffer */ + u32 frameid; /* Frame ID */ + u32 dmacmd; /* DMA command */ + u32 pagenum; +}; + +/* + * CIM device structure + */ +struct cim_device { + struct video_device *jz_cim; + unsigned char *framebuf; + unsigned int frame_size; + unsigned int page_order; + wait_queue_head_t wait_queue; + struct cim_desc *frame_desc __attribute__ ((aligned (16))); +}; + +/* global*/ +static struct cim_device *cim_dev; + +/*========================================================================== + * CIM init routines + *========================================================================*/ +#if defined(CONFIG_SOC_JZ4750) +static void cim_image_area(img_param_t *c) { + /*set the image data area start 0, 0, lines_per_frame and pixels_per_line*/ + REG_CIM_SIZE = 0; + REG_CIM_OFFSET = 0; + if (REG_CIM_CTRL & CIM_CTRL_SIZEEN_MASK) { + REG_CIM_SIZE = (c->height << CIM_SIZE_LPF_BIT) | (c->width << CIM_SIZE_PPL_BIT); + REG_CIM_OFFSET = (0 << CIM_OFFSET_V_BIT) | (0 << CIM_OFFSET_H_BIT); +// REG_CIM_OFFSET = (100 << CIM_OFFSET_V_BIT) | (50 << CIM_OFFSET_H_BIT); + } +} +#endif + +static void cim_config(cim_config_t *c) +{ + REG_CIM_CFG = c->cfg; + REG_CIM_CTRL = c->ctrl; + + /*Set the master clock output*/ +#if defined(CONFIG_SOC_JZ4730) + __cim_set_master_clk(__cpm_get_sclk(), c->mclk); +#elif defined(CONFIG_SOC_JZ4740) + __cim_set_master_clk(__cpm_get_hclk(), c->mclk); +#elif defined(CONFIG_SOC_JZ4750) + __cim_set_master_clk(__cpm_get_hclk(), c->mclk); +#else + __cim_set_master_clk(__cpm_get_hclk(), c->mclk); +#endif + /* Enable sof, eof and stop interrupts*/ + __cim_enable_sof_intr(); + __cim_enable_eof_intr(); + __cim_enable_stop_intr(); +} + +/*========================================================================== + * CIM start/stop operations + *========================================================================*/ +static int cim_start_dma(char *ubuf) +{ + struct cim_desc *jz_frame_desc; + int cim_frame_size = 0; + jz_frame_desc = cim_dev->frame_desc; + dprintk("framedesc = %x\n", (u32) jz_frame_desc); + __cim_disable(); + dprintk("__cim_disable\n"); + __cim_set_da(virt_to_phys(cim_dev->frame_desc)); + __cim_clear_state(); // clear state register + __cim_reset_rxfifo(); // resetting rxfifo + __cim_unreset_rxfifo(); + __cim_enable_dma(); // enable dma + __cim_enable(); + + dprintk("__cim_enable\n"); +// while(1) { +// mdelay(10); +// dprintk("REG_CIM_DA = 0x%08x\n", REG_CIM_DA); +// dprintk("REG_CIM_FA = 0x%08x\n", REG_CIM_FA); +// dprintk("REG_CIM_FID = 0x%08x\n", REG_CIM_FID); +// dprintk("REG_CIM_CMD = 0x%08x\n", REG_CIM_CMD); +// dprintk("REG_CIM_CFG = 0x%08x\n", REG_CIM_CFG); +// dprintk("REG_CIM_STATE = 0x%08x\n", REG_CIM_STATE); +// dprintk("REG_CIM_CTRL = 0x%08x\n", REG_CIM_CTRL); +// dprintk("REG_CIM_SIZE = 0x%08x\n", REG_CIM_SIZE); +// dprintk("REG_CIM_OFFSET = 0x%08x\n", REG_CIM_OFFSET); +// mdelay(100); +// } + // wait for interrupts + interruptible_sleep_on(&cim_dev->wait_queue); + dprintk("interruptible_sleep_on\n"); + dprintk("REG_CIM_DA = 0x%08x\n", REG_CIM_DA); + dprintk("REG_CIM_FA = 0x%08x\n", REG_CIM_FA); + dprintk("REG_CIM_FID = 0x%08x\n", REG_CIM_FID); + dprintk("REG_CIM_CMD = 0x%08x\n", REG_CIM_CMD); + dprintk("REG_CIM_CFG = 0x%08x\n", REG_CIM_CFG); + dprintk("REG_CIM_STATE = 0x%08x\n", REG_CIM_STATE); + dprintk("REG_CIM_CTRL = 0x%08x\n", REG_CIM_CTRL); + dprintk("REG_CIM_SIZE = 0x%08x\n", REG_CIM_SIZE); + dprintk("REG_CIM_OFFSET = 0x%08x\n", REG_CIM_OFFSET); + dprintk("REG_CIM_CMD_3 = %x\n", REG_CIM_CMD); + dprintk("REG_CIM_FA = %x\n", REG_CIM_FA); + /* copy frame data to user buffer */ + jz_frame_desc = cim_dev->frame_desc; + + while(jz_frame_desc != NULL) + { + dprintk("ubuf = %x, framebuf = %x,frame_size= %d\n", (u32)ubuf,(u32) jz_frame_desc->framebuf, jz_frame_desc->dmacmd & 0xffffff); + memcpy(ubuf, phys_to_virt(jz_frame_desc->framebuf), ((jz_frame_desc->dmacmd & CIM_CMD_LEN_MASK) * 4)); + ubuf += (jz_frame_desc->dmacmd & CIM_CMD_LEN_MASK) * 4; + cim_frame_size += (jz_frame_desc->dmacmd & CIM_CMD_LEN_MASK) * 4; + jz_frame_desc = (struct cim_desc *)phys_to_virt(jz_frame_desc->nextdesc); + } + return cim_dev->frame_size; +} +static void cim_stop(void) +{ + __cim_disable(); + __cim_clear_state(); +} + +/*========================================================================== + * Framebuffer allocation and destroy + *========================================================================*/ +static void cim_fb_destroy(void) +{ + int pages; + struct cim_desc *jz_frame_desc, *p_desc; + if (cim_dev->frame_desc == NULL) { + printk("Original memory is NULL\n"); + return; + } + jz_frame_desc = cim_dev->frame_desc; + while (jz_frame_desc != NULL) { + dprintk("framebuf = %x,thisdesc = %x,frame_size= %d\n", (u32) jz_frame_desc->framebuf, (unsigned int)jz_frame_desc, (jz_frame_desc->dmacmd & 0xffffff) * 4); + p_desc = (struct cim_desc *)phys_to_virt(jz_frame_desc->nextdesc); + pages = jz_frame_desc->pagenum; + dprintk("page_order = %d\n", pages); + free_pages((unsigned long)phys_to_virt(jz_frame_desc->framebuf), pages); + kfree(jz_frame_desc); + jz_frame_desc = p_desc; + } + cim_dev->frame_desc = NULL; +} + +static struct cim_desc *get_desc_list(int page_order) +{ + int num, page_nums = 0; + unsigned char *p_buf; + struct cim_desc *desc_list_head __attribute__ ((aligned (16))); + struct cim_desc *desc_list_tail __attribute__ ((aligned (16))); + struct cim_desc *p_desc; +// num = page_order - 1; + num = page_order; + desc_list_head = desc_list_tail = NULL; + + while(page_nums < (1 << page_order)) { + p_desc = (struct cim_desc *)kmalloc(sizeof(struct cim_desc), GFP_KERNEL); + if (NULL == p_desc) + return NULL; + //return -ENOMEM; + cim_realloc_pages: + p_buf = (unsigned char *)__get_free_pages(GFP_KERNEL, num); + if ( !(p_buf) && num != 0) { + num --; + goto cim_realloc_pages; + } + else if ( !(p_buf) && num == 0) { + printk("No memory can be alloc!\n"); + //return -ENOMEM; + return NULL; + } + else { + if (desc_list_head == NULL) { + dprintk("Page_list_head\n"); + desc_list_head = p_desc; + } + + else + desc_list_tail->nextdesc = virt_to_phys(p_desc); + + desc_list_tail = p_desc; + desc_list_tail->framebuf = virt_to_phys(p_buf); + dprintk("framebuf addr is 0x%08x\n", (u32)desc_list_tail->framebuf); + dprintk("frame_desc addr is 0x%08x\n",(u32)virt_to_phys(desc_list_tail)); + + desc_list_tail->frameid = 0x52052018; + desc_list_tail->pagenum = num; + if ((page_nums + (1<< num)) < (1 << page_order)) { + desc_list_tail->dmacmd = ((1 << num) * 4096) >> 2 ; + } + else + desc_list_tail->dmacmd = + (cim_dev->frame_size - page_nums * 4096) >> 2 ; + dprintk("the desc_list_tail->dmacmd is 0x%08x\n", desc_list_tail->dmacmd); + page_nums += (1 << num); + dprintk("the pages_num is %d\n", page_nums); + } + } + + desc_list_tail->nextdesc = virt_to_phys(NULL); + /* stop after capturing a frame */ + desc_list_tail->dmacmd |= (CIM_CMD_STOP | CIM_CMD_EOFINT); + dprintk("the desc_list_tail->dmacmd is 0x%08x\n", desc_list_tail->dmacmd); + + return desc_list_head; +} + +static int cim_fb_alloc(int img_width, int img_height, int img_bpp) +{ +#if defined(CONFIG_SOC_JZ4750) + if ((REG_CIM_CFG & (CIM_CFG_DF_MASK | CIM_CFG_BYPASS_MASK)) == 0) + cim_dev->frame_size = img_width * (img_height-1) * (img_bpp/8); + else + cim_dev->frame_size = img_width * img_height * (img_bpp/8); +#else + cim_dev->frame_size = img_width * img_height * (img_bpp/8); +#endif + cim_dev->page_order = get_order(cim_dev->frame_size); + dprintk("cim_dev->page_order=%d\n", cim_dev->page_order); + /* frame buffer ?? need large mem ??*/ + cim_dev->frame_desc = get_desc_list(cim_dev->page_order); + if (cim_dev->frame_desc == NULL) + return -ENOMEM; + dma_cache_wback((unsigned long)(cim_dev->frame_desc), 16); + return 0; +} + +/*========================================================================== + * File operations + *========================================================================*/ + +static int cim_open(struct inode *inode, struct file *filp); +static int cim_release(struct inode *inode, struct file *filp); +static ssize_t cim_read(struct file *filp, char *buf, size_t size, loff_t *l); +static ssize_t cim_write(struct file *filp, const char *buf, size_t size, loff_t *l); +static int cim_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); +static int cim_mmap(struct file *file, struct vm_area_struct *vma); + +static struct file_operations cim_fops = +{ + open: cim_open, + release: cim_release, + read: cim_read, + write: cim_write, + ioctl: cim_ioctl, + compat_ioctl: v4l_compat_ioctl32, + mmap: cim_mmap +}; + +static struct video_device jz_v4l_device = { + .name = "jz cim", + //.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE | + // VID_TYPE_CLIPPING | VID_TYPE_SCALES, VID_TYPE_OVERLAY + .fops = &cim_fops, + .minor = -1, + .owner = THIS_MODULE, + .release = video_device_release, +}; + +static int cim_open(struct inode *inode, struct file *filp) +{ + + try_module_get(THIS_MODULE); + return 0; +} + +static int cim_release(struct inode *inode, struct file *filp) +{ + cim_fb_destroy(); + cim_stop(); + + module_put(THIS_MODULE); + return 0; +} + +static ssize_t cim_read(struct file *filp, char *buf, size_t size, loff_t *l) +{ + if (size < cim_dev->frame_size) + return -EINVAL; + dprintk("read cim\n"); + return cim_start_dma(buf); +} + +static ssize_t cim_write(struct file *filp, const char *buf, size_t size, loff_t *l) +{ + printk("cim error: write is not implemented\n"); + return -1; +} + +static int cim_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + switch (cmd) { + case IOCTL_GET_IMG_PARAM: + { + img_param_t i; + return copy_to_user(argp, &i, sizeof(img_param_t)) ? -EFAULT : 0; + } + case IOCTL_SET_IMG_PARAM: + { + img_param_t i; + int img_width, img_height, img_bpp; + if (copy_from_user((void *)&i, (void *)arg, sizeof(img_param_t))) + return -EFAULT; +#if defined(CONFIG_SOC_JZ4750) + cim_image_area(&i); +#endif + img_width = i.width; + img_height = i.height; + img_bpp = i.bpp; + dprintk("ioctl_set_cim_param\n"); + if ((img_width * img_height * img_bpp/8) > MAX_FRAME_SIZE){ + printk("ERROR! Image is too large!\n"); + return -EINVAL; + } + /* allocate frame buffers */ + if (cim_dev->frame_desc == NULL){ + if (cim_fb_alloc(img_width, img_height, img_bpp) < 0){ + printk("ERROR! Init & alloc cim fail!\n"); + return -ENOMEM; + } + } + else + if ((img_width * img_height * img_bpp/8) > cim_dev->frame_size){ + /* realloc the buffer */ + cim_fb_destroy(); + if (cim_fb_alloc(img_width, img_height, img_bpp) < 0){ + printk("ERRROR! Init & alloc cim fail!\n"); + return -ENOMEM; + } + } + break; + } + case IOCTL_CIM_CONFIG: + { + cim_config_t c; + + if (copy_from_user((void *)&c, (void *)arg, sizeof(cim_config_t))) + return -EFAULT; + + cim_config(&c); + + break; + } + case IOCTL_TEST_CIM_RAM: + { + + int i; + volatile unsigned int *ptr; + ptr = (volatile unsigned int *)(CIM_RAM_ADDR); + printk("RAM test!\n"); + printk("CIM_RAM_ADDR = 0x%08x\n", CIM_RAM_ADDR); + for (i = 0; i < 1024; ptr++, i++) + *ptr = i; + ptr = (volatile unsigned int *)(CIM_RAM_ADDR); + dma_cache_wback((unsigned long)CIM_RAM_ADDR,0xffc); + + for (i = 0; i < 1024; i++) { + if (i != *ptr) + printk("*ptr!=i, *ptr=%d, i=%d\n", *ptr, i); + if (i%32 == 0) { + if (i%128 == 0) + printk("\n"); + printk("*ptr=%04d, i=%04d | ", *ptr, i); + } + ptr++; + } + printk("\n"); + break; + } + default: + printk("Not supported command: 0x%x\n", cmd); + return -EINVAL; + break; + } + return 0; +} + +/* Use mmap /dev/fb can only get a non-cacheable Virtual Address. */ +static int cim_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long start; + unsigned long off; + u32 len; + + off = vma->vm_pgoff << PAGE_SHIFT; + //fb->fb_get_fix(&fix, PROC_CONSOLE(info), info); + + /* frame buffer memory */ + start = cim_dev->frame_desc->framebuf; + len = PAGE_ALIGN((start & ~PAGE_MASK) + (cim_dev->frame_desc->dmacmd & CIM_CMD_LEN_MASK)); + 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_CACHABLE_NO_WA; /* WT cachable */ + pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED; +#endif + + if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + vma->vm_flags |= VM_IO; + return -EAGAIN; + + return 0; +} +/*========================================================================== + * Interrupt handler + *========================================================================*/ + +static irqreturn_t cim_irq_handler(int irq, void *dev_id) +{ + u32 state = REG_CIM_STATE; + dprintk("REG_CIM_STATE = %x\n", REG_CIM_STATE); + dprintk("REG_CIM_CTRL = %x\n", REG_CIM_CTRL); +#if 1 + if (state & CIM_STATE_RXF_OF) { + dprintk("OverFlow interrupt!\n"); + } +#endif + if (state & CIM_STATE_DMA_EOF) { + dprintk("EOF interrupt!\n"); + __cim_disable_dma(); + __cim_disable(); + wake_up_interruptible(&cim_dev->wait_queue); + dprintk("EOF interrupt wake up!\n"); + } + + if (state & CIM_STATE_DMA_STOP) { + // Got a frame, wake up wait routine + __cim_disable_dma(); + __cim_disable(); + dprintk("Stop interrupt!\n"); + wake_up_interruptible(&cim_dev->wait_queue); + } +#if 1 + if (state & CIM_STATE_RXF_TRIG) { + dprintk("Trig!\n"); + } +#endif + + /* clear status flags*/ + REG_CIM_STATE = 0; + return IRQ_HANDLED; +} + +static int v4l_device_init(void) +{ + cim_dev = kzalloc(sizeof(struct cim_device), GFP_KERNEL); + if (!cim_dev) return -ENOMEM; + cim_dev->jz_cim = video_device_alloc(); + if (!cim_dev->jz_cim) { + return -ENOMEM; + } + memcpy(cim_dev->jz_cim, &jz_v4l_device, sizeof(struct video_device)); + cim_dev->frame_desc = NULL; + cim_dev->frame_size = 0; + cim_dev->page_order = 0; + return 0; +} +/*========================================================================== + * Module init and exit + *========================================================================*/ + +static int __init jz_cim_init(void) +{ + struct cim_device *dev; + int ret; + /* allocate device */ + ret = v4l_device_init(); + if (ret) + return ret; + /* record device */ + dev = cim_dev; + init_waitqueue_head(&dev->wait_queue); + + ret = video_register_device(dev->jz_cim, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + printk(KERN_ERR "CIM Video4Linux-device " + "registration failed\n"); + return -EINVAL; + } + + if (ret < 0) { + cim_fb_destroy(); + kfree(dev); + return ret; + } + + if ((ret = request_irq(IRQ_CIM, cim_irq_handler, IRQF_DISABLED, + CIM_NAME, dev))) { + printk(KERN_ERR "request_irq return error, ret=%d\n", ret); + cim_fb_destroy(); + kfree(dev); + printk(KERN_ERR "CIM could not get IRQ\n"); + return ret; + } + + printk("JzSOC Camera Interface Module (CIM) driver registered\n"); + + return 0; +} + +static void __exit jz_cim_exit(void) +{ + free_irq(IRQ_CIM, cim_dev); + kfree(cim_dev); + video_unregister_device(cim_dev->jz_cim); +} + +module_init(jz_cim_init); +module_exit(jz_cim_exit); diff --git a/drivers/media/video/jz4750_cim.c b/drivers/media/video/jz4750_cim.c new file mode 100644 index 0000000..7edf285 --- /dev/null +++ b/drivers/media/video/jz4750_cim.c @@ -0,0 +1,734 @@ +/* + * linux/drivers/char/jzchar/cim.c + * + * Camera Interface Module (CIM) driver for JzSOC + * This driver is independent of the camera sensor + * + * Copyright (C) 2005 JunZheng semiconductor + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "jz4750_cim.h" + +#define CIM_NAME "cim" + +MODULE_AUTHOR("Jianli Wei"); +MODULE_DESCRIPTION("JzSOC Camera Interface Module driver"); +MODULE_LICENSE("GPL"); + +#if defined(CONFIG_SOC_JZ4750D) +//#define USE_CIM_OFRCV 1 /* Test overflow recovery */ +#define USE_CIM_DMA_SYNC 1 //set da every time +#endif + +//#define CIM_DEBUG +#undef CIM_DEBUG +#ifdef CIM_DEBUG +#define dprintk(x...) printk(x) +#else +#define dprintk(x...) +#endif +/* + * Define the Max Image Size + */ +#define MAX_IMAGE_WIDTH 2048 +#define MAX_IMAGE_HEIGHT 2048 +#define MAX_IMAGE_BPP 16 +#define MAX_FRAME_SIZE (MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT * MAX_IMAGE_BPP / 8) +#define CIM_RAM_ADDR (CIM_BASE + 0x1000) + +typedef struct +{ + u32 width; + u32 height; + u32 bpp; +} img_param_t; + +typedef struct +{ + u32 cfg; + u32 ctrl; + u32 mclk; + u32 size; + u32 offs; +} cim_config_t; + +/* + * IOCTL_XXX commands + */ +#define IOCTL_SET_IMG_PARAM 0 // arg type: img_param_t * +#define IOCTL_CIM_CONFIG 1 // arg type: cim_config_t * +#define IOCTL_STOP_CIM 2 // arg type: void +#define IOCTL_GET_IMG_PARAM 3 // arg type: img_param_t * +#define IOCTL_GET_CIM_CONFIG 4 // arg type: cim_config_t * +#define IOCTL_TEST_CIM_RAM 5 // no arg type * +#define IOCTL_START_CIM 6 // arg type: void + +/* + * CIM DMA descriptor + */ +struct cim_desc { + u32 nextdesc; /* Physical address of next desc */ + u32 framebuf; /* Physical address of frame buffer */ + u32 frameid; /* Frame ID */ + u32 dmacmd; /* DMA command */ + u32 pagenum; +}; + +/* + * CIM device structure + */ +struct cim_device { + struct video_device *jz_cim; + unsigned char *framebuf; + unsigned int frame_size; + unsigned int page_order; + wait_queue_head_t wait_queue; + struct cim_desc *frame_desc __attribute__ ((aligned (16))); +}; + +/* global*/ +static struct cim_device *cim_dev; +static int start_init = 1; +static int irq_sleep; +/*========================================================================== + * CIM init routines + *========================================================================*/ + +static void cim_image_area(img_param_t *c) { + /*set the image data area start 0, 0, lines_per_frame and pixels_per_line*/ + REG_CIM_SIZE = 0; + REG_CIM_OFFSET = 0; +#if defined(CONFIG_SOC_JZ4750D) + if (REG_CIM_CTRL & CIM_CTRL_WIN_EN) { + REG_CIM_SIZE = (c->height << CIM_SIZE_LPF_BIT) | (c->width << CIM_SIZE_PPL_BIT); +// REG_CIM_OFFSET = (0 << CIM_OFFSET_V_BIT) | (0 << CIM_OFFSET_H_BIT); +// REG_CIM_OFFSET = (100 << CIM_OFFSET_V_BIT) | (50 << CIM_OFFSET_H_BIT); + REG_CIM_OFFSET = (200 << CIM_OFFSET_V_BIT) | (300 << CIM_OFFSET_H_BIT); + } +#endif +} + + +static void cim_config(cim_config_t *c) +{ + REG_CIM_CFG = c->cfg; + REG_CIM_CTRL = c->ctrl; + REG_CIM_SIZE = c->size; + REG_CIM_OFFSET = c->offs; + + dprintk("REG_CIM_SIZE = 0x%08x\n", REG_CIM_SIZE); + dprintk("REG_CIM_OFFSET = 0x%08x\n", REG_CIM_OFFSET); + /* Set the master clock output */ + /* If use pll clock, enable it */ +// __cim_set_master_clk(__cpm_get_hclk(), c->mclk); + + /* Enable sof, eof and stop interrupts*/ + +// __cim_enable_sof_intr(); + __cim_enable_eof_intr(); +#if defined(USE_CIM_EEOFINT) + __cim_enable_eeof_intr(); +#endif +// __cim_enable_stop_intr(); +// __cim_enable_trig_intr(); +// __cim_enable_rxfifo_overflow_intr(); +// __cim_enable_vdd_intr(); +// printk("hclk=%d, mclk = %d\n", __cpm_get_hclk(),c->mclk); + dprintk("REG_CIM_CTRL = 0x%08x\n", REG_CIM_CTRL); +} + +/*========================================================================== + * CIM start/stop operations + *========================================================================*/ +static int cim_start_dma(char *ubuf) +{ + + struct cim_desc *jz_frame_desc; + int cim_frame_size = 0; + dprintk("==========start_init = %d\n", start_init); + __cim_disable(); + __cim_set_da(virt_to_phys(cim_dev->frame_desc)); + __cim_clear_state(); // clear state register + __cim_reset_rxfifo(); // resetting rxfifo + __cim_unreset_rxfifo(); + __cim_enable_dma(); // enable dma + __cim_enable(); + interruptible_sleep_on(&cim_dev->wait_queue); + +#if 1 + dprintk("interruptible_sleep_on\n"); + dprintk("REG_CIM_DA = 0x%08x\n", REG_CIM_DA); + dprintk("REG_CIM_FA = 0x%08x\n", REG_CIM_FA); + dprintk("REG_CIM_FID = 0x%08x\n", REG_CIM_FID); + dprintk("REG_CIM_CMD = 0x%08x\n", REG_CIM_CMD); + dprintk("REG_CIM_CFG = 0x%08x\n", REG_CIM_CFG); + dprintk("REG_CIM_STATE = 0x%08x\n", REG_CIM_STATE); + dprintk("REG_CIM_CTRL = 0x%08x\n", REG_CIM_CTRL); + dprintk("REG_CIM_SIZE = 0x%08x\n", REG_CIM_SIZE); + dprintk("REG_CIM_OFFSET = 0x%08x\n", REG_CIM_OFFSET); + dprintk("REG_CIM_CMD_3 = %x\n", REG_CIM_CMD); + dprintk("REG_CIM_FA = %x\n", REG_CIM_FA); +#endif + /* copy frame data to user buffer */ +#if 0 + jz_frame_desc = cim_dev->frame_desc; + + while (jz_frame_desc != NULL) + { + dprintk("ubuf = %x, framebuf = %x,frame_size= %d\n", (u32)ubuf,(u32) jz_frame_desc->framebuf, jz_frame_desc->dmacmd & 0xffffff); + memcpy(ubuf, phys_to_virt(jz_frame_desc->framebuf), ((jz_frame_desc->dmacmd & CIM_CMD_LEN_MASK) * 4)); + ubuf += (jz_frame_desc->dmacmd & CIM_CMD_LEN_MASK) * 4; + cim_frame_size += (jz_frame_desc->dmacmd & CIM_CMD_LEN_MASK) * 4; + jz_frame_desc = (struct cim_desc *)phys_to_virt(jz_frame_desc->nextdesc); + } +#endif + dprintk("---------**********-----\n"); + return cim_dev->frame_size; +} +static void cim_stop(void) +{ + __cim_disable(); + __cim_clear_state(); +} + +/*========================================================================== + * Framebuffer allocation and destroy + *========================================================================*/ +static void cim_fb_destroy(void) +{ + int pages; + struct cim_desc *jz_frame_desc, *p_desc; + __cim_disable_dma(); + __cim_disable(); + + dprintk("cim_dev->frame_desc = %x\n", (u32)cim_dev->frame_desc); + if (cim_dev->frame_desc == NULL) { + printk("Original memory is NULL\n"); + return; + } + jz_frame_desc = cim_dev->frame_desc; +// while (jz_frame_desc != NULL) { +// while (jz_frame_desc != cim_dev->frame_desc) { + dprintk("framebuf = %x,thisdesc = %x,frame_size= %d\n", (u32) jz_frame_desc->framebuf, (unsigned int)jz_frame_desc, (jz_frame_desc->dmacmd & 0xffffff) * 4); + p_desc = (struct cim_desc *)phys_to_virt(jz_frame_desc->nextdesc); + pages = jz_frame_desc->pagenum; + dprintk("page_order = %d\n", pages); + free_pages((unsigned long)phys_to_virt(jz_frame_desc->framebuf), pages); + kfree(jz_frame_desc); + jz_frame_desc = p_desc; +// } + cim_dev->frame_desc = NULL; + start_init = 1; +} + +static struct cim_desc *get_desc_list(int page_order) +{ + int num, page_nums = 0; + unsigned char *p_buf; + struct cim_desc *desc_list_head __attribute__ ((aligned (16))); + struct cim_desc *desc_list_tail __attribute__ ((aligned (16))); + struct cim_desc *p_desc; +// num = page_order - 1; + num = page_order; + desc_list_head = desc_list_tail = NULL; + + while(page_nums < (1 << page_order)) { + p_desc = (struct cim_desc *)kmalloc(sizeof(struct cim_desc), GFP_KERNEL); + if (NULL == p_desc) + return NULL; + //return -ENOMEM; + cim_realloc_pages: + p_buf = (unsigned char *)__get_free_pages(GFP_KERNEL, num); + if ( !(p_buf) && num != 0) { + num --; + goto cim_realloc_pages; + } + else if ( !(p_buf) && num == 0) { + printk("No memory can be alloc!\n"); + //return -ENOMEM; + return NULL; + } + else { + if (desc_list_head == NULL) { + dprintk("Page_list_head\n"); + desc_list_head = p_desc; + } + + else + desc_list_tail->nextdesc = virt_to_phys(p_desc); + + desc_list_tail = p_desc; + desc_list_tail->framebuf = virt_to_phys(p_buf); + dprintk("framebuf addr is 0x%08x\n", (u32)desc_list_tail->framebuf); + dprintk("frame_desc addr is 0x%08x\n",(u32)virt_to_phys(desc_list_tail)); + + desc_list_tail->frameid = 0x52052018; + desc_list_tail->pagenum = num; + if ((page_nums + (1<< num)) < (1 << page_order)) { + desc_list_tail->dmacmd = ((1 << num) * 4096) >> 2 ; + } + else + desc_list_tail->dmacmd = + (cim_dev->frame_size - page_nums * 4096) >> 2 ; + dprintk("the desc_list_tail->dmacmd is 0x%08x\n", desc_list_tail->dmacmd); + page_nums += (1 << num); + dprintk("the pages_num is %d\n", page_nums); + dma_cache_wback((unsigned long)(desc_list_tail), 16); + } + } + +// desc_list_tail->nextdesc = virt_to_phys(NULL); + desc_list_tail->nextdesc = virt_to_phys(desc_list_head); + desc_list_tail->dmacmd |= CIM_CMD_EOFINT; +#if defined(CONFIG_SOC_JZ4750D) +#if defined(USE_CIM_OFRCV) + desc_list_tail->dmacmd |= (CIM_CMD_EOFINT | CIM_CMD_OFRCV); +#endif +#if defined(USE_CIM_DMA_SYNC) /* wake ervry time */ + desc_list_tail->nextdesc = virt_to_phys(NULL); + desc_list_tail->dmacmd |= (CIM_CMD_STOP | CIM_CMD_EOFINT | CIM_CMD_OFRCV); +#endif +#if defined(USE_CIM_EEOFINT) +// desc_list_tail->dmacmd |= CIM_CMD_EEOFINT; + desc_list_tail->dmacmd |= (CIM_CMD_STOP | CIM_CMD_EOFINT | CIM_CMD_EEOFINT); +#endif +#endif + /* stop after capturing a frame */ +// desc_list_tail->dmacmd |= (CIM_CMD_STOP | CIM_CMD_EOFINT | CIM_CMD_SOFINT); + + + + + dma_cache_wback((unsigned long)(desc_list_tail), 16); + dprintk("the desc_list_tail->dmacmd is 0x%08x\n", desc_list_tail->dmacmd); + + return desc_list_head; +} + +static int cim_fb_alloc(int img_width, int img_height, int img_bpp) +{ + if ((REG_CIM_CFG & (CIM_CFG_DF_MASK | CIM_CFG_BYPASS_MASK)) == 0) + cim_dev->frame_size = img_width * (img_height-1) * (img_bpp/8); + else + cim_dev->frame_size = img_width * img_height * (img_bpp/8); + + cim_dev->page_order = get_order(cim_dev->frame_size); + dprintk("cim_dev->page_order=%d\n", cim_dev->page_order); + /* frame buffer ?? need large mem ??*/ + cim_dev->frame_desc = get_desc_list(cim_dev->page_order); + if (cim_dev->frame_desc == NULL) + return -ENOMEM; + dma_cache_wback((unsigned long)(cim_dev->frame_desc), 16); + return 0; +} + +/*========================================================================== + * File operations + *========================================================================*/ + +static int cim_open(struct inode *inode, struct file *filp); +static int cim_release(struct inode *inode, struct file *filp); +static ssize_t cim_read(struct file *filp, char *buf, size_t size, loff_t *l); +static ssize_t cim_write(struct file *filp, const char *buf, size_t size, loff_t *l); +static int cim_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); +static int cim_mmap(struct file *file, struct vm_area_struct *vma); + +static struct file_operations cim_fops = +{ + open: cim_open, + release: cim_release, + read: cim_read, + write: cim_write, + ioctl: cim_ioctl, + compat_ioctl: v4l_compat_ioctl32, + mmap: cim_mmap +}; + +static struct video_device jz_v4l_device = { + .name = "jz cim", + //.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE | + // VID_TYPE_CLIPPING | VID_TYPE_SCALES, VID_TYPE_OVERLAY + .fops = &cim_fops, + .minor = -1, + .owner = THIS_MODULE, + .release = video_device_release, +}; + +static int cim_open(struct inode *inode, struct file *filp) +{ + + try_module_get(THIS_MODULE); + return 0; +} + +static int cim_release(struct inode *inode, struct file *filp) +{ + dprintk("%s, %s, %d\n", __FILE__, __FUNCTION__, __LINE__); + cim_fb_destroy(); + cim_stop(); + + module_put(THIS_MODULE); + return 0; +} + +static ssize_t cim_read(struct file *filp, char *buf, size_t size, loff_t *l) +{ + printk("============cim error: write is not implemented\n"); + if (size < cim_dev->frame_size) + return -EINVAL; + return cim_start_dma(buf); +} + +static ssize_t cim_write(struct file *filp, const char *buf, size_t size, loff_t *l) +{ + printk("cim error: write is not implemented\n"); + return -1; +} + +static int cim_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + switch (cmd) { + case IOCTL_GET_IMG_PARAM: + { + unsigned int i; + i = cim_dev->frame_desc->framebuf; +// printk("cim_dev->frame_desc->framebuf = 0x%08x\n", cim_dev->frame_desc->framebuf); + dprintk("&&cim_dev->frame_desc->framebuf = 0x%08x\n", i); + + return copy_to_user(argp, &i, sizeof(unsigned int)) ? -EFAULT : 0; + } + case IOCTL_STOP_CIM: + { + __cim_disable_dma(); // enable dma + __cim_disable(); + +// cim_fb_destroy(); + return 0; + } + case IOCTL_START_CIM: + { + __cim_set_da(virt_to_phys(cim_dev->frame_desc)); + __cim_clear_state(); // clear state register + __cim_reset_rxfifo(); // resetting rxfifo + __cim_unreset_rxfifo(); + __cim_enable_dma(); // enable dma + __cim_enable(); + return 0; + } + case IOCTL_SET_IMG_PARAM: + { + img_param_t i; + int img_width, img_height, img_bpp; + if (copy_from_user((void *)&i, (void *)arg, sizeof(img_param_t))) + return -EFAULT; + img_width = i.width; + img_height = i.height; + img_bpp = i.bpp; + printk("ALLOC =========\n"); + if ((img_width * img_height * img_bpp/8) > MAX_FRAME_SIZE){ + printk("ERROR! Image is too large!\n"); + return -EINVAL; + } + /* allocate frame buffers */ + if (cim_dev->frame_desc == NULL){ + if (cim_fb_alloc(img_width, img_height, img_bpp) < 0){ + printk("ERROR! Init & alloc cim fail!\n"); + return -ENOMEM; + } + } + else + if ((img_width * img_height * img_bpp/8) > cim_dev->frame_size){ + /* realloc the buffer */ + dprintk("%s, %s, %d\n", __FILE__, __FUNCTION__, __LINE__); + cim_fb_destroy(); + if (cim_fb_alloc(img_width, img_height, img_bpp) < 0){ + printk("ERRROR! Init & alloc cim fail!\n"); + return -ENOMEM; + } + } + break; + } + case IOCTL_CIM_CONFIG: + { + cim_config_t c; + + if (copy_from_user((void *)&c, (void *)arg, sizeof(cim_config_t))) + return -EFAULT; + cim_config(&c); + + break; + } + case IOCTL_TEST_CIM_RAM: + { + + int i; + volatile unsigned int *ptr; + ptr = (volatile unsigned int *)(CIM_RAM_ADDR); + dprintk("RAM test!\n"); + dprintk("CIM_RAM_ADDR = 0x%08x\n", CIM_RAM_ADDR); + for (i = 0; i < 1024; ptr++, i++) + *ptr = i; + ptr = (volatile unsigned int *)(CIM_RAM_ADDR); + dma_cache_wback((unsigned long)CIM_RAM_ADDR,0xffc); + + for (i = 0; i < 1024; i++) { + if (i != *ptr) + dprintk("*ptr!=i, *ptr=%d, i=%d\n", *ptr, i); + if (i%32 == 0) { + if (i%128 == 0) + dprintk("\n"); + dprintk("*ptr=%04d, i=%04d | ", *ptr, i); + } + ptr++; + } + dprintk("\n"); + break; + } + default: + printk("Not supported command: 0x%x\n", cmd); + return -EINVAL; + break; + } + return 0; +} + +/* Use mmap /dev/fb can only get a non-cacheable Virtual Address. */ +static int cim_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long start; + unsigned long off; + u32 len; + + dprintk("%s, %s, %d\n", __FILE__, __FUNCTION__, __LINE__); + off = vma->vm_pgoff << PAGE_SHIFT; + + /* frame buffer memory */ + start = cim_dev->frame_desc->framebuf; + len = PAGE_ALIGN((start & ~PAGE_MASK) + (cim_dev->frame_desc->dmacmd & CIM_CMD_LEN_MASK)*4); + start &= PAGE_MASK; + printk("vma->vm_end = 0x%08lx,\nvma->vm_start = 0x%08lx,\noff = 0x%08lx,\n len = 0x%08x\n\n", vma->vm_end, vma->vm_start, off, len); + if ((vma->vm_end - vma->vm_start + off) > len) { + printk("Error: vma is larger than memory length\n"); + return -EINVAL; + } + off += start; + + vma->vm_pgoff = off >> PAGE_SHIFT; + vma->vm_flags |= VM_IO; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); /* Uncacheable */ + +#if defined(CONFIG_MIPS32) + pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK; + pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED; /* Uncacheable */ +#endif + + if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) { + return -EAGAIN; + } + return 0; +} +/*========================================================================== + * Interrupt handler + *========================================================================*/ + +static irqreturn_t cim_irq_handler(int irq, void *dev_id) +{ + u32 state = REG_CIM_STATE; + dprintk("REG_CIM_STATE = %x\n", REG_CIM_STATE); + dprintk("IRQ:REG_CIM_CTRL = %x\n", REG_CIM_CTRL); + +#if 0 //sof + /* recommed don't open it */ + if ((REG_CIM_CTRL & CIM_CTRL_DMA_SOFM) && (state & CIM_STATE_DMA_SOF)) { + dprintk("SOF interrupt!\n"); + REG_CIM_STATE &= ~CIM_STATE_DMA_SOF; + } +#endif +#if 0 //eeof + if ((REG_CIM_CTRL & CIM_CTRL_DMA_EEOFM) && (state & CIM_STATE_DMA_EEOF)) { + dprintk("EEOF interrupt!\n"); + __cim_disable_dma(); + __cim_disable(); + wake_up_interruptible(&cim_dev->wait_queue); + REG_CIM_STATE &= ~CIM_STATE_DMA_EEOF; + } +#endif + +#if 1 //eof + if ((REG_CIM_CTRL & CIM_CTRL_DMA_EOFM) && (state & CIM_STATE_DMA_EOF)) { +// if (state & CIM_STATE_DMA_EOF) { + dprintk("EOF interrupt!\n"); + +#if defined(USE_CIM_DMA_SYNC) /* wake ervry time */ +// __cim_disable_dma(); +// __cim_disable(); + wake_up_interruptible(&cim_dev->wait_queue); +#else +// if(irq_sleep == 1) + wake_up_interruptible(&cim_dev->wait_queue); +#endif + REG_CIM_STATE &= ~CIM_STATE_DMA_EOF; + return IRQ_HANDLED; + } +#endif +#if 0 //overflow + if (state & CIM_STATE_RXF_OF) { + printk("OverFlow interrupt!\n"); + REG_CIM_STATE &= ~CIM_STATE_RXF_OF; +// dprintk("REG_CIM_STATE = %x\n", REG_CIM_STATE); + return IRQ_HANDLED; + } +#endif +#if 1 // stop + if ((REG_CIM_CTRL & CIM_CTRL_DMA_STOPM) && (state & CIM_STATE_DMA_STOP)) { + // Got a frame, wake up wait routine +//#if defined(USE_CIM_DMA_SYNC) /* wake ervry time */ + __cim_disable_dma(); +// __cim_disable(); + + dprintk("Stop interrupt!\n"); +// wake_up_interruptible(&cim_dev->wait_queue); + REG_CIM_STATE &= ~CIM_STATE_DMA_STOP; + } +#endif + +#if 0 //trig + if ((REG_CIM_CTRL & CIM_CTRL_RXF_TRIGM) && (state & CIM_STATE_RXF_TRIG)) { + REG_CIM_STATE &= ~CIM_STATE_RXF_TRIG; + dprintk("Trig interrupt!\n"); + } +#endif + +#if 0 //vdd + /* only happen disable cim during DMA transfer*/ + if ((REG_CIM_CTRL & CIM_CTRL_VDDM) && (state & CIM_STATE_VDD)) { + dprintk(">>CIM Disable Done Interrupt!\n"); + REG_CIM_STATE &= ~CIM_STATE_VDD; + } +#endif + /* clear status flags*/ + dprintk("before clear REG_CIM_STATE = %x\n", REG_CIM_STATE); +// REG_CIM_STATE = 0; + + return IRQ_HANDLED; +} + +/*Camera gpio init, different operationg according sensor*/ +static void camera_gpio_init(void) { + + __gpio_as_cim(); + __gpio_as_i2c(); + __sensor_gpio_init(); +} + +static int v4l_device_init(void) +{ + camera_gpio_init(); + cim_dev = kzalloc(sizeof(struct cim_device), GFP_KERNEL); + if (!cim_dev) return -ENOMEM; + cim_dev->jz_cim = video_device_alloc(); + if (!cim_dev->jz_cim) { + return -ENOMEM; + } + memcpy(cim_dev->jz_cim, &jz_v4l_device, sizeof(struct video_device)); + cim_dev->frame_desc = NULL; + cim_dev->frame_size = 0; + cim_dev->page_order = 0; + return 0; +} +/*========================================================================== + * Module init and exit + *========================================================================*/ + +static int __init jz4750_cim_init(void) +{ + struct cim_device *dev; + int ret; + /* allocate device */ + ret = v4l_device_init(); + if (ret) + return ret; + /* record device */ + dev = cim_dev; + init_waitqueue_head(&dev->wait_queue); + + ret = video_register_device(dev->jz_cim, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + printk(KERN_ERR "CIM Video4Linux-device " + "registration failed\n"); + return -EINVAL; + } + + if (ret < 0) { + dprintk("%s, %s, %d\n", __FILE__, __FUNCTION__, __LINE__); + cim_fb_destroy(); + kfree(dev); + return ret; + } + + if ((ret = request_irq(IRQ_CIM, cim_irq_handler, IRQF_DISABLED, + CIM_NAME, dev))) { + printk(KERN_ERR "request_irq return error, ret=%d\n", ret); + dprintk("%s, %s, %d\n", __FILE__, __FUNCTION__, __LINE__); + cim_fb_destroy(); + kfree(dev); + printk(KERN_ERR "CIM could not get IRQ\n"); + return ret; + } + + printk("JzSOC Camera Interface Module (CIM) driver registered\n"); + + return 0; +} + +static void __exit jz4750_cim_exit(void) +{ + free_irq(IRQ_CIM, cim_dev); + kfree(cim_dev); + video_unregister_device(cim_dev->jz_cim); +} + +module_init(jz4750_cim_init); +module_exit(jz4750_cim_exit); diff --git a/drivers/media/video/jz4750_cim.h b/drivers/media/video/jz4750_cim.h new file mode 100644 index 0000000..7e73c7c --- /dev/null +++ b/drivers/media/video/jz4750_cim.h @@ -0,0 +1,49 @@ +/* + * linux/drivers/media/video/jz4750_cim.h -- Ingenic Jz4750 On-Chip CIM 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. + * + */ + +#ifndef __JZ4750_CIM_H__ +#define __JZ4750_CIM_H__ + +/* gpio init */ +#if defined(CONFIG_JZ4750_APUS) || defined(CONFIG_JZ4750D_FUWA1) /* board pavo */ +#define GPIO_CAMERA_RST (32*4+8) /* CIM_MCLK as reset */ +#else +#error "driver/video/Jzlcd.h, please define SPI pins on your board." +#endif + +#define CONFIG_OV9650 1 + +#if defined(CONFIG_OV9650) || defined(CONFIG_OV2640) +#if defined(CONFIG_JZ4750_APUS) /* board pavo */ +#define __sensor_gpio_init() \ +do {\ + __gpio_as_output(GPIO_CAMERA_RST); \ + __gpio_set_pin(GPIO_CAMERA_RST); \ + mdelay(50); \ + __gpio_clear_pin(GPIO_CAMERA_RST);\ +} while(0) + +#elif defined(CONFIG_JZ4750D_FUWA1) /* board pavo */ +#define __sensor_gpio_init() \ +do {\ + __gpio_as_output(GPIO_CAMERA_RST); \ + __gpio_set_pin(GPIO_CAMERA_RST); \ + mdelay(50); \ + __gpio_clear_pin(GPIO_CAMERA_RST);\ +} while(0) +#endif +#endif + +#ifndef __sensor_gpio_init +#define __sensor_gpio_init() +#endif +#endif /* __JZ4750_CIM_H__ */ + diff --git a/drivers/media/video/jz_cim.h b/drivers/media/video/jz_cim.h new file mode 100644 index 0000000..f72cfa4 --- /dev/null +++ b/drivers/media/video/jz_cim.h @@ -0,0 +1,36 @@ +/* + * JzSOC CIM driver + * + * Copyright (C) 2005 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __JZ_CIM_H__ +#define __JZ_CIM_H__ + +typedef struct +{ + u32 width; + u32 height; + u32 bpp; +} IMG_PARAM; + +/* + * IOCTL_XXX commands + */ +#define IOCTL_SET_IMG_PARAM 0 // arg type: IMG_PARAM * + +#endif /* __JZ__CIM_H__ */ diff --git a/drivers/media/video/jz_sensor.c b/drivers/media/video/jz_sensor.c new file mode 100644 index 0000000..2d6005f --- /dev/null +++ b/drivers/media/video/jz_sensor.c @@ -0,0 +1,202 @@ +/* + * linux/drivers/char/jzchar/sensor.c + * + * Common CMOS Camera Sensor Driver + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +//#include "jz-chars.h" + +#include +#include +#include + +#include +#include +#include + +MODULE_AUTHOR("Jianli Wei"); +MODULE_DESCRIPTION("Common CMOS Camera Sensor Driver"); +MODULE_LICENSE("GPL"); + +/* + * ioctl commands + */ +#define IOCTL_SET_ADDR 0 /* set i2c address */ +#define IOCTL_SET_CLK 1 /* set i2c clock */ +#define IOCTL_WRITE_REG 2 /* write sensor register */ +#define IOCTL_READ_REG 3 /* read sensor register */ + +/* + * i2c related + */ +static unsigned int i2c_addr = 0x42; +static unsigned int i2c_clk = 100000; + +struct video_device *jz_sensor; + +static void write_reg(u8 reg, u8 val) +{ + i2c_open(); + i2c_setclk(i2c_clk); + i2c_write((i2c_addr >> 1), &val, reg, 1); + i2c_close(); +} + +static u8 read_reg(u8 reg) +{ + u8 val; + + i2c_open(); + i2c_setclk(i2c_clk); + i2c_read((i2c_addr >> 1), &val, reg, 1); + i2c_close(); + return val; +} + +/* + * fops routines + */ + +static int sensor_open(struct inode *inode, struct file *filp); +static int sensor_release(struct inode *inode, struct file *filp); +static ssize_t sensor_read(struct file *filp, char *buf, size_t size, loff_t *l); +static ssize_t sensor_write(struct file *filp, const char *buf, size_t size, loff_t *l); +static int sensor_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); + +static struct file_operations sensor_fops = +{ + open: sensor_open, + release: sensor_release, + read: sensor_read, + write: sensor_write, + ioctl: sensor_ioctl, +}; + +static int sensor_open(struct inode *inode, struct file *filp) +{ + try_module_get(THIS_MODULE); + return 0; +} + +static int sensor_release(struct inode *inode, struct file *filp) +{ + module_put(THIS_MODULE); + return 0; +} + +static ssize_t sensor_read(struct file *filp, char *buf, size_t size, loff_t *l) +{ + printk("sensor: read is not implemented\n"); + return -1; +} + +static ssize_t sensor_write(struct file *filp, const char *buf, size_t size, loff_t *l) +{ + printk("sensor: write is not implemented\n"); + return -1; +} + +static int sensor_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + + switch (cmd) { + case IOCTL_SET_ADDR: + if (copy_from_user(&i2c_addr, (void *)arg, 4)) + return -EFAULT; + break; + case IOCTL_SET_CLK: + if (copy_from_user(&i2c_clk, (void *)arg, 4)) + return -EFAULT; + break; + case IOCTL_WRITE_REG: + { + u8 regval[2]; + + if (copy_from_user(regval, (void *)arg, 2)) + return -EFAULT; + + write_reg(regval[0], regval[1]); + break; + } + case IOCTL_READ_REG: + { + u8 reg, val; + + if (copy_from_user(®, (void *)arg, 1)) + return -EFAULT; + + val = read_reg(reg); + + if (copy_to_user((void *)(arg + 1), &val, 1)) + return -EFAULT; + break; + } + default: + printk("Not supported command: 0x%x\n", cmd); + return -EINVAL; + break; + } + return ret; +} + +static struct video_device jz_v4l_device = { + .name = "jz sensor", + //.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE | + // VID_TYPE_CLIPPING | VID_TYPE_SCALES, VID_TYPE_OVERLAY + .fops = &sensor_fops, + .minor = -1, +}; + +/* + * Module init and exit + */ + +static int __init jz_sensor_init(void) +{ + int ret; +// cim_dev = kzalloc(sizeof(struct cim_device), GFP_KERNEL); + jz_sensor = video_device_alloc(); + memcpy(jz_sensor, &jz_v4l_device, sizeof(struct video_device)); + jz_sensor->release = video_device_release; +// ret = jz_register_chrdev(SENSOR_MINOR, "sensor", &sensor_fops, NULL); + ret = video_register_device(jz_sensor, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + return ret; + } + + printk("Ingenic CMOS camera sensor driver registered\n"); + + return 0; +} + +static void __exit jz_sensor_exit(void) +{ +// jz_unregister_chrdev(SENSOR_MINOR, "sensor"); + video_unregister_device(jz_sensor); +} + +module_init(jz_sensor_init); +module_exit(jz_sensor_exit); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 06084db..1185e08 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -140,8 +140,13 @@ static int mmc_decode_csd(struct mmc_card *card) e = UNSTUFF_BITS(resp, 47, 3); m = UNSTUFF_BITS(resp, 62, 12); - csd->capacity = (1 + m) << (e + 2); +#ifdef CONFIG_JZ4750_BOOT_FROM_MSC0 + csd->capacity = (1 + m) << (e + 2); + csd->capacity -= 16384; +#else + csd->capacity = (1 + m) << (e + 2); +#endif csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); csd->read_partial = UNSTUFF_BITS(resp, 79, 1); csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index cd81c39..47033d7 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -109,8 +109,13 @@ static int mmc_decode_csd(struct mmc_card *card) e = UNSTUFF_BITS(resp, 47, 3); m = UNSTUFF_BITS(resp, 62, 12); - csd->capacity = (1 + m) << (e + 2); +#ifdef CONFIG_JZ4750_BOOT_FROM_MSC0 + csd->capacity = (1 + m) << (e + 2); + csd->capacity -= 16384; +#else + csd->capacity = (1 + m) << (e + 2); +#endif csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); csd->read_partial = UNSTUFF_BITS(resp, 79, 1); csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); @@ -137,8 +142,13 @@ static int mmc_decode_csd(struct mmc_card *card) csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); m = UNSTUFF_BITS(resp, 48, 22); - csd->capacity = (1 + m) << 10; +#ifdef CONFIG_JZ4750_BOOT_FROM_MSC0 + csd->capacity = (1 + m) << 10; + csd->capacity -= 16384; +#else + csd->capacity = (1 + m) << 10; +#endif csd->read_blkbits = 9; csd->read_partial = 0; csd->write_misalign = 0; @@ -268,9 +278,11 @@ static int mmc_switch_hs(struct mmc_card *card) goto out; if ((status[16] & 0xF) != 1) { +#if 0 printk(KERN_WARNING "%s: Problem switching card " "into high-speed mode!\n", mmc_hostname(card->host)); +#endif } else { mmc_card_set_highspeed(card); mmc_set_timing(card->host, MMC_TIMING_SD_HS); @@ -417,6 +429,9 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, goto free_card; mmc_decode_cid(card); + + /* set 24MHz clock again, why?? */ + mmc_set_clock(host, 24000000); } /* diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 891ef18..c774b9a 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -4,6 +4,104 @@ comment "MMC/SD/SDIO Host Controller Drivers" +config MMC_JZ + tristate "JZ SD/Multimedia Card Interface support" + depends on SOC_JZ4730 || SOC_JZ4740 + help + This selects the Ingenic JZ4730/JZ4740 SD/Multimedia card Interface. + If you have abIngenic platform with a Multimedia Card slot, + say Y or M here. + + If unsure, say N. +choice + depends on MMC_JZ + prompt "MMC BUS Width" + default JZ_MMC_BUS_4 + help + This defines the BUS Width of the Ingenic JZ4730/JZ4740 SD/Multimedia card Interface. + +config JZ_MMC_BUS_1 + bool "1 Bit Bus" + help + 1 Bit SD/Multimedia Card Bus + +config JZ_MMC_BUS_4 + bool "4 Bit Bus" + help + 4 Bit SD/Multimedia Card Bus + +endchoice + +config MSC0_JZ4750 + tristate "JZ4750 SD/Multimedia Card 0 Interface support" + depends on SOC_JZ4750 || SOC_JZ4750D + help + This selects the Ingenic JZ4750 SD/Multimedia card 0 Interface. + If you have a Ingenic platform with a Multimedia Card slot, + say Y or M here. + + If unsure, say N. + +choice + depends on MSC0_JZ4750 + prompt "MSC0 BUS Width" + default JZ4750_MSC0_BUS_4 + help + This defines the BUS Width of the Ingenic JZ4750 SD/Multimedia card Interface. + +config JZ4750_MSC0_BUS_1 + bool "1 Bit Bus" + help + 1 Bit SD/Multimedia Card Bus + +config JZ4750_MSC0_BUS_4 + bool "4 Bit Bus" + help + 4 Bit SD/Multimedia Card Bus + +config JZ4750_MSC0_BUS_8 + bool "8 Bit Bus" + help + 8 Bit Multimedia Card Bus + +endchoice + +config MSC1_JZ4750 + tristate "JZ4750 SD/Multimedia Card 1 Interface support" + depends on SOC_JZ4750 || SOC_JZ4750D + help + This selects the Ingenic JZ4750 SD/Multimedia card 1 Interface. + If you have a Ingenic platform with a Multimedia Card slot, + say Y or M here. + + If unsure, say N. + +choice + depends on MSC1_JZ4750 + prompt "MSC1 BUS Width" + default JZ4750_MSC1_BUS_4 + help + This defines the BUS Width of the Ingenic JZ4750 SD/Multimedia card Interface. + +config JZ4750_MSC1_BUS_1 + bool "1 Bit Bus" + help + 1 Bit SD/Multimedia Card Bus + +config JZ4750_MSC1_BUS_4 + bool "4 Bit Bus" + help + 4 Bit SD/Multimedia Card Bus +endchoice + +config JZ4750_BOOT_FROM_MSC0 + tristate "JZ4750 Boot from SD/Multimedia Card Interface support" + depends on SOC_JZ4750 || SOC_JZ4750D + help + This selects boot from the Sd/Multimedia Card. + + If unsure,say N. + config MMC_ARMMMCI tristate "ARM AMBA Multimedia Card Interface support" depends on ARM_AMBA diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index cf153f6..92d76ab 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -6,6 +6,9 @@ ifeq ($(CONFIG_MMC_DEBUG),y) EXTRA_CFLAGS += -DDEBUG endif +obj-$(CONFIG_MMC_JZ) += jz_mmc.o +obj-$(CONFIG_MSC0_JZ4750) += jz4750_mmc.o +obj-$(CONFIG_MSC1_JZ4750) += jz4750_mmc.o obj-$(CONFIG_MMC_ARMMMCI) += mmci.o obj-$(CONFIG_MMC_PXA) += pxamci.o obj-$(CONFIG_MMC_IMX) += imxmmc.o diff --git a/drivers/mmc/host/jz4750_mmc.c b/drivers/mmc/host/jz4750_mmc.c new file mode 100644 index 0000000..94004fa --- /dev/null +++ b/drivers/mmc/host/jz4750_mmc.c @@ -0,0 +1,1051 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "jz4750_mmc.h" + +#define DRIVER_NAME "jz-mmc" + +#define USE_DMA + +static int r_type = 0; +static int rxdmachan = 0; +static int txdmachan = 0; +static int mmc_slot_enable = 0; +static int auto_select_bus = MSC_4BIT_BUS; /* default 4 bit bus*/ + +/* Start the MMC clock and operation */ +static inline int jz_mmc_start_op(void) +{ + REG_MSC_STRPCL(MSC_ID) = MSC_STRPCL_START_OP; + + return MMC_NO_ERROR; +} + +static inline u32 jz_mmc_calc_clkrt(int is_low, u32 rate) +{ + u32 clkrt; + u32 clk_src = is_low ? 24000000 : 48000000; + + 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; + + /* __cpm_select_msc_clk_high will select 48M clock for MMC/SD card + * perhaps this will made some card with bad quality init fail,or + * bad stabilization. + */ + if (rate > SD_CLOCK_FAST) { + __cpm_select_msc_clk_high(MSC_ID,1); /* select clock source from CPM */ + clkrt = jz_mmc_calc_clkrt(0, rate); + } else { + __cpm_select_msc_clk(MSC_ID,1); /* select clock source from CPM */ + clkrt = jz_mmc_calc_clkrt(1, rate); + } + +#ifndef CONFIG_FPGA + REG_MSC_CLKRT(MSC_ID) = clkrt; +#else + REG_MSC_CLKRT(MSC_ID) = 7; +#endif + 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(MSC_ID) = 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(MSC_ID) = 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(MSC_ID) = nob; + REG_MSC_BLKLEN(MSC_ID) = 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(MSC_ID) = nob; + REG_MSC_BLKLEN(MSC_ID) = 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(MSC_ID) = nob; + REG_MSC_BLKLEN(MSC_ID) = 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_ID) & MSC_STAT_DATA_FIFO_EMPTY) + ; + *buf++ = REG_MSC_RXFIFO(MSC_ID); + } + 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(MSC_ID) = nob; + REG_MSC_BLKLEN(MSC_ID) = 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_ID) & MSC_STAT_DATA_FIFO_FULL) + ; + REG_MSC_TXFIFO(MSC_ID) = *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) +{ + 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; + + /* mask interrupts */ + REG_MSC_IMASK(MSC_ID) = 0xffff; + + /* clear status */ + REG_MSC_IREG(MSC_ID) = 0xffff; + + 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(MSC_ID) = cmd->opcode; + + /* Set argument */ +#ifdef CONFIG_MSC0_JZ4750 +#ifdef CONFIG_JZ4750_MSC0_BUS_1 + if (cmd->opcode == 6) { + /* set 1 bit sd card bus*/ + if (cmd->arg ==2) + REG_MSC_ARG(MSC_ID) = 0; + + /* set 1 bit mmc card bus*/ + if (cmd->arg == 0x3b70101) { + REG_MSC_ARG(MSC_ID) = 0x3b70001; + } + } else + REG_MSC_ARG(MSC_ID) = cmd->arg; + +#elif defined CONFIG_JZ4750_MSC0_BUS_8 + if (cmd->opcode == 6) { + /* set 8 bit mmc card bus*/ + if (cmd->arg == 0x3b70101) + REG_MSC_ARG(MSC_ID) = 0x3b70201; + else + REG_MSC_ARG(MSC_ID) = cmd->arg; + + } else + REG_MSC_ARG(MSC_ID) = cmd->arg; +#else + REG_MSC_ARG(MSC_ID) = cmd->arg; +#endif /* CONFIG_JZ4750_MSC0_BUS_1 */ +#else +#ifdef CONFIG_JZ4750_MSC1_BUS_1 + if (cmd->opcode == 6) { + /* set 1 bit sd card bus*/ + if (cmd->arg ==2) + REG_MSC_ARG(MSC_ID) = 0; + + /* set 1 bit mmc card bus*/ + if (cmd->arg == 0x3b70101) { + REG_MSC_ARG(MSC_ID) = 0x3b70001; + } + } else + REG_MSC_ARG(MSC_ID) = cmd->arg; + +#else + REG_MSC_ARG(MSC_ID) = cmd->arg; +#endif /* CONFIG_JZ4750_MSC1_BUS_1 */ +#endif /* CONFIG_MSC0_JZ4750*/ + + /* Set command */ + REG_MSC_CMDAT(MSC_ID) = cmdat; + + /* Send command */ + jz_mmc_start_op(); + + while (timeout-- && !(REG_MSC_STAT(MSC_ID) & MSC_STAT_END_CMD_RES)) + ; + + REG_MSC_IREG(MSC_ID) = MSC_IREG_END_CMD_RES; /* clear irq flag */ + if (cmd->opcode == 12) { + while (timeout-- && !(REG_MSC_IREG(MSC_ID) & MSC_IREG_PRG_DONE)) + ; + REG_MSC_IREG(MSC_ID) = 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(MSC_ID); + 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(MSC_ID); + buf[0] = (data >> 8) & 0xff; + buf[1] = data & 0xff; + data = REG_MSC_RES(MSC_ID); + buf[2] = (data >> 8) & 0xff; + buf[3] = data & 0xff; + data = REG_MSC_RES(MSC_ID); + 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(MSC_ID); + v = data & 0xffff; + for (i = 0; i < 4; i++) { + data = REG_MSC_RES(MSC_ID); + w1 = data & 0xffff; + data = REG_MSC_RES(MSC_ID); + 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_ID) = MSC_IREG_DATA_TRAN_DONE; /* clear status */ + 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_ID) & + (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_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; + + /* 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_MSC0_JZ4750 +#ifdef CONFIG_JZ4750_MSC0_BUS_1 + cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK; + cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN | + MSC_CMDAT_DMA_EN; +#elif defined CONFIG_JZ4750_MSC0_BUS_4 + if(auto_select_bus == MSC_1BIT_BUS) { + cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK; + cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN | + MSC_CMDAT_DMA_EN; + } else { + cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK; + cmdat |= MSC_CMDAT_BUS_WIDTH_4BIT | MSC_CMDAT_DATA_EN | + MSC_CMDAT_DMA_EN; + } +#else + cmdat |= MSC_CMDAT_DATA_EN | MSC_CMDAT_DMA_EN; +#endif /* CONFIG_JZ4750_MSC0_BUS_1 */ +#else +#ifdef CONFIG_JZ4750_MSC1_BUS_1 + cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK; + 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 /* CONFIG_JZ4750_MSC1_BUS_1 */ +#endif /* CONFIG_MSC0_JZ4750 */ + } + 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_MSC0_JZ4750 +#ifdef CONFIG_JZ4750_MSC0_BUS_1 + cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK; + cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN; +#elif defined CONFIG_JZ4750_MSC0_BUS_4 + if(auto_select_bus == MSC_1BIT_BUS) { + cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK; + cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN; + } else { + cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK; + cmdat |= MSC_CMDAT_BUS_WIDTH_4BIT | MSC_CMDAT_DATA_EN; + } +#else + cmdat |= MSC_CMDAT_DATA_EN; +#endif +#else +#ifdef CONFIG_JZ4750_MSC1_BUS_1 + cmdat &= ~MSC_CMDAT_BUS_WIDTH_MASK; + cmdat |= MSC_CMDAT_BUS_WIDTH_1BIT | MSC_CMDAT_DATA_EN; +#else + cmdat |= MSC_CMDAT_DATA_EN; +#endif /* CONFIG_JZ4750_MSC1_BUS_1 */ +#endif /* CONFIG_MSC0_JZ4750 */ + } + 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(MSC_ID); + + if (ireg) { + unsigned stat = REG_MSC_STAT(MSC_ID); + 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; + +#ifdef CONFIG_FPGA + return 0; +#endif + +#ifdef CONFIG_MSC1_JZ4750 + empty = (__msc1_card_detected(slot) == 0) ? 1 : 0; +#else + empty = (__msc0_card_detected(slot) == 0) ? 1 : 0; + +#endif + if (empty) { + /* wait for card insertion */ +#ifdef CONFIG_MSC1_JZ4750 + __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_MSC1_JZ4750 + __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; + + auto_select_bus = MSC_4BIT_BUS; + 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); + + 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) { + auto_select_bus = MSC_4BIT_BUS; + host->cmdat |= MSC_CMDAT_BUS_WIDTH_4BIT; + } + else if (ios->bus_width == MMC_BUS_WIDTH_8) { + host->cmdat |= MSC_CMDAT_BUS_WIDTH_8BIT; + auto_select_bus = MSC_8BIT_BUS; + } else { + /* 1 bit bus*/ + host->cmdat &= ~MSC_CMDAT_BUS_WIDTH_8BIT; + auto_select_bus = MSC_1BIT_BUS; + } +} + +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_pm_callback(struct pm_dev *pm_dev, + pm_request_t req, void *data); + +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; + +#ifdef CONFIG_MSC0_JZ4750 +#ifdef CONFIG_SOC_JZ4750 + __gpio_as_msc0_8bit(); // for jz4750 +#else + __gpio_as_msc0_4bit(); // for jz4750d +#endif + __msc0_init_io(); + __msc0_enable_power(); +#else + __gpio_as_msc1_4bit(); + __msc1_init_io(); + __msc1_enable_power(); +#endif + __msc_reset(MSC_ID); + REG_MSC_LPM(MSC_ID) = 0x1; + + 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_HIGH; + /* + * 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; + + mmc->max_seg_size = PAGE_SIZE * 16; + mmc->max_req_size = mmc->max_seg_size; + mmc->max_blk_size = 4095; + /* + * 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_MULTIWRITE | 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; /* is it useful ?*/ + host->imask = 0xffff; + /* + * 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; + } + + if (rxdmachan < HALF_DMA_NUM) + REG_DMAC_DMACR(0) |= DMAC_DMACR_FMSC; + else + REG_DMAC_DMACR(1) |= DMAC_DMACR_FMSC; + + /* 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; + } + + if (txdmachan < HALF_DMA_NUM) + REG_DMAC_DMACR(0) |= DMAC_DMACR_FMSC; + else + REG_DMAC_DMACR(1) |= DMAC_DMACR_FMSC; + +#endif + platform_set_drvdata(pdev, mmc); + mmc_add_host(mmc); +#ifdef CONFIG_PM + /* Register MMC slot as as power-managed device */ + pm_register(PM_UNKNOWN_DEV, PM_SYS_UNKNOWN, jz_mmc_pm_callback); +#endif + printk("JZ SD/MMC card driver registered\n"); + + /* Detect card during initialization */ +#if defined(CONFIG_SOC_JZ4750) || defined(CONFIG_SOC_JZ4750D) + 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); + long flags; + + 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); + + local_irq_save(flags); + __msc0_disable_power(); + jz_free_dma(rxdmachan); + jz_free_dma(txdmachan); + free_irq(IRQ_MSC, host); + local_irq_restore(flags); + 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; + + 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 (mmc) + ret = mmc_resume_host(mmc); + + return ret; +} +static int jz_mmc_pm_callback(struct pm_dev *pm_dev, + pm_request_t req, void *data) +{ + struct platform_device *pdev = (struct platform_device *)pm_dev; + + switch(req) { + case PM_RESUME: + jz_mmc_resume(pdev); + break; + case PM_SUSPEND: + /* state has no use */ + jz_mmc_suspend(pdev,state); + break; + default: + printk("MMC/SD: invalid PM request %d\n", req); + break; + } + return 0; +} +#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"); diff --git a/drivers/mmc/host/jz4750_mmc.h b/drivers/mmc/host/jz4750_mmc.h new file mode 100644 index 0000000..8577db6 --- /dev/null +++ b/drivers/mmc/host/jz4750_mmc.h @@ -0,0 +1,88 @@ +#ifndef __JZ4750_MMC_H__ +#define __JZ4750_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 SD_CLOCK_HIGH 24000000 /* 24 MHz for SD Cards */ +#define MMC_NO_ERROR 0 + +#define NR_SG 1 + +#ifdef CONFIG_MSC0_JZ4750 +#define MSC_ID 0 +#define MSC_HOTPLUG_IRQ MSC0_HOTPLUG_IRQ +#define IRQ_MSC IRQ_MSC0 +#define DMA_ID_MSC_RX DMA_ID_MSC0_RX +#define DMA_ID_MSC_TX DMA_ID_MSC0_TX +#define MSC_HOTPLUG_PIN MSC0_HOTPLUG_PIN +#else +#define MSC_ID 1 +#define MSC_HOTPLUG_IRQ MSC1_HOTPLUG_IRQ +#define IRQ_MSC IRQ_MSC1 +#define DMA_ID_MSC_RX DMA_ID_MSC1_RX +#define DMA_ID_MSC_TX DMA_ID_MSC1_TX +#define MSC_HOTPLUG_PIN MSC1_HOTPLUG_PIN +#endif + +#define MSC_1BIT_BUS 0 +#define MSC_4BIT_BUS 1 +#define MSC_8BIT_BUS 2 + +#define SZ_4K 0x00001000 + +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; +}; + +#define MMC_IRQ_MASK() \ +do { \ + REG_MSC_IMASK(MSC_ID) = 0xffff; \ + REG_MSC_IREG(MSC_ID) = 0xffff; \ +} while (0) + +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 + +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 *); +}; + +#endif /* __JZ4750_MMC_H__ */ diff --git a/drivers/mmc/host/jz_mmc.c b/drivers/mmc/host/jz_mmc.c new file mode 100644 index 0000000..29ab9bb --- /dev/null +++ b/drivers/mmc/host/jz_mmc.c @@ -0,0 +1,1026 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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_pm_callback(struct pm_dev *pm_dev, + pm_request_t req, void *data); + +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_MULTIWRITE | 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); +#ifdef CONFIG_PM + /* Register MMC slot as as power-managed device */ + host->pmdev = pm_register(PM_UNKNOWN_DEV, PM_SYS_UNKNOWN, jz_mmc_pm_callback); + if (host->pmdev) + host->pmdev->data = pdev; +#endif + 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); + long flags; + + 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); + + local_irq_save(flags); + jz_mmc_stop_clock(); + __msc_disable_power(); + jz_free_dma(rxdmachan); + jz_free_dma(txdmachan); + free_irq(IRQ_MSC, host); + local_irq_restore(flags); + 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; +} +static int jz_mmc_pm_callback(struct pm_dev *pm_dev, + pm_request_t req, void *data) +{ + struct platform_device *pdev = (struct platform_device *)pm_dev->data; + + switch(req) { + case PM_RESUME: + jz_mmc_resume(pdev); + break; + case PM_SUSPEND: + /* state has no use */ + jz_mmc_suspend(pdev, state); + break; + default: + printk("MMC/SD: invalid PM request %d\n", req); + break; + } + return 0; +} +#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"); diff --git a/drivers/mmc/host/jz_mmc.h b/drivers/mmc/host/jz_mmc.h new file mode 100644 index 0000000..c733529 --- /dev/null +++ b/drivers/mmc/host/jz_mmc.h @@ -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 + +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__ */ diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 77db5ce..b9541ab 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -13,7 +13,7 @@ #include #include #include - +#include #include #include #include @@ -371,12 +371,27 @@ static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) kfree(dev); } + +static int mtdblock_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) +{ + struct gendisk *gd = dev->blkcore_priv; + memset(geo, 0, sizeof(*geo)); + geo->heads = 4; + geo->sectors = 16; + geo->cylinders = dev->size/(4*16); + + printk("cylinders: %x \n", geo->cylinders); + printk("sects: %x\n", dev->size); + return 0; +} + static struct mtd_blktrans_ops mtdblock_tr = { .name = "mtdblock", .major = 31, .part_bits = 0, .blksize = 512, .open = mtdblock_open, + .getgeo = mtdblock_getgeo, .flush = mtdblock_flush, .release = mtdblock_release, .readsect = mtdblock_readsect, diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 5b081cb..cdc69c0 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -635,6 +635,73 @@ static int mtd_ioctl(struct inode *inode, struct file *file, break; } + case MEMWRITEPAGE: + { + struct mtd_page_buf buf; + struct mtd_oob_ops ops; + + memset(&ops, 0, sizeof(ops)); +#if 1 + if(!(file->f_mode & 2)) + return -EPERM; +#endif + + if (copy_from_user(&buf, argp, sizeof(struct mtd_page_buf))) + return -EFAULT; + + if (buf.ooblength > mtd->oobsize) + return -EINVAL; + + if (!mtd->write_oob) + ret = -EOPNOTSUPP; + else + ret = access_ok(VERIFY_READ, buf.oobptr, + buf.ooblength) ? 0 : EFAULT; + + if (ret) + return ret; + + ops.len = mtd->writesize; + ops.ooblen = buf.ooblength; + ops.ooboffs = buf.start & (mtd->oobsize - 1); + ops.mode = MTD_OOB_PLACE; + + if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) + return -EINVAL; + + /* alloc memory and copy oob data from user mode to kernel mode */ + ops.oobbuf = kmalloc(buf.ooblength, GFP_KERNEL); + if (!ops.oobbuf) + return -ENOMEM; + + if (copy_from_user(ops.oobbuf, buf.oobptr, buf.ooblength)) { + kfree(ops.oobbuf); + return -EFAULT; + } + + /* alloc memory and copy page data from user mode to kernel mode */ + ops.datbuf = kmalloc(mtd->writesize, GFP_KERNEL); + if (!ops.datbuf) + return -ENOMEM; + + if (copy_from_user(ops.datbuf, buf.datptr, mtd->writesize)) { + kfree(ops.datbuf); + return -EFAULT; + } + + buf.start &= ~(mtd->oobsize - 1); + ret = mtd->write_oob(mtd, buf.start, &ops); + + if (copy_to_user(argp + 2*sizeof(uint32_t), &ops.retlen, + sizeof(uint32_t))) + ret = -EFAULT; + + kfree(ops.oobbuf); + kfree(ops.datbuf); + break; + } + + case MEMLOCK: { struct erase_info_user einfo; diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 349fcbe..69f511c 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -346,6 +346,10 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, /* set up the MTD object for this partition */ slave->mtd.type = master->type; slave->mtd.flags = master->flags & ~part->mask_flags; + if (part[i].mtdblock_jz_invalid) + slave->mtd.flags |= MTD_MTDBLOCK_JZ_INVALID; + if (part[i].cpu_mode) + slave->mtd.flags |= MTD_NAND_CPU_MODE; slave->mtd.size = part->size; slave->mtd.writesize = master->writesize; slave->mtd.oobsize = master->oobsize; @@ -363,6 +367,7 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, slave->mtd.read = part_read; slave->mtd.write = part_write; + slave->mtd.priv = master->priv; if (master->panic_write) slave->mtd.panic_write = part_panic_write; diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index ce96c09..138afb0 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -452,4 +452,134 @@ config MTD_NAND_SOCRATES help Enables support for NAND Flash chips wired onto Socrates board. +config MTD_NAND_JZ4730 + tristate "Support NAND Flash device on Jz4730 board" + depends on SOC_JZ4730 + help + Support NAND Flash device on Jz4730 board + +config MTD_NAND_JZ4740 + tristate "Support NAND Flash device on Jz4740 board" + depends on SOC_JZ4740 + help + Support NAND Flash device on Jz4740 board + +config MTD_NAND_JZ4750 + tristate "Support NAND Flash device on Jz4750 board" + depends on SOC_JZ4750 || SOC_JZ4750D + help + Support NAND Flash device on Jz4750 board + +config MTD_NAND_CS2 + depends on MTD_NAND_JZ4740 || MTD_NAND_JZ4750 + bool 'Use NAND on CS2_N of JZSOC' + default n + +config MTD_NAND_CS3 + depends on MTD_NAND_JZ4740 || MTD_NAND_JZ4750 + bool 'Use NAND on CS3_N of JZSOC' + default n + +config MTD_NAND_CS4 + depends on MTD_NAND_JZ4740 || MTD_NAND_JZ4750 + bool 'Use NAND on CS4_N of JZSOC' + default n + +config MTD_NAND_MULTI_PLANE + depends on MTD_NAND_JZ4730 || MTD_NAND_JZ4740 || MTD_NAND_JZ4750 + bool 'Use multiple planes if the NAND supports' + default y + help + It is just supported on jz4740 now. + +if MTD_NAND_JZ4740 || MTD_NAND_JZ4730 || MTD_NAND_JZ4750 +choice + prompt "ECC type" + default CONFIG_MTD_SW_HM_ECC + +config MTD_HW_HM_ECC + depends on MTD_NAND_JZ4740 || MTD_NAND_JZ4730 + bool 'Select hardware HM ECC' + +config MTD_SW_HM_ECC + bool 'Select software HM ECC' + +config MTD_HW_RS_ECC + depends on MTD_NAND_JZ4740 + bool 'Select hardware RS ECC' + +config MTD_HW_BCH_ECC + depends on MTD_NAND_JZ4750 + bool 'Select hardware BCH ECC' +endchoice + +choice + prompt "4 bit or 8 bit BCH ecc" + depends on MTD_HW_BCH_ECC + default CONFIG_MTD_HW_BCH_4BIT + +config MTD_HW_BCH_4BIT + bool '4 bit' + +config MTD_HW_BCH_8BIT + bool '8 bit' + +endchoice + +config MTD_NAND_DMA + depends on MTD_HW_BCH_ECC || MTD_HW_RS_ECC + bool 'Use DMA mode' + help + This enables using DMA for reading and writing NAND flash, if not selected, + then CPU mode is used. DMA is only used for two planes for jz4740. + +config MTD_NAND_DMABUF + depends on MTD_NAND_DMA && MTD_HW_BCH_ECC + bool 'use DMA buffer in NAND driver' + help + It's better to say NO. If saying yes, DMA buffers will be allocated for + NAND reading and writing in NAND driver instead of upper layer. It's + slower. Just usable on CS1_N now. By saying NO, upper buffers will be + used as DMA buffer. It's faster, but kmalloc instead of vmalloc is required. +endif + +config ALLOCATE_MTDBLOCK_JZ_EARLY + bool "Allocate memory for mtdblock-jz early" + default n + help + Allocate memory (a nand block cache) for mtdblock-jz.c early in nand_base.c, + Especially for the vfat partition which will be used as USB disk by PC. + +config MTD_MTDBLOCK_WRITE_VERIFY_ENABLE + bool "MTDBLOCK write verify enable" + default n + help + This will enable the write verification, which will read back data to check + after doing a write opetaion. + + It will be used by the JZ mtdblock driver (mtdblock-jz.c). + +config MTD_OOB_COPIES + int "how many copies of the fs info in the oob area" + default 3 + range 0 128 + depends on MTD + help + This defines the copies of the fs info in the oob area inside a block. + Value ranges from 0 to (pages_per_block - 1). + + It will be used by the JZ mtdblock driver (mtdblock-jz.c). + +config MTD_BADBLOCK_FLAG_PAGE + int "which page inside a block will store the badblock mark" + default 0 + range 0 127 + depends on MTD + help + This defines which page of a block will store the badblock mark. + Value ranges from 0 to (pages_per_block - 1). + + Old SLC NANDs store the badblock mark in the first page of a block, but + most modern MLC NANDs store it in the last page of a block. + endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index f3a786b..748a92a 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -32,6 +32,9 @@ obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o obj-$(CONFIG_MTD_ALAUDA) += alauda.o +obj-$(CONFIG_MTD_NAND_JZ4730) += jz4730_nand.o +obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o +obj-$(CONFIG_MTD_NAND_JZ4750) += jz4750_nand.o obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o diff --git a/drivers/mtd/nand/jz4730_nand.c b/drivers/mtd/nand/jz4730_nand.c new file mode 100644 index 0000000..f3e57f7 --- /dev/null +++ b/drivers/mtd/nand/jz4730_nand.c @@ -0,0 +1,367 @@ +/* + * linux/drivers/mtd/nand/jz4730_nand.c + * + * Copyright (c) 2005 - 2007 Ingenic Semiconductor Inc. + * + * Ingenic JZ4730 NAND 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. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define NAND_DATA_PORT 0xB4000000 /* read-write area */ +#define __nand_ecc() (REG_EMC_NFECC & 0x00ffffff) +#define __nand_ecc_enable() (REG_EMC_NFCSR |= EMC_NFCSR_ECCE | EMC_NFCSR_ERST) +#define __nand_ecc_disable() (REG_EMC_NFCSR &= ~EMC_NFCSR_ECCE) + +/* + * MTD structure for JzSOC board + */ +static struct mtd_info *jz_mtd = NULL; + +/* + * Define partitions for flash devices + */ +#ifdef CONFIG_JZ4730_PMP +static struct mtd_partition partition_info[] = { + { 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: 56 * 0x100000 }, + { name: "NAND SSFDC partition", + offset: 64 * 0x100000, + size: 64 * 0x100000 }, +}; + +/* Define max reserved bad blocks for each partition. + * This is used by the mtdblock-jz.c NAND FTL driver only. + * + * The NAND FTL driver reserves some good blocks which can't be + * seen by the upper layer. When the bad block number of a partition + * exceeds the max reserved blocks, then there is no more reserved + * good blocks to be used by the NAND FTL driver when another bad + * block generated. + */ +static int partition_reserved_badblocks[]= { + 2, /* reserved blocks of mtd0 */ + 2, /* reserved blocks of mtd1 */ + 10, /* reserved blocks of mtd2 */ + 10}; /* reserved blocks of mtd3 */ +#elif CONFIG_JZ4730_PMPV1 +static struct mtd_partition partition_info[] = { + { name: "NAND ROOTFS partition", + offset: 3 * 0x100000, + size: (32-3) * 0x100000 }, + { name: "NAND DATAFS partition", + offset: 32 * 0x100000, + size: 32 * 0x100000 }, +}; +static int partition_reserved_badblocks[]={ + 10, + 10}; +#else +static struct mtd_partition partition_info[] = { + { name: "NAND ROOTFS partition", + offset: 3 * 0x100000, + size: (128-3) * 0x100000 }, +}; +static int partition_reserved_badblocks[]={ + 20}; +#endif + +/*------------------------------------------------------------------------- + * Following three functions are exported and used by the mtdblock-jz.c + * NAND FTL driver only. + */ + +unsigned short get_mtdblock_write_verify_enable(void) +{ +#ifdef CONFIG_MTD_MTDBLOCK_WRITE_VERIFY_ENABLE + return 1; +#endif + return 0; +} +EXPORT_SYMBOL(get_mtdblock_write_verify_enable); + +unsigned short get_mtdblock_oob_copies(void) +{ + return CONFIG_MTD_OOB_COPIES; +} +EXPORT_SYMBOL(get_mtdblock_oob_copies); + +int *get_jz_badblock_table(void) +{ + return partition_reserved_badblocks; +} +EXPORT_SYMBOL(get_jz_badblock_table); + +/*-------------------------------------------------------------------------*/ + +static void jz_hwcontrol(struct mtd_info *mtd, int dat, + unsigned int ctrl) +{ + struct nand_chip *this = (struct nand_chip *)(mtd->priv); + unsigned int nandaddr = (unsigned int)this->IO_ADDR_W; + + if (ctrl & NAND_CTRL_CHANGE) { + if ( ctrl & NAND_ALE ) + nandaddr = (unsigned int)((unsigned long)(this->IO_ADDR_W) | 0x00080000); + else + nandaddr = (unsigned int)((unsigned long)(this->IO_ADDR_W) & ~0x00080000); + + if ( ctrl & NAND_CLE ) + nandaddr = nandaddr | 0x00040000; + else + nandaddr = nandaddr & ~0x00040000; + if ( ctrl & NAND_NCE ) + REG_EMC_NFCSR |= EMC_NFCSR_FCE; + else + REG_EMC_NFCSR &= ~EMC_NFCSR_FCE; + } + + this->IO_ADDR_W = (void __iomem *)nandaddr; + if (dat != NAND_CMD_NONE) + writeb(dat , this->IO_ADDR_W); + +} + +static int jz_device_ready(struct mtd_info *mtd) +{ + int ready; + ready = (REG_EMC_NFCSR & EMC_NFCSR_RB) ? 1 : 0; + return ready; +} + +/* + * EMC setup + */ +static void jz_device_setup(void) +{ + /* Set NFE bit */ + REG_EMC_NFCSR |= EMC_NFCSR_NFE; +} + +static void jzsoc_nand_enable_hwecc(struct mtd_info* mtd, int mode) +{ + __nand_ecc_enable(); +} + +static int jzsoc_nand_calculate_ecc(struct mtd_info* mtd, const u_char* dat, + u_char* ecc_code) +{ + unsigned int calc_ecc; + unsigned char *tmp; + + __nand_ecc_disable(); + + calc_ecc = ~(__nand_ecc()) | 0x00030000; + + tmp = (unsigned char *)&calc_ecc; + + ecc_code[0] = tmp[1]; + ecc_code[1] = tmp[0]; + ecc_code[2] = tmp[2]; + + return 0; +} + +/* ECC handling functions */ + +static int jzsoc_nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + u_char a, b, c, d1, d2, d3, add, bit, i; + + /* Do error detection */ + d1 = calc_ecc[0] ^ read_ecc[0]; + d2 = calc_ecc[1] ^ read_ecc[1]; + d3 = calc_ecc[2] ^ read_ecc[2]; + + if ((d1 | d2 | d3) == 0) { + /* No errors */ + return 0; + } + else { + a = (d1 ^ (d1 >> 1)) & 0x55; + b = (d2 ^ (d2 >> 1)) & 0x55; + c = (d3 ^ (d3 >> 1)) & 0x54; + + /* Found and will correct single bit error in the data */ + if ((a == 0x55) && (b == 0x55) && (c == 0x54)) { + c = 0x80; + add = 0; + a = 0x80; + for (i=0; i<4; i++) { + if (d1 & c) + add |= a; + c >>= 2; + a >>= 1; + } + c = 0x80; + for (i=0; i<4; i++) { + if (d2 & c) + add |= a; + c >>= 2; + a >>= 1; + } + bit = 0; + b = 0x04; + c = 0x80; + for (i=0; i<3; i++) { + if (d3 & c) + bit |= b; + c >>= 2; + b >>= 1; + } + b = 0x01; + a = dat[add]; + a ^= (b << bit); + dat[add] = a; + return 0; + } + else { + i = 0; + while (d1) { + if (d1 & 0x01) + ++i; + d1 >>= 1; + } + while (d2) { + if (d2 & 0x01) + ++i; + d2 >>= 1; + } + while (d3) { + if (d3 & 0x01) + ++i; + d3 >>= 1; + } + if (i == 1) { + /* ECC Code Error Correction */ + read_ecc[0] = calc_ecc[0]; + read_ecc[1] = calc_ecc[1]; + read_ecc[2] = calc_ecc[2]; + return 0; + } + else { + /* Uncorrectable Error */ + printk("uncorrectable ECC error\n"); + return -1; + } + } + } + + /* Should never happen */ + return -1; +} + + +/* + * Main initialization routine + */ +int __init jznand_init(void) +{ + struct nand_chip *this; + int nr_partitions; + + /* Allocate memory for MTD device structure and private data */ + jz_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), + GFP_KERNEL); + if (!jz_mtd) { + printk ("Unable to allocate JzSOC NAND MTD device structure.\n"); + return -ENOMEM; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (&jz_mtd[1]); + + /* Initialize structures */ + memset((char *) jz_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + jz_mtd->priv = this; + + /* Set & initialize NAND Flash controller */ + jz_device_setup(); + + /* Set address of NAND IO lines */ + this->IO_ADDR_R = (void __iomem *) NAND_DATA_PORT; + this->IO_ADDR_W = (void __iomem *) NAND_DATA_PORT; + this->cmd_ctrl = jz_hwcontrol; + this->dev_ready = jz_device_ready; + +#ifdef CONFIG_MTD_HW_HM_ECC + this->ecc.calculate = jzsoc_nand_calculate_ecc; + this->ecc.correct = jzsoc_nand_correct_data; + this->ecc.hwctl = jzsoc_nand_enable_hwecc; + this->ecc.mode = NAND_ECC_HW; + this->ecc.size = 256; + this->ecc.bytes = 3; + +#endif + +#ifdef CONFIG_MTD_SW_HM_ECC + this->eccmode = NAND_ECC_SOFT; +#endif + + /* 20 us command delay time */ + this->chip_delay = 20; + + /* Scan to find existance of the device */ + if (nand_scan(jz_mtd, 1)) { + kfree (jz_mtd); + return -ENXIO; + } + + /* Register the partitions */ + nr_partitions = sizeof(partition_info) / sizeof(struct mtd_partition); + printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nr_partitions, jz_mtd->name); + add_mtd_partitions(jz_mtd, partition_info, nr_partitions); + + return 0; +} +module_init(jznand_init); + +/* + * Clean up routine + */ +#ifdef MODULE +static void __exit jznand_cleanup(void) +{ + struct nand_chip *this = (struct nand_chip *) &jz_mtd[1]; + + /* Unregister partitions */ + del_mtd_partitions(jz_mtd); + + /* Unregister the device */ + del_mtd_device (jz_mtd); + + /* Free internal data buffers */ + kfree (this->data_buf); + + /* Free the MTD device structure */ + kfree (jz_mtd); +} +module_exit(jznand_cleanup); +#endif diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c new file mode 100644 index 0000000..5269670 --- /dev/null +++ b/drivers/mtd/nand/jz4740_nand.c @@ -0,0 +1,1123 @@ +/* + * linux/drivers/mtd/nand/jz4740_nand.c + * + * Copyright (c) 2005 - 2007 Ingenic Semiconductor Inc. + * + * Ingenic JZ4740 NAND 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. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define NAND_DATA_PORT1 0xB8000000 /* read-write area in static bank 1 */ +#define NAND_DATA_PORT2 0xB4000000 /* read-write area in static bank 2 */ +#define NAND_DATA_PORT3 0xAC000000 /* read-write area in static bank 3 */ +#define NAND_DATA_PORT4 0xA8000000 /* read-write area in static bank 4 */ + +#define PAR_SIZE 9 + +#define __nand_enable() (REG_EMC_NFCSR |= EMC_NFCSR_NFE1 | EMC_NFCSR_NFCE1) +#define __nand_disable() (REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1) + +#define __nand_ecc_enable() (REG_EMC_NFECR = EMC_NFECR_ECCE | EMC_NFECR_ERST ) +#define __nand_ecc_disable() (REG_EMC_NFECR &= ~EMC_NFECR_ECCE) + +#define __nand_select_hm_ecc() (REG_EMC_NFECR &= ~EMC_NFECR_RS ) +#define __nand_select_rs_ecc() (REG_EMC_NFECR |= EMC_NFECR_RS) + +#define __nand_read_hm_ecc() (REG_EMC_NFECC & 0x00ffffff) + +#define __nand_rs_ecc_encoding() (REG_EMC_NFECR |= EMC_NFECR_RS_ENCODING) +#define __nand_rs_ecc_decoding() (REG_EMC_NFECR &= ~EMC_NFECR_RS_ENCODING) +#define __nand_ecc_encode_sync() while (!(REG_EMC_NFINTS & EMC_NFINTS_ENCF)) +#define __nand_ecc_decode_sync() while (!(REG_EMC_NFINTS & EMC_NFINTS_DECF)) + +/* + * MTD structure for JzSOC board + */ +static struct mtd_info *jz_mtd = NULL; +extern struct mtd_info *jz_mtd1; +extern char all_use_planes; +extern int global_page; /* for two-plane operations */ + +int nr_partitions; /* Number of partitions */ + +/* + * Define partitions for flash devices + */ +#ifdef CONFIG_JZ4740_PAVO +struct mtd_partition partition_info[] = { + {name:"NAND BOOT partition", + offset:0 * 0x100000, + size:4 * 0x100000, + use_planes: 0, + mtdblock_jz_invalid: 1}, + {name:"NAND KERNEL partition", + offset:4 * 0x100000, + size:4 * 0x100000, + use_planes: 0, + mtdblock_jz_invalid: 1}, + {name:"NAND ROOTFS partition", + offset:8 * 0x100000, + size:504 * 0x100000, + use_planes: 0, + mtdblock_jz_invalid: 1}, + {name:"NAND VFAT partition", + offset:512 * 0x100000, + size:512 * 0x100000, + use_planes: 1, + mtdblock_jz_invalid: 0}, +}; + + +/* Define max reserved bad blocks for each partition. + * This is used by the mtdblock-jz.c NAND FTL driver only. + * + * The NAND FTL driver reserves some good blocks which can't be + * seen by the upper layer. When the bad block number of a partition + * exceeds the max reserved blocks, then there is no more reserved + * good blocks to be used by the NAND FTL driver when another bad + * block generated. + */ +static int partition_reserved_badblocks[] = { + 2, /* reserved blocks of mtd0 */ + 2, /* reserved blocks of mtd1 */ + 10, /* reserved blocks of mtd2 */ + 10, /* reserved blocks of mtd3 */ + 20, /* reserved blocks of mtd4 */ + 20}; /* reserved blocks of mtd5 */ +#endif /* CONFIG_JZ4740_PAVO */ + +#ifdef CONFIG_JZ4740_LEO +struct mtd_partition partition_info[] = { + { name: "NAND BOOT partition", + offset: 0 * 0x100000, + size: 4 * 0x100000, + use_planes: 0, + mtdblock_jz_invalid: 1 }, + { name: "NAND KERNEL partition", + offset: 4 * 0x100000, + size: 4 * 0x100000, + use_planes: 0, + mtdblock_jz_invalid: 1 }, + { name: "NAND ROOTFS partition", + offset: 8 * 0x100000, + size: 56 * 0x100000, + use_planes: 0, + mtdblock_jz_invalid: 1 }, + { name: "NAND VFAT partition", + offset: 64 * 0x100000, + size: 64 * 0x100000, + use_planes: 1, + mtdblock_jz_invalid: 0 }, +}; +static int partition_reserved_badblocks[] = { + 2, /* reserved blocks of mtd0 */ + 2, /* reserved blocks of mtd1 */ + 10, /* reserved blocks of mtd2 */ + 10}; /* reserved blocks of mtd3 */ +#endif /* CONFIG_JZ4740_LEO */ + +#ifdef CONFIG_JZ4740_LYRA +struct mtd_partition partition_info[] = { + { name: "NAND BOOT partition", + offset: 0 * 0x100000, + size: 4 * 0x100000, + use_planes: 0, + mtdblock_jz_invalid: 1 }, + { name: "NAND KERNEL partition", + offset: 4 * 0x100000, + size: 4 * 0x100000, + use_planes: 0, + mtdblock_jz_invalid: 1 }, + { name: "NAND ROOTFS partition", + offset: 8 * 0x100000, + size: 120 * 0x100000, + use_planes: 0, + mtdblock_jz_invalid: 1 }, + { name: "NAND DATA1 partition", + offset: 128 * 0x100000, + size: 128 * 0x100000, + use_planes: 0, + mtdblock_jz_invalid: 1 }, + { name: "NAND DATA2 partition", + offset: 256 * 0x100000, + size: 256 * 0x100000, + use_planes: 0, + mtdblock_jz_invalid: 1 }, + { name: "NAND VFAT partition", + offset: 512 * 0x100000, + size: 512 * 0x100000, + use_planes: 1, + mtdblock_jz_invalid: 0 }, +}; + +/* Define max reserved bad blocks for each partition. + * This is used by the mtdblock-jz.c NAND FTL driver only. + * + * The NAND FTL driver reserves some good blocks which can't be + * seen by the upper layer. When the bad block number of a partition + * exceeds the max reserved blocks, then there is no more reserved + * good blocks to be used by the NAND FTL driver when another bad + * block generated. + */ +static int partition_reserved_badblocks[] = { + 2, /* reserved blocks of mtd0 */ + 2, /* reserved blocks of mtd1 */ + 10, /* reserved blocks of mtd2 */ + 10, /* reserved blocks of mtd3 */ + 20, /* reserved blocks of mtd4 */ + 20}; /* reserved blocks of mtd5 */ +#endif /* CONFIG_JZ4740_LYRA */ + +#ifdef CONFIG_JZ4725_DIPPER +struct mtd_partition partition_info[] = { + { name: "NAND BOOT partition", + offset: 0 * 0x100000, + size: 4 * 0x100000, + use_planes: 0, + mtdblock_jz_invalid: 1 }, + { name: "NAND KERNEL partition", + offset: 4 * 0x100000, + size: 4 * 0x100000, + use_planes: 0, + mtdblock_jz_invalid: 1 }, + { name: "NAND ROOTFS partition", + offset: 8 * 0x100000, + size: 56 * 0x100000, + use_planes: 0, + mtdblock_jz_invalid: 1 }, + { name: "NAND VFAT partition", + offset: 64 * 0x100000, + size: 64 * 0x100000, + use_planes: 1, + mtdblock_jz_invalid: 0 }, +}; + +/* Define max reserved bad blocks for each partition. + * This is used by the mtdblock-jz.c NAND FTL driver only. + * + * The NAND FTL driver reserves some good blocks which can't be + * seen by the upper layer. When the bad block number of a partition + * exceeds the max reserved blocks, then there is no more reserved + * good blocks to be used by the NAND FTL driver when another bad + * block generated. + */ +static int partition_reserved_badblocks[] = { + 2, /* reserved blocks of mtd0 */ + 2, /* reserved blocks of mtd1 */ + 10, /* reserved blocks of mtd2 */ + 10}; /* reserved blocks of mtd3 */ +#endif /* CONFIG_JZ4740_DIPPER */ + +#ifdef CONFIG_JZ4720_VIRGO +struct mtd_partition partition_info[] = { + { name: "NAND BOOT partition", + offset: 0 * 0x100000, + size: 4 * 0x100000, + use_planes: 0, + mtdblock_jz_invalid: 1 }, + { name: "NAND KERNEL partition", + offset: 4 * 0x100000, + size: 4 * 0x100000, + use_planes: 0, + mtdblock_jz_invalid: 1 }, + { name: "NAND ROOTFS partition", + offset: 8 * 0x100000, + size: 120 * 0x100000, + use_planes: 0, + mtdblock_jz_invalid: 1 }, + { name: "NAND DATA1 partition", + offset: 128 * 0x100000, + size: 128 * 0x100000, + use_planes: 0, + mtdblock_jz_invalid: 1 }, + { name: "NAND DATA2 partition", + offset: 256 * 0x100000, + size: 256 * 0x100000, + use_planes: 0, + mtdblock_jz_invalid: 1 }, + { name: "NAND VFAT partition", + offset: 512 * 0x100000, + size: 512 * 0x100000, + use_planes: 1, + mtdblock_jz_invalid: 0 }, +}; + + +/* Define max reserved bad blocks for each partition. + * This is used by the mtdblock-jz.c NAND FTL driver only. + * + * The NAND FTL driver reserves some good blocks which can't be + * seen by the upper layer. When the bad block number of a partition + * exceeds the max reserved blocks, then there is no more reserved + * good blocks to be used by the NAND FTL driver when another bad + * block generated. + */ +static int partition_reserved_badblocks[] = { + 2, /* reserved blocks of mtd0 */ + 2, /* reserved blocks of mtd1 */ + 10, /* reserved blocks of mtd2 */ + 10, /* reserved blocks of mtd3 */ + 20, /* reserved blocks of mtd4 */ + 20}; /* reserved blocks of mtd5 */ +#endif /* CONFIG_JZ4720_VIRGO */ + +#ifdef CONFIG_JZ4740_QI_LB60 +static struct mtd_partition partition_info[] = { + { name: "NAND BOOT partition", + offset: 0 * 0x100000, + size: 4 * 0x100000, + use_planes: 0 }, + { name: "NAND KERNEL partition", + offset: 4 * 0x100000, + size: 4 * 0x100000, + use_planes: 0 }, + { name: "NAND ROOTFS partition", + offset: 8 * 0x100000, + size: 504 * 0x100000, + use_planes: 0 }, + { name: "NAND DATA1 partition", + offset: 512 * 0x100000, + size: 512 * 0x100000, + use_planes: 1 }, + { name: "NAND DATA2 partition", + offset: 1024 * 0x100000, + size: 512 * 0x100000, + use_planes: 1 }, + { name: "NAND VFAT partition", + offset: (1024 + 512) * 0x100000, + size: 512 * 0x100000, + use_planes: 1 }, +}; + +/* Define max reserved bad blocks for each partition. + * This is used by the mtdblock-jz.c NAND FTL driver only. + * + * The NAND FTL driver reserves some good blocks which can't be + * seen by the upper layer. When the bad block number of a partition + * exceeds the max reserved blocks, then there is no more reserved + * good blocks to be used by the NAND FTL driver when another bad + * block generated. + */ +static int partition_reserved_badblocks[] = { + 2, /* reserved blocks of mtd0 */ + 2, /* reserved blocks of mtd1 */ + 10, /* reserved blocks of mtd2 */ + 10, /* reserved blocks of mtd3 */ + 10, /* reserved blocks of mtd4 */ + 20}; /* reserved blocks of mtd5 */ +#endif /* CONFIG_JZ4740_QI_LB60 */ + +/*------------------------------------------------------------------------- + * Following three functions are exported and used by the mtdblock-jz.c + * NAND FTL driver only. + */ + +unsigned short get_mtdblock_write_verify_enable(void) +{ +#ifdef CONFIG_MTD_MTDBLOCK_WRITE_VERIFY_ENABLE + return 1; +#endif + return 0; +} +EXPORT_SYMBOL(get_mtdblock_write_verify_enable); + +unsigned short get_mtdblock_oob_copies(void) +{ + return CONFIG_MTD_OOB_COPIES; +} +EXPORT_SYMBOL(get_mtdblock_oob_copies); + +int *get_jz_badblock_table(void) +{ + return partition_reserved_badblocks; +} +EXPORT_SYMBOL(get_jz_badblock_table); + +/*-------------------------------------------------------------------------*/ + +static void jz_hwcontrol(struct mtd_info *mtd, int dat, + unsigned int ctrl) +{ + struct nand_chip *this = (struct nand_chip *)(mtd->priv); + unsigned int nandaddr = (unsigned int)this->IO_ADDR_W; + extern u8 nand_nce; /* in nand_base.c, indicates which chip select is used for current nand chip */ + + if (ctrl & NAND_CTRL_CHANGE) { + if (ctrl & NAND_NCE) { + switch (nand_nce) { + case NAND_NCE1: + this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT1; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4; + REG_EMC_NFCSR |= EMC_NFCSR_NFCE1; + break; + case NAND_NCE2: + this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT2; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4; + REG_EMC_NFCSR |= EMC_NFCSR_NFCE2; + break; + case NAND_NCE3: + this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT3; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4; + REG_EMC_NFCSR |= EMC_NFCSR_NFCE3; + break; + case NAND_NCE4: + this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT4; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3; + REG_EMC_NFCSR |= EMC_NFCSR_NFCE4; + break; + default: + printk("error: no nand_nce 0x%x\n",nand_nce); + break; + } + } else { + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4; + } + + if ( ctrl & NAND_ALE ) + nandaddr = (unsigned int)((unsigned long)(this->IO_ADDR_W) | 0x00010000); + else + nandaddr = (unsigned int)((unsigned long)(this->IO_ADDR_W) & ~0x00010000); + + if ( ctrl & NAND_CLE ) + nandaddr = nandaddr | 0x00008000; + else + nandaddr = nandaddr & ~0x00008000; + } + + this->IO_ADDR_W = (void __iomem *)nandaddr; + if (dat != NAND_CMD_NONE) + writeb(dat, this->IO_ADDR_W); +} + +static int jz_device_ready(struct mtd_info *mtd) +{ + int ready, wait = 10; + while (wait--); + ready = __gpio_get_pin(94); + return ready; +} + +/* + * EMC setup + */ +static void jz_device_setup(void) +{ +// PORT 0: +// ... +// PORT 1: +// PIN/BIT N FUNC0 FUNC1 +// 25 CS1# - +// 26 CS2# - +// 27 CS3# - +// 28 CS4# - +#define GPIO_CS2_N (32+26) +#define GPIO_CS3_N (32+27) +#define GPIO_CS4_N (32+28) +#define SMCR_VAL 0x0d221200 + + /* Set NFE bit */ + REG_EMC_NFCSR |= EMC_NFCSR_NFE1; + /* Read/Write timings */ + REG_EMC_SMCR1 = SMCR_VAL; + +#if defined(CONFIG_MTD_NAND_CS2) + /* Set CS2# pin as function 0 */ + __gpio_as_func0(GPIO_CS2_N); + REG_EMC_NFCSR |= EMC_NFCSR_NFE2; + REG_EMC_SMCR2 = SMCR_VAL; +#endif + +#if defined(CONFIG_MTD_NAND_CS3) + __gpio_as_func0(GPIO_CS3_N); + REG_EMC_NFCSR |= EMC_NFCSR_NFE3; + REG_EMC_SMCR3 = SMCR_VAL; +#endif + +#if defined(CONFIG_MTD_NAND_CS4) + __gpio_as_func0(GPIO_CS4_N); + REG_EMC_NFCSR |= EMC_NFCSR_NFE4; + REG_EMC_SMCR4 = SMCR_VAL; +#endif +} + +#ifdef CONFIG_MTD_HW_HM_ECC + +static int jzsoc_nand_calculate_hm_ecc(struct mtd_info* mtd, + const u_char* dat, u_char* ecc_code) +{ + unsigned int calc_ecc; + unsigned char *tmp; + + __nand_ecc_disable(); + + calc_ecc = ~(__nand_read_hm_ecc()) | 0x00030000; + + tmp = (unsigned char *)&calc_ecc; + //adjust eccbytes order for compatible with software ecc + ecc_code[0] = tmp[1]; + ecc_code[1] = tmp[0]; + ecc_code[2] = tmp[2]; + + return 0; +} + +static void jzsoc_nand_enable_hm_hwecc(struct mtd_info* mtd, int mode) +{ + __nand_ecc_enable(); + __nand_select_hm_ecc(); +} + +static int jzsoc_nand_hm_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + u_char a, b, c, d1, d2, d3, add, bit, i; + + /* Do error detection */ + d1 = calc_ecc[0] ^ read_ecc[0]; + d2 = calc_ecc[1] ^ read_ecc[1]; + d3 = calc_ecc[2] ^ read_ecc[2]; + + if ((d1 | d2 | d3) == 0) { + /* No errors */ + return 0; + } + else { + a = (d1 ^ (d1 >> 1)) & 0x55; + b = (d2 ^ (d2 >> 1)) & 0x55; + c = (d3 ^ (d3 >> 1)) & 0x54; + + /* Found and will correct single bit error in the data */ + if ((a == 0x55) && (b == 0x55) && (c == 0x54)) { + c = 0x80; + add = 0; + a = 0x80; + for (i=0; i<4; i++) { + if (d1 & c) + add |= a; + c >>= 2; + a >>= 1; + } + c = 0x80; + for (i=0; i<4; i++) { + if (d2 & c) + add |= a; + c >>= 2; + a >>= 1; + } + bit = 0; + b = 0x04; + c = 0x80; + for (i=0; i<3; i++) { + if (d3 & c) + bit |= b; + c >>= 2; + b >>= 1; + } + b = 0x01; + a = dat[add]; + a ^= (b << bit); + dat[add] = a; + return 0; + } + else { + i = 0; + while (d1) { + if (d1 & 0x01) + ++i; + d1 >>= 1; + } + while (d2) { + if (d2 & 0x01) + ++i; + d2 >>= 1; + } + while (d3) { + if (d3 & 0x01) + ++i; + d3 >>= 1; + } + if (i == 1) { + /* ECC Code Error Correction */ + read_ecc[0] = calc_ecc[0]; + read_ecc[1] = calc_ecc[1]; + read_ecc[2] = calc_ecc[2]; + return 0; + } + else { + /* Uncorrectable Error */ + printk("NAND: uncorrectable ECC error\n"); + return -1; + } + } + } + + /* Should never happen */ + return -1; +} + +#endif /* CONFIG_MTD_HW_HM_ECC */ + +#ifdef CONFIG_MTD_HW_RS_ECC + +static void jzsoc_nand_enable_rs_hwecc(struct mtd_info* mtd, int mode) +{ + REG_EMC_NFINTS = 0x0; + __nand_ecc_enable(); + __nand_select_rs_ecc(); + + if (mode == NAND_ECC_READ) + __nand_rs_ecc_decoding(); + + if (mode == NAND_ECC_WRITE) + __nand_rs_ecc_encoding(); +} + +static void jzsoc_rs_correct(unsigned char *dat, int idx, int mask) +{ + int i; + + idx--; + + i = idx + (idx >> 3); + if (i >= 512) + return; + + mask <<= (idx & 0x7); + + dat[i] ^= mask & 0xff; + if (i < 511) + dat[i+1] ^= (mask >> 8) & 0xff; +} + +/* + * calc_ecc points to oob_buf for us + */ +static int jzsoc_nand_rs_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + volatile u8 *paraddr = (volatile u8 *)EMC_NFPAR0; + short k; + u32 stat; + + /* Set PAR values */ + for (k = 0; k < PAR_SIZE; k++) { + *paraddr++ = read_ecc[k]; + } + + /* Set PRDY */ + REG_EMC_NFECR |= EMC_NFECR_PRDY; + + /* Wait for completion */ + __nand_ecc_decode_sync(); + __nand_ecc_disable(); + + /* Check decoding */ + stat = REG_EMC_NFINTS; + + if (stat & EMC_NFINTS_ERR) { + /* Error occurred */ + if (stat & EMC_NFINTS_UNCOR) { + printk("NAND: Uncorrectable ECC error\n"); + return -1; + } else { + u32 errcnt = (stat & EMC_NFINTS_ERRCNT_MASK) >> EMC_NFINTS_ERRCNT_BIT; + switch (errcnt) { + case 4: + jzsoc_rs_correct(dat, (REG_EMC_NFERR3 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR3 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT); + /* FALL-THROUGH */ + case 3: + jzsoc_rs_correct(dat, (REG_EMC_NFERR2 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR2 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT); + /* FALL-THROUGH */ + case 2: + jzsoc_rs_correct(dat, (REG_EMC_NFERR1 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR1 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT); + /* FALL-THROUGH */ + case 1: + jzsoc_rs_correct(dat, (REG_EMC_NFERR0 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR0 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT); + return 0; + default: + break; + } + } + } + + return 0; +} + +static int jzsoc_nand_calculate_rs_ecc(struct mtd_info* mtd, const u_char* dat, + u_char* ecc_code) +{ + volatile u8 *paraddr = (volatile u8 *)EMC_NFPAR0; + short i; + + __nand_ecc_encode_sync(); + __nand_ecc_disable(); + + for(i = 0; i < PAR_SIZE; i++) { + ecc_code[i] = *paraddr++; + } + + return 0; +} + +#endif /* CONFIG_MTD_HW_RS_ECC */ + +/* Nand optimized functions */ +static int dma_chan; +static unsigned int dma_src_phys_addr, dma_dst_phys_addr; +extern int jz_request_dma(int dev_id, const char *dev_str, + irqreturn_t (*irqhandler)(int, void *), + unsigned long irqflags, void *irq_dev_id); + +static void dma_setup(void) +{ + /* Request DMA channel and setup irq handler */ + dma_chan = jz_request_dma(DMA_ID_AUTO, "auto", NULL, IRQF_DISABLED, NULL); + if (dma_chan < 0) { + printk("Setup irq for nand failed!\n"); + return; + } else + printk("Nand DMA request channel %d.\n",dma_chan); +} + +static void jz4740_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + + if ((len <= 32) || (len & 0xf) || ((u32)buf >= (u32)high_memory)) + { + for (i = 0; i < len; i++) + buf[i] = readb(chip->IO_ADDR_R); + } else { + REG_DMAC_DRSR(dma_chan) = DMAC_DRSR_RS_AUTO; + dma_src_phys_addr = CPHYSADDR(chip->IO_ADDR_R); + dma_dst_phys_addr = CPHYSADDR(buf); + dma_cache_inv((u32)buf, len); + REG_DMAC_DSAR(dma_chan) = dma_src_phys_addr; + REG_DMAC_DTAR(dma_chan) = dma_dst_phys_addr; + REG_DMAC_DTCR(dma_chan) = len / 16; + REG_DMAC_DCMD(dma_chan) = DMAC_DCMD_DAI | DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_16BYTE; + REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_NDES | DMAC_DCCSR_EN; + REG_DMAC_DMACR = DMAC_DMACR_DMAE; /* global DMA enable bit */ + + while(!(REG_DMAC_DCCSR(dma_chan) & DMAC_DCCSR_TT)); + REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + __dmac_channel_clear_transmit_end(dma_chan); + } +} + +static void jz4740_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + + if ((len <= 32) || (len & 0xf) || ((u32)buf >= (u32)high_memory)) + { + for (i = 0; i < len; i++) + writeb(buf[i], chip->IO_ADDR_W); + } else { + REG_DMAC_DRSR(dma_chan) = DMAC_DRSR_RS_AUTO; + dma_dst_phys_addr = CPHYSADDR(chip->IO_ADDR_R); + dma_src_phys_addr = CPHYSADDR(buf); + dma_cache_wback((unsigned long)buf, len); + REG_DMAC_DSAR(dma_chan) = dma_src_phys_addr; + REG_DMAC_DTAR(dma_chan) = dma_dst_phys_addr; + REG_DMAC_DTCR(dma_chan) = len / 16; + REG_DMAC_DCMD(dma_chan) = DMAC_DCMD_SAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | DMAC_DCMD_DS_16BYTE ; + REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_NDES | DMAC_DCCSR_EN; + REG_DMAC_DMACR = DMAC_DMACR_DMAE; /* global DMA enable bit */ + + while(!(REG_DMAC_DCCSR(dma_chan) & DMAC_DCCSR_TT)); + REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + __dmac_channel_clear_transmit_end(dma_chan); + } +} + +static int nand_read_page_hwecc_rs_planes(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps >> 1; + uint8_t *p; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t page; + uint8_t flag = 0; + int oobsize = mtd->oobsize >> 1; + int ppb = mtd->erasesize / mtd->writesize; + int ecctotal = chip->ecc.total >> 1; + + page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ + + /* Read first page */ + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + chip->read_buf(mtd, chip->oob_poi, oobsize); + for (i = 0; i < ecctotal; i++) { + ecc_code[i] = chip->oob_poi[eccpos[i]]; + if (ecc_code[i] != 0xff) flag = 1; + } + + p = buf; + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0x00, -1); + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + if (flag) { + 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; + } + else { + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + } + } + /* Read second page */ + page += ppb; + flag = 0; + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + chip->read_buf(mtd, chip->oob_poi + oobsize, oobsize); + for (i = 0; i < ecctotal; i++) { + ecc_code[i] = chip->oob_poi[oobsize + eccpos[i]]; + if (ecc_code[i] != 0xff) flag = 1; + } + + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0x00, -1); + eccsteps = chip->ecc.steps >> 1; + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + if (flag) { + 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; + } + else { + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + } + } + + return 0; +} + +static int nand_read_oob_std_planes(struct mtd_info *mtd, struct nand_chip *chip, + int global_page, int sndcmd) +{ + int page; + int oobsize = mtd->oobsize >> 1; + int ppb = mtd->erasesize / mtd->writesize; + + page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ + + /* Read first page OOB */ + if (sndcmd) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + } + chip->read_buf(mtd, chip->oob_poi, oobsize); + /* Read second page OOB */ + page += ppb; + if (sndcmd) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + sndcmd = 0; + } + chip->read_buf(mtd, chip->oob_poi+oobsize, oobsize); + return 0; +} + +static int nand_write_oob_std_planes(struct mtd_info *mtd, struct nand_chip *chip, + int global_page) +{ + int status = 0,page; + int pagesize = mtd->writesize >> 1; + int oobsize = mtd->oobsize >> 1; + int ppb = mtd->erasesize / mtd->writesize; + const uint8_t *buf = chip->oob_poi; + + page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ + + /* send cmd 0x80, the MSB should be valid if realplane is 4 */ + if (chip->realplanenum == 2) + chip->cmdfunc(mtd, 0x80, pagesize, 0x00); + else + chip->cmdfunc(mtd, 0x80, pagesize, page & (1 << (chip->chip_shift - chip->page_shift))); + + chip->write_buf(mtd, buf, oobsize); + /* Send first command to program the OOB data */ + chip->cmdfunc(mtd, 0x11, -1, -1); + ndelay(100); + status = chip->waitfunc(mtd, chip); + + page += ppb; + buf += oobsize; + chip->cmdfunc(mtd, 0x81, pagesize, page); + chip->write_buf(mtd, buf, oobsize); + /* Send command to program the OOB data */ + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + /* Wait long R/B */ + ndelay(100); + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +static void nand_write_page_hwecc_planes(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps >> 1; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *p = (uint8_t *)buf; + uint32_t *eccpos = chip->ecc.layout->eccpos; + int oobsize = mtd->oobsize >> 1; + int ppb = mtd->erasesize / mtd->writesize; + int ecctotal = chip->ecc.total >> 1; + int page; + + page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ + + /* send cmd 0x80, the MSB should be valid if realplane is 4 */ + if (chip->realplanenum == 2) + chip->cmdfunc(mtd, 0x80, 0x00, 0x00); + else + chip->cmdfunc(mtd, 0x80, 0x00, page & (1 << (chip->chip_shift - chip->page_shift))); + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->write_buf(mtd, p, eccsize); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + } + for (i = 0; i < ecctotal; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; + + chip->write_buf(mtd, chip->oob_poi, oobsize); + + chip->cmdfunc(mtd, 0x11, -1, -1); /* send cmd 0x11 */ + ndelay(100); + while(!chip->dev_ready(mtd)); + + page += ppb; + chip->cmdfunc(mtd, 0x81, 0x00, page); /* send cmd 0x81 */ + eccsteps = chip->ecc.steps >> 1; + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->write_buf(mtd, p, eccsize); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + } + + for (i = 0; i < ecctotal; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; + + chip->write_buf(mtd, chip->oob_poi, oobsize); +} + +static void single_erase_cmd_planes(struct mtd_info *mtd, int global_page) +{ + struct nand_chip *chip = mtd->priv; + + /* Send commands to erase a block */ + int page; + int ppb = mtd->erasesize / mtd->writesize; + + page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ + + /* send cmd 0x60, the MSB should be valid if realplane is 4 */ + if (chip->realplanenum == 2) + chip->cmdfunc(mtd, 0x60, -1, 0x00); + else + chip->cmdfunc(mtd, 0x60, -1, page & (1 << (chip->chip_shift - chip->page_shift))); + + page += ppb; + chip->cmdfunc(mtd, 0x60, -1, page & (~(ppb-1))); /* send cmd 0x60 */ + + chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); /* send cmd 0xd0 */ + /* Do not need wait R/B or check status */ +} + +/* + * Main initialization routine + */ +int __init jznand_init(void) +{ + struct nand_chip *this; + int ret, i; + + /* Allocate memory for MTD device structure and private data */ + jz_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), + GFP_KERNEL); + if (!jz_mtd) { + printk ("Unable to allocate JzSOC NAND MTD device structure.\n"); + return -ENOMEM; + } + + jz_mtd1 = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), + GFP_KERNEL); + if (!jz_mtd1) { + printk ("Unable to allocate JzSOC NAND MTD device structure 1.\n"); + kfree(jz_mtd); + return -ENOMEM; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (&jz_mtd[1]); + + /* Initialize structures */ + memset((char *) jz_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + jz_mtd->priv = this; + + /* Set & initialize NAND Flash controller */ + jz_device_setup(); + + /* Set address of NAND IO lines */ + this->IO_ADDR_R = (void __iomem *) NAND_DATA_PORT1; + this->IO_ADDR_W = (void __iomem *) NAND_DATA_PORT1; + this->cmd_ctrl = jz_hwcontrol; + this->dev_ready = jz_device_ready; + +#ifdef CONFIG_MTD_HW_HM_ECC + this->ecc.calculate = jzsoc_nand_calculate_hm_ecc; + this->ecc.correct = jzsoc_nand_hm_correct_data; + this->ecc.hwctl = jzsoc_nand_enable_hm_hwecc; + this->ecc.mode = NAND_ECC_HW; + this->ecc.size = 256; + this->ecc.bytes = 3; + +#endif + +#ifdef CONFIG_MTD_HW_RS_ECC + this->ecc.calculate = jzsoc_nand_calculate_rs_ecc; + this->ecc.correct = jzsoc_nand_rs_correct_data; + this->ecc.hwctl = jzsoc_nand_enable_rs_hwecc; + this->ecc.mode = NAND_ECC_HW; + this->ecc.size = 512; + this->ecc.bytes = 9; +#endif + +#ifdef CONFIG_MTD_SW_HM_ECC + this->ecc.mode = NAND_ECC_SOFT; +#endif + /* 20 us command delay time */ + this->chip_delay = 20; + +#ifdef CONFIG_MTD_NAND_DMA + dma_setup(); +#endif + /* Scan to find existance of the device */ + ret = nand_scan_ident(jz_mtd, NAND_MAX_CHIPS); + if (!ret) { + if (this->planenum == 2) { + /* reset nand functions */ + this->erase_cmd = single_erase_cmd_planes; + this->ecc.read_page = nand_read_page_hwecc_rs_planes; //Muti planes read + this->ecc.write_page = nand_write_page_hwecc_planes; + this->ecc.read_oob = nand_read_oob_std_planes; + this->ecc.write_oob = nand_write_oob_std_planes; +#ifdef CONFIG_MTD_NAND_DMA + this->write_buf = jz4740_nand_write_buf; + this->read_buf = jz4740_nand_read_buf; +#endif + printk(KERN_INFO "Nand using two-plane mode, " + "and resized to writesize:%d oobsize:%d blocksize:0x%x \n", + jz_mtd->writesize, jz_mtd->oobsize, jz_mtd->erasesize); + } + } + + /* Determine whether all the partitions will use multiple planes if supported */ + nr_partitions = sizeof(partition_info) / sizeof(struct mtd_partition); + all_use_planes = 1; + for (i = 0; i < nr_partitions; i++) { + all_use_planes &= partition_info[i].use_planes; + } + + if (!ret) + ret = nand_scan_tail(jz_mtd); + + if (ret){ + kfree (jz_mtd1); + kfree (jz_mtd); + return -ENXIO; + } + + /* Register the partitions */ + printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nr_partitions, jz_mtd->name); + + if ((this->planenum == 2) && !all_use_planes) { + for (i = 0; i < nr_partitions; i++) { + if (partition_info[i].use_planes) + add_mtd_partitions(jz_mtd, &partition_info[i], 1); + else + add_mtd_partitions(jz_mtd1, &partition_info[i], 1); + } + } else { + kfree(jz_mtd1); + add_mtd_partitions(jz_mtd, partition_info, nr_partitions); + } + return 0; +} +module_init(jznand_init); + +/* + * Clean up routine + */ +#ifdef MODULE +static void __exit jznand_cleanup(void) +{ + struct nand_chip *this = (struct nand_chip *) &jz_mtd[1]; + + /* Unregister partitions */ + del_mtd_partitions(jz_mtd); + + /* Unregister the device */ + del_mtd_device (jz_mtd); + + /* Free internal data buffers */ + kfree (this->data_buf); + + /* Free the MTD device structure */ + if ((this->planenum == 2) && !all_use_planes) + kfree (jz_mtd1); + kfree (jz_mtd); +} +module_exit(jznand_cleanup); +#endif diff --git a/drivers/mtd/nand/jz4750_nand.c b/drivers/mtd/nand/jz4750_nand.c new file mode 100644 index 0000000..f50fe74 --- /dev/null +++ b/drivers/mtd/nand/jz4750_nand.c @@ -0,0 +1,1857 @@ +/* + * linux/drivers/mtd/nand/jz4750_nand.c + * + * JZ4750 NAND driver + * + * Copyright (c) 2005 - 2007 Ingenic Semiconductor Inc. + * Author: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +/* 32bit instead of 16byte burst is used by DMA to read or + write NAND and BCH avoiding grabbing bus for too long */ +#define DMAC_DCMD_DS_NAND DMAC_DCMD_DS_32BIT +#define DIV_DS_NAND 4 + +#define DMAC_DCMD_DS_BCH DMAC_DCMD_DS_32BIT +#define DIV_DS_BCH 4 + +#define DEBUG1 0 +#if DEBUG1 +#define dprintk(n,x...) printk(n,##x) +#else +#define dprintk(n,x...) +#endif + +#if defined(CONFIG_MTD_HW_BCH_8BIT) +#define __ECC_ENCODING __ecc_encoding_8bit +#define __ECC_DECODING __ecc_decoding_8bit +#define ERRS_SIZE 5 /* 5 words */ +#else +#define __ECC_ENCODING __ecc_encoding_4bit +#define __ECC_DECODING __ecc_decoding_4bit +#define ERRS_SIZE 3 /* 3 words */ +#endif + +#define NAND_DATA_PORT1 0xB8000000 /* read-write area in static bank 1 */ +#define NAND_DATA_PORT2 0xB4000000 /* read-write area in static bank 2 */ +#define NAND_DATA_PORT3 0xAC000000 /* read-write area in static bank 3 */ +#define NAND_DATA_PORT4 0xA8000000 /* read-write area in static bank 4 */ + +#define NAND_ADDR_OFFSET0 0x00010000 /* address port offset for share mode */ +#define NAND_CMD_OFFSET0 0x00008000 /* command port offset for share mode */ +#define NAND_ADDR_OFFSET1 0x00000010 /* address port offset for unshare mode */ +#define NAND_CMD_OFFSET1 0x00000008 /* command port offset for unshare mode */ + +#if defined(CONFIG_MTD_NAND_DMA) +#define USE_IRQ 1 +enum { + NAND_NONE, + NAND_PROG, + NAND_READ +}; +static volatile u8 nand_status; +static volatile int dma_ack = 0; +static volatile int dma_ack1 = 0; +static char nand_dma_chan; /* automatically select a free channel */ +static char bch_dma_chan = 0; /* fixed to channel 0 */ +static u32 *errs; +static jz_dma_desc_8word *dma_desc_enc, *dma_desc_enc1, *dma_desc_dec, *dma_desc_dec1, *dma_desc_dec2, + *dma_desc_nand_prog, *dma_desc_nand_read; +static u32 *pval_nand_ddr; +static u8 *pval_nand_cmd_pgprog; /* for sending 0x11 or 0x10 when programing*/ +#if defined(CONFIG_MTD_NAND_DMABUF) +u8 *prog_buf, *read_buf; +#endif +DECLARE_WAIT_QUEUE_HEAD(nand_prog_wait_queue); +DECLARE_WAIT_QUEUE_HEAD(nand_read_wait_queue); +#endif + +struct buf_be_corrected { + u8 *data; + u8 *oob; +}; + +static u32 addr_offset; +static u32 cmd_offset; +static int nand_chips = 1; /* Number of nand chips to be scanned */ +extern int global_page; /* for two-plane operations */ + +/* + * MTD structure for JzSOC board + */ +static struct mtd_info *jz_mtd = NULL; +extern struct mtd_info *jz_mtd1; +extern char all_use_planes; + +int nr_partitions; /* Number of partitions */ + +/* + * Define partitions for flash devices + */ +#if defined(CONFIG_JZ4750_FUWA) || defined(CONFIG_JZ4750D_FUWA1) || defined(CONFIG_JZ4750D_CETUS) +struct mtd_partition partition_info[] = { + {name:"NAND BOOT partition", + offset:0 * 0x100000, + size:4 * 0x100000, + cpu_mode: 1, + use_planes: 0, + mtdblock_jz_invalid: 1}, + {name:"NAND KERNEL partition", + offset:4 * 0x100000, + size:4 * 0x100000, + cpu_mode: 1, + use_planes: 0, + mtdblock_jz_invalid: 1}, + {name:"NAND ROOTFS partition", + offset:8 * 0x100000, + size:120 * 0x100000, + cpu_mode: 1, + use_planes: 0, + mtdblock_jz_invalid: 1}, + {name:"NAND DATA1 partition", + offset:128 * 0x100000, + size:128 * 0x100000, + cpu_mode: 1, + use_planes: 1, + mtdblock_jz_invalid: 1}, + {name:"NAND DATA2 partition", + offset:256 * 0x100000, + size:256 * 0x100000, + cpu_mode: 1, + use_planes: 1, + mtdblock_jz_invalid: 1}, + {name:"NAND VFAT partition", + offset:512 * 0x100000, + size:512 * 0x100000, + cpu_mode: 0, + use_planes: 1, + mtdblock_jz_invalid: 0}, +}; + +/* Define max reserved bad blocks for each partition. + * This is used by the mtdblock-jz.c NAND FTL driver only. + * + * The NAND FTL driver reserves some good blocks which can't be + * seen by the upper layer. When the bad block number of a partition + * exceeds the max reserved blocks, then there is no more reserved + * good blocks to be used by the NAND FTL driver when another bad + * block generated. + */ +static int partition_reserved_badblocks[] = { + 2, /* reserved blocks of mtd0 */ + 2, /* reserved blocks of mtd1 */ + 10, /* reserved blocks of mtd2 */ + 10, /* reserved blocks of mtd3 */ + 20, /* reserved blocks of mtd4 */ + 20 +}; /* reserved blocks of mtd5 */ +#endif /* CONFIG_JZ4750_FUWA */ + +#if defined(CONFIG_JZ4750_APUS) +struct mtd_partition partition_info[] = { + {name:"NAND BOOT partition", + offset:0 * 0x100000, + size:4 * 0x100000, + cpu_mode: 0, + use_planes: 0, + mtdblock_jz_invalid: 1}, + {name:"NAND KERNEL partition", + offset:4 * 0x100000, + size:4 * 0x100000, + cpu_mode: 0, + use_planes: 0, + mtdblock_jz_invalid: 1}, + {name:"NAND ROOTFS partition", + offset:8 * 0x100000, + size:504 * 0x100000, + cpu_mode: 1, + use_planes: 0, + mtdblock_jz_invalid: 1}, + {name:"NAND DATA partition", + offset:512 * 0x100000, + size:512 * 0x100000, + cpu_mode: 0, + use_planes: 1, + mtdblock_jz_invalid: 1}, + {name:"NAND VFAT partition", + offset:1024 * 0x100000, + size:1024 * 0x100000, + cpu_mode: 0, + use_planes: 1, + mtdblock_jz_invalid: 0}, +}; + + +/* Define max reserved bad blocks for each partition. + * This is used by the mtdblock-jz.c NAND FTL driver only. + * + * The NAND FTL driver reserves some good blocks which can't be + * seen by the upper layer. When the bad block number of a partition + * exceeds the max reserved blocks, then there is no more reserved + * good blocks to be used by the NAND FTL driver when another bad + * block generated. + */ +static int partition_reserved_badblocks[] = { + 2, /* reserved blocks of mtd0 */ + 2, /* reserved blocks of mtd1 */ + 10, /* reserved blocks of mtd2 */ + 10, /* reserved blocks of mtd3 */ + 10, /* reserved blocks of mtd4 */ +}; +#endif /* CONFIG_JZ4750_APUS */ + +/*------------------------------------------------------------------------- + * Following three functions are exported and used by the mtdblock-jz.c + * NAND FTL driver only. + */ + +unsigned short get_mtdblock_write_verify_enable(void) +{ +#ifdef CONFIG_MTD_MTDBLOCK_WRITE_VERIFY_ENABLE + return 1; +#endif + return 0; +} + +EXPORT_SYMBOL(get_mtdblock_write_verify_enable); + +unsigned short get_mtdblock_oob_copies(void) +{ + return CONFIG_MTD_OOB_COPIES; +} + +EXPORT_SYMBOL(get_mtdblock_oob_copies); + +int *get_jz_badblock_table(void) +{ + if (sizeof(partition_reserved_badblocks) / sizeof(int) != nr_partitions) + printk("partition_reserved_badblocks setting error!\n"); + + return partition_reserved_badblocks; +} + +EXPORT_SYMBOL(get_jz_badblock_table); + +/*-------------------------------------------------------------------------*/ + +static void jz_hwcontrol(struct mtd_info *mtd, int dat, u32 ctrl) +{ + struct nand_chip *this = (struct nand_chip *)(mtd->priv); + u32 nandaddr = (u32)this->IO_ADDR_W; + extern u8 nand_nce; /* defined in nand_base.c, indicates which chip select is used for current nand chip */ + + if (ctrl & NAND_CTRL_CHANGE) { + if (ctrl & NAND_NCE) { + switch (nand_nce) { + case NAND_NCE1: + this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT1; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4; + REG_EMC_NFCSR |= EMC_NFCSR_NFCE1; + break; + case NAND_NCE2: + this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT2; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4; + REG_EMC_NFCSR |= EMC_NFCSR_NFCE2; + break; + case NAND_NCE3: + this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT3; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4; + REG_EMC_NFCSR |= EMC_NFCSR_NFCE3; + break; + case NAND_NCE4: + this->IO_ADDR_W = this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT4; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3; + REG_EMC_NFCSR |= EMC_NFCSR_NFCE4; + break; + default: + printk("error: no nand_nce 0x%x\n",nand_nce); + break; + } + } else { + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE1; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE2; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE3; + REG_EMC_NFCSR &= ~EMC_NFCSR_NFCE4; + } + + if (ctrl & NAND_ALE) + nandaddr = (u32)((u32)(this->IO_ADDR_W) | addr_offset); + else + nandaddr = (u32)((u32)(this->IO_ADDR_W) & ~addr_offset); + if (ctrl & NAND_CLE) + nandaddr = (u32)(nandaddr | cmd_offset); + else + nandaddr = (u32)(nandaddr & ~cmd_offset); + } + + this->IO_ADDR_W = (void __iomem *)nandaddr; + if (dat != NAND_CMD_NONE) { + writeb(dat, this->IO_ADDR_W); + /* printk("write cmd:0x%x to 0x%x\n",dat,(u32)this->IO_ADDR_W); */ + } +} + +static int jz_device_ready(struct mtd_info *mtd) +{ + int ready, wait = 10; + while (wait--); + ready = __gpio_get_pin(91); + return ready; +} + +/* + * EMC setup + */ +static void jz_device_setup(void) +{ +// PORT 0: +// PORT 1: +// PORT 2: +// PIN/BIT N FUNC0 FUNC1 +// 21 CS1# - +// 22 CS2# - +// 23 CS3# - +// 24 CS4# - +#define GPIO_CS2_N (32*2+22) +#define GPIO_CS3_N (32*2+23) +#define GPIO_CS4_N (32*2+24) +#define SMCR_VAL 0x0d444400 + + __gpio_as_nand_8bit(1); + /* Set NFE bit */ + REG_EMC_NFCSR |= EMC_NFCSR_NFE1; + /* Read/Write timings */ + REG_EMC_SMCR1 = SMCR_VAL; + +#if defined(CONFIG_MTD_NAND_CS2) + __gpio_as_func0(GPIO_CS2_N); + /* Set NFE bit */ + REG_EMC_NFCSR |= EMC_NFCSR_NFE2; + /* Read/Write timings */ + REG_EMC_SMCR2 = SMCR_VAL; + nand_chips++; +#endif + +#if defined(CONFIG_MTD_NAND_CS3) + __gpio_as_func0(GPIO_CS3_N); + /* Set NFE bit */ + REG_EMC_NFCSR |= EMC_NFCSR_NFE3; + /* Read/Write timings */ + REG_EMC_SMCR3 = SMCR_VAL; + nand_chips++; +#endif + +#if defined(CONFIG_MTD_NAND_CS4) + __gpio_as_func0(GPIO_CS4_N); + /* Set NFE bit */ + REG_EMC_NFCSR |= EMC_NFCSR_NFE4; + /* Read/Write timings */ + REG_EMC_SMCR4 = SMCR_VAL; + nand_chips++; +#endif +} + +#ifdef CONFIG_MTD_HW_BCH_ECC + +static void jzsoc_nand_enable_bch_hwecc(struct mtd_info *mtd, int mode) +{ + struct nand_chip *this = (struct nand_chip *)(mtd->priv); + int eccsize = this->ecc.size; + int eccbytes = this->ecc.bytes; + int eccsteps = this->ecc.steps / this->planenum; + int oob_per_eccsize = this->ecc.layout->eccpos[0] / eccsteps; + + REG_BCH_INTS = 0xffffffff; + if (mode == NAND_ECC_READ) { + __ECC_DECODING(); + __ecc_cnt_dec(eccsize + oob_per_eccsize + eccbytes); + + if (!(mtd->flags & MTD_NAND_CPU_MODE)) + __ecc_dma_enable(); + else + __ecc_dma_disable(); + } + + if (mode == NAND_ECC_WRITE) { + __ECC_ENCODING(); + __ecc_cnt_enc(eccsize + oob_per_eccsize); + + if (!(mtd->flags & MTD_NAND_CPU_MODE)) + __ecc_dma_enable(); + else + __ecc_dma_disable(); + } +} + +/** + * bch_correct + * @dat: data to be corrected + * @idx: the index of error bit in an eccsize + */ +static void bch_correct(struct mtd_info *mtd, u8 * dat, int idx) +{ + struct nand_chip *this = (struct nand_chip *)(mtd->priv); + int eccsize = this->ecc.size; + int eccsteps = this->ecc.steps / this->planenum; + int ecc_pos = this->ecc.layout->eccpos[0]; + int oob_per_eccsize = ecc_pos / eccsteps; + int i, bit; /* the 'bit' of i byte is error */ + + i = (idx - 1) >> 3; + bit = (idx - 1) & 0x7; + + dprintk("error:i=%d, bit=%d\n",i,bit); + + if (i < eccsize){ + ((struct buf_be_corrected *)dat)->data[i] ^= (1 << bit); + } else if (i < eccsize + oob_per_eccsize) { + ((struct buf_be_corrected *)dat)->oob[i-eccsize] ^= (1 << bit); + } +} + +#if defined(CONFIG_MTD_NAND_DMA) + +/** + * jzsoc_nand_bch_correct_data + * @mtd: mtd info structure + * @dat: data to be corrected + * @errs0: pointer to the dma target buffer of bch decoding which stores BHINTS and + * BHERR0~3(8-bit BCH) or BHERR0~1(4-bit BCH) + * @calc_ecc: no used + */ +static int jzsoc_nand_bch_correct_data(struct mtd_info *mtd, u_char * dat, u_char * errs0, u_char * calc_ecc) +{ + u32 stat; + u32 *errs = (u32 *)errs0; + + if (REG_DMAC_DCCSR(0) & DMAC_DCCSR_BERR) { + stat = errs[0]; + dprintk("stat=%x err0:%x err1:%x \n", stat, errs[1], errs[2]); + + if (stat & BCH_INTS_ERR) { + if (stat & BCH_INTS_UNCOR) { + printk("NAND: Uncorrectable ECC error\n"); + return -1; + } else { + u32 errcnt = (stat & BCH_INTS_ERRC_MASK) >> BCH_INTS_ERRC_BIT; + switch (errcnt) { +#if defined(CONFIG_MTD_HW_BCH_8BIT) + case 8: + bch_correct(mtd, dat, (errs[4] & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT); + case 7: + bch_correct(mtd, dat, (errs[4] & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT); + case 6: + bch_correct(mtd, dat, (errs[3] & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT); + case 5: + bch_correct(mtd, dat, (errs[3] & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT); +#endif + case 4: + bch_correct(mtd, dat, (errs[2] & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT); + case 3: + bch_correct(mtd, dat, (errs[2] & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT); + case 2: + bch_correct(mtd, dat, (errs[1] & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT); + case 1: + bch_correct(mtd, dat, (errs[1] & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT); + default: + break; + } + } + } + } + + return 0; +} + +#endif /* CONFIG_MTD_NAND_DMA */ + +/** + * jzsoc_nand_bch_correct_data_cpu + * @mtd: mtd info structure + * @dat: data to be corrected + * @read_ecc: pointer to ecc buffer calculated when nand writing + * @calc_ecc: no used + */ +static int jzsoc_nand_bch_correct_data_cpu(struct mtd_info *mtd, u_char * dat, u_char * read_ecc, u_char * calc_ecc) +{ + struct nand_chip *this = (struct nand_chip *)(mtd->priv); + int eccsize = this->ecc.size; + int eccbytes = this->ecc.bytes; + int eccsteps = this->ecc.steps / this->planenum; + int ecc_pos = this->ecc.layout->eccpos[0]; + int oob_per_eccsize = ecc_pos / eccsteps; + short k; + u32 stat; + + /* Write data to REG_BCH_DR */ + for (k = 0; k < eccsize; k++) { + REG_BCH_DR = ((struct buf_be_corrected *)dat)->data[k]; + } + /* Write oob to REG_BCH_DR */ + for (k = 0; k < oob_per_eccsize; k++) { + REG_BCH_DR = ((struct buf_be_corrected *)dat)->oob[k]; + } + /* Write parities to REG_BCH_DR */ + for (k = 0; k < eccbytes; k++) { + REG_BCH_DR = read_ecc[k]; + } + + /* Wait for completion */ + __ecc_decode_sync(); + __ecc_disable(); + + /* Check decoding */ + stat = REG_BCH_INTS; + + if (stat & BCH_INTS_ERR) { + /* Error occurred */ + if (stat & BCH_INTS_UNCOR) { + printk("NAND: Uncorrectable ECC error--\n"); + return -1; + } else { + u32 errcnt = (stat & BCH_INTS_ERRC_MASK) >> BCH_INTS_ERRC_BIT; + switch (errcnt) { +#if defined(CONFIG_MTD_HW_BCH_8BIT) + case 8: + bch_correct(mtd, dat, (REG_BCH_ERR3 & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT); + /* FALL-THROUGH */ + case 7: + bch_correct(mtd, dat, (REG_BCH_ERR3 & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT); + /* FALL-THROUGH */ + case 6: + bch_correct(mtd, dat, (REG_BCH_ERR2 & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT); + /* FALL-THROUGH */ + case 5: + bch_correct(mtd, dat, (REG_BCH_ERR2 & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT); + /* FALL-THROUGH */ +#endif + case 4: + bch_correct(mtd, dat, (REG_BCH_ERR1 & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT); + /* FALL-THROUGH */ + case 3: + bch_correct(mtd, dat, (REG_BCH_ERR1 & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT); + /* FALL-THROUGH */ + case 2: + bch_correct(mtd, dat, (REG_BCH_ERR0 & BCH_ERR_INDEX_ODD_MASK) >> BCH_ERR_INDEX_ODD_BIT); + /* FALL-THROUGH */ + case 1: + bch_correct(mtd, dat, (REG_BCH_ERR0 & BCH_ERR_INDEX_EVEN_MASK) >> BCH_ERR_INDEX_EVEN_BIT); + return 0; + default: + break; + } + } + } + + return 0; +} + + +static int jzsoc_nand_calculate_bch_ecc(struct mtd_info *mtd, const u_char * dat, u_char * ecc_code) +{ + struct nand_chip *this = (struct nand_chip *)(mtd->priv); + int eccsize = this->ecc.size; + int eccbytes = this->ecc.bytes; + int eccsteps = this->ecc.steps / this->planenum; + int ecc_pos = this->ecc.layout->eccpos[0]; + int oob_per_eccsize = ecc_pos / eccsteps; + volatile u8 *paraddr = (volatile u8 *)BCH_PAR0; + short i; + + /* Write data to REG_BCH_DR */ + for (i = 0; i < eccsize; i++) { + REG_BCH_DR = ((struct buf_be_corrected *)dat)->data[i]; + } + /* Write oob to REG_BCH_DR */ + for (i = 0; i < oob_per_eccsize; i++) { + REG_BCH_DR = ((struct buf_be_corrected *)dat)->oob[i]; + } + __ecc_encode_sync(); + __ecc_disable(); + + for (i = 0; i < eccbytes; i++) { + ecc_code[i] = *paraddr++; + } + + return 0; +} + +#if defined(CONFIG_MTD_NAND_DMA) + +/** + * nand_write_page_hwecc_bch - [REPLACABLE] hardware ecc based page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + */ +static void nand_write_page_hwecc_bch0(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t * buf, u8 cmd_pgprog) +{ + int eccsize = chip->ecc.size; + int eccsteps = chip->ecc.steps / chip->planenum; + int eccbytes = chip->ecc.bytes; + int ecc_pos = chip->ecc.layout->eccpos[0]; + int oob_per_eccsize = ecc_pos / eccsteps; + int pagesize = mtd->writesize / chip->planenum; + int oobsize = mtd->oobsize / chip->planenum; + int i, err, timeout; + const u8 *databuf; + u8 *oobbuf; + jz_dma_desc_8word *desc; + +#if defined(CONFIG_MTD_NAND_DMABUF) + memcpy(prog_buf, buf, pagesize); + memcpy(prog_buf + pagesize, chip->oob_poi, oobsize); + dma_cache_wback_inv((u32)prog_buf, pagesize + oobsize); +#else + databuf = buf; + oobbuf = chip->oob_poi; + + /* descriptors for encoding data blocks */ + desc = dma_desc_enc; + for (i = 0; i < eccsteps; i++) { + desc->dsadr = CPHYSADDR((u32)databuf) + i * eccsize; /* DMA source address */ + desc->dtadr = CPHYSADDR((u32)oobbuf) + ecc_pos + i * eccbytes; /* DMA target address */ + dprintk("dma_desc_enc:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, + desc->ddadr); + desc++; + } + + /* descriptors for encoding oob blocks */ + desc = dma_desc_enc1; + for (i = 0; i < eccsteps; i++) { + desc->dsadr = CPHYSADDR((u32)oobbuf) + oob_per_eccsize * i; /* DMA source address, 28/4 = 7bytes */ + desc->dtadr = CPHYSADDR((u32)oobbuf) + ecc_pos + i * eccbytes; /* DMA target address */ + dprintk("dma_desc_enc1:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, + desc->ddadr); + desc++; + } + + /* descriptor for nand programing data block */ + desc = dma_desc_nand_prog; + desc->dsadr = CPHYSADDR((u32)databuf); /* DMA source address */ + desc->dtadr = CPHYSADDR((u32)chip->IO_ADDR_W); /* It will be changed when using multiply chip select */ + dprintk("dma_desc_nand_prog:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, + desc->ddadr); + + /* descriptor for nand programing oob block */ + desc++; + desc->dsadr = CPHYSADDR((u32)oobbuf); /* DMA source address */ + desc->dtadr = CPHYSADDR((u32)chip->IO_ADDR_W); /* It will be changed when using multiply chip select */ + dprintk("dma_desc_oob_prog:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, + desc->ddadr); + + /* descriptor for __nand_cmd(CMD_PGPROG) */ + desc++; + *pval_nand_cmd_pgprog = cmd_pgprog; + desc->dsadr = CPHYSADDR((u32)pval_nand_cmd_pgprog); + desc->dtadr = CPHYSADDR((u32)chip->IO_ADDR_R | cmd_offset); /* DMA target address: cmdport */ + if (cmd_pgprog == 0x10) + desc->dcmd |= DMAC_DCMD_LINK; /* __nand_sync() by a DMA descriptor */ + else if (cmd_pgprog == 0x11) + desc->dcmd &= ~DMAC_DCMD_LINK; /* __nand_sync() by polling */ + + dma_cache_wback_inv((u32)dma_desc_enc, (eccsteps * 2 + 2 + 1) * (sizeof(jz_dma_desc_8word))); + dma_cache_wback_inv((u32)databuf, pagesize); + dma_cache_wback_inv((u32)oobbuf, oobsize); + /* 4*6: pval_nand_ddr, pval_nand_dcs, pval_bch_ddr, pval_bch_dcs, dummy, pval_nand_cmd_pgprog */ + dma_cache_wback_inv((u32)pval_nand_ddr, 4 * 8); /* 8 words, a cache line */ +#endif + + REG_DMAC_DCCSR(bch_dma_chan) = 0; + REG_DMAC_DCCSR(nand_dma_chan) = 0; + + /* Setup DMA descriptor address */ + REG_DMAC_DDA(bch_dma_chan) = CPHYSADDR((u32)dma_desc_enc); + REG_DMAC_DDA(nand_dma_chan) = CPHYSADDR((u32)dma_desc_nand_prog); + + /* Setup request source */ + REG_DMAC_DRSR(bch_dma_chan) = DMAC_DRSR_RS_BCH_ENC; + REG_DMAC_DRSR(nand_dma_chan) = DMAC_DRSR_RS_AUTO; + + /* Setup DMA channel control/status register */ + REG_DMAC_DCCSR(bch_dma_chan) = DMAC_DCCSR_DES8 | DMAC_DCCSR_EN; /* descriptor transfer, clear status, start channel */ + + /* Enable DMA */ + REG_DMAC_DMACR(0) |= DMAC_DMACR_DMAE; + REG_DMAC_DMACR(nand_dma_chan/HALF_DMA_NUM) |= DMAC_DMACR_DMAE; + + /* Enable BCH encoding */ + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + + dma_ack1 = 0; + nand_status = NAND_PROG; + + /* DMA doorbell set -- start DMA now ... */ + __dmac_channel_set_doorbell(bch_dma_chan); + +#if USE_IRQ + if (cmd_pgprog == 0x10) { + dprintk("nand prog before wake up\n"); + err = wait_event_interruptible_timeout(nand_prog_wait_queue, dma_ack1, 3 * HZ); + nand_status = NAND_NONE; + dprintk("nand prog after wake up\n"); + if (!err) { + printk("*** NAND WRITE, Warning, wait event 3s timeout!\n"); + dump_jz_dma_channel(0); + dump_jz_dma_channel(nand_dma_chan); + printk("REG_BCH_CR=%x REG_BCH_CNT=0x%x REG_BCH_INTS=%x\n", REG_BCH_CR, REG_BCH_CNT, REG_BCH_INTS); + } + dprintk("timeout remain = %d\n", err); + } else if (cmd_pgprog == 0x11) { + timeout = 100000; + while ((!__dmac_channel_transmit_end_detected(nand_dma_chan)) && (timeout--)); + if (timeout <= 0) + printk("two-plane prog 0x11 timeout!\n"); + } +#else + timeout = 100000; + while ((!__dmac_channel_transmit_end_detected(nand_dma_chan)) && (timeout--)); + while(!chip->dev_ready(mtd)); + if (timeout <= 0) + printk("not use irq, prog timeout!\n"); +#endif +} + +static void nand_write_page_hwecc_bch(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t * buf) +{ + nand_write_page_hwecc_bch0(mtd, chip, buf, 0x10); +} + +static void nand_write_page_hwecc_bch_planes(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t * buf) +{ + int page; + int pagesize = mtd->writesize >> 1; + int ppb = mtd->erasesize / mtd->writesize; + + page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ + + /* send cmd 0x80, the MSB should be valid if realplane is 4 */ + if (chip->realplanenum == 2) + chip->cmdfunc(mtd, 0x80, 0x00, 0x00); + else + chip->cmdfunc(mtd, 0x80, 0x00, page & (1 << (chip->chip_shift - chip->page_shift))); + + nand_write_page_hwecc_bch0(mtd, chip, buf, 0x11); + chip->cmdfunc(mtd, 0x81, 0x00, page + ppb); + nand_write_page_hwecc_bch0(mtd, chip, buf + pagesize, 0x10); +} + +#endif /* CONFIG_MTD_NAND_DMA */ + +static void nand_write_page_hwecc_bch_cpu(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps / chip->planenum; + int oob_per_eccsize = chip->ecc.layout->eccpos[0] / eccsteps; + int oobsize = mtd->oobsize / chip->planenum; + int ecctotal = chip->ecc.total / chip->planenum; + uint8_t *p = (uint8_t *)buf; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint32_t *eccpos = chip->ecc.layout->eccpos; + static struct buf_be_corrected buf_calc0; + struct buf_be_corrected *buf_calc = &buf_calc0; + + for (i = 0; i < eccsteps; i++, p += eccsize) { + buf_calc->data = (u8 *)buf + eccsize * i; + buf_calc->oob = chip->oob_poi + oob_per_eccsize * i; + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->ecc.calculate(mtd, (u8 *)buf_calc, &ecc_calc[eccbytes*i]); + chip->write_buf(mtd, p, eccsize); + } + + for (i = 0; i < ecctotal; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; + + chip->write_buf(mtd, chip->oob_poi, oobsize); +} + +/* nand write using two-plane mode with cpu mode */ +static void nand_write_page_hwecc_bch_planes_cpu(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + int pagesize = mtd->writesize >> 1; + int ppb = mtd->erasesize / mtd->writesize; + int page; + + page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ + + /* send cmd 0x80, the MSB should be valid if realplane is 4 */ + if (chip->realplanenum == 2) + chip->cmdfunc(mtd, 0x80, 0x00, 0x00); + else + chip->cmdfunc(mtd, 0x80, 0x00, page & (1 << (chip->chip_shift - chip->page_shift))); + + nand_write_page_hwecc_bch_cpu(mtd, chip, buf); + + chip->cmdfunc(mtd, 0x11, -1, -1); /* send cmd 0x11 */ + ndelay(100); + while(!chip->dev_ready(mtd)); + + chip->cmdfunc(mtd, 0x81, 0x00, page + ppb); /* send cmd 0x81 */ + nand_write_page_hwecc_bch_cpu(mtd, chip, buf + pagesize); +} + +/** + * nand_read_page_hwecc_bch - [REPLACABLE] hardware ecc based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * + * Not for syndrome calculating ecc controllers which need a special oob layout + */ +#if defined(CONFIG_MTD_NAND_DMA) +static int nand_read_page_hwecc_bch0(struct mtd_info *mtd, struct nand_chip *chip, uint8_t * buf, u32 page) +{ + int i, eccsize = chip->ecc.size; + int eccsteps = chip->ecc.steps / chip->planenum; + int eccbytes = chip->ecc.bytes; + int ecc_pos = chip->ecc.layout->eccpos[0]; + int oob_per_eccsize = ecc_pos / eccsteps; + int pagesize = mtd->writesize / chip->planenum; + int oobsize = mtd->oobsize / chip->planenum; + u8 *databuf, *oobbuf; + jz_dma_desc_8word *desc; + int err; + u32 addrport, cmdport; + static struct buf_be_corrected buf_correct0; + + addrport = (u32)(chip->IO_ADDR_R) | addr_offset; + cmdport = (u32)(chip->IO_ADDR_R) | cmd_offset; + +#if defined(CONFIG_MTD_NAND_DMABUF) + databuf = read_buf; + oobbuf = read_buf + pagesize; + + dma_cache_inv((u32)read_buf, pagesize + oobsize); // databuf should be invalidated. + memset(errs, 0, eccsteps * ERRS_SIZE * 4); + dma_cache_wback_inv((u32)errs, eccsteps * ERRS_SIZE * 4); +#else + + databuf = buf; + oobbuf = chip->oob_poi; + + /* descriptor for nand reading data block */ + desc = dma_desc_nand_read; + desc->dsadr = CPHYSADDR((u32)chip->IO_ADDR_R); /* It will be changed when using multiply chip select */ + desc->dtadr = CPHYSADDR((u32)databuf); /* DMA target address */ + + dprintk("desc_nand_read:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, + desc->ddadr); + + /* descriptor for nand reading oob block */ + desc++; + desc->dsadr = CPHYSADDR((u32)chip->IO_ADDR_R); /* It will be changed when using multiply chip select */ + desc->dtadr = CPHYSADDR((u32)oobbuf); /* DMA target address */ + dprintk("desc_oob_read:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, + desc->ddadr); + + /* descriptors for data to be written to bch */ + desc = dma_desc_dec; + for (i = 0; i < eccsteps; i++) { + desc->dsadr = CPHYSADDR((u32)databuf) + i * eccsize; /* DMA source address */ + dprintk("dma_desc_dec:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, + desc->ddadr); + desc++; + } + + /* descriptors for oob to be written to bch */ + desc = dma_desc_dec1; + for (i = 0; i < eccsteps; i++) { + desc->dsadr = CPHYSADDR((u32)oobbuf) + oob_per_eccsize * i; /* DMA source address */ + dprintk("dma_desc_dec1:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, + desc->ddadr); + desc++; + } + + /* descriptors for parities to be written to bch */ + desc = dma_desc_dec2; + for (i = 0; i < eccsteps; i++) { + desc->dsadr = CPHYSADDR((u32)oobbuf) + ecc_pos + i * eccbytes; /* DMA source address */ + dprintk("dma_desc_dec2:desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, + desc->ddadr); + desc++; + } + + dma_cache_wback_inv((u32)dma_desc_nand_read, (2 + eccsteps * 3) * (sizeof(jz_dma_desc_8word))); + + memset(errs, 0, eccsteps * ERRS_SIZE * 4); + dma_cache_inv((u32)databuf, pagesize); // databuf should be invalidated. + dma_cache_inv((u32)oobbuf, oobsize); // oobbuf should be invalidated too + dma_cache_wback_inv((u32)errs, eccsteps * ERRS_SIZE * 4); +#endif + REG_DMAC_DCCSR(bch_dma_chan) = 0; + REG_DMAC_DCCSR(nand_dma_chan) = 0; + + /* Setup DMA descriptor address */ + REG_DMAC_DDA(nand_dma_chan) = CPHYSADDR((u32)dma_desc_nand_read); + REG_DMAC_DDA(bch_dma_chan) = CPHYSADDR((u32)dma_desc_dec); + + /* Setup request source */ + REG_DMAC_DRSR(nand_dma_chan) = DMAC_DRSR_RS_NAND; + REG_DMAC_DRSR(bch_dma_chan) = DMAC_DRSR_RS_BCH_DEC; + + /* Enable DMA */ + REG_DMAC_DMACR(0) |= DMAC_DMACR_DMAE; + REG_DMAC_DMACR(nand_dma_chan/HALF_DMA_NUM) |= DMAC_DMACR_DMAE; + + /* Enable BCH decoding */ + chip->ecc.hwctl(mtd, NAND_ECC_READ); + + dma_ack = 0; + nand_status = NAND_READ; + /* DMA doorbell set -- start nand DMA now ... */ + __dmac_channel_set_doorbell(nand_dma_chan); + + /* Setup DMA channel control/status register */ + REG_DMAC_DCCSR(nand_dma_chan) = DMAC_DCCSR_DES8 | DMAC_DCCSR_EN; + +#define __nand_cmd(n) (REG8(cmdport) = (n)) +#define __nand_addr(n) (REG8(addrport) = (n)) + + __nand_cmd(NAND_CMD_READ0); + + __nand_addr(0); + if (pagesize != 512) + __nand_addr(0); + + __nand_addr(page & 0xff); + __nand_addr((page >> 8) & 0xff); + + /* One more address cycle for the devices whose number of page address bits > 16 */ + if (((chip->chipsize >> chip->page_shift) >> 16) - 1 > 0) + __nand_addr((page >> 16) & 0xff); + + if (pagesize != 512) + __nand_cmd(NAND_CMD_READSTART); + +#if USE_IRQ + do { + err = wait_event_interruptible_timeout(nand_read_wait_queue, dma_ack, 3 * HZ); + }while(err == -ERESTARTSYS); + nand_status = NAND_NONE; + + if (!err) { + printk("*** NAND READ, Warning, wait event 3s timeout!\n"); + dump_jz_dma_channel(0); + dump_jz_dma_channel(nand_dma_chan); + printk("REG_BCH_CR=%x REG_BCH_CNT=0x%x REG_BCH_INTS=%x\n", REG_BCH_CR, REG_BCH_CNT, REG_BCH_INTS); + printk("databuf[0]=%x\n", databuf[0]); + } + dprintk("timeout remain = %d\n", err); +#else + int timeout; + timeout = 100000; + while ((!__dmac_channel_transmit_end_detected(bch_dma_chan)) && (timeout--)); + if (timeout <= 0) { + printk("not use irq, NAND READ timeout!\n"); + } +#endif + + for (i = 0; i < eccsteps; i++) { + int stat; + struct buf_be_corrected *buf_correct = &buf_correct0; + + buf_correct->data = databuf + eccsize * i; + buf_correct->oob = oobbuf + oob_per_eccsize * i; + + stat = chip->ecc.correct(mtd, (u8 *)buf_correct, (u8 *)&errs[i * ERRS_SIZE], NULL); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + +#if defined(CONFIG_MTD_NAND_DMABUF) + memcpy(buf, read_buf, pagesize); + memcpy(chip->oob_poi, read_buf + pagesize, oobsize); +#endif + return 0; +} + +static int nand_read_page_hwecc_bch(struct mtd_info *mtd, struct nand_chip *chip, uint8_t * buf) +{ + u32 page = global_page; + + nand_read_page_hwecc_bch0(mtd, chip, buf, page); + return 0; +} + +static int nand_read_page_hwecc_bch_planes(struct mtd_info *mtd, struct nand_chip *chip, uint8_t * buf) +{ + u32 page; + int pagesize = mtd->writesize >> 1; + int ppb = mtd->erasesize / mtd->writesize; + + page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ + + /* read 1st page */ + nand_read_page_hwecc_bch0(mtd, chip, buf, page); + + /* read 2nd page */ + nand_read_page_hwecc_bch0(mtd, chip, buf + pagesize, page + ppb); + return 0; +} + +#endif /* CONFIG_MTD_NAND_DMA */ + +static int nand_read_page_hwecc_bch_cpu(struct mtd_info *mtd, struct nand_chip *chip, uint8_t * buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps / chip->planenum; + int ecc_pos = chip->ecc.layout->eccpos[0]; + int oob_per_eccsize = ecc_pos / eccsteps; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + uint32_t *eccpos = chip->ecc.layout->eccpos; + int pagesize = mtd->writesize / chip->planenum; + int oobsize = mtd->oobsize / chip->planenum; + int ecctotal = chip->ecc.total / chip->planenum; + static struct buf_be_corrected buf_correct0; + + chip->read_buf(mtd, buf, pagesize); + chip->read_buf(mtd, chip->oob_poi, oobsize); + + for (i = 0; i < ecctotal; i++) { + ecc_code[i] = chip->oob_poi[eccpos[i]]; + } + + for (i = 0; i < eccsteps; i++) { + int stat; + struct buf_be_corrected *buf_correct = &buf_correct0; + + buf_correct->data = buf + eccsize * i; + buf_correct->oob = chip->oob_poi + oob_per_eccsize * i; + + chip->ecc.hwctl(mtd, NAND_ECC_READ); + stat = chip->ecc.correct(mtd, (u8 *)buf_correct, &ecc_code[eccbytes*i], &ecc_calc[eccbytes*i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + + return 0; +} + +static int nand_read_page_hwecc_bch_planes_cpu(struct mtd_info *mtd, struct nand_chip *chip, uint8_t * buf) +{ + int pagesize = mtd->writesize >> 1; + int ppb = mtd->erasesize / mtd->writesize; + uint32_t page; + + page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ + + /* Read first page */ + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + nand_read_page_hwecc_bch_cpu(mtd, chip, buf); + + /* Read 2nd page */ + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page + ppb); + nand_read_page_hwecc_bch_cpu(mtd, chip, buf+pagesize); + return 0; +} + +#endif /* CONFIG_MTD_HW_BCH_ECC */ + +/* read oob using two-plane mode */ +static int nand_read_oob_std_planes(struct mtd_info *mtd, struct nand_chip *chip, + int global_page, int sndcmd) +{ + int page; + int oobsize = mtd->oobsize >> 1; + int ppb = mtd->erasesize / mtd->writesize; + + page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ + + /* Read first page OOB */ + if (sndcmd) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + } + chip->read_buf(mtd, chip->oob_poi, oobsize); + /* Read second page OOB */ + page += ppb; + if (sndcmd) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + sndcmd = 0; + } + chip->read_buf(mtd, chip->oob_poi+oobsize, oobsize); + return 0; +} + +/* write oob using two-plane mode */ +static int nand_write_oob_std_planes(struct mtd_info *mtd, struct nand_chip *chip, + int global_page) +{ + int status = 0, page; + const uint8_t *buf = chip->oob_poi; + int pagesize = mtd->writesize >> 1; + int oobsize = mtd->oobsize >> 1; + int ppb = mtd->erasesize / mtd->writesize; + + page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ + + /* send cmd 0x80, the MSB should be valid if realplane is 4 */ + if (chip->realplanenum == 2) + chip->cmdfunc(mtd, 0x80, pagesize, 0x00); + else + chip->cmdfunc(mtd, 0x80, pagesize, page & (1 << (chip->chip_shift - chip->page_shift))); + + chip->write_buf(mtd, buf, oobsize); + /* Send first command to program the OOB data */ + chip->cmdfunc(mtd, 0x11, -1, -1); + ndelay(100); + status = chip->waitfunc(mtd, chip); + + page += ppb; + buf += oobsize; + chip->cmdfunc(mtd, 0x81, pagesize, page); + chip->write_buf(mtd, buf, oobsize); + /* Send command to program the OOB data */ + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + /* Wait long R/B */ + ndelay(100); + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +/* nand erase using two-plane mode */ +static void single_erase_cmd_planes(struct mtd_info *mtd, int global_page) +{ + struct nand_chip *chip = mtd->priv; + int page, ppb = mtd->erasesize / mtd->writesize; + + page = (global_page / ppb) * ppb + global_page; /* = global_page%ppb + (global_page/ppb)*ppb*2 */ + + /* send cmd 0x60, the MSB should be valid if realplane is 4 */ + if (chip->realplanenum == 2) + chip->cmdfunc(mtd, 0x60, -1, 0x00); + else + chip->cmdfunc(mtd, 0x60, -1, page & (1 << (chip->chip_shift - chip->page_shift))); + + page += ppb; + chip->cmdfunc(mtd, 0x60, -1, page & (~(ppb-1))); /* send cmd 0x60 */ + + chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); /* send cmd 0xd0 */ + /* Do not need wait R/B or check status */ +} + +#if defined(CONFIG_MTD_NAND_DMA) + +#if USE_IRQ +static irqreturn_t nand_dma_irq(int irq, void *dev_id) +{ + u8 dma_chan; + volatile int wakeup = 0; + + dma_chan = irq - IRQ_DMA_0; + + dprintk("jz4750_dma_irq %d, channel %d\n", irq, dma_chan); + + if (__dmac_channel_transmit_halt_detected(dma_chan)) { + __dmac_channel_clear_transmit_halt(dma_chan); + wakeup = 1; + printk("DMA HALT\n"); + } + + if (__dmac_channel_address_error_detected(dma_chan)) { + + REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + __dmac_channel_clear_address_error(dma_chan); + + REG_DMAC_DSAR(dma_chan) = 0; /* reset source address register */ + REG_DMAC_DTAR(dma_chan) = 0; /* reset destination address register */ + + /* clear address error in DMACR */ + REG_DMAC_DMACR((dma_chan / HALF_DMA_NUM)) &= ~(1 << 2); + wakeup = 1; + printk("DMA address error!\n"); + } + + if (__dmac_channel_descriptor_invalid_detected(dma_chan)) { + __dmac_channel_clear_descriptor_invalid(dma_chan); + wakeup = 1; + printk("DMA DESC INVALID\n"); + } +#if 1 + + while (!__dmac_channel_transmit_end_detected(dma_chan)); + + if (__dmac_channel_count_terminated_detected(dma_chan)) { + dprintk("DMA CT\n"); + __dmac_channel_clear_count_terminated(dma_chan); + wakeup = 0; + } +#endif + + if (__dmac_channel_transmit_end_detected(dma_chan)) { + dprintk("DMA TT\n"); + REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + __dmac_channel_clear_transmit_end(dma_chan); + wakeup = 1; + } + + if (wakeup) { + dprintk("ack %d irq , wake up dma_chan %d nand_status %d\n", dma_ack, dma_chan, nand_status); + /* wakeup wait event */ + if ((dma_chan == nand_dma_chan) && (nand_status == NAND_PROG)) { + dprintk("nand prog dma irq, wake up----\n"); + dma_ack1 = 1; + wake_up_interruptible(&nand_prog_wait_queue); + } + + if ((dma_chan == bch_dma_chan) && (nand_status == NAND_READ)) { + dprintk("nand read irq, wake up----\n"); + dma_ack = 1; + wake_up_interruptible(&nand_read_wait_queue); + } + wakeup = 0; + } + + return IRQ_HANDLED; +} +#endif /* USE_IRQ */ + +static int jz4750_nand_dma_init(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + int eccsize = chip->ecc.size; + int eccsteps = chip->ecc.steps / chip->planenum; + int eccbytes = chip->ecc.bytes; + int ecc_pos = chip->ecc.layout->eccpos[0]; + int oob_per_eccsize = ecc_pos / eccsteps; + int pagesize = mtd->writesize / chip->planenum; + int oobsize = mtd->oobsize / chip->planenum; + int i, err; + jz_dma_desc_8word *desc, *dma_desc_bch_ddr, *dma_desc_nand_ddr, *dma_desc_nand_cmd_pgprog; + u32 *pval_nand_dcs, *pval_bch_ddr, *pval_bch_dcs, *dummy; + u32 next; +#if defined(CONFIG_MTD_NAND_DMABUF) + u8 *oobbuf; +#endif + +#if USE_IRQ + if ((nand_dma_chan = jz_request_dma(DMA_ID_NAND, "nand read or write", nand_dma_irq, IRQF_DISABLED, NULL)) < 0) { + printk("can't reqeust DMA nand channel.\n"); + return 0; + } + dprintk("nand dma channel:%d----\n", nand_dma_chan); + + if ((err = request_irq(IRQ_DMA_0 + bch_dma_chan, nand_dma_irq, IRQF_DISABLED, "bch_dma", NULL))) { + printk("bch_dma irq request err\n"); + return 0; + } +#else + if ((nand_dma_chan = jz_request_dma(DMA_ID_NAND, "nand read or write", NULL, IRQF_DISABLED, NULL)) < 0) { + printk("can't reqeust DMA nand channel.\n"); + return 0; + } + dprintk("nand dma channel:%d----\n", nand_dma_chan); +#endif + + __dmac_channel_enable_clk(nand_dma_chan); + __dmac_channel_enable_clk(bch_dma_chan); + +#if defined(CONFIG_MTD_NAND_DMABUF) + if (pagesize < 4096) { + read_buf = prog_buf = (u8 *) __get_free_page(GFP_KERNEL); + } else { + read_buf = prog_buf = (u8 *) __get_free_pages(GFP_KERNEL, 1); + } + if (!read_buf) + return -ENOMEM; +#endif + /* space for the error reports of bch decoding((4 * 5 * eccsteps) bytes), and the space for the value + * of ddr and dcs of channel 0 and channel nand_dma_chan (4 * (2 + 2) bytes) */ + errs = (u32 *)kmalloc(4 * (2 + 2 + 5 * eccsteps), GFP_KERNEL); + if (!errs) + return -ENOMEM; + + pval_nand_ddr = errs + 5 * eccsteps; + pval_nand_dcs = pval_nand_ddr + 1; + pval_bch_ddr = pval_nand_dcs + 1; + pval_bch_dcs = pval_bch_ddr + 1; + /* space for nand prog waiting target, the content is useless */ + dummy = pval_bch_dcs + 1; + /* space to store CMD_PGPROG(0x10) or 0x11 */ + pval_nand_cmd_pgprog = (u8 *)(dummy + 1); + + /* desc can't across 4KB boundary, as desc base address is fixed */ + /* space of descriptors for nand reading data and oob blocks */ + dma_desc_nand_read = (jz_dma_desc_8word *) __get_free_page(GFP_KERNEL); + if (!dma_desc_nand_read) + return -ENOMEM; + + /* space of descriptors for bch decoding */ + dma_desc_dec = dma_desc_nand_read + 2; + dma_desc_dec1 = dma_desc_dec + eccsteps; + dma_desc_dec2 = dma_desc_dec + eccsteps * 2; + + /* space of descriptors for notifying bch channel */ + dma_desc_bch_ddr = dma_desc_dec2 + eccsteps; + + /* space of descriptors for bch encoding */ + dma_desc_enc = dma_desc_bch_ddr + 2; + dma_desc_enc1 = dma_desc_enc + eccsteps; + + /* space of descriptors for nand programing data and oob blocks */ + dma_desc_nand_prog = dma_desc_enc1 + eccsteps; + + /* space of descriptors for nand prog waiting, including pgprog and sync */ + dma_desc_nand_cmd_pgprog = dma_desc_nand_prog + 2; + + /* space of descriptors for notifying nand channel, including ddr and dcsr */ + dma_desc_nand_ddr = dma_desc_nand_cmd_pgprog + 2; + +/************************************* + * Setup of nand programing descriptors + *************************************/ +#if defined(CONFIG_MTD_NAND_DMABUF) + oobbuf = prog_buf + pagesize; +#endif + /* set descriptor for encoding data blocks */ + desc = dma_desc_enc; + for (i = 0; i < eccsteps; i++) { + next = (CPHYSADDR((u32)dma_desc_enc1) + i * (sizeof(jz_dma_desc_8word))) >> 4; + + desc->dcmd = + DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | + DMAC_DCMD_DS_BCH | DMAC_DCMD_LINK; +#if defined(CONFIG_MTD_NAND_DMABUF) + desc->dsadr = CPHYSADDR((u32)prog_buf) + i * eccsize; /* DMA source address */ + desc->dtadr = CPHYSADDR((u32)oobbuf) + ecc_pos + i * eccbytes; /* DMA target address */ +#endif + desc->ddadr = (next << 24) + eccsize / DIV_DS_BCH; /* size: eccsize bytes */ + desc->dreqt = DMAC_DRSR_RS_BCH_ENC; + dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr); + desc++; + } + + /* set descriptor for encoding oob blocks */ + desc = dma_desc_enc1; + for (i = 0; i < eccsteps; i++) { + next = (CPHYSADDR((u32)dma_desc_enc) + (i + 1) * (sizeof(jz_dma_desc_8word))) >> 4; + + desc->dcmd = + DMAC_DCMD_BLAST | DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_8 | + DMAC_DCMD_DWDH_8 | DMAC_DCMD_DS_32BIT | DMAC_DCMD_LINK; +#if defined(CONFIG_MTD_NAND_DMABUF) + desc->dsadr = CPHYSADDR((u32)oobbuf) + oob_per_eccsize * i; /* DMA source address, 28/4 = 7bytes */ + desc->dtadr = CPHYSADDR((u32)oobbuf) + ecc_pos + i * eccbytes; /* DMA target address */ +#endif + desc->ddadr = (next << 24) + (oob_per_eccsize + 3) / 4; /* size: 7 bytes -> 2 words */ + desc->dreqt = DMAC_DRSR_RS_BCH_ENC; + dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr); + desc++; + } + + next = (CPHYSADDR((u32)dma_desc_nand_ddr)) >> 4; + desc--; + desc->ddadr = (next << 24) + (oob_per_eccsize + 3) / 4; + + /* set the descriptor to set door bell of nand_dma_chan for programing nand */ + desc = dma_desc_nand_ddr; + *pval_nand_ddr = 1 << (nand_dma_chan - nand_dma_chan / HALF_DMA_NUM * HALF_DMA_NUM); + next = (CPHYSADDR((u32)dma_desc_nand_ddr) + sizeof(jz_dma_desc_8word)) >> 4; + desc->dcmd = DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT | DMAC_DCMD_LINK; + desc->dsadr = CPHYSADDR((u32)pval_nand_ddr); /* DMA source address */ + desc->dtadr = CPHYSADDR(DMAC_DMADBSR(nand_dma_chan / HALF_DMA_NUM)); /* nand_dma_chan's descriptor addres register */ + desc->ddadr = (next << 24) + 1; /* size: 1 word */ + desc->dreqt = DMAC_DRSR_RS_AUTO; + dprintk("*pval_nand_ddr=0x%x\n", *pval_nand_ddr); + + /* set the descriptor to write dccsr of nand_dma_chan for programing nand, dccsr should be set at last */ + desc++; + *pval_nand_dcs = DMAC_DCCSR_DES8 | DMAC_DCCSR_EN; /* set value for writing ddr to enable channel nand_dma_chan */ + desc->dcmd = DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT; + desc->dsadr = CPHYSADDR((u32)pval_nand_dcs); /* DMA source address */ + desc->dtadr = CPHYSADDR(DMAC_DCCSR(nand_dma_chan)); /* address of dma door bell set register */ + desc->ddadr = (0 << 24) + 1; /* size: 1 word */ + desc->dreqt = DMAC_DRSR_RS_AUTO; + dprintk("*pval_nand_dcs=0x%x\n", *pval_nand_dcs); + + /* set descriptor for nand programing data block */ + desc = dma_desc_nand_prog; + next = (CPHYSADDR((u32)dma_desc_nand_prog) + sizeof(jz_dma_desc_8word)) >> 4; + desc->dcmd = + DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | + DMAC_DCMD_DS_NAND | DMAC_DCMD_LINK; +#if defined(CONFIG_MTD_NAND_DMABUF) + desc->dsadr = CPHYSADDR((u32)prog_buf); /* DMA source address */ +#endif + desc->dtadr = CPHYSADDR((u32)(chip->IO_ADDR_W)); /* DMA target address */ + desc->ddadr = (next << 24) + pagesize / DIV_DS_NAND; /* size: eccsize bytes */ + desc->dreqt = DMAC_DRSR_RS_AUTO; + dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr); + + /* set descriptor for nand programing oob block */ + desc++; + next = (CPHYSADDR((u32)dma_desc_nand_cmd_pgprog)) >> 4; + desc->dcmd = + DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_8 | + DMAC_DCMD_DS_NAND | DMAC_DCMD_LINK; +#if defined(CONFIG_MTD_NAND_DMABUF) + desc->dsadr = CPHYSADDR((u32)oobbuf); /* DMA source address */ +#endif + desc->dtadr = CPHYSADDR((u32)(chip->IO_ADDR_W)); /* DMA target address: dataport */ + desc->ddadr = (next << 24) + oobsize / DIV_DS_NAND; /* size: eccsize bytes */ + desc->dreqt = DMAC_DRSR_RS_AUTO; + dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr); + + /* set descriptor for __nand_cmd(CMD_PGPROG) */ + desc = dma_desc_nand_cmd_pgprog; + *pval_nand_cmd_pgprog = NAND_CMD_PAGEPROG; + next = (CPHYSADDR((u32)dma_desc_nand_cmd_pgprog) + sizeof(jz_dma_desc_8word)) >> 4; + desc->dcmd = + DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_8 | DMAC_DCMD_DS_8BIT | DMAC_DCMD_LINK; + desc->dsadr = CPHYSADDR((u32)pval_nand_cmd_pgprog); /* DMA source address */ + desc->dtadr = CPHYSADDR((u32)chip->IO_ADDR_R | cmd_offset); /* DMA target address: cmdport */ + desc->ddadr = (next << 24) + 1; /* size: 1 byte */ + desc->dreqt = DMAC_DRSR_RS_AUTO; + dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr); + + /* set descriptor for __nand_sync() */ + desc++; +#if USE_IRQ + desc->dcmd = + DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT | DMAC_DCMD_TIE; +#else + desc->dcmd = + DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT; +#endif + desc->dsadr = CPHYSADDR((u32)pval_nand_ddr); /* DMA source address */ + desc->dtadr = CPHYSADDR((u32)dummy); /* DMA target address, the content is useless */ + desc->ddadr = (0 << 24) + 1; /* size: 1 word */ + desc->dreqt = DMAC_DRSR_RS_NAND; + dprintk("1cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr); + + /* eccsteps*2 + 2 + 2 + 2: + dma_desc_enc + dma_desc_enc1 + dma_desc_nand_prog(oob) + dma_desc_nand_ddr(csr) + + dma_desc_nand_cmd_pgprog(sync) */ + dma_cache_wback_inv((u32)dma_desc_enc, (eccsteps * 2 + 2 + 2 + 2) * (sizeof(jz_dma_desc_8word))); + /* 4*6: pval_nand_ddr, pval_nand_dcs, pval_bch_ddr, pval_bch_dcs, dummy, pval_nand_cmd_pgprog */ + dma_cache_wback_inv((u32)pval_nand_ddr, 4 * 8); /* 8 words, a cache line */ + +/************************************* + * Setup of nand reading descriptors + *************************************/ +#if defined(CONFIG_MTD_NAND_DMABUF) + oobbuf = read_buf + pagesize; +#endif + /* set descriptor for nand reading data block */ + desc = dma_desc_nand_read; + next = (CPHYSADDR((u32)dma_desc_nand_read) + sizeof(jz_dma_desc_8word)) >> 4; + desc->dcmd = + DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | + DMAC_DCMD_DS_NAND | DMAC_DCMD_LINK; + desc->dsadr = CPHYSADDR((u32)(chip->IO_ADDR_R)); /* DMA source address */ +#if defined(CONFIG_MTD_NAND_DMABUF) + desc->dtadr = CPHYSADDR((u32)read_buf); /* DMA target address */ +#endif + desc->ddadr = (next << 24) + pagesize / DIV_DS_NAND; /* size: eccsize bytes */ + desc->dreqt = DMAC_DRSR_RS_NAND; + dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr); + + /* set descriptor for nand reading oob block */ + desc++; + next = (CPHYSADDR((u32)dma_desc_bch_ddr)) >> 4; + desc->dcmd = + DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | + DMAC_DCMD_DS_NAND | DMAC_DCMD_LINK; + desc->dsadr = CPHYSADDR((u32)(chip->IO_ADDR_R)); /* DMA source address */ +#if defined(CONFIG_MTD_NAND_DMABUF) + desc->dtadr = CPHYSADDR((u32)oobbuf); /* DMA target address */ +#endif + desc->ddadr = (next << 24) + oobsize / DIV_DS_NAND; /* size: eccsize bytes */ + desc->dreqt = DMAC_DRSR_RS_AUTO; + dprintk("cmd:%x sadr:%x tadr:%x dadr:%x\n", desc->dcmd, desc->dsadr, desc->dtadr, desc->ddadr); + + /* set the descriptor to set door bell for bch */ + desc = dma_desc_bch_ddr; + *pval_bch_ddr = DMAC_DMADBSR_DBS0; // set value for writing ddr to enable channel 0 + next = (CPHYSADDR((u32)dma_desc_bch_ddr) + sizeof(jz_dma_desc_8word)) >> 4; + desc->dcmd = DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT | DMAC_DCMD_LINK; + desc->dsadr = CPHYSADDR((u32)pval_bch_ddr); /* DMA source address */ + desc->dtadr = CPHYSADDR(DMAC_DMADBSR(0)); /* channel 1's descriptor addres register */ + desc->ddadr = (next << 24) + 1; /* size: 1 word */ + desc->dreqt = DMAC_DRSR_RS_AUTO; + + /* set descriptor for writing dcsr */ + desc++; + *pval_bch_dcs = DMAC_DCCSR_DES8 | DMAC_DCCSR_EN; // set value for writing ddr to enable channel 1 + desc->dcmd = DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT; + desc->dsadr = CPHYSADDR((u32)pval_bch_dcs); /* DMA source address */ + desc->dtadr = CPHYSADDR(DMAC_DCCSR(bch_dma_chan)); /* address of dma door bell set register */ + desc->ddadr = (0 << 24) + 1; /* size: 1 word */ + desc->dreqt = DMAC_DRSR_RS_AUTO; + + /* descriptors for data to be written to bch */ + desc = dma_desc_dec; + for (i = 0; i < eccsteps; i++) { + next = CPHYSADDR((u32)dma_desc_dec1 + i * (sizeof(jz_dma_desc_8word))) >> 4; + + desc->dcmd = + DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | + DMAC_DCMD_DS_BCH | DMAC_DCMD_LINK; +#if defined(CONFIG_MTD_NAND_DMABUF) + desc->dsadr = CPHYSADDR((u32)read_buf) + i * eccsize; /* DMA source address */ +#endif + desc->dtadr = CPHYSADDR((u32)errs) + i * 4 * ERRS_SIZE; /* DMA target address */ + desc->ddadr = (next << 24) + eccsize / DIV_DS_BCH; /* size: eccsize bytes */ + desc->dreqt = DMAC_DRSR_RS_BCH_DEC; + dprintk("desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, + desc->ddadr); + desc++; + } + + /* descriptors for oob to be written to bch */ + desc = dma_desc_dec1; + for (i = 0; i < eccsteps; i++) { + next = CPHYSADDR((u32)dma_desc_dec2 + i * (sizeof(jz_dma_desc_8word))) >> 4; + + desc->dcmd = + DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_32 | + DMAC_DCMD_DS_8BIT | DMAC_DCMD_LINK; +#if defined(CONFIG_MTD_NAND_DMABUF) + desc->dsadr = CPHYSADDR((u32)oobbuf) + oob_per_eccsize * i; /* DMA source address */ +#endif + desc->dtadr = CPHYSADDR((u32)errs) + i * 4 * ERRS_SIZE; /* DMA target address */ + desc->ddadr = (next << 24) + oob_per_eccsize; /* size: 7 bytes */ + desc->dreqt = DMAC_DRSR_RS_BCH_DEC; + dprintk("desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, + desc->ddadr); + desc++; + } + + /* descriptors for parities to be written to bch */ + desc = dma_desc_dec2; + for (i = 0; i < eccsteps; i++) { + next = (CPHYSADDR((u32)dma_desc_dec) + (i + 1) * (sizeof(jz_dma_desc_8word))) >> 4; + + desc->dcmd = + DMAC_DCMD_BLAST | DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_8 | + DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_BCH | DMAC_DCMD_LINK; +#if defined(CONFIG_MTD_NAND_DMABUF) + desc->dsadr = CPHYSADDR((u32)oobbuf) + ecc_pos + i * eccbytes; /* DMA source address */ +#endif + desc->dtadr = CPHYSADDR((u32)errs) + i * 4 * ERRS_SIZE; /* DMA target address */ + desc->ddadr = (next << 24) + (eccbytes + 15) / DIV_DS_BCH; /* size: eccbytes bytes */ + desc->dreqt = DMAC_DRSR_RS_BCH_DEC; + dprintk("desc:%x cmd:%x sadr:%x tadr:%x dadr:%x\n", (u32)desc, desc->dcmd, desc->dsadr, desc->dtadr, + desc->ddadr); + desc++; + } + desc--; + desc->dcmd &= ~DMAC_DCMD_LINK; +#if USE_IRQ + desc->dcmd |= DMAC_DCMD_TIE; +#endif + + dma_cache_wback_inv((u32)dma_desc_nand_read, (2 + 2 + eccsteps * 3) * (sizeof(jz_dma_desc_8word))); + dma_cache_wback_inv((u32)pval_bch_ddr, 4 * 2); /* two words */ + + return 0; +} + +#endif /* CONFIG_MTD_NAND_DMA */ + +void copy_to_cpu_mode(struct mtd_info *mtd_cpu, struct mtd_info *mtd_dma, char use_planes) +{ + struct nand_chip *chip_cpu = (struct nand_chip *)(&mtd_cpu[1]); + struct nand_chip *chip_dma = (struct nand_chip *)(&mtd_dma[1]); + + memcpy(mtd_cpu, mtd_dma, sizeof(struct mtd_info)); + mtd_cpu->priv = chip_cpu; + memcpy(chip_cpu, chip_dma, sizeof(struct nand_chip)); + + chip_cpu->ecc.correct = jzsoc_nand_bch_correct_data_cpu; + + if (use_planes) { + chip_cpu->ecc.read_page = nand_read_page_hwecc_bch_planes_cpu; + chip_cpu->ecc.write_page = nand_write_page_hwecc_bch_planes_cpu; + } else { + chip_cpu->ecc.read_page = nand_read_page_hwecc_bch_cpu; + chip_cpu->ecc.write_page = nand_write_page_hwecc_bch_cpu; + } + + mtd_cpu->flags |= MTD_NAND_CPU_MODE; +} + +/* + * Main initialization routine + */ +int __init jznand_init(void) +{ + struct nand_chip *this; + struct mtd_info *jz_mtd_cpu, *jz_mtd_cpu1; /* jz_mtd_cpu for 2 planes, jz_mtd_cpu1 for 1 plane */ + int ret, i; + + printk("JZ NAND init:"); +#if defined(CONFIG_MTD_NAND_DMA) +#if defined(CONFIG_MTD_NAND_DMABUF) + printk(" DMA mode, using DMA buffer in NAND driver, "); +#else + printk(" DMA mode, using DMA buffer in upper layer, "); +#endif +#else + printk(KERN_INFO " CPU mode, "); +#endif +#if defined(CONFIG_MTD_HW_BCH_8BIT) + printk(" 8bit BCH.\n"); +#else + printk(" 4bit BCH.\n"); +#endif + + /* Allocate memory for MTD device structure and private data */ + jz_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); + if (!jz_mtd) { + printk("Unable to allocate JzSOC NAND MTD device structure for dma mode using multi-planes.\n"); + return -ENOMEM; + } + + jz_mtd1 = kmalloc(sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL); + if (!jz_mtd1) { + printk ("Unable to allocate JzSOC NAND MTD device structure for dma mode using 1 plane.\n"); + kfree(jz_mtd); + return -ENOMEM; + } + + jz_mtd_cpu = kmalloc(sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL); + if (!jz_mtd_cpu) { + printk ("Unable to allocate JzSOC NAND MTD device structure for cpu mode using multi-planes.\n"); + kfree(jz_mtd); + kfree(jz_mtd1); + return -ENOMEM; + } + + jz_mtd_cpu1 = kmalloc(sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL); + if (!jz_mtd_cpu) { + printk ("Unable to allocate JzSOC NAND MTD device structure for cpu mode using 1 plane.\n"); + kfree(jz_mtd); + kfree(jz_mtd1); + kfree(jz_mtd_cpu); + return -ENOMEM; + } + + /* Get pointer to private data */ + this = (struct nand_chip *)(&jz_mtd[1]); + + /* Initialize structures */ + memset((char *)jz_mtd, 0, sizeof(struct mtd_info)); + memset((char *)this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + jz_mtd->priv = this; + + if (is_share_mode()) { + addr_offset = NAND_ADDR_OFFSET0; + cmd_offset = NAND_CMD_OFFSET0; + } else { + addr_offset = NAND_ADDR_OFFSET1; + cmd_offset = NAND_CMD_OFFSET1; + } + + /* Set & initialize NAND Flash controller */ + jz_device_setup(); + + /* Set address of NAND IO lines to static bank1 by default */ + this->IO_ADDR_R = (void __iomem *)NAND_DATA_PORT1; + this->IO_ADDR_W = (void __iomem *)NAND_DATA_PORT1; + this->cmd_ctrl = jz_hwcontrol; + this->dev_ready = jz_device_ready; + +#ifdef CONFIG_MTD_HW_BCH_ECC + this->ecc.calculate = jzsoc_nand_calculate_bch_ecc; + this->ecc.hwctl = jzsoc_nand_enable_bch_hwecc; + this->ecc.mode = NAND_ECC_HW; + this->ecc.size = 512; +#if defined(CONFIG_MTD_NAND_DMA) + this->ecc.correct = jzsoc_nand_bch_correct_data; + this->ecc.read_page = nand_read_page_hwecc_bch; + this->ecc.write_page = nand_write_page_hwecc_bch; +#else + this->ecc.correct = jzsoc_nand_bch_correct_data_cpu; + this->ecc.read_page = nand_read_page_hwecc_bch_cpu; + this->ecc.write_page = nand_write_page_hwecc_bch_cpu; +#endif +#if defined(CONFIG_MTD_HW_BCH_8BIT) + this->ecc.bytes = 13; +#else + this->ecc.bytes = 7; +#endif +#endif + +#ifdef CONFIG_MTD_SW_HM_ECC + this->ecc.mode = NAND_ECC_SOFT; +#endif + /* 20 us command delay time */ + this->chip_delay = 20; + /* Scan to find existance of the device */ + ret = nand_scan_ident(jz_mtd, nand_chips); + + if (!ret) { + if (this->planenum == 2) { + /* reset nand functions */ + this->erase_cmd = single_erase_cmd_planes; +#if defined(CONFIG_MTD_NAND_DMA) + this->ecc.read_page = nand_read_page_hwecc_bch_planes; + this->ecc.write_page = nand_write_page_hwecc_bch_planes; +#else + this->ecc.read_page = nand_read_page_hwecc_bch_planes_cpu; + this->ecc.write_page = nand_write_page_hwecc_bch_planes_cpu; +#endif + this->ecc.read_oob = nand_read_oob_std_planes; + this->ecc.write_oob = nand_write_oob_std_planes; + + printk(KERN_INFO "Nand using two-plane mode, " + "and resized to writesize:%d oobsize:%d blocksize:0x%x \n", + jz_mtd->writesize, jz_mtd->oobsize, jz_mtd->erasesize); + } + } + + /* Determine whether all the partitions will use multiple planes if supported */ + nr_partitions = sizeof(partition_info) / sizeof(struct mtd_partition); + all_use_planes = 1; + for (i = 0; i < nr_partitions; i++) { + all_use_planes &= partition_info[i].use_planes; + } + + if (!ret) + ret = nand_scan_tail(jz_mtd); + + if (ret){ + kfree (jz_mtd1); + kfree (jz_mtd); + return -ENXIO; + } + +#if defined(CONFIG_MTD_NAND_DMA) + jz4750_nand_dma_init(jz_mtd); + + ((struct nand_chip *) (&jz_mtd1[1]))->ecc.read_page = nand_read_page_hwecc_bch; + ((struct nand_chip *) (&jz_mtd1[1]))->ecc.write_page = nand_write_page_hwecc_bch; +#else + ((struct nand_chip *) (&jz_mtd1[1]))->ecc.read_page = nand_read_page_hwecc_bch_cpu; + ((struct nand_chip *) (&jz_mtd1[1]))->ecc.write_page = nand_write_page_hwecc_bch_cpu; +#endif + + copy_to_cpu_mode(jz_mtd_cpu, jz_mtd, 1); + copy_to_cpu_mode(jz_mtd_cpu1, jz_mtd1, 0); + + /* Register the partitions */ + printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nr_partitions, jz_mtd->name); + + if ((this->planenum == 2) && !all_use_planes) { + for (i = 0; i < nr_partitions; i++) { + if (partition_info[i].use_planes) { + if (partition_info[i].cpu_mode) + add_mtd_partitions(jz_mtd_cpu, &partition_info[i], 1); + else + add_mtd_partitions(jz_mtd, &partition_info[i], 1); + } else { + if (partition_info[i].cpu_mode) + add_mtd_partitions(jz_mtd_cpu1, &partition_info[i], 1); + else + add_mtd_partitions(jz_mtd1, &partition_info[i], 1); + } + } + } else { + kfree(jz_mtd1); + kfree(jz_mtd_cpu1); + for (i = 0; i < nr_partitions; i++) { + if (partition_info[i].cpu_mode) + add_mtd_partitions(jz_mtd_cpu, &partition_info[i], 1); + else + add_mtd_partitions(jz_mtd, &partition_info[i], 1); + } + } + return 0; +} + +module_init(jznand_init); + +/* + * Clean up routine + */ +#ifdef MODULE + +#if defined(CONFIG_MTD_NAND_DMA) +static int jz4750_nand_dma_exit(struct mtd_info *mtd) +{ + int pagesize = mtd->writesize / chip->planenum; + +#if USE_IRQ + free_irq(IRQ_DMA_0 + nand_dma_chan, NULL); + free_irq(IRQ_DMA_0 + bch_dma_chan, NULL); +#endif + + /* space for the error reports of bch decoding((4 * 5 * eccsteps) bytes), + * and the space for the value of ddr and dcs of channel 0 and channel + * nand_dma_chan (4 * (2 + 2) bytes) */ + kfree(errs); + + /* space for dma_desc_nand_read contains dma_desc_nand_prog, + * dma_desc_enc and dma_desc_dec */ + free_page((u32)dma_desc_nand_read); + +#if defined(CONFIG_MTD_NAND_DMABUF) + if (pagesize < 4096) { + free_page((u32)prog_buf); + } else { + free_pages((u32)prog_buf, 1); + } +#endif + + return 0; +} +#endif + +static void __exit jznand_cleanup(void) +{ +#if defined(CONFIG_MTD_NAND_DMA) + jz4750_nand_dma_exit(jz_mtd); +#endif + + /* Unregister partitions */ + del_mtd_partitions(jz_mtd); + + /* Unregister the device */ + del_mtd_device(jz_mtd); + + /* Free the MTD device structure */ + if ((this->planenum == 2) && !all_use_planes) + kfree (jz_mtd1); + kfree(jz_mtd); +} + +module_exit(jznand_cleanup); +#endif diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 8c21b89..fbbcfdb 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -52,6 +52,22 @@ #include #endif +#include + + +u8 nand_nce; /* indicates which chip select on JZSOC is used for current nand chip */ +int global_page; /* page index of large page used for nand with multiple planes */ +struct mtd_info *jz_mtd1 = NULL; /* for 1 plane operation */ + +/* indicates whether multiple planes operation is used by all partitions + if multiple planes is supported by NAND */ +char all_use_planes = 1; + +/* The pointer to the address of block cache for partitions which work + over mtdblock-jz */ +extern struct mtd_partition partition_info[]; /* defined in jz47xx_nand.c */ +unsigned char **jz_mtdblock_cache = NULL; /* used by mtdblock-jz.c */ + /* Define default oob placement schemes for large and small page devices */ static struct nand_ecclayout nand_oob_8 = { .eccbytes = 3, @@ -68,10 +84,35 @@ static struct nand_ecclayout nand_oob_16 = { .eccpos = {0, 1, 2, 3, 6, 7}, .oobfree = { {.offset = 8, - . length = 8}} + .length = 8}} }; static struct nand_ecclayout nand_oob_64 = { +#if defined(CONFIG_MTD_HW_RS_ECC) +/* Reed-Solomon ECC */ + .eccbytes = 36, + .eccpos = { + 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63}, + .oobfree = { + {.offset = 2, + .length = 26}} +#elif defined(CONFIG_MTD_HW_BCH_ECC) +/* BCH ECC */ + .eccbytes = 28, + .eccpos = { + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51}, + .oobfree = { + {.offset = 2, + .length = 22}} +#else +/* HW&SW Hamming ECC */ .eccbytes = 24, .eccpos = { 40, 41, 42, 43, 44, 45, 46, 47, @@ -109,6 +150,35 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, DEFINE_LED_TRIGGER(nand_led_trigger); /** + * ffs_ll - find first bit set in a 64bit word. + * @word: The word to search + */ +static inline int ffs_ll(u64 word) +{ + u32 low = word & 0xffffffff; + u32 high = word >> 32; + int i; + + for(i = 0; i < 32; i++) { + if (low & 0x1) + break; + low >>= 1; + } + if (i == 32) { + for(i = 0; i < 32; i++) { + if (high & 0x1) + break; + high >>= 1; + } + i += 32; + } + if (i == 64) + return 0; + else + return (i+1); +} + +/** * nand_release_device - [GENERIC] release chip * @mtd: MTD device structure * @@ -183,6 +253,20 @@ static void nand_select_chip(struct mtd_info *mtd, int chipnr) chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); break; case 0: + nand_nce = NAND_NCE1; + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + break; + case 1: + nand_nce = NAND_NCE2; + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + break; + case 2: + nand_nce = NAND_NCE3; + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + break; + case 3: + nand_nce = NAND_NCE4; + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); break; default: @@ -314,11 +398,17 @@ static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) */ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) { - int page, chipnr, res = 0; + int page, page1 = 0, chipnr, res = 0; struct nand_chip *chip = mtd->priv; u16 bad; - page = (int)(ofs >> chip->page_shift) & chip->pagemask; + if (chip->planenum > 1) { + page = ((int)(ofs >> chip->page_shift) * chip->planenum + CONFIG_MTD_BADBLOCK_FLAG_PAGE); + page1 = page + mtd->erasesize / mtd->writesize; + page &= chip->pagemask; + page1 &= chip->pagemask; + } else + page = ((int)(ofs >> chip->page_shift) + CONFIG_MTD_BADBLOCK_FLAG_PAGE) & chip->pagemask; if (getchip) { chipnr = (int)(ofs >> chip->chip_shift); @@ -341,6 +431,11 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page); if (chip->read_byte(mtd) != 0xff) res = 1; + if (chip->planenum > 1) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page1); + if (chip->read_byte(mtd) != 0xff) + res = 1; + } } if (getchip) @@ -377,6 +472,7 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) */ nand_get_device(chip, mtd, FL_WRITING); ofs += mtd->oobsize; + ofs += (CONFIG_MTD_BADBLOCK_FLAG_PAGE << chip->page_shift); chip->ops.len = chip->ops.ooblen = 2; chip->ops.datbuf = NULL; chip->ops.oobbuf = buf; @@ -568,7 +664,10 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, /* Emulate NAND_CMD_READOOB */ if (command == NAND_CMD_READOOB) { - column += mtd->writesize; + if (chip->planenum > 1) + column += (mtd->writesize / chip->planenum); + else + column += mtd->writesize; command = NAND_CMD_READ0; } @@ -614,6 +713,8 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, case NAND_CMD_RNDIN: case NAND_CMD_STATUS: case NAND_CMD_DEPLETE1: + case 0x81: /* for two-plane page program */ + case 0x11: /* for two-plane page program */ return; /* @@ -935,6 +1036,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint3 return 0; } +#ifndef CONFIG_MTD_HW_RS_ECC /* HW&SW Hamming ECC */ /** * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function * @mtd: mtd info structure @@ -979,6 +1081,63 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, return 0; } +#else /* CONFIG_MTD_HW_RS_ECC */ + +/** + * nand_read_page_hwecc_rs - [REPLACABLE] hardware rs ecc based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * + * Not for syndrome calculating ecc controllers which need a special oob layout + */ +static int nand_read_page_hwecc_rs(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t page; + uint8_t flag = 0; + + page = (buf[3]<<24) + (buf[2]<<16) + (buf[1]<<8) + buf[0]; + + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + for (i = 0; i < chip->ecc.total; i++) { + ecc_code[i] = chip->oob_poi[eccpos[i]]; + if (ecc_code[i] != 0xff) flag = 1; + } + + eccsteps = chip->ecc.steps; + p = buf; + + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0x00, -1); + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + if (flag) { + 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; + } + else { + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + } + } + return 0; +} + +#endif /* CONFIG_MTD_HW_RS_ECC */ + /** * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read * @mtd: mtd info structure @@ -1124,10 +1283,25 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, if (realpage != chip->pagebuf || oob) { bufpoi = aligned ? buf : chip->buffers->databuf; + global_page = page; +#if defined(CONFIG_MTD_HW_RS_ECC) + bufpoi[0] = (uint8_t)page; + bufpoi[1] = (uint8_t)(page >> 8); + bufpoi[2] = (uint8_t)(page >> 16); + bufpoi[3] = (uint8_t)(page >> 24); +#elif defined(CONFIG_SOC_JZ4730) if (likely(sndcmd)) { chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); sndcmd = 0; } +#else /* for jz4750 and later chip */ + if (mtd->flags & MTD_NAND_CPU_MODE) { + if (likely(sndcmd)) { + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); + sndcmd = 0; + } + } +#endif /* Now read the page into the buffer */ if (unlikely(ops->mode == MTD_OOB_RAW)) @@ -1699,12 +1873,17 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, { int status; - chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); - - if (unlikely(raw)) - chip->ecc.write_page_raw(mtd, chip, buf); - else + global_page = page; + if (chip->planenum > 1) chip->ecc.write_page(mtd, chip, buf); + else { + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + + if (unlikely(raw)) + chip->ecc.write_page_raw(mtd, chip, buf); + else + chip->ecc.write_page(mtd, chip, buf); + } /* * Cached progamming disabled for now, Not sure if its worth the @@ -1713,9 +1892,19 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, cached = 0; if (!cached || !(chip->options & NAND_CACHEPRG)) { - - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); +/* +* __nand_cmd(CMD_PAGEPROG) and __nand_sync() have been done by DMA for jz4750 and +* later chip, status should still be read by "status = chip->waitfunc(mtd, chip)" +*/ +#if defined(CONFIG_SOC_JZ4730) || defined(CONFIG_SOC_JZ4740) + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); +#else + if (mtd->flags & MTD_NAND_CPU_MODE) { + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + } +#endif status = chip->waitfunc(mtd, chip); + /* * See if operation failed and additional status checks are * available @@ -2485,7 +2674,21 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, extid >>= 2; /* Get buswidth information */ busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + /* The 5th id byte */ +#if defined(CONFIG_MTD_NAND_MULTI_PLANE) + extid = chip->read_byte(mtd); + chip->realplanenum = 1 << ((extid & 0x0c) >> 2); +#else + chip->realplanenum = 1; +#endif + if (chip->realplanenum > 1) { /* use muti planes mode */ + chip->planenum = 2; + mtd->writesize *= 2; /* two pages as one page */ + mtd->oobsize *= 2; + mtd->erasesize *= 2; /* two blocks as one block */ + } else + chip->planenum = 1; } else { /* * Old devices have chip data hardcoded in the device id table @@ -2558,8 +2761,8 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, chip->cmdfunc = nand_command_lp; printk(KERN_INFO "NAND device: Manufacturer ID:" - " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id, - nand_manuf_ids[maf_idx].name, type->name); + " 0x%02x, Chip ID: 0x%02x (%s %s) planenum:%d\n", *maf_id, dev_id, + nand_manuf_ids[maf_idx].name, type->name, chip->realplanenum); return type; } @@ -2627,7 +2830,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips) */ int nand_scan_tail(struct mtd_info *mtd) { - int i; + int i, res; struct nand_chip *chip = mtd->priv; if (!(chip->options & NAND_OWN_BUFFERS)) @@ -2655,6 +2858,16 @@ int nand_scan_tail(struct mtd_info *mtd) case 128: chip->ecc.layout = &nand_oob_128; break; + case 128: + if (chip->planenum > 1) + chip->ecc.layout = &nand_oob_64; + else + chip->ecc.layout = &nand_oob_128; + break; + case 256: + if (chip->planenum > 1) + chip->ecc.layout = &nand_oob_128; + break; default: printk(KERN_WARNING "No oob scheme defined for " "oobsize %d\n", mtd->oobsize); @@ -2673,8 +2886,13 @@ int nand_scan_tail(struct mtd_info *mtd) switch (chip->ecc.mode) { case NAND_ECC_HW: /* Use standard hwecc read page function ? */ - if (!chip->ecc.read_page) + if (!chip->ecc.read_page) { +#ifndef CONFIG_MTD_HW_RS_ECC chip->ecc.read_page = nand_read_page_hwecc; +#else + chip->ecc.read_page = nand_read_page_hwecc_rs; +#endif + } if (!chip->ecc.write_page) chip->ecc.write_page = nand_write_page_hwecc; if (!chip->ecc.read_page_raw) @@ -2826,8 +3044,80 @@ int nand_scan_tail(struct mtd_info *mtd) if (chip->options & NAND_SKIP_BBTSCAN) return 0; - /* Build bad block table */ - return chip->scan_bbt(mtd); + /* Create jz_mtd1 for one plane operation if the NAND support multiple + planes operation, because some partitions will only use one plane. */ + if ((chip->planenum == 2) && !all_use_planes) { + int i, len, numblocks; + struct nand_chip *this = (struct nand_chip *) (&jz_mtd1[1]); + + memcpy(jz_mtd1, mtd, sizeof(*mtd)); + jz_mtd1->priv = this; + memcpy(this, chip, sizeof(*chip)); + + this->planenum = 1; + jz_mtd1->writesize >>= 1; + jz_mtd1->oobsize >>= 1; + jz_mtd1->erasesize >>= 1; + this->page_shift = chip->page_shift - 1; + this->pagemask = (this->chipsize >> this->page_shift) - 1; + this->bbt_erase_shift = this->phys_erase_shift = + chip->phys_erase_shift - 1; + this->ecc.steps >>= 1; + this->ecc.total = this->ecc.steps * this->ecc.bytes; + this->subpagesize = jz_mtd1->writesize; + + this->erase_cmd = single_erase_cmd; +#if defined(CONFIG_MTD_HW_RS_ECC) + this->ecc.read_page = nand_read_page_hwecc_rs; + this->ecc.write_page = nand_write_page_hwecc; +#endif + this->ecc.read_oob = nand_read_oob_std; + this->ecc.write_oob = nand_write_oob_std; + this->write_buf = nand_write_buf; + this->read_buf = nand_read_buf; + + /* Firstly, build bad block table as one plane */ + res = this->scan_bbt(jz_mtd1); + + /* Secondly, build bad block table as 2 plane based on bbt of jz_mtd1 */ + numblocks = chip->chipsize >> (chip->bbt_erase_shift - 1); /* = (real numblocks * 2) */ + len = mtd->size >> (chip->bbt_erase_shift + 2); + chip->bbt = kzalloc(len, GFP_KERNEL); + +#define isbad_2plane(block) (((this->bbt[(block) >> 3] >> ((block) & 0x06)) \ + | (this->bbt[((block)+2) >> 3] >> (((block)+2) & 0x06))) & 0x03) + + for (i = 0; i < numblocks; i += 2) { + if (isbad_2plane(2*i)) + chip->bbt[i >> 3] |= 0x03 << (i & 0x6); + } + } else { + res = chip->scan_bbt(mtd); + } + +#if defined(CONFIG_ALLOCATE_MTDBLOCK_JZ_EARLY) && !defined(CONFIG_SOC_JZ4730) + /* Allocate a block cache for every partitions which works over mtdblock-jz */ + { + extern int nr_partitions; + + jz_mtdblock_cache = kmalloc(nr_partitions * sizeof(unsigned char *), GFP_KERNEL); + + for (i = 0; i < nr_partitions; i++) { + if (!(partition_info[i].mtdblock_jz_invalid) && !(partition_info[i].cpu_mode)) { + if (partition_info[i].use_planes) { + jz_mtdblock_cache[i] = kmalloc(mtd->erasesize, GFP_KERNEL); + printk("Allocate 0x%x bytes for jz_mtdblock%d at address:0x%p.\n", mtd->erasesize, i, jz_mtdblock_cache[i]); + } else { + jz_mtdblock_cache[i] = kmalloc(jz_mtd1->erasesize, GFP_KERNEL); + printk("Allocate 0x%x bytes for jz_mtdblock%d at address:0x%p.\n", jz_mtd1->erasesize, i, jz_mtdblock_cache[i]); + } + if (!jz_mtdblock_cache[i]) + printk("Cannot allocate memory for jz_mtdblock%d!\n", i); + } + } + } +#endif + return res; } /* is_module_text_address() isn't exported, and it's mostly a pointless diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 55c23e5..34c5a29 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -401,7 +401,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, * below as it makes shifting and masking less painful */ numblocks = mtd->size >> (this->bbt_erase_shift - 1); startblock = 0; - from = 0; + from = (CONFIG_MTD_BADBLOCK_FLAG_PAGE << this->page_shift); //from = 0; } else { if (chip >= this->numchips) { printk(KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n", diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 69ee2c9..8665859 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -109,6 +109,9 @@ struct nand_flash_dev nand_flash_ids[] = { {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, LP_OPTIONS16}, {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16}, + /* 32 Gigabit */ + {"NAND 4GiB 3,3V 8-bit", 0xD7, 0, 4096, 0, LP_OPTIONS}, + /* * Renesas AND 1 Gigabit. Those chips do not support extended id and * have a strange page/block layout ! The chosen minimum erasesize is diff --git a/drivers/mtd/udc_cache.c b/drivers/mtd/udc_cache.c new file mode 100644 index 0000000..35b4be0 --- /dev/null +++ b/drivers/mtd/udc_cache.c @@ -0,0 +1,531 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CACHE_MAX_NUM 256 +#define SECTOR_SIZE 512 + +//#define UDC_CACHE_DEBUG + +#ifdef UDC_CACHE_DEBUG +#define dprintk(a...) printk(a) +#else +#define dprintk(a...) while(0){} +#endif + +typedef struct { + unsigned short CacheState; + unsigned short UseCount; + unsigned short CacheChange; + unsigned short CacheReserve; + unsigned int BlockId; + unsigned char *aBlockData; +} SSFDC__LB_CACHE; + +#define FREE_CACHE 0 +#define PREWRITE_CACHE 2 +#define OFTEN_USE_CACHE 3 +#define SECTOR_SHIFT 9 + +#define CACHE_TO_UNCATCH(x) ((unsigned int)x | 0xa0000000) +static unsigned int __aBlockData[SECTOR_SIZE * CACHE_MAX_NUM / 4] __attribute__ ((aligned (32))); +static SSFDC__LB_CACHE ssfdc_cache[CACHE_MAX_NUM]; +static unsigned short Cur_CacheCount = 0; +int FlushDataState = 0; +static struct mtdblk_dev *g_udc_mtdblk; +static struct mtd_info *g_udc_mtd; + +extern int udc_mtdblock_readsect(struct mtdblk_dev *, unsigned long, char *, int); +extern int udc_mtdblock_writesect(struct mtdblk_dev *, unsigned long, char *); +extern struct mtdblk_dev *udc_get_mtdblk(void); +extern struct mtd_info *udc_get_mtd(void); +extern void udc_flush_cache(struct mtdblk_dev *mtdblk); + +#define _NAND_LB_Write(pCache) udc_mtdblock_writesect(g_udc_mtdblk, pCache->BlockId,pCache->aBlockData) +#define _NAND_LB_Read(Sector,pBuffer) udc_mtdblock_readsect(g_udc_mtdblk, Sector, pBuffer, SECTOR_SIZE); + +#define DMA_ENABLE 0 + +#if DMA_ENABLE +#define DMA_CHANNEL 5 +#define PHYSADDR(x) virt_to_phys((void *)x) +#else +#define lb_memcpy memcpy +#endif + +#if DMA_ENABLE +static void lb_memcpy(void *target,void* source,unsigned int len) +{ + int ch = DMA_CHANNEL; + if(((unsigned int)source < 0xa0000000) && len) + dma_cache_wback_inv((unsigned long)source, len); + if(((unsigned int)target < 0xa0000000) && len) + dma_cache_wback_inv((unsigned long)target, len); + + REG_DMAC_DSAR(ch) = PHYSADDR((unsigned long)source); + REG_DMAC_DTAR(ch) = PHYSADDR((unsigned long)target); + REG_DMAC_DTCR(ch) = len / 32; + REG_DMAC_DRSR(ch) = DMAC_DRSR_RS_AUTO; + REG_DMAC_DCMD(ch) = DMAC_DCMD_SAI| DMAC_DCMD_DAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32|DMAC_DCMD_DS_32BYTE; + REG_DMAC_DCCSR(ch) = DMAC_DCCSR_EN | DMAC_DCCSR_NDES; + while ( REG_DMAC_DTCR(ch) ); +} +#endif + +static void _NAND_LB_InitCache(void) +{ + int i; + SSFDC__LB_CACHE *pCache = ssfdc_cache; +#if DMA_ENABLE + unsigned char * ptr = (unsigned char *)CACHE_TO_UNCATCH(__aBlockData); +#else + unsigned char * ptr = (unsigned char *)(__aBlockData); +#endif + for(i = 0;i < CACHE_MAX_NUM;i++) + { + pCache->CacheState = FREE_CACHE; + pCache->UseCount = 0; + pCache->CacheChange = 0; + pCache->aBlockData = ptr; + ptr+=SECTOR_SIZE; + pCache++; + } + Cur_CacheCount = 0; +} + +static SSFDC__LB_CACHE * _NAND_LB_GetFreeCache(void) +{ + int ret = 0; + SSFDC__LB_CACHE *pCacheInfo = &ssfdc_cache[Cur_CacheCount]; + while(1) + { + if(ret >= CACHE_MAX_NUM) + return 0; + if(pCacheInfo >= &ssfdc_cache[CACHE_MAX_NUM]) + { + pCacheInfo = ssfdc_cache; + Cur_CacheCount = 0; + } + + if(pCacheInfo->CacheState == FREE_CACHE) + { + return pCacheInfo; + } + pCacheInfo++; + Cur_CacheCount++; + ret++; + } + return 0; +} + +static void _NAND_LB_CloseCACHES(unsigned int sectorstart,unsigned int sectorend) +{ + unsigned int i; + SSFDC__LB_CACHE *pCache = ssfdc_cache; + for( i = 0;i < CACHE_MAX_NUM;i++){ + if((pCache->CacheState != FREE_CACHE) && (pCache->BlockId >= sectorstart) && (pCache->BlockId < sectorend)){ + pCache->CacheChange = 0; + pCache->CacheState = FREE_CACHE; + pCache->UseCount = 0; + } + pCache++; + } +} + +static void _NAND_LB_FLUSHCACHES(unsigned int sectorstart,unsigned int sectorend) +{ + unsigned int i; + SSFDC__LB_CACHE *pCache = ssfdc_cache; + for( i = 0;i < CACHE_MAX_NUM;i++){ + if((pCache->CacheState != FREE_CACHE) && (pCache->BlockId >= sectorstart) && (pCache->BlockId < sectorend)){ + if(pCache->CacheChange) + _NAND_LB_Write(pCache); + pCache->CacheChange = 0; + pCache->CacheState = FREE_CACHE; + pCache->UseCount = 0; + } + pCache++; + + } +} + +inline static int Get_NAND_CacheFreeCount(void) +{ + SSFDC__LB_CACHE *pCache = ssfdc_cache; + SSFDC__LB_CACHE *pEndCache = &ssfdc_cache[CACHE_MAX_NUM]; + unsigned int count = 0; + while(pCache < pEndCache) + { + if(pCache->CacheState == FREE_CACHE) + count++; + pCache++; + } + return count; + +} + +static unsigned int _NAND_LB_PreWiteToNand(SSFDC__LB_CACHE *pCache,unsigned short *count,unsigned int update) +{ + SSFDC__LB_CACHE *pWriteCache; + SSFDC__LB_CACHE *pEndCache = &ssfdc_cache[CACHE_MAX_NUM]; + unsigned int sector = -1; + unsigned int flag; + while(1) + { + sector = -1; + flag = 0; + pWriteCache = ssfdc_cache; + while(pWriteCache < pEndCache) + { + if(pWriteCache->CacheState == update) //PREWRITE_CACHE + { + if(pWriteCache->BlockId < sector) + { + sector = pWriteCache->BlockId; + pCache = pWriteCache; + } + }else + flag++; + pWriteCache++; + } + + if(flag < CACHE_MAX_NUM) + { + if(pCache->CacheChange) + { + _NAND_LB_Write(pCache); + pCache->CacheChange = 0; + } + pCache->CacheState = FREE_CACHE; + pCache->UseCount = 0; + (*count)++; + }else + break; + } + return 0; +} + +static void _NAND_LB_OftenToNand(SSFDC__LB_CACHE *pCache,unsigned short *count,unsigned int update) +{ + SSFDC__LB_CACHE *pWriteCache = pCache; + SSFDC__LB_CACHE *pOldCache = pCache; + SSFDC__LB_CACHE *pEndCache = &ssfdc_cache[CACHE_MAX_NUM]; + + dprintk("%s!\n",__FUNCTION__); + while(pCache) + { + if(pCache->CacheState == OFTEN_USE_CACHE) + { + if(pWriteCache->CacheState != OFTEN_USE_CACHE) + pWriteCache = pCache; + else if(pWriteCache->UseCount > pCache->UseCount) + { + pWriteCache = pCache; + } + } + pCache++; + if(pCache >= pEndCache) + break; + } + if(pWriteCache->CacheState == OFTEN_USE_CACHE) + { + (*count)++; + if(pWriteCache->CacheChange) + _NAND_LB_Write(pWriteCache); + pWriteCache->CacheState = FREE_CACHE; + + pWriteCache->UseCount = 0; + pWriteCache->CacheChange = 0; + if(update != -1) + update--; + if(update != 0) + _NAND_LB_OftenToNand(pOldCache,count,update); + } +} + +static int _NAND_LB_FreeCache(unsigned int update) +{ + unsigned short freecount = 0,totalfree = 0; + + freecount = 0; + _NAND_LB_PreWiteToNand(ssfdc_cache,&freecount,PREWRITE_CACHE); + + totalfree += freecount; + dprintk("free count = %d\n",freecount); + if(freecount == 0) + { + freecount = 0; + _NAND_LB_PreWiteToNand(ssfdc_cache,&freecount,OFTEN_USE_CACHE); + totalfree += freecount; + update = 0; + } + if(update) + { + if(Get_NAND_CacheFreeCount() < CACHE_MAX_NUM * 1 / 4) // because fat is 4 sector + { + freecount = 0; + _NAND_LB_PreWiteToNand(ssfdc_cache,&freecount,OFTEN_USE_CACHE); + totalfree += freecount; + } + } + + dprintk("Free = %d\r\n",totalfree); + return totalfree; +} + +static int _NAND_LB_GetFromCache(unsigned int Sector, void *pBuffer) { + + SSFDC__LB_CACHE *pCache = &ssfdc_cache[Cur_CacheCount]; + SSFDC__LB_CACHE *pUseCache = 0; + unsigned short i; + dprintk("sector = %x pBuffer = %x\n",Sector,pBuffer); + if(pCache >= &ssfdc_cache[CACHE_MAX_NUM]) + pCache = ssfdc_cache; + + i = 0; + while (1) { + if(pCache->CacheState != FREE_CACHE) + { + if (Sector == pCache->BlockId) { + dprintk("Cache is use = %d\r\n",pCache->BlockId); + pUseCache = pCache; + pCache->UseCount++; + if(pCache->UseCount == 0) + pCache->UseCount = -1; + pCache->CacheState = OFTEN_USE_CACHE; + } + } + pCache--; + if(pCache < ssfdc_cache) + pCache = &ssfdc_cache[CACHE_MAX_NUM - 1]; + + i++; + if (i >= CACHE_MAX_NUM) { + break; /* Sector not in cache */ + } + } + if (pUseCache) { + dprintk("From Cache %d\r\n",Sector); + lb_memcpy(pBuffer, pUseCache->aBlockData, SECTOR_SIZE); + return 0; + } + return -1; +} + +static void _NAND_LB_ClearCache(void) { + + unsigned short freecount = 0; + dprintk("Clear Cache\r\n"); + + _NAND_LB_PreWiteToNand(ssfdc_cache,&freecount,PREWRITE_CACHE); + _NAND_LB_PreWiteToNand(ssfdc_cache,&freecount,OFTEN_USE_CACHE); +} + +static void _NAND_LB_CopyToCache(unsigned int Sector, void *pBuffer,unsigned short rw) +{ + SSFDC__LB_CACHE *pCache = _NAND_LB_GetFreeCache(); + dprintk("Copy to Cache = 0x%08x 0x%08x\r\n",pCache,ssfdc_cache); + + if(!pCache) + { + _NAND_LB_FreeCache(rw); + + pCache = _NAND_LB_GetFreeCache(); + } + pCache->BlockId = Sector; + pCache->CacheState = PREWRITE_CACHE; + pCache->UseCount = 0; + pCache->CacheChange = rw; + + lb_memcpy(pCache->aBlockData,pBuffer,SECTOR_SIZE); +} + + +static int _NAND_LB_UpdateInCache(unsigned int Sector, void *pBuffer) { + short i,ret = 0; + i = Cur_CacheCount; + if(Cur_CacheCount > CACHE_MAX_NUM) + i = 0; + while(1) + { + if(ret >= CACHE_MAX_NUM) + return -1; + if(ssfdc_cache[i].CacheState != FREE_CACHE) + { + + if(ssfdc_cache[i].BlockId == Sector) + { + dprintk("UpdateInCache = %d\r\n",Sector); + ssfdc_cache[i].CacheState = OFTEN_USE_CACHE; + ssfdc_cache[i].UseCount++; + ssfdc_cache[i].CacheChange = 1; + lb_memcpy(ssfdc_cache[i].aBlockData,pBuffer,SECTOR_SIZE); + return 0; + } + } + i--; + if(i < 0) + i = CACHE_MAX_NUM - 1; + ret++; + } + return -1; +} + +static int NAND_LB_MultiRead(unsigned int Sector, void *pBuffer,unsigned int SectorCount) +{ + int i,ret,end; + void *p; + + dprintk("NAND_LB_MultiRead = %d %d \n",Sector,SectorCount); + end = Sector + SectorCount; + _NAND_LB_FLUSHCACHES(Sector,end); + + p = pBuffer; + for (i = Sector; i < end; i ++) + { + ret = udc_mtdblock_readsect(g_udc_mtdblk, i, p, SECTOR_SIZE); + p += SECTOR_SIZE; + } + return ret; +} + +static int NAND_LB_Read(unsigned int Sector, void *pBuffer) +{ + int x; +#if DMA_ENABLE + unsigned char *ptr = (unsigned char *)CACHE_TO_UNCATCH(pBuffer); + dma_cache_wback_inv(pBuffer,SECTOR_SIZE); +#else + unsigned char *ptr = (unsigned char *)pBuffer; +#endif + dprintk("LB_Read = %d \n",Sector); + if(_NAND_LB_GetFromCache(Sector,ptr)) + { + x = _NAND_LB_Read(Sector,ptr); + _NAND_LB_CopyToCache(Sector,ptr,0); + } + return 512; +} + +static int NAND_LB_MultiWrite(unsigned int Sector, void *pBuffer,unsigned int SectorCount) +{ + int i,ret; + unsigned char *p; + + _NAND_LB_CloseCACHES(Sector,Sector + SectorCount); + p = (unsigned char *)pBuffer; + for (i = Sector; i < Sector + SectorCount; i ++) + { + ret = udc_mtdblock_writesect(g_udc_mtdblk, i, p); + p += 512; + } + return ret; +} + +static int NAND_LB_Write(unsigned int Sector, void *pBuffer) +{ +#if DMA_ENABLE + unsigned char *ptr = (unsigned char *)CACHE_TO_UNCATCH(pBuffer); + dma_cache_wback_inv(pBuffer,SECTOR_SIZE); +#else + unsigned char *ptr = (unsigned char *)pBuffer; +#endif + dprintk("LB_Write = %x %x\r\n",Sector,pBuffer); + if(_NAND_LB_UpdateInCache(Sector,ptr)) + { + _NAND_LB_CopyToCache(Sector,ptr,1); + } + return 512; +} +/********************************************************************* +* +* Global functions +* +***********************************************************************/ + +int NAND_LB_Init(void) +{ + dprintk("UDC CACHE Init \n"); + _NAND_LB_InitCache(); + g_udc_mtdblk = udc_get_mtdblk(); + g_udc_mtd = udc_get_mtd(); + return 0; +} + +int NAND_LB_FLASHCACHE(void) +{ + dprintk("Flush lb cache !\n"); + _NAND_LB_ClearCache(); +// dprintk("Flush mtd cache !\n"); +// udc_flush_cache(g_udc_mtdblk); + return 0; +} + +int NAND_MTD_FLASHCACHE(void) +{ + dprintk("Flush mtd cache !\n"); + udc_flush_cache(g_udc_mtdblk); + return 0; +} + +int udc_read(unsigned long long offset, unsigned int len, unsigned char *buf) +{ + unsigned long block,sector,i; + + block = offset >> SECTOR_SHIFT; + sector = len >> SECTOR_SHIFT; + dprintk("read dev = ia:%x, s:%d c:%d\r\n",buf,block,sector); + + if (sector <= 8) + { + for(i = 0;i < sector; i++) + { + NAND_LB_Read(block + i,(void *)(buf)); + buf += 512; + } + } + else + NAND_LB_MultiRead(block, buf, sector); + + return len; +} + +int udc_write(unsigned long long offset, unsigned int len, unsigned char *buf) +{ + unsigned long block,sector,i; + + block = offset >> SECTOR_SHIFT; + sector = len >> SECTOR_SHIFT; + dprintk("write dev s:%d c:%d\r\n",block,sector); + + if(sector <= 8) + { + for(i = 0;i < sector; i++) + { + NAND_LB_Write(block + i,(void *)(buf)); + buf += 512; + FlushDataState = 1; + } + }else + NAND_LB_MultiWrite(block,(void *)(buf),sector); + + return len; +} + +EXPORT_SYMBOL_GPL(udc_write); +EXPORT_SYMBOL_GPL(udc_read); +EXPORT_SYMBOL_GPL(NAND_LB_Init); +EXPORT_SYMBOL_GPL(NAND_LB_FLASHCACHE); +EXPORT_SYMBOL_GPL(FlushDataState); +EXPORT_SYMBOL_GPL(NAND_MTD_FLASHCACHE); diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 5f6509a..c0acd09 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -207,6 +207,24 @@ config MII or internal device. It is safe to say Y or M here even if your ethernet card lack MII. +config JZ_ETH + tristate "JZ4730/JZ5730 On-Chip Ethernet support" + depends on NET_ETHERNET && (SOC_JZ4730 || SOC_JZ5730 || JZ_FPGA) + help + Say Y for support of JZ4730/JZ5730 On-Chip Ethernet interface. + + To compile this driver as a module, choose M here: the module + will be called jz_eth. + +config JZCS8900 + tristate "JZ CS8900A Ethernet support" + depends on NET_ETHERNET && (SOC_JZ4740 || SOC_JZ4750) + help + Say Y for support of JZ CS8900A Ethernet interface. + + To compile this driver as a module, choose M here: the module + will be called jzcs8900a. + config MACB tristate "Atmel MACB support" depends on AVR32 || ARCH_AT91SAM9260 || ARCH_AT91SAM9263 || ARCH_AT91SAM9G20 || ARCH_AT91CAP9 diff --git a/drivers/net/Makefile b/drivers/net/Makefile index ead8cab..8d87dfd 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -103,6 +103,8 @@ obj-$(CONFIG_MII) += mii.o obj-$(CONFIG_MDIO) += mdio.o obj-$(CONFIG_PHYLIB) += phy/ +obj-$(CONFIG_JZ_ETH) += jz_eth.o +obj-$(CONFIG_JZCS8900) += jzcs8900a.o obj-$(CONFIG_SUNDANCE) += sundance.o obj-$(CONFIG_HAMACHI) += hamachi.o obj-$(CONFIG_NET) += Space.o loopback.o diff --git a/drivers/net/jz_eth.c b/drivers/net/jz_eth.c new file mode 100644 index 0000000..ade9556 --- /dev/null +++ b/drivers/net/jz_eth.c @@ -0,0 +1,1290 @@ +/* + * linux/drivers/net/jz_eth.c + * + * Jz4730/Jz5730 On-Chip ethernet driver. + * + * 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "jz_eth.h" + +#define P2ADDR(a) (((unsigned long)(a) & 0x1fffffff) | 0xa0000000) +#define P1ADDR(a) (((unsigned long)(a) & 0x1fffffff) | 0x80000000) + +//#define DEBUG +#ifdef DEBUG +# define DBPRINTK(fmt,args...) printk(KERN_DEBUG fmt,##args) +#else +# define DBPRINTK(fmt,args...) do {} while(0) +#endif + +#define errprintk(fmt,args...) printk(KERN_ERR fmt,##args); +#define infoprintk(fmt,args...) printk(KERN_INFO fmt,##args); + +#define DRV_NAME "jz_eth" +#define DRV_VERSION "1.2" +#define DRV_AUTHOR "Peter Wei " +#define DRV_DESC "JzSOC On-chip Ethernet driver" + +MODULE_AUTHOR(DRV_AUTHOR); +MODULE_DESCRIPTION(DRV_DESC); +MODULE_LICENSE("GPL"); + +/* + * Local variables + */ +static struct net_device *netdev; +static char * hwaddr = NULL; +static int debug = -1; +static struct mii_if_info mii_info; + +MODULE_PARM_DESC(debug, "i"); +MODULE_PARM_DESC(hwaddr,"s"); + +/* + * Local routines + */ +static irqreturn_t jz_eth_interrupt(int irq, void *dev_id); + +static int link_check_thread (void *data); + +/* + * Get MAC address + */ + +#define I2C_DEVICE 0x57 +#define MAC_OFFSET 64 + +extern void i2c_open(void); +extern void i2c_close(void); +extern int i2c_read(unsigned char device, unsigned char *buf, + unsigned char address, int count); + +static 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 */ +} + +static 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; + } +} + +static int ethaddr_cmd = 0; +static unsigned char ethaddr_hex[6]; + +static int __init ethernet_addr_setup(char *str) +{ + if (!str) { + printk("ethaddr not set in command line\n"); + return -1; + } + ethaddr_cmd = 1; + str2eaddr(ethaddr_hex, str); + + return 0; +} + +__setup("ethaddr=", ethernet_addr_setup); + +static int get_mac_address(struct net_device *dev) +{ + int i; + unsigned char flag0=0; + unsigned char flag1=0xff; + + dev->dev_addr[0] = 0xff; + if (hwaddr != NULL) { + /* insmod jz-ethc.o hwaddr=00:ef:a3:c1:00:10 */ + str2eaddr(dev->dev_addr, hwaddr); + } else if (ethaddr_cmd) { + /* linux command line: ethaddr=00:ef:a3:c1:00:10 */ + for (i=0; i<6; i++) + dev->dev_addr[i] = ethaddr_hex[i]; + } else { +#if 0 + /* mac address in eeprom: byte 0x40-0x45 */ + i2c_open(); + i2c_read(I2C_DEVICE, dev->dev_addr, MAC_OFFSET, 6); + i2c_close(); +#endif + } + + /* check whether valid MAC address */ + for (i=0; i<6; i++) { + flag0 |= dev->dev_addr[i]; + flag1 &= dev->dev_addr[i]; + } + if ((dev->dev_addr[0] & 0xC0) || (flag0 == 0) || (flag1 == 0xff)) { + printk("WARNING: There is not MAC address, use default ..\n"); + dev->dev_addr[0] = 0x00; + dev->dev_addr[1] = 0xef; + dev->dev_addr[2] = 0xa3; + dev->dev_addr[3] = 0xc1; + dev->dev_addr[4] = 0x00; + dev->dev_addr[5] = 0x10; + dev->dev_addr[5] = 0x03; + } + return 0; +} + +/*---------------------------------------------------------------------*/ + +static u32 jz_eth_curr_mode(struct net_device *dev); + +/* + * Ethernet START/STOP routines + */ +#define START_ETH { \ + s32 val; \ + val = readl(DMA_OMR); \ + val |= OMR_ST | OMR_SR; \ + writel(val, DMA_OMR); \ +} + +#define STOP_ETH { \ + s32 val; \ + val = readl(DMA_OMR); \ + val &= ~(OMR_ST|OMR_SR); \ + writel(val, DMA_OMR); \ +} + +/* + * Link check routines + */ +static void start_check(struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + + np->thread_die = 0; + init_waitqueue_head(&np->thr_wait); + init_completion (&np->thr_exited); + np->thr_pid = kernel_thread (link_check_thread,(void *)dev, + CLONE_FS | CLONE_FILES); + if (np->thr_pid < 0) + errprintk("%s: unable to start kernel thread\n",dev->name); +} + +static int close_check(struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + int ret = 0; + + if (np->thr_pid >= 0) { + np->thread_die = 1; + wmb(); + ret = kill_proc (np->thr_pid, SIGTERM, 1); + if (ret) { + errprintk("%s: unable to signal thread\n", dev->name); + return 1; + } + wait_for_completion (&np->thr_exited); + } + return 0; +} + +static int link_check_thread(void *data) +{ + struct net_device *dev=(struct net_device *)data; + struct jz_eth_private *np = (struct jz_eth_private *)netdev->priv; + unsigned char current_link; + unsigned long timeout; + + daemonize("%s", dev->name); + spin_lock_irq(¤t->sighand->siglock); + sigemptyset(¤t->blocked); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + strncpy (current->comm, dev->name, sizeof(current->comm) - 1); + current->comm[sizeof(current->comm) - 1] = '\0'; + + while (1) { + timeout = 3*HZ; + do { + timeout = interruptible_sleep_on_timeout (&np->thr_wait, timeout); + /* make swsusp happy with our thread */ +// if (current->flags & PF_FREEZE) +// refrigerator(PF_FREEZE); + } while (!signal_pending (current) && (timeout > 0)); + + if (signal_pending (current)) { + spin_lock_irq(¤t->sighand->siglock); + flush_signals(current); + spin_unlock_irq(¤t->sighand->siglock); + } + + if (np->thread_die) + break; + + current_link=mii_link_ok(&mii_info); + if (np->link_state!=current_link) { + if (current_link) { + infoprintk("%s: Ethernet Link OK!\n",dev->name); + jz_eth_curr_mode(dev); + netif_carrier_on(dev); + } + else { + errprintk("%s: Ethernet Link offline!\n",dev->name); + netif_carrier_off(dev); + } + } + np->link_state=current_link; + + } + complete_and_exit (&np->thr_exited, 0); +} + +#ifdef DEBUG +/* + * Display ethernet packet header + * This routine is used for test function + */ +static void eth_dbg_rx(struct sk_buff *skb, int len) +{ + + int i, j; + + printk("R: %02x:%02x:%02x:%02x:%02x:%02x <- %02x:%02x:%02x:%02x:%02x:%02x len/SAP:%02x%02x [%d]\n", + (u8)skb->data[0], + (u8)skb->data[1], + (u8)skb->data[2], + (u8)skb->data[3], + (u8)skb->data[4], + (u8)skb->data[5], + (u8)skb->data[6], + (u8)skb->data[7], + (u8)skb->data[8], + (u8)skb->data[9], + (u8)skb->data[10], + (u8)skb->data[11], + (u8)skb->data[12], + (u8)skb->data[13], + len); + for (j=0; len>0; j+=16, len-=16) { + printk(" %03x: ",j); + for (i=0; i<16 && idata[i+j]); + } + printk("\n"); + } + return; + } +#endif + +/* + * Reset ethernet device + */ +static inline void jz_eth_reset(void) +{ + u32 i; + i = readl(DMA_BMR); + writel(i | BMR_SWR, DMA_BMR); + for(i = 0; i < 1000; i++) { + if(!(readl(DMA_BMR) & BMR_SWR)) break; + mdelay(1); + } +} + +/* + * MII operation routines + */ +static inline void mii_wait(void) +{ + int i; + for(i = 0; i < 10000; i++) { + if(!(readl(MAC_MIIA) & 0x1)) + break; + mdelay(1); + } + if (i >= 10000) + printk("MII wait timeout : %d.\n", i); +} + +static int mdio_read(struct net_device *dev,int phy_id, int location) +{ + u32 mii_cmd = (phy_id << 11) | (location << 6) | 1; + int retval = 0; + + writel(mii_cmd, MAC_MIIA); + mii_wait(); + retval = readl(MAC_MIID) & 0x0000ffff; + + return retval; + +} + +static void mdio_write(struct net_device *dev,int phy_id, int location, int data) +{ + u32 mii_cmd = (phy_id << 11) | (location << 6) | 0x2 | 1; + + writel(mii_cmd, MAC_MIIA); + writel(data & 0x0000ffff, MAC_MIID); + mii_wait(); +} + + +/* + * Search MII phy + */ +static int jz_search_mii_phy(struct net_device *dev) +{ + + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + int phy, phy_idx = 0; + + np->valid_phy = 0xff; + for (phy = 0; phy < 32; phy++) { + int mii_status = mdio_read(dev,phy, 1); + if (mii_status != 0xffff && mii_status != 0x0000) { + np->phys[phy_idx] = phy; + np->ecmds[phy_idx].speed=SPEED_100; + np->ecmds[phy_idx].duplex=DUPLEX_FULL; + np->ecmds[phy_idx].port=PORT_MII; + np->ecmds[phy_idx].transceiver=XCVR_INTERNAL; + np->ecmds[phy_idx].phy_address=np->phys[phy_idx]; + np->ecmds[phy_idx].autoneg=AUTONEG_ENABLE; + np->ecmds[phy_idx].advertising=(ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_100baseT_Full); + phy_idx++; + } + } + if (phy_idx == 1) { + np->valid_phy = np->phys[0]; + np->phy_type = 0; + } + if (phy_idx != 0) { + phy = np->valid_phy; + np->advertising = mdio_read(dev,phy, 4); + } + return phy_idx; +} + +/* + * CRC calc for Destination Address for gets hashtable index + */ + +#define POLYNOMIAL 0x04c11db7UL +static u16 jz_hashtable_index(u8 *addr) +{ +#if 1 + u32 crc = 0xffffffff, msb; + int i, j; + u32 byte; + for (i = 0; i < 6; i++) { + byte = *addr++; + for (j = 0; j < 8; j++) { + msb = crc >> 31; + crc <<= 1; + if (msb ^ (byte & 1)) crc ^= POLYNOMIAL; + byte >>= 1; + } + } + return ((int)(crc >> 26)); +#endif +#if 0 + int crc = -1; + int length=6; + int bit; + unsigned char current_octet; + while (--length >= 0) { + current_octet = *addr++; + for (bit = 0; bit < 8; bit++, current_octet >>= 1) + crc = (crc << 1) ^ ((crc < 0) ^ (current_octet & 1) ? + POLYNOMIAL : 0); + } + return ((int)(crc >> 26)); +#endif +} + +/* + * Multicast filter and config multicast hash table + */ +#define MULTICAST_FILTER_LIMIT 64 + +static void jz_set_multicast_list(struct net_device *dev) +{ + int i, hash_index; + u32 mcr, hash_h, hash_l, hash_bit; + + mcr = readl(MAC_MCR); + mcr &= ~(MCR_PR | MCR_PM | MCR_HP); + + if (dev->flags & IFF_PROMISC) { + /* Accept any kinds of packets */ + mcr |= MCR_PR; + hash_h = 0xffffffff; + hash_l = 0xffffffff; + DBPRINTK("%s: enter promisc mode!\n",dev->name); + } + else if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > MULTICAST_FILTER_LIMIT)){ + /* Accept all multicast packets */ + mcr |= MCR_PM; + hash_h = 0xffffffff; + hash_l = 0xffffffff; + DBPRINTK("%s: enter allmulticast mode! %d \n",dev->name,dev->mc_count); + } + else if (dev->flags & IFF_MULTICAST) + { + /* Update multicast hash table */ + struct dev_mc_list *mclist; + hash_h = readl(MAC_HTH); + hash_l = readl(MAC_HTL); + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) + { + hash_index = jz_hashtable_index(mclist->dmi_addr); + hash_bit=0x00000001; + hash_bit <<= (hash_index & 0x1f); + if (hash_index > 0x1f) + hash_h |= hash_bit; + else + hash_l |= hash_bit; + DBPRINTK("----------------------------\n"); +#ifdef DEBUG + int j; + for (j=0;jdmi_addrlen;j++) + printk("%2.2x:",mclist->dmi_addr[j]); + printk("\n"); +#endif + DBPRINTK("dmi.addrlen => %d\n",mclist->dmi_addrlen); + DBPRINTK("dmi.users => %d\n",mclist->dmi_users); + DBPRINTK("dmi.gusers => %d\n",mclist->dmi_users); + } + writel(hash_h,MAC_HTH); + writel(hash_l,MAC_HTL); + mcr |= MCR_HP; + DBPRINTK("This is multicast hash table high bits [%4.4x]\n",readl(MAC_HTH)); + DBPRINTK("This is multicast hash table low bits [%4.4x]\n",readl(MAC_HTL)); + DBPRINTK("%s: enter multicast mode!\n",dev->name); + } + writel(mcr,MAC_MCR); +} + +static inline int jz_phy_reset(struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + unsigned int mii_reg0; + unsigned int count; + + mii_reg0 = mdio_read(dev,np->valid_phy,MII_BMCR); + mii_reg0 |=MII_CR_RST; + mdio_write(dev,np->valid_phy,MII_BMCR,mii_reg0); //reset phy + for ( count = 0; count < 1000; count++) { + mdelay(1); + mii_reg0 = mdio_read(dev,np->valid_phy,MII_BMCR); + if (!(mii_reg0 & MII_CR_RST)) break; //reset completed + } + if (count>=100) + return 1; //phy error + else + return 0; +} + +/* + * Show all mii registers - this routine is used for test + */ +#ifdef DEBUG +static void mii_db_out(struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + unsigned int mii_test; + + mii_test = mdio_read(dev,np->valid_phy,MII_BMCR); + DBPRINTK("BMCR ====> 0x%4.4x \n",mii_test); + + mii_test = mdio_read(dev,np->valid_phy,MII_BMSR); + DBPRINTK("BMSR ====> 0x%4.4x \n",mii_test); + + mii_test = mdio_read(dev,np->valid_phy,MII_ANAR); + DBPRINTK("ANAR ====> 0x%4.4x \n",mii_test); + + mii_test = mdio_read(dev,np->valid_phy,MII_ANLPAR); + DBPRINTK("ANLPAR ====> 0x%4.4x \n",mii_test); + + mii_test = mdio_read(dev,np->valid_phy,16); + DBPRINTK("REG16 ====> 0x%4.4x \n",mii_test); + + mii_test = mdio_read(dev,np->valid_phy,17); + DBPRINTK("REG17 ====> 0x%4.4x \n",mii_test); +} +#endif + +/* + * Start Auto-Negotiation function for PHY + */ +static int jz_autonet_complete(struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + int count; + u32 mii_reg1, timeout = 3000; + + for (count = 0; count < timeout; count++) { + mdelay(1); + mii_reg1 = mdio_read(dev,np->valid_phy,MII_BMSR); + if (mii_reg1 & 0x0020) break; + } + //mii_db_out(dev); //for debug to display all register of MII + if (count >= timeout) + return 1; //auto negotiation error + else + return 0; +} + +/* + * Get current mode of eth phy + */ +static u32 jz_eth_curr_mode(struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + unsigned int mii_reg17; + u32 flag = 0; + + mii_reg17 = mdio_read(dev,np->valid_phy,MII_DSCSR); + np->media = mii_reg17>>12; + if (np->media==8) { + infoprintk("%s: Current Operation Mode is [100M Full Duplex]",dev->name); + flag = 0; + np->full_duplex=1; + } + if (np->media==4) { + infoprintk("%s: Current Operation Mode is [100M Half Duplex]",dev->name); + flag = 0; + np->full_duplex=0; + } + if (np->media==2) { + infoprintk("%s: Current Operation Mode is [10M Full Duplex]",dev->name); + flag = OMR_TTM; + np->full_duplex=1; + } + if (np->media==1) { + infoprintk("%s: Current Operation Mode is [10M Half Duplex]",dev->name); + flag = OMR_TTM; + np->full_duplex=0; + } + printk("\n"); + return flag; +} + +/* + * Ethernet device hardware init + * This routine initializes the ethernet device hardware and PHY + */ +static int jz_init_hw(struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + struct ethtool_cmd ecmd; + u32 mcr, omr; + u32 sts, flag = 0; + int i; + + jz_eth_reset(); + STOP_ETH; +#if 0 + /* mii operation */ + if (jz_phy_reset(dev)) { + errprintk("PHY device do not reset!\n"); + return -EPERM; // return operation not permitted + } +#endif + /* Set MAC address */ + writel(le32_to_cpu(*(unsigned long *)&dev->dev_addr[0]), MAC_MAL); + writel(le32_to_cpu(*(unsigned long *)&dev->dev_addr[4]), MAC_MAH); + printk("%s: JZ On-Chip ethernet (MAC ", dev->name); + for (i = 0; i < 5; i++) { + printk("%2.2x:", dev->dev_addr[i]); + } + printk("%2.2x, IRQ %d)\n", dev->dev_addr[i], dev->irq); + + np->mii_phy_cnt = jz_search_mii_phy(dev); + printk("%s: Found %d PHY on JZ MAC\n", dev->name, np->mii_phy_cnt); + + mii_info.phy_id = np->valid_phy; + mii_info.dev = dev; + mii_info.mdio_read = &mdio_read; + mii_info.mdio_write = &mdio_write; + + ecmd.speed = SPEED_100; + ecmd.duplex = DUPLEX_FULL; + ecmd.port = PORT_MII; + ecmd.transceiver = XCVR_INTERNAL; + ecmd.phy_address = np->valid_phy; + ecmd.autoneg = AUTONEG_ENABLE; + + mii_ethtool_sset(&mii_info,&ecmd); + if (jz_autonet_complete(dev)) + errprintk("%s: Ethernet Module AutoNegotiation failed\n",dev->name); + mii_ethtool_gset(&mii_info,&ecmd); + + infoprintk("%s: Provide Modes: ",dev->name); + for (i = 0; i < 5;i++) + if (ecmd.advertising & (1<full_duplex) + mcr |= MCR_FDX; + mcr |= MCR_BFD | MCR_TE | MCR_RE | MCR_OWD|MCR_HBD; + writel(mcr, MAC_MCR); +// mcr &= (readl(MAC_MCR) & ~(MCR_PM | MCR_PR | MCR_IF | MCR_HO | MCR_HP)); +// mcr &= 0xffdf; +// mcr |= 0x0020; +// writel(mcr, MAC_MCR); + + /* Set base address of TX and RX descriptors */ + writel(np->dma_rx_ring, DMA_RRBA); + writel(np->dma_tx_ring, DMA_TRBA); + + START_ETH; + + /* set interrupt mask */ + writel(IMR_DEFAULT | IMR_ENABLE, DMA_IMR); + + /* Reset any pending (stale) interrupts */ + sts = readl(DMA_STS); + writel(sts, DMA_STS); + + return 0; +} + +static int jz_eth_open(struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + int retval, i; + + retval = request_irq(dev->irq, jz_eth_interrupt, 0, dev->name, dev); + if (retval) { + errprintk("%s: unable to get IRQ %d .\n", dev->name, dev->irq); + return -EAGAIN; + } + + for (i = 0; i < NUM_RX_DESCS; i++) { + np->rx_ring[i].status = cpu_to_le32(R_OWN); + np->rx_ring[i].desc1 = cpu_to_le32(RX_BUF_SIZE | RD_RCH); + np->rx_ring[i].buf1_addr = cpu_to_le32(np->dma_rx_buf + i*RX_BUF_SIZE); + np->rx_ring[i].next_addr = cpu_to_le32(np->dma_rx_ring + (i+1) * sizeof (jz_desc_t)); + } + np->rx_ring[NUM_RX_DESCS - 1].next_addr = cpu_to_le32(np->dma_rx_ring); + + for (i = 0; i < NUM_TX_DESCS; i++) { + np->tx_ring[i].status = cpu_to_le32(0); + np->tx_ring[i].desc1 = cpu_to_le32(TD_TCH); + np->tx_ring[i].buf1_addr = 0; + np->tx_ring[i].next_addr = cpu_to_le32(np->dma_tx_ring + (i+1) * sizeof (jz_desc_t)); + } + np->tx_ring[NUM_TX_DESCS - 1].next_addr = cpu_to_le32(np->dma_tx_ring); + + np->rx_head = 0; + np->tx_head = np->tx_tail = 0; + + jz_init_hw(dev); + + dev->trans_start = jiffies; + netif_start_queue(dev); + start_check(dev); + + return 0; +} + +static int jz_eth_close(struct net_device *dev) +{ + netif_stop_queue(dev); + close_check(dev); + STOP_ETH; + free_irq(dev->irq, dev); + return 0; +} + +/* + * Get the current statistics. + * This may be called with the device open or closed. + */ +static struct net_device_stats * jz_eth_get_stats(struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + int tmp; + + tmp = readl(DMA_MFC); // After read clear to zero + np->stats.rx_missed_errors += (tmp & MFC_CNT2) + ((tmp & MFC_CNT1) >> 16); + + return &np->stats; +} + +/* + * ethtool routines + */ +static int jz_ethtool_ioctl(struct net_device *dev, void *useraddr) +{ + struct jz_eth_private *np = dev->priv; + u32 ethcmd; + + /* dev_ioctl() in ../../net/core/dev.c has already checked + capable(CAP_NET_ADMIN), so don't bother with that here. */ + + if (get_user(ethcmd, (u32 *)useraddr)) + return -EFAULT; + + switch (ethcmd) { + + case ETHTOOL_GDRVINFO: { + struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; + strcpy (info.driver, DRV_NAME); + strcpy (info.version, DRV_VERSION); + strcpy (info.bus_info, "OCS"); + if (copy_to_user (useraddr, &info, sizeof (info))) + return -EFAULT; + return 0; + } + + /* get settings */ + case ETHTOOL_GSET: { + struct ethtool_cmd ecmd = { ETHTOOL_GSET }; + spin_lock_irq(&np->lock); + mii_ethtool_gset(&mii_info, &ecmd); + spin_unlock_irq(&np->lock); + if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) + return -EFAULT; + return 0; + } + /* set settings */ + case ETHTOOL_SSET: { + int r; + struct ethtool_cmd ecmd; + if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) + return -EFAULT; + spin_lock_irq(&np->lock); + r = mii_ethtool_sset(&mii_info, &ecmd); + spin_unlock_irq(&np->lock); + return r; + } + /* restart autonegotiation */ + case ETHTOOL_NWAY_RST: { + return mii_nway_restart(&mii_info); + } + /* get link status */ + case ETHTOOL_GLINK: { + struct ethtool_value edata = {ETHTOOL_GLINK}; + edata.data = mii_link_ok(&mii_info); + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + + /* get message-level */ + case ETHTOOL_GMSGLVL: { + struct ethtool_value edata = {ETHTOOL_GMSGLVL}; + edata.data = debug; + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + /* set message-level */ + case ETHTOOL_SMSGLVL: { + struct ethtool_value edata; + if (copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + debug = edata.data; + return 0; + } + + + default: + break; + } + + return -EOPNOTSUPP; + +} + +/* + * Config device + */ +static int jz_eth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct jz_eth_private *np =(struct jz_eth_private *)dev->priv; + struct mii_ioctl_data *data, rdata; + + switch (cmd) { + case SIOCETHTOOL: + return jz_ethtool_ioctl(dev, (void *) rq->ifr_data); + case SIOCGMIIPHY: + case SIOCDEVPRIVATE: + data = (struct mii_ioctl_data *)&rq->ifr_data; + data->phy_id = np->valid_phy; + case SIOCGMIIREG: + case SIOCDEVPRIVATE+1: + data = (struct mii_ioctl_data *)&rq->ifr_data; + data->val_out = mdio_read(dev,np->valid_phy, data->reg_num & 0x1f); + return 0; + case SIOCSMIIREG: + case SIOCDEVPRIVATE+2: + data = (struct mii_ioctl_data *)&rq->ifr_data; + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + mdio_write(dev,np->valid_phy, data->reg_num & 0x1f, data->val_in); + return 0; + case READ_COMMAND: + data = (struct mii_ioctl_data *)rq->ifr_data; + if (copy_from_user(&rdata,data,sizeof(rdata))) + return -EFAULT; + rdata.val_out = mdio_read(dev,rdata.phy_id, rdata.reg_num & 0x1f); + if (copy_to_user(data,&rdata,sizeof(rdata))) + return -EFAULT; + return 0; + case WRITE_COMMAND: + if (np->phy_type==1) { + data = (struct mii_ioctl_data *)rq->ifr_data; + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (copy_from_user(&rdata,data,sizeof(rdata))) + return -EFAULT; + mdio_write(dev,rdata.phy_id, rdata.reg_num & 0x1f, rdata.val_in); + } + return 0; + case GETDRIVERINFO: + if (np->phy_type==1) { + data = (struct mii_ioctl_data *)rq->ifr_data; + if (copy_from_user(&rdata,data,sizeof(rdata))) + return -EFAULT; + rdata.val_in = 0x1; + rdata.val_out = 0x00d0; + if (copy_to_user(data,&rdata,sizeof(rdata))) + return -EFAULT; + } + return 0; + default: + return -EOPNOTSUPP; + } + return 0; +} + +/* + * Received one packet + */ +static void eth_rxready(struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private*)dev->priv; + struct sk_buff *skb; + unsigned char *pkt_ptr; + u32 pkt_len; + u32 status; + + status = le32_to_cpu(np->rx_ring[np->rx_head].status); + while (!(status & R_OWN)) { /* owner bit = 0 */ + if (status & RD_ES) { /* error summary */ + np->stats.rx_errors++; /* Update the error stats. */ + if (status & (RD_RF | RD_TL)) + np->stats.rx_frame_errors++; + if (status & RD_CE) + np->stats.rx_crc_errors++; + if (status & RD_TL) + np->stats.rx_length_errors++; + } else { + pkt_ptr = bus_to_virt(le32_to_cpu(np->rx_ring[np->rx_head].buf1_addr)); + pkt_len = ((status & RD_FL) >> 16) - 4; + + skb = dev_alloc_skb(pkt_len + 2); + if (skb == NULL) { + printk("%s: Memory squeeze, dropping.\n", + dev->name); + np->stats.rx_dropped++; + break; + } + skb->dev = dev; + skb_reserve(skb, 2); /* 16 byte align */ + + //pkt_ptr = P1ADDR(pkt_ptr); + //dma_cache_inv(pkt_ptr, pkt_len); + memcpy(skb->data, pkt_ptr, pkt_len); + skb_put(skb, pkt_len); + + //eth_dbg_rx(skb, pkt_len); + skb->protocol = eth_type_trans(skb,dev); + netif_rx(skb); /* pass the packet to upper layers */ + dev->last_rx = jiffies; + np->stats.rx_packets++; + np->stats.rx_bytes += pkt_len; + } + np->rx_ring[np->rx_head].status = cpu_to_le32(R_OWN); + + np->rx_head ++; + if (np->rx_head >= NUM_RX_DESCS) + np->rx_head = 0; + status = le32_to_cpu(np->rx_ring[np->rx_head].status); + } +} + +/* + * Tx timeout routine + */ +static void jz_eth_tx_timeout(struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + + jz_init_hw(dev); + np->stats.tx_errors ++; + netif_wake_queue(dev); +} + +/* + * One packet was transmitted + */ +static void eth_txdone(struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private*)dev->priv; + int tx_tail = np->tx_tail; + + while (tx_tail != np->tx_head) { + int entry = tx_tail % NUM_TX_DESCS; + s32 status = le32_to_cpu(np->tx_ring[entry].status); + if(status < 0) break; + if (status & TD_ES ) { /* Error summary */ + np->stats.tx_errors++; + if (status & TD_NC) np->stats.tx_carrier_errors++; + if (status & TD_LC) np->stats.tx_window_errors++; + if (status & TD_UF) np->stats.tx_fifo_errors++; + if (status & TD_DE) np->stats.tx_aborted_errors++; + if (np->tx_head != np->tx_tail) + writel(1, DMA_TPD); /* Restart a stalled TX */ + } else + np->stats.tx_packets++; + /* Update the collision counter */ + np->stats.collisions += ((status & TD_EC) ? 16 : ((status & TD_CC) >> 3)); + /* Free the original skb */ + if (np->tx_skb[entry]) { + dev_kfree_skb_irq(np->tx_skb[entry]); + np->tx_skb[entry] = 0; + } + tx_tail++; + } + if (np->tx_full && (tx_tail + NUM_TX_DESCS > np->tx_head + 1)) { + /* The ring is no longer full */ + np->tx_full = 0; + netif_start_queue(dev); + } + np->tx_tail = tx_tail; +} + +/* + * Update the tx descriptor + */ +static void load_tx_packet(struct net_device *dev, char *buf, u32 flags, struct sk_buff *skb) +{ + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + int entry = np->tx_head % NUM_TX_DESCS; + + np->tx_ring[entry].buf1_addr = cpu_to_le32(virt_to_bus(buf)); + np->tx_ring[entry].desc1 &= cpu_to_le32((TD_TER | TD_TCH)); + np->tx_ring[entry].desc1 |= cpu_to_le32(flags); + np->tx_ring[entry].status = cpu_to_le32(T_OWN); + np->tx_skb[entry] = skb; +} + +/* + * Transmit one packet + */ +static int jz_eth_send_packet(struct sk_buff *skb, struct net_device *dev) +{ + struct jz_eth_private *np = (struct jz_eth_private *)dev->priv; + u32 length; + + if (np->tx_full) { + return 0; + } +#ifdef CONFIG_FPGA + mdelay(10); +#else + udelay(500); /* FIXME: can we remove this delay ? */ +#endif + length = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len; + dma_cache_wback((unsigned long)skb->data, length); + load_tx_packet(dev, (char *)skb->data, TD_IC | TD_LS | TD_FS | length, skb); + spin_lock_irq(&np->lock); + np->tx_head ++; + np->stats.tx_bytes += length; + writel(1, DMA_TPD); /* Start the TX */ + dev->trans_start = jiffies; /* for timeout */ + if (np->tx_tail + NUM_TX_DESCS > np->tx_head + 1) { + np->tx_full = 0; + } + else { + np->tx_full = 1; + netif_stop_queue(dev); + } + spin_unlock_irq(&np->lock); + + return 0; +} + +/* + * Interrupt service routine + */ +static irqreturn_t jz_eth_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct jz_eth_private *np = dev->priv; + u32 sts; + int i; + + spin_lock(&np->lock); + + writel((readl(DMA_IMR) & ~IMR_ENABLE), DMA_IMR); /* Disable interrupt */ + + for (i = 0; i < 100; i++) { + sts = readl(DMA_STS); + writel(sts, DMA_STS); /* clear status */ + + if (!(sts & IMR_DEFAULT)) break; + + if (sts & (DMA_INT_RI | DMA_INT_RU)) /* Rx IRQ */ + eth_rxready(dev); + if (sts & (DMA_INT_TI | DMA_INT_TU)) /* Tx IRQ */ + eth_txdone(dev); + + /* check error conditions */ + if (sts & DMA_INT_FB){ /* fatal bus error */ + STOP_ETH; + errprintk("%s: Fatal bus error occurred, sts=%#8x, device stopped.\n",dev->name, sts); + break; + } + + if (sts & DMA_INT_UN) { /* Transmit underrun */ + u32 omr; + omr = readl(DMA_OMR); + if (!(omr & OMR_SF)) { + omr &= ~(OMR_ST | OMR_SR); + writel(omr, DMA_OMR); + while (readl(DMA_STS) & STS_TS); /* wait for stop */ + if ((omr & OMR_TR) < OMR_TR) { /* ? */ + omr += TR_24; + } else { + omr |= OMR_SF; + } + writel(omr | OMR_ST | OMR_SR, DMA_OMR); + } + } + } + + writel(readl(DMA_IMR) | IMR_ENABLE, DMA_IMR); /* enable interrupt */ + + spin_unlock(&np->lock); + + return IRQ_HANDLED; +} + +#if 0 //def CONFIG_PM +/* + * Suspend the ETH interface. + */ +static int jz_eth_suspend(struct net_device *dev, int state) +{ + struct jz_eth_private *jep = (struct jz_eth_private *)dev->priv; + unsigned long flags, tmp; + + printk("ETH suspend.\n"); + + if (!netif_running(dev)) { + return 0; + } + + netif_device_detach(dev); + + spin_lock_irqsave(&jep->lock, flags); + + /* Disable interrupts, stop Tx and Rx. */ + REG32(DMA_IMR) = 0; + STOP_ETH; + + /* Update the error counts. */ + tmp = REG32(DMA_MFC); + jep->stats.rx_missed_errors += (tmp & 0x1ffff); + jep->stats.rx_fifo_errors += ((tmp >> 17) & 0x7ff); + + spin_unlock_irqrestore(&jep->lock, flags); + + return 0; +} + +/* + * Resume the ETH interface. + */ +static int jz_eth_resume(struct net_device *dev) +{ + printk("ETH resume.\n"); + + if (!netif_running(dev)) + return 0; + + jz_init_hw(dev); + + netif_device_attach(dev); + jz_eth_tx_timeout(dev); + netif_wake_queue(dev); + + return 0; +} + +static int jz_eth_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + int ret; + + if (!dev->data) + return -EINVAL; + + switch (rqst) { + case PM_SUSPEND: + ret = jz_eth_suspend((struct net_device *)dev->data, + (int)data); + break; + + case PM_RESUME: + ret = jz_eth_resume((struct net_device *)dev->data); + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +#endif /* CONFIG_PM */ + +static int __init jz_eth_init(void) +{ + struct net_device *dev; + struct jz_eth_private *np; + int err; + + dev = alloc_etherdev(sizeof(struct jz_eth_private)); + if (!dev) { + printk(KERN_ERR "%s: alloc_etherdev failed\n", DRV_NAME); + return -ENOMEM; + } + netdev = dev; + + np = (struct jz_eth_private *)P2ADDR(dev->priv); + dev->priv = np; + memset(np, 0, sizeof(struct jz_eth_private)); + + np->vaddr_rx_buf = (u32)dma_alloc_noncoherent(NULL, NUM_RX_DESCS*RX_BUF_SIZE, + &np->dma_rx_buf, 0); + + if (!np->vaddr_rx_buf) { + printk(KERN_ERR "%s: Cannot alloc dma buffers\n", DRV_NAME); + unregister_netdev(dev); + free_netdev(dev); + return -ENOMEM; + } + + np->dma_rx_ring = virt_to_bus(np->rx_ring); + np->dma_tx_ring = virt_to_bus(np->tx_ring); + np->full_duplex = 1; + np->link_state = 1; + + spin_lock_init(&np->lock); + + ether_setup(dev); + dev->irq = IRQ_ETH; + dev->open = jz_eth_open; + dev->stop = jz_eth_close; + dev->hard_start_xmit = jz_eth_send_packet; + dev->get_stats = jz_eth_get_stats; + dev->set_multicast_list = jz_set_multicast_list; + dev->do_ioctl = jz_eth_ioctl; + dev->tx_timeout = jz_eth_tx_timeout; + dev->watchdog_timeo = ETH_TX_TIMEOUT; + + /* configure MAC address */ + get_mac_address(dev); + + if ((err = register_netdev(dev)) != 0) { + printk(KERN_ERR "%s: Cannot register net device, error %d\n", + DRV_NAME, err); + free_netdev(dev); + return -ENOMEM; + } + +//#ifdef 0 //CONFIG_PM +// np->pmdev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, jz_eth_pm_callback); +// if (np->pmdev) +// np->pmdev->data = dev; +//#endif + + return 0; +} + +static void __exit jz_eth_exit(void) +{ + struct net_device *dev = netdev; + struct jz_eth_private *np = dev->priv; + + unregister_netdev(dev); + dma_free_noncoherent(NULL, NUM_RX_DESCS * RX_BUF_SIZE, + (void *)np->vaddr_rx_buf, np->dma_rx_buf); + free_netdev(dev); +} + +module_init(jz_eth_init); +module_exit(jz_eth_exit); diff --git a/drivers/net/jz_eth.h b/drivers/net/jz_eth.h new file mode 100644 index 0000000..cb8486e --- /dev/null +++ b/drivers/net/jz_eth.h @@ -0,0 +1,403 @@ +/* + * linux/drivers/net/jz_eth.h + * + * Jz4730/Jz5730 On-Chip ethernet driver. + * + * 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __JZ_ETH_H__ +#define __JZ_ETH_H__ + +/* DMA control and status registers */ +#define DMA_BMR (ETH_BASE + 0x1000) // Bus mode +#define DMA_TPD (ETH_BASE + 0x1004) // Transmit poll demand register +#define DMA_RPD (ETH_BASE + 0x1008) // Receieve poll demand register +#define DMA_RRBA (ETH_BASE + 0x100C) // Receieve descriptor base address +#define DMA_TRBA (ETH_BASE + 0x1010) // Transmit descriptor base address +#define DMA_STS (ETH_BASE + 0x1014) // Status register +#define DMA_OMR (ETH_BASE + 0x1018) // Command register +#define DMA_IMR (ETH_BASE + 0x101C) +#define DMA_MFC (ETH_BASE + 0x1020) + +/* DMA CSR8-CSR19 reserved */ +#define DMA_CTA (ETH_BASE + 0x1050) +#define DMA_CRA (ETH_BASE + 0x1054) + +/* Mac control and status registers */ +#define MAC_MCR (ETH_BASE + 0x0000) +#define MAC_MAH (ETH_BASE + 0x0004) +#define MAC_MAL (ETH_BASE + 0x0008) +#define MAC_HTH (ETH_BASE + 0x000C) +#define MAC_HTL (ETH_BASE + 0x0010) +#define MAC_MIIA (ETH_BASE + 0x0014) +#define MAC_MIID (ETH_BASE + 0x0018) +#define MAC_FCR (ETH_BASE + 0x001C) +#define MAC_VTR1 (ETH_BASE + 0x0020) +#define MAC_VTR2 (ETH_BASE + 0x0024) + +/* + * Bus Mode Register (DMA_BMR) + */ +#define BMR_PBL 0x00003f00 /* Programmable Burst Length */ +#define BMR_DSL 0x0000007c /* Descriptor Skip Length */ +#define BMR_BAR 0x00000002 /* Bus ARbitration */ +#define BMR_SWR 0x00000001 /* Software Reset */ + +#define PBL_0 0x00000000 /* DMA burst length = amount in RX FIFO */ +#define PBL_1 0x00000100 /* 1 longword DMA burst length */ +#define PBL_2 0x00000200 /* 2 longwords DMA burst length */ +#define PBL_4 0x00000400 /* 4 longwords DMA burst length */ +#define PBL_8 0x00000800 /* 8 longwords DMA burst length */ +#define PBL_16 0x00001000 /* 16 longwords DMA burst length */ +#define PBL_32 0x00002000 /* 32 longwords DMA burst length */ + +#define DSL_0 0x00000000 /* 0 longword / descriptor */ +#define DSL_1 0x00000004 /* 1 longword / descriptor */ +#define DSL_2 0x00000008 /* 2 longwords / descriptor */ +#define DSL_4 0x00000010 /* 4 longwords / descriptor */ +#define DSL_8 0x00000020 /* 8 longwords / descriptor */ +#define DSL_16 0x00000040 /* 16 longwords / descriptor */ +#define DSL_32 0x00000080 /* 32 longwords / descriptor */ + +/* + * Status Register (DMA_STS) + */ +#define STS_BE 0x03800000 /* Bus Error Bits */ +#define STS_TS 0x00700000 /* Transmit Process State */ +#define STS_RS 0x000e0000 /* Receive Process State */ + +#define TS_STOP 0x00000000 /* Stopped */ +#define TS_FTD 0x00100000 /* Running Fetch Transmit Descriptor */ +#define TS_WEOT 0x00200000 /* Running Wait for End Of Transmission */ +#define TS_QDAT 0x00300000 /* Running Queue skb data into TX FIFO */ +#define TS_RES 0x00400000 /* Reserved */ +#define TS_SPKT 0x00500000 /* Reserved */ +#define TS_SUSP 0x00600000 /* Suspended */ +#define TS_CLTD 0x00700000 /* Running Close Transmit Descriptor */ + +#define RS_STOP 0x00000000 /* Stopped */ +#define RS_FRD 0x00020000 /* Running Fetch Receive Descriptor */ +#define RS_CEOR 0x00040000 /* Running Check for End of Receive Packet */ +#define RS_WFRP 0x00060000 /* Running Wait for Receive Packet */ +#define RS_SUSP 0x00080000 /* Suspended */ +#define RS_CLRD 0x000a0000 /* Running Close Receive Descriptor */ +#define RS_FLUSH 0x000c0000 /* Running Flush RX FIFO */ +#define RS_QRFS 0x000e0000 /* Running Queue RX FIFO into RX Skb */ + +/* + * Operation Mode Register (DMA_OMR) + */ +#define OMR_TTM 0x00400000 /* Transmit Threshold Mode */ +#define OMR_SF 0x00200000 /* Store and Forward */ +#define OMR_TR 0x0000c000 /* Threshold Control Bits */ +#define OMR_ST 0x00002000 /* Start/Stop Transmission Command */ +#define OMR_OSF 0x00000004 /* Operate on Second Frame */ +#define OMR_SR 0x00000002 /* Start/Stop Receive */ + +#define TR_18 0x00000000 /* Threshold set to 18 (32) bytes */ +#define TR_24 0x00004000 /* Threshold set to 24 (64) bytes */ +#define TR_32 0x00008000 /* Threshold set to 32 (128) bytes */ +#define TR_40 0x0000c000 /* Threshold set to 40 (256) bytes */ + +/* + * Missed Frames Counters (DMA_MFC) + */ +//#define MFC_CNT1 0xffff0000 /* Missed Frames Counter Bits by application */ +#define MFC_CNT1 0x0ffe0000 /* Missed Frames Counter Bits by application */ +#define MFC_CNT2 0x0000ffff /* Missed Frames Counter Bits by controller */ + +/* + * Mac control Register (MAC_MCR) + */ +#define MCR_RA 0x80000000 /* Receive All */ +#define MCR_HBD 0x10000000 /* HeartBeat Disable */ +#define MCR_PS 0x08000000 /* Port Select */ +#define MCR_OWD 0x00800000 /* Receive own Disable */ +#define MCR_OM 0x00600000 /* Operating(loopback) Mode */ +#define MCR_FDX 0x00100000 /* Full Duplex Mode */ +#define MCR_PM 0x00080000 /* Pass All Multicast */ +#define MCR_PR 0x00040000 /* Promiscuous Mode */ +#define MCR_IF 0x00020000 /* Inverse Filtering */ +#define MCR_PB 0x00010000 /* Pass Bad Frames */ +#define MCR_HO 0x00008000 /* Hash Only Filtering Mode */ +#define MCR_HP 0x00002000 /* Hash/Perfect Receive Filtering Mode */ +#define MCR_FC 0x00001000 /* Late Collision control */ +#define MCR_BFD 0x00000800 /* Boardcast frame Disable */ +#define MCR_RED 0x00000400 /* Retry Disable */ +#define MCR_APS 0x00000100 /* Automatic pad stripping */ +#define MCR_BL 0x000000c0 /* Back off Limit */ +#define MCR_DC 0x00000020 /* Deferral check */ +#define MCR_TE 0x00000008 /* Transmitter enable */ +#define MCR_RE 0x00000004 /* Receiver enable */ + +#define MCR_MII_10 ( OMR_TTM | MCR_PS) +#define MCR_MII_100 ( MCR_HBD | MCR_PS) + +/* Flow control Register (MAC_FCR) */ +#define FCR_PT 0xffff0000 /* Pause time */ +#define FCR_PCF 0x00000004 /* Pass control frames */ +#define FCR_FCE 0x00000002 /* Flow control enable */ +#define FCR_FCB 0x00000001 /* Flow control busy */ + + +/* Constants for the interrupt mask and + * interrupt status registers. (DMA_SIS and DMA_IMR) + */ +#define DMA_INT_NI 0x00010000 // Normal interrupt summary +#define DMA_INT_AI 0x00008000 // Abnormal interrupt summary +#define DMA_INT_ER 0x00004000 // Early receive interrupt +#define DMA_INT_FB 0x00002000 // Fatal bus error +#define DMA_INT_ET 0x00000400 // Early transmit interrupt +#define DMA_INT_RW 0x00000200 // Receive watchdog timeout +#define DMA_INT_RS 0x00000100 // Receive stop +#define DMA_INT_RU 0x00000080 // Receive buffer unavailble +#define DMA_INT_RI 0x00000040 // Receive interrupt +#define DMA_INT_UN 0x00000020 // Underflow +#define DMA_INT_TJ 0x00000008 // Transmit jabber timeout +#define DMA_INT_TU 0x00000004 // Transmit buffer unavailble +#define DMA_INT_TS 0x00000002 // Transmit stop +#define DMA_INT_TI 0x00000001 // Transmit interrupt + +/* + * Receive Descriptor Bit Summary + */ +#define R_OWN 0x80000000 /* Own Bit */ +#define RD_FF 0x40000000 /* Filtering Fail */ +#define RD_FL 0x3fff0000 /* Frame Length */ +#define RD_ES 0x00008000 /* Error Summary */ +#define RD_DE 0x00004000 /* Descriptor Error */ +#define RD_LE 0x00001000 /* Length Error */ +#define RD_RF 0x00000800 /* Runt Frame */ +#define RD_MF 0x00000400 /* Multicast Frame */ +#define RD_FS 0x00000200 /* First Descriptor */ +#define RD_LS 0x00000100 /* Last Descriptor */ +#define RD_TL 0x00000080 /* Frame Too Long */ +#define RD_CS 0x00000040 /* Collision Seen */ +#define RD_FT 0x00000020 /* Frame Type */ +#define RD_RJ 0x00000010 /* Receive Watchdog timeout*/ +#define RD_RE 0x00000008 /* Report on MII Error */ +#define RD_DB 0x00000004 /* Dribbling Bit */ +#define RD_CE 0x00000002 /* CRC Error */ + +#define RD_RER 0x02000000 /* Receive End Of Ring */ +#define RD_RCH 0x01000000 /* Second Address Chained */ +#define RD_RBS2 0x003ff800 /* Buffer 2 Size */ +#define RD_RBS1 0x000007ff /* Buffer 1 Size */ + +/* + * Transmit Descriptor Bit Summary + */ +#define T_OWN 0x80000000 /* Own Bit */ +#define TD_ES 0x00008000 /* Frame Aborted (error summary)*/ +#define TD_LO 0x00000800 /* Loss Of Carrier */ +#define TD_NC 0x00000400 /* No Carrier */ +#define TD_LC 0x00000200 /* Late Collision */ +#define TD_EC 0x00000100 /* Excessive Collisions */ +#define TD_HF 0x00000080 /* Heartbeat Fail */ +#define TD_CC 0x0000003c /* Collision Counter */ +#define TD_UF 0x00000002 /* Underflow Error */ +#define TD_DE 0x00000001 /* Deferred */ + +#define TD_IC 0x80000000 /* Interrupt On Completion */ +#define TD_LS 0x40000000 /* Last Segment */ +#define TD_FS 0x20000000 /* First Segment */ +#define TD_FT1 0x10000000 /* Filtering Type */ +#define TD_SET 0x08000000 /* Setup Packet */ +#define TD_AC 0x04000000 /* Add CRC Disable */ +#define TD_TER 0x02000000 /* Transmit End Of Ring */ +#define TD_TCH 0x01000000 /* Second Address Chained */ +#define TD_DPD 0x00800000 /* Disabled Padding */ +#define TD_FT0 0x00400000 /* Filtering Type */ +#define TD_TBS2 0x003ff800 /* Buffer 2 Size */ +#define TD_TBS1 0x000007ff /* Buffer 1 Size */ + +#define PERFECT_F 0x00000000 +#define HASH_F TD_FT0 +#define INVERSE_F TD_FT1 +#define HASH_O_F (TD_FT1 | TD_F0) + +/* + * Constant setting + */ + +#define IMR_DEFAULT ( DMA_INT_TI | DMA_INT_RI | \ + DMA_INT_TS | DMA_INT_RS | \ + DMA_INT_TU | DMA_INT_RU | \ + DMA_INT_FB ) + +#define IMR_ENABLE (DMA_INT_NI | DMA_INT_AI) + +#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */ +#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */ + +#define HASH_TABLE_LEN 512 /* Bits */ +#define HASH_BITS 0x01ff /* 9 LS bits */ + +#define SETUP_FRAME_LEN 192 /* Bytes */ +#define IMPERF_PA_OFFSET 156 /* Bytes */ + +/* + * Address Filtering Modes + */ +#define PERFECT 0 /* 16 perfect physical addresses */ +#define HASH_PERF 1 /* 1 perfect, 512 multicast addresses */ +#define PERFECT_REJ 2 /* Reject 16 perfect physical addresses */ +#define ALL_HASH 3 /* Hashes all physical & multicast addrs */ + +#define ALL 0 /* Clear out all the setup frame */ +#define PHYS_ADDR_ONLY 1 /* Update the physical address only */ + +/* MII register */ +#define MII_BMCR 0x00 /* MII Basic Mode Control Register */ +#define MII_BMSR 0x01 /* MII Basic Mode Status Register */ +#define MII_ID1 0x02 /* PHY Identifier Register 1 */ +#define MII_ID2 0x03 /* PHY Identifier Register 2 */ +#define MII_ANAR 0x04 /* Auto Negotiation Advertisement Register */ +#define MII_ANLPAR 0x05 /* Auto Negotiation Link Partner Ability */ +#define MII_ANER 0x06 /* Auto Negotiation Expansion */ +#define MII_DSCR 0x10 /* Davicom Specified Configration Register */ +#define MII_DSCSR 0x11 /* Davicom Specified Configration/Status Register */ +#define MII_10BTCSR 0x12 /* 10base-T Specified Configration/Status Register */ + + +#define MII_PREAMBLE 0xffffffff /* MII Management Preamble */ +#define MII_TEST 0xaaaaaaaa /* MII Test Signal */ +#define MII_STRD 0x06 /* Start of Frame+Op Code: use low nibble */ +#define MII_STWR 0x0a /* Start of Frame+Op Code: use low nibble */ + +/* + * MII Management Control Register + */ +#define MII_CR_RST 0x8000 /* RESET the PHY chip */ +#define MII_CR_LPBK 0x4000 /* Loopback enable */ +#define MII_CR_SPD 0x2000 /* 0: 10Mb/s; 1: 100Mb/s */ +#define MII_CR_ASSE 0x1000 /* Auto Speed Select Enable */ +#define MII_CR_PD 0x0800 /* Power Down */ +#define MII_CR_ISOL 0x0400 /* Isolate Mode */ +#define MII_CR_RAN 0x0200 /* Restart Auto Negotiation */ +#define MII_CR_FDM 0x0100 /* Full Duplex Mode */ +#define MII_CR_CTE 0x0080 /* Collision Test Enable */ + +/* + * MII Management Status Register + */ +#define MII_SR_T4C 0x8000 /* 100BASE-T4 capable */ +#define MII_SR_TXFD 0x4000 /* 100BASE-TX Full Duplex capable */ +#define MII_SR_TXHD 0x2000 /* 100BASE-TX Half Duplex capable */ +#define MII_SR_TFD 0x1000 /* 10BASE-T Full Duplex capable */ +#define MII_SR_THD 0x0800 /* 10BASE-T Half Duplex capable */ +#define MII_SR_ASSC 0x0020 /* Auto Speed Selection Complete*/ +#define MII_SR_RFD 0x0010 /* Remote Fault Detected */ +#define MII_SR_ANC 0x0008 /* Auto Negotiation capable */ +#define MII_SR_LKS 0x0004 /* Link Status */ +#define MII_SR_JABD 0x0002 /* Jabber Detect */ +#define MII_SR_XC 0x0001 /* Extended Capabilities */ + +/* + * MII Management Auto Negotiation Advertisement Register + */ +#define MII_ANA_TAF 0x03e0 /* Technology Ability Field */ +#define MII_ANA_T4AM 0x0200 /* T4 Technology Ability Mask */ +#define MII_ANA_TXAM 0x0180 /* TX Technology Ability Mask */ +#define MII_ANA_FDAM 0x0140 /* Full Duplex Technology Ability Mask */ +#define MII_ANA_HDAM 0x02a0 /* Half Duplex Technology Ability Mask */ +#define MII_ANA_100M 0x0380 /* 100Mb Technology Ability Mask */ +#define MII_ANA_10M 0x0060 /* 10Mb Technology Ability Mask */ +#define MII_ANA_CSMA 0x0001 /* CSMA-CD Capable */ + +/* + * MII Management Auto Negotiation Remote End Register + */ +#define MII_ANLPA_NP 0x8000 /* Next Page (Enable) */ +#define MII_ANLPA_ACK 0x4000 /* Remote Acknowledge */ +#define MII_ANLPA_RF 0x2000 /* Remote Fault */ +#define MII_ANLPA_TAF 0x03e0 /* Technology Ability Field */ +#define MII_ANLPA_T4AM 0x0200 /* T4 Technology Ability Mask */ +#define MII_ANLPA_TXAM 0x0180 /* TX Technology Ability Mask */ +#define MII_ANLPA_FDAM 0x0140 /* Full Duplex Technology Ability Mask */ +#define MII_ANLPA_HDAM 0x02a0 /* Half Duplex Technology Ability Mask */ +#define MII_ANLPA_100M 0x0380 /* 100Mb Technology Ability Mask */ +#define MII_ANLPA_10M 0x0060 /* 10Mb Technology Ability Mask */ +#define MII_ANLPA_CSMA 0x0001 /* CSMA-CD Capable */ + +/* + * MII Management DAVICOM Specified Configuration And Status Register + */ +#define MII_DSCSR_100FDX 0x8000 /* 100M Full Duplex Operation Mode */ +#define MII_DSCSR_100HDX 0x4000 /* 100M Half Duplex Operation Mode */ +#define MII_DSCSR_10FDX 0x2000 /* 10M Full Duplex Operation Mode */ +#define MII_DSCSR_10HDX 0x1000 /* 10M Half Duplex Operation Mode */ +#define MII_DSCSR_ANMB 0x000f /* Auto-Negotiation Monitor Bits */ + + +/* + * Used by IOCTL + */ +#define READ_COMMAND (SIOCDEVPRIVATE+4) +#define WRITE_COMMAND (SIOCDEVPRIVATE+5) +#define GETDRIVERINFO (SIOCDEVPRIVATE+6) + +/* + * Device data and structure + */ + +#define ETH_TX_TIMEOUT (6*HZ) + +#define RX_BUF_SIZE 1536 + +#define NUM_RX_DESCS 32 +#define NUM_TX_DESCS 16 + +static const char *media_types[] = { + "10BaseT-HD ", "10BaseT-FD ","100baseTx-HD ", + "100baseTx-FD", "100baseT4", 0 +}; + +typedef struct { + unsigned int status; + unsigned int desc1; + unsigned int buf1_addr; + unsigned int next_addr; +} jz_desc_t; + +struct jz_eth_private { + jz_desc_t tx_ring[NUM_TX_DESCS]; /* transmit descriptors */ + jz_desc_t rx_ring[NUM_RX_DESCS]; /* receive descriptors */ + dma_addr_t dma_tx_ring; /* bus address of tx ring */ + dma_addr_t dma_rx_ring; /* bus address of rx ring */ + dma_addr_t dma_rx_buf; /* DMA address of rx buffer */ + unsigned int vaddr_rx_buf; /* virtual address of rx buffer */ + + unsigned int rx_head; /* first rx descriptor */ + unsigned int tx_head; /* first tx descriptor */ + unsigned int tx_tail; /* last unacked transmit packet */ + unsigned int tx_full; /* transmit buffers are full */ + struct sk_buff *tx_skb[NUM_TX_DESCS]; /* skbuffs for packets to transmit */ + + struct net_device_stats stats; + spinlock_t lock; + + int media; /* Media (eg TP), mode (eg 100B)*/ + int full_duplex; /* Current duplex setting. */ + int link_state; + char phys[32]; /* List of attached PHY devices */ + char valid_phy; /* Current linked phy-id with MAC */ + int mii_phy_cnt; + int phy_type; /* 1-RTL8309,0-DVCOM */ + struct ethtool_cmd ecmds[32]; + u16 advertising; /* NWay media advertisement */ + + pid_t thr_pid; /* Link cheak thread ID */ + int thread_die; + struct completion thr_exited; + wait_queue_head_t thr_wait; + + struct pm_dev *pmdev; +}; + +#endif /* __JZ_ETH_H__ */ diff --git a/drivers/net/jzcs8900a.c b/drivers/net/jzcs8900a.c new file mode 100644 index 0000000..db345d0 --- /dev/null +++ b/drivers/net/jzcs8900a.c @@ -0,0 +1,649 @@ + +/* + * linux/drivers/net/jzcs8900a.c + * + * Author: Lucifer + * + * A Cirrus Logic CS8900A driver for Linux + * based on the cs89x0 driver written by Russell Nelson, + * Donald Becker, and others. + * + * This source code 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. + */ + +/* + * At the moment the driver does not support memory mode operation. + * It is trivial to implement this, but not worth the effort. + */ + +/* + * TODO: + * + * 1. If !ready in send_start(), queue buffer and send it in interrupt handler + * when we receive a BufEvent with Rdy4Tx, send it again. dangerous! + * 2. how do we prevent interrupt handler destroying integrity of get_stats()? + * 3. Change reset code to check status. + * 4. Implement set_mac_address and remove fake mac address + * 5. Link status detection stuff + * 6. Write utility to write EEPROM, do self testing, etc. + * 7. Implement DMA routines (I need a board w/ DMA support for that) + * 8. Power management + * 9. Add support for multiple ethernet chips + * 10. Add support for other cs89xx chips (need hardware for that) + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "jzcs8900a.h" + +#define FULL_DUPLEX +#define INT_PIN 0 +#ifdef CONFIG_SOC_JZ4740 +#define CIRRUS_DEFAULT_IO 0xa8000000 +#define CIRRUS_DEFAULT_IRQ 107 + +#elif CONFIG_SOC_JZ4750 +#define CIRRUS_DEFAULT_IO 0xac000000 + +#ifdef CONFIG_JZ4750_FUWA +#define CIRRUS_DEFAULT_IRQ (32*4+20+48) +#else +#define CIRRUS_DEFAULT_IRQ (32*2 +6+48) +#endif + + +#endif + +typedef struct { + struct net_device_stats stats; + u16 txlen; +} cirrus_t; + +static int ethaddr_cmd = 0; +static unsigned char ethaddr_hex[6]; +static struct net_device *dev; + +/* + * I/O routines + */ +static void gpio_init_cs8900(void) +{ +#ifdef CONFIG_SOC_JZ4740 + __gpio_as_func0(60); //CS4# + __gpio_as_func0(61); //RD# + __gpio_as_func0(62); //WR# + __gpio_as_irq_high_level(59); //irq + __gpio_disable_pull(59); //disable pull + REG_EMC_SMCR4 |= (1 << 6); //16bit +#elif CONFIG_SOC_JZ4750 + __gpio_as_func0(32*2+23); //CS3# + __gpio_as_func0(32*2+25); //RD# + __gpio_as_func0(32*2+26); //WR# + +#ifdef CONFIG_JZ4750_FUWA + __gpio_as_irq_high_level(32*4+20); //irq + __gpio_disable_pull(32*4+20); //disable pull +#else + __gpio_as_irq_high_level(32*2 +6); //irq + __gpio_disable_pull(32*2 +6); //disable pull +#endif + + REG_EMC_SMCR3 |= (1 << 6); //16bit +#endif + udelay(1); +} + +static inline u16 cirrus_read (struct net_device *dev,u16 reg) +{ + outw (reg,dev->base_addr + PP_Address); + return (inw (dev->base_addr + PP_Data)); +} + +static inline void cirrus_write (struct net_device *dev,u16 reg,u16 value) +{ + outw (reg,dev->base_addr + PP_Address); + outw (value,dev->base_addr + PP_Data); +} + +static inline void cirrus_set (struct net_device *dev,u16 reg,u16 value) +{ + cirrus_write (dev,reg,cirrus_read (dev,reg) | value); +} + +static inline void cirrus_clear (struct net_device *dev,u16 reg,u16 value) +{ + cirrus_write (dev,reg,cirrus_read (dev,reg) & ~value); +} + +static inline void cirrus_frame_read (struct net_device *dev,struct sk_buff *skb,u16 length) +{ + insw (dev->base_addr,skb_put (skb,length),(length + 1) / 2); +} + +static inline void cirrus_frame_write (struct net_device *dev,struct sk_buff *skb) +{ + outsw (dev->base_addr,skb->data,(skb->len + 1) / 2); +} + +/* + * Debugging functions + */ + +#ifdef DEBUG +static inline int printable (int c) +{ + return ((c >= 32 && c <= 126) || + (c >= 174 && c <= 223) || + (c >= 242 && c <= 243) || + (c >= 252 && c <= 253)); +} + +static void dump16 (struct net_device *dev,const u8 *s,size_t len) +{ + int i; + char str[128]; + + if (!len) return; + + *str = '\0'; + + for (i = 0; i < len; i++) { + if (i && !(i % 4)) strcat (str," "); + sprintf (str,"%s%.2x ",str,s[i]); + } + + for ( ; i < 16; i++) { + if (i && !(i % 4)) strcat (str," "); + strcat (str," "); + } + + strcat (str," "); + for (i = 0; i < len; i++) sprintf (str,"%s%c",str,printable (s[i]) ? s[i] : '.'); + + printk (KERN_DEBUG "%s: %s\n",dev->name,str); +} + +static void hexdump (struct net_device *dev,const void *ptr,size_t size) +{ + const u8 *s = (u8 *) ptr; + int i; + for (i = 0; i < size / 16; i++, s += 16) dump16 (dev,s,16); + dump16 (dev,s,size % 16); +} + +static void dump_packet (struct net_device *dev,struct sk_buff *skb,const char *type) +{ + printk (KERN_INFO "%s: %s %d byte frame %.2x:%.2x:%.2x:%.2x:%.2x:%.2x to %.2x:%.2x:%.2x:%.2x:%.2x:%.2x type %.4x\n", + dev->name, + type, + skb->len, + skb->data[0],skb->data[1],skb->data[2],skb->data[3],skb->data[4],skb->data[5], + skb->data[6],skb->data[7],skb->data[8],skb->data[9],skb->data[10],skb->data[11], + (skb->data[12] << 8) | skb->data[13]); + if (skb->len < 0x100) hexdump (dev,skb->data,skb->len); +} +#endif /* #ifdef DEBUG */ + +/* + * Driver functions + */ + +static void cirrus_receive (struct net_device *dev) +{ + cirrus_t *priv = (cirrus_t *) dev->priv; + struct sk_buff *skb; + u16 status,length; + + status = cirrus_read (dev,PP_RxStatus); + length = cirrus_read (dev,PP_RxLength); + + if (!(status & RxOK)) { + priv->stats.rx_errors++; + if ((status & (Runt | Extradata))) priv->stats.rx_length_errors++; + if ((status & CRCerror)) priv->stats.rx_crc_errors++; + return; + } + + if ((skb = dev_alloc_skb (length + 4)) == NULL) { + priv->stats.rx_dropped++; + return; + } + + skb->dev = dev; + skb_reserve (skb,2); + + cirrus_frame_read (dev,skb,length); + skb->protocol = eth_type_trans (skb,dev); + + netif_rx (skb); + dev->last_rx = jiffies; + + priv->stats.rx_packets++; + priv->stats.rx_bytes += length; +} + +static int cirrus_send_start (struct sk_buff *skb,struct net_device *dev) +{ + cirrus_t *priv = (cirrus_t *) dev->priv; + u16 status; + + mdelay(10); + netif_stop_queue (dev); + + cirrus_write (dev,PP_TxCMD,TxStart (After5)); + cirrus_write (dev,PP_TxLength,skb->len); + + status = cirrus_read (dev,PP_BusST); + + if ((status & TxBidErr)) { + printk (KERN_WARNING "%s: Invalid frame size %d!\n",dev->name,skb->len); + priv->stats.tx_errors++; + priv->stats.tx_aborted_errors++; + priv->txlen = 0; + return (1); + } + + if (!(status & Rdy4TxNOW)) { + //printk (KERN_WARNING "%s: Transmit buffer not free!\n",dev->name); + priv->stats.tx_errors++; + priv->txlen = 0; + /* FIXME: store skb and send it in interrupt handler */ + return (1); + } + + cirrus_frame_write (dev,skb); + dev->trans_start = jiffies; + + dev_kfree_skb (skb); + + priv->txlen = skb->len; + + return (0); +} + +static irqreturn_t cirrus_interrupt(int irq, void *id) +{ + struct net_device *dev = (struct net_device *) id; + cirrus_t *priv; + u16 status; + + if (dev->priv == NULL) { + return IRQ_NONE; + } + + priv = (cirrus_t *) dev->priv; + + while ((status = cirrus_read (dev,PP_ISQ))) { + switch (RegNum (status)) { + case RxEvent: + cirrus_receive (dev); + break; + + case TxEvent: + priv->stats.collisions += ColCount (cirrus_read (dev,PP_TxCOL)); + if (!(RegContent (status) & TxOK)) { + priv->stats.tx_errors++; + if ((RegContent (status) & Out_of_window)) priv->stats.tx_window_errors++; + if ((RegContent (status) & Jabber)) priv->stats.tx_aborted_errors++; + break; + } else if (priv->txlen) { + priv->stats.tx_packets++; + priv->stats.tx_bytes += priv->txlen; + } + priv->txlen = 0; + netif_wake_queue (dev); + break; + + case BufEvent: + if ((RegContent (status) & RxMiss)) { + u16 missed = MissCount (cirrus_read (dev,PP_RxMISS)); + priv->stats.rx_errors += missed; + priv->stats.rx_missed_errors += missed; + } + if ((RegContent (status) & TxUnderrun)) { + priv->stats.tx_errors++; + priv->stats.tx_fifo_errors++; + } + /* FIXME: if Rdy4Tx, transmit last sent packet (if any) */ + priv->txlen = 0; + netif_wake_queue (dev); + break; + + case TxCOL: + priv->stats.collisions += ColCount (cirrus_read (dev,PP_TxCOL)); + break; + + case RxMISS: + status = MissCount (cirrus_read (dev,PP_RxMISS)); + priv->stats.rx_errors += status; + priv->stats.rx_missed_errors += status; + break; + default: + return IRQ_HANDLED; + } + } + + return IRQ_HANDLED; +} + +static void cirrus_transmit_timeout (struct net_device *dev) +{ + cirrus_t *priv = (cirrus_t *) dev->priv; + priv->stats.tx_errors++; + priv->stats.tx_heartbeat_errors++; + priv->txlen = 0; + netif_wake_queue (dev); +} + +static int cirrus_start (struct net_device *dev) +{ + int result; + + /* valid ethernet address? */ + if (!is_valid_ether_addr(dev->dev_addr)) { + printk(KERN_ERR "%s: invalid ethernet MAC address\n",dev->name); + return (-EINVAL); + } + + /* install interrupt handler */ + if ((result = request_irq (dev->irq, &cirrus_interrupt, IRQF_DISABLED, dev->name, dev)) < 0) { + printk (KERN_ERR "%s: could not register interrupt %d\n",dev->name,dev->irq); + return (result); + } + + /* enable the ethernet controller */ + cirrus_set (dev,PP_RxCFG,RxOKiE | BufferCRC | CRCerroriE | RuntiE | ExtradataiE); + cirrus_set (dev,PP_RxCTL,RxOKA | IndividualA | BroadcastA); + cirrus_set (dev,PP_TxCFG,TxOKiE | Out_of_windowiE | JabberiE); + cirrus_set (dev,PP_BufCFG,Rdy4TxiE | RxMissiE | TxUnderruniE | TxColOvfiE | MissOvfloiE); + cirrus_set (dev,PP_LineCTL,SerRxON | SerTxON); + cirrus_set (dev,PP_BusCTL,EnableRQ); + +#ifdef FULL_DUPLEX + cirrus_set (dev,PP_TestCTL,FDX); +#endif /* #ifdef FULL_DUPLEX */ + + /* start the queue */ + netif_start_queue (dev); + __gpio_unmask_irq(59); + + //MOD_INC_USE_COUNT; + return (0); +} + +static int cirrus_stop (struct net_device *dev) +{ + /* disable ethernet controller */ + cirrus_write (dev,PP_BusCTL,0); + cirrus_write (dev,PP_TestCTL,0); + cirrus_write (dev,PP_SelfCTL,0); + cirrus_write (dev,PP_LineCTL,0); + cirrus_write (dev,PP_BufCFG,0); + cirrus_write (dev,PP_TxCFG,0); + cirrus_write (dev,PP_RxCTL,0); + cirrus_write (dev,PP_RxCFG,0); + + /* uninstall interrupt handler */ + free_irq (dev->irq,dev); + + /* stop the queue */ + netif_stop_queue (dev); + + //MOD_DEC_USE_COUNT; + + return (0); +} + +static int cirrus_set_mac_address (struct net_device *dev, void *p) +{ + struct sockaddr *addr = (struct sockaddr *)p; + int i; + + if (netif_running(dev)) + return -EBUSY; + + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + + /* configure MAC address */ + for (i = 0; i < ETH_ALEN; i += 2) + cirrus_write (dev,PP_IA + i,dev->dev_addr[i] | (dev->dev_addr[i + 1] << 8)); + + return 0; +} + +static struct net_device_stats *cirrus_get_stats (struct net_device *dev) +{ + cirrus_t *priv = (cirrus_t *) dev->priv; + return (&priv->stats); +} + +static void cirrus_set_receive_mode (struct net_device *dev) +{ + if ((dev->flags & IFF_PROMISC)) + cirrus_set (dev,PP_RxCTL,PromiscuousA); + else + cirrus_clear (dev,PP_RxCTL,PromiscuousA); + + if ((dev->flags & IFF_ALLMULTI) && dev->mc_list) + cirrus_set (dev,PP_RxCTL,MulticastA); + else + cirrus_clear (dev,PP_RxCTL,MulticastA); +} + +/* + * Architecture dependant code + */ + +/* + * Driver initialization routines + */ + +int __init cirrus_probe(void) +{ + static cirrus_t priv; + int i; + u16 value; + + printk ("Jz CS8900A driver for Linux (V0.02)\n"); + + /* Init hardware for PAVO board */ + gpio_init_cs8900(); + + /* Allocate ethernet device */ + dev = alloc_etherdev(sizeof(struct net_device)); + + memset (&priv,0,sizeof (cirrus_t)); + + ether_setup (dev); + + dev->open = cirrus_start; + dev->stop = cirrus_stop; + dev->hard_start_xmit = cirrus_send_start; + dev->get_stats = cirrus_get_stats; + dev->set_multicast_list = cirrus_set_receive_mode; + dev->set_mac_address = cirrus_set_mac_address; + dev->tx_timeout = cirrus_transmit_timeout; + dev->watchdog_timeo = HZ; + + if (ethaddr_cmd==1) + { + dev->dev_addr[0] = ethaddr_hex[0]; + dev->dev_addr[1] = ethaddr_hex[1]; + dev->dev_addr[2] = ethaddr_hex[2]; + dev->dev_addr[3] = ethaddr_hex[3]; + dev->dev_addr[4] = ethaddr_hex[4]; + dev->dev_addr[5] = ethaddr_hex[5]; + } + else //default mac address 00:2a:cc:2a:af:fe + { + dev->dev_addr[0] = 0x00; + dev->dev_addr[1] = 0x62; + dev->dev_addr[2] = 0x9c; + dev->dev_addr[3] = 0x61; + dev->dev_addr[4] = 0xcf; + dev->dev_addr[5] = 0x16; + } + dev->if_port = IF_PORT_10BASET; + dev->priv = (void *) &priv; + + dev->base_addr = CIRRUS_DEFAULT_IO; + dev->irq = CIRRUS_DEFAULT_IRQ; + + + /* module parameters override everything */ + if (!dev->base_addr) { + printk (KERN_ERR + "%s: No default I/O base address defined. Use io=... or\n" + "%s: define CIRRUS_DEFAULT_IO for your platform\n", + dev->name,dev->name); + return (-EINVAL); + } + + if (!dev->irq) { + printk (KERN_ERR + "%s: No default IRQ number defined. Use irq=... or\n" + "%s: define CIRRUS_DEFAULT_IRQ for your platform\n", + dev->name,dev->name); + return (-EINVAL); + } +#if 0 + if ((result = check_region (dev->base_addr,16))) { + printk (KERN_ERR "%s: can't get I/O port address 0x%lx\n",dev->name,dev->base_addr); + return (result); + } +#endif + if (!request_region (dev->base_addr,16,dev->name)) + return -EBUSY; +#if 0 + /* verify EISA registration number for Cirrus Logic */ + if ((value = cirrus_read (dev,PP_ProductID)) != EISA_REG_CODE) { + printk (KERN_ERR "%s: incorrect signature 0x%.4x\n",dev->name,value); + return (-ENXIO); + } +#endif + + /* verify chip version */ + value = cirrus_read (dev,PP_ProductID + 2); + if (VERSION (value) != CS8900A) { + printk (KERN_ERR "%s: unknown chip version 0x%.8x\n",dev->name,VERSION (value)); + return (-ENXIO); + } + printk (KERN_INFO "%s: CS8900A rev %c detected\n",dev->name,'B' + REVISION (value) - REV_B); + + /* setup interrupt number */ + cirrus_write (dev,PP_IntNum,INT_PIN); + + /* configure MAC address */ + for (i = 0; i < ETH_ALEN; i += 2) + { + //printk(" %x",dev->dev_addr[i] | (dev->dev_addr[i + 1] << 8)); + cirrus_write (dev,PP_IA + i,dev->dev_addr[i] | (dev->dev_addr[i + 1] << 8)); + } + + if (register_netdev(dev) != 0) { + printk(KERN_ERR " Cannot register net device\n"); + free_netdev(dev); + return -ENOMEM; + } + + return (0); +} + +static 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 */ +} + +static 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; + } +} + +static int __init ethernet_addr_setup(char *str) +{ + if (!str) { + printk("ethaddr not set in command line\n"); + return -1; + } + ethaddr_cmd = 1; + str2eaddr(ethaddr_hex, str); + return 0; +} + +__setup("ethaddr=", ethernet_addr_setup); + +//EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR ("Lucifer "); +MODULE_DESCRIPTION ("Jz CS8900A driver for Linux (V0.02)"); +MODULE_LICENSE ("GPL"); + +//#ifdef MODULE + + +#if 0 +static int io = 0; +static int irq = 0; + +module_param(io, int, 0); +MODULE_PARM_DESC (io,"I/O Base Address"); +//MODULE_PARM (io,"i"); + +module_param(irq, int, 0); +MODULE_PARM_DESC (irq,"IRQ Number"); +//MODULE_PARM (irq,"i"); +#endif + +static int __init jzcs8900_init(void) +{ + if (cirrus_probe()) { + printk(KERN_WARNING "jzcs8900: No cs8900a found\n"); + } + + return 0; +} + +static void __exit jzcs8900_exit(void) +{ + release_region(dev->base_addr,16); + unregister_netdev(dev); + free_netdev(dev); +} + +module_init(jzcs8900_init); +module_exit(jzcs8900_exit); diff --git a/drivers/net/jzcs8900a.h b/drivers/net/jzcs8900a.h new file mode 100644 index 0000000..68d76bb --- /dev/null +++ b/drivers/net/jzcs8900a.h @@ -0,0 +1,235 @@ +#ifndef JZCS8900A_H +#define JZCS8900A_H + +/* + * linux/drivers/net/jzcs8900a.h + * + * Author: Lucifer + * + * A Cirrus Logic CS8900A driver for Linux + * based on the cs89x0 driver written by Russell Nelson, + * Donald Becker, and others. + * + * This source code 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. + */ + +/* + * Ports + */ + +#define PP_Address 0x0a /* PacketPage Pointer Port (Section 4.10.10) */ +#define PP_Data 0x0c /* PacketPage Data Port (Section 4.10.10) */ + +/* + * Registers + */ + +#define PP_ProductID 0x0000 /* Section 4.3.1 Product Identification Code */ +#define PP_MemBase 0x002c /* Section 4.9.2 Memory Base Address Register */ +#define PP_IntNum 0x0022 /* Section 3.2.3 Interrupt Number */ +#define PP_EEPROMCommand 0x0040 /* Section 4.3.11 EEPROM Command */ +#define PP_EEPROMData 0x0042 /* Section 4.3.12 EEPROM Data */ +#define PP_RxCFG 0x0102 /* Section 4.4.6 Receiver Configuration */ +#define PP_RxCTL 0x0104 /* Section 4.4.8 Receiver Control */ +#define PP_TxCFG 0x0106 /* Section 4.4.9 Transmit Configuration */ +#define PP_BufCFG 0x010a /* Section 4.4.12 Buffer Configuration */ +#define PP_LineCTL 0x0112 /* Section 4.4.16 Line Control */ +#define PP_SelfCTL 0x0114 /* Section 4.4.18 Self Control */ +#define PP_BusCTL 0x0116 /* Section 4.4.20 Bus Control */ +#define PP_TestCTL 0x0118 /* Section 4.4.22 Test Control */ +#define PP_ISQ 0x0120 /* Section 4.4.5 Interrupt Status Queue */ +#define PP_TxEvent 0x0128 /* Section 4.4.10 Transmitter Event */ +#define PP_BufEvent 0x012c /* Section 4.4.13 Buffer Event */ +#define PP_RxMISS 0x0130 /* Section 4.4.14 Receiver Miss Counter */ +#define PP_TxCOL 0x0132 /* Section 4.4.15 Transmit Collision Counter */ +#define PP_SelfST 0x0136 /* Section 4.4.19 Self Status */ +#define PP_BusST 0x0138 /* Section 4.4.21 Bus Status */ +#define PP_TxCMD 0x0144 /* Section 4.4.11 Transmit Command */ +#define PP_TxLength 0x0146 /* Section 4.5.2 Transmit Length */ +#define PP_IA 0x0158 /* Section 4.6.2 Individual Address (IEEE Address) */ +#define PP_RxStatus 0x0400 /* Section 4.7.1 Receive Status */ +#define PP_RxLength 0x0402 /* Section 4.7.1 Receive Length (in bytes) */ +#define PP_RxFrame 0x0404 /* Section 4.7.2 Receive Frame Location */ +#define PP_TxFrame 0x0a00 /* Section 4.7.2 Transmit Frame Location */ + +/* + * Values + */ + +/* PP_IntNum */ +#define INTRQ0 0x0000 +#define INTRQ1 0x0001 +#define INTRQ2 0x0002 +#define INTRQ3 0x0003 + +/* PP_ProductID */ +#define EISA_REG_CODE 0x630e +#define REVISION(x) (((x) & 0x1f00) >> 8) +#define VERSION(x) ((x) & ~0x1f00) + +#define CS8900A 0x0000 +#define REV_B 7 +#define REV_C 8 +#define REV_D 9 + +/* PP_RxCFG */ +#define Skip_1 0x0040 +#define StreamE 0x0080 +#define RxOKiE 0x0100 +#define RxDMAonly 0x0200 +#define AutoRxDMAE 0x0400 +#define BufferCRC 0x0800 +#define CRCerroriE 0x1000 +#define RuntiE 0x2000 +#define ExtradataiE 0x4000 + +/* PP_RxCTL */ +#define IAHashA 0x0040 +#define PromiscuousA 0x0080 +#define RxOKA 0x0100 +#define MulticastA 0x0200 +#define IndividualA 0x0400 +#define BroadcastA 0x0800 +#define CRCerrorA 0x1000 +#define RuntA 0x2000 +#define ExtradataA 0x4000 + +/* PP_TxCFG */ +#define Loss_of_CRSiE 0x0040 +#define SQErroriE 0x0080 +#define TxOKiE 0x0100 +#define Out_of_windowiE 0x0200 +#define JabberiE 0x0400 +#define AnycolliE 0x0800 +#define T16colliE 0x8000 + +/* PP_BufCFG */ +#define SWint_X 0x0040 +#define RxDMAiE 0x0080 +#define Rdy4TxiE 0x0100 +#define TxUnderruniE 0x0200 +#define RxMissiE 0x0400 +#define Rx128iE 0x0800 +#define TxColOvfiE 0x1000 +#define MissOvfloiE 0x2000 +#define RxDestiE 0x8000 + +/* PP_LineCTL */ +#define SerRxON 0x0040 +#define SerTxON 0x0080 +#define AUIonly 0x0100 +#define AutoAUI_10BT 0x0200 +#define ModBackoffE 0x0800 +#define PolarityDis 0x1000 +#define L2_partDefDis 0x2000 +#define LoRxSquelch 0x4000 + +/* PP_SelfCTL */ +#define RESET 0x0040 +#define SWSuspend 0x0100 +#define HWSleepE 0x0200 +#define HWStandbyE 0x0400 +#define HC0E 0x1000 +#define HC1E 0x2000 +#define HCB0 0x4000 +#define HCB1 0x8000 + +/* PP_BusCTL */ +#define ResetRxDMA 0x0040 +#define DMAextend 0x0100 +#define UseSA 0x0200 +#define MemoryE 0x0400 +#define DMABurst 0x0800 +#define IOCHRDYE 0x1000 +#define RxDMAsize 0x2000 +#define EnableRQ 0x8000 + +/* PP_TestCTL */ +#define DisableLT 0x0080 +#define ENDECloop 0x0200 +#define AUIloop 0x0400 +#define DisableBackoff 0x0800 +#define FDX 0x4000 + +/* PP_ISQ */ +#define RegNum(x) ((x) & 0x3f) +#define RegContent(x) ((x) & ~0x3d) + +#define RxEvent 0x0004 +#define TxEvent 0x0008 +#define BufEvent 0x000c +#define RxMISS 0x0010 +#define TxCOL 0x0012 + +/* PP_RxStatus */ +#define IAHash 0x0040 +#define Dribblebits 0x0080 +#define RxOK 0x0100 +#define Hashed 0x0200 +#define IndividualAdr 0x0400 +#define Broadcast 0x0800 +#define CRCerror 0x1000 +#define Runt 0x2000 +#define Extradata 0x4000 + +#define HashTableIndex(x) ((x) >> 0xa) + +/* PP_TxCMD */ +#define After5 0 +#define After381 1 +#define After1021 2 +#define AfterAll 3 +#define TxStart(x) ((x) << 6) + +#define Force 0x0100 +#define Onecoll 0x0200 +#define InhibitCRC 0x1000 +#define TxPadDis 0x2000 + +/* PP_BusST */ +#define TxBidErr 0x0080 +#define Rdy4TxNOW 0x0100 + +/* PP_TxEvent */ +#define Loss_of_CRS 0x0040 +#define SQEerror 0x0080 +#define TxOK 0x0100 +#define Out_of_window 0x0200 +#define Jabber 0x0400 +#define T16coll 0x8000 + +#define TX_collisions(x) (((x) >> 0xb) & ~0x8000) + +/* PP_BufEvent */ +#define SWint 0x0040 +#define RxDMAFrame 0x0080 +#define Rdy4Tx 0x0100 +#define TxUnderrun 0x0200 +#define RxMiss 0x0400 +#define Rx128 0x0800 +#define RxDest 0x8000 + +/* PP_RxMISS */ +#define MissCount(x) ((x) >> 6) + +/* PP_TxCOL */ +#define ColCount(x) ((x) >> 6) + +/* PP_SelfST */ +#define T3VActive 0x0040 +#define INITD 0x0080 +#define SIBUSY 0x0100 +#define EEPROMpresent 0x0200 +#define EEPROMOK 0x0400 +#define ELpresent 0x0800 +#define EEsize 0x1000 + +/* PP_EEPROMCommand */ +#define EEWriteRegister 0x0100 +#define EEReadRegister 0x0200 +#define EEEraseRegister 0x0300 +#define ELSEL 0x0400 + +#endif /* #ifndef CIRRUS_H */ diff --git a/drivers/pnp/Kconfig b/drivers/pnp/Kconfig index 2a37b3f..8a8e831 100644 --- a/drivers/pnp/Kconfig +++ b/drivers/pnp/Kconfig @@ -5,7 +5,7 @@ menuconfig PNP bool "Plug and Play support" depends on HAS_IOMEM - depends on ISA || ACPI +# depends on ISA || ACPI ---help--- Plug and Play (PnP) is a standard for peripherals which allows those peripherals to be configured by software, e.g. assign IRQ's or other diff --git a/drivers/pnp/core.c b/drivers/pnp/core.c index 5dba909..fd77eb4 100644 --- a/drivers/pnp/core.c +++ b/drivers/pnp/core.c @@ -74,6 +74,8 @@ int pnp_register_protocol(struct pnp_protocol *protocol) return device_register(&protocol->dev); } +EXPORT_SYMBOL(pnp_register_protocol); + /** * pnp_protocol_unregister - removes a pnp protocol from the pnp layer * @protocol: pointer to the corresponding pnp_protocol structure @@ -86,6 +88,8 @@ void pnp_unregister_protocol(struct pnp_protocol *protocol) device_unregister(&protocol->dev); } +EXPORT_SYMBOL(pnp_unregister_protocol); + static void pnp_free_ids(struct pnp_dev *dev) { struct pnp_id *id; @@ -196,6 +200,8 @@ int pnp_add_device(struct pnp_dev *dev) return 0; } +EXPORT_SYMBOL(pnp_add_device); + void __pnp_remove_device(struct pnp_dev *dev) { spin_lock(&pnp_lock); @@ -205,6 +211,21 @@ void __pnp_remove_device(struct pnp_dev *dev) device_unregister(&dev->dev); } +/** + * pnp_remove_device - removes a pnp device from the pnp layer + * @dev: pointer to dev to add + * + * this function will free all mem used by dev + */ +void pnp_remove_device(struct pnp_dev *dev) +{ + if (!dev || dev->card) + return; + __pnp_remove_device(dev); +} + +EXPORT_SYMBOL(pnp_remove_device); + static int __init pnp_init(void) { return bus_register(&pnp_bus_type); diff --git a/drivers/pnp/driver.c b/drivers/pnp/driver.c index 527ee76..9e1132f 100644 --- a/drivers/pnp/driver.c +++ b/drivers/pnp/driver.c @@ -259,3 +259,4 @@ EXPORT_SYMBOL(pnp_register_driver); EXPORT_SYMBOL(pnp_unregister_driver); EXPORT_SYMBOL(pnp_device_attach); EXPORT_SYMBOL(pnp_device_detach); +EXPORT_SYMBOL(pnp_add_id); diff --git a/drivers/pnp/resource.c b/drivers/pnp/resource.c index ba97654..525211c 100644 --- a/drivers/pnp/resource.c +++ b/drivers/pnp/resource.c @@ -406,6 +406,10 @@ int pnp_check_irq(struct pnp_dev *dev, struct resource *res) int pnp_check_dma(struct pnp_dev *dev, struct resource *res) { + printk("*********** %s, %s, line[%d]: Fix me, this should update in the future *********\n", + __FILE__, __FUNCTION__, __LINE__); + return 0; /* should be update in the future. Wolfgang ???*/ + #ifndef CONFIG_IA64 int i; struct pnp_dev *tdev; diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index fb867a9..e8d0962 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, }, @@ -2213,6 +2213,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 +2309,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 +2319,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 +2354,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 +2437,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 +2475,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 diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index dcd49f1..0eb9723 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/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 7f8e83a..29ca462 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -374,6 +374,61 @@ config SUPERH_BUILT_IN_M66592 # Controllers available only in discrete form (and all PCI 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 + select USB_GADGET_SELECTED + +config USB_GADGET_JZ4750 + boolean "JZ4750 UDC" + depends on SOC_JZ4750 + select USB_GADGET_DUALSPEED + help + Select this to support the Ingenic JZ4750 processor + high speed USB device controller. + +config USB_JZ4750 + tristate + depends on USB_GADGET_JZ4750 + default USB_GADGET + select USB_GADGET_SELECTED + +config USB_GADGET_JZ4750D + boolean "JZ4750D UDC" + depends on SOC_JZ4750D + select USB_GADGET_DUALSPEED + help + Select this to support the Ingenic JZ4750D processor + high speed USB device controller. + +config USB_JZ4750D + tristate + depends on USB_GADGET_JZ4750D + default USB_GADGET + select USB_GADGET_SELECTED + +config USB_GADGET_JZ4730 + boolean "JZ4730 UDC" + depends on SOC_JZ4730 + help + Select this to support the Ingenic JZ4730 processor + full speed USB device controller. + +config USB_JZ4730 + tristate + depends on USB_GADGET_JZ4730 + default USB_GADGET + select USB_GADGET_SELECTED + config USB_GADGET_AMD5536UDC boolean "AMD5536 UDC" depends on PCI @@ -702,6 +757,14 @@ config USB_FILE_STORAGE_TEST behavior of USB Mass Storage hosts. Not needed for normal operation. +config UDC_USE_LB_CACHE + bool "enable lb cache" + depends on USB_FILE_STORAGE && (SOC_JZ4740 || SOC_JZ4750 || SOC_JZ4750D) + default y + help + say "y" to enable lb cache and UDC work in faster speed. + say "n" to disable lb cache and UDC work in normal speed. + config USB_G_SERIAL tristate "Serial Gadget (with CDC ACM and CDC OBEX support)" help diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index e6017e6..03e6df3 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -27,6 +27,11 @@ 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_JZ4730) += jz4730_udc.o +obj-$(CONFIG_USB_JZ4750) += jz4740_udc.o +obj-$(CONFIG_USB_JZ4750D) += jz4740_udc.o + # # USB gadget drivers diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 1e6aa50..ba357c9 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -297,6 +297,18 @@ MODULE_LICENSE("Dual BSD/GPL"); /*-------------------------------------------------------------------------*/ +#if defined(CONFIG_UDC_USE_LB_CACHE) +#define GHOST +#endif + +#ifdef GHOST +extern unsigned long udc_read(unsigned long long offset, unsigned int len, unsigned char *); +extern unsigned long udc_write(unsigned long long offset, unsigned int len, unsigned char *); +extern int NAND_LB_Init(void); +extern int NAND_LB_FLASHCACHE(void); +extern int NAND_MTD_FLASHCACHE(void); +extern int FlushDataState; +#endif #define LDBG(lun,fmt,args...) \ dev_dbg(&(lun)->dev , fmt , ## args) @@ -575,7 +587,9 @@ struct lun { unsigned int prevent_medium_removal : 1; unsigned int registered : 1; unsigned int info_valid : 1; - +#ifdef GHOST + unsigned int is_nand : 1; +#endif u32 sense_data; u32 sense_data_info; u32 unit_attention_data; @@ -819,6 +833,7 @@ static u32 get_unaligned_be24(u8 *buf) #define STRING_SERIAL 3 #define STRING_CONFIG 4 #define STRING_INTERFACE 5 +#define STRING_MS_OS 0xee /* There is only one configuration. */ #define CONFIG_VALUE 1 @@ -1006,6 +1021,7 @@ static struct usb_string strings[] = { {STRING_SERIAL, serial}, {STRING_CONFIG, "Self-powered"}, {STRING_INTERFACE, "Mass Storage"}, + {STRING_MS_OS, "Microsoft"}, {} }; @@ -1627,9 +1643,20 @@ static int do_read(struct fsg_dev *fsg) /* Perform the read */ file_offset_tmp = file_offset; +#ifdef GHOST + if (curlun->is_nand) + nread = udc_read(file_offset_tmp, amount, bh->buf); + else + nread = vfs_read(curlun->filp, + (char __user *) bh->buf, + amount, &file_offset_tmp); + +#else nread = vfs_read(curlun->filp, (char __user *) bh->buf, amount, &file_offset_tmp); +#endif + VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, (unsigned long long) file_offset, (int) nread); @@ -1813,9 +1840,18 @@ static int do_write(struct fsg_dev *fsg) /* Perform the write */ file_offset_tmp = file_offset; +#ifdef GHOST + if (curlun->is_nand) + nwritten = udc_write(file_offset_tmp, amount, bh->buf); + else + nwritten = vfs_write(curlun->filp, + (char __user *) bh->buf, + amount, &file_offset_tmp); +#else nwritten = vfs_write(curlun->filp, (char __user *) bh->buf, amount, &file_offset_tmp); +#endif VLDBG(curlun, "file write %u @ %llu -> %d\n", amount, (unsigned long long) file_offset, (int) nwritten); @@ -1974,9 +2010,19 @@ static int do_verify(struct fsg_dev *fsg) /* Perform the read */ file_offset_tmp = file_offset; +#ifdef GHOST + if (curlun->is_nand) + nread = udc_read(file_offset_tmp, amount, bh->buf); + else + nread = vfs_read(curlun->filp, + (char __user *) bh->buf, + amount, &file_offset_tmp); +#else nread = vfs_read(curlun->filp, (char __user *) bh->buf, amount, &file_offset_tmp); +#endif + VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, (unsigned long long) file_offset, (int) nread); @@ -2992,6 +3038,15 @@ static int do_scsi_command(struct fsg_dev *fsg) reply = check_command(fsg, 6, DATA_DIR_NONE, 0, 1, "TEST UNIT READY"); +#ifdef GHOST + if( FlushDataState >= 1) + FlushDataState++; + if(FlushDataState > 6) + { + NAND_LB_FLASHCACHE(); + FlushDataState = 0; + } +#endif break; /* Although optional, this command is used by MS-Windows. We @@ -3544,6 +3599,13 @@ static int fsg_main_thread(void *fsg_) /* The main loop */ while (fsg->state != FSG_STATE_TERMINATED) { +#ifdef GHOST + if ((fsg->atomic_bitflags & SUSPENDED)) + { + NAND_LB_FLASHCACHE(); + NAND_MTD_FLASHCACHE(); + } +#endif if (exception_in_progress(fsg) || signal_pending(current)) { handle_exception(fsg); continue; @@ -3675,6 +3737,13 @@ static int open_backing_file(struct lun *curlun, const char *filename) curlun->num_sectors = num_sectors; LDBG(curlun, "open backing file: %s\n", filename); rc = 0; +#ifdef GHOST + if (strstr(filename,"mtd")) + { + curlun->is_nand = 1; + NAND_LB_Init(); + } +#endif out: filp_close(filp, current->files); diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index 8e0e9a0..553ff9e 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -15,6 +15,18 @@ #ifndef __GADGET_CHIPS_H #define __GADGET_CHIPS_H +#ifdef CONFIG_USB_GADGET_JZ4740 +#define gadget_is_jz4740(g) !strcmp("jz4740_udc", (g)->name) +#else +#define gadget_is_jz4740(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_JZ4730 +#define gadget_is_jz4730(g) !strcmp("jz4730_udc", (g)->name) +#else +#define gadget_is_jz4730(g) 0 +#endif + #ifdef CONFIG_USB_GADGET_NET2280 #define gadget_is_net2280(g) !strcmp("net2280", (g)->name) #else @@ -239,6 +251,11 @@ 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_jz4730(gadget)) + return 0x25; + else if (gadget_is_jz4740(gadget)) + return 0x26; + return -ENOENT; } diff --git a/drivers/usb/gadget/jz4730_udc.c b/drivers/usb/gadget/jz4730_udc.c new file mode 100644 index 0000000..fb182a6 --- /dev/null +++ b/drivers/usb/gadget/jz4730_udc.c @@ -0,0 +1,1403 @@ +/* + * JZ4730 USB Device Controller driver + * + * 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. + */ + +/* + * This device has ep0 and six bulk/interrupt/iso endpoints. + * + * - Endpoint numbering is fixed: ep0, ep1in-int, ep2in-bulk, ep3in-bulk, + * ep4in-iso, ep5out-bulk, ep6out-bulk, ep7out-iso. + * - Gadget drivers can choose ep maxpacket (8/16/32/64). + * - Just PIO mode currently. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "jz4730_udc.h" + +//#define DEBUG(fmt,args...) printk(KERN_DEBUG fmt , ## args) +//#define DEBUG_EP0(fmt,args...) printk(KERN_DEBUG fmt , ## args) + +#ifndef DEBUG +# define DEBUG(fmt,args...) do {} while(0) +#endif +#ifndef DEBUG_EP0 +# define DEBUG_EP0(fmt,args...) do {} while(0) +#endif + +#define DRIVER_DESC "JZ4730 USB Device Controller" +#define DRIVER_VERSION "20 Sep 2007" + +static const char driver_name [] = "jz4730_udc"; +static const char driver_desc [] = DRIVER_DESC; + +static unsigned int udc_debug = 0; /* 0: normal mode, 1: test udc cable type mode */ + +module_param(udc_debug, int, 0); +MODULE_PARM_DESC(udc_debug, "test udc cable type"); + +#ifdef CONFIG_JZ_UDC_HOTPLUG +extern int jz_udc_active; /* 0: No actions; 1: Have actions */ +#endif + +/* + * Local declarations. + */ +static void nuke(struct jz4730_ep *, int status); +static inline void pio_irq_enable(struct jz4730_ep *ep); +static inline void pio_irq_disable(struct jz4730_ep *ep); +static void jz4730_udc_release (struct device *dev) {} +/*-------------------------------------------------------------------------*/ + +static int jz4730_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct jz4730_udc *dev; + struct jz4730_ep *ep; + unsigned long flags; + u32 max; + + ep = container_of(_ep, struct jz4730_ep, ep); + if (!_ep || !desc || ep->desc + || desc->bDescriptorType != USB_DT_ENDPOINT) + return -EINVAL; + dev = ep->dev; + if (ep == &dev->ep[0]) + return -EINVAL; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + if (ep->index != (desc->bEndpointAddress & 0x0f)) + return -EINVAL; + + switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + break; + default: + return -EINVAL; + } + +// max = le16_to_cpu(get_unaligned(&desc->wMaxPacketSize)); + max = 64; + ep->is_in = (USB_DIR_IN & desc->bEndpointAddress) != 0; + + spin_lock_irqsave(&ep->dev->lock, flags); + + ep->stopped = 0; + ep->desc = desc; + ep->ep.maxpacket = max; + + spin_unlock_irqrestore(&ep->dev->lock, flags); + + DEBUG("enable %s %s maxpacket %u\n", ep->ep.name, + ep->is_in ? "IN" : "OUT", max); + + return 0; +} + +static int jz4730_ep_disable(struct usb_ep *_ep) +{ + struct jz4730_ep *ep; + struct jz4730_udc *dev; + unsigned long flags; + + ep = container_of(_ep, struct jz4730_ep, ep); + if (!_ep || !ep->desc) + return -ENODEV; + dev = ep->dev; + if (dev->ep0state == EP0_SUSPEND) + return -EBUSY; + + DEBUG("disable %s\n", _ep->name); + + spin_lock_irqsave(&dev->lock, flags); + + /* Nuke all pending requests */ + nuke(ep, -ESHUTDOWN); + + /* Disable ep IRQ */ + pio_irq_disable(ep); + + ep->desc = 0; + ep->stopped = 1; + + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +static struct usb_request *jz4730_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct jz4730_request *req; + + req = kzalloc(sizeof *req, gfp_flags); + if (!req) + return 0; + + INIT_LIST_HEAD(&req->queue); + return &req->req; +} + +static void jz4730_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct jz4730_request *req; + + req = container_of(_req, struct jz4730_request, req); + WARN_ON(!list_empty(&req->queue)); + kfree(req); +} + +static void *jz4730_alloc_buffer(struct usb_ep *_ep, unsigned bytes, + dma_addr_t *dma, gfp_t gfp_flags) +{ + void *retval; + + retval = kmalloc(bytes, gfp_flags & ~(__GFP_DMA | __GFP_HIGHMEM)); + if (retval) + *dma = virt_to_phys(retval); + return retval; +} + +static void jz4730_free_buffer(struct usb_ep *_ep, void *buf, + dma_addr_t dma, unsigned bytes) +{ + kfree(buf); +} + +/* + * done - retire a request; caller blocked irqs + */ +static void done(struct jz4730_ep *ep, struct jz4730_request *req, int status) +{ + struct jz4730_udc *dev; + unsigned stopped = ep->stopped; + + list_del_init(&req->queue); + + if (likely(req->req.status == -EINPROGRESS)) + req->req.status = status; + else + status = req->req.status; + + if (status && status != -ESHUTDOWN) + DEBUG("complete %s req %p stat %d len %u/%u\n", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + + dev = ep->dev; + + /* don't modify queue heads during completion callback */ + ep->stopped = 1; + spin_unlock(&dev->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&dev->lock); + ep->stopped = stopped; +} + +/*-------------------------------------------------------------------------*/ + +static __inline__ int write_packet(struct jz4730_ep *ep, + struct jz4730_request *req, int max) +{ + u8 *buf; + int length, nlong, nbyte; + volatile u32 *fifo = (volatile u32 *)ep->fifo; + + buf = req->req.buf + req->req.actual; + prefetch(buf); + + length = req->req.length - req->req.actual; + length = min(length, max); + req->req.actual += length; + + DEBUG("Write %d (max %d), fifo %p\n", length, max, fifo); + + if (!length) { + /* Send ZLP */ + writel(0, (unsigned int *)UDC_TXZLP); + writel(0x12345678, (unsigned int *)fifo); + } + else { + nlong = length >> 2; + nbyte = length & 0x3; + while (nlong--) { + *fifo = *((u32 *)buf); + buf += 4; + } + while (nbyte--) { + *((volatile u8 *)fifo) = *buf++; + } + } + + writel(0, (unsigned int *)UDC_TXCONFIRM); + + return length; +} + +static __inline__ int read_packet(struct jz4730_ep *ep, + struct jz4730_request *req, int count) +{ + u8 *buf; + int length, nlong, nbyte; + volatile u32 *fifo = (volatile u32 *)ep->fifo; + + buf = req->req.buf + req->req.actual; + prefetchw(buf); + + length = req->req.length - req->req.actual; + length = min(length, count); + req->req.actual += length; + + DEBUG("Read %d, fifo %p\n", length, fifo); + + nlong = length >> 2; + nbyte = length & 0x3; + while (nlong--) { + *((u32 *)buf) = *fifo; + buf += 4; + } + if (nbyte) { + u32 data = *fifo; + while (nbyte--) { + *buf++ = data & 0x0ff; + data >>= 8; + } + } + + REG32(UDC_RXCONFIRM); + + return length; +} + +/** Write request to FIFO (max write == maxp size) + * Return: 0 = still running, 1 = completed, negative = errno + */ +static int write_fifo(struct jz4730_ep *ep, struct jz4730_request *req) +{ + u32 max, count; + int is_last; + + max = ep->ep.maxpacket; + + count = write_packet(ep, req, max); + + /* last packet often short (sometimes a zlp, especially on ep0) */ + if (unlikely(count != max)) { + is_last = 1; + } else { + if (likely(req->req.length != req->req.actual) + || req->req.zero) + is_last = 0; + else + is_last = 1; + } + + DEBUG("write %s (%d)(IN) %d bytes%s req %p %d/%d is_last %d\n", + ep->ep.name, ep->index, count, + (count != ep->ep.maxpacket) ? " (short)" : "", + req, req->req.actual, req->req.length, is_last); + + /* requests complete when all IN data is in the FIFO, + * or sometimes later, if a zlp was needed. + */ + if (is_last) { + done(ep, req, 0); + return 1; + } + + return 0; +} + +/** Read to request from FIFO (max read == bytes in fifo) + * Return: 0 = still running, 1 = completed, negative = errno + */ +static int read_fifo(struct jz4730_ep *ep, struct jz4730_request *req, u32 count) +{ + int is_short; + + is_short = (count < ep->ep.maxpacket); + + count = read_packet(ep, req, count); + + DEBUG("read %s %u bytes%s OUT req %p %u/%u is_short %d\n", + ep->ep.name, count, (count < ep->ep.maxpacket) ? "(short)" : "", + req, req->req.actual, req->req.length, is_short); + + /* completion */ + if (is_short || req->req.actual == req->req.length) { + done(ep, req, 0); + return 1; + } + + /* finished that packet. the next one may be waiting... */ + return 0; +} + +static inline void pio_irq_enable(struct jz4730_ep *ep) +{ + switch (ep->index) { + case 0: + REG_UDC_EPIntMR &= ~0x1; + break; + case 1: + case 2: + case 3: + case 4: + REG_UDC_EPIntMR &= ~(1 << ep->index); + break; + case 5: + case 6: + case 7: + REG_UDC_EPIntMR &= ~(1 << (ep->index + 16)); + break; + } +} + +static inline void pio_irq_disable(struct jz4730_ep *ep) +{ + switch (ep->index) { + case 0: + REG_UDC_EPIntMR |= 0x1; + break; + case 1: + case 2: + case 3: + case 4: + REG_UDC_EPIntMR |= (1 << ep->index); + break; + case 5: + case 6: + case 7: + REG_UDC_EPIntMR |= (1 << (ep->index + 16)); + break; + } +} + +/*-------------------------------------------------------------------------*/ + +static int +jz4730_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct jz4730_request *req; + struct jz4730_ep *ep; + struct jz4730_udc *dev; + unsigned long flags; + int status; + + /* always require a cpu-view buffer so pio works */ + req = container_of(_req, struct jz4730_request, req); + if (unlikely(!_req || !_req->complete + || !_req->buf || !list_empty(&req->queue))) + return -EINVAL; + ep = container_of(_ep, struct jz4730_ep, ep); + if (unlikely(!_ep || (!ep->desc && ep->index != 0))) + return -EINVAL; + dev = ep->dev; + if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) + return -ESHUTDOWN; + + DEBUG("%s queue req %p, len %u buf %p\n", + _ep->name, _req, _req->length, _req->buf); + + spin_lock_irqsave(&dev->lock, flags); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + /* for ep0 IN without premature status, zlp is required and + * writing EOP starts the status stage (OUT). + */ + if (unlikely(ep->index == 0 && ep->is_in)) + _req->zero = 1; + + /* kickstart this i/o queue? */ + status = 0; + if (list_empty(&ep->queue) && likely(!ep->stopped)) { + if (unlikely(ep->index == 0)) { + pio_irq_enable(ep); + if (ep->irq_pending || + (REG_UDC_EPIntR & UDC_EPIntR_OUTEP0)) { + u32 stats, count; + + stats = REG_UDC_EP0OutSR; + if (stats & UDC_EPSR_OUT_RCVDATA) { + ep->irq_pending = 0; + REG_UDC_EP0OutSR &= ~UDC_EPSR_OUT_MASK; + if (REG_UDC_EPIntR & UDC_EPIntR_OUTEP0) + REG_UDC_EPIntR = UDC_EPIntR_OUTEP0; + + count = OUT_COUNT(stats); + if (read_fifo(ep, req, count) == 1) + req = 0; + } + } + + } else if (ep->is_in) { + /* EP1 ~ EP4 */ + if (ep->irq_pending || + (REG_UDC_EPIntR & UDC_EPIntR_INEP2)) { + if (REG_UDC_EP2InSR & UDC_EPSR_IN) { + ep->irq_pending = 0; + REG_UDC_EP2InSR &= ~UDC_EPSR_IN; + if (REG_UDC_EPIntR & UDC_EPIntR_INEP2) + REG_UDC_EPIntR = UDC_EPIntR_INEP2; + + if (write_fifo(ep, req) == 1) + req = 0; + } + } + pio_irq_enable(ep); + } else { + /* EP5 ~ EP7 */ + pio_irq_enable(ep); + + if (ep->irq_pending || + (REG_UDC_EPIntR & UDC_EPIntR_OUTEP5)) { + u32 stats, count; + + stats = REG_UDC_EP5OutSR; + if (stats & UDC_EPSR_OUT_RCVDATA) { + ep->irq_pending = 0; + REG_UDC_EP5OutSR &= ~UDC_EPSR_OUT_MASK; + if (REG_UDC_EPIntR & UDC_EPIntR_OUTEP5) + REG_UDC_EPIntR = UDC_EPIntR_OUTEP5; + + count = OUT_COUNT(stats); + if (read_fifo(ep, req, count) == 1) + req = 0; + } + } + } + } + + /* pio or dma irq handler advances the queue. */ + if (likely(req != 0)) { + list_add_tail(&req->queue, &ep->queue); + } + + spin_unlock_irqrestore(&dev->lock, flags); + + return status; +} + +/* dequeue ALL requests */ +static void nuke(struct jz4730_ep *ep, int status) +{ + struct jz4730_request *req; + + if (list_empty(&ep->queue)) + return; + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct jz4730_request, queue); + done(ep, req, status); + } +} + +/* dequeue JUST ONE request */ +static int jz4730_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct jz4730_request *req; + struct jz4730_ep *ep; + struct jz4730_udc *dev; + unsigned long flags; + int stopped; + + ep = container_of(_ep, struct jz4730_ep, ep); + if (!_ep || !_req || (!ep->desc && ep->index != 0)) + return -EINVAL; + dev = ep->dev; + if (!dev->driver) + return -ESHUTDOWN; + + DEBUG("%s %s %p\n", __FUNCTION__, _ep->name,_req); + + spin_lock_irqsave(&dev->lock, flags); + stopped = ep->stopped; + ep->stopped = 1; + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry (req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + spin_unlock_irqrestore (&dev->lock, flags); + return -EINVAL; + } + + /* queue head may be partially complete. */ + if (ep->queue.next == &req->queue) { + done (ep, req, -ECONNRESET); + req = 0; + } + + if (req) + done (ep, req, -ECONNRESET); + ep->stopped = stopped; + + spin_unlock_irqrestore (&ep->dev->lock, flags); + return req ? 0 : -EOPNOTSUPP; +} + +/*-------------------------------------------------------------------------*/ + +static void jz4730_clear_halt(struct jz4730_ep *ep) +{ + if (ep->stopped) { + ep->stopped = 0; + } +} + +static int jz4730_set_halt(struct usb_ep *_ep, int value) +{ + struct jz4730_ep *ep; + unsigned long flags; + int retval = 0; + + if (!_ep) + return -ENODEV; + ep = container_of (_ep, struct jz4730_ep, ep); + if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03) + == USB_ENDPOINT_XFER_ISOC) + return -EINVAL; + + if (ep->index == 0) { + if (value) { + ep->dev->ep0state = EP0_STALL; + ep->dev->ep[0].stopped = 1; + } else + return -EINVAL; + + /* don't change EPxSTATUS_EP_INVALID to READY */ + } else if (!ep->desc) { + DEBUG("%s %s inactive?\n", __FUNCTION__, ep->ep.name); + return -EINVAL; + } + + spin_lock_irqsave(&ep->dev->lock, flags); + + if (!list_empty(&ep->queue)) + retval = -EAGAIN; + else if (!value) + jz4730_clear_halt(ep); + else { + ep->stopped = 1; + } + + spin_unlock_irqrestore(&ep->dev->lock, flags); + return retval; +} + +static int jz4730_fifo_status(struct usb_ep *_ep) +{ + struct jz4730_ep *ep; + u32 size = 0; + + if (!_ep) + return -ENODEV; + ep = container_of(_ep, struct jz4730_ep, ep); + + /* size is only reported sanely for OUT */ + if (ep->is_in) + return -EOPNOTSUPP; + + return size; +} + +static void jz4730_fifo_flush(struct usb_ep *_ep) +{ + struct jz4730_ep *ep; + + if (!_ep) + return; + ep = container_of(_ep, struct jz4730_ep, ep); + + /* don't change EPxSTATUS_EP_INVALID to READY */ + if (!ep->desc && ep->index != 0) { + return; + } +} + +static struct usb_ep_ops jz4730_ep_ops = { + .enable = jz4730_ep_enable, + .disable = jz4730_ep_disable, + + .alloc_request = jz4730_alloc_request, + .free_request = jz4730_free_request, +#if 0 + .alloc_buffer = jz4730_alloc_buffer, + .free_buffer = jz4730_free_buffer, +#endif + .queue = jz4730_queue, + .dequeue = jz4730_dequeue, + + .set_halt = jz4730_set_halt, + .fifo_status = jz4730_fifo_status, + .fifo_flush = jz4730_fifo_flush, +}; + +/*-------------------------------------------------------------------------*/ + +static int jz4730_get_frame(struct usb_gadget *_gadget) +{ + return -EOPNOTSUPP; +} + +static const struct usb_gadget_ops jz4730_ops = { + .get_frame = jz4730_get_frame, + // no remote wakeup + // not selfpowered +}; + +/*-------------------------------------------------------------------------*/ + +static void udc_reinit(struct jz4730_udc *dev) +{ + static char *names [] = { "ep0", "ep1in-int", "ep2in-bulk", "ep3in-bulk", + "ep4in-iso", "ep5out-bulk", "ep6out-bulk", + "ep7out-iso" }; + int i; + + INIT_LIST_HEAD (&dev->gadget.ep_list); + dev->gadget.ep0 = &dev->ep[0].ep; + dev->gadget.speed = USB_SPEED_UNKNOWN; + dev->ep0state = EP0_DISCONNECT; + + for (i = 0; i < MAX_EP_NUM; i++) { + struct jz4730_ep *ep = &dev->ep[i]; + + ep->index = i; + ep->ep.name = names[i]; + ep->fifo = ep_fifo[i]; + ep->ep.maxpacket = 64; + + ep->ep.ops = &jz4730_ep_ops; + list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); + ep->dev = dev; + INIT_LIST_HEAD(&ep->queue); + + ep->desc = 0; + ep->stopped = 1; + ep->irq_pending = 0; + } + + dev->ep[0].ep.maxpacket = MAX_EP0_SIZE; + list_del_init(&dev->ep[0].ep.ep_list); +} + +/* Reset udc registers */ +static void udc_reset(struct jz4730_udc *dev) +{ + REG_UDC_DevIntMR = 0x32; /* Enable RESET and SC interrupts */ + REG_UDC_EPIntMR = 0x0; /* Enable all EP interrupts */ + REG_UDC_DevCFGR = 0x17; + REG_UDC_DevCR = 0x0; + + REG_UDC_EP0InCR = (0 << 4) | (1 << 1); + REG_UDC_EP0InCR = (0 << 4); + REG_UDC_EP1InCR = (3 << 4) | (1 << 1); + REG_UDC_EP1InCR = (3 << 4); + REG_UDC_EP2InCR = (2 << 4) | (1 << 1); + REG_UDC_EP2InCR = (2 << 4); + REG_UDC_EP3InCR = (2 << 4) | (1 << 1); + REG_UDC_EP3InCR = (2 << 4); + REG_UDC_EP4InCR = (1 << 4) | (1 << 1); + REG_UDC_EP4InCR = (1 << 4); + + REG_UDC_EP0OutCR = (0 << 4); + REG_UDC_EP5OutCR = (2 << 4); + REG_UDC_EP6OutCR = (2 << 4); + REG_UDC_EP7OutCR = (1 << 4); + + REG_UDC_EP0InSR = 0; + REG_UDC_EP1InSR = 0; + REG_UDC_EP2InSR = 0; + REG_UDC_EP3InSR = 0; + REG_UDC_EP4InSR = 0; + REG_UDC_EP5OutSR = 0; + REG_UDC_EP6OutSR = 0; + REG_UDC_EP7OutSR = 0; + + REG_UDC_EP0InBSR = MAX_EP0_SIZE/4; + REG_UDC_EP1InBSR = MAX_EP1_SIZE/4; + REG_UDC_EP2InBSR = MAX_EP2_SIZE/4; + REG_UDC_EP3InBSR = MAX_EP3_SIZE/4; + REG_UDC_EP4InBSR = MAX_EP4_SIZE/4; + + REG_UDC_EP0InMPSR = MAX_EP0_SIZE; + REG_UDC_EP1InMPSR = MAX_EP1_SIZE; + REG_UDC_EP2InMPSR = MAX_EP2_SIZE; + REG_UDC_EP3InMPSR = MAX_EP3_SIZE; + REG_UDC_EP4InMPSR = MAX_EP4_SIZE; + + REG_UDC_EP0OutMPSR = MAX_EP0_SIZE; + REG_UDC_EP5OutMPSR = MAX_EP5_SIZE; + REG_UDC_EP6OutMPSR = MAX_EP6_SIZE; + REG_UDC_EP7OutMPSR = MAX_EP7_SIZE; + + REG_UDC_EP0InfR = (MAX_EP0_SIZE << 19) | (0 << 15) | (0 << 11) | (0x1 << 7) | (0 << 5) | (0 << 4) | (0 << 0); + REG_UDC_EP1InfR = (MAX_EP1_SIZE << 19) | (0 << 15) | (0 << 11) | (0x1 << 7) | (3 << 5) | (1 << 4) | (1 << 0); + REG_UDC_EP2InfR = (MAX_EP2_SIZE << 19) | (0 << 15) | (0 << 11) | (0x1 << 7) | (2 << 5) | (1 << 4) | (2 << 0); + REG_UDC_EP3InfR = (MAX_EP3_SIZE << 19) | (0 << 15) | (0 << 11) | (0x1 << 7) | (2 << 5) | (1 << 4) | (3 << 0); + REG_UDC_EP4InfR = (MAX_EP4_SIZE << 19) | (0 << 15) | (0 << 11) | (0x1 << 7) | (1 << 5) | (1 << 4) | (4 << 0); + REG_UDC_EP5InfR = (MAX_EP5_SIZE << 19) | (0 << 15) | (0 << 11) | (0x1 << 7) | (2 << 5) | (0 << 4) | (5 << 0); + REG_UDC_EP6InfR = (MAX_EP6_SIZE << 19) | (0 << 15) | (0 << 11) | (0x1 << 7) | (2 << 5) | (0 << 4) | (6 << 0); + REG_UDC_EP7InfR = (MAX_EP7_SIZE << 19) | (0 << 15) | (0 << 11) | (0x1 << 7) | (1 << 5) | (0 << 4) | (7 << 0); + + REG_UDC_STCMAR = 0xffff; +} + +static void ep0_start(struct jz4730_udc *dev) +{ + udc_reset(dev); + udc_reinit(dev); + + /* expect ep0 requests when the host drops reset */ + dev->gadget.speed = USB_SPEED_FULL; + dev->ep0state = EP0_IDLE; +} + +static void udc_enable(struct jz4730_udc *dev) +{ + /* Enable udc and enable all interrupts */ + __intc_unmask_irq(IRQ_UDC); + __harb_usb0_udc(); + + /* start enumeration now, or after power detect irq */ + ep0_start(dev); +} + +/*-------------------------------------------------------------------------*/ + +/* keeping it simple: + * - one bus driver, initted first; + * - one function driver, initted second + */ + +static struct jz4730_udc *the_controller; + +/* when a driver is successfully registered, it will receive + * control requests including set_configuration(), which enables + * non-control requests. then usb traffic follows until a + * disconnect is reported. then a host may connect again, or + * the driver might get unbound. + */ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct jz4730_udc *dev = the_controller; + int retval; + + if (!driver +// || driver->speed != USB_SPEED_FULL + || !driver->bind + || !driver->unbind + || !driver->disconnect + || !driver->setup) + { + printk("\n -EINVAL"); + return -EINVAL; + } + if (!dev) + return -ENODEV; + + if (dev->driver) + return -EBUSY; + + /* hook up the driver */ + dev->driver = driver; + retval = driver->bind(&dev->gadget); + if (retval) { + DEBUG("bind to driver %s --> error %d\n", + driver->driver.name, retval); + dev->driver = 0; + return retval; + } + /* then enable host detection and ep0; and we're ready + * for set_configuration as well as eventual disconnect. + */ + udc_enable(dev); + + DEBUG("registered gadget driver '%s'\n", driver->driver.name); + return 0; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + +static void +stop_activity(struct jz4730_udc *dev, struct usb_gadget_driver *driver) +{ + unsigned i; + + DEBUG("%s\n", __FUNCTION__); + + if (dev->gadget.speed == USB_SPEED_UNKNOWN) + driver = 0; + + /* disconnect gadget driver after quiesceing hw and the driver */ + udc_reset (dev); + for (i = 0; i < MAX_EP_NUM; i++) + nuke(&dev->ep [i], -ESHUTDOWN); + if (driver) { + spin_unlock(&dev->lock); + driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } + + if (dev->driver) + udc_enable(dev); +} + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct jz4730_udc *dev = the_controller; + unsigned long flags; + + /* disable UDC irq */ + __intc_mask_irq(IRQ_UDC); + __harb_usb0_uhc(); + + if (!dev) + return -ENODEV; + if (!driver || driver != dev->driver) + return -EINVAL; + + spin_lock_irqsave(&dev->lock, flags); + dev->driver = 0; + stop_activity(dev, driver); + spin_unlock_irqrestore(&dev->lock, flags); + + driver->unbind(&dev->gadget); + + DEBUG("unregistered driver '%s'\n", driver->driver.name); + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +static void jz4730_epn_out(struct jz4730_udc *dev, int ep_idx, u32 count) +{ + struct jz4730_request *req; + struct jz4730_ep *ep = &dev->ep[ep_idx]; + + req = list_entry(ep->queue.next, struct jz4730_request, queue); + read_fifo(ep, req, count); +} + +static void jz4730_epn_in(struct jz4730_udc *dev, int ep_idx) +{ + struct jz4730_request *req; + struct jz4730_ep *ep = &dev->ep[ep_idx]; + + req = list_entry(ep->queue.next, struct jz4730_request, queue); + write_fifo(ep, req); +} + +/****************************************************************/ +/* End Point 0 related functions */ +/****************************************************************/ + +/* return: 0 = still running, 1 = completed, negative = errno */ +static int write_fifo_ep0(struct jz4730_ep *ep, struct jz4730_request *req) +{ + u32 max, count; + int is_last; + + max = ep->ep.maxpacket; + + count = write_packet(ep, req, max); + + /* last packet is usually short (or a zlp) */ + if (unlikely(count != max)) + is_last = 1; + else { + if (likely(req->req.length != req->req.actual) || req->req.zero) + is_last = 0; + else + is_last = 1; + } + + DEBUG_EP0("%s: wrote %s %d bytes%s %d left %p\n", __FUNCTION__, + ep->ep.name, count, + is_last ? "/L" : "", req->req.length - req->req.actual, req); + + /* requests complete when all IN data is in the FIFO */ + if (is_last) { + done(ep, req, 0); + return 1; + } + + return 0; +} + +/* + * Simulate a USB_REQ_SET_CONFIGURATION to the function driver, + * this is required to enable the endpoints of the function driver. + * UDC should let software have the chance to handle this standard + * request, unfortunately UDC can't do that. + */ +static void psudo_set_config(void) +{ + struct jz4730_udc *dev = (struct jz4730_udc *) the_controller; + struct usb_ctrlrequest ctrl; + int tmp; + + /* SETUP packet */ + ctrl.bRequestType = 0x00; + ctrl.bRequest = USB_REQ_SET_CONFIGURATION; + ctrl.wValue = 1; + ctrl.wIndex = 0; + ctrl.wLength = 0; + + nuke(&dev->ep[0], 0); + dev->ep[0].stopped = 0; + + if (likely(ctrl.bRequestType & USB_DIR_IN)) { + dev->ep[0].is_in = 1; + dev->ep0state = EP0_IN; + } else { + dev->ep[0].is_in = 0; + dev->ep0state = EP0_OUT; + } + + /* delegate everything to the gadget driver. + * it may respond after this irq handler returns. + */ + spin_unlock (&dev->lock); + tmp = dev->driver->setup(&dev->gadget, &ctrl); + spin_lock (&dev->lock); + if (unlikely(tmp < 0)) { + DEBUG_EP0("req %02x.%02x protocol STALL; err %d\n", + ctrl.bRequestType, ctrl.bRequest, tmp); + dev->ep[0].stopped = 1; + dev->ep0state = EP0_STALL; + } +} + +/* + * Read 8 bytes setup packet from EP0 RX buffer + */ +static void read_setup_packet(u8 *buf) +{ + u32 *tmp = (u32 *)buf; + + *tmp++ = readl((unsigned int *)RXFIFO); + *tmp++ = readl((unsigned int *)RXFIFO); + + REG32(UDC_RXCONFIRM); +} + +static void jz4730_ep0_setup(struct jz4730_udc *dev) +{ + struct jz4730_ep *ep = &dev->ep[0]; + struct usb_ctrlrequest ctrl; + int tmp; + + /* read control req from fifo (8 bytes) */ + read_setup_packet((unsigned char *) &ctrl); + + DEBUG_EP0("SETUP %02x.%02x v%04x i%04x l%04x\n", + ctrl.bRequestType, ctrl.bRequest, + ctrl.wValue, ctrl.wIndex, ctrl.wLength); + + /* Set direction of EP0 */ + if (likely(ctrl.bRequestType & USB_DIR_IN)) { + ep->is_in = 1; + dev->ep0state = EP0_IN; + } else { + ep->is_in = 0; + dev->ep0state = EP0_OUT; + } + + /* Nuke all previous transfers */ + nuke(ep, 0); + ep->stopped = 0; + + /* delegate everything to the gadget driver. + * it may respond after this irq handler returns. + */ + if (likely((u32)dev->driver)) { + /* device-2-host (IN) or no data setup command, process immediately */ + spin_unlock(&dev->lock); + tmp = dev->driver->setup(&dev->gadget, &ctrl); + spin_lock(&dev->lock); + + if (unlikely(tmp < 0)) { + /* setup processing failed, force stall */ + DEBUG_EP0("req %02x.%02x protocol STALL; err %d\n", + ctrl.bRequestType, ctrl.bRequest, tmp); + dev->ep0state = EP0_STALL; + } + } +} + +static int jz4730_ep0_in(struct jz4730_udc *dev) +{ + struct jz4730_request *req; + struct jz4730_ep *ep = &dev->ep[0]; + int ret; + + if (list_empty(&ep->queue)) + req = 0; + else + req = list_entry(ep->queue.next, struct jz4730_request, queue); + + if (!req) { + DEBUG_EP0("%s: NULL REQ\n", __FUNCTION__); + return 0; + } + + ret = write_fifo_ep0(ep, req); + + return ret; +} + +static void jz4730_ep0_out(struct jz4730_udc *dev) +{ + u32 epsr; + struct jz4730_ep *ep = &dev->ep[0]; + + epsr = REG_UDC_EP0OutSR; + REG_UDC_EP0OutSR &= ~UDC_EPSR_OUT_MASK; + + if (epsr & UDC_EPSR_OUT_RCVSETUP) { + jz4730_ep0_setup(dev); + } + else if (epsr & UDC_EPSR_OUT_RCVDATA) { + u32 count = __udc_ep0out_packet_size(); + if (count == 0) { + readl((unsigned int *)UDC_RXCONFIRM); // ack zero packet + } + else { + /* EP0 OUT Data */ + if (list_empty(&ep->queue)) { + ep->irq_pending = 1; + pio_irq_disable(ep); + } + else + jz4730_epn_out(dev, 0, count); + + } + } +} + +static void handle_reset_irq(struct jz4730_udc *dev) +{ + int i; + + /* clear any status */ + REG_UDC_EPIntR = 0xffffffff; + REG_UDC_DevIntR = 0xffffffff; + + /* reset udc */ + udc_reset(dev); + + /* reset driver status */ + for (i = 0; i < MAX_EP_NUM; i++) { + struct jz4730_ep *ep = &dev->ep[i]; + + ep->irq_pending = 0; +// nuke(ep, 0); + nuke(ep, -ESHUTDOWN); + } +} + +static irqreturn_t jz4730_udc_irq(int irq, void *_dev) +{ + struct jz4730_udc *dev = _dev; + struct jz4730_ep *ep; + + u32 intr_dev, intr_ep, stats, count; + + spin_lock(&dev->lock); + + intr_dev = REG_UDC_DevIntR; + intr_ep = REG_UDC_EPIntR; + + DEBUG("*** udc irq intr_dev=0x%x intr_ep=0x%x\n", intr_dev, intr_ep); + + if (!intr_dev && !intr_ep) { + spin_unlock(&dev->lock); + return IRQ_HANDLED; + } + + if (udc_debug) { +#ifdef CONFIG_JZ_UDC_HOTPLUG + jz_udc_active = 1; +#endif + REG_UDC_DevIntR = intr_dev; + REG_UDC_EPIntR = intr_ep; + __harb_usb0_uhc(); + __intc_mask_irq(IRQ_UDC); + spin_unlock(&dev->lock); + return IRQ_HANDLED; + } + + if (intr_dev) { + if (intr_dev & UDC_DevIntR_SC) { + psudo_set_config(); + udelay(100); + } + + if (intr_dev & UDC_DevIntR_UR) { +#ifdef CONFIG_JZ_UDC_HOTPLUG + jz_udc_active = 1; +#endif + handle_reset_irq(dev); + } + + REG_UDC_DevIntR = intr_dev; + } + + if (intr_ep & UDC_EPIntR_OUTEP0) { + REG_UDC_EPIntR = UDC_EPIntR_OUTEP0; + jz4730_ep0_out(dev); + } + + if (intr_ep & UDC_EPIntR_INEP0) { + ep = &dev->ep[0]; + if (list_empty(&ep->queue)) { + pio_irq_disable(ep); + } + else { + stats = REG_UDC_EP0InSR; + if (stats & UDC_EPSR_IN) { + REG_UDC_EPIntR = UDC_EPIntR_INEP0; + REG_UDC_EP0InSR &= ~UDC_EPSR_IN; + + jz4730_ep0_in(dev); + } + } + } + + if (intr_ep & UDC_EPIntR_OUTEP5) { + REG_UDC_EPIntR = UDC_EPIntR_OUTEP5; + ep = &dev->ep[5]; + if (list_empty(&ep->queue)) { + ep->irq_pending = 1; + pio_irq_disable(ep); + } + else { + stats = REG_UDC_EP5OutSR; + if (stats & UDC_EPSR_OUT_RCVDATA) { + REG_UDC_EP5OutSR &= ~UDC_EPSR_OUT_MASK; + + count = OUT_COUNT(stats); + jz4730_epn_out(dev, 5, count); + } + } + } + + if (intr_ep & UDC_EPIntR_INEP2) { + ep = &dev->ep[2]; + if (list_empty(&ep->queue)) { + ep->irq_pending = 1; + pio_irq_disable(ep); + } + else { + stats = REG_UDC_EP2InSR; + if (stats & UDC_EPSR_IN) { + REG_UDC_EP2InSR &= ~UDC_EPSR_IN; + jz4730_epn_in(dev, 2); + } + } + + REG_UDC_EPIntR = UDC_EPIntR_INEP2; + } + + if (intr_ep & UDC_EPIntR_INEP1) { + ep = &dev->ep[1]; + if (list_empty(&ep->queue)) { + ep->irq_pending = 1; + pio_irq_disable(ep); + } + else { + stats = REG_UDC_EP1InSR; + if (stats & UDC_EPSR_IN) { + REG_UDC_EP1InSR &= ~UDC_EPSR_IN; + jz4730_epn_in(dev, 1); + } + } + + REG_UDC_EPIntR = UDC_EPIntR_INEP1; + } + + spin_unlock(&dev->lock); + return IRQ_HANDLED; +} + +/*-------------------------------------------------------------------------*/ + +static struct jz4730_udc udc_dev = { + .usb_address = 0, + + .gadget = { + .ops = &jz4730_ops, + .ep0 = &udc_dev.ep[0].ep, + .name = driver_name, + .dev = { + .bus_id = "gadget", + }, + }, + /* control endpoint no need to init here!*/ + /* control endpoint */ +}; + + +/* tear down the binding between this driver and the pci device */ +static int jz4730_udc_remove(struct platform_device *pdev) +{ + struct jz4730_udc *dev = platform_get_drvdata(pdev); + + if (dev->driver) + return -EBUSY; + + /* USB port0 as UHC */ + __harb_usb0_uhc(); + + /* reset udc */ + udc_reset(dev); + + /* clear any status */ + REG_UDC_EPIntR = 0xffffffff; + REG_UDC_DevIntR = 0xffffffff; + + /* disable all UDC interrupts */ + REG_UDC_DevIntMR = 0xffffffff; + REG_UDC_EPIntMR = 0xffffffff; + + free_irq(IRQ_UDC, dev); + platform_set_drvdata(pdev, 0); + device_unregister(&dev->gadget.dev); + the_controller = 0; + + return 0; +} + +static int jz4730_udc_probe(struct platform_device *pdev) +{ + struct jz4730_udc *dev = &udc_dev; + int retval,rc; + + /* if you want to support more than one controller in a system, + * usb_gadget_driver_{register,unregister}() must change. + */ + if (the_controller) { + printk("Check the_controller: %s\n", driver_name); + return -EBUSY; + } + + spin_lock_init(&dev->lock); + device_initialize(&dev->gadget.dev); + dev->gadget.dev.parent = &pdev->dev; //if no,can only insmod once!! + dev->gadget.dev.release = jz4730_udc_release; + rc = device_register (&dev->gadget.dev); + if (rc < 0) + return rc; + platform_set_drvdata(pdev, dev); + + /* + * Note: we just mask INTC irq but allow UDC irq. + * This avoid that we miss any UDC irqs. + */ + + /* To avoid any UDC irqs here, we call cli() first */ +// cli(); + + /* disable INTC irq */ + __intc_mask_irq(IRQ_UDC); + + /* init to known state, then setup irqs */ + udc_reset(dev); + udc_reinit(dev); + + /* request UDC irq */ + if (request_irq(IRQ_UDC, jz4730_udc_irq, IRQF_DISABLED, // SA_INTERRUPT, + driver_name, dev) != 0) { + printk(KERN_INFO "request UDC interrupt %d failed\n", IRQ_UDC); + retval = -EBUSY; + goto done; + } + + /* disable INTC irq again since request_irq has enabled it */ + __intc_mask_irq(IRQ_UDC); + __intc_ack_irq(IRQ_UDC); + + /* Re-enable irqs */ +// sti(); + + printk(KERN_INFO "%s\n", driver_desc); + printk(KERN_INFO "version: " DRIVER_VERSION "\n"); + + /* done */ + the_controller = dev; + + return 0; + +done: + if (dev) + jz4730_udc_remove (pdev); + return retval; +} + +static struct platform_driver udc_driver = { + .probe = jz4730_udc_probe, + .remove = jz4730_udc_remove, + .suspend = NULL, + .resume = NULL, + .driver = { + .name = (char *) driver_name, + .owner = THIS_MODULE, + }, +}; +static struct platform_device the_udc_pdev = { + .name = (char *) driver_name, + .id = -1, + .dev = { + .release = jz4730_udc_release, + }, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init udc_init (void) +{ + platform_driver_register(&udc_driver); + return platform_device_register (&the_udc_pdev); +} + +static void __exit udc_exit (void) +{ + platform_driver_unregister(&udc_driver); + platform_device_unregister(&the_udc_pdev); +} + +module_init(udc_init); +module_exit(udc_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Wei Jianli "); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/jz4730_udc.h b/drivers/usb/gadget/jz4730_udc.h new file mode 100644 index 0000000..6ea368b --- /dev/null +++ b/drivers/usb/gadget/jz4730_udc.h @@ -0,0 +1,107 @@ +/* + * JZ4730 USB Device Controller driver + * + * Copyright (C) 2005 by Wei Jianli + * + * 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. + */ + +#ifndef __JZ4730_UDC_H__ +#define __JZ4730_UDC_H__ + +/* DRIVER DATA STRUCTURES and UTILITIES */ +#define MAX_EP_NUM 8 /* Number of endpoints on this UDC */ + +#define MAX_EP0_SIZE 32 +#define MAX_EP1_SIZE 64 +#define MAX_EP2_SIZE 64 +#define MAX_EP3_SIZE 64 +#define MAX_EP4_SIZE 64 +#define MAX_EP5_SIZE 64 +#define MAX_EP6_SIZE 64 +#define MAX_EP7_SIZE 64 + +// UDC FIFO +#define RXFIFO (UDC_RXFIFO) /* EP0 OUT, EP5-7 OUT */ +#define TXFIFOEP0 (UDC_TXFIFOEP0) /* EP0 IN */ +#define TXFIFOEP1 (TXFIFOEP0 + MAX_EP0_SIZE) /* EP1 IN */ +#define TXFIFOEP2 (TXFIFOEP1 + MAX_EP1_SIZE) /* EP2 IN */ +#define TXFIFOEP3 (TXFIFOEP2 + MAX_EP2_SIZE) /* EP3 IN */ +#define TXFIFOEP4 (TXFIFOEP3 + MAX_EP3_SIZE) /* EP4 IN */ + +static u32 ep_fifo[MAX_EP_NUM] = {TXFIFOEP0, TXFIFOEP1, TXFIFOEP2, + TXFIFOEP3, TXFIFOEP4, RXFIFO, RXFIFO, + RXFIFO}; + +#define OUT_COUNT(stats) \ + ((stats&UDC_EPSR_RXPKTSIZE_MASK)>>UDC_EPSR_RXPKTSIZE_BIT) + +struct jz4730_ep { + struct usb_ep ep; + struct jz4730_udc *dev; + + u8 index; + u8 is_in; + u8 stopped; + u8 irq_pending; + u32 fifo; + + struct list_head queue; + const struct usb_endpoint_descriptor *desc; +}; + +struct jz4730_request { + struct usb_request req; + struct list_head queue; +}; + +enum ep0state { + EP0_DISCONNECT, /* no host */ + EP0_IDLE, /* between STATUS ack and SETUP report */ + EP0_IN, EP0_OUT, /* data stage */ + EP0_STATUS, /* status stage */ + EP0_STALL, /* data or status stages */ + EP0_SUSPEND, /* usb suspend */ +}; + +struct jz4730_udc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + spinlock_t lock; + + struct jz4730_ep ep[MAX_EP_NUM]; + enum ep0state ep0state; + unsigned char usb_address; +}; + +/*-------------------------------------------------------------------------*/ + +/* 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 /* __JZ4730_UDC_H__ */ diff --git a/drivers/usb/gadget/jz4740_udc.c b/drivers/usb/gadget/jz4740_udc.c new file mode 100644 index 0000000..a1d5dbb --- /dev/null +++ b/drivers/usb/gadget/jz4740_udc.c @@ -0,0 +1,2251 @@ +/* + * linux/drivers/usb/gadget/jz4740_udc.c + * + * Ingenic JZ4740 on-chip high speed USB device controller + * + * Copyright (C) 2006 - 2008 Ingenic Semiconductor Inc. + * Author: + * + * 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 device has ep0, two bulk-in/interrupt-in endpoints, and one bulk-out endpoint. + * + * - Endpoint numbering is fixed: ep0, ep1in-int, ep2in-bulk, ep1out-bulk. + * - DMA works with bulk-in (channel 1) and bulk-out (channel 2) endpoints. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "jz4740_udc.h" + +//#define DEBUG(fmt,args...) printk(KERN_DEBUG fmt , ## args) +//#define DEBUG(fmt,args...) printk(fmt , ## args) +//#define DEBUG_EP0(fmt,args...) printk(fmt , ## args) +//#define DEBUG_SETUP(fmt,args...) printk(fmt , ## args) + +#ifndef DEBUG +# define DEBUG(fmt,args...) do {} while(0) +#endif +#ifndef DEBUG_EP0 +# define NO_STATES +# define DEBUG_EP0(fmt,args...) do {} while(0) +#endif +#ifndef DEBUG_SETUP +# define DEBUG_SETUP(fmt,args...) do {} while(0) +#endif + +static unsigned int udc_debug = 0; /* 0: normal mode, 1: test udc cable type mode */ + +module_param(udc_debug, int, 0); +MODULE_PARM_DESC(udc_debug, "test udc cable or power type"); + +static unsigned int use_dma = 1; /* 1: use DMA, 0: use PIO */ + +module_param(use_dma, int, 0); +MODULE_PARM_DESC(use_dma, "DMA mode enable flag"); + +#ifdef CONFIG_JZ_UDC_HOTPLUG +extern int jz_udc_active; /* 0: No actions; 1: Have actions */ +#endif + +/* + * Local definintions. + */ + +#define DRIVER_VERSION "13-Mar-2008" +#define DRIVER_DESC "JZ4740 USB Device Controller" + +static const char gadget_name [] = "jz4740_udc"; + +struct jz4740_udc *the_controller; + +static const char driver_name [] = "jz4740_udc"; +static const char driver_desc [] = DRIVER_DESC; +static const char ep0name[] = "ep0"; + +#ifndef NO_STATES +static char *state_names[] = { + "WAIT_FOR_SETUP", + "DATA_STATE_XMIT", + "DATA_STATE_NEED_ZLP", + "WAIT_FOR_OUT_STATUS", + "DATA_STATE_RECV" +}; +#endif + +/* + * Local declarations. + */ +static int jz4740_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc); +static int jz4740_ep_disable(struct usb_ep *_ep); +static struct usb_request *jz4740_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags); +static void jz4740_free_request(struct usb_ep *_ep, struct usb_request *_req); + +static int jz4740_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags); +static int jz4740_dequeue(struct usb_ep *_ep, struct usb_request *_req); +static int jz4740_set_halt(struct usb_ep *_ep, int value); +static int jz4740_fifo_status(struct usb_ep *_ep); +static void jz4740_fifo_flush(struct usb_ep *_ep); + +static void jz4740_ep0_kick(struct jz4740_udc *dev, struct jz4740_ep *ep); +static void jz4740_handle_ep0(struct jz4740_udc *dev, u32 intr); + +static void done(struct jz4740_ep *ep, struct jz4740_request *req, + int status); +static void pio_irq_enable(struct jz4740_ep *ep); +static void pio_irq_disable(struct jz4740_ep *ep); +static void stop_activity(struct jz4740_udc *dev, + struct usb_gadget_driver *driver); +static void nuke(struct jz4740_ep *ep, int status); +static void flush(struct jz4740_ep *ep); +static void udc_enable(struct jz4740_udc *dev); +static void udc_set_address(struct jz4740_udc *dev, unsigned char address); +static void jz4740_udc_release (struct device *dev) {} + +extern void *dma_alloc_noncoherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag); +extern void dma_free_noncoherent(struct device *dev, size_t size, + void *vaddr, dma_addr_t dma_handle); + +static struct usb_ep_ops jz4740_ep_ops = { + .enable = jz4740_ep_enable, + .disable = jz4740_ep_disable, + + .alloc_request = jz4740_alloc_request, + .free_request = jz4740_free_request, + + .queue = jz4740_queue, + .dequeue = jz4740_dequeue, + + .set_halt = jz4740_set_halt, + .fifo_status = jz4740_fifo_status, + .fifo_flush = jz4740_fifo_flush, +}; + +/*-------------------------------------------------------------------------*/ + +/* inline functions of register read/write/set/clear */ + +static __inline__ u8 usb_readb(u32 port) +{ + return *(volatile u8 *)port; +} + +static __inline__ u16 usb_readw(u32 port) +{ + return *(volatile u16 *)port; +} + +static __inline__ u32 usb_readl(u32 port) +{ + return *(volatile u32 *)port; +} + +static __inline__ void usb_writeb(u32 port, u8 val) +{ + *(volatile u8 *)port = val; +} + +static __inline__ void usb_writew(u32 port, u16 val) +{ + *(volatile u16 *)port = val; +} + +static __inline__ void usb_writel(u32 port, u32 val) +{ + *(volatile u32 *)port = val; +} + +static __inline__ void usb_setb(u32 port, u8 val) +{ + volatile u8 *ioport = (volatile u8 *)(port); + *ioport = (*ioport) | val; +} + +static __inline__ void usb_setw(u32 port, u16 val) +{ + volatile u16 *ioport = (volatile u16 *)(port); + *ioport = (*ioport) | val; +} + +static __inline__ void usb_setl(u32 port, u32 val) +{ + volatile u32 *ioport = (volatile u32 *)(port); + *ioport = (*ioport) | val; +} + +static __inline__ void usb_clearb(u32 port, u8 val) +{ + volatile u8 *ioport = (volatile u8 *)(port); + *ioport = (*ioport) & ~val; +} + +static __inline__ void usb_clearw(u32 port, u16 val) +{ + volatile u16 *ioport = (volatile u16 *)(port); + *ioport = (*ioport) & ~val; +} + +static __inline__ void usb_clearl(u32 port, u32 val) +{ + volatile u32 *ioport = (volatile u32 *)(port); + *ioport = (*ioport) & ~val; +} + +/*-------------------------------------------------------------------------*/ + +static __inline__ int write_packet(struct jz4740_ep *ep, + struct jz4740_request *req, int max) +{ + u8 *buf; + int length, nlong, nbyte; + volatile u32 *fifo = (volatile u32 *)ep->fifo; + + buf = req->req.buf + req->req.actual; + prefetch(buf); + + length = req->req.length - req->req.actual; + length = min(length, max); + req->req.actual += length; + + DEBUG("Write %d (max %d), fifo %p\n", length, max, fifo); + + nlong = length >> 2; + nbyte = length & 0x3; + while (nlong--) { + *fifo = *((u32 *)buf); + buf += 4; + } + while (nbyte--) { + *((volatile u8 *)fifo) = *buf++; + } + + return length; +} + +static __inline__ int read_packet(struct jz4740_ep *ep, + struct jz4740_request *req, int count) +{ + u8 *buf; + int length, nlong, nbyte; + volatile u32 *fifo = (volatile u32 *)ep->fifo; + + buf = req->req.buf + req->req.actual; + prefetchw(buf); + + length = req->req.length - req->req.actual; + length = min(length, count); + req->req.actual += length; + + DEBUG("Read %d, fifo %p\n", length, fifo); + + nlong = length >> 2; + nbyte = length & 0x3; + while (nlong--) { + *((u32 *)buf) = *fifo; + buf += 4; + } + while (nbyte--) { + *buf++ = *((volatile u8 *)fifo); + } + + return length; +} + +/*-------------------------------------------------------------------------*/ + +/* + * udc_disable - disable USB device controller + */ +static void udc_disable(struct jz4740_udc *dev) +{ + DEBUG("%s, %p\n", __FUNCTION__, dev); + + udc_set_address(dev, 0); + + /* Disable interrupts */ + usb_writew(USB_REG_INTRINE, 0); + usb_writew(USB_REG_INTROUTE, 0); + usb_writeb(USB_REG_INTRUSBE, 0); + + /* Disable DMA */ + usb_writel(USB_REG_CNTL1, 0); + usb_writel(USB_REG_CNTL2, 0); + + /* Disconnect from usb */ + usb_clearb(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 + + dev->ep0state = WAIT_FOR_SETUP; + dev->gadget.speed = USB_SPEED_UNKNOWN; +} + +/* + * udc_reinit - initialize software state + */ +static void udc_reinit(struct jz4740_udc *dev) +{ + u32 i; + + DEBUG("%s, %p\n", __FUNCTION__, dev); + + /* device/ep0 records init */ + INIT_LIST_HEAD(&dev->gadget.ep_list); + INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); + dev->ep0state = WAIT_FOR_SETUP; + + for (i = 0; i < UDC_MAX_ENDPOINTS; i++) { + struct jz4740_ep *ep = &dev->ep[i]; + + if (i != 0) + list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); + + INIT_LIST_HEAD(&ep->queue); + ep->desc = 0; + ep->stopped = 0; + ep->pio_irqs = 0; + } +} + +/* until it's enabled, this UDC should be completely invisible + * to any USB host. + */ +static void udc_enable(struct jz4740_udc *dev) +{ + int i; + + DEBUG("%s, %p\n", __FUNCTION__, dev); + + dev->gadget.speed = USB_SPEED_UNKNOWN; + + /* Flush FIFO for each */ + for (i = 0; i < UDC_MAX_ENDPOINTS; i++) { + struct jz4740_ep *ep = &dev->ep[i]; + + usb_set_index(ep_index(ep)); + flush(ep); + } + + /* Set this bit to allow the UDC entering low-power mode when + * there are no actions on the USB bus. + * UDC still works during this bit was set. + */ + __cpm_stop_udc(); + + /* Enable 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 + + /* Disable interrupts */ + usb_writew(USB_REG_INTRINE, 0); + usb_writew(USB_REG_INTROUTE, 0); + usb_writeb(USB_REG_INTRUSBE, 0); + + /* Enable interrupts */ + usb_setw(USB_REG_INTRINE, USB_INTR_EP0); + usb_setb(USB_REG_INTRUSBE, USB_INTR_RESET); + /* Don't enable rest of the interrupts */ + /* usb_setw(USB_REG_INTRINE, USB_INTR_INEP1 | USB_INTR_INEP2); + usb_setw(USB_REG_INTROUTE, USB_INTR_OUTEP1); */ + + /* Enable SUSPEND */ + /* usb_setb(USB_REG_POWER, USB_POWER_SUSPENDM); */ + + /* Enable HS Mode */ + usb_setb(USB_REG_POWER, USB_POWER_HSENAB); + + /* Let host detect UDC: + * Software must write a 1 to the PMR:USB_POWER_SOFTCONN bit to turn this + * transistor on and pull the USBDP pin HIGH. + */ + usb_setb(USB_REG_POWER, USB_POWER_SOFTCONN); +} + +/*-------------------------------------------------------------------------*/ + +/* keeping it simple: + * - one bus driver, initted first; + * - one function driver, initted second + */ + +/* + * Register entry point for the peripheral controller driver. + */ + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct jz4740_udc *dev = the_controller; + int retval; + + if (!driver + || !driver->bind + || !driver->unbind || !driver->disconnect || !driver->setup) + { + printk("\n-EINVAL"); + return -EINVAL; + } + if (!dev) + { + printk("\n-ENODEV"); + return -ENODEV; + } + if (dev->driver) + { + printk("\n-ENODEV"); + return -EBUSY; + } + + /* hook up the driver */ + dev->driver = driver; + retval = driver->bind(&dev->gadget); + if (retval) { + DEBUG("%s: bind to driver %s --> error %d\n", dev->gadget.name, + driver->driver.name, retval); + dev->driver = 0; + return retval; + } + + /* then enable host detection and ep0; and we're ready + * for set_configuration as well as eventual disconnect. + */ + udc_enable(dev); + DEBUG("%s: registered gadget driver '%s'\n", dev->gadget.name, + driver->driver.name); + + return 0; +} + +EXPORT_SYMBOL(usb_gadget_register_driver); + + +static void stop_activity(struct jz4740_udc *dev, + struct usb_gadget_driver *driver) +{ + int i; + + DEBUG("%s\n", __FUNCTION__); + + /* don't disconnect drivers more than once */ + if (dev->gadget.speed == USB_SPEED_UNKNOWN) + driver = 0; + dev->gadget.speed = USB_SPEED_UNKNOWN; + + /* prevent new request submissions, kill any outstanding requests */ + for (i = 0; i < UDC_MAX_ENDPOINTS; i++) { + struct jz4740_ep *ep = &dev->ep[i]; + + ep->stopped = 1; + + usb_set_index(ep_index(ep)); + nuke(ep, -ESHUTDOWN); + } + + /* report disconnect; the driver is already quiesced */ + if (driver) { + spin_unlock(&dev->lock); + driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } + + /* re-init driver-visible data structures */ + udc_reinit(dev); +} + + +/* + * Unregister entry point for the peripheral controller driver. + */ +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct jz4740_udc *dev = the_controller; + unsigned long flags; + + if (!dev) + return -ENODEV; + if (!driver || driver != dev->driver) + return -EINVAL; + + spin_lock_irqsave(&dev->lock, flags); + dev->driver = 0; + stop_activity(dev, driver); + spin_unlock_irqrestore(&dev->lock, flags); + + driver->unbind(&dev->gadget); + + udc_disable(dev); + + DEBUG("unregistered driver '%s'\n", driver->driver.name); + + return 0; +} + +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +/*-------------------------------------------------------------------------*/ + +/* + * Starting DMA using mode 1 + */ +static void kick_dma(struct jz4740_ep *ep, struct jz4740_request *req) +{ + u32 count = req->req.length; + u32 physaddr = virt_to_phys((void *)req->req.buf); + + usb_set_index(ep_index(ep)); + if (ep_is_in(ep)) { /* Bulk-IN transfer using DMA channel 1 */ + ep->reg_addr = USB_REG_ADDR1; + + dma_cache_wback_inv((unsigned long)req->req.buf, count); + + pio_irq_enable(ep); + + usb_writeb(USB_REG_INCSRH, + USB_INCSRH_DMAREQENAB | USB_INCSRH_AUTOSET | USB_INCSRH_DMAREQMODE); + + usb_writel(USB_REG_ADDR1, physaddr); + usb_writel(USB_REG_COUNT1, count); + usb_writel(USB_REG_CNTL1, USB_CNTL_ENA | USB_CNTL_DIR_IN | USB_CNTL_MODE_1 | + USB_CNTL_INTR_EN | USB_CNTL_BURST_16 | USB_CNTL_EP(ep_index(ep))); + } + else { /* Bulk-OUT transfer using DMA channel 2 */ + ep->reg_addr = USB_REG_ADDR2; + + dma_cache_wback_inv((unsigned long)req->req.buf, count); + + pio_irq_enable(ep); + + usb_setb(USB_REG_OUTCSRH, + USB_OUTCSRH_DMAREQENAB | USB_OUTCSRH_AUTOCLR | USB_OUTCSRH_DMAREQMODE); + + usb_writel(USB_REG_ADDR2, physaddr); + usb_writel(USB_REG_COUNT2, count); + usb_writel(USB_REG_CNTL2, USB_CNTL_ENA | USB_CNTL_MODE_1 | + USB_CNTL_INTR_EN | USB_CNTL_BURST_16 | USB_CNTL_EP(ep_index(ep))); + } +} + +/*-------------------------------------------------------------------------*/ + +/** Write request to FIFO (max write == maxp size) + * Return: 0 = still running, 1 = completed, negative = errno + * NOTE: INDEX register must be set for EP + */ +static int write_fifo(struct jz4740_ep *ep, struct jz4740_request *req) +{ + u32 max, csr; + u32 physaddr = virt_to_phys((void *)req->req.buf); + + max = le16_to_cpu(ep->desc->wMaxPacketSize); + + if (use_dma) { + u32 dma_count; + + /* DMA interrupt generated due to the last packet loaded into the FIFO */ + + dma_count = usb_readl(ep->reg_addr) - physaddr; + req->req.actual += dma_count; + + if (dma_count % max) { + /* If the last packet is less than MAXP, set INPKTRDY manually */ + usb_setb(ep->csr, USB_INCSR_INPKTRDY); + } + + done(ep, req, 0); + if (list_empty(&ep->queue)) { + pio_irq_disable(ep); + return 1; + } + else { + /* advance the request queue */ + req = list_entry(ep->queue.next, struct jz4740_request, queue); + kick_dma(ep, req); + return 0; + } + } + + /* + * PIO mode handling starts here ... + */ + + csr = usb_readb(ep->csr); + + if (!(csr & USB_INCSR_FFNOTEMPT)) { + unsigned count; + int is_last, is_short; + + count = write_packet(ep, req, max); + usb_setb(ep->csr, USB_INCSR_INPKTRDY); + + /* last packet is usually short (or a zlp) */ + if (unlikely(count != max)) + is_last = is_short = 1; + else { + if (likely(req->req.length != req->req.actual) + || req->req.zero) + is_last = 0; + else + is_last = 1; + /* interrupt/iso maxpacket may not fill the fifo */ + is_short = unlikely(max < ep_maxpacket(ep)); + } + + DEBUG("%s: wrote %s %d bytes%s%s %d left %p\n", __FUNCTION__, + ep->ep.name, count, + is_last ? "/L" : "", is_short ? "/S" : "", + req->req.length - req->req.actual, req); + + /* requests complete when all IN data is in the FIFO */ + if (is_last) { + done(ep, req, 0); + if (list_empty(&ep->queue)) { + pio_irq_disable(ep); + } + return 1; + } + } else { + DEBUG("Hmm.. %d ep FIFO is not empty!\n", ep_index(ep)); + } + + return 0; +} + +/** Read to request from FIFO (max read == bytes in fifo) + * Return: 0 = still running, 1 = completed, negative = errno + * NOTE: INDEX register must be set for EP + */ +static int read_fifo(struct jz4740_ep *ep, struct jz4740_request *req) +{ + u32 csr; + unsigned count, is_short; + u32 physaddr = virt_to_phys((void *)req->req.buf); + + if (use_dma) { + u32 dma_count; + + /* DMA interrupt generated due to a packet less than MAXP loaded into the FIFO */ + + dma_count = usb_readl(ep->reg_addr) - physaddr; + req->req.actual += dma_count; + + /* Disable interrupt and DMA */ + pio_irq_disable(ep); + usb_writel(USB_REG_CNTL2, 0); + + /* Read all bytes from this packet */ + count = usb_readw(USB_REG_OUTCOUNT); + count = read_packet(ep, req, count); + + if (count) { + /* If the last packet is greater than zero, clear OUTPKTRDY manually */ + usb_clearb(ep->csr, USB_OUTCSR_OUTPKTRDY); + } + + done(ep, req, 0); + + if (!list_empty(&ep->queue)) { + /* advance the request queue */ + req = list_entry(ep->queue.next, struct jz4740_request, queue); + kick_dma(ep, req); + } + + return 1; + } + + /* + * PIO mode handling starts here ... + */ + + /* make sure there's a packet in the FIFO. */ + csr = usb_readb(ep->csr); + if (!(csr & USB_OUTCSR_OUTPKTRDY)) { + DEBUG("%s: Packet NOT ready!\n", __FUNCTION__); + return -EINVAL; + } + + /* read all bytes from this packet */ + count = usb_readw(USB_REG_OUTCOUNT); + + is_short = (count < ep->ep.maxpacket); + + count = read_packet(ep, req, count); + + DEBUG("read %s %02x, %d bytes%s req %p %d/%d\n", + ep->ep.name, csr, count, + is_short ? "/S" : "", req, req->req.actual, req->req.length); + + /* Clear OutPktRdy */ + usb_clearb(ep->csr, USB_OUTCSR_OUTPKTRDY); + + /* completion */ + if (is_short || req->req.actual == req->req.length) { + done(ep, req, 0); + + if (list_empty(&ep->queue)) + pio_irq_disable(ep); + return 1; + } + + /* finished that packet. the next one may be waiting... */ + return 0; +} + +/* + * done - retire a request; caller blocked irqs + * INDEX register is preserved to keep same + */ +static void done(struct jz4740_ep *ep, struct jz4740_request *req, int status) +{ + unsigned int stopped = ep->stopped; + u32 index; + + DEBUG("%s, %p\n", __FUNCTION__, ep); + list_del_init(&req->queue); + + if (likely(req->req.status == -EINPROGRESS)) + req->req.status = status; + else + status = req->req.status; + + if (status && status != -ESHUTDOWN) + DEBUG("complete %s req %p stat %d len %u/%u\n", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + + /* don't modify queue heads during completion callback */ + ep->stopped = 1; + /* Read current index (completion may modify it) */ + index = usb_readb(USB_REG_INDEX); + + spin_unlock(&ep->dev->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&ep->dev->lock); + + /* Restore index */ + usb_set_index(index); + ep->stopped = stopped; +} + +/** Enable EP interrupt */ +static void pio_irq_enable(struct jz4740_ep *ep) +{ + DEBUG("%s: EP%d %s\n", __FUNCTION__, ep_index(ep), ep_is_in(ep) ? "IN": "OUT"); + + if (ep_is_in(ep)) { + switch (ep_index(ep)) { + case 1: + usb_setw(USB_REG_INTRINE, USB_INTR_INEP1); + break; + case 2: + usb_setw(USB_REG_INTRINE, USB_INTR_INEP2); + break; + default: + DEBUG("Unknown endpoint: %d\n", ep_index(ep)); + break; + } + } + else { + switch (ep_index(ep)) { + case 1: + usb_setw(USB_REG_INTROUTE, USB_INTR_OUTEP1); + break; + default: + DEBUG("Unknown endpoint: %d\n", ep_index(ep)); + break; + } + } +} + +/** Disable EP interrupt */ +static void pio_irq_disable(struct jz4740_ep *ep) +{ + DEBUG("%s: EP%d %s\n", __FUNCTION__, ep_index(ep), ep_is_in(ep) ? "IN": "OUT"); + + if (ep_is_in(ep)) { + switch (ep_index(ep)) { + case 1: + usb_clearw(USB_REG_INTRINE, USB_INTR_INEP1); + break; + case 2: + usb_clearw(USB_REG_INTRINE, USB_INTR_INEP2); + break; + default: + DEBUG("Unknown endpoint: %d\n", ep_index(ep)); + break; + } + } + else { + switch (ep_index(ep)) { + case 1: + usb_clearw(USB_REG_INTROUTE, USB_INTR_OUTEP1); + break; + default: + DEBUG("Unknown endpoint: %d\n", ep_index(ep)); + break; + } + } +} + +/* + * nuke - dequeue ALL requests + */ +static void nuke(struct jz4740_ep *ep, int status) +{ + struct jz4740_request *req; + + DEBUG("%s, %p\n", __FUNCTION__, ep); + + /* Flush FIFO */ + flush(ep); + + /* called with irqs blocked */ + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct jz4740_request, queue); + done(ep, req, status); + } + + /* Disable IRQ if EP is enabled (has descriptor) */ + if (ep->desc) + pio_irq_disable(ep); +} + +/** Flush EP FIFO + * NOTE: INDEX register must be set before this call + */ +static void flush(struct jz4740_ep *ep) +{ + DEBUG("%s, %p\n", __FUNCTION__, ep); + + switch (ep->ep_type) { + case ep_control: + break; + + case ep_bulk_in: + case ep_interrupt: + usb_setb(ep->csr, USB_INCSR_FF); + break; + + case ep_bulk_out: + usb_setb(ep->csr, USB_OUTCSR_FF); + break; + } +} + +/** + * jz4740_in_epn - handle IN interrupt + */ +static void jz4740_in_epn(struct jz4740_udc *dev, u32 ep_idx, u32 intr) +{ + u32 csr; + struct jz4740_ep *ep = &dev->ep[ep_idx + 1]; + struct jz4740_request *req; + + usb_set_index(ep_index(ep)); + + csr = usb_readb(ep->csr); + DEBUG("%s: %d, csr %x\n", __FUNCTION__, ep_idx, csr); + + if (csr & USB_INCSR_SENTSTALL) { + DEBUG("USB_INCSR_SENTSTALL\n"); + usb_clearb(ep->csr, USB_INCSR_SENTSTALL); + return; + } + + if (!ep->desc) { + DEBUG("%s: NO EP DESC\n", __FUNCTION__); + return; + } + + if (list_empty(&ep->queue)) + req = 0; + else + req = list_entry(ep->queue.next, struct jz4740_request, queue); + + DEBUG("req: %p\n", req); + + if (!req) + return; + + write_fifo(ep, req); +} + +/* + * Bulk OUT (recv) + */ +static void jz4740_out_epn(struct jz4740_udc *dev, u32 ep_idx, u32 intr) +{ + struct jz4740_ep *ep = &dev->ep[ep_idx]; + struct jz4740_request *req; + + DEBUG("%s: %d\n", __FUNCTION__, ep_idx); + + usb_set_index(ep_index(ep)); + if (ep->desc) { + u32 csr; + + if (use_dma) { + /* DMA starts here ... */ + if (list_empty(&ep->queue)) + req = 0; + else + req = list_entry(ep->queue.next, struct jz4740_request, queue); + + if (req) + read_fifo(ep, req); + return; + } + + /* + * PIO mode starts here ... + */ + + while ((csr = usb_readb(ep->csr)) & + (USB_OUTCSR_OUTPKTRDY | USB_OUTCSR_SENTSTALL)) { + DEBUG("%s: %x\n", __FUNCTION__, csr); + + if (csr & USB_OUTCSR_SENTSTALL) { + DEBUG("%s: stall sent, flush fifo\n", + __FUNCTION__); + /* usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1); */ + flush(ep); + } else if (csr & USB_OUTCSR_OUTPKTRDY) { + if (list_empty(&ep->queue)) + req = 0; + else + req = + list_entry(ep->queue.next, + struct jz4740_request, + queue); + + if (!req) { + DEBUG("%s: NULL REQ %d\n", + __FUNCTION__, ep_idx); + break; + } else { + read_fifo(ep, req); + } + } + } + } else { + /* Throw packet away.. */ + printk("%s: ep %p ep_indx %d No descriptor?!?\n", __FUNCTION__, ep, ep_idx); + flush(ep); + } +} + +static int jz4740_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct jz4740_ep *ep; + struct jz4740_udc *dev; + unsigned long flags; + u32 max, csrh = 0; + + ep = container_of(_ep, struct jz4740_ep, ep); + if (!_ep || !desc || ep->desc || _ep->name == ep0name + || desc->bDescriptorType != USB_DT_ENDPOINT + || ep->bEndpointAddress != desc->bEndpointAddress) { + DEBUG("%s, bad ep or descriptor\n", __FUNCTION__); + return -EINVAL; + } + + /* xfer types must match, except that interrupt ~= bulk */ + if (ep->bmAttributes != desc->bmAttributes + && ep->bmAttributes != USB_ENDPOINT_XFER_BULK + && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { + DEBUG("%s, %s type mismatch\n", __FUNCTION__, _ep->name); + return -EINVAL; + } + + dev = ep->dev; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { + DEBUG("%s, bogus device state\n", __FUNCTION__); + return -ESHUTDOWN; + } + + max = le16_to_cpu(desc->wMaxPacketSize); + + /* Configure the endpoint */ + usb_set_index(desc->bEndpointAddress & 0x0F); + if (ep_is_in(ep)) { + usb_writew(USB_REG_INMAXP, max); + switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + csrh &= ~USB_INCSRH_ISO; + break; + case USB_ENDPOINT_XFER_ISOC: + csrh |= USB_INCSRH_ISO; + break; + } + usb_writeb(USB_REG_INCSRH, csrh); + } + else { + usb_writew(USB_REG_OUTMAXP, max); + switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_BULK: + csrh &= ~USB_OUTCSRH_ISO; + break; + case USB_ENDPOINT_XFER_INT: + csrh &= ~USB_OUTCSRH_ISO; + csrh |= USB_OUTCSRH_DNYT; + break; + case USB_ENDPOINT_XFER_ISOC: + csrh |= USB_OUTCSRH_ISO; + break; + } + usb_writeb(USB_REG_OUTCSRH, csrh); + } + + spin_lock_irqsave(&ep->dev->lock, flags); + + ep->stopped = 0; + ep->desc = desc; + ep->pio_irqs = 0; + ep->ep.maxpacket = max; + + spin_unlock_irqrestore(&ep->dev->lock, flags); + + /* Reset halt state (does flush) */ + jz4740_set_halt(_ep, 0); + + DEBUG("%s: enabled %s\n", __FUNCTION__, _ep->name); + + return 0; +} + +/** Disable EP + * NOTE: Sets INDEX register + */ +static int jz4740_ep_disable(struct usb_ep *_ep) +{ + struct jz4740_ep *ep; + unsigned long flags; + + DEBUG("%s, %p\n", __FUNCTION__, _ep); + + ep = container_of(_ep, struct jz4740_ep, ep); + if (!_ep || !ep->desc) { + DEBUG("%s, %s not enabled\n", __FUNCTION__, + _ep ? ep->ep.name : NULL); + return -EINVAL; + } + + spin_lock_irqsave(&ep->dev->lock, flags); + + usb_set_index(ep_index(ep)); + + /* Nuke all pending requests (does flush) */ + nuke(ep, -ESHUTDOWN); + + /* Disable ep IRQ */ + pio_irq_disable(ep); + + ep->desc = 0; + ep->stopped = 1; + + spin_unlock_irqrestore(&ep->dev->lock, flags); + + DEBUG("%s: disabled %s\n", __FUNCTION__, _ep->name); + return 0; +} + +static struct usb_request *jz4740_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) +{ + struct jz4740_request *req; + + DEBUG("%s, %p\n", __FUNCTION__, ep); + + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) + return 0; + + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void jz4740_free_request(struct usb_ep *ep, struct usb_request *_req) +{ + struct jz4740_request *req; + + DEBUG("%s, %p\n", __FUNCTION__, ep); + + req = container_of(_req, struct jz4740_request, req); + WARN_ON(!list_empty(&req->queue)); + kfree(req); +} + +/*--------------------------------------------------------------------*/ + +/** Queue one request + * Kickstart transfer if needed + * NOTE: Sets INDEX register + */ +static int jz4740_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct jz4740_request *req; + struct jz4740_ep *ep; + struct jz4740_udc *dev; + unsigned long flags; + + DEBUG("%s, %p\n", __FUNCTION__, _ep); + + req = container_of(_req, struct jz4740_request, req); + if (unlikely + (!_req || !_req->complete || !_req->buf + || !list_empty(&req->queue))) { + DEBUG("%s, bad params\n", __FUNCTION__); + return -EINVAL; + } + + ep = container_of(_ep, struct jz4740_ep, ep); + if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { + DEBUG("%s, bad ep\n", __FUNCTION__); + return -EINVAL; + } + + dev = ep->dev; + if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) { + DEBUG("%s, bogus device state %p\n", __FUNCTION__, dev->driver); + return -ESHUTDOWN; + } + + DEBUG("%s queue req %p, len %d buf %p\n", _ep->name, _req, _req->length, + _req->buf); + + spin_lock_irqsave(&dev->lock, flags); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + /* kickstart this i/o queue? */ + DEBUG("Add to %d Q %d %d\n", ep_index(ep), list_empty(&ep->queue), + ep->stopped); + if (list_empty(&ep->queue) && likely(!ep->stopped)) { + u32 csr; + + if (unlikely(ep_index(ep) == 0)) { + /* EP0 */ + list_add_tail(&req->queue, &ep->queue); + jz4740_ep0_kick(dev, ep); + req = 0; + } else if (use_dma) { + /* DMA */ + kick_dma(ep, req); + } + /* PIO */ + else if (ep_is_in(ep)) { + /* EP1 & EP2 */ + usb_set_index(ep_index(ep)); + csr = usb_readb(ep->csr); + pio_irq_enable(ep); + if (!(csr & USB_INCSR_FFNOTEMPT)) { + if (write_fifo(ep, req) == 1) + req = 0; + } + } else { + /* EP1 */ + usb_set_index(ep_index(ep)); + csr = usb_readb(ep->csr); + pio_irq_enable(ep); + if (csr & USB_OUTCSR_OUTPKTRDY) { + if (read_fifo(ep, req) == 1) + req = 0; + } + } + } + + /* pio or dma irq handler advances the queue. */ + if (likely(req != 0)) + list_add_tail(&req->queue, &ep->queue); + + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +/* dequeue JUST ONE request */ +static int jz4740_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct jz4740_ep *ep; + struct jz4740_request *req; + unsigned long flags; + + DEBUG("%s, %p\n", __FUNCTION__, _ep); + + ep = container_of(_ep, struct jz4740_ep, ep); + if (!_ep || ep->ep.name == ep0name) + return -EINVAL; + + spin_lock_irqsave(&ep->dev->lock, flags); + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + spin_unlock_irqrestore(&ep->dev->lock, flags); + return -EINVAL; + } + + done(ep, req, -ECONNRESET); + + spin_unlock_irqrestore(&ep->dev->lock, flags); + return 0; +} + +/** Halt specific EP + * Return 0 if success + * NOTE: Sets INDEX register to EP ! + */ +static int jz4740_set_halt(struct usb_ep *_ep, int value) +{ + struct jz4740_ep *ep; + unsigned long flags; + + ep = container_of(_ep, struct jz4740_ep, ep); + if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { + DEBUG("%s, bad ep\n", __FUNCTION__); + return -EINVAL; + } + + usb_set_index(ep_index(ep)); + + DEBUG("%s, ep %d, val %d\n", __FUNCTION__, ep_index(ep), value); + + spin_lock_irqsave(&ep->dev->lock, flags); + + if (ep_index(ep) == 0) { + /* EP0 */ + usb_setb(USB_REG_CSR0, USB_CSR0_SENDSTALL); + } else if (ep_is_in(ep)) { + u32 csr = usb_readb(ep->csr); + if (value && ((csr & USB_INCSR_FFNOTEMPT) + || !list_empty(&ep->queue))) { + /* + * Attempts to halt IN endpoints will fail (returning -EAGAIN) + * if any transfer requests are still queued, or if the controller + * FIFO still holds bytes that the host hasnÂ’t collected. + */ + spin_unlock_irqrestore(&ep->dev->lock, flags); + DEBUG + ("Attempt to halt IN endpoint failed (returning -EAGAIN) %d %d\n", + (csr & USB_INCSR_FFNOTEMPT), + !list_empty(&ep->queue)); + return -EAGAIN; + } + flush(ep); + if (value) { + usb_setb(ep->csr, USB_INCSR_SENDSTALL); + } + else { + usb_clearb(ep->csr, USB_INCSR_SENDSTALL); + usb_setb(ep->csr, USB_INCSR_CDT); + } + } else { + + flush(ep); + if (value) { + usb_setb(ep->csr, USB_OUTCSR_SENDSTALL); + } + else { + usb_clearb(ep->csr, USB_OUTCSR_SENDSTALL); + usb_setb(ep->csr, USB_OUTCSR_CDT); + } + } + + if (value) { + ep->stopped = 1; + } else { + ep->stopped = 0; + } + + spin_unlock_irqrestore(&ep->dev->lock, flags); + + DEBUG("%s %s halted\n", _ep->name, value == 0 ? "NOT" : "IS"); + + return 0; +} + +/** Return bytes in EP FIFO + * NOTE: Sets INDEX register to EP + */ +static int jz4740_fifo_status(struct usb_ep *_ep) +{ + u32 csr; + int count = 0; + struct jz4740_ep *ep; + + ep = container_of(_ep, struct jz4740_ep, ep); + if (!_ep) { + DEBUG("%s, bad ep\n", __FUNCTION__); + return -ENODEV; + } + + DEBUG("%s, %d\n", __FUNCTION__, ep_index(ep)); + + /* LPD can't report unclaimed bytes from IN fifos */ + if (ep_is_in(ep)) + return -EOPNOTSUPP; + + usb_set_index(ep_index(ep)); + + csr = usb_readb(ep->csr); + if (ep->dev->gadget.speed != USB_SPEED_UNKNOWN || + csr & 0x1) { + count = usb_readw(USB_REG_OUTCOUNT); + } + + return count; +} + +/** Flush EP FIFO + * NOTE: Sets INDEX register to EP + */ +static void jz4740_fifo_flush(struct usb_ep *_ep) +{ + struct jz4740_ep *ep; + + ep = container_of(_ep, struct jz4740_ep, ep); + if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { + DEBUG("%s, bad ep\n", __FUNCTION__); + return; + } + + usb_set_index(ep_index(ep)); + flush(ep); +} + +/****************************************************************/ +/* End Point 0 related functions */ +/****************************************************************/ + +/* return: 0 = still running, 1 = completed, negative = errno */ +static int write_fifo_ep0(struct jz4740_ep *ep, struct jz4740_request *req) +{ + u32 max; + unsigned count; + int is_last; + + max = ep_maxpacket(ep); + + count = write_packet(ep, req, max); + + /* last packet is usually short (or a zlp) */ + if (unlikely(count != max)) + is_last = 1; + else { + if (likely(req->req.length != req->req.actual) || req->req.zero) + is_last = 0; + else + is_last = 1; + } + + DEBUG_EP0("%s: wrote %s %d bytes%s %d left %p\n", __FUNCTION__, + ep->ep.name, count, + is_last ? "/L" : "", req->req.length - req->req.actual, req); + + /* requests complete when all IN data is in the FIFO */ + if (is_last) { + done(ep, req, 0); + return 1; + } + + return 0; +} + +static __inline__ int jz4740_fifo_read(struct jz4740_ep *ep, + unsigned char *cp, int max) +{ + int bytes; + int count = usb_readw(USB_REG_OUTCOUNT); + volatile u8 *fifo = (volatile u8 *)ep->fifo; + + if (count > max) + count = max; + bytes = count; + while (count--) + *cp++ = *fifo; + return bytes; +} + +static __inline__ void jz4740_fifo_write(struct jz4740_ep *ep, + unsigned char *cp, int count) +{ + volatile u8 *fifo = (volatile u8 *)ep->fifo; + DEBUG_EP0("fifo_write: %d %d\n", ep_index(ep), count); + while (count--) + *fifo = *cp++; +} + +static int read_fifo_ep0(struct jz4740_ep *ep, struct jz4740_request *req) +{ + u32 csr; + u8 *buf; + unsigned bufferspace, count, is_short; + volatile u8 *fifo = (volatile u8 *)ep->fifo; + + DEBUG_EP0("%s\n", __FUNCTION__); + + csr = usb_readb(USB_REG_CSR0); + if (!(csr & USB_CSR0_OUTPKTRDY)) + return 0; + + buf = req->req.buf + req->req.actual; + prefetchw(buf); + bufferspace = req->req.length - req->req.actual; + + /* read all bytes from this packet */ + if (likely(csr & USB_CSR0_OUTPKTRDY)) { + count = usb_readw(USB_REG_OUTCOUNT); + req->req.actual += min(count, bufferspace); + } else /* zlp */ + count = 0; + + is_short = (count < ep->ep.maxpacket); + DEBUG_EP0("read %s %02x, %d bytes%s req %p %d/%d\n", + ep->ep.name, csr, count, + is_short ? "/S" : "", req, req->req.actual, req->req.length); + + while (likely(count-- != 0)) { + u8 byte = (u8) (*fifo & 0xff); + + if (unlikely(bufferspace == 0)) { + /* this happens when the driver's buffer + * is smaller than what the host sent. + * discard the extra data. + */ + if (req->req.status != -EOVERFLOW) + DEBUG_EP0("%s overflow %d\n", ep->ep.name, + count); + req->req.status = -EOVERFLOW; + } else { + *buf++ = byte; + bufferspace--; + } + } + + /* completion */ + if (is_short || req->req.actual == req->req.length) { + done(ep, req, 0); + return 1; + } + + /* finished that packet. the next one may be waiting... */ + return 0; +} + +/** + * udc_set_address - set the USB address for this device + * @address: + * + * Called from control endpoint function after it decodes a set address setup packet. + */ +static void udc_set_address(struct jz4740_udc *dev, unsigned char address) +{ + DEBUG_EP0("%s: %d\n", __FUNCTION__, address); + + dev->usb_address = address; + usb_writeb(USB_REG_FADDR, address); +} + +/* + * DATA_STATE_RECV (USB_CSR0_OUTPKTRDY) + * - if error + * set USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND | USB_CSR0_SENDSTALL bits + * - else + * set USB_CSR0_SVDOUTPKTRDY bit + if last set USB_CSR0_DATAEND bit + */ +static void jz4740_ep0_out(struct jz4740_udc *dev, u32 csr) +{ + struct jz4740_request *req; + struct jz4740_ep *ep = &dev->ep[0]; + int ret; + + DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); + + if (list_empty(&ep->queue)) + req = 0; + else + req = list_entry(ep->queue.next, struct jz4740_request, queue); + + if (req) { + if (req->req.length == 0) { + DEBUG_EP0("ZERO LENGTH OUT!\n"); +/* usb_setb(USB_REG_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND)); */ + usb_setb(USB_REG_CSR0, (USB_CSR0_SVDOUTPKTRDY)); + dev->ep0state = WAIT_FOR_SETUP; + return; + } + ret = read_fifo_ep0(ep, req); + if (ret) { + /* Done! */ + DEBUG_EP0("%s: finished, waiting for status\n", + __FUNCTION__); + usb_setb(USB_REG_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND)); + dev->ep0state = WAIT_FOR_SETUP; + } else { + /* Not done yet.. */ + DEBUG_EP0("%s: not finished\n", __FUNCTION__); + usb_setb(USB_REG_CSR0, USB_CSR0_SVDOUTPKTRDY); + } + } else { + DEBUG_EP0("NO REQ??!\n"); + } +} + +/* + * DATA_STATE_XMIT + */ +static int jz4740_ep0_in(struct jz4740_udc *dev, u32 csr) +{ + struct jz4740_request *req; + struct jz4740_ep *ep = &dev->ep[0]; + int ret, need_zlp = 0; + + DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); + + if (list_empty(&ep->queue)) + req = 0; + else + req = list_entry(ep->queue.next, struct jz4740_request, queue); + + if (!req) { + DEBUG_EP0("%s: NULL REQ\n", __FUNCTION__); + return 0; + } + + if (req->req.length == 0) { + usb_setb(USB_REG_CSR0, (USB_CSR0_INPKTRDY | USB_CSR0_DATAEND)); + dev->ep0state = WAIT_FOR_SETUP; + return 1; + } + + if (req->req.length - req->req.actual == EP0_MAXPACKETSIZE) { + /* Next write will end with the packet size, */ + /* so we need zero-length-packet */ + need_zlp = 1; + } + + ret = write_fifo_ep0(ep, req); + + if (ret == 1 && !need_zlp) { + /* Last packet */ + DEBUG_EP0("%s: finished, waiting for status\n", __FUNCTION__); + + usb_setb(USB_REG_CSR0, (USB_CSR0_INPKTRDY | USB_CSR0_DATAEND)); + dev->ep0state = WAIT_FOR_SETUP; + } else { + DEBUG_EP0("%s: not finished\n", __FUNCTION__); + usb_setb(USB_REG_CSR0, USB_CSR0_INPKTRDY); + } + + if (need_zlp) { + DEBUG_EP0("%s: Need ZLP!\n", __FUNCTION__); + usb_setb(USB_REG_CSR0, USB_CSR0_INPKTRDY); + dev->ep0state = DATA_STATE_NEED_ZLP; + } + + return 1; +} + +#if 0 +static int jz4740_handle_get_status(struct jz4740_udc *dev, + struct usb_ctrlrequest *ctrl) +{ + struct jz4740_ep *ep0 = &dev->ep[0]; + struct jz4740_ep *qep; + int reqtype = (ctrl->bRequestType & USB_RECIP_MASK); + u16 val = 0; + + if (reqtype == USB_RECIP_INTERFACE) { + /* This is not supported. + * And according to the USB spec, this one does nothing.. + * Just return 0 + */ + DEBUG_SETUP("GET_STATUS: USB_RECIP_INTERFACE\n"); + } else if (reqtype == USB_RECIP_DEVICE) { + DEBUG_SETUP("GET_STATUS: USB_RECIP_DEVICE\n"); + val |= (1 << 0); /* Self powered */ + /*val |= (1<<1); *//* Remote wakeup */ + } else if (reqtype == USB_RECIP_ENDPOINT) { + int ep_num = (ctrl->wIndex & ~USB_DIR_IN); + + DEBUG_SETUP + ("GET_STATUS: USB_RECIP_ENDPOINT (%d), ctrl->wLength = %d\n", + ep_num, ctrl->wLength); + + if (ctrl->wLength > 2 || ep_num > 3) + return -EOPNOTSUPP; + + qep = &dev->ep[ep_num]; + if (ep_is_in(qep) != ((ctrl->wIndex & USB_DIR_IN) ? 1 : 0) + && ep_index(qep) != 0) { + return -EOPNOTSUPP; + } + + usb_set_index(ep_index(qep)); + + /* Return status on next IN token */ + switch (qep->ep_type) { + case ep_control: + val = + (usb_readb(qep->csr) & USB_CSR0_SENDSTALL) == + USB_CSR0_SENDSTALL; + break; + case ep_bulk_in: + case ep_interrupt: + val = + (usb_readb(qep->csr) & USB_INCSR_SENDSTALL) == + USB_INCSR_SENDSTALL; + break; + case ep_bulk_out: + val = + (usb_readb(qep->csr) & USB_OUTCSR_SENDSTALL) == + USB_OUTCSR_SENDSTALL; + break; + } + + /* Back to EP0 index */ + usb_set_index(0); + + DEBUG_SETUP("GET_STATUS, ep: %d (%x), val = %d\n", ep_num, + ctrl->wIndex, val); + } else { + DEBUG_SETUP("Unknown REQ TYPE: %d\n", reqtype); + return -EOPNOTSUPP; + } + + /* Clear "out packet ready" */ + usb_setb(USB_REG_CSR0, USB_CSR0_SVDOUTPKTRDY); + /* Put status to FIFO */ + jz4740_fifo_write(ep0, (u8 *) & val, sizeof(val)); + /* Issue "In packet ready" */ + usb_setb(USB_REG_CSR0, (USB_CSR0_INPKTRDY | USB_CSR0_DATAEND)); + + return 0; +} +#endif + +/* + * WAIT_FOR_SETUP (OUTPKTRDY) + * - read data packet from EP0 FIFO + * - decode command + * - if error + * set USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND | USB_CSR0_SENDSTALL bits + * - else + * set USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND bits + */ +static void jz4740_ep0_setup(struct jz4740_udc *dev, u32 csr) +{ + struct jz4740_ep *ep = &dev->ep[0]; + struct usb_ctrlrequest ctrl; + int i; + + DEBUG_SETUP("%s: %x\n", __FUNCTION__, csr); + + /* Nuke all previous transfers */ + nuke(ep, -EPROTO); + + /* read control req from fifo (8 bytes) */ + jz4740_fifo_read(ep, (unsigned char *)&ctrl, 8); + + DEBUG_SETUP("SETUP %02x.%02x v%04x i%04x l%04x\n", + ctrl.bRequestType, ctrl.bRequest, + ctrl.wValue, ctrl.wIndex, ctrl.wLength); + + /* Set direction of EP0 */ + if (likely(ctrl.bRequestType & USB_DIR_IN)) { + ep->bEndpointAddress |= USB_DIR_IN; + } else { + ep->bEndpointAddress &= ~USB_DIR_IN; + } + + /* Handle some SETUP packets ourselves */ + switch (ctrl.bRequest) { + case USB_REQ_SET_ADDRESS: + if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) + break; + + DEBUG_SETUP("USB_REQ_SET_ADDRESS (%d)\n", ctrl.wValue); + udc_set_address(dev, ctrl.wValue); + usb_setb(USB_REG_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND)); + return; + + case USB_REQ_SET_CONFIGURATION: + if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) + break; + + DEBUG_SETUP("USB_REQ_SET_CONFIGURATION (%d)\n", ctrl.wValue); + usb_setb(USB_REG_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND)); + + /* Enable RESUME and SUSPEND interrupts */ + usb_setb(USB_REG_INTRUSBE, (USB_INTR_RESUME | USB_INTR_SUSPEND)); + break; + + case USB_REQ_SET_INTERFACE: + if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) + break; + + DEBUG_SETUP("USB_REQ_SET_INTERFACE (%d)\n", ctrl.wValue); + usb_setb(USB_REG_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND)); + break; + +// case USB_REQ_GET_STATUS: +// if (jz4740_handle_get_status(dev, &ctrl) == 0) +// return; + + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + if (ctrl.bRequestType == USB_RECIP_ENDPOINT) { + struct jz4740_ep *qep; + int ep_num = (ctrl.wIndex & 0x0f); + + /* Support only HALT feature */ + if (ctrl.wValue != 0 || ctrl.wLength != 0 + || ep_num > 3 || ep_num < 1) + break; + + qep = &dev->ep[ep_num]; + spin_unlock(&dev->lock); + if (ctrl.bRequest == USB_REQ_SET_FEATURE) { + DEBUG_SETUP("SET_FEATURE (%d)\n", + ep_num); + jz4740_set_halt(&qep->ep, 1); + } else { + DEBUG_SETUP("CLR_FEATURE (%d)\n", + ep_num); + jz4740_set_halt(&qep->ep, 0); + } + spin_lock(&dev->lock); + + usb_set_index(0); + + /* Reply with a ZLP on next IN token */ + usb_setb(USB_REG_CSR0, + (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND)); + return; + } + break; + + default: + break; + } + + /* gadget drivers see class/vendor specific requests, + * {SET,GET}_{INTERFACE,DESCRIPTOR,CONFIGURATION}, + * and more. + */ + if (likely((u32)dev->driver)) { + /* device-2-host (IN) or no data setup command, process immediately */ + spin_unlock(&dev->lock); + + i = dev->driver->setup(&dev->gadget, &ctrl); + spin_lock(&dev->lock); + + if (unlikely(i < 0)) { + /* setup processing failed, force stall */ + DEBUG_SETUP + (" --> ERROR: gadget setup FAILED (stalling), setup returned %d\n", + i); + usb_set_index(0); + usb_setb(USB_REG_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND | USB_CSR0_SENDSTALL)); + + /* ep->stopped = 1; */ + dev->ep0state = WAIT_FOR_SETUP; + } + else { + DEBUG_SETUP("gadget driver setup ok (%d)\n", ctrl.wLength); + if (!ctrl.wLength) { + usb_setb(USB_REG_CSR0, USB_CSR0_SVDOUTPKTRDY); + } + } + } +} + +/* + * DATA_STATE_NEED_ZLP + */ +static void jz4740_ep0_in_zlp(struct jz4740_udc *dev, u32 csr) +{ + DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); + + usb_setb(USB_REG_CSR0, (USB_CSR0_INPKTRDY | USB_CSR0_DATAEND)); + dev->ep0state = WAIT_FOR_SETUP; +} + +/* + * handle ep0 interrupt + */ +static void jz4740_handle_ep0(struct jz4740_udc *dev, u32 intr) +{ + struct jz4740_ep *ep = &dev->ep[0]; + u32 csr; + + /* Set index 0 */ + usb_set_index(0); + csr = usb_readb(USB_REG_CSR0); + + DEBUG_EP0("%s: csr = %x state = \n", __FUNCTION__, csr);//, state_names[dev->ep0state]); + + /* + * if SENT_STALL is set + * - clear the SENT_STALL bit + */ + if (csr & USB_CSR0_SENTSTALL) { + DEBUG_EP0("%s: USB_CSR0_SENTSTALL is set: %x\n", __FUNCTION__, csr); + usb_clearb(USB_REG_CSR0, USB_CSR0_SENDSTALL | USB_CSR0_SENTSTALL); + nuke(ep, -ECONNABORTED); + dev->ep0state = WAIT_FOR_SETUP; + return; + } + + /* + * if a transfer is in progress && INPKTRDY and OUTPKTRDY are clear + * - fill EP0 FIFO + * - if last packet + * - set IN_PKT_RDY | DATA_END + * - else + * set IN_PKT_RDY + */ + if (!(csr & (USB_CSR0_INPKTRDY | USB_CSR0_OUTPKTRDY))) { + DEBUG_EP0("%s: INPKTRDY and OUTPKTRDY are clear\n", + __FUNCTION__); + + switch (dev->ep0state) { + case DATA_STATE_XMIT: + DEBUG_EP0("continue with DATA_STATE_XMIT\n"); + jz4740_ep0_in(dev, csr); + return; + case DATA_STATE_NEED_ZLP: + DEBUG_EP0("continue with DATA_STATE_NEED_ZLP\n"); + jz4740_ep0_in_zlp(dev, csr); + return; + default: + /* Stall? */ +// DEBUG_EP0("Odd state!! state = %s\n", +// state_names[dev->ep0state]); + dev->ep0state = WAIT_FOR_SETUP; + /* nuke(ep, 0); */ + /* usb_setb(ep->csr, USB_CSR0_SENDSTALL); */ +// break; + return; + } + } + + /* + * if SETUPEND is set + * - abort the last transfer + * - set SERVICED_SETUP_END_BIT + */ + if (csr & USB_CSR0_SETUPEND) { + DEBUG_EP0("%s: USB_CSR0_SETUPEND is set: %x\n", __FUNCTION__, csr); + + usb_setb(USB_REG_CSR0, USB_CSR0_SVDSETUPEND); + nuke(ep, 0); + dev->ep0state = WAIT_FOR_SETUP; + } + + /* + * if USB_CSR0_OUTPKTRDY is set + * - read data packet from EP0 FIFO + * - decode command + * - if error + * set SVDOUTPKTRDY | DATAEND | SENDSTALL bits + * - else + * set SVDOUTPKTRDY | DATAEND bits + */ + if (csr & USB_CSR0_OUTPKTRDY) { + + DEBUG_EP0("%s: EP0_OUT_PKT_RDY is set: %x\n", __FUNCTION__, + csr); + + switch (dev->ep0state) { + case WAIT_FOR_SETUP: + DEBUG_EP0("WAIT_FOR_SETUP\n"); + jz4740_ep0_setup(dev, csr); + break; + + case DATA_STATE_RECV: + DEBUG_EP0("DATA_STATE_RECV\n"); + jz4740_ep0_out(dev, csr); + break; + + default: + /* send stall? */ + DEBUG_EP0("strange state!! 2. send stall? state = %d\n", + dev->ep0state); + break; + } + } +} + +static void jz4740_ep0_kick(struct jz4740_udc *dev, struct jz4740_ep *ep) +{ + u32 csr; + + usb_set_index(0); + csr = usb_readb(USB_REG_CSR0); + + DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); + + /* Clear "out packet ready" */ + usb_setb(USB_REG_CSR0, USB_CSR0_SVDOUTPKTRDY); + + if (ep_is_in(ep)) { + dev->ep0state = DATA_STATE_XMIT; + jz4740_ep0_in(dev, csr); + } else { + dev->ep0state = DATA_STATE_RECV; + jz4740_ep0_out(dev, csr); + } +} + +/** Handle USB RESET interrupt + */ +static void jz4740_reset_irq(struct jz4740_udc *dev) +{ + dev->gadget.speed = (usb_readb(USB_REG_POWER) & USB_POWER_HSMODE) ? + USB_SPEED_HIGH : USB_SPEED_FULL; + + DEBUG_SETUP("%s: address = %d, speed = %s\n", __FUNCTION__, dev->usb_address, + (dev->gadget.speed == USB_SPEED_HIGH) ? "HIGH":"FULL" ); +} + +/* + * jz4740 usb device interrupt handler. + */ +static irqreturn_t jz4740_udc_irq(int irq, void *_dev) +{ + struct jz4740_udc *dev = _dev; + + u32 intr_usb = usb_readb(USB_REG_INTRUSB) & 0x7; /* mask SOF */ + u32 intr_in = usb_readw(USB_REG_INTRIN); + u32 intr_out = usb_readw(USB_REG_INTROUT); + u32 intr_dma = usb_readb(USB_REG_INTR); + + if (!intr_usb && !intr_in && !intr_out && !intr_dma) + return IRQ_HANDLED; + + DEBUG("intr_out = %x intr_in=%x intr_usb=%x\n", + intr_out, intr_in, intr_usb); + + spin_lock(&dev->lock); + + /* Check for resume from suspend mode */ + if ((intr_usb & USB_INTR_RESUME) && + (usb_readb(USB_REG_INTRUSBE) & USB_INTR_RESUME)) { + DEBUG("USB resume\n"); + } + + /* Check for system interrupts */ + if (intr_usb & USB_INTR_RESET) { + DEBUG("USB reset\n"); +#ifdef CONFIG_JZ_UDC_HOTPLUG + jz_udc_active = 1; +#endif + if (udc_debug) { + /* We have tested the cable type, disable module and + * disconnect from host right now. + */ + udc_disable(dev); + spin_unlock(&dev->lock); + return IRQ_HANDLED; + } + jz4740_reset_irq(dev); + } + + /* Check for endpoint 0 interrupt */ + if (intr_in & USB_INTR_EP0) { + DEBUG("USB_INTR_EP0 (control)\n"); + jz4740_handle_ep0(dev, intr_in); + } + + /* Check for Bulk-IN DMA interrupt */ + if (intr_dma & 0x1) { + int ep_num; + ep_num = (usb_readl(USB_REG_CNTL1) >> 4) & 0xf; + jz4740_in_epn(dev, ep_num, intr_in); + } + + /* Check for Bulk-OUT DMA interrupt */ + if (intr_dma & 0x2) { + int ep_num; + ep_num = (usb_readl(USB_REG_CNTL2) >> 4) & 0xf; + jz4740_out_epn(dev, ep_num, intr_out); + } + + /* Check for each configured endpoint interrupt */ + if (intr_in & USB_INTR_INEP1) { + DEBUG("USB_INTR_INEP1\n"); + jz4740_in_epn(dev, 1, intr_in); + } + + if (intr_in & USB_INTR_INEP2) { + DEBUG("USB_INTR_INEP2\n"); + jz4740_in_epn(dev, 2, intr_in); + } + + if (intr_out & USB_INTR_OUTEP1) { + DEBUG("USB_INTR_OUTEP1\n"); + jz4740_out_epn(dev, 1, intr_out); + } + + /* Check for suspend mode */ + if ((intr_usb & USB_INTR_SUSPEND) && + (usb_readb(USB_REG_INTRUSBE) & USB_INTR_SUSPEND)) { + DEBUG("USB suspend\n"); + dev->driver->suspend(&dev->gadget); + /* Host unloaded from us, can do something, such as flushing + the NAND block cache etc. */ + } + +#ifdef CONFIG_JZ_UDC_HOTPLUG + jz_udc_active = 1; +#endif + + spin_unlock(&dev->lock); + return IRQ_HANDLED; +} + +/*-------------------------------------------------------------------------*/ + +static int jz4740_udc_get_frame(struct usb_gadget *_gadget) +{ + DEBUG("%s, %p\n", __FUNCTION__, _gadget); + return usb_readw(USB_REG_FRAME); +} + +static int jz4740_udc_wakeup(struct usb_gadget *_gadget) +{ + /* host may not have enabled remote wakeup */ + /*if ((UDCCS0 & UDCCS0_DRWF) == 0) + return -EHOSTUNREACH; + udc_set_mask_UDCCR(UDCCR_RSM); */ + return -ENOTSUPP; +} + +static const struct usb_gadget_ops jz4740_udc_ops = { + .get_frame = jz4740_udc_get_frame, + .wakeup = jz4740_udc_wakeup, + /* current versions must always be self-powered */ +}; + +/*-------------------------------------------------------------------------*/ + +static struct jz4740_udc udc_dev = { + .usb_address = 0, + + .gadget = { + .ops = &jz4740_udc_ops, + .ep0 = &udc_dev.ep[0].ep, + .name = driver_name, + .dev = { + .bus_id = "gadget", + }, + }, + + /* control endpoint */ + .ep[0] = { + .ep = { + .name = ep0name, + .ops = &jz4740_ep_ops, + .maxpacket = EP0_MAXPACKETSIZE, + }, + .dev = &udc_dev, + + .bEndpointAddress = 0, + .bmAttributes = 0, + + .ep_type = ep_control, + .fifo = USB_FIFO_EP0, + .csr = USB_REG_CSR0, + }, + + /* bulk out endpoint */ + .ep[1] = { + .ep = { + .name = "ep1out-bulk", + .ops = &jz4740_ep_ops, + .maxpacket = EPBULK_MAXPACKETSIZE, + }, + .dev = &udc_dev, + + .bEndpointAddress = 1, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + + .ep_type = ep_bulk_out, + .fifo = USB_FIFO_EP1, + .csr = USB_REG_OUTCSR, + }, + + /* bulk in endpoint */ + .ep[2] = { + .ep = { + .name = "ep1in-bulk", + .ops = &jz4740_ep_ops, + .maxpacket = EPBULK_MAXPACKETSIZE, + }, + .dev = &udc_dev, + + .bEndpointAddress = USB_DIR_IN | 1, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + + .ep_type = ep_bulk_in, + .fifo = USB_FIFO_EP1, + .csr = USB_REG_INCSR, + }, + + /* interrupt in endpoint */ + .ep[3] = { + .ep = { + .name = "ep2in-int", + .ops = &jz4740_ep_ops, + .maxpacket = EPINTR_MAXPACKETSIZE, + }, + .dev = &udc_dev, + + .bEndpointAddress = USB_DIR_IN | 2, + .bmAttributes = USB_ENDPOINT_XFER_INT, + + .ep_type = ep_interrupt, + .fifo = USB_FIFO_EP2, + .csr = USB_REG_INCSR, + }, +}; + +static int jz4740_udc_probe(struct platform_device *pdev) +{ + struct jz4740_udc *dev = &udc_dev; + int rc; + + DEBUG("%s\n", __FUNCTION__); + + spin_lock_init(&dev->lock); + the_controller = dev; + + dev->dev = &pdev->dev; + device_initialize(&dev->gadget.dev); + dev->gadget.dev.parent = &pdev->dev; + +// strcpy (dum->gadget.dev.bus_id, "gadget"); + dev->gadget.dev.release = jz4740_udc_release; + if ((rc = device_register (&dev->gadget.dev)) < 0) + return rc; + platform_set_drvdata(pdev, dev); + + udc_disable(dev); + udc_reinit(dev); + + /* irq setup */ + if (request_irq(IRQ_UDC, jz4740_udc_irq, IRQF_DISABLED,//SA_SHIRQ/*|SA_SAMPLE_RANDOM*/, + driver_name, dev) != 0) { + printk(KERN_INFO "request UDC interrupt %d failed\n", IRQ_UDC); + return -EBUSY; + } + + printk(KERN_INFO "%s\n", driver_desc); + printk(KERN_INFO "version: " DRIVER_VERSION "\n"); + + return 0; +} + +static int jz4740_udc_remove(struct platform_device *pdev) +{ + struct jz4740_udc *dev = platform_get_drvdata(pdev); + DEBUG("%s: %p\n", __FUNCTION__, dev); + + if (dev->driver) + return -EBUSY; + + udc_disable(dev); +#ifdef UDC_PROC_FILE + remove_proc_entry(proc_node_name, NULL); +#endif + + free_irq(IRQ_UDC, dev); + platform_set_drvdata(pdev, 0); + device_unregister(&dev->gadget.dev); + the_controller = 0; + + return 0; +} + +static struct platform_driver udc_driver = { + .probe = jz4740_udc_probe, + .remove = jz4740_udc_remove, + .suspend = NULL, + .resume = NULL, + .driver = { + .name = (char *) driver_name, + .owner = THIS_MODULE, + }, +}; + + + +static struct platform_device the_udc_pdev = { + .name = (char *) gadget_name, + .id = -1, + .dev = { + .release = jz4740_udc_release, + }, +}; + + +/*-------------------------------------------------------------------------*/ + +static int __init udc_init (void) +{ + platform_driver_register(&udc_driver); + return platform_device_register (&the_udc_pdev); +} + +static void __exit udc_exit (void) +{ + platform_driver_unregister(&udc_driver); + platform_device_unregister(&the_udc_pdev); +} + +module_init(udc_init); +module_exit(udc_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Wei Jianli "); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/jz4740_udc.h b/drivers/usb/gadget/jz4740_udc.h new file mode 100644 index 0000000..ee642b4 --- /dev/null +++ b/drivers/usb/gadget/jz4740_udc.h @@ -0,0 +1,112 @@ +/* + * linux/drivers/usb/gadget/jz4740_udc.h + * + * Ingenic JZ4740 on-chip high speed USB device controller + * + * Copyright (C) 2006 Ingenic Semiconductor Inc. + * Author: + * + * 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 */ +}; + +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; +}; + +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__ */ diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 5815168..148ef67 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -992,6 +992,11 @@ MODULE_LICENSE ("GPL"); #define PCI_DRIVER ohci_pci_driver #endif +#ifdef CONFIG_JZSOC +#include "ohci-jz.c" +#define PLATFORM_DRIVER ohci_hcd_jz_driver +#endif + #if defined(CONFIG_ARCH_SA1100) && defined(CONFIG_SA1111) #include "ohci-sa1111.c" #define SA1111_DRIVER ohci_hcd_sa1111_driver diff --git a/drivers/usb/host/ohci-jz.c b/drivers/usb/host/ohci-jz.c new file mode 100644 index 0000000..9604728 --- /dev/null +++ b/drivers/usb/host/ohci-jz.c @@ -0,0 +1,260 @@ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2002 David Brownell + * (C) Copyright 2002 Hewlett-Packard Company + * + * Bus Glue for Ingenic Jz47xx. + * + * Written by Christopher Hoover + * Based on fragments of previous driver by Rusell King et al. + * + * Modified for LH7A404 from ohci-sa1111.c + * by Durgesh Pattamatta + * Modified for AMD Alchemy Au1xxx + * by Matt Porter + * Modified for Jz47xx from ohci-au1xxx.c + * by Peter + * + * This file is licenced under the GPL. + */ + +#include +#include + +#include + +extern int usb_disabled(void); + +/*-------------------------------------------------------------------------*/ + +static void jz_start_ohc(struct platform_device *dev) +{ + printk(KERN_DEBUG __FILE__ + ": starting JZ OHCI USB Controller\n"); + + /* enable host controller */ + __cpm_start_uhc(); + +#ifdef CONFIG_SOC_JZ4750 + __cpm_enable_uhcphy(); +#endif + + printk(KERN_DEBUG __FILE__ + ": Clock to USB host has been enabled \n"); +} + +static void jz_stop_ohc(struct platform_device *dev) +{ + printk(KERN_DEBUG __FILE__ + ": stopping JZ OHCI USB Controller\n"); + +#ifdef CONFIG_SOC_JZ4750 + __cpm_suspend_uhcphy(); +#endif + + __cpm_stop_uhc(); +} + + +/*-------------------------------------------------------------------------*/ + +/* configure so an HC device and id are always provided */ +/* always called with process context; sleeping is OK */ + + +/** + * usb_ohci_jz_probe - initialize Jz-based HCDs + * Context: !in_interrupt() + * + * Allocates basic resources for this USB host controller, and + * then invokes the start() method for the HCD associated with it + * through the hotplug entry's driver_data. + * + */ +static int usb_ohci_jz_probe(const struct hc_driver *driver, + struct platform_device *dev) +{ + int retval; + struct usb_hcd *hcd; + + if (dev->resource[1].flags != IORESOURCE_IRQ) { + pr_debug("resource[1] is not IORESOURCE_IRQ\n"); + return -ENOMEM; + } + + hcd = usb_create_hcd(driver, &dev->dev, "jz"); + if (!hcd) + return -ENOMEM; + hcd->rsrc_start = dev->resource[0].start; + hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + pr_debug("request_mem_region failed\n"); + retval = -EBUSY; + goto err1; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + pr_debug("ioremap failed\n"); + retval = -ENOMEM; + goto err2; + } + + jz_start_ohc(dev); + ohci_hcd_init(hcd_to_ohci(hcd)); + + retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED | IRQF_SHARED); + if (retval == 0) + return retval; + + jz_stop_ohc(dev); + iounmap(hcd->regs); + err2: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + err1: + usb_put_hcd(hcd); + return retval; +} + + +/* may be called without controller electrically present */ +/* may be called with controller, bus, and devices active */ + +/** + * usb_hcd_jz_remove - shutdown processing for Jz-based HCDs + * @dev: USB Host Controller being removed + * Context: !in_interrupt() + * + * Reverses the effect of usb_hcd_jz_probe(), first invoking + * the HCD's stop() method. It is always called from a thread + * context, normally "rmmod", "apmd", or something similar. + * + */ +static void usb_ohci_jz_remove(struct usb_hcd *hcd, struct platform_device *dev) +{ + usb_remove_hcd(hcd); + jz_stop_ohc(dev); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); +} + +/*-------------------------------------------------------------------------*/ + +static int __devinit +ohci_jz_start (struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + int ret; + + ohci_dbg (ohci, "ohci_jz_start, ohci:%p", ohci); + + if ((ret = ohci_init (ohci)) < 0) + return ret; + + if ((ret = ohci_run (ohci)) < 0) { + err ("can't start %s", hcd->self.bus_name); + ohci_stop (hcd); + return ret; + } + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static const struct hc_driver ohci_jz_hc_driver = { + .description = hcd_name, + .product_desc = "JZ OHCI", + .hcd_priv_size = sizeof(struct ohci_hcd), + + /* + * generic hardware linkage + */ + .irq = ohci_irq, + .flags = HCD_USB11 | HCD_MEMORY, + + /* + * basic lifecycle operations + */ + .start = ohci_jz_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ohci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, + .hub_irq_enable = ohci_rhsc_enable, +#ifdef CONFIG_PM + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, +#endif + .start_port_reset = ohci_start_port_reset, +}; + +/*-------------------------------------------------------------------------*/ + +static int ohci_hcd_jz_drv_probe(struct platform_device *pdev) +{ + int ret; + + pr_debug ("In ohci_hcd_jz_drv_probe"); + + if (usb_disabled()) + return -ENODEV; + + ret = usb_ohci_jz_probe(&ohci_jz_hc_driver, pdev); + return ret; +} + +static int ohci_hcd_jz_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + usb_ohci_jz_remove(hcd, pdev); + return 0; +} + /*TBD*/ +/*static int ohci_hcd_jz_drv_suspend(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + + return 0; +} +static int ohci_hcd_jz_drv_resume(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + + return 0; +} +*/ + +static struct platform_driver ohci_hcd_jz_driver = { + .probe = ohci_hcd_jz_drv_probe, + .remove = ohci_hcd_jz_drv_remove, + .shutdown = usb_hcd_platform_shutdown, + /*.suspend = ohci_hcd_jz_drv_suspend, */ + /*.resume = ohci_hcd_jz_drv_resume, */ + .driver = { + .name = "jz-ohci", + .owner = THIS_MODULE, + }, +}; + diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 3b54b39..45df749 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -237,6 +237,226 @@ 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 to . + +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. +config JZLCD_FRAMEBUFFER_MAX + int "Default FrameBuffer num" + depends on FB_JZLCD_4730_4740 + default "1" + ---help--- + JZ LCD driver support multi-framebuffers for video applications. +config JZLCD_FRAMEBUFFER_ROTATE_SUPPORT + bool "JZLCD FrameBuffer Rotate Support(For TEST)" + depends on FB_JZLCD_4730_4740 + default n + ---help--- + JZ LCD driver framebuffer rotate support. Rotate angle can be 0,90,180,270. + Note, this fearture is implemented by software, and will cost a lot of cpu capcity. + That is to say, if you select this function, you system will become slowly. + Rotate cost cpu about: + ratate angle 0'C: 0% cpu + ratate angle 90'C: 40% cpu + ratate angle 180'C: 20% cpu + ratate angle 270'C: 40% cpu + +config JZLCD_FRAMEBUFFER_DEFAULT_ROTATE_ANGLE + int "FrameBuffer default rotate angle" + depends on JZLCD_FRAMEBUFFER_ROTATE_SUPPORT + default 0 + ---help--- + JZ LCD driver framebuffer angle value can be: + 0: 0'C + 1: 90'C + 2: 180'C + 3: 270'C +config JZLCD_FRAMEBUFFER_BPP + int "FrameBuffer bit per pixel" + depends on JZLCD_FRAMEBUFFER_ROTATE_SUPPORT + default 32 + ---help--- + JZ LCD driver framebuffer support 8bpp, 16bpp, 32bpp +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_JZ4750_LCD + tristate "JZ4750 LCD Controller support" + depends on FB_JZSOC && (SOC_JZ4750 || SOC_JZ4750D) + ---help--- + JZ4750 LCD Controller driver. + JZ4750 LCD Controller support OSD function(refer jz4750_lcdc_spec.pdf).JZ4750 LCD OSD implement 2 framebuffer layers: foreground0 and foreground1. JZ4750 LCD driver support only foreground0 default. + +config FB_JZ4750_LCD_USE_2LAYER_FRAMEBUFFER + bool "JZ4750 LCD driver 2 layers framebuffer support." + depends on FB_JZ4750_LCD + ---help--- + JZ4750 LCD driver support only foreground0 by default. + If you need both foreground0 and foreground1, please select this. + +config FB_JZ4750_TVE + tristate "Jz4750 TV Encode support" + depends on FB_JZSOC && FB_JZ4750_LCD + default n + +config IPU_JZ4750 + tristate "Jz4750 ipu support (Only for debug)" + depends on FB_JZSOC && FB_JZ4750_LCD + default n + ---help--- + Test JZ4750 IPU, if not sure, please set it to n. + +config FB_JZ4750_SLCD + bool + depends on FB_JZ4750_LCD + default n +choice + depends on FB_JZ4750_LCD + prompt "JZ4750 LCD Panels Support" + default JZ4750_LCD_SAMSUNG_LTP400WQF02 + ---help--- + Please select the lcd panel in you board + +config JZ4750_LCD_SAMSUNG_LTP400WQF01 + bool "SAMSUNG LTP400WQF01 TFT panel (480x272)(16bits)" + +config JZ4750_LCD_SAMSUNG_LTP400WQF02 + bool "SAMSUNG LTP400WQF02 TFT panel (480x272)(18bits)" + +config JZ4750_LCD_AUO_A043FL01V2 + bool "AUO A043FL01V2 TFT panel (480x272)(24bits)" + +config JZ4750_LCD_FOXCONN_PT035TN01 + bool "FOXCONN PT035TN01 TFT panel (320x240,3.5in)(18bit-parallel mode)" + +config JZ4750_LCD_INNOLUX_PT035TN01_SERIAL + bool "INNOLUX PT035TN01 TFT panel (320x240,3.5in)(8bit-serial mode)" + +config JZ4750_LCD_TOPPOLY_TD025THEA7_RGB_DELTA + bool "TOPPOLY_TD025THEA7 TFT panel(320x240)(serial RGB delta mode)" + +config JZ4750_LCD_TOPPOLY_TD043MGEB1 + bool "TOPPOLY_TD043MGEB1 TFT panel(800x480)(24bit mode)" + +config JZ4750_LCD_TRULY_TFTG320240DTSW_18BIT + bool "TRULY_TFTG320240DTSW TFT panel (320x240) (Parallel 18bit mode)" + +config JZ4750_LCD_TRULY_TFT_GG1P0319LTSW_W + bool "TRULY_TFT_GG1P0319LTSW_W (240x320) (Smart LCD 16bit)" + +config JZ4750_SLCD_KGM701A3_TFT_SPFD5420A + bool "KGM701A3_TFT_SPFD5420A (400x240) (Smart LCD 18bit)" + select FB_JZ4750_SLCD + +config JZ4750D_VGA_DISPLAY + depends on SOC_JZ4750D + bool "Jz4750D VGA Display" +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..5a3c60d 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -28,6 +28,10 @@ obj-$(CONFIG_FB_DDC) += fb_ddc.o obj-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o # Hardware specific drivers go first +obj-$(CONFIG_FB_JZLCD_4730_4740) += jzlcd.o +obj-$(CONFIG_FB_JZ4740_SLCD) += jz4740_slcd.o +obj-$(CONFIG_FB_JZ4750_LCD) += jz4750_lcd.o +obj-$(CONFIG_FB_JZ4750_TVE) += jz4750_tve.o obj-$(CONFIG_FB_AMIGA) += amifb.o c2p_planar.o obj-$(CONFIG_FB_ARC) += arcfb.o obj-$(CONFIG_FB_CLPS711X) += clps711xfb.o diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index 2f50a80..92bd3ce 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -118,6 +118,14 @@ config FRAMEBUFFER_CONSOLE_DETECT_PRIMARY If unsure, select n. +config FRAMEBUFFER_CONSOLE_CURSOR_FLASH + bool "Framebuffer Console Cursor flash" + depends on FRAMEBUFFER_CONSOLE + help + Enable cursor flush for the framebuffer console. This is done + in software and may be significantly slower than a normally oriented + display. + config FRAMEBUFFER_CONSOLE_ROTATION bool "Framebuffer Console Rotation" depends on FRAMEBUFFER_CONSOLE diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 3a44695..212b25a 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -366,6 +366,7 @@ static void fbcon_update_softback(struct vc_data *vc) static void fb_flashcursor(struct work_struct *work) { +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_CURSOR_FLASH struct fb_info *info = container_of(work, struct fb_info, queue); struct fbcon_ops *ops = info->fbcon_par; struct display *p; @@ -391,6 +392,7 @@ static void fb_flashcursor(struct work_struct *work) ops->cursor(vc, info, mode, softback_lines, get_color(vc, info, c, 1), get_color(vc, info, c, 0)); release_console_sem(); +#endif } static void cursor_timer_handler(unsigned long dev_addr) diff --git a/drivers/video/jz4740_slcd.c b/drivers/video/jz4740_slcd.c new file mode 100644 index 0000000..41f0928 --- /dev/null +++ b/drivers/video/jz4740_slcd.c @@ -0,0 +1,1334 @@ +/* + * linux/drivers/video/jzslcd.c -- Ingenic On-Chip Smart 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "console/fbcon.h" + +#include "jz4740_slcd.h" + +#undef DEBUG +//#define DEBUG +#ifdef DEBUG +#define dprintk(x...) printk(x) +#else +#define dprintk(x...) +#endif + +#define print_err(f, arg...) printk(KERN_ERR DRIVER_NAME ": " f "\n", ## arg) +#define print_warn(f, arg...) printk(KERN_WARNING DRIVER_NAME ": " f "\n", ## arg) +#define print_info(f, arg...) printk(KERN_INFO DRIVER_NAME ": " f "\n", ## arg) +#ifdef DEBUG +#define print_dbg(f, arg...) printk("dbg::" __FILE__ ",LINE(%d): " f "\n", __LINE__, ## arg) +#else +#define print_dbg(f, arg...) do {} while (0) +#endif + +static jz_dma_desc slcd_palette_desc __attribute__ ((aligned (16))); +static jz_dma_desc slcd_frame_desc __attribute__ ((aligned (16))); + +static int dma_chan; +static dma_addr_t slcd_frame_desc_phys_addr, slcd_palette_desc_phys_addr; + +static unsigned char non_link_desp = 0; +static unsigned char is_set_reg = 0; +struct lcd_cfb_info { + struct fb_info fb; + struct display_switch *dispsw; + signed int currcon; + int func_use_count; + + struct { + u16 red, green, blue; + } palette[NR_PALETTE]; +#ifdef CONFIG_PM + struct pm_dev *pm; +#endif +}; + +struct slcd_reg_info { + unsigned int cmd; + unsigned int data; +}; +static struct slcd_reg_info reg_buf; +static struct lcd_cfb_info *jzslcd_info; + +struct jzfb_info { + unsigned int cfg; /* panel mode and pin usage etc. */ + unsigned int w; + unsigned int h; + unsigned int bpp; /* bit per pixel */ + unsigned int bus; + unsigned int pclk; /* pixel clk */ + +}; + +static struct jzfb_info jzfb = { +#ifdef CONFIG_JZ_SLCD_LGDP4551 + SLCD_CFG_CS_ACTIVE_LOW | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_TYPE_PARALLEL, + 400, 240, 16, 8, 16000000 /*16 bpp, 8 bus*/ +// 240, 400, 18, 8, 16000000 /*18 bpp, 8 bus*/ +// 400, 240, 18, 8, 16000000 /*18 bpp, 8 bus*/ +#endif + +#ifdef CONFIG_JZ_SLCD_SPFD5420A + SLCD_CFG_CS_ACTIVE_LOW | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_TYPE_PARALLEL, + 400, 240, 18, 18, 16000000 /*18 bpp, 18 bus*/ +#endif +}; + + +static volatile unsigned char *slcd_palette; +static volatile unsigned char *slcd_frame; + +//extern struct display fb_display[MAX_NR_CONSOLES]; +static irqreturn_t slcd_dma_irq(int irq, void *dev_id); + + +static void Mcupanel_RegSet(UINT32 cmd, UINT32 data) +{ + switch (jzfb.bus) { + case 8: + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) >> 8); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff) >> 0); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_DATA | (data&0xffff); + break; + case 9: + data = ((data & 0xff) << 1) | ((data & 0xff00) << 2); + data = ((data << 6) & 0xfc0000) | ((data << 4) & 0xfc00) | ((data << 2) & 0xfc); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) >> 8); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff) >> 0); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_DATA | data; + break; + case 16: + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | (cmd&0xffff); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_DATA | (data&0xffff); + break; + case 18: + cmd = ((cmd & 0xff) << 1) | ((cmd & 0xff00) << 2); + data = ((data & 0xff) << 1) | ((data & 0xff00) << 2); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | cmd; + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_DATA | ((data<<6)&0xfc0000)|((data<<4)&0xfc00) | ((data<<2)&0xfc); + break; + default: + printk("Don't support %d bit Bus\n", jzfb.bus ); + break; + } +} + +/* Sent a command withou data */ +static void Mcupanel_Command(UINT32 cmd) { + switch (jzfb.bus) { + case 8: + case 9: + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) >> 8); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff) >> 0); + break; + case 16: + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | (cmd&0xffff); + break; + case 18: + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) << 2) | ((cmd&0xff) << 1); + break; + default: + printk("Don't support %d bit Bus\n", jzfb.bus ); + break; + } +} + +/* Set the start address of screen, for example (0, 0) */ +#ifdef CONFIG_JZ_SLCD_LGDP4551 +static void Mcupanel_SetAddr(UINT16 x, UINT16 y) +{ + Mcupanel_RegSet(0x20,x) ; + udelay(1); + Mcupanel_RegSet(0x21,y) ; + udelay(1); + Mcupanel_Command(0x22); + +} +#endif +#ifdef CONFIG_JZ_SLCD_SPFD5420A +void Mcupanel_SetAddr(u32 x, u32 y) //u32 +{ + Mcupanel_RegSet(0x200,x) ; + udelay(1); + Mcupanel_RegSet(0x201,y) ; + udelay(1); + Mcupanel_Command(0x202); + +} + +#endif + +static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) +{ + chan &= 0xffff; + chan >>= 16 - bf->length; + return chan << bf->offset; +} + +static int jzfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info) +{ + struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; + unsigned short *ptr, ctmp; + + print_dbg("regno:%d,RGBt:(%d,%d,%d,%d)\t", regno, red, green, blue, transp); + if (regno >= NR_PALETTE) + return 1; + + cfb->palette[regno].red = red ; + cfb->palette[regno].green = green; + cfb->palette[regno].blue = blue; + if (cfb->fb.var.bits_per_pixel <= 16) { + red >>= 8; + green >>= 8; + blue >>= 8; + + red &= 0xff; + green &= 0xff; + blue &= 0xff; + } + switch (cfb->fb.var.bits_per_pixel) { + case 1: + case 2: + case 4: + case 8: + /* RGB 565 */ + if (((red >> 3) == 0) && ((red >> 2) != 0)) + red = 1 << 3; + if (((blue >> 3) == 0) && ((blue >> 2) != 0)) + blue = 1 << 3; + ctmp = ((red >> 3) << 11) + | ((green >> 2) << 5) | (blue >> 3); + + ptr = (unsigned short *)slcd_palette; + ptr = (unsigned short *)(((u32)ptr)|0xa0000000); + ptr[regno] = ctmp; + + break; + + case 15: + if (regno < 16) + ((u32 *)cfb->fb.pseudo_palette)[regno] = + ((red >> 3) << 10) | + ((green >> 3) << 5) | + (blue >> 3); + break; + case 16: + if (regno < 16) { + ((u32 *)cfb->fb.pseudo_palette)[regno] = + ((red >> 3) << 11) | + ((green >> 2) << 5) | + (blue >> 3); + } + break; + case 18: + case 24: + case 32: + if (regno < 16) + ((u32 *)cfb->fb.pseudo_palette)[regno] = + (red << 16) | + (green << 8) | + (blue << 0); + +/* if (regno < 16) { + unsigned val; + val = chan_to_field(red, &cfb->fb.var.red); + val |= chan_to_field(green, &cfb->fb.var.green); + val |= chan_to_field(blue, &cfb->fb.var.blue); + ((u32 *)cfb->fb.pseudo_palette)[regno] = val; + } +*/ + + break; + } + return 0; +} + +static int jzfb_ioctl (struct fb_info *info, unsigned int cmd, unsigned long arg ) +{ + int ret = 0; + void __user *argp = (void __user *)arg; + + switch (cmd) { + case FBIOSETBACKLIGHT: + __slcd_set_backlight_level(arg); /* We support 8 levels here. */ + break; + case FBIODISPON: + __slcd_display_on(); + break; + case FBIODISPOFF: + __slcd_display_off(); + break; + case FBIO_REFRESH_ALWAYS: + dprintk("slcd_frame_desc.dcmd = 0x%08x\n", slcd_frame_desc.dcmd); + if (slcd_frame_desc.dcmd & DMAC_DCMD_LINK) + printk("The Smart LCD refreshes automatically. Option is omitted!\n"); + else { + dprintk("OPEN DMAC_DCMD_LINK \n"); + slcd_frame_desc.dcmd &= ~DMAC_DCMD_TIE; + slcd_frame_desc.dcmd |= DMAC_DCMD_LINK; + dma_cache_wback((unsigned long)(&slcd_frame_desc), 16); + REG_DMAC_DCMD(dma_chan) &= ~DMAC_DCMD_TIE; + __dmac_channel_set_doorbell(dma_chan); + } + break; + case FBIO_REFRESH_EVENTS: + dprintk("slcd_frame_desc.dcmd = 0x%08x\n", slcd_frame_desc.dcmd); + if (!(slcd_frame_desc.dcmd & DMAC_DCMD_LINK)) + printk("The Smart LCD is refreshed by envents. Option is omitted!\n"); + else { + non_link_desp = 1; + REG_DMAC_DCMD(dma_chan) |= DMAC_DCMD_TIE; + REG_DMAC_DCMD(dma_chan) &= ~DMAC_DCMD_LINK; + } + break; + case FBIO_DO_REFRESH: + + dprintk("slcd_frame_desc.dcmd = 0x%08x\n", slcd_frame_desc.dcmd); + if (slcd_frame_desc.dcmd & DMAC_DCMD_LINK) + printk("The Smart LCD can refresh automatically. Option is omitted!\n"); + else { + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + __dmac_channel_set_doorbell(dma_chan); + } + break; + case FBIO_SET_REG: + if (copy_from_user(®_buf, argp, sizeof(reg_buf))) + return -EFAULT; + is_set_reg = 1; + REG_DMAC_DCMD(dma_chan) |= DMAC_DCMD_TIE; + REG_DMAC_DCMD(dma_chan) &= ~DMAC_DCMD_LINK; + break; + default: + break; + } + + return ret; +} + +/* Use mmap /dev/fb can only get a non-cacheable Virtual Address. */ +static int jzfb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; + unsigned long start; + unsigned long off; + u32 len; + + off = vma->vm_pgoff << PAGE_SHIFT; + //fb->fb_get_fix(&fix, PROC_CONSOLE(info), info); + + /* frame buffer memory */ + start = cfb->fb.fix.smem_start; + len = PAGE_ALIGN((start & ~PAGE_MASK) + cfb->fb.fix.smem_len); + start &= PAGE_MASK; + + if ((vma->vm_end - vma->vm_start + off) > len) + return -EINVAL; + off += start; + + vma->vm_pgoff = off >> PAGE_SHIFT; + vma->vm_flags |= VM_IO; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); /* Uncacheable */ + +#if 1 + pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK; +// pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED; /* Uncacheable */ + pgprot_val(vma->vm_page_prot) |= _CACHE_CACHABLE_NONCOHERENT; /* Write-Through */ +#endif + + if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) { + return -EAGAIN; + } + return 0; +} + +/* checks var and eventually tweaks it to something supported, + * DO NOT MODIFY PAR */ +static int jzfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + print_dbg("jzfb_check_var"); + return 0; +} + + +/* + * set the video mode according to info->var + */ +static int jzfb_set_par(struct fb_info *info) +{ +// print_dbg("jzfb_set_par"); + printk("jzfb_set_par"); + return 0; +} + + +/* + * (Un)Blank the display. + * Fix me: should we use VESA value? + */ +static int jzfb_blank(int blank_mode, struct fb_info *info) +{ + + dprintk("fb_blank %d %p", blank_mode, info); + + switch (blank_mode) { + + case FB_BLANK_UNBLANK: + /* Turn on panel */ + break; + + case FB_BLANK_NORMAL: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_POWERDOWN: + /* Turn off panel */ + break; + default: + break; + + } + return 0; +} + +/* + * pan display + */ +static int jzfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; + int dy; + + if (!var || !cfb) { + return -EINVAL; + } + + if (var->xoffset - cfb->fb.var.xoffset) { + /* No support for X panning for now! */ + return -EINVAL; + } + + dy = var->yoffset - cfb->fb.var.yoffset; + print_dbg("var.yoffset: %d", dy); + if (dy) { + + print_dbg("Panning screen of %d lines", dy); +// slcd_frame_desc->databuf += (cfb->fb.fix.line_length * dy); +// slcd_frame_desc->dsadr += (cfb->fb.fix.line_length * dy); + /* TODO: Wait for current frame to finished */ + } + + return 0; +} + + +/* use default function cfb_fillrect, cfb_copyarea, cfb_imageblit */ +static struct fb_ops jzfb_ops = { + .owner = THIS_MODULE, + .fb_setcolreg = jzfb_setcolreg, + .fb_check_var = jzfb_check_var, + .fb_set_par = jzfb_set_par, + .fb_blank = jzfb_blank, + .fb_pan_display = jzfb_pan_display, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_mmap = jzfb_mmap, + .fb_ioctl = jzfb_ioctl, +}; + +static int jzfb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; + //struct display *display; + int chgvar = 0; + + var->height = jzfb.h ; + var->width = jzfb.w ; + var->bits_per_pixel = jzfb.bpp; + + var->vmode = FB_VMODE_NONINTERLACED; + var->activate = cfb->fb.var.activate; + var->xres = var->width; + var->yres = var->height; + var->xres_virtual = var->width; + var->yres_virtual = var->height; + var->xoffset = 0; + var->yoffset = 0; + var->pixclock = 0; + var->left_margin = 0; + var->right_margin = 0; + var->upper_margin = 0; + var->lower_margin = 0; + var->hsync_len = 0; + var->vsync_len = 0; + var->sync = 0; + var->activate &= ~FB_ACTIVATE_TEST; + + /* + * CONUPDATE and SMOOTH_XPAN are equal. However, + * SMOOTH_XPAN is only used internally by fbcon. + */ + if (var->vmode & FB_VMODE_CONUPDATE) { + var->vmode |= FB_VMODE_YWRAP; + var->xoffset = cfb->fb.var.xoffset; + var->yoffset = cfb->fb.var.yoffset; + } + + if (var->activate & FB_ACTIVATE_TEST) + return 0; + + if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) + return -EINVAL; + + if (cfb->fb.var.xres != var->xres) + chgvar = 1; + if (cfb->fb.var.yres != var->yres) + chgvar = 1; + if (cfb->fb.var.xres_virtual != var->xres_virtual) + chgvar = 1; + if (cfb->fb.var.yres_virtual != var->yres_virtual) + chgvar = 1; + if (cfb->fb.var.bits_per_pixel != var->bits_per_pixel) + chgvar = 1; + + //display = fb_display + con; + + var->red.msb_right = 0; + var->green.msb_right = 0; + var->blue.msb_right = 0; + + switch(var->bits_per_pixel){ + case 1: /* Mono */ + cfb->fb.fix.visual = FB_VISUAL_MONO01; + cfb->fb.fix.line_length = (var->xres * var->bits_per_pixel) / 8; + break; + case 2: /* Mono */ + var->red.offset = 0; + var->red.length = 2; + var->green.offset = 0; + var->green.length = 2; + var->blue.offset = 0; + var->blue.length = 2; + + cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; + cfb->fb.fix.line_length = (var->xres * var->bits_per_pixel) / 8; + break; + case 4: /* PSEUDOCOLOUR*/ + var->red.offset = 0; + var->red.length = 4; + var->green.offset = 0; + var->green.length = 4; + var->blue.offset = 0; + var->blue.length = 4; + + cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; + cfb->fb.fix.line_length = var->xres / 2; + break; + case 8: /* PSEUDOCOLOUR, 256 */ + var->red.offset = 0; + var->red.length = 8; + var->green.offset = 0; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + + cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; + cfb->fb.fix.line_length = var->xres ; + break; + case 15: /* DIRECTCOLOUR, 32k */ + var->bits_per_pixel = 15; + var->red.offset = 10; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 5; + var->blue.offset = 0; + var->blue.length = 5; + + cfb->fb.fix.visual = FB_VISUAL_DIRECTCOLOR; + cfb->fb.fix.line_length = var->xres_virtual * 2; + break; + case 16: /* DIRECTCOLOUR, 64k */ + var->bits_per_pixel = 16; + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; + + cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR; + cfb->fb.fix.line_length = var->xres_virtual * 2; + break; + case 18: + case 24: + case 32: + /* DIRECTCOLOUR, 256 */ + var->bits_per_pixel = 32; + + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 24; + var->transp.length = 8; + + cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR; + cfb->fb.fix.line_length = var->xres_virtual * 4; + break; + + default: /* in theory this should never happen */ + printk(KERN_WARNING "%s: don't support for %dbpp\n", + cfb->fb.fix.id, var->bits_per_pixel); + break; + } + + cfb->fb.var = *var; + cfb->fb.var.activate &= ~FB_ACTIVATE_ALL; + + /* + * If we are setting all the virtual consoles, also set the + * defaults used to create new consoles. + */ + fb_set_cmap(&cfb->fb.cmap, &cfb->fb); + dprintk("jzfb_set_var: after fb_set_cmap...\n"); + + return 0; +} + +static struct lcd_cfb_info * jzfb_alloc_fb_info(void) +{ + struct lcd_cfb_info *cfb; + + cfb = kmalloc(sizeof(struct lcd_cfb_info) + sizeof(u32) * 16, GFP_KERNEL); + + if (!cfb) + return NULL; + + jzslcd_info = cfb; + + memset(cfb, 0, sizeof(struct lcd_cfb_info) ); + + cfb->currcon = -1; + + + strcpy(cfb->fb.fix.id, "jz-slcd"); + cfb->fb.fix.type = FB_TYPE_PACKED_PIXELS; + cfb->fb.fix.type_aux = 0; + cfb->fb.fix.xpanstep = 1; + cfb->fb.fix.ypanstep = 1; + cfb->fb.fix.ywrapstep = 0; + cfb->fb.fix.accel = FB_ACCEL_NONE; + + cfb->fb.var.nonstd = 0; + cfb->fb.var.activate = FB_ACTIVATE_NOW; + cfb->fb.var.height = -1; + cfb->fb.var.width = -1; + cfb->fb.var.accel_flags = FB_ACCELF_TEXT; + + cfb->fb.fbops = &jzfb_ops; + cfb->fb.flags = FBINFO_FLAG_DEFAULT; + + cfb->fb.pseudo_palette = (void *)(cfb + 1); + + switch (jzfb.bpp) { + case 1: + fb_alloc_cmap(&cfb->fb.cmap, 4, 0); + break; + case 2: + fb_alloc_cmap(&cfb->fb.cmap, 8, 0); + break; + case 4: + fb_alloc_cmap(&cfb->fb.cmap, 32, 0); + break; + case 8: + + default: + fb_alloc_cmap(&cfb->fb.cmap, 256, 0); + break; + } + dprintk("fb_alloc_cmap,fb.cmap.len:%d....\n", cfb->fb.cmap.len); + + return cfb; +} + +/* + * Map screen memory + */ +static int jzfb_map_smem(struct lcd_cfb_info *cfb) +{ + struct page * map = NULL; + unsigned char *tmp; + unsigned int page_shift, needroom, t; + + t = jzfb.bpp; + if (jzfb.bpp == 15) + t = 16; + if (jzfb.bpp == 18 || jzfb.bpp == 24) + t = 32; + needroom = ((jzfb.w * t + 7) >> 3) * jzfb.h; + + for (page_shift = 0; page_shift < 12; page_shift++) + if ((PAGE_SIZE << page_shift) >= needroom) + break; + + slcd_palette = (unsigned char *)__get_free_pages(GFP_KERNEL, 0); + slcd_frame = (unsigned char *)__get_free_pages(GFP_KERNEL, page_shift); + if ((!slcd_palette) || (!slcd_frame)) + return -ENOMEM; + + memset((void *)slcd_palette, 0, PAGE_SIZE); + memset((void *)slcd_frame, 0, PAGE_SIZE << page_shift); + + map = virt_to_page(slcd_palette); + set_bit(PG_reserved, &map->flags); + + for (tmp=(unsigned char *)slcd_frame; + tmp < slcd_frame + (PAGE_SIZE << page_shift); + tmp += PAGE_SIZE) { + map = virt_to_page(tmp); + set_bit(PG_reserved, &map->flags); + } + + cfb->fb.fix.smem_start = virt_to_phys((void *)slcd_frame); + + cfb->fb.fix.smem_len = (PAGE_SIZE << page_shift); + + cfb->fb.screen_base = + (unsigned char *)(((unsigned int)slcd_frame & 0x1fffffff) | 0xa0000000); + + if (!cfb->fb.screen_base) { + printk("%s: unable to map screen memory\n", cfb->fb.fix.id); + return -ENOMEM; + } + + return 0; +} + +static void jzfb_free_fb_info(struct lcd_cfb_info *cfb) +{ + if (cfb) { + fb_alloc_cmap(&cfb->fb.cmap, 0, 0); + kfree(cfb); + } +} + +static void jzfb_unmap_smem(struct lcd_cfb_info *cfb) +{ + struct page * map = NULL; + unsigned char *tmp; + unsigned int page_shift, needroom, t; + + t = jzfb.bpp; + if (jzfb.bpp == 18 || jzfb.bpp == 24) + t = 32; + if (jzfb.bpp == 15) + t = 16; + needroom = ((jzfb.w * t + 7) >> 3) * jzfb.h; + for (page_shift = 0; page_shift < 12; page_shift++) + if ((PAGE_SIZE << page_shift) >= needroom) + break; + + if (cfb && cfb->fb.screen_base) { + iounmap(cfb->fb.screen_base); + cfb->fb.screen_base = NULL; + release_mem_region(cfb->fb.fix.smem_start, + cfb->fb.fix.smem_len); + } + + if (slcd_palette) { + map = virt_to_page(slcd_palette); + clear_bit(PG_reserved, &map->flags); + free_pages((int)slcd_palette, 0); + } + + if (slcd_frame) { + + for (tmp=(unsigned char *)slcd_frame; + tmp < slcd_frame + (PAGE_SIZE << page_shift); + tmp += PAGE_SIZE) { + map = virt_to_page(tmp); + clear_bit(PG_reserved, &map->flags); + } + + free_pages((int)slcd_frame, page_shift); + } +} + +static void slcd_descriptor_init(void) +{ + int i; + int frm_size, pal_size; + unsigned int next; + unsigned int slcd_frame_src_phys_addr, slcd_palette_src_phys_addr, slcd_dma_dst_phys_addr; + + i = jzfb.bpp; + if (i == 18 || i == 24) + i = 32; + if (i == 15) + i = 16; + + switch (jzfb.bpp) { + case 1: + pal_size = 4; + break; + case 2: + pal_size = 8; + break; + case 4: + pal_size = 32; + break; + case 8: + default: + pal_size = 512; + } + + frm_size = jzfb.w * jzfb.h * jzfb.bpp / 8; + + /*Offset of next descriptor*/ + slcd_frame_desc_phys_addr = (dma_addr_t)CPHYSADDR((unsigned long)(&slcd_frame_desc)); + slcd_palette_desc_phys_addr = (dma_addr_t)CPHYSADDR((unsigned long)(&slcd_palette_desc)); + + /*Soure address and Target address*/ + slcd_palette_src_phys_addr = (unsigned int)virt_to_phys(slcd_palette); + slcd_frame_src_phys_addr = (unsigned int)virt_to_phys(slcd_frame); + slcd_dma_dst_phys_addr = (unsigned int)CPHYSADDR(SLCD_FIFO); + next = slcd_frame_desc_phys_addr >> 4; + + /* Prepare Palette Descriptor */ + slcd_palette_desc.dcmd = DMAC_DCMD_SAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 + | DMAC_DCMD_DWDH_16 | DMAC_DCMD_DS_16BYTE | DMAC_DCMD_TM | DMAC_DCMD_DES_V + | DMAC_DCMD_DES_VIE | DMAC_DCMD_LINK; + switch (slcd_palette_desc.dcmd & DMAC_DCMD_DS_MASK) { + case DMAC_DCMD_DS_32BYTE: + pal_size /= 32; + break; + case DMAC_DCMD_DS_16BYTE: + pal_size /= 16; + break; + case DMAC_DCMD_DS_32BIT: + pal_size /= 4; + break; + case DMAC_DCMD_DS_16BIT: + pal_size /= 2; + break; + case DMAC_DCMD_DS_8BIT: + default: + break; + } + + slcd_palette_desc.dsadr = (unsigned int)virt_to_phys(slcd_palette); /* DMA source address */ + slcd_palette_desc.dtadr = (unsigned int)CPHYSADDR(SLCD_FIFO); /* DMA target address */ + slcd_palette_desc.ddadr = (volatile unsigned int)((next << 24) | (pal_size & 0xffffff)); /* offset and size*/ + dma_cache_wback((unsigned long)(&slcd_palette_desc), 16); + + /*Prepare Frame Descriptor in memory*/ + switch (jzfb.bpp) { + case 8 ... 16: + slcd_frame_desc.dcmd = DMAC_DCMD_SAI | DMAC_DCMD_RDIL_IGN | DMAC_DCMD_SWDH_32 + | DMAC_DCMD_DWDH_16 | DMAC_DCMD_DS_16BYTE | DMAC_DCMD_TM | DMAC_DCMD_DES_V + | DMAC_DCMD_DES_VIE | DMAC_DCMD_LINK; + break; + + case 17 ... 32: + slcd_frame_desc.dcmd = DMAC_DCMD_SAI | 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_VIE | DMAC_DCMD_LINK; + break; + } + switch (slcd_frame_desc.dcmd & DMAC_DCMD_DS_MASK) { + case DMAC_DCMD_DS_32BYTE: + frm_size /= 32; + break; + case DMAC_DCMD_DS_16BYTE: + frm_size /= 16; + break; + case DMAC_DCMD_DS_32BIT: + frm_size /= 4; + break; + case DMAC_DCMD_DS_16BIT: + frm_size /= 2; + break; + case DMAC_DCMD_DS_8BIT: + default: + break; + } + + slcd_frame_desc.dsadr = slcd_frame_src_phys_addr; /* DMA source address */ + slcd_frame_desc.dtadr = slcd_dma_dst_phys_addr; /* DMA target address */ + slcd_frame_desc.ddadr = (volatile unsigned int)((next << 24) | (frm_size & 0xffffff)); /* offset and size*/ + dma_cache_wback((unsigned long)(&slcd_frame_desc), 16); +} + +void slcd_hw_init(void) +{ + unsigned int val, pclk; + int pll_div; + + REG_LCD_CFG &= ~LCD_CFG_LCDPIN_MASK; + REG_LCD_CFG |= LCD_CFG_LCDPIN_SLCD; + + if ((jzfb.bpp == 18) | (jzfb.bpp == 24)) + jzfb.bpp = 32; + + /* Configure SLCD module for initialize smart lcd registers*/ + switch (jzfb.bus) { + case 8: + REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_8_x2 + | SLCD_CFG_CWIDTH_8BIT | SLCD_CFG_CS_ACTIVE_LOW + | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING + | SLCD_CFG_TYPE_PARALLEL; + __gpio_as_slcd_8bit(); + break; + case 9: + REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_8_x2 + | SLCD_CFG_CWIDTH_8BIT | SLCD_CFG_CS_ACTIVE_LOW + | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING + | SLCD_CFG_TYPE_PARALLEL; + __gpio_as_slcd_9bit(); + break; + case 16: + REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_16 + | SLCD_CFG_CWIDTH_16BIT | SLCD_CFG_CS_ACTIVE_LOW + | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING + | SLCD_CFG_TYPE_PARALLEL; + __gpio_as_slcd_16bit(); + break; + case 18: + REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_18 + | SLCD_CFG_CWIDTH_18BIT | SLCD_CFG_CS_ACTIVE_LOW + | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING + | SLCD_CFG_TYPE_PARALLEL; + __gpio_as_slcd_18bit(); + break; + default: + printk("Error: Don't support BUS %d!\n", jzfb.bus); + break; + } + + REG_SLCD_CTRL = SLCD_CTRL_DMA_EN; + __cpm_stop_lcd(); + pclk = jzfb.pclk; + pll_div = ( REG_CPM_CPCCR & CPM_CPCCR_PCS ); /* clock source,0:pllout/2 1: pllout */ + pll_div = pll_div ? 1 : 2 ; + val = ( __cpm_get_pllout()/pll_div ) / pclk; + val--; + if ( val > 0x1ff ) { + printk("CPM_LPCDR too large, set it to 0x1ff\n"); + val = 0x1ff; + } + __cpm_set_pixdiv(val); + + REG_CPM_CPCCR |= CPM_CPCCR_CE ; /* update divide */ + + jz_clocks.pixclk = __cpm_get_pixclk(); + jz_clocks.lcdclk = __cpm_get_lcdclk(); + printk("SLCDC: PixClock:%d LcdClock:%d\n", + jz_clocks.pixclk, jz_clocks.lcdclk); + + __cpm_start_lcd(); + udelay(1000); + __slcd_display_pin_init(); + __slcd_special_on(); + + /* Configure SLCD module for transfer data to smart lcd GRAM*/ + switch (jzfb.bus) { + case 8: + switch (jzfb.bpp) { + case 8: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; + break; + case 15: + case 16: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x2; + break; + case 17 ... 32: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x3; + break; + default: + printk("The BPP %d is not supported\n", jzfb.bpp); + break; + } + break; + case 9: + switch (jzfb.bpp) { + case 8: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; + break; + case 15 ... 16: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x2; + break; + case 17 ... 32: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_9_x2; + break; + default: + printk("The BPP %d is not supported\n", jzfb.bpp); + break; + } + break; + case 16: + switch (jzfb.bpp) { + case 8: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; + break; + case 15 ... 16: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_16; + break; + case 17 ... 32: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x3; + break; + default: + printk("The BPP %d is not supported\n", jzfb.bpp); + break; + } + break; + case 18: + switch (jzfb.bpp) { + case 8: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; + break; + case 15: + case 16: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_16; + break; + case 17 ... 32: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_18; + break; + default: + printk("The BPP %d is not supported\n", jzfb.bpp); + break; + } + break; + default: + printk("Error: The BUS %d is not supported\n", jzfb.bus); + break; + } + dprintk("SLCD_CFG=0x%x\n", REG_SLCD_CFG); +} + +static irqreturn_t slcd_dma_irq(int irq, void *dev_id) +{ + + if (__dmac_channel_transmit_halt_detected(dma_chan)) { + dprintk("DMA HALT\n"); + __dmac_channel_clear_transmit_halt(dma_chan); + } + + if (__dmac_channel_address_error_detected(dma_chan)) { + dprintk("DMA ADDR ERROR\n"); + __dmac_channel_clear_address_error(dma_chan); + } + + if (__dmac_channel_descriptor_invalid_detected(dma_chan)) { + dprintk("DMA DESC INVALID\n"); + __dmac_channel_clear_descriptor_invalid(dma_chan); + } + + if (__dmac_channel_count_terminated_detected(dma_chan)) { + dprintk("DMA CT\n"); + __dmac_channel_clear_count_terminated(dma_chan); + if(is_set_reg){ + printk("Close DMAC_DCMD_LINK \n"); + REG_DMAC_DCMD(dma_chan) &= ~DMAC_DCMD_LINK; + } + if (non_link_desp) { + printk("Close DMAC_DCMD_LINK \n"); + /*Set to Non-Link Descriptor*/ + REG_DMAC_DCMD(dma_chan) &= ~DMAC_DCMD_LINK; + } + } + + if (__dmac_channel_transmit_end_detected(dma_chan)) { + printk("DMA TT\n"); + __dmac_channel_clear_transmit_end(dma_chan); + if (non_link_desp) { + slcd_frame_desc.dcmd |= DMAC_DCMD_TIE; + slcd_frame_desc.dcmd &= ~DMAC_DCMD_LINK; + dma_cache_wback((unsigned long)(&slcd_frame_desc), 16); + non_link_desp = 0; + } + if (is_set_reg) { + is_set_reg = 0; + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_DMAC_DMACR &= ~DMAC_DMACR_DMAE; /* disable DMA */ + REG_DMAC_DCCSR(dma_chan) &= ~DMAC_DCCSR_EN; /* disable DMA */ + REG_SLCD_CTRL = 0; + + /* + *add operation here + */ + Mcupanel_RegSet(reg_buf.cmd, reg_buf.data); + Mcupanel_Command(0x0022);/*Write Data to GRAM */ + mdelay(100); + REG_SLCD_CTRL = SLCD_CTRL_DMA_EN; + REG_DMAC_DMACR = DMAC_DMACR_DMAE; + REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_EN; + __dmac_channel_set_doorbell(dma_chan); + } + } + return IRQ_HANDLED; +} + +static int slcd_dma_init(void) +{ + /* Request DMA channel and setup irq handler */ + dma_chan = jz_request_dma(DMA_ID_AUTO, "auto", slcd_dma_irq, 0, NULL); + if (dma_chan < 0) { + printk("Request DMA Failed\n"); + return -1; + } + printk("DMA channel %d is requested by SLCD!\n", dma_chan); + + /*Init the SLCD DMA and Enable*/ + REG_DMAC_DRSR(dma_chan) = DMAC_DRSR_RS_SLCD; + REG_DMAC_DMACR = DMAC_DMACR_DMAE; + REG_DMAC_DCCSR(dma_chan) = DMAC_DCCSR_EN; /*Descriptor Transfer*/ + + if (jzfb.bpp <= 8) + REG_DMAC_DDA(dma_chan) = slcd_palette_desc_phys_addr; + else + REG_DMAC_DDA(dma_chan) = slcd_frame_desc_phys_addr; + + /* DMA doorbell set -- start DMA now ... */ + __dmac_channel_set_doorbell(dma_chan); + return 0; +} + +#ifdef CONFIG_PM + +/* + * Suspend the LCDC. + */ +static int jzfb_suspend(void) +{ + + __slcd_close_backlight(); + __dmac_disable_channel(dma_chan); + __slcd_dma_disable(); /* Quick Disable */ + __slcd_special_off(); + __cpm_stop_lcd(); + return 0; +} + +/* + * Resume the LCDC. + */ + +static int jzfb_resume(void) +{ + __cpm_start_lcd(); + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + switch (jzfb.bpp) { + case 8: + /* DATA 8-bit once*/ + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; + break; + case 15: + case 16: + case 18: + case 24: + case 32: + /* DATA 8-bit twice*/ + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x2; + break; + default: + REG_SLCD_CFG = SLCD_CFG_DWIDTH_8_x2; + break; + } + __slcd_display_pin_init(); + __slcd_special_on(); + + if (jzfb.bpp == 32) { + /* DATA 8-bit three time*/ + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x3; + } + __slcd_dma_enable(); + udelay(100); + __dmac_enable_channel(dma_chan); + __dmac_channel_set_doorbell(dma_chan); + mdelay(200); + __slcd_set_backlight_level(80); + return 0; +} + +/* + * Power management hook. Note that we won't be called from IRQ context, + * unlike the blank functions above, so we may sleep. + */ +static int jzslcd_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data) +{ + int ret; + struct lcd_cfb_info *cfb = pm_dev->data; + + if (!cfb) return -EINVAL; + + switch (req) { + case PM_SUSPEND: + ret = jzfb_suspend(); + break; + + case PM_RESUME: + ret = jzfb_resume(); + break; + + default: + ret = -EINVAL; + break; + } + return ret; +} +#else +#define jzfb_suspend NULL +#define jzfb_resume NULL +#endif /* CONFIG_PM */ +static int __init jzslcd_fb_init(void) +{ + + struct lcd_cfb_info *cfb; + int err = 0; + + /*the parameters of slcd*/ + cfb = jzfb_alloc_fb_info(); + if (!cfb) + goto failed; + + err = jzfb_map_smem(cfb); + if (err) + goto failed; + jzfb_set_var(&cfb->fb.var, -1, &cfb->fb); + + slcd_hw_init(); + + err = register_framebuffer(&cfb->fb); + if (err < 0) { + printk("jzslcd_fb_init(): slcd register framebuffer err.\n"); + goto failed; + } + + printk("fb%d: %s frame buffer device, using %dK of video memory\n", + cfb->fb.node, cfb->fb.fix.id, cfb->fb.fix.smem_len>>10); + + slcd_descriptor_init(); + err = slcd_dma_init(); + if (err != 0) { + printk("SLCD Init DMA Fail!\n"); + return err; + } + mdelay(100); + __slcd_set_backlight_level(80); + +#ifdef CONFIG_PM + /* + * Note that the console registers this as well, but we want to + * power down the display prior to sleeping. + */ +//struct pm_dev __deprecated *pm_register(pm_dev_t type, unsigned long id, pm_callback callback); + + cfb->pm = pm_register(PM_SYS_DEV, PM_SYS_VGA, jzslcd_pm_callback); + if (cfb->pm) + cfb->pm->data = cfb; + +#endif + return 0; + +failed: + jzfb_unmap_smem(cfb); + jzfb_free_fb_info(cfb); + + return err; +} + +#if 0 +static int jzfb_remove(struct device *dev) +{ + struct lcd_cfb_info *cfb = dev_get_drvdata(dev); + jzfb_unmap_smem(cfb); + jzfb_free_fb_info(cfb); + return 0; +} +#endif + +#if 0 +static struct device_driver jzfb_driver = { + .name = "jz-slcd", + .bus = &platform_bus_type, + .probe = jzfb_probe, + .remove = jzfb_remove, + .suspend = jzfb_suspend, + .resume = jzfb_resume, +}; +#endif + +static void __exit jzslcd_fb_cleanup(void) +{ + //driver_unregister(&jzfb_driver); + //jzfb_remove(); +} + +module_init(jzslcd_fb_init); +module_exit(jzslcd_fb_cleanup); + +MODULE_DESCRIPTION("JzSOC SLCD Controller driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/jz4740_slcd.h b/drivers/video/jz4740_slcd.h new file mode 100644 index 0000000..84754f6 --- /dev/null +++ b/drivers/video/jz4740_slcd.h @@ -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__*/ + diff --git a/drivers/video/jz4750_lcd.c b/drivers/video/jz4750_lcd.c new file mode 100644 index 0000000..05a7799 --- /dev/null +++ b/drivers/video/jz4750_lcd.c @@ -0,0 +1,2229 @@ +/* + * linux/drivers/video/jz4750_lcd.c -- Ingenic Jz4750 LCD frame buffer device + * + * 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. + * + * 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. + */ + +/* + * -------------------------------- + * NOTE: + * This LCD driver support TFT16 TFT32 LCD, not support STN and Special TFT LCD + * now. + * It seems not necessory to support STN and Special TFT. + * If it's necessary, update this driver in the future. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "console/fbcon.h" + +#include "jz4750_lcd.h" +#include "jz4750_tve.h" + +#ifdef CONFIG_JZ4750_SLCD_KGM701A3_TFT_SPFD5420A +#include "jz_kgm_spfd5420a.h" +#endif + +MODULE_DESCRIPTION("Jz4750 LCD Controller driver"); +MODULE_AUTHOR("Wolfgang Wang, "); +MODULE_LICENSE("GPL"); + + +//#define DEBUG +#undef DEBUG + +#ifdef DEBUG +#define dprintk(x...) printk(x) +#define print_dbg(f, arg...) printk("dbg::" __FILE__ ",LINE(%d): " f "\n", __LINE__, ## arg) +#else +#define dprintk(x...) +#define print_dbg(f, arg...) do {} while (0) +#endif + +#define print_err(f, arg...) printk(KERN_ERR DRIVER_NAME ": " f "\n", ## arg) +#define print_warn(f, arg...) printk(KERN_WARNING DRIVER_NAME ": " f "\n", ## arg) +#define print_info(f, arg...) printk(KERN_INFO DRIVER_NAME ": " f "\n", ## arg) + +struct lcd_cfb_info { + struct fb_info fb; + struct display_switch *dispsw; + signed int currcon; + int func_use_count; + + struct { + u16 red, green, blue; + } palette[NR_PALETTE]; +#ifdef CONFIG_PM + struct pm_dev *pm; +#endif +}; + +static struct lcd_cfb_info *jz4750fb_info; +static struct jz4750_lcd_dma_desc *dma_desc_base; +static struct jz4750_lcd_dma_desc *dma0_desc_palette, *dma0_desc0, *dma0_desc1, *dma1_desc0, *dma1_desc1; +#define DMA_DESC_NUM 6 + +static unsigned char *lcd_palette; +static unsigned char *lcd_frame0; +static unsigned char *lcd_frame1; + +static struct jz4750_lcd_dma_desc *dma0_desc_cmd0, *dma0_desc_cmd; +static unsigned char *lcd_cmdbuf ; + +static void jz4750fb_set_mode( struct jz4750lcd_info * lcd_info ); +static void jz4750fb_deep_set_mode( struct jz4750lcd_info * lcd_info ); + + + +struct jz4750lcd_info jz4750_lcd_panel = { +#if defined(CONFIG_JZ4750_LCD_SAMSUNG_LTP400WQF02) + .panel = { + .cfg = LCD_CFG_LCDPIN_LCD | LCD_CFG_RECOVER | /* Underrun recover */ + LCD_CFG_NEWDES | /* 8words descriptor */ + LCD_CFG_MODE_GENERIC_TFT | /* General TFT panel */ + LCD_CFG_MODE_TFT_18BIT | /* output 18bpp */ + LCD_CFG_HSP | /* Hsync polarity: active low */ + LCD_CFG_VSP, /* Vsync polarity: leading edge is falling edge */ + .slcd_cfg = 0, + .ctrl = LCD_CTRL_OFUM | LCD_CTRL_BST_16, /* 16words burst, enable out FIFO underrun irq */ + 480, 272, 60, 41, 10, 2, 2, 2, 2, + }, + .osd = { + .osd_cfg = LCD_OSDC_OSDEN | /* Use OSD mode */ +// LCD_OSDC_ALPHAEN | /* enable alpha */ + LCD_OSDC_F0EN, /* enable Foreground0 */ + .osd_ctrl = 0, /* disable ipu, */ + .rgb_ctrl = 0, + .bgcolor = 0x000000, /* set background color Black */ + .colorkey0 = 0, /* disable colorkey */ + .colorkey1 = 0, /* disable colorkey */ + .alpha = 0xA0, /* alpha value */ + .ipu_restart = 0x80001000, /* ipu restart */ + .fg_change = FG_CHANGE_ALL, /* change all initially */ + .fg0 = {32, 0, 0, 480, 272}, /* bpp, x, y, w, h */ + .fg1 = {32, 0, 0, 480, 272}, /* bpp, x, y, w, h */ + }, +#elif defined(CONFIG_JZ4750_LCD_AUO_A043FL01V2) + .panel = { + .cfg = LCD_CFG_LCDPIN_LCD | LCD_CFG_RECOVER | /* Underrun recover */ + LCD_CFG_NEWDES | /* 8words descriptor */ + LCD_CFG_MODE_GENERIC_TFT | /* General TFT panel */ + LCD_CFG_MODE_TFT_24BIT | /* output 18bpp */ + LCD_CFG_HSP | /* Hsync polarity: active low */ + LCD_CFG_VSP, /* Vsync polarity: leading edge is falling edge */ + .slcd_cfg = 0, + .ctrl = LCD_CTRL_OFUM | LCD_CTRL_BST_16, /* 16words burst, enable out FIFO underrun irq */ + 480, 272, 60, 41, 10, 8, 4, 4, 2, + }, + .osd = { + .osd_cfg = LCD_OSDC_OSDEN | /* Use OSD mode */ +// LCD_OSDC_ALPHAEN | /* enable alpha */ +// LCD_OSDC_F1EN | /* enable Foreground1 */ + LCD_OSDC_F0EN, /* enable Foreground0 */ + .osd_ctrl = 0, /* disable ipu, */ + .rgb_ctrl = 0, + .bgcolor = 0x000000, /* set background color Black */ + .colorkey0 = 0, /* disable colorkey */ + .colorkey1 = 0, /* disable colorkey */ + .alpha = 0xA0, /* alpha value */ + .ipu_restart = 0x80001000, /* ipu restart */ + .fg_change = FG_CHANGE_ALL, /* change all initially */ + .fg0 = {16, 0, 0, 480, 272}, /* bpp, x, y, w, h */ + .fg1 = {16, 0, 0, 480, 272}, /* bpp, x, y, w, h */ + }, +#elif defined(CONFIG_JZ4750_LCD_TOPPOLY_TD043MGEB1) + .panel = { + .cfg = LCD_CFG_LCDPIN_LCD | LCD_CFG_RECOVER | /* Underrun recover */ + LCD_CFG_NEWDES | /* 8words descriptor */ + LCD_CFG_MODE_GENERIC_TFT | /* General TFT panel */ + LCD_CFG_MODE_TFT_24BIT | /* output 18bpp */ + LCD_CFG_HSP | /* Hsync polarity: active low */ + LCD_CFG_VSP, /* Vsync polarity: leading edge is falling edge */ + .slcd_cfg = 0, + .ctrl = LCD_CTRL_OFUM | LCD_CTRL_BST_16, /* 16words burst, enable out FIFO underrun irq */ + 800, 480, 60, 1, 1, 40, 215, 10, 34, + }, + .osd = { + .osd_cfg = LCD_OSDC_OSDEN | /* Use OSD mode */ +// LCD_OSDC_ALPHAEN | /* enable alpha */ +// LCD_OSDC_F1EN | /* enable Foreground1 */ + LCD_OSDC_F0EN, /* enable Foreground0 */ + .osd_ctrl = 0, /* disable ipu, */ + .rgb_ctrl = 0, + .bgcolor = 0xff, /* set background color Black */ + .colorkey0 = 0, /* disable colorkey */ + .colorkey1 = 0, /* disable colorkey */ + .alpha = 0xA0, /* alpha value */ + .ipu_restart = 0x80001000, /* ipu restart */ + .fg_change = FG_CHANGE_ALL, /* change all initially */ + .fg0 = {32, 0, 0, 800, 480}, /* bpp, x, y, w, h */ + .fg1 = {32, 0, 0, 800, 480}, /* bpp, x, y, w, h */ + }, +#elif defined(CONFIG_JZ4750_LCD_TRULY_TFT_GG1P0319LTSW_W) + .panel = { + .cfg = LCD_CFG_LCDPIN_SLCD | /* Underrun recover*/ + LCD_CFG_NEWDES | /* 8words descriptor */ + LCD_CFG_MODE_SLCD, /* TFT Smart LCD panel */ + .slcd_cfg = SLCD_CFG_DWIDTH_16BIT | SLCD_CFG_CWIDTH_16BIT | SLCD_CFG_CS_ACTIVE_LOW | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING | SLCD_CFG_TYPE_PARALLEL, + .ctrl = LCD_CTRL_OFUM | LCD_CTRL_BST_16, /* 16words burst, enable out FIFO underrun irq */ + 240, 320, 60, 0, 0, 0, 0, 0, 0, + }, + .osd = { + .osd_cfg = LCD_OSDC_OSDEN | /* Use OSD mode */ +// LCD_OSDC_ALPHAEN | /* enable alpha */ +// LCD_OSDC_F1EN | /* enable Foreground0 */ + LCD_OSDC_F0EN, /* enable Foreground0 */ + .osd_ctrl = 0, /* disable ipu, */ + .rgb_ctrl = 0, + .bgcolor = 0x000000, /* set background color Black */ + .colorkey0 = 0, /* disable colorkey */ + .colorkey1 = 0, /* disable colorkey */ + .alpha = 0xA0, /* alpha value */ + .ipu_restart = 0x80001000, /* ipu restart */ + .fg_change = FG_CHANGE_ALL, /* change all initially */ + .fg0 = {32, 0, 0, 240, 320}, /* bpp, x, y, w, h */ + .fg1 = {32, 0, 0, 240, 320}, /* bpp, x, y, w, h */ + }, + +#elif defined(CONFIG_JZ4750_LCD_FOXCONN_PT035TN01) + .panel = { + .cfg = LCD_CFG_LCDPIN_LCD | LCD_CFG_RECOVER | /* Underrun recover */ + LCD_CFG_NEWDES | /* 8words descriptor */ + LCD_CFG_MODE_GENERIC_TFT | /* General TFT panel */ +// LCD_CFG_MODE_TFT_18BIT | /* output 18bpp */ + LCD_CFG_MODE_TFT_24BIT | /* output 24bpp */ + LCD_CFG_HSP | /* Hsync polarity: active low */ + LCD_CFG_VSP | /* Vsync polarity: leading edge is falling edge */ + LCD_CFG_PCP, /* Pix-CLK polarity: data translations at falling edge */ + .slcd_cfg = 0, + .ctrl = LCD_CTRL_OFUM | LCD_CTRL_BST_16, /* 16words burst, enable out FIFO underrun irq */ + 320, 240, 80, 1, 1, 10, 50, 10, 13 + }, + .osd = { + .osd_cfg = LCD_OSDC_OSDEN | /* Use OSD mode */ +// LCD_OSDC_ALPHAEN | /* enable alpha */ +// LCD_OSDC_F1EN | /* enable Foreground1 */ + LCD_OSDC_F0EN, /* enable Foreground0 */ + .osd_ctrl = 0, /* disable ipu, */ + .rgb_ctrl = 0, + .bgcolor = 0x000000, /* set background color Black */ + .colorkey0 = 0, /* disable colorkey */ + .colorkey1 = 0, /* disable colorkey */ + .alpha = 0xA0, /* alpha value */ + .ipu_restart = 0x80001000, /* ipu restart */ + .fg_change = FG_CHANGE_ALL, /* change all initially */ + .fg0 = {32, 0, 0, 320, 240}, /* bpp, x, y, w, h */ + .fg1 = {32, 0, 0, 320, 240}, /* bpp, x, y, w, h */ + }, +#elif defined(CONFIG_JZ4750_LCD_INNOLUX_PT035TN01_SERIAL) + .panel = { + .cfg = LCD_CFG_LCDPIN_LCD | LCD_CFG_RECOVER | /* Underrun recover */ + LCD_CFG_NEWDES | /* 8words descriptor */ + LCD_CFG_MODE_SERIAL_TFT | /* Serial TFT panel */ + LCD_CFG_MODE_TFT_18BIT | /* output 18bpp */ + LCD_CFG_HSP | /* Hsync polarity: active low */ + LCD_CFG_VSP | /* Vsync polarity: leading edge is falling edge */ + LCD_CFG_PCP, /* Pix-CLK polarity: data translations at falling edge */ + .slcd_cfg = 0, + .ctrl = LCD_CTRL_OFUM | LCD_CTRL_BST_16, /* 16words burst, enable out FIFO underrun irq */ + 320, 240, 60, 1, 1, 10, 50, 10, 13 + }, + .osd = { + .osd_cfg = LCD_OSDC_OSDEN | /* Use OSD mode */ +// LCD_OSDC_ALPHAEN | /* enable alpha */ + LCD_OSDC_F0EN, /* enable Foreground0 */ + .osd_ctrl = 0, /* disable ipu, */ + .rgb_ctrl = 0, + .bgcolor = 0x000000, /* set background color Black */ + .colorkey0 = 0, /* disable colorkey */ + .colorkey1 = 0, /* disable colorkey */ + .alpha = 0xA0, /* alpha value */ + .ipu_restart = 0x80001000, /* ipu restart */ + .fg_change = FG_CHANGE_ALL, /* change all initially */ + .fg0 = {32, 0, 0, 320, 240}, /* bpp, x, y, w, h */ + .fg1 = {32, 0, 0, 320, 240}, /* bpp, x, y, w, h */ + }, +#elif defined(CONFIG_JZ4750_SLCD_KGM701A3_TFT_SPFD5420A) + .panel = { +// .cfg = LCD_CFG_LCDPIN_SLCD | LCD_CFG_RECOVER | /* Underrun recover*/ + .cfg = LCD_CFG_LCDPIN_SLCD | /* Underrun recover*/ +// LCD_CFG_DITHER | /* dither */ + LCD_CFG_NEWDES | /* 8words descriptor */ + LCD_CFG_MODE_SLCD, /* TFT Smart LCD panel */ + .slcd_cfg = SLCD_CFG_DWIDTH_18BIT | SLCD_CFG_CWIDTH_18BIT | SLCD_CFG_CS_ACTIVE_LOW | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING | SLCD_CFG_TYPE_PARALLEL, + .ctrl = LCD_CTRL_OFUM | LCD_CTRL_BST_16, /* 16words burst, enable out FIFO underrun irq */ + 400, 240, 60, 0, 0, 0, 0, 0, 0, + }, + .osd = { + .osd_cfg = LCD_OSDC_OSDEN | /* Use OSD mode */ +// LCD_OSDC_ALPHAEN | /* enable alpha */ +// LCD_OSDC_ALPHAMD | /* alpha blending mode */ +// LCD_OSDC_F1EN | /* enable Foreground1 */ + LCD_OSDC_F0EN, /* enable Foreground0 */ + .osd_ctrl = 0, /* disable ipu, */ + .rgb_ctrl = 0, + .bgcolor = 0x000000, /* set background color Black */ + .colorkey0 = 0, /* disable colorkey */ + .colorkey1 = 0, /* disable colorkey */ + .alpha = 0xA0, /* alpha value */ + .ipu_restart = 0x80001000, /* ipu restart */ + .fg_change = FG_CHANGE_ALL, /* change all initially */ +// .fg0 = {32, 0, 0, 400, 240}, /* bpp, x, y, w, h */ + .fg0 = {32, 0, 0, 320, 240}, /* bpp, x, y, w, h */ + .fg1 = {32, 0, 0, 400, 240}, /* bpp, x, y, w, h */ + }, +#elif defined(CONFIG_JZ4750D_VGA_DISPLAY) + .panel = { + .cfg = LCD_CFG_LCDPIN_LCD | LCD_CFG_RECOVER |/* Underrun recover */ + LCD_CFG_NEWDES | /* 8words descriptor */ + LCD_CFG_MODE_GENERIC_TFT | /* General TFT panel */ + LCD_CFG_MODE_TFT_24BIT | /* output 18bpp */ + LCD_CFG_HSP | /* Hsync polarity: active low */ + LCD_CFG_VSP, /* Vsync polarity: leading edge is falling edge */ + .slcd_cfg = 0, + .ctrl = LCD_CTRL_OFUM | LCD_CTRL_BST_16, /* 16words burst, enable out FIFO underrun irq */ +// 800, 600, 60, 128, 4, 40, 88, 0, 23 + 640, 480, 54, 96, 2, 16, 48, 10, 33 +// 1280, 720, 50, 152, 15, 22, 200, 14, 1 + }, + .osd = { + .osd_cfg = LCD_OSDC_OSDEN | /* Use OSD mode */ +// LCD_OSDC_ALPHAEN | /* enable alpha */ +// LCD_OSDC_F1EN | /* enable Foreground1 */ + LCD_OSDC_F0EN, /* enable Foreground0 */ + .osd_ctrl = 0, /* disable ipu, */ + .rgb_ctrl = 0, + .bgcolor = 0x000000, /* set background color Black */ + .colorkey0 = 0, /* disable colorkey */ + .colorkey1 = 0, /* disable colorkey */ + .alpha = 0xA0, /* alpha value */ + .ipu_restart = 0x80001000, /* ipu restart */ + .fg_change = FG_CHANGE_ALL, /* change all initially */ + .fg0 = {32, 0, 0, 640, 480}, /* bpp, x, y, w, h */ + .fg1 = {32, 0, 0, 640, 480}, /* bpp, x, y, w, h */ + }, +#else +#error "Select LCD panel first!!!" +#endif +}; + +struct jz4750lcd_info jz4750_info_tve = { + .panel = { + .cfg = LCD_CFG_TVEN | /* output to tve */ + LCD_CFG_NEWDES | /* 8words descriptor */ + LCD_CFG_RECOVER | /* underrun protect */ + LCD_CFG_MODE_INTER_CCIR656, /* Interlace CCIR656 mode */ + .ctrl = LCD_CTRL_OFUM | LCD_CTRL_BST_16, /* 16words burst */ + TVE_WIDTH_PAL, TVE_HEIGHT_PAL, TVE_FREQ_PAL, 0, 0, 0, 0, 0, 0, + }, + .osd = { + .osd_cfg = LCD_OSDC_OSDEN | /* Use OSD mode */ +// LCD_OSDC_ALPHAEN | /* enable alpha */ + LCD_OSDC_F0EN, /* enable Foreground0 */ + .osd_ctrl = 0, /* disable ipu, */ + .rgb_ctrl = LCD_RGBC_YCC, /* enable RGB => YUV */ + .bgcolor = 0x00000000, /* set background color Black */ + .colorkey0 = 0, /* disable colorkey */ + .colorkey1 = 0, /* disable colorkey */ + .alpha = 0xA0, /* alpha value */ + .ipu_restart = 0x80000100, /* ipu restart */ + .fg_change = FG_CHANGE_ALL, /* change all initially */ + .fg0 = {32,}, /* */ + .fg0 = {32,}, + }, +}; + + +struct jz4750lcd_info *jz4750_lcd_info = &jz4750_lcd_panel; /* default output to lcd panel */ + +#ifdef DEBUG +static void print_lcdc_registers(void) /* debug */ +{ + /* LCD Controller Resgisters */ + printk("REG_LCD_CFG:\t0x%08x\n", REG_LCD_CFG); + printk("REG_LCD_CTRL:\t0x%08x\n", REG_LCD_CTRL); + printk("REG_LCD_STATE:\t0x%08x\n", REG_LCD_STATE); + printk("REG_LCD_OSDC:\t0x%08x\n", REG_LCD_OSDC); + printk("REG_LCD_OSDCTRL:\t0x%08x\n", REG_LCD_OSDCTRL); + printk("REG_LCD_OSDS:\t0x%08x\n", REG_LCD_OSDS); + printk("REG_LCD_BGC:\t0x%08x\n", REG_LCD_BGC); + printk("REG_LCD_KEK0:\t0x%08x\n", REG_LCD_KEY0); + printk("REG_LCD_KEY1:\t0x%08x\n", REG_LCD_KEY1); + printk("REG_LCD_ALPHA:\t0x%08x\n", REG_LCD_ALPHA); + printk("REG_LCD_IPUR:\t0x%08x\n", REG_LCD_IPUR); + printk("REG_LCD_VAT:\t0x%08x\n", REG_LCD_VAT); + printk("REG_LCD_DAH:\t0x%08x\n", REG_LCD_DAH); + printk("REG_LCD_DAV:\t0x%08x\n", REG_LCD_DAV); + printk("REG_LCD_XYP0:\t0x%08x\n", REG_LCD_XYP0); + printk("REG_LCD_XYP1:\t0x%08x\n", REG_LCD_XYP1); + printk("REG_LCD_SIZE0:\t0x%08x\n", REG_LCD_SIZE0); + printk("REG_LCD_SIZE1:\t0x%08x\n", REG_LCD_SIZE1); + printk("REG_LCD_RGBC\t0x%08x\n", REG_LCD_RGBC); + printk("REG_LCD_VSYNC:\t0x%08x\n", REG_LCD_VSYNC); + printk("REG_LCD_HSYNC:\t0x%08x\n", REG_LCD_HSYNC); + printk("REG_LCD_PS:\t0x%08x\n", REG_LCD_PS); + printk("REG_LCD_CLS:\t0x%08x\n", REG_LCD_CLS); + printk("REG_LCD_SPL:\t0x%08x\n", REG_LCD_SPL); + printk("REG_LCD_REV:\t0x%08x\n", REG_LCD_REV); + printk("REG_LCD_IID:\t0x%08x\n", REG_LCD_IID); + printk("REG_LCD_DA0:\t0x%08x\n", REG_LCD_DA0); + printk("REG_LCD_SA0:\t0x%08x\n", REG_LCD_SA0); + printk("REG_LCD_FID0:\t0x%08x\n", REG_LCD_FID0); + printk("REG_LCD_CMD0:\t0x%08x\n", REG_LCD_CMD0); + printk("REG_LCD_OFFS0:\t0x%08x\n", REG_LCD_OFFS0); + printk("REG_LCD_PW0:\t0x%08x\n", REG_LCD_PW0); + printk("REG_LCD_CNUM0:\t0x%08x\n", REG_LCD_CNUM0); + printk("REG_LCD_DESSIZE0:\t0x%08x\n", REG_LCD_DESSIZE0); + printk("REG_LCD_DA1:\t0x%08x\n", REG_LCD_DA1); + printk("REG_LCD_SA1:\t0x%08x\n", REG_LCD_SA1); + printk("REG_LCD_FID1:\t0x%08x\n", REG_LCD_FID1); + printk("REG_LCD_CMD1:\t0x%08x\n", REG_LCD_CMD1); + printk("REG_LCD_OFFS1:\t0x%08x\n", REG_LCD_OFFS1); + printk("REG_LCD_PW1:\t0x%08x\n", REG_LCD_PW1); + printk("REG_LCD_CNUM1:\t0x%08x\n", REG_LCD_CNUM1); + printk("REG_LCD_DESSIZE1:\t0x%08x\n", REG_LCD_DESSIZE1); + printk("==================================\n"); + printk("REG_LCD_VSYNC:\t%d:%d\n", REG_LCD_VSYNC>>16, REG_LCD_VSYNC&0xfff); + printk("REG_LCD_HSYNC:\t%d:%d\n", REG_LCD_HSYNC>>16, REG_LCD_HSYNC&0xfff); + printk("REG_LCD_VAT:\t%d:%d\n", REG_LCD_VAT>>16, REG_LCD_VAT&0xfff); + printk("REG_LCD_DAH:\t%d:%d\n", REG_LCD_DAH>>16, REG_LCD_DAH&0xfff); + printk("REG_LCD_DAV:\t%d:%d\n", REG_LCD_DAV>>16, REG_LCD_DAV&0xfff); + printk("==================================\n"); + + /* Smart LCD Controller Resgisters */ + printk("REG_SLCD_CFG:\t0x%08x\n", REG_SLCD_CFG); + printk("REG_SLCD_CTRL:\t0x%08x\n", REG_SLCD_CTRL); + printk("REG_SLCD_STATE:\t0x%08x\n", REG_SLCD_STATE); + printk("==================================\n"); + + /* TVE Controller Resgisters */ + printk("REG_TVE_CTRL:\t0x%08x\n", REG_TVE_CTRL); + printk("REG_TVE_FRCFG:\t0x%08x\n", REG_TVE_FRCFG); + printk("REG_TVE_SLCFG1:\t0x%08x\n", REG_TVE_SLCFG1); + printk("REG_TVE_SLCFG2:\t0x%08x\n", REG_TVE_SLCFG2); + printk("REG_TVE_SLCFG3:\t0x%08x\n", REG_TVE_SLCFG3); + printk("REG_TVE_LTCFG1:\t0x%08x\n", REG_TVE_LTCFG1); + printk("REG_TVE_LTCFG2:\t0x%08x\n", REG_TVE_LTCFG2); + printk("REG_TVE_CFREQ:\t0x%08x\n", REG_TVE_CFREQ); + printk("REG_TVE_CPHASE:\t0x%08x\n", REG_TVE_CPHASE); + printk("REG_TVE_CBCRCFG:\t0x%08x\n", REG_TVE_CBCRCFG); + printk("REG_TVE_WSSCR:\t0x%08x\n", REG_TVE_WSSCR); + printk("REG_TVE_WSSCFG1:\t0x%08x\n", REG_TVE_WSSCFG1); + printk("REG_TVE_WSSCFG2:\t0x%08x\n", REG_TVE_WSSCFG2); + printk("REG_TVE_WSSCFG3:\t0x%08x\n", REG_TVE_WSSCFG3); + + printk("==================================\n"); + + if ( 1 ) { + unsigned int * pii = (unsigned int *)dma_desc_base; + int i, j; + for (j=0;j< DMA_DESC_NUM ; j++) { + printk("dma_desc%d(0x%08x):\n", j, (unsigned int)pii); + for (i =0; i<8; i++ ) { + printk("\t\t0x%08x\n", *pii++); + } + } + } +} +#else +#define print_lcdc_registers() +#endif + +static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) +{ + chan &= 0xffff; + chan >>= 16 - bf->length; + return chan << bf->offset; +} + +static int jz4750fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info) +{ + struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; + unsigned short *ptr, ctmp; + +// print_dbg("regno:%d,RGBt:(%d,%d,%d,%d)\t", regno, red, green, blue, transp); + if (regno >= NR_PALETTE) + return 1; + + cfb->palette[regno].red = red ; + cfb->palette[regno].green = green; + cfb->palette[regno].blue = blue; + if (cfb->fb.var.bits_per_pixel <= 16) { + red >>= 8; + green >>= 8; + blue >>= 8; + + red &= 0xff; + green &= 0xff; + blue &= 0xff; + } + switch (cfb->fb.var.bits_per_pixel) { + case 1: + case 2: + case 4: + case 8: + if (((jz4750_lcd_info->panel.cfg & LCD_CFG_MODE_MASK) == LCD_CFG_MODE_SINGLE_MSTN ) || + ((jz4750_lcd_info->panel.cfg & LCD_CFG_MODE_MASK) == LCD_CFG_MODE_DUAL_MSTN )) { + ctmp = (77L * red + 150L * green + 29L * blue) >> 8; + ctmp = ((ctmp >> 3) << 11) | ((ctmp >> 2) << 5) | + (ctmp >> 3); + } else { + /* RGB 565 */ + if (((red >> 3) == 0) && ((red >> 2) != 0)) + red = 1 << 3; + if (((blue >> 3) == 0) && ((blue >> 2) != 0)) + blue = 1 << 3; + ctmp = ((red >> 3) << 11) + | ((green >> 2) << 5) | (blue >> 3); + } + + ptr = (unsigned short *)lcd_palette; + ptr = (unsigned short *)(((u32)ptr)|0xa0000000); + ptr[regno] = ctmp; + + break; + + case 15: + if (regno < 16) + ((u32 *)cfb->fb.pseudo_palette)[regno] = + ((red >> 3) << 10) | + ((green >> 3) << 5) | + (blue >> 3); + break; + case 16: + if (regno < 16) { + ((u32 *)cfb->fb.pseudo_palette)[regno] = + ((red >> 3) << 11) | + ((green >> 2) << 5) | + (blue >> 3); + } + break; + case 17 ... 32: + if (regno < 16) + ((u32 *)cfb->fb.pseudo_palette)[regno] = + (red << 16) | + (green << 8) | + (blue << 0); + +/* if (regno < 16) { + unsigned val; + val = chan_to_field(red, &cfb->fb.var.red); + val |= chan_to_field(green, &cfb->fb.var.green); + val |= chan_to_field(blue, &cfb->fb.var.blue); + ((u32 *)cfb->fb.pseudo_palette)[regno] = val; + } +*/ + + break; + } + return 0; +} + + +/* + * switch to tve mode from lcd mode + * mode: + * PANEL_MODE_TVE_PAL: switch to TVE_PAL mode + * PANEL_MODE_TVE_NTSC: switch to TVE_NTSC mode + */ +static void jz4750lcd_info_switch_to_TVE(int mode) +{ + struct jz4750lcd_info *info; + struct jz4750lcd_osd_t *osd_lcd; + int x, y, w, h; + + info = jz4750_lcd_info = &jz4750_info_tve; + osd_lcd = &jz4750_lcd_panel.osd; + + switch ( mode ) { + case PANEL_MODE_TVE_PAL: + info->panel.cfg |= LCD_CFG_TVEPEH; /* TVE PAL enable extra halfline signal */ + info->panel.w = TVE_WIDTH_PAL; + info->panel.h = TVE_HEIGHT_PAL; + info->panel.fclk = TVE_FREQ_PAL; + w = ( osd_lcd->fg0.w < TVE_WIDTH_PAL )? osd_lcd->fg0.w:TVE_WIDTH_PAL; + h = ( osd_lcd->fg0.h < TVE_HEIGHT_PAL )?osd_lcd->fg0.h:TVE_HEIGHT_PAL; + x = ((TVE_WIDTH_PAL - w) >> 2) << 1; + y = ((TVE_HEIGHT_PAL - h) >> 2) << 1; +// x = 0; +// y = 0; + info->osd.fg0.bpp = osd_lcd->fg0.bpp; + info->osd.fg0.x = x; + info->osd.fg0.y = y; + info->osd.fg0.w = w; + info->osd.fg0.h = h; + w = ( osd_lcd->fg1.w < TVE_WIDTH_PAL )? osd_lcd->fg1.w:TVE_WIDTH_PAL; + h = ( osd_lcd->fg1.h < TVE_HEIGHT_PAL )?osd_lcd->fg1.h:TVE_HEIGHT_PAL; + x = ((TVE_WIDTH_PAL-w) >> 2) << 1; + y = ((TVE_HEIGHT_PAL-h) >> 2) << 1; + info->osd.fg1.bpp = 32; /* use RGB888 in TVE mode*/ + info->osd.fg1.x = x; + info->osd.fg1.y = y; + info->osd.fg1.w = w; + info->osd.fg1.h = h; + break; + case PANEL_MODE_TVE_NTSC: + info->panel.cfg &= ~LCD_CFG_TVEPEH; /* TVE NTSC disable extra halfline signal */ + info->panel.w = TVE_WIDTH_NTSC; + info->panel.h = TVE_HEIGHT_NTSC; + info->panel.fclk = TVE_FREQ_NTSC; + w = ( osd_lcd->fg0.w < TVE_WIDTH_NTSC )? osd_lcd->fg0.w:TVE_WIDTH_NTSC; + h = ( osd_lcd->fg0.h < TVE_HEIGHT_NTSC)?osd_lcd->fg0.h:TVE_HEIGHT_NTSC; + x = ((TVE_WIDTH_NTSC - w) >> 2) << 1; + y = ((TVE_HEIGHT_NTSC - h) >> 2) << 1; +// x = 0; +// y = 0; + info->osd.fg0.bpp = osd_lcd->fg0.bpp; + info->osd.fg0.x = x; + info->osd.fg0.y = y; + info->osd.fg0.w = w; + info->osd.fg0.h = h; + w = ( osd_lcd->fg1.w < TVE_WIDTH_NTSC )? osd_lcd->fg1.w:TVE_WIDTH_NTSC; + h = ( osd_lcd->fg1.h < TVE_HEIGHT_NTSC)?osd_lcd->fg1.h:TVE_HEIGHT_NTSC; + x = ((TVE_WIDTH_NTSC - w) >> 2) << 1; + y = ((TVE_HEIGHT_NTSC - h) >> 2) << 1; + info->osd.fg1.bpp = 32; /* use RGB888 int TVE mode */ + info->osd.fg1.x = x; + info->osd.fg1.y = y; + info->osd.fg1.w = w; + info->osd.fg1.h = h; + break; + default: + printk("%s, %s: Unknown tve mode\n", __FILE__, __FUNCTION__); + } +} + +static int jz4750fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + + void __user *argp = (void __user *)arg; + +// struct jz4750lcd_info *lcd_info = jz4750_lcd_info; + + + switch (cmd) { + case FBIOSETBACKLIGHT: + __lcd_set_backlight_level(arg); /* We support 8 levels here. */ + break; + case FBIODISPON: + REG_LCD_STATE = 0; /* clear lcdc status */ + __lcd_slcd_special_on(); + __lcd_clr_dis(); + __lcd_set_ena(); /* enable lcdc */ + __lcd_display_on(); + break; + case FBIODISPOFF: + __lcd_display_off(); + if ( jz4750_lcd_info->panel.cfg & LCD_CFG_LCDPIN_SLCD || + jz4750_lcd_info->panel.cfg & LCD_CFG_TVEN ) /* */ + __lcd_clr_ena(); /* Smart lcd and TVE mode only support quick disable */ + else + __lcd_set_dis(); /* regular disable */ + break; + case FBIOPRINT_REG: + print_lcdc_registers(); + break; + case FBIO_GET_MODE: + print_dbg("fbio get mode\n"); + if (copy_to_user(argp, jz4750_lcd_info, sizeof(struct jz4750lcd_info))) + return -EFAULT; + break; + case FBIO_SET_MODE: + print_dbg("fbio set mode\n"); + if (copy_from_user(jz4750_lcd_info, argp, sizeof(struct jz4750lcd_info))) + return -EFAULT; + /* set mode */ + jz4750fb_set_mode(jz4750_lcd_info); + break; + case FBIO_DEEP_SET_MODE: + print_dbg("fbio deep set mode\n"); + if (copy_from_user(jz4750_lcd_info, argp, sizeof(struct jz4750lcd_info))) + return -EFAULT; + jz4750fb_deep_set_mode(jz4750_lcd_info); + break; +#ifdef CONFIG_FB_JZ4750_TVE + case FBIO_MODE_SWITCH: + print_dbg("lcd mode switch between tve and lcd, arg=%lu\n", arg); + switch ( arg ) { + case PANEL_MODE_TVE_PAL: /* switch to TVE_PAL mode */ + case PANEL_MODE_TVE_NTSC: /* switch to TVE_NTSC mode */ + jz4750lcd_info_switch_to_TVE(arg); + jz4750tve_init(arg); /* tve controller init */ + udelay(100); + jz4750tve_enable_tve(); + /* turn off lcd backlight */ + __lcd_display_off(); + break; + case PANEL_MODE_LCD_PANEL: /* switch to LCD mode */ + default : + /* turn off TVE, turn off DACn... */ + jz4750tve_disable_tve(); + jz4750_lcd_info = &jz4750_lcd_panel; + /* turn on lcd backlight */ + __lcd_display_on(); + break; + } + jz4750fb_deep_set_mode(jz4750_lcd_info); + break; + case FBIO_GET_TVE_MODE: + print_dbg("fbio get TVE mode\n"); + if (copy_to_user(argp, jz4750_tve_info, sizeof(struct jz4750tve_info))) + return -EFAULT; + break; + case FBIO_SET_TVE_MODE: + print_dbg("fbio set TVE mode\n"); + if (copy_from_user(jz4750_tve_info, argp, sizeof(struct jz4750tve_info))) + return -EFAULT; + /* set tve mode */ + jz4750tve_set_tve_mode(jz4750_tve_info); + break; +#endif + default: + printk("%s, unknown command(0x%x)", __FILE__, cmd); + break; + } + + return ret; +} + +/* Use mmap /dev/fb can only get a non-cacheable Virtual Address. */ +static int jz4750fb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; + unsigned long start; + unsigned long off; + u32 len; + dprintk("%s, %s, %d\n", __FILE__, __FUNCTION__, __LINE__); + off = vma->vm_pgoff << PAGE_SHIFT; + //fb->fb_get_fix(&fix, PROC_CONSOLE(info), info); + + /* frame buffer memory */ + start = cfb->fb.fix.smem_start; + len = PAGE_ALIGN((start & ~PAGE_MASK) + cfb->fb.fix.smem_len); + start &= PAGE_MASK; + + if ((vma->vm_end - vma->vm_start + off) > len) + return -EINVAL; + off += start; + + vma->vm_pgoff = off >> PAGE_SHIFT; + vma->vm_flags |= VM_IO; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); /* Uncacheable */ + +#if 1 + pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK; + pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED; /* Uncacheable */ +// pgprot_val(vma->vm_page_prot) |= _CACHE_CACHABLE_NONCOHERENT; /* Write-Back */ +#endif + + if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) { + return -EAGAIN; + } + return 0; +} + +/* checks var and eventually tweaks it to something supported, + * DO NOT MODIFY PAR */ +static int jz4750fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + printk("jz4750fb_check_var, not implement\n"); + return 0; +} + + +/* + * set the video mode according to info->var + */ +static int jz4750fb_set_par(struct fb_info *info) +{ + printk("jz4750fb_set_par, not implemented\n"); + return 0; +} + + +/* + * (Un)Blank the display. + * Fix me: should we use VESA value? + */ +static int jz4750fb_blank(int blank_mode, struct fb_info *info) +{ + dprintk("jz4750 fb_blank %d %p", blank_mode, info); + switch (blank_mode) { + case FB_BLANK_UNBLANK: + //case FB_BLANK_NORMAL: + /* Turn on panel */ + __lcd_set_ena(); + __lcd_display_on(); + break; + + case FB_BLANK_NORMAL: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_POWERDOWN: +#if 0 + /* Turn off panel */ + __lcd_display_off(); + __lcd_set_dis(); +#endif + break; + default: + break; + + } + return 0; +} + +/* + * pan display + */ +static int jz4750fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; + int dy; + + if (!var || !cfb) { + return -EINVAL; + } + + if (var->xoffset - cfb->fb.var.xoffset) { + /* No support for X panning for now! */ + return -EINVAL; + } + + dy = var->yoffset; + print_dbg("var.yoffset: %d", dy); + if (dy) { + dma1_desc0->databuf = (unsigned int)virt_to_phys((void *)lcd_frame0 + (cfb->fb.fix.line_length * dy)); + dma_cache_wback((unsigned int)(dma1_desc0), sizeof(struct jz4750_lcd_dma_desc)); + + } + else { + dma1_desc0->databuf = (unsigned int)virt_to_phys((void *)lcd_frame0); + dma_cache_wback((unsigned int)(dma1_desc0), sizeof(struct jz4750_lcd_dma_desc)); + } + + return 0; +} + + +/* use default function cfb_fillrect, cfb_copyarea, cfb_imageblit */ +static struct fb_ops jz4750fb_ops = { + .owner = THIS_MODULE, + .fb_setcolreg = jz4750fb_setcolreg, + .fb_check_var = jz4750fb_check_var, + .fb_set_par = jz4750fb_set_par, + .fb_blank = jz4750fb_blank, + .fb_pan_display = jz4750fb_pan_display, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_mmap = jz4750fb_mmap, + .fb_ioctl = jz4750fb_ioctl, +}; + +static int jz4750fb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; + struct jz4750lcd_info *lcd_info = jz4750_lcd_info; + int chgvar = 0; + + var->height = lcd_info->osd.fg0.h; /* tve mode */ + var->width = lcd_info->osd.fg0.w; + var->bits_per_pixel = lcd_info->osd.fg0.bpp; + + var->vmode = FB_VMODE_NONINTERLACED; + var->activate = cfb->fb.var.activate; + var->xres = var->width; + var->yres = var->height; + var->xres_virtual = var->width; + var->yres_virtual = var->height; + var->xoffset = 0; + var->yoffset = 0; + var->pixclock = 0; + var->left_margin = 0; + var->right_margin = 0; + var->upper_margin = 0; + var->lower_margin = 0; + var->hsync_len = 0; + var->vsync_len = 0; + var->sync = 0; + var->activate &= ~FB_ACTIVATE_TEST; + + /* + * CONUPDATE and SMOOTH_XPAN are equal. However, + * SMOOTH_XPAN is only used internally by fbcon. + */ + if (var->vmode & FB_VMODE_CONUPDATE) { + var->vmode |= FB_VMODE_YWRAP; + var->xoffset = cfb->fb.var.xoffset; + var->yoffset = cfb->fb.var.yoffset; + } + + if (var->activate & FB_ACTIVATE_TEST) + return 0; + + if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) + return -EINVAL; + + if (cfb->fb.var.xres != var->xres) + chgvar = 1; + if (cfb->fb.var.yres != var->yres) + chgvar = 1; + if (cfb->fb.var.xres_virtual != var->xres_virtual) + chgvar = 1; + if (cfb->fb.var.yres_virtual != var->yres_virtual) + chgvar = 1; + if (cfb->fb.var.bits_per_pixel != var->bits_per_pixel) + chgvar = 1; + + //display = fb_display + con; + + var->red.msb_right = 0; + var->green.msb_right = 0; + var->blue.msb_right = 0; + + switch(var->bits_per_pixel){ + case 1: /* Mono */ + cfb->fb.fix.visual = FB_VISUAL_MONO01; + cfb->fb.fix.line_length = (var->xres * var->bits_per_pixel) / 8; + break; + case 2: /* Mono */ + var->red.offset = 0; + var->red.length = 2; + var->green.offset = 0; + var->green.length = 2; + var->blue.offset = 0; + var->blue.length = 2; + + cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; + cfb->fb.fix.line_length = (var->xres * var->bits_per_pixel) / 8; + break; + case 4: /* PSEUDOCOLOUR*/ + var->red.offset = 0; + var->red.length = 4; + var->green.offset = 0; + var->green.length = 4; + var->blue.offset = 0; + var->blue.length = 4; + + cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; + cfb->fb.fix.line_length = var->xres / 2; + break; + case 8: /* PSEUDOCOLOUR, 256 */ + var->red.offset = 0; + var->red.length = 8; + var->green.offset = 0; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + + cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; + cfb->fb.fix.line_length = var->xres ; + break; + case 15: /* DIRECTCOLOUR, 32k */ + var->bits_per_pixel = 15; + var->red.offset = 10; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 5; + var->blue.offset = 0; + var->blue.length = 5; + + cfb->fb.fix.visual = FB_VISUAL_DIRECTCOLOR; + cfb->fb.fix.line_length = var->xres_virtual * 2; + break; + case 16: /* DIRECTCOLOUR, 64k */ + var->bits_per_pixel = 16; + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; + + cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR; + cfb->fb.fix.line_length = var->xres_virtual * 2; + break; + case 17 ... 32: + /* DIRECTCOLOUR, 256 */ + var->bits_per_pixel = 32; + + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 24; + var->transp.length = 8; + + cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR; + cfb->fb.fix.line_length = var->xres_virtual * 4; + break; + + default: /* in theory this should never happen */ + printk(KERN_WARNING "%s: don't support for %dbpp\n", + cfb->fb.fix.id, var->bits_per_pixel); + break; + } + + cfb->fb.var = *var; + cfb->fb.var.activate &= ~FB_ACTIVATE_ALL; + + /* + * Update the old var. The fbcon drivers still use this. + * Once they are using cfb->fb.var, this can be dropped. + * --rmk + */ + //display->var = cfb->fb.var; + /* + * If we are setting all the virtual consoles, also set the + * defaults used to create new consoles. + */ + fb_set_cmap(&cfb->fb.cmap, &cfb->fb); + dprintk("jz4750fb_set_var: after fb_set_cmap...\n"); + + return 0; +} + +static struct lcd_cfb_info * jz4750fb_alloc_fb_info(void) +{ + struct lcd_cfb_info *cfb; + + cfb = kmalloc(sizeof(struct lcd_cfb_info) + sizeof(u32) * 16, GFP_KERNEL); + + if (!cfb) + return NULL; + + jz4750fb_info = cfb; + + memset(cfb, 0, sizeof(struct lcd_cfb_info) ); + + cfb->currcon = -1; + + + strcpy(cfb->fb.fix.id, "jz-lcd"); + cfb->fb.fix.type = FB_TYPE_PACKED_PIXELS; + cfb->fb.fix.type_aux = 0; + cfb->fb.fix.xpanstep = 1; + cfb->fb.fix.ypanstep = 1; + cfb->fb.fix.ywrapstep = 0; + cfb->fb.fix.accel = FB_ACCEL_NONE; + + cfb->fb.var.nonstd = 0; + cfb->fb.var.activate = FB_ACTIVATE_NOW; + cfb->fb.var.height = -1; + cfb->fb.var.width = -1; + cfb->fb.var.accel_flags = FB_ACCELF_TEXT; + + cfb->fb.fbops = &jz4750fb_ops; + cfb->fb.flags = FBINFO_FLAG_DEFAULT; + + cfb->fb.pseudo_palette = (void *)(cfb + 1); + + switch (jz4750_lcd_info->osd.fg0.bpp) { + case 1: + fb_alloc_cmap(&cfb->fb.cmap, 4, 0); + break; + case 2: + fb_alloc_cmap(&cfb->fb.cmap, 8, 0); + break; + case 4: + fb_alloc_cmap(&cfb->fb.cmap, 32, 0); + break; + case 8: + + default: + fb_alloc_cmap(&cfb->fb.cmap, 256, 0); + break; + } + dprintk("fb_alloc_cmap,fb.cmap.len:%d....\n", cfb->fb.cmap.len); + + return cfb; +} + +/* + * Map screen memory + */ +static int jz4750fb_map_smem(struct lcd_cfb_info *cfb) +{ + unsigned long page; + unsigned int page_shift, needroom, needroom1, bpp, w, h; + + bpp = jz4750_lcd_info->osd.fg0.bpp; + if ( bpp == 18 || bpp == 24) + bpp = 32; + if ( bpp == 15 ) + bpp = 16; +#ifndef CONFIG_FB_JZ4750_TVE + w = jz4750_lcd_info->osd.fg0.w; + h = jz4750_lcd_info->osd.fg0.h; +#else + w = ( jz4750_lcd_info->osd.fg0.w > TVE_WIDTH_PAL )?jz4750_lcd_info->osd.fg0.w:TVE_WIDTH_PAL; + h = ( jz4750_lcd_info->osd.fg0.h > TVE_HEIGHT_PAL )?jz4750_lcd_info->osd.fg0.h:TVE_HEIGHT_PAL; +#endif + needroom1 = needroom = ((w * bpp + 7) >> 3) * h; +#if defined(CONFIG_FB_JZ4750_LCD_USE_2LAYER_FRAMEBUFFER) + bpp = jz4750_lcd_info->osd.fg1.bpp; + if ( bpp == 18 || bpp == 24) + bpp = 32; + if ( bpp == 15 ) + bpp = 16; + +#ifndef CONFIG_FB_JZ4750_TVE + w = jz4750_lcd_info->osd.fg1.w; + h = jz4750_lcd_info->osd.fg1.h; +#else + w = ( jz4750_lcd_info->osd.fg1.w > TVE_WIDTH_PAL )?jz4750_lcd_info->osd.fg1.w:TVE_WIDTH_PAL; + h = ( jz4750_lcd_info->osd.fg1.h > TVE_HEIGHT_PAL )?jz4750_lcd_info->osd.fg1.h:TVE_HEIGHT_PAL; +#endif + needroom += ((w * bpp + 7) >> 3) * h; +#endif // two layer + + + for (page_shift = 0; page_shift < 12; page_shift++) + if ((PAGE_SIZE << page_shift) >= needroom) + break; + lcd_palette = (unsigned char *)__get_free_pages(GFP_KERNEL, 0); + lcd_frame0 = (unsigned char *)__get_free_pages(GFP_KERNEL, page_shift); + + if ((!lcd_palette) || (!lcd_frame0)) + return -ENOMEM; + memset((void *)lcd_palette, 0, PAGE_SIZE); + memset((void *)lcd_frame0, 0, PAGE_SIZE << page_shift); + + dma_desc_base = (struct jz4750_lcd_dma_desc *)((void*)lcd_palette + ((PALETTE_SIZE+3)/4)*4); + +#if defined(CONFIG_FB_JZ4750_SLCD) + + lcd_cmdbuf = (unsigned char *)__get_free_pages(GFP_KERNEL, 0); + memset((void *)lcd_cmdbuf, 0, PAGE_SIZE); + + { int data, i, *ptr; + ptr = (unsigned int *)lcd_cmdbuf; + data = WR_GRAM_CMD; + data = ((data & 0xff) << 1) | ((data & 0xff00) << 2); + for(i = 0; i < 3; i++){ + ptr[i] = data; + } + } +#endif + +#if defined(CONFIG_FB_JZ4750_LCD_USE_2LAYER_FRAMEBUFFER) + lcd_frame1 = lcd_frame0 + needroom1; +#endif + + /* + * Set page reserved so that mmap will work. This is necessary + * since we'll be remapping normal memory. + */ + page = (unsigned long)lcd_palette; + SetPageReserved(virt_to_page((void*)page)); + + for (page = (unsigned long)lcd_frame0; + page < PAGE_ALIGN((unsigned long)lcd_frame0 + (PAGE_SIZE<fb.fix.smem_start = virt_to_phys((void *)lcd_frame0); + cfb->fb.fix.smem_len = (PAGE_SIZE << page_shift); /* page_shift/2 ??? */ + cfb->fb.screen_base = + (unsigned char *)(((unsigned int)lcd_frame0&0x1fffffff) | 0xa0000000); + + if (!cfb->fb.screen_base) { + printk("jz4750fb, %s: unable to map screen memory\n", cfb->fb.fix.id); + return -ENOMEM; + } + + return 0; +} + +static void jz4750fb_free_fb_info(struct lcd_cfb_info *cfb) +{ + if (cfb) { + fb_alloc_cmap(&cfb->fb.cmap, 0, 0); + kfree(cfb); + } +} + +static void jz4750fb_unmap_smem(struct lcd_cfb_info *cfb) +{ + struct page * map = NULL; + unsigned char *tmp; + unsigned int page_shift, needroom, bpp, w, h; + + bpp = jz4750_lcd_info->osd.fg0.bpp; + if ( bpp == 18 || bpp == 24) + bpp = 32; + if ( bpp == 15 ) + bpp = 16; + w = jz4750_lcd_info->osd.fg0.w; + h = jz4750_lcd_info->osd.fg0.h; + needroom = ((w * bpp + 7) >> 3) * h; +#if defined(CONFIG_FB_JZ4750_LCD_USE_2LAYER_FRAMEBUFFER) + bpp = jz4750_lcd_info->osd.fg1.bpp; + if ( bpp == 18 || bpp == 24) + bpp = 32; + if ( bpp == 15 ) + bpp = 16; + w = jz4750_lcd_info->osd.fg1.w; + h = jz4750_lcd_info->osd.fg1.h; + needroom += ((w * bpp + 7) >> 3) * h; +#endif + + for (page_shift = 0; page_shift < 12; page_shift++) + if ((PAGE_SIZE << page_shift) >= needroom) + break; + + if (cfb && cfb->fb.screen_base) { + iounmap(cfb->fb.screen_base); + cfb->fb.screen_base = NULL; + release_mem_region(cfb->fb.fix.smem_start, + cfb->fb.fix.smem_len); + } + + if (lcd_palette) { + map = virt_to_page(lcd_palette); + clear_bit(PG_reserved, &map->flags); + free_pages((int)lcd_palette, 0); + } + + if (lcd_frame0) { + for (tmp=(unsigned char *)lcd_frame0; + tmp < lcd_frame0 + (PAGE_SIZE << page_shift); + tmp += PAGE_SIZE) { + map = virt_to_page(tmp); + clear_bit(PG_reserved, &map->flags); + } + free_pages((int)lcd_frame0, page_shift); + } +} + +/* initial dma descriptors */ +static void jz4750fb_descriptor_init( struct jz4750lcd_info * lcd_info ) +{ + unsigned int pal_size; + + switch ( lcd_info->osd.fg0.bpp ) { + case 1: + pal_size = 4; + break; + case 2: + pal_size = 8; + break; + case 4: + pal_size = 32; + break; + case 8: + default: + pal_size = 512; + } + + pal_size /= 4; + + dma0_desc_palette = dma_desc_base + 0; + dma0_desc0 = dma_desc_base + 1; + dma0_desc1 = dma_desc_base + 2; + dma0_desc_cmd0 = dma_desc_base + 3; /* use only once */ + dma0_desc_cmd = dma_desc_base + 4; + dma1_desc0 = dma_desc_base + 5; + dma1_desc1 = dma_desc_base + 6; + + /* + * Normal TFT panel's DMA Chan0: + * TO LCD Panel: + * no palette: dma0_desc0 <<==>> dma0_desc0 + * palette : dma0_desc_palette <<==>> dma0_desc0 + * TO TV Encoder: + * no palette: dma0_desc0 <<==>> dma0_desc1 + * palette: dma0_desc_palette --> dma0_desc0 + * --> dma0_desc1 --> dma0_desc_palette --> ... + * + * SMART LCD TFT panel(dma0_desc_cmd)'s DMA Chan0: + * TO LCD Panel: + * no palette: dma0_desc_cmd <<==>> dma0_desc0 + * palette : dma0_desc_palette --> dma0_desc_cmd + * --> dma0_desc0 --> dma0_desc_palette --> ... + * TO TV Encoder: + * no palette: dma0_desc_cmd --> dma0_desc0 + * --> dma0_desc1 --> dma0_desc_cmd --> ... + * palette: dma0_desc_palette --> dma0_desc_cmd + * --> dma0_desc0 --> dma0_desc1 + * --> dma0_desc_palette --> ... + * DMA Chan1: + * TO LCD Panel: + * dma1_desc0 <<==>> dma1_desc0 + * TO TV Encoder: + * dma1_desc0 <<==>> dma1_desc1 + */ + +#if defined(CONFIG_FB_JZ4750_SLCD) + /* First CMD descriptors, use only once, cmd_num isn't 0 */ + dma0_desc_cmd0->next_desc = (unsigned int)virt_to_phys(dma0_desc0); + dma0_desc_cmd0->databuf = (unsigned int)virt_to_phys((void *)lcd_cmdbuf); + dma0_desc_cmd0->frame_id = (unsigned int)0x0da0cad0; /* dma0's cmd0 */ + dma0_desc_cmd0->cmd = LCD_CMD_CMD | 3; /* command */ + dma0_desc_cmd0->offsize = 0; + dma0_desc_cmd0->page_width = 0; + dma0_desc_cmd0->cmd_num = 3; + + /* Dummy Command Descriptor, cmd_num is 0 */ + dma0_desc_cmd->next_desc = (unsigned int)virt_to_phys(dma0_desc0); + dma0_desc_cmd->databuf = 0; + dma0_desc_cmd->frame_id = (unsigned int)0x0da000cd; /* dma0's cmd0 */ + dma0_desc_cmd->cmd = LCD_CMD_CMD | 0; /* dummy command */ + dma0_desc_cmd->cmd_num = 0; + dma0_desc_cmd->offsize = 0; + dma0_desc_cmd->page_width = 0; + + /* Palette Descriptor */ + dma0_desc_palette->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd0); +#else + /* Palette Descriptor */ + dma0_desc_palette->next_desc = (unsigned int)virt_to_phys(dma0_desc0); +#endif + dma0_desc_palette->databuf = (unsigned int)virt_to_phys((void *)lcd_palette); + dma0_desc_palette->frame_id = (unsigned int)0xaaaaaaaa; + dma0_desc_palette->cmd = LCD_CMD_PAL | pal_size; /* Palette Descriptor */ + + /* DMA0 Descriptor0 */ + if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) /* TVE mode */ + dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc1); + else{ /* Normal TFT LCD */ +#if defined(CONFIG_FB_JZ4750_SLCD) + dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd); +#else + dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc0); +#endif + } + + dma0_desc0->databuf = virt_to_phys((void *)lcd_frame0); + dma0_desc0->frame_id = (unsigned int)0x0000da00; /* DMA0'0 */ + + /* DMA0 Descriptor1 */ + if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) { /* TVE mode */ + + + if (lcd_info->osd.fg0.bpp <= 8) /* load palette only once at setup */ + dma0_desc1->next_desc = (unsigned int)virt_to_phys(dma0_desc_palette); + else +#if defined(CONFIG_FB_JZ4750_SLCD) /* for smatlcd */ + dma0_desc1->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd); +#else + dma0_desc1->next_desc = (unsigned int)virt_to_phys(dma0_desc0); +#endif + dma0_desc1->frame_id = (unsigned int)0x0000da01; /* DMA0'1 */ + } + + if (lcd_info->osd.fg0.bpp <= 8) /* load palette only once at setup */ + REG_LCD_DA0 = virt_to_phys(dma0_desc_palette); + else { +#if defined(CONFIG_FB_JZ4750_SLCD) /* for smartlcd */ + REG_LCD_DA0 = virt_to_phys(dma0_desc_cmd0); //smart lcd +#else + REG_LCD_DA0 = virt_to_phys(dma0_desc0); //tft +#endif + } + + /* DMA1 Descriptor0 */ + if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) /* TVE mode */ + dma1_desc0->next_desc = (unsigned int)virt_to_phys(dma1_desc1); + else /* Normal TFT LCD */ + dma1_desc0->next_desc = (unsigned int)virt_to_phys(dma1_desc0); + + dma1_desc0->databuf = virt_to_phys((void *)lcd_frame1); + dma1_desc0->frame_id = (unsigned int)0x0000da10; /* DMA1'0 */ + + /* DMA1 Descriptor1 */ + if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) { /* TVE mode */ + dma1_desc1->next_desc = (unsigned int)virt_to_phys(dma1_desc0); + dma1_desc1->frame_id = (unsigned int)0x0000da11; /* DMA1'1 */ + } + + REG_LCD_DA1 = virt_to_phys(dma1_desc0); /* set Dma-chan1's Descripter Addrress */ + dma_cache_wback_inv((unsigned int)(dma_desc_base), (DMA_DESC_NUM)*sizeof(struct jz4750_lcd_dma_desc)); + +#if 0 + /* Palette Descriptor */ + if ( lcd_info->panel.cfg & LCD_CFG_LCDPIN_SLCD ) +// dma0_desc_palette->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd); + dma0_desc_palette->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd1); + else + dma0_desc_palette->next_desc = (unsigned int)virt_to_phys(dma0_desc0); + dma0_desc_palette->databuf = (unsigned int)virt_to_phys((void *)lcd_palette); + dma0_desc_palette->frame_id = (unsigned int)0xaaaaaaaa; + dma0_desc_palette->cmd = LCD_CMD_PAL | pal_size; /* Palette Descriptor */ + + /* Dummy Command Descriptor, cmd_num is 0 */ + dma0_desc_cmd->next_desc = (unsigned int)virt_to_phys(dma0_desc0); + dma0_desc_cmd->databuf = (unsigned int)virt_to_phys((void *)lcd_cmdbuf); + dma0_desc_cmd->frame_id = (unsigned int)0x0da0cad0; /* dma0's cmd0 */ + dma0_desc_cmd->cmd = LCD_CMD_CMD | 3; /* dummy command */ + dma0_desc_cmd->offsize = 0; /* dummy command */ + dma0_desc_cmd->page_width = 0; /* dummy command */ + dma0_desc_cmd->cmd_num = 3; + +//--------------------------------- + dma0_desc_cmd1->next_desc = (unsigned int)virt_to_phys(dma0_desc0); + dma0_desc_cmd1->databuf = 0; + dma0_desc_cmd1->frame_id = (unsigned int)0x0da0cad1; /* dma0's cmd0 */ + dma0_desc_cmd1->cmd = LCD_CMD_CMD | 0; /* dummy command */ + dma0_desc_cmd1->cmd_num = 0; + dma0_desc_cmd1->offsize = 0; /* dummy command */ + dma0_desc_cmd1->page_width = 0; /* dummy command */ +//----------------------------------- + /* DMA0 Descriptor0 */ + if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) /* TVE mode */ + dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc1); + else{ /* Normal TFT LCD */ + if (lcd_info->osd.fg0.bpp <= 8) /* load palette only once at setup?? */ +// dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc_palette); //tft + dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd); // smart lcd + else if ( lcd_info->panel.cfg & LCD_CFG_LCDPIN_SLCD ) + dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd1); +// dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd); + else + dma0_desc0->next_desc = (unsigned int)virt_to_phys(dma0_desc0); + } + + dma0_desc0->databuf = virt_to_phys((void *)lcd_frame0); + dma0_desc0->frame_id = (unsigned int)0x0000da00; /* DMA0'0 */ + + /* DMA0 Descriptor1 */ + if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) { /* TVE mode */ + if (lcd_info->osd.fg0.bpp <= 8) /* load palette only once at setup?? */ + dma0_desc1->next_desc = (unsigned int)virt_to_phys(dma0_desc_palette); + + else if ( lcd_info->panel.cfg & LCD_CFG_LCDPIN_SLCD ) + dma0_desc1->next_desc = (unsigned int)virt_to_phys(dma0_desc_cmd); + else + dma0_desc1->next_desc = (unsigned int)virt_to_phys(dma0_desc0); + dma0_desc1->frame_id = (unsigned int)0x0000da01; /* DMA0'1 */ + } + + /* DMA1 Descriptor0 */ + if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) /* TVE mode */ + dma1_desc0->next_desc = (unsigned int)virt_to_phys(dma1_desc1); + else /* Normal TFT LCD */ + dma1_desc0->next_desc = (unsigned int)virt_to_phys(dma1_desc0); + + dma1_desc0->databuf = virt_to_phys((void *)lcd_frame1); + dma1_desc0->frame_id = (unsigned int)0x0000da10; /* DMA1'0 */ + + /* DMA1 Descriptor1 */ + if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) { /* TVE mode */ + dma1_desc1->next_desc = (unsigned int)virt_to_phys(dma1_desc0); + dma1_desc1->frame_id = (unsigned int)0x0000da11; /* DMA1'1 */ + } + + if (lcd_info->osd.fg0.bpp <= 8) /* load palette only once at setup?? */ + REG_LCD_DA0 = virt_to_phys(dma0_desc_palette); + else +// REG_LCD_DA0 = virt_to_phys(dma0_desc_cmd); //smart lcd + REG_LCD_DA0 = virt_to_phys(dma0_desc0); //tft + REG_LCD_DA1 = virt_to_phys(dma1_desc0); /* set Dma-chan1's Descripter Addrress */ + dma_cache_wback_inv((unsigned int)(dma_desc_base), (DMA_DESC_NUM)*sizeof(struct jz4750_lcd_dma_desc)); +#endif +} + +static void jz4750fb_set_panel_mode( struct jz4750lcd_info * lcd_info ) +{ + struct jz4750lcd_panel_t *panel = &lcd_info->panel; +#ifdef CONFIG_JZ4750D_VGA_DISPLAY + REG_TVE_CTRL |= TVE_CTRL_DAPD; + REG_TVE_CTRL &= ~( TVE_CTRL_DAPD1 | TVE_CTRL_DAPD2 | TVE_CTRL_DAPD3); +#endif + /* set bpp */ + lcd_info->panel.ctrl &= ~LCD_CTRL_BPP_MASK; + if ( lcd_info->osd.fg0.bpp == 1 ) + lcd_info->panel.ctrl |= LCD_CTRL_BPP_1; + else if ( lcd_info->osd.fg0.bpp == 2 ) + lcd_info->panel.ctrl |= LCD_CTRL_BPP_2; + else if ( lcd_info->osd.fg0.bpp == 4 ) + lcd_info->panel.ctrl |= LCD_CTRL_BPP_4; + else if ( lcd_info->osd.fg0.bpp == 8 ) + lcd_info->panel.ctrl |= LCD_CTRL_BPP_8; + else if ( lcd_info->osd.fg0.bpp == 15 ) + lcd_info->panel.ctrl |= LCD_CTRL_BPP_16 | LCD_CTRL_RGB555; + else if ( lcd_info->osd.fg0.bpp == 16 ) + lcd_info->panel.ctrl |= LCD_CTRL_BPP_16 | LCD_CTRL_RGB565; + else if ( lcd_info->osd.fg0.bpp > 16 && lcd_info->osd.fg0.bpp < 32+1 ) { + lcd_info->osd.fg0.bpp = 32; + lcd_info->panel.ctrl |= LCD_CTRL_BPP_18_24; + } + else { + printk("The BPP %d is not supported\n", lcd_info->osd.fg0.bpp); + lcd_info->osd.fg0.bpp = 32; + lcd_info->panel.ctrl |= LCD_CTRL_BPP_18_24; + } + + lcd_info->panel.cfg |= LCD_CFG_NEWDES; /* use 8words descriptor always */ + + REG_LCD_CTRL = lcd_info->panel.ctrl; /* LCDC Controll Register */ + REG_LCD_CFG = lcd_info->panel.cfg; /* LCDC Configure Register */ + REG_SLCD_CFG = lcd_info->panel.slcd_cfg; /* Smart LCD Configure Register */ + + if ( lcd_info->panel.cfg & LCD_CFG_LCDPIN_SLCD ) /* enable Smart LCD DMA */ + REG_SLCD_CTRL = SLCD_CTRL_DMA_EN; + + switch ( lcd_info->panel.cfg & LCD_CFG_MODE_MASK ) { + case LCD_CFG_MODE_GENERIC_TFT: + case LCD_CFG_MODE_INTER_CCIR656: + case LCD_CFG_MODE_NONINTER_CCIR656: + case LCD_CFG_MODE_SLCD: + default: /* only support TFT16 TFT32, not support STN and Special TFT by now(10-06-2008)*/ + REG_LCD_VAT = (((panel->blw + panel->w + panel->elw + panel->hsw)) << 16) | (panel->vsw + panel->bfw + panel->h + panel->efw); + REG_LCD_DAH = ((panel->hsw + panel->blw) << 16) | (panel->hsw + panel->blw + panel->w); + REG_LCD_DAV = ((panel->vsw + panel->bfw) << 16) | (panel->vsw + panel->bfw + panel->h); + REG_LCD_HSYNC = (0 << 16) | panel->hsw; + REG_LCD_VSYNC = (0 << 16) | panel->vsw; + break; + } +} + + +static void jz4750fb_set_osd_mode( struct jz4750lcd_info * lcd_info ) +{ + dprintk("%s, %d\n", __FILE__, __LINE__ ); + lcd_info->osd.osd_ctrl &= ~(LCD_OSDCTRL_OSDBPP_MASK); + if ( lcd_info->osd.fg1.bpp == 15 ) + lcd_info->osd.osd_ctrl |= LCD_OSDCTRL_OSDBPP_15_16|LCD_OSDCTRL_RGB555; + else if ( lcd_info->osd.fg1.bpp == 16 ) + lcd_info->osd.osd_ctrl |= LCD_OSDCTRL_OSDBPP_15_16|LCD_OSDCTRL_RGB565; + else { + lcd_info->osd.fg1.bpp = 32; + lcd_info->osd.osd_ctrl |= LCD_OSDCTRL_OSDBPP_18_24; + } + + REG_LCD_OSDC = lcd_info->osd.osd_cfg; /* F0, F1, alpha, */ + + REG_LCD_OSDCTRL = lcd_info->osd.osd_ctrl; /* IPUEN, bpp */ + REG_LCD_RGBC = lcd_info->osd.rgb_ctrl; + REG_LCD_BGC = lcd_info->osd.bgcolor; + REG_LCD_KEY0 = lcd_info->osd.colorkey0; + REG_LCD_KEY1 = lcd_info->osd.colorkey1; + REG_LCD_ALPHA = lcd_info->osd.alpha; + REG_LCD_IPUR = lcd_info->osd.ipu_restart; +} + +static void jz4750fb_foreground_resize( struct jz4750lcd_info * lcd_info ) +{ + int fg0_line_size, fg0_frm_size, fg1_line_size, fg1_frm_size; + /* + * NOTE: + * Foreground change sequence: + * 1. Change Position Registers -> LCD_OSDCTL.Change; + * 2. LCD_OSDCTRL.Change -> descripter->Size + * Foreground, only one of the following can be change at one time: + * 1. F0 size; + * 2. F0 position + * 3. F1 size + * 4. F1 position + */ + + /* + * The rules of f0, f1's position: + * f0.x + f0.w <= panel.w; + * f0.y + f0.h <= panel.h; + * + * When output is LCD panel, fg.y and fg.h can be odd number or even number. + * When output is TVE, as the TVE has odd frame and even frame, + * to simplified operation, fg.y and fg.h should be even number always. + * + */ + + /* Foreground 0 */ + if ( lcd_info->osd.fg0.x >= lcd_info->panel.w ) + lcd_info->osd.fg0.x = lcd_info->panel.w; + if ( lcd_info->osd.fg0.y >= lcd_info->panel.h ) + lcd_info->osd.fg0.y = lcd_info->panel.h; + if ( lcd_info->osd.fg0.x + lcd_info->osd.fg0.w > lcd_info->panel.w ) + lcd_info->osd.fg0.w = lcd_info->panel.w - lcd_info->osd.fg0.x; + if ( lcd_info->osd.fg0.y + lcd_info->osd.fg0.h > lcd_info->panel.h ) + lcd_info->osd.fg0.h = lcd_info->panel.h - lcd_info->osd.fg0.y; + /* Foreground 1 */ + /* Case TVE ??? TVE 720x573 or 720x480*/ + if ( lcd_info->osd.fg1.x >= lcd_info->panel.w ) + lcd_info->osd.fg1.x = lcd_info->panel.w; + if ( lcd_info->osd.fg1.y >= lcd_info->panel.h ) + lcd_info->osd.fg1.y = lcd_info->panel.h; + if ( lcd_info->osd.fg1.x + lcd_info->osd.fg1.w > lcd_info->panel.w ) + lcd_info->osd.fg1.w = lcd_info->panel.w - lcd_info->osd.fg1.x; + if ( lcd_info->osd.fg1.y + lcd_info->osd.fg1.h > lcd_info->panel.h ) + lcd_info->osd.fg1.h = lcd_info->panel.h - lcd_info->osd.fg1.y; + +// fg0_line_size = lcd_info->osd.fg0.w*((lcd_info->osd.fg0.bpp+7)/8); + fg0_line_size = (lcd_info->osd.fg0.w*(lcd_info->osd.fg0.bpp)/8); + fg0_line_size = ((fg0_line_size+3)>>2)<<2; /* word aligned */ + fg0_frm_size = fg0_line_size * lcd_info->osd.fg0.h; + + fg1_line_size = lcd_info->osd.fg1.w*((lcd_info->osd.fg1.bpp+7)/8); + fg1_line_size = ((fg1_line_size+3)>>2)<<2; /* word aligned */ + fg1_frm_size = fg1_line_size * lcd_info->osd.fg1.h; + + if ( lcd_info->osd.fg_change ) { + if ( lcd_info->osd.fg_change & FG0_CHANGE_POSITION ) { /* F1 change position */ + REG_LCD_XYP0 = lcd_info->osd.fg0.y << 16 | lcd_info->osd.fg0.x; + } + if ( lcd_info->osd.fg_change & FG1_CHANGE_POSITION ) { /* F1 change position */ + REG_LCD_XYP1 = lcd_info->osd.fg1.y << 16 | lcd_info->osd.fg1.x; + } + + /* set change */ + if ( !(lcd_info->osd.osd_ctrl & LCD_OSDCTRL_IPU) && + (lcd_info->osd.fg_change != FG_CHANGE_ALL) ) + REG_LCD_OSDCTRL |= LCD_OSDCTRL_CHANGES; + + /* wait change ready??? */ +// while ( REG_LCD_OSDS & LCD_OSDS_READY ) /* fix in the future, Wolfgang, 06-20-2008 */ + print_dbg("wait LCD_OSDS_READY\n"); + + if ( lcd_info->osd.fg_change & FG0_CHANGE_SIZE ) { /* change FG0 size */ + if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) { /* output to TV */ + dma0_desc0->cmd = dma0_desc1->cmd = (fg0_frm_size/4)/2; + dma0_desc0->offsize = dma0_desc1->offsize + = fg0_line_size/4; + dma0_desc0->page_width = dma0_desc1->page_width + = fg0_line_size/4; + dma0_desc1->databuf = virt_to_phys((void *)(lcd_frame0 + fg0_line_size)); + REG_LCD_DA0 = virt_to_phys(dma0_desc0); //tft + } + else { + dma0_desc0->cmd = dma0_desc1->cmd = fg0_frm_size/4; + dma0_desc0->offsize = dma0_desc1->offsize =0; + dma0_desc0->page_width = dma0_desc1->page_width = 0; + } + + dma0_desc0->desc_size = dma0_desc1->desc_size + = lcd_info->osd.fg0.h << 16 | lcd_info->osd.fg0.w; + REG_LCD_SIZE0 = (lcd_info->osd.fg0.h<<16)|lcd_info->osd.fg0.w; + + } + + if ( lcd_info->osd.fg_change & FG1_CHANGE_SIZE ) { /* change FG1 size*/ + if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) { /* output to TV */ + dma1_desc0->cmd = dma1_desc1->cmd = (fg1_frm_size/4)/2; + dma1_desc0->offsize = dma1_desc1->offsize = fg1_line_size/4; + dma1_desc0->page_width = dma1_desc1->page_width = fg1_line_size/4; + dma1_desc1->databuf = virt_to_phys((void *)(lcd_frame1 + fg1_line_size)); + REG_LCD_DA1 = virt_to_phys(dma0_desc1); //tft + + } + else { + dma1_desc0->cmd = dma1_desc1->cmd = fg1_frm_size/4; + dma1_desc0->offsize = dma1_desc1->offsize = 0; + dma1_desc0->page_width = dma1_desc1->page_width = 0; + } + + dma1_desc0->desc_size = dma1_desc1->desc_size + = lcd_info->osd.fg1.h << 16 | lcd_info->osd.fg1.w; + REG_LCD_SIZE1 = lcd_info->osd.fg1.h << 16|lcd_info->osd.fg1.w; + } + + dma_cache_wback((unsigned int)(dma_desc_base), (DMA_DESC_NUM)*sizeof(struct jz4750_lcd_dma_desc)); + lcd_info->osd.fg_change = FG_NOCHANGE; /* clear change flag */ + } +} + +static void jz4750fb_change_clock( struct jz4750lcd_info * lcd_info ) +{ + +#if defined(CONFIG_FPGA) + REG_LCD_REV = 0x00000004; + printk("Fuwa test, pixclk divide REG_LCD_REV=0x%08x\n", REG_LCD_REV); + printk("Fuwa test, pixclk %d\n", JZ_EXTAL/(((REG_LCD_REV&0xFF)+1)*2)); +#else + unsigned int val = 0; + unsigned int pclk; + /* Timing setting */ + __cpm_stop_lcd(); + + val = lcd_info->panel.fclk; /* frame clk */ + + if ( (lcd_info->panel.cfg & LCD_CFG_MODE_MASK) != LCD_CFG_MODE_SERIAL_TFT) { + pclk = val * (lcd_info->panel.w + lcd_info->panel.hsw + lcd_info->panel.elw + lcd_info->panel.blw) * (lcd_info->panel.h + lcd_info->panel.vsw + lcd_info->panel.efw + lcd_info->panel.bfw); /* Pixclk */ + } + else { + /* serial mode: Hsync period = 3*Width_Pixel */ + pclk = val * (lcd_info->panel.w*3 + lcd_info->panel.hsw + lcd_info->panel.elw + lcd_info->panel.blw) * (lcd_info->panel.h + lcd_info->panel.vsw + lcd_info->panel.efw + lcd_info->panel.bfw); /* Pixclk */ + } + + /********* In TVE mode PCLK = 27MHz ***********/ + if ( lcd_info->panel.cfg & LCD_CFG_TVEN ) { /* LCDC output to TVE */ + REG_CPM_LPCDR |= CPM_LPCDR_LTCS; + pclk = 27000000; + val = __cpm_get_pllout2() / pclk; /* pclk */ + val--; + __cpm_set_pixdiv(val); + + dprintk("REG_CPM_LPCDR = 0x%08x\n", REG_CPM_LPCDR); +#if defined(CONFIG_SOC_JZ4750) /* Jz4750D don't use LCLK */ + val = pclk * 3 ; /* LCDClock > 2.5*Pixclock */ + + val =(__cpm_get_pllout()) / val; + if ( val > 0x1f ) { + printk("lcd clock divide is too large, set it to 0x1f\n"); + val = 0x1f; + } + __cpm_set_ldiv( val ); +#endif + __cpm_select_pixclk_tve(); + + REG_CPM_CPCCR |= CPM_CPCCR_CE ; /* update divide */ + } + else { /* LCDC output to LCD panel */ + val = __cpm_get_pllout2() / pclk; /* pclk */ + val--; + dprintk("ratio: val = %d\n", val); + if ( val > 0x7ff ) { + printk("pixel clock divid is too large, set it to 0x7ff\n"); + val = 0x7ff; + } + + __cpm_set_pixdiv(val); + + dprintk("REG_CPM_LPCDR = 0x%08x\n", REG_CPM_LPCDR); +#if defined(CONFIG_SOC_JZ4750) /* Jz4750D don't use LCLK */ + val = pclk * 3 ; /* LCDClock > 2.5*Pixclock */ + val =__cpm_get_pllout2() / val; + if ( val > 0x1f ) { + printk("lcd clock divide is too large, set it to 0x1f\n"); + val = 0x1f; + } + __cpm_set_ldiv( val ); +#endif + REG_CPM_CPCCR |= CPM_CPCCR_CE ; /* update divide */ + + } + + dprintk("REG_CPM_LPCDR=0x%08x\n", REG_CPM_LPCDR); + dprintk("REG_CPM_CPCCR=0x%08x\n", REG_CPM_CPCCR); + + jz_clocks.pixclk = __cpm_get_pixclk(); + printk("LCDC: PixClock:%d\n", jz_clocks.pixclk); + +#if defined(CONFIG_SOC_JZ4750) /* Jz4750D don't use LCLK */ + jz_clocks.lcdclk = __cpm_get_lcdclk(); + printk("LCDC: LcdClock:%d\n", jz_clocks.lcdclk); +#endif + __cpm_start_lcd(); + udelay(1000); + /* + * set lcd device clock and lcd pixel clock. + * what about TVE mode??? + * + */ +#endif + +} + +/* + * jz4750fb_set_mode(), set osd configure, resize foreground + * + */ +static void jz4750fb_set_mode( struct jz4750lcd_info * lcd_info ) +{ + struct lcd_cfb_info *cfb = jz4750fb_info; + + jz4750fb_set_osd_mode(lcd_info); + jz4750fb_foreground_resize(lcd_info); + jz4750fb_set_var(&cfb->fb.var, -1, &cfb->fb); +} + +/* + * jz4750fb_deep_set_mode, + * + */ +static void jz4750fb_deep_set_mode( struct jz4750lcd_info * lcd_info ) +{ + /* configurate sequence: + * 1. disable lcdc. + * 2. init frame descriptor. + * 3. set panel mode + * 4. set osd mode + * 5. start lcd clock in CPM + * 6. enable lcdc. + */ + + __lcd_clr_ena(); /* Quick Disable */ + lcd_info->osd.fg_change = FG_CHANGE_ALL; /* change FG0, FG1 size, postion??? */ + jz4750fb_descriptor_init(lcd_info); + jz4750fb_set_panel_mode(lcd_info); + jz4750fb_set_mode(lcd_info); + jz4750fb_change_clock(lcd_info); + __lcd_set_ena(); /* enable lcdc */ +} + + +static irqreturn_t jz4750fb_interrupt_handler(int irq, void *dev_id) +{ + unsigned int state; + static int irqcnt=0; + + state = REG_LCD_STATE; + dprintk("In the lcd interrupt handler, state=0x%x\n", state); + + if (state & LCD_STATE_EOF) /* End of frame */ + REG_LCD_STATE = state & ~LCD_STATE_EOF; + + if (state & LCD_STATE_IFU0) { + printk("%s, InFiFo0 underrun\n", __FUNCTION__); + REG_LCD_STATE = state & ~LCD_STATE_IFU0; + } + + if (state & LCD_STATE_IFU1) { + printk("%s, InFiFo1 underrun\n", __FUNCTION__); + REG_LCD_STATE = state & ~LCD_STATE_IFU1; + } + + if (state & LCD_STATE_OFU) { /* Out fifo underrun */ + REG_LCD_STATE = state & ~LCD_STATE_OFU; + if ( irqcnt++ > 100 ) { + __lcd_disable_ofu_intr(); + printk("disable Out FiFo underrun irq.\n"); + } + printk("%s, Out FiFo underrun.\n", __FUNCTION__); + } + + return IRQ_HANDLED; +} + +#ifdef CONFIG_PM + +/* + * Suspend the LCDC. + */ +static int jzfb_suspend(void) +{ + __lcd_clr_ena(); /* Quick Disable */ + __lcd_display_off(); + __cpm_stop_lcd(); + + return 0; +} + +/* + * Resume the LCDC. + */ +static int jzfb_resume(void) +{ + __cpm_start_lcd(); + __gpio_set_pin(GPIO_DISP_OFF_N); + __lcd_special_on(); + __lcd_set_ena(); + mdelay(200); + __lcd_set_backlight_level(80); + + return 0; +} + +/* + * Power management hook. Note that we won't be called from IRQ context, + * unlike the blank functions above, so we may sleep. + */ +static int jzlcd_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data) +{ + int ret; + struct lcd_cfb_info *cfb = pm_dev->data; + + if (!cfb) return -EINVAL; + + switch (req) { + case PM_SUSPEND: + ret = jzfb_suspend(); + break; + + case PM_RESUME: + ret = jzfb_resume(); + break; + + default: + ret = -EINVAL; + break; + } + return ret; +} +#else +#define jzfb_suspend NULL +#define jzfb_resume NULL +#endif /* CONFIG_PM */ + +/* The following routine is only for test */ + +#ifdef DEBUG +static void test_gpio(int gpio_num, int delay) { + __gpio_as_output(gpio_num); + while(1) { + __gpio_set_pin(gpio_num); + udelay(delay); + __gpio_clear_pin(gpio_num); + udelay(delay); + } +} +static void display_v_color_bar(int w, int h, int bpp) { + int i, j, wpl, data = 0; + int *ptr; + ptr = (int *)lcd_frame0; +// ptr = (int *)lcd_frame1; + wpl = w*bpp/32; + if (!(bpp > 8)) + switch(bpp){ + case 1: + for (j = 0;j < h; j++) + for (i = 0;i < wpl; i++) { + *ptr++ = 0x00ff00ff; + } + break; + case 2: + for (j = 0;j < h; j++) + for (i = 0;i < wpl; i++) { + data = (i%4)*0x55555555; + *ptr++ = data; + } + break; + case 4: + for (j = 0;j < h; j++) + for (i = 0;i < wpl; i++) { + data = (i%16)*0x11111111; + *ptr++ = data; + } + break; + case 8: + for (j = 0;j < h; j++) + for (i = 0;i < wpl; i+=2) { + data = (i%(256))*0x01010101; + *ptr++ = data; + *ptr++ = data; + } + break; + } + else { + switch(bpp) { + case 16: + for (j = 0;j < h; j++) + for (i = 0;i < wpl; i++) { + if((i/4)%8==0) + *ptr++ = 0xffffffff; + else if ((i/4)%8==1) + *ptr++ = 0xf800f800; + else if ((i/4)%8==2) + *ptr++ = 0xffe0ffe0; + else if ((i/4)%8==3) + *ptr++ = 0x07e007e0; + else if ((i/4)%8==4) + *ptr++ = 0x07ff07ff; + else if ((i/4)%8==5) + *ptr++ = 0x001f001f; + else if ((i/4)%8==6) + *ptr++ = 0xf81ff81f; + else if ((i/4)%8==7) + *ptr++ = 0x00000000; + } + break; + case 18: + case 24: + case 32: + default: +#if 1 + for (j = 0;j < h; j++) + for (i = 0;i < wpl; i++) { + if((i/8)%8==7) + *ptr++ = 0xffffff; + else if ((i/8)%8==1) + *ptr++ = 0xff0000; + else if ((i/8)%8==2) + *ptr++ = 0xffff00; + else if ((i/8)%8==3) + *ptr++ = 0x00ff00; + else if ((i/8)%8==4) + *ptr++ = 0x00ffff; + else if ((i/8)%8==5) + *ptr++ = 0x0000ff; + else if ((i/8)%8==6) + *ptr++ = 0xff00ff; + else if ((i/8)%8==0) + *ptr++ = 0x000000; + } +#else + for (j = 0;j < h; j++) + for (i = 0;i < wpl; i++) { + if((i/8)%8==7) + *ptr++ = 0x00ff0000; + else if ((i/8)%8==1) + *ptr++ = 0xffff0000; + else if ((i/8)%8==2) + *ptr++ = 0x20ff0000; + else if ((i/8)%8==3) + *ptr++ = 0x40ff0000; + else if ((i/8)%8==4) + *ptr++ = 0x60ff0000; + else if ((i/8)%8==5) + *ptr++ = 0x80ff0000; + else if ((i/8)%8==6) + *ptr++ = 0xa0ff0000; + else if ((i/8)%8==0) + *ptr++ = 0xc0ff0000; + } +#endif + break; + } + } +} +static void display_h_color_bar(int w, int h, int bpp) { + int i, data = 0; + int *ptr; + int wpl; //word_per_line + ptr = (int *)lcd_frame0; +// ptr = (int *)lcd_frame1; + wpl = w*bpp/32; + if (!(bpp > 8)) + for (i = 0;i < wpl*h;i++) { + switch(bpp){ + case 1: + if(i%(wpl*8)==0) + data = ((i/(wpl*8))%2)*0xffffffff; + *ptr++ = data; + break; + case 2: + if(i%(wpl*8)==0) + data = ((i/(wpl*8))%4)*0x55555555; + *ptr++ = data; + break; + case 4: + if(i%(wpl*8)==0) + data = ((i/(wpl*8))%16)*0x11111111; + *ptr++ = data; + break; + case 8: + if(i%(wpl*8)==0) + data = ((i/(wpl*8))%256)*0x01010101; + *ptr++ = data; + break; + } + } + else { + + switch(bpp) { + case 15: + case 16: + for (i = 0;i < wpl*h;i++) { + if (((i/(wpl*8)) % 8) == 0) + *ptr++ = 0xffffffff; + else if (((i/(wpl*8)) % 8) == 1) + *ptr++ = 0xf800f800; + else if (((i/(wpl*8)) % 8) == 2) + *ptr++ = 0xffe0ffe0; + else if (((i/(wpl*8)) % 8) == 3) + *ptr++ = 0x07e007e0; + else if (((i/(wpl*8)) % 8) == 4) + *ptr++ = 0x07ff07ff; + else if (((i/(wpl*8)) % 8) == 5) + *ptr++ = 0x001f001f; + else if (((i/(wpl*8)) % 8) == 6) + *ptr++ = 0xf81ff81f; + else if (((i/(wpl*8)) % 8) == 7) + *ptr++ = 0x00000000; + } + break; + case 18: + case 24: + case 32: + default: + for (i = 0;i < wpl*h;i++) { + if (((i/(wpl*8)) % 8) == 7) + *ptr++ = 0xffffff; + else if (((i/(wpl*8)) % 8) == 2) + *ptr++ = 0xff0000; + else if (((i/(wpl*8)) % 8) == 4) + *ptr++ = 0xffff00; + else if (((i/(wpl*8)) % 8) == 6) + *ptr++ = 0x00ff00; + else if (((i/(wpl*8)) % 8) == 1) + *ptr++ = 0x00ffff; + else if (((i/(wpl*8)) % 8) == 3) + *ptr++ = 0x0000ff; + else if (((i/(wpl*8)) % 8) == 5) + *ptr++ = 0x000000; + else if (((i/(wpl*8)) % 8) == 0) + *ptr++ = 0xff00ff; + } + break; + } + + } + +} +#endif + +static int __init jz4750fb_init(void) +{ + struct lcd_cfb_info *cfb; + int err = 0; + + /* gpio init __gpio_as_lcd */ + if (jz4750_lcd_info->panel.cfg & LCD_CFG_MODE_TFT_16BIT) + __gpio_as_lcd_16bit(); + else if (jz4750_lcd_info->panel.cfg & LCD_CFG_MODE_TFT_24BIT) + __gpio_as_lcd_24bit(); + else + __gpio_as_lcd_18bit(); + /* In special mode, we only need init special pin, + * as general lcd pin has init in uboot */ +#if defined(CONFIG_SOC_JZ4750) || defined(CONFIG_SOC_JZ4750D) + switch (jz4750_lcd_info->panel.cfg & LCD_CFG_MODE_MASK) { + case LCD_CFG_MODE_SPECIAL_TFT_1: + case LCD_CFG_MODE_SPECIAL_TFT_2: + case LCD_CFG_MODE_SPECIAL_TFT_3: + __gpio_as_lcd_special(); + break; + default: + ; + } +#endif + if ( jz4750_lcd_info->osd.fg0.bpp > 16 && + jz4750_lcd_info->osd.fg0.bpp < 32 ) { + jz4750_lcd_info->osd.fg0.bpp = 32; + } + + switch ( jz4750_lcd_info->osd.fg1.bpp ) { + case 15: + case 16: + break; + case 17 ... 32: + jz4750_lcd_info->osd.fg1.bpp = 32; + break; + default: + printk("jz4750fb fg1 not support bpp(%d), force to 32bpp\n", + jz4750_lcd_info->osd.fg1.bpp); + jz4750_lcd_info->osd.fg1.bpp = 32; + } + __lcd_clr_dis(); + __lcd_clr_ena(); + + /* Configure SLCD module for setting smart lcd control registers */ +#if defined(CONFIG_FB_JZ4750_SLCD) + __lcd_as_smart_lcd(); + __slcd_disable_dma(); + __init_slcd_bus(); /* Note: modify this depend on you lcd */ + +#endif + /* init clk */ + jz4750fb_change_clock(jz4750_lcd_info); + __lcd_display_pin_init(); + __lcd_slcd_special_on(); + + cfb = jz4750fb_alloc_fb_info(); + if (!cfb) + goto failed; + + err = jz4750fb_map_smem(cfb); + if (err) + goto failed; + + jz4750fb_deep_set_mode( jz4750_lcd_info ); + + err = register_framebuffer(&cfb->fb); + if (err < 0) { + dprintk("jzfb_init(): register framebuffer err.\n"); + goto failed; + } + printk("fb%d: %s frame buffer device, using %dK of video memory\n", + cfb->fb.node, cfb->fb.fix.id, cfb->fb.fix.smem_len>>10); + + if (request_irq(IRQ_LCD, jz4750fb_interrupt_handler, IRQF_DISABLED, + "lcd", 0)) { + err = -EBUSY; + goto failed; + } + +#ifdef CONFIG_PM + /* + * Note that the console registers this as well, but we want to + * power down the display prior to sleeping. + */ + cfb->pm = pm_register(PM_SYS_DEV, PM_SYS_VGA, jzlcd_pm_callback); + if (cfb->pm) + cfb->pm->data = cfb; +#endif + + __lcd_set_ena(); /* enalbe LCD Controller */ + __lcd_display_on(); + +#ifdef DEBUG + display_h_color_bar(jz4750_lcd_info->osd.fg0.w, jz4750_lcd_info->osd.fg0.h, jz4750_lcd_info->osd.fg0.bpp); +#endif + print_lcdc_registers(); + return 0; + +failed: + print_dbg(); + jz4750fb_unmap_smem(cfb); + jz4750fb_free_fb_info(cfb); + + return err; +} + +#if 0 +static int jzfb_remove(struct device *dev) +{ + struct lcd_cfb_info *cfb = dev_get_drvdata(dev); + jzfb_unmap_smem(cfb); + jzfb_free_fb_info(cfb); + return 0; +} +#endif + +#if 0 +static struct device_driver jzfb_driver = { + .name = "jz-lcd", + .bus = &platform_bus_type, + .probe = jzfb_probe, + .remove = jzfb_remove, + .suspend = jzfb_suspend, + .resume = jzfb_resume, +}; +#endif + +static void __exit jz4750fb_cleanup(void) +{ + //driver_unregister(&jzfb_driver); + //jzfb_remove(); +} + +module_init(jz4750fb_init); +module_exit(jz4750fb_cleanup); diff --git a/drivers/video/jz4750_lcd.h b/drivers/video/jz4750_lcd.h new file mode 100644 index 0000000..2d07c43 --- /dev/null +++ b/drivers/video/jz4750_lcd.h @@ -0,0 +1,785 @@ +/* + * linux/drivers/video/jz4750_lcd.h -- Ingenic Jz4750 On-Chip LCD frame buffer device + * + * 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. + * + */ + +#ifndef __JZ4750_LCD_H__ +#define __JZ4750_LCD_H__ + +//#include + + +#define NR_PALETTE 256 +#define PALETTE_SIZE (NR_PALETTE*2) + +/* use new descriptor(8 words) */ +struct jz4750_lcd_dma_desc { + unsigned int next_desc; /* LCDDAx */ + unsigned int databuf; /* LCDSAx */ + unsigned int frame_id; /* LCDFIDx */ + unsigned int cmd; /* LCDCMDx */ + unsigned int offsize; /* Stride Offsize(in word) */ + unsigned int page_width; /* Stride Pagewidth(in word) */ + unsigned int cmd_num; /* Command Number(for SLCD) */ + unsigned int desc_size; /* Foreground Size */ +}; + +struct jz4750lcd_panel_t { + unsigned int cfg; /* panel mode and pin usage etc. */ + unsigned int slcd_cfg; /* Smart lcd configurations */ + unsigned int ctrl; /* lcd controll register */ + unsigned int w; /* Panel Width(in pixel) */ + unsigned int h; /* Panel Height(in line) */ + unsigned int fclk; /* frame clk */ + unsigned int hsw; /* hsync width, in pclk */ + unsigned int vsw; /* vsync width, in line count */ + unsigned int elw; /* end of line, in pclk */ + unsigned int blw; /* begin of line, in pclk */ + unsigned int efw; /* end of frame, in line count */ + unsigned int bfw; /* begin of frame, in line count */ +}; + + +struct jz4750lcd_fg_t { + int bpp; /* foreground bpp */ + int x; /* foreground start position x */ + int y; /* foreground start position y */ + int w; /* foreground width */ + int h; /* foreground height */ +}; + +struct jz4750lcd_osd_t { + unsigned int osd_cfg; /* OSDEN, ALHPAEN, F0EN, F1EN, etc */ + unsigned int osd_ctrl; /* IPUEN, OSDBPP, etc */ + unsigned int rgb_ctrl; /* RGB Dummy, RGB sequence, RGB to YUV */ + unsigned int bgcolor; /* background color(RGB888) */ + unsigned int colorkey0; /* foreground0's Colorkey enable, Colorkey value */ + unsigned int colorkey1; /* foreground1's Colorkey enable, Colorkey value */ + unsigned int alpha; /* ALPHAEN, alpha value */ + unsigned int ipu_restart; /* IPU Restart enable, ipu restart interval time */ + +#define FG_NOCHANGE 0x0000 +#define FG0_CHANGE_SIZE 0x0001 +#define FG0_CHANGE_POSITION 0x0002 +#define FG1_CHANGE_SIZE 0x0010 +#define FG1_CHANGE_POSITION 0x0020 +#define FG_CHANGE_ALL ( FG0_CHANGE_SIZE | FG0_CHANGE_POSITION | \ + FG1_CHANGE_SIZE | FG1_CHANGE_POSITION ) + int fg_change; + struct jz4750lcd_fg_t fg0; /* foreground 0 */ + struct jz4750lcd_fg_t fg1; /* foreground 1 */ +}; + +struct jz4750lcd_info { + struct jz4750lcd_panel_t panel; + struct jz4750lcd_osd_t osd; +}; + + +/* Jz LCDFB supported I/O controls. */ +#define FBIOSETBACKLIGHT 0x4688 /* set back light level */ +#define FBIODISPON 0x4689 /* display on */ +#define FBIODISPOFF 0x468a /* display off */ +#define FBIORESET 0x468b /* lcd reset */ +#define FBIOPRINT_REG 0x468c /* print lcd registers(debug) */ +#define FBIOROTATE 0x46a0 /* rotated fb */ +#define FBIOGETBUFADDRS 0x46a1 /* get buffers addresses */ +#define FBIO_GET_MODE 0x46a2 /* get lcd info */ +#define FBIO_SET_MODE 0x46a3 /* set osd mode */ +#define FBIO_DEEP_SET_MODE 0x46a4 /* set panel and osd mode */ +#define FBIO_MODE_SWITCH 0x46a5 /* switch mode between LCD and TVE */ +#define FBIO_GET_TVE_MODE 0x46a6 /* get tve info */ +#define FBIO_SET_TVE_MODE 0x46a7 /* set tve mode */ + +/* + * LCD panel specific definition + */ +/* AUO */ +#if defined(CONFIG_JZ4750_LCD_AUO_A043FL01V2) +#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_RET (32*4+25) /*LCD_DISP_N use for lcd reset*/ +#elif defined(CONFIG_JZ4750_FUWA) /* 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_RET (32*5+2) /*LCD_DISP_N use for lcd reset*/ +#elif defined(CONFIG_JZ4750D_CETUS) /* board pavo */ + #define SPEN (32*5+13) /*LCD_CS*/ + #define SPCK (32*5+10) /*LCD_SCL*/ + #define SPDA (32*5+11) /*LCD_SDA*/ + #define LCD_RET (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_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); \ +} while (0) + + #define __lcd_special_off() \ + do { \ + __gpio_clear_pin(LCD_RET); \ + } while (0) + +#endif /* CONFIG_JZLCD_AUO_A030FL01_V1 */ + +/* TRULY_TFTG320240DTSW */ +#if defined(CONFIG_JZ4750_LCD_TRULY_TFTG320240DTSW_16BIT) || defined(CONFIG_JZ4750_LCD_TRULY_TFTG320240DTSW_18BIT) + +#if defined(CONFIG_JZ4750_FUWA) +#define LCD_RESET_PIN (32*3+25)// LCD_REV, GPD25 +#else +#error "Define LCD_RESET_PIN on your board" +#endif + +#define __lcd_special_on() \ +do { \ + __gpio_as_output(32*3+30);\ + __gpio_clear_pin(32*3+30);\ + __gpio_as_output(LCD_RESET_PIN); \ + __gpio_set_pin(LCD_RESET_PIN); \ + udelay(100); \ + __gpio_clear_pin(LCD_RESET_PIN); \ + udelay(100); \ + __gpio_set_pin(LCD_RESET_PIN); \ +} while (0) + +#endif /* CONFIG_JZ4750_LCD_TRULY_TFTG320240DTSW */ + +// Wolfgang 2008.02.23 +#if defined(CONFIG_JZ4750_LCD_TOPPOLY_TD025THEA7_RGB_DELTA) || defined(CONFIG_JZ4750_LCD_TOPPOLY_TD025THEA7_RGB_DUMMY) + +#if defined(CONFIG_JZ4750_LCD_TOPPOLY_TD025THEA7_RGB_DELTA) +#define PANEL_MODE 0x02 /* RGB Delta */ +#elif defined(CONFIG_JZ4750_LCD_TOPPOLY_TD025THEA7_RGB_DUMMY) +#define PANEL_MODE 0x00 /* RGB Dummy */ +#endif + +#if defined(CONFIG_JZ4750_FUWA) /* board FuWa */ + #define SPEN (32*3+16) //LCD_D16 - GPD16 + #define SPCK (32*3+17) //LCD_D17 - GPD17 + #define SPDA (32*3+21) //LCD_DE - GPD21 + #define LCD_RET (32*3+25) //LCD_REV - GPD25 //use for lcd reset +#else +#error "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);\ + udelay(100);\ + __gpio_clear_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_clear_pin(SPCK);\ + __gpio_set_pin(SPEN);\ + udelay(100);\ + } while (0) + + #define __spi_write_reg(reg, val) \ + do {\ + __spi_write_reg1((reg<<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(SPDA); /* use reset */\ + __gpio_as_output(LCD_RET); /* use reset */\ + __gpio_set_pin(LCD_RET);\ + mdelay(15);\ + __gpio_clear_pin(LCD_RET);\ + mdelay(15);\ + __gpio_set_pin(LCD_RET);\ + } while (0) + + #define __lcd_special_on() \ + do { \ + mdelay(10); \ + __spi_write_reg(0x00, 0x10); \ + __spi_write_reg(0x01, 0xB1); \ + __spi_write_reg(0x00, 0x10); \ + __spi_write_reg(0x01, 0xB1); \ + __spi_write_reg(0x02, PANEL_MODE); /* RGBD MODE */ \ + __spi_write_reg(0x03, 0x01); /* Noninterlace*/ \ + mdelay(10); \ + } while (0) + + #define __lcd_special_off() \ + do { \ + } while (0) + +#endif /* CONFIG_JZ4750_LCD_TOPPOLY_TD025THEA7_RGB_DELTA */ + + +#if defined(CONFIG_JZ4750_LCD_FOXCONN_PT035TN01) || defined(CONFIG_JZ4750_LCD_INNOLUX_PT035TN01_SERIAL) + +#if defined(CONFIG_JZ4750_LCD_FOXCONN_PT035TN01) /* board FUWA */ +#define MODE 0xcd /* 24bit parellel RGB */ +#endif +#if defined(CONFIG_JZ4750_LCD_INNOLUX_PT035TN01_SERIAL) +#define MODE 0xc9 /* 8bit serial RGB */ +#endif + +#if defined(CONFIG_JZ4750_FUWA) /* board FuWa */ +#if 0 + #define SPEN (32*5+7) //LCD_SPL GPF7 + #define SPCK (32*5+6) //LCD_CLS GPF6 + #define SPDA (32*5+5) //LCD_PS GPF5 + #define LCD_RET (32*5+4) //LCD_REV GPF4 //use for lcd reset +#endif + #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+25) /*LCD_DISP_N use for lcd reset*/ +#elif defined(CONFIG_JZ4750D_FUWA1)/* board FuWa */ + #define SPEN (32*4+0) /*LCD_CS*/ + #define SPCK (32*4+9) /*LCD_SCL*/ + #define SPDA (32*4+1) /*LCD_SDA*/ + #define LCD_RET (32*4+3) /*LCD_DISP_N use for lcd reset*/ +#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_JZ4750_LCD_FOXCONN_PT035TN01 or CONFIG_JZ4750_LCD_INNOLUX_PT035TN01_SERIAL */ + +#if defined(CONFIG_JZ4750_LCD_TRULY_TFT_GG1P0319LTSW_W) +static inline void CmdWrite(unsigned int cmd) +{ + while (REG_SLCD_STATE & SLCD_STATE_BUSY); /* wait slcd ready */ + udelay(30); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | cmd; +} + +static inline void DataWrite(unsigned int data) +{ + while (REG_SLCD_STATE & SLCD_STATE_BUSY); /* wait slcd ready */ +// udelay(30); + REG_SLCD_DATA = SLCD_DATA_RS_DATA | data; +} + + +static inline void delay(long delay_time) +{ + long cnt; + +// delay_time *= (384/8); + delay_time *= (43/8); + + for (cnt=0;cnt SETP 3 + CmdWrite(0x0000); + CmdWrite(0x01A0); + CmdWrite(0x3B01); + + CmdWrite(0x2809); + delay(1000); + CmdWrite(0x1900); + delay(1000); + CmdWrite(0x2110); + delay(1000); + CmdWrite(0x1805); + delay(1000); + CmdWrite(0x1E01); + delay(1000); + CmdWrite(0x1847); + delay(1000); + CmdWrite(0x1867); + delay(1000); + CmdWrite(0x18F7); + delay(1000); + CmdWrite(0x2100); + delay(1000); + CmdWrite(0x2809); + delay(1000); + CmdWrite(0x1A05); + delay(1000); + CmdWrite(0x19E8); + delay(1000); + CmdWrite(0x1F64); + delay(1000); + CmdWrite(0x2045); + delay(1000); + CmdWrite(0x1E81); + delay(1000); + CmdWrite(0x1B09); + delay(1000); + CmdWrite(0x0020); + delay(1000); + CmdWrite(0x0120); + delay(1000); + + CmdWrite(0x3B01); + delay(1000); + + /* Set Window(239,319), Set Cursor(239,319) */ + CmdWrite(0x0510); + CmdWrite(0x01C0); + CmdWrite(0x4500); + CmdWrite(0x46EF); + CmdWrite(0x4800); + CmdWrite(0x4700); + CmdWrite(0x4A3F); + CmdWrite(0x4901); + CmdWrite(0x42EF); + CmdWrite(0x443F); + CmdWrite(0x4301); + +} + +#if defined(CONFIG_JZ4750_FUWA) +//#define PIN_CS_N (32*2+xx) /* a low voltage */ +#define PIN_RD_N (32*3+21) /* LCD_DE: GP D21, a high voltage */ +#define PIN_RESET_N (32*3+25) /* LCD_REV GP D25 */ +#else +#error "Define special lcd pins for your platform." +#endif + +#define __lcd_slcd_pin_init() \ + do { \ + __gpio_as_output(PIN_RD_N); /* RD#: LCD_REV */ \ + __gpio_as_output(PIN_RESET_N); /* RESET#: LCD_SPL */ \ + __gpio_set_pin(PIN_RD_N); /*set read signal high */ \ + __gpio_set_pin(PIN_RESET_N); \ + mdelay(100); \ + __gpio_clear_pin(PIN_RESET_N); \ + mdelay(100); \ + __gpio_set_pin(PIN_RESET_N); \ + /* Configure SLCD module */ \ + REG_LCD_CTRL &= ~(LCD_CTRL_ENA|LCD_CTRL_DIS); /* disable lcdc */ \ + REG_LCD_CFG = LCD_CFG_LCDPIN_SLCD | 0x0D; /* LCM */ \ + REG_SLCD_CTRL &= ~SLCD_CTRL_DMA_EN; /* disable slcd dma */ \ + REG_SLCD_CFG = SLCD_CFG_DWIDTH_16BIT | SLCD_CFG_CWIDTH_16BIT | SLCD_CFG_CS_ACTIVE_LOW | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING | SLCD_CFG_TYPE_PARALLEL; \ + REG_LCD_REV = 0x04; /* lcd clock??? */ \ + printk("Fuwa test, pixclk divide REG_LCD_REV=0x%08x\n", REG_LCD_REV); \ +}while (0) + +#define __lcd_slcd_special_on() \ + do { \ + __lcd_slcd_pin_init(); \ + SlcdInit(); \ + REG_SLCD_CTRL |= SLCD_CTRL_DMA_EN; /* slcdc dma enable */ \ + } while (0) + +#endif /* #if CONFIG_JZ4750_LCD_TRULY_TFT_GG1P0319LTSW_W */ + +#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_JZ4750D_VGA_DISPLAY) +#define __lcd_display_pin_init() +#define __lcd_display_on() +#define __lcd_display_off() +#elif defined(CONFIG_JZ4750_APUS)/* board apus */ +#define __lcd_display_pin_init() \ +do { \ + __gpio_as_output(GPIO_LCD_VCC_EN_N); \ + __lcd_special_pin_init(); \ +} while (0) +#define __lcd_display_on() \ +do { \ + __gpio_clear_pin(GPIO_LCD_VCC_EN_N); \ + __lcd_special_on(); \ + __lcd_set_backlight_level(80); \ +} while (0) + +#define __lcd_display_off() \ +do { \ + __lcd_close_backlight(); \ + __lcd_special_off(); \ +} while (0) +#elif defined(CONFIG_JZ4750D_CETUS)/* board apus */ +#define __lcd_display_pin_init() \ +do { \ + __gpio_as_output(GPIO_LCD_VCC_EN_N); \ + __lcd_special_pin_init(); \ +} while (0) +#define __lcd_display_on() \ +do { \ + __gpio_set_pin(GPIO_LCD_VCC_EN_N); \ + __lcd_special_on(); \ + __lcd_set_backlight_level(80); \ +} while (0) + +#define __lcd_display_off() \ +do { \ + __lcd_close_backlight(); \ + __lcd_special_off(); \ +} while (0) + +#else /* other boards */ + +#define __lcd_display_pin_init() \ +do { \ + __lcd_special_pin_init(); \ +} while (0) +#define __lcd_display_on() \ +do { \ + __lcd_special_on(); \ + __lcd_set_backlight_level(80); \ +} while (0) + +#define __lcd_display_off() \ +do { \ + __lcd_close_backlight(); \ + __lcd_special_off(); \ +} while (0) +#endif /* APUS */ + + +/***************************************************************************** + * LCD display pin dummy macros + *****************************************************************************/ + +#ifndef __lcd_display_pin_init +#define __lcd_display_pin_init() +#endif +#ifndef __lcd_slcd_special_on +#define __lcd_slcd_special_on() +#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 /* __JZ4750_LCD_H__ */ diff --git a/drivers/video/jz4750_tve.c b/drivers/video/jz4750_tve.c new file mode 100644 index 0000000..3a4ea61 --- /dev/null +++ b/drivers/video/jz4750_tve.c @@ -0,0 +1,104 @@ + +/* + * linux/drivers/video/jz4750_tve.c -- Ingenic Jz4750 TVE Controller operation + * interface. + * Copyright (C) 2005-2008, Ingenic Semiconductor Inc. + * Author: Wolfgang Wang, + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include +#include "jz4750_tve.h" + +struct jz4750tve_info jz4750_tve_info_PAL = { + .ctrl = (4 << TVE_CTRL_YCDLY_BIT) | TVE_CTRL_SYNCT | TVE_CTRL_PAL | TVE_CTRL_SWRST, /* PAL, SVIDEO */ + .frcfg = (23 << TVE_FRCFG_L1ST_BIT) | (625 << TVE_FRCFG_NLINE_BIT), + .slcfg1 = (800<ctrl = (jz4750_tve_info->ctrl | TVE_CTRL_DAPD) & ( ~( TVE_CTRL_DAPD1 | TVE_CTRL_DAPD2)); + jz4750_tve_info->ctrl &= ~TVE_CTRL_SWRST; + REG_TVE_CTRL = jz4750_tve_info->ctrl; +} + +/* turn off TVE, turn off DACn... */ +void jz4750tve_disable_tve(void) +{ + jz4750_tve_info->ctrl &= ~TVE_CTRL_DAPD;/* DACn disabled??? */ + jz4750_tve_info->ctrl |= TVE_CTRL_SWRST;/* DACn disabled??? */ + REG_TVE_CTRL = jz4750_tve_info->ctrl; +} + +void jz4750tve_set_tve_mode( struct jz4750tve_info *tve ) +{ + REG_TVE_CTRL = tve->ctrl; + REG_TVE_FRCFG = tve->frcfg; + REG_TVE_SLCFG1 = tve->slcfg1; + REG_TVE_SLCFG2 = tve->slcfg2; + REG_TVE_SLCFG3 = tve->slcfg3; + REG_TVE_LTCFG1 = tve->ltcfg1; + REG_TVE_LTCFG2 = tve->ltcfg2; + REG_TVE_CFREQ = tve->cfreq; + REG_TVE_CPHASE = tve->cphase; + REG_TVE_CBCRCFG = tve->cbcrcfg; + REG_TVE_WSSCR = tve->wsscr; + REG_TVE_WSSCFG1 = tve->wsscfg1; + REG_TVE_WSSCFG2 = tve->wsscfg2; + REG_TVE_WSSCFG3 = tve->wsscfg3; +} + +void jz4750tve_init( int tve_mode ) +{ + switch ( tve_mode ) { + case PANEL_MODE_TVE_PAL: + jz4750_tve_info = &jz4750_tve_info_PAL; + break; + case PANEL_MODE_TVE_NTSC: + jz4750_tve_info = &jz4750_tve_info_NTSC; + break; + } + + jz4750tve_set_tve_mode( jz4750_tve_info ); +// jz4750tve_enable_tve(); +} diff --git a/drivers/video/jz4750_tve.h b/drivers/video/jz4750_tve.h new file mode 100644 index 0000000..00eefe9 --- /dev/null +++ b/drivers/video/jz4750_tve.h @@ -0,0 +1,45 @@ +#ifndef __JZ4750_TVE_H__ +#define __JZ4750_TVE_H__ + + +#define PANEL_MODE_LCD_PANEL 0 +#define PANEL_MODE_TVE_PAL 1 +#define PANEL_MODE_TVE_NTSC 2 + +/* TV parameter */ +#define TVE_WIDTH_PAL 720 +#define TVE_HEIGHT_PAL 573 +#define TVE_FREQ_PAL 50 +#define TVE_WIDTH_NTSC 720 +#define TVE_HEIGHT_NTSC 482 +#define TVE_FREQ_NTSC 60 + + +/* Structure for TVE */ +struct jz4750tve_info { + unsigned int ctrl; + unsigned int frcfg; + unsigned int slcfg1; + unsigned int slcfg2; + unsigned int slcfg3; + unsigned int ltcfg1; + unsigned int ltcfg2; + unsigned int cfreq; + unsigned int cphase; + unsigned int cbcrcfg; + unsigned int wsscr; + unsigned int wsscfg1; + unsigned int wsscfg2; + unsigned int wsscfg3; +}; + +extern struct jz4750tve_info *jz4750_tve_info; + +extern void jz4750tve_enable_tve(void); +extern void jz4750tve_disable_tve(void); + +extern void jz4750tve_set_tve_mode( struct jz4750tve_info *tve ); +extern void jz4750tve_init( int tve_mode ); + + +#endif /* __JZ4750_TVE_H__ */ diff --git a/drivers/video/jz_kgm_spfd5420a.h b/drivers/video/jz_kgm_spfd5420a.h new file mode 100644 index 0000000..33d8bd5 --- /dev/null +++ b/drivers/video/jz_kgm_spfd5420a.h @@ -0,0 +1,385 @@ +/* Set registers of smart lcd acording to the following routines + * Note: BUS width and CMD width and register value width + * This example: BUS is 8, 9, 16 or 18-bit; CMD and DATA is 16-bit + * Configure SLCD module to initialize smart lcd registers + + switch (bus) { + case 8: + REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_8_x2 + | SLCD_CFG_CWIDTH_8BIT | SLCD_CFG_CS_ACTIVE_LOW + | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING + | SLCD_CFG_TYPE_PARALLEL; + __gpio_as_slcd_8bit(); + break; + case 9: + REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_8_x2 + | SLCD_CFG_CWIDTH_8BIT | SLCD_CFG_CS_ACTIVE_LOW + | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING + | SLCD_CFG_TYPE_PARALLEL; + __gpio_as_slcd_9bit(); + break; + case 16: + REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_16 + | SLCD_CFG_CWIDTH_16BIT | SLCD_CFG_CS_ACTIVE_LOW + | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING + | SLCD_CFG_TYPE_PARALLEL; + __gpio_as_slcd_16bit(); + break; + case 18: + REG_SLCD_CFG = SLCD_CFG_BURST_8_WORD | SLCD_CFG_DWIDTH_18 + | SLCD_CFG_CWIDTH_18BIT | SLCD_CFG_CS_ACTIVE_LOW + | SLCD_CFG_RS_CMD_LOW | SLCD_CFG_CLK_ACTIVE_FALLING + | SLCD_CFG_TYPE_PARALLEL; + __gpio_as_slcd_18bit(); + break; + } + + static void Mcupanel_RegSet(unsigned int cmd, unsigned int data) + { + switch (bus) { + case 8: + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) >> 8); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff) >> 0); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_DATA | (data&0xffff); + break; + case 9: + data = ((data & 0xff) << 1) | ((data & 0xff00) << 2); + data = ((data << 6) & 0xfc0000) | ((data << 4) & 0xfc00) | ((data << 2) & 0xfc); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) >> 8); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff) >> 0); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_DATA | data; + break; + case 16: + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | (cmd&0xffff); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_DATA | (data&0xffff); + break; + case 18: + cmd = ((cmd & 0xff) << 1) | ((cmd & 0xff00) << 2); + data = ((data & 0xff) << 1) | ((data & 0xff00) << 2); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | cmd; + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_DATA | ((data<<6)&0xfc0000)|((data<<4)&0xfc00) | ((data<<2)&0xfc); + break; + default: + printk("Don't support %d bit Bus\n", jzfb.bus ); + break; + } + } + + static void Mcupanel_Command(unsigned int cmd) { + switch (bus) { + case 8: + case 9: + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) >> 8); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff) >> 0); + break; + case 16: + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | (cmd&0xffff); + break; + case 18: + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) << 2) | ((cmd&0xff) << 1); + break; + default: + printk("Don't support %d bit Bus\n", jzfb.bus ); + break; + } + } + + *Display---------------------------------------- + Note: BUS and BPP, send data to gram data register to display + BUS: 8, 9, 16 or 18-bit; BPP: 8, 16, 18-bit + switch (bus) { + case 8: + switch (bpp) { + case 8: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; + break; + case 15: + case 16: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x2; + break; + case 17 ... 32: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x3; + break; + default: + printk("The BPP %d is not supported\n", jzfb.bpp); + break; + } + break; + case 9: + switch (bpp) { + case 8: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; + break; + case 15 ... 16: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x2; + break; + case 17 ... 32: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_9_x2; + break; + default: + printk("The BPP %d is not supported\n", jzfb.bpp); + break; + } + break; + case 16: + switch (bpp) { + case 8: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; + break; + case 15 ... 16: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_16; + break; + case 17 ... 32: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x3; + break; + default: + printk("The BPP %d is not supported\n", jzfb.bpp); + break; + } + break; + case 18: + switch (bpp) { + case 8: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_8_x1; + break; + case 15: + case 16: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_16; + break; + case 17 ... 32: + REG_SLCD_CFG &= ~SLCD_CFG_DWIDTH_MASK; + REG_SLCD_CFG |= SLCD_CFG_DWIDTH_18; + break; + default: + printk("The BPP %d is not supported\n", jzfb.bpp); + break; + } + break; + default: + printk("Error: The BUS %d is not supported\n", jzfb.bus); + break; + } + dprintk("SLCD_CFG=0x%x\n", REG_SLCD_CFG); +} + ************************************************************************************************/ + +#ifndef __JZ_KGM_SPF5420A_H__ +#define __JZ_KGM_SPF5420A_H__ + +#include + +#if defined(CONFIG_JZ4750_SLCD_KGM701A3_TFT_SPFD5420A) +#define WR_GRAM_CMD 0x0202 + +#if defined(CONFIG_JZ4750_FUWA) +#define PIN_CS_N (32*3+24) // Chip select //GPD24; +#define PIN_RESET_N (32*5+6) /* LCD_REV GPF6 */ +#elif defined(CONFIG_JZ4750D_FUWA1) +#define PIN_CS_N (32*3+24) // Chip select //GPD24; +#define PIN_RESET_N (32*4+3) /* LCD_REV GPF6 */ +#else +#error "Define special lcd pins for your platform." +#endif + +/* Sent a command with data (18-bit bus, 16-bit index, 16-bit register value) */ +static void Mcupanel_RegSet(unsigned int cmd, unsigned int data) +{ + cmd = ((cmd & 0xff) << 1) | ((cmd & 0xff00) << 2); + data = ((data & 0xff) << 1) | ((data & 0xff00) << 2); + data = ((data<<6)&0xfc0000)|((data<<4)&0xfc00) | ((data<<2)&0xfc); + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | cmd; + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_DATA | data; +} + +/* Sent a command without data (18-bit bus, 16-bit index) */ +static void Mcupanel_Command(unsigned int cmd) { + while (REG_SLCD_STATE & SLCD_STATE_BUSY); + REG_SLCD_DATA = SLCD_DATA_RS_COMMAND | ((cmd&0xff00) << 2) | ((cmd&0xff) << 1); +} + +/* Set the start address of screen, for example (0, 0) */ +void Mcupanel_SetAddr(u32 x, u32 y) //u32 +{ + Mcupanel_RegSet(0x200,x) ; + udelay(1); + Mcupanel_RegSet(0x201,y) ; + udelay(1); + Mcupanel_Command(0x202); + +} + +#undef __lcd_special_pin_init +#define __lcd_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); \ + __gpio_set_pin(PIN_RESET_N); \ + mdelay(10); \ + __gpio_clear_pin(PIN_RESET_N); \ + mdelay(10); \ + __gpio_set_pin(PIN_RESET_N); \ + mdelay(100); \ +} while(0) + + +#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 SlcdInit() \ +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); \ + Mcupanel_RegSet(0x0600, 0x0001); /*soft reset*/ \ + mdelay(10); \ + Mcupanel_RegSet(0x0600, 0x0000); /*soft reset*/ \ + mdelay(10); \ + 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,0x12b8); /*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); \ + Mcupanel_Command(0x0202); /*Write Data to GRAM */ \ + udelay(10);\ + Mcupanel_SetAddr(0,0);\ + udelay(100);\ +} while(0) + +/*---- LCD Initial ----*/ +#undef __lcd_slcd_pin_init +#define __lcd_slcd_pin_init() \ + do { \ + __lcd_special_pin_init(); \ +}while (0) + +#undef __lcd_slcd_special_on +#define __lcd_slcd_special_on() \ + do { \ + __lcd_slcd_pin_init(); \ + SlcdInit(); \ + REG_SLCD_CTRL |= SLCD_CTRL_DMA_EN; /* slcdc dma enable */ \ + } while (0) + +#define __init_slcd_bus()\ +do{\ + __slcd_set_data_18bit();\ + __slcd_set_cmd_18bit();\ + __slcd_set_cs_low();\ + __slcd_set_rs_low();\ + __slcd_set_clk_falling();\ + __slcd_set_parallel_type();\ +}while(0) +#endif /* #if CONFIG_JZ4750_SLCD_KGM701A3_TFT_SPFD5420A */ + +#endif /* __JZ_KGM_SPF5420A_H__ */ diff --git a/drivers/video/jz_toppoly_td043mgeb1.h b/drivers/video/jz_toppoly_td043mgeb1.h new file mode 100644 index 0000000..c66494e --- /dev/null +++ b/drivers/video/jz_toppoly_td043mgeb1.h @@ -0,0 +1,264 @@ + +#ifndef __JZ_KGM_TOPPOLY_TD043MGEB1_H__ +#define __JZ_KGM_TOPPOLY_TD043MGEB1_H__ + +#include + +#if defined(CONFIG_JZ4750_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/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(500); \ + __gpio_clear_pin(SPDA); \ + __gpio_clear_pin(SPEN); \ + udelay(500); \ + 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(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 << 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_STBY); \ + __gpio_as_output(LCD_RET); \ + udelay(500); \ + __gpio_clear_pin(LCD_RET); \ + udelay(1000); \ + __gpio_set_pin(LCD_RET); \ + udelay(1000); \ + __gpio_set_pin(LCD_STBY); \ + udelay(1000); \ + } while (0) +#define __lcd_special_on() \ + do { \ +} while (0) + + #define __lcd_special_off() \ + do { \ + __gpio_clear_pin(LCD_RET); \ + } while (0) + +#endif /* CONFIG_JZLCD_AUO_A030FL01_V1 */ + +#endif /* __JZ_KGM_TOPPOLY_TD043MGEB1_H__ */ +/* 2.2 + __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, 0x26); \ + __spi_write_reg(0x08, 0x13); \ + __spi_write_reg(0x09, 0x33); \ + __spi_write_reg(0x0a, 0x20); \ + __spi_write_reg(0x0b, 0x20); \ + __spi_write_reg(0x0c, 0x20); \ + __spi_write_reg(0x0d, 0x20); \ + __spi_write_reg(0x0e, 0x10); \ + __spi_write_reg(0x0f, 0x10); \ + __spi_write_reg(0x10, 0x10); \ + __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, 0x07); \ +*/ +/* 3.1 + __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, 0x10); \ + __spi_write_reg(0x0f, 0x10); \ + __spi_write_reg(0x10, 0x10); \ + __spi_write_reg(0x11, 0x15); \ + __spi_write_reg(0x12, 0x6a); \ + __spi_write_reg(0x13, 0xff); \ + __spi_write_reg(0x14, 0x86); \ + __spi_write_reg(0x15, 0x7c); \ + __spi_write_reg(0x16, 0xc2); \ + __spi_write_reg(0x17, 0xd1); \ + __spi_write_reg(0x18, 0xf5); \ + __spi_write_reg(0x19, 0x25); \ + __spi_write_reg(0x1a, 0x4a); \ + __spi_write_reg(0x1b, 0xbf); \ + __spi_write_reg(0x1c, 0x15); \ + __spi_write_reg(0x1d, 0x6a); \ + __spi_write_reg(0x1e, 0xa4); \ + __spi_write_reg(0x1f, 0xff); \ + __spi_write_reg(0x20, 0xf0); \ + __spi_write_reg(0x21, 0xf0); \ + __spi_write_reg(0x22, 0x08); \ + */ + /* 2.5 + __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, 0x10); \ + __spi_write_reg(0x0f, 0x10); \ + __spi_write_reg(0x10, 0x10); \ + __spi_write_reg(0x11, 0x15); \ + __spi_write_reg(0x12, 0xaa); \ + __spi_write_reg(0x13, 0xff); \ + __spi_write_reg(0x14, 0x86); \ + __spi_write_reg(0x15, 0x89); \ + __spi_write_reg(0x16, 0xc6); \ + __spi_write_reg(0x17, 0xea); \ + __spi_write_reg(0x18, 0x0c); \ + __spi_write_reg(0x19, 0x33); \ + __spi_write_reg(0x1a, 0x5e); \ + __spi_write_reg(0x1b, 0xd0); \ + __spi_write_reg(0x1c, 0x33); \ + __spi_write_reg(0x1d, 0x7e); \ + __spi_write_reg(0x1e, 0xb3); \ + __spi_write_reg(0x1f, 0xff); \ + __spi_write_reg(0x20, 0xf0); \ + __spi_write_reg(0x21, 0xf0); \ + __spi_write_reg(0x22, 0x08); \ +*/ +/* + __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, 0x10); \ + __spi_write_reg(0x0f, 0x10); \ + __spi_write_reg(0x10, 0x10); \ + __spi_write_reg(0x11, 0x15); \ + __spi_write_reg(0x12, 0xaa); \ + __spi_write_reg(0x13, 0xff); \ + __spi_write_reg(0x14, 0x86); \ + __spi_write_reg(0x15, 0x84); \ + __spi_write_reg(0x16, 0xc3); \ + __spi_write_reg(0x17, 0xd8); \ + __spi_write_reg(0x18, 0x01); \ + __spi_write_reg(0x19, 0x28); \ + __spi_write_reg(0x1a, 0x53); \ + __spi_write_reg(0x1b, 0xc5); \ + __spi_write_reg(0x1c, 0x26); \ + __spi_write_reg(0x1d, 0x74); \ + __spi_write_reg(0x1e, 0xae); \ + __spi_write_reg(0x1f, 0xff); \ + __spi_write_reg(0x20, 0xf0); \ + __spi_write_reg(0x21, 0xf0); \ + __spi_write_reg(0x22, 0x08); \ + */ diff --git a/drivers/video/jzlcd.c b/drivers/video/jzlcd.c new file mode 100644 index 0000000..357004a --- /dev/null +++ b/drivers/video/jzlcd.c @@ -0,0 +1,1574 @@ +/* + * linux/drivers/video/jzlcd.c -- 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "console/fbcon.h" + +#include "jzlcd.h" + +#undef DEBUG +//#define DEBUG +#ifdef DEBUG +#define dprintk(x...) printk(x) +#else +#define dprintk(x...) +#endif + +#define print_err(f, arg...) printk(KERN_ERR DRIVER_NAME ": " f "\n", ## arg) +#define print_warn(f, arg...) printk(KERN_WARNING DRIVER_NAME ": " f "\n", ## arg) +#define print_info(f, arg...) printk(KERN_INFO DRIVER_NAME ": " f "\n", ## arg) +#ifdef DEBUG +#define print_dbg(f, arg...) printk("dbg::" __FILE__ ",LINE(%d): " f "\n", __LINE__, ## arg) +#else +#define print_dbg(f, arg...) do {} while (0) +#endif + +struct lcd_cfb_info { + struct fb_info fb; + struct display_switch *dispsw; + signed int currcon; + int func_use_count; + + struct { + u16 red, green, blue; + } palette[NR_PALETTE]; +#ifdef CONFIG_PM + struct pm_dev *pm; +#endif +#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) + struct task_struct *rotate_daemon_thread; +#endif +}; + +static struct lcd_cfb_info *jzlcd_info; + +struct jzfb_info { + unsigned int cfg; /* panel mode and pin usage etc. */ + unsigned int w; + unsigned int h; + unsigned int bpp; /* bit per pixel */ + unsigned int fclk; /* frame clk */ + unsigned int hsw; /* hsync width, in pclk */ + unsigned int vsw; /* vsync width, in line count */ + unsigned int elw; /* end of line, in pclk */ + unsigned int blw; /* begin of line, in pclk */ + unsigned int efw; /* end of frame, in line count */ + unsigned int bfw; /* begin of frame, in line count */ +}; + +static struct jzfb_info jzfb = { +#if defined(CONFIG_JZLCD_SHARP_LQ035Q7) + MODE_TFT_SHARP | PCLK_N | VSYNC_N, + 240, 320, 16, 60, 1, 2, 1, 2, 0, 6 +#endif +#if defined(CONFIG_JZLCD_SAMSUNG_LTS350Q1) + MODE_TFT_SAMSUNG | PCLK_N, + 240, 320, 16, 60, 1, 2, (254-240), 0, 7, 0 +#endif +#if defined(CONFIG_JZLCD_SAMSUNG_LTV350QVF04) + MODE_TFT_GEN | HSYNC_N | VSYNC_N, + 320, 240, 16, 70, 19, 4, 20, 14, 18, 6 +#endif +#if defined(CONFIG_JZLCD_SAMSUNG_LTP400WQF01) + MODE_TFT_GEN | HSYNC_N | VSYNC_N, + 480, 272, 16, 60, 41, 10, 2, 2, 2, 2 +#endif + +#if defined(CONFIG_JZLCD_SAMSUNG_LTP400WQF02) + /* MODE_TFT_18BIT: JZ4740@ version */ + MODE_TFT_GEN | MODE_TFT_18BIT | HSYNC_N | VSYNC_N, + 480, 272, 32, 60, 41, 10, 2, 2, 2, 2 +#endif +#if defined(CONFIG_JZLCD_TRULY_TFTG320240DTSW) + MODE_TFT_GEN | HSYNC_N | VSYNC_N | PCLK_N, + 320, 240, 16, 85, 30, 3, 38, 20, 11, 8 +#endif +#if defined(CONFIG_JZLCD_TRULY_TFTG320240DTSW_SERIAL) + MODE_8BIT_SERIAL_TFT | HSYNC_N | VSYNC_N | PCLK_N, + /* serial mode 280 lines, parallel mode 240 lines */ + 320, 280, 32, 60, (30*3), 3, (20*3), (38*3), 46, 23 +#endif +#if defined(CONFIG_JZLCD_AUO_A030FL01_V1) + MODE_TFT_GEN | MODE_TFT_18BIT | HSYNC_N | VSYNC_N, + 480, 272, 32, 60, 39, 10, 8, 4, 4, 2 +#endif +#if defined(CONFIG_JZLCD_TRULY_TFTG240320UTSW_63W_E) + 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) + #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 +#endif +#if defined(CONFIG_JZLCD_HYNIX_HT10X21) + MODE_TFT_GEN | PCLK_N, + 1024, 768, 16, 45, 1, 1, 75, 0, 3, 0 +#endif +#if defined(CONFIG_JZLCD_TOSHIBA_LTM084P363) + MODE_TFT_GEN | PCLK_N, + 800, 600, 16, 50, 1, 2, 199, 0, 2, 0 +#endif +#if defined(CONFIG_JZLCD_INNOLUX_AT080TN42) + MODE_TFT_SHARP | PCLK_N, + 800, 600, 16, 40, 1, 1, 255, 0, 34, 0 +#endif +#if defined(CONFIG_JZLCD_CSTN_800x600) + MODE_STN_COLOR_DUAL | STN_DAT_PIN8, + 800, 600, 16, 30, 8, 1, 0, 0, 0, 0 +#endif +#if defined(CONFIG_JZLCD_CSTN_320x240) + MODE_STN_COLOR_SINGLE | STN_DAT_PIN8, + 320, 240, 16, 120, 8, 1, 8, 0, 0, 0 +#endif +#if defined(CONFIG_JZLCD_MSTN_640x480) + MODE_STN_MONO_DUAL | STN_DAT_PIN4, + 640, 480, 8, 110, 4, 1, 4, 0, 0, 0 +#endif +#if defined(CONFIG_JZLCD_MSTN_320x240) + MODE_STN_MONO_SINGLE | STN_DAT_PIN4, + 320, 240, 8, 110, 4, 1, 4, 0, 0, 0 +#endif +#if defined(CONFIG_JZLCD_MSTN_480x320) + MODE_STN_MONO_SINGLE | STN_DAT_PIN8 +#if defined(CONFIG_JZLCD_MSTN_INVERSE) + | DATA_INVERSE +#endif + , 480, 320, 8, 65, 8, 1, 8, 0, 0, 0 +#endif + +#if defined(CONFIG_JZLCD_MSTN_240x128) + MODE_STN_MONO_SINGLE | STN_DAT_PIN1 +#if defined(CONFIG_JZLCD_MSTN_INVERSE) + | DATA_INVERSE +#endif + , 240, 128, 8, 100, 1, 1, 1, 0, 0, 0 +#endif +}; + +static struct lcd_desc *lcd_desc_base; +static struct lcd_desc *lcd_palette_desc; +static struct lcd_desc *lcd_frame_desc0; +static struct lcd_desc *lcd_frame_desc1; + +static unsigned char *lcd_palette; +static unsigned char *lcd_frame[CONFIG_JZLCD_FRAMEBUFFER_MAX]; +struct jz_lcd_buffer_addrs_t jz_lcd_buffer_addrs; +//extern struct display fb_display[MAX_NR_CONSOLES]; +#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) +static unsigned char *lcd_frame_user_fb; +/* default rotate angle */ +static volatile int rotate_angle = CONFIG_JZLCD_FRAMEBUFFER_DEFAULT_ROTATE_ANGLE; +#endif + +#ifdef DEBUG +static void print_regs(void) /* debug */ +{ + printk("REG_LCD_CFG:\t0x%8.8x\n", REG_LCD_CFG); + printk("REG_LCD_VSYNC:\t0x%8.8x\n", REG_LCD_VSYNC); + printk("REG_LCD_HSYNC:\t0x%8.8x\n", REG_LCD_HSYNC); + printk("REG_LCD_VAT:\t0x%8.8x\n", REG_LCD_VAT); + printk("REG_LCD_DAH:\t0x%8.8x\n", REG_LCD_DAH); + printk("REG_LCD_DAV:\t0x%8.8x\n", REG_LCD_DAV); + printk("REG_LCD_PS:\t0x%8.8x\n", REG_LCD_PS); + printk("REG_LCD_CLS:\t0x%8.8x\n", REG_LCD_CLS); + printk("REG_LCD_SPL:\t0x%8.8x\n", REG_LCD_SPL); + printk("REG_LCD_REV:\t0x%8.8x\n", REG_LCD_REV); + printk("REG_LCD_CTRL:\t0x%8.8x\n", REG_LCD_CTRL); + printk("REG_LCD_STATE:\t0x%8.8x\n", REG_LCD_STATE); + printk("REG_LCD_IID:\t0x%8.8x\n", REG_LCD_IID); + printk("REG_LCD_DA0:\t0x%8.8x\n", REG_LCD_DA0); + printk("REG_LCD_SA0:\t0x%8.8x\n", REG_LCD_SA0); + printk("REG_LCD_FID0:\t0x%8.8x\n", REG_LCD_FID0); + printk("REG_LCD_CMD0:\t0x%8.8x\n", REG_LCD_CMD0); + + printk("==================================\n"); + printk("REG_LCD_VSYNC:\t%d:%d\n", REG_LCD_VSYNC>>16, REG_LCD_VSYNC&0xfff); + printk("REG_LCD_HSYNC:\t%d:%d\n", REG_LCD_HSYNC>>16, REG_LCD_HSYNC&0xfff); + printk("REG_LCD_VAT:\t%d:%d\n", REG_LCD_VAT>>16, REG_LCD_VAT&0xfff); + printk("REG_LCD_DAH:\t%d:%d\n", REG_LCD_DAH>>16, REG_LCD_DAH&0xfff); + printk("REG_LCD_DAV:\t%d:%d\n", REG_LCD_DAV>>16, REG_LCD_DAV&0xfff); + printk("==================================\n"); + +} +#else +#define print_regs() +#endif + +#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) +static int jzfb_rotate_daemon_thread(void *info) +{ + int i,j; + struct fb_info *fb = &jzlcd_info->fb; + + while (!kthread_should_stop()) { +#if (CONFIG_JZLCD_FRAMEBUFFER_BPP == 8) + unsigned char *plcd_frame = (unsigned char *)lcd_frame[0]; + unsigned char *pfb = (unsigned char *) (fb->screen_base); +#elif (CONFIG_JZLCD_FRAMEBUFFER_BPP == 16) + unsigned short *plcd_frame = (unsigned short *)lcd_frame[0]; + unsigned short *pfb = (unsigned short *) (fb->screen_base); +#elif (CONFIG_JZLCD_FRAMEBUFFER_BPP == 32) + unsigned int *plcd_frame = (unsigned int *)lcd_frame[0]; + unsigned int *pfb = (unsigned int *) (fb->screen_base); +#else +#error "ERROR, rotate not support this bpp." +#endif + switch ( rotate_angle ) { + case FB_ROTATE_UR: + printk("%s, Warning, this shouldn't reache\n", __FUNCTION__); + ssleep(1); + break; + case FB_ROTATE_UD: /* cost about 30ms, can be accelrated by dma in the future */ + plcd_frame += jzfb.w*jzfb.h -1; + for (i=0;ivar.height+1; i++) { + for (j=1; j < fb->var.width+1; j++) + plcd_frame[j*fb->var.height-i] = *pfb++; + } + msleep(100); /* sleep 100ms */ + break; + case FB_ROTATE_CCW: /* cost about 80ms */ + for (i=0;ivar.height;i++) { + for ( j=fb->var.width-1;j>=0;j--) + plcd_frame[j*fb->var.height+i] = *pfb++; + } + msleep(100); /* sleep 100ms */ + break; + default: /* FB_ROTATE_UR */ + dprintk("Unknown rotate(%d) type\n", rotate_angle); + ssleep(1); + } + + dma_cache_wback_inv((unsigned int)(lcd_frame_user_fb), fb->fix.smem_len); + } + return 0; +} +/* + * rotate param angle: + * 0: FB_ROTATE_UR, 0'C + * 1: FB_ROTATE_CW, 90'C + * 2: FB_ROTATE_UD, 180'C + * 3: FB_ROTATE_CCW, 270'C + */ +static int jzfb_rotate_change( int angle ) +{ + struct fb_info *fb = &jzlcd_info->fb; + + /* clear frame buffer */ + memset((void*)lcd_frame_user_fb, 0x00, fb->fix.smem_len); + switch ( angle ) { + case FB_ROTATE_UR: + fb->var.width = fb->var.xres = fb->var.xres_virtual = jzfb.w; + fb->var.height = fb->var.yres = fb->var.yres_virtual = jzfb.h; + /* change lcd controller's data buffer to lcd_frame_user_fb*/ + lcd_frame_desc0->databuf = virt_to_phys((void *)lcd_frame_user_fb); + if ( rotate_angle != FB_ROTATE_UR ) + kthread_stop(jzlcd_info->rotate_daemon_thread); + rotate_angle = angle; + break; + case FB_ROTATE_UD: + case FB_ROTATE_CW: + case FB_ROTATE_CCW: + if ( angle == FB_ROTATE_UD ) { + fb->var.width = fb->var.xres = fb->var.xres_virtual = jzfb.w; + fb->var.height = fb->var.yres = fb->var.yres_virtual = jzfb.h; + } + else { /* CW, CCW */ + fb->var.width = fb->var.xres = fb->var.xres_virtual = jzfb.h; + fb->var.height = fb->var.yres = fb->var.yres_virtual = jzfb.w; + } + /* change lcd controller's data buffer to lcd_frame[0]*/ + lcd_frame_desc0->databuf = virt_to_phys((void *)lcd_frame[0]); + if ( rotate_angle == FB_ROTATE_UR || \ + jzlcd_info->rotate_daemon_thread == NULL) + jzlcd_info->rotate_daemon_thread = kthread_run( jzfb_rotate_daemon_thread, jzlcd_info, "%s", "jzlcd-rotate-daemon"); /* start rotate daemon */ + rotate_angle = angle; + break; + default: + printk("Invalid angle(%d)\n", (unsigned int)angle); + } + fb->fix.line_length = fb->var.xres * CONFIG_JZLCD_FRAMEBUFFER_BPP/8; + dma_cache_wback_inv((unsigned int)(lcd_frame_desc0), sizeof(struct lcd_desc)); + return 0; +} + +void jzfb_fb_rotate(struct fb_info *fbi, int angle) +{ + jzfb_rotate_change( angle/90 ); +} +#endif /* #if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) */ + +static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) +{ + chan &= 0xffff; + chan >>= 16 - bf->length; + return chan << bf->offset; +} + +static int jzfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info) +{ + struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; + unsigned short *ptr, ctmp; + +// print_dbg("regno:%d,RGBt:(%d,%d,%d,%d)\t", regno, red, green, blue, transp); + if (regno >= NR_PALETTE) + return 1; + + cfb->palette[regno].red = red ; + cfb->palette[regno].green = green; + cfb->palette[regno].blue = blue; + if (cfb->fb.var.bits_per_pixel <= 16) { + red >>= 8; + green >>= 8; + blue >>= 8; + + red &= 0xff; + green &= 0xff; + blue &= 0xff; + } + switch (cfb->fb.var.bits_per_pixel) { + case 1: + case 2: + case 4: + case 8: + if (((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_SINGLE) || + ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL)) { + ctmp = (77L * red + 150L * green + 29L * blue) >> 8; + ctmp = ((ctmp >> 3) << 11) | ((ctmp >> 2) << 5) | + (ctmp >> 3); + } else { + /* RGB 565 */ + if (((red >> 3) == 0) && ((red >> 2) != 0)) + red = 1 << 3; + if (((blue >> 3) == 0) && ((blue >> 2) != 0)) + blue = 1 << 3; + ctmp = ((red >> 3) << 11) + | ((green >> 2) << 5) | (blue >> 3); + } + + ptr = (unsigned short *)lcd_palette; + ptr = (unsigned short *)(((u32)ptr)|0xa0000000); + ptr[regno] = ctmp; + + break; + + case 15: + if (regno < 16) + ((u32 *)cfb->fb.pseudo_palette)[regno] = + ((red >> 3) << 10) | + ((green >> 3) << 5) | + (blue >> 3); + break; + case 16: + if (regno < 16) { + ((u32 *)cfb->fb.pseudo_palette)[regno] = + ((red >> 3) << 11) | + ((green >> 2) << 5) | + (blue >> 3); + } + break; + case 18: + case 24: + case 32: + if (regno < 16) + ((u32 *)cfb->fb.pseudo_palette)[regno] = + (red << 16) | + (green << 8) | + (blue << 0); + +/* if (regno < 16) { + unsigned val; + val = chan_to_field(red, &cfb->fb.var.red); + val |= chan_to_field(green, &cfb->fb.var.green); + val |= chan_to_field(blue, &cfb->fb.var.blue); + ((u32 *)cfb->fb.pseudo_palette)[regno] = val; + } +*/ + + break; + } + return 0; +} + + +static int jzfb_ioctl (struct fb_info *fb, unsigned int cmd, unsigned long arg ) +{ + int ret = 0; + void __user *argp = (void __user *)arg; + + switch (cmd) { + case FBIOSETBACKLIGHT: + __lcd_set_backlight_level(arg); /* We support 8 levels here. */ + break; + case FBIODISPON: + __lcd_display_on(); + break; + case FBIODISPOFF: + __lcd_display_off(); + break; + case FBIOPRINT_REGS: + print_regs(); + break; + case FBIOGETBUFADDRS: + if ( copy_to_user(argp, &jz_lcd_buffer_addrs, + sizeof(struct jz_lcd_buffer_addrs_t)) ) + return -EFAULT; + break; +#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) + case FBIOROTATE: + ret = jzfb_rotate_change(arg); + break; +#endif /* defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) */ + default: + printk("Warn: Command(%x) not support\n", cmd); + ret = -1; + break; + } + return ret; + +} + +/* Use mmap /dev/fb can only get a non-cacheable Virtual Address. */ +static int jzfb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; + unsigned long start; + unsigned long off; + u32 len; + + off = vma->vm_pgoff << PAGE_SHIFT; + //fb->fb_get_fix(&fix, PROC_CONSOLE(info), info); + + /* frame buffer memory */ + start = cfb->fb.fix.smem_start; + len = PAGE_ALIGN((start & ~PAGE_MASK) + cfb->fb.fix.smem_len); + start &= PAGE_MASK; + + if ((vma->vm_end - vma->vm_start + off) > len) + return -EINVAL; + off += start; + + vma->vm_pgoff = off >> PAGE_SHIFT; + vma->vm_flags |= VM_IO; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); /* Uncacheable */ + +#if 1 + pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK; + pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED; /* Uncacheable */ +// pgprot_val(vma->vm_page_prot) |= _CACHE_CACHABLE_NONCOHERENT; /* Write-Through */ +#endif + + if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) { + return -EAGAIN; + } + return 0; +} + +/* checks var and eventually tweaks it to something supported, + * DO NOT MODIFY PAR */ +static int jzfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + print_dbg("jzfb_check_var"); + return 0; +} + + +/* + * set the video mode according to info->var + */ +static int jzfb_set_par(struct fb_info *info) +{ + print_dbg("jzfb_set_par"); + return 0; +} + + +/* + * (Un)Blank the display. + * Fix me: should we use VESA value? + */ +static int jzfb_blank(int blank_mode, struct fb_info *info) +{ + + dprintk("fb_blank %d %p", blank_mode, info); + + switch (blank_mode) { + + case FB_BLANK_UNBLANK: + //case FB_BLANK_NORMAL: + /* Turn on panel */ + __lcd_set_ena(); + __lcd_display_on(); + break; + + case FB_BLANK_NORMAL: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_POWERDOWN: +#if 0 + /* Turn off panel */ + __lcd_set_dis(); + __lcd_display_off(); +#endif + break; + default: + break; + + } + return 0; +} + +/* + * pan display + */ +static int jzfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; + int dy; + + if (!var || !cfb) { + return -EINVAL; + } + + if (var->xoffset - cfb->fb.var.xoffset) { + /* No support for X panning for now! */ + return -EINVAL; + } + + dy = var->yoffset - cfb->fb.var.yoffset; + print_dbg("var.yoffset: %d", dy); + if (dy) { + + print_dbg("Panning screen of %d lines", dy); + + lcd_frame_desc0->databuf += (cfb->fb.fix.line_length * dy); + /* TODO: Wait for current frame to finished */ + } + + return 0; +} + + +/* use default function cfb_fillrect, cfb_copyarea, cfb_imageblit */ +static struct fb_ops jzfb_ops = { + .owner = THIS_MODULE, + .fb_setcolreg = jzfb_setcolreg, + .fb_check_var = jzfb_check_var, + .fb_set_par = jzfb_set_par, + .fb_blank = jzfb_blank, + .fb_pan_display = jzfb_pan_display, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_mmap = jzfb_mmap, + .fb_ioctl = jzfb_ioctl, +#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) + .fb_rotate = jzfb_fb_rotate, +#endif +}; + +static int jzfb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + struct lcd_cfb_info *cfb = (struct lcd_cfb_info *)info; + int chgvar = 0; + + var->height = jzfb.h ; + var->width = jzfb.w ; + var->bits_per_pixel = jzfb.bpp; + + var->vmode = FB_VMODE_NONINTERLACED; + var->activate = cfb->fb.var.activate; + var->xres = var->width; + var->yres = var->height; + var->xres_virtual = var->width; + var->yres_virtual = var->height; + var->xoffset = 0; + var->yoffset = 0; + var->pixclock = 0; + var->left_margin = 0; + var->right_margin = 0; + var->upper_margin = 0; + var->lower_margin = 0; + var->hsync_len = 0; + var->vsync_len = 0; + var->sync = 0; + var->activate &= ~FB_ACTIVATE_TEST; + + /* + * CONUPDATE and SMOOTH_XPAN are equal. However, + * SMOOTH_XPAN is only used internally by fbcon. + */ + if (var->vmode & FB_VMODE_CONUPDATE) { + var->vmode |= FB_VMODE_YWRAP; + var->xoffset = cfb->fb.var.xoffset; + var->yoffset = cfb->fb.var.yoffset; + } + + if (var->activate & FB_ACTIVATE_TEST) + return 0; + + if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) + return -EINVAL; + + if (cfb->fb.var.xres != var->xres) + chgvar = 1; + if (cfb->fb.var.yres != var->yres) + chgvar = 1; + if (cfb->fb.var.xres_virtual != var->xres_virtual) + chgvar = 1; + if (cfb->fb.var.yres_virtual != var->yres_virtual) + chgvar = 1; + if (cfb->fb.var.bits_per_pixel != var->bits_per_pixel) + chgvar = 1; + + var->red.msb_right = 0; + var->green.msb_right = 0; + var->blue.msb_right = 0; + + switch(var->bits_per_pixel){ + case 1: /* Mono */ + cfb->fb.fix.visual = FB_VISUAL_MONO01; + cfb->fb.fix.line_length = (var->xres * var->bits_per_pixel) / 8; + break; + case 2: /* Mono */ + var->red.offset = 0; + var->red.length = 2; + var->green.offset = 0; + var->green.length = 2; + var->blue.offset = 0; + var->blue.length = 2; + + cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; + cfb->fb.fix.line_length = (var->xres * var->bits_per_pixel) / 8; + break; + case 4: /* PSEUDOCOLOUR*/ + var->red.offset = 0; + var->red.length = 4; + var->green.offset = 0; + var->green.length = 4; + var->blue.offset = 0; + var->blue.length = 4; + + cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; + cfb->fb.fix.line_length = var->xres / 2; + break; + case 8: /* PSEUDOCOLOUR, 256 */ + var->red.offset = 0; + var->red.length = 8; + var->green.offset = 0; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + + cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; + cfb->fb.fix.line_length = var->xres ; + break; + case 15: /* DIRECTCOLOUR, 32k */ + var->bits_per_pixel = 15; + var->red.offset = 10; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 5; + var->blue.offset = 0; + var->blue.length = 5; + + cfb->fb.fix.visual = FB_VISUAL_DIRECTCOLOR; + cfb->fb.fix.line_length = var->xres_virtual * 2; + break; + case 16: /* DIRECTCOLOUR, 64k */ + var->bits_per_pixel = 16; + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; + + cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR; + cfb->fb.fix.line_length = var->xres_virtual * 2; + break; + case 18: + case 24: + case 32: + /* DIRECTCOLOUR, 16M */ + var->bits_per_pixel = 32; + + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 24; + var->transp.length = 8; + + cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR; + cfb->fb.fix.line_length = var->xres_virtual * 4; + break; + + default: /* in theory this should never happen */ + printk(KERN_WARNING "%s: don't support for %dbpp\n", + cfb->fb.fix.id, var->bits_per_pixel); + break; + } + + cfb->fb.var = *var; + cfb->fb.var.activate &= ~FB_ACTIVATE_ALL; + + /* + * Update the old var. The fbcon drivers still use this. + * Once they are using cfb->fb.var, this can be dropped. + * --rmk + */ + //display->var = cfb->fb.var; + /* + * If we are setting all the virtual consoles, also set the + * defaults used to create new consoles. + */ + fb_set_cmap(&cfb->fb.cmap, &cfb->fb); + dprintk("jzfb_set_var: after fb_set_cmap...\n"); + + return 0; +} + +static struct lcd_cfb_info * jzfb_alloc_fb_info(void) +{ + struct lcd_cfb_info *cfb; + + cfb = kmalloc(sizeof(struct lcd_cfb_info) + sizeof(u32) * 16, GFP_KERNEL); + + if (!cfb) + return NULL; + + jzlcd_info = cfb; + + memset(cfb, 0, sizeof(struct lcd_cfb_info) ); + + cfb->currcon = -1; + + + strcpy(cfb->fb.fix.id, "jz-lcd"); + cfb->fb.fix.type = FB_TYPE_PACKED_PIXELS; + cfb->fb.fix.type_aux = 0; + cfb->fb.fix.xpanstep = 1; + cfb->fb.fix.ypanstep = 1; + cfb->fb.fix.ywrapstep = 0; + cfb->fb.fix.accel = FB_ACCEL_NONE; + + cfb->fb.var.nonstd = 0; + cfb->fb.var.activate = FB_ACTIVATE_NOW; + cfb->fb.var.height = -1; + cfb->fb.var.width = -1; + cfb->fb.var.accel_flags = FB_ACCELF_TEXT; + + cfb->fb.fbops = &jzfb_ops; + cfb->fb.flags = FBINFO_FLAG_DEFAULT; + + cfb->fb.pseudo_palette = (void *)(cfb + 1); + + switch (jzfb.bpp) { + case 1: + fb_alloc_cmap(&cfb->fb.cmap, 4, 0); + break; + case 2: + fb_alloc_cmap(&cfb->fb.cmap, 8, 0); + break; + case 4: + fb_alloc_cmap(&cfb->fb.cmap, 32, 0); + break; + case 8: + + default: + fb_alloc_cmap(&cfb->fb.cmap, 256, 0); + break; + } + dprintk("fb_alloc_cmap,fb.cmap.len:%d....\n", cfb->fb.cmap.len); + + return cfb; +} + +/* + * Map screen memory + */ +static int jzfb_map_smem(struct lcd_cfb_info *cfb) +{ + struct page * map = NULL; + unsigned char *tmp; + unsigned int page_shift, needroom, t; +#if defined(CONFIG_SOC_JZ4740) + if (jzfb.bpp == 18 || jzfb.bpp == 24) + t = 32; + else + t = jzfb.bpp; +#else + if (jzfb.bpp == 15) + t = 16; + else + t = jzfb.bpp; +#endif + + needroom = ((jzfb.w * t + 7) >> 3) * jzfb.h; + for (page_shift = 0; page_shift < 12; page_shift++) + if ((PAGE_SIZE << page_shift) >= needroom) + break; + + /* lcd_palette room total 4KB: + * 0 -- 512: lcd palette + * 1024 -- [1024+16*3]: lcd descripters + * [1024+16*3] -- 4096: reserved + */ + lcd_palette = (unsigned char *)__get_free_pages(GFP_KERNEL, 0); + if ((!lcd_palette)) + return -ENOMEM; + + memset((void *)lcd_palette, 0, PAGE_SIZE); + map = virt_to_page(lcd_palette); + set_bit(PG_reserved, &map->flags); + lcd_desc_base = (struct lcd_desc *)(lcd_palette + 1024); + + jz_lcd_buffer_addrs.fb_num = CONFIG_JZLCD_FRAMEBUFFER_MAX; + printk("jzlcd use %d framebuffer:\n", CONFIG_JZLCD_FRAMEBUFFER_MAX); + /* alloc frame buffer space */ + for ( t = 0; t < CONFIG_JZLCD_FRAMEBUFFER_MAX; t++ ) { + lcd_frame[t] = (unsigned char *)__get_free_pages(GFP_KERNEL, page_shift); + if ((!lcd_frame[t])) { + printk("no mem for fb[%d]\n", t); + return -ENOMEM; + } +// memset((void *)lcd_frame[t], 0, PAGE_SIZE << page_shift); + for (tmp=(unsigned char *)lcd_frame[t]; + tmp < lcd_frame[t] + (PAGE_SIZE << page_shift); + tmp += PAGE_SIZE) { + map = virt_to_page(tmp); + set_bit(PG_reserved, &map->flags); + } + jz_lcd_buffer_addrs.fb_phys_addr[t] = virt_to_phys((void *)lcd_frame[t]); + printk("jzlcd fb[%d] phys addr =0x%08x\n", + t, jz_lcd_buffer_addrs.fb_phys_addr[t]); + } +#if !defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) + cfb->fb.fix.smem_start = virt_to_phys((void *)lcd_frame[0]); + cfb->fb.fix.smem_len = (PAGE_SIZE << page_shift); + cfb->fb.screen_base = + (unsigned char *)(((unsigned int)lcd_frame[0] & 0x1fffffff) | 0xa0000000); +#else /* Framebuffer rotate */ + lcd_frame_user_fb = (unsigned char *)__get_free_pages(GFP_KERNEL, page_shift); + if ((!lcd_frame_user_fb)) { + printk("no mem for fb[%d]\n", t); + return -ENOMEM; + } + memset((void *)lcd_frame_user_fb, 0, PAGE_SIZE << page_shift); + for (tmp=(unsigned char *)lcd_frame_user_fb; + tmp < lcd_frame_user_fb + (PAGE_SIZE << page_shift); + tmp += PAGE_SIZE) { + map = virt_to_page(tmp); + set_bit(PG_reserved, &map->flags); + } + + printk("Rotate userfb phys addr =0x%08x\n", + (unsigned int)virt_to_phys((void *)lcd_frame_user_fb)); + cfb->fb.fix.smem_start = virt_to_phys((void *)lcd_frame_user_fb); + cfb->fb.fix.smem_len = (PAGE_SIZE << page_shift); + cfb->fb.screen_base = (unsigned char *)(((unsigned int)lcd_frame_user_fb & 0x1fffffff) | 0xa0000000); + +#endif /* #if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) */ + if (!cfb->fb.screen_base) { + printk("%s: unable to map screen memory\n", cfb->fb.fix.id); + return -ENOMEM; + } + + return 0; +} + +static void jzfb_free_fb_info(struct lcd_cfb_info *cfb) +{ + if (cfb) { + fb_alloc_cmap(&cfb->fb.cmap, 0, 0); + kfree(cfb); + } +} + +static void jzfb_unmap_smem(struct lcd_cfb_info *cfb) +{ + struct page * map = NULL; + unsigned char *tmp; + unsigned int page_shift, needroom, t; +#if defined(CONFIG_SOC_JZ4740) + if (jzfb.bpp == 18 || jzfb.bpp == 24) + t = 32; + else + t = jzfb.bpp; +#else + if (jzfb.bpp == 15) + t = 16; + else + t = jzfb.bpp; +#endif + needroom = ((jzfb.w * t + 7) >> 3) * jzfb.h; + for (page_shift = 0; page_shift < 12; page_shift++) + if ((PAGE_SIZE << page_shift) >= needroom) + break; + + if (cfb && cfb->fb.screen_base) { + iounmap(cfb->fb.screen_base); + cfb->fb.screen_base = NULL; + release_mem_region(cfb->fb.fix.smem_start, + cfb->fb.fix.smem_len); + } + + if (lcd_palette) { + map = virt_to_page(lcd_palette); + clear_bit(PG_reserved, &map->flags); + free_pages((int)lcd_palette, 0); + } + + for ( t=0; t < CONFIG_JZLCD_FRAMEBUFFER_MAX; t++ ) { + if (lcd_frame[t]) { + for (tmp=(unsigned char *)lcd_frame[t]; + tmp < lcd_frame[t] + (PAGE_SIZE << page_shift); + tmp += PAGE_SIZE) { + map = virt_to_page(tmp); + clear_bit(PG_reserved, &map->flags); + } + free_pages((int)lcd_frame[t], page_shift); + } + } +#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) + if (lcd_frame_user_fb) { + for (tmp=(unsigned char *)lcd_frame_user_fb; + tmp < lcd_frame_user_fb + (PAGE_SIZE << page_shift); + tmp += PAGE_SIZE) { + map = virt_to_page(tmp); + clear_bit(PG_reserved, &map->flags); + } + free_pages((int)lcd_frame_user_fb, page_shift); + } + +#endif +} + +static void lcd_descriptor_init(void) +{ + int i; + unsigned int pal_size; + unsigned int frm_size, ln_size; + unsigned char dual_panel = 0; + + i = jzfb.bpp; +#if defined(CONFIG_SOC_JZ4740) + if (i == 18 || i == 24) + i = 32; +#else + if (i == 15) + i = 16; +#endif + frm_size = (jzfb.w*jzfb.h*i)>>3; + ln_size = (jzfb.w*i)>>3; + + if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL) || + ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL)) { + dual_panel = 1; + frm_size >>= 1; + } + + frm_size = frm_size / 4; + ln_size = ln_size / 4; + + switch (jzfb.bpp) { + case 1: + pal_size = 4; + break; + case 2: + pal_size = 8; + break; + case 4: + pal_size = 32; + break; + case 8: + default: + pal_size = 512; + } + + pal_size /= 4; + + lcd_frame_desc0 = lcd_desc_base + 0; + lcd_frame_desc1 = lcd_desc_base + 1; + lcd_palette_desc = lcd_desc_base + 2; + + jz_lcd_buffer_addrs.lcd_desc_phys_addr = (unsigned int)virt_to_phys(lcd_frame_desc0); + + /* Palette Descriptor */ + lcd_palette_desc->next_desc = (int)virt_to_phys(lcd_frame_desc0); + lcd_palette_desc->databuf = (int)virt_to_phys((void *)lcd_palette); + lcd_palette_desc->frame_id = (unsigned int)0xdeadbeaf; + lcd_palette_desc->cmd = pal_size|LCD_CMD_PAL; /* Palette Descriptor */ + + /* Frame Descriptor 0 */ + if (jzfb.bpp <= 8) + lcd_frame_desc0->next_desc = (int)virt_to_phys(lcd_palette_desc); + else + lcd_frame_desc0->next_desc = (int)virt_to_phys(lcd_frame_desc0); + lcd_frame_desc0->databuf = virt_to_phys((void *)lcd_frame[0]); + lcd_frame_desc0->frame_id = (unsigned int)0xbeafbeaf; + lcd_frame_desc0->cmd = LCD_CMD_SOFINT | LCD_CMD_EOFINT | frm_size; + dma_cache_wback_inv((unsigned int)(lcd_palette_desc),0x10); + dma_cache_wback_inv((unsigned int)(lcd_frame_desc0),0x10); + + if (!(dual_panel)) + return; + + /* Frame Descriptor 1 */ + lcd_frame_desc1->next_desc = (int)virt_to_phys(lcd_frame_desc1); + lcd_frame_desc1->databuf = virt_to_phys((void *)(lcd_frame[0] + frm_size * 4)); + lcd_frame_desc1->frame_id = (unsigned int)0xdeaddead; + lcd_frame_desc1->cmd = LCD_CMD_SOFINT | LCD_CMD_EOFINT | frm_size; + dma_cache_wback_inv((unsigned int)(lcd_frame_desc1),0x10); +} + +static int lcd_hw_init(void) +{ + unsigned int val = 0; + unsigned int pclk; + unsigned int stnH; + int ret = 0; + + /* Setting Control register */ + switch (jzfb.bpp) { + case 1: + val |= LCD_CTRL_BPP_1; + break; + case 2: + val |= LCD_CTRL_BPP_2; + break; + case 4: + val |= LCD_CTRL_BPP_4; + break; + case 8: + val |= LCD_CTRL_BPP_8; + break; + case 15: + val |= LCD_CTRL_RGB555; + case 16: + val |= LCD_CTRL_BPP_16; + break; +#if defined(CONFIG_SOC_JZ4740) + case 17 ... 32: + val |= LCD_CTRL_BPP_18_24; /* target is 4bytes/pixel */ + break; +#endif + default: + printk("The BPP %d is not supported\n", jzfb.bpp); + val |= LCD_CTRL_BPP_16; + break; + } + + switch (jzfb.cfg & MODE_MASK) { + case MODE_STN_MONO_DUAL: + case MODE_STN_COLOR_DUAL: + case MODE_STN_MONO_SINGLE: + case MODE_STN_COLOR_SINGLE: + switch (jzfb.bpp) { + case 1: + case 2: + val |= LCD_CTRL_FRC_2; + break; + case 4: + val |= LCD_CTRL_FRC_4; + break; + case 8: + default: + val |= LCD_CTRL_FRC_16; + break; + } + break; + } + + val |= LCD_CTRL_BST_16; /* Burst Length is 16WORD=64Byte */ + + switch (jzfb.cfg & MODE_MASK) { + case MODE_STN_MONO_DUAL: + case MODE_STN_COLOR_DUAL: + case MODE_STN_MONO_SINGLE: + case MODE_STN_COLOR_SINGLE: + switch (jzfb.cfg & STN_DAT_PINMASK) { +#define align2(n) (n)=((((n)+1)>>1)<<1) +#define align4(n) (n)=((((n)+3)>>2)<<2) +#define align8(n) (n)=((((n)+7)>>3)<<3) + case STN_DAT_PIN1: + /* Do not adjust the hori-param value. */ + break; + case STN_DAT_PIN2: + align2(jzfb.hsw); + align2(jzfb.elw); + align2(jzfb.blw); + break; + case STN_DAT_PIN4: + align4(jzfb.hsw); + align4(jzfb.elw); + align4(jzfb.blw); + break; + case STN_DAT_PIN8: + align8(jzfb.hsw); + align8(jzfb.elw); + align8(jzfb.blw); + break; + } + break; + } + + val |= 1 << 26; /* Output FIFO underrun protection */ + REG_LCD_CTRL = val; + + switch (jzfb.cfg & MODE_MASK) { + case MODE_STN_MONO_DUAL: + case MODE_STN_COLOR_DUAL: + case MODE_STN_MONO_SINGLE: + case MODE_STN_COLOR_SINGLE: + if (((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL) || + ((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL)) + stnH = jzfb.h >> 1; + else + stnH = jzfb.h; + + REG_LCD_VSYNC = (0 << 16) | jzfb.vsw; + REG_LCD_HSYNC = ((jzfb.blw+jzfb.w) << 16) | (jzfb.blw+jzfb.w+jzfb.hsw); + + /* Screen setting */ + REG_LCD_VAT = ((jzfb.blw + jzfb.w + jzfb.hsw + jzfb.elw) << 16) | (stnH + jzfb.vsw + jzfb.bfw + jzfb.efw); + REG_LCD_DAH = (jzfb.blw << 16) | (jzfb.blw + jzfb.w); + REG_LCD_DAV = (0 << 16) | (stnH); + + /* AC BIAs signal */ + REG_LCD_PS = (0 << 16) | (stnH+jzfb.vsw+jzfb.efw+jzfb.bfw); + + break; + + case MODE_TFT_GEN: + case MODE_TFT_SHARP: + case MODE_TFT_CASIO: + case MODE_TFT_SAMSUNG: + case MODE_8BIT_SERIAL_TFT: + case MODE_TFT_18BIT: + REG_LCD_VSYNC = (0 << 16) | jzfb.vsw; +#if defined(CONFIG_JZLCD_INNOLUX_AT080TN42) + REG_LCD_DAV = (0 << 16) | ( jzfb.h ); +#else + REG_LCD_DAV = ((jzfb.vsw + jzfb.bfw) << 16) | (jzfb.vsw + jzfb.bfw + jzfb.h); +#endif /*#if defined(CONFIG_JZLCD_INNOLUX_AT080TN42)*/ + REG_LCD_VAT = (((jzfb.blw + jzfb.w + jzfb.elw + jzfb.hsw)) << 16) | (jzfb.vsw + jzfb.bfw + jzfb.h + jzfb.efw); + REG_LCD_HSYNC = (0 << 16) | jzfb.hsw; + REG_LCD_DAH = ((jzfb.hsw + jzfb.blw) << 16) | (jzfb.hsw + jzfb.blw + jzfb.w); + break; + } + + switch (jzfb.cfg & MODE_MASK) { + case MODE_TFT_SAMSUNG: + { + unsigned int total, tp_s, tp_e, ckv_s, ckv_e; + unsigned int rev_s, rev_e, inv_s, inv_e; + total = jzfb.blw + jzfb.w + jzfb.elw + jzfb.hsw; + tp_s = jzfb.blw + jzfb.w + 1; + tp_e = tp_s + 1; + ckv_s = tp_s - jz_clocks.pixclk/(1000000000/4100); + ckv_e = tp_s + total; + rev_s = tp_s - 11; /* -11.5 clk */ + rev_e = rev_s + total; + inv_s = tp_s; + inv_e = inv_s + total; + REG_LCD_CLS = (tp_s << 16) | tp_e; + REG_LCD_PS = (ckv_s << 16) | ckv_e; + REG_LCD_SPL = (rev_s << 16) | rev_e; + REG_LCD_REV = (inv_s << 16) | inv_e; + jzfb.cfg |= STFT_REVHI | STFT_SPLHI; + break; + } + case MODE_TFT_SHARP: + { + unsigned int total, cls_s, cls_e, ps_s, ps_e; + unsigned int spl_s, spl_e, rev_s, rev_e; + total = jzfb.blw + jzfb.w + jzfb.elw + jzfb.hsw; +#if !defined(CONFIG_JZLCD_INNOLUX_AT080TN42) + spl_s = 1; + spl_e = spl_s + 1; + cls_s = 0; + cls_e = total - 60; /* > 4us (pclk = 80ns) */ + ps_s = cls_s; + ps_e = cls_e; + rev_s = total - 40; /* > 3us (pclk = 80ns) */ + rev_e = rev_s + total; + jzfb.cfg |= STFT_PSHI; +#else /*#if defined(CONFIG_JZLCD_INNOLUX_AT080TN42)*/ + spl_s = total - 5; /* LD */ + spl_e = total - 3; + cls_s = 32; /* CKV */ + cls_e = 145; + ps_s = 0; /* OEV */ + ps_e = 45; + rev_s = 0; /* POL */ + rev_e = 0; +#endif /*#if defined(CONFIG_JZLCD_INNOLUX_AT080TN42)*/ + REG_LCD_SPL = (spl_s << 16) | spl_e; + REG_LCD_CLS = (cls_s << 16) | cls_e; + REG_LCD_PS = (ps_s << 16) | ps_e; + REG_LCD_REV = (rev_s << 16) | rev_e; + break; + } + case MODE_TFT_CASIO: + break; + } + + /* Configure the LCD panel */ + REG_LCD_CFG = jzfb.cfg; + + /* Timing setting */ + __cpm_stop_lcd(); + + val = jzfb.fclk; /* frame clk */ + + if ( (jzfb.cfg & MODE_MASK) != MODE_8BIT_SERIAL_TFT) { + pclk = val * (jzfb.w + jzfb.hsw + jzfb.elw + jzfb.blw) * + (jzfb.h + jzfb.vsw + jzfb.efw + jzfb.bfw); /* Pixclk */ + } + else { + /* serial mode: Hsync period = 3*Width_Pixel */ + pclk = val * (jzfb.w*3 + jzfb.hsw + jzfb.elw + jzfb.blw) * + (jzfb.h + jzfb.vsw + jzfb.efw + jzfb.bfw); /* Pixclk */ + } + + if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_SINGLE) || + ((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL)) + pclk = (pclk * 3); + + if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_SINGLE) || + ((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL) || + ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_SINGLE) || + ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL)) + pclk = pclk >> ((jzfb.cfg & STN_DAT_PINMASK) >> 4); + + if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL) || + ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL)) + pclk >>= 1; +#if defined(CONFIG_SOC_JZ4730) + val = __cpm_get_pllout() / pclk; + REG_CPM_CFCR2 = val - 1; + val = __cpm_get_pllout() / (pclk * 4); + val = __cpm_divisor_encode(val); + __cpm_set_lcdclk_div(val); + REG_CPM_CFCR |= CPM_CFCR_UPE; +#elif defined(CONFIG_SOC_JZ4740) + val = ( __cpm_get_pllout2()) / pclk; + val--; + if ( val > 0x3ff ) { + printk("pixel clock divid is too large, set it to 0x3ff\n"); + val = 0x3ff; + } + __cpm_set_pixdiv(val); + + val = pclk * 3 ; /* LCDClock > 2.5*Pixclock */ + val =__cpm_get_pllout() / val; + if ( val > 0x1f ) { + printk("lcd clock divide is too large, set it to 0x1f\n"); + val = 0x1f; + } + __cpm_set_ldiv( val ); + REG_CPM_CPCCR |= CPM_CPCCR_CE ; /* update divide */ + +#else + printk("drivers/video/Jzlcd.c, CONFIG_MIPS, please set chip type.\n"); +#endif /*#ifdef CONFIG_MIPS_JZ4730 */ + + jz_clocks.pixclk = __cpm_get_pixclk(); + jz_clocks.lcdclk = __cpm_get_lcdclk(); + printk("LCDC: PixClock:%d LcdClock:%d\n", + jz_clocks.pixclk, jz_clocks.lcdclk); + + __cpm_start_lcd(); + udelay(1000); + return ret; +} + +static irqreturn_t lcd_interrupt_handler(int irq, void *dev_id) +{ + unsigned int state; + + state = REG_LCD_STATE; + + if (state & LCD_STATE_EOF) /* End of frame */ + REG_LCD_STATE = state & ~LCD_STATE_EOF; + + if (state & LCD_STATE_IFU0) { + dprintk("InFiFo0 underrun\n"); + REG_LCD_STATE = state & ~LCD_STATE_IFU0; + } + + if (state & LCD_STATE_OFU) { /* Out fifo underrun */ + REG_LCD_STATE = state & ~LCD_STATE_OFU; + dprintk("Out FiFo underrun.\n"); + } + return IRQ_HANDLED; +} + +#ifdef CONFIG_PM + +/* + * Suspend the LCDC. + */ +static int jzfb_suspend(void) +{ + __lcd_clr_ena(); /* Quick Disable */ + __lcd_display_off(); + __cpm_stop_lcd(); + + return 0; +} + +/* + * Resume the LCDC. + */ +#ifdef CONFIG_SOC_JZ4730 +static int jzfb_resume(void) +{ + __cpm_start_lcd(); + + __lcd_display_pin_init(); + + __lcd_display_on(); + + lcd_hw_init(); + + if (jzfb.bpp <= 8) + REG_LCD_DA0 = virt_to_phys(lcd_palette_desc); + else + REG_LCD_DA0 = virt_to_phys(lcd_frame_desc0); + + if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL) || + ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL)) + REG_LCD_DA1 = virt_to_phys(lcd_frame_desc1); + + __lcd_set_ena(); + return 0; +} + +#else +/* + * Resume the LCDC. + */ +static int jzfb_resume(void) +{ + __cpm_start_lcd(); + __gpio_set_pin(GPIO_DISP_OFF_N); + __lcd_special_on(); + __lcd_set_ena(); + mdelay(200); + __lcd_set_backlight_level(80); + + return 0; +} +#endif /* CONFIG_MIPS_JZ4730 */ + +/* + * Power management hook. Note that we won't be called from IRQ context, + * unlike the blank functions above, so we may sleep. + */ +static int jzlcd_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data) +{ + int ret; + struct lcd_cfb_info *cfb = pm_dev->data; + + if (!cfb) return -EINVAL; + + switch (req) { + case PM_SUSPEND: + ret = jzfb_suspend(); + break; + + case PM_RESUME: + ret = jzfb_resume(); + break; + + default: + ret = -EINVAL; + break; + } + return ret; +} +#else +#define jzfb_suspend NULL +#define jzfb_resume NULL +#endif /* CONFIG_PM */ + +static int __init jzfb_init(void) +{ + struct lcd_cfb_info *cfb; + int err = 0; + + /* In special mode, we only need init special pin, + * as general lcd pin has init in uboot */ +#if defined(CONFIG_SOC_JZ4740) || defined(CONFIG_SOC_JZ4750) + switch (jzfb.cfg & MODE_MASK) { + case LCD_CFG_MODE_SPECIAL_TFT_1: + case LCD_CFG_MODE_SPECIAL_TFT_2: + case LCD_CFG_MODE_SPECIAL_TFT_3: + __gpio_as_lcd_special(); + break; + default: + ; + } +#endif + __lcd_display_pin_init(); + + cfb = jzfb_alloc_fb_info(); + if (!cfb) + goto failed; + + err = jzfb_map_smem(cfb); + if (err) + goto failed; + + jzfb_set_var(&cfb->fb.var, -1, &cfb->fb); + + lcd_descriptor_init(); + + err = lcd_hw_init(); + if (err) + goto failed; + + if (jzfb.bpp <= 8) + REG_LCD_DA0 = virt_to_phys(lcd_palette_desc); + else + REG_LCD_DA0 = virt_to_phys(lcd_frame_desc0); + + if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL) || + ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL)) + REG_LCD_DA1 = virt_to_phys(lcd_frame_desc1); + + __lcd_set_ena(); + + if (request_irq(IRQ_LCD, lcd_interrupt_handler, IRQF_DISABLED, + "lcd", 0)) { + err = -EBUSY; + goto failed; + } + + __lcd_enable_ofu_intr(); /* enable OutFifo underrun */ +// __lcd_enable_ifu0_intr(); /* needn't enable InFifo underrun */ + +#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) + jzfb_rotate_change(rotate_angle); + /* sleep n??? */ +#endif + err = register_framebuffer(&cfb->fb); + if (err < 0) { + dprintk("jzfb_init(): register framebuffer err.\n"); + goto failed; + } + + printk("fb%d: %s frame buffer device, using %dK of video memory\n", + cfb->fb.node, cfb->fb.fix.id, cfb->fb.fix.smem_len>>10); + +#ifdef CONFIG_PM + /* + * Note that the console registers this as well, but we want to + * power down the display prior to sleeping. + */ + cfb->pm = pm_register(PM_SYS_DEV, PM_SYS_VGA, jzlcd_pm_callback); + if (cfb->pm) + cfb->pm->data = cfb; +#endif + + __lcd_display_off(); + + return 0; + +failed: + jzfb_unmap_smem(cfb); + jzfb_free_fb_info(cfb); + + return err; +} + +#if 0 +static int jzfb_remove(struct device *dev) +{ + struct lcd_cfb_info *cfb = dev_get_drvdata(dev); + jzfb_unmap_smem(cfb); + jzfb_free_fb_info(cfb); + return 0; +} +#endif + +#if 0 +static struct device_driver jzfb_driver = { + .name = "jz-lcd", + .bus = &platform_bus_type, + .probe = jzfb_probe, + .remove = jzfb_remove, + .suspend = jzfb_suspend, + .resume = jzfb_resume, +}; +#endif + +static void __exit jzfb_cleanup(void) +{ +#if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) + kthread_stop(jzlcd_info->rotate_daemon_thread); +#endif +// driver_unregister(&jzfb_driver); +// jzfb_remove(); +} + +module_init(jzfb_init); +module_exit(jzfb_cleanup); + +MODULE_DESCRIPTION("JzSOC LCD Controller driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/jzlcd.h b/drivers/video/jzlcd.h new file mode 100644 index 0000000..a4ef75f --- /dev/null +++ b/drivers/video/jzlcd.h @@ -0,0 +1,800 @@ +/* + * 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 + +#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 */ + #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 */ +#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 +#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 + #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) || defined(CONFIG_JZ4740_QI_LB60) +#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(20); \ +} 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__ */ diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index b1ccc04..64eb1ab 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -45,6 +45,15 @@ comment "Watchdog Device Drivers" # Architecture Independent +config JZ_WDT + bool 'JzSoC On-Chip watchdog' + help + Watchdog timer embedded into JZSOC chips. This will reboot your + system when the timeout is reached. + + To compile this driver as a module, choose M here: the + module will be called jz_wdt. + config SOFT_WATCHDOG tristate "Software watchdog" help diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 3d77429..e217064 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -10,6 +10,9 @@ # that also fails then you can fall back to the software watchdog # to give you some cover. +# JZ-watchdog timer +obj-$(CONFIG_JZ_WDT) += jz_wdt.o + # ISA-based Watchdog Cards obj-$(CONFIG_PCWATCHDOG) += pcwd.o obj-$(CONFIG_MIXCOMWD) += mixcomwd.o diff --git a/drivers/watchdog/jz_wdt.c b/drivers/watchdog/jz_wdt.c new file mode 100644 index 0000000..593bb87 --- /dev/null +++ b/drivers/watchdog/jz_wdt.c @@ -0,0 +1,203 @@ +/* + * linux/drivers/char/jz_wdt.c + * + * Watchdog driver for the Ingenic JzSOC + * + * Author: Wei Jianli + * + * 2005 (c) Ingenic Semiconductor. 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 +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TIMER_MARGIN 60 /* (secs) Default is 1 minute */ + +static unsigned int timer_margin = TIMER_MARGIN; /* in seconds */ +static unsigned int timer_rate; +static unsigned int pre_margin; +static unsigned long jz_wdt_users = 0; + +#ifdef MODULE +MODULE_PARM(timer_margin, "i"); +#endif + +static void +jz_wdt_ping(void) +{ + printk("jz_wdt_ping\n"); + /* reload counter with (new) margin */ +#ifdef CONFIG_SOC_JZ4730 + pre_margin = 0xffffffff - timer_rate * timer_margin; + __wdt_set_count(pre_margin); +#endif +#ifdef CONFIG_SOC_JZ4740 + pre_margin = timer_rate * timer_margin; + __wdt_set_count(0); + __wdt_set_data(pre_margin); +#endif +} + +/* + * Allow only one person to hold it open + */ + +static int +jz_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(1, &jz_wdt_users)) + return -EBUSY; + + printk("jz_wdt_open\n"); +#ifdef CONFIG_SOC_JZ4730 + if (REG_CPM_OCR & CPM_OCR_EXT_RTC_CLK) + timer_rate = 32768; + else + timer_rate = JZ_EXTAL/128; +#endif + +#ifdef CONFIG_SOC_JZ4740 + /* Initialize the wdt clocks */ + __wdt_select_rtcclk(); + __wdt_select_clk_div1024(); + __tcu_start_wdt_clock(); + timer_rate = 32; /* 32768 / 1024 */ +#endif + + jz_wdt_ping(); + __wdt_start(); + + return 0; +} + +static int +jz_wdt_release(struct inode *inode, struct file *file) +{ + /* + * Shut off the timer. + * Lock it in if it's a module and we defined ...NOWAYOUT + */ + jz_wdt_ping(); +#ifndef CONFIG_WATCHDOG_NOWAYOUT + /* SW can't stop wdt once it was started */ +#endif + jz_wdt_users = 0; + return 0; +} + +static ssize_t +jz_wdt_write(struct file *file, const char *data, size_t len, loff_t * ppos) +{ + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; + + printk("jz_wdt_write\n"); + + /* Refresh counter */ + if (len) { + jz_wdt_ping(); + return 1; + } + return 0; +} + +static int +jz_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int new_margin; + static struct watchdog_info ident = { + .identity = "JzSOC Watchdog", + .options = WDIOF_SETTIMEOUT, + .firmware_version = 0, + }; + + switch (cmd) { + default: + return -ENOIOCTLCMD; + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info *) arg, &ident, + sizeof (ident)); + case WDIOC_GETSTATUS: + return put_user(0, (int *) arg); + case WDIOC_GETBOOTSTATUS: +#ifdef CONFIG_SOC_JZ4730 + return put_user(REG_CPM_RSTR, (int *) arg); +#endif +#ifdef CONFIG_SOC_JZ4740 + return put_user(REG_RTC_HWRSR, (int *) arg); +#endif + case WDIOC_KEEPALIVE: + jz_wdt_ping(); + return 0; + case WDIOC_SETTIMEOUT: + if (get_user(new_margin, (int *) arg)) + return -EFAULT; + if (new_margin < 1) + return -EINVAL; + timer_margin = new_margin; + jz_wdt_ping(); + /* Fall */ + case WDIOC_GETTIMEOUT: + return put_user(timer_margin, (int *) arg); + } +} + +static struct file_operations jz_wdt_fops = { + .owner = THIS_MODULE, + .write = jz_wdt_write, + .ioctl = jz_wdt_ioctl, + .open = jz_wdt_open, + .release = jz_wdt_release, +}; + +static struct miscdevice jz_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "jz_wdt", + .fops = &jz_wdt_fops +}; + +static int __init +jz_wdt_init(void) +{ + int ret; + + ret = misc_register(&jz_wdt_miscdev); + + if (ret) + return ret; + + printk("JzSOC Watchdog Timer: timer margin %d sec\n", timer_margin); + + return 0; +} + +static void __exit +jz_wdt_exit(void) +{ + misc_deregister(&jz_wdt_miscdev); +} + +module_init(jz_wdt_init); +module_exit(jz_wdt_exit); + +MODULE_AUTHOR("Wei Jianli"); +MODULE_LICENSE("GPL"); diff --git a/include/asm-mips/jzsoc.h b/include/asm-mips/jzsoc.h new file mode 100644 index 0000000..54df338 --- /dev/null +++ b/include/asm-mips/jzsoc.h @@ -0,0 +1,49 @@ +/* + * linux/include/asm-mips/jzsoc.h + * + * Ingenic's JZXXXX SoC common include. + * + * Copyright (C) 2006 - 2008 Ingenic Semiconductor Inc. + * + * Author: + * + * 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_JZ4730 +#include +#endif + +#ifdef CONFIG_SOC_JZ4740 +#include +#endif + +#ifdef CONFIG_SOC_JZ4750 +#include +#endif + +#ifdef CONFIG_SOC_JZ4750D +#include +#endif + +/* + * Generic I/O routines + */ +#define readb(addr) (*(volatile unsigned char *)(addr)) +#define readw(addr) (*(volatile unsigned short *)(addr)) +#define readl(addr) (*(volatile unsigned int *)(addr)) + +#define writeb(b,addr) ((*(volatile unsigned char *)(addr)) = (b)) +#define writew(b,addr) ((*(volatile unsigned short *)(addr)) = (b)) +#define writel(b,addr) ((*(volatile unsigned int *)(addr)) = (b)) + +#endif /* __ASM_JZSOC_H__ */ diff --git a/include/asm-mips/mach-jz4730/board-pmp.h b/include/asm-mips/mach-jz4730/board-pmp.h new file mode 100644 index 0000000..44475d2 --- /dev/null +++ b/include/asm-mips/mach-jz4730/board-pmp.h @@ -0,0 +1,83 @@ +/* + * linux/include/asm-mips/mach-jz4730/board-pmp.h + * + * JZ4730-based PMP board ver 2.x definition. + * + * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. + * + * Author: + * + * 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_JZ4730_PMP_H__ +#define __ASM_JZ4730_PMP_H__ + +/*====================================================================== + * EXTAL frequency + */ +#define JZ_EXTAL 12000000 /* EXTAL: 12 MHz */ +#define JZ_EXTAL2 32768 /* EXTAL2: 32.768 KHz */ + + +/*====================================================================== + * GPIO + */ +#define GPIO_PW_I 97 +#define GPIO_PW_O 66 +#define GPIO_LED_EN 92 +#define GPIO_DISP_OFF_N 93 +#define GPIO_PWM0 94 +#define GPIO_RTC_IRQ 96 +#define GPIO_USB_CLK_EN 29 +#define GPIO_CHARG_STAT 125 +#define GPIO_TS_PENIRQ 98 +#define GPIO_UDC_HOTPLUG 86 + +/*====================================================================== + * MMC/SD + */ +#define MSC_WP_PIN 82 +#define MSC_POWEREN_PIN 91 +#define MSC_HOTPLUG_PIN 90 +#define MSC_HOTPLUG_IRQ (IRQ_GPIO_0 + MSC_HOTPLUG_PIN) + +/* enable slot power */ +#define __msc_init_io() \ +do { \ + __gpio_as_input(MSC_WP_PIN); \ + __gpio_as_output(MSC_POWEREN_PIN); \ +} while (0) + +/* enable slot power */ +#define __msc_enable_power() \ +do { \ + __gpio_clear_pin(MSC_POWEREN_PIN); \ +} while (0) + +/* disable slot power */ +#define __msc_disable_power() \ +do { \ + __gpio_set_pin(MSC_POWEREN_PIN); \ +} while (0) + +/* detect card insertion or not */ +#define __msc_card_detected(slot) \ +({ \ + int ret; \ + if (slot == 0) { \ + __gpio_mask_irq(MSC_HOTPLUG_IRQ); \ + __gpio_as_input(MSC_HOTPLUG_PIN); \ + ret = __gpio_get_pin(MSC_HOTPLUG_PIN); \ + __gpio_unmask_irq(MSC_HOTPLUG_IRQ); \ + } \ + else { \ + ret = 1; \ + } \ + ret = !ret; \ + ret; \ +}) + +#endif /* __ASM_JZ4730_PMP_H__ */ diff --git a/include/asm-mips/mach-jz4730/clock.h b/include/asm-mips/mach-jz4730/clock.h new file mode 100644 index 0000000..16971d0 --- /dev/null +++ b/include/asm-mips/mach-jz4730/clock.h @@ -0,0 +1,184 @@ +/* + * linux/include/asm-mips/mach-jz4730/clock.h + * + * JZ4730 clocks definition. + * + * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. + * + * Author: + * + * 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_JZ4730_CLOCK_H__ +#define __ASM_JZ4730_CLOCK_H__ + +#ifndef JZ_EXTAL +#define JZ_EXTAL 3686400 +#endif + +#ifndef JZ_EXTAL2 +#define JZ_EXTAL2 32768 +#endif + +/* + * JZ4730 clocks structure + */ +typedef struct { + unsigned int iclk; /* CPU core clock */ + unsigned int sclk; /* AHB bus clock */ + unsigned int mclk; /* Memory bus clock */ + unsigned int pclk; /* APB bus clock */ + unsigned int devclk; /* Devcie clock to specific modules */ + unsigned int rtcclk; /* RTC module clock */ + unsigned int uartclk; /* UART module clock */ + unsigned int lcdclk; /* LCD module clock */ + unsigned int pixclk; /* LCD pixel clock */ + unsigned int usbclk; /* USB module clock */ + unsigned int i2sclk; /* I2S module clock */ + unsigned int mscclk; /* MMC/SD module clock */ +} jz_clocks_t; + +extern jz_clocks_t jz_clocks; + + +static __inline__ unsigned int __cpm_get_pllout(void) +{ + unsigned int nf, nr, no, pllout; + unsigned long plcr = REG_CPM_PLCR1; + unsigned long od[4] = {1, 2, 2, 4}; + if (plcr & CPM_PLCR1_PLL1EN) { + nf = (plcr & CPM_PLCR1_PLL1FD_MASK) >> CPM_PLCR1_PLL1FD_BIT; + nr = (plcr & CPM_PLCR1_PLL1RD_MASK) >> CPM_PLCR1_PLL1RD_BIT; + no = od[((plcr & CPM_PLCR1_PLL1OD_MASK) >> CPM_PLCR1_PLL1OD_BIT)]; + pllout = (JZ_EXTAL) / ((nr+2) * no) * (nf+2); + } else + pllout = JZ_EXTAL; + return pllout; +} + +static __inline__ unsigned int __cpm_get_iclk(void) +{ + unsigned int iclk; + int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; + unsigned long cfcr = REG_CPM_CFCR; + unsigned long plcr = REG_CPM_PLCR1; + if (plcr & CPM_PLCR1_PLL1EN) + iclk = __cpm_get_pllout() / + div[(cfcr & CPM_CFCR_IFR_MASK) >> CPM_CFCR_IFR_BIT]; + else + iclk = JZ_EXTAL; + return iclk; +} + +static __inline__ unsigned int __cpm_get_sclk(void) +{ + unsigned int sclk; + int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; + unsigned long cfcr = REG_CPM_CFCR; + unsigned long plcr = REG_CPM_PLCR1; + if (plcr & CPM_PLCR1_PLL1EN) + sclk = __cpm_get_pllout() / + div[(cfcr & CPM_CFCR_SFR_MASK) >> CPM_CFCR_SFR_BIT]; + else + sclk = JZ_EXTAL; + return sclk; +} + +static __inline__ unsigned int __cpm_get_mclk(void) +{ + unsigned int mclk; + int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; + unsigned long cfcr = REG_CPM_CFCR; + unsigned long plcr = REG_CPM_PLCR1; + if (plcr & CPM_PLCR1_PLL1EN) + mclk = __cpm_get_pllout() / + div[(cfcr & CPM_CFCR_MFR_MASK) >> CPM_CFCR_MFR_BIT]; + else + mclk = JZ_EXTAL; + return mclk; +} + +static __inline__ unsigned int __cpm_get_pclk(void) +{ + unsigned int devclk; + int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; + unsigned long cfcr = REG_CPM_CFCR; + unsigned long plcr = REG_CPM_PLCR1; + if (plcr & CPM_PLCR1_PLL1EN) + devclk = __cpm_get_pllout() / + div[(cfcr & CPM_CFCR_PFR_MASK) >> CPM_CFCR_PFR_BIT]; + else + devclk = JZ_EXTAL; + return devclk; +} + +static __inline__ unsigned int __cpm_get_lcdclk(void) +{ + unsigned int lcdclk; + int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; + unsigned long cfcr = REG_CPM_CFCR; + unsigned long plcr = REG_CPM_PLCR1; + if (plcr & CPM_PLCR1_PLL1EN) + lcdclk = __cpm_get_pllout() / + div[(cfcr & CPM_CFCR_LFR_MASK) >> CPM_CFCR_LFR_BIT]; + else + lcdclk = JZ_EXTAL; + return lcdclk; +} + +static __inline__ unsigned int __cpm_get_pixclk(void) +{ + unsigned int pixclk; + unsigned long cfcr2 = REG_CPM_CFCR2; + pixclk = __cpm_get_pllout() / (cfcr2 + 1); + return pixclk; +} + +static __inline__ unsigned int __cpm_get_devclk(void) +{ + return JZ_EXTAL; +} + +static __inline__ unsigned int __cpm_get_rtcclk(void) +{ + return JZ_EXTAL2; +} + +static __inline__ unsigned int __cpm_get_uartclk(void) +{ + return JZ_EXTAL; +} + +static __inline__ unsigned int __cpm_get_usbclk(void) +{ + unsigned int usbclk; + unsigned long cfcr = REG_CPM_CFCR; + if (cfcr & CPM_CFCR_UCS) + usbclk = 48000000; + else + usbclk = __cpm_get_pllout() / + (((cfcr &CPM_CFCR_UFR_MASK) >> CPM_CFCR_UFR_BIT) + 1); + return usbclk; +} + +static __inline__ unsigned int __cpm_get_i2sclk(void) +{ + unsigned int i2sclk; + unsigned long cfcr = REG_CPM_CFCR; + i2sclk = __cpm_get_pllout() / + ((cfcr & CPM_CFCR_I2S) ? 2: 1); + return i2sclk; +} + +static __inline__ unsigned int __cpm_get_mscclk(void) +{ + if (REG_CPM_CFCR & CPM_CFCR_MSC) + return 24000000; + else + return 16000000; +} + +#endif /* __ASM_JZ4730_CLOCK_H__ */ diff --git a/include/asm-mips/mach-jz4730/dma.h b/include/asm-mips/mach-jz4730/dma.h new file mode 100644 index 0000000..511152e --- /dev/null +++ b/include/asm-mips/mach-jz4730/dma.h @@ -0,0 +1,272 @@ +/* + * linux/include/asm-mips/mach-jz4730/dma.h + * + * JZ4730 DMA definition. + * + * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. + * + * Author: + * + * 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_JZ4730_DMA_H__ +#define __ASM_JZ4730_DMA_H__ + +#include +#include /* need byte IO */ +#include /* And spinlocks */ +#include +#include + +#define DMA_UNIT_32 32 +#define DMA_UNIT_16 16 + + +/* block-mode EOP: high DREQ: high DACK: low*/ +#define DMA_BLOCK_CONF \ + DMAC_DCCSR_TM | \ + DMAC_DCCSR_DS_8b | DMAC_DCCSR_RDIL_IGN | \ + DMAC_DCCSR_ERDM_HLEVEL | DMAC_DCCSR_EACKS + +/* single-mode EOP: high DREQ: high DACK: low */ +#define DMA_SINGLE_CONF \ + DMAC_DCCSR_DS_8b | DMAC_DCCSR_RDIL_IGN | \ + DMAC_DCCSR_ERDM_HLEVEL | DMAC_DCCSR_EACKS + +#define DMA_8bit_RX_CONF \ + DMAC_DCCSR_DAM | \ + DMAC_DCCSR_SWDH_8 | DMAC_DCCSR_DWDH_32 | \ + DMAC_DCCSR_DS_8b | DMAC_DCCSR_RDIL_IGN + +#define DMA_8bit_TX_CONF \ + DMAC_DCCSR_SAM | \ + DMAC_DCCSR_SWDH_32 | DMAC_DCCSR_DWDH_8 | \ + DMAC_DCCSR_DS_8b | DMAC_DCCSR_RDIL_IGN + +#define DMA_16bit_RX_CONF \ + DMAC_DCCSR_DAM | \ + DMAC_DCCSR_SWDH_16 | DMAC_DCCSR_DWDH_32 | \ + DMAC_DCCSR_DS_16b | DMAC_DCCSR_RDIL_IGN + +#define DMA_16bit_TX_CONF \ + DMAC_DCCSR_SAM | \ + DMAC_DCCSR_SWDH_32 | DMAC_DCCSR_DWDH_16 | \ + DMAC_DCCSR_DS_16b | DMAC_DCCSR_RDIL_IGN + +#define DMA_32bit_RX_CONF \ + DMAC_DCCSR_DAM | \ + DMAC_DCCSR_SWDH_32 | DMAC_DCCSR_DWDH_32 | \ + DMAC_DCCSR_DS_32b | DMAC_DCCSR_RDIL_IGN + +#define DMA_32bit_TX_CONF \ + DMAC_DCCSR_SAM | \ + DMAC_DCCSR_SWDH_32 | DMAC_DCCSR_DWDH_32 | \ + DMAC_DCCSR_DS_32b | DMAC_DCCSR_RDIL_IGN + +#define DMA_16BYTE_RX_CONF \ + DMAC_DCCSR_DAM | \ + DMAC_DCCSR_SWDH_8 | DMAC_DCCSR_DWDH_32 | \ + DMAC_DCCSR_DS_16B | DMAC_DCCSR_RDIL_IGN + +#define DMA_16BYTE_TX_CONF \ + DMAC_DCCSR_SAM | \ + DMAC_DCCSR_SWDH_32 | DMAC_DCCSR_DWDH_8 | \ + DMAC_DCCSR_DS_16B | DMAC_DCCSR_RDIL_IGN + +#define DMA_AIC_32_16BYTE_TX_CMD \ + DMAC_DCCSR_SAM | \ + DMAC_DCCSR_SWDH_32 | DMAC_DCCSR_DWDH_32 | \ + DMAC_DCCSR_DS_16B | DMAC_DCCSR_RDIL_IGN + +#define DMA_AIC_32_16BYTE_RX_CMD \ + DMAC_DCCSR_DAM | \ + DMAC_DCCSR_SWDH_32 | DMAC_DCCSR_DWDH_32 | \ + DMAC_DCCSR_DS_16B | DMAC_DCCSR_RDIL_IGN + +#define DMA_AIC_16BIT_TX_CMD \ + DMAC_DCCSR_SAM | \ + DMAC_DCCSR_SWDH_16 | DMAC_DCCSR_DWDH_16 | \ + DMAC_DCCSR_DS_16b | DMAC_DCCSR_RDIL_IGN + +#define DMA_AIC_16BIT_RX_CMD \ + DMAC_DCCSR_DAM | \ + DMAC_DCCSR_SWDH_16 | DMAC_DCCSR_DWDH_16 | \ + DMAC_DCCSR_DS_16b | DMAC_DCCSR_RDIL_IGN + +#define DMA_AIC_16BYTE_RX_CMD \ + DMAC_DCCSR_DAM | \ + DMAC_DCCSR_SWDH_16 | DMAC_DCCSR_DWDH_16 | \ + DMAC_DCCSR_DS_16B | DMAC_DCCSR_RDIL_IGN + +#define DMA_AIC_16BYTE_TX_CMD \ + DMAC_DCCSR_SAM | \ + DMAC_DCCSR_SWDH_16 | DMAC_DCCSR_DWDH_16 | \ + DMAC_DCCSR_DS_16B | DMAC_DCCSR_RDIL_IGN + +/* DMA Device ID's follow */ +enum { + DMA_ID_UART0_TX = 0, + DMA_ID_UART0_RX, + DMA_ID_UART1_TX, + DMA_ID_UART1_RX, + DMA_ID_UART2_TX, + DMA_ID_UART2_RX, + DMA_ID_UART3_TX, + DMA_ID_UART3_RX, + DMA_ID_SSI_TX, + DMA_ID_SSI_RX, + DMA_ID_MSC_TX, + DMA_ID_MSC_RX, + DMA_ID_AIC_TX, + DMA_ID_AIC_RX, + DMA_ID_BLOCK, /* DREQ */ + DMA_ID_SINGLE, /* DREQ */ + DMA_ID_PCMCIA0_TX, + DMA_ID_PCMCIA0_RX, + DMA_ID_PCMCIA1_TX, + DMA_ID_PCMCIA2_RX, + DMA_ID_AUTO, + DMA_ID_RAW_SET, + NUM_DMA_DEV +}; + +/* dummy DCCSR bit, i386 style DMA macros compitable */ +#define DMA_MODE_READ 0 /* I/O to memory, no autoinit, + * increment, single mode */ +#define DMA_MODE_WRITE 1 /* memory to I/O, no autoinit, + * increment, single mode */ +#define DMA_MODE_CASCADE 2 /* pass thru DREQ->HRQ, + * DACK<-HLDA only */ +#define DMA_AUTOINIT 3 +#define DMA_MODE_MASK 3 + +struct jz_dma_chan { + int dev_id; /* this channel is allocated if >=0, + * free otherwise */ + unsigned int io; + const char *dev_str; + int irq; + void *irq_dev; + unsigned int fifo_addr; + unsigned int mode; + unsigned int source; +}; + +extern struct jz_dma_chan jz_dma_table[]; + +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 a); +extern void set_dma_count(unsigned int dmanr, unsigned int count); +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 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 > NUM_DMA + || 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_TC | 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_HTR); +} +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_TC | DMAC_DCCSR_AR); + REG_DMAC_DMACR &= ~(DMAC_DMACR_HTR | DMAC_DMACR_AER); +} + +static __inline__ void set_dma_page(unsigned int dmanr, char pagenr) +{ +} + +static __inline__ unsigned int get_dma_done_status(unsigned int dmanr) +{ + struct jz_dma_chan *chan = get_dma_chan(dmanr); + unsigned long dccsr; + if (!chan) + return 0; + + dccsr = REG_DMAC_DCCSR(chan->io); + return dccsr & (DMAC_DCCSR_HLT | DMAC_DCCSR_TC | 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_JZ4730_DMA_H__ */ diff --git a/include/asm-mips/mach-jz4730/jz4730.h b/include/asm-mips/mach-jz4730/jz4730.h new file mode 100644 index 0000000..5688f01 --- /dev/null +++ b/include/asm-mips/mach-jz4730/jz4730.h @@ -0,0 +1,40 @@ +/* + * linux/include/asm-mips/mach-jz4730/jz4730.h + * + * JZ4730 common definition. + * + * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. + * + * Author: + * + * 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_JZ4730_H__ +#define __ASM_JZ4730_H__ + +#include +#include +#include +#include + +/*------------------------------------------------------------------ + * Platform definitions + */ +#ifdef CONFIG_JZ4730_PMP +#include +#endif + +/* Add other platform definition here ... */ + + +/*------------------------------------------------------------------ + * Follows are related to platform definitions + */ + +#include +#include + +#endif /* __ASM_JZ4730_H__ */ diff --git a/include/asm-mips/mach-jz4730/misc.h b/include/asm-mips/mach-jz4730/misc.h new file mode 100644 index 0000000..ea01474 --- /dev/null +++ b/include/asm-mips/mach-jz4730/misc.h @@ -0,0 +1,28 @@ +/* + * linux/include/asm-mips/mach-jz4730/misc.h + * + * JZ4730 miscillaneous definitions. + * + * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. + * + * Author: + * + * 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_JZ4730_MISC_H__ +#define __ASM_JZ4730_MISC_H__ + +/* + * I2C routines + */ + +extern void i2c_open(void); +extern void i2c_close(void); +extern void i2c_setclk(unsigned int i2cclk); +extern int i2c_read(unsigned char, unsigned char *, unsigned char, int); +extern int i2c_write(unsigned char, unsigned char *, unsigned char, int); + +#endif /* __ASM_JZ4730_MISC_H__ */ diff --git a/include/asm-mips/mach-jz4730/ops.h b/include/asm-mips/mach-jz4730/ops.h new file mode 100644 index 0000000..625419e --- /dev/null +++ b/include/asm-mips/mach-jz4730/ops.h @@ -0,0 +1,2541 @@ +/* + * linux/include/asm-mips/mach-jz4730/ops.h + * + * JZ4730 module operations definition. + * + * Copyright (C) 2006 - 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 __ASM_JZ4730_OPS_H__ +#define __ASM_JZ4730_OPS_H__ + +/*************************************************************************** + * MSC + ***************************************************************************/ + +#define __msc_start_op() \ + ( REG_MSC_STRPCL = MSC_STRPCL_START_OP | MSC_STRPCL_CLOCK_CONTROL_START ) + +#define __msc_set_resto(to) ( REG_MSC_RESTO = to ) +#define __msc_set_rdto(to) ( REG_MSC_RDTO = to ) +#define __msc_set_cmd(cmd) ( REG_MSC_CMD = cmd ) +#define __msc_set_arg(arg) ( REG_MSC_ARG = arg ) +#define __msc_set_nob(nob) ( REG_MSC_NOB = nob ) +#define __msc_get_nob() ( REG_MSC_NOB ) +#define __msc_set_blklen(len) ( REG_MSC_BLKLEN = len ) +#define __msc_set_cmdat(cmdat) ( REG_MSC_CMDAT = cmdat ) +#define __msc_set_cmdat_ioabort() ( REG_MSC_CMDAT |= MSC_CMDAT_IO_ABORT ) +#define __msc_clear_cmdat_ioabort() ( REG_MSC_CMDAT &= ~MSC_CMDAT_IO_ABORT ) + +#define __msc_set_cmdat_bus_width1() \ +do { \ + REG_MSC_CMDAT &= ~MSC_CMDAT_BUS_WIDTH_MASK; \ + REG_MSC_CMDAT |= MSC_CMDAT_BUS_WIDTH_1BIT; \ +} while(0) + +#define __msc_set_cmdat_bus_width4() \ +do { \ + REG_MSC_CMDAT &= ~MSC_CMDAT_BUS_WIDTH_MASK; \ + REG_MSC_CMDAT |= MSC_CMDAT_BUS_WIDTH_4BIT; \ +} while(0) + +#define __msc_set_cmdat_dma_en() ( REG_MSC_CMDAT |= MSC_CMDAT_DMA_EN ) +#define __msc_set_cmdat_init() ( REG_MSC_CMDAT |= MSC_CMDAT_INIT ) +#define __msc_set_cmdat_busy() ( REG_MSC_CMDAT |= MSC_CMDAT_BUSY ) +#define __msc_set_cmdat_stream() ( REG_MSC_CMDAT |= MSC_CMDAT_STREAM_BLOCK ) +#define __msc_set_cmdat_block() ( REG_MSC_CMDAT &= ~MSC_CMDAT_STREAM_BLOCK ) +#define __msc_set_cmdat_read() ( REG_MSC_CMDAT &= ~MSC_CMDAT_WRITE_READ ) +#define __msc_set_cmdat_write() ( REG_MSC_CMDAT |= MSC_CMDAT_WRITE_READ ) +#define __msc_set_cmdat_data_en() ( REG_MSC_CMDAT |= MSC_CMDAT_DATA_EN ) + +/* r is MSC_CMDAT_RESPONSE_FORMAT_Rx or MSC_CMDAT_RESPONSE_FORMAT_NONE */ +#define __msc_set_cmdat_res_format(r) \ +do { \ + REG_MSC_CMDAT &= ~MSC_CMDAT_RESPONSE_FORMAT_MASK; \ + REG_MSC_CMDAT |= (r); \ +} while(0) + +#define __msc_clear_cmdat() \ + REG_MSC_CMDAT &= ~( MSC_CMDAT_IO_ABORT | MSC_CMDAT_DMA_EN | MSC_CMDAT_INIT| \ + MSC_CMDAT_BUSY | MSC_CMDAT_STREAM_BLOCK | MSC_CMDAT_WRITE_READ | \ + MSC_CMDAT_DATA_EN | MSC_CMDAT_RESPONSE_FORMAT_MASK ) + +#define __msc_get_imask() ( REG_MSC_IMASK ) +#define __msc_mask_all_intrs() ( REG_MSC_IMASK = 0xff ) +#define __msc_unmask_all_intrs() ( REG_MSC_IMASK = 0x00 ) +#define __msc_mask_rd() ( REG_MSC_IMASK |= MSC_IMASK_RXFIFO_RD_REQ ) +#define __msc_unmask_rd() ( REG_MSC_IMASK &= ~MSC_IMASK_RXFIFO_RD_REQ ) +#define __msc_mask_wr() ( REG_MSC_IMASK |= MSC_IMASK_TXFIFO_WR_REQ ) +#define __msc_unmask_wr() ( REG_MSC_IMASK &= ~MSC_IMASK_TXFIFO_WR_REQ ) +#define __msc_mask_endcmdres() ( REG_MSC_IMASK |= MSC_IMASK_END_CMD_RES ) +#define __msc_unmask_endcmdres() ( REG_MSC_IMASK &= ~MSC_IMASK_END_CMD_RES ) +#define __msc_mask_datatrandone() ( REG_MSC_IMASK |= MSC_IMASK_DATA_TRAN_DONE ) +#define __msc_unmask_datatrandone() ( REG_MSC_IMASK &= ~MSC_IMASK_DATA_TRAN_DONE ) +#define __msc_mask_prgdone() ( REG_MSC_IMASK |= MSC_IMASK_PRG_DONE ) +#define __msc_unmask_prgdone() ( REG_MSC_IMASK &= ~MSC_IMASK_PRG_DONE ) + +/* n=0,1,2,3,4,5,6,7 */ +#define __msc_set_clkrt(n) \ +do { \ + REG_MSC_CLKRT = n; \ +} while(0) + +#define __msc_get_ireg() ( REG_MSC_IREG ) +#define __msc_ireg_rd() ( REG_MSC_IREG & MSC_IREG_RXFIFO_RD_REQ ) +#define __msc_ireg_wr() ( REG_MSC_IREG & MSC_IREG_TXFIFO_WR_REQ ) +#define __msc_ireg_end_cmd_res() ( REG_MSC_IREG & MSC_IREG_END_CMD_RES ) +#define __msc_ireg_data_tran_done() ( REG_MSC_IREG & MSC_IREG_DATA_TRAN_DONE ) +#define __msc_ireg_prg_done() ( REG_MSC_IREG & MSC_IREG_PRG_DONE ) +#define __msc_ireg_clear_end_cmd_res() ( REG_MSC_IREG = MSC_IREG_END_CMD_RES ) +#define __msc_ireg_clear_data_tran_done() ( REG_MSC_IREG = MSC_IREG_DATA_TRAN_DONE ) +#define __msc_ireg_clear_prg_done() ( REG_MSC_IREG = MSC_IREG_PRG_DONE ) + +#define __msc_get_stat() ( REG_MSC_STAT ) +#define __msc_stat_not_end_cmd_res() ( (REG_MSC_STAT & MSC_STAT_END_CMD_RES) == 0) +#define __msc_stat_crc_err() \ + ( REG_MSC_STAT & (MSC_STAT_CRC_RES_ERR | MSC_STAT_CRC_READ_ERROR | MSC_STAT_CRC_WRITE_ERROR_YES) ) +#define __msc_stat_res_crc_err() ( REG_MSC_STAT & MSC_STAT_CRC_RES_ERR ) +#define __msc_stat_rd_crc_err() ( REG_MSC_STAT & MSC_STAT_CRC_READ_ERROR ) +#define __msc_stat_wr_crc_err() ( REG_MSC_STAT & MSC_STAT_CRC_WRITE_ERROR_YES ) +#define __msc_stat_resto_err() ( REG_MSC_STAT & MSC_STAT_TIME_OUT_RES ) +#define __msc_stat_rdto_err() ( REG_MSC_STAT & MSC_STAT_TIME_OUT_READ ) + +#define __msc_rd_resfifo() ( REG_MSC_RES ) +#define __msc_rd_rxfifo() ( REG_MSC_RXFIFO ) +#define __msc_wr_txfifo(v) ( REG_MSC_TXFIFO = v ) + +#define __msc_reset() \ +do { \ + REG_MSC_STRPCL = MSC_STRPCL_RESET; \ + while (REG_MSC_STAT & MSC_STAT_IS_RESETTING); \ +} while (0) + +#define __msc_start_clk() \ +do { \ + REG_MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_START; \ +} while (0) + +#define __msc_stop_clk() \ +do { \ + REG_MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_STOP; \ +} while (0) + +#define MMC_CLK 19169200 +#define SD_CLK 24576000 + +/* msc_clk should little than pclk and little than clk retrieve from card */ +#define __msc_calc_clk_divisor(type,dev_clk,msc_clk,lv) \ +do { \ + unsigned int rate, pclk, i; \ + pclk = dev_clk; \ + rate = type?SD_CLK:MMC_CLK; \ + if (msc_clk && msc_clk < pclk) \ + pclk = msc_clk; \ + i = 0; \ + while (pclk < rate) \ + { \ + i ++; \ + rate >>= 1; \ + } \ + lv = i; \ +} while(0) + +/* divide rate to little than or equal to 400kHz */ +#define __msc_calc_slow_clk_divisor(type, lv) \ +do { \ + unsigned int rate, i; \ + rate = (type?SD_CLK:MMC_CLK)/1000/400; \ + i = 0; \ + while (rate > 0) \ + { \ + rate >>= 1; \ + i ++; \ + } \ + lv = i; \ +} while(0) + +/*************************************************************************** + * RTC + ***************************************************************************/ + +#define __rtc_start() ( REG_RTC_RCR |= RTC_RCR_START ) +#define __rtc_stop() ( REG_RTC_RCR &= ~RTC_RCR_START ) + +#define __rtc_enable_alarm() ( REG_RTC_RCR |= RTC_RCR_AE ) +#define __rtc_disable_alarm() ( REG_RTC_RCR &= ~RTC_RCR_AE ) +#define __rtc_enable_alarm_irq() ( REG_RTC_RCR |= RTC_RCR_AIE ) +#define __rtc_disable_alarm_irq() ( REG_RTC_RCR &= ~RTC_RCR_AIE ) + +#define __rtc_enable_1hz_irq() ( REG_RTC_RCR |= RTC_RCR_HZIE ) +#define __rtc_disable_1hz_irq() ( REG_RTC_RCR &= ~RTC_RCR_HZIE ) + +#define __rtc_is_alarm_flag() ( REG_RTC_RCR & RTC_RCR_AF ) +#define __rtc_is_1hz_flag() ( REG_RTC_RCR & RTC_RCR_HZ ) +#define __rtc_clear_alarm_flag() ( REG_RTC_RCR &= ~RTC_RCR_AF ) +#define __rtc_clear_1hz_flag() ( REG_RTC_RCR &= ~RTC_RCR_HZ ) + +#define __rtc_set_second(s) ( REG_RTC_RSR = (s) ) +#define __rtc_get_second() REG_RTC_RSR +#define __rtc_set_alarm(s) ( REG_RTC_RSAR = (s) ) +#define __rtc_get_alarm() REG_RTC_RSAR + +#define __rtc_adjust_1hz(f32k) \ + ( REG_RTC_RGR = (REG_RTC_RGR & ~(RTC_REG_DIV_MASK | RTC_RGR_ADJ_MASK)) | f32k | 0 ) +#define __rtc_lock_1hz() ( REG_RTC_RGR |= RTC_RGR_LOCK ) + + +/*************************************************************************** + * FIR + ***************************************************************************/ + +/* enable/disable fir unit */ +#define __fir_enable() ( REG_FIR_CR1 |= FIR_CR1_FIRUE ) +#define __fir_disable() ( REG_FIR_CR1 &= ~FIR_CR1_FIRUE ) + +/* enable/disable address comparison */ +#define __fir_enable_ac() ( REG_FIR_CR1 |= FIR_CR1_ACE ) +#define __fir_disable_ac() ( REG_FIR_CR1 &= ~FIR_CR1_ACE ) + +/* select frame end mode as underrun or normal */ +#define __fir_set_eous() ( REG_FIR_CR1 |= FIR_CR1_EOUS ) +#define __fir_clear_eous() ( REG_FIR_CR1 &= ~FIR_CR1_EOUS ) + +/* enable/disable transmitter idle interrupt */ +#define __fir_enable_tii() ( REG_FIR_CR1 |= FIR_CR1_TIIE ) +#define __fir_disable_tii() ( REG_FIR_CR1 &= ~FIR_CR1_TIIE ) + +/* enable/disable transmit FIFO service request interrupt */ +#define __fir_enable_tfi() ( REG_FIR_CR1 |= FIR_CR1_TFIE ) +#define __fir_disable_tfi() ( REG_FIR_CR1 &= ~FIR_CR1_TFIE ) + +/* enable/disable receive FIFO service request interrupt */ +#define __fir_enable_rfi() ( REG_FIR_CR1 |= FIR_CR1_RFIE ) +#define __fir_disable_rfi() ( REG_FIR_CR1 &= ~FIR_CR1_RFIE ) + +/* enable/disable tx function */ +#define __fir_tx_enable() ( REG_FIR_CR1 |= FIR_CR1_TXE ) +#define __fir_tx_disable() ( REG_FIR_CR1 &= ~FIR_CR1_TXE ) + +/* enable/disable rx function */ +#define __fir_rx_enable() ( REG_FIR_CR1 |= FIR_CR1_RXE ) +#define __fir_rx_disable() ( REG_FIR_CR1 &= ~FIR_CR1_RXE ) + + +/* enable/disable serial infrared interaction pulse (SIP) */ +#define __fir_enable_sip() ( REG_FIR_CR2 |= FIR_CR2_SIPE ) +#define __fir_disable_sip() ( REG_FIR_CR2 &= ~FIR_CR2_SIPE ) + +/* un-inverted CRC value is sent out */ +#define __fir_enable_bcrc() ( REG_FIR_CR2 |= FIR_CR2_BCRC ) + +/* inverted CRC value is sent out */ +#define __fir_disable_bcrc() ( REG_FIR_CR2 &= ~FIR_CR2_BCRC ) + +/* enable/disable Transmit Frame Length Register */ +#define __fir_enable_tflr() ( REG_FIR_CR2 |= FIR_CR2_TFLRS ) +#define __fir_disable_tflr() ( REG_FIR_CR2 &= ~FIR_CR2_TFLRS ) + +/* Preamble is transmitted in idle state */ +#define __fir_set_iss() ( REG_FIR_CR2 |= FIR_CR2_ISS ) + +/* Abort symbol is transmitted in idle state */ +#define __fir_clear_iss() ( REG_FIR_CR2 &= ~FIR_CR2_ISS ) + +/* enable/disable loopback mode */ +#define __fir_enable_loopback() ( REG_FIR_CR2 |= FIR_CR2_LMS ) +#define __fir_disable_loopback() ( REG_FIR_CR2 &= ~FIR_CR2_LMS ) + +/* select transmit pin polarity */ +#define __fir_tpp_negative() ( REG_FIR_CR2 |= FIR_CR2_TPPS ) +#define __fir_tpp_positive() ( REG_FIR_CR2 &= ~FIR_CR2_TPPS ) + +/* select receive pin polarity */ +#define __fir_rpp_negative() ( REG_FIR_CR2 |= FIR_CR2_RPPS ) +#define __fir_rpp_positive() ( REG_FIR_CR2 &= ~FIR_CR2_RPPS ) + +/* n=16,32,64,128 */ +#define __fir_set_txfifo_trigger(n) \ +do { \ + REG_FIR_CR2 &= ~FIR_CR2_TTRG_MASK; \ + REG_FIR_CR2 |= FIR_CR2_TTRG_##n; \ +} while (0) + +/* n=16,32,64,128 */ +#define __fir_set_rxfifo_trigger(n) \ +do { \ + REG_FIR_CR2 &= ~FIR_CR2_RTRG_MASK; \ + REG_FIR_CR2 |= FIR_CR2_RTRG_##n; \ +} while (0) + + +/* FIR status checking */ + +#define __fir_test_rfw() ( REG_FIR_SR & FIR_SR_RFW ) +#define __fir_test_rfa() ( REG_FIR_SR & FIR_SR_RFA ) +#define __fir_test_tfrtl() ( REG_FIR_SR & FIR_SR_TFRTL ) +#define __fir_test_rfrtl() ( REG_FIR_SR & FIR_SR_RFRTL ) +#define __fir_test_urun() ( REG_FIR_SR & FIR_SR_URUN ) +#define __fir_test_rfte() ( REG_FIR_SR & FIR_SR_RFTE ) +#define __fir_test_orun() ( REG_FIR_SR & FIR_SR_ORUN ) +#define __fir_test_crce() ( REG_FIR_SR & FIR_SR_CRCE ) +#define __fir_test_fend() ( REG_FIR_SR & FIR_SR_FEND ) +#define __fir_test_tff() ( REG_FIR_SR & FIR_SR_TFF ) +#define __fir_test_rfe() ( REG_FIR_SR & FIR_SR_RFE ) +#define __fir_test_tidle() ( REG_FIR_SR & FIR_SR_TIDLE ) +#define __fir_test_rb() ( REG_FIR_SR & FIR_SR_RB ) + +#define __fir_clear_status() \ +do { \ + REG_FIR_SR |= FIR_SR_RFW | FIR_SR_RFA | FIR_SR_URUN; \ +} while (0) + +#define __fir_clear_rfw() ( REG_FIR_SR |= FIR_SR_RFW ) +#define __fir_clear_rfa() ( REG_FIR_SR |= FIR_SR_RFA ) +#define __fir_clear_urun() ( REG_FIR_SR |= FIR_SR_URUN ) + +#define __fir_set_tflr(len) \ +do { \ + REG_FIR_TFLR = len; \ +} while (0) + +#define __fir_set_addr(a) ( REG_FIR_AR = (a) ) + +#define __fir_write_data(data) ( REG_FIR_TDR = data ) +#define __fir_read_data(data) ( data = REG_FIR_RDR ) + +/*************************************************************************** + * SCC + ***************************************************************************/ + +#define __scc_enable(base) ( REG_SCC_CR(base) |= SCC_CR_SCCE ) +#define __scc_disable(base) ( REG_SCC_CR(base) &= ~SCC_CR_SCCE ) + +#define __scc_set_tx_mode(base) ( REG_SCC_CR(base) |= SCC_CR_TRS ) +#define __scc_set_rx_mode(base) ( REG_SCC_CR(base) &= ~SCC_CR_TRS ) + +#define __scc_enable_t2r(base) ( REG_SCC_CR(base) |= SCC_CR_T2R ) +#define __scc_disable_t2r(base) ( REG_SCC_CR(base) &= ~SCC_CR_T2R ) + +#define __scc_clk_as_devclk(base) \ +do { \ + REG_SCC_CR(base) &= ~SCC_CR_FDIV_MASK; \ + REG_SCC_CR(base) |= SCC_CR_FDIV_1; \ +} while (0) + +#define __scc_clk_as_half_devclk(base) \ +do { \ + REG_SCC_CR(base) &= ~SCC_CR_FDIV_MASK; \ + REG_SCC_CR(base) |= SCC_CR_FDIV_2; \ +} while (0) + +/* n=1,4,8,14 */ +#define __scc_set_fifo_trigger(base, n) \ +do { \ + REG_SCC_CR(base) &= ~SCC_CR_TRIG_MASK; \ + REG_SCC_CR(base) |= SCC_CR_TRIG_##n; \ +} while (0) + +#define __scc_set_protocol(base, p) \ +do { \ + if (p) \ + REG_SCC_CR(base) |= SCC_CR_TP; \ + else \ + REG_SCC_CR(base) &= ~SCC_CR_TP; \ +} while (0) + +#define __scc_flush_fifo(base) ( REG_SCC_CR(base) |= SCC_CR_FLUSH ) + +#define __scc_set_invert_mode(base) ( REG_SCC_CR(base) |= SCC_CR_CONV ) +#define __scc_set_direct_mode(base) ( REG_SCC_CR(base) &= ~SCC_CR_CONV ) + +#define SCC_ERR_INTRS \ + ( SCC_CR_ECIE | SCC_CR_EPIE | SCC_CR_RETIE | SCC_CR_EOIE ) +#define SCC_ALL_INTRS \ + ( SCC_CR_TXIE | SCC_CR_RXIE | SCC_CR_TENDIE | SCC_CR_RTOIE | \ + SCC_CR_ECIE | SCC_CR_EPIE | SCC_CR_RETIE | SCC_CR_EOIE ) + +#define __scc_enable_err_intrs(base) ( REG_SCC_CR(base) |= SCC_ERR_INTRS ) +#define __scc_disable_err_intrs(base) ( REG_SCC_CR(base) &= ~SCC_ERR_INTRS ) + +#define SCC_ALL_ERRORS \ + ( SCC_SR_ORER | SCC_SR_RTO | SCC_SR_PER | SCC_SR_RETR_3 | SCC_SR_ECNTO) + +#define __scc_clear_errors(base) ( REG_SCC_SR(base) &= ~SCC_ALL_ERRORS ) + +#define __scc_enable_all_intrs(base) ( REG_SCC_CR(base) |= SCC_ALL_INTRS ) +#define __scc_disable_all_intrs(base) ( REG_SCC_CR(base) &= ~SCC_ALL_INTRS ) + +#define __scc_enable_tx_intr(base) ( REG_SCC_CR(base) |= SCC_CR_TXIE | SCC_CR_TENDIE ) +#define __scc_disable_tx_intr(base) ( REG_SCC_CR(base) &= ~(SCC_CR_TXIE | SCC_CR_TENDIE) ) + +#define __scc_enable_rx_intr(base) ( REG_SCC_CR(base) |= SCC_CR_RXIE) +#define __scc_disable_rx_intr(base) ( REG_SCC_CR(base) &= ~SCC_CR_RXIE) + +#define __scc_set_tsend(base) ( REG_SCC_CR(base) |= SCC_CR_TSEND ) +#define __scc_clear_tsend(base) ( REG_SCC_CR(base) &= ~SCC_CR_TSEND ) + +#define __scc_set_clockstop(base) ( REG_SCC_CR(base) |= SCC_CR_CLKSTP ) +#define __scc_clear_clockstop(base) ( REG_SCC_CR(base) &= ~SCC_CR_CLKSTP ) + +#define __scc_clockstop_low(base) \ +do { \ + REG_SCC_CR(base) &= ~SCC_CR_PX_MASK; \ + REG_SCC_CR(base) |= SCC_CR_PX_STOP_LOW; \ +} while (0) + +#define __scc_clockstop_high(base) \ +do { \ + REG_SCC_CR(base) &= ~SCC_CR_PX_MASK; \ + REG_SCC_CR(base) |= SCC_CR_PX_STOP_HIGH; \ +} while (0) + + +/* SCC status checking */ +#define __scc_check_transfer_status(base) ( REG_SCC_SR(base) & SCC_SR_TRANS ) +#define __scc_check_rx_overrun_error(base) ( REG_SCC_SR(base) & SCC_SR_ORER ) +#define __scc_check_rx_timeout(base) ( REG_SCC_SR(base) & SCC_SR_RTO ) +#define __scc_check_parity_error(base) ( REG_SCC_SR(base) & SCC_SR_PER ) +#define __scc_check_txfifo_trigger(base) ( REG_SCC_SR(base) & SCC_SR_TFTG ) +#define __scc_check_rxfifo_trigger(base) ( REG_SCC_SR(base) & SCC_SR_RFTG ) +#define __scc_check_tx_end(base) ( REG_SCC_SR(base) & SCC_SR_TEND ) +#define __scc_check_retx_3(base) ( REG_SCC_SR(base) & SCC_SR_RETR_3 ) +#define __scc_check_ecnt_overflow(base) ( REG_SCC_SR(base) & SCC_SR_ECNTO ) + + +/*************************************************************************** + * WDT + ***************************************************************************/ + +#define __wdt_set_count(count) ( REG_WDT_WTCNT = (count) ) +#define __wdt_start() ( REG_WDT_WTCSR |= WDT_WTCSR_START ) +#define __wdt_stop() ( REG_WDT_WTCSR &= ~WDT_WTCSR_START ) + + +/*************************************************************************** + * OST + ***************************************************************************/ + +#define __ost_enable_all() ( REG_OST_TER |= 0x07 ) +#define __ost_disable_all() ( REG_OST_TER &= ~0x07 ) +#define __ost_enable_channel(n) ( REG_OST_TER |= (1 << (n)) ) +#define __ost_disable_channel(n) ( REG_OST_TER &= ~(1 << (n)) ) +#define __ost_set_reload(n, val) ( REG_OST_TRDR(n) = (val) ) +#define __ost_set_count(n, val) ( REG_OST_TCNT(n) = (val) ) +#define __ost_get_count(n) ( REG_OST_TCNT(n) ) +#define __ost_set_clock(n, cs) \ +do { \ + REG_OST_TCSR(n) &= ~OST_TCSR_CKS_MASK; \ + REG_OST_TCSR(n) |= cs; \ +} while (0) +#define __ost_set_mode(n, val) ( REG_OST_TCSR(n) = (val) ) +#define __ost_enable_interrupt(n) ( REG_OST_TCSR(n) |= OST_TCSR_UIE ) +#define __ost_disable_interrupt(n) ( REG_OST_TCSR(n) &= ~OST_TCSR_UIE ) +#define __ost_uf_detected(n) ( REG_OST_TCSR(n) & OST_TCSR_UF ) +#define __ost_clear_uf(n) ( REG_OST_TCSR(n) &= ~OST_TCSR_UF ) +#define __ost_is_busy(n) ( REG_OST_TCSR(n) & OST_TCSR_BUSY ) +#define __ost_clear_busy(n) ( REG_OST_TCSR(n) &= ~OST_TCSR_BUSY ) + + +/*************************************************************************** + * UART + ***************************************************************************/ + +#define __uart_enable(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_FCR) |= UARTFCR_UUE | UARTFCR_FE ) +#define __uart_disable(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_FCR) = ~UARTFCR_UUE ) + +#define __uart_enable_transmit_irq(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) |= UARTIER_TIE ) +#define __uart_disable_transmit_irq(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) &= ~UARTIER_TIE ) + +#define __uart_enable_receive_irq(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) |= UARTIER_RIE | UARTIER_RLIE | UARTIER_RTIE ) +#define __uart_disable_receive_irq(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) &= ~(UARTIER_RIE | UARTIER_RLIE | UARTIER_RTIE) ) + +#define __uart_enable_loopback(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_MCR) |= UARTMCR_LOOP ) +#define __uart_disable_loopback(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_MCR) &= ~UARTMCR_LOOP ) + +#define __uart_set_8n1(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_LCR) = UARTLCR_WLEN_8 ) + +#define __uart_set_baud(n, devclk, baud) \ + do { \ + REG8(UART_BASE + UART_OFF*(n) + OFF_LCR) |= UARTLCR_DLAB; \ + REG8(UART_BASE + UART_OFF*(n) + OFF_DLLR) = (devclk / 16 / baud) & 0xff; \ + REG8(UART_BASE + UART_OFF*(n) + OFF_DLHR) = ((devclk / 16 / baud) >> 8) & 0xff; \ + REG8(UART_BASE + UART_OFF*(n) + OFF_LCR) &= ~UARTLCR_DLAB; \ + } while (0) + +#define __uart_parity_error(n) \ + ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_PER) != 0 ) + +#define __uart_clear_errors(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) &= ~(UARTLSR_ORER | UARTLSR_BRK | UARTLSR_FER | UARTLSR_PER | UARTLSR_RFER) ) + +#define __uart_transmit_fifo_empty(n) \ + ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_TDRQ) != 0 ) + +#define __uart_transmit_end(n) \ + ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_TEMT) != 0 ) + +#define __uart_transmit_char(n, ch) \ + REG8(UART_BASE + UART_OFF*(n) + OFF_TDR) = (ch) + +#define __uart_receive_fifo_full(n) \ + ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_DR) != 0 ) + +#define __uart_receive_ready(n) \ + ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_DR) != 0 ) + +#define __uart_receive_char(n) \ + REG8(UART_BASE + UART_OFF*(n) + OFF_RDR) + +#define __uart_disable_irda() \ + ( REG8(IRDA_BASE + OFF_SIRCR) &= ~(SIRCR_TSIRE | SIRCR_RSIRE) ) +#define __uart_enable_irda() \ + /* Tx high pulse as 0, Rx low pulse as 0 */ \ + ( REG8(IRDA_BASE + OFF_SIRCR) = SIRCR_TSIRE | SIRCR_RSIRE | SIRCR_RXPL | SIRCR_TPWS ) + + +/*************************************************************************** + * INTC + ***************************************************************************/ +#define __intc_unmask_irq(n) ( REG_INTC_IMCR = (1 << (n)) ) +#define __intc_mask_irq(n) ( REG_INTC_IMSR = (1 << (n)) ) +#define __intc_ack_irq(n) ( REG_INTC_IPR = (1 << (n)) ) + +/*************************************************************************** + * CIM + ***************************************************************************/ + +#define __cim_enable() ( REG_CIM_CTRL |= CIM_CTRL_ENA ) +#define __cim_disable() ( REG_CIM_CTRL &= ~CIM_CTRL_ENA ) + +#define __cim_input_data_inverse() ( REG_CIM_CFG |= CIM_CFG_INV_DAT ) +#define __cim_input_data_normal() ( REG_CIM_CFG &= ~CIM_CFG_INV_DAT ) + +#define __cim_vsync_active_low() ( REG_CIM_CFG |= CIM_CFG_VSP ) +#define __cim_vsync_active_high() ( REG_CIM_CFG &= ~CIM_CFG_VSP ) + +#define __cim_hsync_active_low() ( REG_CIM_CFG |= CIM_CFG_HSP ) +#define __cim_hsync_active_high() ( REG_CIM_CFG &= ~CIM_CFG_HSP ) + +#define __cim_sample_data_at_pclk_falling_edge() \ + ( REG_CIM_CFG |= CIM_CFG_PCP ) +#define __cim_sample_data_at_pclk_rising_edge() \ + ( REG_CIM_CFG &= ~CIM_CFG_PCP ) + +#define __cim_enable_dummy_zero() ( REG_CIM_CFG |= CIM_CFG_DUMMY_ZERO ) +#define __cim_disable_dummy_zero() ( REG_CIM_CFG &= ~CIM_CFG_DUMMY_ZERO ) + +#define __cim_select_external_vsync() ( REG_CIM_CFG |= CIM_CFG_EXT_VSYNC ) +#define __cim_select_internal_vsync() ( REG_CIM_CFG &= ~CIM_CFG_EXT_VSYNC ) + +/* n=0-7 */ +#define __cim_set_data_packing_mode(n) \ +do { \ + REG_CIM_CFG &= ~CIM_CFG_PACK_MASK; \ + REG_CIM_CFG |= (CIM_CFG_PACK_##n); \ +} while (0) + +#define __cim_enable_ccir656_progressive_mode() \ +do { \ + REG_CIM_CFG &= ~CIM_CFG_DSM_MASK; \ + REG_CIM_CFG |= CIM_CFG_DSM_CPM; \ +} while (0) + +#define __cim_enable_ccir656_interlace_mode() \ +do { \ + REG_CIM_CFG &= ~CIM_CFG_DSM_MASK; \ + REG_CIM_CFG |= CIM_CFG_DSM_CIM; \ +} while (0) + +#define __cim_enable_gated_clock_mode() \ +do { \ + REG_CIM_CFG &= ~CIM_CFG_DSM_MASK; \ + REG_CIM_CFG |= CIM_CFG_DSM_GCM; \ +} while (0) + +#define __cim_enable_nongated_clock_mode() \ +do { \ + REG_CIM_CFG &= ~CIM_CFG_DSM_MASK; \ + REG_CIM_CFG |= CIM_CFG_DSM_NGCM; \ +} while (0) + +/* sclk:system bus clock + * mclk: CIM master clock + */ +#define __cim_set_master_clk(sclk, mclk) \ +do { \ + REG_CIM_CTRL &= ~CIM_CTRL_MCLKDIV_MASK; \ + REG_CIM_CTRL |= (((sclk)/(mclk) - 1) << CIM_CTRL_MCLKDIV_BIT); \ +} while (0) + +#define __cim_enable_sof_intr() \ + ( REG_CIM_CTRL |= CIM_CTRL_DMA_SOFM ) +#define __cim_disable_sof_intr() \ + ( REG_CIM_CTRL &= ~CIM_CTRL_DMA_SOFM ) + +#define __cim_enable_eof_intr() \ + ( REG_CIM_CTRL |= CIM_CTRL_DMA_EOFM ) +#define __cim_disable_eof_intr() \ + ( REG_CIM_CTRL &= ~CIM_CTRL_DMA_EOFM ) + +#define __cim_enable_stop_intr() \ + ( REG_CIM_CTRL |= CIM_CTRL_DMA_STOPM ) +#define __cim_disable_stop_intr() \ + ( REG_CIM_CTRL &= ~CIM_CTRL_DMA_STOPM ) + +#define __cim_enable_trig_intr() \ + ( REG_CIM_CTRL |= CIM_CTRL_RXF_TRIGM ) +#define __cim_disable_trig_intr() \ + ( REG_CIM_CTRL &= ~CIM_CTRL_RXF_TRIGM ) + +#define __cim_enable_rxfifo_overflow_intr() \ + ( REG_CIM_CTRL |= CIM_CTRL_RXF_OFM ) +#define __cim_disable_rxfifo_overflow_intr() \ + ( REG_CIM_CTRL &= ~CIM_CTRL_RXF_OFM ) + +/* n=1-16 */ +#define __cim_set_frame_rate(n) \ +do { \ + REG_CIM_CTRL &= ~CIM_CTRL_FRC_MASK; \ + REG_CIM_CTRL |= CIM_CTRL_FRC_##n; \ +} while (0) + +#define __cim_enable_dma() ( REG_CIM_CTRL |= CIM_CTRL_DMA_EN ) +#define __cim_disable_dma() ( REG_CIM_CTRL &= ~CIM_CTRL_DMA_EN ) + +#define __cim_reset_rxfifo() ( REG_CIM_CTRL |= CIM_CTRL_RXF_RST ) +#define __cim_unreset_rxfifo() ( REG_CIM_CTRL &= ~CIM_CTRL_RXF_RST ) + +/* n=4,8,12,16,20,24,28,32 */ +#define __cim_set_rxfifo_trigger(n) \ +do { \ + REG_CIM_CTRL &= ~CIM_CTRL_RXF_TRIG_MASK; \ + REG_CIM_CTRL |= CIM_CTRL_RXF_TRIG_##n; \ +} while (0) + +#define __cim_clear_state() ( REG_CIM_STATE = 0 ) + +#define __cim_disable_done() ( REG_CIM_STATE & CIM_STATE_VDD ) +#define __cim_rxfifo_empty() ( REG_CIM_STATE & CIM_STATE_RXF_EMPTY ) +#define __cim_rxfifo_reach_trigger() ( REG_CIM_STATE & CIM_STATE_RXF_TRIG ) +#define __cim_rxfifo_overflow() ( REG_CIM_STATE & CIM_STATE_RXF_OF ) +#define __cim_clear_rxfifo_overflow() ( REG_CIM_STATE &= ~CIM_STATE_RXF_OF ) +#define __cim_dma_stop() ( REG_CIM_STATE & CIM_STATE_DMA_STOP ) +#define __cim_dma_eof() ( REG_CIM_STATE & CIM_STATE_DMA_EOF ) +#define __cim_dma_sof() ( REG_CIM_STATE & CIM_STATE_DMA_SOF ) + +#define __cim_get_iid() ( REG_CIM_IID ) +#define __cim_get_image_data() ( REG_CIM_RXFIFO ) +#define __cim_get_dam_cmd() ( REG_CIM_CMD ) + +#define __cim_set_da(a) ( REG_CIM_DA = (a) ) + +/*************************************************************************** + * PWM + ***************************************************************************/ + +/* n is the pwm channel (0,1,..) */ +#define __pwm_enable_module(n) ( REG_PWM_CTR(n) |= PWM_CTR_EN ) +#define __pwm_disable_module(n) ( REG_PWM_CTR(n) &= ~PWM_CTR_EN ) +#define __pwm_graceful_shutdown_mode(n) ( REG_PWM_CTR(n) &= ~PWM_CTR_SD ) +#define __pwm_abrupt_shutdown_mode(n) ( REG_PWM_CTR(n) |= PWM_CTR_SD ) +#define __pwm_set_full_duty(n) ( REG_PWM_DUT(n) |= PWM_DUT_FDUTY ) + +#define __pwm_set_prescale(n, p) \ + ( REG_PWM_CTR(n) = ((REG_PWM_CTR(n) & ~PWM_CTR_PRESCALE_MASK) | (p) ) ) +#define __pwm_set_period(n, p) \ + ( REG_PWM_PER(n) = ( (REG_PWM_PER(n) & ~PWM_PER_PERIOD_MASK) | (p) ) ) +#define __pwm_set_duty(n, d) \ + ( REG_PWM_DUT(n) = ( (REG_PWM_DUT(n) & ~(PWM_DUT_FDUTY | PWM_DUT_DUTY_MASK)) | (d) ) ) + +/*************************************************************************** + * EMC + ***************************************************************************/ + +#define __emc_enable_split() ( REG_EMC_BCR = EMC_BCR_BRE ) +#define __emc_disable_split() ( REG_EMC_BCR = 0 ) + +#define __emc_smem_bus_width(n) /* 8, 16 or 32*/ \ + ( REG_EMC_SMCR = (REG_EMC_SMCR & EMC_SMCR_BW_MASK) | \ + EMC_SMCR_BW_##n##BIT ) +#define __emc_smem_byte_control() \ + ( REG_EMC_SMCR = (REG_EMC_SMCR | EMC_SMCR_BCM ) +#define __emc_normal_smem() \ + ( REG_EMC_SMCR = (REG_EMC_SMCR & ~EMC_SMCR_SMT ) +#define __emc_burst_smem() \ + ( REG_EMC_SMCR = (REG_EMC_SMCR | EMC_SMCR_SMT ) +#define __emc_smem_burstlen(n) /* 4, 8, 16 or 32 */ \ + ( REG_EMC_SMCR = (REG_EMC_SMCR & EMC_SMCR_BL_MASK) | (EMC_SMCR_BL_##n ) + +/*************************************************************************** + * GPIO + ***************************************************************************/ + +/* p is the port number (0,1,2,3) + * o is the pin offset (0-31) inside the port + * n is the absolute number of a pin (0-124), regardless of the port + * m is the interrupt manner (low/high/falling/rising) + */ + +#define __gpio_port_data(p) ( REG_GPIO_GPDR(p) ) + +#define __gpio_port_as_output(p, o) \ +do { \ + unsigned int tmp; \ + REG_GPIO_GPIER(p) &= ~(1 << (o)); \ + REG_GPIO_GPDIR(p) |= (1 << (o)); \ + if (o < 16) { \ + tmp = REG_GPIO_GPALR(p); \ + tmp &= ~(3 << ((o) << 1)); \ + REG_GPIO_GPALR(p) = tmp; \ + } else { \ + tmp = REG_GPIO_GPAUR(p); \ + tmp &= ~(3 << (((o) - 16)<< 1)); \ + REG_GPIO_GPAUR(p) = tmp; \ + } \ +} while (0) + +#define __gpio_port_as_input(p, o) \ +do { \ + unsigned int tmp; \ + REG_GPIO_GPIER(p) &= ~(1 << (o)); \ + REG_GPIO_GPDIR(p) &= ~(1 << (o)); \ + if (o < 16) { \ + tmp = REG_GPIO_GPALR(p); \ + tmp &= ~(3 << ((o) << 1)); \ + REG_GPIO_GPALR(p) = tmp; \ + } else { \ + tmp = REG_GPIO_GPAUR(p); \ + tmp &= ~(3 << (((o) - 16)<< 1)); \ + REG_GPIO_GPAUR(p) = tmp; \ + } \ +} while (0) + +#define __gpio_as_output(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + __gpio_port_as_output(p, o); \ +} while (0) + +#define __gpio_as_input(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + __gpio_port_as_input(p, o); \ +} while (0) + +#define __gpio_set_pin(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + __gpio_port_data(p) |= (1 << o); \ +} while (0) + +#define __gpio_clear_pin(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + __gpio_port_data(p) &= ~(1 << o); \ +} while (0) + +static __inline__ unsigned int __gpio_get_pin(unsigned int n) +{ + unsigned int p, o; + p = (n) / 32; + o = (n) % 32; + if (__gpio_port_data(p) & (1 << o)) + return 1; + else + return 0; +} + +#define __gpio_set_irq_detect_manner(p, o, m) \ +do { \ + unsigned int tmp; \ + if (o < 16) { \ + tmp = REG_GPIO_GPIDLR(p); \ + tmp &= ~(3 << ((o) << 1)); \ + tmp |= ((m) << ((o) << 1)); \ + REG_GPIO_GPIDLR(p) = tmp; \ + } else { \ + tmp = REG_GPIO_GPIDUR(p); \ + tmp &= ~(3 << (((o)-16) << 1)); \ + tmp |= ((m) << (((o)-16) << 1)); \ + REG_GPIO_GPIDUR(p) = tmp; \ + } \ +} while (0) + +#define __gpio_port_as_irq(p, o, m) \ +do { \ + __gpio_port_as_input(p, o); \ + __gpio_set_irq_detect_manner(p, o, m); \ +} while (0) + +#define __gpio_as_irq(n, m) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + __gpio_port_as_irq(p, o, m); \ +} while (0) + + +#define __gpio_as_irq_high_level(n) __gpio_as_irq(n, GPIO_IRQ_HILEVEL) +#define __gpio_as_irq_low_level(n) __gpio_as_irq(n, GPIO_IRQ_LOLEVEL) +#define __gpio_as_irq_fall_edge(n) __gpio_as_irq(n, GPIO_IRQ_FALLEDG) +#define __gpio_as_irq_rise_edge(n) __gpio_as_irq(n, GPIO_IRQ_RAISEDG) + + +#define __gpio_mask_irq(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_GPIER(p) &= ~(1 << o); \ +} while (0) + +#define __gpio_unmask_irq(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_GPIER(p) |= (1 << o); \ +} while (0) + +#define __gpio_ack_irq(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_GPFR(p) |= (1 << o); \ +} while (0) + + +static __inline__ unsigned int __gpio_get_irq(void) +{ + unsigned int tmp, i; + + tmp = REG_GPIO_GPFR(3); + for (i=0; i<32; i++) + if (tmp & (1 << i)) + return 0x60 + i; + tmp = REG_GPIO_GPFR(2); + for (i=0; i<32; i++) + if (tmp & (1 << i)) + return 0x40 + i; + tmp = REG_GPIO_GPFR(1); + for (i=0; i<32; i++) + if (tmp & (1 << i)) + return 0x20 + i; + tmp = REG_GPIO_GPFR(0); + for (i=0; i<32; i++) + if (tmp & (1 << i)) + return i; + return 0; +} + +#define __gpio_group_irq(n) \ +({ \ + register int tmp, i; \ + tmp = REG_GPIO_GPFR((n)); \ + for (i=31;i>=0;i--) \ + if (tmp & (1 << i)) \ + break; \ + i; \ +}) + +#define __gpio_enable_pull(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_GPPUR(p) |= (1 << o); \ +} while (0) + +#define __gpio_disable_pull(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_GPPUR(p) &= ~(1 << o); \ +} while (0) + + +/* Init the alternate function pins */ + + +#define __gpio_as_ssi() \ +do { \ + REG_GPIO_GPALR(2) &= 0xFC00FFFF; \ + REG_GPIO_GPALR(2) |= 0x01550000; \ +} while (0) + +#define __gpio_as_uart3() \ +do { \ + REG_GPIO_GPAUR(0) &= 0xFFFF0000; \ + REG_GPIO_GPAUR(0) |= 0x00005555; \ +} while (0) + +#define __gpio_as_uart2() \ +do { \ + REG_GPIO_GPALR(3) &= 0x3FFFFFFF; \ + REG_GPIO_GPALR(3) |= 0x40000000; \ + REG_GPIO_GPAUR(3) &= 0xF3FFFFFF; \ + REG_GPIO_GPAUR(3) |= 0x04000000; \ +} while (0) + +#define __gpio_as_uart1() \ +do { \ + REG_GPIO_GPAUR(0) &= 0xFFF0FFFF; \ + REG_GPIO_GPAUR(0) |= 0x00050000; \ +} while (0) + +#define __gpio_as_uart0() \ +do { \ + REG_GPIO_GPAUR(3) &= 0x0FFFFFFF; \ + REG_GPIO_GPAUR(3) |= 0x50000000; \ +} while (0) + + +#define __gpio_as_scc0() \ +do { \ + REG_GPIO_GPALR(2) &= 0xFFFFFFCC; \ + REG_GPIO_GPALR(2) |= 0x00000011; \ +} while (0) + +#define __gpio_as_scc1() \ +do { \ + REG_GPIO_GPALR(2) &= 0xFFFFFF33; \ + REG_GPIO_GPALR(2) |= 0x00000044; \ +} while (0) + +#define __gpio_as_scc() \ +do { \ + __gpio_as_scc0(); \ + __gpio_as_scc1(); \ +} while (0) + +#define __gpio_as_dma() \ +do { \ + REG_GPIO_GPALR(0) &= 0x00FFFFFF; \ + REG_GPIO_GPALR(0) |= 0x55000000; \ + REG_GPIO_GPAUR(0) &= 0xFF0FFFFF; \ + REG_GPIO_GPAUR(0) |= 0x00500000; \ +} while (0) + +#define __gpio_as_msc() \ +do { \ + REG_GPIO_GPALR(1) &= 0xFFFF000F; \ + REG_GPIO_GPALR(1) |= 0x00005550; \ +} while (0) + +#define __gpio_as_pcmcia() \ +do { \ + REG_GPIO_GPAUR(2) &= 0xF000FFFF; \ + REG_GPIO_GPAUR(2) |= 0x05550000; \ +} while (0) + +#define __gpio_as_emc(csmask) \ +do { \ + REG_GPIO_GPALR(2) &= 0x3FFFFFFF; \ + REG_GPIO_GPALR(2) |= 0x40000000; \ + REG_GPIO_GPAUR(2) &= 0xFFFF0000; \ + REG_GPIO_GPAUR(2) |= 0x00005555; \ +} while (0) + +#define __gpio_as_lcd_slave() \ +do { \ + REG_GPIO_GPALR(1) &= 0x0000FFFF; \ + REG_GPIO_GPALR(1) |= 0x55550000; \ + REG_GPIO_GPAUR(1) &= 0x00000000; \ + REG_GPIO_GPAUR(1) |= 0x55555555; \ +} while (0) + +#define __gpio_as_lcd_master() \ +do { \ + REG_GPIO_GPALR(1) &= 0x0000FFFF; \ + REG_GPIO_GPALR(1) |= 0x55550000; \ + REG_GPIO_GPAUR(1) &= 0x00000000; \ + REG_GPIO_GPAUR(1) |= 0x556A5555; \ +} while (0) + +#define __gpio_as_usb() \ +do { \ + REG_GPIO_GPAUR(0) &= 0x00FFFFFF; \ + REG_GPIO_GPAUR(0) |= 0x55000000; \ +} while (0) + +#define __gpio_as_ac97() \ +do { \ + REG_GPIO_GPALR(2) &= 0xC3FF03FF; \ + REG_GPIO_GPALR(2) |= 0x24005400; \ +} while (0) + +#define __gpio_as_i2s_slave() \ +do { \ + REG_GPIO_GPALR(2) &= 0xC3FF0CFF; \ + REG_GPIO_GPALR(2) |= 0x14005100; \ +} while (0) + +#define __gpio_as_i2s_master() \ +do { \ + REG_GPIO_GPALR(2) &= 0xC3FF0CFF; \ + REG_GPIO_GPALR(2) |= 0x28005100; \ +} while (0) + +#define __gpio_as_eth() \ +do { \ + REG_GPIO_GPAUR(3) &= 0xFC000000; \ + REG_GPIO_GPAUR(3) |= 0x01555555; \ +} while (0) + +#define __gpio_as_pwm() \ +do { \ + REG_GPIO_GPAUR(2) &= 0x0FFFFFFF; \ + REG_GPIO_GPAUR(2) |= 0x50000000; \ +} while (0) + +#define __gpio_as_ps2() \ +do { \ + REG_GPIO_GPALR(1) &= 0xFFFFFFF0; \ + REG_GPIO_GPALR(1) |= 0x00000005; \ +} while (0) + +#define __gpio_as_uprt() \ +do { \ + REG_GPIO_GPALR(1) &= 0x0000000F; \ + REG_GPIO_GPALR(1) |= 0x55555550; \ + REG_GPIO_GPALR(3) &= 0xC0000000; \ + REG_GPIO_GPALR(3) |= 0x15555555; \ +} while (0) + +#define __gpio_as_cim() \ +do { \ + REG_GPIO_GPALR(0) &= 0xFF000000; \ + REG_GPIO_GPALR(0) |= 0x00555555; \ +} while (0) + +/*************************************************************************** + * HARB + ***************************************************************************/ + +#define __harb_usb0_udc() \ +do { \ + REG_HARB_HAPOR &= ~HARB_HAPOR_UCHSEL; \ +} while (0) + +#define __harb_usb0_uhc() \ +do { \ + REG_HARB_HAPOR |= HARB_HAPOR_UCHSEL; \ +} while (0) + +#define __harb_set_priority(n) \ +do { \ + REG_HARB_HAPOR = ((REG_HARB_HAPOR & ~HARB_HAPOR_PRIO_MASK) | n); \ +} while (0) + +/*************************************************************************** + * I2C + ***************************************************************************/ + +#define __i2c_enable() ( REG_I2C_CR |= I2C_CR_I2CE ) +#define __i2c_disable() ( REG_I2C_CR &= ~I2C_CR_I2CE ) + +#define __i2c_send_start() ( REG_I2C_CR |= I2C_CR_STA ) +#define __i2c_send_stop() ( REG_I2C_CR |= I2C_CR_STO ) +#define __i2c_send_ack() ( REG_I2C_CR &= ~I2C_CR_AC ) +#define __i2c_send_nack() ( REG_I2C_CR |= I2C_CR_AC ) + +#define __i2c_set_drf() ( REG_I2C_SR |= I2C_SR_DRF ) +#define __i2c_clear_drf() ( REG_I2C_SR &= ~I2C_SR_DRF ) +#define __i2c_check_drf() ( REG_I2C_SR & I2C_SR_DRF ) + +#define __i2c_received_ack() ( !(REG_I2C_SR & I2C_SR_ACKF) ) +#define __i2c_is_busy() ( REG_I2C_SR & I2C_SR_BUSY ) +#define __i2c_transmit_ended() ( REG_I2C_SR & I2C_SR_TEND ) + +#define __i2c_set_clk(dev_clk, i2c_clk) \ + ( REG_I2C_GR = (dev_clk) / (16*(i2c_clk)) - 1 ) + +#define __i2c_read() ( REG_I2C_DR ) +#define __i2c_write(val) ( REG_I2C_DR = (val) ) + +/*************************************************************************** + * UDC + ***************************************************************************/ + +#define __udc_set_16bit_phy() ( REG_UDC_DevCFGR |= UDC_DevCFGR_PI ) +#define __udc_set_8bit_phy() ( REG_UDC_DevCFGR &= ~UDC_DevCFGR_PI ) + +#define __udc_enable_sync_frame() ( REG_UDC_DevCFGR |= UDC_DevCFGR_SS ) +#define __udc_disable_sync_frame() ( REG_UDC_DevCFGR &= ~UDC_DevCFGR_SS ) + +#define __udc_self_powered() ( REG_UDC_DevCFGR |= UDC_DevCFGR_SP ) +#define __udc_bus_powered() ( REG_UDC_DevCFGR &= ~UDC_DevCFGR_SP ) + +#define __udc_enable_remote_wakeup() ( REG_UDC_DevCFGR |= UDC_DevCFGR_RW ) +#define __udc_disable_remote_wakeup() ( REG_UDC_DevCFGR &= ~UDC_DevCFGR_RW ) + +#define __udc_set_speed_high() \ +do { \ + REG_UDC_DevCFGR &= ~UDC_DevCFGR_SPD_MASK; \ + REG_UDC_DevCFGR |= UDC_DevCFGR_SPD_HS; \ +} while (0) + +#define __udc_set_speed_full() \ +do { \ + REG_UDC_DevCFGR &= ~UDC_DevCFGR_SPD_MASK; \ + REG_UDC_DevCFGR |= UDC_DevCFGR_SPD_FS; \ +} while (0) + +#define __udc_set_speed_low() \ +do { \ + REG_UDC_DevCFGR &= ~UDC_DevCFGR_SPD_MASK; \ + REG_UDC_DevCFGR |= UDC_DevCFGR_SPD_LS; \ +} while (0) + + +#define __udc_set_dma_mode() ( REG_UDC_DevCR |= UDC_DevCR_DM ) +#define __udc_set_slave_mode() ( REG_UDC_DevCR &= ~UDC_DevCR_DM ) +#define __udc_set_big_endian() ( REG_UDC_DevCR |= UDC_DevCR_BE ) +#define __udc_set_little_endian() ( REG_UDC_DevCR &= ~UDC_DevCR_BE ) +#define __udc_generate_resume() ( REG_UDC_DevCR |= UDC_DevCR_RES ) +#define __udc_clear_resume() ( REG_UDC_DevCR &= ~UDC_DevCR_RES ) + + +#define __udc_get_enumarated_speed() ( REG_UDC_DevSR & UDC_DevSR_ENUMSPD_MASK ) +#define __udc_suspend_detected() ( REG_UDC_DevSR & UDC_DevSR_SUSP ) +#define __udc_get_alternate_setting() ( (REG_UDC_DevSR & UDC_DevSR_ALT_MASK) >> UDC_DevSR_ALT_BIT ) +#define __udc_get_interface_number() ( (REG_UDC_DevSR & UDC_DevSR_INTF_MASK) >> UDC_DevSR_INTF_BIT ) +#define __udc_get_config_number() ( (REG_UDC_DevSR & UDC_DevSR_CFG_MASK) >> UDC_DevSR_CFG_BIT ) + + +#define __udc_sof_detected(r) ( (r) & UDC_DevIntR_SOF ) +#define __udc_usb_suspend_detected(r) ( (r) & UDC_DevIntR_US ) +#define __udc_usb_reset_detected(r) ( (r) & UDC_DevIntR_UR ) +#define __udc_set_interface_detected(r) ( (r) & UDC_DevIntR_SI ) +#define __udc_set_config_detected(r) ( (r) & UDC_DevIntR_SC ) + +#define __udc_clear_sof() ( REG_UDC_DevIntR |= UDC_DevIntR_SOF ) +#define __udc_clear_usb_suspend() ( REG_UDC_DevIntR |= UDC_DevIntR_US ) +#define __udc_clear_usb_reset() ( REG_UDC_DevIntR |= UDC_DevIntR_UR ) +#define __udc_clear_set_interface() ( REG_UDC_DevIntR |= UDC_DevIntR_SI ) +#define __udc_clear_set_config() ( REG_UDC_DevIntR |= UDC_DevIntR_SC ) + +#define __udc_mask_sof() ( REG_UDC_DevIntMR |= UDC_DevIntR_SOF ) +#define __udc_mask_usb_suspend() ( REG_UDC_DevIntMR |= UDC_DevIntR_US ) +#define __udc_mask_usb_reset() ( REG_UDC_DevIntMR |= UDC_DevIntR_UR ) +#define __udc_mask_set_interface() ( REG_UDC_DevIntMR |= UDC_DevIntR_SI ) +#define __udc_mask_set_config() ( REG_UDC_DevIntMR |= UDC_DevIntR_SC ) +#define __udc_mask_all_dev_intrs() \ + ( REG_UDC_DevIntMR = UDC_DevIntR_SOF | UDC_DevIntR_US | \ + UDC_DevIntR_UR | UDC_DevIntR_SI | UDC_DevIntR_SC ) + +#define __udc_unmask_sof() ( REG_UDC_DevIntMR &= ~UDC_DevIntR_SOF ) +#define __udc_unmask_usb_suspend() ( REG_UDC_DevIntMR &= ~UDC_DevIntR_US ) +#define __udc_unmask_usb_reset() ( REG_UDC_DevIntMR &= ~UDC_DevIntR_UR ) +#define __udc_unmask_set_interface() ( REG_UDC_DevIntMR &= ~UDC_DevIntR_SI ) +#define __udc_unmask_set_config() ( REG_UDC_DevIntMR &= ~UDC_DevIntR_SC ) +#if 0 +#define __udc_unmask_all_dev_intrs() \ + ( REG_UDC_DevIntMR = ~(UDC_DevIntR_SOF | UDC_DevIntR_US | \ + UDC_DevIntR_UR | UDC_DevIntR_SI | UDC_DevIntR_SC) ) +#else +#define __udc_unmask_all_dev_intrs() \ + ( REG_UDC_DevIntMR = 0x00000000 ) +#endif + + +#define __udc_ep0out_irq_detected(epintr) \ + ( (((epintr) & UDC_EPIntR_OUTEP_MASK) >> (UDC_EPIntR_OUTEP_BIT + 0)) & 0x1 ) +#define __udc_ep5out_irq_detected(epintr) \ + ( (((epintr) & UDC_EPIntR_OUTEP_MASK) >> (UDC_EPIntR_OUTEP_BIT + 5)) & 0x1 ) +#define __udc_ep6out_irq_detected(epintr) \ + ( (((epintr) & UDC_EPIntR_OUTEP_MASK) >> (UDC_EPIntR_OUTEP_BIT + 6)) & 0x1 ) +#define __udc_ep7out_irq_detected(epintr) \ + ( (((epintr) & UDC_EPIntR_OUTEP_MASK) >> (UDC_EPIntR_OUTEP_BIT + 7)) & 0x1 ) + +#define __udc_ep0in_irq_detected(epintr) \ + ( (((epintr) & UDC_EPIntR_INEP_MASK) >> (UDC_EPIntR_INEP_BIT + 0)) & 0x1 ) +#define __udc_ep1in_irq_detected(epintr) \ + ( (((epintr) & UDC_EPIntR_INEP_MASK) >> (UDC_EPIntR_INEP_BIT + 1)) & 0x1 ) +#define __udc_ep2in_irq_detected(epintr) \ + ( (((epintr) & UDC_EPIntR_INEP_MASK) >> (UDC_EPIntR_INEP_BIT + 2)) & 0x1 ) +#define __udc_ep3in_irq_detected(epintr) \ + ( (((epintr) & UDC_EPIntR_INEP_MASK) >> (UDC_EPIntR_INEP_BIT + 3)) & 0x1 ) +#define __udc_ep4in_irq_detected(epintr) \ + ( (((epintr) & UDC_EPIntR_INEP_MASK) >> (UDC_EPIntR_INEP_BIT + 4)) & 0x1 ) + + +#define __udc_mask_ep0out_irq() \ + ( REG_UDC_EPIntMR |= (1 << (UDC_EPIntMR_OUTEP_BIT + 0)) ) +#define __udc_mask_ep5out_irq() \ + ( REG_UDC_EPIntMR |= (1 << (UDC_EPIntMR_OUTEP_BIT + 5)) ) +#define __udc_mask_ep6out_irq() \ + ( REG_UDC_EPIntMR |= (1 << (UDC_EPIntMR_OUTEP_BIT + 6)) ) +#define __udc_mask_ep7out_irq() \ + ( REG_UDC_EPIntMR |= (1 << (UDC_EPIntMR_OUTEP_BIT + 7)) ) + +#define __udc_unmask_ep0out_irq() \ + ( REG_UDC_EPIntMR &= ~(1 << (UDC_EPIntMR_OUTEP_BIT + 0)) ) +#define __udc_unmask_ep5out_irq() \ + ( REG_UDC_EPIntMR &= ~(1 << (UDC_EPIntMR_OUTEP_BIT + 5)) ) +#define __udc_unmask_ep6out_irq() \ + ( REG_UDC_EPIntMR &= ~(1 << (UDC_EPIntMR_OUTEP_BIT + 6)) ) +#define __udc_unmask_ep7out_irq() \ + ( REG_UDC_EPIntMR &= ~(1 << (UDC_EPIntMR_OUTEP_BIT + 7)) ) + +#define __udc_mask_ep0in_irq() \ + ( REG_UDC_EPIntMR |= (1 << (UDC_EPIntMR_INEP_BIT + 0)) ) +#define __udc_mask_ep1in_irq() \ + ( REG_UDC_EPIntMR |= (1 << (UDC_EPIntMR_INEP_BIT + 1)) ) +#define __udc_mask_ep2in_irq() \ + ( REG_UDC_EPIntMR |= (1 << (UDC_EPIntMR_INEP_BIT + 2)) ) +#define __udc_mask_ep3in_irq() \ + ( REG_UDC_EPIntMR |= (1 << (UDC_EPIntMR_INEP_BIT + 3)) ) +#define __udc_mask_ep4in_irq() \ + ( REG_UDC_EPIntMR |= (1 << (UDC_EPIntMR_INEP_BIT + 4)) ) + +#define __udc_unmask_ep0in_irq() \ + ( REG_UDC_EPIntMR &= ~(1 << (UDC_EPIntMR_INEP_BIT + 0)) ) +#define __udc_unmask_ep1in_irq() \ + ( REG_UDC_EPIntMR &= ~(1 << (UDC_EPIntMR_INEP_BIT + 1)) ) +#define __udc_unmask_ep2in_irq() \ + ( REG_UDC_EPIntMR &= ~(1 << (UDC_EPIntMR_INEP_BIT + 2)) ) +#define __udc_unmask_ep3in_irq() \ + ( REG_UDC_EPIntMR &= ~(1 << (UDC_EPIntMR_INEP_BIT + 3)) ) +#define __udc_unmask_ep4in_irq() \ + ( REG_UDC_EPIntMR &= ~(1 << (UDC_EPIntMR_INEP_BIT + 4)) ) + +#define __udc_mask_all_ep_intrs() \ + ( REG_UDC_EPIntMR = 0xffffffff ) +#define __udc_unmask_all_ep_intrs() \ + ( REG_UDC_EPIntMR = 0x00000000 ) + + +/* ep0 only CTRL, ep1 only INTR, ep2/3/5/6 only BULK, ep4/7 only ISO */ +#define __udc_config_endpoint_type() \ +do { \ + REG_UDC_EP0InCR = (REG_UDC_EP0InCR & ~UDC_EPCR_ET_MASK) | UDC_EPCR_ET_CTRL; \ + REG_UDC_EP0OutCR = (REG_UDC_EP0OutCR & ~UDC_EPCR_ET_MASK) | UDC_EPCR_ET_CTRL; \ + REG_UDC_EP1InCR = (REG_UDC_EP1InCR & ~UDC_EPCR_ET_MASK) | UDC_EPCR_ET_INTR; \ + REG_UDC_EP2InCR = (REG_UDC_EP2InCR & ~UDC_EPCR_ET_MASK) | UDC_EPCR_ET_BULK; \ + REG_UDC_EP3InCR = (REG_UDC_EP3InCR & ~UDC_EPCR_ET_MASK) | UDC_EPCR_ET_BULK; \ + REG_UDC_EP4InCR = (REG_UDC_EP4InCR & ~UDC_EPCR_ET_MASK) | UDC_EPCR_ET_ISO; \ + REG_UDC_EP5OutCR = (REG_UDC_EP5OutCR & ~UDC_EPCR_ET_MASK) | UDC_EPCR_ET_BULK; \ + REG_UDC_EP6OutCR = (REG_UDC_EP6OutCR & ~UDC_EPCR_ET_MASK) | UDC_EPCR_ET_BULK; \ + REG_UDC_EP7OutCR = (REG_UDC_EP7OutCR & ~UDC_EPCR_ET_MASK) | UDC_EPCR_ET_ISO; \ +} while (0) + +#define __udc_enable_ep0out_snoop_mode() ( REG_UDC_EP0OutCR |= UDC_EPCR_SN ) +#define __udc_enable_ep5out_snoop_mode() ( REG_UDC_EP5OutCR |= UDC_EPCR_SN ) +#define __udc_enable_ep6out_snoop_mode() ( REG_UDC_EP6OutCR |= UDC_EPCR_SN ) +#define __udc_enable_ep7out_snoop_mode() ( REG_UDC_EP7OutCR |= UDC_EPCR_SN ) + +#define __udc_disable_ep0out_snoop_mode() ( REG_UDC_EP0OutCR &= ~UDC_EPCR_SN ) +#define __udc_disable_ep5out_snoop_mode() ( REG_UDC_EP5OutCR &= ~UDC_EPCR_SN ) +#define __udc_disable_ep6out_snoop_mode() ( REG_UDC_EP6OutCR &= ~UDC_EPCR_SN ) +#define __udc_disable_ep7out_snoop_mode() ( REG_UDC_EP7OutCR &= ~UDC_EPCR_SN ) + +#define __udc_flush_ep0in_fifo() ( REG_UDC_EP0InCR |= UDC_EPCR_F ) +#define __udc_flush_ep1in_fifo() ( REG_UDC_EP1InCR |= UDC_EPCR_F ) +#define __udc_flush_ep2in_fifo() ( REG_UDC_EP2InCR |= UDC_EPCR_F ) +#define __udc_flush_ep3in_fifo() ( REG_UDC_EP3InCR |= UDC_EPCR_F ) +#define __udc_flush_ep4in_fifo() ( REG_UDC_EP4InCR |= UDC_EPCR_F ) + +#define __udc_unflush_ep0in_fifo() ( REG_UDC_EP0InCR &= ~UDC_EPCR_F ) +#define __udc_unflush_ep1in_fifo() ( REG_UDC_EP1InCR &= ~UDC_EPCR_F ) +#define __udc_unflush_ep2in_fifo() ( REG_UDC_EP2InCR &= ~UDC_EPCR_F ) +#define __udc_unflush_ep3in_fifo() ( REG_UDC_EP3InCR &= ~UDC_EPCR_F ) +#define __udc_unflush_ep4in_fifo() ( REG_UDC_EP4InCR &= ~UDC_EPCR_F ) + +#define __udc_enable_ep0in_stall() ( REG_UDC_EP0InCR |= UDC_EPCR_S ) +#define __udc_enable_ep0out_stall() ( REG_UDC_EP0OutCR |= UDC_EPCR_S ) +#define __udc_enable_ep1in_stall() ( REG_UDC_EP1InCR |= UDC_EPCR_S ) +#define __udc_enable_ep2in_stall() ( REG_UDC_EP2InCR |= UDC_EPCR_S ) +#define __udc_enable_ep3in_stall() ( REG_UDC_EP3InCR |= UDC_EPCR_S ) +#define __udc_enable_ep4in_stall() ( REG_UDC_EP4InCR |= UDC_EPCR_S ) +#define __udc_enable_ep5out_stall() ( REG_UDC_EP5OutCR |= UDC_EPCR_S ) +#define __udc_enable_ep6out_stall() ( REG_UDC_EP6OutCR |= UDC_EPCR_S ) +#define __udc_enable_ep7out_stall() ( REG_UDC_EP7OutCR |= UDC_EPCR_S ) + +#define __udc_disable_ep0in_stall() ( REG_UDC_EP0InCR &= ~UDC_EPCR_S ) +#define __udc_disable_ep0out_stall() ( REG_UDC_EP0OutCR &= ~UDC_EPCR_S ) +#define __udc_disable_ep1in_stall() ( REG_UDC_EP1InCR &= ~UDC_EPCR_S ) +#define __udc_disable_ep2in_stall() ( REG_UDC_EP2InCR &= ~UDC_EPCR_S ) +#define __udc_disable_ep3in_stall() ( REG_UDC_EP3InCR &= ~UDC_EPCR_S ) +#define __udc_disable_ep4in_stall() ( REG_UDC_EP4InCR &= ~UDC_EPCR_S ) +#define __udc_disable_ep5out_stall() ( REG_UDC_EP5OutCR &= ~UDC_EPCR_S ) +#define __udc_disable_ep6out_stall() ( REG_UDC_EP6OutCR &= ~UDC_EPCR_S ) +#define __udc_disable_ep7out_stall() ( REG_UDC_EP7OutCR &= ~UDC_EPCR_S ) + + +#define __udc_ep0out_packet_size() \ + ( (REG_UDC_EP0OutSR & UDC_EPSR_RXPKTSIZE_MASK) >> UDC_EPSR_RXPKTSIZE_BIT ) +#define __udc_ep5out_packet_size() \ + ( (REG_UDC_EP5OutSR & UDC_EPSR_RXPKTSIZE_MASK) >> UDC_EPSR_RXPKTSIZE_BIT ) +#define __udc_ep6out_packet_size() \ + ( (REG_UDC_EP6OutSR & UDC_EPSR_RXPKTSIZE_MASK) >> UDC_EPSR_RXPKTSIZE_BIT ) +#define __udc_ep7out_packet_size() \ + ( (REG_UDC_EP7OutSR & UDC_EPSR_RXPKTSIZE_MASK) >> UDC_EPSR_RXPKTSIZE_BIT ) + +#define __udc_ep0in_received_intoken() ( (REG_UDC_EP0InSR & UDC_EPSR_IN) ) +#define __udc_ep1in_received_intoken() ( (REG_UDC_EP1InSR & UDC_EPSR_IN) ) +#define __udc_ep2in_received_intoken() ( (REG_UDC_EP2InSR & UDC_EPSR_IN) ) +#define __udc_ep3in_received_intoken() ( (REG_UDC_EP3InSR & UDC_EPSR_IN) ) +#define __udc_ep4in_received_intoken() ( (REG_UDC_EP4InSR & UDC_EPSR_IN) ) + +#define __udc_ep0out_received_none() \ + ( (REG_UDC_EP0OutSR & UDC_EPSR_OUT_MASK) == UDC_EPSR_OUT_NONE ) +#define __udc_ep0out_received_data() \ + ( (REG_UDC_EP0OutSR & UDC_EPSR_OUT_MASK) == UDC_EPSR_OUT_RCVDATA ) +#define __udc_ep0out_received_setup() \ + ( (REG_UDC_EP0OutSR & UDC_EPSR_OUT_MASK) == UDC_EPSR_OUT_RCVSETUP ) + +#define __udc_ep5out_received_none() \ + ( (REG_UDC_EP5OutSR & UDC_EPSR_OUT_MASK) == UDC_EPSR_OUT_NONE ) +#define __udc_ep5out_received_data() \ + ( (REG_UDC_EP5OutSR & UDC_EPSR_OUT_MASK) == UDC_EPSR_OUT_RCVDATA ) +#define __udc_ep5out_received_setup() \ + ( (REG_UDC_EP5OutSR & UDC_EPSR_OUT_MASK) == UDC_EPSR_OUT_RCVSETUP ) + +#define __udc_ep6out_received_none() \ + ( (REG_UDC_EP6OutSR & UDC_EPSR_OUT_MASK) == UDC_EPSR_OUT_NONE ) +#define __udc_ep6out_received_data() \ + ( (REG_UDC_EP6OutSR & UDC_EPSR_OUT_MASK) == UDC_EPSR_OUT_RCVDATA ) +#define __udc_ep6out_received_setup() \ + ( (REG_UDC_EP6OutSR & UDC_EPSR_OUT_MASK) == UDC_EPSR_OUT_RCVSETUP ) + +#define __udc_ep7out_received_none() \ + ( (REG_UDC_EP7OutSR & UDC_EPSR_OUT_MASK) == UDC_EPSR_OUT_NONE ) +#define __udc_ep7out_received_data() \ + ( (REG_UDC_EP7OutSR & UDC_EPSR_OUT_MASK) == UDC_EPSR_OUT_RCVDATA ) +#define __udc_ep7out_received_setup() \ + ( (REG_UDC_EP7OutSR & UDC_EPSR_OUT_MASK) == UDC_EPSR_OUT_RCVSETUP ) + +/* ep7out ISO only */ +#define __udc_ep7out_get_pid() \ + ( (REG_UDC_EP7OutSR & UDC_EPSR_PID_MASK) >> UDC_EPSR_PID_BIT ) + + +#define __udc_ep0in_set_buffer_size(n) ( REG_UDC_EP0InBSR = (n) ) +#define __udc_ep1in_set_buffer_size(n) ( REG_UDC_EP1InBSR = (n) ) +#define __udc_ep2in_set_buffer_size(n) ( REG_UDC_EP2InBSR = (n) ) +#define __udc_ep3in_set_buffer_size(n) ( REG_UDC_EP3InBSR = (n) ) +#define __udc_ep4in_set_buffer_size(n) ( REG_UDC_EP4InBSR = (n) ) + +#define __udc_ep0out_get_frame_number(n) ( UDC_EP0OutPFNR ) +#define __udc_ep5out_get_frame_number(n) ( UDC_EP5OutPFNR ) +#define __udc_ep6out_get_frame_number(n) ( UDC_EP6OutPFNR ) +#define __udc_ep7out_get_frame_number(n) ( UDC_EP7OutPFNR ) + + +#define __udc_ep0in_set_max_packet_size(n) ( REG_UDC_EP0InMPSR = (n) ) +#define __udc_ep0out_set_max_packet_size(n) ( REG_UDC_EP0OutMPSR = (n) ) +#define __udc_ep1in_set_max_packet_size(n) ( REG_UDC_EP1InMPSR = (n) ) +#define __udc_ep2in_set_max_packet_size(n) ( REG_UDC_EP2InMPSR = (n) ) +#define __udc_ep3in_set_max_packet_size(n) ( REG_UDC_EP3InMPSR = (n) ) +#define __udc_ep4in_set_max_packet_size(n) ( REG_UDC_EP4InMPSR = (n) ) +#define __udc_ep5out_set_max_packet_size(n) ( REG_UDC_EP5OutMPSR = (n) ) +#define __udc_ep6out_set_max_packet_size(n) ( REG_UDC_EP6OutMPSR = (n) ) +#define __udc_ep7out_set_max_packet_size(n) ( REG_UDC_EP7OutMPSR = (n) ) + +/* set to 0xFFFF for UDC */ +#define __udc_set_setup_command_address(n) ( REG_UDC_STCMAR = (n) ) + +/* Init and configure EPxInfR(x=0,1,2,3,4,5,6,7) + * c: Configuration number to which this endpoint belongs + * i: Interface number to which this endpoint belongs + * a: Alternate setting to which this endpoint belongs + * p: max Packet size of this endpoint + */ + +#define __udc_ep0info_init(c,i,a,p) \ +do { \ + REG_UDC_EP0InfR &= ~UDC_EPInfR_MPS_MASK; \ + REG_UDC_EP0InfR |= ((p) << UDC_EPInfR_MPS_BIT); \ + REG_UDC_EP0InfR &= ~UDC_EPInfR_ALTS_MASK; \ + REG_UDC_EP0InfR |= ((a) << UDC_EPInfR_ALTS_BIT); \ + REG_UDC_EP0InfR &= ~UDC_EPInfR_IFN_MASK; \ + REG_UDC_EP0InfR |= ((i) << UDC_EPInfR_IFN_BIT); \ + REG_UDC_EP0InfR &= ~UDC_EPInfR_CGN_MASK; \ + REG_UDC_EP0InfR |= ((c) << UDC_EPInfR_CGN_BIT); \ + REG_UDC_EP0InfR &= ~UDC_EPInfR_EPT_MASK; \ + REG_UDC_EP0InfR |= UDC_EPInfR_EPT_CTRL; \ + REG_UDC_EP0InfR &= ~UDC_EPInfR_EPD; \ + REG_UDC_EP0InfR |= UDC_EPInfR_EPD_OUT; \ + REG_UDC_EP0InfR &= ~UDC_EPInfR_EPN_MASK; \ + REG_UDC_EP0InfR |= (0 << UDC_EPInfR_EPN_BIT); \ +} while (0) + +#define __udc_ep1info_init(c,i,a,p) \ +do { \ + REG_UDC_EP1InfR &= ~UDC_EPInfR_MPS_MASK; \ + REG_UDC_EP1InfR |= ((p) << UDC_EPInfR_MPS_BIT); \ + REG_UDC_EP1InfR &= ~UDC_EPInfR_ALTS_MASK; \ + REG_UDC_EP1InfR |= ((a) << UDC_EPInfR_ALTS_BIT); \ + REG_UDC_EP1InfR &= ~UDC_EPInfR_IFN_MASK; \ + REG_UDC_EP1InfR |= ((i) << UDC_EPInfR_IFN_BIT); \ + REG_UDC_EP1InfR &= ~UDC_EPInfR_CGN_MASK; \ + REG_UDC_EP1InfR |= ((c) << UDC_EPInfR_CGN_BIT); \ + REG_UDC_EP1InfR &= ~UDC_EPInfR_EPT_MASK; \ + REG_UDC_EP1InfR |= UDC_EPInfR_EPT_INTR; \ + REG_UDC_EP1InfR &= ~UDC_EPInfR_EPD; \ + REG_UDC_EP1InfR |= UDC_EPInfR_EPD_IN; \ + REG_UDC_EP1InfR &= ~UDC_EPInfR_EPN_MASK; \ + REG_UDC_EP1InfR |= (1 << UDC_EPInfR_EPN_BIT); \ +} while (0) + +#define __udc_ep2info_init(c,i,a,p) \ +do { \ + REG_UDC_EP2InfR &= ~UDC_EPInfR_MPS_MASK; \ + REG_UDC_EP2InfR |= ((p) << UDC_EPInfR_MPS_BIT); \ + REG_UDC_EP2InfR &= ~UDC_EPInfR_ALTS_MASK; \ + REG_UDC_EP2InfR |= ((a) << UDC_EPInfR_ALTS_BIT); \ + REG_UDC_EP2InfR &= ~UDC_EPInfR_IFN_MASK; \ + REG_UDC_EP2InfR |= ((i) << UDC_EPInfR_IFN_BIT); \ + REG_UDC_EP2InfR &= ~UDC_EPInfR_CGN_MASK; \ + REG_UDC_EP2InfR |= ((c) << UDC_EPInfR_CGN_BIT); \ + REG_UDC_EP2InfR &= ~UDC_EPInfR_EPT_MASK; \ + REG_UDC_EP2InfR |= UDC_EPInfR_EPT_BULK; \ + REG_UDC_EP2InfR &= ~UDC_EPInfR_EPD; \ + REG_UDC_EP2InfR |= UDC_EPInfR_EPD_IN; \ + REG_UDC_EP2InfR &= ~UDC_EPInfR_EPN_MASK; \ + REG_UDC_EP2InfR |= (2 << UDC_EPInfR_EPN_BIT); \ +} while (0) + +#define __udc_ep3info_init(c,i,a,p) \ +do { \ + REG_UDC_EP3InfR &= ~UDC_EPInfR_MPS_MASK; \ + REG_UDC_EP3InfR |= ((p) << UDC_EPInfR_MPS_BIT); \ + REG_UDC_EP3InfR &= ~UDC_EPInfR_ALTS_MASK; \ + REG_UDC_EP3InfR |= ((a) << UDC_EPInfR_ALTS_BIT); \ + REG_UDC_EP3InfR &= ~UDC_EPInfR_IFN_MASK; \ + REG_UDC_EP3InfR |= ((i) << UDC_EPInfR_IFN_BIT); \ + REG_UDC_EP3InfR &= ~UDC_EPInfR_CGN_MASK; \ + REG_UDC_EP3InfR |= ((c) << UDC_EPInfR_CGN_BIT); \ + REG_UDC_EP3InfR &= ~UDC_EPInfR_EPT_MASK; \ + REG_UDC_EP3InfR |= UDC_EPInfR_EPT_BULK; \ + REG_UDC_EP3InfR &= ~UDC_EPInfR_EPD; \ + REG_UDC_EP3InfR |= UDC_EPInfR_EPD_IN; \ + REG_UDC_EP3InfR &= ~UDC_EPInfR_EPN_MASK; \ + REG_UDC_EP3InfR |= (3 << UDC_EPInfR_EPN_BIT); \ +} while (0) + +#define __udc_ep4info_init(c,i,a,p) \ +do { \ + REG_UDC_EP4InfR &= ~UDC_EPInfR_MPS_MASK; \ + REG_UDC_EP4InfR |= ((p) << UDC_EPInfR_MPS_BIT); \ + REG_UDC_EP4InfR &= ~UDC_EPInfR_ALTS_MASK; \ + REG_UDC_EP4InfR |= ((a) << UDC_EPInfR_ALTS_BIT); \ + REG_UDC_EP4InfR &= ~UDC_EPInfR_IFN_MASK; \ + REG_UDC_EP4InfR |= ((i) << UDC_EPInfR_IFN_BIT); \ + REG_UDC_EP4InfR &= ~UDC_EPInfR_CGN_MASK; \ + REG_UDC_EP4InfR |= ((c) << UDC_EPInfR_CGN_BIT); \ + REG_UDC_EP4InfR &= ~UDC_EPInfR_EPT_MASK; \ + REG_UDC_EP4InfR |= UDC_EPInfR_EPT_ISO; \ + REG_UDC_EP4InfR &= ~UDC_EPInfR_EPD; \ + REG_UDC_EP4InfR |= UDC_EPInfR_EPD_IN; \ + REG_UDC_EP4InfR &= ~UDC_EPInfR_EPN_MASK; \ + REG_UDC_EP4InfR |= (4 << UDC_EPInfR_EPN_BIT); \ +} while (0) + +#define __udc_ep5info_init(c,i,a,p) \ +do { \ + REG_UDC_EP5InfR &= ~UDC_EPInfR_MPS_MASK; \ + REG_UDC_EP5InfR |= ((p) << UDC_EPInfR_MPS_BIT); \ + REG_UDC_EP5InfR &= ~UDC_EPInfR_ALTS_MASK; \ + REG_UDC_EP5InfR |= ((a) << UDC_EPInfR_ALTS_BIT); \ + REG_UDC_EP5InfR &= ~UDC_EPInfR_IFN_MASK; \ + REG_UDC_EP5InfR |= ((i) << UDC_EPInfR_IFN_BIT); \ + REG_UDC_EP5InfR &= ~UDC_EPInfR_CGN_MASK; \ + REG_UDC_EP5InfR |= ((c) << UDC_EPInfR_CGN_BIT); \ + REG_UDC_EP5InfR &= ~UDC_EPInfR_EPT_MASK; \ + REG_UDC_EP5InfR |= UDC_EPInfR_EPT_BULK; \ + REG_UDC_EP5InfR &= ~UDC_EPInfR_EPD; \ + REG_UDC_EP5InfR |= UDC_EPInfR_EPD_OUT; \ + REG_UDC_EP5InfR &= ~UDC_EPInfR_EPN_MASK; \ + REG_UDC_EP5InfR |= (5 << UDC_EPInfR_EPN_BIT); \ +} while (0) + +#define __udc_ep6info_init(c,i,a,p) \ +do { \ + REG_UDC_EP6InfR &= ~UDC_EPInfR_MPS_MASK; \ + REG_UDC_EP6InfR |= ((p) << UDC_EPInfR_MPS_BIT); \ + REG_UDC_EP6InfR &= ~UDC_EPInfR_ALTS_MASK; \ + REG_UDC_EP6InfR |= ((a) << UDC_EPInfR_ALTS_BIT); \ + REG_UDC_EP6InfR &= ~UDC_EPInfR_IFN_MASK; \ + REG_UDC_EP6InfR |= ((i) << UDC_EPInfR_IFN_BIT); \ + REG_UDC_EP6InfR &= ~UDC_EPInfR_CGN_MASK; \ + REG_UDC_EP6InfR |= ((c) << UDC_EPInfR_CGN_BIT); \ + REG_UDC_EP6InfR &= ~UDC_EPInfR_EPT_MASK; \ + REG_UDC_EP6InfR |= UDC_EPInfR_EPT_BULK; \ + REG_UDC_EP6InfR &= ~UDC_EPInfR_EPD; \ + REG_UDC_EP6InfR |= UDC_EPInfR_EPD_OUT; \ + REG_UDC_EP6InfR &= ~UDC_EPInfR_EPN_MASK; \ + REG_UDC_EP6InfR |= (6 << UDC_EPInfR_EPN_BIT); \ +} while (0) + +#define __udc_ep7info_init(c,i,a,p) \ +do { \ + REG_UDC_EP7InfR &= ~UDC_EPInfR_MPS_MASK; \ + REG_UDC_EP7InfR |= ((p) << UDC_EPInfR_MPS_BIT); \ + REG_UDC_EP7InfR &= ~UDC_EPInfR_ALTS_MASK; \ + REG_UDC_EP7InfR |= ((a) << UDC_EPInfR_ALTS_BIT); \ + REG_UDC_EP7InfR &= ~UDC_EPInfR_IFN_MASK; \ + REG_UDC_EP7InfR |= ((i) << UDC_EPInfR_IFN_BIT); \ + REG_UDC_EP7InfR &= ~UDC_EPInfR_CGN_MASK; \ + REG_UDC_EP7InfR |= ((c) << UDC_EPInfR_CGN_BIT); \ + REG_UDC_EP7InfR &= ~UDC_EPInfR_EPT_MASK; \ + REG_UDC_EP7InfR |= UDC_EPInfR_EPT_ISO; \ + REG_UDC_EP7InfR &= ~UDC_EPInfR_EPD; \ + REG_UDC_EP7InfR |= UDC_EPInfR_EPD_OUT; \ + REG_UDC_EP7InfR &= ~UDC_EPInfR_EPN_MASK; \ + REG_UDC_EP7InfR |= (7 << UDC_EPInfR_EPN_BIT); \ +} while (0) + + +/*************************************************************************** + * DMAC + ***************************************************************************/ + +/* n is the DMA channel (0 - 7) */ + +#define __dmac_enable_all_channels() \ + ( REG_DMAC_DMACR |= DMAC_DMACR_DME | DMAC_DMACR_PR_ROUNDROBIN ) +#define __dmac_disable_all_channels() \ + ( REG_DMAC_DMACR &= ~DMAC_DMACR_DME ) + +/* p=0,1,2,3 */ +#define __dmac_set_priority(p) \ +do { \ + REG_DMAC_DMACR &= ~DMAC_DMACR_PR_MASK; \ + REG_DMAC_DMACR |= ((p) << DMAC_DMACR_PR_BIT); \ +} while (0) + +#define __dmac_test_halt_error() ( REG_DMAC_DMACR & DMAC_DMACR_HTR ) +#define __dmac_test_addr_error() ( REG_DMAC_DMACR & DMAC_DMACR_AER ) + +#define __dmac_enable_channel(n) \ + ( REG_DMAC_DCCSR(n) |= DMAC_DCCSR_CHDE ) +#define __dmac_disable_channel(n) \ + ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_CHDE ) +#define __dmac_channel_enabled(n) \ + ( REG_DMAC_DCCSR(n) & DMAC_DCCSR_CHDE ) + +#define __dmac_channel_enable_irq(n) \ + ( REG_DMAC_DCCSR(n) |= DMAC_DCCSR_TCIE ) +#define __dmac_channel_disable_irq(n) \ + ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_TCIE ) + +#define __dmac_channel_transmit_halt_detected(n) \ + ( REG_DMAC_DCCSR(n) & DMAC_DCCSR_HLT ) +#define __dmac_channel_transmit_end_detected(n) \ + ( REG_DMAC_DCCSR(n) & DMAC_DCCSR_TC ) +#define __dmac_channel_address_error_detected(n) \ + ( REG_DMAC_DCCSR(n) & DMAC_DCCSR_AR ) + +#define __dmac_channel_clear_transmit_halt(n) \ + ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_HLT ) +#define __dmac_channel_clear_transmit_end(n) \ + ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_TC ) +#define __dmac_channel_clear_address_error(n) \ + ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_AR ) + +#define __dmac_channel_set_single_mode(n) \ + ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_TM ) +#define __dmac_channel_set_block_mode(n) \ + ( REG_DMAC_DCCSR(n) |= DMAC_DCCSR_TM ) + +#define __dmac_channel_set_transfer_unit_32bit(n) \ +do { \ + REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_DS_MASK; \ + REG_DMAC_DCCSR(n) |= DMAC_DCCSR_DS_32b; \ +} while (0) + +#define __dmac_channel_set_transfer_unit_16bit(n) \ +do { \ + REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_DS_MASK; \ + REG_DMAC_DCCSR(n) |= DMAC_DCCSR_DS_16b; \ +} while (0) + +#define __dmac_channel_set_transfer_unit_8bit(n) \ +do { \ + REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_DS_MASK; \ + REG_DMAC_DCCSR(n) |= DMAC_DCCSR_DS_8b; \ +} while (0) + +#define __dmac_channel_set_transfer_unit_16byte(n) \ +do { \ + REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_DS_MASK; \ + REG_DMAC_DCCSR(n) |= DMAC_DCCSR_DS_16B; \ +} while (0) + +#define __dmac_channel_set_transfer_unit_32byte(n) \ +do { \ + REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_DS_MASK; \ + REG_DMAC_DCCSR(n) |= DMAC_DCCSR_DS_32B; \ +} while (0) + +/* w=8,16,32 */ +#define __dmac_channel_set_dest_port_width(n,w) \ +do { \ + REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_DWDH_MASK; \ + REG_DMAC_DCCSR(n) |= DMAC_DCCSR_DWDH_##w; \ +} while (0) + +/* w=8,16,32 */ +#define __dmac_channel_set_src_port_width(n,w) \ +do { \ + REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_SWDH_MASK; \ + REG_DMAC_DCCSR(n) |= DMAC_DCCSR_SWDH_##w; \ +} while (0) + +/* v=0-15 */ +#define __dmac_channel_set_rdil(n,v) \ +do { \ + REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_RDIL_MASK; \ + REG_DMAC_DCCSR(n) |= ((v) << DMAC_DCCSR_RDIL_BIT); \ +} while (0) + +#define __dmac_channel_dest_addr_fixed(n) \ + ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_DAM ) +#define __dmac_channel_dest_addr_increment(n) \ + ( REG_DMAC_DCCSR(n) |= DMAC_DCCSR_DAM ) + +#define __dmac_channel_src_addr_fixed(n) \ + ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_SAM ) +#define __dmac_channel_src_addr_increment(n) \ + ( REG_DMAC_DCCSR(n) |= DMAC_DCCSR_SAM ) + +#define __dmac_channel_set_eop_high(n) \ + ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_EOPM ) +#define __dmac_channel_set_eop_low(n) \ + ( REG_DMAC_DCCSR(n) |= DMAC_DCCSR_EOPM ) + +#define __dmac_channel_set_erdm(n,m) \ +do { \ + REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_SWDH_MASK; \ + REG_DMAC_DCCSR(n) |= ((m) << DMAC_DCCSR_ERDM_BIT); \ +} while (0) + +#define __dmac_channel_set_eackm(n) \ + ( REG_DMAC_DCCSR(n) |= DMAC_DCCSR_EACKM ) +#define __dmac_channel_clear_eackm(n) \ + ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_EACKM ) + +#define __dmac_channel_set_eacks(n) \ + ( REG_DMAC_DCCSR(n) |= DMAC_DCCSR_EACKS ) +#define __dmac_channel_clear_eacks(n) \ + ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_EACKS ) + + +#define __dmac_channel_irq_detected(n) \ + ( REG_DMAC_DCCSR(n) & (DMAC_DCCSR_TC | DMAC_DCCSR_AR) ) + +static __inline__ int __dmac_get_irq(void) +{ + int i; + for (i=0;i> AIC_SR_TFL_BIT ) +#define __aic_get_receive_count() \ + ( (REG_AIC_SR & AIC_SR_RFL_MASK) >> AIC_SR_RFL_BIT ) + +#define __ac97_command_transmitted() ( REG_AIC_ACSR & AIC_ACSR_CADT ) +#define __ac97_status_received() ( REG_AIC_ACSR & AIC_ACSR_SADR ) +#define __ac97_status_receive_timeout() ( REG_AIC_ACSR & AIC_ACSR_RSTO ) +#define __ac97_codec_is_low_power_mode() ( REG_AIC_ACSR & AIC_ACSR_CLPM ) +#define __ac97_codec_is_ready() ( REG_AIC_ACSR & AIC_ACSR_CRDY ) + +#define __i2s_is_busy() ( REG_AIC_I2SSR & AIC_I2SSR_BSY ) + +#define CODEC_READ_CMD (1 << 19) +#define CODEC_WRITE_CMD (0 << 19) +#define CODEC_REG_INDEX_BIT 12 +#define CODEC_REG_INDEX_MASK (0x7f << CODEC_REG_INDEX_BIT) /* 18:12 */ +#define CODEC_REG_DATA_BIT 4 +#define CODEC_REG_DATA_MASK (0x0ffff << 4) /* 19:4 */ + +#define __ac97_out_rcmd_addr(reg) \ +do { \ + REG_AIC_ACCAR = CODEC_READ_CMD | ((reg) << CODEC_REG_INDEX_BIT); \ +} while (0) + +#define __ac97_out_wcmd_addr(reg) \ +do { \ + REG_AIC_ACCAR = CODEC_WRITE_CMD | ((reg) << CODEC_REG_INDEX_BIT); \ +} while (0) + +#define __ac97_out_data(value) \ +do { \ + REG_AIC_ACCDR = ((value) << CODEC_REG_DATA_BIT); \ +} while (0) + +#define __ac97_in_data() \ + ( (REG_AIC_ACSDR & CODEC_REG_DATA_MASK) >> CODEC_REG_DATA_BIT ) + +#define __ac97_in_status_addr() \ + ( (REG_AIC_ACSAR & CODEC_REG_INDEX_MASK) >> CODEC_REG_INDEX_BIT ) + +#define __i2s_set_sample_rate(i2sclk, sync) \ + ( REG_AIC_I2SDIV = ((i2sclk) / (4*64)) / (sync) ) + +#define __aic_write_tfifo(v) ( REG_AIC_DR = (v) ) +#define __aic_read_rfifo() ( REG_AIC_DR ) + +// +// Define next ops for AC97 compatible +// + +#define AC97_ACSR AIC_ACSR + +#define __ac97_enable() __aic_enable(); __aic_select_ac97() +#define __ac97_disable() __aic_disable() +#define __ac97_reset() __aic_reset() + +#define __ac97_set_transmit_trigger(n) __aic_set_transmit_trigger(n) +#define __ac97_set_receive_trigger(n) __aic_set_receive_trigger(n) + +#define __ac97_enable_record() __aic_enable_record() +#define __ac97_disable_record() __aic_disable_record() +#define __ac97_enable_replay() __aic_enable_replay() +#define __ac97_disable_replay() __aic_disable_replay() +#define __ac97_enable_loopback() __aic_enable_loopback() +#define __ac97_disable_loopback() __aic_disable_loopback() + +#define __ac97_enable_transmit_dma() __aic_enable_transmit_dma() +#define __ac97_disable_transmit_dma() __aic_disable_transmit_dma() +#define __ac97_enable_receive_dma() __aic_enable_receive_dma() +#define __ac97_disable_receive_dma() __aic_disable_receive_dma() + +#define __ac97_transmit_request() __aic_transmit_request() +#define __ac97_receive_request() __aic_receive_request() +#define __ac97_transmit_underrun() __aic_transmit_underrun() +#define __ac97_receive_overrun() __aic_receive_overrun() + +#define __ac97_clear_errors() __aic_clear_errors() + +#define __ac97_get_transmit_resident() __aic_get_transmit_resident() +#define __ac97_get_receive_count() __aic_get_receive_count() + +#define __ac97_enable_transmit_intr() __aic_enable_transmit_intr() +#define __ac97_disable_transmit_intr() __aic_disable_transmit_intr() +#define __ac97_enable_receive_intr() __aic_enable_receive_intr() +#define __ac97_disable_receive_intr() __aic_disable_receive_intr() + +#define __ac97_write_tfifo(v) __aic_write_tfifo(v) +#define __ac97_read_rfifo() __aic_read_rfifo() + +// +// Define next ops for I2S compatible +// + +#define I2S_ACSR AIC_I2SSR + +#define __i2s_enable() __aic_enable(); __aic_select_i2s() +#define __i2s_disable() __aic_disable() +#define __i2s_reset() __aic_reset() + +#define __i2s_set_transmit_trigger(n) __aic_set_transmit_trigger(n) +#define __i2s_set_receive_trigger(n) __aic_set_receive_trigger(n) + +#define __i2s_enable_record() __aic_enable_record() +#define __i2s_disable_record() __aic_disable_record() +#define __i2s_enable_replay() __aic_enable_replay() +#define __i2s_disable_replay() __aic_disable_replay() +#define __i2s_enable_loopback() __aic_enable_loopback() +#define __i2s_disable_loopback() __aic_disable_loopback() + +#define __i2s_enable_transmit_dma() __aic_enable_transmit_dma() +#define __i2s_disable_transmit_dma() __aic_disable_transmit_dma() +#define __i2s_enable_receive_dma() __aic_enable_receive_dma() +#define __i2s_disable_receive_dma() __aic_disable_receive_dma() + +#define __i2s_transmit_request() __aic_transmit_request() +#define __i2s_receive_request() __aic_receive_request() +#define __i2s_transmit_underrun() __aic_transmit_underrun() +#define __i2s_receive_overrun() __aic_receive_overrun() + +#define __i2s_clear_errors() __aic_clear_errors() + +#define __i2s_get_transmit_resident() __aic_get_transmit_resident() +#define __i2s_get_receive_count() __aic_get_receive_count() + +#define __i2s_enable_transmit_intr() __aic_enable_transmit_intr() +#define __i2s_disable_transmit_intr() __aic_disable_transmit_intr() +#define __i2s_enable_receive_intr() __aic_enable_receive_intr() +#define __i2s_disable_receive_intr() __aic_disable_receive_intr() + +#define __i2s_write_tfifo(v) __aic_write_tfifo(v) +#define __i2s_read_rfifo() __aic_read_rfifo() + +#define __i2s_reset_codec() \ + do { \ + __gpio_as_output(70); /* SDATA_OUT */ \ + __gpio_as_input(71); /* SDATA_IN */ \ + __gpio_as_output(78); /* SYNC */ \ + __gpio_as_output(69); /* RESET# */ \ + __gpio_clear_pin(70); \ + __gpio_clear_pin(71); \ + __gpio_clear_pin(78); \ + __gpio_clear_pin(69); \ + __gpio_as_i2s_master(); \ + } while (0) + + +/*************************************************************************** + * LCD + ***************************************************************************/ + +#define __lcd_set_dis() ( REG_LCD_CTRL |= LCD_CTRL_DIS ) +#define __lcd_clr_dis() ( REG_LCD_CTRL &= ~LCD_CTRL_DIS ) + +#define __lcd_set_ena() ( REG_LCD_CTRL |= LCD_CTRL_ENA ) +#define __lcd_clr_ena() ( REG_LCD_CTRL &= ~LCD_CTRL_ENA ) + +/* n=1,2,4,8,16 */ +#define __lcd_set_bpp(n) \ + ( REG_LCD_CTRL = (REG_LCD_CTRL & ~LCD_CTRL_BPP_MASK) | LCD_CTRL_BPP_##n ) + +/* n=4,8,16 */ +#define __lcd_set_burst_length(n) \ +do { \ + REG_LCD_CTRL &= ~LCD_CTRL_BST_MASK; \ + REG_LCD_CTRL |= LCD_CTRL_BST_n##; \ +} while (0) + +#define __lcd_select_rgb565() ( REG_LCD_CTRL &= ~LCD_CTRL_RGB555 ) +#define __lcd_select_rgb555() ( REG_LCD_CTRL |= LCD_CTRL_RGB555 ) + +#define __lcd_set_ofup() ( REG_LCD_CTRL |= LCD_CTRL_OFUP ) +#define __lcd_clr_ofup() ( REG_LCD_CTRL &= ~LCD_CTRL_OFUP ) + +/* n=2,4,16 */ +#define __lcd_set_stn_frc(n) \ +do { \ + REG_LCD_CTRL &= ~LCD_CTRL_FRC_MASK; \ + REG_LCD_CTRL |= LCD_CTRL_FRC_n##; \ +} while (0) + + +#define __lcd_pixel_endian_little() ( REG_LCD_CTRL |= LCD_CTRL_PEDN ) +#define __lcd_pixel_endian_big() ( REG_LCD_CTRL &= ~LCD_CTRL_PEDN ) + +#define __lcd_reverse_byte_endian() ( REG_LCD_CTRL |= LCD_CTRL_BEDN ) +#define __lcd_normal_byte_endian() ( REG_LCD_CTRL &= ~LCD_CTRL_BEDN ) + +#define __lcd_enable_eof_intr() ( REG_LCD_CTRL |= LCD_CTRL_EOFM ) +#define __lcd_disable_eof_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_EOFM ) + +#define __lcd_enable_sof_intr() ( REG_LCD_CTRL |= LCD_CTRL_SOFM ) +#define __lcd_disable_sof_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_SOFM ) + +#define __lcd_enable_ofu_intr() ( REG_LCD_CTRL |= LCD_CTRL_OFUM ) +#define __lcd_disable_ofu_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_OFUM ) + +#define __lcd_enable_ifu0_intr() ( REG_LCD_CTRL |= LCD_CTRL_IFUM0 ) +#define __lcd_disable_ifu0_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_IFUM0 ) + +#define __lcd_enable_ifu1_intr() ( REG_LCD_CTRL |= LCD_CTRL_IFUM1 ) +#define __lcd_disable_ifu1_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_IFUM1 ) + +#define __lcd_enable_ldd_intr() ( REG_LCD_CTRL |= LCD_CTRL_LDDM ) +#define __lcd_disable_ldd_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_LDDM ) + +#define __lcd_enable_qd_intr() ( REG_LCD_CTRL |= LCD_CTRL_QDM ) +#define __lcd_disable_qd_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_QDM ) + + +/* LCD status register indication */ + +#define __lcd_quick_disable_done() ( REG_LCD_STATE & LCD_STATE_QD ) +#define __lcd_disable_done() ( REG_LCD_STATE & LCD_STATE_LDD ) +#define __lcd_infifo0_underrun() ( REG_LCD_STATE & LCD_STATE_IFU0 ) +#define __lcd_infifo1_underrun() ( REG_LCD_STATE & LCD_STATE_IFU1 ) +#define __lcd_outfifo_underrun() ( REG_LCD_STATE & LCD_STATE_OFU ) +#define __lcd_start_of_frame() ( REG_LCD_STATE & LCD_STATE_SOF ) +#define __lcd_end_of_frame() ( REG_LCD_STATE & LCD_STATE_EOF ) + +#define __lcd_clr_outfifounderrun() ( REG_LCD_STATE &= ~LCD_STATE_OFU ) +#define __lcd_clr_sof() ( REG_LCD_STATE &= ~LCD_STATE_SOF ) +#define __lcd_clr_eof() ( REG_LCD_STATE &= ~LCD_STATE_EOF ) + +#define __lcd_panel_white() ( REG_LCD_DEV |= LCD_DEV_WHITE ) +#define __lcd_panel_black() ( REG_LCD_DEV &= ~LCD_DEV_WHITE ) + +/* n=1,2,4,8 for single mono-STN + * n=4,8 for dual mono-STN + */ +#define __lcd_set_panel_datawidth(n) \ +do { \ + REG_LCD_DEV &= ~LCD_DEV_PDW_MASK; \ + REG_LCD_DEV |= LCD_DEV_PDW_n##; \ +} while (0) + +/* m=LCD_DEV_MODE_GENERUIC_TFT_xxx */ +#define __lcd_set_panel_mode(m) \ +do { \ + REG_LCD_DEV &= ~LCD_DEV_MODE_MASK; \ + REG_LCD_DEV |= (m); \ +} while(0) + +/* n = 0-255 */ +#define __lcd_disable_ac_bias() ( REG_LCD_IO = 0xff ) +#define __lcd_set_ac_bias(n) \ +do { \ + REG_LCD_IO &= ~LCD_IO_ACB_MASK; \ + REG_LCD_IO |= ((n) << LCD_IO_ACB_BIT); \ +} while(0) + +#define __lcd_io_set_dir() ( REG_LCD_IO |= LCD_IO_DIR ) +#define __lcd_io_clr_dir() ( REG_LCD_IO &= ~LCD_IO_DIR ) + +#define __lcd_io_set_dep() ( REG_LCD_IO |= LCD_IO_DEP ) +#define __lcd_io_clr_dep() ( REG_LCD_IO &= ~LCD_IO_DEP ) + +#define __lcd_io_set_vsp() ( REG_LCD_IO |= LCD_IO_VSP ) +#define __lcd_io_clr_vsp() ( REG_LCD_IO &= ~LCD_IO_VSP ) + +#define __lcd_io_set_hsp() ( REG_LCD_IO |= LCD_IO_HSP ) +#define __lcd_io_clr_hsp() ( REG_LCD_IO &= ~LCD_IO_HSP ) + +#define __lcd_io_set_pcp() ( REG_LCD_IO |= LCD_IO_PCP ) +#define __lcd_io_clr_pcp() ( REG_LCD_IO &= ~LCD_IO_PCP ) + +#define __lcd_vsync_get_vps() \ + ( (REG_LCD_VSYNC & LCD_VSYNC_VPS_MASK) >> LCD_VSYNC_VPS_BIT ) + +#define __lcd_vsync_get_vpe() \ + ( (REG_LCD_VSYNC & LCD_VSYNC_VPE_MASK) >> LCD_VSYNC_VPE_BIT ) +#define __lcd_vsync_set_vpe(n) \ +do { \ + REG_LCD_VSYNC &= ~LCD_VSYNC_VPE_MASK; \ + REG_LCD_VSYNC |= (n) << LCD_VSYNC_VPE_BIT; \ +} while (0) + +#define __lcd_hsync_get_hps() \ + ( (REG_LCD_HSYNC & LCD_HSYNC_HPS_MASK) >> LCD_HSYNC_HPS_BIT ) +#define __lcd_hsync_set_hps(n) \ +do { \ + REG_LCD_HSYNC &= ~LCD_HSYNC_HPS_MASK; \ + REG_LCD_HSYNC |= (n) << LCD_HSYNC_HPS_BIT; \ +} while (0) + +#define __lcd_hsync_get_hpe() \ + ( (REG_LCD_HSYNC & LCD_HSYNC_HPE_MASK) >> LCD_VSYNC_HPE_BIT ) +#define __lcd_hsync_set_hpe(n) \ +do { \ + REG_LCD_HSYNC &= ~LCD_HSYNC_HPE_MASK; \ + REG_LCD_HSYNC |= (n) << LCD_HSYNC_HPE_BIT; \ +} while (0) + +#define __lcd_vat_get_ht() \ + ( (REG_LCD_VAT & LCD_VAT_HT_MASK) >> LCD_VAT_HT_BIT ) +#define __lcd_vat_set_ht(n) \ +do { \ + REG_LCD_VAT &= ~LCD_VAT_HT_MASK; \ + REG_LCD_VAT |= (n) << LCD_VAT_HT_BIT; \ +} while (0) + +#define __lcd_vat_get_vt() \ + ( (REG_LCD_VAT & LCD_VAT_VT_MASK) >> LCD_VAT_VT_BIT ) +#define __lcd_vat_set_vt(n) \ +do { \ + REG_LCD_VAT &= ~LCD_VAT_VT_MASK; \ + REG_LCD_VAT |= (n) << LCD_VAT_VT_BIT; \ +} while (0) + +#define __lcd_dah_get_hds() \ + ( (REG_LCD_DAH & LCD_DAH_HDS_MASK) >> LCD_DAH_HDS_BIT ) +#define __lcd_dah_set_hds(n) \ +do { \ + REG_LCD_DAH &= ~LCD_DAH_HDS_MASK; \ + REG_LCD_DAH |= (n) << LCD_DAH_HDS_BIT; \ +} while (0) + +#define __lcd_dah_get_hde() \ + ( (REG_LCD_DAH & LCD_DAH_HDE_MASK) >> LCD_DAH_HDE_BIT ) +#define __lcd_dah_set_hde(n) \ +do { \ + REG_LCD_DAH &= ~LCD_DAH_HDE_MASK; \ + REG_LCD_DAH |= (n) << LCD_DAH_HDE_BIT; \ +} while (0) + +#define __lcd_dav_get_vds() \ + ( (REG_LCD_DAV & LCD_DAV_VDS_MASK) >> LCD_DAV_VDS_BIT ) +#define __lcd_dav_set_vds(n) \ +do { \ + REG_LCD_DAV &= ~LCD_DAV_VDS_MASK; \ + REG_LCD_DAV |= (n) << LCD_DAV_VDS_BIT; \ +} while (0) + +#define __lcd_dav_get_vde() \ + ( (REG_LCD_DAV & LCD_DAV_VDE_MASK) >> LCD_DAV_VDE_BIT ) +#define __lcd_dav_set_vde(n) \ +do { \ + REG_LCD_DAV &= ~LCD_DAV_VDE_MASK; \ + REG_LCD_DAV |= (n) << LCD_DAV_VDE_BIT; \ +} while (0) + +#define __lcd_cmd0_set_sofint() ( REG_LCD_CMD0 |= LCD_CMD_SOFINT ) +#define __lcd_cmd0_clr_sofint() ( REG_LCD_CMD0 &= ~LCD_CMD_SOFINT ) +#define __lcd_cmd1_set_sofint() ( REG_LCD_CMD1 |= LCD_CMD_SOFINT ) +#define __lcd_cmd1_clr_sofint() ( REG_LCD_CMD1 &= ~LCD_CMD_SOFINT ) + +#define __lcd_cmd0_set_eofint() ( REG_LCD_CMD0 |= LCD_CMD_EOFINT ) +#define __lcd_cmd0_clr_eofint() ( REG_LCD_CMD0 &= ~LCD_CMD_EOFINT ) +#define __lcd_cmd1_set_eofint() ( REG_LCD_CMD1 |= LCD_CMD_EOFINT ) +#define __lcd_cmd1_clr_eofint() ( REG_LCD_CMD1 &= ~LCD_CMD_EOFINT ) + +#define __lcd_cmd0_set_pal() ( REG_LCD_CMD0 |= LCD_CMD_PAL ) +#define __lcd_cmd0_clr_pal() ( REG_LCD_CMD0 &= ~LCD_CMD_PAL ) + +#define __lcd_cmd0_get_len() \ + ( (REG_LCD_CMD0 & LCD_CMD_LEN_MASK) >> LCD_CMD_LEN_BIT ) +#define __lcd_cmd1_get_len() \ + ( (REG_LCD_CMD1 & LCD_CMD_LEN_MASK) >> LCD_CMD_LEN_BIT ) + + + +/*************************************************************************** + * DES + ***************************************************************************/ + + +/*************************************************************************** + * CPM + ***************************************************************************/ +#define __cpm_plcr1_fd() \ + ((REG_CPM_PLCR1 & CPM_PLCR1_PLL1FD_MASK) >> CPM_PLCR1_PLL1FD_BIT) +#define __cpm_plcr1_rd() \ + ((REG_CPM_PLCR1 & CPM_PLCR1_PLL1RD_MASK) >> CPM_PLCR1_PLL1RD_BIT) +#define __cpm_plcr1_od() \ + ((REG_CPM_PLCR1 & CPM_PLCR1_PLL1OD_MASK) >> CPM_PLCR1_PLL1OD_BIT) +#define __cpm_cfcr_mfr() \ + ((REG_CPM_CFCR & CPM_CFCR_MFR_MASK) >> CPM_CFCR_MFR_BIT) +#define __cpm_cfcr_pfr() \ + ((REG_CPM_CFCR & CPM_CFCR_PFR_MASK) >> CPM_CFCR_PFR_BIT) +#define __cpm_cfcr_sfr() \ + ((REG_CPM_CFCR & CPM_CFCR_SFR_MASK) >> CPM_CFCR_SFR_BIT) +#define __cpm_cfcr_ifr() \ + ((REG_CPM_CFCR & CPM_CFCR_IFR_MASK) >> CPM_CFCR_IFR_BIT) + +static __inline__ unsigned int __cpm_divisor_encode(unsigned int n) +{ + unsigned int encode[10] = {1,2,3,4,6,8,12,16,24,32}; + int i; + for (i=0;i<10;i++) + if (n < encode[i]) + break; + return i; +} + +#define __cpm_set_mclk_div(n) \ +do { \ + REG_CPM_CFCR = (REG_CPM_CFCR & ~CPM_CFCR_MFR_MASK) | \ + ((n) << (CPM_CFCR_MFR_BIT)); \ +} while (0) + +#define __cpm_set_pclk_div(n) \ +do { \ + REG_CPM_CFCR = (REG_CPM_CFCR & ~CPM_CFCR_PFR_MASK) | \ + ((n) << (CPM_CFCR_PFR_BIT)); \ +} while (0) + +#define __cpm_set_sclk_div(n) \ +do { \ + REG_CPM_CFCR = (REG_CPM_CFCR & ~CPM_CFCR_SFR_MASK) | \ + ((n) << (CPM_CFCR_SFR_BIT)); \ +} while (0) + +#define __cpm_set_iclk_div(n) \ +do { \ + REG_CPM_CFCR = (REG_CPM_CFCR & ~CPM_CFCR_IFR_MASK) | \ + ((n) << (CPM_CFCR_IFR_BIT)); \ +} while (0) + +#define __cpm_set_lcdclk_div(n) \ +do { \ + REG_CPM_CFCR = (REG_CPM_CFCR & ~CPM_CFCR_LFR_MASK) | \ + ((n) << (CPM_CFCR_LFR_BIT)); \ +} while (0) + +#define __cpm_enable_cko1() (REG_CPM_CFCR |= CPM_CFCR_CKOEN1) +#define __cpm_enable_cko2() (REG_CPM_CFCR |= CPM_CFCR_CKOEN2) +#define __cpm_disable_cko1() (REG_CPM_CFCR &= ~CPM_CFCR_CKOEN1) +#define __cpm_disable_cko2() (REG_CPM_CFCR &= ~CPM_CFCR_CKOEN2) + +#define __cpm_select_msc_clk(type) \ +do { \ + if (type == 0) \ + REG_CPM_CFCR &= ~CPM_CFCR_MSC; \ + else \ + REG_CPM_CFCR |= CPM_CFCR_MSC; \ + REG_CPM_CFCR |= CPM_CFCR_UPE; \ +} while(0) + +#define __cpm_idle_mode() \ + (REG_CPM_LPCR = (REG_CPM_LPCR & ~CPM_LPCR_LPM_MASK) | \ + CPM_LPCR_LPM_IDLE) +#define __cpm_sleep_mode() \ + (REG_CPM_LPCR = (REG_CPM_LPCR & ~CPM_LPCR_LPM_MASK) | \ + CPM_LPCR_LPM_SLEEP) +#define __cpm_hibernate_mode() \ + (REG_CPM_LPCR = (REG_CPM_LPCR & ~CPM_LPCR_LPM_MASK) | \ + CPM_LPCR_LPM_HIBERNATE) + +#define __cpm_start_uart0() \ + (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_UART0)) +#define __cpm_start_uart1() \ + (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_UART1)) +#define __cpm_start_uart2() \ + (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_UART2)) +#define __cpm_start_uart3() \ + (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_UART3)) +#define __cpm_start_ost() \ + (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_OST)) +#define __cpm_start_dmac() \ + (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_DMAC)) +#define __cpm_start_uhc() \ + (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_UHC)) +#define __cpm_start_lcd() \ + (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_LCD)) +#define __cpm_start_i2c() \ + (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_I2C)) +#define __cpm_start_aic_pclk() \ + (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_AICPCLK)) +#define __cpm_start_aic_bitclk() \ + (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_AICBCLK)) +#define __cpm_start_pwm0() \ + (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_PWM0)) +#define __cpm_start_pwm1() \ + (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_PWM1)) +#define __cpm_start_ssi() \ + (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_SSI)) +#define __cpm_start_msc() \ + (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_MSC)) +#define __cpm_start_scc() \ + (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_SCC)) +#define __cpm_start_eth() \ + (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_ETH)) +#define __cpm_start_kbc() \ + (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_KBC)) +#define __cpm_start_cim() \ + (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_CIM)) +#define __cpm_start_udc() \ + (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_UDC)) +#define __cpm_start_uprt() \ + (REG_CPM_MSCR &= ~(1 << CPM_MSCR_MSTP_UPRT)) +#define __cpm_start_all() (REG_CPM_MSCR = 0) + +#define __cpm_stop_uart0() \ + (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_UART0)) +#define __cpm_stop_uart1() \ + (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_UART1)) +#define __cpm_stop_uart2() \ + (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_UART2)) +#define __cpm_stop_uart3() \ + (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_UART3)) +#define __cpm_stop_ost() \ + (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_OST)) +#define __cpm_stop_dmac() \ + (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_DMAC)) +#define __cpm_stop_uhc() \ + (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_UHC)) +#define __cpm_stop_lcd() \ + (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_LCD)) +#define __cpm_stop_i2c() \ + (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_I2C)) +#define __cpm_stop_aic_pclk() \ + (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_AICPCLK)) +#define __cpm_stop_aic_bitclk() \ + (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_AICBCLK)) +#define __cpm_stop_pwm0() \ + (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_PWM0)) +#define __cpm_stop_pwm1() \ + (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_PWM1)) +#define __cpm_stop_ssi() \ + (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_SSI)) +#define __cpm_stop_msc() \ + (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_MSC)) +#define __cpm_stop_scc() \ + (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_SCC)) +#define __cpm_stop_eth() \ + (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_ETH)) +#define __cpm_stop_kbc() \ + (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_KBC)) +#define __cpm_stop_cim() \ + (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_CIM)) +#define __cpm_stop_udc() \ + (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_UDC)) +#define __cpm_stop_uprt() \ + (REG_CPM_MSCR |= (1 << CPM_MSCR_MSTP_UPRT)) +#define __cpm_stop_all() (REG_CPM_MSCR = 0xffffffff) + +#define __cpm_set_pin(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + if (p == 0) \ + REG_CPM_GSR0 |= (1 << o); \ + else if (p == 1) \ + REG_CPM_GSR1 |= (1 << o); \ + else if (p == 2) \ + REG_CPM_GSR2 |= (1 << o); \ + else if (p == 3) \ + REG_CPM_GSR3 |= (1 << o); \ +} while (0) + +#define __cpm_clear_pin(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + if (p == 0) \ + REG_CPM_GSR0 &= ~(1 << o); \ + else if (p == 1) \ + REG_CPM_GSR1 &= ~(1 << o); \ + else if (p == 2) \ + REG_CPM_GSR2 &= ~(1 << o); \ + else if (p == 3) \ + REG_CPM_GSR3 &= ~(1 << o); \ +} while (0) + + +/*************************************************************************** + * SSI + ***************************************************************************/ + +#define __ssi_enable() ( REG_SSI_CR0 |= SSI_CR0_SSIE ) +#define __ssi_disable() ( REG_SSI_CR0 &= ~SSI_CR0_SSIE ) +#define __ssi_select_ce() ( REG_SSI_CR0 &= ~SSI_CR0_FSEL ) + +#define __ssi_normal_mode() ( REG_SSI_ITR &= ~SSI_ITR_IVLTM_MASK ) + +#define __ssi_select_ce2() \ +do { \ + REG_SSI_CR0 |= SSI_CR0_FSEL; \ + REG_SSI_CR1 &= ~SSI_CR1_MULTS; \ +} while (0) + +#define __ssi_select_gpc() \ +do { \ + REG_SSI_CR0 &= ~SSI_CR0_FSEL; \ + REG_SSI_CR1 |= SSI_CR1_MULTS; \ +} while (0) + +#define __ssi_enable_tx_intr() \ + ( REG_SSI_CR0 |= SSI_CR0_TIE | SSI_CR0_TEIE ) + +#define __ssi_disable_tx_intr() \ + ( REG_SSI_CR0 &= ~(SSI_CR0_TIE | SSI_CR0_TEIE) ) + +#define __ssi_enable_rx_intr() \ + ( REG_SSI_CR0 |= SSI_CR0_RIE | SSI_CR0_REIE ) + +#define __ssi_disable_rx_intr() \ + ( REG_SSI_CR0 &= ~(SSI_CR0_RIE | SSI_CR0_REIE) ) + +#define __ssi_enable_loopback() ( REG_SSI_CR0 |= SSI_CR0_LOOP ) +#define __ssi_disable_loopback() ( REG_SSI_CR0 &= ~SSI_CR0_LOOP ) + +#define __ssi_enable_receive() ( REG_SSI_CR0 &= ~SSI_CR0_DISREV ) +#define __ssi_disable_receive() ( REG_SSI_CR0 |= SSI_CR0_DISREV ) + +#define __ssi_finish_receive() \ + ( REG_SSI_CR0 |= (SSI_CR0_RFINE | SSI_CR0_RFINC) ) + +#define __ssi_disable_recvfinish() \ + ( REG_SSI_CR0 &= ~(SSI_CR0_RFINE | SSI_CR0_RFINC) ) + +#define __ssi_flush_txfifo() ( REG_SSI_CR0 |= SSI_CR0_TFLUSH ) +#define __ssi_flush_rxfifo() ( REG_SSI_CR0 |= SSI_CR0_RFLUSH ) + +#define __ssi_flush_fifo() \ + ( REG_SSI_CR0 |= SSI_CR0_TFLUSH | SSI_CR0_RFLUSH ) + +#define __ssi_finish_transmit() ( REG_SSI_CR1 &= ~SSI_CR1_UNFIN ) + +#define __ssi_spi_format() \ +do { \ + REG_SSI_CR1 &= ~SSI_CR1_FMAT_MASK; \ + REG_SSI_CR1 |= SSI_CR1_FMAT_SPI; \ + REG_SSI_CR1 &= ~(SSI_CR1_TFVCK_MASK|SSI_CR1_TCKFI_MASK);\ + REG_SSI_CR1 |= (SSI_CR1_TFVCK_1 | SSI_CR1_TCKFI_1); \ +} while (0) + +/* TI's SSP format, must clear SSI_CR1.UNFIN */ +#define __ssi_ssp_format() \ +do { \ + REG_SSI_CR1 &= ~(SSI_CR1_FMAT_MASK | SSI_CR1_UNFIN); \ + REG_SSI_CR1 |= SSI_CR1_FMAT_SSP; \ +} while (0) + +/* National's Microwire format, must clear SSI_CR0.RFINE, and set max delay */ +#define __ssi_microwire_format() \ +do { \ + REG_SSI_CR1 &= ~SSI_CR1_FMAT_MASK; \ + REG_SSI_CR1 |= SSI_CR1_FMAT_MW1; \ + REG_SSI_CR1 &= ~(SSI_CR1_TFVCK_MASK|SSI_CR1_TCKFI_MASK);\ + REG_SSI_CR1 |= (SSI_CR1_TFVCK_3 | SSI_CR1_TCKFI_3); \ + REG_SSI_CR0 &= ~SSI_CR0_RFINE; \ +} while (0) + +/* CE# level (FRMHL), CE# in interval time (ITFRM), + clock phase and polarity (PHA POL), + interval time (SSIITR), interval characters/frame (SSIICR) */ + + /* frmhl,endian,mcom,flen,pha,pol MASK */ +#define SSICR1_MISC_MASK \ + ( SSI_CR1_FRMHL_MASK | SSI_CR1_LFST | SSI_CR1_MCOM_MASK \ + | SSI_CR1_FLEN_MASK | SSI_CR1_PHA | SSI_CR1_POL ) \ + +#define __ssi_spi_set_misc(frmhl,endian,flen,mcom,pha,pol) \ +do { \ + REG_SSI_CR1 &= ~SSICR1_MISC_MASK; \ + REG_SSI_CR1 |= ((frmhl) << 30) | ((endian) << 25) | \ + (((mcom) - 1) << 12) | (((flen) - 2) << 4) | \ + ((pha) << 1) | (pol); \ +} while(0) + +/* Transfer with MSB or LSB first */ +#define __ssi_set_msb() ( REG_SSI_CR1 &= ~SSI_CR1_LFST ) +#define __ssi_set_lsb() ( REG_SSI_CR1 |= SSI_CR1_LFST ) + +#define __ssi_set_frame_length(n) \ + REG_SSI_CR1 = (REG_SSI_CR1 & ~SSI_CR1_FLEN_MASK) | (((n) - 2) << 4) + +/* n = 1 - 16 */ +#define __ssi_set_microwire_command_length(n) \ + ( REG_SSI_CR1 = ((REG_SSI_CR1 & ~SSI_CR1_MCOM_MASK) | SSI_CR1_MCOM_##n##BIT) ) + +/* Set the clock phase for SPI */ +#define __ssi_set_spi_clock_phase(n) \ + ( REG_SSI_CR1 = ((REG_SSI_CR1 & ~SSI_CR1_PHA) | ((n&0x1) << 1 ))) + +/* Set the clock polarity for SPI */ +#define __ssi_set_spi_clock_polarity(n) \ + ( REG_SSI_CR1 = ((REG_SSI_CR1 & ~SSI_CR1_POL) | ((n&0x1) << 0 ))) + +/* n = 1,4,8,14 */ +#define __ssi_set_tx_trigger(n) \ +do { \ + REG_SSI_CR1 &= ~SSI_CR1_TTRG_MASK; \ + REG_SSI_CR1 |= SSI_CR1_TTRG_##n; \ +} while (0) + +/* n = 1,4,8,14 */ +#define __ssi_set_rx_trigger(n) \ +do { \ + REG_SSI_CR1 &= ~SSI_CR1_RTRG_MASK; \ + REG_SSI_CR1 |= SSI_CR1_RTRG_##n; \ +} while (0) + +#define __ssi_get_txfifo_count() \ + ( (REG_SSI_SR & SSI_SR_TFIFONUM_MASK) >> SSI_SR_TFIFONUM_BIT ) + +#define __ssi_get_rxfifo_count() \ + ( (REG_SSI_SR & SSI_SR_RFIFONUM_MASK) >> SSI_SR_RFIFONUM_BIT ) + +#define __ssi_clear_errors() \ + ( REG_SSI_SR &= ~(SSI_SR_UNDR | SSI_SR_OVER) ) + +#define __ssi_transfer_end() ( REG_SSI_SR & SSI_SR_END ) +#define __ssi_is_busy() ( REG_SSI_SR & SSI_SR_BUSY ) + +#define __ssi_txfifo_full() ( REG_SSI_SR & SSI_SR_TFF ) +#define __ssi_rxfifo_empty() ( REG_SSI_SR & SSI_SR_RFE ) +#define __ssi_rxfifo_noempty() ( REG_SSI_SR & SSI_SR_RFHF ) +#define __ssi_rxfifo_half_full() ( REG_SSI_SR & SSI_SR_RFHF ) +#define __ssi_txfifo_half_empty() ( REG_SSI_SR & SSI_SR_TFHE ) +#define __ssi_underrun() ( REG_SSI_SR & SSI_SR_UNDR ) +#define __ssi_overrun() ( REG_SSI_SR & SSI_SR_OVER ) + +#define __ssi_set_clk(dev_clk, ssi_clk) \ + ( REG_SSI_GR = (dev_clk) / (2*(ssi_clk)) - 1 ) + +#define __ssi_receive_data() REG_SSI_DR +#define __ssi_transmit_data(v) ( REG_SSI_DR = (v) ) + +#endif /* __ASM_JZ4730_OPS_H__ */ diff --git a/include/asm-mips/mach-jz4730/regs.h b/include/asm-mips/mach-jz4730/regs.h new file mode 100644 index 0000000..86bde91 --- /dev/null +++ b/include/asm-mips/mach-jz4730/regs.h @@ -0,0 +1,2550 @@ +/* + * linux/include/asm-mips/mach-jz4730/regs.h + * + * JZ4730 registers definition. + * + * Copyright (C) 2006 - 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 __ASM_JZ4730_REGS_H__ +#define __ASM_JZ4730_REGS_H__ + +#if defined(__ASSEMBLY__) || defined(__LANGUAGE_ASSEMBLY) +#define REG8(addr) (addr) +#define REG16(addr) (addr) +#define REG32(addr) (addr) +#else +#define REG8(addr) *((volatile unsigned char *)(addr)) +#define REG16(addr) *((volatile unsigned short *)(addr)) +#define REG32(addr) *((volatile unsigned int *)(addr)) +#endif + +#define HARB_BASE 0xB3000000 +#define EMC_BASE 0xB3010000 +#define DMAC_BASE 0xB3020000 +#define UHC_BASE 0xB3030000 +#define UDC_BASE 0xB3040000 +#define LCD_BASE 0xB3050000 +#define CIM_BASE 0xB3060000 +#define ETH_BASE 0xB3100000 +#define NBM_BASE 0xB3F00000 + +#define CPM_BASE 0xB0000000 +#define INTC_BASE 0xB0001000 +#define OST_BASE 0xB0002000 +#define RTC_BASE 0xB0003000 +#define WDT_BASE 0xB0004000 +#define GPIO_BASE 0xB0010000 +#define AIC_BASE 0xB0020000 +#define MSC_BASE 0xB0021000 +#define UART0_BASE 0xB0030000 +#define UART1_BASE 0xB0031000 +#define UART2_BASE 0xB0032000 +#define UART3_BASE 0xB0033000 +#define FIR_BASE 0xB0040000 +#define SCC_BASE 0xB0041000 +#define SCC0_BASE 0xB0041000 +#define I2C_BASE 0xB0042000 +#define SSI_BASE 0xB0043000 +#define SCC1_BASE 0xB0044000 +#define PWM0_BASE 0xB0050000 +#define PWM1_BASE 0xB0051000 +#define DES_BASE 0xB0060000 +#define UPRT_BASE 0xB0061000 +#define KBC_BASE 0xB0062000 + + + + +/************************************************************************* + * MSC + *************************************************************************/ +#define MSC_STRPCL (MSC_BASE + 0x000) +#define MSC_STAT (MSC_BASE + 0x004) +#define MSC_CLKRT (MSC_BASE + 0x008) +#define MSC_CMDAT (MSC_BASE + 0x00C) +#define MSC_RESTO (MSC_BASE + 0x010) +#define MSC_RDTO (MSC_BASE + 0x014) +#define MSC_BLKLEN (MSC_BASE + 0x018) +#define MSC_NOB (MSC_BASE + 0x01C) +#define MSC_SNOB (MSC_BASE + 0x020) +#define MSC_IMASK (MSC_BASE + 0x024) +#define MSC_IREG (MSC_BASE + 0x028) +#define MSC_CMD (MSC_BASE + 0x02C) +#define MSC_ARG (MSC_BASE + 0x030) +#define MSC_RES (MSC_BASE + 0x034) +#define MSC_RXFIFO (MSC_BASE + 0x038) +#define MSC_TXFIFO (MSC_BASE + 0x03C) + +#define REG_MSC_STRPCL REG16(MSC_STRPCL) +#define REG_MSC_STAT REG32(MSC_STAT) +#define REG_MSC_CLKRT REG16(MSC_CLKRT) +#define REG_MSC_CMDAT REG32(MSC_CMDAT) +#define REG_MSC_RESTO REG16(MSC_RESTO) +#define REG_MSC_RDTO REG16(MSC_RDTO) +#define REG_MSC_BLKLEN REG16(MSC_BLKLEN) +#define REG_MSC_NOB REG16(MSC_NOB) +#define REG_MSC_SNOB REG16(MSC_SNOB) +#define REG_MSC_IMASK REG16(MSC_IMASK) +#define REG_MSC_IREG REG16(MSC_IREG) +#define REG_MSC_CMD REG8(MSC_CMD) +#define REG_MSC_ARG REG32(MSC_ARG) +#define REG_MSC_RES REG16(MSC_RES) +#define REG_MSC_RXFIFO REG32(MSC_RXFIFO) +#define REG_MSC_TXFIFO REG32(MSC_TXFIFO) + +/* MSC Clock and Control Register (MSC_STRPCL) */ + +#define MSC_STRPCL_EXIT_MULTIPLE (1 << 7) +#define MSC_STRPCL_EXIT_TRANSFER (1 << 6) +#define MSC_STRPCL_START_READWAIT (1 << 5) +#define MSC_STRPCL_STOP_READWAIT (1 << 4) +#define MSC_STRPCL_RESET (1 << 3) +#define MSC_STRPCL_START_OP (1 << 2) +#define MSC_STRPCL_CLOCK_CONTROL_BIT 0 +#define MSC_STRPCL_CLOCK_CONTROL_MASK (0x3 << MSC_STRPCL_CLOCK_CONTROL_BIT) + #define MSC_STRPCL_CLOCK_CONTROL_STOP (0x1 << MSC_STRPCL_CLOCK_CONTROL_BIT) /* Stop MMC/SD clock */ + #define MSC_STRPCL_CLOCK_CONTROL_START (0x2 << MSC_STRPCL_CLOCK_CONTROL_BIT) /* Start MMC/SD clock */ + +/* MSC Status Register (MSC_STAT) */ + +#define MSC_STAT_IS_RESETTING (1 << 15) +#define MSC_STAT_SDIO_INT_ACTIVE (1 << 14) +#define MSC_STAT_PRG_DONE (1 << 13) +#define MSC_STAT_DATA_TRAN_DONE (1 << 12) +#define MSC_STAT_END_CMD_RES (1 << 11) +#define MSC_STAT_DATA_FIFO_AFULL (1 << 10) +#define MSC_STAT_IS_READWAIT (1 << 9) +#define MSC_STAT_CLK_EN (1 << 8) +#define MSC_STAT_DATA_FIFO_FULL (1 << 7) +#define MSC_STAT_DATA_FIFO_EMPTY (1 << 6) +#define MSC_STAT_CRC_RES_ERR (1 << 5) +#define MSC_STAT_CRC_READ_ERROR (1 << 4) +#define MSC_STAT_CRC_WRITE_ERROR_BIT 2 +#define MSC_STAT_CRC_WRITE_ERROR_MASK (0x3 << MSC_STAT_CRC_WRITE_ERROR_BIT) + #define MSC_STAT_CRC_WRITE_ERROR_NO (0 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* No error on transmission of data */ + #define MSC_STAT_CRC_WRITE_ERROR (1 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* Card observed erroneous transmission of data */ + #define MSC_STAT_CRC_WRITE_ERROR_NOSTS (2 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* No CRC status is sent back */ +#define MSC_STAT_TIME_OUT_RES (1 << 1) +#define MSC_STAT_TIME_OUT_READ (1 << 0) + +/* MSC Bus Clock Control Register (MSC_CLKRT) */ + +#define MSC_CLKRT_CLK_RATE_BIT 0 +#define MSC_CLKRT_CLK_RATE_MASK (0x7 << MSC_CLKRT_CLK_RATE_BIT) + #define MSC_CLKRT_CLK_RATE_DIV_1 (0x0 << MSC_CLKRT_CLK_RATE_BIT) /* CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_2 (0x1 << MSC_CLKRT_CLK_RATE_BIT) /* 1/2 of CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_4 (0x2 << MSC_CLKRT_CLK_RATE_BIT) /* 1/4 of CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_8 (0x3 << MSC_CLKRT_CLK_RATE_BIT) /* 1/8 of CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_16 (0x4 << MSC_CLKRT_CLK_RATE_BIT) /* 1/16 of CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_32 (0x5 << MSC_CLKRT_CLK_RATE_BIT) /* 1/32 of CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_64 (0x6 << MSC_CLKRT_CLK_RATE_BIT) /* 1/64 of CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_128 (0x7 << MSC_CLKRT_CLK_RATE_BIT) /* 1/128 of CLK_SRC */ + +/* MSC Command Sequence Control Register (MSC_CMDAT) */ + +#define MSC_CMDAT_IO_ABORT (1 << 11) +#define MSC_CMDAT_BUS_WIDTH_BIT 9 +#define MSC_CMDAT_BUS_WIDTH_MASK (0x3 << MSC_CMDAT_BUS_WIDTH_BIT) + #define MSC_CMDAT_BUS_WIDTH_1BIT (0x0 << MSC_CMDAT_BUS_WIDTH_BIT) /* 1-bit data bus */ + #define MSC_CMDAT_BUS_WIDTH_4BIT (0x2 << MSC_CMDAT_BUS_WIDTH_BIT) /* 4-bit data bus */ + #define CMDAT_BUS_WIDTH1 (0x0 << MSC_CMDAT_BUS_WIDTH_BIT) + #define CMDAT_BUS_WIDTH4 (0x2 << MSC_CMDAT_BUS_WIDTH_BIT) +#define MSC_CMDAT_DMA_EN (1 << 8) +#define MSC_CMDAT_INIT (1 << 7) +#define MSC_CMDAT_BUSY (1 << 6) +#define MSC_CMDAT_STREAM_BLOCK (1 << 5) +#define MSC_CMDAT_WRITE (1 << 4) +#define MSC_CMDAT_READ (0 << 4) +#define MSC_CMDAT_DATA_EN (1 << 3) +#define MSC_CMDAT_RESPONSE_BIT 0 +#define MSC_CMDAT_RESPONSE_MASK (0x7 << MSC_CMDAT_RESPONSE_BIT) + #define MSC_CMDAT_RESPONSE_NONE (0x0 << MSC_CMDAT_RESPONSE_BIT) /* No response */ + #define MSC_CMDAT_RESPONSE_R1 (0x1 << MSC_CMDAT_RESPONSE_BIT) /* Format R1 and R1b */ + #define MSC_CMDAT_RESPONSE_R2 (0x2 << MSC_CMDAT_RESPONSE_BIT) /* Format R2 */ + #define MSC_CMDAT_RESPONSE_R3 (0x3 << MSC_CMDAT_RESPONSE_BIT) /* Format R3 */ + #define MSC_CMDAT_RESPONSE_R4 (0x4 << MSC_CMDAT_RESPONSE_BIT) /* Format R4 */ + #define MSC_CMDAT_RESPONSE_R5 (0x5 << MSC_CMDAT_RESPONSE_BIT) /* Format R5 */ + #define MSC_CMDAT_RESPONSE_R6 (0x6 << MSC_CMDAT_RESPONSE_BIT) /* Format R6 */ + +#define CMDAT_DMA_EN (1 << 8) +#define CMDAT_INIT (1 << 7) +#define CMDAT_BUSY (1 << 6) +#define CMDAT_STREAM (1 << 5) +#define CMDAT_WRITE (1 << 4) +#define CMDAT_DATA_EN (1 << 3) + +/* MSC Interrupts Mask Register (MSC_IMASK) */ + +#define MSC_IMASK_SDIO (1 << 7) +#define MSC_IMASK_TXFIFO_WR_REQ (1 << 6) +#define MSC_IMASK_RXFIFO_RD_REQ (1 << 5) +#define MSC_IMASK_END_CMD_RES (1 << 2) +#define MSC_IMASK_PRG_DONE (1 << 1) +#define MSC_IMASK_DATA_TRAN_DONE (1 << 0) + + +/* MSC Interrupts Status Register (MSC_IREG) */ + +#define MSC_IREG_SDIO (1 << 7) +#define MSC_IREG_TXFIFO_WR_REQ (1 << 6) +#define MSC_IREG_RXFIFO_RD_REQ (1 << 5) +#define MSC_IREG_END_CMD_RES (1 << 2) +#define MSC_IREG_PRG_DONE (1 << 1) +#define MSC_IREG_DATA_TRAN_DONE (1 << 0) + + + + +/************************************************************************* + * RTC + *************************************************************************/ +#define RTC_RCR (RTC_BASE + 0x00) +#define RTC_RSR (RTC_BASE + 0x04) +#define RTC_RSAR (RTC_BASE + 0x08) +#define RTC_RGR (RTC_BASE + 0x0c) + +#define REG_RTC_RCR REG32(RTC_RCR) +#define REG_RTC_RSR REG32(RTC_RSR) +#define REG_RTC_RSAR REG32(RTC_RSAR) +#define REG_RTC_RGR REG32(RTC_RGR) + +#define RTC_RCR_HZ (1 << 6) +#define RTC_RCR_HZIE (1 << 5) +#define RTC_RCR_AF (1 << 4) +#define RTC_RCR_AIE (1 << 3) +#define RTC_RCR_AE (1 << 2) +#define RTC_RCR_START (1 << 0) + +#define RTC_RGR_LOCK (1 << 31) +#define RTC_RGR_ADJ_BIT 16 +#define RTC_RGR_ADJ_MASK (0x3ff << RTC_RGR_ADJ_BIT) +#define RTC_RGR_DIV_BIT 0 +#define RTC_REG_DIV_MASK (0xff << RTC_RGR_DIV_BIT) + + + + +/************************************************************************* + * FIR + *************************************************************************/ +#define FIR_TDR (FIR_BASE + 0x000) +#define FIR_RDR (FIR_BASE + 0x004) +#define FIR_TFLR (FIR_BASE + 0x008) +#define FIR_AR (FIR_BASE + 0x00C) +#define FIR_CR1 (FIR_BASE + 0x010) +#define FIR_CR2 (FIR_BASE + 0x014) +#define FIR_SR (FIR_BASE + 0x018) + +#define REG_FIR_TDR REG8(FIR_TDR) +#define REG_FIR_RDR REG8(FIR_RDR) +#define REG_FIR_TFLR REG16(FIR_TFLR) +#define REG_FIR_AR REG8(FIR_AR) +#define REG_FIR_CR1 REG8(FIR_CR1) +#define REG_FIR_CR2 REG16(FIR_CR2) +#define REG_FIR_SR REG16(FIR_SR) + +/* FIR Control Register 1 (FIR_CR1) */ + +#define FIR_CR1_FIRUE (1 << 7) +#define FIR_CR1_ACE (1 << 6) +#define FIR_CR1_EOUS (1 << 5) +#define FIR_CR1_TIIE (1 << 4) +#define FIR_CR1_TFIE (1 << 3) +#define FIR_CR1_RFIE (1 << 2) +#define FIR_CR1_TXE (1 << 1) +#define FIR_CR1_RXE (1 << 0) + +/* FIR Control Register 2 (FIR_CR2) */ + +#define FIR_CR2_SIPE (1 << 10) +#define FIR_CR2_BCRC (1 << 9) +#define FIR_CR2_TFLRS (1 << 8) +#define FIR_CR2_ISS (1 << 7) +#define FIR_CR2_LMS (1 << 6) +#define FIR_CR2_TPPS (1 << 5) +#define FIR_CR2_RPPS (1 << 4) +#define FIR_CR2_TTRG_BIT 2 +#define FIR_CR2_TTRG_MASK (0x3 << FIR_CR2_TTRG_BIT) + #define FIR_CR2_TTRG_16 (0 << FIR_CR2_TTRG_BIT) /* Transmit Trigger Level is 16 */ + #define FIR_CR2_TTRG_32 (1 << FIR_CR2_TTRG_BIT) /* Transmit Trigger Level is 32 */ + #define FIR_CR2_TTRG_64 (2 << FIR_CR2_TTRG_BIT) /* Transmit Trigger Level is 64 */ + #define FIR_CR2_TTRG_128 (3 << FIR_CR2_TTRG_BIT) /* Transmit Trigger Level is 128 */ +#define FIR_CR2_RTRG_BIT 0 +#define FIR_CR2_RTRG_MASK (0x3 << FIR_CR2_RTRG_BIT) + #define FIR_CR2_RTRG_16 (0 << FIR_CR2_RTRG_BIT) /* Receive Trigger Level is 16 */ + #define FIR_CR2_RTRG_32 (1 << FIR_CR2_RTRG_BIT) /* Receive Trigger Level is 32 */ + #define FIR_CR2_RTRG_64 (2 << FIR_CR2_RTRG_BIT) /* Receive Trigger Level is 64 */ + #define FIR_CR2_RTRG_128 (3 << FIR_CR2_RTRG_BIT) /* Receive Trigger Level is 128 */ + +/* FIR Status Register (FIR_SR) */ + +#define FIR_SR_RFW (1 << 12) +#define FIR_SR_RFA (1 << 11) +#define FIR_SR_TFRTL (1 << 10) +#define FIR_SR_RFRTL (1 << 9) +#define FIR_SR_URUN (1 << 8) +#define FIR_SR_RFTE (1 << 7) +#define FIR_SR_ORUN (1 << 6) +#define FIR_SR_CRCE (1 << 5) +#define FIR_SR_FEND (1 << 4) +#define FIR_SR_TFF (1 << 3) +#define FIR_SR_RFE (1 << 2) +#define FIR_SR_TIDLE (1 << 1) +#define FIR_SR_RB (1 << 0) + + + + +/************************************************************************* + * SCC + *************************************************************************/ +#define SCC_DR(base) ((base) + 0x000) +#define SCC_FDR(base) ((base) + 0x004) +#define SCC_CR(base) ((base) + 0x008) +#define SCC1_CR(base) ((base) + 0x008) +#define SCC_SR(base) ((base) + 0x00C) +#define SCC_TFR(base) ((base) + 0x010) +#define SCC_EGTR(base) ((base) + 0x014) +#define SCC_ECR(base) ((base) + 0x018) +#define SCC_RTOR(base) ((base) + 0x01C) + +#define REG_SCC_DR(base) REG8(SCC_DR(base)) +#define REG_SCC_FDR(base) REG8(SCC_FDR(base)) +#define REG_SCC_CR(base) REG32(SCC_CR(base)) +#define REG_SCC1_CR(base) REG32(SCC1_CR(base)) +#define REG_SCC_SR(base) REG16(SCC_SR(base)) +#define REG_SCC_TFR(base) REG16(SCC_TFR(base)) +#define REG_SCC_EGTR(base) REG8(SCC_EGTR(base)) +#define REG_SCC_ECR(base) REG32(SCC_ECR(base)) +#define REG_SCC_RTOR(base) REG8(SCC_RTOR(base)) + +/* SCC FIFO Data Count Register (SCC_FDR) */ + +#define SCC_FDR_EMPTY 0x00 +#define SCC_FDR_FULL 0x10 + +/* SCC Control Register (SCC_CR) */ + +#define SCC_CR_SCCE (1 << 31) +#define SCC_CR_TRS (1 << 30) +#define SCC_CR_T2R (1 << 29) +#define SCC_CR_FDIV_BIT 24 +#define SCC_CR_FDIV_MASK (0x3 << SCC_CR_FDIV_BIT) + #define SCC_CR_FDIV_1 (0 << SCC_CR_FDIV_BIT) /* SCC_CLK frequency is the same as device clock */ + #define SCC_CR_FDIV_2 (1 << SCC_CR_FDIV_BIT) /* SCC_CLK frequency is half of device clock */ +#define SCC_CR_FLUSH (1 << 23) +#define SCC_CR_TRIG_BIT 16 +#define SCC_CR_TRIG_MASK (0x3 << SCC_CR_TRIG_BIT) + #define SCC_CR_TRIG_1 (0 << SCC_CR_TRIG_BIT) /* Receive/Transmit-FIFO Trigger is 1 */ + #define SCC_CR_TRIG_4 (1 << SCC_CR_TRIG_BIT) /* Receive/Transmit-FIFO Trigger is 4 */ + #define SCC_CR_TRIG_8 (2 << SCC_CR_TRIG_BIT) /* Receive/Transmit-FIFO Trigger is 8 */ + #define SCC_CR_TRIG_14 (3 << SCC_CR_TRIG_BIT) /* Receive/Transmit-FIFO Trigger is 14 */ +#define SCC_CR_TP (1 << 15) +#define SCC_CR_CONV (1 << 14) +#define SCC_CR_TXIE (1 << 13) +#define SCC_CR_RXIE (1 << 12) +#define SCC_CR_TENDIE (1 << 11) +#define SCC_CR_RTOIE (1 << 10) +#define SCC_CR_ECIE (1 << 9) +#define SCC_CR_EPIE (1 << 8) +#define SCC_CR_RETIE (1 << 7) +#define SCC_CR_EOIE (1 << 6) +#define SCC_CR_TSEND (1 << 3) +#define SCC_CR_PX_BIT 1 +#define SCC_CR_PX_MASK (0x3 << SCC_CR_PX_BIT) + #define SCC_CR_PX_NOT_SUPPORT (0 << SCC_CR_PX_BIT) /* SCC does not support clock stop */ + #define SCC_CR_PX_STOP_LOW (1 << SCC_CR_PX_BIT) /* SCC_CLK stops at state low */ + #define SCC_CR_PX_STOP_HIGH (2 << SCC_CR_PX_BIT) /* SCC_CLK stops at state high */ +#define SCC_CR_CLKSTP (1 << 0) + +/* SCC Status Register (SCC_SR) */ + +#define SCC_SR_TRANS (1 << 15) +#define SCC_SR_ORER (1 << 12) +#define SCC_SR_RTO (1 << 11) +#define SCC_SR_PER (1 << 10) +#define SCC_SR_TFTG (1 << 9) +#define SCC_SR_RFTG (1 << 8) +#define SCC_SR_TEND (1 << 7) +#define SCC_SR_RETR_3 (1 << 4) +#define SCC_SR_ECNTO (1 << 0) + + + + +/************************************************************************* + * ETH + *************************************************************************/ +#define ETH_BMR (ETH_BASE + 0x1000) +#define ETH_TPDR (ETH_BASE + 0x1004) +#define ETH_RPDR (ETH_BASE + 0x1008) +#define ETH_RAR (ETH_BASE + 0x100C) +#define ETH_TAR (ETH_BASE + 0x1010) +#define ETH_SR (ETH_BASE + 0x1014) +#define ETH_CR (ETH_BASE + 0x1018) +#define ETH_IER (ETH_BASE + 0x101C) +#define ETH_MFCR (ETH_BASE + 0x1020) +#define ETH_CTAR (ETH_BASE + 0x1050) +#define ETH_CRAR (ETH_BASE + 0x1054) +#define ETH_MCR (ETH_BASE + 0x0000) +#define ETH_MAHR (ETH_BASE + 0x0004) +#define ETH_MALR (ETH_BASE + 0x0008) +#define ETH_HTHR (ETH_BASE + 0x000C) +#define ETH_HTLR (ETH_BASE + 0x0010) +#define ETH_MIAR (ETH_BASE + 0x0014) +#define ETH_MIDR (ETH_BASE + 0x0018) +#define ETH_FCR (ETH_BASE + 0x001C) +#define ETH_VTR1 (ETH_BASE + 0x0020) +#define ETH_VTR2 (ETH_BASE + 0x0024) +#define ETH_WKFR (ETH_BASE + 0x0028) +#define ETH_PMTR (ETH_BASE + 0x002C) + +#define REG_ETH_BMR REG32(ETH_BMR) +#define REG_ETH_TPDR REG32(ETH_TPDR) +#define REG_ETH_RPDR REG32(ETH_RPDR) +#define REG_ETH_RAR REG32(ETH_RAR) +#define REG_ETH_TAR REG32(ETH_TAR) +#define REG_ETH_SR REG32(ETH_SR) +#define REG_ETH_CR REG32(ETH_CR) +#define REG_ETH_IER REG32(ETH_IER) +#define REG_ETH_MFCR REG32(ETH_MFCR) +#define REG_ETH_CTAR REG32(ETH_CTAR) +#define REG_ETH_CRAR REG32(ETH_CRAR) +#define REG_ETH_MCR REG32(ETH_MCR) +#define REG_ETH_MAHR REG32(ETH_MAHR) +#define REG_ETH_MALR REG32(ETH_MALR) +#define REG_ETH_HTHR REG32(ETH_HTHR) +#define REG_ETH_HTLR REG32(ETH_HTLR) +#define REG_ETH_MIAR REG32(ETH_MIAR) +#define REG_ETH_MIDR REG32(ETH_MIDR) +#define REG_ETH_FCR REG32(ETH_FCR) +#define REG_ETH_VTR1 REG32(ETH_VTR1) +#define REG_ETH_VTR2 REG32(ETH_VTR2) +#define REG_ETH_WKFR REG32(ETH_WKFR) +#define REG_ETH_PMTR REG32(ETH_PMTR) + +/* Bus Mode Register (ETH_BMR) */ + +#define ETH_BMR_DBO (1 << 20) +#define ETH_BMR_PBL_BIT 8 +#define ETH_BMR_PBL_MASK (0x3f << ETH_BMR_PBL_BIT) + #define ETH_BMR_PBL_1 (0x1 << ETH_BMR_PBL_BIT) + #define ETH_BMR_PBL_4 (0x4 << ETH_BMR_PBL_BIT) +#define ETH_BMR_BLE (1 << 7) +#define ETH_BMR_DSL_BIT 2 +#define ETH_BMR_DSL_MASK (0x1f << ETH_BMR_DSL_BIT) + #define ETH_BMR_DSL_0 (0x0 << ETH_BMR_DSL_BIT) + #define ETH_BMR_DSL_1 (0x1 << ETH_BMR_DSL_BIT) + #define ETH_BMR_DSL_2 (0x2 << ETH_BMR_DSL_BIT) + #define ETH_BMR_DSL_4 (0x4 << ETH_BMR_DSL_BIT) + #define ETH_BMR_DSL_8 (0x8 << ETH_BMR_DSL_BIT) +#define ETH_BMR_SWR (1 << 0) + +/* DMA Status Register (ETH_SR) */ + +#define ETH_SR_EB_BIT 23 +#define ETH_SR_EB_MASK (0x7 << ETH_SR_EB_BIT) + #define ETH_SR_EB_TX_ABORT (0x1 << ETH_SR_EB_BIT) + #define ETH_SR_EB_RX_ABORT (0x2 << ETH_SR_EB_BIT) +#define ETH_SR_TS_BIT 20 +#define ETH_SR_TS_MASK (0x7 << ETH_SR_TS_BIT) + #define ETH_SR_TS_STOP (0x0 << ETH_SR_TS_BIT) + #define ETH_SR_TS_FTD (0x1 << ETH_SR_TS_BIT) + #define ETH_SR_TS_WEOT (0x2 << ETH_SR_TS_BIT) + #define ETH_SR_TS_QDAT (0x3 << ETH_SR_TS_BIT) + #define ETH_SR_TS_SUSPEND (0x6 << ETH_SR_TS_BIT) + #define ETH_SR_TS_CTD (0x7 << ETH_SR_TS_BIT) +#define ETH_SR_RS_BIT 17 +#define ETH_SR_RS_MASK (0x7 << ETH_SR_RS_BIT) + #define ETH_SR_RS_STOP (0x0 << ETH_SR_RS_BIT) + #define ETH_SR_RS_FRD (0x1 << ETH_SR_RS_BIT) + #define ETH_SR_RS_CEOR (0x2 << ETH_SR_RS_BIT) + #define ETH_SR_RS_WRP (0x3 << ETH_SR_RS_BIT) + #define ETH_SR_RS_SUSPEND (0x4 << ETH_SR_RS_BIT) + #define ETH_SR_RS_CRD (0x5 << ETH_SR_RS_BIT) + #define ETH_SR_RS_FCF (0x6 << ETH_SR_RS_BIT) + #define ETH_SR_RS_QRF (0x7 << ETH_SR_RS_BIT) +#define ETH_SR_NIS (1 << 16) +#define ETH_SR_AIS (1 << 15) +#define ETH_SR_ERI (1 << 14) +#define ETH_SR_FBE (1 << 13) +#define ETH_SR_ETI (1 << 10) +#define ETH_SR_RWT (1 << 9) +#define ETH_SR_RPS (1 << 8) +#define ETH_SR_RU (1 << 7) +#define ETH_SR_RI (1 << 6) +#define ETH_SR_UNF (1 << 5) +#define ETH_SR_TJT (1 << 3) +#define ETH_SR_TU (1 << 2) +#define ETH_SR_TPS (1 << 1) +#define ETH_SR_TI (1 << 0) + +/* Control (Operation Mode) Register (ETH_CR) */ + +#define ETH_CR_TTM (1 << 22) +#define ETH_CR_SF (1 << 21) +#define ETH_CR_TR_BIT 14 +#define ETH_CR_TR_MASK (0x3 << ETH_CR_TR_BIT) +#define ETH_CR_ST (1 << 13) +#define ETH_CR_OSF (1 << 2) +#define ETH_CR_SR (1 << 1) + +/* Interrupt Enable Register (ETH_IER) */ + +#define ETH_IER_NI (1 << 16) +#define ETH_IER_AI (1 << 15) +#define ETH_IER_ERE (1 << 14) +#define ETH_IER_FBE (1 << 13) +#define ETH_IER_ET (1 << 10) +#define ETH_IER_RWE (1 << 9) +#define ETH_IER_RS (1 << 8) +#define ETH_IER_RU (1 << 7) +#define ETH_IER_RI (1 << 6) +#define ETH_IER_UN (1 << 5) +#define ETH_IER_TJ (1 << 3) +#define ETH_IER_TU (1 << 2) +#define ETH_IER_TS (1 << 1) +#define ETH_IER_TI (1 << 0) + +/* Missed Frame and Buffer Overflow Counter Register (ETH_MFCR) */ + +#define ETH_MFCR_OVERFLOW_BIT 17 +#define ETH_MFCR_OVERFLOW_MASK (0x7ff << ETH_MFCR_OVERFLOW_BIT) +#define ETH_MFCR_MFC_BIT 0 +#define ETH_MFCR_MFC_MASK (0xffff << ETH_MFCR_MFC_BIT) + +/* MAC Control Register (ETH_MCR) */ + +#define ETH_MCR_RA (1 << 31) +#define ETH_MCR_HBD (1 << 28) +#define ETH_MCR_PS (1 << 27) +#define ETH_MCR_DRO (1 << 23) +#define ETH_MCR_OM_BIT 21 +#define ETH_MCR_OM_MASK (0x3 << ETH_MCR_OM_BIT) + #define ETH_MCR_OM_NORMAL (0x0 << ETH_MCR_OM_BIT) + #define ETH_MCR_OM_INTERNAL (0x1 << ETH_MCR_OM_BIT) + #define ETH_MCR_OM_EXTERNAL (0x2 << ETH_MCR_OM_BIT) +#define ETH_MCR_F (1 << 20) +#define ETH_MCR_PM (1 << 19) +#define ETH_MCR_PR (1 << 18) +#define ETH_MCR_IF (1 << 17) +#define ETH_MCR_PB (1 << 16) +#define ETH_MCR_HO (1 << 15) +#define ETH_MCR_HP (1 << 13) +#define ETH_MCR_LCC (1 << 12) +#define ETH_MCR_DBF (1 << 11) +#define ETH_MCR_DTRY (1 << 10) +#define ETH_MCR_ASTP (1 << 8) +#define ETH_MCR_BOLMT_BIT 6 +#define ETH_MCR_BOLMT_MASK (0x3 << ETH_MCR_BOLMT_BIT) + #define ETH_MCR_BOLMT_10 (0 << ETH_MCR_BOLMT_BIT) + #define ETH_MCR_BOLMT_8 (1 << ETH_MCR_BOLMT_BIT) + #define ETH_MCR_BOLMT_4 (2 << ETH_MCR_BOLMT_BIT) + #define ETH_MCR_BOLMT_1 (3 << ETH_MCR_BOLMT_BIT) +#define ETH_MCR_DC (1 << 5) +#define ETH_MCR_TE (1 << 3) +#define ETH_MCR_RE (1 << 2) + +/* MII Address Register (ETH_MIAR) */ + +#define ETH_MIAR_PHY_ADDR_BIT 11 +#define ETH_MIAR_PHY_ADDR_MASK (0x1f << ETH_MIAR_PHY_ADDR_BIT) +#define ETH_MIAR_MII_REG_BIT 6 +#define ETH_MIAR_MII_REG_MASK (0x1f << ETH_MIAR_MII_REG_BIT) +#define ETH_MIAR_MII_WRITE (1 << 1) +#define ETH_MIAR_MII_BUSY (1 << 0) + +/* Flow Control Register (ETH_FCR) */ + +#define ETH_FCR_PAUSE_TIME_BIT 16 +#define ETH_FCR_PAUSE_TIME_MASK (0xffff << ETH_FCR_PAUSE_TIME_BIT) +#define ETH_FCR_PCF (1 << 2) +#define ETH_FCR_FCE (1 << 1) +#define ETH_FCR_BUSY (1 << 0) + +/* PMT Control and Status Register (ETH_PMTR) */ + +#define ETH_PMTR_GU (1 << 9) +#define ETH_PMTR_RF (1 << 6) +#define ETH_PMTR_MF (1 << 5) +#define ETH_PMTR_RWK (1 << 2) +#define ETH_PMTR_MPK (1 << 1) + +/* Receive Descriptor 0 (ETH_RD0) Bits */ + +#define ETH_RD0_OWN (1 << 31) +#define ETH_RD0_FF (1 << 30) +#define ETH_RD0_FL_BIT 16 +#define ETH_RD0_FL_MASK (0x3fff << ETH_RD0_FL_BIT) +#define ETH_RD0_ES (1 << 15) +#define ETH_RD0_DE (1 << 14) +#define ETH_RD0_LE (1 << 12) +#define ETH_RD0_RF (1 << 11) +#define ETH_RD0_MF (1 << 10) +#define ETH_RD0_FD (1 << 9) +#define ETH_RD0_LD (1 << 8) +#define ETH_RD0_TL (1 << 7) +#define ETH_RD0_CS (1 << 6) +#define ETH_RD0_FT (1 << 5) +#define ETH_RD0_WT (1 << 4) +#define ETH_RD0_ME (1 << 3) +#define ETH_RD0_DB (1 << 2) +#define ETH_RD0_CE (1 << 1) + +/* Receive Descriptor 1 (ETH_RD1) Bits */ + +#define ETH_RD1_RER (1 << 25) +#define ETH_RD1_RCH (1 << 24) +#define ETH_RD1_RBS2_BIT 11 +#define ETH_RD1_RBS2_MASK (0x7ff << ETH_RD1_RBS2_BIT) +#define ETH_RD1_RBS1_BIT 0 +#define ETH_RD1_RBS1_MASK (0x7ff << ETH_RD1_RBS1_BIT) + +/* Transmit Descriptor 0 (ETH_TD0) Bits */ + +#define ETH_TD0_OWN (1 << 31) +#define ETH_TD0_FA (1 << 15) +#define ETH_TD0_LOC (1 << 11) +#define ETH_TD0_NC (1 << 10) +#define ETH_TD0_LC (1 << 9) +#define ETH_TD0_EC (1 << 8) +#define ETH_TD0_HBF (1 << 7) +#define ETH_TD0_CC_BIT 3 +#define ETH_TD0_CC_MASK (0xf << ETH_TD0_CC_BIT) +#define ETH_TD0_ED (1 << 2) +#define ETH_TD0_UF (1 << 1) +#define ETH_TD0_DF (1 << 0) + +/* Transmit Descriptor 1 (ETH_TD1) Bits */ + +#define ETH_TD1_IC (1 << 31) +#define ETH_TD1_LS (1 << 30) +#define ETH_TD1_FS (1 << 29) +#define ETH_TD1_AC (1 << 26) +#define ETH_TD1_TER (1 << 25) +#define ETH_TD1_TCH (1 << 24) +#define ETH_TD1_DPD (1 << 23) +#define ETH_TD1_TBS2_BIT 11 +#define ETH_TD1_TBS2_MASK (0x7ff << ETH_TD1_TBS2_BIT) +#define ETH_TD1_TBS1_BIT 0 +#define ETH_TD1_TBS1_MASK (0x7ff << ETH_TD1_TBS1_BIT) + + + + +/************************************************************************* + * WDT + *************************************************************************/ +#define WDT_WTCSR (WDT_BASE + 0x00) +#define WDT_WTCNT (WDT_BASE + 0x04) + +#define REG_WDT_WTCSR REG8(WDT_WTCSR) +#define REG_WDT_WTCNT REG32(WDT_WTCNT) + +#define WDT_WTCSR_START (1 << 4) + + + + +/************************************************************************* + * OST + *************************************************************************/ +#define OST_TER (OST_BASE + 0x00) +#define OST_TRDR(n) (OST_BASE + 0x10 + ((n) * 0x20)) +#define OST_TCNT(n) (OST_BASE + 0x14 + ((n) * 0x20)) +#define OST_TCSR(n) (OST_BASE + 0x18 + ((n) * 0x20)) +#define OST_TCRB(n) (OST_BASE + 0x1c + ((n) * 0x20)) + +#define REG_OST_TER REG8(OST_TER) +#define REG_OST_TRDR(n) REG32(OST_TRDR((n))) +#define REG_OST_TCNT(n) REG32(OST_TCNT((n))) +#define REG_OST_TCSR(n) REG16(OST_TCSR((n))) +#define REG_OST_TCRB(n) REG32(OST_TCRB((n))) + +#define OST_TCSR_BUSY (1 << 7) +#define OST_TCSR_UF (1 << 6) +#define OST_TCSR_UIE (1 << 5) +#define OST_TCSR_CKS_BIT 0 +#define OST_TCSR_CKS_MASK (0x07 << OST_TCSR_CKS_BIT) + #define OST_TCSR_CKS_PCLK_4 (0 << OST_TCSR_CKS_BIT) + #define OST_TCSR_CKS_PCLK_16 (1 << OST_TCSR_CKS_BIT) + #define OST_TCSR_CKS_PCLK_64 (2 << OST_TCSR_CKS_BIT) + #define OST_TCSR_CKS_PCLK_256 (3 << OST_TCSR_CKS_BIT) + #define OST_TCSR_CKS_RTCCLK (4 << OST_TCSR_CKS_BIT) + #define OST_TCSR_CKS_EXTAL (5 << OST_TCSR_CKS_BIT) + +#define OST_TCSR0 OST_TCSR(0) +#define OST_TCSR1 OST_TCSR(1) +#define OST_TCSR2 OST_TCSR(2) +#define OST_TRDR0 OST_TRDR(0) +#define OST_TRDR1 OST_TRDR(1) +#define OST_TRDR2 OST_TRDR(2) +#define OST_TCNT0 OST_TCNT(0) +#define OST_TCNT1 OST_TCNT(1) +#define OST_TCNT2 OST_TCNT(2) +#define OST_TCRB0 OST_TCRB(0) +#define OST_TCRB1 OST_TCRB(1) +#define OST_TCRB2 OST_TCRB(2) + +/************************************************************************* + * UART + *************************************************************************/ + +#define IRDA_BASE UART0_BASE +#define UART_BASE UART0_BASE +#define UART_OFF 0x1000 + +/* register offset */ +#define OFF_RDR (0x00) /* R 8b H'xx */ +#define OFF_TDR (0x00) /* W 8b H'xx */ +#define OFF_DLLR (0x00) /* RW 8b H'00 */ +#define OFF_DLHR (0x04) /* RW 8b H'00 */ +#define OFF_IER (0x04) /* RW 8b H'00 */ +#define OFF_ISR (0x08) /* R 8b H'01 */ +#define OFF_FCR (0x08) /* W 8b H'00 */ +#define OFF_LCR (0x0C) /* RW 8b H'00 */ +#define OFF_MCR (0x10) /* RW 8b H'00 */ +#define OFF_LSR (0x14) /* R 8b H'00 */ +#define OFF_MSR (0x18) /* R 8b H'00 */ +#define OFF_SPR (0x1C) /* RW 8b H'00 */ +#define OFF_MCR (0x10) /* RW 8b H'00 */ +#define OFF_SIRCR (0x20) /* RW 8b H'00, UART0 */ + +/* register address */ +#define UART0_RDR (UART0_BASE + OFF_RDR) +#define UART0_TDR (UART0_BASE + OFF_TDR) +#define UART0_DLLR (UART0_BASE + OFF_DLLR) +#define UART0_DLHR (UART0_BASE + OFF_DLHR) +#define UART0_IER (UART0_BASE + OFF_IER) +#define UART0_ISR (UART0_BASE + OFF_ISR) +#define UART0_FCR (UART0_BASE + OFF_FCR) +#define UART0_LCR (UART0_BASE + OFF_LCR) +#define UART0_MCR (UART0_BASE + OFF_MCR) +#define UART0_LSR (UART0_BASE + OFF_LSR) +#define UART0_MSR (UART0_BASE + OFF_MSR) +#define UART0_SPR (UART0_BASE + OFF_SPR) +#define UART0_SIRCR (UART0_BASE + OFF_SIRCR) + +#define UART1_RDR (UART1_BASE + OFF_RDR) +#define UART1_TDR (UART1_BASE + OFF_TDR) +#define UART1_DLLR (UART1_BASE + OFF_DLLR) +#define UART1_DLHR (UART1_BASE + OFF_DLHR) +#define UART1_IER (UART1_BASE + OFF_IER) +#define UART1_ISR (UART1_BASE + OFF_ISR) +#define UART1_FCR (UART1_BASE + OFF_FCR) +#define UART1_LCR (UART1_BASE + OFF_LCR) +#define UART1_MCR (UART1_BASE + OFF_MCR) +#define UART1_LSR (UART1_BASE + OFF_LSR) +#define UART1_MSR (UART1_BASE + OFF_MSR) +#define UART1_SPR (UART1_BASE + OFF_SPR) +#define UART1_SIRCR (UART1_BASE + OFF_SIRCR) + +#define UART2_RDR (UART2_BASE + OFF_RDR) +#define UART2_TDR (UART2_BASE + OFF_TDR) +#define UART2_DLLR (UART2_BASE + OFF_DLLR) +#define UART2_DLHR (UART2_BASE + OFF_DLHR) +#define UART2_IER (UART2_BASE + OFF_IER) +#define UART2_ISR (UART2_BASE + OFF_ISR) +#define UART2_FCR (UART2_BASE + OFF_FCR) +#define UART2_LCR (UART2_BASE + OFF_LCR) +#define UART2_MCR (UART2_BASE + OFF_MCR) +#define UART2_LSR (UART2_BASE + OFF_LSR) +#define UART2_MSR (UART2_BASE + OFF_MSR) +#define UART2_SPR (UART2_BASE + OFF_SPR) +#define UART2_SIRCR (UART2_BASE + OFF_SIRCR) + +#define UART3_RDR (UART3_BASE + OFF_RDR) +#define UART3_TDR (UART3_BASE + OFF_TDR) +#define UART3_DLLR (UART3_BASE + OFF_DLLR) +#define UART3_DLHR (UART3_BASE + OFF_DLHR) +#define UART3_IER (UART3_BASE + OFF_IER) +#define UART3_ISR (UART3_BASE + OFF_ISR) +#define UART3_FCR (UART3_BASE + OFF_FCR) +#define UART3_LCR (UART3_BASE + OFF_LCR) +#define UART3_MCR (UART3_BASE + OFF_MCR) +#define UART3_LSR (UART3_BASE + OFF_LSR) +#define UART3_MSR (UART3_BASE + OFF_MSR) +#define UART3_SPR (UART3_BASE + OFF_SPR) +#define UART3_SIRCR (UART3_BASE + OFF_SIRCR) + +/* + * Define macros for UARTIER + * UART Interrupt Enable Register + */ +#define UARTIER_RIE (1 << 0) /* 0: receive fifo "full" interrupt disable */ +#define UARTIER_TIE (1 << 1) /* 0: transmit fifo "empty" interrupt disable */ +#define UARTIER_RLIE (1 << 2) /* 0: receive line status interrupt disable */ +#define UARTIER_MIE (1 << 3) /* 0: modem status interrupt disable */ +#define UARTIER_RTIE (1 << 4) /* 0: receive timeout interrupt disable */ + +/* + * Define macros for UARTISR + * UART Interrupt Status Register + */ +#define UARTISR_IP (1 << 0) /* 0: interrupt is pending 1: no interrupt */ +#define UARTISR_IID (7 << 1) /* Source of Interrupt */ +#define UARTISR_IID_MSI (0 << 1) /* Modem status interrupt */ +#define UARTISR_IID_THRI (1 << 1) /* Transmitter holding register empty */ +#define UARTISR_IID_RDI (2 << 1) /* Receiver data interrupt */ +#define UARTISR_IID_RLSI (3 << 1) /* Receiver line status interrupt */ +#define UARTISR_FFMS (3 << 6) /* FIFO mode select, set when UARTFCR.FE is set to 1 */ +#define UARTISR_FFMS_NO_FIFO (0 << 6) +#define UARTISR_FFMS_FIFO_MODE (3 << 6) + +/* + * Define macros for UARTFCR + * UART FIFO Control Register + */ +#define UARTFCR_FE (1 << 0) /* 0: non-FIFO mode 1: FIFO mode */ +#define UARTFCR_RFLS (1 << 1) /* write 1 to flush receive FIFO */ +#define UARTFCR_TFLS (1 << 2) /* write 1 to flush transmit FIFO */ +#define UARTFCR_DMS (1 << 3) /* 0: disable DMA mode */ +#define UARTFCR_UUE (1 << 4) /* 0: disable UART */ +#define UARTFCR_RTRG (3 << 6) /* Receive FIFO Data Trigger */ +#define UARTFCR_RTRG_1 (0 << 6) +#define UARTFCR_RTRG_4 (1 << 6) +#define UARTFCR_RTRG_8 (2 << 6) +#define UARTFCR_RTRG_15 (3 << 6) + +/* + * Define macros for UARTLCR + * UART Line Control Register + */ +#define UARTLCR_WLEN (3 << 0) /* word length */ +#define UARTLCR_WLEN_5 (0 << 0) +#define UARTLCR_WLEN_6 (1 << 0) +#define UARTLCR_WLEN_7 (2 << 0) +#define UARTLCR_WLEN_8 (3 << 0) +#define UARTLCR_STOP (1 << 2) /* 0: 1 stop bit when word length is 5,6,7,8 + 1: 1.5 stop bits when 5; 2 stop bits when 6,7,8 */ +#define UARTLCR_PE (1 << 3) /* 0: parity disable */ +#define UARTLCR_PROE (1 << 4) /* 0: even parity 1: odd parity */ +#define UARTLCR_SPAR (1 << 5) /* 0: sticky parity disable */ +#define UARTLCR_SBRK (1 << 6) /* write 0 normal, write 1 send break */ +#define UARTLCR_DLAB (1 << 7) /* 0: access UARTRDR/TDR/IER 1: access UARTDLLR/DLHR */ + +/* + * Define macros for UARTLSR + * UART Line Status Register + */ +#define UARTLSR_DR (1 << 0) /* 0: receive FIFO is empty 1: receive data is ready */ +#define UARTLSR_ORER (1 << 1) /* 0: no overrun error */ +#define UARTLSR_PER (1 << 2) /* 0: no parity error */ +#define UARTLSR_FER (1 << 3) /* 0; no framing error */ +#define UARTLSR_BRK (1 << 4) /* 0: no break detected 1: receive a break signal */ +#define UARTLSR_TDRQ (1 << 5) /* 1: transmit FIFO half "empty" */ +#define UARTLSR_TEMT (1 << 6) /* 1: transmit FIFO and shift registers empty */ +#define UARTLSR_RFER (1 << 7) /* 0: no receive error 1: receive error in FIFO mode */ + +/* + * Define macros for UARTMCR + * UART Modem Control Register + */ +#define UARTMCR_DTR (1 << 0) /* 0: DTR_ ouput high */ +#define UARTMCR_RTS (1 << 1) /* 0: RTS_ output high */ +#define UARTMCR_OUT1 (1 << 2) /* 0: UARTMSR.RI is set to 0 and RI_ input high */ +#define UARTMCR_OUT2 (1 << 3) /* 0: UARTMSR.DCD is set to 0 and DCD_ input high */ +#define UARTMCR_LOOP (1 << 4) /* 0: normal 1: loopback mode */ +#define UARTMCR_MCE (1 << 7) /* 0: modem function is disable */ + +/* + * Define macros for UARTMSR + * UART Modem Status Register + */ +#define UARTMSR_DCTS (1 << 0) /* 0: no change on CTS_ pin since last read of UARTMSR */ +#define UARTMSR_DDSR (1 << 1) /* 0: no change on DSR_ pin since last read of UARTMSR */ +#define UARTMSR_DRI (1 << 2) /* 0: no change on RI_ pin since last read of UARTMSR */ +#define UARTMSR_DDCD (1 << 3) /* 0: no change on DCD_ pin since last read of UARTMSR */ +#define UARTMSR_CTS (1 << 4) /* 0: CTS_ pin is high */ +#define UARTMSR_DSR (1 << 5) /* 0: DSR_ pin is high */ +#define UARTMSR_RI (1 << 6) /* 0: RI_ pin is high */ +#define UARTMSR_DCD (1 << 7) /* 0: DCD_ pin is high */ + +/* + * Define macros for SIRCR + * Slow IrDA Control Register + */ +#define SIRCR_TSIRE (1 << 0) /* 0: transmitter is in UART mode 1: IrDA mode */ +#define SIRCR_RSIRE (1 << 1) /* 0: receiver is in UART mode 1: IrDA mode */ +#define SIRCR_TPWS (1 << 2) /* 0: transmit 0 pulse width is 3/16 of bit length + 1: 0 pulse width is 1.6us for 115.2Kbps */ +#define SIRCR_TXPL (1 << 3) /* 0: encoder generates a positive pulse for 0 */ +#define SIRCR_RXPL (1 << 4) /* 0: decoder interprets positive pulse as 0 */ + + + +/************************************************************************* + * INTC + *************************************************************************/ +#define INTC_ISR (INTC_BASE + 0x00) +#define INTC_IMR (INTC_BASE + 0x04) +#define INTC_IMSR (INTC_BASE + 0x08) +#define INTC_IMCR (INTC_BASE + 0x0c) +#define INTC_IPR (INTC_BASE + 0x10) + +#define REG_INTC_ISR REG32(INTC_ISR) +#define REG_INTC_IMR REG32(INTC_IMR) +#define REG_INTC_IMSR REG32(INTC_IMSR) +#define REG_INTC_IMCR REG32(INTC_IMCR) +#define REG_INTC_IPR REG32(INTC_IPR) + +#define IRQ_I2C 1 +#define IRQ_PS2 2 +#define IRQ_UPRT 3 +#define IRQ_CORE 4 +#define IRQ_UART3 6 +#define IRQ_UART2 7 +#define IRQ_UART1 8 +#define IRQ_UART0 9 +#define IRQ_SCC1 10 +#define IRQ_SCC0 11 +#define IRQ_UDC 12 +#define IRQ_UHC 13 +#define IRQ_MSC 14 +#define IRQ_RTC 15 +#define IRQ_FIR 16 +#define IRQ_SSI 17 +#define IRQ_CIM 18 +#define IRQ_ETH 19 +#define IRQ_AIC 20 +#define IRQ_DMAC 21 +#define IRQ_OST2 22 +#define IRQ_OST1 23 +#define IRQ_OST0 24 +#define IRQ_GPIO3 25 +#define IRQ_GPIO2 26 +#define IRQ_GPIO1 27 +#define IRQ_GPIO0 28 +#define IRQ_LCD 30 + + + + +/************************************************************************* + * CIM + *************************************************************************/ +#define CIM_CFG (CIM_BASE + 0x0000) +#define CIM_CTRL (CIM_BASE + 0x0004) +#define CIM_STATE (CIM_BASE + 0x0008) +#define CIM_IID (CIM_BASE + 0x000C) +#define CIM_RXFIFO (CIM_BASE + 0x0010) +#define CIM_DA (CIM_BASE + 0x0020) +#define CIM_FA (CIM_BASE + 0x0024) +#define CIM_FID (CIM_BASE + 0x0028) +#define CIM_CMD (CIM_BASE + 0x002C) + +#define REG_CIM_CFG REG32(CIM_CFG) +#define REG_CIM_CTRL REG32(CIM_CTRL) +#define REG_CIM_STATE REG32(CIM_STATE) +#define REG_CIM_IID REG32(CIM_IID) +#define REG_CIM_RXFIFO REG32(CIM_RXFIFO) +#define REG_CIM_DA REG32(CIM_DA) +#define REG_CIM_FA REG32(CIM_FA) +#define REG_CIM_FID REG32(CIM_FID) +#define REG_CIM_CMD REG32(CIM_CMD) + +/* CIM Configuration Register (CIM_CFG) */ + +#define CIM_CFG_INV_DAT (1 << 15) +#define CIM_CFG_VSP (1 << 14) +#define CIM_CFG_HSP (1 << 13) +#define CIM_CFG_PCP (1 << 12) +#define CIM_CFG_DUMMY_ZERO (1 << 9) +#define CIM_CFG_EXT_VSYNC (1 << 8) +#define CIM_CFG_PACK_BIT 4 +#define CIM_CFG_PACK_MASK (0x7 << CIM_CFG_PACK_BIT) + #define CIM_CFG_PACK_0 (0 << CIM_CFG_PACK_BIT) + #define CIM_CFG_PACK_1 (1 << CIM_CFG_PACK_BIT) + #define CIM_CFG_PACK_2 (2 << CIM_CFG_PACK_BIT) + #define CIM_CFG_PACK_3 (3 << CIM_CFG_PACK_BIT) + #define CIM_CFG_PACK_4 (4 << CIM_CFG_PACK_BIT) + #define CIM_CFG_PACK_5 (5 << CIM_CFG_PACK_BIT) + #define CIM_CFG_PACK_6 (6 << CIM_CFG_PACK_BIT) + #define CIM_CFG_PACK_7 (7 << CIM_CFG_PACK_BIT) +#define CIM_CFG_DSM_BIT 0 +#define CIM_CFG_DSM_MASK (0x3 << CIM_CFG_DSM_BIT) + #define CIM_CFG_DSM_CPM (0 << CIM_CFG_DSM_BIT) /* CCIR656 Progressive Mode */ + #define CIM_CFG_DSM_CIM (1 << CIM_CFG_DSM_BIT) /* CCIR656 Interlace Mode */ + #define CIM_CFG_DSM_GCM (2 << CIM_CFG_DSM_BIT) /* Gated Clock Mode */ + #define CIM_CFG_DSM_NGCM (3 << CIM_CFG_DSM_BIT) /* Non-Gated Clock Mode */ + +/* CIM Control Register (CIM_CTRL) */ + +#define CIM_CTRL_MCLKDIV_BIT 24 +#define CIM_CTRL_MCLKDIV_MASK (0xff << CIM_CTRL_MCLKDIV_BIT) +#define CIM_CTRL_FRC_BIT 16 +#define CIM_CTRL_FRC_MASK (0xf << CIM_CTRL_FRC_BIT) + #define CIM_CTRL_FRC_1 (0x0 << CIM_CTRL_FRC_BIT) /* Sample every frame */ + #define CIM_CTRL_FRC_2 (0x1 << CIM_CTRL_FRC_BIT) /* Sample 1/2 frame */ + #define CIM_CTRL_FRC_3 (0x2 << CIM_CTRL_FRC_BIT) /* Sample 1/3 frame */ + #define CIM_CTRL_FRC_4 (0x3 << CIM_CTRL_FRC_BIT) /* Sample 1/4 frame */ + #define CIM_CTRL_FRC_5 (0x4 << CIM_CTRL_FRC_BIT) /* Sample 1/5 frame */ + #define CIM_CTRL_FRC_6 (0x5 << CIM_CTRL_FRC_BIT) /* Sample 1/6 frame */ + #define CIM_CTRL_FRC_7 (0x6 << CIM_CTRL_FRC_BIT) /* Sample 1/7 frame */ + #define CIM_CTRL_FRC_8 (0x7 << CIM_CTRL_FRC_BIT) /* Sample 1/8 frame */ + #define CIM_CTRL_FRC_9 (0x8 << CIM_CTRL_FRC_BIT) /* Sample 1/9 frame */ + #define CIM_CTRL_FRC_10 (0x9 << CIM_CTRL_FRC_BIT) /* Sample 1/10 frame */ + #define CIM_CTRL_FRC_11 (0xA << CIM_CTRL_FRC_BIT) /* Sample 1/11 frame */ + #define CIM_CTRL_FRC_12 (0xB << CIM_CTRL_FRC_BIT) /* Sample 1/12 frame */ + #define CIM_CTRL_FRC_13 (0xC << CIM_CTRL_FRC_BIT) /* Sample 1/13 frame */ + #define CIM_CTRL_FRC_14 (0xD << CIM_CTRL_FRC_BIT) /* Sample 1/14 frame */ + #define CIM_CTRL_FRC_15 (0xE << CIM_CTRL_FRC_BIT) /* Sample 1/15 frame */ + #define CIM_CTRL_FRC_16 (0xF << CIM_CTRL_FRC_BIT) /* Sample 1/16 frame */ +#define CIM_CTRL_VDDM (1 << 13) +#define CIM_CTRL_DMA_SOFM (1 << 12) +#define CIM_CTRL_DMA_EOFM (1 << 11) +#define CIM_CTRL_DMA_STOPM (1 << 10) +#define CIM_CTRL_RXF_TRIGM (1 << 9) +#define CIM_CTRL_RXF_OFM (1 << 8) +#define CIM_CTRL_RXF_TRIG_BIT 4 +#define CIM_CTRL_RXF_TRIG_MASK (0x7 << CIM_CTRL_RXF_TRIG_BIT) + #define CIM_CTRL_RXF_TRIG_4 (0 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 4 */ + #define CIM_CTRL_RXF_TRIG_8 (1 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 8 */ + #define CIM_CTRL_RXF_TRIG_12 (2 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 12 */ + #define CIM_CTRL_RXF_TRIG_16 (3 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 16 */ + #define CIM_CTRL_RXF_TRIG_20 (4 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 20 */ + #define CIM_CTRL_RXF_TRIG_24 (5 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 24 */ + #define CIM_CTRL_RXF_TRIG_28 (6 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 28 */ + #define CIM_CTRL_RXF_TRIG_32 (7 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 32 */ +#define CIM_CTRL_DMA_EN (1 << 2) +#define CIM_CTRL_RXF_RST (1 << 1) +#define CIM_CTRL_ENA (1 << 0) + +/* CIM State Register (CIM_STATE) */ + +#define CIM_STATE_DMA_SOF (1 << 6) +#define CIM_STATE_DMA_EOF (1 << 5) +#define CIM_STATE_DMA_STOP (1 << 4) +#define CIM_STATE_RXF_OF (1 << 3) +#define CIM_STATE_RXF_TRIG (1 << 2) +#define CIM_STATE_RXF_EMPTY (1 << 1) +#define CIM_STATE_VDD (1 << 0) + +/* CIM DMA Command Register (CIM_CMD) */ + +#define CIM_CMD_SOFINT (1 << 31) +#define CIM_CMD_EOFINT (1 << 30) +#define CIM_CMD_STOP (1 << 28) +#define CIM_CMD_LEN_BIT 0 +#define CIM_CMD_LEN_MASK (0xffffff << CIM_CMD_LEN_BIT) + + + + +/************************************************************************* + * PWM + *************************************************************************/ +#define PWM_CTR(n) (PWM##n##_BASE + 0x000) +#define PWM_PER(n) (PWM##n##_BASE + 0x004) +#define PWM_DUT(n) (PWM##n##_BASE + 0x008) + +#define REG_PWM_CTR(n) REG8(PWM_CTR(n)) +#define REG_PWM_PER(n) REG16(PWM_PER(n)) +#define REG_PWM_DUT(n) REG16(PWM_DUT(n)) + +/* PWM Control Register (PWM_CTR) */ + +#define PWM_CTR_EN (1 << 7) +#define PWM_CTR_SD (1 << 6) +#define PWM_CTR_PRESCALE_BIT 0 +#define PWM_CTR_PRESCALE_MASK (0x3f << PWM_CTR_PRESCALE_BIT) + +/* PWM Period Register (PWM_PER) */ + +#define PWM_PER_PERIOD_BIT 0 +#define PWM_PER_PERIOD_MASK (0x3ff << PWM_PER_PERIOD_BIT) + +/* PWM Duty Register (PWM_DUT) */ + +#define PWM_DUT_FDUTY (1 << 10) +#define PWM_DUT_DUTY_BIT 0 +#define PWM_DUT_DUTY_MASK (0x3ff << PWM_DUT_DUTY_BIT) + + + + +/************************************************************************* + * EMC + *************************************************************************/ +#define EMC_BCR (EMC_BASE + 0x00) +#define EMC_SMCR0 (EMC_BASE + 0x10) +#define EMC_SMCR1 (EMC_BASE + 0x14) +#define EMC_SMCR2 (EMC_BASE + 0x18) +#define EMC_SMCR3 (EMC_BASE + 0x1c) +#define EMC_SMCR4 (EMC_BASE + 0x20) +#define EMC_SMCR5 (EMC_BASE + 0x24) +#define EMC_SMCR6 (EMC_BASE + 0x28) +#define EMC_SMCR7 (EMC_BASE + 0x2c) +#define EMC_SACR0 (EMC_BASE + 0x30) +#define EMC_SACR1 (EMC_BASE + 0x34) +#define EMC_SACR2 (EMC_BASE + 0x38) +#define EMC_SACR3 (EMC_BASE + 0x3c) +#define EMC_SACR4 (EMC_BASE + 0x40) +#define EMC_SACR5 (EMC_BASE + 0x44) +#define EMC_SACR6 (EMC_BASE + 0x48) +#define EMC_SACR7 (EMC_BASE + 0x4c) +#define EMC_NFCSR (EMC_BASE + 0x50) +#define EMC_NFECC (EMC_BASE + 0x54) +#define EMC_PCCR1 (EMC_BASE + 0x60) +#define EMC_PCCR2 (EMC_BASE + 0x64) +#define EMC_PCCR3 (EMC_BASE + 0x68) +#define EMC_PCCR4 (EMC_BASE + 0x6c) +#define EMC_DMCR (EMC_BASE + 0x80) +#define EMC_RTCSR (EMC_BASE + 0x84) +#define EMC_RTCNT (EMC_BASE + 0x88) +#define EMC_RTCOR (EMC_BASE + 0x8c) +#define EMC_DMAR1 (EMC_BASE + 0x90) +#define EMC_DMAR2 (EMC_BASE + 0x94) +#define EMC_DMAR3 (EMC_BASE + 0x98) +#define EMC_DMAR4 (EMC_BASE + 0x9c) + +#define EMC_SDMR0 (EMC_BASE + 0xa000) +#define EMC_SDMR1 (EMC_BASE + 0xb000) +#define EMC_SDMR2 (EMC_BASE + 0xc000) +#define EMC_SDMR3 (EMC_BASE + 0xd000) + +#define REG_EMC_BCR REG32(EMC_BCR) +#define REG_EMC_SMCR0 REG32(EMC_SMCR0) +#define REG_EMC_SMCR1 REG32(EMC_SMCR1) +#define REG_EMC_SMCR2 REG32(EMC_SMCR2) +#define REG_EMC_SMCR3 REG32(EMC_SMCR3) +#define REG_EMC_SMCR4 REG32(EMC_SMCR4) +#define REG_EMC_SMCR5 REG32(EMC_SMCR5) +#define REG_EMC_SMCR6 REG32(EMC_SMCR6) +#define REG_EMC_SMCR7 REG32(EMC_SMCR7) +#define REG_EMC_SACR0 REG32(EMC_SACR0) +#define REG_EMC_SACR1 REG32(EMC_SACR1) +#define REG_EMC_SACR2 REG32(EMC_SACR2) +#define REG_EMC_SACR3 REG32(EMC_SACR3) +#define REG_EMC_SACR4 REG32(EMC_SACR4) +#define REG_EMC_SACR5 REG32(EMC_SACR5) +#define REG_EMC_SACR6 REG32(EMC_SACR6) +#define REG_EMC_SACR7 REG32(EMC_SACR7) +#define REG_EMC_NFCSR REG32(EMC_NFCSR) +#define REG_EMC_NFECC REG32(EMC_NFECC) +#define REG_EMC_DMCR REG32(EMC_DMCR) +#define REG_EMC_RTCSR REG16(EMC_RTCSR) +#define REG_EMC_RTCNT REG16(EMC_RTCNT) +#define REG_EMC_RTCOR REG16(EMC_RTCOR) +#define REG_EMC_DMAR1 REG32(EMC_DMAR1) +#define REG_EMC_DMAR2 REG32(EMC_DMAR2) +#define REG_EMC_DMAR3 REG32(EMC_DMAR3) +#define REG_EMC_DMAR4 REG32(EMC_DMAR4) +#define REG_EMC_PCCR1 REG32(EMC_PCCR1) +#define REG_EMC_PCCR2 REG32(EMC_PCCR2) +#define REG_EMC_PCCR3 REG32(EMC_PCCR3) +#define REG_EMC_PCCR4 REG32(EMC_PCCR4) + + +#define EMC_BCR_BRE (1 << 1) + +#define EMC_SMCR_STRV_BIT 24 +#define EMC_SMCR_STRV_MASK (0x0f << EMC_SMCR_STRV_BIT) +#define EMC_SMCR_TAW_BIT 20 +#define EMC_SMCR_TAW_MASK (0x0f << EMC_SMCR_TAW_BIT) +#define EMC_SMCR_TBP_BIT 16 +#define EMC_SMCR_TBP_MASK (0x0f << EMC_SMCR_TBP_BIT) +#define EMC_SMCR_TAH_BIT 12 +#define EMC_SMCR_TAH_MASK (0x07 << EMC_SMCR_TAH_BIT) +#define EMC_SMCR_TAS_BIT 8 +#define EMC_SMCR_TAS_MASK (0x07 << EMC_SMCR_TAS_BIT) +#define EMC_SMCR_BW_BIT 6 +#define EMC_SMCR_BW_MASK (0x03 << EMC_SMCR_BW_BIT) + #define EMC_SMCR_BW_8BIT (0 << EMC_SMCR_BW_BIT) + #define EMC_SMCR_BW_16BIT (1 << EMC_SMCR_BW_BIT) + #define EMC_SMCR_BW_32BIT (2 << EMC_SMCR_BW_BIT) +#define EMC_SMCR_BCM (1 << 3) +#define EMC_SMCR_BL_BIT 1 +#define EMC_SMCR_BL_MASK (0x03 << EMC_SMCR_BL_BIT) + #define EMC_SMCR_BL_4 (0 << EMC_SMCR_BL_BIT) + #define EMC_SMCR_BL_8 (1 << EMC_SMCR_BL_BIT) + #define EMC_SMCR_BL_16 (2 << EMC_SMCR_BL_BIT) + #define EMC_SMCR_BL_32 (3 << EMC_SMCR_BL_BIT) +#define EMC_SMCR_SMT (1 << 0) + +#define EMC_SACR_BASE_BIT 8 +#define EMC_SACR_BASE_MASK (0xff << EMC_SACR_BASE_BIT) +#define EMC_SACR_MASK_BIT 0 +#define EMC_SACR_MASK_MASK (0xff << EMC_SACR_MASK_BIT) + +#define EMC_NFCSR_RB (1 << 7) +#define EMC_NFCSR_BOOT_SEL_BIT 4 +#define EMC_NFCSR_BOOT_SEL_MASK (0x07 << EMC_NFCSR_BOOT_SEL_BIT) +#define EMC_NFCSR_ERST (1 << 3) +#define EMC_NFCSR_ECCE (1 << 2) +#define EMC_NFCSR_FCE (1 << 1) +#define EMC_NFCSR_NFE (1 << 0) + +#define EMC_NFECC_ECC2_BIT 16 +#define EMC_NFECC_ECC2_MASK (0xff << EMC_NFECC_ECC2_BIT) +#define EMC_NFECC_ECC1_BIT 8 +#define EMC_NFECC_ECC1_MASK (0xff << EMC_NFECC_ECC1_BIT) +#define EMC_NFECC_ECC0_BIT 0 +#define EMC_NFECC_ECC0_MASK (0xff << EMC_NFECC_ECC0_BIT) + +#define EMC_DMCR_BW_BIT 31 +#define EMC_DMCR_BW (1 << EMC_DMCR_BW_BIT) +#define EMC_DMCR_CA_BIT 26 +#define EMC_DMCR_CA_MASK (0x07 << EMC_DMCR_CA_BIT) + #define EMC_DMCR_CA_8 (0 << EMC_DMCR_CA_BIT) + #define EMC_DMCR_CA_9 (1 << EMC_DMCR_CA_BIT) + #define EMC_DMCR_CA_10 (2 << EMC_DMCR_CA_BIT) + #define EMC_DMCR_CA_11 (3 << EMC_DMCR_CA_BIT) + #define EMC_DMCR_CA_12 (4 << EMC_DMCR_CA_BIT) +#define EMC_DMCR_RMODE (1 << 25) +#define EMC_DMCR_RFSH (1 << 24) +#define EMC_DMCR_MRSET (1 << 23) +#define EMC_DMCR_RA_BIT 20 +#define EMC_DMCR_RA_MASK (0x03 << EMC_DMCR_RA_BIT) + #define EMC_DMCR_RA_11 (0 << EMC_DMCR_RA_BIT) + #define EMC_DMCR_RA_12 (1 << EMC_DMCR_RA_BIT) + #define EMC_DMCR_RA_13 (2 << EMC_DMCR_RA_BIT) +#define EMC_DMCR_BA_BIT 19 +#define EMC_DMCR_BA (1 << EMC_DMCR_BA_BIT) +#define EMC_DMCR_PDM (1 << 18) +#define EMC_DMCR_EPIN (1 << 17) +#define EMC_DMCR_TRAS_BIT 13 +#define EMC_DMCR_TRAS_MASK (0x07 << EMC_DMCR_TRAS_BIT) +#define EMC_DMCR_RCD_BIT 11 +#define EMC_DMCR_RCD_MASK (0x03 << EMC_DMCR_RCD_BIT) +#define EMC_DMCR_TPC_BIT 8 +#define EMC_DMCR_TPC_MASK (0x07 << EMC_DMCR_TPC_BIT) +#define EMC_DMCR_TRWL_BIT 5 +#define EMC_DMCR_TRWL_MASK (0x03 << EMC_DMCR_TRWL_BIT) +#define EMC_DMCR_TRC_BIT 2 +#define EMC_DMCR_TRC_MASK (0x07 << EMC_DMCR_TRC_BIT) +#define EMC_DMCR_TCL_BIT 0 +#define EMC_DMCR_TCL_MASK (0x03 << EMC_DMCR_TCL_BIT) + +#define EMC_RTCSR_CMF (1 << 7) +#define EMC_RTCSR_CKS_BIT 0 +#define EMC_RTCSR_CKS_MASK (0x07 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_DISABLE (0 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_4 (1 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_16 (2 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_64 (3 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_256 (4 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_1024 (5 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_2048 (6 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_4096 (7 << EMC_RTCSR_CKS_BIT) + +#define EMC_DMAR_BASE_BIT 8 +#define EMC_DMAR_BASE_MASK (0xff << EMC_DMAR_BASE_BIT) +#define EMC_DMAR_MASK_BIT 0 +#define EMC_DMAR_MASK_MASK (0xff << EMC_DMAR_MASK_BIT) + +#define EMC_SDMR_BM (1 << 9) +#define EMC_SDMR_OM_BIT 7 +#define EMC_SDMR_OM_MASK (3 << EMC_SDMR_OM_BIT) + #define EMC_SDMR_OM_NORMAL (0 << EMC_SDMR_OM_BIT) +#define EMC_SDMR_CAS_BIT 4 +#define EMC_SDMR_CAS_MASK (7 << EMC_SDMR_CAS_BIT) + #define EMC_SDMR_CAS_1 (1 << EMC_SDMR_CAS_BIT) + #define EMC_SDMR_CAS_2 (2 << EMC_SDMR_CAS_BIT) + #define EMC_SDMR_CAS_3 (3 << EMC_SDMR_CAS_BIT) +#define EMC_SDMR_BT_BIT 3 +#define EMC_SDMR_BT_MASK (1 << EMC_SDMR_BT_BIT) + #define EMC_SDMR_BT_SEQ (0 << EMC_SDMR_BT_BIT) + #define EMC_SDMR_BT_INTR (1 << EMC_SDMR_BT_BIT) +#define EMC_SDMR_BL_BIT 0 +#define EMC_SDMR_BL_MASK (7 << EMC_SDMR_BL_BIT) + #define EMC_SDMR_BL_1 (0 << EMC_SDMR_BL_BIT) + #define EMC_SDMR_BL_2 (1 << EMC_SDMR_BL_BIT) + #define EMC_SDMR_BL_4 (2 << EMC_SDMR_BL_BIT) + #define EMC_SDMR_BL_8 (3 << EMC_SDMR_BL_BIT) + +#define EMC_SDMR_CAS2_16BIT \ + (EMC_SDMR_CAS_2 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_2) +#define EMC_SDMR_CAS2_32BIT \ + (EMC_SDMR_CAS_2 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_4) +#define EMC_SDMR_CAS3_16BIT \ + (EMC_SDMR_CAS_3 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_2) +#define EMC_SDMR_CAS3_32BIT \ + (EMC_SDMR_CAS_3 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_4) + +#define EMC_PCCR12_AMW (1 << 31) +#define EMC_PCCR12_AMAS_BIT 28 +#define EMC_PCCR12_AMAS_MASK (0x07 << EMC_PCCR12_AMAS_BIT) +#define EMC_PCCR12_AMAH_BIT 24 +#define EMC_PCCR12_AMAH_MASK (0x07 << EMC_PCCR12_AMAH_BIT) +#define EMC_PCCR12_AMPW_BIT 20 +#define EMC_PCCR12_AMPW_MASK (0x0f << EMC_PCCR12_AMPW_BIT) +#define EMC_PCCR12_AMRT_BIT 16 +#define EMC_PCCR12_AMRT_MASK (0x0f << EMC_PCCR12_AMRT_BIT) +#define EMC_PCCR12_CMW (1 << 15) +#define EMC_PCCR12_CMAS_BIT 12 +#define EMC_PCCR12_CMAS_MASK (0x07 << EMC_PCCR12_CMAS_BIT) +#define EMC_PCCR12_CMAH_BIT 8 +#define EMC_PCCR12_CMAH_MASK (0x07 << EMC_PCCR12_CMAH_BIT) +#define EMC_PCCR12_CMPW_BIT 4 +#define EMC_PCCR12_CMPW_MASK (0x0f << EMC_PCCR12_CMPW_BIT) +#define EMC_PCCR12_CMRT_BIT 0 +#define EMC_PCCR12_CMRT_MASK (0x07 << EMC_PCCR12_CMRT_BIT) + +#define EMC_PCCR34_DRS_BIT 16 +#define EMC_PCCR34_DRS_MASK (0x03 << EMC_PCCR34_DRS_BIT) + #define EMC_PCCR34_DRS_SPKR (1 << EMC_PCCR34_DRS_BIT) + #define EMC_PCCR34_DRS_IOIS16 (2 << EMC_PCCR34_DRS_BIT) + #define EMC_PCCR34_DRS_INPACK (3 << EMC_PCCR34_DRS_BIT) +#define EMC_PCCR34_IOIS16 (1 << 15) +#define EMC_PCCR34_IOW (1 << 14) +#define EMC_PCCR34_TCB_BIT 12 +#define EMC_PCCR34_TCB_MASK (0x03 << EMC_PCCR34_TCB_BIT) +#define EMC_PCCR34_IORT_BIT 8 +#define EMC_PCCR34_IORT_MASK (0x07 << EMC_PCCR34_IORT_BIT) +#define EMC_PCCR34_IOAE_BIT 6 +#define EMC_PCCR34_IOAE_MASK (0x03 << EMC_PCCR34_IOAE_BIT) + #define EMC_PCCR34_IOAE_NONE (0 << EMC_PCCR34_IOAE_BIT) + #define EMC_PCCR34_IOAE_1 (1 << EMC_PCCR34_IOAE_BIT) + #define EMC_PCCR34_IOAE_2 (2 << EMC_PCCR34_IOAE_BIT) + #define EMC_PCCR34_IOAE_5 (3 << EMC_PCCR34_IOAE_BIT) +#define EMC_PCCR34_IOAH_BIT 4 +#define EMC_PCCR34_IOAH_MASK (0x03 << EMC_PCCR34_IOAH_BIT) + #define EMC_PCCR34_IOAH_NONE (0 << EMC_PCCR34_IOAH_BIT) + #define EMC_PCCR34_IOAH_1 (1 << EMC_PCCR34_IOAH_BIT) + #define EMC_PCCR34_IOAH_2 (2 << EMC_PCCR34_IOAH_BIT) + #define EMC_PCCR34_IOAH_5 (3 << EMC_PCCR34_IOAH_BIT) +#define EMC_PCCR34_IOPW_BIT 0 +#define EMC_PCCR34_IOPW_MASK (0x0f << EMC_PCCR34_IOPW_BIT) + + + + +/************************************************************************* + * GPIO + *************************************************************************/ +#define GPIO_GPDR(n) (GPIO_BASE + (0x00 + (n)*0x30)) +#define GPIO_GPDIR(n) (GPIO_BASE + (0x04 + (n)*0x30)) +#define GPIO_GPODR(n) (GPIO_BASE + (0x08 + (n)*0x30)) +#define GPIO_GPPUR(n) (GPIO_BASE + (0x0c + (n)*0x30)) +#define GPIO_GPALR(n) (GPIO_BASE + (0x10 + (n)*0x30)) +#define GPIO_GPAUR(n) (GPIO_BASE + (0x14 + (n)*0x30)) +#define GPIO_GPIDLR(n) (GPIO_BASE + (0x18 + (n)*0x30)) +#define GPIO_GPIDUR(n) (GPIO_BASE + (0x1c + (n)*0x30)) +#define GPIO_GPIER(n) (GPIO_BASE + (0x20 + (n)*0x30)) +#define GPIO_GPIMR(n) (GPIO_BASE + (0x24 + (n)*0x30)) +#define GPIO_GPFR(n) (GPIO_BASE + (0x28 + (n)*0x30)) + +#define REG_GPIO_GPDR(n) REG32(GPIO_GPDR((n))) +#define REG_GPIO_GPDIR(n) REG32(GPIO_GPDIR((n))) +#define REG_GPIO_GPODR(n) REG32(GPIO_GPODR((n))) +#define REG_GPIO_GPPUR(n) REG32(GPIO_GPPUR((n))) +#define REG_GPIO_GPALR(n) REG32(GPIO_GPALR((n))) +#define REG_GPIO_GPAUR(n) REG32(GPIO_GPAUR((n))) +#define REG_GPIO_GPIDLR(n) REG32(GPIO_GPIDLR((n))) +#define REG_GPIO_GPIDUR(n) REG32(GPIO_GPIDUR((n))) +#define REG_GPIO_GPIER(n) REG32(GPIO_GPIER((n))) +#define REG_GPIO_GPIMR(n) REG32(GPIO_GPIMR((n))) +#define REG_GPIO_GPFR(n) REG32(GPIO_GPFR((n))) + +#define GPIO_IRQ_LOLEVEL 0 +#define GPIO_IRQ_HILEVEL 1 +#define GPIO_IRQ_FALLEDG 2 +#define GPIO_IRQ_RAISEDG 3 + +#define IRQ_GPIO_0 48 +#define NUM_GPIO 128 + +#define GPIO_GPDR0 GPIO_GPDR(0) +#define GPIO_GPDR1 GPIO_GPDR(1) +#define GPIO_GPDR2 GPIO_GPDR(2) +#define GPIO_GPDR3 GPIO_GPDR(3) +#define GPIO_GPDIR0 GPIO_GPDIR(0) +#define GPIO_GPDIR1 GPIO_GPDIR(1) +#define GPIO_GPDIR2 GPIO_GPDIR(2) +#define GPIO_GPDIR3 GPIO_GPDIR(3) +#define GPIO_GPODR0 GPIO_GPODR(0) +#define GPIO_GPODR1 GPIO_GPODR(1) +#define GPIO_GPODR2 GPIO_GPODR(2) +#define GPIO_GPODR3 GPIO_GPODR(3) +#define GPIO_GPPUR0 GPIO_GPPUR(0) +#define GPIO_GPPUR1 GPIO_GPPUR(1) +#define GPIO_GPPUR2 GPIO_GPPUR(2) +#define GPIO_GPPUR3 GPIO_GPPUR(3) +#define GPIO_GPALR0 GPIO_GPALR(0) +#define GPIO_GPALR1 GPIO_GPALR(1) +#define GPIO_GPALR2 GPIO_GPALR(2) +#define GPIO_GPALR3 GPIO_GPALR(3) +#define GPIO_GPAUR0 GPIO_GPAUR(0) +#define GPIO_GPAUR1 GPIO_GPAUR(1) +#define GPIO_GPAUR2 GPIO_GPAUR(2) +#define GPIO_GPAUR3 GPIO_GPAUR(3) +#define GPIO_GPIDLR0 GPIO_GPIDLR(0) +#define GPIO_GPIDLR1 GPIO_GPIDLR(1) +#define GPIO_GPIDLR2 GPIO_GPIDLR(2) +#define GPIO_GPIDLR3 GPIO_GPIDLR(3) +#define GPIO_GPIDUR0 GPIO_GPIDUR(0) +#define GPIO_GPIDUR1 GPIO_GPIDUR(1) +#define GPIO_GPIDUR2 GPIO_GPIDUR(2) +#define GPIO_GPIDUR3 GPIO_GPIDUR(3) +#define GPIO_GPIER0 GPIO_GPIER(0) +#define GPIO_GPIER1 GPIO_GPIER(1) +#define GPIO_GPIER2 GPIO_GPIER(2) +#define GPIO_GPIER3 GPIO_GPIER(3) +#define GPIO_GPIMR0 GPIO_GPIMR(0) +#define GPIO_GPIMR1 GPIO_GPIMR(1) +#define GPIO_GPIMR2 GPIO_GPIMR(2) +#define GPIO_GPIMR3 GPIO_GPIMR(3) +#define GPIO_GPFR0 GPIO_GPFR(0) +#define GPIO_GPFR1 GPIO_GPFR(1) +#define GPIO_GPFR2 GPIO_GPFR(2) +#define GPIO_GPFR3 GPIO_GPFR(3) + + +/************************************************************************* + * HARB + *************************************************************************/ +#define HARB_HAPOR (HARB_BASE + 0x000) +#define HARB_HMCTR (HARB_BASE + 0x010) +#define HARB_HME8H (HARB_BASE + 0x014) +#define HARB_HMCR1 (HARB_BASE + 0x018) +#define HARB_HMER2 (HARB_BASE + 0x01C) +#define HARB_HMER3 (HARB_BASE + 0x020) +#define HARB_HMLTR (HARB_BASE + 0x024) + +#define REG_HARB_HAPOR REG32(HARB_HAPOR) +#define REG_HARB_HMCTR REG32(HARB_HMCTR) +#define REG_HARB_HME8H REG32(HARB_HME8H) +#define REG_HARB_HMCR1 REG32(HARB_HMCR1) +#define REG_HARB_HMER2 REG32(HARB_HMER2) +#define REG_HARB_HMER3 REG32(HARB_HMER3) +#define REG_HARB_HMLTR REG32(HARB_HMLTR) + +/* HARB Priority Order Register (HARB_HAPOR) */ + +#define HARB_HAPOR_UCHSEL (1 << 7) +#define HARB_HAPOR_PRIO_BIT 0 +#define HARB_HAPOR_PRIO_MASK (0xf << HARB_HAPOR_PRIO_BIT) + +/* AHB Monitor Control Register (HARB_HMCTR) */ + +#define HARB_HMCTR_HET3_BIT 20 +#define HARB_HMCTR_HET3_MASK (0xf << HARB_HMCTR_HET3_BIT) +#define HARB_HMCTR_HMS3_BIT 16 +#define HARB_HMCTR_HMS3_MASK (0xf << HARB_HMCTR_HMS3_BIT) +#define HARB_HMCTR_HET2_BIT 12 +#define HARB_HMCTR_HET2_MASK (0xf << HARB_HMCTR_HET2_BIT) +#define HARB_HMCTR_HMS2_BIT 8 +#define HARB_HMCTR_HMS2_MASK (0xf << HARB_HMCTR_HMS2_BIT) +#define HARB_HMCTR_HOVF3 (1 << 7) +#define HARB_HMCTR_HOVF2 (1 << 6) +#define HARB_HMCTR_HOVF1 (1 << 5) +#define HARB_HMCTR_HRST (1 << 4) +#define HARB_HMCTR_HEE3 (1 << 2) +#define HARB_HMCTR_HEE2 (1 << 1) +#define HARB_HMCTR_HEE1 (1 << 0) + +/* AHB Monitor Event 8bits High Register (HARB_HME8H) */ + +#define HARB_HME8H_HC8H1_BIT 16 +#define HARB_HME8H_HC8H1_MASK (0xff << HARB_HME8H_HC8H1_BIT) +#define HARB_HME8H_HC8H2_BIT 8 +#define HARB_HME8H_HC8H2_MASK (0xff << HARB_HME8H_HC8H2_BIT) +#define HARB_HME8H_HC8H3_BIT 0 +#define HARB_HME8H_HC8H3_MASK (0xff << HARB_HME8H_HC8H3_BIT) + +/* AHB Monitor Latency Register (HARB_HMLTR) */ + +#define HARB_HMLTR_HLT2_BIT 16 +#define HARB_HMLTR_HLT2_MASK (0xffff << HARB_HMLTR_HLT2_BIT) +#define HARB_HMLTR_HLT3_BIT 0 +#define HARB_HMLTR_HLT3_MASK (0xffff << HARB_HMLTR_HLT3_BIT) + + + + +/************************************************************************* + * I2C + *************************************************************************/ +#define I2C_DR (I2C_BASE + 0x000) +#define I2C_CR (I2C_BASE + 0x004) +#define I2C_SR (I2C_BASE + 0x008) +#define I2C_GR (I2C_BASE + 0x00C) + +#define REG_I2C_DR REG8(I2C_DR) +#define REG_I2C_CR REG8(I2C_CR) +#define REG_I2C_SR REG8(I2C_SR) +#define REG_I2C_GR REG16(I2C_GR) + +/* I2C Control Register (I2C_CR) */ + +#define I2C_CR_IEN (1 << 4) +#define I2C_CR_STA (1 << 3) +#define I2C_CR_STO (1 << 2) +#define I2C_CR_AC (1 << 1) +#define I2C_CR_I2CE (1 << 0) + +/* I2C Status Register (I2C_SR) */ + +#define I2C_SR_STX (1 << 4) +#define I2C_SR_BUSY (1 << 3) +#define I2C_SR_TEND (1 << 2) +#define I2C_SR_DRF (1 << 1) +#define I2C_SR_ACKF (1 << 0) + + + + +/************************************************************************* + * UDC + *************************************************************************/ +#define UDC_EP0InCR (UDC_BASE + 0x00) +#define UDC_EP0InSR (UDC_BASE + 0x04) +#define UDC_EP0InBSR (UDC_BASE + 0x08) +#define UDC_EP0InMPSR (UDC_BASE + 0x0c) +#define UDC_EP0InDesR (UDC_BASE + 0x14) +#define UDC_EP1InCR (UDC_BASE + 0x20) +#define UDC_EP1InSR (UDC_BASE + 0x24) +#define UDC_EP1InBSR (UDC_BASE + 0x28) +#define UDC_EP1InMPSR (UDC_BASE + 0x2c) +#define UDC_EP1InDesR (UDC_BASE + 0x34) +#define UDC_EP2InCR (UDC_BASE + 0x40) +#define UDC_EP2InSR (UDC_BASE + 0x44) +#define UDC_EP2InBSR (UDC_BASE + 0x48) +#define UDC_EP2InMPSR (UDC_BASE + 0x4c) +#define UDC_EP2InDesR (UDC_BASE + 0x54) +#define UDC_EP3InCR (UDC_BASE + 0x60) +#define UDC_EP3InSR (UDC_BASE + 0x64) +#define UDC_EP3InBSR (UDC_BASE + 0x68) +#define UDC_EP3InMPSR (UDC_BASE + 0x6c) +#define UDC_EP3InDesR (UDC_BASE + 0x74) +#define UDC_EP4InCR (UDC_BASE + 0x80) +#define UDC_EP4InSR (UDC_BASE + 0x84) +#define UDC_EP4InBSR (UDC_BASE + 0x88) +#define UDC_EP4InMPSR (UDC_BASE + 0x8c) +#define UDC_EP4InDesR (UDC_BASE + 0x94) + +#define UDC_EP0OutCR (UDC_BASE + 0x200) +#define UDC_EP0OutSR (UDC_BASE + 0x204) +#define UDC_EP0OutPFNR (UDC_BASE + 0x208) +#define UDC_EP0OutMPSR (UDC_BASE + 0x20c) +#define UDC_EP0OutSBPR (UDC_BASE + 0x210) +#define UDC_EP0OutDesR (UDC_BASE + 0x214) +#define UDC_EP5OutCR (UDC_BASE + 0x2a0) +#define UDC_EP5OutSR (UDC_BASE + 0x2a4) +#define UDC_EP5OutPFNR (UDC_BASE + 0x2a8) +#define UDC_EP5OutMPSR (UDC_BASE + 0x2ac) +#define UDC_EP5OutDesR (UDC_BASE + 0x2b4) +#define UDC_EP6OutCR (UDC_BASE + 0x2c0) +#define UDC_EP6OutSR (UDC_BASE + 0x2c4) +#define UDC_EP6OutPFNR (UDC_BASE + 0x2c8) +#define UDC_EP6OutMPSR (UDC_BASE + 0x2cc) +#define UDC_EP6OutDesR (UDC_BASE + 0x2d4) +#define UDC_EP7OutCR (UDC_BASE + 0x2e0) +#define UDC_EP7OutSR (UDC_BASE + 0x2e4) +#define UDC_EP7OutPFNR (UDC_BASE + 0x2e8) +#define UDC_EP7OutMPSR (UDC_BASE + 0x2ec) +#define UDC_EP7OutDesR (UDC_BASE + 0x2f4) + +#define UDC_DevCFGR (UDC_BASE + 0x400) +#define UDC_DevCR (UDC_BASE + 0x404) +#define UDC_DevSR (UDC_BASE + 0x408) +#define UDC_DevIntR (UDC_BASE + 0x40c) +#define UDC_DevIntMR (UDC_BASE + 0x410) +#define UDC_EPIntR (UDC_BASE + 0x414) +#define UDC_EPIntMR (UDC_BASE + 0x418) + +#define UDC_STCMAR (UDC_BASE + 0x500) +#define UDC_EP0InfR (UDC_BASE + 0x504) +#define UDC_EP1InfR (UDC_BASE + 0x508) +#define UDC_EP2InfR (UDC_BASE + 0x50c) +#define UDC_EP3InfR (UDC_BASE + 0x510) +#define UDC_EP4InfR (UDC_BASE + 0x514) +#define UDC_EP5InfR (UDC_BASE + 0x518) +#define UDC_EP6InfR (UDC_BASE + 0x51c) +#define UDC_EP7InfR (UDC_BASE + 0x520) + +#define UDC_TXCONFIRM (UDC_BASE + 0x41C) +#define UDC_TXZLP (UDC_BASE + 0x420) +#define UDC_RXCONFIRM (UDC_BASE + 0x41C) + +#define UDC_RXFIFO (UDC_BASE + 0x800) +#define UDC_TXFIFOEP0 (UDC_BASE + 0x840) + +#define REG_UDC_EP0InCR REG32(UDC_EP0InCR) +#define REG_UDC_EP0InSR REG32(UDC_EP0InSR) +#define REG_UDC_EP0InBSR REG32(UDC_EP0InBSR) +#define REG_UDC_EP0InMPSR REG32(UDC_EP0InMPSR) +#define REG_UDC_EP0InDesR REG32(UDC_EP0InDesR) +#define REG_UDC_EP1InCR REG32(UDC_EP1InCR) +#define REG_UDC_EP1InSR REG32(UDC_EP1InSR) +#define REG_UDC_EP1InBSR REG32(UDC_EP1InBSR) +#define REG_UDC_EP1InMPSR REG32(UDC_EP1InMPSR) +#define REG_UDC_EP1InDesR REG32(UDC_EP1InDesR) +#define REG_UDC_EP2InCR REG32(UDC_EP2InCR) +#define REG_UDC_EP2InSR REG32(UDC_EP2InSR) +#define REG_UDC_EP2InBSR REG32(UDC_EP2InBSR) +#define REG_UDC_EP2InMPSR REG32(UDC_EP2InMPSR) +#define REG_UDC_EP2InDesR REG32(UDC_EP2InDesR) +#define REG_UDC_EP3InCR REG32(UDC_EP3InCR) +#define REG_UDC_EP3InSR REG32(UDC_EP3InSR) +#define REG_UDC_EP3InBSR REG32(UDC_EP3InBSR) +#define REG_UDC_EP3InMPSR REG32(UDC_EP3InMPSR) +#define REG_UDC_EP3InDesR REG32(UDC_EP3InDesR) +#define REG_UDC_EP4InCR REG32(UDC_EP4InCR) +#define REG_UDC_EP4InSR REG32(UDC_EP4InSR) +#define REG_UDC_EP4InBSR REG32(UDC_EP4InBSR) +#define REG_UDC_EP4InMPSR REG32(UDC_EP4InMPSR) +#define REG_UDC_EP4InDesR REG32(UDC_EP4InDesR) + +#define REG_UDC_EP0OutCR REG32(UDC_EP0OutCR) +#define REG_UDC_EP0OutSR REG32(UDC_EP0OutSR) +#define REG_UDC_EP0OutPFNR REG32(UDC_EP0OutPFNR) +#define REG_UDC_EP0OutMPSR REG32(UDC_EP0OutMPSR) +#define REG_UDC_EP0OutSBPR REG32(UDC_EP0OutSBPR) +#define REG_UDC_EP0OutDesR REG32(UDC_EP0OutDesR) +#define REG_UDC_EP5OutCR REG32(UDC_EP5OutCR) +#define REG_UDC_EP5OutSR REG32(UDC_EP5OutSR) +#define REG_UDC_EP5OutPFNR REG32(UDC_EP5OutPFNR) +#define REG_UDC_EP5OutMPSR REG32(UDC_EP5OutMPSR) +#define REG_UDC_EP5OutDesR REG32(UDC_EP5OutDesR) +#define REG_UDC_EP6OutCR REG32(UDC_EP6OutCR) +#define REG_UDC_EP6OutSR REG32(UDC_EP6OutSR) +#define REG_UDC_EP6OutPFNR REG32(UDC_EP6OutPFNR) +#define REG_UDC_EP6OutMPSR REG32(UDC_EP6OutMPSR) +#define REG_UDC_EP6OutDesR REG32(UDC_EP6OutDesR) +#define REG_UDC_EP7OutCR REG32(UDC_EP7OutCR) +#define REG_UDC_EP7OutSR REG32(UDC_EP7OutSR) +#define REG_UDC_EP7OutPFNR REG32(UDC_EP7OutPFNR) +#define REG_UDC_EP7OutMPSR REG32(UDC_EP7OutMPSR) +#define REG_UDC_EP7OutDesR REG32(UDC_EP7OutDesR) + +#define REG_UDC_DevCFGR REG32(UDC_DevCFGR) +#define REG_UDC_DevCR REG32(UDC_DevCR) +#define REG_UDC_DevSR REG32(UDC_DevSR) +#define REG_UDC_DevIntR REG32(UDC_DevIntR) +#define REG_UDC_DevIntMR REG32(UDC_DevIntMR) +#define REG_UDC_EPIntR REG32(UDC_EPIntR) +#define REG_UDC_EPIntMR REG32(UDC_EPIntMR) + +#define REG_UDC_STCMAR REG32(UDC_STCMAR) +#define REG_UDC_EP0InfR REG32(UDC_EP0InfR) +#define REG_UDC_EP1InfR REG32(UDC_EP1InfR) +#define REG_UDC_EP2InfR REG32(UDC_EP2InfR) +#define REG_UDC_EP3InfR REG32(UDC_EP3InfR) +#define REG_UDC_EP4InfR REG32(UDC_EP4InfR) +#define REG_UDC_EP5InfR REG32(UDC_EP5InfR) +#define REG_UDC_EP6InfR REG32(UDC_EP6InfR) +#define REG_UDC_EP7InfR REG32(UDC_EP7InfR) + +#define UDC_DevCFGR_PI (1 << 5) +#define UDC_DevCFGR_SS (1 << 4) +#define UDC_DevCFGR_SP (1 << 3) +#define UDC_DevCFGR_RW (1 << 2) +#define UDC_DevCFGR_SPD_BIT 0 +#define UDC_DevCFGR_SPD_MASK (0x03 << UDC_DevCFGR_SPD_BIT) + #define UDC_DevCFGR_SPD_HS (0 << UDC_DevCFGR_SPD_BIT) + #define UDC_DevCFGR_SPD_LS (2 << UDC_DevCFGR_SPD_BIT) + #define UDC_DevCFGR_SPD_FS (3 << UDC_DevCFGR_SPD_BIT) + +#define UDC_DevCR_DM (1 << 9) +#define UDC_DevCR_BE (1 << 5) +#define UDC_DevCR_RES (1 << 0) + +#define UDC_DevSR_ENUMSPD_BIT 13 +#define UDC_DevSR_ENUMSPD_MASK (0x03 << UDC_DevSR_ENUMSPD_BIT) + #define UDC_DevSR_ENUMSPD_HS (0 << UDC_DevSR_ENUMSPD_BIT) + #define UDC_DevSR_ENUMSPD_LS (2 << UDC_DevSR_ENUMSPD_BIT) + #define UDC_DevSR_ENUMSPD_FS (3 << UDC_DevSR_ENUMSPD_BIT) +#define UDC_DevSR_SUSP (1 << 12) +#define UDC_DevSR_ALT_BIT 8 +#define UDC_DevSR_ALT_MASK (0x0f << UDC_DevSR_ALT_BIT) +#define UDC_DevSR_INTF_BIT 4 +#define UDC_DevSR_INTF_MASK (0x0f << UDC_DevSR_INTF_BIT) +#define UDC_DevSR_CFG_BIT 0 +#define UDC_DevSR_CFG_MASK (0x0f << UDC_DevSR_CFG_BIT) + +#define UDC_DevIntR_ENUM (1 << 6) +#define UDC_DevIntR_SOF (1 << 5) +#define UDC_DevIntR_US (1 << 4) +#define UDC_DevIntR_UR (1 << 3) +#define UDC_DevIntR_SI (1 << 1) +#define UDC_DevIntR_SC (1 << 0) + +#define UDC_EPIntR_OUTEP_BIT 16 +#define UDC_EPIntR_OUTEP_MASK (0xffff << UDC_EPIntR_OUTEP_BIT) +#define UDC_EPIntR_OUTEP0 0x00010000 +#define UDC_EPIntR_OUTEP5 0x00200000 +#define UDC_EPIntR_OUTEP6 0x00400000 +#define UDC_EPIntR_OUTEP7 0x00800000 +#define UDC_EPIntR_INEP_BIT 0 +#define UDC_EPIntR_INEP_MASK (0xffff << UDC_EPIntR_INEP_BIT) +#define UDC_EPIntR_INEP0 0x00000001 +#define UDC_EPIntR_INEP1 0x00000002 +#define UDC_EPIntR_INEP2 0x00000004 +#define UDC_EPIntR_INEP3 0x00000008 +#define UDC_EPIntR_INEP4 0x00000010 + + +#define UDC_EPIntMR_OUTEP_BIT 16 +#define UDC_EPIntMR_OUTEP_MASK (0xffff << UDC_EPIntMR_OUTEP_BIT) +#define UDC_EPIntMR_INEP_BIT 0 +#define UDC_EPIntMR_INEP_MASK (0xffff << UDC_EPIntMR_INEP_BIT) + +#define UDC_EPCR_ET_BIT 4 +#define UDC_EPCR_ET_MASK (0x03 << UDC_EPCR_ET_BIT) + #define UDC_EPCR_ET_CTRL (0 << UDC_EPCR_ET_BIT) + #define UDC_EPCR_ET_ISO (1 << UDC_EPCR_ET_BIT) + #define UDC_EPCR_ET_BULK (2 << UDC_EPCR_ET_BIT) + #define UDC_EPCR_ET_INTR (3 << UDC_EPCR_ET_BIT) +#define UDC_EPCR_SN (1 << 2) +#define UDC_EPCR_F (1 << 1) +#define UDC_EPCR_S (1 << 0) + +#define UDC_EPSR_RXPKTSIZE_BIT 11 +#define UDC_EPSR_RXPKTSIZE_MASK (0x7ff << UDC_EPSR_RXPKTSIZE_BIT) +#define UDC_EPSR_IN (1 << 6) +#define UDC_EPSR_OUT_BIT 4 +#define UDC_EPSR_OUT_MASK (0x03 << UDC_EPSR_OUT_BIT) + #define UDC_EPSR_OUT_NONE (0 << UDC_EPSR_OUT_BIT) + #define UDC_EPSR_OUT_RCVDATA (1 << UDC_EPSR_OUT_BIT) + #define UDC_EPSR_OUT_RCVSETUP (2 << UDC_EPSR_OUT_BIT) +#define UDC_EPSR_PID_BIT 0 +#define UDC_EPSR_PID_MASK (0x0f << UDC_EPSR_PID_BIT) + +#define UDC_EPInfR_MPS_BIT 19 +#define UDC_EPInfR_MPS_MASK (0x3ff << UDC_EPInfR_MPS_BIT) +#define UDC_EPInfR_ALTS_BIT 15 +#define UDC_EPInfR_ALTS_MASK (0x0f << UDC_EPInfR_ALTS_BIT) +#define UDC_EPInfR_IFN_BIT 11 +#define UDC_EPInfR_IFN_MASK (0x0f << UDC_EPInfR_IFN_BIT) +#define UDC_EPInfR_CGN_BIT 7 +#define UDC_EPInfR_CGN_MASK (0x0f << UDC_EPInfR_CGN_BIT) +#define UDC_EPInfR_EPT_BIT 5 +#define UDC_EPInfR_EPT_MASK (0x03 << UDC_EPInfR_EPT_BIT) + #define UDC_EPInfR_EPT_CTRL (0 << UDC_EPInfR_EPT_BIT) + #define UDC_EPInfR_EPT_ISO (1 << UDC_EPInfR_EPT_BIT) + #define UDC_EPInfR_EPT_BULK (2 << UDC_EPInfR_EPT_BIT) + #define UDC_EPInfR_EPT_INTR (3 << UDC_EPInfR_EPT_BIT) +#define UDC_EPInfR_EPD (1 << 4) + #define UDC_EPInfR_EPD_OUT (0 << 4) + #define UDC_EPInfR_EPD_IN (1 << 4) + +#define UDC_EPInfR_EPN_BIT 0 +#define UDC_EPInfR_EPN_MASK (0xf << UDC_EPInfR_EPN_BIT) + + + + +/************************************************************************* + * DMAC + *************************************************************************/ +#define DMAC_DSAR(n) (DMAC_BASE + (0x00 + (n) * 0x20)) +#define DMAC_DDAR(n) (DMAC_BASE + (0x04 + (n) * 0x20)) +#define DMAC_DTCR(n) (DMAC_BASE + (0x08 + (n) * 0x20)) +#define DMAC_DRSR(n) (DMAC_BASE + (0x0c + (n) * 0x20)) +#define DMAC_DCCSR(n) (DMAC_BASE + (0x10 + (n) * 0x20)) +#define DMAC_DMAIPR (DMAC_BASE + 0xf8) +#define DMAC_DMACR (DMAC_BASE + 0xfc) + +#define REG_DMAC_DSAR(n) REG32(DMAC_DSAR((n))) +#define REG_DMAC_DDAR(n) REG32(DMAC_DDAR((n))) +#define REG_DMAC_DTCR(n) REG32(DMAC_DTCR((n))) +#define REG_DMAC_DRSR(n) REG32(DMAC_DRSR((n))) +#define REG_DMAC_DCCSR(n) REG32(DMAC_DCCSR((n))) +#define REG_DMAC_DMAIPR REG32(DMAC_DMAIPR) +#define REG_DMAC_DMACR REG32(DMAC_DMACR) + +#define DMAC_DRSR_RS_BIT 0 +#define DMAC_DRSR_RS_MASK (0x1f << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_EXTREXTR (0 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_PCMCIAOUT (4 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_PCMCIAIN (5 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_AUTO (8 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_DESOUT (10 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_DESIN (11 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_UART3OUT (14 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_UART3IN (15 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_UART2OUT (16 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_UART2IN (17 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_UART1OUT (18 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_UART1IN (19 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_UART0OUT (20 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_UART0IN (21 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_SSIOUT (22 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_SSIIN (23 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_AICOUT (24 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_AICIN (25 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_MSCOUT (26 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_MSCIN (27 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_OST2 (28 << DMAC_DRSR_RS_BIT) + +#define DMAC_DCCSR_EACKS (1 << 31) +#define DMAC_DCCSR_EACKM (1 << 30) +#define DMAC_DCCSR_ERDM_BIT 28 +#define DMAC_DCCSR_ERDM_MASK (0x03 << DMAC_DCCSR_ERDM_BIT) + #define DMAC_DCCSR_ERDM_LLEVEL (0 << DMAC_DCCSR_ERDM_BIT) + #define DMAC_DCCSR_ERDM_FEDGE (1 << DMAC_DCCSR_ERDM_BIT) + #define DMAC_DCCSR_ERDM_HLEVEL (2 << DMAC_DCCSR_ERDM_BIT) + #define DMAC_DCCSR_ERDM_REDGE (3 << DMAC_DCCSR_ERDM_BIT) +#define DMAC_DCCSR_EOPM (1 << 27) +#define DMAC_DCCSR_SAM (1 << 23) +#define DMAC_DCCSR_DAM (1 << 22) +#define DMAC_DCCSR_RDIL_BIT 16 +#define DMAC_DCCSR_RDIL_MASK (0x0f << DMAC_DCCSR_RDIL_BIT) + #define DMAC_DCCSR_RDIL_IGN (0 << DMAC_DCCSR_RDIL_BIT) + #define DMAC_DCCSR_RDIL_2 (1 << DMAC_DCCSR_RDIL_BIT) + #define DMAC_DCCSR_RDIL_4 (2 << DMAC_DCCSR_RDIL_BIT) + #define DMAC_DCCSR_RDIL_8 (3 << DMAC_DCCSR_RDIL_BIT) + #define DMAC_DCCSR_RDIL_12 (4 << DMAC_DCCSR_RDIL_BIT) + #define DMAC_DCCSR_RDIL_16 (5 << DMAC_DCCSR_RDIL_BIT) + #define DMAC_DCCSR_RDIL_20 (6 << DMAC_DCCSR_RDIL_BIT) + #define DMAC_DCCSR_RDIL_24 (7 << DMAC_DCCSR_RDIL_BIT) + #define DMAC_DCCSR_RDIL_28 (8 << DMAC_DCCSR_RDIL_BIT) + #define DMAC_DCCSR_RDIL_32 (9 << DMAC_DCCSR_RDIL_BIT) + #define DMAC_DCCSR_RDIL_48 (10 << DMAC_DCCSR_RDIL_BIT) + #define DMAC_DCCSR_RDIL_60 (11 << DMAC_DCCSR_RDIL_BIT) + #define DMAC_DCCSR_RDIL_64 (12 << DMAC_DCCSR_RDIL_BIT) + #define DMAC_DCCSR_RDIL_124 (13 << DMAC_DCCSR_RDIL_BIT) + #define DMAC_DCCSR_RDIL_128 (14 << DMAC_DCCSR_RDIL_BIT) + #define DMAC_DCCSR_RDIL_200 (15 << DMAC_DCCSR_RDIL_BIT) +#define DMAC_DCCSR_SWDH_BIT 14 +#define DMAC_DCCSR_SWDH_MASK (0x03 << DMAC_DCCSR_SWDH_BIT) + #define DMAC_DCCSR_SWDH_32 (0 << DMAC_DCCSR_SWDH_BIT) + #define DMAC_DCCSR_SWDH_8 (1 << DMAC_DCCSR_SWDH_BIT) + #define DMAC_DCCSR_SWDH_16 (2 << DMAC_DCCSR_SWDH_BIT) +#define DMAC_DCCSR_DWDH_BIT 12 +#define DMAC_DCCSR_DWDH_MASK (0x03 << DMAC_DCCSR_DWDH_BIT) + #define DMAC_DCCSR_DWDH_32 (0 << DMAC_DCCSR_DWDH_BIT) + #define DMAC_DCCSR_DWDH_8 (1 << DMAC_DCCSR_DWDH_BIT) + #define DMAC_DCCSR_DWDH_16 (2 << DMAC_DCCSR_DWDH_BIT) +#define DMAC_DCCSR_DS_BIT 8 +#define DMAC_DCCSR_DS_MASK (0x07 << DMAC_DCCSR_DS_BIT) + #define DMAC_DCCSR_DS_32b (0 << DMAC_DCCSR_DS_BIT) + #define DMAC_DCCSR_DS_8b (1 << DMAC_DCCSR_DS_BIT) + #define DMAC_DCCSR_DS_16b (2 << DMAC_DCCSR_DS_BIT) + #define DMAC_DCCSR_DS_16B (3 << DMAC_DCCSR_DS_BIT) + #define DMAC_DCCSR_DS_32B (4 << DMAC_DCCSR_DS_BIT) +#define DMAC_DCCSR_TM (1 << 7) +#define DMAC_DCCSR_AR (1 << 4) +#define DMAC_DCCSR_TC (1 << 3) +#define DMAC_DCCSR_HLT (1 << 2) +#define DMAC_DCCSR_TCIE (1 << 1) +#define DMAC_DCCSR_CHDE (1 << 0) + +#define DMAC_DMAIPR_CINT_BIT 8 +#define DMAC_DMAIPR_CINT_MASK (0xff << DMAC_DMAIPR_CINT_BIT) + +#define DMAC_DMACR_PR_BIT 8 +#define DMAC_DMACR_PR_MASK (0x03 << DMAC_DMACR_PR_BIT) + #define DMAC_DMACR_PR_01234567 (0 << DMAC_DMACR_PR_BIT) + #define DMAC_DMACR_PR_02314675 (1 << DMAC_DMACR_PR_BIT) + #define DMAC_DMACR_PR_20136457 (2 << DMAC_DMACR_PR_BIT) + #define DMAC_DMACR_PR_ROUNDROBIN (3 << DMAC_DMACR_PR_BIT) +#define DMAC_DMACR_HTR (1 << 3) +#define DMAC_DMACR_AER (1 << 2) +#define DMAC_DMACR_DME (1 << 0) + +#define IRQ_DMA_0 32 +#define NUM_DMA 6 + +#define DMAC_DSAR0 DMAC_DSAR(0) +#define DMAC_DDAR0 DMAC_DDAR(0) +#define DMAC_DTCR0 DMAC_DTCR(0) +#define DMAC_DRSR0 DMAC_DRSR(0) +#define DMAC_DCCSR0 DMAC_DCCSR(0) + +#define DMAC_DSAR1 DMAC_DSAR(1) +#define DMAC_DDAR1 DMAC_DDAR(1) +#define DMAC_DTCR1 DMAC_DTCR(1) +#define DMAC_DRSR1 DMAC_DRSR(1) +#define DMAC_DCCSR1 DMAC_DCCSR(1) + +#define DMAC_DSAR2 DMAC_DSAR(2) +#define DMAC_DDAR2 DMAC_DDAR(2) +#define DMAC_DTCR2 DMAC_DTCR(2) +#define DMAC_DRSR2 DMAC_DRSR(2) +#define DMAC_DCCSR2 DMAC_DCCSR(2) + +#define DMAC_DSAR3 DMAC_DSAR(3) +#define DMAC_DDAR3 DMAC_DDAR(3) +#define DMAC_DTCR3 DMAC_DTCR(3) +#define DMAC_DRSR3 DMAC_DRSR(3) +#define DMAC_DCCSR3 DMAC_DCCSR(3) + +#define DMAC_DSAR4 DMAC_DSAR(4) +#define DMAC_DDAR4 DMAC_DDAR(4) +#define DMAC_DTCR4 DMAC_DTCR(4) +#define DMAC_DRSR4 DMAC_DRSR(4) +#define DMAC_DCCSR4 DMAC_DCCSR(4) + +#define DMAC_DSAR5 DMAC_DSAR(5) +#define DMAC_DDAR5 DMAC_DDAR(5) +#define DMAC_DTCR5 DMAC_DTCR(5) +#define DMAC_DRSR5 DMAC_DRSR(5) +#define DMAC_DCCSR5 DMAC_DCCSR(5) + +#define DMAC_DSAR6 DMAC_DSAR(6) +#define DMAC_DDAR6 DMAC_DDAR(6) +#define DMAC_DTCR6 DMAC_DTCR(6) +#define DMAC_DRSR6 DMAC_DRSR(6) +#define DMAC_DCCSR6 DMAC_DCCSR(6) + +#define DMAC_DSAR7 DMAC_DSAR(7) +#define DMAC_DDAR7 DMAC_DDAR(7) +#define DMAC_DTCR7 DMAC_DTCR(7) +#define DMAC_DRSR7 DMAC_DRSR(7) +#define DMAC_DCCSR7 DMAC_DCCSR(7) + + + +/************************************************************************* + * AIC + *************************************************************************/ +#define AIC_FR (AIC_BASE + 0x000) +#define AIC_CR (AIC_BASE + 0x004) +#define AIC_ACCR1 (AIC_BASE + 0x008) +#define AIC_ACCR2 (AIC_BASE + 0x00C) +#define AIC_I2SCR (AIC_BASE + 0x010) +#define AIC_SR (AIC_BASE + 0x014) +#define AIC_ACSR (AIC_BASE + 0x018) +#define AIC_I2SSR (AIC_BASE + 0x01C) +#define AIC_ACCAR (AIC_BASE + 0x020) +#define AIC_ACCDR (AIC_BASE + 0x024) +#define AIC_ACSAR (AIC_BASE + 0x028) +#define AIC_ACSDR (AIC_BASE + 0x02C) +#define AIC_I2SDIV (AIC_BASE + 0x030) +#define AIC_DR (AIC_BASE + 0x034) + +#define REG_AIC_FR REG32(AIC_FR) +#define REG_AIC_CR REG32(AIC_CR) +#define REG_AIC_ACCR1 REG32(AIC_ACCR1) +#define REG_AIC_ACCR2 REG32(AIC_ACCR2) +#define REG_AIC_I2SCR REG32(AIC_I2SCR) +#define REG_AIC_SR REG32(AIC_SR) +#define REG_AIC_ACSR REG32(AIC_ACSR) +#define REG_AIC_I2SSR REG32(AIC_I2SSR) +#define REG_AIC_ACCAR REG32(AIC_ACCAR) +#define REG_AIC_ACCDR REG32(AIC_ACCDR) +#define REG_AIC_ACSAR REG32(AIC_ACSAR) +#define REG_AIC_ACSDR REG32(AIC_ACSDR) +#define REG_AIC_I2SDIV REG32(AIC_I2SDIV) +#define REG_AIC_DR REG32(AIC_DR) + +/* AIC Controller Configuration Register (AIC_FR) */ + +#define AIC_FR_RFTH_BIT 12 +#define AIC_FR_RFTH_MASK (0xf << AIC_FR_RFTH_BIT) +#define AIC_FR_TFTH_BIT 8 +#define AIC_FR_TFTH_MASK (0xf << AIC_FR_TFTH_BIT) +#define AIC_FR_AUSEL (1 << 4) +#define AIC_FR_RST (1 << 3) +#define AIC_FR_BCKD (1 << 2) +#define AIC_FR_SYNCD (1 << 1) +#define AIC_FR_ENB (1 << 0) + +/* AIC Controller Common Control Register (AIC_CR) */ + +#define AIC_CR_RDMS (1 << 15) +#define AIC_CR_TDMS (1 << 14) +#define AIC_CR_FLUSH (1 << 8) +#define AIC_CR_EROR (1 << 6) +#define AIC_CR_ETUR (1 << 5) +#define AIC_CR_ERFS (1 << 4) +#define AIC_CR_ETFS (1 << 3) +#define AIC_CR_ENLBF (1 << 2) +#define AIC_CR_ERPL (1 << 1) +#define AIC_CR_EREC (1 << 0) + +/* AIC Controller AC-link Control Register 1 (AIC_ACCR1) */ + +#define AIC_ACCR1_RS_BIT 16 +#define AIC_ACCR1_RS_MASK (0x3ff << AIC_ACCR1_RS_BIT) + #define AIC_ACCR1_RS_SLOT12 (1 << 25) /* Slot 12 valid bit */ + #define AIC_ACCR1_RS_SLOT11 (1 << 24) /* Slot 11 valid bit */ + #define AIC_ACCR1_RS_SLOT10 (1 << 23) /* Slot 10 valid bit */ + #define AIC_ACCR1_RS_SLOT9 (1 << 22) /* Slot 9 valid bit */ + #define AIC_ACCR1_RS_SLOT8 (1 << 21) /* Slot 8 valid bit */ + #define AIC_ACCR1_RS_SLOT7 (1 << 20) /* Slot 7 valid bit */ + #define AIC_ACCR1_RS_SLOT6 (1 << 19) /* Slot 6 valid bit */ + #define AIC_ACCR1_RS_SLOT5 (1 << 18) /* Slot 5 valid bit */ + #define AIC_ACCR1_RS_SLOT4 (1 << 17) /* Slot 4 valid bit */ + #define AIC_ACCR1_RS_SLOT3 (1 << 16) /* Slot 3 valid bit */ +#define AIC_ACCR1_XS_BIT 0 +#define AIC_ACCR1_XS_MASK (0x3ff << AIC_ACCR1_XS_BIT) + #define AIC_ACCR1_XS_SLOT12 (1 << 9) /* Slot 12 valid bit */ + #define AIC_ACCR1_XS_SLOT11 (1 << 8) /* Slot 11 valid bit */ + #define AIC_ACCR1_XS_SLOT10 (1 << 7) /* Slot 10 valid bit */ + #define AIC_ACCR1_XS_SLOT9 (1 << 6) /* Slot 9 valid bit */ + #define AIC_ACCR1_XS_SLOT8 (1 << 5) /* Slot 8 valid bit */ + #define AIC_ACCR1_XS_SLOT7 (1 << 4) /* Slot 7 valid bit */ + #define AIC_ACCR1_XS_SLOT6 (1 << 3) /* Slot 6 valid bit */ + #define AIC_ACCR1_XS_SLOT5 (1 << 2) /* Slot 5 valid bit */ + #define AIC_ACCR1_XS_SLOT4 (1 << 1) /* Slot 4 valid bit */ + #define AIC_ACCR1_XS_SLOT3 (1 << 0) /* Slot 3 valid bit */ + +/* AIC Controller AC-link Control Register 2 (AIC_ACCR2) */ + +#define AIC_ACCR2_ERSTO (1 << 18) +#define AIC_ACCR2_ESADR (1 << 17) +#define AIC_ACCR2_ECADT (1 << 16) +#define AIC_ACCR2_OASS_BIT 8 +#define AIC_ACCR2_OASS_MASK (0x3 << AIC_ACCR2_OASS_BIT) + #define AIC_ACCR2_OASS_20BIT (0 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 20-bit */ + #define AIC_ACCR2_OASS_18BIT (1 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 18-bit */ + #define AIC_ACCR2_OASS_16BIT (2 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 16-bit */ + #define AIC_ACCR2_OASS_8BIT (3 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 8-bit */ +#define AIC_ACCR2_IASS_BIT 6 +#define AIC_ACCR2_IASS_MASK (0x3 << AIC_ACCR2_IASS_BIT) + #define AIC_ACCR2_IASS_20BIT (0 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 20-bit */ + #define AIC_ACCR2_IASS_18BIT (1 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 18-bit */ + #define AIC_ACCR2_IASS_16BIT (2 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 16-bit */ + #define AIC_ACCR2_IASS_8BIT (3 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 8-bit */ +#define AIC_ACCR2_SO (1 << 3) +#define AIC_ACCR2_SR (1 << 2) +#define AIC_ACCR2_SS (1 << 1) +#define AIC_ACCR2_SA (1 << 0) + +/* AIC Controller I2S/MSB-justified Control Register (AIC_I2SCR) */ + +#define AIC_I2SCR_STPBK (1 << 12) +#define AIC_I2SCR_WL_BIT 1 +#define AIC_I2SCR_WL_MASK (0x7 << AIC_I2SCR_WL_BIT) + #define AIC_I2SCR_WL_24BIT (0 << AIC_I2SCR_WL_BIT) /* Word Length is 24 bit */ + #define AIC_I2SCR_WL_20BIT (1 << AIC_I2SCR_WL_BIT) /* Word Length is 20 bit */ + #define AIC_I2SCR_WL_18BIT (2 << AIC_I2SCR_WL_BIT) /* Word Length is 18 bit */ + #define AIC_I2SCR_WL_16BIT (3 << AIC_I2SCR_WL_BIT) /* Word Length is 16 bit */ + #define AIC_I2SCR_WL_8BIT (4 << AIC_I2SCR_WL_BIT) /* Word Length is 8 bit */ +#define AIC_I2SCR_AMSL (1 << 0) + +/* AIC Controller FIFO Status Register (AIC_SR) */ + +#define AIC_SR_RFL_BIT 24 +#define AIC_SR_RFL_MASK (0x1f << AIC_SR_RFL_BIT) +#define AIC_SR_TFL_BIT 8 +#define AIC_SR_TFL_MASK (0x1f << AIC_SR_TFL_BIT) +#define AIC_SR_ROR (1 << 6) +#define AIC_SR_TUR (1 << 5) +#define AIC_SR_RFS (1 << 4) +#define AIC_SR_TFS (1 << 3) + +/* AIC Controller AC-link Status Register (AIC_ACSR) */ + +#define AIC_ACSR_CRDY (1 << 20) +#define AIC_ACSR_CLPM (1 << 19) +#define AIC_ACSR_RSTO (1 << 18) +#define AIC_ACSR_SADR (1 << 17) +#define AIC_ACSR_CADT (1 << 16) + +/* AIC Controller I2S/MSB-justified Status Register (AIC_I2SSR) */ + +#define AIC_I2SSR_BSY (1 << 2) + +/* AIC Controller AC97 codec Command Address Register (AIC_ACCAR) */ + +#define AIC_ACCAR_CAR_BIT 0 +#define AIC_ACCAR_CAR_MASK (0xfffff << AIC_ACCAR_CAR_BIT) + +/* AIC Controller AC97 codec Command Data Register (AIC_ACCDR) */ + +#define AIC_ACCDR_CDR_BIT 0 +#define AIC_ACCDR_CDR_MASK (0xfffff << AIC_ACCDR_CDR_BIT) + +/* AIC Controller AC97 codec Status Address Register (AIC_ACSAR) */ + +#define AIC_ACSAR_SAR_BIT 0 +#define AIC_ACSAR_SAR_MASK (0xfffff << AIC_ACSAR_SAR_BIT) + +/* AIC Controller AC97 codec Status Data Register (AIC_ACSDR) */ + +#define AIC_ACSDR_SDR_BIT 0 +#define AIC_ACSDR_SDR_MASK (0xfffff << AIC_ACSDR_SDR_BIT) + +/* AIC Controller I2S/MSB-justified Clock Divider Register (AIC_I2SDIV) */ + +#define AIC_I2SDIV_DIV_BIT 0 +#define AIC_I2SDIV_DIV_MASK (0x7f << AIC_I2SDIV_DIV_BIT) + #define AIC_I2SDIV_BITCLK_3072KHZ (0x0C << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 3.072MHz */ + #define AIC_I2SDIV_BITCLK_2836KHZ (0x0D << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 2.836MHz */ + #define AIC_I2SDIV_BITCLK_1418KHZ (0x1A << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 1.418MHz */ + #define AIC_I2SDIV_BITCLK_1024KHZ (0x24 << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 1.024MHz */ + #define AIC_I2SDIV_BITCLK_7089KHZ (0x34 << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 708.92KHz */ + #define AIC_I2SDIV_BITCLK_512KHZ (0x48 << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 512.00KHz */ + + + + +/************************************************************************* + * LCD + *************************************************************************/ +#define LCD_CFG (LCD_BASE + 0x00) +#define LCD_VSYNC (LCD_BASE + 0x04) +#define LCD_HSYNC (LCD_BASE + 0x08) +#define LCD_VAT (LCD_BASE + 0x0c) +#define LCD_DAH (LCD_BASE + 0x10) +#define LCD_DAV (LCD_BASE + 0x14) +#define LCD_PS (LCD_BASE + 0x18) +#define LCD_CLS (LCD_BASE + 0x1c) +#define LCD_SPL (LCD_BASE + 0x20) +#define LCD_REV (LCD_BASE + 0x24) +#define LCD_CTRL (LCD_BASE + 0x30) +#define LCD_STATE (LCD_BASE + 0x34) +#define LCD_IID (LCD_BASE + 0x38) +#define LCD_DA0 (LCD_BASE + 0x40) +#define LCD_SA0 (LCD_BASE + 0x44) +#define LCD_FID0 (LCD_BASE + 0x48) +#define LCD_CMD0 (LCD_BASE + 0x4c) +#define LCD_DA1 (LCD_BASE + 0x50) +#define LCD_SA1 (LCD_BASE + 0x54) +#define LCD_FID1 (LCD_BASE + 0x58) +#define LCD_CMD1 (LCD_BASE + 0x5c) + +#define REG_LCD_CFG REG32(LCD_CFG) +#define REG_LCD_VSYNC REG32(LCD_VSYNC) +#define REG_LCD_HSYNC REG32(LCD_HSYNC) +#define REG_LCD_VAT REG32(LCD_VAT) +#define REG_LCD_DAH REG32(LCD_DAH) +#define REG_LCD_DAV REG32(LCD_DAV) +#define REG_LCD_PS REG32(LCD_PS) +#define REG_LCD_CLS REG32(LCD_CLS) +#define REG_LCD_SPL REG32(LCD_SPL) +#define REG_LCD_REV REG32(LCD_REV) +#define REG_LCD_CTRL REG32(LCD_CTRL) +#define REG_LCD_STATE REG32(LCD_STATE) +#define REG_LCD_IID REG32(LCD_IID) +#define REG_LCD_DA0 REG32(LCD_DA0) +#define REG_LCD_SA0 REG32(LCD_SA0) +#define REG_LCD_FID0 REG32(LCD_FID0) +#define REG_LCD_CMD0 REG32(LCD_CMD0) +#define REG_LCD_DA1 REG32(LCD_DA1) +#define REG_LCD_SA1 REG32(LCD_SA1) +#define REG_LCD_FID1 REG32(LCD_FID1) +#define REG_LCD_CMD1 REG32(LCD_CMD1) + +#define LCD_CFG_PDW_BIT 4 +#define LCD_CFG_PDW_MASK (0x03 << LCD_DEV_PDW_BIT) + #define LCD_CFG_PDW_1 (0 << LCD_DEV_PDW_BIT) + #define LCD_CFG_PDW_2 (1 << LCD_DEV_PDW_BIT) + #define LCD_CFG_PDW_4 (2 << LCD_DEV_PDW_BIT) + #define LCD_CFG_PDW_8 (3 << LCD_DEV_PDW_BIT) +#define LCD_CFG_MODE_BIT 0 +#define LCD_CFG_MODE_MASK (0x0f << LCD_DEV_MODE_BIT) + #define LCD_CFG_MODE_GENERIC_TFT (0 << LCD_DEV_MODE_BIT) + #define LCD_CFG_MODE_SHARP_HR (1 << LCD_DEV_MODE_BIT) + #define LCD_CFG_MODE_CASIO_TFT (2 << LCD_DEV_MODE_BIT) + #define LCD_CFG_MODE_SAMSUNG_ALPHA (3 << LCD_DEV_MODE_BIT) + #define LCD_CFG_MODE_NONINTER_CCIR656 (4 << LCD_DEV_MODE_BIT) + #define LCD_CFG_MODE_INTER_CCIR656 (6 << LCD_DEV_MODE_BIT) + #define LCD_CFG_MODE_SINGLE_CSTN (8 << LCD_DEV_MODE_BIT) + #define LCD_CFG_MODE_SINGLE_MSTN (9 << LCD_DEV_MODE_BIT) + #define LCD_CFG_MODE_DUAL_CSTN (10 << LCD_DEV_MODE_BIT) + #define LCD_CFG_MODE_DUAL_MSTN (11 << LCD_DEV_MODE_BIT) + +#define LCD_VSYNC_VPS_BIT 16 +#define LCD_VSYNC_VPS_MASK (0xffff << LCD_VSYNC_VPS_BIT) +#define LCD_VSYNC_VPE_BIT 0 +#define LCD_VSYNC_VPE_MASK (0xffff << LCD_VSYNC_VPS_BIT) + +#define LCD_HSYNC_HPS_BIT 16 +#define LCD_HSYNC_HPS_MASK (0xffff << LCD_HSYNC_HPS_BIT) +#define LCD_HSYNC_HPE_BIT 0 +#define LCD_HSYNC_HPE_MASK (0xffff << LCD_HSYNC_HPE_BIT) + +#define LCD_VAT_HT_BIT 16 +#define LCD_VAT_HT_MASK (0xffff << LCD_VAT_HT_BIT) +#define LCD_VAT_VT_BIT 0 +#define LCD_VAT_VT_MASK (0xffff << LCD_VAT_VT_BIT) + +#define LCD_DAH_HDS_BIT 16 +#define LCD_DAH_HDS_MASK (0xffff << LCD_DAH_HDS_BIT) +#define LCD_DAH_HDE_BIT 0 +#define LCD_DAH_HDE_MASK (0xffff << LCD_DAH_HDE_BIT) + +#define LCD_DAV_VDS_BIT 16 +#define LCD_DAV_VDS_MASK (0xffff << LCD_DAV_VDS_BIT) +#define LCD_DAV_VDE_BIT 0 +#define LCD_DAV_VDE_MASK (0xffff << LCD_DAV_VDE_BIT) + +#define LCD_CTRL_BST_BIT 28 +#define LCD_CTRL_BST_MASK (0x03 << LCD_CTRL_BST_BIT) + #define LCD_CTRL_BST_4 (0 << LCD_CTRL_BST_BIT) + #define LCD_CTRL_BST_8 (1 << LCD_CTRL_BST_BIT) + #define LCD_CTRL_BST_16 (2 << LCD_CTRL_BST_BIT) +#define LCD_CTRL_RGB555 (1 << 27) +#define LCD_CTRL_OFUP (1 << 26) +#define LCD_CTRL_FRC_BIT 24 +#define LCD_CTRL_FRC_MASK (0x03 << LCD_CTRL_FRC_BIT) + #define LCD_CTRL_FRC_16 (0 << LCD_CTRL_FRC_BIT) + #define LCD_CTRL_FRC_4 (1 << LCD_CTRL_FRC_BIT) + #define LCD_CTRL_FRC_2 (2 << LCD_CTRL_FRC_BIT) +#define LCD_CTRL_PDD_BIT 16 +#define LCD_CTRL_PDD_MASK (0xff << LCD_CTRL_PDD_BIT) +#define LCD_CTRL_EOFM (1 << 13) +#define LCD_CTRL_SOFM (1 << 12) +#define LCD_CTRL_OFUM (1 << 11) +#define LCD_CTRL_IFUM0 (1 << 10) +#define LCD_CTRL_IFUM1 (1 << 9) +#define LCD_CTRL_LDDM (1 << 8) +#define LCD_CTRL_QDM (1 << 7) +#define LCD_CTRL_BEDN (1 << 6) +#define LCD_CTRL_PEDN (1 << 5) +#define LCD_CTRL_DIS (1 << 4) +#define LCD_CTRL_ENA (1 << 3) +#define LCD_CTRL_BPP_BIT 0 +#define LCD_CTRL_BPP_MASK (0x07 << LCD_CTRL_BPP_BIT) + #define LCD_CTRL_BPP_1 (0 << LCD_CTRL_BPP_BIT) + #define LCD_CTRL_BPP_2 (1 << LCD_CTRL_BPP_BIT) + #define LCD_CTRL_BPP_4 (2 << LCD_CTRL_BPP_BIT) + #define LCD_CTRL_BPP_8 (3 << LCD_CTRL_BPP_BIT) + #define LCD_CTRL_BPP_16 (4 << LCD_CTRL_BPP_BIT) + +#define LCD_STATE_QD (1 << 7) +#define LCD_STATE_EOF (1 << 5) +#define LCD_STATE_SOF (1 << 4) +#define LCD_STATE_OFU (1 << 3) +#define LCD_STATE_IFU0 (1 << 2) +#define LCD_STATE_IFU1 (1 << 1) +#define LCD_STATE_LDD (1 << 0) + +#define LCD_CMD_SOFINT (1 << 31) +#define LCD_CMD_EOFINT (1 << 30) +#define LCD_CMD_PAL (1 << 28) +#define LCD_CMD_LEN_BIT 0 +#define LCD_CMD_LEN_MASK (0xffffff << LCD_CMD_LEN_BIT) + + + + +/************************************************************************* + * DES + *************************************************************************/ +#define DES_CR1 (DES_BASE + 0x000) +#define DES_CR2 (DES_BASE + 0x004) +#define DES_SR (DES_BASE + 0x008) +#define DES_K1L (DES_BASE + 0x010) +#define DES_K1R (DES_BASE + 0x014) +#define DES_K2L (DES_BASE + 0x018) +#define DES_K2R (DES_BASE + 0x01C) +#define DES_K3L (DES_BASE + 0x020) +#define DES_K3R (DES_BASE + 0x024) +#define DES_IVL (DES_BASE + 0x028) +#define DES_IVR (DES_BASE + 0x02C) +#define DES_DIN (DES_BASE + 0x030) +#define DES_DOUT (DES_BASE + 0x034) + +#define REG_DES_CR1 REG32(DES_CR1) +#define REG_DES_CR2 REG32(DES_CR2) +#define REG_DES_SR REG32(DES_SR) +#define REG_DES_K1L REG32(DES_K1L) +#define REG_DES_K1R REG32(DES_K1R) +#define REG_DES_K2L REG32(DES_K2L) +#define REG_DES_K2R REG32(DES_K2R) +#define REG_DES_K3L REG32(DES_K3L) +#define REG_DES_K3R REG32(DES_K3R) +#define REG_DES_IVL REG32(DES_IVL) +#define REG_DES_IVR REG32(DES_IVR) +#define REG_DES_DIN REG32(DES_DIN) +#define REG_DES_DOUT REG32(DES_DOUT) + +/* DES Control Register 1 (DES_CR1) */ + +#define DES_CR1_EN (1 << 0) + +/* DES Control Register 2 (DES_CR2) */ + +#define DES_CR2_ENDEC (1 << 3) +#define DES_CR2_MODE (1 << 2) +#define DES_CR2_ALG (1 << 1) +#define DES_CR2_DMAE (1 << 0) + +/* DES State Register (DES_SR) */ + +#define DES_SR_IN_FULL (1 << 5) +#define DES_SR_IN_LHF (1 << 4) +#define DES_SR_IN_EMPTY (1 << 3) +#define DES_SR_OUT_FULL (1 << 2) +#define DES_SR_OUT_GHF (1 << 1) +#define DES_SR_OUT_EMPTY (1 << 0) + + + + +/************************************************************************* + * CPM + *************************************************************************/ +#define CPM_CFCR (CPM_BASE+0x00) +#define CPM_PLCR1 (CPM_BASE+0x10) +#define CPM_OCR (CPM_BASE+0x1c) +#define CPM_CFCR2 (CPM_BASE+0x60) +#define CPM_LPCR (CPM_BASE+0x04) +#define CPM_RSTR (CPM_BASE+0x08) +#define CPM_MSCR (CPM_BASE+0x20) +#define CPM_SCR (CPM_BASE+0x24) +#define CPM_WRER (CPM_BASE+0x28) +#define CPM_WFER (CPM_BASE+0x2c) +#define CPM_WER (CPM_BASE+0x30) +#define CPM_WSR (CPM_BASE+0x34) +#define CPM_GSR0 (CPM_BASE+0x38) +#define CPM_GSR1 (CPM_BASE+0x3c) +#define CPM_GSR2 (CPM_BASE+0x40) +#define CPM_SPR (CPM_BASE+0x44) +#define CPM_GSR3 (CPM_BASE+0x48) + +#define REG_CPM_CFCR REG32(CPM_CFCR) +#define REG_CPM_PLCR1 REG32(CPM_PLCR1) +#define REG_CPM_OCR REG32(CPM_OCR) +#define REG_CPM_CFCR2 REG32(CPM_CFCR2) +#define REG_CPM_LPCR REG32(CPM_LPCR) +#define REG_CPM_RSTR REG32(CPM_RSTR) +#define REG_CPM_MSCR REG32(CPM_MSCR) +#define REG_CPM_SCR REG32(CPM_SCR) +#define REG_CPM_WRER REG32(CPM_WRER) +#define REG_CPM_WFER REG32(CPM_WFER) +#define REG_CPM_WER REG32(CPM_WER) +#define REG_CPM_WSR REG32(CPM_WSR) +#define REG_CPM_GSR0 REG32(CPM_GSR0) +#define REG_CPM_GSR1 REG32(CPM_GSR1) +#define REG_CPM_GSR2 REG32(CPM_GSR2) +#define REG_CPM_SPR REG32(CPM_SPR) +#define REG_CPM_GSR3 REG32(CPM_GSR3) + +#define CPM_CFCR_SSI (1 << 31) +#define CPM_CFCR_LCD (1 << 30) +#define CPM_CFCR_I2S (1 << 29) +#define CPM_CFCR_UCS (1 << 28) +#define CPM_CFCR_UFR_BIT 25 +#define CPM_CFCR_UFR_MASK (0x07 << CPM_CFCR_UFR_BIT) +#define CPM_CFCR_MSC (1 << 24) +#define CPM_CFCR_CKOEN2 (1 << 23) +#define CPM_CFCR_CKOEN1 (1 << 22) +#define CPM_CFCR_UPE (1 << 20) +#define CPM_CFCR_MFR_BIT 16 +#define CPM_CFCR_MFR_MASK (0x0f << CPM_CFCR_MFR_BIT) +#define CPM_CFCR_LFR_BIT 12 +#define CPM_CFCR_LFR_MASK (0x0f << CPM_CFCR_LFR_BIT) +#define CPM_CFCR_PFR_BIT 8 +#define CPM_CFCR_PFR_MASK (0x0f << CPM_CFCR_PFR_BIT) +#define CPM_CFCR_SFR_BIT 4 +#define CPM_CFCR_SFR_MASK (0x0f << CPM_CFCR_SFR_BIT) +#define CPM_CFCR_IFR_BIT 0 +#define CPM_CFCR_IFR_MASK (0x0f << CPM_CFCR_IFR_BIT) + +#define CPM_PLCR1_PLL1FD_BIT 23 +#define CPM_PLCR1_PLL1FD_MASK (0x1ff << CPM_PLCR1_PLL1FD_BIT) +#define CPM_PLCR1_PLL1RD_BIT 18 +#define CPM_PLCR1_PLL1RD_MASK (0x1f << CPM_PLCR1_PLL1RD_BIT) +#define CPM_PLCR1_PLL1OD_BIT 16 +#define CPM_PLCR1_PLL1OD_MASK (0x03 << CPM_PLCR1_PLL1OD_BIT) +#define CPM_PLCR1_PLL1S (1 << 10) +#define CPM_PLCR1_PLL1BP (1 << 9) +#define CPM_PLCR1_PLL1EN (1 << 8) +#define CPM_PLCR1_PLL1ST_BIT 0 +#define CPM_PLCR1_PLL1ST_MASK (0xff << CPM_PLCR1_PLL1ST_BIT) + +#define CPM_OCR_O1ST_BIT 16 +#define CPM_OCR_O1ST_MASK (0xff << CPM_OCR_O1ST_BIT) +#define CPM_OCR_EXT_RTC_CLK (1<<8) +#define CPM_OCR_SUSPEND_PHY1 (1<<7) +#define CPM_OCR_SUSPEND_PHY0 (1<<6) + +#define CPM_CFCR2_PXFR_BIT 0 +#define CPM_CFCR2_PXFR_MASK (0x1ff << CPM_CFCR2_PXFR_BIT) + +#define CPM_LPCR_DUTY_BIT 3 +#define CPM_LPCR_DUTY_MASK (0x1f << CPM_LPCR_DUTY_BIT) +#define CPM_LPCR_DOZE (1 << 2) +#define CPM_LPCR_LPM_BIT 0 +#define CPM_LPCR_LPM_MASK (0x03 << CPM_LPCR_LPM_BIT) + #define CPM_LPCR_LPM_IDLE (0 << CPM_LPCR_LPM_BIT) + #define CPM_LPCR_LPM_SLEEP (1 << CPM_LPCR_LPM_BIT) + #define CPM_LPCR_LPM_HIBERNATE (2 << CPM_LPCR_LPM_BIT) + +#define CPM_RSTR_SR (1 << 2) +#define CPM_RSTR_WR (1 << 1) +#define CPM_RSTR_HR (1 << 0) + +#define CPM_MSCR_MSTP_BIT 0 +#define CPM_MSCR_MSTP_MASK (0x1ffffff << CPM_MSCR_MSTP_BIT) + #define CPM_MSCR_MSTP_UART0 0 + #define CPM_MSCR_MSTP_UART1 1 + #define CPM_MSCR_MSTP_UART2 2 + #define CPM_MSCR_MSTP_OST 3 + #define CPM_MSCR_MSTP_DMAC 5 + #define CPM_MSCR_MSTP_UHC 6 + #define CPM_MSCR_MSTP_LCD 7 + #define CPM_MSCR_MSTP_I2C 8 + #define CPM_MSCR_MSTP_AICPCLK 9 + #define CPM_MSCR_MSTP_PWM0 10 + #define CPM_MSCR_MSTP_PWM1 11 + #define CPM_MSCR_MSTP_SSI 12 + #define CPM_MSCR_MSTP_MSC 13 + #define CPM_MSCR_MSTP_SCC 14 + #define CPM_MSCR_MSTP_AICBCLK 18 + #define CPM_MSCR_MSTP_UART3 20 + #define CPM_MSCR_MSTP_ETH 21 + #define CPM_MSCR_MSTP_KBC 22 + #define CPM_MSCR_MSTP_CIM 23 + #define CPM_MSCR_MSTP_UDC 24 + #define CPM_MSCR_MSTP_UPRT 25 + +#define CPM_SCR_O1SE (1 << 4) +#define CPM_SCR_HGP (1 << 3) +#define CPM_SCR_HZP (1 << 2) +#define CPM_SCR_HZM (1 << 1) + +#define CPM_WRER_RE_BIT 0 +#define CPM_WRER_RE_MASK (0xffff << CPM_WRER_RE_BIT) + +#define CPM_WFER_FE_BIT 0 +#define CPM_WFER_FE_MASK (0xffff << CPM_WFER_FE_BIT) + +#define CPM_WER_WERTC (1 << 31) +#define CPM_WER_WEETH (1 << 30) +#define CPM_WER_WE_BIT 0 +#define CPM_WER_WE_MASK (0xffff << CPM_WER_WE_BIT) + +#define CPM_WSR_WSRTC (1 << 31) +#define CPM_WSR_WSETH (1 << 30) +#define CPM_WSR_WS_BIT 0 +#define CPM_WSR_WS_MASK (0xffff << CPM_WSR_WS_BIT) + + + + +/************************************************************************* + * SSI + *************************************************************************/ +#define SSI_DR (SSI_BASE + 0x000) +#define SSI_CR0 (SSI_BASE + 0x004) +#define SSI_CR1 (SSI_BASE + 0x008) +#define SSI_SR (SSI_BASE + 0x00C) +#define SSI_ITR (SSI_BASE + 0x010) +#define SSI_ICR (SSI_BASE + 0x014) +#define SSI_GR (SSI_BASE + 0x018) + +#define REG_SSI_DR REG32(SSI_DR) +#define REG_SSI_CR0 REG16(SSI_CR0) +#define REG_SSI_CR1 REG32(SSI_CR1) +#define REG_SSI_SR REG32(SSI_SR) +#define REG_SSI_ITR REG16(SSI_ITR) +#define REG_SSI_ICR REG8(SSI_ICR) +#define REG_SSI_GR REG16(SSI_GR) + +/* SSI Data Register (SSI_DR) */ + +#define SSI_DR_GPC_BIT 0 +#define SSI_DR_GPC_MASK (0x1ff << SSI_DR_GPC_BIT) + +/* SSI Control Register 0 (SSI_CR0) */ + +#define SSI_CR0_SSIE (1 << 15) +#define SSI_CR0_TIE (1 << 14) +#define SSI_CR0_RIE (1 << 13) +#define SSI_CR0_TEIE (1 << 12) +#define SSI_CR0_REIE (1 << 11) +#define SSI_CR0_LOOP (1 << 10) +#define SSI_CR0_RFINE (1 << 9) +#define SSI_CR0_RFINC (1 << 8) +#define SSI_CR0_FSEL (1 << 6) +#define SSI_CR0_TFLUSH (1 << 2) +#define SSI_CR0_RFLUSH (1 << 1) +#define SSI_CR0_DISREV (1 << 0) + +/* SSI Control Register 1 (SSI_CR1) */ + +#define SSI_CR1_FRMHL_BIT 30 +#define SSI_CR1_FRMHL_MASK (0x3 << SSI_CR1_FRMHL_BIT) + #define SSI_CR1_FRMHL_CELOW_CE2LOW (0 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is low valid and SSI_CE2_ is low valid */ + #define SSI_CR1_FRMHL_CEHIGH_CE2LOW (1 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is high valid and SSI_CE2_ is low valid */ + #define SSI_CR1_FRMHL_CELOW_CE2HIGH (2 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is low valid and SSI_CE2_ is high valid */ + #define SSI_CR1_FRMHL_CEHIGH_CE2HIGH (3 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is high valid and SSI_CE2_ is high valid */ +#define SSI_CR1_TFVCK_BIT 28 +#define SSI_CR1_TFVCK_MASK (0x3 << SSI_CR1_TFVCK_BIT) + #define SSI_CR1_TFVCK_0 (0 << SSI_CR1_TFVCK_BIT) + #define SSI_CR1_TFVCK_1 (1 << SSI_CR1_TFVCK_BIT) + #define SSI_CR1_TFVCK_2 (2 << SSI_CR1_TFVCK_BIT) + #define SSI_CR1_TFVCK_3 (3 << SSI_CR1_TFVCK_BIT) +#define SSI_CR1_TCKFI_BIT 26 +#define SSI_CR1_TCKFI_MASK (0x3 << SSI_CR1_TCKFI_BIT) + #define SSI_CR1_TCKFI_0 (0 << SSI_CR1_TCKFI_BIT) + #define SSI_CR1_TCKFI_1 (1 << SSI_CR1_TCKFI_BIT) + #define SSI_CR1_TCKFI_2 (2 << SSI_CR1_TCKFI_BIT) + #define SSI_CR1_TCKFI_3 (3 << SSI_CR1_TCKFI_BIT) +#define SSI_CR1_LFST (1 << 25) +#define SSI_CR1_ITFRM (1 << 24) +#define SSI_CR1_UNFIN (1 << 23) +#define SSI_CR1_MULTS (1 << 22) +#define SSI_CR1_FMAT_BIT 20 +#define SSI_CR1_FMAT_MASK (0x3 << SSI_CR1_FMAT_BIT) + #define SSI_CR1_FMAT_SPI (0 << SSI_CR1_FMAT_BIT) /* Motorola¡¯s SPI format */ + #define SSI_CR1_FMAT_SSP (1 << SSI_CR1_FMAT_BIT) /* TI's SSP format */ + #define SSI_CR1_FMAT_MW1 (2 << SSI_CR1_FMAT_BIT) /* National Microwire 1 format */ + #define SSI_CR1_FMAT_MW2 (3 << SSI_CR1_FMAT_BIT) /* National Microwire 2 format */ +#define SSI_CR1_MCOM_BIT 12 +#define SSI_CR1_MCOM_MASK (0xf << SSI_CR1_MCOM_BIT) + #define SSI_CR1_MCOM_1BIT (0x0 << SSI_CR1_MCOM_BIT) /* 1-bit command selected */ + #define SSI_CR1_MCOM_2BIT (0x1 << SSI_CR1_MCOM_BIT) /* 2-bit command selected */ + #define SSI_CR1_MCOM_3BIT (0x2 << SSI_CR1_MCOM_BIT) /* 3-bit command selected */ + #define SSI_CR1_MCOM_4BIT (0x3 << SSI_CR1_MCOM_BIT) /* 4-bit command selected */ + #define SSI_CR1_MCOM_5BIT (0x4 << SSI_CR1_MCOM_BIT) /* 5-bit command selected */ + #define SSI_CR1_MCOM_6BIT (0x5 << SSI_CR1_MCOM_BIT) /* 6-bit command selected */ + #define SSI_CR1_MCOM_7BIT (0x6 << SSI_CR1_MCOM_BIT) /* 7-bit command selected */ + #define SSI_CR1_MCOM_8BIT (0x7 << SSI_CR1_MCOM_BIT) /* 8-bit command selected */ + #define SSI_CR1_MCOM_9BIT (0x8 << SSI_CR1_MCOM_BIT) /* 9-bit command selected */ + #define SSI_CR1_MCOM_10BIT (0x9 << SSI_CR1_MCOM_BIT) /* 10-bit command selected */ + #define SSI_CR1_MCOM_11BIT (0xA << SSI_CR1_MCOM_BIT) /* 11-bit command selected */ + #define SSI_CR1_MCOM_12BIT (0xB << SSI_CR1_MCOM_BIT) /* 12-bit command selected */ + #define SSI_CR1_MCOM_13BIT (0xC << SSI_CR1_MCOM_BIT) /* 13-bit command selected */ + #define SSI_CR1_MCOM_14BIT (0xD << SSI_CR1_MCOM_BIT) /* 14-bit command selected */ + #define SSI_CR1_MCOM_15BIT (0xE << SSI_CR1_MCOM_BIT) /* 15-bit command selected */ + #define SSI_CR1_MCOM_16BIT (0xF << SSI_CR1_MCOM_BIT) /* 16-bit command selected */ +#define SSI_CR1_TTRG_BIT 10 +#define SSI_CR1_TTRG_MASK (0x3 << SSI_CR1_TTRG_BIT) + #define SSI_CR1_TTRG_1 (0 << SSI_CR1_TTRG_BIT)/* Less than or equal to 1 */ + #define SSI_CR1_TTRG_4 (1 << SSI_CR1_TTRG_BIT) /* Less than or equal to 4 */ + #define SSI_CR1_TTRG_8 (2 << SSI_CR1_TTRG_BIT) /* Less than or equal to 8 */ + #define SSI_CR1_TTRG_14 (3 << SSI_CR1_TTRG_BIT) /* Less than or equal to 14 */ +#define SSI_CR1_RTRG_BIT 8 +#define SSI_CR1_RTRG_MASK (0x3 << SSI_CR1_RTRG_BIT) + #define SSI_CR1_RTRG_1 (0 << SSI_CR1_RTRG_BIT) /* More than or equal to 1 */ + #define SSI_CR1_RTRG_4 (1 << SSI_CR1_RTRG_BIT) /* More than or equal to 4 */ + #define SSI_CR1_RTRG_8 (2 << SSI_CR1_RTRG_BIT) /* More than or equal to 8 */ + #define SSI_CR1_RTRG_14 (3 << SSI_CR1_RTRG_BIT) /* More than or equal to 14 */ +#define SSI_CR1_FLEN_BIT 4 +#define SSI_CR1_FLEN_MASK (0xf << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_2BIT (0x0 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_3BIT (0x1 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_4BIT (0x2 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_5BIT (0x3 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_6BIT (0x4 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_7BIT (0x5 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_8BIT (0x6 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_9BIT (0x7 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_10BIT (0x8 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_11BIT (0x9 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_12BIT (0xA << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_13BIT (0xB << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_14BIT (0xC << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_15BIT (0xD << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_16BIT (0xE << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_17BIT (0xF << SSI_CR1_FLEN_BIT) +#define SSI_CR1_PHA (1 << 1) +#define SSI_CR1_POL (1 << 0) + +/* SSI Status Register (SSI_SR) */ + +#define SSI_SR_TFIFONUM_BIT 13 +#define SSI_SR_TFIFONUM_MASK (0x1f << SSI_SR_TFIFONUM_BIT) +#define SSI_SR_RFIFONUM_BIT 8 +#define SSI_SR_RFIFONUM_MASK (0x1f << SSI_SR_RFIFONUM_BIT) +#define SSI_SR_END (1 << 7) +#define SSI_SR_BUSY (1 << 6) +#define SSI_SR_TFF (1 << 5) +#define SSI_SR_RFE (1 << 4) +#define SSI_SR_TFHE (1 << 3) +#define SSI_SR_RFHF (1 << 2) +#define SSI_SR_UNDR (1 << 1) +#define SSI_SR_OVER (1 << 0) + +/* SSI Interval Time Control Register (SSI_ITR) */ + +#define SSI_ITR_CNTCLK (1 << 15) +#define SSI_ITR_IVLTM_BIT 0 +#define SSI_ITR_IVLTM_MASK (0x7fff << SSI_ITR_IVLTM_BIT) + +#endif /* __ASM_JZ4730_REGS_H__ */ diff --git a/include/asm-mips/mach-jz4730/serial.h b/include/asm-mips/mach-jz4730/serial.h new file mode 100644 index 0000000..4a0afc3 --- /dev/null +++ b/include/asm-mips/mach-jz4730/serial.h @@ -0,0 +1,33 @@ +/* + * linux/include/asm-mips/mach-jz4730/serial.h + * + * JZ4730 serial port definition. + * + * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. + * + * Author: + * + * 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_JZ4730_SERIAL_H__ +#define __ASM_JZ4730_SERIAL_H__ + +#define JZ_BASE_BAUD (JZ_EXTAL/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 }, \ + { .baud_base = JZ_BASE_BAUD, .irq = IRQ_UART1, \ + .flags = STD_COM_FLAGS, .iomem_base = (u8 *)UART1_BASE, \ + .iomem_reg_shift = 2, .io_type = SERIAL_IO_MEM }, \ + { .baud_base = JZ_BASE_BAUD, .irq = IRQ_UART2, \ + .flags = STD_COM_FLAGS, .iomem_base = (u8 *)UART2_BASE, \ + .iomem_reg_shift = 2, .io_type = SERIAL_IO_MEM }, \ + { .baud_base = JZ_BASE_BAUD, .irq = IRQ_UART3, \ + .flags = STD_COM_FLAGS, .iomem_base = (u8 *)UART3_BASE, \ + .iomem_reg_shift = 2, .io_type = SERIAL_IO_MEM }, + +#endif /* __ASM_JZ4730_SERIAL_H__ */ diff --git a/include/asm-mips/mach-jz4730/war.h b/include/asm-mips/mach-jz4730/war.h new file mode 100644 index 0000000..3a5bc17 --- /dev/null +++ b/include/asm-mips/mach-jz4730/war.h @@ -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 + */ +#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 */ diff --git a/include/asm-mips/mach-jz4740/board-dipper.h b/include/asm-mips/mach-jz4740/board-dipper.h new file mode 100644 index 0000000..ae84f24 --- /dev/null +++ b/include/asm-mips/mach-jz4740/board-dipper.h @@ -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: + * + * 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__ */ diff --git a/include/asm-mips/mach-jz4740/board-leo.h b/include/asm-mips/mach-jz4740/board-leo.h new file mode 100644 index 0000000..4b883e2 --- /dev/null +++ b/include/asm-mips/mach-jz4740/board-leo.h @@ -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__ */ diff --git a/include/asm-mips/mach-jz4740/board-lyra.h b/include/asm-mips/mach-jz4740/board-lyra.h new file mode 100644 index 0000000..29e0ce0 --- /dev/null +++ b/include/asm-mips/mach-jz4740/board-lyra.h @@ -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: + * + * 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__ */ diff --git a/include/asm-mips/mach-jz4740/board-pavo.h b/include/asm-mips/mach-jz4740/board-pavo.h new file mode 100644 index 0000000..f33831b --- /dev/null +++ b/include/asm-mips/mach-jz4740/board-pavo.h @@ -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: + * + * 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__ */ diff --git a/include/asm-mips/mach-jz4740/board-virgo.h b/include/asm-mips/mach-jz4740/board-virgo.h new file mode 100644 index 0000000..acd7bb7 --- /dev/null +++ b/include/asm-mips/mach-jz4740/board-virgo.h @@ -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: + * + * 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__ */ diff --git a/include/asm-mips/mach-jz4740/clock.h b/include/asm-mips/mach-jz4740/clock.h new file mode 100644 index 0000000..11ffe88 --- /dev/null +++ b/include/asm-mips/mach-jz4740/clock.h @@ -0,0 +1,173 @@ +/* + * linux/include/asm-mips/mach-jz4740/clock.h + * + * JZ4740 clocks definition. + * + * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. + * + * Author: + * + * 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__ */ diff --git a/include/asm-mips/mach-jz4740/dma.h b/include/asm-mips/mach-jz4740/dma.h new file mode 100644 index 0000000..b82b984 --- /dev/null +++ b/include/asm-mips/mach-jz4740/dma.h @@ -0,0 +1,265 @@ +/* + * linux/include/asm-mips/mach-jz4740/dma.h + * + * JZ4740 DMA definition. + * + * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. + * + * Author: + * + * 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 +#include /* need byte IO */ +#include /* And spinlocks */ +#include +#include + +/* + * 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__ */ diff --git a/include/asm-mips/mach-jz4740/jz4740.h b/include/asm-mips/mach-jz4740/jz4740.h new file mode 100644 index 0000000..d38d5f1 --- /dev/null +++ b/include/asm-mips/mach-jz4740/jz4740.h @@ -0,0 +1,60 @@ +/* + * linux/include/asm-mips/mach-jz4740/jz4740.h + * + * JZ4740 common definition. + * + * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. + * + * Author: + * + * 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 +#include +#include +#include + +/*------------------------------------------------------------------ + * Platform definitions + */ +#ifdef CONFIG_JZ4740_PAVO +#include +#endif + +#ifdef CONFIG_JZ4740_LEO +#include +#endif + +#ifdef CONFIG_JZ4740_LYRA +#include +#endif + +#ifdef CONFIG_JZ4725_DIPPER +#include +#endif + +#ifdef CONFIG_JZ4720_VIRGO +#include +#endif + +#ifdef CONFIG_JZ4740_QI_LB60 +#include +#endif + +/* Add other platform definition here ... */ + + +/*------------------------------------------------------------------ + * Follows are related to platform definitions + */ + +#include +#include + +#endif /* __ASM_JZ4740_H__ */ diff --git a/include/asm-mips/mach-jz4740/misc.h b/include/asm-mips/mach-jz4740/misc.h new file mode 100644 index 0000000..8f14a5a --- /dev/null +++ b/include/asm-mips/mach-jz4740/misc.h @@ -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: + * + * 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__ */ diff --git a/include/asm-mips/mach-jz4740/ops.h b/include/asm-mips/mach-jz4740/ops.h new file mode 100644 index 0000000..6ff050a --- /dev/null +++ b/include/asm-mips/mach-jz4740/ops.h @@ -0,0 +1,2224 @@ +/* + * linux/include/asm-mips/mach-jz4740/ops.h + * + * Ingenic's JZ4740 common include. + * + * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. + * + * Author: + * + * 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_OPS_H__ +#define __JZ4740_OPS_H__ + +/* + * Definition of Module Operations + */ + +/*************************************************************************** + * GPIO + ***************************************************************************/ + +//------------------------------------------------------ +// GPIO Pins Description +// +// PORT 0: +// +// PIN/BIT N FUNC0 FUNC1 +// 0 D0 - +// 1 D1 - +// 2 D2 - +// 3 D3 - +// 4 D4 - +// 5 D5 - +// 6 D6 - +// 7 D7 - +// 8 D8 - +// 9 D9 - +// 10 D10 - +// 11 D11 - +// 12 D12 - +// 13 D13 - +// 14 D14 - +// 15 D15 - +// 16 D16 - +// 17 D17 - +// 18 D18 - +// 19 D19 - +// 20 D20 - +// 21 D21 - +// 22 D22 - +// 23 D23 - +// 24 D24 - +// 25 D25 - +// 26 D26 - +// 27 D27 - +// 28 D28 - +// 29 D29 - +// 30 D30 - +// 31 D31 - +// +//------------------------------------------------------ +// PORT 1: +// +// PIN/BIT N FUNC0 FUNC1 +// 0 A0 - +// 1 A1 - +// 2 A2 - +// 3 A3 - +// 4 A4 - +// 5 A5 - +// 6 A6 - +// 7 A7 - +// 8 A8 - +// 9 A9 - +// 10 A10 - +// 11 A11 - +// 12 A12 - +// 13 A13 - +// 14 A14 - +// 15 A15/CL - +// 16 A16/AL - +// 17 LCD_CLS A21 +// 18 LCD_SPL A22 +// 19 DCS# - +// 20 RAS# - +// 21 CAS# - +// 22 RDWE#/BUFD# - +// 23 CKE - +// 24 CKO - +// 25 CS1# - +// 26 CS2# - +// 27 CS3# - +// 28 CS4# - +// 29 RD# - +// 30 WR# - +// 31 WE0# - +// +// Note: PIN15&16 are CL&AL when connecting to NAND flash. +//------------------------------------------------------ +// PORT 2: +// +// PIN/BIT N FUNC0 FUNC1 +// 0 LCD_D0 - +// 1 LCD_D1 - +// 2 LCD_D2 - +// 3 LCD_D3 - +// 4 LCD_D4 - +// 5 LCD_D5 - +// 6 LCD_D6 - +// 7 LCD_D7 - +// 8 LCD_D8 - +// 9 LCD_D9 - +// 10 LCD_D10 - +// 11 LCD_D11 - +// 12 LCD_D12 - +// 13 LCD_D13 - +// 14 LCD_D14 - +// 15 LCD_D15 - +// 16 LCD_D16 - +// 17 LCD_D17 - +// 18 LCD_PCLK - +// 19 LCD_HSYNC - +// 20 LCD_VSYNC - +// 21 LCD_DE - +// 22 LCD_PS A19 +// 23 LCD_REV A20 +// 24 WE1# - +// 25 WE2# - +// 26 WE3# - +// 27 WAIT# - +// 28 FRE# - +// 29 FWE# - +// 30(NOTE:FRB#) - - +// 31 - - +// +// NOTE(1): PIN30 is used for FRB# when connecting to NAND flash. +//------------------------------------------------------ +// PORT 3: +// +// PIN/BIT N FUNC0 FUNC1 +// 0 CIM_D0 - +// 1 CIM_D1 - +// 2 CIM_D2 - +// 3 CIM_D3 - +// 4 CIM_D4 - +// 5 CIM_D5 - +// 6 CIM_D6 - +// 7 CIM_D7 - +// 8 MSC_CMD - +// 9 MSC_CLK - +// 10 MSC_D0 - +// 11 MSC_D1 - +// 12 MSC_D2 - +// 13 MSC_D3 - +// 14 CIM_MCLK - +// 15 CIM_PCLK - +// 16 CIM_VSYNC - +// 17 CIM_HSYNC - +// 18 SSI_CLK SCLK_RSTN +// 19 SSI_CE0# BIT_CLK(AIC) +// 20 SSI_DT SDATA_OUT(AIC) +// 21 SSI_DR SDATA_IN(AIC) +// 22 SSI_CE1#&GPC SYNC(AIC) +// 23 PWM0 I2C_SDA +// 24 PWM1 I2C_SCK +// 25 PWM2 UART0_TxD +// 26 PWM3 UART0_RxD +// 27 PWM4 A17 +// 28 PWM5 A18 +// 29 - - +// 30 PWM6 UART0_CTS/UART1_RxD +// 31 PWM7 UART0_RTS/UART1_TxD +// +////////////////////////////////////////////////////////// + +/* + * p is the port number (0,1,2,3) + * o is the pin offset (0-31) inside the port + * n is the absolute number of a pin (0-127), regardless of the port + */ + +//------------------------------------------- +// Function Pins Mode + +#define __gpio_as_func0(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXFUNS(p) = (1 << o); \ + REG_GPIO_PXSELC(p) = (1 << o); \ +} while (0) + +#define __gpio_as_func1(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXFUNS(p) = (1 << o); \ + REG_GPIO_PXSELS(p) = (1 << o); \ +} while (0) + +/* + * D0 ~ D31, A0 ~ A16, DCS#, RAS#, CAS#, CKE#, + * RDWE#, CKO#, WE0#, WE1#, WE2#, WE3# + */ +#define __gpio_as_sdram_32bit() \ +do { \ + REG_GPIO_PXFUNS(0) = 0xffffffff; \ + REG_GPIO_PXSELC(0) = 0xffffffff; \ + REG_GPIO_PXPES(0) = 0xffffffff; \ + REG_GPIO_PXFUNS(1) = 0x81f9ffff; \ + REG_GPIO_PXSELC(1) = 0x81f9ffff; \ + REG_GPIO_PXPES(1) = 0x81f9ffff; \ + REG_GPIO_PXFUNS(2) = 0x07000000; \ + REG_GPIO_PXSELC(2) = 0x07000000; \ + REG_GPIO_PXPES(2) = 0x07000000; \ +} while (0) + +/* + * D0 ~ D15, A0 ~ A16, DCS#, RAS#, CAS#, CKE#, + * RDWE#, CKO#, WE0#, WE1# + */ +#define __gpio_as_sdram_16bit() \ +do { \ + REG_GPIO_PXFUNS(0) = 0x5442bfaa; \ + REG_GPIO_PXSELC(0) = 0x5442bfaa; \ + REG_GPIO_PXPES(0) = 0x5442bfaa; \ + REG_GPIO_PXFUNS(1) = 0x81f9ffff; \ + REG_GPIO_PXSELC(1) = 0x81f9ffff; \ + REG_GPIO_PXPES(1) = 0x81f9ffff; \ + REG_GPIO_PXFUNS(2) = 0x01000000; \ + REG_GPIO_PXSELC(2) = 0x01000000; \ + REG_GPIO_PXPES(2) = 0x01000000; \ +} while (0) + +/* + * CS1#, CLE, ALE, FRE#, FWE#, FRB#, RDWE#/BUFD# + */ +#define __gpio_as_nand() \ +do { \ + REG_GPIO_PXFUNS(1) = 0x02018000; \ + REG_GPIO_PXSELC(1) = 0x02018000; \ + REG_GPIO_PXPES(1) = 0x02018000; \ + REG_GPIO_PXFUNS(2) = 0x30000000; \ + REG_GPIO_PXSELC(2) = 0x30000000; \ + REG_GPIO_PXPES(2) = 0x30000000; \ + REG_GPIO_PXFUNC(2) = 0x40000000; \ + REG_GPIO_PXSELC(2) = 0x40000000; \ + REG_GPIO_PXDIRC(2) = 0x40000000; \ + REG_GPIO_PXPES(2) = 0x40000000; \ + REG_GPIO_PXFUNS(1) = 0x00400000; \ + REG_GPIO_PXSELC(1) = 0x00400000; \ +} while (0) + +/* + * CS4#, RD#, WR#, WAIT#, A0 ~ A22, D0 ~ D7 + */ +#define __gpio_as_nor_8bit() \ +do { \ + REG_GPIO_PXFUNS(0) = 0x000000ff; \ + REG_GPIO_PXSELC(0) = 0x000000ff; \ + REG_GPIO_PXPES(0) = 0x000000ff; \ + REG_GPIO_PXFUNS(1) = 0x7041ffff; \ + REG_GPIO_PXSELC(1) = 0x7041ffff; \ + REG_GPIO_PXPES(1) = 0x7041ffff; \ + REG_GPIO_PXFUNS(1) = 0x00060000; \ + REG_GPIO_PXSELS(1) = 0x00060000; \ + REG_GPIO_PXPES(1) = 0x00060000; \ + REG_GPIO_PXFUNS(2) = 0x08000000; \ + REG_GPIO_PXSELC(2) = 0x08000000; \ + REG_GPIO_PXPES(2) = 0x08000000; \ + REG_GPIO_PXFUNS(2) = 0x00c00000; \ + REG_GPIO_PXSELS(2) = 0x00c00000; \ + REG_GPIO_PXPES(2) = 0x00c00000; \ + REG_GPIO_PXFUNS(3) = 0x18000000; \ + REG_GPIO_PXSELS(3) = 0x18000000; \ + REG_GPIO_PXPES(3) = 0x18000000; \ +} while (0) + +/* + * CS4#, RD#, WR#, WAIT#, A0 ~ A22, D0 ~ D15 + */ +#define __gpio_as_nor_16bit() \ +do { \ + REG_GPIO_PXFUNS(0) = 0x0000ffff; \ + REG_GPIO_PXSELC(0) = 0x0000ffff; \ + REG_GPIO_PXPES(0) = 0x0000ffff; \ + REG_GPIO_PXFUNS(1) = 0x7041ffff; \ + REG_GPIO_PXSELC(1) = 0x7041ffff; \ + REG_GPIO_PXPES(1) = 0x7041ffff; \ + REG_GPIO_PXFUNS(1) = 0x00060000; \ + REG_GPIO_PXSELS(1) = 0x00060000; \ + REG_GPIO_PXPES(1) = 0x00060000; \ + REG_GPIO_PXFUNS(2) = 0x08000000; \ + REG_GPIO_PXSELC(2) = 0x08000000; \ + REG_GPIO_PXPES(2) = 0x08000000; \ + REG_GPIO_PXFUNS(2) = 0x00c00000; \ + REG_GPIO_PXSELS(2) = 0x00c00000; \ + REG_GPIO_PXPES(2) = 0x00c00000; \ + REG_GPIO_PXFUNS(3) = 0x18000000; \ + REG_GPIO_PXSELS(3) = 0x18000000; \ + REG_GPIO_PXPES(3) = 0x18000000; \ +} while (0) + +/* + * UART0_TxD, UART_RxD0 + */ +#define __gpio_as_uart0() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x06000000; \ + REG_GPIO_PXSELS(3) = 0x06000000; \ + REG_GPIO_PXPES(3) = 0x06000000; \ +} while (0) + +/* + * UART0_CTS, UART0_RTS + */ +#define __gpio_as_ctsrts() \ +do { \ + REG_GPIO_PXFUNS(3) = 0xc0000000; \ + REG_GPIO_PXSELS(3) = 0xc0000000; \ + REG_GPIO_PXTRGC(3) = 0xc0000000; \ + REG_GPIO_PXPES(3) = 0xc0000000; \ +} while (0) + +/* + * UART1_TxD, UART1_RxD1 + */ +#define __gpio_as_uart1() \ +do { \ + REG_GPIO_PXFUNS(3) = 0xc0000000; \ + REG_GPIO_PXSELC(3) = 0xc0000000; \ + REG_GPIO_PXTRGS(3) = 0xc0000000; \ + REG_GPIO_PXPES(3) = 0xc0000000; \ +} while (0) + +/* + * LCD_D0~LCD_D15, LCD_PCLK, LCD_HSYNC, LCD_VSYNC, LCD_DE + */ +#define __gpio_as_lcd_16bit() \ +do { \ + REG_GPIO_PXFUNS(2) = 0x003cffff; \ + REG_GPIO_PXSELC(2) = 0x003cffff; \ + REG_GPIO_PXPES(2) = 0x003cffff; \ +} while (0) + +/* + * LCD_D0~LCD_D17, LCD_PCLK, LCD_HSYNC, LCD_VSYNC, LCD_DE + */ +#define __gpio_as_lcd_18bit() \ +do { \ + REG_GPIO_PXFUNS(2) = 0x003fffff; \ + REG_GPIO_PXSELC(2) = 0x003fffff; \ + REG_GPIO_PXPES(2) = 0x003fffff; \ +} while (0) + +/* + * LCD_PS, LCD_REV, LCD_CLS, LCD_SPL + */ +#define __gpio_as_lcd_special() \ +do { \ + REG_GPIO_PXFUNS(1) = 0x00060000; \ + REG_GPIO_PXSELC(1) = 0x00060000; \ + REG_GPIO_PXPES(1) = 0x00060000; \ + REG_GPIO_PXFUNS(2) = 0x00c00000; \ + REG_GPIO_PXSELC(2) = 0x00c00000; \ + REG_GPIO_PXPES(2) = 0x00c00000; \ +} while (0) + +/* LCD_D0~LCD_D7, SLCD_RS, SLCD_CS */ +#define __gpio_as_slcd_8bit() \ +do { \ + REG_GPIO_PXFUNS(2) = 0x001800ff; \ + REG_GPIO_PXSELC(2) = 0x001800ff; \ +} while (0) + +/* LCD_D0~LCD_D7, SLCD_RS, SLCD_CS */ +#define __gpio_as_slcd_9bit() \ +do { \ + REG_GPIO_PXFUNS(2) = 0x001801ff; \ + REG_GPIO_PXSELC(2) = 0x001801ff; \ +} while (0) + +/* LCD_D0~LCD_D15, SLCD_RS, SLCD_CS */ +#define __gpio_as_slcd_16bit() \ +do { \ + REG_GPIO_PXFUNS(2) = 0x0018ffff; \ + REG_GPIO_PXSELC(2) = 0x0018ffff; \ +} while (0) + +/* LCD_D0~LCD_D17, SLCD_RS, SLCD_CS */ +#define __gpio_as_slcd_18bit() \ +do { \ + REG_GPIO_PXFUNS(2) = 0x001bffff; \ + REG_GPIO_PXSELC(2) = 0x001bffff; \ +} while (0) + +/* + * CIM_D0~CIM_D7, CIM_MCLK, CIM_PCLK, CIM_VSYNC, CIM_HSYNC + */ +#define __gpio_as_cim() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x0003c0ff; \ + REG_GPIO_PXSELC(3) = 0x0003c0ff; \ + REG_GPIO_PXPES(3) = 0x0003c0ff; \ +} while (0) + +/* + * SDATA_OUT, SDATA_IN, BIT_CLK, SYNC, SCLK_RESET + */ +#define __gpio_as_aic() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x007c0000; \ + REG_GPIO_PXSELS(3) = 0x007c0000; \ + REG_GPIO_PXPES(3) = 0x007c0000; \ +} while (0) + +/* + * MSC_CMD, MSC_CLK, MSC_D0 ~ MSC_D3 + */ +#define __gpio_as_msc() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x00003f00; \ + REG_GPIO_PXSELC(3) = 0x00003f00; \ + REG_GPIO_PXPES(3) = 0x00003f00; \ +} while (0) + +/* + * SSI_CS0, SSI_CLK, SSI_DT, SSI_DR + */ +#define __gpio_as_ssi() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x003c0000; \ + REG_GPIO_PXSELC(3) = 0x003c0000; \ + REG_GPIO_PXPES(3) = 0x003c0000; \ +} while (0) + +/* + * I2C_SCK, I2C_SDA + */ +#define __gpio_as_i2c() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x01800000; \ + REG_GPIO_PXSELS(3) = 0x01800000; \ + REG_GPIO_PXPES(3) = 0x01800000; \ +} while (0) + +/* + * PWM0 + */ +#define __gpio_as_pwm0() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x00800000; \ + REG_GPIO_PXSELC(3) = 0x00800000; \ + REG_GPIO_PXPES(3) = 0x00800000; \ +} while (0) + +/* + * PWM1 + */ +#define __gpio_as_pwm1() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x01000000; \ + REG_GPIO_PXSELC(3) = 0x01000000; \ + REG_GPIO_PXPES(3) = 0x01000000; \ +} while (0) + +/* + * PWM2 + */ +#define __gpio_as_pwm2() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x02000000; \ + REG_GPIO_PXSELC(3) = 0x02000000; \ + REG_GPIO_PXPES(3) = 0x02000000; \ +} while (0) + +/* + * PWM3 + */ +#define __gpio_as_pwm3() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x04000000; \ + REG_GPIO_PXSELC(3) = 0x04000000; \ + REG_GPIO_PXPES(3) = 0x04000000; \ +} while (0) + +/* + * PWM4 + */ +#define __gpio_as_pwm4() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x08000000; \ + REG_GPIO_PXSELC(3) = 0x08000000; \ + REG_GPIO_PXPES(3) = 0x08000000; \ +} while (0) + +/* + * PWM5 + */ +#define __gpio_as_pwm5() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x10000000; \ + REG_GPIO_PXSELC(3) = 0x10000000; \ + REG_GPIO_PXPES(3) = 0x10000000; \ +} while (0) + +/* + * PWM6 + */ +#define __gpio_as_pwm6() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x40000000; \ + REG_GPIO_PXSELC(3) = 0x40000000; \ + REG_GPIO_PXPES(3) = 0x40000000; \ +} while (0) + +/* + * PWM7 + */ +#define __gpio_as_pwm7() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x80000000; \ + REG_GPIO_PXSELC(3) = 0x80000000; \ + REG_GPIO_PXPES(3) = 0x80000000; \ +} while (0) + +/* + * n = 0 ~ 7 + */ +#define __gpio_as_pwm(n) __gpio_as_pwm##n() + +//------------------------------------------- +// GPIO or Interrupt Mode + +#define __gpio_get_port(p) (REG_GPIO_PXPIN(p)) + +#define __gpio_port_as_output(p, o) \ +do { \ + REG_GPIO_PXFUNC(p) = (1 << (o)); \ + REG_GPIO_PXSELC(p) = (1 << (o)); \ + REG_GPIO_PXDIRS(p) = (1 << (o)); \ +} while (0) + +#define __gpio_port_as_input(p, o) \ +do { \ + REG_GPIO_PXFUNC(p) = (1 << (o)); \ + REG_GPIO_PXSELC(p) = (1 << (o)); \ + REG_GPIO_PXDIRC(p) = (1 << (o)); \ +} while (0) + +#define __gpio_as_output(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + __gpio_port_as_output(p, o); \ +} while (0) + +#define __gpio_as_input(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + __gpio_port_as_input(p, o); \ +} while (0) + +#define __gpio_set_pin(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXDATS(p) = (1 << o); \ +} while (0) + +#define __gpio_clear_pin(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXDATC(p) = (1 << o); \ +} while (0) + +#define __gpio_get_pin(n) \ +({ \ + unsigned int p, o, v; \ + p = (n) / 32; \ + o = (n) % 32; \ + if (__gpio_get_port(p) & (1 << o)) \ + v = 1; \ + else \ + v = 0; \ + v; \ +}) + +#define __gpio_as_irq_high_level(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXIMS(p) = (1 << o); \ + REG_GPIO_PXTRGC(p) = (1 << o); \ + REG_GPIO_PXFUNC(p) = (1 << o); \ + REG_GPIO_PXSELS(p) = (1 << o); \ + REG_GPIO_PXDIRS(p) = (1 << o); \ + REG_GPIO_PXFLGC(p) = (1 << o); \ + REG_GPIO_PXIMC(p) = (1 << o); \ +} while (0) + +#define __gpio_as_irq_low_level(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXIMS(p) = (1 << o); \ + REG_GPIO_PXTRGC(p) = (1 << o); \ + REG_GPIO_PXFUNC(p) = (1 << o); \ + REG_GPIO_PXSELS(p) = (1 << o); \ + REG_GPIO_PXDIRC(p) = (1 << o); \ + REG_GPIO_PXFLGC(p) = (1 << o); \ + REG_GPIO_PXIMC(p) = (1 << o); \ +} while (0) + +#define __gpio_as_irq_rise_edge(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXIMS(p) = (1 << o); \ + REG_GPIO_PXTRGS(p) = (1 << o); \ + REG_GPIO_PXFUNC(p) = (1 << o); \ + REG_GPIO_PXSELS(p) = (1 << o); \ + REG_GPIO_PXDIRS(p) = (1 << o); \ + REG_GPIO_PXFLGC(p) = (1 << o); \ + REG_GPIO_PXIMC(p) = (1 << o); \ +} while (0) + +#define __gpio_as_irq_fall_edge(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXIMS(p) = (1 << o); \ + REG_GPIO_PXTRGS(p) = (1 << o); \ + REG_GPIO_PXFUNC(p) = (1 << o); \ + REG_GPIO_PXSELS(p) = (1 << o); \ + REG_GPIO_PXDIRC(p) = (1 << o); \ + REG_GPIO_PXFLGC(p) = (1 << o); \ + REG_GPIO_PXIMC(p) = (1 << o); \ +} while (0) + +#define __gpio_mask_irq(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXIMS(p) = (1 << o); \ +} while (0) + +#define __gpio_unmask_irq(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXIMC(p) = (1 << o); \ +} while (0) + +#define __gpio_ack_irq(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXFLGC(p) = (1 << o); \ +} while (0) + +#define __gpio_get_irq() \ +({ \ + unsigned int p, i, tmp, v = 0; \ + for (p = 3; p >= 0; p--) { \ + tmp = REG_GPIO_PXFLG(p); \ + for (i = 0; i < 32; i++) \ + if (tmp & (1 << i)) \ + v = (32*p + i); \ + } \ + v; \ +}) + +#define __gpio_group_irq(n) \ +({ \ + register int tmp, i; \ + tmp = REG_GPIO_PXFLG((n)); \ + for (i=31;i>=0;i--) \ + if (tmp & (1 << i)) \ + break; \ + i; \ +}) + +#define __gpio_enable_pull(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXPEC(p) = (1 << o); \ +} while (0) + +#define __gpio_disable_pull(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXPES(p) = (1 << o); \ +} while (0) + + +/*************************************************************************** + * CPM + ***************************************************************************/ +#define __cpm_get_pllm() \ + ((REG_CPM_CPPCR & CPM_CPPCR_PLLM_MASK) >> CPM_CPPCR_PLLM_BIT) +#define __cpm_get_plln() \ + ((REG_CPM_CPPCR & CPM_CPPCR_PLLN_MASK) >> CPM_CPPCR_PLLN_BIT) +#define __cpm_get_pllod() \ + ((REG_CPM_CPPCR & CPM_CPPCR_PLLOD_MASK) >> CPM_CPPCR_PLLOD_BIT) + +#define __cpm_get_cdiv() \ + ((REG_CPM_CPCCR & CPM_CPCCR_CDIV_MASK) >> CPM_CPCCR_CDIV_BIT) +#define __cpm_get_hdiv() \ + ((REG_CPM_CPCCR & CPM_CPCCR_HDIV_MASK) >> CPM_CPCCR_HDIV_BIT) +#define __cpm_get_pdiv() \ + ((REG_CPM_CPCCR & CPM_CPCCR_PDIV_MASK) >> CPM_CPCCR_PDIV_BIT) +#define __cpm_get_mdiv() \ + ((REG_CPM_CPCCR & CPM_CPCCR_MDIV_MASK) >> CPM_CPCCR_MDIV_BIT) +#define __cpm_get_ldiv() \ + ((REG_CPM_CPCCR & CPM_CPCCR_LDIV_MASK) >> CPM_CPCCR_LDIV_BIT) +#define __cpm_get_udiv() \ + ((REG_CPM_CPCCR & CPM_CPCCR_UDIV_MASK) >> CPM_CPCCR_UDIV_BIT) +#define __cpm_get_i2sdiv() \ + ((REG_CPM_I2SCDR & CPM_I2SCDR_I2SDIV_MASK) >> CPM_I2SCDR_I2SDIV_BIT) +#define __cpm_get_pixdiv() \ + ((REG_CPM_LPCDR & CPM_LPCDR_PIXDIV_MASK) >> CPM_LPCDR_PIXDIV_BIT) +#define __cpm_get_mscdiv() \ + ((REG_CPM_MSCCDR & CPM_MSCCDR_MSCDIV_MASK) >> CPM_MSCCDR_MSCDIV_BIT) +#define __cpm_get_uhcdiv() \ + ((REG_CPM_UHCCDR & CPM_UHCCDR_UHCDIV_MASK) >> CPM_UHCCDR_UHCDIV_BIT) +#define __cpm_get_ssidiv() \ + ((REG_CPM_SSICCDR & CPM_SSICDR_SSICDIV_MASK) >> CPM_SSICDR_SSIDIV_BIT) + +#define __cpm_set_cdiv(v) \ + (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_CDIV_MASK) | ((v) << (CPM_CPCCR_CDIV_BIT))) +#define __cpm_set_hdiv(v) \ + (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_HDIV_MASK) | ((v) << (CPM_CPCCR_HDIV_BIT))) +#define __cpm_set_pdiv(v) \ + (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_PDIV_MASK) | ((v) << (CPM_CPCCR_PDIV_BIT))) +#define __cpm_set_mdiv(v) \ + (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_MDIV_MASK) | ((v) << (CPM_CPCCR_MDIV_BIT))) +#define __cpm_set_ldiv(v) \ + (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_LDIV_MASK) | ((v) << (CPM_CPCCR_LDIV_BIT))) +#define __cpm_set_udiv(v) \ + (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_UDIV_MASK) | ((v) << (CPM_CPCCR_UDIV_BIT))) +#define __cpm_set_i2sdiv(v) \ + (REG_CPM_I2SCDR = (REG_CPM_I2SCDR & ~CPM_I2SCDR_I2SDIV_MASK) | ((v) << (CPM_I2SCDR_I2SDIV_BIT))) +#define __cpm_set_pixdiv(v) \ + (REG_CPM_LPCDR = (REG_CPM_LPCDR & ~CPM_LPCDR_PIXDIV_MASK) | ((v) << (CPM_LPCDR_PIXDIV_BIT))) +#define __cpm_set_mscdiv(v) \ + (REG_CPM_MSCCDR = (REG_CPM_MSCCDR & ~CPM_MSCCDR_MSCDIV_MASK) | ((v) << (CPM_MSCCDR_MSCDIV_BIT))) +#define __cpm_set_uhcdiv(v) \ + (REG_CPM_UHCCDR = (REG_CPM_UHCCDR & ~CPM_UHCCDR_UHCDIV_MASK) | ((v) << (CPM_UHCCDR_UHCDIV_BIT))) +#define __cpm_ssiclk_select_exclk() \ + (REG_CPM_SSICDR &= ~CPM_SSICDR_SCS) +#define __cpm_ssiclk_select_pllout() \ + (REG_CPM_SSICDR |= CPM_SSICDR_SCS) +#define __cpm_set_ssidiv(v) \ + (REG_CPM_SSICDR = (REG_CPM_SSICDR & ~CPM_SSICDR_SSIDIV_MASK) | ((v) << (CPM_SSICDR_SSIDIV_BIT))) + +#define __cpm_select_i2sclk_exclk() (REG_CPM_CPCCR &= ~CPM_CPCCR_I2CS) +#define __cpm_select_i2sclk_pll() (REG_CPM_CPCCR |= CPM_CPCCR_I2CS) +#define __cpm_enable_cko() (REG_CPM_CPCCR |= CPM_CPCCR_CLKOEN) +#define __cpm_select_usbclk_exclk() (REG_CPM_CPCCR &= ~CPM_CPCCR_UCS) +#define __cpm_select_usbclk_pll() (REG_CPM_CPCCR |= CPM_CPCCR_UCS) +#define __cpm_enable_pll_change() (REG_CPM_CPCCR |= CPM_CPCCR_CE) +#define __cpm_pllout_direct() (REG_CPM_CPCCR |= CPM_CPCCR_PCS) +#define __cpm_pllout_div2() (REG_CPM_CPCCR &= ~CPM_CPCCR_PCS) + +#define __cpm_pll_is_on() (REG_CPM_CPPCR & CPM_CPPCR_PLLS) +#define __cpm_pll_bypass() (REG_CPM_CPPCR |= CPM_CPPCR_PLLBP) +#define __cpm_pll_enable() (REG_CPM_CPPCR |= CPM_CPPCR_PLLEN) + +#define __cpm_get_cclk_doze_duty() \ + ((REG_CPM_LCR & CPM_LCR_DOZE_DUTY_MASK) >> CPM_LCR_DOZE_DUTY_BIT) +#define __cpm_set_cclk_doze_duty(v) \ + (REG_CPM_LCR = (REG_CPM_LCR & ~CPM_LCR_DOZE_DUTY_MASK) | ((v) << (CPM_LCR_DOZE_DUTY_BIT))) + +#define __cpm_doze_mode() (REG_CPM_LCR |= CPM_LCR_DOZE_ON) +#define __cpm_idle_mode() \ + (REG_CPM_LCR = (REG_CPM_LCR & ~CPM_LCR_LPM_MASK) | CPM_LCR_LPM_IDLE) +#define __cpm_sleep_mode() \ + (REG_CPM_LCR = (REG_CPM_LCR & ~CPM_LCR_LPM_MASK) | CPM_LCR_LPM_SLEEP) + +#define __cpm_stop_all() (REG_CPM_CLKGR = 0x7fff) +#define __cpm_stop_uart1() (REG_CPM_CLKGR |= CPM_CLKGR_UART1) +#define __cpm_stop_uhc() (REG_CPM_CLKGR |= CPM_CLKGR_UHC) +#define __cpm_stop_ipu() (REG_CPM_CLKGR |= CPM_CLKGR_IPU) +#define __cpm_stop_dmac() (REG_CPM_CLKGR |= CPM_CLKGR_DMAC) +#define __cpm_stop_udc() (REG_CPM_CLKGR |= CPM_CLKGR_UDC) +#define __cpm_stop_lcd() (REG_CPM_CLKGR |= CPM_CLKGR_LCD) +#define __cpm_stop_cim() (REG_CPM_CLKGR |= CPM_CLKGR_CIM) +#define __cpm_stop_sadc() (REG_CPM_CLKGR |= CPM_CLKGR_SADC) +#define __cpm_stop_msc() (REG_CPM_CLKGR |= CPM_CLKGR_MSC) +#define __cpm_stop_aic1() (REG_CPM_CLKGR |= CPM_CLKGR_AIC1) +#define __cpm_stop_aic2() (REG_CPM_CLKGR |= CPM_CLKGR_AIC2) +#define __cpm_stop_ssi() (REG_CPM_CLKGR |= CPM_CLKGR_SSI) +#define __cpm_stop_i2c() (REG_CPM_CLKGR |= CPM_CLKGR_I2C) +#define __cpm_stop_rtc() (REG_CPM_CLKGR |= CPM_CLKGR_RTC) +#define __cpm_stop_tcu() (REG_CPM_CLKGR |= CPM_CLKGR_TCU) +#define __cpm_stop_uart0() (REG_CPM_CLKGR |= CPM_CLKGR_UART0) + +#define __cpm_start_all() (REG_CPM_CLKGR = 0x0) +#define __cpm_start_uart1() (REG_CPM_CLKGR &= ~CPM_CLKGR_UART1) +#define __cpm_start_uhc() (REG_CPM_CLKGR &= ~CPM_CLKGR_UHC) +#define __cpm_start_ipu() (REG_CPM_CLKGR &= ~CPM_CLKGR_IPU) +#define __cpm_start_dmac() (REG_CPM_CLKGR &= ~CPM_CLKGR_DMAC) +#define __cpm_start_udc() (REG_CPM_CLKGR &= ~CPM_CLKGR_UDC) +#define __cpm_start_lcd() (REG_CPM_CLKGR &= ~CPM_CLKGR_LCD) +#define __cpm_start_cim() (REG_CPM_CLKGR &= ~CPM_CLKGR_CIM) +#define __cpm_start_sadc() (REG_CPM_CLKGR &= ~CPM_CLKGR_SADC) +#define __cpm_start_msc() (REG_CPM_CLKGR &= ~CPM_CLKGR_MSC) +#define __cpm_start_aic1() (REG_CPM_CLKGR &= ~CPM_CLKGR_AIC1) +#define __cpm_start_aic2() (REG_CPM_CLKGR &= ~CPM_CLKGR_AIC2) +#define __cpm_start_ssi() (REG_CPM_CLKGR &= ~CPM_CLKGR_SSI) +#define __cpm_start_i2c() (REG_CPM_CLKGR &= ~CPM_CLKGR_I2C) +#define __cpm_start_rtc() (REG_CPM_CLKGR &= ~CPM_CLKGR_RTC) +#define __cpm_start_tcu() (REG_CPM_CLKGR &= ~CPM_CLKGR_TCU) +#define __cpm_start_uart0() (REG_CPM_CLKGR &= ~CPM_CLKGR_UART0) + +#define __cpm_get_o1st() \ + ((REG_CPM_SCR & CPM_SCR_O1ST_MASK) >> CPM_SCR_O1ST_BIT) +#define __cpm_set_o1st(v) \ + (REG_CPM_SCR = (REG_CPM_SCR & ~CPM_SCR_O1ST_MASK) | ((v) << (CPM_SCR_O1ST_BIT))) +#define __cpm_suspend_usbphy() (REG_CPM_SCR |= CPM_SCR_USBPHY_SUSPEND) +#define __cpm_enable_osc_in_sleep() (REG_CPM_SCR |= CPM_SCR_OSC_ENABLE) + + +/*************************************************************************** + * TCU + ***************************************************************************/ +// where 'n' is the TCU channel +#define __tcu_select_extalclk(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~(TCU_TCSR_EXT_EN | TCU_TCSR_RTC_EN | TCU_TCSR_PCK_EN)) | TCU_TCSR_EXT_EN) +#define __tcu_select_rtcclk(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~(TCU_TCSR_EXT_EN | TCU_TCSR_RTC_EN | TCU_TCSR_PCK_EN)) | TCU_TCSR_RTC_EN) +#define __tcu_select_pclk(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~(TCU_TCSR_EXT_EN | TCU_TCSR_RTC_EN | TCU_TCSR_PCK_EN)) | TCU_TCSR_PCK_EN) + +#define __tcu_select_clk_div1(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE1) +#define __tcu_select_clk_div4(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE4) +#define __tcu_select_clk_div16(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE16) +#define __tcu_select_clk_div64(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE64) +#define __tcu_select_clk_div256(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE256) +#define __tcu_select_clk_div1024(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE1024) + +#define __tcu_enable_pwm_output(n) ( REG_TCU_TCSR((n)) |= TCU_TCSR_PWM_EN ) +#define __tcu_disable_pwm_output(n) ( REG_TCU_TCSR((n)) &= ~TCU_TCSR_PWM_EN ) + +#define __tcu_init_pwm_output_high(n) ( REG_TCU_TCSR((n)) |= TCU_TCSR_PWM_INITL_HIGH ) +#define __tcu_init_pwm_output_low(n) ( REG_TCU_TCSR((n)) &= ~TCU_TCSR_PWM_INITL_HIGH ) + +#define __tcu_set_pwm_output_shutdown_graceful(n) ( REG_TCU_TCSR((n)) &= ~TCU_TCSR_PWM_SD ) +#define __tcu_set_pwm_output_shutdown_abrupt(n) ( REG_TCU_TCSR((n)) |= TCU_TCSR_PWM_SD ) + +#define __tcu_start_counter(n) ( REG_TCU_TESR |= (1 << (n)) ) +#define __tcu_stop_counter(n) ( REG_TCU_TECR |= (1 << (n)) ) + +#define __tcu_half_match_flag(n) ( REG_TCU_TFR & (1 << ((n) + 16)) ) +#define __tcu_full_match_flag(n) ( REG_TCU_TFR & (1 << (n)) ) +#define __tcu_set_half_match_flag(n) ( REG_TCU_TFSR = (1 << ((n) + 16)) ) +#define __tcu_set_full_match_flag(n) ( REG_TCU_TFSR = (1 << (n)) ) +#define __tcu_clear_half_match_flag(n) ( REG_TCU_TFCR = (1 << ((n) + 16)) ) +#define __tcu_clear_full_match_flag(n) ( REG_TCU_TFCR = (1 << (n)) ) +#define __tcu_mask_half_match_irq(n) ( REG_TCU_TMSR = (1 << ((n) + 16)) ) +#define __tcu_mask_full_match_irq(n) ( REG_TCU_TMSR = (1 << (n)) ) +#define __tcu_unmask_half_match_irq(n) ( REG_TCU_TMCR = (1 << ((n) + 16)) ) +#define __tcu_unmask_full_match_irq(n) ( REG_TCU_TMCR = (1 << (n)) ) + +#define __tcu_wdt_clock_stopped() ( REG_TCU_TSR & TCU_TSSR_WDTSC ) +#define __tcu_timer_clock_stopped(n) ( REG_TCU_TSR & (1 << (n)) ) + +#define __tcu_start_wdt_clock() ( REG_TCU_TSCR = TCU_TSSR_WDTSC ) +#define __tcu_start_timer_clock(n) ( REG_TCU_TSCR = (1 << (n)) ) + +#define __tcu_stop_wdt_clock() ( REG_TCU_TSSR = TCU_TSSR_WDTSC ) +#define __tcu_stop_timer_clock(n) ( REG_TCU_TSSR = (1 << (n)) ) + +#define __tcu_get_count(n) ( REG_TCU_TCNT((n)) ) +#define __tcu_set_count(n,v) ( REG_TCU_TCNT((n)) = (v) ) +#define __tcu_set_full_data(n,v) ( REG_TCU_TDFR((n)) = (v) ) +#define __tcu_set_half_data(n,v) ( REG_TCU_TDHR((n)) = (v) ) + + +/*************************************************************************** + * WDT + ***************************************************************************/ +#define __wdt_start() ( REG_WDT_TCER |= WDT_TCER_TCEN ) +#define __wdt_stop() ( REG_WDT_TCER &= ~WDT_TCER_TCEN ) +#define __wdt_set_count(v) ( REG_WDT_TCNT = (v) ) +#define __wdt_set_data(v) ( REG_WDT_TDR = (v) ) + +#define __wdt_select_extalclk() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~(WDT_TCSR_EXT_EN | WDT_TCSR_RTC_EN | WDT_TCSR_PCK_EN)) | WDT_TCSR_EXT_EN) +#define __wdt_select_rtcclk() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~(WDT_TCSR_EXT_EN | WDT_TCSR_RTC_EN | WDT_TCSR_PCK_EN)) | WDT_TCSR_RTC_EN) +#define __wdt_select_pclk() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~(WDT_TCSR_EXT_EN | WDT_TCSR_RTC_EN | WDT_TCSR_PCK_EN)) | WDT_TCSR_PCK_EN) + +#define __wdt_select_clk_div1() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE1) +#define __wdt_select_clk_div4() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE4) +#define __wdt_select_clk_div16() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE16) +#define __wdt_select_clk_div64() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE64) +#define __wdt_select_clk_div256() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE256) +#define __wdt_select_clk_div1024() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE1024) + + +/*************************************************************************** + * UART + ***************************************************************************/ + +#define __uart_enable(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_FCR) |= UARTFCR_UUE | UARTFCR_FE ) +#define __uart_disable(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_FCR) = ~UARTFCR_UUE ) + +#define __uart_enable_transmit_irq(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) |= UARTIER_TIE ) +#define __uart_disable_transmit_irq(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) &= ~UARTIER_TIE ) + +#define __uart_enable_receive_irq(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) |= UARTIER_RIE | UARTIER_RLIE | UARTIER_RTIE ) +#define __uart_disable_receive_irq(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) &= ~(UARTIER_RIE | UARTIER_RLIE | UARTIER_RTIE) ) + +#define __uart_enable_loopback(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_MCR) |= UARTMCR_LOOP ) +#define __uart_disable_loopback(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_MCR) &= ~UARTMCR_LOOP ) + +#define __uart_set_8n1(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_LCR) = UARTLCR_WLEN_8 ) + +#define __uart_set_baud(n, devclk, baud) \ + do { \ + REG8(UART_BASE + UART_OFF*(n) + OFF_LCR) |= UARTLCR_DLAB; \ + REG8(UART_BASE + UART_OFF*(n) + OFF_DLLR) = (devclk / 16 / baud) & 0xff; \ + REG8(UART_BASE + UART_OFF*(n) + OFF_DLHR) = ((devclk / 16 / baud) >> 8) & 0xff; \ + REG8(UART_BASE + UART_OFF*(n) + OFF_LCR) &= ~UARTLCR_DLAB; \ + } while (0) + +#define __uart_parity_error(n) \ + ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_PER) != 0 ) + +#define __uart_clear_errors(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) &= ~(UARTLSR_ORER | UARTLSR_BRK | UARTLSR_FER | UARTLSR_PER | UARTLSR_RFER) ) + +#define __uart_transmit_fifo_empty(n) \ + ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_TDRQ) != 0 ) + +#define __uart_transmit_end(n) \ + ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_TEMT) != 0 ) + +#define __uart_transmit_char(n, ch) \ + REG8(UART_BASE + UART_OFF*(n) + OFF_TDR) = (ch) + +#define __uart_receive_fifo_full(n) \ + ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_DR) != 0 ) + +#define __uart_receive_ready(n) \ + ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_DR) != 0 ) + +#define __uart_receive_char(n) \ + REG8(UART_BASE + UART_OFF*(n) + OFF_RDR) + +#define __uart_disable_irda() \ + ( REG8(IRDA_BASE + OFF_SIRCR) &= ~(SIRCR_TSIRE | SIRCR_RSIRE) ) +#define __uart_enable_irda() \ + /* Tx high pulse as 0, Rx low pulse as 0 */ \ + ( REG8(IRDA_BASE + OFF_SIRCR) = SIRCR_TSIRE | SIRCR_RSIRE | SIRCR_RXPL | SIRCR_TPWS ) + + +/*************************************************************************** + * DMAC + ***************************************************************************/ + +/* n is the DMA channel (0 - 5) */ + +#define __dmac_enable_module() \ + ( REG_DMAC_DMACR |= DMAC_DMACR_DMAE | DMAC_DMACR_PR_RR ) +#define __dmac_disable_module() \ + ( REG_DMAC_DMACR &= ~DMAC_DMACR_DMAE ) + +/* p=0,1,2,3 */ +#define __dmac_set_priority(p) \ +do { \ + REG_DMAC_DMACR &= ~DMAC_DMACR_PR_MASK; \ + REG_DMAC_DMACR |= ((p) << DMAC_DMACR_PR_BIT); \ +} while (0) + +#define __dmac_test_halt_error() ( REG_DMAC_DMACR & DMAC_DMACR_HLT ) +#define __dmac_test_addr_error() ( REG_DMAC_DMACR & DMAC_DMACR_AR ) + +#define __dmac_enable_descriptor(n) \ + ( REG_DMAC_DCCSR((n)) &= ~DMAC_DCCSR_NDES ) +#define __dmac_disable_descriptor(n) \ + ( REG_DMAC_DCCSR((n)) |= DMAC_DCCSR_NDES ) + +#define __dmac_enable_channel(n) \ + ( REG_DMAC_DCCSR((n)) |= DMAC_DCCSR_EN ) +#define __dmac_disable_channel(n) \ + ( REG_DMAC_DCCSR((n)) &= ~DMAC_DCCSR_EN ) +#define __dmac_channel_enabled(n) \ + ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_EN ) + +#define __dmac_channel_enable_irq(n) \ + ( REG_DMAC_DCMD((n)) |= DMAC_DCMD_TIE ) +#define __dmac_channel_disable_irq(n) \ + ( REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_TIE ) + +#define __dmac_channel_transmit_halt_detected(n) \ + ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_HLT ) +#define __dmac_channel_transmit_end_detected(n) \ + ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_TT ) +#define __dmac_channel_address_error_detected(n) \ + ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_AR ) +#define __dmac_channel_count_terminated_detected(n) \ + ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_CT ) +#define __dmac_channel_descriptor_invalid_detected(n) \ + ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_INV ) + +#define __dmac_channel_clear_transmit_halt(n) \ + ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_HLT ) +#define __dmac_channel_clear_transmit_end(n) \ + ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_TT ) +#define __dmac_channel_clear_address_error(n) \ + ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_AR ) +#define __dmac_channel_clear_count_terminated(n) \ + ( REG_DMAC_DCCSR((n)) &= ~DMAC_DCCSR_CT ) +#define __dmac_channel_clear_descriptor_invalid(n) \ + ( REG_DMAC_DCCSR((n)) &= ~DMAC_DCCSR_INV ) + +#define __dmac_channel_set_single_mode(n) \ + ( REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_TM ) +#define __dmac_channel_set_block_mode(n) \ + ( REG_DMAC_DCMD((n)) |= DMAC_DCMD_TM ) + +#define __dmac_channel_set_transfer_unit_32bit(n) \ +do { \ + REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ + REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_32BIT; \ +} while (0) + +#define __dmac_channel_set_transfer_unit_16bit(n) \ +do { \ + REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ + REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_16BIT; \ +} while (0) + +#define __dmac_channel_set_transfer_unit_8bit(n) \ +do { \ + REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ + REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_8BIT; \ +} while (0) + +#define __dmac_channel_set_transfer_unit_16byte(n) \ +do { \ + REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ + REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_16BYTE; \ +} while (0) + +#define __dmac_channel_set_transfer_unit_32byte(n) \ +do { \ + REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ + REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_32BYTE; \ +} while (0) + +/* w=8,16,32 */ +#define __dmac_channel_set_dest_port_width(n,w) \ +do { \ + REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DWDH_MASK; \ + REG_DMAC_DCMD((n)) |= DMAC_DCMD_DWDH_##w; \ +} while (0) + +/* w=8,16,32 */ +#define __dmac_channel_set_src_port_width(n,w) \ +do { \ + REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_SWDH_MASK; \ + REG_DMAC_DCMD((n)) |= DMAC_DCMD_SWDH_##w; \ +} while (0) + +/* v=0-15 */ +#define __dmac_channel_set_rdil(n,v) \ +do { \ + REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_RDIL_MASK; \ + REG_DMAC_DCMD((n) |= ((v) << DMAC_DCMD_RDIL_BIT); \ +} while (0) + +#define __dmac_channel_dest_addr_fixed(n) \ + ( REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DAI ) +#define __dmac_channel_dest_addr_increment(n) \ + ( REG_DMAC_DCMD((n)) |= DMAC_DCMD_DAI ) + +#define __dmac_channel_src_addr_fixed(n) \ + ( REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_SAI ) +#define __dmac_channel_src_addr_increment(n) \ + ( REG_DMAC_DCMD((n)) |= DMAC_DCMD_SAI ) + +#define __dmac_channel_set_doorbell(n) \ + ( REG_DMAC_DMADBSR = (1 << (n)) ) + +#define __dmac_channel_irq_detected(n) ( REG_DMAC_DMAIPR & (1 << (n)) ) +#define __dmac_channel_ack_irq(n) ( REG_DMAC_DMAIPR &= ~(1 << (n)) ) + +static __inline__ int __dmac_get_irq(void) +{ + int i; + for (i = 0; i < MAX_DMA_NUM; i++) + if (__dmac_channel_irq_detected(i)) + return i; + return -1; +} + + +/*************************************************************************** + * AIC (AC'97 & I2S Controller) + ***************************************************************************/ + +#define __aic_enable() ( REG_AIC_FR |= AIC_FR_ENB ) +#define __aic_disable() ( REG_AIC_FR &= ~AIC_FR_ENB ) + +#define __aic_select_ac97() ( REG_AIC_FR &= ~AIC_FR_AUSEL ) +#define __aic_select_i2s() ( REG_AIC_FR |= AIC_FR_AUSEL ) + +#define __aic_play_zero() ( REG_AIC_FR &= ~AIC_FR_LSMP ) +#define __aic_play_lastsample() ( REG_AIC_FR |= AIC_FR_LSMP ) + +#define __i2s_as_master() ( REG_AIC_FR |= AIC_FR_BCKD | AIC_FR_SYNCD ) +#define __i2s_as_slave() ( REG_AIC_FR &= ~(AIC_FR_BCKD | AIC_FR_SYNCD) ) +#define __aic_reset_status() ( REG_AIC_FR & AIC_FR_RST ) + +#define __aic_reset() \ +do { \ + REG_AIC_FR |= AIC_FR_RST; \ +} while(0) + + +#define __aic_set_transmit_trigger(n) \ +do { \ + REG_AIC_FR &= ~AIC_FR_TFTH_MASK; \ + REG_AIC_FR |= ((n) << AIC_FR_TFTH_BIT); \ +} while(0) + +#define __aic_set_receive_trigger(n) \ +do { \ + REG_AIC_FR &= ~AIC_FR_RFTH_MASK; \ + REG_AIC_FR |= ((n) << AIC_FR_RFTH_BIT); \ +} while(0) + +#define __aic_enable_record() ( REG_AIC_CR |= AIC_CR_EREC ) +#define __aic_disable_record() ( REG_AIC_CR &= ~AIC_CR_EREC ) +#define __aic_enable_replay() ( REG_AIC_CR |= AIC_CR_ERPL ) +#define __aic_disable_replay() ( REG_AIC_CR &= ~AIC_CR_ERPL ) +#define __aic_enable_loopback() ( REG_AIC_CR |= AIC_CR_ENLBF ) +#define __aic_disable_loopback() ( REG_AIC_CR &= ~AIC_CR_ENLBF ) + +#define __aic_flush_fifo() ( REG_AIC_CR |= AIC_CR_FLUSH ) +#define __aic_unflush_fifo() ( REG_AIC_CR &= ~AIC_CR_FLUSH ) + +#define __aic_enable_transmit_intr() \ + ( REG_AIC_CR |= (AIC_CR_ETFS | AIC_CR_ETUR) ) +#define __aic_disable_transmit_intr() \ + ( REG_AIC_CR &= ~(AIC_CR_ETFS | AIC_CR_ETUR) ) +#define __aic_enable_receive_intr() \ + ( REG_AIC_CR |= (AIC_CR_ERFS | AIC_CR_EROR) ) +#define __aic_disable_receive_intr() \ + ( REG_AIC_CR &= ~(AIC_CR_ERFS | AIC_CR_EROR) ) + +#define __aic_enable_transmit_dma() ( REG_AIC_CR |= AIC_CR_TDMS ) +#define __aic_disable_transmit_dma() ( REG_AIC_CR &= ~AIC_CR_TDMS ) +#define __aic_enable_receive_dma() ( REG_AIC_CR |= AIC_CR_RDMS ) +#define __aic_disable_receive_dma() ( REG_AIC_CR &= ~AIC_CR_RDMS ) + +#define __aic_enable_mono2stereo() ( REG_AIC_CR |= AIC_CR_M2S ) +#define __aic_disable_mono2stereo() ( REG_AIC_CR &= ~AIC_CR_M2S ) +#define __aic_enable_byteswap() ( REG_AIC_CR |= AIC_CR_ENDSW ) +#define __aic_disable_byteswap() ( REG_AIC_CR &= ~AIC_CR_ENDSW ) +#define __aic_enable_unsignadj() ( REG_AIC_CR |= AIC_CR_AVSTSU ) +#define __aic_disable_unsignadj() ( REG_AIC_CR &= ~AIC_CR_AVSTSU ) + +#define AC97_PCM_XS_L_FRONT AIC_ACCR1_XS_SLOT3 +#define AC97_PCM_XS_R_FRONT AIC_ACCR1_XS_SLOT4 +#define AC97_PCM_XS_CENTER AIC_ACCR1_XS_SLOT6 +#define AC97_PCM_XS_L_SURR AIC_ACCR1_XS_SLOT7 +#define AC97_PCM_XS_R_SURR AIC_ACCR1_XS_SLOT8 +#define AC97_PCM_XS_LFE AIC_ACCR1_XS_SLOT9 + +#define AC97_PCM_RS_L_FRONT AIC_ACCR1_RS_SLOT3 +#define AC97_PCM_RS_R_FRONT AIC_ACCR1_RS_SLOT4 +#define AC97_PCM_RS_CENTER AIC_ACCR1_RS_SLOT6 +#define AC97_PCM_RS_L_SURR AIC_ACCR1_RS_SLOT7 +#define AC97_PCM_RS_R_SURR AIC_ACCR1_RS_SLOT8 +#define AC97_PCM_RS_LFE AIC_ACCR1_RS_SLOT9 + +#define __ac97_set_xs_none() ( REG_AIC_ACCR1 &= ~AIC_ACCR1_XS_MASK ) +#define __ac97_set_xs_mono() \ +do { \ + REG_AIC_ACCR1 &= ~AIC_ACCR1_XS_MASK; \ + REG_AIC_ACCR1 |= AC97_PCM_XS_R_FRONT; \ +} while(0) +#define __ac97_set_xs_stereo() \ +do { \ + REG_AIC_ACCR1 &= ~AIC_ACCR1_XS_MASK; \ + REG_AIC_ACCR1 |= AC97_PCM_XS_L_FRONT | AC97_PCM_XS_R_FRONT; \ +} while(0) + +/* In fact, only stereo is support now. */ +#define __ac97_set_rs_none() ( REG_AIC_ACCR1 &= ~AIC_ACCR1_RS_MASK ) +#define __ac97_set_rs_mono() \ +do { \ + REG_AIC_ACCR1 &= ~AIC_ACCR1_RS_MASK; \ + REG_AIC_ACCR1 |= AC97_PCM_RS_R_FRONT; \ +} while(0) +#define __ac97_set_rs_stereo() \ +do { \ + REG_AIC_ACCR1 &= ~AIC_ACCR1_RS_MASK; \ + REG_AIC_ACCR1 |= AC97_PCM_RS_L_FRONT | AC97_PCM_RS_R_FRONT; \ +} while(0) + +#define __ac97_warm_reset_codec() \ + do { \ + REG_AIC_ACCR2 |= AIC_ACCR2_SA; \ + REG_AIC_ACCR2 |= AIC_ACCR2_SS; \ + udelay(2); \ + REG_AIC_ACCR2 &= ~AIC_ACCR2_SS; \ + REG_AIC_ACCR2 &= ~AIC_ACCR2_SA; \ + } while (0) + +#define __ac97_cold_reset_codec() \ + do { \ + REG_AIC_ACCR2 |= AIC_ACCR2_SR; \ + udelay(2); \ + REG_AIC_ACCR2 &= ~AIC_ACCR2_SR; \ + } while (0) + +/* n=8,16,18,20 */ +#define __ac97_set_iass(n) \ + ( REG_AIC_ACCR2 = (REG_AIC_ACCR2 & ~AIC_ACCR2_IASS_MASK) | AIC_ACCR2_IASS_##n##BIT ) +#define __ac97_set_oass(n) \ + ( REG_AIC_ACCR2 = (REG_AIC_ACCR2 & ~AIC_ACCR2_OASS_MASK) | AIC_ACCR2_OASS_##n##BIT ) + +#define __i2s_select_i2s() ( REG_AIC_I2SCR &= ~AIC_I2SCR_AMSL ) +#define __i2s_select_msbjustified() ( REG_AIC_I2SCR |= AIC_I2SCR_AMSL ) + +/* n=8,16,18,20,24 */ +/*#define __i2s_set_sample_size(n) \ + ( REG_AIC_I2SCR |= (REG_AIC_I2SCR & ~AIC_I2SCR_WL_MASK) | AIC_I2SCR_WL_##n##BIT )*/ + +#define __i2s_set_oss_sample_size(n) \ + ( REG_AIC_CR = (REG_AIC_CR & ~AIC_CR_OSS_MASK) | AIC_CR_OSS_##n##BIT ) +#define __i2s_set_iss_sample_size(n) \ + ( REG_AIC_CR = (REG_AIC_CR & ~AIC_CR_ISS_MASK) | AIC_CR_ISS_##n##BIT ) + +#define __i2s_stop_bitclk() ( REG_AIC_I2SCR |= AIC_I2SCR_STPBK ) +#define __i2s_start_bitclk() ( REG_AIC_I2SCR &= ~AIC_I2SCR_STPBK ) + +#define __aic_transmit_request() ( REG_AIC_SR & AIC_SR_TFS ) +#define __aic_receive_request() ( REG_AIC_SR & AIC_SR_RFS ) +#define __aic_transmit_underrun() ( REG_AIC_SR & AIC_SR_TUR ) +#define __aic_receive_overrun() ( REG_AIC_SR & AIC_SR_ROR ) + +#define __aic_clear_errors() ( REG_AIC_SR &= ~(AIC_SR_TUR | AIC_SR_ROR) ) + +#define __aic_get_transmit_resident() \ + ( (REG_AIC_SR & AIC_SR_TFL_MASK) >> AIC_SR_TFL_BIT ) +#define __aic_get_receive_count() \ + ( (REG_AIC_SR & AIC_SR_RFL_MASK) >> AIC_SR_RFL_BIT ) + +#define __ac97_command_transmitted() ( REG_AIC_ACSR & AIC_ACSR_CADT ) +#define __ac97_status_received() ( REG_AIC_ACSR & AIC_ACSR_SADR ) +#define __ac97_status_receive_timeout() ( REG_AIC_ACSR & AIC_ACSR_RSTO ) +#define __ac97_codec_is_low_power_mode() ( REG_AIC_ACSR & AIC_ACSR_CLPM ) +#define __ac97_codec_is_ready() ( REG_AIC_ACSR & AIC_ACSR_CRDY ) +#define __ac97_slot_error_detected() ( REG_AIC_ACSR & AIC_ACSR_SLTERR ) +#define __ac97_clear_slot_error() ( REG_AIC_ACSR &= ~AIC_ACSR_SLTERR ) + +#define __i2s_is_busy() ( REG_AIC_I2SSR & AIC_I2SSR_BSY ) + +#define CODEC_READ_CMD (1 << 19) +#define CODEC_WRITE_CMD (0 << 19) +#define CODEC_REG_INDEX_BIT 12 +#define CODEC_REG_INDEX_MASK (0x7f << CODEC_REG_INDEX_BIT) /* 18:12 */ +#define CODEC_REG_DATA_BIT 4 +#define CODEC_REG_DATA_MASK (0x0ffff << 4) /* 19:4 */ + +#define __ac97_out_rcmd_addr(reg) \ +do { \ + REG_AIC_ACCAR = CODEC_READ_CMD | ((reg) << CODEC_REG_INDEX_BIT); \ +} while (0) + +#define __ac97_out_wcmd_addr(reg) \ +do { \ + REG_AIC_ACCAR = CODEC_WRITE_CMD | ((reg) << CODEC_REG_INDEX_BIT); \ +} while (0) + +#define __ac97_out_data(value) \ +do { \ + REG_AIC_ACCDR = ((value) << CODEC_REG_DATA_BIT); \ +} while (0) + +#define __ac97_in_data() \ + ( (REG_AIC_ACSDR & CODEC_REG_DATA_MASK) >> CODEC_REG_DATA_BIT ) + +#define __ac97_in_status_addr() \ + ( (REG_AIC_ACSAR & CODEC_REG_INDEX_MASK) >> CODEC_REG_INDEX_BIT ) + +#define __i2s_set_sample_rate(i2sclk, sync) \ + ( REG_AIC_I2SDIV = ((i2sclk) / (4*64)) / (sync) ) + +#define __aic_write_tfifo(v) ( REG_AIC_DR = (v) ) +#define __aic_read_rfifo() ( REG_AIC_DR ) + +#define __aic_internal_codec() ( REG_AIC_FR |= AIC_FR_ICDC ) +#define __aic_external_codec() ( REG_AIC_FR &= ~AIC_FR_ICDC ) + +// +// Define next ops for AC97 compatible +// + +#define AC97_ACSR AIC_ACSR + +#define __ac97_enable() __aic_enable(); __aic_select_ac97() +#define __ac97_disable() __aic_disable() +#define __ac97_reset() __aic_reset() + +#define __ac97_set_transmit_trigger(n) __aic_set_transmit_trigger(n) +#define __ac97_set_receive_trigger(n) __aic_set_receive_trigger(n) + +#define __ac97_enable_record() __aic_enable_record() +#define __ac97_disable_record() __aic_disable_record() +#define __ac97_enable_replay() __aic_enable_replay() +#define __ac97_disable_replay() __aic_disable_replay() +#define __ac97_enable_loopback() __aic_enable_loopback() +#define __ac97_disable_loopback() __aic_disable_loopback() + +#define __ac97_enable_transmit_dma() __aic_enable_transmit_dma() +#define __ac97_disable_transmit_dma() __aic_disable_transmit_dma() +#define __ac97_enable_receive_dma() __aic_enable_receive_dma() +#define __ac97_disable_receive_dma() __aic_disable_receive_dma() + +#define __ac97_transmit_request() __aic_transmit_request() +#define __ac97_receive_request() __aic_receive_request() +#define __ac97_transmit_underrun() __aic_transmit_underrun() +#define __ac97_receive_overrun() __aic_receive_overrun() + +#define __ac97_clear_errors() __aic_clear_errors() + +#define __ac97_get_transmit_resident() __aic_get_transmit_resident() +#define __ac97_get_receive_count() __aic_get_receive_count() + +#define __ac97_enable_transmit_intr() __aic_enable_transmit_intr() +#define __ac97_disable_transmit_intr() __aic_disable_transmit_intr() +#define __ac97_enable_receive_intr() __aic_enable_receive_intr() +#define __ac97_disable_receive_intr() __aic_disable_receive_intr() + +#define __ac97_write_tfifo(v) __aic_write_tfifo(v) +#define __ac97_read_rfifo() __aic_read_rfifo() + +// +// Define next ops for I2S compatible +// + +#define I2S_ACSR AIC_I2SSR + +#define __i2s_enable() __aic_enable(); __aic_select_i2s() +#define __i2s_disable() __aic_disable() +#define __i2s_reset() __aic_reset() + +#define __i2s_set_transmit_trigger(n) __aic_set_transmit_trigger(n) +#define __i2s_set_receive_trigger(n) __aic_set_receive_trigger(n) + +#define __i2s_enable_record() __aic_enable_record() +#define __i2s_disable_record() __aic_disable_record() +#define __i2s_enable_replay() __aic_enable_replay() +#define __i2s_disable_replay() __aic_disable_replay() +#define __i2s_enable_loopback() __aic_enable_loopback() +#define __i2s_disable_loopback() __aic_disable_loopback() + +#define __i2s_enable_transmit_dma() __aic_enable_transmit_dma() +#define __i2s_disable_transmit_dma() __aic_disable_transmit_dma() +#define __i2s_enable_receive_dma() __aic_enable_receive_dma() +#define __i2s_disable_receive_dma() __aic_disable_receive_dma() + +#define __i2s_transmit_request() __aic_transmit_request() +#define __i2s_receive_request() __aic_receive_request() +#define __i2s_transmit_underrun() __aic_transmit_underrun() +#define __i2s_receive_overrun() __aic_receive_overrun() + +#define __i2s_clear_errors() __aic_clear_errors() + +#define __i2s_get_transmit_resident() __aic_get_transmit_resident() +#define __i2s_get_receive_count() __aic_get_receive_count() + +#define __i2s_enable_transmit_intr() __aic_enable_transmit_intr() +#define __i2s_disable_transmit_intr() __aic_disable_transmit_intr() +#define __i2s_enable_receive_intr() __aic_enable_receive_intr() +#define __i2s_disable_receive_intr() __aic_disable_receive_intr() + +#define __i2s_write_tfifo(v) __aic_write_tfifo(v) +#define __i2s_read_rfifo() __aic_read_rfifo() + +#define __i2s_reset_codec() \ + do { \ + } while (0) + + +/*************************************************************************** + * ICDC + ***************************************************************************/ +#define __i2s_internal_codec() __aic_internal_codec() +#define __i2s_external_codec() __aic_external_codec() + +/*************************************************************************** + * INTC + ***************************************************************************/ +#define __intc_unmask_irq(n) ( REG_INTC_IMCR = (1 << (n)) ) +#define __intc_mask_irq(n) ( REG_INTC_IMSR = (1 << (n)) ) +#define __intc_ack_irq(n) ( REG_INTC_IPR = (1 << (n)) ) + + +/*************************************************************************** + * I2C + ***************************************************************************/ + +#define __i2c_enable() ( REG_I2C_CR |= I2C_CR_I2CE ) +#define __i2c_disable() ( REG_I2C_CR &= ~I2C_CR_I2CE ) + +#define __i2c_send_start() ( REG_I2C_CR |= I2C_CR_STA ) +#define __i2c_send_stop() ( REG_I2C_CR |= I2C_CR_STO ) +#define __i2c_send_ack() ( REG_I2C_CR &= ~I2C_CR_AC ) +#define __i2c_send_nack() ( REG_I2C_CR |= I2C_CR_AC ) + +#define __i2c_set_drf() ( REG_I2C_SR |= I2C_SR_DRF ) +#define __i2c_clear_drf() ( REG_I2C_SR &= ~I2C_SR_DRF ) +#define __i2c_check_drf() ( REG_I2C_SR & I2C_SR_DRF ) + +#define __i2c_received_ack() ( !(REG_I2C_SR & I2C_SR_ACKF) ) +#define __i2c_is_busy() ( REG_I2C_SR & I2C_SR_BUSY ) +#define __i2c_transmit_ended() ( REG_I2C_SR & I2C_SR_TEND ) + +#define __i2c_set_clk(dev_clk, i2c_clk) \ + ( REG_I2C_GR = (dev_clk) / (16*(i2c_clk)) - 1 ) + +#define __i2c_read() ( REG_I2C_DR ) +#define __i2c_write(val) ( REG_I2C_DR = (val) ) + + +/*************************************************************************** + * MSC + ***************************************************************************/ + +#define __msc_start_op() \ + ( REG_MSC_STRPCL = MSC_STRPCL_START_OP | MSC_STRPCL_CLOCK_CONTROL_START ) + +#define __msc_set_resto(to) ( REG_MSC_RESTO = to ) +#define __msc_set_rdto(to) ( REG_MSC_RDTO = to ) +#define __msc_set_cmd(cmd) ( REG_MSC_CMD = cmd ) +#define __msc_set_arg(arg) ( REG_MSC_ARG = arg ) +#define __msc_set_nob(nob) ( REG_MSC_NOB = nob ) +#define __msc_get_nob() ( REG_MSC_NOB ) +#define __msc_set_blklen(len) ( REG_MSC_BLKLEN = len ) +#define __msc_set_cmdat(cmdat) ( REG_MSC_CMDAT = cmdat ) +#define __msc_set_cmdat_ioabort() ( REG_MSC_CMDAT |= MSC_CMDAT_IO_ABORT ) +#define __msc_clear_cmdat_ioabort() ( REG_MSC_CMDAT &= ~MSC_CMDAT_IO_ABORT ) + +#define __msc_set_cmdat_bus_width1() \ +do { \ + REG_MSC_CMDAT &= ~MSC_CMDAT_BUS_WIDTH_MASK; \ + REG_MSC_CMDAT |= MSC_CMDAT_BUS_WIDTH_1BIT; \ +} while(0) + +#define __msc_set_cmdat_bus_width4() \ +do { \ + REG_MSC_CMDAT &= ~MSC_CMDAT_BUS_WIDTH_MASK; \ + REG_MSC_CMDAT |= MSC_CMDAT_BUS_WIDTH_4BIT; \ +} while(0) + +#define __msc_set_cmdat_dma_en() ( REG_MSC_CMDAT |= MSC_CMDAT_DMA_EN ) +#define __msc_set_cmdat_init() ( REG_MSC_CMDAT |= MSC_CMDAT_INIT ) +#define __msc_set_cmdat_busy() ( REG_MSC_CMDAT |= MSC_CMDAT_BUSY ) +#define __msc_set_cmdat_stream() ( REG_MSC_CMDAT |= MSC_CMDAT_STREAM_BLOCK ) +#define __msc_set_cmdat_block() ( REG_MSC_CMDAT &= ~MSC_CMDAT_STREAM_BLOCK ) +#define __msc_set_cmdat_read() ( REG_MSC_CMDAT &= ~MSC_CMDAT_WRITE_READ ) +#define __msc_set_cmdat_write() ( REG_MSC_CMDAT |= MSC_CMDAT_WRITE_READ ) +#define __msc_set_cmdat_data_en() ( REG_MSC_CMDAT |= MSC_CMDAT_DATA_EN ) + +/* r is MSC_CMDAT_RESPONSE_FORMAT_Rx or MSC_CMDAT_RESPONSE_FORMAT_NONE */ +#define __msc_set_cmdat_res_format(r) \ +do { \ + REG_MSC_CMDAT &= ~MSC_CMDAT_RESPONSE_FORMAT_MASK; \ + REG_MSC_CMDAT |= (r); \ +} while(0) + +#define __msc_clear_cmdat() \ + REG_MSC_CMDAT &= ~( MSC_CMDAT_IO_ABORT | MSC_CMDAT_DMA_EN | MSC_CMDAT_INIT| \ + MSC_CMDAT_BUSY | MSC_CMDAT_STREAM_BLOCK | MSC_CMDAT_WRITE_READ | \ + MSC_CMDAT_DATA_EN | MSC_CMDAT_RESPONSE_FORMAT_MASK ) + +#define __msc_get_imask() ( REG_MSC_IMASK ) +#define __msc_mask_all_intrs() ( REG_MSC_IMASK = 0xff ) +#define __msc_unmask_all_intrs() ( REG_MSC_IMASK = 0x00 ) +#define __msc_mask_rd() ( REG_MSC_IMASK |= MSC_IMASK_RXFIFO_RD_REQ ) +#define __msc_unmask_rd() ( REG_MSC_IMASK &= ~MSC_IMASK_RXFIFO_RD_REQ ) +#define __msc_mask_wr() ( REG_MSC_IMASK |= MSC_IMASK_TXFIFO_WR_REQ ) +#define __msc_unmask_wr() ( REG_MSC_IMASK &= ~MSC_IMASK_TXFIFO_WR_REQ ) +#define __msc_mask_endcmdres() ( REG_MSC_IMASK |= MSC_IMASK_END_CMD_RES ) +#define __msc_unmask_endcmdres() ( REG_MSC_IMASK &= ~MSC_IMASK_END_CMD_RES ) +#define __msc_mask_datatrandone() ( REG_MSC_IMASK |= MSC_IMASK_DATA_TRAN_DONE ) +#define __msc_unmask_datatrandone() ( REG_MSC_IMASK &= ~MSC_IMASK_DATA_TRAN_DONE ) +#define __msc_mask_prgdone() ( REG_MSC_IMASK |= MSC_IMASK_PRG_DONE ) +#define __msc_unmask_prgdone() ( REG_MSC_IMASK &= ~MSC_IMASK_PRG_DONE ) + +/* n=0,1,2,3,4,5,6,7 */ +#define __msc_set_clkrt(n) \ +do { \ + REG_MSC_CLKRT = n; \ +} while(0) + +#define __msc_get_ireg() ( REG_MSC_IREG ) +#define __msc_ireg_rd() ( REG_MSC_IREG & MSC_IREG_RXFIFO_RD_REQ ) +#define __msc_ireg_wr() ( REG_MSC_IREG & MSC_IREG_TXFIFO_WR_REQ ) +#define __msc_ireg_end_cmd_res() ( REG_MSC_IREG & MSC_IREG_END_CMD_RES ) +#define __msc_ireg_data_tran_done() ( REG_MSC_IREG & MSC_IREG_DATA_TRAN_DONE ) +#define __msc_ireg_prg_done() ( REG_MSC_IREG & MSC_IREG_PRG_DONE ) +#define __msc_ireg_clear_end_cmd_res() ( REG_MSC_IREG = MSC_IREG_END_CMD_RES ) +#define __msc_ireg_clear_data_tran_done() ( REG_MSC_IREG = MSC_IREG_DATA_TRAN_DONE ) +#define __msc_ireg_clear_prg_done() ( REG_MSC_IREG = MSC_IREG_PRG_DONE ) + +#define __msc_get_stat() ( REG_MSC_STAT ) +#define __msc_stat_not_end_cmd_res() ( (REG_MSC_STAT & MSC_STAT_END_CMD_RES) == 0) +#define __msc_stat_crc_err() \ + ( REG_MSC_STAT & (MSC_STAT_CRC_RES_ERR | MSC_STAT_CRC_READ_ERROR | MSC_STAT_CRC_WRITE_ERROR_YES) ) +#define __msc_stat_res_crc_err() ( REG_MSC_STAT & MSC_STAT_CRC_RES_ERR ) +#define __msc_stat_rd_crc_err() ( REG_MSC_STAT & MSC_STAT_CRC_READ_ERROR ) +#define __msc_stat_wr_crc_err() ( REG_MSC_STAT & MSC_STAT_CRC_WRITE_ERROR_YES ) +#define __msc_stat_resto_err() ( REG_MSC_STAT & MSC_STAT_TIME_OUT_RES ) +#define __msc_stat_rdto_err() ( REG_MSC_STAT & MSC_STAT_TIME_OUT_READ ) + +#define __msc_rd_resfifo() ( REG_MSC_RES ) +#define __msc_rd_rxfifo() ( REG_MSC_RXFIFO ) +#define __msc_wr_txfifo(v) ( REG_MSC_TXFIFO = v ) + +#define __msc_reset() \ +do { \ + REG_MSC_STRPCL = MSC_STRPCL_RESET; \ + while (REG_MSC_STAT & MSC_STAT_IS_RESETTING); \ +} while (0) + +#define __msc_start_clk() \ +do { \ + REG_MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_START; \ +} while (0) + +#define __msc_stop_clk() \ +do { \ + REG_MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_STOP; \ +} while (0) + +#define MMC_CLK 19169200 +#define SD_CLK 24576000 + +/* msc_clk should little than pclk and little than clk retrieve from card */ +#define __msc_calc_clk_divisor(type,dev_clk,msc_clk,lv) \ +do { \ + unsigned int rate, pclk, i; \ + pclk = dev_clk; \ + rate = type?SD_CLK:MMC_CLK; \ + if (msc_clk && msc_clk < pclk) \ + pclk = msc_clk; \ + i = 0; \ + while (pclk < rate) \ + { \ + i ++; \ + rate >>= 1; \ + } \ + lv = i; \ +} while(0) + +/* divide rate to little than or equal to 400kHz */ +#define __msc_calc_slow_clk_divisor(type, lv) \ +do { \ + unsigned int rate, i; \ + rate = (type?SD_CLK:MMC_CLK)/1000/400; \ + i = 0; \ + while (rate > 0) \ + { \ + rate >>= 1; \ + i ++; \ + } \ + lv = i; \ +} while(0) + + +/*************************************************************************** + * SSI + ***************************************************************************/ + +#define __ssi_enable() ( REG_SSI_CR0 |= SSI_CR0_SSIE ) +#define __ssi_disable() ( REG_SSI_CR0 &= ~SSI_CR0_SSIE ) +#define __ssi_select_ce() ( REG_SSI_CR0 &= ~SSI_CR0_FSEL ) + +#define __ssi_normal_mode() ( REG_SSI_ITR &= ~SSI_ITR_IVLTM_MASK ) + +#define __ssi_select_ce2() \ +do { \ + REG_SSI_CR0 |= SSI_CR0_FSEL; \ + REG_SSI_CR1 &= ~SSI_CR1_MULTS; \ +} while (0) + +#define __ssi_select_gpc() \ +do { \ + REG_SSI_CR0 &= ~SSI_CR0_FSEL; \ + REG_SSI_CR1 |= SSI_CR1_MULTS; \ +} while (0) + +#define __ssi_enable_tx_intr() \ + ( REG_SSI_CR0 |= SSI_CR0_TIE | SSI_CR0_TEIE ) + +#define __ssi_disable_tx_intr() \ + ( REG_SSI_CR0 &= ~(SSI_CR0_TIE | SSI_CR0_TEIE) ) + +#define __ssi_enable_rx_intr() \ + ( REG_SSI_CR0 |= SSI_CR0_RIE | SSI_CR0_REIE ) + +#define __ssi_disable_rx_intr() \ + ( REG_SSI_CR0 &= ~(SSI_CR0_RIE | SSI_CR0_REIE) ) + +#define __ssi_enable_txfifo_half_empty_intr() \ + ( REG_SSI_CR0 |= SSI_CR0_TIE ) +#define __ssi_disable_txfifo_half_empty_intr() \ + ( REG_SSI_CR0 &= ~SSI_CR0_TIE ) +#define __ssi_enable_tx_error_intr() \ + ( REG_SSI_CR0 |= SSI_CR0_TEIE ) +#define __ssi_disable_tx_error_intr() \ + ( REG_SSI_CR0 &= ~SSI_CR0_TEIE ) + +#define __ssi_enable_rxfifo_half_full_intr() \ + ( REG_SSI_CR0 |= SSI_CR0_RIE ) +#define __ssi_disable_rxfifo_half_full_intr() \ + ( REG_SSI_CR0 &= ~SSI_CR0_RIE ) +#define __ssi_enable_rx_error_intr() \ + ( REG_SSI_CR0 |= SSI_CR0_REIE ) +#define __ssi_disable_rx_error_intr() \ + ( REG_SSI_CR0 &= ~SSI_CR0_REIE ) + +#define __ssi_enable_loopback() ( REG_SSI_CR0 |= SSI_CR0_LOOP ) +#define __ssi_disable_loopback() ( REG_SSI_CR0 &= ~SSI_CR0_LOOP ) + +#define __ssi_enable_receive() ( REG_SSI_CR0 &= ~SSI_CR0_DISREV ) +#define __ssi_disable_receive() ( REG_SSI_CR0 |= SSI_CR0_DISREV ) + +#define __ssi_finish_receive() \ + ( REG_SSI_CR0 |= (SSI_CR0_RFINE | SSI_CR0_RFINC) ) + +#define __ssi_disable_recvfinish() \ + ( REG_SSI_CR0 &= ~(SSI_CR0_RFINE | SSI_CR0_RFINC) ) + +#define __ssi_flush_txfifo() ( REG_SSI_CR0 |= SSI_CR0_TFLUSH ) +#define __ssi_flush_rxfifo() ( REG_SSI_CR0 |= SSI_CR0_RFLUSH ) + +#define __ssi_flush_fifo() \ + ( REG_SSI_CR0 |= SSI_CR0_TFLUSH | SSI_CR0_RFLUSH ) + +#define __ssi_finish_transmit() ( REG_SSI_CR1 &= ~SSI_CR1_UNFIN ) +#define __ssi_wait_transmit() ( REG_SSI_CR1 |= SSI_CR1_UNFIN ) + +#define __ssi_spi_format() \ +do { \ + REG_SSI_CR1 &= ~SSI_CR1_FMAT_MASK; \ + REG_SSI_CR1 |= SSI_CR1_FMAT_SPI; \ + REG_SSI_CR1 &= ~(SSI_CR1_TFVCK_MASK|SSI_CR1_TCKFI_MASK);\ + REG_SSI_CR1 |= (SSI_CR1_TFVCK_1 | SSI_CR1_TCKFI_1); \ +} while (0) + +/* TI's SSP format, must clear SSI_CR1.UNFIN */ +#define __ssi_ssp_format() \ +do { \ + REG_SSI_CR1 &= ~(SSI_CR1_FMAT_MASK | SSI_CR1_UNFIN); \ + REG_SSI_CR1 |= SSI_CR1_FMAT_SSP; \ +} while (0) + +/* National's Microwire format, must clear SSI_CR0.RFINE, and set max delay */ +#define __ssi_microwire_format() \ +do { \ + REG_SSI_CR1 &= ~SSI_CR1_FMAT_MASK; \ + REG_SSI_CR1 |= SSI_CR1_FMAT_MW1; \ + REG_SSI_CR1 &= ~(SSI_CR1_TFVCK_MASK|SSI_CR1_TCKFI_MASK);\ + REG_SSI_CR1 |= (SSI_CR1_TFVCK_3 | SSI_CR1_TCKFI_3); \ + REG_SSI_CR0 &= ~SSI_CR0_RFINE; \ +} while (0) + +/* CE# level (FRMHL), CE# in interval time (ITFRM), + clock phase and polarity (PHA POL), + interval time (SSIITR), interval characters/frame (SSIICR) */ + + /* frmhl,endian,mcom,flen,pha,pol MASK */ +#define SSICR1_MISC_MASK \ + ( SSI_CR1_FRMHL_MASK | SSI_CR1_LFST | SSI_CR1_MCOM_MASK \ + | SSI_CR1_FLEN_MASK | SSI_CR1_PHA | SSI_CR1_POL ) \ + +#define __ssi_spi_set_misc(frmhl,endian,flen,mcom,pha,pol) \ +do { \ + REG_SSI_CR1 &= ~SSICR1_MISC_MASK; \ + REG_SSI_CR1 |= ((frmhl) << 30) | ((endian) << 25) | \ + (((mcom) - 1) << 12) | (((flen) - 2) << 4) | \ + ((pha) << 1) | (pol); \ +} while(0) + +/* Transfer with MSB or LSB first */ +#define __ssi_set_msb() ( REG_SSI_CR1 &= ~SSI_CR1_LFST ) +#define __ssi_set_lsb() ( REG_SSI_CR1 |= SSI_CR1_LFST ) + +#define __ssi_set_frame_length(n) \ + REG_SSI_CR1 = (REG_SSI_CR1 & ~SSI_CR1_FLEN_MASK) | (((n) - 2) << 4) + +/* n = 1 - 16 */ +#define __ssi_set_microwire_command_length(n) \ + ( REG_SSI_CR1 = ((REG_SSI_CR1 & ~SSI_CR1_MCOM_MASK) | SSI_CR1_MCOM_##n##BIT) ) + +/* Set the clock phase for SPI */ +#define __ssi_set_spi_clock_phase(n) \ + ( REG_SSI_CR1 = ((REG_SSI_CR1 & ~SSI_CR1_PHA) | ((n&0x1)<< 1))) + +/* Set the clock polarity for SPI */ +#define __ssi_set_spi_clock_polarity(n) \ + ( REG_SSI_CR1 = ((REG_SSI_CR1 & ~SSI_CR1_POL) | (n&0x1)) ) + +/* n = ix8 */ +#define __ssi_set_tx_trigger(n) \ +do { \ + REG_SSI_CR1 &= ~SSI_CR1_TTRG_MASK; \ + REG_SSI_CR1 |= (n/8)<> SSI_SR_TFIFONUM_BIT ) + +#define __ssi_get_rxfifo_count() \ + ( (REG_SSI_SR & SSI_SR_RFIFONUM_MASK) >> SSI_SR_RFIFONUM_BIT ) + +#define __ssi_transfer_end() ( REG_SSI_SR & SSI_SR_END ) +#define __ssi_is_busy() ( REG_SSI_SR & SSI_SR_BUSY ) + +#define __ssi_txfifo_full() ( REG_SSI_SR & SSI_SR_TFF ) +#define __ssi_rxfifo_empty() ( REG_SSI_SR & SSI_SR_RFE ) +#define __ssi_rxfifo_half_full() ( REG_SSI_SR & SSI_SR_RFHF ) +#define __ssi_txfifo_half_empty() ( REG_SSI_SR & SSI_SR_TFHE ) +#define __ssi_underrun() ( REG_SSI_SR & SSI_SR_UNDR ) +#define __ssi_overrun() ( REG_SSI_SR & SSI_SR_OVER ) +#define __ssi_clear_underrun() ( REG_SSI_SR = ~SSI_SR_UNDR ) +#define __ssi_clear_overrun() ( REG_SSI_SR = ~SSI_SR_OVER ) +#define __ssi_clear_errors() \ + ( REG_SSI_SR &= ~(SSI_SR_UNDR | SSI_SR_OVER) ) + + +#define __ssi_set_clk(dev_clk, ssi_clk) \ + ( REG_SSI_GR = (dev_clk) / (2*(ssi_clk)) - 1 ) + +#define __ssi_receive_data() REG_SSI_DR +#define __ssi_transmit_data(v) ( REG_SSI_DR = (v) ) + + +/*************************************************************************** + * CIM + ***************************************************************************/ + +#define __cim_enable() ( REG_CIM_CTRL |= CIM_CTRL_ENA ) +#define __cim_disable() ( REG_CIM_CTRL &= ~CIM_CTRL_ENA ) + +#define __cim_input_data_inverse() ( REG_CIM_CFG |= CIM_CFG_INV_DAT ) +#define __cim_input_data_normal() ( REG_CIM_CFG &= ~CIM_CFG_INV_DAT ) + +#define __cim_vsync_active_low() ( REG_CIM_CFG |= CIM_CFG_VSP ) +#define __cim_vsync_active_high() ( REG_CIM_CFG &= ~CIM_CFG_VSP ) + +#define __cim_hsync_active_low() ( REG_CIM_CFG |= CIM_CFG_HSP ) +#define __cim_hsync_active_high() ( REG_CIM_CFG &= ~CIM_CFG_HSP ) + +#define __cim_sample_data_at_pclk_falling_edge() \ + ( REG_CIM_CFG |= CIM_CFG_PCP ) +#define __cim_sample_data_at_pclk_rising_edge() \ + ( REG_CIM_CFG &= ~CIM_CFG_PCP ) + +#define __cim_enable_dummy_zero() ( REG_CIM_CFG |= CIM_CFG_DUMMY_ZERO ) +#define __cim_disable_dummy_zero() ( REG_CIM_CFG &= ~CIM_CFG_DUMMY_ZERO ) + +#define __cim_select_external_vsync() ( REG_CIM_CFG |= CIM_CFG_EXT_VSYNC ) +#define __cim_select_internal_vsync() ( REG_CIM_CFG &= ~CIM_CFG_EXT_VSYNC ) + +/* n=0-7 */ +#define __cim_set_data_packing_mode(n) \ +do { \ + REG_CIM_CFG &= ~CIM_CFG_PACK_MASK; \ + REG_CIM_CFG |= (CIM_CFG_PACK_##n); \ +} while (0) + +#define __cim_enable_ccir656_progressive_mode() \ +do { \ + REG_CIM_CFG &= ~CIM_CFG_DSM_MASK; \ + REG_CIM_CFG |= CIM_CFG_DSM_CPM; \ +} while (0) + +#define __cim_enable_ccir656_interlace_mode() \ +do { \ + REG_CIM_CFG &= ~CIM_CFG_DSM_MASK; \ + REG_CIM_CFG |= CIM_CFG_DSM_CIM; \ +} while (0) + +#define __cim_enable_gated_clock_mode() \ +do { \ + REG_CIM_CFG &= ~CIM_CFG_DSM_MASK; \ + REG_CIM_CFG |= CIM_CFG_DSM_GCM; \ +} while (0) + +#define __cim_enable_nongated_clock_mode() \ +do { \ + REG_CIM_CFG &= ~CIM_CFG_DSM_MASK; \ + REG_CIM_CFG |= CIM_CFG_DSM_NGCM; \ +} while (0) + +/* sclk:system bus clock + * mclk: CIM master clock + */ +#define __cim_set_master_clk(sclk, mclk) \ +do { \ + REG_CIM_CTRL &= ~CIM_CTRL_MCLKDIV_MASK; \ + REG_CIM_CTRL |= (((sclk)/(mclk) - 1) << CIM_CTRL_MCLKDIV_BIT); \ +} while (0) + +#define __cim_enable_sof_intr() \ + ( REG_CIM_CTRL |= CIM_CTRL_DMA_SOFM ) +#define __cim_disable_sof_intr() \ + ( REG_CIM_CTRL &= ~CIM_CTRL_DMA_SOFM ) + +#define __cim_enable_eof_intr() \ + ( REG_CIM_CTRL |= CIM_CTRL_DMA_EOFM ) +#define __cim_disable_eof_intr() \ + ( REG_CIM_CTRL &= ~CIM_CTRL_DMA_EOFM ) + +#define __cim_enable_stop_intr() \ + ( REG_CIM_CTRL |= CIM_CTRL_DMA_STOPM ) +#define __cim_disable_stop_intr() \ + ( REG_CIM_CTRL &= ~CIM_CTRL_DMA_STOPM ) + +#define __cim_enable_trig_intr() \ + ( REG_CIM_CTRL |= CIM_CTRL_RXF_TRIGM ) +#define __cim_disable_trig_intr() \ + ( REG_CIM_CTRL &= ~CIM_CTRL_RXF_TRIGM ) + +#define __cim_enable_rxfifo_overflow_intr() \ + ( REG_CIM_CTRL |= CIM_CTRL_RXF_OFM ) +#define __cim_disable_rxfifo_overflow_intr() \ + ( REG_CIM_CTRL &= ~CIM_CTRL_RXF_OFM ) + +/* n=1-16 */ +#define __cim_set_frame_rate(n) \ +do { \ + REG_CIM_CTRL &= ~CIM_CTRL_FRC_MASK; \ + REG_CIM_CTRL |= CIM_CTRL_FRC_##n; \ +} while (0) + +#define __cim_enable_dma() ( REG_CIM_CTRL |= CIM_CTRL_DMA_EN ) +#define __cim_disable_dma() ( REG_CIM_CTRL &= ~CIM_CTRL_DMA_EN ) + +#define __cim_reset_rxfifo() ( REG_CIM_CTRL |= CIM_CTRL_RXF_RST ) +#define __cim_unreset_rxfifo() ( REG_CIM_CTRL &= ~CIM_CTRL_RXF_RST ) + +/* n=4,8,12,16,20,24,28,32 */ +#define __cim_set_rxfifo_trigger(n) \ +do { \ + REG_CIM_CTRL &= ~CIM_CTRL_RXF_TRIG_MASK; \ + REG_CIM_CTRL |= CIM_CTRL_RXF_TRIG_##n; \ +} while (0) + +#define __cim_clear_state() ( REG_CIM_STATE = 0 ) + +#define __cim_disable_done() ( REG_CIM_STATE & CIM_STATE_VDD ) +#define __cim_rxfifo_empty() ( REG_CIM_STATE & CIM_STATE_RXF_EMPTY ) +#define __cim_rxfifo_reach_trigger() ( REG_CIM_STATE & CIM_STATE_RXF_TRIG ) +#define __cim_rxfifo_overflow() ( REG_CIM_STATE & CIM_STATE_RXF_OF ) +#define __cim_clear_rxfifo_overflow() ( REG_CIM_STATE &= ~CIM_STATE_RXF_OF ) +#define __cim_dma_stop() ( REG_CIM_STATE & CIM_STATE_DMA_STOP ) +#define __cim_dma_eof() ( REG_CIM_STATE & CIM_STATE_DMA_EOF ) +#define __cim_dma_sof() ( REG_CIM_STATE & CIM_STATE_DMA_SOF ) + +#define __cim_get_iid() ( REG_CIM_IID ) +#define __cim_get_image_data() ( REG_CIM_RXFIFO ) +#define __cim_get_dam_cmd() ( REG_CIM_CMD ) + +#define __cim_set_da(a) ( REG_CIM_DA = (a) ) + +/*************************************************************************** + * LCD + ***************************************************************************/ +#define __lcd_as_smart_lcd() ( REG_LCD_CFG |= (1<> LCD_VSYNC_VPS_BIT ) + +#define __lcd_vsync_get_vpe() \ + ( (REG_LCD_VSYNC & LCD_VSYNC_VPE_MASK) >> LCD_VSYNC_VPE_BIT ) +#define __lcd_vsync_set_vpe(n) \ +do { \ + REG_LCD_VSYNC &= ~LCD_VSYNC_VPE_MASK; \ + REG_LCD_VSYNC |= (n) << LCD_VSYNC_VPE_BIT; \ +} while (0) + +#define __lcd_hsync_get_hps() \ + ( (REG_LCD_HSYNC & LCD_HSYNC_HPS_MASK) >> LCD_HSYNC_HPS_BIT ) +#define __lcd_hsync_set_hps(n) \ +do { \ + REG_LCD_HSYNC &= ~LCD_HSYNC_HPS_MASK; \ + REG_LCD_HSYNC |= (n) << LCD_HSYNC_HPS_BIT; \ +} while (0) + +#define __lcd_hsync_get_hpe() \ + ( (REG_LCD_HSYNC & LCD_HSYNC_HPE_MASK) >> LCD_VSYNC_HPE_BIT ) +#define __lcd_hsync_set_hpe(n) \ +do { \ + REG_LCD_HSYNC &= ~LCD_HSYNC_HPE_MASK; \ + REG_LCD_HSYNC |= (n) << LCD_HSYNC_HPE_BIT; \ +} while (0) + +#define __lcd_vat_get_ht() \ + ( (REG_LCD_VAT & LCD_VAT_HT_MASK) >> LCD_VAT_HT_BIT ) +#define __lcd_vat_set_ht(n) \ +do { \ + REG_LCD_VAT &= ~LCD_VAT_HT_MASK; \ + REG_LCD_VAT |= (n) << LCD_VAT_HT_BIT; \ +} while (0) + +#define __lcd_vat_get_vt() \ + ( (REG_LCD_VAT & LCD_VAT_VT_MASK) >> LCD_VAT_VT_BIT ) +#define __lcd_vat_set_vt(n) \ +do { \ + REG_LCD_VAT &= ~LCD_VAT_VT_MASK; \ + REG_LCD_VAT |= (n) << LCD_VAT_VT_BIT; \ +} while (0) + +#define __lcd_dah_get_hds() \ + ( (REG_LCD_DAH & LCD_DAH_HDS_MASK) >> LCD_DAH_HDS_BIT ) +#define __lcd_dah_set_hds(n) \ +do { \ + REG_LCD_DAH &= ~LCD_DAH_HDS_MASK; \ + REG_LCD_DAH |= (n) << LCD_DAH_HDS_BIT; \ +} while (0) + +#define __lcd_dah_get_hde() \ + ( (REG_LCD_DAH & LCD_DAH_HDE_MASK) >> LCD_DAH_HDE_BIT ) +#define __lcd_dah_set_hde(n) \ +do { \ + REG_LCD_DAH &= ~LCD_DAH_HDE_MASK; \ + REG_LCD_DAH |= (n) << LCD_DAH_HDE_BIT; \ +} while (0) + +#define __lcd_dav_get_vds() \ + ( (REG_LCD_DAV & LCD_DAV_VDS_MASK) >> LCD_DAV_VDS_BIT ) +#define __lcd_dav_set_vds(n) \ +do { \ + REG_LCD_DAV &= ~LCD_DAV_VDS_MASK; \ + REG_LCD_DAV |= (n) << LCD_DAV_VDS_BIT; \ +} while (0) + +#define __lcd_dav_get_vde() \ + ( (REG_LCD_DAV & LCD_DAV_VDE_MASK) >> LCD_DAV_VDE_BIT ) +#define __lcd_dav_set_vde(n) \ +do { \ + REG_LCD_DAV &= ~LCD_DAV_VDE_MASK; \ + REG_LCD_DAV |= (n) << LCD_DAV_VDE_BIT; \ +} while (0) + +#define __lcd_cmd0_set_sofint() ( REG_LCD_CMD0 |= LCD_CMD_SOFINT ) +#define __lcd_cmd0_clr_sofint() ( REG_LCD_CMD0 &= ~LCD_CMD_SOFINT ) +#define __lcd_cmd1_set_sofint() ( REG_LCD_CMD1 |= LCD_CMD_SOFINT ) +#define __lcd_cmd1_clr_sofint() ( REG_LCD_CMD1 &= ~LCD_CMD_SOFINT ) + +#define __lcd_cmd0_set_eofint() ( REG_LCD_CMD0 |= LCD_CMD_EOFINT ) +#define __lcd_cmd0_clr_eofint() ( REG_LCD_CMD0 &= ~LCD_CMD_EOFINT ) +#define __lcd_cmd1_set_eofint() ( REG_LCD_CMD1 |= LCD_CMD_EOFINT ) +#define __lcd_cmd1_clr_eofint() ( REG_LCD_CMD1 &= ~LCD_CMD_EOFINT ) + +#define __lcd_cmd0_set_pal() ( REG_LCD_CMD0 |= LCD_CMD_PAL ) +#define __lcd_cmd0_clr_pal() ( REG_LCD_CMD0 &= ~LCD_CMD_PAL ) + +#define __lcd_cmd0_get_len() \ + ( (REG_LCD_CMD0 & LCD_CMD_LEN_MASK) >> LCD_CMD_LEN_BIT ) +#define __lcd_cmd1_get_len() \ + ( (REG_LCD_CMD1 & LCD_CMD_LEN_MASK) >> LCD_CMD_LEN_BIT ) + +/******************************************************* + * SMART LCD + *******************************************************/ + +#define __slcd_dma_enable() (REG_SLCD_CTRL |= SLCD_CTRL_DMA_EN) +#define __slcd_dma_disable() \ +do {\ + while (REG_SLCD_STATE & SLCD_STATE_BUSY); \ + REG_SLCD_CTRL &= ~SLCD_CTRL_DMA_EN; \ +} while(0) + +/******************************************************* + * SMART LCD + *******************************************************/ + +#define __slcd_dma_enable() (REG_SLCD_CTRL |= SLCD_CTRL_DMA_EN) +#define __slcd_dma_disable() \ +do {\ + while (REG_SLCD_STATE & SLCD_STATE_BUSY); \ + REG_SLCD_CTRL &= ~SLCD_CTRL_DMA_EN; \ +} while(0) + +/*************************************************************************** + * RTC ops + ***************************************************************************/ + +#define __rtc_write_ready() ( (REG_RTC_RCR & RTC_RCR_WRDY) >> RTC_RCR_WRDY_BIT ) +#define __rtc_enabled() ( REG_RTC_RCR |= RTC_RCR_RTCE ) +#define __rtc_disabled() ( REG_RTC_RCR &= ~RTC_RCR_RTCE ) +#define __rtc_enable_alarm() ( REG_RTC_RCR |= RTC_RCR_AE ) +#define __rtc_disable_alarm() ( REG_RTC_RCR &= ~RTC_RCR_AE ) +#define __rtc_enable_alarm_irq() ( REG_RTC_RCR |= RTC_RCR_AIE ) +#define __rtc_disable_alarm_irq() ( REG_RTC_RCR &= ~RTC_RCR_AIE ) +#define __rtc_enable_1Hz_irq() ( REG_RTC_RCR |= RTC_RCR_1HZIE ) +#define __rtc_disable_1Hz_irq() ( REG_RTC_RCR &= ~RTC_RCR_1HZIE ) + +#define __rtc_get_1Hz_flag() ( (REG_RTC_RCR >> RTC_RCR_1HZ_BIT) & 0x1 ) +#define __rtc_clear_1Hz_flag() ( REG_RTC_RCR &= ~RTC_RCR_1HZ ) +#define __rtc_get_alarm_flag() ( (REG_RTC_RCR >> RTC_RCR_AF_BIT) & 0x1 ) +#define __rtc_clear_alarm_flag() ( REG_RTC_RCR &= ~RTC_RCR_AF ) + +#define __rtc_get_second() ( REG_RTC_RSR ) +#define __rtc_set_second(v) ( REG_RTC_RSR = v ) + +#define __rtc_get_alarm_second() ( REG_RTC_RSAR ) +#define __rtc_set_alarm_second(v) ( REG_RTC_RSAR = v ) + +#define __rtc_RGR_is_locked() ( (REG_RTC_RGR >> RTC_RGR_LOCK) ) +#define __rtc_lock_RGR() ( REG_RTC_RGR |= RTC_RGR_LOCK ) +#define __rtc_unlock_RGR() ( REG_RTC_RGR &= ~RTC_RGR_LOCK ) +#define __rtc_get_adjc_val() ( (REG_RTC_RGR & RTC_RGR_ADJC_MASK) >> RTC_RGR_ADJC_BIT ) +#define __rtc_set_adjc_val(v) \ + ( REG_RTC_RGR = ( (REG_RTC_RGR & ~RTC_RGR_ADJC_MASK) | (v << RTC_RGR_ADJC_BIT) )) +#define __rtc_get_nc1Hz_val() ( (REG_RTC_RGR & RTC_RGR_NC1HZ_MASK) >> RTC_RGR_NC1HZ_BIT ) +#define __rtc_set_nc1Hz_val(v) \ + ( REG_RTC_RGR = ( (REG_RTC_RGR & ~RTC_RGR_NC1HZ_MASK) | (v << RTC_RGR_NC1HZ_BIT) )) + +#define __rtc_power_down() ( REG_RTC_HCR |= RTC_HCR_PD ) + +#define __rtc_get_hwfcr_val() ( REG_RTC_HWFCR & RTC_HWFCR_MASK ) +#define __rtc_set_hwfcr_val(v) ( REG_RTC_HWFCR = (v) & RTC_HWFCR_MASK ) +#define __rtc_get_hrcr_val() ( REG_RTC_HRCR & RTC_HRCR_MASK ) +#define __rtc_set_hrcr_val(v) ( REG_RTC_HRCR = (v) & RTC_HRCR_MASK ) + +#define __rtc_enable_alarm_wakeup() ( REG_RTC_HWCR |= RTC_HWCR_EALM ) +#define __rtc_disable_alarm_wakeup() ( REG_RTC_HWCR &= ~RTC_HWCR_EALM ) + +#define __rtc_status_hib_reset_occur() ( (REG_RTC_HWRSR >> RTC_HWRSR_HR) & 0x1 ) +#define __rtc_status_ppr_reset_occur() ( (REG_RTC_HWRSR >> RTC_HWRSR_PPR) & 0x1 ) +#define __rtc_status_wakeup_pin_waken_up() ( (REG_RTC_HWRSR >> RTC_HWRSR_PIN) & 0x1 ) +#define __rtc_status_alarm_waken_up() ( (REG_RTC_HWRSR >> RTC_HWRSR_ALM) & 0x1 ) +#define __rtc_clear_hib_stat_all() ( REG_RTC_HWRSR = 0 ) + +#define __rtc_get_scratch_pattern() (REG_RTC_HSPR) +#define __rtc_set_scratch_pattern(n) (REG_RTC_HSPR = n ) + + + +#endif /* __JZ4740_OPS_H__ */ diff --git a/include/asm-mips/mach-jz4740/regs.h b/include/asm-mips/mach-jz4740/regs.h new file mode 100644 index 0000000..07528bc --- /dev/null +++ b/include/asm-mips/mach-jz4740/regs.h @@ -0,0 +1,2392 @@ +/* + * linux/include/asm-mips/mach-jz4740/regs.h + * + * Ingenic's JZ4740 common include. + * + * Copyright (C) 2006 - 2007 Ingenic Semiconductor Inc. + * + * Author: + * + * 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_REGS_H__ +#define __JZ4740_REGS_H__ + +#if defined(__ASSEMBLY__) || defined(__LANGUAGE_ASSEMBLY) +#define REG8(addr) (addr) +#define REG16(addr) (addr) +#define REG32(addr) (addr) +#else +#define REG8(addr) *((volatile unsigned char *)(addr)) +#define REG16(addr) *((volatile unsigned short *)(addr)) +#define REG32(addr) *((volatile unsigned int *)(addr)) +#endif + +/* + * Define the module base addresses + */ +#define CPM_BASE 0xB0000000 +#define INTC_BASE 0xB0001000 +#define TCU_BASE 0xB0002000 +#define WDT_BASE 0xB0002000 +#define RTC_BASE 0xB0003000 +#define GPIO_BASE 0xB0010000 +#define AIC_BASE 0xB0020000 +#define ICDC_BASE 0xB0020000 +#define MSC_BASE 0xB0021000 +#define UART0_BASE 0xB0030000 +#define UART1_BASE 0xB0031000 +#define I2C_BASE 0xB0042000 +#define SSI_BASE 0xB0043000 +#define SADC_BASE 0xB0070000 +#define EMC_BASE 0xB3010000 +#define DMAC_BASE 0xB3020000 +#define UHC_BASE 0xB3030000 +#define UDC_BASE 0xB3040000 +#define LCD_BASE 0xB3050000 +#define SLCD_BASE 0xB3050000 +#define CIM_BASE 0xB3060000 +#define IPU_BASE 0xB3080000 +#define ETH_BASE 0xB3100000 + + +/************************************************************************* + * INTC (Interrupt Controller) + *************************************************************************/ +#define INTC_ISR (INTC_BASE + 0x00) +#define INTC_IMR (INTC_BASE + 0x04) +#define INTC_IMSR (INTC_BASE + 0x08) +#define INTC_IMCR (INTC_BASE + 0x0c) +#define INTC_IPR (INTC_BASE + 0x10) + +#define REG_INTC_ISR REG32(INTC_ISR) +#define REG_INTC_IMR REG32(INTC_IMR) +#define REG_INTC_IMSR REG32(INTC_IMSR) +#define REG_INTC_IMCR REG32(INTC_IMCR) +#define REG_INTC_IPR REG32(INTC_IPR) + +// 1st-level interrupts +#define IRQ_I2C 1 +#define IRQ_UHC 3 +#define IRQ_UART1 8 +#define IRQ_UART0 9 +#define IRQ_SADC 12 +#define IRQ_MSC 14 +#define IRQ_RTC 15 +#define IRQ_SSI 16 +#define IRQ_CIM 17 +#define IRQ_AIC 18 +#define IRQ_ETH 19 +#define IRQ_DMAC 20 +#define IRQ_TCU2 21 +#define IRQ_TCU1 22 +#define IRQ_TCU0 23 +#define IRQ_UDC 24 +#define IRQ_GPIO3 25 +#define IRQ_GPIO2 26 +#define IRQ_GPIO1 27 +#define IRQ_GPIO0 28 +#define IRQ_IPU 29 +#define IRQ_LCD 30 + +// 2nd-level interrupts +#define IRQ_DMA_0 32 /* 32 to 37 for DMAC channel 0 to 5 */ +#define IRQ_GPIO_0 48 /* 48 to 175 for GPIO pin 0 to 127 */ + +#define NUM_DMA 6 +#define NUM_GPIO 128 +/************************************************************************* + * RTC + *************************************************************************/ +#define RTC_RCR (RTC_BASE + 0x00) /* RTC Control Register */ +#define RTC_RSR (RTC_BASE + 0x04) /* RTC Second Register */ +#define RTC_RSAR (RTC_BASE + 0x08) /* RTC Second Alarm Register */ +#define RTC_RGR (RTC_BASE + 0x0c) /* RTC Regulator Register */ + +#define RTC_HCR (RTC_BASE + 0x20) /* Hibernate Control Register */ +#define RTC_HWFCR (RTC_BASE + 0x24) /* Hibernate Wakeup Filter Counter Reg */ +#define RTC_HRCR (RTC_BASE + 0x28) /* Hibernate Reset Counter Register */ +#define RTC_HWCR (RTC_BASE + 0x2c) /* Hibernate Wakeup Control Register */ +#define RTC_HWRSR (RTC_BASE + 0x30) /* Hibernate Wakeup Status Register */ +#define RTC_HSPR (RTC_BASE + 0x34) /* Hibernate Scratch Pattern Register */ + +#define REG_RTC_RCR REG32(RTC_RCR) +#define REG_RTC_RSR REG32(RTC_RSR) +#define REG_RTC_RSAR REG32(RTC_RSAR) +#define REG_RTC_RGR REG32(RTC_RGR) +#define REG_RTC_HCR REG32(RTC_HCR) +#define REG_RTC_HWFCR REG32(RTC_HWFCR) +#define REG_RTC_HRCR REG32(RTC_HRCR) +#define REG_RTC_HWCR REG32(RTC_HWCR) +#define REG_RTC_HWRSR REG32(RTC_HWRSR) +#define REG_RTC_HSPR REG32(RTC_HSPR) + +/* RTC Control Register */ +#define RTC_RCR_WRDY_BIT 7 +#define RTC_RCR_WRDY (1 << 7) /* Write Ready Flag */ +#define RTC_RCR_1HZ_BIT 6 +#define RTC_RCR_1HZ (1 << RTC_RCR_1HZ_BIT) /* 1Hz Flag */ +#define RTC_RCR_1HZIE (1 << 5) /* 1Hz Interrupt Enable */ +#define RTC_RCR_AF_BIT 4 +#define RTC_RCR_AF (1 << RTC_RCR_AF_BIT) /* Alarm Flag */ +#define RTC_RCR_AIE (1 << 3) /* Alarm Interrupt Enable */ +#define RTC_RCR_AE (1 << 2) /* Alarm Enable */ +#define RTC_RCR_RTCE (1 << 0) /* RTC Enable */ + +/* RTC Regulator Register */ +#define RTC_RGR_LOCK (1 << 31) /* Lock Bit */ +#define RTC_RGR_ADJC_BIT 16 +#define RTC_RGR_ADJC_MASK (0x3ff << RTC_RGR_ADJC_BIT) +#define RTC_RGR_NC1HZ_BIT 0 +#define RTC_RGR_NC1HZ_MASK (0xffff << RTC_RGR_NC1HZ_BIT) + +/* Hibernate Control Register */ +#define RTC_HCR_PD (1 << 0) /* Power Down */ + +/* Hibernate Wakeup Filter Counter Register */ +#define RTC_HWFCR_BIT 5 +#define RTC_HWFCR_MASK (0x7ff << RTC_HWFCR_BIT) + +/* Hibernate Reset Counter Register */ +#define RTC_HRCR_BIT 5 +#define RTC_HRCR_MASK (0x7f << RTC_HRCR_BIT) + +/* Hibernate Wakeup Control Register */ +#define RTC_HWCR_EALM (1 << 0) /* RTC alarm wakeup enable */ + +/* Hibernate Wakeup Status Register */ +#define RTC_HWRSR_HR (1 << 5) /* Hibernate reset */ +#define RTC_HWRSR_PPR (1 << 4) /* PPR reset */ +#define RTC_HWRSR_PIN (1 << 1) /* Wakeup pin status bit */ +#define RTC_HWRSR_ALM (1 << 0) /* RTC alarm status bit */ + + +/************************************************************************* + * CPM (Clock reset and Power control Management) + *************************************************************************/ +#define CPM_CPCCR (CPM_BASE+0x00) +#define CPM_CPPCR (CPM_BASE+0x10) +#define CPM_I2SCDR (CPM_BASE+0x60) +#define CPM_LPCDR (CPM_BASE+0x64) +#define CPM_MSCCDR (CPM_BASE+0x68) +#define CPM_UHCCDR (CPM_BASE+0x6C) +#define CPM_SSICDR (CPM_BASE+0x74) + +#define CPM_LCR (CPM_BASE+0x04) +#define CPM_CLKGR (CPM_BASE+0x20) +#define CPM_SCR (CPM_BASE+0x24) + +#define CPM_HCR (CPM_BASE+0x30) +#define CPM_HWFCR (CPM_BASE+0x34) +#define CPM_HRCR (CPM_BASE+0x38) +#define CPM_HWCR (CPM_BASE+0x3c) +#define CPM_HWSR (CPM_BASE+0x40) +#define CPM_HSPR (CPM_BASE+0x44) + +#define CPM_RSR (CPM_BASE+0x08) + + +#define REG_CPM_CPCCR REG32(CPM_CPCCR) +#define REG_CPM_CPPCR REG32(CPM_CPPCR) +#define REG_CPM_I2SCDR REG32(CPM_I2SCDR) +#define REG_CPM_LPCDR REG32(CPM_LPCDR) +#define REG_CPM_MSCCDR REG32(CPM_MSCCDR) +#define REG_CPM_UHCCDR REG32(CPM_UHCCDR) +#define REG_CPM_SSICDR REG32(CPM_SSICDR) + +#define REG_CPM_LCR REG32(CPM_LCR) +#define REG_CPM_CLKGR REG32(CPM_CLKGR) +#define REG_CPM_SCR REG32(CPM_SCR) +#define REG_CPM_HCR REG32(CPM_HCR) +#define REG_CPM_HWFCR REG32(CPM_HWFCR) +#define REG_CPM_HRCR REG32(CPM_HRCR) +#define REG_CPM_HWCR REG32(CPM_HWCR) +#define REG_CPM_HWSR REG32(CPM_HWSR) +#define REG_CPM_HSPR REG32(CPM_HSPR) + +#define REG_CPM_RSR REG32(CPM_RSR) + + +/* Clock Control Register */ +#define CPM_CPCCR_I2CS (1 << 31) +#define CPM_CPCCR_CLKOEN (1 << 30) +#define CPM_CPCCR_UCS (1 << 29) +#define CPM_CPCCR_UDIV_BIT 23 +#define CPM_CPCCR_UDIV_MASK (0x3f << CPM_CPCCR_UDIV_BIT) +#define CPM_CPCCR_CE (1 << 22) +#define CPM_CPCCR_PCS (1 << 21) +#define CPM_CPCCR_LDIV_BIT 16 +#define CPM_CPCCR_LDIV_MASK (0x1f << CPM_CPCCR_LDIV_BIT) +#define CPM_CPCCR_MDIV_BIT 12 +#define CPM_CPCCR_MDIV_MASK (0x0f << CPM_CPCCR_MDIV_BIT) +#define CPM_CPCCR_PDIV_BIT 8 +#define CPM_CPCCR_PDIV_MASK (0x0f << CPM_CPCCR_PDIV_BIT) +#define CPM_CPCCR_HDIV_BIT 4 +#define CPM_CPCCR_HDIV_MASK (0x0f << CPM_CPCCR_HDIV_BIT) +#define CPM_CPCCR_CDIV_BIT 0 +#define CPM_CPCCR_CDIV_MASK (0x0f << CPM_CPCCR_CDIV_BIT) + +/* I2S Clock Divider Register */ +#define CPM_I2SCDR_I2SDIV_BIT 0 +#define CPM_I2SCDR_I2SDIV_MASK (0x1ff << CPM_I2SCDR_I2SDIV_BIT) + +/* LCD Pixel Clock Divider Register */ +#define CPM_LPCDR_PIXDIV_BIT 0 +#define CPM_LPCDR_PIXDIV_MASK (0x7ff << CPM_LPCDR_PIXDIV_BIT) + +/* MSC Clock Divider Register */ +#define CPM_MSCCDR_MSCDIV_BIT 0 +#define CPM_MSCCDR_MSCDIV_MASK (0x1f << CPM_MSCCDR_MSCDIV_BIT) + +/* UHC Clock Divider Register */ +#define CPM_UHCCDR_UHCDIV_BIT 0 +#define CPM_UHCCDR_UHCDIV_MASK (0xf << CPM_UHCCDR_UHCDIV_BIT) + +/* SSI Clock Divider Register */ +#define CPM_SSICDR_SCS (1<<31) /* SSI clock source selection, 0:EXCLK, 1: PLL */ +#define CPM_SSICDR_SSIDIV_BIT 0 +#define CPM_SSICDR_SSIDIV_MASK (0xf << CPM_SSICDR_SSIDIV_BIT) + +/* PLL Control Register */ +#define CPM_CPPCR_PLLM_BIT 23 +#define CPM_CPPCR_PLLM_MASK (0x1ff << CPM_CPPCR_PLLM_BIT) +#define CPM_CPPCR_PLLN_BIT 18 +#define CPM_CPPCR_PLLN_MASK (0x1f << CPM_CPPCR_PLLN_BIT) +#define CPM_CPPCR_PLLOD_BIT 16 +#define CPM_CPPCR_PLLOD_MASK (0x03 << CPM_CPPCR_PLLOD_BIT) +#define CPM_CPPCR_PLLS (1 << 10) +#define CPM_CPPCR_PLLBP (1 << 9) +#define CPM_CPPCR_PLLEN (1 << 8) +#define CPM_CPPCR_PLLST_BIT 0 +#define CPM_CPPCR_PLLST_MASK (0xff << CPM_CPPCR_PLLST_BIT) + +/* Low Power Control Register */ +#define CPM_LCR_DOZE_DUTY_BIT 3 +#define CPM_LCR_DOZE_DUTY_MASK (0x1f << CPM_LCR_DOZE_DUTY_BIT) +#define CPM_LCR_DOZE_ON (1 << 2) +#define CPM_LCR_LPM_BIT 0 +#define CPM_LCR_LPM_MASK (0x3 << CPM_LCR_LPM_BIT) + #define CPM_LCR_LPM_IDLE (0x0 << CPM_LCR_LPM_BIT) + #define CPM_LCR_LPM_SLEEP (0x1 << CPM_LCR_LPM_BIT) + +/* Clock Gate Register */ +#define CPM_CLKGR_UART1 (1 << 15) +#define CPM_CLKGR_UHC (1 << 14) +#define CPM_CLKGR_IPU (1 << 13) +#define CPM_CLKGR_DMAC (1 << 12) +#define CPM_CLKGR_UDC (1 << 11) +#define CPM_CLKGR_LCD (1 << 10) +#define CPM_CLKGR_CIM (1 << 9) +#define CPM_CLKGR_SADC (1 << 8) +#define CPM_CLKGR_MSC (1 << 7) +#define CPM_CLKGR_AIC1 (1 << 6) +#define CPM_CLKGR_AIC2 (1 << 5) +#define CPM_CLKGR_SSI (1 << 4) +#define CPM_CLKGR_I2C (1 << 3) +#define CPM_CLKGR_RTC (1 << 2) +#define CPM_CLKGR_TCU (1 << 1) +#define CPM_CLKGR_UART0 (1 << 0) + +/* Sleep Control Register */ +#define CPM_SCR_O1ST_BIT 8 +#define CPM_SCR_O1ST_MASK (0xff << CPM_SCR_O1ST_BIT) +#define CPM_SCR_USBPHY_ENABLE (1 << 6) +#define CPM_SCR_OSC_ENABLE (1 << 4) + +/* Hibernate Control Register */ +#define CPM_HCR_PD (1 << 0) + +/* Wakeup Filter Counter Register in Hibernate Mode */ +#define CPM_HWFCR_TIME_BIT 0 +#define CPM_HWFCR_TIME_MASK (0x3ff << CPM_HWFCR_TIME_BIT) + +/* Reset Counter Register in Hibernate Mode */ +#define CPM_HRCR_TIME_BIT 0 +#define CPM_HRCR_TIME_MASK (0x7f << CPM_HRCR_TIME_BIT) + +/* Wakeup Control Register in Hibernate Mode */ +#define CPM_HWCR_WLE_LOW (0 << 2) +#define CPM_HWCR_WLE_HIGH (1 << 2) +#define CPM_HWCR_PIN_WAKEUP (1 << 1) +#define CPM_HWCR_RTC_WAKEUP (1 << 0) + +/* Wakeup Status Register in Hibernate Mode */ +#define CPM_HWSR_WSR_PIN (1 << 1) +#define CPM_HWSR_WSR_RTC (1 << 0) + +/* Reset Status Register */ +#define CPM_RSR_HR (1 << 2) +#define CPM_RSR_WR (1 << 1) +#define CPM_RSR_PR (1 << 0) + + +/************************************************************************* + * TCU (Timer Counter Unit) + *************************************************************************/ +#define TCU_TSR (TCU_BASE + 0x1C) /* Timer Stop Register */ +#define TCU_TSSR (TCU_BASE + 0x2C) /* Timer Stop Set Register */ +#define TCU_TSCR (TCU_BASE + 0x3C) /* Timer Stop Clear Register */ +#define TCU_TER (TCU_BASE + 0x10) /* Timer Counter Enable Register */ +#define TCU_TESR (TCU_BASE + 0x14) /* Timer Counter Enable Set Register */ +#define TCU_TECR (TCU_BASE + 0x18) /* Timer Counter Enable Clear Register */ +#define TCU_TFR (TCU_BASE + 0x20) /* Timer Flag Register */ +#define TCU_TFSR (TCU_BASE + 0x24) /* Timer Flag Set Register */ +#define TCU_TFCR (TCU_BASE + 0x28) /* Timer Flag Clear Register */ +#define TCU_TMR (TCU_BASE + 0x30) /* Timer Mask Register */ +#define TCU_TMSR (TCU_BASE + 0x34) /* Timer Mask Set Register */ +#define TCU_TMCR (TCU_BASE + 0x38) /* Timer Mask Clear Register */ +#define TCU_TDFR0 (TCU_BASE + 0x40) /* Timer Data Full Register */ +#define TCU_TDHR0 (TCU_BASE + 0x44) /* Timer Data Half Register */ +#define TCU_TCNT0 (TCU_BASE + 0x48) /* Timer Counter Register */ +#define TCU_TCSR0 (TCU_BASE + 0x4C) /* Timer Control Register */ +#define TCU_TDFR1 (TCU_BASE + 0x50) +#define TCU_TDHR1 (TCU_BASE + 0x54) +#define TCU_TCNT1 (TCU_BASE + 0x58) +#define TCU_TCSR1 (TCU_BASE + 0x5C) +#define TCU_TDFR2 (TCU_BASE + 0x60) +#define TCU_TDHR2 (TCU_BASE + 0x64) +#define TCU_TCNT2 (TCU_BASE + 0x68) +#define TCU_TCSR2 (TCU_BASE + 0x6C) +#define TCU_TDFR3 (TCU_BASE + 0x70) +#define TCU_TDHR3 (TCU_BASE + 0x74) +#define TCU_TCNT3 (TCU_BASE + 0x78) +#define TCU_TCSR3 (TCU_BASE + 0x7C) +#define TCU_TDFR4 (TCU_BASE + 0x80) +#define TCU_TDHR4 (TCU_BASE + 0x84) +#define TCU_TCNT4 (TCU_BASE + 0x88) +#define TCU_TCSR4 (TCU_BASE + 0x8C) +#define TCU_TDFR5 (TCU_BASE + 0x90) +#define TCU_TDHR5 (TCU_BASE + 0x94) +#define TCU_TCNT5 (TCU_BASE + 0x98) +#define TCU_TCSR5 (TCU_BASE + 0x9C) + +#define REG_TCU_TSR REG32(TCU_TSR) +#define REG_TCU_TSSR REG32(TCU_TSSR) +#define REG_TCU_TSCR REG32(TCU_TSCR) +#define REG_TCU_TER REG8(TCU_TER) +#define REG_TCU_TESR REG8(TCU_TESR) +#define REG_TCU_TECR REG8(TCU_TECR) +#define REG_TCU_TFR REG32(TCU_TFR) +#define REG_TCU_TFSR REG32(TCU_TFSR) +#define REG_TCU_TFCR REG32(TCU_TFCR) +#define REG_TCU_TMR REG32(TCU_TMR) +#define REG_TCU_TMSR REG32(TCU_TMSR) +#define REG_TCU_TMCR REG32(TCU_TMCR) +#define REG_TCU_TDFR0 REG16(TCU_TDFR0) +#define REG_TCU_TDHR0 REG16(TCU_TDHR0) +#define REG_TCU_TCNT0 REG16(TCU_TCNT0) +#define REG_TCU_TCSR0 REG16(TCU_TCSR0) +#define REG_TCU_TDFR1 REG16(TCU_TDFR1) +#define REG_TCU_TDHR1 REG16(TCU_TDHR1) +#define REG_TCU_TCNT1 REG16(TCU_TCNT1) +#define REG_TCU_TCSR1 REG16(TCU_TCSR1) +#define REG_TCU_TDFR2 REG16(TCU_TDFR2) +#define REG_TCU_TDHR2 REG16(TCU_TDHR2) +#define REG_TCU_TCNT2 REG16(TCU_TCNT2) +#define REG_TCU_TCSR2 REG16(TCU_TCSR2) +#define REG_TCU_TDFR3 REG16(TCU_TDFR3) +#define REG_TCU_TDHR3 REG16(TCU_TDHR3) +#define REG_TCU_TCNT3 REG16(TCU_TCNT3) +#define REG_TCU_TCSR3 REG16(TCU_TCSR3) +#define REG_TCU_TDFR4 REG16(TCU_TDFR4) +#define REG_TCU_TDHR4 REG16(TCU_TDHR4) +#define REG_TCU_TCNT4 REG16(TCU_TCNT4) +#define REG_TCU_TCSR4 REG16(TCU_TCSR4) + +// n = 0,1,2,3,4,5 +#define TCU_TDFR(n) (TCU_BASE + (0x40 + (n)*0x10)) /* Timer Data Full Reg */ +#define TCU_TDHR(n) (TCU_BASE + (0x44 + (n)*0x10)) /* Timer Data Half Reg */ +#define TCU_TCNT(n) (TCU_BASE + (0x48 + (n)*0x10)) /* Timer Counter Reg */ +#define TCU_TCSR(n) (TCU_BASE + (0x4C + (n)*0x10)) /* Timer Control Reg */ + +#define REG_TCU_TDFR(n) REG16(TCU_TDFR((n))) +#define REG_TCU_TDHR(n) REG16(TCU_TDHR((n))) +#define REG_TCU_TCNT(n) REG16(TCU_TCNT((n))) +#define REG_TCU_TCSR(n) REG16(TCU_TCSR((n))) + +// Register definitions +#define TCU_TCSR_PWM_SD (1 << 9) +#define TCU_TCSR_PWM_INITL_HIGH (1 << 8) +#define TCU_TCSR_PWM_EN (1 << 7) +#define TCU_TCSR_PRESCALE_BIT 3 +#define TCU_TCSR_PRESCALE_MASK (0x7 << TCU_TCSR_PRESCALE_BIT) + #define TCU_TCSR_PRESCALE1 (0x0 << TCU_TCSR_PRESCALE_BIT) + #define TCU_TCSR_PRESCALE4 (0x1 << TCU_TCSR_PRESCALE_BIT) + #define TCU_TCSR_PRESCALE16 (0x2 << TCU_TCSR_PRESCALE_BIT) + #define TCU_TCSR_PRESCALE64 (0x3 << TCU_TCSR_PRESCALE_BIT) + #define TCU_TCSR_PRESCALE256 (0x4 << TCU_TCSR_PRESCALE_BIT) + #define TCU_TCSR_PRESCALE1024 (0x5 << TCU_TCSR_PRESCALE_BIT) +#define TCU_TCSR_EXT_EN (1 << 2) +#define TCU_TCSR_RTC_EN (1 << 1) +#define TCU_TCSR_PCK_EN (1 << 0) + +#define TCU_TER_TCEN5 (1 << 5) +#define TCU_TER_TCEN4 (1 << 4) +#define TCU_TER_TCEN3 (1 << 3) +#define TCU_TER_TCEN2 (1 << 2) +#define TCU_TER_TCEN1 (1 << 1) +#define TCU_TER_TCEN0 (1 << 0) + +#define TCU_TESR_TCST5 (1 << 5) +#define TCU_TESR_TCST4 (1 << 4) +#define TCU_TESR_TCST3 (1 << 3) +#define TCU_TESR_TCST2 (1 << 2) +#define TCU_TESR_TCST1 (1 << 1) +#define TCU_TESR_TCST0 (1 << 0) + +#define TCU_TECR_TCCL5 (1 << 5) +#define TCU_TECR_TCCL4 (1 << 4) +#define TCU_TECR_TCCL3 (1 << 3) +#define TCU_TECR_TCCL2 (1 << 2) +#define TCU_TECR_TCCL1 (1 << 1) +#define TCU_TECR_TCCL0 (1 << 0) + +#define TCU_TFR_HFLAG5 (1 << 21) +#define TCU_TFR_HFLAG4 (1 << 20) +#define TCU_TFR_HFLAG3 (1 << 19) +#define TCU_TFR_HFLAG2 (1 << 18) +#define TCU_TFR_HFLAG1 (1 << 17) +#define TCU_TFR_HFLAG0 (1 << 16) +#define TCU_TFR_FFLAG5 (1 << 5) +#define TCU_TFR_FFLAG4 (1 << 4) +#define TCU_TFR_FFLAG3 (1 << 3) +#define TCU_TFR_FFLAG2 (1 << 2) +#define TCU_TFR_FFLAG1 (1 << 1) +#define TCU_TFR_FFLAG0 (1 << 0) + +#define TCU_TFSR_HFLAG5 (1 << 21) +#define TCU_TFSR_HFLAG4 (1 << 20) +#define TCU_TFSR_HFLAG3 (1 << 19) +#define TCU_TFSR_HFLAG2 (1 << 18) +#define TCU_TFSR_HFLAG1 (1 << 17) +#define TCU_TFSR_HFLAG0 (1 << 16) +#define TCU_TFSR_FFLAG5 (1 << 5) +#define TCU_TFSR_FFLAG4 (1 << 4) +#define TCU_TFSR_FFLAG3 (1 << 3) +#define TCU_TFSR_FFLAG2 (1 << 2) +#define TCU_TFSR_FFLAG1 (1 << 1) +#define TCU_TFSR_FFLAG0 (1 << 0) + +#define TCU_TFCR_HFLAG5 (1 << 21) +#define TCU_TFCR_HFLAG4 (1 << 20) +#define TCU_TFCR_HFLAG3 (1 << 19) +#define TCU_TFCR_HFLAG2 (1 << 18) +#define TCU_TFCR_HFLAG1 (1 << 17) +#define TCU_TFCR_HFLAG0 (1 << 16) +#define TCU_TFCR_FFLAG5 (1 << 5) +#define TCU_TFCR_FFLAG4 (1 << 4) +#define TCU_TFCR_FFLAG3 (1 << 3) +#define TCU_TFCR_FFLAG2 (1 << 2) +#define TCU_TFCR_FFLAG1 (1 << 1) +#define TCU_TFCR_FFLAG0 (1 << 0) + +#define TCU_TMR_HMASK5 (1 << 21) +#define TCU_TMR_HMASK4 (1 << 20) +#define TCU_TMR_HMASK3 (1 << 19) +#define TCU_TMR_HMASK2 (1 << 18) +#define TCU_TMR_HMASK1 (1 << 17) +#define TCU_TMR_HMASK0 (1 << 16) +#define TCU_TMR_FMASK5 (1 << 5) +#define TCU_TMR_FMASK4 (1 << 4) +#define TCU_TMR_FMASK3 (1 << 3) +#define TCU_TMR_FMASK2 (1 << 2) +#define TCU_TMR_FMASK1 (1 << 1) +#define TCU_TMR_FMASK0 (1 << 0) + +#define TCU_TMSR_HMST5 (1 << 21) +#define TCU_TMSR_HMST4 (1 << 20) +#define TCU_TMSR_HMST3 (1 << 19) +#define TCU_TMSR_HMST2 (1 << 18) +#define TCU_TMSR_HMST1 (1 << 17) +#define TCU_TMSR_HMST0 (1 << 16) +#define TCU_TMSR_FMST5 (1 << 5) +#define TCU_TMSR_FMST4 (1 << 4) +#define TCU_TMSR_FMST3 (1 << 3) +#define TCU_TMSR_FMST2 (1 << 2) +#define TCU_TMSR_FMST1 (1 << 1) +#define TCU_TMSR_FMST0 (1 << 0) + +#define TCU_TMCR_HMCL5 (1 << 21) +#define TCU_TMCR_HMCL4 (1 << 20) +#define TCU_TMCR_HMCL3 (1 << 19) +#define TCU_TMCR_HMCL2 (1 << 18) +#define TCU_TMCR_HMCL1 (1 << 17) +#define TCU_TMCR_HMCL0 (1 << 16) +#define TCU_TMCR_FMCL5 (1 << 5) +#define TCU_TMCR_FMCL4 (1 << 4) +#define TCU_TMCR_FMCL3 (1 << 3) +#define TCU_TMCR_FMCL2 (1 << 2) +#define TCU_TMCR_FMCL1 (1 << 1) +#define TCU_TMCR_FMCL0 (1 << 0) + +#define TCU_TSR_WDTS (1 << 16) +#define TCU_TSR_STOP5 (1 << 5) +#define TCU_TSR_STOP4 (1 << 4) +#define TCU_TSR_STOP3 (1 << 3) +#define TCU_TSR_STOP2 (1 << 2) +#define TCU_TSR_STOP1 (1 << 1) +#define TCU_TSR_STOP0 (1 << 0) + +#define TCU_TSSR_WDTSS (1 << 16) +#define TCU_TSSR_STPS5 (1 << 5) +#define TCU_TSSR_STPS4 (1 << 4) +#define TCU_TSSR_STPS3 (1 << 3) +#define TCU_TSSR_STPS2 (1 << 2) +#define TCU_TSSR_STPS1 (1 << 1) +#define TCU_TSSR_STPS0 (1 << 0) + +#define TCU_TSSR_WDTSC (1 << 16) +#define TCU_TSSR_STPC5 (1 << 5) +#define TCU_TSSR_STPC4 (1 << 4) +#define TCU_TSSR_STPC3 (1 << 3) +#define TCU_TSSR_STPC2 (1 << 2) +#define TCU_TSSR_STPC1 (1 << 1) +#define TCU_TSSR_STPC0 (1 << 0) + + +/************************************************************************* + * WDT (WatchDog Timer) + *************************************************************************/ +#define WDT_TDR (WDT_BASE + 0x00) +#define WDT_TCER (WDT_BASE + 0x04) +#define WDT_TCNT (WDT_BASE + 0x08) +#define WDT_TCSR (WDT_BASE + 0x0C) + +#define REG_WDT_TDR REG16(WDT_TDR) +#define REG_WDT_TCER REG8(WDT_TCER) +#define REG_WDT_TCNT REG16(WDT_TCNT) +#define REG_WDT_TCSR REG16(WDT_TCSR) + +// Register definition +#define WDT_TCSR_PRESCALE_BIT 3 +#define WDT_TCSR_PRESCALE_MASK (0x7 << WDT_TCSR_PRESCALE_BIT) + #define WDT_TCSR_PRESCALE1 (0x0 << WDT_TCSR_PRESCALE_BIT) + #define WDT_TCSR_PRESCALE4 (0x1 << WDT_TCSR_PRESCALE_BIT) + #define WDT_TCSR_PRESCALE16 (0x2 << WDT_TCSR_PRESCALE_BIT) + #define WDT_TCSR_PRESCALE64 (0x3 << WDT_TCSR_PRESCALE_BIT) + #define WDT_TCSR_PRESCALE256 (0x4 << WDT_TCSR_PRESCALE_BIT) + #define WDT_TCSR_PRESCALE1024 (0x5 << WDT_TCSR_PRESCALE_BIT) +#define WDT_TCSR_EXT_EN (1 << 2) +#define WDT_TCSR_RTC_EN (1 << 1) +#define WDT_TCSR_PCK_EN (1 << 0) + +#define WDT_TCER_TCEN (1 << 0) + + +/************************************************************************* + * DMAC (DMA Controller) + *************************************************************************/ + +#define MAX_DMA_NUM 6 /* max 6 channels */ + +#define DMAC_DSAR(n) (DMAC_BASE + (0x00 + (n) * 0x20)) /* DMA source address */ +#define DMAC_DTAR(n) (DMAC_BASE + (0x04 + (n) * 0x20)) /* DMA target address */ +#define DMAC_DTCR(n) (DMAC_BASE + (0x08 + (n) * 0x20)) /* DMA transfer count */ +#define DMAC_DRSR(n) (DMAC_BASE + (0x0c + (n) * 0x20)) /* DMA request source */ +#define DMAC_DCCSR(n) (DMAC_BASE + (0x10 + (n) * 0x20)) /* DMA control/status */ +#define DMAC_DCMD(n) (DMAC_BASE + (0x14 + (n) * 0x20)) /* DMA command */ +#define DMAC_DDA(n) (DMAC_BASE + (0x18 + (n) * 0x20)) /* DMA descriptor address */ +#define DMAC_DMACR (DMAC_BASE + 0x0300) /* DMA control register */ +#define DMAC_DMAIPR (DMAC_BASE + 0x0304) /* DMA interrupt pending */ +#define DMAC_DMADBR (DMAC_BASE + 0x0308) /* DMA doorbell */ +#define DMAC_DMADBSR (DMAC_BASE + 0x030C) /* DMA doorbell set */ + +// channel 0 +#define DMAC_DSAR0 DMAC_DSAR(0) +#define DMAC_DTAR0 DMAC_DTAR(0) +#define DMAC_DTCR0 DMAC_DTCR(0) +#define DMAC_DRSR0 DMAC_DRSR(0) +#define DMAC_DCCSR0 DMAC_DCCSR(0) +#define DMAC_DCMD0 DMAC_DCMD(0) +#define DMAC_DDA0 DMAC_DDA(0) + +// channel 1 +#define DMAC_DSAR1 DMAC_DSAR(1) +#define DMAC_DTAR1 DMAC_DTAR(1) +#define DMAC_DTCR1 DMAC_DTCR(1) +#define DMAC_DRSR1 DMAC_DRSR(1) +#define DMAC_DCCSR1 DMAC_DCCSR(1) +#define DMAC_DCMD1 DMAC_DCMD(1) +#define DMAC_DDA1 DMAC_DDA(1) + +// channel 2 +#define DMAC_DSAR2 DMAC_DSAR(2) +#define DMAC_DTAR2 DMAC_DTAR(2) +#define DMAC_DTCR2 DMAC_DTCR(2) +#define DMAC_DRSR2 DMAC_DRSR(2) +#define DMAC_DCCSR2 DMAC_DCCSR(2) +#define DMAC_DCMD2 DMAC_DCMD(2) +#define DMAC_DDA2 DMAC_DDA(2) + +// channel 3 +#define DMAC_DSAR3 DMAC_DSAR(3) +#define DMAC_DTAR3 DMAC_DTAR(3) +#define DMAC_DTCR3 DMAC_DTCR(3) +#define DMAC_DRSR3 DMAC_DRSR(3) +#define DMAC_DCCSR3 DMAC_DCCSR(3) +#define DMAC_DCMD3 DMAC_DCMD(3) +#define DMAC_DDA3 DMAC_DDA(3) + +// channel 4 +#define DMAC_DSAR4 DMAC_DSAR(4) +#define DMAC_DTAR4 DMAC_DTAR(4) +#define DMAC_DTCR4 DMAC_DTCR(4) +#define DMAC_DRSR4 DMAC_DRSR(4) +#define DMAC_DCCSR4 DMAC_DCCSR(4) +#define DMAC_DCMD4 DMAC_DCMD(4) +#define DMAC_DDA4 DMAC_DDA(4) + +// channel 5 +#define DMAC_DSAR5 DMAC_DSAR(5) +#define DMAC_DTAR5 DMAC_DTAR(5) +#define DMAC_DTCR5 DMAC_DTCR(5) +#define DMAC_DRSR5 DMAC_DRSR(5) +#define DMAC_DCCSR5 DMAC_DCCSR(5) +#define DMAC_DCMD5 DMAC_DCMD(5) +#define DMAC_DDA5 DMAC_DDA(5) + +#define REG_DMAC_DSAR(n) REG32(DMAC_DSAR((n))) +#define REG_DMAC_DTAR(n) REG32(DMAC_DTAR((n))) +#define REG_DMAC_DTCR(n) REG32(DMAC_DTCR((n))) +#define REG_DMAC_DRSR(n) REG32(DMAC_DRSR((n))) +#define REG_DMAC_DCCSR(n) REG32(DMAC_DCCSR((n))) +#define REG_DMAC_DCMD(n) REG32(DMAC_DCMD((n))) +#define REG_DMAC_DDA(n) REG32(DMAC_DDA((n))) +#define REG_DMAC_DMACR REG32(DMAC_DMACR) +#define REG_DMAC_DMAIPR REG32(DMAC_DMAIPR) +#define REG_DMAC_DMADBR REG32(DMAC_DMADBR) +#define REG_DMAC_DMADBSR REG32(DMAC_DMADBSR) + +// DMA request source register +#define DMAC_DRSR_RS_BIT 0 +#define DMAC_DRSR_RS_MASK (0x1f << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_AUTO (8 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_UART0OUT (20 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_UART0IN (21 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_SSIOUT (22 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_SSIIN (23 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_AICOUT (24 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_AICIN (25 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_MSCOUT (26 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_MSCIN (27 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_TCU (28 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_SADC (29 << DMAC_DRSR_RS_BIT) + #define DMAC_DRSR_RS_SLCD (30 << DMAC_DRSR_RS_BIT) + +// DMA channel control/status register +#define DMAC_DCCSR_NDES (1 << 31) /* descriptor (0) or not (1) ? */ +#define DMAC_DCCSR_CDOA_BIT 16 /* copy of DMA offset address */ +#define DMAC_DCCSR_CDOA_MASK (0xff << DMAC_DCCSR_CDOA_BIT) +#define DMAC_DCCSR_INV (1 << 6) /* descriptor invalid */ +#define DMAC_DCCSR_AR (1 << 4) /* address error */ +#define DMAC_DCCSR_TT (1 << 3) /* transfer terminated */ +#define DMAC_DCCSR_HLT (1 << 2) /* DMA halted */ +#define DMAC_DCCSR_CT (1 << 1) /* count terminated */ +#define DMAC_DCCSR_EN (1 << 0) /* channel enable bit */ + +// DMA channel command register +#define DMAC_DCMD_SAI (1 << 23) /* source address increment */ +#define DMAC_DCMD_DAI (1 << 22) /* dest address increment */ +#define DMAC_DCMD_RDIL_BIT 16 /* request detection interval length */ +#define DMAC_DCMD_RDIL_MASK (0x0f << DMAC_DCMD_RDIL_BIT) + #define DMAC_DCMD_RDIL_IGN (0 << DMAC_DCMD_RDIL_BIT) + #define DMAC_DCMD_RDIL_2 (1 << DMAC_DCMD_RDIL_BIT) + #define DMAC_DCMD_RDIL_4 (2 << DMAC_DCMD_RDIL_BIT) + #define DMAC_DCMD_RDIL_8 (3 << DMAC_DCMD_RDIL_BIT) + #define DMAC_DCMD_RDIL_12 (4 << DMAC_DCMD_RDIL_BIT) + #define DMAC_DCMD_RDIL_16 (5 << DMAC_DCMD_RDIL_BIT) + #define DMAC_DCMD_RDIL_20 (6 << DMAC_DCMD_RDIL_BIT) + #define DMAC_DCMD_RDIL_24 (7 << DMAC_DCMD_RDIL_BIT) + #define DMAC_DCMD_RDIL_28 (8 << DMAC_DCMD_RDIL_BIT) + #define DMAC_DCMD_RDIL_32 (9 << DMAC_DCMD_RDIL_BIT) + #define DMAC_DCMD_RDIL_48 (10 << DMAC_DCMD_RDIL_BIT) + #define DMAC_DCMD_RDIL_60 (11 << DMAC_DCMD_RDIL_BIT) + #define DMAC_DCMD_RDIL_64 (12 << DMAC_DCMD_RDIL_BIT) + #define DMAC_DCMD_RDIL_124 (13 << DMAC_DCMD_RDIL_BIT) + #define DMAC_DCMD_RDIL_128 (14 << DMAC_DCMD_RDIL_BIT) + #define DMAC_DCMD_RDIL_200 (15 << DMAC_DCMD_RDIL_BIT) +#define DMAC_DCMD_SWDH_BIT 14 /* source port width */ +#define DMAC_DCMD_SWDH_MASK (0x03 << DMAC_DCMD_SWDH_BIT) + #define DMAC_DCMD_SWDH_32 (0 << DMAC_DCMD_SWDH_BIT) + #define DMAC_DCMD_SWDH_8 (1 << DMAC_DCMD_SWDH_BIT) + #define DMAC_DCMD_SWDH_16 (2 << DMAC_DCMD_SWDH_BIT) +#define DMAC_DCMD_DWDH_BIT 12 /* dest port width */ +#define DMAC_DCMD_DWDH_MASK (0x03 << DMAC_DCMD_DWDH_BIT) + #define DMAC_DCMD_DWDH_32 (0 << DMAC_DCMD_DWDH_BIT) + #define DMAC_DCMD_DWDH_8 (1 << DMAC_DCMD_DWDH_BIT) + #define DMAC_DCMD_DWDH_16 (2 << DMAC_DCMD_DWDH_BIT) +#define DMAC_DCMD_DS_BIT 8 /* transfer data size of a data unit */ +#define DMAC_DCMD_DS_MASK (0x07 << DMAC_DCMD_DS_BIT) + #define DMAC_DCMD_DS_32BIT (0 << DMAC_DCMD_DS_BIT) + #define DMAC_DCMD_DS_8BIT (1 << DMAC_DCMD_DS_BIT) + #define DMAC_DCMD_DS_16BIT (2 << DMAC_DCMD_DS_BIT) + #define DMAC_DCMD_DS_16BYTE (3 << DMAC_DCMD_DS_BIT) + #define DMAC_DCMD_DS_32BYTE (4 << DMAC_DCMD_DS_BIT) +#define DMAC_DCMD_TM (1 << 7) /* transfer mode: 0-single 1-block */ +#define DMAC_DCMD_DES_V (1 << 4) /* descriptor valid flag */ +#define DMAC_DCMD_DES_VM (1 << 3) /* descriptor valid mask: 1:support V-bit */ +#define DMAC_DCMD_DES_VIE (1 << 2) /* DMA valid error interrupt enable */ +#define DMAC_DCMD_TIE (1 << 1) /* DMA transfer interrupt enable */ +#define DMAC_DCMD_LINK (1 << 0) /* descriptor link enable */ + +// DMA descriptor address register +#define DMAC_DDA_BASE_BIT 12 /* descriptor base address */ +#define DMAC_DDA_BASE_MASK (0x0fffff << DMAC_DDA_BASE_BIT) +#define DMAC_DDA_OFFSET_BIT 4 /* descriptor offset address */ +#define DMAC_DDA_OFFSET_MASK (0x0ff << DMAC_DDA_OFFSET_BIT) + +// DMA control register +#define DMAC_DMACR_PR_BIT 8 /* channel priority mode */ +#define DMAC_DMACR_PR_MASK (0x03 << DMAC_DMACR_PR_BIT) + #define DMAC_DMACR_PR_012345 (0 << DMAC_DMACR_PR_BIT) + #define DMAC_DMACR_PR_023145 (1 << DMAC_DMACR_PR_BIT) + #define DMAC_DMACR_PR_201345 (2 << DMAC_DMACR_PR_BIT) + #define DMAC_DMACR_PR_RR (3 << DMAC_DMACR_PR_BIT) /* round robin */ +#define DMAC_DMACR_HLT (1 << 3) /* DMA halt flag */ +#define DMAC_DMACR_AR (1 << 2) /* address error flag */ +#define DMAC_DMACR_DMAE (1 << 0) /* DMA enable bit */ + +// DMA doorbell register +#define DMAC_DMADBR_DB5 (1 << 5) /* doorbell for channel 5 */ +#define DMAC_DMADBR_DB4 (1 << 5) /* doorbell for channel 4 */ +#define DMAC_DMADBR_DB3 (1 << 5) /* doorbell for channel 3 */ +#define DMAC_DMADBR_DB2 (1 << 5) /* doorbell for channel 2 */ +#define DMAC_DMADBR_DB1 (1 << 5) /* doorbell for channel 1 */ +#define DMAC_DMADBR_DB0 (1 << 5) /* doorbell for channel 0 */ + +// DMA doorbell set register +#define DMAC_DMADBSR_DBS5 (1 << 5) /* enable doorbell for channel 5 */ +#define DMAC_DMADBSR_DBS4 (1 << 5) /* enable doorbell for channel 4 */ +#define DMAC_DMADBSR_DBS3 (1 << 5) /* enable doorbell for channel 3 */ +#define DMAC_DMADBSR_DBS2 (1 << 5) /* enable doorbell for channel 2 */ +#define DMAC_DMADBSR_DBS1 (1 << 5) /* enable doorbell for channel 1 */ +#define DMAC_DMADBSR_DBS0 (1 << 5) /* enable doorbell for channel 0 */ + +// DMA interrupt pending register +#define DMAC_DMAIPR_CIRQ5 (1 << 5) /* irq pending status for channel 5 */ +#define DMAC_DMAIPR_CIRQ4 (1 << 4) /* irq pending status for channel 4 */ +#define DMAC_DMAIPR_CIRQ3 (1 << 3) /* irq pending status for channel 3 */ +#define DMAC_DMAIPR_CIRQ2 (1 << 2) /* irq pending status for channel 2 */ +#define DMAC_DMAIPR_CIRQ1 (1 << 1) /* irq pending status for channel 1 */ +#define DMAC_DMAIPR_CIRQ0 (1 << 0) /* irq pending status for channel 0 */ + + +/************************************************************************* + * GPIO (General-Purpose I/O Ports) + *************************************************************************/ +#define MAX_GPIO_NUM 128 + +//n = 0,1,2,3 +#define GPIO_PXPIN(n) (GPIO_BASE + (0x00 + (n)*0x100)) /* PIN Level Register */ +#define GPIO_PXDAT(n) (GPIO_BASE + (0x10 + (n)*0x100)) /* Port Data Register */ +#define GPIO_PXDATS(n) (GPIO_BASE + (0x14 + (n)*0x100)) /* Port Data Set Register */ +#define GPIO_PXDATC(n) (GPIO_BASE + (0x18 + (n)*0x100)) /* Port Data Clear Register */ +#define GPIO_PXIM(n) (GPIO_BASE + (0x20 + (n)*0x100)) /* Interrupt Mask Register */ +#define GPIO_PXIMS(n) (GPIO_BASE + (0x24 + (n)*0x100)) /* Interrupt Mask Set Reg */ +#define GPIO_PXIMC(n) (GPIO_BASE + (0x28 + (n)*0x100)) /* Interrupt Mask Clear Reg */ +#define GPIO_PXPE(n) (GPIO_BASE + (0x30 + (n)*0x100)) /* Pull Enable Register */ +#define GPIO_PXPES(n) (GPIO_BASE + (0x34 + (n)*0x100)) /* Pull Enable Set Reg. */ +#define GPIO_PXPEC(n) (GPIO_BASE + (0x38 + (n)*0x100)) /* Pull Enable Clear Reg. */ +#define GPIO_PXFUN(n) (GPIO_BASE + (0x40 + (n)*0x100)) /* Function Register */ +#define GPIO_PXFUNS(n) (GPIO_BASE + (0x44 + (n)*0x100)) /* Function Set Register */ +#define GPIO_PXFUNC(n) (GPIO_BASE + (0x48 + (n)*0x100)) /* Function Clear Register */ +#define GPIO_PXSEL(n) (GPIO_BASE + (0x50 + (n)*0x100)) /* Select Register */ +#define GPIO_PXSELS(n) (GPIO_BASE + (0x54 + (n)*0x100)) /* Select Set Register */ +#define GPIO_PXSELC(n) (GPIO_BASE + (0x58 + (n)*0x100)) /* Select Clear Register */ +#define GPIO_PXDIR(n) (GPIO_BASE + (0x60 + (n)*0x100)) /* Direction Register */ +#define GPIO_PXDIRS(n) (GPIO_BASE + (0x64 + (n)*0x100)) /* Direction Set Register */ +#define GPIO_PXDIRC(n) (GPIO_BASE + (0x68 + (n)*0x100)) /* Direction Clear Register */ +#define GPIO_PXTRG(n) (GPIO_BASE + (0x70 + (n)*0x100)) /* Trigger Register */ +#define GPIO_PXTRGS(n) (GPIO_BASE + (0x74 + (n)*0x100)) /* Trigger Set Register */ +#define GPIO_PXTRGC(n) (GPIO_BASE + (0x78 + (n)*0x100)) /* Trigger Set Register */ +#define GPIO_PXFLG(n) (GPIO_BASE + (0x80 + (n)*0x100)) /* Port Flag Register */ +#define GPIO_PXFLGC(n) (GPIO_BASE + (0x14 + (n)*0x100)) /* Port Flag Clear Register */ + +#define REG_GPIO_PXPIN(n) REG32(GPIO_PXPIN((n))) /* PIN level */ +#define REG_GPIO_PXDAT(n) REG32(GPIO_PXDAT((n))) /* 1: interrupt pending */ +#define REG_GPIO_PXDATS(n) REG32(GPIO_PXDATS((n))) +#define REG_GPIO_PXDATC(n) REG32(GPIO_PXDATC((n))) +#define REG_GPIO_PXIM(n) REG32(GPIO_PXIM((n))) /* 1: mask pin interrupt */ +#define REG_GPIO_PXIMS(n) REG32(GPIO_PXIMS((n))) +#define REG_GPIO_PXIMC(n) REG32(GPIO_PXIMC((n))) +#define REG_GPIO_PXPE(n) REG32(GPIO_PXPE((n))) /* 1: disable pull up/down */ +#define REG_GPIO_PXPES(n) REG32(GPIO_PXPES((n))) +#define REG_GPIO_PXPEC(n) REG32(GPIO_PXPEC((n))) +#define REG_GPIO_PXFUN(n) REG32(GPIO_PXFUN((n))) /* 0:GPIO or intr, 1:FUNC */ +#define REG_GPIO_PXFUNS(n) REG32(GPIO_PXFUNS((n))) +#define REG_GPIO_PXFUNC(n) REG32(GPIO_PXFUNC((n))) +#define REG_GPIO_PXSEL(n) REG32(GPIO_PXSEL((n))) /* 0:GPIO/Fun0,1:intr/fun1*/ +#define REG_GPIO_PXSELS(n) REG32(GPIO_PXSELS((n))) +#define REG_GPIO_PXSELC(n) REG32(GPIO_PXSELC((n))) +#define REG_GPIO_PXDIR(n) REG32(GPIO_PXDIR((n))) /* 0:input/low-level-trig/falling-edge-trig, 1:output/high-level-trig/rising-edge-trig */ +#define REG_GPIO_PXDIRS(n) REG32(GPIO_PXDIRS((n))) +#define REG_GPIO_PXDIRC(n) REG32(GPIO_PXDIRC((n))) +#define REG_GPIO_PXTRG(n) REG32(GPIO_PXTRG((n))) /* 0:level-trigger, 1:edge-trigger */ +#define REG_GPIO_PXTRGS(n) REG32(GPIO_PXTRGS((n))) +#define REG_GPIO_PXTRGC(n) REG32(GPIO_PXTRGC((n))) +#define REG_GPIO_PXFLG(n) REG32(GPIO_PXFLG((n))) /* interrupt flag */ +#define REG_GPIO_PXFLGC(n) REG32(GPIO_PXFLGC((n))) /* interrupt flag */ + + +/************************************************************************* + * UART + *************************************************************************/ + +#define IRDA_BASE UART0_BASE +#define UART_BASE UART0_BASE +#define UART_OFF 0x1000 + +/* Register Offset */ +#define OFF_RDR (0x00) /* R 8b H'xx */ +#define OFF_TDR (0x00) /* W 8b H'xx */ +#define OFF_DLLR (0x00) /* RW 8b H'00 */ +#define OFF_DLHR (0x04) /* RW 8b H'00 */ +#define OFF_IER (0x04) /* RW 8b H'00 */ +#define OFF_ISR (0x08) /* R 8b H'01 */ +#define OFF_FCR (0x08) /* W 8b H'00 */ +#define OFF_LCR (0x0C) /* RW 8b H'00 */ +#define OFF_MCR (0x10) /* RW 8b H'00 */ +#define OFF_LSR (0x14) /* R 8b H'00 */ +#define OFF_MSR (0x18) /* R 8b H'00 */ +#define OFF_SPR (0x1C) /* RW 8b H'00 */ +#define OFF_SIRCR (0x20) /* RW 8b H'00, UART0 */ +#define OFF_UMR (0x24) /* RW 8b H'00, UART M Register */ +#define OFF_UACR (0x28) /* RW 8b H'00, UART Add Cycle Register */ + +/* Register Address */ +#define UART0_RDR (UART0_BASE + OFF_RDR) +#define UART0_TDR (UART0_BASE + OFF_TDR) +#define UART0_DLLR (UART0_BASE + OFF_DLLR) +#define UART0_DLHR (UART0_BASE + OFF_DLHR) +#define UART0_IER (UART0_BASE + OFF_IER) +#define UART0_ISR (UART0_BASE + OFF_ISR) +#define UART0_FCR (UART0_BASE + OFF_FCR) +#define UART0_LCR (UART0_BASE + OFF_LCR) +#define UART0_MCR (UART0_BASE + OFF_MCR) +#define UART0_LSR (UART0_BASE + OFF_LSR) +#define UART0_MSR (UART0_BASE + OFF_MSR) +#define UART0_SPR (UART0_BASE + OFF_SPR) +#define UART0_SIRCR (UART0_BASE + OFF_SIRCR) +#define UART0_UMR (UART0_BASE + OFF_UMR) +#define UART0_UACR (UART0_BASE + OFF_UACR) + +/* + * Define macros for UARTIER + * UART Interrupt Enable Register + */ +#define UARTIER_RIE (1 << 0) /* 0: receive fifo full interrupt disable */ +#define UARTIER_TIE (1 << 1) /* 0: transmit fifo empty interrupt disable */ +#define UARTIER_RLIE (1 << 2) /* 0: receive line status interrupt disable */ +#define UARTIER_MIE (1 << 3) /* 0: modem status interrupt disable */ +#define UARTIER_RTIE (1 << 4) /* 0: receive timeout interrupt disable */ + +/* + * Define macros for UARTISR + * UART Interrupt Status Register + */ +#define UARTISR_IP (1 << 0) /* 0: interrupt is pending 1: no interrupt */ +#define UARTISR_IID (7 << 1) /* Source of Interrupt */ +#define UARTISR_IID_MSI (0 << 1) /* Modem status interrupt */ +#define UARTISR_IID_THRI (1 << 1) /* Transmitter holding register empty */ +#define UARTISR_IID_RDI (2 << 1) /* Receiver data interrupt */ +#define UARTISR_IID_RLSI (3 << 1) /* Receiver line status interrupt */ +#define UARTISR_IID_RTO (6 << 1) /* Receive timeout */ +#define UARTISR_FFMS (3 << 6) /* FIFO mode select, set when UARTFCR.FE is set to 1 */ +#define UARTISR_FFMS_NO_FIFO (0 << 6) +#define UARTISR_FFMS_FIFO_MODE (3 << 6) + +/* + * Define macros for UARTFCR + * UART FIFO Control Register + */ +#define UARTFCR_FE (1 << 0) /* 0: non-FIFO mode 1: FIFO mode */ +#define UARTFCR_RFLS (1 << 1) /* write 1 to flush receive FIFO */ +#define UARTFCR_TFLS (1 << 2) /* write 1 to flush transmit FIFO */ +#define UARTFCR_DMS (1 << 3) /* 0: disable DMA mode */ +#define UARTFCR_UUE (1 << 4) /* 0: disable UART */ +#define UARTFCR_RTRG (3 << 6) /* Receive FIFO Data Trigger */ +#define UARTFCR_RTRG_1 (0 << 6) +#define UARTFCR_RTRG_4 (1 << 6) +#define UARTFCR_RTRG_8 (2 << 6) +#define UARTFCR_RTRG_15 (3 << 6) + +/* + * Define macros for UARTLCR + * UART Line Control Register + */ +#define UARTLCR_WLEN (3 << 0) /* word length */ +#define UARTLCR_WLEN_5 (0 << 0) +#define UARTLCR_WLEN_6 (1 << 0) +#define UARTLCR_WLEN_7 (2 << 0) +#define UARTLCR_WLEN_8 (3 << 0) +#define UARTLCR_STOP (1 << 2) /* 0: 1 stop bit when word length is 5,6,7,8 + 1: 1.5 stop bits when 5; 2 stop bits when 6,7,8 */ +#define UARTLCR_STOP1 (0 << 2) +#define UARTLCR_STOP2 (1 << 2) +#define UARTLCR_PE (1 << 3) /* 0: parity disable */ +#define UARTLCR_PROE (1 << 4) /* 0: even parity 1: odd parity */ +#define UARTLCR_SPAR (1 << 5) /* 0: sticky parity disable */ +#define UARTLCR_SBRK (1 << 6) /* write 0 normal, write 1 send break */ +#define UARTLCR_DLAB (1 << 7) /* 0: access UARTRDR/TDR/IER 1: access UARTDLLR/DLHR */ + +/* + * Define macros for UARTLSR + * UART Line Status Register + */ +#define UARTLSR_DR (1 << 0) /* 0: receive FIFO is empty 1: receive data is ready */ +#define UARTLSR_ORER (1 << 1) /* 0: no overrun error */ +#define UARTLSR_PER (1 << 2) /* 0: no parity error */ +#define UARTLSR_FER (1 << 3) /* 0; no framing error */ +#define UARTLSR_BRK (1 << 4) /* 0: no break detected 1: receive a break signal */ +#define UARTLSR_TDRQ (1 << 5) /* 1: transmit FIFO half "empty" */ +#define UARTLSR_TEMT (1 << 6) /* 1: transmit FIFO and shift registers empty */ +#define UARTLSR_RFER (1 << 7) /* 0: no receive error 1: receive error in FIFO mode */ + +/* + * Define macros for UARTMCR + * UART Modem Control Register + */ +#define UARTMCR_RTS (1 << 1) /* 0: RTS_ output high, 1: RTS_ output low */ +#define UARTMCR_LOOP (1 << 4) /* 0: normal 1: loopback mode */ +#define UARTMCR_MCE (1 << 7) /* 0: modem function is disable */ + +/* + * Define macros for UARTMSR + * UART Modem Status Register + */ +#define UARTMSR_CCTS (1 << 0) /* 1: a change on CTS_ pin */ +#define UARTMSR_CTS (1 << 4) /* 0: CTS_ pin is high */ + +/* + * Define macros for SIRCR + * Slow IrDA Control Register + */ +#define SIRCR_TSIRE (1 << 0) /* 0: transmitter is in UART mode 1: SIR mode */ +#define SIRCR_RSIRE (1 << 1) /* 0: receiver is in UART mode 1: SIR mode */ +#define SIRCR_TPWS (1 << 2) /* 0: transmit 0 pulse width is 3/16 of bit length + 1: 0 pulse width is 1.6us for 115.2Kbps */ +#define SIRCR_TDPL (1 << 3) /* 0: encoder generates a positive pulse for 0 */ +#define SIRCR_RDPL (1 << 4) /* 0: decoder interprets positive pulse as 0 */ + + +/************************************************************************* + * AIC (AC97/I2S Controller) + *************************************************************************/ +#define AIC_FR (AIC_BASE + 0x000) +#define AIC_CR (AIC_BASE + 0x004) +#define AIC_ACCR1 (AIC_BASE + 0x008) +#define AIC_ACCR2 (AIC_BASE + 0x00C) +#define AIC_I2SCR (AIC_BASE + 0x010) +#define AIC_SR (AIC_BASE + 0x014) +#define AIC_ACSR (AIC_BASE + 0x018) +#define AIC_I2SSR (AIC_BASE + 0x01C) +#define AIC_ACCAR (AIC_BASE + 0x020) +#define AIC_ACCDR (AIC_BASE + 0x024) +#define AIC_ACSAR (AIC_BASE + 0x028) +#define AIC_ACSDR (AIC_BASE + 0x02C) +#define AIC_I2SDIV (AIC_BASE + 0x030) +#define AIC_DR (AIC_BASE + 0x034) + +#define REG_AIC_FR REG32(AIC_FR) +#define REG_AIC_CR REG32(AIC_CR) +#define REG_AIC_ACCR1 REG32(AIC_ACCR1) +#define REG_AIC_ACCR2 REG32(AIC_ACCR2) +#define REG_AIC_I2SCR REG32(AIC_I2SCR) +#define REG_AIC_SR REG32(AIC_SR) +#define REG_AIC_ACSR REG32(AIC_ACSR) +#define REG_AIC_I2SSR REG32(AIC_I2SSR) +#define REG_AIC_ACCAR REG32(AIC_ACCAR) +#define REG_AIC_ACCDR REG32(AIC_ACCDR) +#define REG_AIC_ACSAR REG32(AIC_ACSAR) +#define REG_AIC_ACSDR REG32(AIC_ACSDR) +#define REG_AIC_I2SDIV REG32(AIC_I2SDIV) +#define REG_AIC_DR REG32(AIC_DR) + +/* AIC Controller Configuration Register (AIC_FR) */ + +#define AIC_FR_RFTH_BIT 12 /* Receive FIFO Threshold */ +#define AIC_FR_RFTH_MASK (0xf << AIC_FR_RFTH_BIT) +#define AIC_FR_TFTH_BIT 8 /* Transmit FIFO Threshold */ +#define AIC_FR_TFTH_MASK (0xf << AIC_FR_TFTH_BIT) +#define AIC_FR_LSMP (1 << 6) /* Play Zero sample or last sample */ +#define AIC_FR_ICDC (1 << 5) /* External(0) or Internal CODEC(1) */ +#define AIC_FR_AUSEL (1 << 4) /* AC97(0) or I2S/MSB-justified(1) */ +#define AIC_FR_RST (1 << 3) /* AIC registers reset */ +#define AIC_FR_BCKD (1 << 2) /* I2S BIT_CLK direction, 0:input,1:output */ +#define AIC_FR_SYNCD (1 << 1) /* I2S SYNC direction, 0:input,1:output */ +#define AIC_FR_ENB (1 << 0) /* AIC enable bit */ + +/* AIC Controller Common Control Register (AIC_CR) */ + +#define AIC_CR_OSS_BIT 19 /* Output Sample Size from memory (AIC V2 only) */ +#define AIC_CR_OSS_MASK (0x7 << AIC_CR_OSS_BIT) + #define AIC_CR_OSS_8BIT (0x0 << AIC_CR_OSS_BIT) + #define AIC_CR_OSS_16BIT (0x1 << AIC_CR_OSS_BIT) + #define AIC_CR_OSS_18BIT (0x2 << AIC_CR_OSS_BIT) + #define AIC_CR_OSS_20BIT (0x3 << AIC_CR_OSS_BIT) + #define AIC_CR_OSS_24BIT (0x4 << AIC_CR_OSS_BIT) +#define AIC_CR_ISS_BIT 16 /* Input Sample Size from memory (AIC V2 only) */ +#define AIC_CR_ISS_MASK (0x7 << AIC_CR_ISS_BIT) + #define AIC_CR_ISS_8BIT (0x0 << AIC_CR_ISS_BIT) + #define AIC_CR_ISS_16BIT (0x1 << AIC_CR_ISS_BIT) + #define AIC_CR_ISS_18BIT (0x2 << AIC_CR_ISS_BIT) + #define AIC_CR_ISS_20BIT (0x3 << AIC_CR_ISS_BIT) + #define AIC_CR_ISS_24BIT (0x4 << AIC_CR_ISS_BIT) +#define AIC_CR_RDMS (1 << 15) /* Receive DMA enable */ +#define AIC_CR_TDMS (1 << 14) /* Transmit DMA enable */ +#define AIC_CR_M2S (1 << 11) /* Mono to Stereo enable */ +#define AIC_CR_ENDSW (1 << 10) /* Endian switch enable */ +#define AIC_CR_AVSTSU (1 << 9) /* Signed <-> Unsigned toggle enable */ +#define AIC_CR_FLUSH (1 << 8) /* Flush FIFO */ +#define AIC_CR_EROR (1 << 6) /* Enable ROR interrupt */ +#define AIC_CR_ETUR (1 << 5) /* Enable TUR interrupt */ +#define AIC_CR_ERFS (1 << 4) /* Enable RFS interrupt */ +#define AIC_CR_ETFS (1 << 3) /* Enable TFS interrupt */ +#define AIC_CR_ENLBF (1 << 2) /* Enable Loopback Function */ +#define AIC_CR_ERPL (1 << 1) /* Enable Playback Function */ +#define AIC_CR_EREC (1 << 0) /* Enable Record Function */ + +/* AIC Controller AC-link Control Register 1 (AIC_ACCR1) */ + +#define AIC_ACCR1_RS_BIT 16 /* Receive Valid Slots */ +#define AIC_ACCR1_RS_MASK (0x3ff << AIC_ACCR1_RS_BIT) + #define AIC_ACCR1_RS_SLOT12 (1 << 25) /* Slot 12 valid bit */ + #define AIC_ACCR1_RS_SLOT11 (1 << 24) /* Slot 11 valid bit */ + #define AIC_ACCR1_RS_SLOT10 (1 << 23) /* Slot 10 valid bit */ + #define AIC_ACCR1_RS_SLOT9 (1 << 22) /* Slot 9 valid bit, LFE */ + #define AIC_ACCR1_RS_SLOT8 (1 << 21) /* Slot 8 valid bit, Surround Right */ + #define AIC_ACCR1_RS_SLOT7 (1 << 20) /* Slot 7 valid bit, Surround Left */ + #define AIC_ACCR1_RS_SLOT6 (1 << 19) /* Slot 6 valid bit, PCM Center */ + #define AIC_ACCR1_RS_SLOT5 (1 << 18) /* Slot 5 valid bit */ + #define AIC_ACCR1_RS_SLOT4 (1 << 17) /* Slot 4 valid bit, PCM Right */ + #define AIC_ACCR1_RS_SLOT3 (1 << 16) /* Slot 3 valid bit, PCM Left */ +#define AIC_ACCR1_XS_BIT 0 /* Transmit Valid Slots */ +#define AIC_ACCR1_XS_MASK (0x3ff << AIC_ACCR1_XS_BIT) + #define AIC_ACCR1_XS_SLOT12 (1 << 9) /* Slot 12 valid bit */ + #define AIC_ACCR1_XS_SLOT11 (1 << 8) /* Slot 11 valid bit */ + #define AIC_ACCR1_XS_SLOT10 (1 << 7) /* Slot 10 valid bit */ + #define AIC_ACCR1_XS_SLOT9 (1 << 6) /* Slot 9 valid bit, LFE */ + #define AIC_ACCR1_XS_SLOT8 (1 << 5) /* Slot 8 valid bit, Surround Right */ + #define AIC_ACCR1_XS_SLOT7 (1 << 4) /* Slot 7 valid bit, Surround Left */ + #define AIC_ACCR1_XS_SLOT6 (1 << 3) /* Slot 6 valid bit, PCM Center */ + #define AIC_ACCR1_XS_SLOT5 (1 << 2) /* Slot 5 valid bit */ + #define AIC_ACCR1_XS_SLOT4 (1 << 1) /* Slot 4 valid bit, PCM Right */ + #define AIC_ACCR1_XS_SLOT3 (1 << 0) /* Slot 3 valid bit, PCM Left */ + +/* AIC Controller AC-link Control Register 2 (AIC_ACCR2) */ + +#define AIC_ACCR2_ERSTO (1 << 18) /* Enable RSTO interrupt */ +#define AIC_ACCR2_ESADR (1 << 17) /* Enable SADR interrupt */ +#define AIC_ACCR2_ECADT (1 << 16) /* Enable CADT interrupt */ +#define AIC_ACCR2_OASS_BIT 8 /* Output Sample Size for AC-link */ +#define AIC_ACCR2_OASS_MASK (0x3 << AIC_ACCR2_OASS_BIT) + #define AIC_ACCR2_OASS_20BIT (0 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 20-bit */ + #define AIC_ACCR2_OASS_18BIT (1 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 18-bit */ + #define AIC_ACCR2_OASS_16BIT (2 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 16-bit */ + #define AIC_ACCR2_OASS_8BIT (3 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 8-bit */ +#define AIC_ACCR2_IASS_BIT 6 /* Output Sample Size for AC-link */ +#define AIC_ACCR2_IASS_MASK (0x3 << AIC_ACCR2_IASS_BIT) + #define AIC_ACCR2_IASS_20BIT (0 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 20-bit */ + #define AIC_ACCR2_IASS_18BIT (1 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 18-bit */ + #define AIC_ACCR2_IASS_16BIT (2 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 16-bit */ + #define AIC_ACCR2_IASS_8BIT (3 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 8-bit */ +#define AIC_ACCR2_SO (1 << 3) /* SDATA_OUT output value */ +#define AIC_ACCR2_SR (1 << 2) /* RESET# pin level */ +#define AIC_ACCR2_SS (1 << 1) /* SYNC pin level */ +#define AIC_ACCR2_SA (1 << 0) /* SYNC and SDATA_OUT alternation */ + +/* AIC Controller I2S/MSB-justified Control Register (AIC_I2SCR) */ + +#define AIC_I2SCR_STPBK (1 << 12) /* Stop BIT_CLK for I2S/MSB-justified */ +#define AIC_I2SCR_WL_BIT 1 /* Input/Output Sample Size for I2S/MSB-justified */ +#define AIC_I2SCR_WL_MASK (0x7 << AIC_I2SCR_WL_BIT) + #define AIC_I2SCR_WL_24BIT (0 << AIC_I2SCR_WL_BIT) /* Word Length is 24 bit */ + #define AIC_I2SCR_WL_20BIT (1 << AIC_I2SCR_WL_BIT) /* Word Length is 20 bit */ + #define AIC_I2SCR_WL_18BIT (2 << AIC_I2SCR_WL_BIT) /* Word Length is 18 bit */ + #define AIC_I2SCR_WL_16BIT (3 << AIC_I2SCR_WL_BIT) /* Word Length is 16 bit */ + #define AIC_I2SCR_WL_8BIT (4 << AIC_I2SCR_WL_BIT) /* Word Length is 8 bit */ +#define AIC_I2SCR_AMSL (1 << 0) /* 0:I2S, 1:MSB-justified */ + +/* AIC Controller FIFO Status Register (AIC_SR) */ + +#define AIC_SR_RFL_BIT 24 /* Receive FIFO Level */ +#define AIC_SR_RFL_MASK (0x3f << AIC_SR_RFL_BIT) +#define AIC_SR_TFL_BIT 8 /* Transmit FIFO level */ +#define AIC_SR_TFL_MASK (0x3f << AIC_SR_TFL_BIT) +#define AIC_SR_ROR (1 << 6) /* Receive FIFO Overrun */ +#define AIC_SR_TUR (1 << 5) /* Transmit FIFO Underrun */ +#define AIC_SR_RFS (1 << 4) /* Receive FIFO Service Request */ +#define AIC_SR_TFS (1 << 3) /* Transmit FIFO Service Request */ + +/* AIC Controller AC-link Status Register (AIC_ACSR) */ + +#define AIC_ACSR_SLTERR (1 << 21) /* Slot Error Flag */ +#define AIC_ACSR_CRDY (1 << 20) /* External CODEC Ready Flag */ +#define AIC_ACSR_CLPM (1 << 19) /* External CODEC low power mode flag */ +#define AIC_ACSR_RSTO (1 << 18) /* External CODEC regs read status timeout */ +#define AIC_ACSR_SADR (1 << 17) /* External CODEC regs status addr and data received */ +#define AIC_ACSR_CADT (1 << 16) /* Command Address and Data Transmitted */ + +/* AIC Controller I2S/MSB-justified Status Register (AIC_I2SSR) */ + +#define AIC_I2SSR_BSY (1 << 2) /* AIC Busy in I2S/MSB-justified format */ + +/* AIC Controller AC97 codec Command Address Register (AIC_ACCAR) */ + +#define AIC_ACCAR_CAR_BIT 0 +#define AIC_ACCAR_CAR_MASK (0xfffff << AIC_ACCAR_CAR_BIT) + +/* AIC Controller AC97 codec Command Data Register (AIC_ACCDR) */ + +#define AIC_ACCDR_CDR_BIT 0 +#define AIC_ACCDR_CDR_MASK (0xfffff << AIC_ACCDR_CDR_BIT) + +/* AIC Controller AC97 codec Status Address Register (AIC_ACSAR) */ + +#define AIC_ACSAR_SAR_BIT 0 +#define AIC_ACSAR_SAR_MASK (0xfffff << AIC_ACSAR_SAR_BIT) + +/* AIC Controller AC97 codec Status Data Register (AIC_ACSDR) */ + +#define AIC_ACSDR_SDR_BIT 0 +#define AIC_ACSDR_SDR_MASK (0xfffff << AIC_ACSDR_SDR_BIT) + +/* AIC Controller I2S/MSB-justified Clock Divider Register (AIC_I2SDIV) */ + +#define AIC_I2SDIV_DIV_BIT 0 +#define AIC_I2SDIV_DIV_MASK (0x7f << AIC_I2SDIV_DIV_BIT) + #define AIC_I2SDIV_BITCLK_3072KHZ (0x0C << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 3.072MHz */ + #define AIC_I2SDIV_BITCLK_2836KHZ (0x0D << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 2.836MHz */ + #define AIC_I2SDIV_BITCLK_1418KHZ (0x1A << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 1.418MHz */ + #define AIC_I2SDIV_BITCLK_1024KHZ (0x24 << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 1.024MHz */ + #define AIC_I2SDIV_BITCLK_7089KHZ (0x34 << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 708.92KHz */ + #define AIC_I2SDIV_BITCLK_512KHZ (0x48 << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 512.00KHz */ + + +/************************************************************************* + * ICDC (Internal CODEC) + *************************************************************************/ +#define ICDC_CR (ICDC_BASE + 0x0400) /* ICDC Control Register */ +#define ICDC_APWAIT (ICDC_BASE + 0x0404) /* Anti-Pop WAIT Stage Timing Control Register */ +#define ICDC_APPRE (ICDC_BASE + 0x0408) /* Anti-Pop HPEN-PRE Stage Timing Control Register */ +#define ICDC_APHPEN (ICDC_BASE + 0x040C) /* Anti-Pop HPEN Stage Timing Control Register */ +#define ICDC_APSR (ICDC_BASE + 0x0410) /* Anti-Pop Status Register */ +#define ICDC_CDCCR1 (ICDC_BASE + 0x0080) +#define ICDC_CDCCR2 (ICDC_BASE + 0x0084) + +#define REG_ICDC_CR REG32(ICDC_CR) +#define REG_ICDC_APWAIT REG32(ICDC_APWAIT) +#define REG_ICDC_APPRE REG32(ICDC_APPRE) +#define REG_ICDC_APHPEN REG32(ICDC_APHPEN) +#define REG_ICDC_APSR REG32(ICDC_APSR) +#define REG_ICDC_CDCCR1 REG32(ICDC_CDCCR1) +#define REG_ICDC_CDCCR2 REG32(ICDC_CDCCR2) + +/* ICDC Control Register */ +#define ICDC_CR_LINVOL_BIT 24 /* LINE Input Volume Gain: GAIN=LINVOL*1.5-34.5 */ +#define ICDC_CR_LINVOL_MASK (0x1f << ICDC_CR_LINVOL_BIT) +#define ICDC_CR_ASRATE_BIT 20 /* Audio Sample Rate */ +#define ICDC_CR_ASRATE_MASK (0x0f << ICDC_CR_ASRATE_BIT) + #define ICDC_CR_ASRATE_8000 (0x0 << ICDC_CR_ASRATE_BIT) + #define ICDC_CR_ASRATE_11025 (0x1 << ICDC_CR_ASRATE_BIT) + #define ICDC_CR_ASRATE_12000 (0x2 << ICDC_CR_ASRATE_BIT) + #define ICDC_CR_ASRATE_16000 (0x3 << ICDC_CR_ASRATE_BIT) + #define ICDC_CR_ASRATE_22050 (0x4 << ICDC_CR_ASRATE_BIT) + #define ICDC_CR_ASRATE_24000 (0x5 << ICDC_CR_ASRATE_BIT) + #define ICDC_CR_ASRATE_32000 (0x6 << ICDC_CR_ASRATE_BIT) + #define ICDC_CR_ASRATE_44100 (0x7 << ICDC_CR_ASRATE_BIT) + #define ICDC_CR_ASRATE_48000 (0x8 << ICDC_CR_ASRATE_BIT) +#define ICDC_CR_MICBG_BIT 18 /* MIC Boost Gain */ +#define ICDC_CR_MICBG_MASK (0x3 << ICDC_CR_MICBG_BIT) + #define ICDC_CR_MICBG_0DB (0x0 << ICDC_CR_MICBG_BIT) + #define ICDC_CR_MICBG_6DB (0x1 << ICDC_CR_MICBG_BIT) + #define ICDC_CR_MICBG_12DB (0x2 << ICDC_CR_MICBG_BIT) + #define ICDC_CR_MICBG_20DB (0x3 << ICDC_CR_MICBG_BIT) +#define ICDC_CR_HPVOL_BIT 16 /* Headphone Volume Gain */ +#define ICDC_CR_HPVOL_MASK (0x3 << ICDC_CR_HPVOL_BIT) + #define ICDC_CR_HPVOL_0DB (0x0 << ICDC_CR_HPVOL_BIT) + #define ICDC_CR_HPVOL_2DB (0x1 << ICDC_CR_HPVOL_BIT) + #define ICDC_CR_HPVOL_4DB (0x2 << ICDC_CR_HPVOL_BIT) + #define ICDC_CR_HPVOL_6DB (0x3 << ICDC_CR_HPVOL_BIT) +#define ICDC_CR_ELINEIN (1 << 13) /* Enable LINE Input */ +#define ICDC_CR_EMIC (1 << 12) /* Enable MIC Input */ +#define ICDC_CR_SW1ON (1 << 11) /* Switch 1 in CODEC is on */ +#define ICDC_CR_EADC (1 << 10) /* Enable ADC */ +#define ICDC_CR_SW2ON (1 << 9) /* Switch 2 in CODEC is on */ +#define ICDC_CR_EDAC (1 << 8) /* Enable DAC */ +#define ICDC_CR_HPMUTE (1 << 5) /* Headphone Mute */ +#define ICDC_CR_HPTON (1 << 4) /* Headphone Amplifier Trun On */ +#define ICDC_CR_HPTOFF (1 << 3) /* Headphone Amplifier Trun Off */ +#define ICDC_CR_TAAP (1 << 2) /* Turn Around of the Anti-Pop Procedure */ +#define ICDC_CR_EAP (1 << 1) /* Enable Anti-Pop Procedure */ +#define ICDC_CR_SUSPD (1 << 0) /* CODEC Suspend */ + +/* Anti-Pop WAIT Stage Timing Control Register */ +#define ICDC_APWAIT_WAITSN_BIT 0 +#define ICDC_APWAIT_WAITSN_MASK (0x7ff << ICDC_APWAIT_WAITSN_BIT) + +/* Anti-Pop HPEN-PRE Stage Timing Control Register */ +#define ICDC_APPRE_PRESN_BIT 0 +#define ICDC_APPRE_PRESN_MASK (0x1ff << ICDC_APPRE_PRESN_BIT) + +/* Anti-Pop HPEN Stage Timing Control Register */ +#define ICDC_APHPEN_HPENSN_BIT 0 +#define ICDC_APHPEN_HPENSN_MASK (0x3fff << ICDC_APHPEN_HPENSN_BIT) + +/* Anti-Pop Status Register */ +#define ICDC_SR_HPST_BIT 14 /* Headphone Amplifier State */ +#define ICDC_SR_HPST_MASK (0x7 << ICDC_SR_HPST_BIT) +#define ICDC_SR_HPST_HP_OFF (0x0 << ICDC_SR_HPST_BIT) /* HP amplifier is off */ +#define ICDC_SR_HPST_TON_WAIT (0x1 << ICDC_SR_HPST_BIT) /* wait state in turn-on */ + #define ICDC_SR_HPST_TON_PRE (0x2 << ICDC_SR_HPST_BIT) /* pre-enable state in turn-on */ +#define ICDC_SR_HPST_TON_HPEN (0x3 << ICDC_SR_HPST_BIT) /* HP enable state in turn-on */ + #define ICDC_SR_HPST_TOFF_HPEN (0x4 << ICDC_SR_HPST_BIT) /* HP enable state in turn-off */ + #define ICDC_SR_HPST_TOFF_PRE (0x5 << ICDC_SR_HPST_BIT) /* pre-enable state in turn-off */ + #define ICDC_SR_HPST_TOFF_WAIT (0x6 << ICDC_SR_HPST_BIT) /* wait state in turn-off */ + #define ICDC_SR_HPST_HP_ON (0x7 << ICDC_SR_HPST_BIT) /* HP amplifier is on */ +#define ICDC_SR_SNCNT_BIT 0 /* Sample Number Counter */ +#define ICDC_SR_SNCNT_MASK (0x3fff << ICDC_SR_SNCNT_BIT) + + +/************************************************************************* + * I2C + *************************************************************************/ +#define I2C_DR (I2C_BASE + 0x000) +#define I2C_CR (I2C_BASE + 0x004) +#define I2C_SR (I2C_BASE + 0x008) +#define I2C_GR (I2C_BASE + 0x00C) + +#define REG_I2C_DR REG8(I2C_DR) +#define REG_I2C_CR REG8(I2C_CR) +#define REG_I2C_SR REG8(I2C_SR) +#define REG_I2C_GR REG16(I2C_GR) + +/* I2C Control Register (I2C_CR) */ + +#define I2C_CR_IEN (1 << 4) +#define I2C_CR_STA (1 << 3) +#define I2C_CR_STO (1 << 2) +#define I2C_CR_AC (1 << 1) +#define I2C_CR_I2CE (1 << 0) + +/* I2C Status Register (I2C_SR) */ + +#define I2C_SR_STX (1 << 4) +#define I2C_SR_BUSY (1 << 3) +#define I2C_SR_TEND (1 << 2) +#define I2C_SR_DRF (1 << 1) +#define I2C_SR_ACKF (1 << 0) + + +/************************************************************************* + * SSI + *************************************************************************/ +#define SSI_DR (SSI_BASE + 0x000) +#define SSI_CR0 (SSI_BASE + 0x004) +#define SSI_CR1 (SSI_BASE + 0x008) +#define SSI_SR (SSI_BASE + 0x00C) +#define SSI_ITR (SSI_BASE + 0x010) +#define SSI_ICR (SSI_BASE + 0x014) +#define SSI_GR (SSI_BASE + 0x018) + +#define REG_SSI_DR REG32(SSI_DR) +#define REG_SSI_CR0 REG16(SSI_CR0) +#define REG_SSI_CR1 REG32(SSI_CR1) +#define REG_SSI_SR REG32(SSI_SR) +#define REG_SSI_ITR REG16(SSI_ITR) +#define REG_SSI_ICR REG8(SSI_ICR) +#define REG_SSI_GR REG16(SSI_GR) + +/* SSI Data Register (SSI_DR) */ + +#define SSI_DR_GPC_BIT 0 +#define SSI_DR_GPC_MASK (0x1ff << SSI_DR_GPC_BIT) + +/* SSI Control Register 0 (SSI_CR0) */ + +#define SSI_CR0_SSIE (1 << 15) +#define SSI_CR0_TIE (1 << 14) +#define SSI_CR0_RIE (1 << 13) +#define SSI_CR0_TEIE (1 << 12) +#define SSI_CR0_REIE (1 << 11) +#define SSI_CR0_LOOP (1 << 10) +#define SSI_CR0_RFINE (1 << 9) +#define SSI_CR0_RFINC (1 << 8) +#define SSI_CR0_FSEL (1 << 6) +#define SSI_CR0_TFLUSH (1 << 2) +#define SSI_CR0_RFLUSH (1 << 1) +#define SSI_CR0_DISREV (1 << 0) + +/* SSI Control Register 1 (SSI_CR1) */ + +#define SSI_CR1_FRMHL_BIT 30 +#define SSI_CR1_FRMHL_MASK (0x3 << SSI_CR1_FRMHL_BIT) + #define SSI_CR1_FRMHL_CELOW_CE2LOW (0 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is low valid and SSI_CE2_ is low valid */ + #define SSI_CR1_FRMHL_CEHIGH_CE2LOW (1 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is high valid and SSI_CE2_ is low valid */ + #define SSI_CR1_FRMHL_CELOW_CE2HIGH (2 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is low valid and SSI_CE2_ is high valid */ + #define SSI_CR1_FRMHL_CEHIGH_CE2HIGH (3 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is high valid and SSI_CE2_ is high valid */ +#define SSI_CR1_TFVCK_BIT 28 +#define SSI_CR1_TFVCK_MASK (0x3 << SSI_CR1_TFVCK_BIT) + #define SSI_CR1_TFVCK_0 (0 << SSI_CR1_TFVCK_BIT) + #define SSI_CR1_TFVCK_1 (1 << SSI_CR1_TFVCK_BIT) + #define SSI_CR1_TFVCK_2 (2 << SSI_CR1_TFVCK_BIT) + #define SSI_CR1_TFVCK_3 (3 << SSI_CR1_TFVCK_BIT) +#define SSI_CR1_TCKFI_BIT 26 +#define SSI_CR1_TCKFI_MASK (0x3 << SSI_CR1_TCKFI_BIT) + #define SSI_CR1_TCKFI_0 (0 << SSI_CR1_TCKFI_BIT) + #define SSI_CR1_TCKFI_1 (1 << SSI_CR1_TCKFI_BIT) + #define SSI_CR1_TCKFI_2 (2 << SSI_CR1_TCKFI_BIT) + #define SSI_CR1_TCKFI_3 (3 << SSI_CR1_TCKFI_BIT) +#define SSI_CR1_LFST (1 << 25) +#define SSI_CR1_ITFRM (1 << 24) +#define SSI_CR1_UNFIN (1 << 23) +#define SSI_CR1_MULTS (1 << 22) +#define SSI_CR1_FMAT_BIT 20 +#define SSI_CR1_FMAT_MASK (0x3 << SSI_CR1_FMAT_BIT) + #define SSI_CR1_FMAT_SPI (0 << SSI_CR1_FMAT_BIT) /* Motorola¡¯s SPI format */ + #define SSI_CR1_FMAT_SSP (1 << SSI_CR1_FMAT_BIT) /* TI's SSP format */ + #define SSI_CR1_FMAT_MW1 (2 << SSI_CR1_FMAT_BIT) /* National Microwire 1 format */ + #define SSI_CR1_FMAT_MW2 (3 << SSI_CR1_FMAT_BIT) /* National Microwire 2 format */ +#define SSI_CR1_TTRG_BIT 16 +#define SSI_CR1_TTRG_MASK (0xf << SSI_CR1_TTRG_BIT) + #define SSI_CR1_TTRG_1 (0 << SSI_CR1_TTRG_BIT) + #define SSI_CR1_TTRG_8 (1 << SSI_CR1_TTRG_BIT) + #define SSI_CR1_TTRG_16 (2 << SSI_CR1_TTRG_BIT) + #define SSI_CR1_TTRG_24 (3 << SSI_CR1_TTRG_BIT) + #define SSI_CR1_TTRG_32 (4 << SSI_CR1_TTRG_BIT) + #define SSI_CR1_TTRG_40 (5 << SSI_CR1_TTRG_BIT) + #define SSI_CR1_TTRG_48 (6 << SSI_CR1_TTRG_BIT) + #define SSI_CR1_TTRG_56 (7 << SSI_CR1_TTRG_BIT) + #define SSI_CR1_TTRG_64 (8 << SSI_CR1_TTRG_BIT) + #define SSI_CR1_TTRG_72 (9 << SSI_CR1_TTRG_BIT) + #define SSI_CR1_TTRG_80 (10<< SSI_CR1_TTRG_BIT) + #define SSI_CR1_TTRG_88 (11<< SSI_CR1_TTRG_BIT) + #define SSI_CR1_TTRG_96 (12<< SSI_CR1_TTRG_BIT) + #define SSI_CR1_TTRG_104 (13<< SSI_CR1_TTRG_BIT) + #define SSI_CR1_TTRG_112 (14<< SSI_CR1_TTRG_BIT) + #define SSI_CR1_TTRG_120 (15<< SSI_CR1_TTRG_BIT) +#define SSI_CR1_MCOM_BIT 12 +#define SSI_CR1_MCOM_MASK (0xf << SSI_CR1_MCOM_BIT) + #define SSI_CR1_MCOM_1BIT (0x0 << SSI_CR1_MCOM_BIT) /* 1-bit command selected */ + #define SSI_CR1_MCOM_2BIT (0x1 << SSI_CR1_MCOM_BIT) /* 2-bit command selected */ + #define SSI_CR1_MCOM_3BIT (0x2 << SSI_CR1_MCOM_BIT) /* 3-bit command selected */ + #define SSI_CR1_MCOM_4BIT (0x3 << SSI_CR1_MCOM_BIT) /* 4-bit command selected */ + #define SSI_CR1_MCOM_5BIT (0x4 << SSI_CR1_MCOM_BIT) /* 5-bit command selected */ + #define SSI_CR1_MCOM_6BIT (0x5 << SSI_CR1_MCOM_BIT) /* 6-bit command selected */ + #define SSI_CR1_MCOM_7BIT (0x6 << SSI_CR1_MCOM_BIT) /* 7-bit command selected */ + #define SSI_CR1_MCOM_8BIT (0x7 << SSI_CR1_MCOM_BIT) /* 8-bit command selected */ + #define SSI_CR1_MCOM_9BIT (0x8 << SSI_CR1_MCOM_BIT) /* 9-bit command selected */ + #define SSI_CR1_MCOM_10BIT (0x9 << SSI_CR1_MCOM_BIT) /* 10-bit command selected */ + #define SSI_CR1_MCOM_11BIT (0xA << SSI_CR1_MCOM_BIT) /* 11-bit command selected */ + #define SSI_CR1_MCOM_12BIT (0xB << SSI_CR1_MCOM_BIT) /* 12-bit command selected */ + #define SSI_CR1_MCOM_13BIT (0xC << SSI_CR1_MCOM_BIT) /* 13-bit command selected */ + #define SSI_CR1_MCOM_14BIT (0xD << SSI_CR1_MCOM_BIT) /* 14-bit command selected */ + #define SSI_CR1_MCOM_15BIT (0xE << SSI_CR1_MCOM_BIT) /* 15-bit command selected */ + #define SSI_CR1_MCOM_16BIT (0xF << SSI_CR1_MCOM_BIT) /* 16-bit command selected */ +#define SSI_CR1_RTRG_BIT 8 +#define SSI_CR1_RTRG_MASK (0xf << SSI_CR1_RTRG_BIT) + #define SSI_CR1_RTRG_1 (0 << SSI_CR1_RTRG_BIT) + #define SSI_CR1_RTRG_8 (1 << SSI_CR1_RTRG_BIT) + #define SSI_CR1_RTRG_16 (2 << SSI_CR1_RTRG_BIT) + #define SSI_CR1_RTRG_24 (3 << SSI_CR1_RTRG_BIT) + #define SSI_CR1_RTRG_32 (4 << SSI_CR1_RTRG_BIT) + #define SSI_CR1_RTRG_40 (5 << SSI_CR1_RTRG_BIT) + #define SSI_CR1_RTRG_48 (6 << SSI_CR1_RTRG_BIT) + #define SSI_CR1_RTRG_56 (7 << SSI_CR1_RTRG_BIT) + #define SSI_CR1_RTRG_64 (8 << SSI_CR1_RTRG_BIT) + #define SSI_CR1_RTRG_72 (9 << SSI_CR1_RTRG_BIT) + #define SSI_CR1_RTRG_80 (10<< SSI_CR1_RTRG_BIT) + #define SSI_CR1_RTRG_88 (11<< SSI_CR1_RTRG_BIT) + #define SSI_CR1_RTRG_96 (12<< SSI_CR1_RTRG_BIT) + #define SSI_CR1_RTRG_104 (13<< SSI_CR1_RTRG_BIT) + #define SSI_CR1_RTRG_112 (14<< SSI_CR1_RTRG_BIT) + #define SSI_CR1_RTRG_120 (15<< SSI_CR1_RTRG_BIT) +#define SSI_CR1_FLEN_BIT 4 +#define SSI_CR1_FLEN_MASK (0xf << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_2BIT (0x0 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_3BIT (0x1 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_4BIT (0x2 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_5BIT (0x3 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_6BIT (0x4 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_7BIT (0x5 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_8BIT (0x6 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_9BIT (0x7 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_10BIT (0x8 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_11BIT (0x9 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_12BIT (0xA << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_13BIT (0xB << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_14BIT (0xC << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_15BIT (0xD << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_16BIT (0xE << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_17BIT (0xF << SSI_CR1_FLEN_BIT) +#define SSI_CR1_PHA (1 << 1) +#define SSI_CR1_POL (1 << 0) + +/* SSI Status Register (SSI_SR) */ + +#define SSI_SR_TFIFONUM_BIT 16 +#define SSI_SR_TFIFONUM_MASK (0xff << SSI_SR_TFIFONUM_BIT) +#define SSI_SR_RFIFONUM_BIT 8 +#define SSI_SR_RFIFONUM_MASK (0xff << SSI_SR_RFIFONUM_BIT) +#define SSI_SR_END (1 << 7) +#define SSI_SR_BUSY (1 << 6) +#define SSI_SR_TFF (1 << 5) +#define SSI_SR_RFE (1 << 4) +#define SSI_SR_TFHE (1 << 3) +#define SSI_SR_RFHF (1 << 2) +#define SSI_SR_UNDR (1 << 1) +#define SSI_SR_OVER (1 << 0) + +/* SSI Interval Time Control Register (SSI_ITR) */ + +#define SSI_ITR_CNTCLK (1 << 15) +#define SSI_ITR_IVLTM_BIT 0 +#define SSI_ITR_IVLTM_MASK (0x7fff << SSI_ITR_IVLTM_BIT) + + +/************************************************************************* + * MSC + *************************************************************************/ +#define MSC_STRPCL (MSC_BASE + 0x000) +#define MSC_STAT (MSC_BASE + 0x004) +#define MSC_CLKRT (MSC_BASE + 0x008) +#define MSC_CMDAT (MSC_BASE + 0x00C) +#define MSC_RESTO (MSC_BASE + 0x010) +#define MSC_RDTO (MSC_BASE + 0x014) +#define MSC_BLKLEN (MSC_BASE + 0x018) +#define MSC_NOB (MSC_BASE + 0x01C) +#define MSC_SNOB (MSC_BASE + 0x020) +#define MSC_IMASK (MSC_BASE + 0x024) +#define MSC_IREG (MSC_BASE + 0x028) +#define MSC_CMD (MSC_BASE + 0x02C) +#define MSC_ARG (MSC_BASE + 0x030) +#define MSC_RES (MSC_BASE + 0x034) +#define MSC_RXFIFO (MSC_BASE + 0x038) +#define MSC_TXFIFO (MSC_BASE + 0x03C) + +#define REG_MSC_STRPCL REG16(MSC_STRPCL) +#define REG_MSC_STAT REG32(MSC_STAT) +#define REG_MSC_CLKRT REG16(MSC_CLKRT) +#define REG_MSC_CMDAT REG32(MSC_CMDAT) +#define REG_MSC_RESTO REG16(MSC_RESTO) +#define REG_MSC_RDTO REG16(MSC_RDTO) +#define REG_MSC_BLKLEN REG16(MSC_BLKLEN) +#define REG_MSC_NOB REG16(MSC_NOB) +#define REG_MSC_SNOB REG16(MSC_SNOB) +#define REG_MSC_IMASK REG16(MSC_IMASK) +#define REG_MSC_IREG REG16(MSC_IREG) +#define REG_MSC_CMD REG8(MSC_CMD) +#define REG_MSC_ARG REG32(MSC_ARG) +#define REG_MSC_RES REG16(MSC_RES) +#define REG_MSC_RXFIFO REG32(MSC_RXFIFO) +#define REG_MSC_TXFIFO REG32(MSC_TXFIFO) + +/* MSC Clock and Control Register (MSC_STRPCL) */ + +#define MSC_STRPCL_EXIT_MULTIPLE (1 << 7) +#define MSC_STRPCL_EXIT_TRANSFER (1 << 6) +#define MSC_STRPCL_START_READWAIT (1 << 5) +#define MSC_STRPCL_STOP_READWAIT (1 << 4) +#define MSC_STRPCL_RESET (1 << 3) +#define MSC_STRPCL_START_OP (1 << 2) +#define MSC_STRPCL_CLOCK_CONTROL_BIT 0 +#define MSC_STRPCL_CLOCK_CONTROL_MASK (0x3 << MSC_STRPCL_CLOCK_CONTROL_BIT) + #define MSC_STRPCL_CLOCK_CONTROL_STOP (0x1 << MSC_STRPCL_CLOCK_CONTROL_BIT) /* Stop MMC/SD clock */ + #define MSC_STRPCL_CLOCK_CONTROL_START (0x2 << MSC_STRPCL_CLOCK_CONTROL_BIT) /* Start MMC/SD clock */ + +/* MSC Status Register (MSC_STAT) */ + +#define MSC_STAT_IS_RESETTING (1 << 15) +#define MSC_STAT_SDIO_INT_ACTIVE (1 << 14) +#define MSC_STAT_PRG_DONE (1 << 13) +#define MSC_STAT_DATA_TRAN_DONE (1 << 12) +#define MSC_STAT_END_CMD_RES (1 << 11) +#define MSC_STAT_DATA_FIFO_AFULL (1 << 10) +#define MSC_STAT_IS_READWAIT (1 << 9) +#define MSC_STAT_CLK_EN (1 << 8) +#define MSC_STAT_DATA_FIFO_FULL (1 << 7) +#define MSC_STAT_DATA_FIFO_EMPTY (1 << 6) +#define MSC_STAT_CRC_RES_ERR (1 << 5) +#define MSC_STAT_CRC_READ_ERROR (1 << 4) +#define MSC_STAT_CRC_WRITE_ERROR_BIT 2 +#define MSC_STAT_CRC_WRITE_ERROR_MASK (0x3 << MSC_STAT_CRC_WRITE_ERROR_BIT) + #define MSC_STAT_CRC_WRITE_ERROR_NO (0 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* No error on transmission of data */ + #define MSC_STAT_CRC_WRITE_ERROR (1 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* Card observed erroneous transmission of data */ + #define MSC_STAT_CRC_WRITE_ERROR_NOSTS (2 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* No CRC status is sent back */ +#define MSC_STAT_TIME_OUT_RES (1 << 1) +#define MSC_STAT_TIME_OUT_READ (1 << 0) + +/* MSC Bus Clock Control Register (MSC_CLKRT) */ + +#define MSC_CLKRT_CLK_RATE_BIT 0 +#define MSC_CLKRT_CLK_RATE_MASK (0x7 << MSC_CLKRT_CLK_RATE_BIT) + #define MSC_CLKRT_CLK_RATE_DIV_1 (0x0 << MSC_CLKRT_CLK_RATE_BIT) /* CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_2 (0x1 << MSC_CLKRT_CLK_RATE_BIT) /* 1/2 of CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_4 (0x2 << MSC_CLKRT_CLK_RATE_BIT) /* 1/4 of CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_8 (0x3 << MSC_CLKRT_CLK_RATE_BIT) /* 1/8 of CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_16 (0x4 << MSC_CLKRT_CLK_RATE_BIT) /* 1/16 of CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_32 (0x5 << MSC_CLKRT_CLK_RATE_BIT) /* 1/32 of CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_64 (0x6 << MSC_CLKRT_CLK_RATE_BIT) /* 1/64 of CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_128 (0x7 << MSC_CLKRT_CLK_RATE_BIT) /* 1/128 of CLK_SRC */ + +/* MSC Command Sequence Control Register (MSC_CMDAT) */ + +#define MSC_CMDAT_IO_ABORT (1 << 11) +#define MSC_CMDAT_BUS_WIDTH_BIT 9 +#define MSC_CMDAT_BUS_WIDTH_MASK (0x3 << MSC_CMDAT_BUS_WIDTH_BIT) + #define MSC_CMDAT_BUS_WIDTH_1BIT (0x0 << MSC_CMDAT_BUS_WIDTH_BIT) /* 1-bit data bus */ + #define MSC_CMDAT_BUS_WIDTH_4BIT (0x2 << MSC_CMDAT_BUS_WIDTH_BIT) /* 4-bit data bus */ + #define CMDAT_BUS_WIDTH1 (0x0 << MSC_CMDAT_BUS_WIDTH_BIT) + #define CMDAT_BUS_WIDTH4 (0x2 << MSC_CMDAT_BUS_WIDTH_BIT) +#define MSC_CMDAT_DMA_EN (1 << 8) +#define MSC_CMDAT_INIT (1 << 7) +#define MSC_CMDAT_BUSY (1 << 6) +#define MSC_CMDAT_STREAM_BLOCK (1 << 5) +#define MSC_CMDAT_WRITE (1 << 4) +#define MSC_CMDAT_READ (0 << 4) +#define MSC_CMDAT_DATA_EN (1 << 3) +#define MSC_CMDAT_RESPONSE_BIT 0 +#define MSC_CMDAT_RESPONSE_MASK (0x7 << MSC_CMDAT_RESPONSE_BIT) + #define MSC_CMDAT_RESPONSE_NONE (0x0 << MSC_CMDAT_RESPONSE_BIT) /* No response */ + #define MSC_CMDAT_RESPONSE_R1 (0x1 << MSC_CMDAT_RESPONSE_BIT) /* Format R1 and R1b */ + #define MSC_CMDAT_RESPONSE_R2 (0x2 << MSC_CMDAT_RESPONSE_BIT) /* Format R2 */ + #define MSC_CMDAT_RESPONSE_R3 (0x3 << MSC_CMDAT_RESPONSE_BIT) /* Format R3 */ + #define MSC_CMDAT_RESPONSE_R4 (0x4 << MSC_CMDAT_RESPONSE_BIT) /* Format R4 */ + #define MSC_CMDAT_RESPONSE_R5 (0x5 << MSC_CMDAT_RESPONSE_BIT) /* Format R5 */ + #define MSC_CMDAT_RESPONSE_R6 (0x6 << MSC_CMDAT_RESPONSE_BIT) /* Format R6 */ + +#define CMDAT_DMA_EN (1 << 8) +#define CMDAT_INIT (1 << 7) +#define CMDAT_BUSY (1 << 6) +#define CMDAT_STREAM (1 << 5) +#define CMDAT_WRITE (1 << 4) +#define CMDAT_DATA_EN (1 << 3) + +/* MSC Interrupts Mask Register (MSC_IMASK) */ + +#define MSC_IMASK_SDIO (1 << 7) +#define MSC_IMASK_TXFIFO_WR_REQ (1 << 6) +#define MSC_IMASK_RXFIFO_RD_REQ (1 << 5) +#define MSC_IMASK_END_CMD_RES (1 << 2) +#define MSC_IMASK_PRG_DONE (1 << 1) +#define MSC_IMASK_DATA_TRAN_DONE (1 << 0) + + +/* MSC Interrupts Status Register (MSC_IREG) */ + +#define MSC_IREG_SDIO (1 << 7) +#define MSC_IREG_TXFIFO_WR_REQ (1 << 6) +#define MSC_IREG_RXFIFO_RD_REQ (1 << 5) +#define MSC_IREG_END_CMD_RES (1 << 2) +#define MSC_IREG_PRG_DONE (1 << 1) +#define MSC_IREG_DATA_TRAN_DONE (1 << 0) + + +/************************************************************************* + * EMC (External Memory Controller) + *************************************************************************/ +#define EMC_SMCR0 (EMC_BASE + 0x10) /* Static Memory Control Register 0 */ +#define EMC_SMCR1 (EMC_BASE + 0x14) /* Static Memory Control Register 1 */ +#define EMC_SMCR2 (EMC_BASE + 0x18) /* Static Memory Control Register 2 */ +#define EMC_SMCR3 (EMC_BASE + 0x1c) /* Static Memory Control Register 3 */ +#define EMC_SMCR4 (EMC_BASE + 0x20) /* Static Memory Control Register 4 */ +#define EMC_SACR0 (EMC_BASE + 0x30) /* Static Memory Bank 0 Addr Config Reg */ +#define EMC_SACR1 (EMC_BASE + 0x34) /* Static Memory Bank 1 Addr Config Reg */ +#define EMC_SACR2 (EMC_BASE + 0x38) /* Static Memory Bank 2 Addr Config Reg */ +#define EMC_SACR3 (EMC_BASE + 0x3c) /* Static Memory Bank 3 Addr Config Reg */ +#define EMC_SACR4 (EMC_BASE + 0x40) /* Static Memory Bank 4 Addr Config Reg */ + +#define EMC_NFCSR (EMC_BASE + 0x050) /* NAND Flash Control/Status Register */ +#define EMC_NFECR (EMC_BASE + 0x100) /* NAND Flash ECC Control Register */ +#define EMC_NFECC (EMC_BASE + 0x104) /* NAND Flash ECC Data Register */ +#define EMC_NFPAR0 (EMC_BASE + 0x108) /* NAND Flash RS Parity 0 Register */ +#define EMC_NFPAR1 (EMC_BASE + 0x10c) /* NAND Flash RS Parity 1 Register */ +#define EMC_NFPAR2 (EMC_BASE + 0x110) /* NAND Flash RS Parity 2 Register */ +#define EMC_NFINTS (EMC_BASE + 0x114) /* NAND Flash Interrupt Status Register */ +#define EMC_NFINTE (EMC_BASE + 0x118) /* NAND Flash Interrupt Enable Register */ +#define EMC_NFERR0 (EMC_BASE + 0x11c) /* NAND Flash RS Error Report 0 Register */ +#define EMC_NFERR1 (EMC_BASE + 0x120) /* NAND Flash RS Error Report 1 Register */ +#define EMC_NFERR2 (EMC_BASE + 0x124) /* NAND Flash RS Error Report 2 Register */ +#define EMC_NFERR3 (EMC_BASE + 0x128) /* NAND Flash RS Error Report 3 Register */ + +#define EMC_DMCR (EMC_BASE + 0x80) /* DRAM Control Register */ +#define EMC_RTCSR (EMC_BASE + 0x84) /* Refresh Time Control/Status Register */ +#define EMC_RTCNT (EMC_BASE + 0x88) /* Refresh Timer Counter */ +#define EMC_RTCOR (EMC_BASE + 0x8c) /* Refresh Time Constant Register */ +#define EMC_DMAR0 (EMC_BASE + 0x90) /* SDRAM Bank 0 Addr Config Register */ +#define EMC_SDMR0 (EMC_BASE + 0xa000) /* Mode Register of SDRAM bank 0 */ + + +#define REG_EMC_SMCR0 REG32(EMC_SMCR0) +#define REG_EMC_SMCR1 REG32(EMC_SMCR1) +#define REG_EMC_SMCR2 REG32(EMC_SMCR2) +#define REG_EMC_SMCR3 REG32(EMC_SMCR3) +#define REG_EMC_SMCR4 REG32(EMC_SMCR4) +#define REG_EMC_SACR0 REG32(EMC_SACR0) +#define REG_EMC_SACR1 REG32(EMC_SACR1) +#define REG_EMC_SACR2 REG32(EMC_SACR2) +#define REG_EMC_SACR3 REG32(EMC_SACR3) +#define REG_EMC_SACR4 REG32(EMC_SACR4) + +#define REG_EMC_NFCSR REG32(EMC_NFCSR) +#define REG_EMC_NFECR REG32(EMC_NFECR) +#define REG_EMC_NFECC REG32(EMC_NFECC) +#define REG_EMC_NFPAR0 REG32(EMC_NFPAR0) +#define REG_EMC_NFPAR1 REG32(EMC_NFPAR1) +#define REG_EMC_NFPAR2 REG32(EMC_NFPAR2) +#define REG_EMC_NFINTS REG32(EMC_NFINTS) +#define REG_EMC_NFINTE REG32(EMC_NFINTE) +#define REG_EMC_NFERR0 REG32(EMC_NFERR0) +#define REG_EMC_NFERR1 REG32(EMC_NFERR1) +#define REG_EMC_NFERR2 REG32(EMC_NFERR2) +#define REG_EMC_NFERR3 REG32(EMC_NFERR3) + +#define REG_EMC_DMCR REG32(EMC_DMCR) +#define REG_EMC_RTCSR REG16(EMC_RTCSR) +#define REG_EMC_RTCNT REG16(EMC_RTCNT) +#define REG_EMC_RTCOR REG16(EMC_RTCOR) +#define REG_EMC_DMAR0 REG32(EMC_DMAR0) + +/* Static Memory Control Register */ +#define EMC_SMCR_STRV_BIT 24 +#define EMC_SMCR_STRV_MASK (0x0f << EMC_SMCR_STRV_BIT) +#define EMC_SMCR_TAW_BIT 20 +#define EMC_SMCR_TAW_MASK (0x0f << EMC_SMCR_TAW_BIT) +#define EMC_SMCR_TBP_BIT 16 +#define EMC_SMCR_TBP_MASK (0x0f << EMC_SMCR_TBP_BIT) +#define EMC_SMCR_TAH_BIT 12 +#define EMC_SMCR_TAH_MASK (0x07 << EMC_SMCR_TAH_BIT) +#define EMC_SMCR_TAS_BIT 8 +#define EMC_SMCR_TAS_MASK (0x07 << EMC_SMCR_TAS_BIT) +#define EMC_SMCR_BW_BIT 6 +#define EMC_SMCR_BW_MASK (0x03 << EMC_SMCR_BW_BIT) + #define EMC_SMCR_BW_8BIT (0 << EMC_SMCR_BW_BIT) + #define EMC_SMCR_BW_16BIT (1 << EMC_SMCR_BW_BIT) + #define EMC_SMCR_BW_32BIT (2 << EMC_SMCR_BW_BIT) +#define EMC_SMCR_BCM (1 << 3) +#define EMC_SMCR_BL_BIT 1 +#define EMC_SMCR_BL_MASK (0x03 << EMC_SMCR_BL_BIT) + #define EMC_SMCR_BL_4 (0 << EMC_SMCR_BL_BIT) + #define EMC_SMCR_BL_8 (1 << EMC_SMCR_BL_BIT) + #define EMC_SMCR_BL_16 (2 << EMC_SMCR_BL_BIT) + #define EMC_SMCR_BL_32 (3 << EMC_SMCR_BL_BIT) +#define EMC_SMCR_SMT (1 << 0) + +/* Static Memory Bank Addr Config Reg */ +#define EMC_SACR_BASE_BIT 8 +#define EMC_SACR_BASE_MASK (0xff << EMC_SACR_BASE_BIT) +#define EMC_SACR_MASK_BIT 0 +#define EMC_SACR_MASK_MASK (0xff << EMC_SACR_MASK_BIT) + +/* NAND Flash Control/Status Register */ +#define EMC_NFCSR_NFCE4 (1 << 7) /* NAND Flash Enable */ +#define EMC_NFCSR_NFE4 (1 << 6) /* NAND Flash FCE# Assertion Enable */ +#define EMC_NFCSR_NFCE3 (1 << 5) +#define EMC_NFCSR_NFE3 (1 << 4) +#define EMC_NFCSR_NFCE2 (1 << 3) +#define EMC_NFCSR_NFE2 (1 << 2) +#define EMC_NFCSR_NFCE1 (1 << 1) +#define EMC_NFCSR_NFE1 (1 << 0) + +/* NAND Flash ECC Control Register */ +#define EMC_NFECR_PRDY (1 << 4) /* Parity Ready */ +#define EMC_NFECR_RS_DECODING (0 << 3) /* RS is in decoding phase */ +#define EMC_NFECR_RS_ENCODING (1 << 3) /* RS is in encoding phase */ +#define EMC_NFECR_HAMMING (0 << 2) /* Select HAMMING Correction Algorithm */ +#define EMC_NFECR_RS (1 << 2) /* Select RS Correction Algorithm */ +#define EMC_NFECR_ERST (1 << 1) /* ECC Reset */ +#define EMC_NFECR_ECCE (1 << 0) /* ECC Enable */ + +/* NAND Flash ECC Data Register */ +#define EMC_NFECC_ECC2_BIT 16 +#define EMC_NFECC_ECC2_MASK (0xff << EMC_NFECC_ECC2_BIT) +#define EMC_NFECC_ECC1_BIT 8 +#define EMC_NFECC_ECC1_MASK (0xff << EMC_NFECC_ECC1_BIT) +#define EMC_NFECC_ECC0_BIT 0 +#define EMC_NFECC_ECC0_MASK (0xff << EMC_NFECC_ECC0_BIT) + +/* NAND Flash Interrupt Status Register */ +#define EMC_NFINTS_ERRCNT_BIT 29 /* Error Count */ +#define EMC_NFINTS_ERRCNT_MASK (0x7 << EMC_NFINTS_ERRCNT_BIT) +#define EMC_NFINTS_PADF (1 << 4) /* Padding Finished */ +#define EMC_NFINTS_DECF (1 << 3) /* Decoding Finished */ +#define EMC_NFINTS_ENCF (1 << 2) /* Encoding Finished */ +#define EMC_NFINTS_UNCOR (1 << 1) /* Uncorrectable Error Occurred */ +#define EMC_NFINTS_ERR (1 << 0) /* Error Occurred */ + +/* NAND Flash Interrupt Enable Register */ +#define EMC_NFINTE_PADFE (1 << 4) /* Padding Finished Interrupt Enable */ +#define EMC_NFINTE_DECFE (1 << 3) /* Decoding Finished Interrupt Enable */ +#define EMC_NFINTE_ENCFE (1 << 2) /* Encoding Finished Interrupt Enable */ +#define EMC_NFINTE_UNCORE (1 << 1) /* Uncorrectable Error Occurred Intr Enable */ +#define EMC_NFINTE_ERRE (1 << 0) /* Error Occurred Interrupt */ + +/* NAND Flash RS Error Report Register */ +#define EMC_NFERR_INDEX_BIT 16 /* Error Symbol Index */ +#define EMC_NFERR_INDEX_MASK (0x1ff << EMC_NFERR_INDEX_BIT) +#define EMC_NFERR_MASK_BIT 0 /* Error Symbol Value */ +#define EMC_NFERR_MASK_MASK (0x1ff << EMC_NFERR_MASK_BIT) + + +/* DRAM Control Register */ +#define EMC_DMCR_BW_BIT 31 +#define EMC_DMCR_BW (1 << EMC_DMCR_BW_BIT) +#define EMC_DMCR_CA_BIT 26 +#define EMC_DMCR_CA_MASK (0x07 << EMC_DMCR_CA_BIT) + #define EMC_DMCR_CA_8 (0 << EMC_DMCR_CA_BIT) + #define EMC_DMCR_CA_9 (1 << EMC_DMCR_CA_BIT) + #define EMC_DMCR_CA_10 (2 << EMC_DMCR_CA_BIT) + #define EMC_DMCR_CA_11 (3 << EMC_DMCR_CA_BIT) + #define EMC_DMCR_CA_12 (4 << EMC_DMCR_CA_BIT) +#define EMC_DMCR_RMODE (1 << 25) +#define EMC_DMCR_RFSH (1 << 24) +#define EMC_DMCR_MRSET (1 << 23) +#define EMC_DMCR_RA_BIT 20 +#define EMC_DMCR_RA_MASK (0x03 << EMC_DMCR_RA_BIT) + #define EMC_DMCR_RA_11 (0 << EMC_DMCR_RA_BIT) + #define EMC_DMCR_RA_12 (1 << EMC_DMCR_RA_BIT) + #define EMC_DMCR_RA_13 (2 << EMC_DMCR_RA_BIT) +#define EMC_DMCR_BA_BIT 19 +#define EMC_DMCR_BA (1 << EMC_DMCR_BA_BIT) +#define EMC_DMCR_PDM (1 << 18) +#define EMC_DMCR_EPIN (1 << 17) +#define EMC_DMCR_TRAS_BIT 13 +#define EMC_DMCR_TRAS_MASK (0x07 << EMC_DMCR_TRAS_BIT) +#define EMC_DMCR_RCD_BIT 11 +#define EMC_DMCR_RCD_MASK (0x03 << EMC_DMCR_RCD_BIT) +#define EMC_DMCR_TPC_BIT 8 +#define EMC_DMCR_TPC_MASK (0x07 << EMC_DMCR_TPC_BIT) +#define EMC_DMCR_TRWL_BIT 5 +#define EMC_DMCR_TRWL_MASK (0x03 << EMC_DMCR_TRWL_BIT) +#define EMC_DMCR_TRC_BIT 2 +#define EMC_DMCR_TRC_MASK (0x07 << EMC_DMCR_TRC_BIT) +#define EMC_DMCR_TCL_BIT 0 +#define EMC_DMCR_TCL_MASK (0x03 << EMC_DMCR_TCL_BIT) + +/* Refresh Time Control/Status Register */ +#define EMC_RTCSR_CMF (1 << 7) +#define EMC_RTCSR_CKS_BIT 0 +#define EMC_RTCSR_CKS_MASK (0x07 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_DISABLE (0 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_4 (1 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_16 (2 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_64 (3 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_256 (4 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_1024 (5 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_2048 (6 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_4096 (7 << EMC_RTCSR_CKS_BIT) + +/* SDRAM Bank Address Configuration Register */ +#define EMC_DMAR_BASE_BIT 8 +#define EMC_DMAR_BASE_MASK (0xff << EMC_DMAR_BASE_BIT) +#define EMC_DMAR_MASK_BIT 0 +#define EMC_DMAR_MASK_MASK (0xff << EMC_DMAR_MASK_BIT) + +/* Mode Register of SDRAM bank 0 */ +#define EMC_SDMR_BM (1 << 9) /* Write Burst Mode */ +#define EMC_SDMR_OM_BIT 7 /* Operating Mode */ +#define EMC_SDMR_OM_MASK (3 << EMC_SDMR_OM_BIT) + #define EMC_SDMR_OM_NORMAL (0 << EMC_SDMR_OM_BIT) +#define EMC_SDMR_CAS_BIT 4 /* CAS Latency */ +#define EMC_SDMR_CAS_MASK (7 << EMC_SDMR_CAS_BIT) + #define EMC_SDMR_CAS_1 (1 << EMC_SDMR_CAS_BIT) + #define EMC_SDMR_CAS_2 (2 << EMC_SDMR_CAS_BIT) + #define EMC_SDMR_CAS_3 (3 << EMC_SDMR_CAS_BIT) +#define EMC_SDMR_BT_BIT 3 /* Burst Type */ +#define EMC_SDMR_BT_MASK (1 << EMC_SDMR_BT_BIT) + #define EMC_SDMR_BT_SEQ (0 << EMC_SDMR_BT_BIT) /* Sequential */ + #define EMC_SDMR_BT_INT (1 << EMC_SDMR_BT_BIT) /* Interleave */ +#define EMC_SDMR_BL_BIT 0 /* Burst Length */ +#define EMC_SDMR_BL_MASK (7 << EMC_SDMR_BL_BIT) + #define EMC_SDMR_BL_1 (0 << EMC_SDMR_BL_BIT) + #define EMC_SDMR_BL_2 (1 << EMC_SDMR_BL_BIT) + #define EMC_SDMR_BL_4 (2 << EMC_SDMR_BL_BIT) + #define EMC_SDMR_BL_8 (3 << EMC_SDMR_BL_BIT) + +#define EMC_SDMR_CAS2_16BIT \ + (EMC_SDMR_CAS_2 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_2) +#define EMC_SDMR_CAS2_32BIT \ + (EMC_SDMR_CAS_2 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_4) +#define EMC_SDMR_CAS3_16BIT \ + (EMC_SDMR_CAS_3 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_2) +#define EMC_SDMR_CAS3_32BIT \ + (EMC_SDMR_CAS_3 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_4) + + +/************************************************************************* + * CIM + *************************************************************************/ +#define CIM_CFG (CIM_BASE + 0x0000) +#define CIM_CTRL (CIM_BASE + 0x0004) +#define CIM_STATE (CIM_BASE + 0x0008) +#define CIM_IID (CIM_BASE + 0x000C) +#define CIM_RXFIFO (CIM_BASE + 0x0010) +#define CIM_DA (CIM_BASE + 0x0020) +#define CIM_FA (CIM_BASE + 0x0024) +#define CIM_FID (CIM_BASE + 0x0028) +#define CIM_CMD (CIM_BASE + 0x002C) + +#define REG_CIM_CFG REG32(CIM_CFG) +#define REG_CIM_CTRL REG32(CIM_CTRL) +#define REG_CIM_STATE REG32(CIM_STATE) +#define REG_CIM_IID REG32(CIM_IID) +#define REG_CIM_RXFIFO REG32(CIM_RXFIFO) +#define REG_CIM_DA REG32(CIM_DA) +#define REG_CIM_FA REG32(CIM_FA) +#define REG_CIM_FID REG32(CIM_FID) +#define REG_CIM_CMD REG32(CIM_CMD) + +/* CIM Configuration Register (CIM_CFG) */ + +#define CIM_CFG_INV_DAT (1 << 15) +#define CIM_CFG_VSP (1 << 14) +#define CIM_CFG_HSP (1 << 13) +#define CIM_CFG_PCP (1 << 12) +#define CIM_CFG_DUMMY_ZERO (1 << 9) +#define CIM_CFG_EXT_VSYNC (1 << 8) +#define CIM_CFG_PACK_BIT 4 +#define CIM_CFG_PACK_MASK (0x7 << CIM_CFG_PACK_BIT) + #define CIM_CFG_PACK_0 (0 << CIM_CFG_PACK_BIT) + #define CIM_CFG_PACK_1 (1 << CIM_CFG_PACK_BIT) + #define CIM_CFG_PACK_2 (2 << CIM_CFG_PACK_BIT) + #define CIM_CFG_PACK_3 (3 << CIM_CFG_PACK_BIT) + #define CIM_CFG_PACK_4 (4 << CIM_CFG_PACK_BIT) + #define CIM_CFG_PACK_5 (5 << CIM_CFG_PACK_BIT) + #define CIM_CFG_PACK_6 (6 << CIM_CFG_PACK_BIT) + #define CIM_CFG_PACK_7 (7 << CIM_CFG_PACK_BIT) +#define CIM_CFG_DSM_BIT 0 +#define CIM_CFG_DSM_MASK (0x3 << CIM_CFG_DSM_BIT) + #define CIM_CFG_DSM_CPM (0 << CIM_CFG_DSM_BIT) /* CCIR656 Progressive Mode */ + #define CIM_CFG_DSM_CIM (1 << CIM_CFG_DSM_BIT) /* CCIR656 Interlace Mode */ + #define CIM_CFG_DSM_GCM (2 << CIM_CFG_DSM_BIT) /* Gated Clock Mode */ + #define CIM_CFG_DSM_NGCM (3 << CIM_CFG_DSM_BIT) /* Non-Gated Clock Mode */ + +/* CIM Control Register (CIM_CTRL) */ + +#define CIM_CTRL_MCLKDIV_BIT 24 +#define CIM_CTRL_MCLKDIV_MASK (0xff << CIM_CTRL_MCLKDIV_BIT) +#define CIM_CTRL_FRC_BIT 16 +#define CIM_CTRL_FRC_MASK (0xf << CIM_CTRL_FRC_BIT) + #define CIM_CTRL_FRC_1 (0x0 << CIM_CTRL_FRC_BIT) /* Sample every frame */ + #define CIM_CTRL_FRC_2 (0x1 << CIM_CTRL_FRC_BIT) /* Sample 1/2 frame */ + #define CIM_CTRL_FRC_3 (0x2 << CIM_CTRL_FRC_BIT) /* Sample 1/3 frame */ + #define CIM_CTRL_FRC_4 (0x3 << CIM_CTRL_FRC_BIT) /* Sample 1/4 frame */ + #define CIM_CTRL_FRC_5 (0x4 << CIM_CTRL_FRC_BIT) /* Sample 1/5 frame */ + #define CIM_CTRL_FRC_6 (0x5 << CIM_CTRL_FRC_BIT) /* Sample 1/6 frame */ + #define CIM_CTRL_FRC_7 (0x6 << CIM_CTRL_FRC_BIT) /* Sample 1/7 frame */ + #define CIM_CTRL_FRC_8 (0x7 << CIM_CTRL_FRC_BIT) /* Sample 1/8 frame */ + #define CIM_CTRL_FRC_9 (0x8 << CIM_CTRL_FRC_BIT) /* Sample 1/9 frame */ + #define CIM_CTRL_FRC_10 (0x9 << CIM_CTRL_FRC_BIT) /* Sample 1/10 frame */ + #define CIM_CTRL_FRC_11 (0xA << CIM_CTRL_FRC_BIT) /* Sample 1/11 frame */ + #define CIM_CTRL_FRC_12 (0xB << CIM_CTRL_FRC_BIT) /* Sample 1/12 frame */ + #define CIM_CTRL_FRC_13 (0xC << CIM_CTRL_FRC_BIT) /* Sample 1/13 frame */ + #define CIM_CTRL_FRC_14 (0xD << CIM_CTRL_FRC_BIT) /* Sample 1/14 frame */ + #define CIM_CTRL_FRC_15 (0xE << CIM_CTRL_FRC_BIT) /* Sample 1/15 frame */ + #define CIM_CTRL_FRC_16 (0xF << CIM_CTRL_FRC_BIT) /* Sample 1/16 frame */ +#define CIM_CTRL_VDDM (1 << 13) +#define CIM_CTRL_DMA_SOFM (1 << 12) +#define CIM_CTRL_DMA_EOFM (1 << 11) +#define CIM_CTRL_DMA_STOPM (1 << 10) +#define CIM_CTRL_RXF_TRIGM (1 << 9) +#define CIM_CTRL_RXF_OFM (1 << 8) +#define CIM_CTRL_RXF_TRIG_BIT 4 +#define CIM_CTRL_RXF_TRIG_MASK (0x7 << CIM_CTRL_RXF_TRIG_BIT) + #define CIM_CTRL_RXF_TRIG_4 (0 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 4 */ + #define CIM_CTRL_RXF_TRIG_8 (1 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 8 */ + #define CIM_CTRL_RXF_TRIG_12 (2 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 12 */ + #define CIM_CTRL_RXF_TRIG_16 (3 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 16 */ + #define CIM_CTRL_RXF_TRIG_20 (4 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 20 */ + #define CIM_CTRL_RXF_TRIG_24 (5 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 24 */ + #define CIM_CTRL_RXF_TRIG_28 (6 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 28 */ + #define CIM_CTRL_RXF_TRIG_32 (7 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 32 */ +#define CIM_CTRL_DMA_EN (1 << 2) +#define CIM_CTRL_RXF_RST (1 << 1) +#define CIM_CTRL_ENA (1 << 0) + +/* CIM State Register (CIM_STATE) */ + +#define CIM_STATE_DMA_SOF (1 << 6) +#define CIM_STATE_DMA_EOF (1 << 5) +#define CIM_STATE_DMA_STOP (1 << 4) +#define CIM_STATE_RXF_OF (1 << 3) +#define CIM_STATE_RXF_TRIG (1 << 2) +#define CIM_STATE_RXF_EMPTY (1 << 1) +#define CIM_STATE_VDD (1 << 0) + +/* CIM DMA Command Register (CIM_CMD) */ + +#define CIM_CMD_SOFINT (1 << 31) +#define CIM_CMD_EOFINT (1 << 30) +#define CIM_CMD_STOP (1 << 28) +#define CIM_CMD_LEN_BIT 0 +#define CIM_CMD_LEN_MASK (0xffffff << CIM_CMD_LEN_BIT) + + +/************************************************************************* + * SADC (Smart A/D Controller) + *************************************************************************/ + +#define SADC_ENA (SADC_BASE + 0x00) /* ADC Enable Register */ +#define SADC_CFG (SADC_BASE + 0x04) /* ADC Configure Register */ +#define SADC_CTRL (SADC_BASE + 0x08) /* ADC Control Register */ +#define SADC_STATE (SADC_BASE + 0x0C) /* ADC Status Register*/ +#define SADC_SAMETIME (SADC_BASE + 0x10) /* ADC Same Point Time Register */ +#define SADC_WAITTIME (SADC_BASE + 0x14) /* ADC Wait Time Register */ +#define SADC_TSDAT (SADC_BASE + 0x18) /* ADC Touch Screen Data Register */ +#define SADC_BATDAT (SADC_BASE + 0x1C) /* ADC PBAT Data Register */ +#define SADC_SADDAT (SADC_BASE + 0x20) /* ADC SADCIN Data Register */ + +#define REG_SADC_ENA REG8(SADC_ENA) +#define REG_SADC_CFG REG32(SADC_CFG) +#define REG_SADC_CTRL REG8(SADC_CTRL) +#define REG_SADC_STATE REG8(SADC_STATE) +#define REG_SADC_SAMETIME REG16(SADC_SAMETIME) +#define REG_SADC_WAITTIME REG16(SADC_WAITTIME) +#define REG_SADC_TSDAT REG32(SADC_TSDAT) +#define REG_SADC_BATDAT REG16(SADC_BATDAT) +#define REG_SADC_SADDAT REG16(SADC_SADDAT) + +/* ADC Enable Register */ +#define SADC_ENA_ADEN (1 << 7) /* Touch Screen Enable */ +#define SADC_ENA_TSEN (1 << 2) /* Touch Screen Enable */ +#define SADC_ENA_PBATEN (1 << 1) /* PBAT Enable */ +#define SADC_ENA_SADCINEN (1 << 0) /* SADCIN Enable */ + +/* ADC Configure Register */ +#define SADC_CFG_EXIN (1 << 30) +#define SADC_CFG_CLKOUT_NUM_BIT 16 +#define SADC_CFG_CLKOUT_NUM_MASK (0x7 << SADC_CFG_CLKOUT_NUM_BIT) +#define SADC_CFG_TS_DMA (1 << 15) /* Touch Screen DMA Enable */ +#define SADC_CFG_XYZ_BIT 13 /* XYZ selection */ +#define SADC_CFG_XYZ_MASK (0x3 << SADC_CFG_XYZ_BIT) + #define SADC_CFG_XY (0 << SADC_CFG_XYZ_BIT) + #define SADC_CFG_XYZ (1 << SADC_CFG_XYZ_BIT) + #define SADC_CFG_XYZ1Z2 (2 << SADC_CFG_XYZ_BIT) +#define SADC_CFG_SNUM_BIT 10 /* Sample Number */ +#define SADC_CFG_SNUM_MASK (0x7 << SADC_CFG_SNUM_BIT) + #define SADC_CFG_SNUM_1 (0x0 << SADC_CFG_SNUM_BIT) + #define SADC_CFG_SNUM_2 (0x1 << SADC_CFG_SNUM_BIT) + #define SADC_CFG_SNUM_3 (0x2 << SADC_CFG_SNUM_BIT) + #define SADC_CFG_SNUM_4 (0x3 << SADC_CFG_SNUM_BIT) + #define SADC_CFG_SNUM_5 (0x4 << SADC_CFG_SNUM_BIT) + #define SADC_CFG_SNUM_6 (0x5 << SADC_CFG_SNUM_BIT) + #define SADC_CFG_SNUM_8 (0x6 << SADC_CFG_SNUM_BIT) + #define SADC_CFG_SNUM_9 (0x7 << SADC_CFG_SNUM_BIT) +#define SADC_CFG_CLKDIV_BIT 5 /* AD Converter frequency clock divider */ +#define SADC_CFG_CLKDIV_MASK (0x1f << SADC_CFG_CLKDIV_BIT) +#define SADC_CFG_PBAT_HIGH (0 << 4) /* PBAT >= 2.5V */ +#define SADC_CFG_PBAT_LOW (1 << 4) /* PBAT < 2.5V */ +#define SADC_CFG_CMD_BIT 0 /* ADC Command */ +#define SADC_CFG_CMD_MASK (0xf << SADC_CFG_CMD_BIT) + #define SADC_CFG_CMD_X_SE (0x0 << SADC_CFG_CMD_BIT) /* X Single-End */ + #define SADC_CFG_CMD_Y_SE (0x1 << SADC_CFG_CMD_BIT) /* Y Single-End */ + #define SADC_CFG_CMD_X_DIFF (0x2 << SADC_CFG_CMD_BIT) /* X Differential */ + #define SADC_CFG_CMD_Y_DIFF (0x3 << SADC_CFG_CMD_BIT) /* Y Differential */ + #define SADC_CFG_CMD_Z1_DIFF (0x4 << SADC_CFG_CMD_BIT) /* Z1 Differential */ + #define SADC_CFG_CMD_Z2_DIFF (0x5 << SADC_CFG_CMD_BIT) /* Z2 Differential */ + #define SADC_CFG_CMD_Z3_DIFF (0x6 << SADC_CFG_CMD_BIT) /* Z3 Differential */ + #define SADC_CFG_CMD_Z4_DIFF (0x7 << SADC_CFG_CMD_BIT) /* Z4 Differential */ + #define SADC_CFG_CMD_TP_SE (0x8 << SADC_CFG_CMD_BIT) /* Touch Pressure */ + #define SADC_CFG_CMD_PBATH_SE (0x9 << SADC_CFG_CMD_BIT) /* PBAT >= 2.5V */ + #define SADC_CFG_CMD_PBATL_SE (0xa << SADC_CFG_CMD_BIT) /* PBAT < 2.5V */ + #define SADC_CFG_CMD_SADCIN_SE (0xb << SADC_CFG_CMD_BIT) /* Measure SADCIN */ + #define SADC_CFG_CMD_INT_PEN (0xc << SADC_CFG_CMD_BIT) /* INT_PEN Enable */ + +/* ADC Control Register */ +#define SADC_CTRL_PENDM (1 << 4) /* Pen Down Interrupt Mask */ +#define SADC_CTRL_PENUM (1 << 3) /* Pen Up Interrupt Mask */ +#define SADC_CTRL_TSRDYM (1 << 2) /* Touch Screen Data Ready Interrupt Mask */ +#define SADC_CTRL_PBATRDYM (1 << 1) /* PBAT Data Ready Interrupt Mask */ +#define SADC_CTRL_SRDYM (1 << 0) /* SADCIN Data Ready Interrupt Mask */ + +/* ADC Status Register */ +#define SADC_STATE_TSBUSY (1 << 7) /* TS A/D is working */ +#define SADC_STATE_PBATBUSY (1 << 6) /* PBAT A/D is working */ +#define SADC_STATE_SBUSY (1 << 5) /* SADCIN A/D is working */ +#define SADC_STATE_PEND (1 << 4) /* Pen Down Interrupt Flag */ +#define SADC_STATE_PENU (1 << 3) /* Pen Up Interrupt Flag */ +#define SADC_STATE_TSRDY (1 << 2) /* Touch Screen Data Ready Interrupt Flag */ +#define SADC_STATE_PBATRDY (1 << 1) /* PBAT Data Ready Interrupt Flag */ +#define SADC_STATE_SRDY (1 << 0) /* SADCIN Data Ready Interrupt Flag */ + +/* ADC Touch Screen Data Register */ +#define SADC_TSDAT_DATA0_BIT 0 +#define SADC_TSDAT_DATA0_MASK (0xfff << SADC_TSDAT_DATA0_BIT) +#define SADC_TSDAT_TYPE0 (1 << 15) +#define SADC_TSDAT_DATA1_BIT 16 +#define SADC_TSDAT_DATA1_MASK (0xfff << SADC_TSDAT_DATA1_BIT) +#define SADC_TSDAT_TYPE1 (1 << 31) + + +/************************************************************************* + * SLCD (Smart LCD Controller) + *************************************************************************/ + +#define SLCD_CFG (SLCD_BASE + 0xA0) /* SLCD Configure Register */ +#define SLCD_CTRL (SLCD_BASE + 0xA4) /* SLCD Control Register */ +#define SLCD_STATE (SLCD_BASE + 0xA8) /* SLCD Status Register */ +#define SLCD_DATA (SLCD_BASE + 0xAC) /* SLCD Data Register */ +#define SLCD_FIFO (SLCD_BASE + 0xB0) /* SLCD FIFO Register */ + +#define REG_SLCD_CFG REG32(SLCD_CFG) +#define REG_SLCD_CTRL REG8(SLCD_CTRL) +#define REG_SLCD_STATE REG8(SLCD_STATE) +#define REG_SLCD_DATA REG32(SLCD_DATA) +#define REG_SLCD_FIFO REG32(SLCD_FIFO) + +/* SLCD Configure Register */ +#define SLCD_CFG_BURST_BIT 14 +#define SLCD_CFG_BURST_MASK (0x3 << SLCD_CFG_BURST_BIT) + #define SLCD_CFG_BURST_4_WORD (0 << SLCD_CFG_BURST_BIT) + #define SLCD_CFG_BURST_8_WORD (1 << SLCD_CFG_BURST_BIT) +#define SLCD_CFG_DWIDTH_BIT 10 +#define SLCD_CFG_DWIDTH_MASK (0x7 << SLCD_CFG_DWIDTH_BIT) + #define SLCD_CFG_DWIDTH_18 (0 << SLCD_CFG_DWIDTH_BIT) + #define SLCD_CFG_DWIDTH_16 (1 << SLCD_CFG_DWIDTH_BIT) + #define SLCD_CFG_DWIDTH_8_x3 (2 << SLCD_CFG_DWIDTH_BIT) + #define SLCD_CFG_DWIDTH_8_x2 (3 << SLCD_CFG_DWIDTH_BIT) + #define SLCD_CFG_DWIDTH_8_x1 (4 << SLCD_CFG_DWIDTH_BIT) + #define SLCD_CFG_DWIDTH_9_x2 (7 << SLCD_CFG_DWIDTH_BIT) +#define SLCD_CFG_CWIDTH_16BIT (0 << 8) +#define SLCD_CFG_CWIDTH_8BIT (1 << 8) +#define SLCD_CFG_CWIDTH_18BIT (2 << 8) +#define SLCD_CFG_CS_ACTIVE_LOW (0 << 4) +#define SLCD_CFG_CS_ACTIVE_HIGH (1 << 4) +#define SLCD_CFG_RS_CMD_LOW (0 << 3) +#define SLCD_CFG_RS_CMD_HIGH (1 << 3) +#define SLCD_CFG_CLK_ACTIVE_FALLING (0 << 1) +#define SLCD_CFG_CLK_ACTIVE_RISING (1 << 1) +#define SLCD_CFG_TYPE_PARALLEL (0 << 0) +#define SLCD_CFG_TYPE_SERIAL (1 << 0) + +/* SLCD Control Register */ +#define SLCD_CTRL_DMA_EN (1 << 0) + +/* SLCD Status Register */ +#define SLCD_STATE_BUSY (1 << 0) + +/* SLCD Data Register */ +#define SLCD_DATA_RS_DATA (0 << 31) +#define SLCD_DATA_RS_COMMAND (1 << 31) + +/* SLCD FIFO Register */ +#define SLCD_FIFO_RS_DATA (0 << 31) +#define SLCD_FIFO_RS_COMMAND (1 << 31) + + +/************************************************************************* + * LCD (LCD Controller) + *************************************************************************/ +#define LCD_CFG (LCD_BASE + 0x00) /* LCD Configure Register */ +#define LCD_VSYNC (LCD_BASE + 0x04) /* Vertical Synchronize Register */ +#define LCD_HSYNC (LCD_BASE + 0x08) /* Horizontal Synchronize Register */ +#define LCD_VAT (LCD_BASE + 0x0c) /* Virtual Area Setting Register */ +#define LCD_DAH (LCD_BASE + 0x10) /* Display Area Horizontal Start/End Point */ +#define LCD_DAV (LCD_BASE + 0x14) /* Display Area Vertical Start/End Point */ +#define LCD_PS (LCD_BASE + 0x18) /* PS Signal Setting */ +#define LCD_CLS (LCD_BASE + 0x1c) /* CLS Signal Setting */ +#define LCD_SPL (LCD_BASE + 0x20) /* SPL Signal Setting */ +#define LCD_REV (LCD_BASE + 0x24) /* REV Signal Setting */ +#define LCD_CTRL (LCD_BASE + 0x30) /* LCD Control Register */ +#define LCD_STATE (LCD_BASE + 0x34) /* LCD Status Register */ +#define LCD_IID (LCD_BASE + 0x38) /* Interrupt ID Register */ +#define LCD_DA0 (LCD_BASE + 0x40) /* Descriptor Address Register 0 */ +#define LCD_SA0 (LCD_BASE + 0x44) /* Source Address Register 0 */ +#define LCD_FID0 (LCD_BASE + 0x48) /* Frame ID Register 0 */ +#define LCD_CMD0 (LCD_BASE + 0x4c) /* DMA Command Register 0 */ +#define LCD_DA1 (LCD_BASE + 0x50) /* Descriptor Address Register 1 */ +#define LCD_SA1 (LCD_BASE + 0x54) /* Source Address Register 1 */ +#define LCD_FID1 (LCD_BASE + 0x58) /* Frame ID Register 1 */ +#define LCD_CMD1 (LCD_BASE + 0x5c) /* DMA Command Register 1 */ + +#define REG_LCD_CFG REG32(LCD_CFG) +#define REG_LCD_VSYNC REG32(LCD_VSYNC) +#define REG_LCD_HSYNC REG32(LCD_HSYNC) +#define REG_LCD_VAT REG32(LCD_VAT) +#define REG_LCD_DAH REG32(LCD_DAH) +#define REG_LCD_DAV REG32(LCD_DAV) +#define REG_LCD_PS REG32(LCD_PS) +#define REG_LCD_CLS REG32(LCD_CLS) +#define REG_LCD_SPL REG32(LCD_SPL) +#define REG_LCD_REV REG32(LCD_REV) +#define REG_LCD_CTRL REG32(LCD_CTRL) +#define REG_LCD_STATE REG32(LCD_STATE) +#define REG_LCD_IID REG32(LCD_IID) +#define REG_LCD_DA0 REG32(LCD_DA0) +#define REG_LCD_SA0 REG32(LCD_SA0) +#define REG_LCD_FID0 REG32(LCD_FID0) +#define REG_LCD_CMD0 REG32(LCD_CMD0) +#define REG_LCD_DA1 REG32(LCD_DA1) +#define REG_LCD_SA1 REG32(LCD_SA1) +#define REG_LCD_FID1 REG32(LCD_FID1) +#define REG_LCD_CMD1 REG32(LCD_CMD1) + +/* LCD Configure Register */ +#define LCD_CFG_LCDPIN_BIT 31 /* LCD pins selection */ +#define LCD_CFG_LCDPIN_MASK (0x1 << LCD_CFG_LCDPIN_BIT) + #define LCD_CFG_LCDPIN_LCD (0x0 << LCD_CFG_LCDPIN_BIT) + #define LCD_CFG_LCDPIN_SLCD (0x1 << LCD_CFG_LCDPIN_BIT) +#define LCD_CFG_PSM (1 << 23) /* PS signal mode */ +#define LCD_CFG_CLSM (1 << 22) /* CLS signal mode */ +#define LCD_CFG_SPLM (1 << 21) /* SPL signal mode */ +#define LCD_CFG_REVM (1 << 20) /* REV signal mode */ +#define LCD_CFG_HSYNM (1 << 19) /* HSYNC signal mode */ +#define LCD_CFG_PCLKM (1 << 18) /* PCLK signal mode */ +#define LCD_CFG_INVDAT (1 << 17) /* Inverse output data */ +#define LCD_CFG_SYNDIR_IN (1 << 16) /* VSYNC&HSYNC direction */ +#define LCD_CFG_PSP (1 << 15) /* PS pin reset state */ +#define LCD_CFG_CLSP (1 << 14) /* CLS pin reset state */ +#define LCD_CFG_SPLP (1 << 13) /* SPL pin reset state */ +#define LCD_CFG_REVP (1 << 12) /* REV pin reset state */ +#define LCD_CFG_HSP (1 << 11) /* HSYNC pority:0-active high,1-active low */ +#define LCD_CFG_PCP (1 << 10) /* PCLK pority:0-rising,1-falling */ +#define LCD_CFG_DEP (1 << 9) /* DE pority:0-active high,1-active low */ +#define LCD_CFG_VSP (1 << 8) /* VSYNC pority:0-rising,1-falling */ +#define LCD_CFG_PDW_BIT 4 /* STN pins utilization */ +#define LCD_CFG_PDW_MASK (0x3 << LCD_DEV_PDW_BIT) +#define LCD_CFG_PDW_1 (0 << LCD_CFG_PDW_BIT) /* LCD_D[0] */ + #define LCD_CFG_PDW_2 (1 << LCD_CFG_PDW_BIT) /* LCD_D[0:1] */ + #define LCD_CFG_PDW_4 (2 << LCD_CFG_PDW_BIT) /* LCD_D[0:3]/LCD_D[8:11] */ + #define LCD_CFG_PDW_8 (3 << LCD_CFG_PDW_BIT) /* LCD_D[0:7]/LCD_D[8:15] */ +#define LCD_CFG_MODE_BIT 0 /* Display Device Mode Select */ +#define LCD_CFG_MODE_MASK (0x0f << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_GENERIC_TFT (0 << LCD_CFG_MODE_BIT) /* 16,18 bit TFT */ + #define LCD_CFG_MODE_SPECIAL_TFT_1 (1 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_SPECIAL_TFT_2 (2 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_SPECIAL_TFT_3 (3 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_NONINTER_CCIR656 (4 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_INTER_CCIR656 (6 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_SINGLE_CSTN (8 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_SINGLE_MSTN (9 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_DUAL_CSTN (10 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_DUAL_MSTN (11 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_SERIAL_TFT (12 << LCD_CFG_MODE_BIT) + /* JZ47XX defines */ + #define LCD_CFG_MODE_SHARP_HR (1 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_CASIO_TFT (2 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_SAMSUNG_ALPHA (3 << LCD_CFG_MODE_BIT) + + + +/* Vertical Synchronize Register */ +#define LCD_VSYNC_VPS_BIT 16 /* VSYNC pulse start in line clock, fixed to 0 */ +#define LCD_VSYNC_VPS_MASK (0xffff << LCD_VSYNC_VPS_BIT) +#define LCD_VSYNC_VPE_BIT 0 /* VSYNC pulse end in line clock */ +#define LCD_VSYNC_VPE_MASK (0xffff << LCD_VSYNC_VPS_BIT) + +/* Horizontal Synchronize Register */ +#define LCD_HSYNC_HPS_BIT 16 /* HSYNC pulse start position in dot clock */ +#define LCD_HSYNC_HPS_MASK (0xffff << LCD_HSYNC_HPS_BIT) +#define LCD_HSYNC_HPE_BIT 0 /* HSYNC pulse end position in dot clock */ +#define LCD_HSYNC_HPE_MASK (0xffff << LCD_HSYNC_HPE_BIT) + +/* Virtual Area Setting Register */ +#define LCD_VAT_HT_BIT 16 /* Horizontal Total size in dot clock */ +#define LCD_VAT_HT_MASK (0xffff << LCD_VAT_HT_BIT) +#define LCD_VAT_VT_BIT 0 /* Vertical Total size in dot clock */ +#define LCD_VAT_VT_MASK (0xffff << LCD_VAT_VT_BIT) + +/* Display Area Horizontal Start/End Point Register */ +#define LCD_DAH_HDS_BIT 16 /* Horizontal display area start in dot clock */ +#define LCD_DAH_HDS_MASK (0xffff << LCD_DAH_HDS_BIT) +#define LCD_DAH_HDE_BIT 0 /* Horizontal display area end in dot clock */ +#define LCD_DAH_HDE_MASK (0xffff << LCD_DAH_HDE_BIT) + +/* Display Area Vertical Start/End Point Register */ +#define LCD_DAV_VDS_BIT 16 /* Vertical display area start in line clock */ +#define LCD_DAV_VDS_MASK (0xffff << LCD_DAV_VDS_BIT) +#define LCD_DAV_VDE_BIT 0 /* Vertical display area end in line clock */ +#define LCD_DAV_VDE_MASK (0xffff << LCD_DAV_VDE_BIT) + +/* PS Signal Setting */ +#define LCD_PS_PSS_BIT 16 /* PS signal start position in dot clock */ +#define LCD_PS_PSS_MASK (0xffff << LCD_PS_PSS_BIT) +#define LCD_PS_PSE_BIT 0 /* PS signal end position in dot clock */ +#define LCD_PS_PSE_MASK (0xffff << LCD_PS_PSE_BIT) + +/* CLS Signal Setting */ +#define LCD_CLS_CLSS_BIT 16 /* CLS signal start position in dot clock */ +#define LCD_CLS_CLSS_MASK (0xffff << LCD_CLS_CLSS_BIT) +#define LCD_CLS_CLSE_BIT 0 /* CLS signal end position in dot clock */ +#define LCD_CLS_CLSE_MASK (0xffff << LCD_CLS_CLSE_BIT) + +/* SPL Signal Setting */ +#define LCD_SPL_SPLS_BIT 16 /* SPL signal start position in dot clock */ +#define LCD_SPL_SPLS_MASK (0xffff << LCD_SPL_SPLS_BIT) +#define LCD_SPL_SPLE_BIT 0 /* SPL signal end position in dot clock */ +#define LCD_SPL_SPLE_MASK (0xffff << LCD_SPL_SPLE_BIT) + +/* REV Signal Setting */ +#define LCD_REV_REVS_BIT 16 /* REV signal start position in dot clock */ +#define LCD_REV_REVS_MASK (0xffff << LCD_REV_REVS_BIT) + +/* LCD Control Register */ +#define LCD_CTRL_BST_BIT 28 /* Burst Length Selection */ +#define LCD_CTRL_BST_MASK (0x03 << LCD_CTRL_BST_BIT) + #define LCD_CTRL_BST_4 (0 << LCD_CTRL_BST_BIT) /* 4-word */ + #define LCD_CTRL_BST_8 (1 << LCD_CTRL_BST_BIT) /* 8-word */ + #define LCD_CTRL_BST_16 (2 << LCD_CTRL_BST_BIT) /* 16-word */ +#define LCD_CTRL_RGB565 (0 << 27) /* RGB565 mode */ +#define LCD_CTRL_RGB555 (1 << 27) /* RGB555 mode */ +#define LCD_CTRL_OFUP (1 << 26) /* Output FIFO underrun protection enable */ +#define LCD_CTRL_FRC_BIT 24 /* STN FRC Algorithm Selection */ +#define LCD_CTRL_FRC_MASK (0x03 << LCD_CTRL_FRC_BIT) + #define LCD_CTRL_FRC_16 (0 << LCD_CTRL_FRC_BIT) /* 16 grayscale */ + #define LCD_CTRL_FRC_4 (1 << LCD_CTRL_FRC_BIT) /* 4 grayscale */ + #define LCD_CTRL_FRC_2 (2 << LCD_CTRL_FRC_BIT) /* 2 grayscale */ +#define LCD_CTRL_PDD_BIT 16 /* Load Palette Delay Counter */ +#define LCD_CTRL_PDD_MASK (0xff << LCD_CTRL_PDD_BIT) +#define LCD_CTRL_EOFM (1 << 13) /* EOF interrupt mask */ +#define LCD_CTRL_SOFM (1 << 12) /* SOF interrupt mask */ +#define LCD_CTRL_OFUM (1 << 11) /* Output FIFO underrun interrupt mask */ +#define LCD_CTRL_IFUM0 (1 << 10) /* Input FIFO 0 underrun interrupt mask */ +#define LCD_CTRL_IFUM1 (1 << 9) /* Input FIFO 1 underrun interrupt mask */ +#define LCD_CTRL_LDDM (1 << 8) /* LCD disable done interrupt mask */ +#define LCD_CTRL_QDM (1 << 7) /* LCD quick disable done interrupt mask */ +#define LCD_CTRL_BEDN (1 << 6) /* Endian selection */ +#define LCD_CTRL_PEDN (1 << 5) /* Endian in byte:0-msb first, 1-lsb first */ +#define LCD_CTRL_DIS (1 << 4) /* Disable indicate bit */ +#define LCD_CTRL_ENA (1 << 3) /* LCD enable bit */ +#define LCD_CTRL_BPP_BIT 0 /* Bits Per Pixel */ +#define LCD_CTRL_BPP_MASK (0x07 << LCD_CTRL_BPP_BIT) + #define LCD_CTRL_BPP_1 (0 << LCD_CTRL_BPP_BIT) /* 1 bpp */ + #define LCD_CTRL_BPP_2 (1 << LCD_CTRL_BPP_BIT) /* 2 bpp */ + #define LCD_CTRL_BPP_4 (2 << LCD_CTRL_BPP_BIT) /* 4 bpp */ + #define LCD_CTRL_BPP_8 (3 << LCD_CTRL_BPP_BIT) /* 8 bpp */ + #define LCD_CTRL_BPP_16 (4 << LCD_CTRL_BPP_BIT) /* 15/16 bpp */ + #define LCD_CTRL_BPP_18_24 (5 << LCD_CTRL_BPP_BIT) /* 18/24/32 bpp */ + +/* LCD Status Register */ +#define LCD_STATE_QD (1 << 7) /* Quick Disable Done */ +#define LCD_STATE_EOF (1 << 5) /* EOF Flag */ +#define LCD_STATE_SOF (1 << 4) /* SOF Flag */ +#define LCD_STATE_OFU (1 << 3) /* Output FIFO Underrun */ +#define LCD_STATE_IFU0 (1 << 2) /* Input FIFO 0 Underrun */ +#define LCD_STATE_IFU1 (1 << 1) /* Input FIFO 1 Underrun */ +#define LCD_STATE_LDD (1 << 0) /* LCD Disabled */ + +/* DMA Command Register */ +#define LCD_CMD_SOFINT (1 << 31) +#define LCD_CMD_EOFINT (1 << 30) +#define LCD_CMD_PAL (1 << 28) +#define LCD_CMD_LEN_BIT 0 +#define LCD_CMD_LEN_MASK (0xffffff << LCD_CMD_LEN_BIT) + + +/************************************************************************* + * USB Device + *************************************************************************/ +#define USB_BASE UDC_BASE + +#define USB_REG_FADDR (USB_BASE + 0x00) /* Function Address 8-bit */ +#define USB_REG_POWER (USB_BASE + 0x01) /* Power Managemetn 8-bit */ +#define USB_REG_INTRIN (USB_BASE + 0x02) /* Interrupt IN 16-bit */ +#define USB_REG_INTROUT (USB_BASE + 0x04) /* Interrupt OUT 16-bit */ +#define USB_REG_INTRINE (USB_BASE + 0x06) /* Intr IN enable 16-bit */ +#define USB_REG_INTROUTE (USB_BASE + 0x08) /* Intr OUT enable 16-bit */ +#define USB_REG_INTRUSB (USB_BASE + 0x0a) /* Interrupt USB 8-bit */ +#define USB_REG_INTRUSBE (USB_BASE + 0x0b) /* Interrupt USB Enable 8-bit */ +#define USB_REG_FRAME (USB_BASE + 0x0c) /* Frame number 16-bit */ +#define USB_REG_INDEX (USB_BASE + 0x0e) /* Index register 8-bit */ +#define USB_REG_TESTMODE (USB_BASE + 0x0f) /* USB test mode 8-bit */ + +#define USB_REG_CSR0 (USB_BASE + 0x12) /* EP0 CSR 8-bit */ +#define USB_REG_INMAXP (USB_BASE + 0x10) /* EP1-2 IN Max Pkt Size 16-bit */ +#define USB_REG_INCSR (USB_BASE + 0x12) /* EP1-2 IN CSR LSB 8/16bit */ +#define USB_REG_INCSRH (USB_BASE + 0x13) /* EP1-2 IN CSR MSB 8-bit */ +#define USB_REG_OUTMAXP (USB_BASE + 0x14) /* EP1 OUT Max Pkt Size 16-bit */ +#define USB_REG_OUTCSR (USB_BASE + 0x16) /* EP1 OUT CSR LSB 8/16bit */ +#define USB_REG_OUTCSRH (USB_BASE + 0x17) /* EP1 OUT CSR MSB 8-bit */ +#define USB_REG_OUTCOUNT (USB_BASE + 0x18) /* bytes in EP0/1 OUT FIFO 16-bit */ + +#define USB_FIFO_EP0 (USB_BASE + 0x20) +#define USB_FIFO_EP1 (USB_BASE + 0x24) +#define USB_FIFO_EP2 (USB_BASE + 0x28) + +#define USB_REG_EPINFO (USB_BASE + 0x78) /* Endpoint information */ +#define USB_REG_RAMINFO (USB_BASE + 0x79) /* RAM information */ + +#define USB_REG_INTR (USB_BASE + 0x200) /* DMA pending interrupts */ +#define USB_REG_CNTL1 (USB_BASE + 0x204) /* DMA channel 1 control */ +#define USB_REG_ADDR1 (USB_BASE + 0x208) /* DMA channel 1 AHB memory addr */ +#define USB_REG_COUNT1 (USB_BASE + 0x20c) /* DMA channel 1 byte count */ +#define USB_REG_CNTL2 (USB_BASE + 0x214) /* DMA channel 2 control */ +#define USB_REG_ADDR2 (USB_BASE + 0x218) /* DMA channel 2 AHB memory addr */ +#define USB_REG_COUNT2 (USB_BASE + 0x21c) /* DMA channel 2 byte count */ + + +/* Power register bit masks */ +#define USB_POWER_SUSPENDM 0x01 +#define USB_POWER_RESUME 0x04 +#define USB_POWER_HSMODE 0x10 +#define USB_POWER_HSENAB 0x20 +#define USB_POWER_SOFTCONN 0x40 + +/* Interrupt register bit masks */ +#define USB_INTR_SUSPEND 0x01 +#define USB_INTR_RESUME 0x02 +#define USB_INTR_RESET 0x04 + +#define USB_INTR_EP0 0x0001 +#define USB_INTR_INEP1 0x0002 +#define USB_INTR_INEP2 0x0004 +#define USB_INTR_OUTEP1 0x0002 + +/* CSR0 bit masks */ +#define USB_CSR0_OUTPKTRDY 0x01 +#define USB_CSR0_INPKTRDY 0x02 +#define USB_CSR0_SENTSTALL 0x04 +#define USB_CSR0_DATAEND 0x08 +#define USB_CSR0_SETUPEND 0x10 +#define USB_CSR0_SENDSTALL 0x20 +#define USB_CSR0_SVDOUTPKTRDY 0x40 +#define USB_CSR0_SVDSETUPEND 0x80 + +/* Endpoint CSR register bits */ +#define USB_INCSRH_AUTOSET 0x80 +#define USB_INCSRH_ISO 0x40 +#define USB_INCSRH_MODE 0x20 +#define USB_INCSRH_DMAREQENAB 0x10 +#define USB_INCSRH_DMAREQMODE 0x04 +#define USB_INCSR_CDT 0x40 +#define USB_INCSR_SENTSTALL 0x20 +#define USB_INCSR_SENDSTALL 0x10 +#define USB_INCSR_FF 0x08 +#define USB_INCSR_UNDERRUN 0x04 +#define USB_INCSR_FFNOTEMPT 0x02 +#define USB_INCSR_INPKTRDY 0x01 +#define USB_OUTCSRH_AUTOCLR 0x80 +#define USB_OUTCSRH_ISO 0x40 +#define USB_OUTCSRH_DMAREQENAB 0x20 +#define USB_OUTCSRH_DNYT 0x10 +#define USB_OUTCSRH_DMAREQMODE 0x08 +#define USB_OUTCSR_CDT 0x80 +#define USB_OUTCSR_SENTSTALL 0x40 +#define USB_OUTCSR_SENDSTALL 0x20 +#define USB_OUTCSR_FF 0x10 +#define USB_OUTCSR_DATAERR 0x08 +#define USB_OUTCSR_OVERRUN 0x04 +#define USB_OUTCSR_FFFULL 0x02 +#define USB_OUTCSR_OUTPKTRDY 0x01 + +/* Testmode register bits */ +#define USB_TEST_SE0NAK 0x01 +#define USB_TEST_J 0x02 +#define USB_TEST_K 0x04 +#define USB_TEST_PACKET 0x08 + +/* DMA control bits */ +#define USB_CNTL_ENA 0x01 +#define USB_CNTL_DIR_IN 0x02 +#define USB_CNTL_MODE_1 0x04 +#define USB_CNTL_INTR_EN 0x08 +#define USB_CNTL_EP(n) ((n) << 4) +#define USB_CNTL_BURST_0 (0 << 9) +#define USB_CNTL_BURST_4 (1 << 9) +#define USB_CNTL_BURST_8 (2 << 9) +#define USB_CNTL_BURST_16 (3 << 9) + +#endif /* __JZ4740_REGS_H__ */ diff --git a/include/asm-mips/mach-jz4740/serial.h b/include/asm-mips/mach-jz4740/serial.h new file mode 100644 index 0000000..c4819b9 --- /dev/null +++ b/include/asm-mips/mach-jz4740/serial.h @@ -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: + * + * 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__ */ diff --git a/include/asm-mips/mach-jz4740/war.h b/include/asm-mips/mach-jz4740/war.h new file mode 100644 index 0000000..3a5bc17 --- /dev/null +++ b/include/asm-mips/mach-jz4740/war.h @@ -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 + */ +#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 */ diff --git a/include/asm-mips/mach-jz4750/board-apus.h b/include/asm-mips/mach-jz4750/board-apus.h new file mode 100644 index 0000000..18bba40 --- /dev/null +++ b/include/asm-mips/mach-jz4750/board-apus.h @@ -0,0 +1,119 @@ +/* + * linux/include/asm-mips/mach-jz4750/board-apus.h + * + * JZ4750-based APUS board ver 1.x definition. + * + * Copyright (C) 2008 Ingenic Semiconductor Inc. + * + * Author: + * + * 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_JZ4750_APUS_H__ +#define __ASM_JZ4750_APUS_H__ + +/*====================================================================== + * Frequencies of on-board oscillators + */ +#define JZ_EXTAL 24000000 /* Main extal freq: 24 MHz */ +#define JZ_EXTAL2 32768 /* RTC extal freq: 32.768 KHz */ + +/*====================================================================== + * GPIO + */ +#define GPIO_DISP_OFF_N (32*4+25) /* GPE25 */ +#define GPIO_SD0_VCC_EN_N (32*2+10) /* GPC10 */ +#define GPIO_SD0_CD_N (32*2+11) /* GPC11 */ +#define GPIO_SD0_WP (32*2+12) /* GPC12 */ +#define GPIO_SD1_VCC_EN_N (32*2+13) /* GPC13 */ +#define GPIO_SD1_CD_N (32*2+14) /* GPC14 */ +#define GPIO_USB_DETE (32*2+11) /* GPC15 */ +#define GPIO_DC_DETE_N (32*2+8) /* GPC8 */ +#define GPIO_CHARG_STAT_N (32*2+9) /* GPC9 */ +#define GPIO_LCD_VCC_EN_N (32*3+30) /* GPC10 */ +#define GPIO_LCD_PWM (32*4+24) /* GPE24 */ +#define GPIO_UDC_HOTPLUG GPIO_USB_DETE + + +/*====================================================================== + * LCD backlight + */ +#define LCD_PWM_CHN 4 /* pwm channel */ +#define LCD_PWM_FULL 101 +/* 100 level: 0,1,...,100 */ +#define __lcd_set_backlight_level(n) \ +do { \ + __gpio_as_output(GPIO_LCD_PWM); \ + __gpio_set_pin(GPIO_LCD_PWM); \ +} while (0) + +#define __lcd_close_backlight() \ +do { \ + __gpio_as_output(GPIO_LCD_PWM); \ + __gpio_clear_pin(GPIO_LCD_PWM); \ +} while (0) + +/*====================================================================== + * MMC/SD + */ + +#define MSC0_WP_PIN GPIO_SD0_WP +#define MSC0_HOTPLUG_PIN GPIO_SD0_CD_N +#define MSC0_HOTPLUG_IRQ (IRQ_GPIO_0 + GPIO_SD0_CD_N) + +#define MSC1_WP_PIN GPIO_SD1_WP +#define MSC1_HOTPLUG_PIN GPIO_SD1_CD_N +#define MSC1_HOTPLUG_IRQ (IRQ_GPIO_0 + GPIO_SD1_CD_N) + +#define __msc0_init_io() \ +do { \ + __gpio_as_output(GPIO_SD0_VCC_EN_N); \ + __gpio_as_input(GPIO_SD0_CD_N); \ +} while (0) + +#define __msc0_enable_power() \ +do { \ + __gpio_clear_pin(GPIO_SD0_VCC_EN_N); \ +} while (0) + +#define __msc0_disable_power() \ +do { \ + __gpio_set_pin(GPIO_SD0_VCC_EN_N); \ +} while (0) + +#define __msc0_card_detected(s) \ +({ \ + int detected = 1; \ + if (__gpio_get_pin(GPIO_SD0_CD_N)) \ + detected = 0; \ + detected; \ +}) + +#define __msc1_init_io() \ +do { \ + __gpio_as_output(GPIO_SD1_VCC_EN_N); \ + __gpio_as_input(GPIO_SD1_CD_N); \ +} while (0) + +#define __msc1_enable_power() \ +do { \ + __gpio_clear_pin(GPIO_SD1_VCC_EN_N); \ +} while (0) + +#define __msc1_disable_power() \ +do { \ + __gpio_set_pin(GPIO_SD1_VCC_EN_N); \ +} while (0) + +#define __msc1_card_detected(s) \ +({ \ + int detected = 0; \ + if (__gpio_get_pin(GPIO_SD1_CD_N)) \ + detected = 1; \ + detected; \ +}) + +#endif /* __ASM_JZ4750_APUS_H__ */ diff --git a/include/asm-mips/mach-jz4750/board-fuwa.h b/include/asm-mips/mach-jz4750/board-fuwa.h new file mode 100644 index 0000000..8b6a199 --- /dev/null +++ b/include/asm-mips/mach-jz4750/board-fuwa.h @@ -0,0 +1,93 @@ +/* + * linux/include/asm-mips/mach-jz4750/board-fuwa.h + * + * JZ4750-based FUWA board ver 1.x definition. + * + * Copyright (C) 2008 Ingenic Semiconductor Inc. + * + * Author: + * + * 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_JZ4750_FUWA_H__ +#define __ASM_JZ4750_FUWA_H__ + +#define CONFIG_FPGA /* fuwa is an FPGA board */ + +/*====================================================================== + * Frequencies of on-board oscillators + */ +#define JZ_EXTAL 48000000 /* 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 121 /* GPD25, LCD_REV */ +#define GPIO_LED_EN 124 /* GPD28 */ + +#define GPIO_UDC_HOTPLUG GPIO_USB_DETE + +/*====================================================================== + * LCD backlight + */ +#define GPIO_LCD_PWM (32*4+20) /* GPE20 */ + +#define LCD_PWM_CHN 0 /* pwm channel */ +#define LCD_PWM_FULL 101 +/* 100 level: 0,1,...,100 */ +#define __lcd_set_backlight_level(n) \ +do { \ + __gpio_as_output(GPIO_LCD_PWM); \ + __gpio_set_pin(GPIO_LCD_PWM); \ +} while (0) + +#define __lcd_close_backlight() \ +do { \ + __gpio_as_output(GPIO_LCD_PWM); \ + __gpio_clear_pin(GPIO_LCD_PWM); \ +} while (0) + +/*====================================================================== + * 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_JZ4750_FUWA_H__ */ diff --git a/include/asm-mips/mach-jz4750/clock.h b/include/asm-mips/mach-jz4750/clock.h new file mode 100644 index 0000000..5747e45 --- /dev/null +++ b/include/asm-mips/mach-jz4750/clock.h @@ -0,0 +1,204 @@ +/* + * linux/include/asm-mips/mach-jz4750/clock.h + * + * JZ4750 clocks definition. + * + * Copyright (C) 2008 Ingenic Semiconductor Inc. + * + * Author: + * + * 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_JZ4750_CLOCK_H__ +#define __ASM_JZ4750_CLOCK_H__ + +#ifndef JZ_EXTAL +#define JZ_EXTAL 12000000 /* 3.6864 MHz */ +#endif +#ifndef JZ_EXTAL2 +#define JZ_EXTAL2 32768 /* 32.768 KHz */ +#endif + +/* + * JZ4750 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 + * @n: the index of MMC/SD controller + */ +static __inline__ unsigned int __cpm_get_mscclk(int n) +{ + return __cpm_get_pllout2() / (__cpm_get_mscdiv(n) + 1); +} + +/* EXTAL clock */ +static __inline__ unsigned int __cpm_get_extalclk0(void) +{ + return JZ_EXTAL; +} + +/* EXTAL clock for UART,I2C,SSI,TCU,USB-PHY */ +static __inline__ unsigned int __cpm_get_extalclk(void) +{ +#if defined(CONFIG_FPGA) + return JZ_EXTAL; +#else + if (REG_CPM_CPCCR & CPM_CPCCR_ECS) + return __cpm_get_extalclk0()/2; + else + return __cpm_get_extalclk0(); +#endif +} + +/* 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. + * @n: the index of MMC/SD controller + */ +static inline void __cpm_select_msc_clk(int n, int sd) +{ + unsigned int pllout2 = __cpm_get_pllout2(); + unsigned int div = 0; + + if (sd) { + div = pllout2 / 24000000; + } + else { + div = pllout2 / 16000000; + } + + REG_CPM_MSCCDR(n) = div - 1; + REG_CPM_CPCCR |= CPM_CPCCR_CE; +} + +/* + * Output 48MHz for high speed card. + */ +static inline void __cpm_select_msc_clk_high(int n, int sd) +{ + unsigned int pllout2 = __cpm_get_pllout2(); + unsigned int div = 0; + + div = pllout2 / 48000000; + + REG_CPM_MSCCDR(n) = div - 1; + REG_CPM_CPCCR |= CPM_CPCCR_CE; +} + +#endif /* __ASM_JZ4750_CLOCK_H__ */ diff --git a/include/asm-mips/mach-jz4750/dma.h b/include/asm-mips/mach-jz4750/dma.h new file mode 100644 index 0000000..fc1f2c8 --- /dev/null +++ b/include/asm-mips/mach-jz4750/dma.h @@ -0,0 +1,307 @@ +/* + * linux/include/asm-mips/mach-jz4750/dma.h + * + * JZ4750 DMA definition. + * + * Copyright (C) 2008 Ingenic Semiconductor Inc. + * + * Author: + * + * 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_JZ4750_DMA_H__ +#define __ASM_JZ4750_DMA_H__ + +#include +#include /* need byte IO */ +#include /* And spinlocks */ +#include +#include + +/* + * Descriptor structure for JZ4750 DMA engine + * Note: this structure must always be aligned to a 16-bytes boundary. + */ + +/* old descriptor 4-word */ +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; + +/* new descriptor 8-word */ +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 */ + volatile u32 dstrd; /* DMA source and target stride address */ + volatile u32 dreqt; /* DMA request type for current transfer */ + volatile u32 reserved0; /* Reserved */ + volatile u32 reserved1; /* Reserved */ +} jz_dma_desc_8word; + +/* DMA Device ID's follow */ +enum { + DMA_ID_EXT = 0, /* External request with DREQn */ + DMA_ID_NAND, /* NAND DMA request */ + DMA_ID_BCH_ENC, /* BCH Encoding DMA request */ + DMA_ID_BCH_DEC, /* BCH Decoding DMA request */ + DMA_ID_AUTO, /* Auto-request */ +// DMA_ID_TSSI_RX, /* TSSI receive fifo full request */ + DMA_ID_UART3_TX, /* UART3 transmit-fifo-empty request */ + DMA_ID_UART3_RX, /* UART3 receve-fifo-full request */ + DMA_ID_UART2_TX, /* UART2 transmit-fifo-empty request */ + DMA_ID_UART2_RX, /* UART2 receve-fifo-full request */ + DMA_ID_UART1_TX, /* UART1 transmit-fifo-empty request */ + DMA_ID_UART1_RX, /* UART1 receve-fifo-full request */ + DMA_ID_UART0_TX, /* UART0 transmit-fifo-empty request */ + DMA_ID_UART0_RX, /* UART0 receve-fifo-full request */ + DMA_ID_SSI0_TX, /* SSI0 transmit-fifo-full request */ + DMA_ID_SSI0_RX, /* SSI0 receive-fifo-empty request */ + DMA_ID_AIC_TX, /* AIC transmit-fifo-full request */ + DMA_ID_AIC_RX, /* AIC receive-fifo-empty request */ + DMA_ID_MSC0_TX, /* MSC0 transmit-fifo-full request */ + DMA_ID_MSC0_RX, /* MSC0 receive-fifo-empty request */ + DMA_ID_TCU_OVERFLOW, /* TCU channel n overflow interrupt */ + DMA_ID_SADC, /* SADC transfer request */ + DMA_ID_MSC1_TX, /* MSC1 transmit-fifo-full request */ + DMA_ID_MSC1_RX, /* MSC1 receive-fifo-empty request */ + DMA_ID_SSI1_TX, /* SSI1 transmit-fifo-full request */ + DMA_ID_SSI1_RX, /* SSI1 receive-fifo-empty request */ + DMA_ID_PCM_TX, /* PM transmit-fifo-full request */ + DMA_ID_PCM_RX, /* PM receive-fifo-empty request */ + 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_32BYTE_TX_CMD \ + DMAC_DCMD_SAI | \ + DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | \ + 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 + +#define DMA_AIC_16BYTE_TX_CMD_UC \ + DMAC_DCMD_SAI | \ + DMAC_DCMD_SWDH_32 | 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 void jz_set_dma_src_width(int dmanr, int nbit); +extern void jz_set_dma_dest_width(int dmanr, int nbit); +extern void jz_set_dma_block_size(int dmanr, int nbyte); +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((chan->io)/HALF_DMA_NUM) &= ~(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((chan->io)/HALF_DMA_NUM) &= ~(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_JZ4750_DMA_H__ */ diff --git a/include/asm-mips/mach-jz4750/jz4750.h b/include/asm-mips/mach-jz4750/jz4750.h new file mode 100644 index 0000000..9517780 --- /dev/null +++ b/include/asm-mips/mach-jz4750/jz4750.h @@ -0,0 +1,44 @@ +/* + * linux/include/asm-mips/mach-jz4750/jz4750.h + * + * JZ4750 common definition. + * + * Copyright (C) 2008 Ingenic Semiconductor Inc. + * + * Author: + * + * 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_JZ4750_H__ +#define __ASM_JZ4750_H__ + +#include +#include +#include +#include + +/*------------------------------------------------------------------ + * Platform definitions + */ +#ifdef CONFIG_JZ4750_FUWA +#include +#endif + +#ifdef CONFIG_JZ4750_APUS +#include +#endif + +/* Add other platform definition here ... */ + + +/*------------------------------------------------------------------ + * Follows are related to platform definitions + */ + +#include +#include + +#endif /* __ASM_JZ4750_H__ */ diff --git a/include/asm-mips/mach-jz4750/misc.h b/include/asm-mips/mach-jz4750/misc.h new file mode 100644 index 0000000..f6c75c9 --- /dev/null +++ b/include/asm-mips/mach-jz4750/misc.h @@ -0,0 +1,44 @@ +/* + * linux/include/asm-mips/mach-jz4750/misc.h + * + * Ingenic's JZ4750 common include. + * + * Copyright (C) 2008 Ingenic Semiconductor Inc. + * + * Author: + * + * 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_JZ4750_MISC_H__ +#define __ASM_JZ4750_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_JZ4750_MISC_H__ */ diff --git a/include/asm-mips/mach-jz4750/ops.h b/include/asm-mips/mach-jz4750/ops.h new file mode 100644 index 0000000..c345ed2 --- /dev/null +++ b/include/asm-mips/mach-jz4750/ops.h @@ -0,0 +1,3570 @@ +/* + * linux/include/asm-mips/mach-jz4750/ops.h + * + * JZ4750 register definition. + * + * Copyright (C) 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. + */ + + +#ifndef __JZ4750_OPS_H__ +#define __JZ4750_OPS_H__ + +/* + * Definition of Module Operations + */ + +/*************************************************************************** + * EMC + ***************************************************************************/ +#define is_share_mode() ((REG_EMC_BCR & EMC_BCR_BSR_MASK) == EMC_BCR_BSR_SHARE) +#define is_normal_order() (!(REG_EMC_BCR & EMC_BCR_PK_SEL)) + +/*************************************************************************** + * GPIO + ***************************************************************************/ + +//------------------------------------------------------ +// GPIO Pins Description +// +// PORT 0: +// +// PIN/BIT N FUNC0 FUNC1 NOTE +// 0 D0 - +// 1 D1 - +// 2 D2 - +// 3 D3 - +// 4 D4 - +// 5 D5 - +// 6 D6 - +// 7 D7 - +// 8 D8 - +// 9 D9 - +// 10 D10 - +// 11 D11 - +// 12 D12 - +// 13 D13 - +// 14 D14 - +// 15 D15 - +// 16 D16 - +// 17 D17 - +// 18 D18 - +// 19 D19 - +// 20 D20 - +// 21 D21 - +// 22 D22 - +// 23 D23 - +// 24 D24 - +// 25 D25 - +// 26 D26 - +// 27 D27 - +// 28 D28 - +// 29 D29 - +// 30 D30 - +// 31 D31 - +// +//------------------------------------------------------ +// PORT 1: +// +// PIN/BIT N FUNC0 FUNC1 NOTE +// 0 A0 - +// 1 A1 - +// 2 A2 - +// 3 A3 - +// 4 A4 - +// 5 A5 - +// 6 A6 - +// 7 A7 - +// 8 A8 - +// 9 A9 - +// 10 A10 - +// 11 A11 - +// 12 A12 - +// 13 A13 - +// 14 A14 - +// 15 A15/CLE SA3 +// 16 DCS0# - +// 17 RAS# - +// 18 CAS# - +// 19 RDWE#/BUFD# - +// 20 WE0# - +// 21 WE1# - +// 22 WE2# - +// 23 WE3# - +// 24 CKO - Note1 +// 25 CKE - +// 26 SSI0_CLK - +// 27 SSI0_DT - +// 28 SSI0_DR - +// 29 SSI0_CE0# - +// 30 SSI0_CE1#_GPC - +// 31 SSI0_CE2# - +// +// Note1: BIT24: it is CKO when chip is reset +// +//------------------------------------------------------ +// PORT 2: +// +// PIN/BIT N FUNC0 FUNC1 NOTE +// 0 SD0 A20 +// 1 SD1 A21 +// 2 SD2 A22 +// 3 SD3 A23 +// 4 SD4 A24 +// 5 SD5 A25 +// 6 SD6 - +// 7 SD7 - +// 8 SD8 TSDI0 +// 9 SD9 TSDI1 +// 10 SD10 TSDI2 +// 11 SD11 TSDI3 +// 12 SD12 TSDI4 +// 13 SD13 TSDI5 +// 14 SD14 TSDI6 +// 15 SD15 TSDI7 +// 16 A16/ALE SA4 +// 17 SA0 A17 +// 18 SA1 A18 +// 19 SA2 A19 +// 20 WAIT# - Note2 +// 21 CS1# - +// 22 CS2# - +// 23 CS3# - +// 24 CS4# - +// 25 RD# - +// 26 WR# - +// 27 FRB# - Note3 +// 28 FRE# - +// 29 FWE# - +// 30 BOOT_SEL0 - Note4 +// 31 BOOT_SEL1 - Note5 +// +// Note2: BIT20: it is WAIT# pin when chip is reset +// +// Note3: BIT27: when NAND is used, it should connect to NANF FRB#. +// +// Note4: BIT30: it is BOOT_SEL0 when chip is reset, it can used as output GPIO. +// +// Note5: BIT31: it is BOOT_SEL1 when chip is reset, it can used as general GPIO. +// +//------------------------------------------------------ +// PORT 3: +// +// PIN/BIT N FUNC0 FUNC1 NOTE +// 0 LCD_D0 - +// 1 LCD_D1 - +// 2 LCD_D2 - +// 3 LCD_D3 - +// 4 LCD_D4 - +// 5 LCD_D5 - +// 6 LCD_D6 - +// 7 LCD_D7 - +// 8 LCD_D8 - +// 9 LCD_D9 - +// 10 LCD_D10 - +// 11 LCD_D11 - +// 12 LCD_D12 - +// 13 LCD_D13 - +// 14 LCD_D14 - +// 15 LCD_D15 - +// 16 LCD_D16 - +// 17 LCD_D17 - +// 18 LCD_PCLK - +// 19 LCD_HSYNC - +// 20 LCD_VSYNC - +// 21 LCD_DE - +// 22 LCD_CLS - +// 23 LCD_SPL - +// 24 LCD_PS - +// 25 LCD_REV - +// 26 SSI1_CLK - +// 27 SSI1_DT - +// 28 SSI1_DR - +// 29 SSI1_CE0# - +// 30 SSI1_CE1# - +// 31 - - +// +//------------------------------------------------------ +// PORT 4: +// +// PIN/BIT N FUNC0 FUNC1 NOTE +// 0 CIM_D0 - +// 1 CIM_D1 - +// 2 CIM_D2 - +// 3 CIM_D3 - +// 4 CIM_D4 - +// 5 CIM_D5 - +// 6 CIM_D6 - +// 7 CIM_D7 - +// 8 CIM_MCLK - +// 9 CIM_PCLK - +// 10 CIM_VSYNC - +// 11 CIM_HSYNC - +// 12 I2C_SDA - +// 13 I2C_SCK - +// 14 - - +// 15 - - +// 16 UART1_RxD - +// 17 UART1_TxD - +// 18 UART1_CTS PCM_DIN +// 19 UART1_RTS PCM_DOUT +// 20 PWM0 PCM_CLK +// 21 PWM1 PCM_SYN +// 22 PWM2 SCLK_RSTN +// 23 PWM3 BCLK +// 24 PWM4 SYNC +// 25 PWM5 OWI +// 26 SDATO UART2_TxD +// 27 SDATI UART2_RxD +// 28 DCS1# - +// 29 - - +// 30 WKUP - Note6 +// 31 - - Note7 +// +// Note6: BIT30: it is only used as input and interrupt, and with no pull-up and pull-down +// +// Note7: BIT31: it is used to select the function of UART or JTAG set by PESEL[31] +// PESEL[31] = 0, select JTAG function +// PESEL[31] = 1, select UART function +// +//------------------------------------------------------ +// PORT 5: +// +// PIN/BIT N FUNC0 FUNC1 NOTE +// 0 MSC0_D0 - +// 1 MSC0_D1 - +// 2 MSC0_D2 DREQ +// 3 MSC0_D3 DACK +// 4 MSC0_D4 UART0_RxD +// 5 MSC0_D5 UART0_TxD +// 6 MSC0_D6 UART0_CTS +// 7 MSC0_D7 UART0_RTS +// 8 MSC0_CLK - +// 9 MSC0_CMD - +// 10 MSC1_D0 - +// 11 MSC1_D1 - +// 12 MSC1_D2 - +// 13 MSC1_D3 - +// 14 MSC1_CLK - +// 15 MSC1_CMD - +// 16 UART3_RxD - +// 17 UART3_TxD - +// 18 UART3_CTS - +// 19 UART3_RTS - +// 20 TSCLK - +// 21 TSSTR - +// 22 TSFRM - +// 23 TSFAIL - +// 24 - - +// 25 - - +// 26 - - +// 27 - - +// 28 - - +// 29 - - +// 30 - - +// 31 - - +// +////////////////////////////////////////////////////////// + +/* + * p is the port number (0,1,2,3,4,5) + * o is the pin offset (0-31) inside the port + * n is the absolute number of a pin (0-191), regardless of the port + */ + +//------------------------------------------- +// Function Pins Mode + +#define __gpio_as_func0(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXFUNS(p) = (1 << o); \ + REG_GPIO_PXSELC(p) = (1 << o); \ +} while (0) + +#define __gpio_as_func1(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXFUNS(p) = (1 << o); \ + REG_GPIO_PXSELS(p) = (1 << o); \ +} while (0) + +/* + * D0 ~ D31, A0 ~ A14, DCS0#, RAS#, CAS#, + * RDWE#, WE0#, WE1#, WE2#, WE3#, CKO#, CKE# + */ +#define __gpio_as_sdram_32bit() \ +do { \ + REG_GPIO_PXFUNS(0) = 0xffffffff; \ + REG_GPIO_PXSELC(0) = 0xffffffff; \ + REG_GPIO_PXPES(0) = 0xffffffff; \ + REG_GPIO_PXFUNS(1) = 0x03ff7fff; \ + REG_GPIO_PXSELC(1) = 0x03ff7fff; \ + REG_GPIO_PXPES(1) = 0x03ff7fff; \ +} while (0) + +/* + * D0 ~ D15, A0 ~ A14, DCS0#, RAS#, CAS#, + * RDWE#, WE0#, WE1#, WE2#, WE3#, CKO#, CKE# + */ +#define __gpio_as_sdram_16bit() \ +do { \ + if (is_normal_order()) { \ + /* 32/16-bit data normal order */ \ + REG_GPIO_PXFUNS(0) = 0x0000ffff; \ + REG_GPIO_PXSELC(0) = 0x0000ffff; \ + REG_GPIO_PXPES(0) = 0x0000ffff; \ + } else { \ + /* 16-bit data special order */ \ + REG_GPIO_PXFUNS(0) = 0x00ffff00; \ + REG_GPIO_PXSELC(0) = 0x00ffff00; \ + REG_GPIO_PXPES(0) = 0x00ffff00; \ + } \ + REG_GPIO_PXFUNS(1) = 0x03ff7fff; \ + REG_GPIO_PXSELC(1) = 0x03ff7fff; \ + REG_GPIO_PXPES(1) = 0x03ff7fff; \ +} while (0) + +/* + * D0 ~ D7, CS1#, CLE, ALE, FRE#, FWE#, FRB#, RDWE#/BUFD# + * @n: chip select number(1 ~ 4) + */ +#define __gpio_as_nand_8bit(n) \ +do { \ + if (!is_share_mode()) { \ + /* unshare mode */ \ + REG_GPIO_PXFUNS(2) = 0x000000ff; /* SD0~SD7 */ \ + REG_GPIO_PXSELS(2) = 0x000000ff; \ + REG_GPIO_PXPES(2) = 0x000000ff; \ + REG_GPIO_PXFUNS(1) = 0x00008000; /* CLE(SA3) */ \ + REG_GPIO_PXSELS(1) = 0x00008000; \ + REG_GPIO_PXPES(1) = 0x00008000; \ + REG_GPIO_PXFUNS(2) = 0x00010000; /* ALE(SA4) */ \ + REG_GPIO_PXSELS(2) = 0x00010000; \ + REG_GPIO_PXPES(2) = 0x00010000; \ + } else { \ + /* share mode */ \ + if (is_normal_order()) { \ + /* 32/16-bit data normal order */ \ + REG_GPIO_PXFUNS(0) = 0x000000ff; /* D0~D7 */ \ + REG_GPIO_PXSELC(0) = 0x000000ff; \ + REG_GPIO_PXPES(0) = 0x000000ff; \ + } else { \ + /* 16-bit data special order */ \ + REG_GPIO_PXFUNS(0) = 0x0000ff00; /* D0~D7 */ \ + REG_GPIO_PXSELC(0) = 0x0000ff00; \ + REG_GPIO_PXPES(0) = 0x0000ff00; \ + } \ + REG_GPIO_PXFUNS(1) = 0x00008000; /* CLE(A15) */ \ + REG_GPIO_PXSELC(1) = 0x00008000; \ + REG_GPIO_PXPES(1) = 0x00008000; \ + REG_GPIO_PXFUNS(2) = 0x00010000; /* ALE(A16) */ \ + REG_GPIO_PXSELC(2) = 0x00010000; \ + REG_GPIO_PXPES(2) = 0x00010000; \ + } \ + REG_GPIO_PXFUNS(2) = 0x00200000 << ((n)-1); /* CSn */ \ + REG_GPIO_PXSELC(2) = 0x00200000 << ((n)-1); \ + REG_GPIO_PXPES(2) = 0x00200000 << ((n)-1); \ + \ + REG_GPIO_PXFUNS(1) = 0x00080000; /* RDWE#/BUFD# */ \ + REG_GPIO_PXSELC(1) = 0x00080000; \ + REG_GPIO_PXPES(1) = 0x00080000; \ + REG_GPIO_PXFUNS(2) = 0x30000000; /* FRE#, FWE# */ \ + REG_GPIO_PXSELC(2) = 0x30000000; \ + REG_GPIO_PXPES(2) = 0x30000000; \ + REG_GPIO_PXFUNC(2) = 0x08000000; /* FRB#(input) */ \ + REG_GPIO_PXSELC(2) = 0x08000000; \ + REG_GPIO_PXDIRC(2) = 0x08000000; \ + REG_GPIO_PXPES(2) = 0x08000000; \ +} while (0) + + +/* + * CS4#, RD#, WR#, WAIT#, A0 ~ A22, D0 ~ D7 + * @n: chip select number(1 ~ 4) + */ +#define __gpio_as_nor_8bit(n) \ +do { \ + if (is_normal_order()) { \ + /* 32/16-bit data normal order */ \ + REG_GPIO_PXFUNS(0) = 0x000000ff; \ + REG_GPIO_PXSELC(0) = 0x000000ff; \ + REG_GPIO_PXPES(0) = 0x000000ff; \ + } else { \ + /* 16-bit data special order */ \ + REG_GPIO_PXFUNS(0) = 0x0000ff00; \ + REG_GPIO_PXSELC(0) = 0x0000ff00; \ + REG_GPIO_PXPES(0) = 0x0000ff00; \ + } \ + REG_GPIO_PXFUNS(2) = 0x00200000 << ((n)-1); /* CSn */ \ + REG_GPIO_PXSELC(2) = 0x00200000 << ((n)-1); \ + REG_GPIO_PXPES(2) = 0x00200000 << ((n)-1); \ + \ + REG_GPIO_PXFUNS(1) = 0x0000ffff; /* A0~A15 */ \ + REG_GPIO_PXSELC(1) = 0x0000ffff; \ + REG_GPIO_PXPES(1) = 0x0000ffff; \ + REG_GPIO_PXFUNS(2) = 0x06110007; /* RD#, WR#, WAIT#, A20~A22 */ \ + REG_GPIO_PXSELC(2) = 0x06110007; \ + REG_GPIO_PXPES(2) = 0x06110007; \ + REG_GPIO_PXFUNS(2) = 0x000e0000; /* A17~A19 */ \ + REG_GPIO_PXSELS(2) = 0x000e0000; \ + REG_GPIO_PXPES(2) = 0x000e0000; \ +} while (0) + +/* + * CS4#, RD#, WR#, WAIT#, A0 ~ A22, D0 ~ D15 + * @n: chip select number(1 ~ 4) + */ +#define __gpio_as_nor_16bit(n) \ +do { \ + if (is_normal_order()) { \ + /* 32/16-bit data normal order */ \ + REG_GPIO_PXFUNS(0) = 0x0000ffff; \ + REG_GPIO_PXSELC(0) = 0x0000ffff; \ + REG_GPIO_PXPES(0) = 0x0000ffff; \ + } else { \ + /* 16-bit data special order */ \ + REG_GPIO_PXFUNS(0) = 0x00ffff00; \ + REG_GPIO_PXSELC(0) = 0x00ffff00; \ + REG_GPIO_PXPES(0) = 0x00ffff00; \ + } \ + REG_GPIO_PXFUNS(2) = 0x00200000 << ((n)-1); /* CSn */ \ + REG_GPIO_PXSELC(2) = 0x00200000 << ((n)-1); \ + REG_GPIO_PXPES(2) = 0x00200000 << ((n)-1); \ + \ + REG_GPIO_PXFUNS(1) = 0x0000ffff; /* A0~A15 */ \ + REG_GPIO_PXSELC(1) = 0x0000ffff; \ + REG_GPIO_PXPES(1) = 0x0000ffff; \ + REG_GPIO_PXFUNS(2) = 0x06110007; /* RD#, WR#, WAIT#, A20~A22 */ \ + REG_GPIO_PXSELC(2) = 0x06110007; \ + REG_GPIO_PXPES(2) = 0x06110007; \ + REG_GPIO_PXFUNS(2) = 0x000e0000; /* A17~A19 */ \ + REG_GPIO_PXSELS(2) = 0x000e0000; \ + REG_GPIO_PXPES(2) = 0x000e0000; \ +} while (0) + +/* + * UART0_TxD, UART0_RxD + */ +#define __gpio_as_uart0() \ +do { \ + REG_GPIO_PXFUNS(5) = 0x00000030; \ + REG_GPIO_PXSELS(5) = 0x00000030; \ + REG_GPIO_PXPES(5) = 0x00000030; \ +} while (0) + +/* + * UART0_TxD, UART0_RxD, UART0_CTS, UART0_RTS + */ +#define __gpio_as_uart0_ctsrts() \ +do { \ + REG_GPIO_PXFUNS(5) = 0x000000f0; \ + REG_GPIO_PXSELS(5) = 0x000000f0; \ + REG_GPIO_PXPES(5) = 0x000000f0; \ +} while (0) + +/* + * UART1_TxD, UART1_RxD + */ +#define __gpio_as_uart1() \ +do { \ + REG_GPIO_PXFUNS(4) = 0x00030000; \ + REG_GPIO_PXSELC(4) = 0x00030000; \ + REG_GPIO_PXPES(4) = 0x00030000; \ +} while (0) + +/* + * UART1_TxD, UART1_RxD, UART1_CTS, UART1_RTS + */ +#define __gpio_as_uart1_ctsrts() \ +do { \ + REG_GPIO_PXFUNS(4) = 0x000f0000; \ + REG_GPIO_PXSELC(4) = 0x000f0000; \ + REG_GPIO_PXPES(4) = 0x000f0000; \ +} while (0) + +/* + * UART2_TxD, UART2_RxD + */ +#define __gpio_as_uart2() \ +do { \ + REG_GPIO_PXFUNS(4) = 0x0c000000; \ + REG_GPIO_PXSELS(4) = 0x0c000000; \ + REG_GPIO_PXPES(4) = 0x0c000000; \ +} while (0) + +/* + * UART3_TxD, UART3_RxD + */ +#define __gpio_as_uart3() \ +do { \ + REG_GPIO_PXFUNS(5) = 0x00030000; \ + REG_GPIO_PXSELC(5) = 0x00030000; \ + REG_GPIO_PXPES(5) = 0x00030000; \ +} while (0) + +/* + * UART3_TxD, UART3_RxD, UART3_CTS, UART3_RTS + */ +#define __gpio_as_uart3_ctsrts() \ +do { \ + REG_GPIO_PXFUNS(5) = 0x000f0000; \ + REG_GPIO_PXSELC(5) = 0x000f0000; \ + REG_GPIO_PXPES(5) = 0x000f0000; \ +} while (0) + +/* + * TSCLK, TSSTR, TSFRM, TSFAIL, TSDI0~7 + */ +#define __gpio_as_tssi() \ +do { \ + REG_GPIO_PXFUNS(2) = 0x0000ff00; \ + REG_GPIO_PXSELS(2) = 0x0000ff00; \ + REG_GPIO_PXPES(2) = 0x0000ff00; \ + REG_GPIO_PXFUNS(5) = 0x00f00000; \ + REG_GPIO_PXSELC(5) = 0x00f00000; \ + REG_GPIO_PXPES(5) = 0x00f00000; \ +} while (0) + +/* + * LCD_D0~LCD_D7, LCD_PCLK, LCD_HSYNC, LCD_VSYNC, LCD_DE + */ +#define __gpio_as_lcd_8bit() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x003c00ff; \ + REG_GPIO_PXSELC(3) = 0x003c00ff; \ + REG_GPIO_PXPES(3) = 0x003c00ff; \ +} while (0) + +/* + * LCD_D0~LCD_D15, LCD_PCLK, LCD_HSYNC, LCD_VSYNC, LCD_DE + */ +#define __gpio_as_lcd_16bit() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x003cffff; \ + REG_GPIO_PXSELC(3) = 0x003cffff; \ + REG_GPIO_PXPES(3) = 0x003cffff; \ +} while (0) + +/* + * LCD_D0~LCD_D17, LCD_PCLK, LCD_HSYNC, LCD_VSYNC, LCD_DE + */ +#define __gpio_as_lcd_18bit() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x003fffff; \ + REG_GPIO_PXSELC(3) = 0x003fffff; \ + REG_GPIO_PXPES(3) = 0x003fffff; \ +} while (0) + +/* + * LCD_D0~LCD_D17, LCD_D_R1, LCD_D_G0, LCD_D_G1, LCD_D_B1, + * LCD_D_R0, LCD_D_B0, LCD_PCLK, LCD_HSYNC, LCD_VSYNC, LCD_DE + */ +#define __gpio_as_lcd_24bit() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x003fffff; \ + REG_GPIO_PXSELC(3) = 0x003fffff; \ + REG_GPIO_PXPES(3) = 0x003fffff; \ + REG_GPIO_PXFUNS(3) = 0x03c00000; \ + REG_GPIO_PXSELS(3) = 0x03c00000; \ + REG_GPIO_PXPES(3) = 0x03c00000; \ + REG_GPIO_PXFUNS(5) = 0x000c0000; \ + REG_GPIO_PXSELS(5) = 0x000c0000; \ + REG_GPIO_PXPES(5) = 0x000c0000; \ +} while (0) + +/* + * SLCD_DAT0~7, SLCD_CLK, SLCD_RS, SLCD_CS + */ +#define __gpio_as_lcd_smart_pal_8bit() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x001c00ff; \ + REG_GPIO_PXSELC(3) = 0x001c00ff; \ + REG_GPIO_PXPES(3) = 0x001c00ff; \ +} while (0) + +/* + * SLCD_DAT0~15, SLCD_CLK, SLCD_RS, SLCD_CS + */ +#define __gpio_as_lcd_smart_pal_15bit() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x001cffff; \ + REG_GPIO_PXSELC(3) = 0x001cffff; \ + REG_GPIO_PXPES(3) = 0x001cffff; \ +} while (0) + +/* + * SLCD_DAT0~17, SLCD_CLK, SLCD_RS, SLCD_CS + */ +#define __gpio_as_lcd_smart_pal_17bit() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x001fffff; \ + REG_GPIO_PXSELC(3) = 0x001fffff; \ + REG_GPIO_PXPES(3) = 0x001fffff; \ +} while (0) + +/* + * SLCD_DAT15, SLCD_CLK, SLCD_RS, SLCD_CS + */ +#define __gpio_as_lcd_smart_serial() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x001c8000; \ + REG_GPIO_PXSELC(3) = 0x001c8000; \ + REG_GPIO_PXPES(3) = 0x001c8000; \ +} while (0) + +/* + * LCD_CLS, LCD_SPL, LCD_PS, LCD_REV + */ +#define __gpio_as_lcd_special() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x03C00000; \ + REG_GPIO_PXSELC(3) = 0x03C00000; \ + REG_GPIO_PXPES(3) = 0x03C00000; \ +} while (0) + +/* + * CIM_D0~CIM_D7, CIM_MCLK, CIM_PCLK, CIM_VSYNC, CIM_HSYNC + */ +#define __gpio_as_cim() \ +do { \ + REG_GPIO_PXFUNS(4) = 0x00000fff; \ + REG_GPIO_PXSELC(4) = 0x00000fff; \ + REG_GPIO_PXPES(4) = 0x00000fff; \ +} while (0) + +/* + * SDATO, SDATI, BCLK, SYNC, SCLK_RSTN(gpio sepc) or + * SDATA_OUT, SDATA_IN, BIT_CLK, SYNC, SCLK_RESET(aic spec) + */ +#define __gpio_as_aic() \ +do { \ + REG_GPIO_PXFUNS(4) = 0x0c000000; \ + REG_GPIO_PXSELS(4) = 0x0c000000; \ + REG_GPIO_PXPES(4) = 0x0c000000; \ + REG_GPIO_PXFUNS(4) = 0x00e00000; \ + REG_GPIO_PXSELC(4) = 0x00e00000; \ + REG_GPIO_PXPES(4) = 0x00e00000; \ +} while (0) + +/* + * PCM_DIN, PCM_DOUT, PCM_CLK, PCM_SYN +*/ +#define __gpio_as_pcm() \ +do { \ + REG_GPIO_PXFUNS(4) = 0x003c0000; \ + REG_GPIO_PXSELS(4) = 0x003c0000; \ + REG_GPIO_PXPES(4) = 0x003c0000; \ +} while (0) + +/* + * OWI +*/ +#define __gpio_as_owi() \ +do { \ + REG_GPIO_PXFUNS(4) = 0x02000000; \ + REG_GPIO_PXSELS(4) = 0x02000000; \ + REG_GPIO_PXPES(4) = 0x02000000; \ +} while (0) + +/* + * MSC0_CMD, MSC0_CLK, MSC0_D0 ~ MSC0_D3 + */ +#define __gpio_as_msc0_4bit() \ +do { \ + REG_GPIO_PXFUNS(5) = 0x0000030f; \ + REG_GPIO_PXSELC(5) = 0x0000030f; \ + REG_GPIO_PXPES(5) = 0x0000030f; \ +} while (0) + +/* + * MSC0_CMD, MSC0_CLK, MSC0_D0 ~ MSC0_D7 + */ +#define __gpio_as_msc0_8bit() \ +do { \ + REG_GPIO_PXFUNS(5) = 0x000003ff; \ + REG_GPIO_PXSELC(5) = 0x000003ff; \ + REG_GPIO_PXPES(5) = 0x000003ff; \ +} while (0) + +/* + * MSC1_CMD, MSC1_CLK, MSC1_D0 ~ MSC1_D3 + */ +#define __gpio_as_msc1_4bit() \ +do { \ + REG_GPIO_PXFUNS(5) = 0x0000fc00; \ + REG_GPIO_PXSELC(5) = 0x0000fc00; \ + REG_GPIO_PXPES(5) = 0x0000fc00; \ +} while (0) + +#define __gpio_as_msc __gpio_as_msc0_8bit /* default as msc0 8bit */ +#define __gpio_as_msc0 __gpio_as_msc0_8bit /* msc0 default as 8bit */ +#define __gpio_as_msc1 __gpio_as_msc1_4bit /* msc1 only support 4bit */ + +/* + * SSI0_CE0, SSI0_CE1#_GPC, SSI0_CE2, SSI0_CLK, SSI0_DT, SSI0_DR + */ +#define __gpio_as_ssi0() \ +do { \ + REG_GPIO_PXFUNS(1) = 0xfc000000; \ + REG_GPIO_PXSELC(1) = 0xfc000000; \ + REG_GPIO_PXPES(1) = 0xfc000000; \ +} while (0) + +/* + * SSI1_CE0, SSI1_CE1, SSI1_CLK, SSI1_DT, SSI1_DR + */ +#define __gpio_as_ssi1() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x7c000000; \ + REG_GPIO_PXSELC(3) = 0x7c000000; \ + REG_GPIO_PXPES(3) = 0x7c000000; \ +} while (0) + +/* n = 0(SSI0), 1(SSI1) */ +#define __gpio_as_ssi(n) __gpio_as_ssi##n() + +/* + * I2C_SCK, I2C_SDA + */ +#define __gpio_as_i2c() \ +do { \ + REG_GPIO_PXFUNS(4) = 0x00003000; \ + REG_GPIO_PXSELC(4) = 0x00003000; \ + REG_GPIO_PXPES(4) = 0x00003000; \ +} while (0) + +/* + * PWM0 + */ +#define __gpio_as_pwm0() \ +do { \ + REG_GPIO_PXFUNS(4) = 0x00100000; \ + REG_GPIO_PXSELC(4) = 0x00100000; \ + REG_GPIO_PXPES(4) = 0x00100000; \ +} while (0) + +/* + * PWM1 + */ +#define __gpio_as_pwm1() \ +do { \ + REG_GPIO_PXFUNS(4) = 0x00200000; \ + REG_GPIO_PXSELC(4) = 0x00200000; \ + REG_GPIO_PXPES(4) = 0x00200000; \ +} while (0) + +/* + * PWM2 + */ +#define __gpio_as_pwm2() \ +do { \ + REG_GPIO_PXFUNS(4) = 0x00400000; \ + REG_GPIO_PXSELC(4) = 0x00400000; \ + REG_GPIO_PXPES(4) = 0x00400000; \ +} while (0) + +/* + * PWM3 + */ +#define __gpio_as_pwm3() \ +do { \ + REG_GPIO_PXFUNS(4) = 0x00800000; \ + REG_GPIO_PXSELC(4) = 0x00800000; \ + REG_GPIO_PXPES(4) = 0x00800000; \ +} while (0) + +/* + * PWM4 + */ +#define __gpio_as_pwm4() \ +do { \ + REG_GPIO_PXFUNS(4) = 0x01000000; \ + REG_GPIO_PXSELC(4) = 0x01000000; \ + REG_GPIO_PXPES(4) = 0x01000000; \ +} while (0) + +/* + * PWM5 + */ +#define __gpio_as_pwm5() \ +do { \ + REG_GPIO_PXFUNS(4) = 0x02000000; \ + REG_GPIO_PXSELC(4) = 0x02000000; \ + REG_GPIO_PXPES(4) = 0x02000000; \ +} while (0) + +/* + * n = 0 ~ 5 + */ +#define __gpio_as_pwm(n) __gpio_as_pwm##n() + +/* + * DREQ + */ +#define __gpio_as_dreq() \ +do { \ + REG_GPIO_PXFUNS(5) = 0x00000004; \ + REG_GPIO_PXSELS(5) = 0x00000004; \ + REG_GPIO_PXPES(5) = 0x00000004; \ +} while (0) + +/* + * DACK + */ +#define __gpio_as_dack() \ +do { \ + REG_GPIO_PXFUNS(5) = 0x00000008; \ + REG_GPIO_PXSELS(5) = 0x00000008; \ + REG_GPIO_PXPES(5) = 0x00000008; \ +} while (0) + +/* + * GPIO or Interrupt Mode + */ +#define __gpio_get_port(p) (REG_GPIO_PXPIN(p)) + +#define __gpio_port_as_output(p, o) \ +do { \ + REG_GPIO_PXFUNC(p) = (1 << (o)); \ + REG_GPIO_PXSELC(p) = (1 << (o)); \ + REG_GPIO_PXDIRS(p) = (1 << (o)); \ +} while (0) + +#define __gpio_port_as_input(p, o) \ +do { \ + REG_GPIO_PXFUNC(p) = (1 << (o)); \ + REG_GPIO_PXSELC(p) = (1 << (o)); \ + REG_GPIO_PXDIRC(p) = (1 << (o)); \ +} while (0) + +#define __gpio_as_output(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + __gpio_port_as_output(p, o); \ +} while (0) + +#define __gpio_as_input(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + __gpio_port_as_input(p, o); \ +} while (0) + +#define __gpio_set_pin(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXDATS(p) = (1 << o); \ +} while (0) + +#define __gpio_clear_pin(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXDATC(p) = (1 << o); \ +} while (0) + +#define __gpio_get_pin(n) \ +({ \ + unsigned int p, o, v; \ + p = (n) / 32; \ + o = (n) % 32; \ + if (__gpio_get_port(p) & (1 << o)) \ + v = 1; \ + else \ + v = 0; \ + v; \ +}) + +#define __gpio_as_irq_high_level(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXIMS(p) = (1 << o); \ + REG_GPIO_PXTRGC(p) = (1 << o); \ + REG_GPIO_PXFUNC(p) = (1 << o); \ + REG_GPIO_PXSELS(p) = (1 << o); \ + REG_GPIO_PXDIRS(p) = (1 << o); \ + REG_GPIO_PXFLGC(p) = (1 << o); \ + REG_GPIO_PXIMC(p) = (1 << o); \ +} while (0) + +#define __gpio_as_irq_low_level(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXIMS(p) = (1 << o); \ + REG_GPIO_PXTRGC(p) = (1 << o); \ + REG_GPIO_PXFUNC(p) = (1 << o); \ + REG_GPIO_PXSELS(p) = (1 << o); \ + REG_GPIO_PXDIRC(p) = (1 << o); \ + REG_GPIO_PXFLGC(p) = (1 << o); \ + REG_GPIO_PXIMC(p) = (1 << o); \ +} while (0) + +#define __gpio_as_irq_rise_edge(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXIMS(p) = (1 << o); \ + REG_GPIO_PXTRGS(p) = (1 << o); \ + REG_GPIO_PXFUNC(p) = (1 << o); \ + REG_GPIO_PXSELS(p) = (1 << o); \ + REG_GPIO_PXDIRS(p) = (1 << o); \ + REG_GPIO_PXFLGC(p) = (1 << o); \ + REG_GPIO_PXIMC(p) = (1 << o); \ +} while (0) + +#define __gpio_as_irq_fall_edge(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXIMS(p) = (1 << o); \ + REG_GPIO_PXTRGS(p) = (1 << o); \ + REG_GPIO_PXFUNC(p) = (1 << o); \ + REG_GPIO_PXSELS(p) = (1 << o); \ + REG_GPIO_PXDIRC(p) = (1 << o); \ + REG_GPIO_PXFLGC(p) = (1 << o); \ + REG_GPIO_PXIMC(p) = (1 << o); \ +} while (0) + +#define __gpio_mask_irq(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXIMS(p) = (1 << o); \ +} while (0) + +#define __gpio_unmask_irq(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXIMC(p) = (1 << o); \ +} while (0) + +#define __gpio_ack_irq(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXFLGC(p) = (1 << o); \ +} while (0) + +#define __gpio_get_irq() \ +({ \ + unsigned int p, i, tmp, v = 0; \ + for (p = 3; p >= 0; p--) { \ + tmp = REG_GPIO_PXFLG(p); \ + for (i = 0; i < 32; i++) \ + if (tmp & (1 << i)) \ + v = (32*p + i); \ + } \ + v; \ +}) + +#define __gpio_group_irq(n) \ +({ \ + register int tmp, i; \ + tmp = REG_GPIO_PXFLG((n)); \ + for (i=31;i>=0;i--) \ + if (tmp & (1 << i)) \ + break; \ + i; \ +}) + +#define __gpio_enable_pull(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXPEC(p) = (1 << o); \ +} while (0) + +#define __gpio_disable_pull(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXPES(p) = (1 << o); \ +} while (0) + + +/*************************************************************************** + * CPM + ***************************************************************************/ +#define __cpm_get_pllm() \ + ((REG_CPM_CPPCR & CPM_CPPCR_PLLM_MASK) >> CPM_CPPCR_PLLM_BIT) +#define __cpm_get_plln() \ + ((REG_CPM_CPPCR & CPM_CPPCR_PLLN_MASK) >> CPM_CPPCR_PLLN_BIT) +#define __cpm_get_pllod() \ + ((REG_CPM_CPPCR & CPM_CPPCR_PLLOD_MASK) >> CPM_CPPCR_PLLOD_BIT) + +#define __cpm_get_cdiv() \ + ((REG_CPM_CPCCR & CPM_CPCCR_CDIV_MASK) >> CPM_CPCCR_CDIV_BIT) +#define __cpm_get_hdiv() \ + ((REG_CPM_CPCCR & CPM_CPCCR_HDIV_MASK) >> CPM_CPCCR_HDIV_BIT) +#define __cpm_get_pdiv() \ + ((REG_CPM_CPCCR & CPM_CPCCR_PDIV_MASK) >> CPM_CPCCR_PDIV_BIT) +#define __cpm_get_mdiv() \ + ((REG_CPM_CPCCR & CPM_CPCCR_MDIV_MASK) >> CPM_CPCCR_MDIV_BIT) +#define __cpm_get_ldiv() \ + ((REG_CPM_CPCCR & CPM_CPCCR_LDIV_MASK) >> CPM_CPCCR_LDIV_BIT) +#define __cpm_get_udiv() \ + ((REG_CPM_CPCCR & CPM_CPCCR_UDIV_MASK) >> CPM_CPCCR_UDIV_BIT) +#define __cpm_get_i2sdiv() \ + ((REG_CPM_I2SCDR & CPM_I2SCDR_I2SDIV_MASK) >> CPM_I2SCDR_I2SDIV_BIT) +#define __cpm_get_pixdiv() \ + ((REG_CPM_LPCDR & CPM_LPCDR_PIXDIV_MASK) >> CPM_LPCDR_PIXDIV_BIT) +#define __cpm_get_mscdiv(n) \ + ((REG_CPM_MSCCDR(n) & CPM_MSCCDR_MSCDIV_MASK) >> CPM_MSCCDR_MSCDIV_BIT) +#define __cpm_get_uhcdiv() \ + ((REG_CPM_UHCCDR & CPM_UHCCDR_UHCDIV_MASK) >> CPM_UHCCDR_UHCDIV_BIT) +#define __cpm_get_ssidiv() \ + ((REG_CPM_SSICCDR & CPM_SSICDR_SSICDIV_MASK) >> CPM_SSICDR_SSIDIV_BIT) +#define __cpm_get_pcmdiv(v) \ + ((REG_CPM_PCMCDR & CPM_PCMCDR_PCMCD_MASK) >> CPM_PCMCDR_PCMCD_BIT) + +#define __cpm_set_cdiv(v) \ + (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_CDIV_MASK) | ((v) << (CPM_CPCCR_CDIV_BIT))) +#define __cpm_set_hdiv(v) \ + (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_HDIV_MASK) | ((v) << (CPM_CPCCR_HDIV_BIT))) +#define __cpm_set_pdiv(v) \ + (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_PDIV_MASK) | ((v) << (CPM_CPCCR_PDIV_BIT))) +#define __cpm_set_mdiv(v) \ + (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_MDIV_MASK) | ((v) << (CPM_CPCCR_MDIV_BIT))) +#define __cpm_set_ldiv(v) \ + (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_LDIV_MASK) | ((v) << (CPM_CPCCR_LDIV_BIT))) +#define __cpm_set_udiv(v) \ + (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_UDIV_MASK) | ((v) << (CPM_CPCCR_UDIV_BIT))) +#define __cpm_set_i2sdiv(v) \ + (REG_CPM_I2SCDR = (REG_CPM_I2SCDR & ~CPM_I2SCDR_I2SDIV_MASK) | ((v) << (CPM_I2SCDR_I2SDIV_BIT))) +#define __cpm_set_pixdiv(v) \ + (REG_CPM_LPCDR = (REG_CPM_LPCDR & ~CPM_LPCDR_PIXDIV_MASK) | ((v) << (CPM_LPCDR_PIXDIV_BIT))) +#define __cpm_set_mscdiv(n, v) \ + (REG_CPM_MSCCDR(n) = (REG_CPM_MSCCDR(n) & ~CPM_MSCCDR_MSCDIV_MASK) | ((v) << (CPM_MSCCDR_MSCDIV_BIT))) +#define __cpm_set_uhcdiv(v) \ + (REG_CPM_UHCCDR = (REG_CPM_UHCCDR & ~CPM_UHCCDR_UHCDIV_MASK) | ((v) << (CPM_UHCCDR_UHCDIV_BIT))) +#define __cpm_set_ssidiv(v) \ + (REG_CPM_SSICDR = (REG_CPM_SSICDR & ~CPM_SSICDR_SSIDIV_MASK) | ((v) << (CPM_SSICDR_SSIDIV_BIT))) +#define __cpm_set_pcmdiv(v) \ + (REG_CPM_PCMCDR = (REG_CPM_PCMCDR & ~CPM_PCMCDR_PCMCD_MASK) | ((v) << (CPM_PCMCDR_PCMCD_BIT))) + +#define __cpm_select_pcmclk_pll() (REG_CPM_PCMCDR |= CPM_PCMCDR_PCMS) +#define __cpm_select_pcmclk_exclk() (REG_CPM_PCMCDR &= ~CPM_PCMCDR_PCMS) +#define __cpm_select_pixclk_ext() (REG_CPM_LPCDR |= CPM_LPCDR_LPCS) +#define __cpm_select_pixclk_pll() (REG_CPM_LPCDR &= ~CPM_LPCDR_LPCS) +#define __cpm_select_tveclk_exclk() (REG_CPM_LPCDR |= CPM_CPCCR_LSCS) +#define __cpm_select_tveclk_pll() (REG_CPM_LPCDR &= ~CPM_LPCDR_LSCS) +#define __cpm_select_pixclk_lcd() (REG_CPM_LPCDR &= ~CPM_LPCDR_LTCS) +#define __cpm_select_pixclk_tve() (REG_CPM_LPCDR |= CPM_LPCDR_LTCS) +#define __cpm_select_i2sclk_exclk() (REG_CPM_CPCCR &= ~CPM_CPCCR_I2CS) +#define __cpm_select_i2sclk_pll() (REG_CPM_CPCCR |= CPM_CPCCR_I2CS) +#define __cpm_select_usbclk_exclk() (REG_CPM_CPCCR &= ~CPM_CPCCR_UCS) +#define __cpm_select_usbclk_pll() (REG_CPM_CPCCR |= CPM_CPCCR_UCS) + +#define __cpm_enable_cko() +#define __cpm_exclk_direct() (REG_CPM_CPCCR &= ~CPM_CPCCR_ECS) +#define __cpm_exclk_div2() (REG_CPM_CPCCR |= CPM_CPCCR_ECS) +#define __cpm_enable_pll_change() (REG_CPM_CPCCR |= CPM_CPCCR_CE) +#define __cpm_pllout_direct() (REG_CPM_CPCCR |= CPM_CPCCR_PCS) +#define __cpm_pllout_div2() (REG_CPM_CPCCR &= ~CPM_CPCCR_PCS) +#define __cpm_pll_enable() (REG_CPM_CPPCR |= CPM_CPPCR_PLLEN) + +#define __cpm_pll_is_off() (REG_CPM_CPPSR & CPM_CPPSR_PLLOFF) +#define __cpm_pll_is_on() (REG_CPM_CPPSR & CPM_CPPSR_PLLON) +#define __cpm_pll_bypass() (REG_CPM_CPPSR |= CPM_CPPSR_PLLBP) + +#define __cpm_get_cclk_doze_duty() \ + ((REG_CPM_LCR & CPM_LCR_DOZE_DUTY_MASK) >> CPM_LCR_DOZE_DUTY_BIT) +#define __cpm_set_cclk_doze_duty(v) \ + (REG_CPM_LCR = (REG_CPM_LCR & ~CPM_LCR_DOZE_DUTY_MASK) | ((v) << (CPM_LCR_DOZE_DUTY_BIT))) + +#define __cpm_doze_mode() (REG_CPM_LCR |= CPM_LCR_DOZE_ON) +#define __cpm_idle_mode() \ + (REG_CPM_LCR = (REG_CPM_LCR & ~CPM_LCR_LPM_MASK) | CPM_LCR_LPM_IDLE) +#define __cpm_sleep_mode() \ + (REG_CPM_LCR = (REG_CPM_LCR & ~CPM_LCR_LPM_MASK) | CPM_LCR_LPM_SLEEP) + +#define __cpm_stop_all() (REG_CPM_CLKGR = 0x1fffffff) +#define __cpm_stop_cimram() (REG_CPM_CLKGR |= CPM_CLKGR_CIMRAM) +#define __cpm_stop_idct() (REG_CPM_CLKGR |= CPM_CLKGR_IDCT) +#define __cpm_stop_db() (REG_CPM_CLKGR |= CPM_CLKGR_DB) +#define __cpm_stop_me() (REG_CPM_CLKGR |= CPM_CLKGR_ME) +#define __cpm_stop_mc() (REG_CPM_CLKGR |= CPM_CLKGR_MC) +#define __cpm_stop_tve() (REG_CPM_CLKGR |= CPM_CLKGR_TVE) +#define __cpm_stop_tssi() (REG_CPM_CLKGR |= CPM_CLKGR_TSSI) +#define __cpm_stop_owi() (REG_CPM_CLKGR |= CPM_CLKGR_OWI) +#define __cpm_stop_pcm() (REG_CPM_CLKGR |= CPM_CLKGR_PCM) +#define __cpm_stop_uart3() (REG_CPM_CLKGR |= CPM_CLKGR_UART3) +#define __cpm_stop_uart2() (REG_CPM_CLKGR |= CPM_CLKGR_UART2) +#define __cpm_stop_uart1() (REG_CPM_CLKGR |= CPM_CLKGR_UART1) +#define __cpm_stop_uhc() (REG_CPM_CLKGR |= CPM_CLKGR_UHC) +#define __cpm_stop_ipu() (REG_CPM_CLKGR |= CPM_CLKGR_IPU) +#define __cpm_stop_dmac() (REG_CPM_CLKGR |= CPM_CLKGR_DMAC) +#define __cpm_stop_udc() (REG_CPM_CLKGR |= CPM_CLKGR_UDC) +#define __cpm_stop_lcd() (REG_CPM_CLKGR |= CPM_CLKGR_LCD) +#define __cpm_stop_cim() (REG_CPM_CLKGR |= CPM_CLKGR_CIM) +#define __cpm_stop_sadc() (REG_CPM_CLKGR |= CPM_CLKGR_SADC) +#define __cpm_stop_msc(n) (REG_CPM_CLKGR |= CPM_CLKGR_MSC##n) +#define __cpm_stop_aic1() (REG_CPM_CLKGR |= CPM_CLKGR_AIC1) +#define __cpm_stop_aic2() (REG_CPM_CLKGR |= CPM_CLKGR_AIC2) +#define __cpm_stop_ssi(n) (REG_CPM_CLKGR |= CPM_CLKGR_SSI##n) +#define __cpm_stop_i2c() (REG_CPM_CLKGR |= CPM_CLKGR_I2C) +#define __cpm_stop_rtc() (REG_CPM_CLKGR |= CPM_CLKGR_RTC) +#define __cpm_stop_tcu() (REG_CPM_CLKGR |= CPM_CLKGR_TCU) +#define __cpm_stop_uart0() (REG_CPM_CLKGR |= CPM_CLKGR_UART0) + +#define __cpm_start_all() (REG_CPM_CLKGR = 0x0) +#define __cpm_start_cimram() (REG_CPM_CLKGR &= ~CPM_CLKGR_CIMRAM) +#define __cpm_start_idct() (REG_CPM_CLKGR &= ~CPM_CLKGR_IDCT) +#define __cpm_start_db() (REG_CPM_CLKGR &= ~CPM_CLKGR_DB) +#define __cpm_start_me() (REG_CPM_CLKGR &= ~CPM_CLKGR_ME) +#define __cpm_start_mc() (REG_CPM_CLKGR &= ~CPM_CLKGR_MC) +#define __cpm_start_tve() (REG_CPM_CLKGR &= ~CPM_CLKGR_TVE) +#define __cpm_start_tssi() (REG_CPM_CLKGR &= ~CPM_CLKGR_TSSI) +#define __cpm_start_owi() (REG_CPM_CLKGR &= ~CPM_CLKGR_OWI) +#define __cpm_start_pcm() (REG_CPM_CLKGR &= ~CPM_CLKGR_PCM) +#define __cpm_start_uart3() (REG_CPM_CLKGR &= ~CPM_CLKGR_UART3) +#define __cpm_start_uart2() (REG_CPM_CLKGR &= ~CPM_CLKGR_UART2) +#define __cpm_start_uart1() (REG_CPM_CLKGR &= ~CPM_CLKGR_UART1) +#define __cpm_start_uhc() (REG_CPM_CLKGR &= ~CPM_CLKGR_UHC) +#define __cpm_start_ipu() (REG_CPM_CLKGR &= ~CPM_CLKGR_IPU) +#define __cpm_start_dmac() (REG_CPM_CLKGR &= ~CPM_CLKGR_DMAC) +#define __cpm_start_udc() (REG_CPM_CLKGR &= ~CPM_CLKGR_UDC) +#define __cpm_start_lcd() (REG_CPM_CLKGR &= ~CPM_CLKGR_LCD) +#define __cpm_start_cim() (REG_CPM_CLKGR &= ~CPM_CLKGR_CIM) +#define __cpm_start_sadc() (REG_CPM_CLKGR &= ~CPM_CLKGR_SADC) +#define __cpm_start_msc(n) (REG_CPM_CLKGR &= ~CPM_CLKGR_MSC##n) +#define __cpm_start_aic1() (REG_CPM_CLKGR &= ~CPM_CLKGR_AIC1) +#define __cpm_start_aic2() (REG_CPM_CLKGR &= ~CPM_CLKGR_AIC2) +#define __cpm_start_ssi(n) (REG_CPM_CLKGR &= ~CPM_CLKGR_SSI##n) +#define __cpm_start_i2c() (REG_CPM_CLKGR &= ~CPM_CLKGR_I2C) +#define __cpm_start_rtc() (REG_CPM_CLKGR &= ~CPM_CLKGR_RTC) +#define __cpm_start_tcu() (REG_CPM_CLKGR &= ~CPM_CLKGR_TCU) +#define __cpm_start_uart0() (REG_CPM_CLKGR &= ~CPM_CLKGR_UART0) + +#define __cpm_get_o1st() \ + ((REG_CPM_OPCR & CPM_OPCR_O1ST_MASK) >> CPM_OPCR_O1ST_BIT) +#define __cpm_set_o1st(v) \ + (REG_CPM_OPCR = (REG_CPM_OPCR & ~CPM_OPCR_O1ST_MASK) | ((v) << (CPM_OPCR_O1ST_BIT))) +#define __cpm_enable_uhcphy() (REG_CPM_OPCR &= ~CPM_OPCR_UHCPHY_DISABLE) +#define __cpm_suspend_uhcphy() (REG_CPM_OPCR |= CPM_OPCR_UHCPHY_DISABLE) +#define __cpm_enable_udcphy() (REG_CPM_OPCR |= CPM_OPCR_UDCPHY_ENABLE) +#define __cpm_suspend_udcphy() (REG_CPM_OPCR &= ~CPM_OPCR_UDCPHY_ENABLE) +#define __cpm_enable_osc_in_sleep() (REG_CPM_OPCR |= CPM_OPCR_OSC_ENABLE) +#define __cpm_disable_osc_in_sleep() (REG_CPM_OPCR &= ~CPM_OPCR_OSC_ENABLE) +#define __cpm_select_rtcclk_rtc() (REG_CPM_OPCR |= CPM_OPCR_ERCS) +#define __cpm_select_rtcclk_exclk() (REG_CPM_OPCR &= ~CPM_OPCR_ERCS) + + +/*************************************************************************** + * TCU + ***************************************************************************/ +// where 'n' is the TCU channel +#define __tcu_select_extalclk(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~(TCU_TCSR_EXT_EN | TCU_TCSR_RTC_EN | TCU_TCSR_PCK_EN)) | TCU_TCSR_EXT_EN) +#define __tcu_select_rtcclk(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~(TCU_TCSR_EXT_EN | TCU_TCSR_RTC_EN | TCU_TCSR_PCK_EN)) | TCU_TCSR_RTC_EN) +#define __tcu_select_pclk(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~(TCU_TCSR_EXT_EN | TCU_TCSR_RTC_EN | TCU_TCSR_PCK_EN)) | TCU_TCSR_PCK_EN) +#define __tcu_disable_pclk(n) \ + REG_TCU_TCSR(n) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PCK_EN); +#define __tcu_select_clk_div1(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE1) +#define __tcu_select_clk_div4(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE4) +#define __tcu_select_clk_div16(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE16) +#define __tcu_select_clk_div64(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE64) +#define __tcu_select_clk_div256(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE256) +#define __tcu_select_clk_div1024(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE1024) + +#define __tcu_enable_pwm_output(n) (REG_TCU_TCSR((n)) |= TCU_TCSR_PWM_EN) +#define __tcu_disable_pwm_output(n) (REG_TCU_TCSR((n)) &= ~TCU_TCSR_PWM_EN) + +#define __tcu_init_pwm_output_high(n) (REG_TCU_TCSR((n)) |= TCU_TCSR_PWM_INITL_HIGH) +#define __tcu_init_pwm_output_low(n) (REG_TCU_TCSR((n)) &= ~TCU_TCSR_PWM_INITL_HIGH) + +#define __tcu_set_pwm_output_shutdown_graceful(n) (REG_TCU_TCSR((n)) &= ~TCU_TCSR_PWM_SD) +#define __tcu_set_pwm_output_shutdown_abrupt(n) (REG_TCU_TCSR((n)) |= TCU_TCSR_PWM_SD) + +#define __tcu_clear_counter_to_zero(n) (REG_TCU_TCSR((n)) |= TCU_TCSR_CNT_CLRZ) + +#define __tcu_ost_enabled() (REG_TCU_TER & TCU_TER_OSTEN) +#define __tcu_enable_ost() (REG_TCU_TESR = TCU_TESR_OSTST) +#define __tcu_disable_ost() (REG_TCU_TECR = TCU_TECR_OSTCL) + +#define __tcu_counter_enabled(n) (REG_TCU_TER & (1 << (n))) +#define __tcu_start_counter(n) (REG_TCU_TESR |= (1 << (n))) +#define __tcu_stop_counter(n) (REG_TCU_TECR |= (1 << (n))) + +#define __tcu_half_match_flag(n) (REG_TCU_TFR & (1 << ((n) + 16))) +#define __tcu_full_match_flag(n) (REG_TCU_TFR & (1 << (n))) +#define __tcu_set_half_match_flag(n) (REG_TCU_TFSR = (1 << ((n) + 16))) +#define __tcu_set_full_match_flag(n) (REG_TCU_TFSR = (1 << (n))) +#define __tcu_clear_half_match_flag(n) (REG_TCU_TFCR = (1 << ((n) + 16))) +#define __tcu_clear_full_match_flag(n) (REG_TCU_TFCR = (1 << (n))) +#define __tcu_mask_half_match_irq(n) (REG_TCU_TMSR = (1 << ((n) + 16))) +#define __tcu_mask_full_match_irq(n) (REG_TCU_TMSR = (1 << (n))) +#define __tcu_unmask_half_match_irq(n) (REG_TCU_TMCR = (1 << ((n) + 16))) +#define __tcu_unmask_full_match_irq(n) (REG_TCU_TMCR = (1 << (n))) + +#define __tcu_ost_match_flag() (REG_TCU_TFR & TCU_TFR_OSTFLAG) +#define __tcu_set_ost_match_flag() (REG_TCU_TFSR = TCU_TFSR_OSTFST) +#define __tcu_clear_ost_match_flag() (REG_TCU_TFCR = TCU_TFCR_OSTFCL) +#define __tcu_ost_match_irq_masked() (REG_TCU_TMR & TCU_TMR_OSTMASK) +#define __tcu_mask_ost_match_irq() (REG_TCU_TMSR = TCU_TMSR_OSTMST) +#define __tcu_unmask_ost_match_irq() (REG_TCU_TMCR = TCU_TMCR_OSTMCL) + +#define __tcu_wdt_clock_stopped() (REG_TCU_TSR & TCU_TSSR_WDTSC) +#define __tcu_ost_clock_stopped() (REG_TCU_TSR & TCU_TSR_OST) +#define __tcu_timer_clock_stopped(n) (REG_TCU_TSR & (1 << (n))) + +#define __tcu_start_wdt_clock() (REG_TCU_TSCR = TCU_TSSR_WDTSC) +#define __tcu_start_ost_clock() (REG_TCU_TSCR = TCU_TSCR_OSTSC) +#define __tcu_start_timer_clock(n) (REG_TCU_TSCR = (1 << (n))) + +#define __tcu_stop_wdt_clock() (REG_TCU_TSSR = TCU_TSSR_WDTSC) +#define __tcu_stop_ost_clock() (REG_TCU_TSSR = TCU_TSSR_OSTSS) +#define __tcu_stop_timer_clock(n) (REG_TCU_TSSR = (1 << (n))) + +#define __tcu_get_count(n) (REG_TCU_TCNT((n))) +#define __tcu_set_count(n,v) (REG_TCU_TCNT((n)) = (v)) +#define __tcu_set_full_data(n,v) (REG_TCU_TDFR((n)) = (v)) +#define __tcu_set_half_data(n,v) (REG_TCU_TDHR((n)) = (v)) + +/* TCU2, counter 1, 2*/ +#define __tcu_read_real_value(n) (REG_TCU_TSTR & (1 << ((n) + 16))) +#define __tcu_read_false_value(n) (REG_TCU_TSTR & (1 << ((n) + 16))) +#define __tcu_counter_busy(n) (REG_TCU_TSTR & (1 << (n))) +#define __tcu_counter_ready(n) (REG_TCU_TSTR & (1 << (n))) + +#define __tcu_set_read_real_value(n) (REG_TCU_TSTSR = (1 << ((n) + 16))) +#define __tcu_set_read_false_value(n) (REG_TCU_TSTCR = (1 << ((n) + 16))) +#define __tcu_set_counter_busy(n) (REG_TCU_TSTSR = (1 << (n))) +#define __tcu_set_counter_ready(n) (REG_TCU_TSTCR = (1 << (n))) + +/* ost counter */ +#define __ostcu_set_pwm_output_shutdown_graceful() (REG_TCU_OSTCSR &= ~TCU_TCSR_PWM_SD) +#define __ostcu_set_ost_output_shutdown_abrupt() (REG_TCU_OSTCSR |= TCU_TCSR_PWM_SD) +#define __ostcu_select_clk_div1() \ + (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~TCU_OSTCSR_PRESCALE_MASK) | TCU_OSTCSR_PRESCALE1) +#define __ostcu_select_clk_div4() \ + (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~TCU_OSTCSR_PRESCALE_MASK) | TCU_OSTCSR_PRESCALE4) +#define __ostcu_select_clk_div16() \ + (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~TCU_OSTCSR_PRESCALE_MASK) | TCU_OSTCSR_PRESCALE16) +#define __ostcu_select_clk_div64() \ + (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~TCU_OSTCSR_PRESCALE_MASK) | TCU_OSTCSR_PRESCALE64) +#define __ostcu_select_clk_div256() \ + (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~TCU_OSTCSR_PRESCALE_MASK) | TCU_OSTCSR_PRESCALE256) +#define __ostcu_select_clk_div1024() \ + (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~TCU_OSTCSR_PRESCALE_MASK) | TCU_OSTCSR_PRESCALE1024) +#define __ostcu_select_rtcclk() \ + (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~(TCU_OSTCSR_EXT_EN | TCU_OSTCSR_RTC_EN | TCU_OSTCSR_PCK_EN)) | TCU_OSTCSR_RTC_EN) +#define __ostcu_select_extalclk() \ + (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~(TCU_OSTCSR_EXT_EN | TCU_OSTCSR_RTC_EN | TCU_OSTCSR_PCK_EN)) | TCU_OSTCSR_EXT_EN) +#define __ostcu_select_pclk() \ + (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~(TCU_OSTCSR_EXT_EN | TCU_OSTCSR_RTC_EN | TCU_OSTCSR_PCK_EN)) | TCU_OSTCSR_PCK_EN) + + +/*************************************************************************** + * WDT + ***************************************************************************/ +#define __wdt_start() ( REG_WDT_TCER |= WDT_TCER_TCEN ) +#define __wdt_stop() ( REG_WDT_TCER &= ~WDT_TCER_TCEN ) +#define __wdt_set_count(v) ( REG_WDT_TCNT = (v) ) +#define __wdt_set_data(v) ( REG_WDT_TDR = (v) ) + +#define __wdt_select_extalclk() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~(WDT_TCSR_EXT_EN | WDT_TCSR_RTC_EN | WDT_TCSR_PCK_EN)) | WDT_TCSR_EXT_EN) +#define __wdt_select_rtcclk() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~(WDT_TCSR_EXT_EN | WDT_TCSR_RTC_EN | WDT_TCSR_PCK_EN)) | WDT_TCSR_RTC_EN) +#define __wdt_select_pclk() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~(WDT_TCSR_EXT_EN | WDT_TCSR_RTC_EN | WDT_TCSR_PCK_EN)) | WDT_TCSR_PCK_EN) + +#define __wdt_select_clk_div1() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE1) +#define __wdt_select_clk_div4() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE4) +#define __wdt_select_clk_div16() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE16) +#define __wdt_select_clk_div64() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE64) +#define __wdt_select_clk_div256() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE256) +#define __wdt_select_clk_div1024() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE1024) + + +/*************************************************************************** + * UART + ***************************************************************************/ + +#define __uart_enable(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_FCR) |= UARTFCR_UUE | UARTFCR_FE ) +#define __uart_disable(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_FCR) = ~UARTFCR_UUE ) + +#define __uart_enable_transmit_irq(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) |= UARTIER_TIE ) +#define __uart_disable_transmit_irq(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) &= ~UARTIER_TIE ) + +#define __uart_enable_receive_irq(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) |= UARTIER_RIE | UARTIER_RLIE | UARTIER_RTIE ) +#define __uart_disable_receive_irq(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) &= ~(UARTIER_RIE | UARTIER_RLIE | UARTIER_RTIE) ) + +#define __uart_enable_loopback(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_MCR) |= UARTMCR_LOOP ) +#define __uart_disable_loopback(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_MCR) &= ~UARTMCR_LOOP ) + +#define __uart_set_8n1(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_LCR) = UARTLCR_WLEN_8 ) + +#define __uart_set_baud(n, devclk, baud) \ + do { \ + REG8(UART_BASE + UART_OFF*(n) + OFF_LCR) |= UARTLCR_DLAB; \ + REG8(UART_BASE + UART_OFF*(n) + OFF_DLLR) = (devclk / 16 / baud) & 0xff; \ + REG8(UART_BASE + UART_OFF*(n) + OFF_DLHR) = ((devclk / 16 / baud) >> 8) & 0xff; \ + REG8(UART_BASE + UART_OFF*(n) + OFF_LCR) &= ~UARTLCR_DLAB; \ + } while (0) + +#define __uart_parity_error(n) \ + ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_PER) != 0 ) + +#define __uart_clear_errors(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) &= ~(UARTLSR_ORER | UARTLSR_BRK | UARTLSR_FER | UARTLSR_PER | UARTLSR_RFER) ) + +#define __uart_transmit_fifo_empty(n) \ + ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_TDRQ) != 0 ) + +#define __uart_transmit_end(n) \ + ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_TEMT) != 0 ) + +#define __uart_transmit_char(n, ch) \ + REG8(UART_BASE + UART_OFF*(n) + OFF_TDR) = (ch) + +#define __uart_receive_fifo_full(n) \ + ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_DR) != 0 ) + +#define __uart_receive_ready(n) \ + ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_DR) != 0 ) + +#define __uart_receive_char(n) \ + REG8(UART_BASE + UART_OFF*(n) + OFF_RDR) + +#define __uart_disable_irda() \ + ( REG8(IRDA_BASE + OFF_SIRCR) &= ~(SIRCR_TSIRE | SIRCR_RSIRE) ) +#define __uart_enable_irda() \ + /* Tx high pulse as 0, Rx low pulse as 0 */ \ + ( REG8(IRDA_BASE + OFF_SIRCR) = SIRCR_TSIRE | SIRCR_RSIRE | SIRCR_RXPL | SIRCR_TPWS ) + + +/*************************************************************************** + * DMAC + ***************************************************************************/ + +/* m is the DMA controller index (0, 1), n is the DMA channel index (0 - 11) */ + +#define __dmac_enable_module(m) \ + ( REG_DMAC_DMACR(m) |= DMAC_DMACR_DMAE | DMAC_DMACR_PR_012345 ) +#define __dmac_disable_module(m) \ + ( REG_DMAC_DMACR(m) &= ~DMAC_DMACR_DMAE ) + +/* p=0,1,2,3 */ +#define __dmac_set_priority(m,p) \ +do { \ + REG_DMAC_DMACR(m) &= ~DMAC_DMACR_PR_MASK; \ + REG_DMAC_DMACR(m) |= ((p) << DMAC_DMACR_PR_BIT); \ +} while (0) + +#define __dmac_test_halt_error(m) ( REG_DMAC_DMACR(m) & DMAC_DMACR_HLT ) +#define __dmac_test_addr_error(m) ( REG_DMAC_DMACR(m) & DMAC_DMACR_AR ) + +#define __dmac_channel_enable_clk(n) \ + REG_DMAC_DMACKE((n)/HALF_DMA_NUM) |= 1 << ((n)-(n)/HALF_DMA_NUM*HALF_DMA_NUM); + +#define __dmac_enable_descriptor(n) \ + ( REG_DMAC_DCCSR((n)) &= ~DMAC_DCCSR_NDES ) +#define __dmac_disable_descriptor(n) \ + ( REG_DMAC_DCCSR((n)) |= DMAC_DCCSR_NDES ) + +#define __dmac_enable_channel(n) \ +do { \ + REG_DMAC_DCCSR((n)) |= DMAC_DCCSR_EN; \ +} while (0) +#define __dmac_disable_channel(n) \ +do { \ + REG_DMAC_DCCSR((n)) &= ~DMAC_DCCSR_EN; \ +} while (0) +#define __dmac_channel_enabled(n) \ + ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_EN ) + +#define __dmac_channel_enable_irq(n) \ + ( REG_DMAC_DCMD((n)) |= DMAC_DCMD_TIE ) +#define __dmac_channel_disable_irq(n) \ + ( REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_TIE ) + +#define __dmac_channel_transmit_halt_detected(n) \ + ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_HLT ) +#define __dmac_channel_transmit_end_detected(n) \ + ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_TT ) +#define __dmac_channel_address_error_detected(n) \ + ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_AR ) +#define __dmac_channel_count_terminated_detected(n) \ + ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_CT ) +#define __dmac_channel_descriptor_invalid_detected(n) \ + ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_INV ) + +#define __dmac_channel_clear_transmit_halt(n) \ + do { \ + /* clear both channel halt error and globle halt error */ \ + REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_HLT; \ + REG_DMAC_DMACR(n/HALF_DMA_NUM) &= ~DMAC_DMACR_HLT; \ + } while (0) +#define __dmac_channel_clear_transmit_end(n) \ + ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_TT ) +#define __dmac_channel_clear_address_error(n) \ + do { \ + REG_DMAC_DDA(n) = 0; /* clear descriptor address register */ \ + REG_DMAC_DSAR(n) = 0; /* clear source address register */ \ + REG_DMAC_DTAR(n) = 0; /* clear target address register */ \ + /* clear both channel addr error and globle address error */ \ + REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_AR; \ + REG_DMAC_DMACR(n/HALF_DMA_NUM) &= ~DMAC_DMACR_AR; \ + } while (0) +#define __dmac_channel_clear_count_terminated(n) \ + ( REG_DMAC_DCCSR((n)) &= ~DMAC_DCCSR_CT ) +#define __dmac_channel_clear_descriptor_invalid(n) \ + ( REG_DMAC_DCCSR((n)) &= ~DMAC_DCCSR_INV ) + +#define __dmac_channel_set_transfer_unit_32bit(n) \ +do { \ + REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ + REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_32BIT; \ +} while (0) + +#define __dmac_channel_set_transfer_unit_16bit(n) \ +do { \ + REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ + REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_16BIT; \ +} while (0) + +#define __dmac_channel_set_transfer_unit_8bit(n) \ +do { \ + REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ + REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_8BIT; \ +} while (0) + +#define __dmac_channel_set_transfer_unit_16byte(n) \ +do { \ + REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ + REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_16BYTE; \ +} while (0) + +#define __dmac_channel_set_transfer_unit_32byte(n) \ +do { \ + REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ + REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_32BYTE; \ +} while (0) + +/* w=8,16,32 */ +#define __dmac_channel_set_dest_port_width(n,w) \ +do { \ + REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DWDH_MASK; \ + REG_DMAC_DCMD((n)) |= DMAC_DCMD_DWDH_##w; \ +} while (0) + +/* w=8,16,32 */ +#define __dmac_channel_set_src_port_width(n,w) \ +do { \ + REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_SWDH_MASK; \ + REG_DMAC_DCMD((n)) |= DMAC_DCMD_SWDH_##w; \ +} while (0) + +/* v=0-15 */ +#define __dmac_channel_set_rdil(n,v) \ +do { \ + REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_RDIL_MASK; \ + REG_DMAC_DCMD((n) |= ((v) << DMAC_DCMD_RDIL_BIT); \ +} while (0) + +#define __dmac_channel_dest_addr_fixed(n) \ + ( REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DAI ) +#define __dmac_channel_dest_addr_increment(n) \ + ( REG_DMAC_DCMD((n)) |= DMAC_DCMD_DAI ) + +#define __dmac_channel_src_addr_fixed(n) \ + ( REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_SAI ) +#define __dmac_channel_src_addr_increment(n) \ + ( REG_DMAC_DCMD((n)) |= DMAC_DCMD_SAI ) + +#define __dmac_channel_set_doorbell(n) \ + ( REG_DMAC_DMADBSR((n)/HALF_DMA_NUM) = (1 << ((n)-(n)/HALF_DMA_NUM*HALF_DMA_NUM)) ) + +#define __dmac_channel_irq_detected(n) ( REG_DMAC_DMAIPR((n)/HALF_DMA_NUM) & (1 << ((n)-(n)/HALF_DMA_NUM*HALF_DMA_NUM)) ) +#define __dmac_channel_ack_irq(n) ( REG_DMAC_DMAIPR((n)/HALF_DMA_NUM) &= ~(1 <<((n)-(n)/HALF_DMA_NUM*HALF_DMA_NUM)) ) + +static __inline__ int __dmac_get_irq(void) +{ + int i; + for (i = 0; i < MAX_DMA_NUM; i++) + if (__dmac_channel_irq_detected(i)) + return i; + return -1; +} + + +/*************************************************************************** + * AIC (AC'97 & I2S Controller) + ***************************************************************************/ + +#define __aic_enable() ( REG_AIC_FR |= AIC_FR_ENB ) +#define __aic_disable() ( REG_AIC_FR &= ~AIC_FR_ENB ) + +#define __aic_select_ac97() ( REG_AIC_FR &= ~AIC_FR_AUSEL ) +#define __aic_select_i2s() ( REG_AIC_FR |= AIC_FR_AUSEL ) + +#define __aic_play_zero() ( REG_AIC_FR &= ~AIC_FR_LSMP ) +#define __aic_play_lastsample() ( REG_AIC_FR |= AIC_FR_LSMP ) + +#define __i2s_as_master() ( REG_AIC_FR |= AIC_FR_BCKD | AIC_FR_SYNCD ) +#define __i2s_as_slave() ( REG_AIC_FR &= ~(AIC_FR_BCKD | AIC_FR_SYNCD) ) +#define __aic_reset_status() ( REG_AIC_FR & AIC_FR_RST ) + +#define __aic_reset() \ +do { \ + REG_AIC_FR |= AIC_FR_RST; \ +} while(0) + + +#define __aic_set_transmit_trigger(n) \ +do { \ + REG_AIC_FR &= ~AIC_FR_TFTH_MASK; \ + REG_AIC_FR |= ((n) << AIC_FR_TFTH_BIT); \ +} while(0) + +#define __aic_set_receive_trigger(n) \ +do { \ + REG_AIC_FR &= ~AIC_FR_RFTH_MASK; \ + REG_AIC_FR |= ((n) << AIC_FR_RFTH_BIT); \ +} while(0) + +#define __aic_enable_record() ( REG_AIC_CR |= AIC_CR_EREC ) +#define __aic_disable_record() ( REG_AIC_CR &= ~AIC_CR_EREC ) +#define __aic_enable_replay() ( REG_AIC_CR |= AIC_CR_ERPL ) +#define __aic_disable_replay() ( REG_AIC_CR &= ~AIC_CR_ERPL ) +#define __aic_enable_loopback() ( REG_AIC_CR |= AIC_CR_ENLBF ) +#define __aic_disable_loopback() ( REG_AIC_CR &= ~AIC_CR_ENLBF ) + +#define __aic_flush_fifo() ( REG_AIC_CR |= AIC_CR_FLUSH ) +#define __aic_unflush_fifo() ( REG_AIC_CR &= ~AIC_CR_FLUSH ) + +#define __aic_enable_transmit_intr() \ + ( REG_AIC_CR |= (AIC_CR_ETFS | AIC_CR_ETUR) ) +#define __aic_disable_transmit_intr() \ + ( REG_AIC_CR &= ~(AIC_CR_ETFS | AIC_CR_ETUR) ) +#define __aic_enable_receive_intr() \ + ( REG_AIC_CR |= (AIC_CR_ERFS | AIC_CR_EROR) ) +#define __aic_disable_receive_intr() \ + ( REG_AIC_CR &= ~(AIC_CR_ERFS | AIC_CR_EROR) ) + +#define __aic_enable_transmit_dma() ( REG_AIC_CR |= AIC_CR_TDMS ) +#define __aic_disable_transmit_dma() ( REG_AIC_CR &= ~AIC_CR_TDMS ) +#define __aic_enable_receive_dma() ( REG_AIC_CR |= AIC_CR_RDMS ) +#define __aic_disable_receive_dma() ( REG_AIC_CR &= ~AIC_CR_RDMS ) + +#define __aic_enable_mono2stereo() ( REG_AIC_CR |= AIC_CR_M2S ) +#define __aic_disable_mono2stereo() ( REG_AIC_CR &= ~AIC_CR_M2S ) +#define __aic_enable_byteswap() ( REG_AIC_CR |= AIC_CR_ENDSW ) +#define __aic_disable_byteswap() ( REG_AIC_CR &= ~AIC_CR_ENDSW ) +#define __aic_enable_unsignadj() ( REG_AIC_CR |= AIC_CR_AVSTSU ) +#define __aic_disable_unsignadj() ( REG_AIC_CR &= ~AIC_CR_AVSTSU ) + +#define AC97_PCM_XS_L_FRONT AIC_ACCR1_XS_SLOT3 +#define AC97_PCM_XS_R_FRONT AIC_ACCR1_XS_SLOT4 +#define AC97_PCM_XS_CENTER AIC_ACCR1_XS_SLOT6 +#define AC97_PCM_XS_L_SURR AIC_ACCR1_XS_SLOT7 +#define AC97_PCM_XS_R_SURR AIC_ACCR1_XS_SLOT8 +#define AC97_PCM_XS_LFE AIC_ACCR1_XS_SLOT9 + +#define AC97_PCM_RS_L_FRONT AIC_ACCR1_RS_SLOT3 +#define AC97_PCM_RS_R_FRONT AIC_ACCR1_RS_SLOT4 +#define AC97_PCM_RS_CENTER AIC_ACCR1_RS_SLOT6 +#define AC97_PCM_RS_L_SURR AIC_ACCR1_RS_SLOT7 +#define AC97_PCM_RS_R_SURR AIC_ACCR1_RS_SLOT8 +#define AC97_PCM_RS_LFE AIC_ACCR1_RS_SLOT9 + +#define __ac97_set_xs_none() ( REG_AIC_ACCR1 &= ~AIC_ACCR1_XS_MASK ) +#define __ac97_set_xs_mono() \ +do { \ + REG_AIC_ACCR1 &= ~AIC_ACCR1_XS_MASK; \ + REG_AIC_ACCR1 |= AC97_PCM_XS_R_FRONT; \ +} while(0) +#define __ac97_set_xs_stereo() \ +do { \ + REG_AIC_ACCR1 &= ~AIC_ACCR1_XS_MASK; \ + REG_AIC_ACCR1 |= AC97_PCM_XS_L_FRONT | AC97_PCM_XS_R_FRONT; \ +} while(0) + +/* In fact, only stereo is support now. */ +#define __ac97_set_rs_none() ( REG_AIC_ACCR1 &= ~AIC_ACCR1_RS_MASK ) +#define __ac97_set_rs_mono() \ +do { \ + REG_AIC_ACCR1 &= ~AIC_ACCR1_RS_MASK; \ + REG_AIC_ACCR1 |= AC97_PCM_RS_R_FRONT; \ +} while(0) +#define __ac97_set_rs_stereo() \ +do { \ + REG_AIC_ACCR1 &= ~AIC_ACCR1_RS_MASK; \ + REG_AIC_ACCR1 |= AC97_PCM_RS_L_FRONT | AC97_PCM_RS_R_FRONT; \ +} while(0) + +#define __ac97_warm_reset_codec() \ + do { \ + REG_AIC_ACCR2 |= AIC_ACCR2_SA; \ + REG_AIC_ACCR2 |= AIC_ACCR2_SS; \ + udelay(2); \ + REG_AIC_ACCR2 &= ~AIC_ACCR2_SS; \ + REG_AIC_ACCR2 &= ~AIC_ACCR2_SA; \ + } while (0) + +#define __ac97_cold_reset_codec() \ + do { \ + REG_AIC_ACCR2 |= AIC_ACCR2_SR; \ + udelay(2); \ + REG_AIC_ACCR2 &= ~AIC_ACCR2_SR; \ + } while (0) + +/* n=8,16,18,20 */ +#define __ac97_set_iass(n) \ + ( REG_AIC_ACCR2 = (REG_AIC_ACCR2 & ~AIC_ACCR2_IASS_MASK) | AIC_ACCR2_IASS_##n##BIT ) +#define __ac97_set_oass(n) \ + ( REG_AIC_ACCR2 = (REG_AIC_ACCR2 & ~AIC_ACCR2_OASS_MASK) | AIC_ACCR2_OASS_##n##BIT ) + +#define __i2s_select_i2s() ( REG_AIC_I2SCR &= ~AIC_I2SCR_AMSL ) +#define __i2s_select_msbjustified() ( REG_AIC_I2SCR |= AIC_I2SCR_AMSL ) + +/* n=8,16,18,20,24 */ +/*#define __i2s_set_sample_size(n) \ + ( REG_AIC_I2SCR |= (REG_AIC_I2SCR & ~AIC_I2SCR_WL_MASK) | AIC_I2SCR_WL_##n##BIT )*/ + +#define __i2s_set_oss_sample_size(n) \ + ( REG_AIC_CR = (REG_AIC_CR & ~AIC_CR_OSS_MASK) | AIC_CR_OSS_##n##BIT ) +#define __i2s_set_iss_sample_size(n) \ + ( REG_AIC_CR = (REG_AIC_CR & ~AIC_CR_ISS_MASK) | AIC_CR_ISS_##n##BIT ) + +#define __i2s_stop_bitclk() ( REG_AIC_I2SCR |= AIC_I2SCR_STPBK ) +#define __i2s_start_bitclk() ( REG_AIC_I2SCR &= ~AIC_I2SCR_STPBK ) + +#define __aic_transmit_request() ( REG_AIC_SR & AIC_SR_TFS ) +#define __aic_receive_request() ( REG_AIC_SR & AIC_SR_RFS ) +#define __aic_transmit_underrun() ( REG_AIC_SR & AIC_SR_TUR ) +#define __aic_receive_overrun() ( REG_AIC_SR & AIC_SR_ROR ) + +#define __aic_clear_errors() ( REG_AIC_SR &= ~(AIC_SR_TUR | AIC_SR_ROR) ) + +#define __aic_get_transmit_resident() \ + ( (REG_AIC_SR & AIC_SR_TFL_MASK) >> AIC_SR_TFL_BIT ) +#define __aic_get_receive_count() \ + ( (REG_AIC_SR & AIC_SR_RFL_MASK) >> AIC_SR_RFL_BIT ) + +#define __ac97_command_transmitted() ( REG_AIC_ACSR & AIC_ACSR_CADT ) +#define __ac97_status_received() ( REG_AIC_ACSR & AIC_ACSR_SADR ) +#define __ac97_status_receive_timeout() ( REG_AIC_ACSR & AIC_ACSR_RSTO ) +#define __ac97_codec_is_low_power_mode() ( REG_AIC_ACSR & AIC_ACSR_CLPM ) +#define __ac97_codec_is_ready() ( REG_AIC_ACSR & AIC_ACSR_CRDY ) +#define __ac97_slot_error_detected() ( REG_AIC_ACSR & AIC_ACSR_SLTERR ) +#define __ac97_clear_slot_error() ( REG_AIC_ACSR &= ~AIC_ACSR_SLTERR ) + +#define __i2s_is_busy() ( REG_AIC_I2SSR & AIC_I2SSR_BSY ) + +#define CODEC_READ_CMD (1 << 19) +#define CODEC_WRITE_CMD (0 << 19) +#define CODEC_REG_INDEX_BIT 12 +#define CODEC_REG_INDEX_MASK (0x7f << CODEC_REG_INDEX_BIT) /* 18:12 */ +#define CODEC_REG_DATA_BIT 4 +#define CODEC_REG_DATA_MASK (0x0ffff << 4) /* 19:4 */ + +#define __ac97_out_rcmd_addr(reg) \ +do { \ + REG_AIC_ACCAR = CODEC_READ_CMD | ((reg) << CODEC_REG_INDEX_BIT); \ +} while (0) + +#define __ac97_out_wcmd_addr(reg) \ +do { \ + REG_AIC_ACCAR = CODEC_WRITE_CMD | ((reg) << CODEC_REG_INDEX_BIT); \ +} while (0) + +#define __ac97_out_data(value) \ +do { \ + REG_AIC_ACCDR = ((value) << CODEC_REG_DATA_BIT); \ +} while (0) + +#define __ac97_in_data() \ + ( (REG_AIC_ACSDR & CODEC_REG_DATA_MASK) >> CODEC_REG_DATA_BIT ) + +#define __ac97_in_status_addr() \ + ( (REG_AIC_ACSAR & CODEC_REG_INDEX_MASK) >> CODEC_REG_INDEX_BIT ) + +#define __i2s_set_sample_rate(i2sclk, sync) \ + ( REG_AIC_I2SDIV = ((i2sclk) / (4*64)) / (sync) ) + +#define __aic_write_tfifo(v) ( REG_AIC_DR = (v) ) +#define __aic_read_rfifo() ( REG_AIC_DR ) + +#define __aic_internal_codec() ( REG_AIC_FR |= AIC_FR_ICDC ) +#define __aic_external_codec() ( REG_AIC_FR &= ~AIC_FR_ICDC ) + +// +// Define next ops for AC97 compatible +// + +#define AC97_ACSR AIC_ACSR + +#define __ac97_enable() __aic_enable(); __aic_select_ac97() +#define __ac97_disable() __aic_disable() +#define __ac97_reset() __aic_reset() + +#define __ac97_set_transmit_trigger(n) __aic_set_transmit_trigger(n) +#define __ac97_set_receive_trigger(n) __aic_set_receive_trigger(n) + +#define __ac97_enable_record() __aic_enable_record() +#define __ac97_disable_record() __aic_disable_record() +#define __ac97_enable_replay() __aic_enable_replay() +#define __ac97_disable_replay() __aic_disable_replay() +#define __ac97_enable_loopback() __aic_enable_loopback() +#define __ac97_disable_loopback() __aic_disable_loopback() + +#define __ac97_enable_transmit_dma() __aic_enable_transmit_dma() +#define __ac97_disable_transmit_dma() __aic_disable_transmit_dma() +#define __ac97_enable_receive_dma() __aic_enable_receive_dma() +#define __ac97_disable_receive_dma() __aic_disable_receive_dma() + +#define __ac97_transmit_request() __aic_transmit_request() +#define __ac97_receive_request() __aic_receive_request() +#define __ac97_transmit_underrun() __aic_transmit_underrun() +#define __ac97_receive_overrun() __aic_receive_overrun() + +#define __ac97_clear_errors() __aic_clear_errors() + +#define __ac97_get_transmit_resident() __aic_get_transmit_resident() +#define __ac97_get_receive_count() __aic_get_receive_count() + +#define __ac97_enable_transmit_intr() __aic_enable_transmit_intr() +#define __ac97_disable_transmit_intr() __aic_disable_transmit_intr() +#define __ac97_enable_receive_intr() __aic_enable_receive_intr() +#define __ac97_disable_receive_intr() __aic_disable_receive_intr() + +#define __ac97_write_tfifo(v) __aic_write_tfifo(v) +#define __ac97_read_rfifo() __aic_read_rfifo() + +// +// Define next ops for I2S compatible +// + +#define I2S_ACSR AIC_I2SSR + +#define __i2s_enable() __aic_enable(); __aic_select_i2s() +#define __i2s_disable() __aic_disable() +#define __i2s_reset() __aic_reset() + +#define __i2s_set_transmit_trigger(n) __aic_set_transmit_trigger(n) +#define __i2s_set_receive_trigger(n) __aic_set_receive_trigger(n) + +#define __i2s_enable_record() __aic_enable_record() +#define __i2s_disable_record() __aic_disable_record() +#define __i2s_enable_replay() __aic_enable_replay() +#define __i2s_disable_replay() __aic_disable_replay() +#define __i2s_enable_loopback() __aic_enable_loopback() +#define __i2s_disable_loopback() __aic_disable_loopback() + +#define __i2s_enable_transmit_dma() __aic_enable_transmit_dma() +#define __i2s_disable_transmit_dma() __aic_disable_transmit_dma() +#define __i2s_enable_receive_dma() __aic_enable_receive_dma() +#define __i2s_disable_receive_dma() __aic_disable_receive_dma() + +#define __i2s_transmit_request() __aic_transmit_request() +#define __i2s_receive_request() __aic_receive_request() +#define __i2s_transmit_underrun() __aic_transmit_underrun() +#define __i2s_receive_overrun() __aic_receive_overrun() + +#define __i2s_clear_errors() __aic_clear_errors() + +#define __i2s_get_transmit_resident() __aic_get_transmit_resident() +#define __i2s_get_receive_count() __aic_get_receive_count() + +#define __i2s_enable_transmit_intr() __aic_enable_transmit_intr() +#define __i2s_disable_transmit_intr() __aic_disable_transmit_intr() +#define __i2s_enable_receive_intr() __aic_enable_receive_intr() +#define __i2s_disable_receive_intr() __aic_disable_receive_intr() + +#define __i2s_write_tfifo(v) __aic_write_tfifo(v) +#define __i2s_read_rfifo() __aic_read_rfifo() + +#define __i2s_reset_codec() \ + do { \ + } while (0) + +/************************************************************************* + * PCM Controller operation + *************************************************************************/ + +#define __pcm_enable() ( REG_PCM_CTL |= PCM_CTL_PCMEN ) +#define __pcm_disable() ( REG_PCM_CTL &= ~PCM_CTL_PCMEN ) + +#define __pcm_clk_enable() ( REG_PCM_CTL |= PCM_CTL_CLKEN ) +#define __pcm_clk_disable() ( REG_PCM_CTL &= ~PCM_CTL_CLKEN ) + +#define __pcm_reset() ( REG_PCM_CTL |= PCM_CTL_RST ) +#define __pcm_flush_fifo() ( REG_PCM_CTL |= PCM_CTL_FLUSH ) + +#define __pcm_enable_record() ( REG_PCM_CTL |= PCM_CTL_EREC ) +#define __pcm_disable_record() ( REG_PCM_CTL &= ~PCM_CTL_EREC ) +#define __pcm_enable_playback() ( REG_PCM_CTL |= PCM_CTL_ERPL ) +#define __pcm_disable_playback() ( REG_PCM_CTL &= ~PCM_CTL_ERPL ) + +#define __pcm_enable_rxfifo() __pcm_enable_record() +#define __pcm_disable_rxfifo() __pcm_disable_record() +#define __pcm_enable_txfifo() __pcm_enable_playback() +#define __pcm_disable_txfifo() __pcm_disable_playback() + +#define __pcm_last_sample() ( REG_PCM_CTL |= PCM_CTL_LSMP ) +#define __pcm_zero_sample() ( REG_PCM_CTL &= ~PCM_CTL_LSMP ) + +#define __pcm_enable_transmit_dma() ( REG_PCM_CTL |= PCM_CTL_ETDMA ) +#define __pcm_disable_transmit_dma() ( REG_PCM_CTL &= ~PCM_CTL_ETDMA ) +#define __pcm_enable_receive_dma() ( REG_PCM_CTL |= PCM_CTL_ERDMA ) +#define __pcm_disable_receive_dma() ( REG_PCM_CTL &= ~PCM_CTL_ERDMA ) + +#define __pcm_as_master() ( REG_PCM_CFG &= PCM_CFG_MODE ) +#define __pcm_as_slave() ( REG_PCM_CFG |= ~PCM_CFG_MODE ) + +#define __pcm_set_transmit_trigger(n) \ +do { \ + REG_PCM_CFG &= ~PCM_CFG_TFTH_MASK; \ + REG_PCM_CFG |= ((n) << PCM_CFG_TFTH_BIT); \ +} while(0) + +#define __pcm_set_receive_trigger(n) \ +do { \ + REG_PCM_CFG &= ~PCM_CFG_RFTH_MASK; \ + REG_PCM_CFG |= ((n) << PCM_CFG_RFTH_BIT); \ +} while(0) + +#define __pcm_omsb_same_sync() ( REG_PCM_CFG &= ~PCM_CFG_OMSBPOS ) +#define __pcm_omsb_next_sync() ( REG_PCM_CFG |= PCM_CFG_OMSBPOS ) + +#define __pcm_imsb_same_sync() ( REG_PCM_CFG &= ~PCM_CFG_IMSBPOS ) +#define __pcm_imsb_next_sync() ( REG_PCM_CFG |= PCM_CFG_IMSBPOS ) + +/* set input sample size 8 or 16*/ +#define __pcm_set_iss(n) \ +( REG_PCM_CFG = (REG_PCM_CFG & ~PCM_CFG_ISS_MASK) | PCM_CFG_ISS_##n ) +/* set output sample size 8 or 16*/ +#define __pcm_set_oss(n) \ +( REG_PCM_CFG = (REG_PCM_CFG & ~PCM_CFG_OSS_MASK) | PCM_CFG_OSS_##n ) + +#define __pcm_set_valid_slot(n) \ +( REG_PCM_CFG = (REG_PCM_CFG & ~PCM_CFG_SLOT_MASK) | PCM_CFG_SLOT_##n ) + +#define __pcm_write_data(v) ( REG_PCM_DP = (v) ) +#define __pcm_read_data() ( REG_PCM_DP ) + +#define __pcm_enable_tfs_intr() ( REG_PCM_INTC |= PCM_INTC_ETFS ) +#define __pcm_disable_tfs_intr() ( REG_PCM_INTC &= ~PCM_INTC_ETFS ) + +#define __pcm_enable_tur_intr() ( REG_PCM_INTC |= PCM_INTC_ETUR ) +#define __pcm_disable_tur_intr() ( REG_PCM_INTC &= ~PCM_INTC_ETUR ) + +#define __pcm_enable_rfs_intr() ( REG_PCM_INTC |= PCM_INTC_ERFS ) +#define __pcm_disable_rfs_intr() ( REG_PCM_INTC &= ~PCM_INTC_ERFS ) + +#define __pcm_enable_ror_intr() ( REG_PCM_INTC |= PCM_INTC_EROR ) +#define __pcm_disable_ror_intr() ( REG_PCM_INTC &= ~PCM_INTC_EROR ) + +#define __pcm_ints_valid_tx() \ +( ((REG_PCM_INTS & PCM_INTS_TFL_MASK) >> PCM_INTS_TFL_BIT) ) +#define __pcm_ints_valid_rx() \ +( ((REG_PCM_INTS & PCM_INTS_RFL_MASK) >> PCM_INTS_RFL_BIT) ) + +#define __pcm_set_clk_div(n) \ +( REG_PCM_DIV = (REG_PCM_DIV & ~PCM_DIV_CLKDIV_MASK) | ((n) << PCM_DIV_CLKDIV_BIT) ) + +/* sysclk(cpm_pcm_sysclk) Hz is created by cpm logic, and pcmclk Hz is the pcm in/out clock wanted */ +#define __pcm_set_clk_rate(sysclk, pcmclk) \ +__pcm_set_clk_div(((sysclk) / (pcmclk) - 1)) + +#define __pcm_set_sync_div(n) \ +( REG_PCM_DIV = (REG_PCM_DIV & ~PCM_DIV_SYNDIV_MASK) | ((n) << PCM_DIV_SYNDIV_BIT) ) + +/* pcmclk is source clock Hz, and sync is the frame sync clock Hz wanted */ +#define __pcm_set_sync_rate(pcmclk, sync) \ +__pcm_set_sync_div(((pcmclk) / (8 * (sync)) - 1)) + + /* set sync length in pcmclk n = 0 ... 63 */ +#define __pcm_set_sync_len(n) \ +( REG_PCM_DIV = (REG_PCM_DIV & ~PCM_DIV_SYNL_MASK) | (n << PCM_DIV_SYNL_BIT) ) + + +/*************************************************************************** + * ICDC + ***************************************************************************/ +#define __i2s_internal_codec() __aic_internal_codec() +#define __i2s_external_codec() __aic_external_codec() + +#define __icdc_clk_ready() ( REG_ICDC_CKCFG & ICDC_CKCFG_CKRDY ) +#define __icdc_sel_adc() ( REG_ICDC_CKCFG |= ICDC_CKCFG_SELAD ) +#define __icdc_sel_dac() ( REG_ICDC_CKCFG &= ~ICDC_CKCFG_SELAD ) + +#define __icdc_set_rgwr() ( REG_ICDC_RGADW |= ICDC_RGADW_RGWR ) +#define __icdc_clear_rgwr() ( REG_ICDC_RGADW &= ~ICDC_RGADW_RGWR ) +#define __icdc_rgwr_ready() ( REG_ICDC_RGADW & ICDC_RGADW_RGWR ) + +#define __icdc_set_addr(n) \ +do { \ + REG_ICDC_RGADW &= ~ICDC_RGADW_RGADDR_MASK; \ + REG_ICDC_RGADW |= (n) << ICDC_RGADW_RGADDR_BIT; \ +} while(0) + +#define __icdc_set_cmd(n) \ +do { \ + REG_ICDC_RGADW &= ~ICDC_RGADW_RGDIN_MASK; \ + REG_ICDC_RGADW |= (n) << ICDC_RGADW_RGDIN_BIT; \ +} while(0) + +#define __icdc_irq_pending() ( REG_ICDC_RGDATA & ICDC_RGDATA_IRQ ) +#define __icdc_get_value() ( REG_ICDC_RGDATA & ICDC_RGDATA_RGDOUT_MASK ) + +/*************************************************************************** + * INTC + ***************************************************************************/ +#define __intc_unmask_irq(n) ( REG_INTC_IMCR = (1 << (n)) ) +#define __intc_mask_irq(n) ( REG_INTC_IMSR = (1 << (n)) ) +#define __intc_ack_irq(n) ( REG_INTC_IPR = (1 << (n)) ) /* A dummy ack, as the Pending Register is Read Only. Should we remove __intc_ack_irq() */ + + +/*************************************************************************** + * I2C + ***************************************************************************/ + +#define __i2c_enable() ( REG_I2C_CR |= I2C_CR_I2CE ) +#define __i2c_disable() ( REG_I2C_CR &= ~I2C_CR_I2CE ) + +#define __i2c_send_start() ( REG_I2C_CR |= I2C_CR_STA ) +#define __i2c_send_stop() ( REG_I2C_CR |= I2C_CR_STO ) +#define __i2c_send_ack() ( REG_I2C_CR &= ~I2C_CR_AC ) +#define __i2c_send_nack() ( REG_I2C_CR |= I2C_CR_AC ) + +#define __i2c_set_drf() ( REG_I2C_SR |= I2C_SR_DRF ) +#define __i2c_clear_drf() ( REG_I2C_SR &= ~I2C_SR_DRF ) +#define __i2c_check_drf() ( REG_I2C_SR & I2C_SR_DRF ) + +#define __i2c_received_ack() ( !(REG_I2C_SR & I2C_SR_ACKF) ) +#define __i2c_is_busy() ( REG_I2C_SR & I2C_SR_BUSY ) +#define __i2c_transmit_ended() ( REG_I2C_SR & I2C_SR_TEND ) + +#define __i2c_set_clk(dev_clk, i2c_clk) \ + ( REG_I2C_GR = (dev_clk) / (16*(i2c_clk)) - 1 ) + +#define __i2c_read() ( REG_I2C_DR ) +#define __i2c_write(val) ( REG_I2C_DR = (val) ) + + +/*************************************************************************** + * MSC + ***************************************************************************/ +/* n = 0, 1 (MSC0, MSC1) */ + +#define __msc_start_op(n) \ + ( REG_MSC_STRPCL(n) = MSC_STRPCL_START_OP | MSC_STRPCL_CLOCK_CONTROL_START ) + +#define __msc_set_resto(n, to) ( REG_MSC_RESTO(n) = to ) +#define __msc_set_rdto(n, to) ( REG_MSC_RDTO(n) = to ) +#define __msc_set_cmd(n, cmd) ( REG_MSC_CMD(n) = cmd ) +#define __msc_set_arg(n, arg) ( REG_MSC_ARG(n) = arg ) +#define __msc_set_nob(n, nob) ( REG_MSC_NOB(n) = nob ) +#define __msc_get_nob(n) ( REG_MSC_NOB(n) ) +#define __msc_set_blklen(n, len) ( REG_MSC_BLKLEN(n) = len ) +#define __msc_set_cmdat(n, cmdat) ( REG_MSC_CMDAT(n) = cmdat ) +#define __msc_set_cmdat_ioabort(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_IO_ABORT ) +#define __msc_clear_cmdat_ioabort(n) ( REG_MSC_CMDAT(n) &= ~MSC_CMDAT_IO_ABORT ) + +#define __msc_set_cmdat_bus_width1(n) \ +do { \ + REG_MSC_CMDAT(n) &= ~MSC_CMDAT_BUS_WIDTH_MASK; \ + REG_MSC_CMDAT(n) |= MSC_CMDAT_BUS_WIDTH_1BIT; \ +} while(0) + +#define __msc_set_cmdat_bus_width4(n) \ +do { \ + REG_MSC_CMDAT(n) &= ~MSC_CMDAT_BUS_WIDTH_MASK; \ + REG_MSC_CMDAT(n) |= MSC_CMDAT_BUS_WIDTH_4BIT; \ +} while(0) + +#define __msc_set_cmdat_dma_en(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_DMA_EN ) +#define __msc_set_cmdat_init(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_INIT ) +#define __msc_set_cmdat_busy(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_BUSY ) +#define __msc_set_cmdat_stream(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_STREAM_BLOCK ) +#define __msc_set_cmdat_block(n) ( REG_MSC_CMDAT(n) &= ~MSC_CMDAT_STREAM_BLOCK ) +#define __msc_set_cmdat_read(n) ( REG_MSC_CMDAT(n) &= ~MSC_CMDAT_WRITE_READ ) +#define __msc_set_cmdat_write(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_WRITE_READ ) +#define __msc_set_cmdat_data_en(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_DATA_EN ) + +/* r is MSC_CMDAT_RESPONSE_FORMAT_Rx or MSC_CMDAT_RESPONSE_FORMAT_NONE */ +#define __msc_set_cmdat_res_format(n, r) \ +do { \ + REG_MSC_CMDAT(n) &= ~MSC_CMDAT_RESPONSE_FORMAT_MASK; \ + REG_MSC_CMDAT(n) |= (r); \ +} while(0) + +#define __msc_clear_cmdat(n) \ + REG_MSC_CMDAT(n) &= ~( MSC_CMDAT_IO_ABORT | MSC_CMDAT_DMA_EN | MSC_CMDAT_INIT| \ + MSC_CMDAT_BUSY | MSC_CMDAT_STREAM_BLOCK | MSC_CMDAT_WRITE_READ | \ + MSC_CMDAT_DATA_EN | MSC_CMDAT_RESPONSE_FORMAT_MASK ) + +#define __msc_get_imask(n) ( REG_MSC_IMASK(n) ) +#define __msc_mask_all_intrs(n) ( REG_MSC_IMASK(n) = 0xff ) +#define __msc_unmask_all_intrs(n) ( REG_MSC_IMASK(n) = 0x00 ) +#define __msc_mask_rd(n) ( REG_MSC_IMASK(n) |= MSC_IMASK_RXFIFO_RD_REQ ) +#define __msc_unmask_rd(n) ( REG_MSC_IMASK(n) &= ~MSC_IMASK_RXFIFO_RD_REQ ) +#define __msc_mask_wr(n) ( REG_MSC_IMASK(n) |= MSC_IMASK_TXFIFO_WR_REQ ) +#define __msc_unmask_wr(n) ( REG_MSC_IMASK(n) &= ~MSC_IMASK_TXFIFO_WR_REQ ) +#define __msc_mask_endcmdres(n) ( REG_MSC_IMASK(n) |= MSC_IMASK_END_CMD_RES ) +#define __msc_unmask_endcmdres(n) ( REG_MSC_IMASK(n) &= ~MSC_IMASK_END_CMD_RES ) +#define __msc_mask_datatrandone(n) ( REG_MSC_IMASK(n) |= MSC_IMASK_DATA_TRAN_DONE ) +#define __msc_unmask_datatrandone(n) ( REG_MSC_IMASK(n) &= ~MSC_IMASK_DATA_TRAN_DONE ) +#define __msc_mask_prgdone(n) ( REG_MSC_IMASK(n) |= MSC_IMASK_PRG_DONE ) +#define __msc_unmask_prgdone(n) ( REG_MSC_IMASK(n) &= ~MSC_IMASK_PRG_DONE ) + +/* m=0,1,2,3,4,5,6,7 */ +#define __msc_set_clkrt(n, m) \ +do { \ + REG_MSC_CLKRT(n) = m; \ +} while(0) + +#define __msc_get_ireg(n) ( REG_MSC_IREG(n) ) +#define __msc_ireg_rd(n) ( REG_MSC_IREG(n) & MSC_IREG_RXFIFO_RD_REQ ) +#define __msc_ireg_wr(n) ( REG_MSC_IREG(n) & MSC_IREG_TXFIFO_WR_REQ ) +#define __msc_ireg_end_cmd_res(n) ( REG_MSC_IREG(n) & MSC_IREG_END_CMD_RES ) +#define __msc_ireg_data_tran_done(n) ( REG_MSC_IREG(n) & MSC_IREG_DATA_TRAN_DONE ) +#define __msc_ireg_prg_done(n) ( REG_MSC_IREG(n) & MSC_IREG_PRG_DONE ) +#define __msc_ireg_clear_end_cmd_res(n) ( REG_MSC_IREG(n) = MSC_IREG_END_CMD_RES ) +#define __msc_ireg_clear_data_tran_done(n) ( REG_MSC_IREG(n) = MSC_IREG_DATA_TRAN_DONE ) +#define __msc_ireg_clear_prg_done(n) ( REG_MSC_IREG(n) = MSC_IREG_PRG_DONE ) + +#define __msc_get_stat(n) ( REG_MSC_STAT(n) ) +#define __msc_stat_not_end_cmd_res(n) ( (REG_MSC_STAT(n) & MSC_STAT_END_CMD_RES) == 0) +#define __msc_stat_crc_err(n) \ + ( REG_MSC_STAT(n) & (MSC_STAT_CRC_RES_ERR | MSC_STAT_CRC_READ_ERROR | MSC_STAT_CRC_WRITE_ERROR_YES) ) +#define __msc_stat_res_crc_err(n) ( REG_MSC_STAT(n) & MSC_STAT_CRC_RES_ERR ) +#define __msc_stat_rd_crc_err(n) ( REG_MSC_STAT(n) & MSC_STAT_CRC_READ_ERROR ) +#define __msc_stat_wr_crc_err(n) ( REG_MSC_STAT(n) & MSC_STAT_CRC_WRITE_ERROR_YES ) +#define __msc_stat_resto_err(n) ( REG_MSC_STAT(n) & MSC_STAT_TIME_OUT_RES ) +#define __msc_stat_rdto_err(n) ( REG_MSC_STAT(n) & MSC_STAT_TIME_OUT_READ ) + +#define __msc_rd_resfifo(n) ( REG_MSC_RES(n) ) +#define __msc_rd_rxfifo(n) ( REG_MSC_RXFIFO(n) ) +#define __msc_wr_txfifo(n, v) ( REG_MSC_TXFIFO(n) = v ) + +#define __msc_reset(n) \ +do { \ + REG_MSC_STRPCL(n) = MSC_STRPCL_RESET; \ + while (REG_MSC_STAT(n) & MSC_STAT_IS_RESETTING); \ +} while (0) + +#define __msc_start_clk(n) \ +do { \ + REG_MSC_STRPCL(n) = MSC_STRPCL_CLOCK_CONTROL_START; \ +} while (0) + +#define __msc_stop_clk(n) \ +do { \ + REG_MSC_STRPCL(n) = MSC_STRPCL_CLOCK_CONTROL_STOP; \ +} while (0) + +#define MMC_CLK 19169200 +#define SD_CLK 24576000 + +/* msc_clk should little than pclk and little than clk retrieve from card */ +#define __msc_calc_clk_divisor(type,dev_clk,msc_clk,lv) \ +do { \ + unsigned int rate, pclk, i; \ + pclk = dev_clk; \ + rate = type?SD_CLK:MMC_CLK; \ + if (msc_clk && msc_clk < pclk) \ + pclk = msc_clk; \ + i = 0; \ + while (pclk < rate) \ + { \ + i ++; \ + rate >>= 1; \ + } \ + lv = i; \ +} while(0) + +/* divide rate to little than or equal to 400kHz */ +#define __msc_calc_slow_clk_divisor(type, lv) \ +do { \ + unsigned int rate, i; \ + rate = (type?SD_CLK:MMC_CLK)/1000/400; \ + i = 0; \ + while (rate > 0) \ + { \ + rate >>= 1; \ + i ++; \ + } \ + lv = i; \ +} while(0) + + +/*************************************************************************** + * SSI (Synchronous Serial Interface) + ***************************************************************************/ +/* n = 0, 1 (SSI0, SSI1) */ +#define __ssi_enable(n) ( REG_SSI_CR0(n) |= SSI_CR0_SSIE ) +#define __ssi_disable(n) ( REG_SSI_CR0(n) &= ~SSI_CR0_SSIE ) +#define __ssi_select_ce(n) ( REG_SSI_CR0(n) &= ~SSI_CR0_FSEL ) + +#define __ssi_normal_mode(n) ( REG_SSI_ITR(n) &= ~SSI_ITR_IVLTM_MASK ) + +#define __ssi_select_ce2(n) \ +do { \ + REG_SSI_CR0(n) |= SSI_CR0_FSEL; \ + REG_SSI_CR1(n) &= ~SSI_CR1_MULTS; \ +} while (0) + +#define __ssi_select_gpc(n) \ +do { \ + REG_SSI_CR0(n) &= ~SSI_CR0_FSEL; \ + REG_SSI_CR1(n) |= SSI_CR1_MULTS; \ +} while (0) + +#define __ssi_underrun_auto_clear(n) \ +do { \ + REG_SSI_CR0(n) |= SSI_CR0_EACLRUN; \ +} while (0) + +#define __ssi_underrun_clear_manually(n) \ +do { \ + REG_SSI_CR0(n) &= ~SSI_CR0_EACLRUN; \ +} while (0) + +#define __ssi_enable_tx_intr(n) \ + ( REG_SSI_CR0(n) |= SSI_CR0_TIE | SSI_CR0_TEIE ) + +#define __ssi_disable_tx_intr(n) \ + ( REG_SSI_CR0(n) &= ~(SSI_CR0_TIE | SSI_CR0_TEIE) ) + +#define __ssi_enable_rx_intr(n) \ + ( REG_SSI_CR0(n) |= SSI_CR0_RIE | SSI_CR0_REIE ) + +#define __ssi_disable_rx_intr(n) \ + ( REG_SSI_CR0(n) &= ~(SSI_CR0_RIE | SSI_CR0_REIE) ) + +#define __ssi_enable_txfifo_half_empty_intr(n) \ + ( REG_SSI_CR0(n) |= SSI_CR0_TIE ) +#define __ssi_disable_txfifo_half_empty_intr(n) \ + ( REG_SSI_CR0(n) &= ~SSI_CR0_TIE ) +#define __ssi_enable_tx_error_intr(n) \ + ( REG_SSI_CR0(n) |= SSI_CR0_TEIE ) +#define __ssi_disable_tx_error_intr(n) \ + ( REG_SSI_CR0(n) &= ~SSI_CR0_TEIE ) +#define __ssi_enable_rxfifo_half_full_intr(n) \ + ( REG_SSI_CR0(n) |= SSI_CR0_RIE ) +#define __ssi_disable_rxfifo_half_full_intr(n) \ + ( REG_SSI_CR0(n) &= ~SSI_CR0_RIE ) +#define __ssi_enable_rx_error_intr(n) \ + ( REG_SSI_CR0(n) |= SSI_CR0_REIE ) +#define __ssi_disable_rx_error_intr(n) \ + ( REG_SSI_CR0(n) &= ~SSI_CR0_REIE ) + +#define __ssi_enable_loopback(n) ( REG_SSI_CR0(n) |= SSI_CR0_LOOP ) +#define __ssi_disable_loopback(n) ( REG_SSI_CR0(n) &= ~SSI_CR0_LOOP ) + +#define __ssi_enable_receive(n) ( REG_SSI_CR0(n) &= ~SSI_CR0_DISREV ) +#define __ssi_disable_receive(n) ( REG_SSI_CR0(n) |= SSI_CR0_DISREV ) + +#define __ssi_finish_receive(n) \ + ( REG_SSI_CR0(n) |= (SSI_CR0_RFINE | SSI_CR0_RFINC) ) + +#define __ssi_disable_recvfinish(n) \ + ( REG_SSI_CR0(n) &= ~(SSI_CR0_RFINE | SSI_CR0_RFINC) ) + +#define __ssi_flush_txfifo(n) ( REG_SSI_CR0(n) |= SSI_CR0_TFLUSH ) +#define __ssi_flush_rxfifo(n) ( REG_SSI_CR0(n) |= SSI_CR0_RFLUSH ) + +#define __ssi_flush_fifo(n) \ + ( REG_SSI_CR0(n) |= SSI_CR0_TFLUSH | SSI_CR0_RFLUSH ) + +#define __ssi_finish_transmit(n) ( REG_SSI_CR1(n) &= ~SSI_CR1_UNFIN ) +#define __ssi_wait_transmit(n) ( REG_SSI_CR1(n) |= SSI_CR1_UNFIN ) +#define __ssi_use_busy_wait_mode(n) __ssi_wait_transmit(n) +#define __ssi_unset_busy_wait_mode(n) __ssi_finish_transmit(n) + +#define __ssi_spi_format(n) \ + do { \ + REG_SSI_CR1(n) &= ~SSI_CR1_FMAT_MASK; \ + REG_SSI_CR1(n) |= SSI_CR1_FMAT_SPI; \ + REG_SSI_CR1(n) &= ~(SSI_CR1_TFVCK_MASK|SSI_CR1_TCKFI_MASK); \ + REG_SSI_CR1(n) |= (SSI_CR1_TFVCK_1 | SSI_CR1_TCKFI_1); \ + } while (0) + +/* TI's SSP format, must clear SSI_CR1.UNFIN */ +#define __ssi_ssp_format(n) \ + do { \ + REG_SSI_CR1(n) &= ~(SSI_CR1_FMAT_MASK | SSI_CR1_UNFIN); \ + REG_SSI_CR1(n) |= SSI_CR1_FMAT_SSP; \ + } while (0) + +/* National's Microwire format, must clear SSI_CR0.RFINE, and set max delay */ +#define __ssi_microwire_format(n) \ + do { \ + REG_SSI_CR1(n) &= ~SSI_CR1_FMAT_MASK; \ + REG_SSI_CR1(n) |= SSI_CR1_FMAT_MW1; \ + REG_SSI_CR1(n) &= ~(SSI_CR1_TFVCK_MASK|SSI_CR1_TCKFI_MASK); \ + REG_SSI_CR1(n) |= (SSI_CR1_TFVCK_3 | SSI_CR1_TCKFI_3); \ + REG_SSI_CR0(n) &= ~SSI_CR0_RFINE; \ + } while (0) + +/* CE# level (FRMHL), CE# in interval time (ITFRM), + clock phase and polarity (PHA POL), + interval time (SSIITR), interval characters/frame (SSIICR) */ + +/* frmhl,endian,mcom,flen,pha,pol MASK */ +#define SSICR1_MISC_MASK \ + ( SSI_CR1_FRMHL_MASK | SSI_CR1_LFST | SSI_CR1_MCOM_MASK \ + | SSI_CR1_FLEN_MASK | SSI_CR1_PHA | SSI_CR1_POL ) + +#define __ssi_spi_set_misc(n,frmhl,endian,flen,mcom,pha,pol) \ + do { \ + REG_SSI_CR1(n) &= ~SSICR1_MISC_MASK; \ + REG_SSI_CR1(n) |= ((frmhl) << 30) | ((endian) << 25) | \ + (((mcom) - 1) << 12) | (((flen) - 2) << 4) | \ + ((pha) << 1) | (pol); \ + } while(0) + +/* Transfer with MSB or LSB first */ +#define __ssi_set_msb(n) ( REG_SSI_CR1(n) &= ~SSI_CR1_LFST ) +#define __ssi_set_lsb(n) ( REG_SSI_CR1(n) |= SSI_CR1_LFST ) + +#define __ssi_set_frame_length(n, m) \ + REG_SSI_CR1(n) = (REG_SSI_CR1(n) & ~SSI_CR1_FLEN_MASK) | (((m) - 2) << 4) + +/* m = 1 - 16 */ +#define __ssi_set_microwire_command_length(n,m) \ + ( REG_SSI_CR1(n) = ((REG_SSI_CR1(n) & ~SSI_CR1_MCOM_MASK) | SSI_CR1_MCOM_##m##BIT) ) + +/* Set the clock phase for SPI */ +#define __ssi_set_spi_clock_phase(n, m) \ + ( REG_SSI_CR1(n) = ((REG_SSI_CR1(n) & ~SSI_CR1_PHA) | (((m)&0x1)<< 1))) + +/* Set the clock polarity for SPI */ +#define __ssi_set_spi_clock_polarity(n, p) \ + ( REG_SSI_CR1(n) = ((REG_SSI_CR1(n) & ~SSI_CR1_POL) | ((p)&0x1)) ) + +/* SSI tx trigger, m = i x 8 */ +#define __ssi_set_tx_trigger(n, m) \ + do { \ + REG_SSI_CR1(n) &= ~SSI_CR1_TTRG_MASK; \ + REG_SSI_CR1(n) |= ((m)/8)<> SSI_SR_TFIFONUM_BIT ) + +#define __ssi_get_rxfifo_count(n) \ + ( (REG_SSI_SR(n) & SSI_SR_RFIFONUM_MASK) >> SSI_SR_RFIFONUM_BIT ) + +#define __ssi_transfer_end(n) ( REG_SSI_SR(n) & SSI_SR_END ) +#define __ssi_is_busy(n) ( REG_SSI_SR(n) & SSI_SR_BUSY ) + +#define __ssi_txfifo_full(n) ( REG_SSI_SR(n) & SSI_SR_TFF ) +#define __ssi_rxfifo_empty(n) ( REG_SSI_SR(n) & SSI_SR_RFE ) +#define __ssi_rxfifo_half_full(n) ( REG_SSI_SR(n) & SSI_SR_RFHF ) +#define __ssi_txfifo_half_empty(n) ( REG_SSI_SR(n) & SSI_SR_TFHE ) +#define __ssi_underrun(n) ( REG_SSI_SR(n) & SSI_SR_UNDR ) +#define __ssi_overrun(n) ( REG_SSI_SR(n) & SSI_SR_OVER ) +#define __ssi_clear_underrun(n) ( REG_SSI_SR(n) = ~SSI_SR_UNDR ) +#define __ssi_clear_overrun(n) ( REG_SSI_SR(n) = ~SSI_SR_OVER ) +#define __ssi_clear_errors(n) ( REG_SSI_SR(n) &= ~(SSI_SR_UNDR | SSI_SR_OVER) ) + +#define __ssi_set_clk(n, dev_clk, ssi_clk) \ + ( REG_SSI_GR(n) = (dev_clk) / (2*(ssi_clk)) - 1 ) + +#define __ssi_receive_data(n) REG_SSI_DR(n) +#define __ssi_transmit_data(n, v) (REG_SSI_DR(n) = (v)) + + +/*************************************************************************** + * CIM + ***************************************************************************/ + +#define __cim_enable() ( REG_CIM_CTRL |= CIM_CTRL_ENA ) +#define __cim_disable() ( REG_CIM_CTRL &= ~CIM_CTRL_ENA ) + +/* n = 0, 1, 2, 3 */ +#define __cim_set_input_data_stream_order(n) \ + do { \ + REG_CIM_CFG &= CIM_CFG_ORDER_MASK; \ + REG_CIM_CFG |= ((n)<>CIM_SIZE_LPF_BIT) +#define __cim_get_pixel() ((REG_CIM_SIZE&CIM_SIZE_PPL_MASK)>>CIM_SIZE_PPL_BIT) + +#define __cim_set_v_offset(a) ( REG_CIM_OFFSET = (REG_CIM_OFFSET&(~CIM_OFFSET_V_MASK)) | ((a)<>CIM_OFFSET_V_BIT) +#define __cim_get_h_offset() ((REG_CIM_OFFSET&CIM_OFFSET_H_MASK)>>CIM_OFFSET_H_BIT) + +/************************************************************************* + * SLCD (Smart LCD Controller) + *************************************************************************/ +#define __slcd_set_data_18bit() \ + ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_18BIT ) +#define __slcd_set_data_16bit() \ + ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_16BIT ) +#define __slcd_set_data_8bit_x3() \ + ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_8BIT_x3 ) +#define __slcd_set_data_8bit_x2() \ + ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_8BIT_x2 ) +#define __slcd_set_data_8bit_x1() \ + ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_8BIT_x1 ) +#define __slcd_set_data_24bit() \ + ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_24BIT ) +#define __slcd_set_data_9bit_x2() \ + ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_9BIT_x2 ) + +#define __slcd_set_cmd_16bit() \ + ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_CWIDTH_MASK) | SLCD_CFG_CWIDTH_16BIT ) +#define __slcd_set_cmd_8bit() \ + ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_CWIDTH_MASK) | SLCD_CFG_CWIDTH_8BIT ) +#define __slcd_set_cmd_18bit() \ + ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_CWIDTH_MASK) | SLCD_CFG_CWIDTH_18BIT ) +#define __slcd_set_cmd_24bit() \ + ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_CWIDTH_MASK) | SLCD_CFG_CWIDTH_24BIT ) + +#define __slcd_set_cs_high() ( REG_SLCD_CFG |= SLCD_CFG_CS_ACTIVE_HIGH ) +#define __slcd_set_cs_low() ( REG_SLCD_CFG &= ~SLCD_CFG_CS_ACTIVE_HIGH ) + +#define __slcd_set_rs_high() ( REG_SLCD_CFG |= SLCD_CFG_RS_CMD_HIGH ) +#define __slcd_set_rs_low() ( REG_SLCD_CFG &= ~SLCD_CFG_RS_CMD_HIGH ) + +#define __slcd_set_clk_falling() ( REG_SLCD_CFG &= ~SLCD_CFG_CLK_ACTIVE_RISING ) +#define __slcd_set_clk_rising() ( REG_SLCD_CFG |= SLCD_CFG_CLK_ACTIVE_RISING ) + +#define __slcd_set_parallel_type() ( REG_SLCD_CFG &= ~SLCD_CFG_TYPE_SERIAL ) +#define __slcd_set_serial_type() ( REG_SLCD_CFG |= SLCD_CFG_TYPE_SERIAL ) + +/* SLCD Control Register */ +#define __slcd_enable_dma() ( REG_SLCD_CTRL |= SLCD_CTRL_DMA_EN ) +#define __slcd_disable_dma() ( REG_SLCD_CTRL &= ~SLCD_CTRL_DMA_EN ) + +/* SLCD Status Register */ +#define __slcd_is_busy() ( REG_SLCD_STATE & SLCD_STATE_BUSY ) + +/* SLCD Data Register */ +#define __slcd_set_cmd_rs() ( REG_SLCD_DATA |= SLCD_DATA_RS_COMMAND) +#define __slcd_set_data_rs() ( REG_SLCD_DATA &= ~SLCD_DATA_RS_COMMAND) + + +/*************************************************************************** + * LCD + ***************************************************************************/ + +/*************************************************************************** + * LCD + ***************************************************************************/ +#define __lcd_as_smart_lcd() ( REG_LCD_CFG |= ( LCD_CFG_LCDPIN_SLCD | LCD_CFG_MODE_SLCD)) +#define __lcd_as_general_lcd() ( REG_LCD_CFG &= ~( LCD_CFG_LCDPIN_SLCD | LCD_CFG_MODE_SLCD)) + +#define __lcd_enable_tvepeh() ( REG_LCD_CFG |= LCD_CFG_TVEPEH ) +#define __lcd_disable_tvepeh() ( REG_LCD_CFG &= ~LCD_CFG_TVEPEH ) + +#define __lcd_enable_fuhold() ( REG_LCD_CFG |= LCD_CFG_FUHOLD ) +#define __lcd_disable_fuhold() ( REG_LCD_CFG &= ~LCD_CFG_FUHOLD ) + +#define __lcd_des_8word() ( REG_LCD_CFG |= LCD_CFG_NEWDES ) +#define __lcd_des_4word() ( REG_LCD_CFG &= ~LCD_CFG_NEWDES ) + +#define __lcd_enable_bypass_pal() ( REG_LCD_CFG |= LCD_CFG_PALBP ) +#define __lcd_disable_bypass_pal() ( REG_LCD_CFG &= ~LCD_CFG_PALBP ) + +#define __lcd_set_lcdpnl_term() ( REG_LCD_CTRL |= LCD_CFG_TVEN ) +#define __lcd_set_tv_term() ( REG_LCD_CTRL &= ~LCD_CFG_TVEN ) + +#define __lcd_enable_auto_recover() ( REG_LCD_CFG |= LCD_CFG_RECOVER ) +#define __lcd_disable_auto_recover() ( REG_LCD_CFG &= ~LCD_CFG_RECOVER ) + +#define __lcd_enable_dither() ( REG_LCD_CFG |= LCD_CFG_DITHER ) +#define __lcd_disable_dither() ( REG_LCD_CFG &= ~LCD_CFG_DITHER ) + +#define __lcd_disable_ps_mode() ( REG_LCD_CFG |= LCD_CFG_PSM ) +#define __lcd_enable_ps_mode() ( REG_LCD_CFG &= ~LCD_CFG_PSM ) + +#define __lcd_disable_cls_mode() ( REG_LCD_CFG |= LCD_CFG_CLSM ) +#define __lcd_enable_cls_mode() ( REG_LCD_CFG &= ~LCD_CFG_CLSM ) + +#define __lcd_disable_spl_mode() ( REG_LCD_CFG |= LCD_CFG_SPLM ) +#define __lcd_enable_spl_mode() ( REG_LCD_CFG &= ~LCD_CFG_SPLM ) + +#define __lcd_disable_rev_mode() ( REG_LCD_CFG |= LCD_CFG_REVM ) +#define __lcd_enable_rev_mode() ( REG_LCD_CFG &= ~LCD_CFG_REVM ) + +#define __lcd_disable_hsync_mode() ( REG_LCD_CFG |= LCD_CFG_HSYNM ) +#define __lcd_enable_hsync_mode() ( REG_LCD_CFG &= ~LCD_CFG_HSYNM ) + +#define __lcd_disable_pclk_mode() ( REG_LCD_CFG |= LCD_CFG_PCLKM ) +#define __lcd_enable_pclk_mode() ( REG_LCD_CFG &= ~LCD_CFG_PCLKM ) + +#define __lcd_normal_outdata() ( REG_LCD_CFG &= ~LCD_CFG_INVDAT ) +#define __lcd_inverse_outdata() ( REG_LCD_CFG |= LCD_CFG_INVDAT ) + +#define __lcd_sync_input() ( REG_LCD_CFG |= LCD_CFG_SYNDIR_IN ) +#define __lcd_sync_output() ( REG_LCD_CFG &= ~LCD_CFG_SYNDIR_IN ) + +#define __lcd_hsync_active_high() ( REG_LCD_CFG &= ~LCD_CFG_HSP ) +#define __lcd_hsync_active_low() ( REG_LCD_CFG |= LCD_CFG_HSP ) + +#define __lcd_pclk_rising() ( REG_LCD_CFG &= ~LCD_CFG_PCP ) +#define __lcd_pclk_falling() ( REG_LCD_CFG |= LCD_CFG_PCP ) + +#define __lcd_de_active_high() ( REG_LCD_CFG &= ~LCD_CFG_DEP ) +#define __lcd_de_active_low() ( REG_LCD_CFG |= LCD_CFG_DEP ) + +#define __lcd_vsync_rising() ( REG_LCD_CFG &= ~LCD_CFG_VSP ) +#define __lcd_vsync_falling() ( REG_LCD_CFG |= LCD_CFG_VSP ) + +#define __lcd_set_16_tftpnl() \ + ( REG_LCD_CFG = (REG_LCD_CFG & ~LCD_CFG_MODE_TFT_MASK) | LCD_CFG_MODE_TFT_16BIT ) + +#define __lcd_set_18_tftpnl() \ + ( REG_LCD_CFG = (REG_LCD_CFG & ~LCD_CFG_MODE_TFT_MASK) | LCD_CFG_MODE_TFT_18BIT ) + +#define __lcd_set_24_tftpnl() ( REG_LCD_CFG |= LCD_CFG_MODE_TFT_24BIT ) + +/* + * n=1,2,4,8 for single mono-STN + * n=4,8 for dual mono-STN + */ +#define __lcd_set_panel_datawidth(n) \ +do { \ + REG_LCD_CFG &= ~LCD_CFG_PDW_MASK; \ + REG_LCD_CFG |= LCD_CFG_PDW_n##; \ +} while (0) + +/* m = LCD_CFG_MODE_GENERUIC_TFT_xxx */ +#define __lcd_set_panel_mode(m) \ +do { \ + REG_LCD_CFG &= ~LCD_CFG_MODE_MASK; \ + REG_LCD_CFG |= (m); \ +} while(0) + +/* n=4,8,16 */ +#define __lcd_set_burst_length(n) \ +do { \ + REG_LCD_CTRL &= ~LCD_CTRL_BST_MASK; \ + REG_LCD_CTRL |= LCD_CTRL_BST_n##; \ +} while (0) + +#define __lcd_select_rgb565() ( REG_LCD_CTRL &= ~LCD_CTRL_RGB555 ) +#define __lcd_select_rgb555() ( REG_LCD_CTRL |= LCD_CTRL_RGB555 ) + +#define __lcd_set_ofup() ( REG_LCD_CTRL |= LCD_CTRL_OFUP ) +#define __lcd_clr_ofup() ( REG_LCD_CTRL &= ~LCD_CTRL_OFUP ) + +/* n=2,4,16 */ +#define __lcd_set_stn_frc(n) \ +do { \ + REG_LCD_CTRL &= ~LCD_CTRL_FRC_MASK; \ + REG_LCD_CTRL |= LCD_CTRL_FRC_n##; \ +} while (0) + +#define __lcd_enable_eof_intr() ( REG_LCD_CTRL |= LCD_CTRL_EOFM ) +#define __lcd_disable_eof_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_EOFM ) + +#define __lcd_enable_sof_intr() ( REG_LCD_CTRL |= LCD_CTRL_SOFM ) +#define __lcd_disable_sof_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_SOFM ) + +#define __lcd_enable_ofu_intr() ( REG_LCD_CTRL |= LCD_CTRL_OFUM ) +#define __lcd_disable_ofu_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_OFUM ) + +#define __lcd_enable_ifu0_intr() ( REG_LCD_CTRL |= LCD_CTRL_IFUM0 ) +#define __lcd_disable_ifu0_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_IFUM0 ) + +#define __lcd_enable_ifu1_intr() ( REG_LCD_CTRL |= LCD_CTRL_IFUM1 ) +#define __lcd_disable_ifu1_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_IFUM1 ) + +#define __lcd_enable_ldd_intr() ( REG_LCD_CTRL |= LCD_CTRL_LDDM ) +#define __lcd_disable_ldd_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_LDDM ) + +#define __lcd_enable_qd_intr() ( REG_LCD_CTRL |= LCD_CTRL_QDM ) +#define __lcd_disable_qd_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_QDM ) + +#define __lcd_reverse_byte_endian() ( REG_LCD_CTRL |= LCD_CTRL_BEDN ) +#define __lcd_normal_byte_endian() ( REG_LCD_CTRL &= ~LCD_CTRL_BEDN ) + +#define __lcd_pixel_endian_little() ( REG_LCD_CTRL |= LCD_CTRL_PEDN ) +#define __lcd_pixel_endian_big() ( REG_LCD_CTRL &= ~LCD_CTRL_PEDN ) + +#define __lcd_set_dis() ( REG_LCD_CTRL |= LCD_CTRL_DIS ) +#define __lcd_clr_dis() ( REG_LCD_CTRL &= ~LCD_CTRL_DIS ) + +#define __lcd_set_ena() ( REG_LCD_CTRL |= LCD_CTRL_ENA ) +#define __lcd_clr_ena() ( REG_LCD_CTRL &= ~LCD_CTRL_ENA ) + +/* n=1,2,4,8,16 */ +#define __lcd_set_bpp(n) \ + ( REG_LCD_CTRL = (REG_LCD_CTRL & ~LCD_CTRL_BPP_MASK) | LCD_CTRL_BPP_##n ) + +/* LCD status register indication */ + +#define __lcd_quick_disable_done() ( REG_LCD_STATE & LCD_STATE_QD ) +#define __lcd_disable_done() ( REG_LCD_STATE & LCD_STATE_LDD ) +#define __lcd_infifo0_underrun() ( REG_LCD_STATE & LCD_STATE_IFU0 ) +#define __lcd_infifo1_underrun() ( REG_LCD_STATE & LCD_STATE_IFU1 ) +#define __lcd_outfifo_underrun() ( REG_LCD_STATE & LCD_STATE_OFU ) +#define __lcd_start_of_frame() ( REG_LCD_STATE & LCD_STATE_SOF ) +#define __lcd_end_of_frame() ( REG_LCD_STATE & LCD_STATE_EOF ) + +#define __lcd_clr_outfifounderrun() ( REG_LCD_STATE &= ~LCD_STATE_OFU ) +#define __lcd_clr_sof() ( REG_LCD_STATE &= ~LCD_STATE_SOF ) +#define __lcd_clr_eof() ( REG_LCD_STATE &= ~LCD_STATE_EOF ) + +/* OSD functions */ +#define __lcd_enable_osd() (REG_LCD_OSDC |= LCD_OSDC_OSDEN) +#define __lcd_enable_f0() (REG_LCD_OSDC |= LCD_OSDC_F0EN) +#define __lcd_enable_f1() (REG_LCD_OSDC |= LCD_OSDC_F1EN) +#define __lcd_enable_alpha() (REG_LCD_OSDC |= LCD_OSDC_ALPHAEN) +#define __lcd_enable_alphamd() (REG_LCD_OSDC |= LCD_OSDC_ALPHAMD) + +#define __lcd_disable_osd() (REG_LCD_OSDC &= ~LCD_OSDC_OSDEN) +#define __lcd_disable_f0() (REG_LCD_OSDC &= ~LCD_OSDC_F0EN) +#define __lcd_disable_f1() (REG_LCD_OSDC &= ~LCD_OSDC_F1EN) +#define __lcd_disable_alpha() (REG_LCD_OSDC &= ~LCD_OSDC_ALPHAEN) +#define __lcd_disable_alphamd() (REG_LCD_OSDC &= ~LCD_OSDC_ALPHAMD) + +/* OSD Controll Register */ +#define __lcd_fg1_use_ipu() (REG_LCD_OSDCTRL |= LCD_OSDCTRL_IPU) +#define __lcd_fg1_use_dma_chan1() (REG_LCD_OSDCTRL &= ~LCD_OSDCTRL_IPU) +#define __lcd_fg1_unuse_ipu() __lcd_fg1_use_dma_chan1() +#define __lcd_osd_rgb555_mode() ( REG_LCD_OSDCTRL |= LCD_OSDCTRL_RGB555 ) +#define __lcd_osd_rgb565_mode() ( REG_LCD_OSDCTRL &= ~LCD_OSDCTRL_RGB555 ) +#define __lcd_osd_change_size() ( REG_LCD_OSDCTRL |= LCD_OSDCTRL_CHANGES ) +#define __lcd_osd_bpp_15_16() \ + ( REG_LCD_OSDCTRL = (REG_LCD_OSDCTRL & ~LCD_OSDCTRL_OSDBPP_MASK) | LCD_OSDCTRL_OSDBPP_15_16 ) +#define __lcd_osd_bpp_18_24() \ + ( REG_LCD_OSDCTRL = (REG_LCD_OSDCTRL & ~LCD_OSDCTRL_OSDBPP_MASK) | LCD_OSDCTRL_OSDBPP_18_24 ) + +/* OSD State Register */ +#define __lcd_start_of_fg1() ( REG_LCD_STATE & LCD_OSDS_SOF1 ) +#define __lcd_end_of_fg1() ( REG_LCD_STATE & LCD_OSDS_EOF1 ) +#define __lcd_start_of_fg0() ( REG_LCD_STATE & LCD_OSDS_SOF0 ) +#define __lcd_end_of_fg0() ( REG_LCD_STATE & LCD_OSDS_EOF0 ) +#define __lcd_change_is_rdy() ( REG_LCD_STATE & LCD_OSDS_READY ) + +/* Foreground Color Key Register 0,1(foreground 0, foreground 1) */ +#define __lcd_enable_colorkey0() (REG_LCD_KEY0 |= LCD_KEY_KEYEN) +#define __lcd_enable_colorkey1() (REG_LCD_KEY1 |= LCD_KEY_KEYEN) +#define __lcd_enable_colorkey0_md() (REG_LCD_KEY0 |= LCD_KEY_KEYMD) +#define __lcd_enable_colorkey1_md() (REG_LCD_KEY1 |= LCD_KEY_KEYMD) +#define __lcd_set_colorkey0(key) (REG_LCD_KEY0 = (REG_LCD_KEY0&~0xFFFFFF)|(key)) +#define __lcd_set_colorkey1(key) (REG_LCD_KEY1 = (REG_LCD_KEY1&~0xFFFFFF)|(key)) + +#define __lcd_disable_colorkey0() (REG_LCD_KEY0 &= ~LCD_KEY_KEYEN) +#define __lcd_disable_colorkey1() (REG_LCD_KEY1 &= ~LCD_KEY_KEYEN) +#define __lcd_disable_colorkey0_md() (REG_LCD_KEY0 &= ~LCD_KEY_KEYMD) +#define __lcd_disable_colorkey1_md() (REG_LCD_KEY1 &= ~LCD_KEY_KEYMD) + +/* IPU Restart Register */ +#define __lcd_enable_ipu_restart() (REG_LCD_IPUR |= LCD_IPUR_IPUREN) +#define __lcd_disable_ipu_restart() (REG_LCD_IPUR &= ~LCD_IPUR_IPUREN) +#define __lcd_set_ipu_restart_triger(n) (REG_LCD_IPUR = (REG_LCD_IPUR&(~0xFFFFFF))|(n)) + +/* RGB Control Register */ +#define __lcd_enable_rgb_dummy() (REG_LCD_RGBC |= LCD_RGBC_RGBDM) +#define __lcd_disable_rgb_dummy() (REG_LCD_RGBC &= ~LCD_RGBC_RGBDM) + +#define __lcd_dummy_rgb() (REG_LCD_RGBC |= LCD_RGBC_DMM) +#define __lcd_rgb_dummy() (REG_LCD_RGBC &= ~LCD_RGBC_DMM) + +#define __lcd_rgb2ycc() (REG_LCD_RGBC |= LCD_RGBC_YCC) +#define __lcd_notrgb2ycc() (REG_LCD_RGBC &= ~LCD_RGBC_YCC) + +#define __lcd_odd_mode_rgb() \ + ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_ODDRGB_MASK) | LCD_RGBC_ODD_RGB ) +#define __lcd_odd_mode_rbg() \ + ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_ODDRGB_MASK) | LCD_RGBC_ODD_RBG ) +#define __lcd_odd_mode_grb() \ + ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_ODDRGB_MASK) | LCD_RGBC_ODD_GRB) + +#define __lcd_odd_mode_gbr() \ + ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_ODDRGB_MASK) | LCD_RGBC_ODD_GBR) +#define __lcd_odd_mode_brg() \ + ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_ODDRGB_MASK) | LCD_RGBC_ODD_BRG) +#define __lcd_odd_mode_bgr() \ + ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_ODDRGB_MASK) | LCD_RGBC_ODD_BGR) + +#define __lcd_even_mode_rgb() \ + ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_EVENRGB_MASK) | LCD_RGBC_EVEN_RGB ) +#define __lcd_even_mode_rbg() \ + ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_EVENRGB_MASK) | LCD_RGBC_EVEN_RBG ) +#define __lcd_even_mode_grb() \ + ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_EVENRGB_MASK) | LCD_RGBC_EVEN_GRB) + +#define __lcd_even_mode_gbr() \ + ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_EVENRGB_MASK) | LCD_RGBC_EVEN_GBR) +#define __lcd_even_mode_brg() \ + ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_EVENRGB_MASK) | LCD_RGBC_EVEN_BRG) +#define __lcd_even_mode_bgr() \ + ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_EVENRGB_MASK) | LCD_RGBC_EVEN_BGR) + +/* Vertical Synchronize Register */ +#define __lcd_vsync_get_vps() \ + ( (REG_LCD_VSYNC & LCD_VSYNC_VPS_MASK) >> LCD_VSYNC_VPS_BIT ) + +#define __lcd_vsync_get_vpe() \ + ( (REG_LCD_VSYNC & LCD_VSYNC_VPE_MASK) >> LCD_VSYNC_VPE_BIT ) +#define __lcd_vsync_set_vpe(n) \ +do { \ + REG_LCD_VSYNC &= ~LCD_VSYNC_VPE_MASK; \ + REG_LCD_VSYNC |= (n) << LCD_VSYNC_VPE_BIT; \ +} while (0) + +#define __lcd_hsync_get_hps() \ + ( (REG_LCD_HSYNC & LCD_HSYNC_HPS_MASK) >> LCD_HSYNC_HPS_BIT ) +#define __lcd_hsync_set_hps(n) \ +do { \ + REG_LCD_HSYNC &= ~LCD_HSYNC_HPS_MASK; \ + REG_LCD_HSYNC |= (n) << LCD_HSYNC_HPS_BIT; \ +} while (0) + +#define __lcd_hsync_get_hpe() \ + ( (REG_LCD_HSYNC & LCD_HSYNC_HPE_MASK) >> LCD_VSYNC_HPE_BIT ) +#define __lcd_hsync_set_hpe(n) \ +do { \ + REG_LCD_HSYNC &= ~LCD_HSYNC_HPE_MASK; \ + REG_LCD_HSYNC |= (n) << LCD_HSYNC_HPE_BIT; \ +} while (0) + +#define __lcd_vat_get_ht() \ + ( (REG_LCD_VAT & LCD_VAT_HT_MASK) >> LCD_VAT_HT_BIT ) +#define __lcd_vat_set_ht(n) \ +do { \ + REG_LCD_VAT &= ~LCD_VAT_HT_MASK; \ + REG_LCD_VAT |= (n) << LCD_VAT_HT_BIT; \ +} while (0) + +#define __lcd_vat_get_vt() \ + ( (REG_LCD_VAT & LCD_VAT_VT_MASK) >> LCD_VAT_VT_BIT ) +#define __lcd_vat_set_vt(n) \ +do { \ + REG_LCD_VAT &= ~LCD_VAT_VT_MASK; \ + REG_LCD_VAT |= (n) << LCD_VAT_VT_BIT; \ +} while (0) + +#define __lcd_dah_get_hds() \ + ( (REG_LCD_DAH & LCD_DAH_HDS_MASK) >> LCD_DAH_HDS_BIT ) +#define __lcd_dah_set_hds(n) \ +do { \ + REG_LCD_DAH &= ~LCD_DAH_HDS_MASK; \ + REG_LCD_DAH |= (n) << LCD_DAH_HDS_BIT; \ +} while (0) + +#define __lcd_dah_get_hde() \ + ( (REG_LCD_DAH & LCD_DAH_HDE_MASK) >> LCD_DAH_HDE_BIT ) +#define __lcd_dah_set_hde(n) \ +do { \ + REG_LCD_DAH &= ~LCD_DAH_HDE_MASK; \ + REG_LCD_DAH |= (n) << LCD_DAH_HDE_BIT; \ +} while (0) + +#define __lcd_dav_get_vds() \ + ( (REG_LCD_DAV & LCD_DAV_VDS_MASK) >> LCD_DAV_VDS_BIT ) +#define __lcd_dav_set_vds(n) \ +do { \ + REG_LCD_DAV &= ~LCD_DAV_VDS_MASK; \ + REG_LCD_DAV |= (n) << LCD_DAV_VDS_BIT; \ +} while (0) + +#define __lcd_dav_get_vde() \ + ( (REG_LCD_DAV & LCD_DAV_VDE_MASK) >> LCD_DAV_VDE_BIT ) +#define __lcd_dav_set_vde(n) \ +do { \ + REG_LCD_DAV &= ~LCD_DAV_VDE_MASK; \ + REG_LCD_DAV |= (n) << LCD_DAV_VDE_BIT; \ +} while (0) + +/* DMA Command Register */ +#define __lcd_cmd0_set_sofint() ( REG_LCD_CMD0 |= LCD_CMD_SOFINT ) +#define __lcd_cmd0_clr_sofint() ( REG_LCD_CMD0 &= ~LCD_CMD_SOFINT ) +#define __lcd_cmd1_set_sofint() ( REG_LCD_CMD1 |= LCD_CMD_SOFINT ) +#define __lcd_cmd1_clr_sofint() ( REG_LCD_CMD1 &= ~LCD_CMD_SOFINT ) + +#define __lcd_cmd0_set_eofint() ( REG_LCD_CMD0 |= LCD_CMD_EOFINT ) +#define __lcd_cmd0_clr_eofint() ( REG_LCD_CMD0 &= ~LCD_CMD_EOFINT ) +#define __lcd_cmd1_set_eofint() ( REG_LCD_CMD1 |= LCD_CMD_EOFINT ) +#define __lcd_cmd1_clr_eofint() ( REG_LCD_CMD1 &= ~LCD_CMD_EOFINT ) + +#define __lcd_cmd0_set_pal() ( REG_LCD_CMD0 |= LCD_CMD_PAL ) +#define __lcd_cmd0_clr_pal() ( REG_LCD_CMD0 &= ~LCD_CMD_PAL ) + +#define __lcd_cmd0_get_len() \ + ( (REG_LCD_CMD0 & LCD_CMD_LEN_MASK) >> LCD_CMD_LEN_BIT ) +#define __lcd_cmd1_get_len() \ + ( (REG_LCD_CMD1 & LCD_CMD_LEN_MASK) >> LCD_CMD_LEN_BIT ) + +/************************************************************************* + * TVE (TV Encoder Controller) ops + *************************************************************************/ +/* TV Encoder Control register ops */ +#define __tve_soft_reset() (REG_TVE_CTRL |= TVE_CTRL_SWRST) + +#define __tve_output_colorbar() (REG_TVE_CTRL |= TVE_CTRL_CLBAR) +#define __tve_output_video() (REG_TVE_CTRL &= ~TVE_CTRL_CLBAR) + +#define __tve_input_cr_first() (REG_TVE_CTRL |= TVE_CTRL_CR1ST) +#define __tve_input_cb_first() (REG_TVE_CTRL &= ~TVE_CTRL_CR1ST) + +#define __tve_set_0_as_black() (REG_TVE_CTRL |= TVE_CTRL_ZBLACK) +#define __tve_set_16_as_black() (REG_TVE_CTRL &= ~TVE_CTRL_ZBLACK) + +#define __tve_ena_invert_top_bottom() (REG_TVE_CTRL |= TVE_CTRL_FINV) +#define __tve_dis_invert_top_bottom() (REG_TVE_CTRL &= ~TVE_CTRL_FINV) + +#define __tve_set_pal_mode() (REG_TVE_CTRL |= TVE_CTRL_PAL) +#define __tve_set_ntsc_mode() (REG_TVE_CTRL &= ~TVE_CTRL_PAL) + +#define __tve_set_pal_dura() (REG_TVE_CTRL |= TVE_CTRL_SYNCT) +#define __tve_set_ntsc_dura() (REG_TVE_CTRL &= ~TVE_CTRL_SYNCT) + +/* n = 0 ~ 3 */ +#define __tve_set_c_bandwidth(n) \ +do {\ + REG_TVE_CTRL &= ~TVE_CTRL_CBW_MASK;\ + REG_TVE_CTRL |= (n) << TVE_CTRL_CBW_BIT; \ +}while(0) + +/* n = 0 ~ 3 */ +#define __tve_set_c_gain(n) \ +do {\ + REG_TVE_CTRL &= ~TVE_CTRL_CGAIN_MASK;\ + (REG_TVE_CTRL |= (n) << TVE_CTRL_CGAIN_BIT; \ +}while(0) + +/* n = 0 ~ 7 */ +#define __tve_set_yc_delay(n) \ +do { \ + REG_TVE_CTRL &= ~TVE_CTRL_YCDLY_MASK \ + REG_TVE_CTRL |= ((n) << TVE_CTRL_YCDLY_BIT); \ +} while(0) + +#define __tve_disable_all_dacs() (REG_TVE_CTRL |= TVE_CTRL_DAPD) +#define __tve_disable_dac1() (REG_TVE_CTRL |= TVE_CTRL_DAPD1) +#define __tve_enable_dac1() (REG_TVE_CTRL &= ~TVE_CTRL_DAPD1) +#define __tve_disable_dac2() (REG_TVE_CTRL |= TVE_CTRL_DAPD2) +#define __tve_enable_dac2() (REG_TVE_CTRL &= ~TVE_CTRL_DAPD2) +#define __tve_disable_dac3() (REG_TVE_CTRL |= TVE_CTRL_DAPD3) +#define __tve_enable_dac3() (REG_TVE_CTRL &= ~TVE_CTRL_DAPD3) + +#define __tve_enable_svideo_fmt() (REG_TVE_CTRL |= TVE_CTRL_ECVBS) +#define __tve_enable_cvbs_fmt() (REG_TVE_CTRL &= ~TVE_CTRL_ECVBS) + +/* TV Encoder Frame Configure register ops */ +/* n = 0 ~ 255 */ +#define __tve_set_first_video_line(n) \ +do {\ + REG_TVE_FRCFG &= ~TVE_FRCFG_L1ST_MASK;\ + REG_TVE_FRCFG |= (n) << TVE_FRCFG_L1ST_BIT;\ +} while(0) +/* n = 0 ~ 1023 */ +#define __tve_set_line_num_per_frm(n) \ +do {\ + REG_TVE_FRCFG &= ~TVE_FRCFG_NLINE_MASK;\ + REG_TVE_CFG |= (n) << TVE_FRCFG_NLINE_BIT;\ +} while(0) +#define __tve_get_video_line_num()\ + (((REG_TVE_FRCFG & TVE_FRCFG_NLINE_MASK) >> TVE_FRCFG_NLINE_BIT) - 1 - 2 * ((REG_TVE_FRCFG & TVE_FRCFG_L1ST_MASK) >> TVE_FRCFG_L1ST_BIT)) + +/* TV Encoder Signal Level Configure register ops */ +/* n = 0 ~ 1023 */ +#define __tve_set_white_level(n) \ +do {\ + REG_TVE_SLCFG1 &= ~TVE_SLCFG1_WHITEL_MASK;\ + REG_TVE_SLCFG1 |= (n) << TVE_SLCFG1_WHITEL_BIT;\ +} while(0) +/* n = 0 ~ 1023 */ +#define __tve_set_black_level(n) \ +do {\ + REG_TVE_SLCFG1 &= ~TVE_SLCFG1_BLACKL_MASK;\ + REG_TVE_SLCFG1 |= (n) << TVE_SLCFG1_BLACKL_BIT;\ +} while(0) +/* n = 0 ~ 1023 */ +#define __tve_set_blank_level(n) \ +do {\ + REG_TVE_SLCFG2 &= ~TVE_SLCFG2_BLANKL_MASK;\ + REG_TVE_SLCFG2 |= (n) << TVE_SLCFG2_BLANKL_BIT;\ +} while(0) +/* n = 0 ~ 1023 */ +#define __tve_set_vbi_blank_level(n) \ +do {\ + REG_TVE_SLCFG2 &= ~TVE_SLCFG2_VBLANKL_MASK;\ + REG_TVE_SLCFG2 |= (n) << TVE_SLCFG2_VBLANKL_BIT;\ +} while(0) +/* n = 0 ~ 1023 */ +#define __tve_set_sync_level(n) \ +do {\ + REG_TVE_SLCFG3 &= ~TVE_SLCFG3_SYNCL_MASK;\ + REG_TVE_SLCFG3 |= (n) << TVE_SLCFG3_SYNCL_BIT;\ +} while(0) + +/* TV Encoder Signal Level Configure register ops */ +/* n = 0 ~ 31 */ +#define __tve_set_front_porch(n) \ +do {\ + REG_TVE_LTCFG1 &= ~TVE_LTCFG1_FRONTP_MASK;\ + REG_TVE_LTCFG1 |= (n) << TVE_LTCFG1_FRONTP_BIT; \ +} while(0) +/* n = 0 ~ 127 */ +#define __tve_set_hsync_width(n) \ +do {\ + REG_TVE_LTCFG1 &= ~TVE_LTCFG1_HSYNCW_MASK;\ + REG_TVE_LTCFG1 |= (n) << TVE_LTCFG1_HSYNCW_BIT; \ +} while(0) +/* n = 0 ~ 127 */ +#define __tve_set_back_porch(n) \ +do {\ + REG_TVE_LTCFG1 &= ~TVE_LTCFG1_BACKP_MASK;\ + REG_TVE_LTCFG1 |= (n) << TVE_LTCFG1_BACKP_BIT; \ +} while(0) +/* n = 0 ~ 2047 */ +#define __tve_set_active_linec(n) \ +do {\ + REG_TVE_LTCFG2 &= ~TVE_LTCFG2_ACTLIN_MASK;\ + REG_TVE_LTCFG2 |= (n) << TVE_LTCFG2_ACTLIN_BIT; \ +} while(0) +/* n = 0 ~ 31 */ +#define __tve_set_breezy_way(n) \ +do {\ + REG_TVE_LTCFG2 &= ~TVE_LTCFG2_PREBW_MASK;\ + REG_TVE_LTCFG2 |= (n) << TVE_LTCFG2_PREBW_BIT; \ +} while(0) + +/* n = 0 ~ 127 */ +#define __tve_set_burst_width(n) \ +do {\ + REG_TVE_LTCFG2 &= ~TVE_LTCFG2_BURSTW_MASK;\ + REG_TVE_LTCFG2 |= (n) << TVE_LTCFG2_BURSTW_BIT; \ +} while(0) + +/* TV Encoder Chrominance filter and Modulation register ops */ +/* n = 0 ~ (2^32-1) */ +#define __tve_set_c_sub_carrier_freq(n) REG_TVE_CFREQ = (n) +/* n = 0 ~ 255 */ +#define __tve_set_c_sub_carrier_init_phase(n) \ +do { \ + REG_TVE_CPHASE &= ~TVE_CPHASE_INITPH_MASK; \ + REG_TVE_CPHASE |= (n) << TVE_CPHASE_INITPH_BIT; \ +} while(0) +/* n = 0 ~ 255 */ +#define __tve_set_c_sub_carrier_act_phase(n) \ +do { \ + REG_TVE_CPHASE &= ~TVE_CPHASE_ACTPH_MASK; \ + REG_TVE_CPHASE |= (n) << TVE_CPHASE_ACTPH_BIT; \ +} while(0) +/* n = 0 ~ 255 */ +#define __tve_set_c_phase_rst_period(n) \ +do { \ + REG_TVE_CPHASE &= ~TVE_CPHASE_CCRSTP_MASK; \ + REG_TVE_CPHASE |= (n) << TVE_CPHASE_CCRSTP_BIT; \ +} while(0) +/* n = 0 ~ 255 */ +#define __tve_set_cb_burst_amp(n) \ +do { \ + REG_TVE_CBCRCFG &= ~TVE_CBCRCFG_CBBA_MASK; \ + REG_TVE_CBCRCFG |= (n) << TVE_CBCRCFG_CBBA_BIT; \ +} while(0) +/* n = 0 ~ 255 */ +#define __tve_set_cr_burst_amp(n) \ +do { \ + REG_TVE_CBCRCFG &= ~TVE_CBCRCFG_CRBA_MASK; \ + REG_TVE_CBCRCFG |= (n) << TVE_CBCRCFG_CRBA_BIT; \ +} while(0) +/* n = 0 ~ 255 */ +#define __tve_set_cb_gain_amp(n) \ +do { \ + REG_TVE_CBCRCFG &= ~TVE_CBCRCFG_CBGAIN_MASK; \ + REG_TVE_CBCRCFG |= (n) << TVE_CBCRCFG_CBGAIN_BIT; \ +} while(0) +/* n = 0 ~ 255 */ +#define __tve_set_cr_gain_amp(n) \ +do { \ + REG_TVE_CBCRCFG &= ~TVE_CBCRCFG_CRGAIN_MASK; \ + REG_TVE_CBCRCFG |= (n) << TVE_CBCRCFG_CRGAIN_BIT; \ +} while(0) + +/* TV Encoder Wide Screen Signal Control register ops */ +/* n = 0 ~ 7 */ +#define __tve_set_notch_freq(n) \ +do { \ + REG_TVE_WSSCR &= ~TVE_WSSCR_NCHFREQ_MASK; \ + REG_TVE_WSSCR |= (n) << TVE_WSSCR_NCHFREQ_BIT; \ +} while(0) +/* n = 0 ~ 7 */ +#define __tve_set_notch_width() (REG_TVE_WSSCR |= TVE_WSSCR_NCHW_BIT) +#define __tve_clear_notch_width() (REG_TVE_WSSCR &= ~TVE_WSSCR_NCHW_BIT) +#define __tve_enable_notch() (REG_TVE_WSSCR |= TVE_WSSCR_ENCH_BIT) +#define __tve_disable_notch() (REG_TVE_WSSCR &= ~TVE_WSSCR_ENCH_BIT) +/* n = 0 ~ 7 */ +#define __tve_set_wss_edge(n) \ +do { \ + REG_TVE_WSSCR &= ~TVE_WSSCR_WSSEDGE_MASK; \ + REG_TVE_WSSCR |= (n) << TVE_WSSCR_WSSEDGE_BIT; \ +} while(0) +#define __tve_set_wss_clkbyp() (REG_TVE_WSSCR |= TVE_WSSCR_WSSCKBP_BIT) +#define __tve_set_wss_type() (REG_TVE_WSSCR |= TVE_WSSCR_WSSTP_BIT) +#define __tve_enable_wssf1() (REG_TVE_WSSCR |= TVE_WSSCR_EWSS1_BIT) +#define __tve_enable_wssf0() (REG_TVE_WSSCR |= TVE_WSSCR_EWSS0_BIT) + +/* TV Encoder Wide Screen Signal Configure register 1, 2 and 3 ops */ +/* n = 0 ~ 1023 */ +#define __tve_set_wss_level(n) \ +do { \ + REG_TVE_WSSCFG1 &= ~TVE_WSSCFG1_WSSL_MASK; \ + REG_TVE_WSSCFG1 |= (n) << TVE_WSSCFG1_WSSL_BIT; \ +} while(0) +/* n = 0 ~ 4095 */ +#define __tve_set_wss_freq(n) \ +do { \ + REG_TVE_WSSCFG1 &= ~TVE_WSSCFG1_WSSFREQ_MASK; \ + REG_TVE_WSSCFG1 |= (n) << TVE_WSSCFG1_WSSFREQ_BIT; \ +} while(0) +/* n = 0, 1; l = 0 ~ 255 */ +#define __tve_set_wss_line(n,v) \ +do { \ + REG_TVE_WSSCFG##n &= ~TVE_WSSCFG_WSSLINE_MASK; \ + REG_TVE_WSSCFG##n |= (v) << TVE_WSSCFG_WSSLINE_BIT; \ +} while(0) +/* n = 0, 1; d = 0 ~ (2^20-1) */ +#define __tve_set_wss_data(n, v) \ +do { \ + REG_TVE_WSSCFG##n &= ~TVE_WSSCFG_WSSLINE_MASK; \ + REG_TVE_WSSCFG##n |= (v) << TVE_WSSCFG_WSSLINE_BIT; \ +} while(0) + +/*************************************************************************** + * RTC ops + ***************************************************************************/ + +#define __rtc_write_ready() ( (REG_RTC_RCR & RTC_RCR_WRDY) >> RTC_RCR_WRDY_BIT ) +#define __rtc_enabled() ( REG_RTC_RCR |= RTC_RCR_RTCE ) +#define __rtc_disabled() ( REG_RTC_RCR &= ~RTC_RCR_RTCE ) +#define __rtc_enable_alarm() ( REG_RTC_RCR |= RTC_RCR_AE ) +#define __rtc_disable_alarm() ( REG_RTC_RCR &= ~RTC_RCR_AE ) +#define __rtc_enable_alarm_irq() ( REG_RTC_RCR |= RTC_RCR_AIE ) +#define __rtc_disable_alarm_irq() ( REG_RTC_RCR &= ~RTC_RCR_AIE ) +#define __rtc_enable_1Hz_irq() ( REG_RTC_RCR |= RTC_RCR_1HZIE ) +#define __rtc_disable_1Hz_irq() ( REG_RTC_RCR &= ~RTC_RCR_1HZIE ) + +#define __rtc_get_1Hz_flag() ( (REG_RTC_RCR >> RTC_RCR_1HZ_BIT) & 0x1 ) +#define __rtc_clear_1Hz_flag() ( REG_RTC_RCR &= ~RTC_RCR_1HZ ) +#define __rtc_get_alarm_flag() ( (REG_RTC_RCR >> RTC_RCR_AF_BIT) & 0x1 ) +#define __rtc_clear_alarm_flag() ( REG_RTC_RCR &= ~RTC_RCR_AF ) + +#define __rtc_get_second() ( REG_RTC_RSR ) +#define __rtc_set_second(v) ( REG_RTC_RSR = v ) + +#define __rtc_get_alarm_second() ( REG_RTC_RSAR ) +#define __rtc_set_alarm_second(v) ( REG_RTC_RSAR = v ) + +#define __rtc_RGR_is_locked() ( (REG_RTC_RGR >> RTC_RGR_LOCK) ) +#define __rtc_lock_RGR() ( REG_RTC_RGR |= RTC_RGR_LOCK ) +#define __rtc_unlock_RGR() ( REG_RTC_RGR &= ~RTC_RGR_LOCK ) +#define __rtc_get_adjc_val() ( (REG_RTC_RGR & RTC_RGR_ADJC_MASK) >> RTC_RGR_ADJC_BIT ) +#define __rtc_set_adjc_val(v) \ + ( REG_RTC_RGR = ( (REG_RTC_RGR & ~RTC_RGR_ADJC_MASK) | (v << RTC_RGR_ADJC_BIT) )) +#define __rtc_get_nc1Hz_val() ( (REG_RTC_RGR & RTC_RGR_NC1HZ_MASK) >> RTC_RGR_NC1HZ_BIT ) +#define __rtc_set_nc1Hz_val(v) \ + ( REG_RTC_RGR = ( (REG_RTC_RGR & ~RTC_RGR_NC1HZ_MASK) | (v << RTC_RGR_NC1HZ_BIT) )) + +#define __rtc_power_down() ( REG_RTC_HCR |= RTC_HCR_PD ) + +#define __rtc_get_hwfcr_val() ( REG_RTC_HWFCR & RTC_HWFCR_MASK ) +#define __rtc_set_hwfcr_val(v) ( REG_RTC_HWFCR = (v) & RTC_HWFCR_MASK ) +#define __rtc_get_hrcr_val() ( REG_RTC_HRCR & RTC_HRCR_MASK ) +#define __rtc_set_hrcr_val(v) ( REG_RTC_HRCR = (v) & RTC_HRCR_MASK ) + +#define __rtc_enable_alarm_wakeup() ( REG_RTC_HWCR |= RTC_HWCR_EALM ) +#define __rtc_disable_alarm_wakeup() ( REG_RTC_HWCR &= ~RTC_HWCR_EALM ) + +#define __rtc_status_hib_reset_occur() ( (REG_RTC_HWRSR >> RTC_HWRSR_HR) & 0x1 ) +#define __rtc_status_ppr_reset_occur() ( (REG_RTC_HWRSR >> RTC_HWRSR_PPR) & 0x1 ) +#define __rtc_status_wakeup_pin_waken_up() ( (REG_RTC_HWRSR >> RTC_HWRSR_PIN) & 0x1 ) +#define __rtc_status_alarm_waken_up() ( (REG_RTC_HWRSR >> RTC_HWRSR_ALM) & 0x1 ) +#define __rtc_clear_hib_stat_all() ( REG_RTC_HWRSR = 0 ) + +#define __rtc_get_scratch_pattern() (REG_RTC_HSPR) +#define __rtc_set_scratch_pattern(n) (REG_RTC_HSPR = n ) + +/************************************************************************* + * BCH + *************************************************************************/ +#define __ecc_encoding_4bit() \ +do { \ + REG_BCH_CRS = BCH_CR_ENCE | BCH_CR_BRST | BCH_CR_BCHE; \ + REG_BCH_CRC = BCH_CR_BSEL8; \ +} while(0) +#define __ecc_decoding_4bit() \ +do { \ + REG_BCH_CRS = BCH_CR_BRST | BCH_CR_BCHE; \ + REG_BCH_CRC = BCH_CR_ENCE | BCH_CR_BSEL8; \ +} while(0) +#define __ecc_encoding_8bit() \ +do { \ + REG_BCH_CRS = BCH_CR_ENCE | BCH_CR_BRST | BCH_CR_BSEL8 | BCH_CR_BCHE; \ +} while(0) +#define __ecc_decoding_8bit() \ +do { \ + REG_BCH_CRS = BCH_CR_BRST | BCH_CR_BSEL8 | BCH_CR_BCHE; \ + REG_BCH_CRC = BCH_CR_ENCE; \ +} while(0) +#define __ecc_dma_enable() ( REG_BCH_CRS = BCH_CR_DMAE ) +#define __ecc_dma_disable() ( REG_BCH_CRC = BCH_CR_DMAE ) +#define __ecc_disable() ( REG_BCH_CRC = BCH_CR_BCHE ) +#define __ecc_encode_sync() while (!(REG_BCH_INTS & BCH_INTS_ENCF)) +#define __ecc_decode_sync() while (!(REG_BCH_INTS & BCH_INTS_DECF)) +#define __ecc_cnt_dec(n) \ +do { \ + REG_BCH_CNT &= ~(BCH_CNT_DEC_MASK << BCH_CNT_DEC_BIT); \ + REG_BCH_CNT = (n) << BCH_CNT_DEC_BIT; \ +} while(0) +#define __ecc_cnt_enc(n) \ +do { \ + REG_BCH_CNT &= ~(BCH_CNT_ENC_MASK << BCH_CNT_ENC_BIT); \ + REG_BCH_CNT = (n) << BCH_CNT_ENC_BIT; \ +} while(0) + +/*************************************************************************** + * OWI (one-wire bus) ops + ***************************************************************************/ + +/* OW control register ops */ +#define __owi_enable_all_interrupts() ( REG_OWI_CTL = (OWI_CTL_EBYTE | OWI_CTL_EBIT | OWI_CTL_ERST) ) +#define __owi_disable_all_interrupts() ( REG_OWI_CTL = 0 ) + +#define __owi_enable_byte_interrupt() ( REG_OWI_CTL |= OWI_CTL_EBYTE ) +#define __owi_disable_byte_interrupt() ( REG_OWI_CTL &= ~OWI_CTL_EBYTE ) +#define __owi_enable_bit_interrupt() ( REG_OWI_CTL |= OWI_CTL_EBIT ) +#define __owi_disable_bit_interrupt() ( REG_OWI_CTL &= ~OWI_CTL_EBIT ) +#define __owi_enable_rst_interrupt() ( REG_OWI_CTL |= OWI_CTL_ERST ) +#define __owi_disable_rst_interrupt() ( REG_OWI_CTL &=~OWI_CTL_ERST ) + +/* OW configure register ops */ +#define __owi_select_regular_mode() ( REG_OWI_CFG &= ~OWI_CFG_MODE ) +#define __owi_select_overdrive_mode() ( REG_OWI_CFG |= OWI_CFG_MODE ) + +#define __owi_set_rddata() ( REG_OWI_CFG |= OWI_CFG_RDDATA ) +#define __owi_clr_rddata() ( REG_OWI_CFG &= ~OWI_CFG_RDDATA ) +#define __owi_get_rddata() ( REG_OWI_CFG & OWI_CFG_RDDATA ) + +#define __owi_set_wrdata() ( REG_OWI_CFG |= OWI_CFG_WRDATA ) +#define __owi_clr_wrdata() ( REG_OWI_CFG &= ~OWI_CFG_WRDATA ) +#define __owi_get_wrdata() ( REG_OWI_CFG & OWI_CFG_WRDATA ) + +#define __owi_get_rdst() ( REG_OWI_CFG & OWI_CFG_RDST ) + +#define __owi_set_wr1rd() ( REG_OWI_CFG |= OWI_CFG_WR1RD ) +#define __owi_clr_wr1rd() ( REG_OWI_CFG &= ~OWI_CFG_WR1RD ) +#define __owi_get_wr1rd() ( REG_OWI_CFG & OWI_CFG_WR1RD ) + +#define __owi_set_wr0() ( REG_OWI_CFG |= OWI_CFG_WR0 ) +#define __owi_clr_wr0() ( REG_OWI_CFG &= ~OWI_CFG_WR0 ) +#define __owi_get_wr0() ( REG_OWI_CFG & OWI_CFG_WR0 ) + +#define __owi_set_rst() ( REG_OWI_CFG |= OWI_CFG_RST ) +#define __owi_clr_rst() ( REG_OWI_CFG &= ~OWI_CFG_RST ) +#define __owi_get_rst() ( REG_OWI_CFG & OWI_CFG_RST ) + +#define __owi_enable_ow_ops() ( REG_OWI_CFG |= OWI_CFG_ENA ) +#define __owi_disable_ow_ops() ( REG_OWI_CFG &= ~OWI_CFG_ENA ) +#define __owi_get_enable() ( REG_OWI_CFG & OWI_CFG_ENA ) + +#define __owi_wait_ops_rdy() \ + do { \ + while(__owi_get_enable()); \ + udelay(1); \ + } while(0); + +/* OW status register ops */ +#define __owi_clr_sts() ( REG_OWI_STS = 0 ) +#define __owi_get_sts_pst() ( REG_OWI_STS & OWI_STS_PST ) +#define __owi_get_sts_byte_rdy() ( REG_OWI_STS & OWI_STS_BYTE_RDY ) +#define __owi_get_sts_bit_rdy() ( REG_OWI_STS & OWI_STS_BIT_RDY ) +#define __owi_get_sts_pst_rdy() ( REG_OWI_STS & OWI_STS_PST_RDY ) + +/************************************************************************* + * TSSI MPEG 2-TS slave interface operation + *************************************************************************/ +#define __tssi_enable() ( REG_TSSI_ENA |= TSSI_ENA_ENA ) +#define __tssi_disable() ( REG_TSSI_ENA &= ~TSSI_ENA_ENA ) +#define __tssi_soft_reset() ( REG_TSSI_ENA |= TSSI_ENA_SFT_RST ) +#define __tssi_dma_enable() ( REG_TSSI_ENA |= TSSI_ENA_DMA_EN ) +#define __tssi_dma_disable() ( REG_TSSI_ENA &= ~TSSI_ENA_DMA_EN ) +#define __tssi_filter_enable() ( REG_TSSI_ENA |= TSSI_ENA_PID_EN ) +#define __tssi_filter_disable() ( REG_TSSI_ENA &= ~TSSI_ENA_PID_EN ) + +/* n = 4, 8, 16 */ +#define __tssi_set_tigger_num(n) \ + do { \ + REG_TSSI_CFG &= ~TSSI_CFG_TRIG_MASK; \ + REG_TSSI_CFG |= TSSI_CFG_TRIG_##n; \ + } while (0) + +#define __tssi_set_wd_1() ( REG_TSSI_CFG |= TSSI_CFG_END_WD ) +#define __tssi_set_wd_0() ( REG_TSSI_CFG &= ~TSSI_CFG_END_WD ) + +#define __tssi_set_bt_1() ( REG_TSSI_CFG |= TSSI_CFG_END_BD ) +#define __tssi_set_bt_0() ( REG_TSSI_CFG &= ~TSSI_CFG_END_BD ) + +#define __tssi_set_data_pola_high() ( REG_TSSI_CFG |= TSSI_CFG_TSDI_H ) +#define __tssi_set_data_pola_low() ( REG_TSSI_CFG &= ~TSSI_CFG_TSDI_H ) + +#define __tssi_set_data_use_data0() ( REG_TSSI_CFG |= TSSI_CFG_USE_0 ) +#define __tssi_set_data_use_data7() ( REG_TSSI_CFG &= ~TSSI_CFG_USE_0 ) + +#define __tssi_select_clk_fast() ( REG_TSSI_CFG &= ~TSSI_CFG_TSCLK_CH ) +#define __tssi_select_clk_slow() ( REG_TSSI_CFG |= TSSI_CFG_TSCLK_CH ) + +#define __tssi_select_serail_mode() ( REG_TSSI_CFG &= ~TSSI_CFG_PARAL ) +#define __tssi_select_paral_mode() ( REG_TSSI_CFG |= TSSI_CFG_PARAL ) + +#define __tssi_select_clk_nega_edge() ( REG_TSSI_CFG &= ~TSSI_CFG_TSCLK_P ) +#define __tssi_select_clk_posi_edge() ( REG_TSSI_CFG |= TSSI_CFG_TSCLK_P ) + +#define __tssi_select_frm_act_high() ( REG_TSSI_CFG |= TSSI_CFG_TSFRM_H ) +#define __tssi_select_frm_act_low() ( REG_TSSI_CFG &= ~TSSI_CFG_TSFRM_H ) + +#define __tssi_select_str_act_high() ( REG_TSSI_CFG |= TSSI_CFG_TSSTR_H ) +#define __tssi_select_str_act_low() ( REG_TSSI_CFG &= ~TSSI_CFG_TSSTR_H ) + +#define __tssi_select_fail_act_high() ( REG_TSSI_CFG |= TSSI_CFG_TSFAIL_H ) +#define __tssi_select_fail_act_low() ( REG_TSSI_CFG &= ~TSSI_CFG_TSFAIL_H ) + +#define __tssi_enable_ovrn_irq() ( REG_TSSI_CTRL &= ~TSSI_CTRL_OVRNM ) +#define __tssi_disable_ovrn_irq() ( REG_TSSI_CTRL |= TSSI_CTRL_OVRNM ) + +#define __tssi_enable_trig_irq() ( REG_TSSI_CTRL &= ~TSSI_CTRL_TRIGM ) +#define __tssi_disable_trig_irq() ( REG_TSSI_CTRL |= TSSI_CTRL_TRIGM ) + +#define __tssi_state_is_overrun() ( REG_TSSI_STAT & TSSI_STAT_OVRN ) +#define __tssi_state_trigger_meet() ( REG_TSSI_STAT & TSSI_STAT_TRIG ) +#define __tssi_clear_state() ( REG_TSSI_STAT = 0 ) /* write 0??? */ +#define __tssi_state_clear_overrun() ( REG_TSSI_STAT = TSSI_STAT_OVRN ) + +#define __tssi_enable_filte_pid0() ( REG_TSSI_PEN |= TSSI_PEN_PID0 ) +#define __tssi_disable_filte_pid0() ( REG_TSSI_PEN &= ~TSSI_PEN_PID0 ) + +/* m = 0, ..., 15 */ +#define __tssi_enable_pid_filter(m) \ + do { \ + int n = (m); \ + if ( n>=0 && n <(TSSI_PID_MAX*2) ) { \ + if ( n >= TSSI_PID_MAX ) n += 8; \ + REG_TSSI_PEN |= ( 1 << n ); \ + } \ + } while (0) + +/* m = 0, ..., 15 */ +#define __tssi_disable_pid_filter(m) \ + do { \ + int n = (m); \ + if ( n>=0 && n <(TSSI_PID_MAX*2) ) { \ + if ( n >= TSSI_PID_MAX ) n += 8; \ + REG_TSSI_PEN &= ~( 1 << n ); \ + } \ + } while (0) + +/* n = 0, ..., 7 */ +#define __tssi_set_pid0(n, pid0) \ + do { \ + REG_TSSI_PID(n) &= ~TSSI_PID_PID0_MASK; \ + REG_TSSI_PID(n) |= ((pid0)<=0 && n < TSSI_PID_MAX*2) { \ + if ( n < TSSI_PID_MAX ) \ + __tssi_set_pid0(n, pid); \ + else \ + __tssi_set_pid1(n-TSSI_PID_MAX, pid); \ + } \ + }while (0) + + +#if 0 +/************************************************************************* + * IPU (Image Processing Unit) + *************************************************************************/ +#define u32 volatile unsigned long + +#define write_reg(reg, val) \ +do { \ + *(u32 *)(reg) = (val); \ +} while(0) + +#define read_reg(reg, off) (*(u32 *)((reg)+(off))) + + +#define set_ipu_fmt(rgb_888_out_fmt, rgb_out_oft, out_fmt, yuv_pkg_out, in_oft, in_fmt ) \ +({ write_reg( (IPU_V_BASE + REG_D_FMT), ((in_fmt) & IN_FMT_MSK)< Unsigned toggle enable */ +#define AIC_CR_FLUSH (1 << 8) /* Flush FIFO */ +#define AIC_CR_EROR (1 << 6) /* Enable ROR interrupt */ +#define AIC_CR_ETUR (1 << 5) /* Enable TUR interrupt */ +#define AIC_CR_ERFS (1 << 4) /* Enable RFS interrupt */ +#define AIC_CR_ETFS (1 << 3) /* Enable TFS interrupt */ +#define AIC_CR_ENLBF (1 << 2) /* Enable Loopback Function */ +#define AIC_CR_ERPL (1 << 1) /* Enable Playback Function */ +#define AIC_CR_EREC (1 << 0) /* Enable Record Function */ + +/* AIC Controller AC-link Control Register 1 (AIC_ACCR1) */ + +#define AIC_ACCR1_RS_BIT 16 /* Receive Valid Slots */ +#define AIC_ACCR1_RS_MASK (0x3ff << AIC_ACCR1_RS_BIT) + #define AIC_ACCR1_RS_SLOT12 (1 << 25) /* Slot 12 valid bit */ + #define AIC_ACCR1_RS_SLOT11 (1 << 24) /* Slot 11 valid bit */ + #define AIC_ACCR1_RS_SLOT10 (1 << 23) /* Slot 10 valid bit */ + #define AIC_ACCR1_RS_SLOT9 (1 << 22) /* Slot 9 valid bit, LFE */ + #define AIC_ACCR1_RS_SLOT8 (1 << 21) /* Slot 8 valid bit, Surround Right */ + #define AIC_ACCR1_RS_SLOT7 (1 << 20) /* Slot 7 valid bit, Surround Left */ + #define AIC_ACCR1_RS_SLOT6 (1 << 19) /* Slot 6 valid bit, PCM Center */ + #define AIC_ACCR1_RS_SLOT5 (1 << 18) /* Slot 5 valid bit */ + #define AIC_ACCR1_RS_SLOT4 (1 << 17) /* Slot 4 valid bit, PCM Right */ + #define AIC_ACCR1_RS_SLOT3 (1 << 16) /* Slot 3 valid bit, PCM Left */ +#define AIC_ACCR1_XS_BIT 0 /* Transmit Valid Slots */ +#define AIC_ACCR1_XS_MASK (0x3ff << AIC_ACCR1_XS_BIT) + #define AIC_ACCR1_XS_SLOT12 (1 << 9) /* Slot 12 valid bit */ + #define AIC_ACCR1_XS_SLOT11 (1 << 8) /* Slot 11 valid bit */ + #define AIC_ACCR1_XS_SLOT10 (1 << 7) /* Slot 10 valid bit */ + #define AIC_ACCR1_XS_SLOT9 (1 << 6) /* Slot 9 valid bit, LFE */ + #define AIC_ACCR1_XS_SLOT8 (1 << 5) /* Slot 8 valid bit, Surround Right */ + #define AIC_ACCR1_XS_SLOT7 (1 << 4) /* Slot 7 valid bit, Surround Left */ + #define AIC_ACCR1_XS_SLOT6 (1 << 3) /* Slot 6 valid bit, PCM Center */ + #define AIC_ACCR1_XS_SLOT5 (1 << 2) /* Slot 5 valid bit */ + #define AIC_ACCR1_XS_SLOT4 (1 << 1) /* Slot 4 valid bit, PCM Right */ + #define AIC_ACCR1_XS_SLOT3 (1 << 0) /* Slot 3 valid bit, PCM Left */ + +/* AIC Controller AC-link Control Register 2 (AIC_ACCR2) */ + +#define AIC_ACCR2_ERSTO (1 << 18) /* Enable RSTO interrupt */ +#define AIC_ACCR2_ESADR (1 << 17) /* Enable SADR interrupt */ +#define AIC_ACCR2_ECADT (1 << 16) /* Enable CADT interrupt */ +#define AIC_ACCR2_OASS_BIT 8 /* Output Sample Size for AC-link */ +#define AIC_ACCR2_OASS_MASK (0x3 << AIC_ACCR2_OASS_BIT) + #define AIC_ACCR2_OASS_20BIT (0 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 20-bit */ + #define AIC_ACCR2_OASS_18BIT (1 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 18-bit */ + #define AIC_ACCR2_OASS_16BIT (2 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 16-bit */ + #define AIC_ACCR2_OASS_8BIT (3 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 8-bit */ +#define AIC_ACCR2_IASS_BIT 6 /* Output Sample Size for AC-link */ +#define AIC_ACCR2_IASS_MASK (0x3 << AIC_ACCR2_IASS_BIT) + #define AIC_ACCR2_IASS_20BIT (0 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 20-bit */ + #define AIC_ACCR2_IASS_18BIT (1 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 18-bit */ + #define AIC_ACCR2_IASS_16BIT (2 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 16-bit */ + #define AIC_ACCR2_IASS_8BIT (3 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 8-bit */ +#define AIC_ACCR2_SO (1 << 3) /* SDATA_OUT output value */ +#define AIC_ACCR2_SR (1 << 2) /* RESET# pin level */ +#define AIC_ACCR2_SS (1 << 1) /* SYNC pin level */ +#define AIC_ACCR2_SA (1 << 0) /* SYNC and SDATA_OUT alternation */ + +/* AIC Controller I2S/MSB-justified Control Register (AIC_I2SCR) */ + +#define AIC_I2SCR_STPBK (1 << 12) /* Stop BIT_CLK for I2S/MSB-justified */ +#define AIC_I2SCR_WL_BIT 1 /* Input/Output Sample Size for I2S/MSB-justified */ +#define AIC_I2SCR_WL_MASK (0x7 << AIC_I2SCR_WL_BIT) + #define AIC_I2SCR_WL_24BIT (0 << AIC_I2SCR_WL_BIT) /* Word Length is 24 bit */ + #define AIC_I2SCR_WL_20BIT (1 << AIC_I2SCR_WL_BIT) /* Word Length is 20 bit */ + #define AIC_I2SCR_WL_18BIT (2 << AIC_I2SCR_WL_BIT) /* Word Length is 18 bit */ + #define AIC_I2SCR_WL_16BIT (3 << AIC_I2SCR_WL_BIT) /* Word Length is 16 bit */ + #define AIC_I2SCR_WL_8BIT (4 << AIC_I2SCR_WL_BIT) /* Word Length is 8 bit */ +#define AIC_I2SCR_AMSL (1 << 0) /* 0:I2S, 1:MSB-justified */ + +/* AIC Controller FIFO Status Register (AIC_SR) */ + +#define AIC_SR_RFL_BIT 24 /* Receive FIFO Level */ +#define AIC_SR_RFL_MASK (0x3f << AIC_SR_RFL_BIT) +#define AIC_SR_TFL_BIT 8 /* Transmit FIFO level */ +#define AIC_SR_TFL_MASK (0x3f << AIC_SR_TFL_BIT) +#define AIC_SR_ROR (1 << 6) /* Receive FIFO Overrun */ +#define AIC_SR_TUR (1 << 5) /* Transmit FIFO Underrun */ +#define AIC_SR_RFS (1 << 4) /* Receive FIFO Service Request */ +#define AIC_SR_TFS (1 << 3) /* Transmit FIFO Service Request */ + +/* AIC Controller AC-link Status Register (AIC_ACSR) */ + +#define AIC_ACSR_SLTERR (1 << 21) /* Slot Error Flag */ +#define AIC_ACSR_CRDY (1 << 20) /* External CODEC Ready Flag */ +#define AIC_ACSR_CLPM (1 << 19) /* External CODEC low power mode flag */ +#define AIC_ACSR_RSTO (1 << 18) /* External CODEC regs read status timeout */ +#define AIC_ACSR_SADR (1 << 17) /* External CODEC regs status addr and data received */ +#define AIC_ACSR_CADT (1 << 16) /* Command Address and Data Transmitted */ + +/* AIC Controller I2S/MSB-justified Status Register (AIC_I2SSR) */ + +#define AIC_I2SSR_BSY (1 << 2) /* AIC Busy in I2S/MSB-justified format */ + +/* AIC Controller AC97 codec Command Address Register (AIC_ACCAR) */ + +#define AIC_ACCAR_CAR_BIT 0 +#define AIC_ACCAR_CAR_MASK (0xfffff << AIC_ACCAR_CAR_BIT) + +/* AIC Controller AC97 codec Command Data Register (AIC_ACCDR) */ + +#define AIC_ACCDR_CDR_BIT 0 +#define AIC_ACCDR_CDR_MASK (0xfffff << AIC_ACCDR_CDR_BIT) + +/* AIC Controller AC97 codec Status Address Register (AIC_ACSAR) */ + +#define AIC_ACSAR_SAR_BIT 0 +#define AIC_ACSAR_SAR_MASK (0xfffff << AIC_ACSAR_SAR_BIT) + +/* AIC Controller AC97 codec Status Data Register (AIC_ACSDR) */ + +#define AIC_ACSDR_SDR_BIT 0 +#define AIC_ACSDR_SDR_MASK (0xfffff << AIC_ACSDR_SDR_BIT) + +/* AIC Controller I2S/MSB-justified Clock Divider Register (AIC_I2SDIV) */ + +#define AIC_I2SDIV_DIV_BIT 0 +#define AIC_I2SDIV_DIV_MASK (0x7f << AIC_I2SDIV_DIV_BIT) + #define AIC_I2SDIV_BITCLK_3072KHZ (0x0C << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 3.072MHz */ + #define AIC_I2SDIV_BITCLK_2836KHZ (0x0D << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 2.836MHz */ + #define AIC_I2SDIV_BITCLK_1418KHZ (0x1A << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 1.418MHz */ + #define AIC_I2SDIV_BITCLK_1024KHZ (0x24 << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 1.024MHz */ + #define AIC_I2SDIV_BITCLK_7089KHZ (0x34 << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 708.92KHz */ + #define AIC_I2SDIV_BITCLK_512KHZ (0x48 << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 512.00KHz */ + + +/************************************************************************* + * ICDC (Internal CODEC) + *************************************************************************/ + +#define ICDC_CKCFG (ICDC_BASE + 0x00a0) /* Clock Configure Register */ +#define ICDC_RGADW (ICDC_BASE + 0x00a4) /* internal register access control */ +#define ICDC_RGDATA (ICDC_BASE + 0x00a8) /* internal register data output */ + +#define REG_ICDC_CKCFG REG32(ICDC_CKCFG) +#define REG_ICDC_RGADW REG32(ICDC_RGADW) +#define REG_ICDC_RGDATA REG32(ICDC_RGDATA) + +/* ICDC Clock Configure Register */ +#define ICDC_CKCFG_CKRDY (1 << 1) +#define ICDC_CKCFG_SELAD (1 << 0) + +/* ICDC internal register access control Register */ +#define ICDC_RGADW_RGWR (1 << 16) +#define ICDC_RGADW_RGADDR_BIT 8 +#define ICDC_RGADW_RGADDR_MASK (0x7f << ICDC_RGADW_RGADDR_BIT) +#define ICDC_RGADW_RGDIN_BIT 0 +#define ICDC_RGADW_RGDIN_MASK (0xff << ICDC_RGADW_RGDIN_BIT) + +/* ICDC internal register data output Register */ +#define ICDC_RGDATA_IRQ (1 << 8) +#define ICDC_RGDATA_RGDOUT_BIT 0 +#define ICDC_RGDATA_RGDOUT_MASK (0xff << ICDC_RGDATA_RGDOUT_BIT) + +/************************************************************************* + * PCM Controller + *************************************************************************/ + +#define PCM_CTL (PCM_BASE + 0x000) +#define PCM_CFG (PCM_BASE + 0x004) +#define PCM_DP (PCM_BASE + 0x008) +#define PCM_INTC (PCM_BASE + 0x00c) +#define PCM_INTS (PCM_BASE + 0x010) +#define PCM_DIV (PCM_BASE + 0x014) + +#define REG_PCM_CTL REG32(PCM_CTL) +#define REG_PCM_CFG REG32(PCM_CFG) +#define REG_PCM_DP REG32(PCM_DP) +#define REG_PCM_INTC REG32(PCM_INTC) +#define REG_PCM_INTS REG32(PCM_INTS) +#define REG_PCM_DIV REG32(PCM_DIV) + +/* PCM Controller control Register (PCM_CTL) */ + +#define PCM_CTL_ERDMA (1 << 9) /* Enable Receive DMA */ +#define PCM_CTL_ETDMA (1 << 8) /* Enable Transmit DMA */ +#define PCM_CTL_LSMP (1 << 7) /* Play Zero sample or last sample */ +#define PCM_CTL_ERPL (1 << 6) /* Enable Playing Back Function */ +#define PCM_CTL_EREC (1 << 5) /* Enable Recording Function */ +#define PCM_CTL_FLUSH (1 << 4) /* FIFO flush */ +#define PCM_CTL_RST (1 << 3) /* Reset PCM */ +#define PCM_CTL_CLKEN (1 << 1) /* Enable the clock division logic */ +#define PCM_CTL_PCMEN (1 << 0) /* Enable PCM module */ + +/* PCM Controller configure Register (PCM_CFG) */ + +#define PCM_CFG_SLOT_BIT 13 +#define PCM_CFG_SLOT_MASK (0x3 << PCM_CFG_SLOT_BIT) + #define PCM_CFG_SLOT_0 (0 << PCM_CFG_SLOT_BIT) /* Slot is 0 */ + #define PCM_CFG_SLOT_1 (1 << PCM_CFG_SLOT_BIT) /* Slot is 1 */ + #define PCM_CFG_SLOT_2 (2 << PCM_CFG_SLOT_BIT) /* Slot is 2 */ + #define PCM_CFG_SLOT_3 (3 << PCM_CFG_SLOT_BIT) /* Slot is 3 */ +#define PCM_CFG_ISS_BIT 12 +#define PCM_CFG_ISS_MASK (0x1 << PCM_CFG_ISS_BIT) + #define PCM_CFG_ISS_8 (0 << PCM_CFG_ISS_BIT) + #define PCM_CFG_ISS_16 (1 << PCM_CFG_ISS_BIT) +#define PCM_CFG_OSS_BIT 11 +#define PCM_CFG_OSS_MASK (0x1 << PCM_CFG_OSS_BIT) + #define PCM_CFG_OSS_8 (0 << PCM_CFG_OSS_BIT) + #define PCM_CFG_OSS_16 (1 << PCM_CFG_OSS_BIT) +#define PCM_CFG_IMSBPOS (1 << 10) +#define PCM_CFG_OMSBPOS (1 << 9) +#define PCM_CFG_RFTH_BIT 5 /* Receive FIFO Threshold */ +#define PCM_CFG_RFTH_MASK (0xf << PCM_CFG_RFTH_BIT) +#define PCM_CFG_TFTH_BIT 1 /* Transmit FIFO Threshold */ +#define PCM_CFG_TFTH_MASK (0xf << PCM_CFG_TFTH_BIT) +#define PCM_CFG_MODE (0x0 << 0) + +/* PCM Controller interrupt control Register (PCM_INTC) */ + +#define PCM_INTC_ETFS (1 << 3) +#define PCM_INTC_ETUR (1 << 2) +#define PCM_INTC_ERFS (1 << 1) +#define PCM_INTC_EROR (1 << 0) + +/* PCM Controller interrupt status Register (PCM_INTS) */ + +#define PCM_INTS_RSTS (1 << 14) /* Reset or flush has not complete */ +#define PCM_INTS_TFL_BIT 9 +#define PCM_INTS_TFL_MASK (0x1f << PCM_INTS_TFL_BIT) +#define PCM_INTS_TFS (1 << 8) /* Tranmit FIFO Service Request */ +#define PCM_INTS_TUR (1 << 7) /* Transmit FIFO Under Run */ +#define PCM_INTS_RFL_BIT 2 +#define PCM_INTS_RFL_MASK (0x1f << PCM_INTS_RFL_BIT) +#define PCM_INTS_RFS (1 << 1) /* Receive FIFO Service Request */ +#define PCM_INTS_ROR (1 << 0) /* Receive FIFO Over Run */ + +/* PCM Controller clock division Register (PCM_DIV) */ +#define PCM_DIV_SYNL_BIT 11 +#define PCM_DIV_SYNL_MASK (0x3f << PCM_DIV_SYNL_BIT) +#define PCM_DIV_SYNDIV_BIT 6 +#define PCM_DIV_SYNDIV_MASK (0x1f << PCM_DIV_SYNDIV_BIT) +#define PCM_DIV_CLKDIV_BIT 0 +#define PCM_DIV_CLKDIV_MASK (0x3f << PCM_DIV_CLKDIV_BIT) + + +/************************************************************************* + * I2C + *************************************************************************/ +#define I2C_DR (I2C_BASE + 0x000) +#define I2C_CR (I2C_BASE + 0x004) +#define I2C_SR (I2C_BASE + 0x008) +#define I2C_GR (I2C_BASE + 0x00C) + +#define REG_I2C_DR REG8(I2C_DR) +#define REG_I2C_CR REG8(I2C_CR) +#define REG_I2C_SR REG8(I2C_SR) +#define REG_I2C_GR REG16(I2C_GR) + +/* I2C Control Register (I2C_CR) */ + +#define I2C_CR_IEN (1 << 4) +#define I2C_CR_STA (1 << 3) +#define I2C_CR_STO (1 << 2) +#define I2C_CR_AC (1 << 1) +#define I2C_CR_I2CE (1 << 0) + +/* I2C Status Register (I2C_SR) */ + +#define I2C_SR_STX (1 << 4) +#define I2C_SR_BUSY (1 << 3) +#define I2C_SR_TEND (1 << 2) +#define I2C_SR_DRF (1 << 1) +#define I2C_SR_ACKF (1 << 0) + + +/************************************************************************* + * SSI (Synchronous Serial Interface) + *************************************************************************/ +/* n = 0, 1 (SSI0, SSI1) */ +#define SSI_DR(n) (SSI_BASE + 0x000 + (n)*0x2000) +#define SSI_CR0(n) (SSI_BASE + 0x004 + (n)*0x2000) +#define SSI_CR1(n) (SSI_BASE + 0x008 + (n)*0x2000) +#define SSI_SR(n) (SSI_BASE + 0x00C + (n)*0x2000) +#define SSI_ITR(n) (SSI_BASE + 0x010 + (n)*0x2000) +#define SSI_ICR(n) (SSI_BASE + 0x014 + (n)*0x2000) +#define SSI_GR(n) (SSI_BASE + 0x018 + (n)*0x2000) + +#define REG_SSI_DR(n) REG32(SSI_DR(n)) +#define REG_SSI_CR0(n) REG16(SSI_CR0(n)) +#define REG_SSI_CR1(n) REG32(SSI_CR1(n)) +#define REG_SSI_SR(n) REG32(SSI_SR(n)) +#define REG_SSI_ITR(n) REG16(SSI_ITR(n)) +#define REG_SSI_ICR(n) REG8(SSI_ICR(n)) +#define REG_SSI_GR(n) REG16(SSI_GR(n)) + +/* SSI Data Register (SSI_DR) */ + +#define SSI_DR_GPC_BIT 0 +#define SSI_DR_GPC_MASK (0x1ff << SSI_DR_GPC_BIT) + +#define SSI_MAX_FIFO_ENTRIES 128 /* 128 txfifo and 128 rxfifo */ + +/* SSI Control Register 0 (SSI_CR0) */ + +#define SSI_CR0_SSIE (1 << 15) +#define SSI_CR0_TIE (1 << 14) +#define SSI_CR0_RIE (1 << 13) +#define SSI_CR0_TEIE (1 << 12) +#define SSI_CR0_REIE (1 << 11) +#define SSI_CR0_LOOP (1 << 10) +#define SSI_CR0_RFINE (1 << 9) +#define SSI_CR0_RFINC (1 << 8) +#define SSI_CR0_EACLRUN (1 << 7) /* hardware auto clear underrun when TxFifo no empty */ +#define SSI_CR0_FSEL (1 << 6) +#define SSI_CR0_TFLUSH (1 << 2) +#define SSI_CR0_RFLUSH (1 << 1) +#define SSI_CR0_DISREV (1 << 0) + +/* SSI Control Register 1 (SSI_CR1) */ + +#define SSI_CR1_FRMHL_BIT 30 +#define SSI_CR1_FRMHL_MASK (0x3 << SSI_CR1_FRMHL_BIT) + #define SSI_CR1_FRMHL_CELOW_CE2LOW (0 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is low valid and SSI_CE2_ is low valid */ + #define SSI_CR1_FRMHL_CEHIGH_CE2LOW (1 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is high valid and SSI_CE2_ is low valid */ + #define SSI_CR1_FRMHL_CELOW_CE2HIGH (2 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is low valid and SSI_CE2_ is high valid */ + #define SSI_CR1_FRMHL_CEHIGH_CE2HIGH (3 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is high valid and SSI_CE2_ is high valid */ +#define SSI_CR1_TFVCK_BIT 28 +#define SSI_CR1_TFVCK_MASK (0x3 << SSI_CR1_TFVCK_BIT) + #define SSI_CR1_TFVCK_0 (0 << SSI_CR1_TFVCK_BIT) + #define SSI_CR1_TFVCK_1 (1 << SSI_CR1_TFVCK_BIT) + #define SSI_CR1_TFVCK_2 (2 << SSI_CR1_TFVCK_BIT) + #define SSI_CR1_TFVCK_3 (3 << SSI_CR1_TFVCK_BIT) +#define SSI_CR1_TCKFI_BIT 26 +#define SSI_CR1_TCKFI_MASK (0x3 << SSI_CR1_TCKFI_BIT) + #define SSI_CR1_TCKFI_0 (0 << SSI_CR1_TCKFI_BIT) + #define SSI_CR1_TCKFI_1 (1 << SSI_CR1_TCKFI_BIT) + #define SSI_CR1_TCKFI_2 (2 << SSI_CR1_TCKFI_BIT) + #define SSI_CR1_TCKFI_3 (3 << SSI_CR1_TCKFI_BIT) +#define SSI_CR1_LFST (1 << 25) +#define SSI_CR1_ITFRM (1 << 24) +#define SSI_CR1_UNFIN (1 << 23) +#define SSI_CR1_MULTS (1 << 22) +#define SSI_CR1_FMAT_BIT 20 +#define SSI_CR1_FMAT_MASK (0x3 << SSI_CR1_FMAT_BIT) + #define SSI_CR1_FMAT_SPI (0 << SSI_CR1_FMAT_BIT) /* Motorola¡¯s SPI format */ + #define SSI_CR1_FMAT_SSP (1 << SSI_CR1_FMAT_BIT) /* TI's SSP format */ + #define SSI_CR1_FMAT_MW1 (2 << SSI_CR1_FMAT_BIT) /* National Microwire 1 format */ + #define SSI_CR1_FMAT_MW2 (3 << SSI_CR1_FMAT_BIT) /* National Microwire 2 format */ +#define SSI_CR1_TTRG_BIT 16 /* SSI1 TX trigger */ +#define SSI_CR1_TTRG_MASK (0xf << SSI_CR1_TTRG_BIT) +#define SSI_CR1_MCOM_BIT 12 +#define SSI_CR1_MCOM_MASK (0xf << SSI_CR1_MCOM_BIT) + #define SSI_CR1_MCOM_1BIT (0x0 << SSI_CR1_MCOM_BIT) /* 1-bit command selected */ + #define SSI_CR1_MCOM_2BIT (0x1 << SSI_CR1_MCOM_BIT) /* 2-bit command selected */ + #define SSI_CR1_MCOM_3BIT (0x2 << SSI_CR1_MCOM_BIT) /* 3-bit command selected */ + #define SSI_CR1_MCOM_4BIT (0x3 << SSI_CR1_MCOM_BIT) /* 4-bit command selected */ + #define SSI_CR1_MCOM_5BIT (0x4 << SSI_CR1_MCOM_BIT) /* 5-bit command selected */ + #define SSI_CR1_MCOM_6BIT (0x5 << SSI_CR1_MCOM_BIT) /* 6-bit command selected */ + #define SSI_CR1_MCOM_7BIT (0x6 << SSI_CR1_MCOM_BIT) /* 7-bit command selected */ + #define SSI_CR1_MCOM_8BIT (0x7 << SSI_CR1_MCOM_BIT) /* 8-bit command selected */ + #define SSI_CR1_MCOM_9BIT (0x8 << SSI_CR1_MCOM_BIT) /* 9-bit command selected */ + #define SSI_CR1_MCOM_10BIT (0x9 << SSI_CR1_MCOM_BIT) /* 10-bit command selected */ + #define SSI_CR1_MCOM_11BIT (0xA << SSI_CR1_MCOM_BIT) /* 11-bit command selected */ + #define SSI_CR1_MCOM_12BIT (0xB << SSI_CR1_MCOM_BIT) /* 12-bit command selected */ + #define SSI_CR1_MCOM_13BIT (0xC << SSI_CR1_MCOM_BIT) /* 13-bit command selected */ + #define SSI_CR1_MCOM_14BIT (0xD << SSI_CR1_MCOM_BIT) /* 14-bit command selected */ + #define SSI_CR1_MCOM_15BIT (0xE << SSI_CR1_MCOM_BIT) /* 15-bit command selected */ + #define SSI_CR1_MCOM_16BIT (0xF << SSI_CR1_MCOM_BIT) /* 16-bit command selected */ +#define SSI_CR1_RTRG_BIT 8 /* SSI RX trigger */ +#define SSI_CR1_RTRG_MASK (0xf << SSI_CR1_RTRG_BIT) +#define SSI_CR1_FLEN_BIT 4 +#define SSI_CR1_FLEN_MASK (0xf << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_2BIT (0x0 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_3BIT (0x1 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_4BIT (0x2 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_5BIT (0x3 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_6BIT (0x4 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_7BIT (0x5 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_8BIT (0x6 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_9BIT (0x7 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_10BIT (0x8 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_11BIT (0x9 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_12BIT (0xA << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_13BIT (0xB << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_14BIT (0xC << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_15BIT (0xD << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_16BIT (0xE << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_17BIT (0xF << SSI_CR1_FLEN_BIT) +#define SSI_CR1_PHA (1 << 1) +#define SSI_CR1_POL (1 << 0) + +/* SSI Status Register (SSI_SR) */ + +#define SSI_SR_TFIFONUM_BIT 16 +#define SSI_SR_TFIFONUM_MASK (0xff << SSI_SR_TFIFONUM_BIT) +#define SSI_SR_RFIFONUM_BIT 8 +#define SSI_SR_RFIFONUM_MASK (0xff << SSI_SR_RFIFONUM_BIT) +#define SSI_SR_END (1 << 7) +#define SSI_SR_BUSY (1 << 6) +#define SSI_SR_TFF (1 << 5) +#define SSI_SR_RFE (1 << 4) +#define SSI_SR_TFHE (1 << 3) +#define SSI_SR_RFHF (1 << 2) +#define SSI_SR_UNDR (1 << 1) +#define SSI_SR_OVER (1 << 0) + +/* SSI Interval Time Control Register (SSI_ITR) */ + +#define SSI_ITR_CNTCLK (1 << 15) +#define SSI_ITR_IVLTM_BIT 0 +#define SSI_ITR_IVLTM_MASK (0x7fff << SSI_ITR_IVLTM_BIT) + + +/************************************************************************* + * MSC + ************************************************************************/ +/* n = 0, 1 (MSC0, MSC1) */ +#define MSC_STRPCL(n) (MSC_BASE + (n)*0x1000 + 0x000) +#define MSC_STAT(n) (MSC_BASE + (n)*0x1000 + 0x004) +#define MSC_CLKRT(n) (MSC_BASE + (n)*0x1000 + 0x008) +#define MSC_CMDAT(n) (MSC_BASE + (n)*0x1000 + 0x00C) +#define MSC_RESTO(n) (MSC_BASE + (n)*0x1000 + 0x010) +#define MSC_RDTO(n) (MSC_BASE + (n)*0x1000 + 0x014) +#define MSC_BLKLEN(n) (MSC_BASE + (n)*0x1000 + 0x018) +#define MSC_NOB(n) (MSC_BASE + (n)*0x1000 + 0x01C) +#define MSC_SNOB(n) (MSC_BASE + (n)*0x1000 + 0x020) +#define MSC_IMASK(n) (MSC_BASE + (n)*0x1000 + 0x024) +#define MSC_IREG(n) (MSC_BASE + (n)*0x1000 + 0x028) +#define MSC_CMD(n) (MSC_BASE + (n)*0x1000 + 0x02C) +#define MSC_ARG(n) (MSC_BASE + (n)*0x1000 + 0x030) +#define MSC_RES(n) (MSC_BASE + (n)*0x1000 + 0x034) +#define MSC_RXFIFO(n) (MSC_BASE + (n)*0x1000 + 0x038) +#define MSC_TXFIFO(n) (MSC_BASE + (n)*0x1000 + 0x03C) +#define MSC_LPM(n) (MSC_BASE + (n)*0x1000 + 0x040) + +#define REG_MSC_STRPCL(n) REG16(MSC_STRPCL(n)) +#define REG_MSC_STAT(n) REG32(MSC_STAT(n)) +#define REG_MSC_CLKRT(n) REG16(MSC_CLKRT(n)) +#define REG_MSC_CMDAT(n) REG32(MSC_CMDAT(n)) +#define REG_MSC_RESTO(n) REG16(MSC_RESTO(n)) +#define REG_MSC_RDTO(n) REG16(MSC_RDTO(n)) +#define REG_MSC_BLKLEN(n) REG16(MSC_BLKLEN(n)) +#define REG_MSC_NOB(n) REG16(MSC_NOB(n)) +#define REG_MSC_SNOB(n) REG16(MSC_SNOB(n)) +#define REG_MSC_IMASK(n) REG32(MSC_IMASK(n)) +#define REG_MSC_IREG(n) REG16(MSC_IREG(n)) +#define REG_MSC_CMD(n) REG8(MSC_CMD(n)) +#define REG_MSC_ARG(n) REG32(MSC_ARG(n)) +#define REG_MSC_RES(n) REG16(MSC_RES(n)) +#define REG_MSC_RXFIFO(n) REG32(MSC_RXFIFO(n)) +#define REG_MSC_TXFIFO(n) REG32(MSC_TXFIFO(n)) +#define REG_MSC_LPM(n) REG32(MSC_LPM(n)) + +/* MSC Clock and Control Register (MSC_STRPCL) */ +#define MSC_STRPCL_SEND_CCSD (1 << 15) /*send command completion signal disable to ceata */ +#define MSC_STRPCL_SEND_AS_CCSD (1 << 14) /*send internally generated stop after sending ccsd */ +#define MSC_STRPCL_EXIT_MULTIPLE (1 << 7) +#define MSC_STRPCL_EXIT_TRANSFER (1 << 6) +#define MSC_STRPCL_START_READWAIT (1 << 5) +#define MSC_STRPCL_STOP_READWAIT (1 << 4) +#define MSC_STRPCL_RESET (1 << 3) +#define MSC_STRPCL_START_OP (1 << 2) +#define MSC_STRPCL_CLOCK_CONTROL_BIT 0 +#define MSC_STRPCL_CLOCK_CONTROL_MASK (0x3 << MSC_STRPCL_CLOCK_CONTROL_BIT) + #define MSC_STRPCL_CLOCK_CONTROL_STOP (0x1 << MSC_STRPCL_CLOCK_CONTROL_BIT) /* Stop MMC/SD clock */ + #define MSC_STRPCL_CLOCK_CONTROL_START (0x2 << MSC_STRPCL_CLOCK_CONTROL_BIT) /* Start MMC/SD clock */ + +/* MSC Status Register (MSC_STAT) */ +#define MSC_STAT_AUTO_CMD_DONE (1 << 31) /*12 is internally generated by controller has finished */ +#define MSC_STAT_IS_RESETTING (1 << 15) +#define MSC_STAT_SDIO_INT_ACTIVE (1 << 14) +#define MSC_STAT_PRG_DONE (1 << 13) +#define MSC_STAT_DATA_TRAN_DONE (1 << 12) +#define MSC_STAT_END_CMD_RES (1 << 11) +#define MSC_STAT_DATA_FIFO_AFULL (1 << 10) +#define MSC_STAT_IS_READWAIT (1 << 9) +#define MSC_STAT_CLK_EN (1 << 8) +#define MSC_STAT_DATA_FIFO_FULL (1 << 7) +#define MSC_STAT_DATA_FIFO_EMPTY (1 << 6) +#define MSC_STAT_CRC_RES_ERR (1 << 5) +#define MSC_STAT_CRC_READ_ERROR (1 << 4) +#define MSC_STAT_CRC_WRITE_ERROR_BIT 2 +#define MSC_STAT_CRC_WRITE_ERROR_MASK (0x3 << MSC_STAT_CRC_WRITE_ERROR_BIT) + #define MSC_STAT_CRC_WRITE_ERROR_NO (0 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* No error on transmission of data */ + #define MSC_STAT_CRC_WRITE_ERROR (1 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* Card observed erroneous transmission of data */ + #define MSC_STAT_CRC_WRITE_ERROR_NOSTS (2 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* No CRC status is sent back */ +#define MSC_STAT_TIME_OUT_RES (1 << 1) +#define MSC_STAT_TIME_OUT_READ (1 << 0) + +/* MSC Bus Clock Control Register (MSC_CLKRT) */ +#define MSC_CLKRT_CLK_RATE_BIT 0 +#define MSC_CLKRT_CLK_RATE_MASK (0x7 << MSC_CLKRT_CLK_RATE_BIT) + #define MSC_CLKRT_CLK_RATE_DIV_1 (0x0 << MSC_CLKRT_CLK_RATE_BIT) /* CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_2 (0x1 << MSC_CLKRT_CLK_RATE_BIT) /* 1/2 of CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_4 (0x2 << MSC_CLKRT_CLK_RATE_BIT) /* 1/4 of CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_8 (0x3 << MSC_CLKRT_CLK_RATE_BIT) /* 1/8 of CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_16 (0x4 << MSC_CLKRT_CLK_RATE_BIT) /* 1/16 of CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_32 (0x5 << MSC_CLKRT_CLK_RATE_BIT) /* 1/32 of CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_64 (0x6 << MSC_CLKRT_CLK_RATE_BIT) /* 1/64 of CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_128 (0x7 << MSC_CLKRT_CLK_RATE_BIT) /* 1/128 of CLK_SRC */ + +/* MSC Command Sequence Control Register (MSC_CMDAT) */ +#define MSC_CMDAT_CCS_EXPECTED (1 << 31) /* interrupts are enabled in ce-ata */ +#define MSC_CMDAT_READ_CEATA (1 << 30) +#define MSC_CMDAT_SDIO_PRDT (1 << 17) /* exact 2 cycle */ +#define MSC_CMDAT_SEND_AS_STOP (1 << 16) +#define MSC_CMDAT_RTRG_BIT 14 + #define MSC_CMDAT_RTRG_EQUALT_8 (0x0 << MSC_CMDAT_RTRG_BIT) + #define MSC_CMDAT_RTRG_EQUALT_16 (0x1 << MSC_CMDAT_RTRG_BIT) /* reset value */ + #define MSC_CMDAT_RTRG_EQUALT_24 (0x2 << MSC_CMDAT_RTRG_BIT) + +#define MSC_CMDAT_TTRG_BIT 12 + #define MSC_CMDAT_TTRG_LESS_8 (0x0 << MSC_CMDAT_TTRG_BIT) + #define MSC_CMDAT_TTRG_LESS_16 (0x1 << MSC_CMDAT_TTRG_BIT) /*reset value */ + #define MSC_CMDAT_TTRG_LESS_24 (0x2 << MSC_CMDAT_TTRG_BIT) +#define MSC_CMDAT_STOP_ABORT (1 << 11) +#define MSC_CMDAT_BUS_WIDTH_BIT 9 +#define MSC_CMDAT_BUS_WIDTH_MASK (0x3 << MSC_CMDAT_BUS_WIDTH_BIT) + #define MSC_CMDAT_BUS_WIDTH_1BIT (0x0 << MSC_CMDAT_BUS_WIDTH_BIT) /* 1-bit data bus */ + #define MSC_CMDAT_BUS_WIDTH_4BIT (0x2 << MSC_CMDAT_BUS_WIDTH_BIT) /* 4-bit data bus */ + #define MSC_CMDAT_BUS_WIDTH_8BIT (0x3 << MSC_CMDAT_BUS_WIDTH_BIT) /* 8-bit data bus */ +#define MSC_CMDAT_DMA_EN (1 << 8) +#define MSC_CMDAT_INIT (1 << 7) +#define MSC_CMDAT_BUSY (1 << 6) +#define MSC_CMDAT_STREAM_BLOCK (1 << 5) +#define MSC_CMDAT_WRITE (1 << 4) +#define MSC_CMDAT_READ (0 << 4) +#define MSC_CMDAT_DATA_EN (1 << 3) +#define MSC_CMDAT_RESPONSE_BIT 0 +#define MSC_CMDAT_RESPONSE_MASK (0x7 << MSC_CMDAT_RESPONSE_BIT) + #define MSC_CMDAT_RESPONSE_NONE (0x0 << MSC_CMDAT_RESPONSE_BIT) /* No response */ + #define MSC_CMDAT_RESPONSE_R1 (0x1 << MSC_CMDAT_RESPONSE_BIT) /* Format R1 and R1b */ + #define MSC_CMDAT_RESPONSE_R2 (0x2 << MSC_CMDAT_RESPONSE_BIT) /* Format R2 */ + #define MSC_CMDAT_RESPONSE_R3 (0x3 << MSC_CMDAT_RESPONSE_BIT) /* Format R3 */ + #define MSC_CMDAT_RESPONSE_R4 (0x4 << MSC_CMDAT_RESPONSE_BIT) /* Format R4 */ + #define MSC_CMDAT_RESPONSE_R5 (0x5 << MSC_CMDAT_RESPONSE_BIT) /* Format R5 */ + #define MSC_CMDAT_RESPONSE_R6 (0x6 << MSC_CMDAT_RESPONSE_BIT) /* Format R6 */ + +#define CMDAT_DMA_EN (1 << 8) +#define CMDAT_INIT (1 << 7) +#define CMDAT_BUSY (1 << 6) +#define CMDAT_STREAM (1 << 5) +#define CMDAT_WRITE (1 << 4) +#define CMDAT_DATA_EN (1 << 3) + +/* MSC Interrupts Mask Register (MSC_IMASK) */ +#define MSC_IMASK_AUTO_CMD_DONE (1 << 8) +#define MSC_IMASK_SDIO (1 << 7) +#define MSC_IMASK_TXFIFO_WR_REQ (1 << 6) +#define MSC_IMASK_RXFIFO_RD_REQ (1 << 5) +#define MSC_IMASK_END_CMD_RES (1 << 2) +#define MSC_IMASK_PRG_DONE (1 << 1) +#define MSC_IMASK_DATA_TRAN_DONE (1 << 0) + +/* MSC Interrupts Status Register (MSC_IREG) */ +#define MSC_IREG_AUTO_CMD_DONE (1 << 8) +#define MSC_IREG_SDIO (1 << 7) +#define MSC_IREG_TXFIFO_WR_REQ (1 << 6) +#define MSC_IREG_RXFIFO_RD_REQ (1 << 5) +#define MSC_IREG_END_CMD_RES (1 << 2) +#define MSC_IREG_PRG_DONE (1 << 1) +#define MSC_IREG_DATA_TRAN_DONE (1 << 0) + +/* MSC Low Power Mode Register (MSC_LPM) */ +#define MSC_SET_LPM (1 << 0) + +/************************************************************************* + * EMC (External Memory Controller) + *************************************************************************/ +#define EMC_BCR (EMC_BASE + 0x00) /* Bus Control Register */ +#define EMC_SMCR0 (EMC_BASE + 0x10) /* Static Memory Control Register 0 */ +#define EMC_SMCR1 (EMC_BASE + 0x14) /* Static Memory Control Register 1 */ +#define EMC_SMCR2 (EMC_BASE + 0x18) /* Static Memory Control Register 2 */ +#define EMC_SMCR3 (EMC_BASE + 0x1c) /* Static Memory Control Register 3 */ +#define EMC_SMCR4 (EMC_BASE + 0x20) /* Static Memory Control Register 4 */ +#define EMC_SACR0 (EMC_BASE + 0x30) /* Static Memory Bank 0 Addr Config Reg */ +#define EMC_SACR1 (EMC_BASE + 0x34) /* Static Memory Bank 1 Addr Config Reg */ +#define EMC_SACR2 (EMC_BASE + 0x38) /* Static Memory Bank 2 Addr Config Reg */ +#define EMC_SACR3 (EMC_BASE + 0x3c) /* Static Memory Bank 3 Addr Config Reg */ +#define EMC_SACR4 (EMC_BASE + 0x40) /* Static Memory Bank 4 Addr Config Reg */ + +#define EMC_NFCSR (EMC_BASE + 0x050) /* NAND Flash Control/Status Register */ + +#define EMC_DMCR (EMC_BASE + 0x80) /* DRAM Control Register */ +#define EMC_RTCSR (EMC_BASE + 0x84) /* Refresh Time Control/Status Register */ +#define EMC_RTCNT (EMC_BASE + 0x88) /* Refresh Timer Counter */ +#define EMC_RTCOR (EMC_BASE + 0x8c) /* Refresh Time Constant Register */ +#define EMC_DMAR0 (EMC_BASE + 0x90) /* SDRAM Bank 0 Addr Config Register */ +#define EMC_DMAR1 (EMC_BASE + 0x94) /* SDRAM Bank 1 Addr Config Register */ +#define EMC_SDMR0 (EMC_BASE + 0xa000) /* Mode Register of SDRAM bank 0 */ + +#define REG_EMC_BCR REG32(EMC_BCR) +#define REG_EMC_SMCR0 REG32(EMC_SMCR0) +#define REG_EMC_SMCR1 REG32(EMC_SMCR1) +#define REG_EMC_SMCR2 REG32(EMC_SMCR2) +#define REG_EMC_SMCR3 REG32(EMC_SMCR3) +#define REG_EMC_SMCR4 REG32(EMC_SMCR4) +#define REG_EMC_SACR0 REG32(EMC_SACR0) +#define REG_EMC_SACR1 REG32(EMC_SACR1) +#define REG_EMC_SACR2 REG32(EMC_SACR2) +#define REG_EMC_SACR3 REG32(EMC_SACR3) +#define REG_EMC_SACR4 REG32(EMC_SACR4) + +#define REG_EMC_NFCSR REG32(EMC_NFCSR) + +#define REG_EMC_DMCR REG32(EMC_DMCR) +#define REG_EMC_RTCSR REG16(EMC_RTCSR) +#define REG_EMC_RTCNT REG16(EMC_RTCNT) +#define REG_EMC_RTCOR REG16(EMC_RTCOR) +#define REG_EMC_DMAR0 REG32(EMC_DMAR0) +#define REG_EMC_DMAR1 REG32(EMC_DMAR1) + +/* Bus Control Register */ +#define EMC_BCR_BT_SEL_BIT 30 +#define EMC_BCR_BT_SEL_MASK (0x3 << EMC_BCR_BT_SEL_BIT) +#define EMC_BCR_PK_SEL (1 << 24) +#define EMC_BCR_BSR_MASK (1 << 2) /* Nand and SDRAM Bus Share Select: 0, share; 1, unshare */ + #define EMC_BCR_BSR_SHARE (0 << 2) + #define EMC_BCR_BSR_UNSHARE (1 << 2) +#define EMC_BCR_BRE (1 << 1) +#define EMC_BCR_ENDIAN (1 << 0) + +/* Static Memory Control Register */ +#define EMC_SMCR_STRV_BIT 24 +#define EMC_SMCR_STRV_MASK (0x0f << EMC_SMCR_STRV_BIT) +#define EMC_SMCR_TAW_BIT 20 +#define EMC_SMCR_TAW_MASK (0x0f << EMC_SMCR_TAW_BIT) +#define EMC_SMCR_TBP_BIT 16 +#define EMC_SMCR_TBP_MASK (0x0f << EMC_SMCR_TBP_BIT) +#define EMC_SMCR_TAH_BIT 12 +#define EMC_SMCR_TAH_MASK (0x07 << EMC_SMCR_TAH_BIT) +#define EMC_SMCR_TAS_BIT 8 +#define EMC_SMCR_TAS_MASK (0x07 << EMC_SMCR_TAS_BIT) +#define EMC_SMCR_BW_BIT 6 +#define EMC_SMCR_BW_MASK (0x03 << EMC_SMCR_BW_BIT) + #define EMC_SMCR_BW_8BIT (0 << EMC_SMCR_BW_BIT) + #define EMC_SMCR_BW_16BIT (1 << EMC_SMCR_BW_BIT) + #define EMC_SMCR_BW_32BIT (2 << EMC_SMCR_BW_BIT) +#define EMC_SMCR_BCM (1 << 3) +#define EMC_SMCR_BL_BIT 1 +#define EMC_SMCR_BL_MASK (0x03 << EMC_SMCR_BL_BIT) + #define EMC_SMCR_BL_4 (0 << EMC_SMCR_BL_BIT) + #define EMC_SMCR_BL_8 (1 << EMC_SMCR_BL_BIT) + #define EMC_SMCR_BL_16 (2 << EMC_SMCR_BL_BIT) + #define EMC_SMCR_BL_32 (3 << EMC_SMCR_BL_BIT) +#define EMC_SMCR_SMT (1 << 0) + +/* Static Memory Bank Addr Config Reg */ +#define EMC_SACR_BASE_BIT 8 +#define EMC_SACR_BASE_MASK (0xff << EMC_SACR_BASE_BIT) +#define EMC_SACR_MASK_BIT 0 +#define EMC_SACR_MASK_MASK (0xff << EMC_SACR_MASK_BIT) + +/* NAND Flash Control/Status Register */ +#define EMC_NFCSR_NFCE4 (1 << 7) /* NAND Flash Enable */ +#define EMC_NFCSR_NFE4 (1 << 6) /* NAND Flash FCE# Assertion Enable */ +#define EMC_NFCSR_NFCE3 (1 << 5) +#define EMC_NFCSR_NFE3 (1 << 4) +#define EMC_NFCSR_NFCE2 (1 << 3) +#define EMC_NFCSR_NFE2 (1 << 2) +#define EMC_NFCSR_NFCE1 (1 << 1) +#define EMC_NFCSR_NFE1 (1 << 0) + +/* DRAM Control Register */ +#define EMC_DMCR_BW_BIT 31 +#define EMC_DMCR_BW (1 << EMC_DMCR_BW_BIT) +#define EMC_DMCR_CA_BIT 26 +#define EMC_DMCR_CA_MASK (0x07 << EMC_DMCR_CA_BIT) + #define EMC_DMCR_CA_8 (0 << EMC_DMCR_CA_BIT) + #define EMC_DMCR_CA_9 (1 << EMC_DMCR_CA_BIT) + #define EMC_DMCR_CA_10 (2 << EMC_DMCR_CA_BIT) + #define EMC_DMCR_CA_11 (3 << EMC_DMCR_CA_BIT) + #define EMC_DMCR_CA_12 (4 << EMC_DMCR_CA_BIT) +#define EMC_DMCR_RMODE (1 << 25) +#define EMC_DMCR_RFSH (1 << 24) +#define EMC_DMCR_MRSET (1 << 23) +#define EMC_DMCR_RA_BIT 20 +#define EMC_DMCR_RA_MASK (0x03 << EMC_DMCR_RA_BIT) + #define EMC_DMCR_RA_11 (0 << EMC_DMCR_RA_BIT) + #define EMC_DMCR_RA_12 (1 << EMC_DMCR_RA_BIT) + #define EMC_DMCR_RA_13 (2 << EMC_DMCR_RA_BIT) +#define EMC_DMCR_BA_BIT 19 +#define EMC_DMCR_BA (1 << EMC_DMCR_BA_BIT) +#define EMC_DMCR_PDM (1 << 18) +#define EMC_DMCR_EPIN (1 << 17) +#define EMC_DMCR_MBSEL (1 << 16) +#define EMC_DMCR_TRAS_BIT 13 +#define EMC_DMCR_TRAS_MASK (0x07 << EMC_DMCR_TRAS_BIT) +#define EMC_DMCR_RCD_BIT 11 +#define EMC_DMCR_RCD_MASK (0x03 << EMC_DMCR_RCD_BIT) +#define EMC_DMCR_TPC_BIT 8 +#define EMC_DMCR_TPC_MASK (0x07 << EMC_DMCR_TPC_BIT) +#define EMC_DMCR_TRWL_BIT 5 +#define EMC_DMCR_TRWL_MASK (0x03 << EMC_DMCR_TRWL_BIT) +#define EMC_DMCR_TRC_BIT 2 +#define EMC_DMCR_TRC_MASK (0x07 << EMC_DMCR_TRC_BIT) +#define EMC_DMCR_TCL_BIT 0 +#define EMC_DMCR_TCL_MASK (0x03 << EMC_DMCR_TCL_BIT) + +/* Refresh Time Control/Status Register */ +#define EMC_RTCSR_SFR (1 << 8) /* self refresh flag */ +#define EMC_RTCSR_CMF (1 << 7) +#define EMC_RTCSR_CKS_BIT 0 +#define EMC_RTCSR_CKS_MASK (0x07 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_DISABLE (0 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_4 (1 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_16 (2 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_64 (3 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_256 (4 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_1024 (5 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_2048 (6 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_4096 (7 << EMC_RTCSR_CKS_BIT) + +/* SDRAM Bank Address Configuration Register */ +#define EMC_DMAR_BASE_BIT 8 +#define EMC_DMAR_BASE_MASK (0xff << EMC_DMAR_BASE_BIT) +#define EMC_DMAR_MASK_BIT 0 +#define EMC_DMAR_MASK_MASK (0xff << EMC_DMAR_MASK_BIT) + +/* Mode Register of SDRAM bank 0 */ +#define EMC_SDMR_BM (1 << 9) /* Write Burst Mode */ +#define EMC_SDMR_OM_BIT 7 /* Operating Mode */ +#define EMC_SDMR_OM_MASK (3 << EMC_SDMR_OM_BIT) + #define EMC_SDMR_OM_NORMAL (0 << EMC_SDMR_OM_BIT) +#define EMC_SDMR_CAS_BIT 4 /* CAS Latency */ +#define EMC_SDMR_CAS_MASK (7 << EMC_SDMR_CAS_BIT) + #define EMC_SDMR_CAS_1 (1 << EMC_SDMR_CAS_BIT) + #define EMC_SDMR_CAS_2 (2 << EMC_SDMR_CAS_BIT) + #define EMC_SDMR_CAS_3 (3 << EMC_SDMR_CAS_BIT) +#define EMC_SDMR_BT_BIT 3 /* Burst Type */ +#define EMC_SDMR_BT_MASK (1 << EMC_SDMR_BT_BIT) + #define EMC_SDMR_BT_SEQ (0 << EMC_SDMR_BT_BIT) /* Sequential */ + #define EMC_SDMR_BT_INT (1 << EMC_SDMR_BT_BIT) /* Interleave */ +#define EMC_SDMR_BL_BIT 0 /* Burst Length */ +#define EMC_SDMR_BL_MASK (7 << EMC_SDMR_BL_BIT) + #define EMC_SDMR_BL_1 (0 << EMC_SDMR_BL_BIT) + #define EMC_SDMR_BL_2 (1 << EMC_SDMR_BL_BIT) + #define EMC_SDMR_BL_4 (2 << EMC_SDMR_BL_BIT) + #define EMC_SDMR_BL_8 (3 << EMC_SDMR_BL_BIT) + +#define EMC_SDMR_CAS2_16BIT \ + (EMC_SDMR_CAS_2 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_2) +#define EMC_SDMR_CAS2_32BIT \ + (EMC_SDMR_CAS_2 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_4) +#define EMC_SDMR_CAS3_16BIT \ + (EMC_SDMR_CAS_3 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_2) +#define EMC_SDMR_CAS3_32BIT \ + (EMC_SDMR_CAS_3 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_4) + + +/************************************************************************* + * CIM + *************************************************************************/ +#define CIM_CFG (CIM_BASE + 0x0000) +#define CIM_CTRL (CIM_BASE + 0x0004) +#define CIM_STATE (CIM_BASE + 0x0008) +#define CIM_IID (CIM_BASE + 0x000C) +#define CIM_RXFIFO (CIM_BASE + 0x0010) +#define CIM_DA (CIM_BASE + 0x0020) +#define CIM_FA (CIM_BASE + 0x0024) +#define CIM_FID (CIM_BASE + 0x0028) +#define CIM_CMD (CIM_BASE + 0x002C) +#define CIM_SIZE (CIM_BASE + 0x0030) +#define CIM_OFFSET (CIM_BASE + 0x0034) +#define CIM_RAM_ADDR (CIM_BASE + 0x1000) + +#define REG_CIM_CFG REG32(CIM_CFG) +#define REG_CIM_CTRL REG32(CIM_CTRL) +#define REG_CIM_STATE REG32(CIM_STATE) +#define REG_CIM_IID REG32(CIM_IID) +#define REG_CIM_RXFIFO REG32(CIM_RXFIFO) +#define REG_CIM_DA REG32(CIM_DA) +#define REG_CIM_FA REG32(CIM_FA) +#define REG_CIM_FID REG32(CIM_FID) +#define REG_CIM_CMD REG32(CIM_CMD) +#define REG_CIM_SIZE REG32(CIM_SIZE) +#define REG_CIM_OFFSET REG32(CIM_OFFSET) + +/* CIM Configuration Register (CIM_CFG) */ + +#define CIM_CFG_ORDER_BIT 18 +#define CIM_CFG_ORDER_MASK (0x3 << CIM_CFG_ORDER_BIT) + #define CIM_CFG_ORDER_0 (0x0 << CIM_CFG_ORDER_BIT) /* RG-GB; YUYV; YUV */ + #define CIM_CFG_ORDER_1 (0x1 << CIM_CFG_ORDER_BIT) /* GR-BG; YVYU; YVU */ + #define CIM_CFG_ORDER_2 (0x2 << CIM_CFG_ORDER_BIT) /* BG-GR; UYVY; UVY */ + #define CIM_CFG_ORDER_3 (0x3 << CIM_CFG_ORDER_BIT) /* GB-RG; VYUY; VUY */ +#define CIM_CFG_DF_BIT 16 +#define CIM_CFG_DF_MASK (0x3 << CIM_CFG_DF_BIT) + #define CIM_CFG_DF_RGB (0x0 << CIM_CFG_DF_BIT) + #define CIM_CFG_DF_RAWRGB CIM_CFG_DF_RGB + #define CIM_CFG_DF_BAYERRGB CIM_CFG_DF_RGB + #define CIM_CFG_DF_YUV444 (0x1 << CIM_CFG_DF_BIT) + #define CIM_CFG_DF_YUV422 (0x2 << CIM_CFG_DF_BIT) + #define CIM_CFG_DF_ITU656 (0x3 << CIM_CFG_DF_BIT) +#define CIM_CFG_INV_DAT (1 << 15) +#define CIM_CFG_VSP (1 << 14) /* VSYNC Polarity:0-rising edge active,1-falling edge active */ +#define CIM_CFG_HSP (1 << 13) /* HSYNC Polarity:0-rising edge active,1-falling edge active */ +#define CIM_CFG_PCP (1 << 12) /* PCLK working edge: 0-rising, 1-falling */ +#define CIM_CFG_DMA_BURST_TYPE_BIT 10 +#define CIM_CFG_DMA_BURST_TYPE_MASK (0x3 << CIM_CFG_DMA_BURST_TYPE_BIT) +#define CIM_CFG_DMA_BURST_INCR (0 << CIM_CFG_DMA_BURST_TYPE_BIT) +#define CIM_CFG_DMA_BURST_INCR4 (1 << CIM_CFG_DMA_BURST_TYPE_BIT) +#define CIM_CFG_DMA_BURST_INCR8 (2 << CIM_CFG_DMA_BURST_TYPE_BIT) +#define CIM_CFG_DUMMY_ZERO (1 << 9) +#define CIM_CFG_EXT_VSYNC (1 << 8) +#define CIM_CFG_PACK_BIT 4 +#define CIM_CFG_PACK_MASK (0x7 << CIM_CFG_PACK_BIT) + #define CIM_CFG_PACK_0 (0 << CIM_CFG_PACK_BIT) /* 11 22 33 44 */ + #define CIM_CFG_PACK_1 (1 << CIM_CFG_PACK_BIT) /* 22 33 44 11 */ + #define CIM_CFG_PACK_2 (2 << CIM_CFG_PACK_BIT) /* 33 44 11 22 */ + #define CIM_CFG_PACK_3 (3 << CIM_CFG_PACK_BIT) /* 44 11 22 33 */ + #define CIM_CFG_PACK_4 (4 << CIM_CFG_PACK_BIT) /* 44 33 22 11 */ + #define CIM_CFG_PACK_5 (5 << CIM_CFG_PACK_BIT) /* 33 22 11 44 */ + #define CIM_CFG_PACK_6 (6 << CIM_CFG_PACK_BIT) /* 22 11 44 33 */ + #define CIM_CFG_PACK_7 (7 << CIM_CFG_PACK_BIT) /* 11 44 33 22 */ +#define CIM_CFG_BYPASS_BIT 2 +#define CIM_CFG_BYPASS_MASK (1 << CIM_CFG_BYPASS_BIT) + #define CIM_CFG_BYPASS (1 << CIM_CFG_BYPASS_BIT) +#define CIM_CFG_DSM_BIT 0 +#define CIM_CFG_DSM_MASK (0x3 << CIM_CFG_DSM_BIT) + #define CIM_CFG_DSM_CPM (0 << CIM_CFG_DSM_BIT) /* CCIR656 Progressive Mode */ + #define CIM_CFG_DSM_CIM (1 << CIM_CFG_DSM_BIT) /* CCIR656 Interlace Mode */ + #define CIM_CFG_DSM_GCM (2 << CIM_CFG_DSM_BIT) /* Gated Clock Mode */ + #define CIM_CFG_DSM_NGCM (3 << CIM_CFG_DSM_BIT) /* Non-Gated Clock Mode */ + +/* CIM Control Register (CIM_CTRL) */ + +#define CIM_CTRL_MCLKDIV_BIT 24 +#define CIM_CTRL_MCLKDIV_MASK (0xff << CIM_CTRL_MCLKDIV_BIT) +#define CIM_CTRL_FRC_BIT 16 +#define CIM_CTRL_FRC_MASK (0xf << CIM_CTRL_FRC_BIT) + #define CIM_CTRL_FRC_1 (0x0 << CIM_CTRL_FRC_BIT) /* Sample every frame */ + #define CIM_CTRL_FRC_2 (0x1 << CIM_CTRL_FRC_BIT) /* Sample 1/2 frame */ + #define CIM_CTRL_FRC_3 (0x2 << CIM_CTRL_FRC_BIT) /* Sample 1/3 frame */ + #define CIM_CTRL_FRC_4 (0x3 << CIM_CTRL_FRC_BIT) /* Sample 1/4 frame */ + #define CIM_CTRL_FRC_5 (0x4 << CIM_CTRL_FRC_BIT) /* Sample 1/5 frame */ + #define CIM_CTRL_FRC_6 (0x5 << CIM_CTRL_FRC_BIT) /* Sample 1/6 frame */ + #define CIM_CTRL_FRC_7 (0x6 << CIM_CTRL_FRC_BIT) /* Sample 1/7 frame */ + #define CIM_CTRL_FRC_8 (0x7 << CIM_CTRL_FRC_BIT) /* Sample 1/8 frame */ + #define CIM_CTRL_FRC_9 (0x8 << CIM_CTRL_FRC_BIT) /* Sample 1/9 frame */ + #define CIM_CTRL_FRC_10 (0x9 << CIM_CTRL_FRC_BIT) /* Sample 1/10 frame */ + #define CIM_CTRL_FRC_11 (0xA << CIM_CTRL_FRC_BIT) /* Sample 1/11 frame */ + #define CIM_CTRL_FRC_12 (0xB << CIM_CTRL_FRC_BIT) /* Sample 1/12 frame */ + #define CIM_CTRL_FRC_13 (0xC << CIM_CTRL_FRC_BIT) /* Sample 1/13 frame */ + #define CIM_CTRL_FRC_14 (0xD << CIM_CTRL_FRC_BIT) /* Sample 1/14 frame */ + #define CIM_CTRL_FRC_15 (0xE << CIM_CTRL_FRC_BIT) /* Sample 1/15 frame */ + #define CIM_CTRL_FRC_16 (0xF << CIM_CTRL_FRC_BIT) /* Sample 1/16 frame */ +#define CIM_CTRL_SIZEEN_BIT 14 +#define CIM_CTRL_SIZEEN_MASK (0x1 << CIM_CTRL_SIZEEN_BIT) +#define CIM_CTRL_SIZEEN (0x1 << CIM_CTRL_SIZEEN_BIT) +#define CIM_CTRL_VDDM (1 << 13) /* VDD interrupt enable */ +#define CIM_CTRL_DMA_SOFM (1 << 12) +#define CIM_CTRL_DMA_EOFM (1 << 11) +#define CIM_CTRL_DMA_STOPM (1 << 10) +#define CIM_CTRL_RXF_TRIGM (1 << 9) +#define CIM_CTRL_RXF_OFM (1 << 8) +#define CIM_CTRL_RXF_TRIG_BIT 4 +#define CIM_CTRL_RXF_TRIG_MASK (0x7 << CIM_CTRL_RXF_TRIG_BIT) + #define CIM_CTRL_RXF_TRIG_4 (0 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 4 */ + #define CIM_CTRL_RXF_TRIG_8 (1 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 8 */ + #define CIM_CTRL_RXF_TRIG_12 (2 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 12 */ + #define CIM_CTRL_RXF_TRIG_16 (3 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 16 */ + #define CIM_CTRL_RXF_TRIG_20 (4 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 20 */ + #define CIM_CTRL_RXF_TRIG_24 (5 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 24 */ + #define CIM_CTRL_RXF_TRIG_28 (6 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 28 */ + #define CIM_CTRL_RXF_TRIG_32 (7 << CIM_CTRL_RXF_TRIG_BIT) /* RXFIFO Trigger Value is 32 */ +#define CIM_CTRL_FAST_MODE_MASK (1 << 3) /* CIM fast mode mask */ +#define CIM_CTRL_FAST_MODE (1 << 3) /* CIM works in fast mode */ +#define CIM_CTRL_NORMAL_MODE (0 << 3) /* CIM works in normal mode */ +#define CIM_CTRL_DMA_EN (1 << 2) /* Enable DMA */ +#define CIM_CTRL_RXF_RST (1 << 1) /* RxFIFO reset */ +#define CIM_CTRL_ENA (1 << 0) /* Enable CIM */ + +/* CIM State Register (CIM_STATE) */ + +#define CIM_STATE_DMA_SOF (1 << 6) /* DMA start irq */ +#define CIM_STATE_DMA_EOF (1 << 5) /* DMA end irq */ +#define CIM_STATE_DMA_STOP (1 << 4) /* DMA stop irq */ +#define CIM_STATE_RXF_OF (1 << 3) /* RXFIFO over flow irq */ +#define CIM_STATE_RXF_TRIG (1 << 2) /* RXFIFO triger meet irq */ +#define CIM_STATE_RXF_EMPTY (1 << 1) /* RXFIFO empty irq */ +#define CIM_STATE_VDD (1 << 0) /* CIM disabled irq */ + +/* CIM DMA Command Register (CIM_CMD) */ + +#define CIM_CMD_SOFINT (1 << 31) /* enable DMA start irq */ +#define CIM_CMD_EOFINT (1 << 30) /* enable DMA end irq */ +#define CIM_CMD_STOP (1 << 28) /* enable DMA stop irq */ +#define CIM_CMD_LEN_BIT 0 +#define CIM_CMD_LEN_MASK (0xffffff << CIM_CMD_LEN_BIT) + +/* CIM Image Size Register (CIM_SIZE) */ +#define CIM_SIZE_LPF_BIT 16 /* Lines per freame for csc output image */ +#define CIM_SIZE_LPF_MASK (0x1fff << CIM_SIZE_LPF_BIT) +#define CIM_SIZE_PPL_BIT 0 /* Pixels per line for csc output image, should be an even number */ +#define CIM_SIZE_PPL_MASK (0x1fff << CIM_SIZE_PPL_BIT) + +/* CIM Image Offset Register (CIM_OFFSET) */ +#define CIM_OFFSET_V_BIT 16 /* Vertical offset */ +#define CIM_OFFSET_V_MASK (0xfff << CIM_OFFSET_V_BIT) +#define CIM_OFFSET_H_BIT 0 /* Horizontal offset, should be an enen number */ +#define CIM_OFFSET_H_MASK (0xfff << CIM_OFFSET_H_BIT) /*OFFSET_H should be even number*/ + +/************************************************************************* + * SADC (Smart A/D Controller) + *************************************************************************/ + +#define SADC_ENA (SADC_BASE + 0x00) /* ADC Enable Register */ +#define SADC_CFG (SADC_BASE + 0x04) /* ADC Configure Register */ +#define SADC_CTRL (SADC_BASE + 0x08) /* ADC Control Register */ +#define SADC_STATE (SADC_BASE + 0x0C) /* ADC Status Register*/ +#define SADC_SAMETIME (SADC_BASE + 0x10) /* ADC Same Point Time Register */ +#define SADC_WAITTIME (SADC_BASE + 0x14) /* ADC Wait Time Register */ +#define SADC_TSDAT (SADC_BASE + 0x18) /* ADC Touch Screen Data Register */ +#define SADC_BATDAT (SADC_BASE + 0x1C) /* ADC PBAT Data Register */ +#define SADC_SADDAT (SADC_BASE + 0x20) /* ADC SADCIN Data Register */ +#define SADC_ADCLK (SADC_BASE + 0x28) /* ADC Clock Divide Register */ + +#define REG_SADC_ENA REG8(SADC_ENA) +#define REG_SADC_CFG REG32(SADC_CFG) +#define REG_SADC_CTRL REG8(SADC_CTRL) +#define REG_SADC_STATE REG8(SADC_STATE) +#define REG_SADC_SAMETIME REG16(SADC_SAMETIME) +#define REG_SADC_WAITTIME REG16(SADC_WAITTIME) +#define REG_SADC_TSDAT REG32(SADC_TSDAT) +#define REG_SADC_BATDAT REG16(SADC_BATDAT) +#define REG_SADC_SADDAT REG16(SADC_SADDAT) +#define REG_SADC_ADCLK REG32(SADC_ADCLK) + +/* ADC Enable Register */ +#define SADC_ENA_ADEN (1 << 7) /* Touch Screen Enable */ +#define SADC_ENA_ENTR_SLP (1 << 6) /* Touch Screen Enable */ +#define SADC_ENA_EXIT_SLP (1 << 5) /* Touch Screen Enable */ +#define SADC_ENA_TSEN (1 << 2) /* Touch Screen Enable */ +#define SADC_ENA_PBATEN (1 << 1) /* PBAT Enable */ +#define SADC_ENA_SADCINEN (1 << 0) /* SADCIN Enable */ + +/* ADC Configure Register */ +#define SADC_CFG_EXIN (1 << 30) +#define SADC_CFG_CLKOUT_NUM_BIT 16 +#define SADC_CFG_CLKOUT_NUM_MASK (0x7 << SADC_CFG_CLKOUT_NUM_BIT) +#define SADC_CFG_TS_DMA (1 << 15) /* Touch Screen DMA Enable */ +#define SADC_CFG_XYZ_BIT 13 /* XYZ selection */ +#define SADC_CFG_XYZ_MASK (0x3 << SADC_CFG_XYZ_BIT) + #define SADC_CFG_XY (0 << SADC_CFG_XYZ_BIT) + #define SADC_CFG_XYZ (1 << SADC_CFG_XYZ_BIT) + #define SADC_CFG_XYZ1Z2 (2 << SADC_CFG_XYZ_BIT) +#define SADC_CFG_SNUM_BIT 10 /* Sample Number */ +#define SADC_CFG_SNUM_MASK (0x7 << SADC_CFG_SNUM_BIT) + #define SADC_CFG_SNUM_1 (0x0 << SADC_CFG_SNUM_BIT) + #define SADC_CFG_SNUM_2 (0x1 << SADC_CFG_SNUM_BIT) + #define SADC_CFG_SNUM_3 (0x2 << SADC_CFG_SNUM_BIT) + #define SADC_CFG_SNUM_4 (0x3 << SADC_CFG_SNUM_BIT) + #define SADC_CFG_SNUM_5 (0x4 << SADC_CFG_SNUM_BIT) + #define SADC_CFG_SNUM_6 (0x5 << SADC_CFG_SNUM_BIT) + #define SADC_CFG_SNUM_8 (0x6 << SADC_CFG_SNUM_BIT) + #define SADC_CFG_SNUM_9 (0x7 << SADC_CFG_SNUM_BIT) +#define SADC_CFG_CLKDIV_BIT 5 /* AD Converter frequency clock divider */ +#define SADC_CFG_CLKDIV_MASK (0x1f << SADC_CFG_CLKDIV_BIT) +#define SADC_CFG_PBAT_HIGH (0 << 4) /* PBAT >= 2.5V */ +#define SADC_CFG_PBAT_LOW (1 << 4) /* PBAT < 2.5V */ +#define SADC_CFG_CMD_BIT 0 /* ADC Command */ +#define SADC_CFG_CMD_MASK (0xf << SADC_CFG_CMD_BIT) + #define SADC_CFG_CMD_X_SE (0x0 << SADC_CFG_CMD_BIT) /* X Single-End */ + #define SADC_CFG_CMD_Y_SE (0x1 << SADC_CFG_CMD_BIT) /* Y Single-End */ + #define SADC_CFG_CMD_X_DIFF (0x2 << SADC_CFG_CMD_BIT) /* X Differential */ + #define SADC_CFG_CMD_Y_DIFF (0x3 << SADC_CFG_CMD_BIT) /* Y Differential */ + #define SADC_CFG_CMD_Z1_DIFF (0x4 << SADC_CFG_CMD_BIT) /* Z1 Differential */ + #define SADC_CFG_CMD_Z2_DIFF (0x5 << SADC_CFG_CMD_BIT) /* Z2 Differential */ + #define SADC_CFG_CMD_Z3_DIFF (0x6 << SADC_CFG_CMD_BIT) /* Z3 Differential */ + #define SADC_CFG_CMD_Z4_DIFF (0x7 << SADC_CFG_CMD_BIT) /* Z4 Differential */ + #define SADC_CFG_CMD_TP_SE (0x8 << SADC_CFG_CMD_BIT) /* Touch Pressure */ + #define SADC_CFG_CMD_PBATH_SE (0x9 << SADC_CFG_CMD_BIT) /* PBAT >= 2.5V */ + #define SADC_CFG_CMD_PBATL_SE (0xa << SADC_CFG_CMD_BIT) /* PBAT < 2.5V */ + #define SADC_CFG_CMD_SADCIN_SE (0xb << SADC_CFG_CMD_BIT) /* Measure SADCIN */ + #define SADC_CFG_CMD_INT_PEN (0xc << SADC_CFG_CMD_BIT) /* INT_PEN Enable */ + +/* ADC Control Register */ +#define SADC_CTRL_SLPENDM (1 << 5) /* sleep Interrupt Mask */ +#define SADC_CTRL_PENDM (1 << 4) /* Pen Down Interrupt Mask */ +#define SADC_CTRL_PENUM (1 << 3) /* Pen Up Interrupt Mask */ +#define SADC_CTRL_TSRDYM (1 << 2) /* Touch Screen Data Ready Interrupt Mask */ +#define SADC_CTRL_PBATRDYM (1 << 1) /* PBAT Data Ready Interrupt Mask */ +#define SADC_CTRL_SRDYM (1 << 0) /* SADCIN Data Ready Interrupt Mask */ + +/* ADC Status Register */ +#define SADC_STATE_SLEEPND (1 << 5) /* Pen Down Interrupt Flag */ +#define SADC_STATE_PEND (1 << 4) /* Pen Down Interrupt Flag */ +#define SADC_STATE_PENU (1 << 3) /* Pen Up Interrupt Flag */ +#define SADC_STATE_TSRDY (1 << 2) /* Touch Screen Data Ready Interrupt Flag */ +#define SADC_STATE_PBATRDY (1 << 1) /* PBAT Data Ready Interrupt Flag */ +#define SADC_STATE_SRDY (1 << 0) /* SADCIN Data Ready Interrupt Flag */ + +/* ADC Touch Screen Data Register */ +#define SADC_TSDAT_DATA0_BIT 0 +#define SADC_TSDAT_DATA0_MASK (0xfff << SADC_TSDAT_DATA0_BIT) +#define SADC_TSDAT_TYPE0 (1 << 15) +#define SADC_TSDAT_DATA1_BIT 16 +#define SADC_TSDAT_DATA1_MASK (0xfff << SADC_TSDAT_DATA1_BIT) +#define SADC_TSDAT_TYPE1 (1 << 31) + +/* ADC Clock Divide Register */ +#define SADC_ADCLK_CLKDIV_10_BIT 16 +#define SADC_ADCLK_CLKDIV_10_MASK (0x7f << SADC_ADCLK_CLKDIV_10_BIT) +#define SADC_ADCLK_CLKDIV_BIT 0 +#define SADC_ADCLK_CLKDIV_MASK (0x3f << SADC_ADCLK_CLKDIV_BIT) + +/************************************************************************* + * SLCD (Smart LCD Controller) + *************************************************************************/ + +#define SLCD_CFG (SLCD_BASE + 0xA0) /* SLCD Configure Register */ +#define SLCD_CTRL (SLCD_BASE + 0xA4) /* SLCD Control Register */ +#define SLCD_STATE (SLCD_BASE + 0xA8) /* SLCD Status Register */ +#define SLCD_DATA (SLCD_BASE + 0xAC) /* SLCD Data Register */ + +#define REG_SLCD_CFG REG32(SLCD_CFG) +#define REG_SLCD_CTRL REG8(SLCD_CTRL) +#define REG_SLCD_STATE REG8(SLCD_STATE) +#define REG_SLCD_DATA REG32(SLCD_DATA) + +/* SLCD Configure Register */ +#define SLCD_CFG_DWIDTH_BIT 10 +#define SLCD_CFG_DWIDTH_MASK (0x7 << SLCD_CFG_DWIDTH_BIT) + #define SLCD_CFG_DWIDTH_18BIT (0 << SLCD_CFG_DWIDTH_BIT) + #define SLCD_CFG_DWIDTH_16BIT (1 << SLCD_CFG_DWIDTH_BIT) + #define SLCD_CFG_DWIDTH_8BIT_x3 (2 << SLCD_CFG_DWIDTH_BIT) + #define SLCD_CFG_DWIDTH_8BIT_x2 (3 << SLCD_CFG_DWIDTH_BIT) + #define SLCD_CFG_DWIDTH_8BIT_x1 (4 << SLCD_CFG_DWIDTH_BIT) + #define SLCD_CFG_DWIDTH_24BIT (5 << SLCD_CFG_DWIDTH_BIT) + #define SLCD_CFG_DWIDTH_9BIT_x2 (7 << SLCD_CFG_DWIDTH_BIT) +#define SLCD_CFG_CWIDTH_BIT (8) +#define SLCD_CFG_CWIDTH_MASK (0x7 << SLCD_CFG_CWIDTH_BIT) +#define SLCD_CFG_CWIDTH_16BIT (0 << SLCD_CFG_CWIDTH_BIT) +#define SLCD_CFG_CWIDTH_8BIT (1 << SLCD_CFG_CWIDTH_BIT) +#define SLCD_CFG_CWIDTH_18BIT (2 << SLCD_CFG_CWIDTH_BIT) +#define SLCD_CFG_CWIDTH_24BIT (3 << SLCD_CFG_CWIDTH_BIT) +#define SLCD_CFG_CS_ACTIVE_LOW (0 << 4) +#define SLCD_CFG_CS_ACTIVE_HIGH (1 << 4) +#define SLCD_CFG_RS_CMD_LOW (0 << 3) +#define SLCD_CFG_RS_CMD_HIGH (1 << 3) +#define SLCD_CFG_CLK_ACTIVE_FALLING (0 << 1) +#define SLCD_CFG_CLK_ACTIVE_RISING (1 << 1) +#define SLCD_CFG_TYPE_PARALLEL (0 << 0) +#define SLCD_CFG_TYPE_SERIAL (1 << 0) + +/* SLCD Control Register */ +#define SLCD_CTRL_DMA_EN (1 << 0) + +/* SLCD Status Register */ +#define SLCD_STATE_BUSY (1 << 0) + +/* SLCD Data Register */ +#define SLCD_DATA_RS_DATA (0 << 31) +#define SLCD_DATA_RS_COMMAND (1 << 31) + +/************************************************************************* + * LCD (LCD Controller) + *************************************************************************/ +#define LCD_CFG (LCD_BASE + 0x00) /* LCD Configure Register */ +#define LCD_CTRL (LCD_BASE + 0x30) /* LCD Control Register */ +#define LCD_STATE (LCD_BASE + 0x34) /* LCD Status Register */ + +#define LCD_OSDC (LCD_BASE + 0x100) /* LCD OSD Configure Register */ +#define LCD_OSDCTRL (LCD_BASE + 0x104) /* LCD OSD Control Register */ +#define LCD_OSDS (LCD_BASE + 0x108) /* LCD OSD Status Register */ +#define LCD_BGC (LCD_BASE + 0x10C) /* LCD Background Color Register */ +#define LCD_KEY0 (LCD_BASE + 0x110) /* LCD Foreground Color Key Register 0 */ +#define LCD_KEY1 (LCD_BASE + 0x114) /* LCD Foreground Color Key Register 1 */ +#define LCD_ALPHA (LCD_BASE + 0x118) /* LCD ALPHA Register */ +#define LCD_IPUR (LCD_BASE + 0x11C) /* LCD IPU Restart Register */ + +#define LCD_VAT (LCD_BASE + 0x0c) /* Virtual Area Setting Register */ +#define LCD_DAH (LCD_BASE + 0x10) /* Display Area Horizontal Start/End Point */ +#define LCD_DAV (LCD_BASE + 0x14) /* Display Area Vertical Start/End Point */ + +#define LCD_XYP0 (LCD_BASE + 0x120) /* Foreground 0 XY Position Register */ +#define LCD_XYP1 (LCD_BASE + 0x124) /* Foreground 1 XY Position Register */ +#define LCD_SIZE0 (LCD_BASE + 0x128) /* Foreground 0 Size Register */ +#define LCD_SIZE1 (LCD_BASE + 0x12C) /* Foreground 1 Size Register */ +#define LCD_RGBC (LCD_BASE + 0x90) /* RGB Controll Register */ + +#define LCD_VSYNC (LCD_BASE + 0x04) /* Vertical Synchronize Register */ +#define LCD_HSYNC (LCD_BASE + 0x08) /* Horizontal Synchronize Register */ +#define LCD_PS (LCD_BASE + 0x18) /* PS Signal Setting */ +#define LCD_CLS (LCD_BASE + 0x1c) /* CLS Signal Setting */ +#define LCD_SPL (LCD_BASE + 0x20) /* SPL Signal Setting */ +#define LCD_REV (LCD_BASE + 0x24) /* REV Signal Setting */ +#define LCD_IID (LCD_BASE + 0x38) /* Interrupt ID Register */ +#define LCD_DA0 (LCD_BASE + 0x40) /* Descriptor Address Register 0 */ +#define LCD_SA0 (LCD_BASE + 0x44) /* Source Address Register 0 */ +#define LCD_FID0 (LCD_BASE + 0x48) /* Frame ID Register 0 */ +#define LCD_CMD0 (LCD_BASE + 0x4c) /* DMA Command Register 0 */ +#define LCD_DA1 (LCD_BASE + 0x50) /* Descriptor Address Register 1 */ +#define LCD_SA1 (LCD_BASE + 0x54) /* Source Address Register 1 */ +#define LCD_FID1 (LCD_BASE + 0x58) /* Frame ID Register 1 */ +#define LCD_CMD1 (LCD_BASE + 0x5c) /* DMA Command Register 1 */ + +#define LCD_OFFS0 (LCD_BASE + 0x60) /* DMA Offsize Register 0 */ +#define LCD_PW0 (LCD_BASE + 0x64) /* DMA Page Width Register 0 */ +#define LCD_CNUM0 (LCD_BASE + 0x68) /* DMA Command Counter Register 0 */ +#define LCD_DESSIZE0 (LCD_BASE + 0x6C) /* Foreground Size in Descriptor 0 Register*/ +#define LCD_OFFS1 (LCD_BASE + 0x70) /* DMA Offsize Register 1 */ +#define LCD_PW1 (LCD_BASE + 0x74) /* DMA Page Width Register 1 */ +#define LCD_CNUM1 (LCD_BASE + 0x78) /* DMA Command Counter Register 1 */ +#define LCD_DESSIZE1 (LCD_BASE + 0x7C) /* Foreground Size in Descriptor 1 Register*/ + +#define REG_LCD_CFG REG32(LCD_CFG) +#define REG_LCD_CTRL REG32(LCD_CTRL) +#define REG_LCD_STATE REG32(LCD_STATE) + +#define REG_LCD_OSDC REG16(LCD_OSDC) +#define REG_LCD_OSDCTRL REG16(LCD_OSDCTRL) +#define REG_LCD_OSDS REG16(LCD_OSDS) +#define REG_LCD_BGC REG32(LCD_BGC) +#define REG_LCD_KEY0 REG32(LCD_KEY0) +#define REG_LCD_KEY1 REG32(LCD_KEY1) +#define REG_LCD_ALPHA REG8(LCD_ALPHA) +#define REG_LCD_IPUR REG32(LCD_IPUR) + +#define REG_LCD_VAT REG32(LCD_VAT) +#define REG_LCD_DAH REG32(LCD_DAH) +#define REG_LCD_DAV REG32(LCD_DAV) + +#define REG_LCD_XYP0 REG32(LCD_XYP0) +#define REG_LCD_XYP1 REG32(LCD_XYP1) +#define REG_LCD_SIZE0 REG32(LCD_SIZE0) +#define REG_LCD_SIZE1 REG32(LCD_SIZE1) +#define REG_LCD_RGBC REG16(LCD_RGBC) + +#define REG_LCD_VSYNC REG32(LCD_VSYNC) +#define REG_LCD_HSYNC REG32(LCD_HSYNC) +#define REG_LCD_PS REG32(LCD_PS) +#define REG_LCD_CLS REG32(LCD_CLS) +#define REG_LCD_SPL REG32(LCD_SPL) +#define REG_LCD_REV REG32(LCD_REV) +#define REG_LCD_IID REG32(LCD_IID) +#define REG_LCD_DA0 REG32(LCD_DA0) +#define REG_LCD_SA0 REG32(LCD_SA0) +#define REG_LCD_FID0 REG32(LCD_FID0) +#define REG_LCD_CMD0 REG32(LCD_CMD0) +#define REG_LCD_DA1 REG32(LCD_DA1) +#define REG_LCD_SA1 REG32(LCD_SA1) +#define REG_LCD_FID1 REG32(LCD_FID1) +#define REG_LCD_CMD1 REG32(LCD_CMD1) + +#define REG_LCD_OFFS0 REG32(LCD_OFFS0) +#define REG_LCD_PW0 REG32(LCD_PW0) +#define REG_LCD_CNUM0 REG32(LCD_CNUM0) +#define REG_LCD_DESSIZE0 REG32(LCD_DESSIZE0) +#define REG_LCD_OFFS1 REG32(LCD_OFFS1) +#define REG_LCD_PW1 REG32(LCD_PW1) +#define REG_LCD_CNUM1 REG32(LCD_CNUM1) +#define REG_LCD_DESSIZE1 REG32(LCD_DESSIZE1) + +/* LCD Configure Register */ +#define LCD_CFG_LCDPIN_BIT 31 /* LCD pins selection */ +#define LCD_CFG_LCDPIN_MASK (0x1 << LCD_CFG_LCDPIN_BIT) + #define LCD_CFG_LCDPIN_LCD (0x0 << LCD_CFG_LCDPIN_BIT) + #define LCD_CFG_LCDPIN_SLCD (0x1 << LCD_CFG_LCDPIN_BIT) +#define LCD_CFG_TVEPEH (1 << 30) /* TVE PAL enable extra halfline signal */ +#define LCD_CFG_FUHOLD (1 << 29) /* hold pixel clock when outFIFO underrun */ +#define LCD_CFG_NEWDES (1 << 28) /* use new descripter. old: 4words, new:8words */ +#define LCD_CFG_PALBP (1 << 27) /* bypass data format and alpha blending */ +#define LCD_CFG_TVEN (1 << 26) /* indicate the terminal is lcd or tv */ +#define LCD_CFG_RECOVER (1 << 25) /* Auto recover when output fifo underrun */ +#define LCD_CFG_DITHER (1 << 24) /* Dither function */ +#define LCD_CFG_PSM (1 << 23) /* PS signal mode */ +#define LCD_CFG_CLSM (1 << 22) /* CLS signal mode */ +#define LCD_CFG_SPLM (1 << 21) /* SPL signal mode */ +#define LCD_CFG_REVM (1 << 20) /* REV signal mode */ +#define LCD_CFG_HSYNM (1 << 19) /* HSYNC signal mode */ +#define LCD_CFG_PCLKM (1 << 18) /* PCLK signal mode */ +#define LCD_CFG_INVDAT (1 << 17) /* Inverse output data */ +#define LCD_CFG_SYNDIR_IN (1 << 16) /* VSYNC&HSYNC direction */ +#define LCD_CFG_PSP (1 << 15) /* PS pin reset state */ +#define LCD_CFG_CLSP (1 << 14) /* CLS pin reset state */ +#define LCD_CFG_SPLP (1 << 13) /* SPL pin reset state */ +#define LCD_CFG_REVP (1 << 12) /* REV pin reset state */ +#define LCD_CFG_HSP (1 << 11) /* HSYNC polarity:0-active high,1-active low */ +#define LCD_CFG_PCP (1 << 10) /* PCLK polarity:0-rising,1-falling */ +#define LCD_CFG_DEP (1 << 9) /* DE polarity:0-active high,1-active low */ +#define LCD_CFG_VSP (1 << 8) /* VSYNC polarity:0-rising,1-falling */ +#define LCD_CFG_MODE_TFT_18BIT (1 << 7) /* 18bit TFT */ +#define LCD_CFG_MODE_TFT_16BIT (0 << 7) /* 16bit TFT */ +#define LCD_CFG_MODE_TFT_24BIT (1 << 6) /* 24bit TFT */ +#define LCD_CFG_PDW_BIT 4 /* STN pins utilization */ +#define LCD_CFG_PDW_MASK (0x3 << LCD_DEV_PDW_BIT) +#define LCD_CFG_PDW_1 (0 << LCD_CFG_PDW_BIT) /* LCD_D[0] */ + #define LCD_CFG_PDW_2 (1 << LCD_CFG_PDW_BIT) /* LCD_D[0:1] */ + #define LCD_CFG_PDW_4 (2 << LCD_CFG_PDW_BIT) /* LCD_D[0:3]/LCD_D[8:11] */ + #define LCD_CFG_PDW_8 (3 << LCD_CFG_PDW_BIT) /* LCD_D[0:7]/LCD_D[8:15] */ +#define LCD_CFG_MODE_BIT 0 /* Display Device Mode Select */ +#define LCD_CFG_MODE_MASK (0x0f << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_GENERIC_TFT (0 << LCD_CFG_MODE_BIT) /* 16,18 bit TFT */ + #define LCD_CFG_MODE_SPECIAL_TFT_1 (1 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_SPECIAL_TFT_2 (2 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_SPECIAL_TFT_3 (3 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_NONINTER_CCIR656 (4 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_INTER_CCIR656 (6 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_SINGLE_CSTN (8 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_SINGLE_MSTN (9 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_DUAL_CSTN (10 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_DUAL_MSTN (11 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_SERIAL_TFT (12 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_LCM (13 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_SLCD LCD_CFG_MODE_LCM + +/* LCD Control Register */ +#define LCD_CTRL_BST_BIT 28 /* Burst Length Selection */ +#define LCD_CTRL_BST_MASK (0x03 << LCD_CTRL_BST_BIT) + #define LCD_CTRL_BST_4 (0 << LCD_CTRL_BST_BIT) /* 4-word */ + #define LCD_CTRL_BST_8 (1 << LCD_CTRL_BST_BIT) /* 8-word */ + #define LCD_CTRL_BST_16 (2 << LCD_CTRL_BST_BIT) /* 16-word */ + #define LCD_CTRL_BST_32 (3 << LCD_CTRL_BST_BIT) /* 32-word */ +#define LCD_CTRL_RGB565 (0 << 27) /* RGB565 mode(foreground 0 in OSD mode) */ +#define LCD_CTRL_RGB555 (1 << 27) /* RGB555 mode(foreground 0 in OSD mode) */ +#define LCD_CTRL_OFUP (1 << 26) /* Output FIFO underrun protection enable */ +#define LCD_CTRL_FRC_BIT 24 /* STN FRC Algorithm Selection */ +#define LCD_CTRL_FRC_MASK (0x03 << LCD_CTRL_FRC_BIT) + #define LCD_CTRL_FRC_16 (0 << LCD_CTRL_FRC_BIT) /* 16 grayscale */ + #define LCD_CTRL_FRC_4 (1 << LCD_CTRL_FRC_BIT) /* 4 grayscale */ + #define LCD_CTRL_FRC_2 (2 << LCD_CTRL_FRC_BIT) /* 2 grayscale */ +#define LCD_CTRL_PDD_BIT 16 /* Load Palette Delay Counter */ +#define LCD_CTRL_PDD_MASK (0xff << LCD_CTRL_PDD_BIT) +#define LCD_CTRL_EOFM (1 << 13) /* EOF interrupt mask */ +#define LCD_CTRL_SOFM (1 << 12) /* SOF interrupt mask */ +#define LCD_CTRL_OFUM (1 << 11) /* Output FIFO underrun interrupt mask */ +#define LCD_CTRL_IFUM0 (1 << 10) /* Input FIFO 0 underrun interrupt mask */ +#define LCD_CTRL_IFUM1 (1 << 9) /* Input FIFO 1 underrun interrupt mask */ +#define LCD_CTRL_LDDM (1 << 8) /* LCD disable done interrupt mask */ +#define LCD_CTRL_QDM (1 << 7) /* LCD quick disable done interrupt mask */ +#define LCD_CTRL_BEDN (1 << 6) /* Endian selection */ +#define LCD_CTRL_PEDN (1 << 5) /* Endian in byte:0-msb first, 1-lsb first */ +#define LCD_CTRL_DIS (1 << 4) /* Disable indicate bit */ +#define LCD_CTRL_ENA (1 << 3) /* LCD enable bit */ +#define LCD_CTRL_BPP_BIT 0 /* Bits Per Pixel */ +#define LCD_CTRL_BPP_MASK (0x07 << LCD_CTRL_BPP_BIT) + #define LCD_CTRL_BPP_1 (0 << LCD_CTRL_BPP_BIT) /* 1 bpp */ + #define LCD_CTRL_BPP_2 (1 << LCD_CTRL_BPP_BIT) /* 2 bpp */ + #define LCD_CTRL_BPP_4 (2 << LCD_CTRL_BPP_BIT) /* 4 bpp */ + #define LCD_CTRL_BPP_8 (3 << LCD_CTRL_BPP_BIT) /* 8 bpp */ + #define LCD_CTRL_BPP_16 (4 << LCD_CTRL_BPP_BIT) /* 15/16 bpp */ + #define LCD_CTRL_BPP_18_24 (5 << LCD_CTRL_BPP_BIT) /* 18/24/32 bpp */ + +/* LCD Status Register */ +#define LCD_STATE_QD (1 << 7) /* Quick Disable Done */ +#define LCD_STATE_EOF (1 << 5) /* EOF Flag */ +#define LCD_STATE_SOF (1 << 4) /* SOF Flag */ +#define LCD_STATE_OFU (1 << 3) /* Output FIFO Underrun */ +#define LCD_STATE_IFU0 (1 << 2) /* Input FIFO 0 Underrun */ +#define LCD_STATE_IFU1 (1 << 1) /* Input FIFO 1 Underrun */ +#define LCD_STATE_LDD (1 << 0) /* LCD Disabled */ + +/* OSD Configure Register */ +#define LCD_OSDC_SOFM1 (1 << 15) /* Start of frame interrupt mask for foreground 1 */ +#define LCD_OSDC_EOFM1 (1 << 14) /* End of frame interrupt mask for foreground 1 */ +#define LCD_OSDC_REM1 (1 << 13) /* Real end of frame mask for foreground 1 */ +#define LCD_OSDC_SOFM0 (1 << 11) /* Start of frame interrupt mask for foreground 0 */ +#define LCD_OSDC_EOFM0 (1 << 10) /* End of frame interrupt mask for foreground 0 */ +#define LCD_OSDC_REM0 (1 << 9) /* Real end of frame mask for foreground 0 */ +#define LCD_OSDC_REMB (1 << 7) /* Real end of frame mask for background */ +#define LCD_OSDC_F1EN (1 << 4) /* enable foreground 1 */ +#define LCD_OSDC_F0EN (1 << 3) /* enable foreground 0 */ +#define LCD_OSDC_ALPHAEN (1 << 2) /* enable alpha blending */ +#define LCD_OSDC_ALPHAMD (1 << 1) /* alpha blending mode */ +#define LCD_OSDC_OSDEN (1 << 0) /* OSD mode enable */ + +/* OSD Controll Register */ +#define LCD_OSDCTRL_IPU (1 << 15) /* input data from IPU */ +#define LCD_OSDCTRL_RGB565 (0 << 4) /* foreground 1, 16bpp, 0-RGB565, 1-RGB555 */ +#define LCD_OSDCTRL_RGB555 (1 << 4) /* foreground 1, 16bpp, 0-RGB565, 1-RGB555 */ +#define LCD_OSDCTRL_CHANGES (1 << 3) /* Change size flag */ +#define LCD_OSDCTRL_OSDBPP_BIT 0 /* Bits Per Pixel of OSD Channel 1 */ +#define LCD_OSDCTRL_OSDBPP_MASK (0x7< + * + * 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__ */ diff --git a/include/asm-mips/mach-jz4750/war.h b/include/asm-mips/mach-jz4750/war.h new file mode 100644 index 0000000..3a5bc17 --- /dev/null +++ b/include/asm-mips/mach-jz4750/war.h @@ -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 + */ +#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 */ diff --git a/include/asm-mips/mach-jz4750d/board-cetus.h b/include/asm-mips/mach-jz4750d/board-cetus.h new file mode 100644 index 0000000..aea475d --- /dev/null +++ b/include/asm-mips/mach-jz4750d/board-cetus.h @@ -0,0 +1,124 @@ +/* + * linux/include/asm-mips/mach-jz4750d/board-cetus.h + * + * JZ4750D-based CETUS board ver 1.x definition. + * + * Copyright (C) 2008 Ingenic Semiconductor Inc. + * + * Author: + * + * 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_JZ4750D_CETUS_H__ +#define __ASM_JZ4750D_CETUS_H__ + +/*====================================================================== + * Frequencies of on-board oscillators + */ +#define JZ_EXTAL 24000000 +#define JZ_EXTAL2 32768 /* RTC extal freq: 32.768 KHz */ +//#define CFG_DIV 1 /* hclk=pclk=mclk=CFG_EXTAL/CFG_DIV, just for FPGA board */ + + +/*====================================================================== + * GPIO + */ + +#define GPIO_SD0_VCC_EN_N (32*4+0) /* CIM_D0 */ +#define GPIO_SD0_CD_N (32*4+1) /* CIM_D1 */ +#define GPIO_SD0_WP (32*4+2) /* CIM_D2 */ +#define GPIO_SD1_VCC_EN_N (32*4+3) /* CIM_D3 */ +#define GPIO_SD1_CD_N (32*4+4) /* CIM_D4 */ + +#define GPIO_USB_DETE (32*4+6) /* CIM_D6 */ +#define GPIO_DC_DETE_N (32*4+8) /* CIM_MCLK */ +#define GPIO_CHARG_STAT_N (32*4+10) /* CIM_VSYNC */ +#define GPIO_DISP_OFF_N (32*4+18) /* SDATO */ +#define GPIO_LCD_VCC_EN_N (32*4+19) /* SDATI */ +//#define GPIO_LED_EN 124 /* GPD28 */ + +#define GPIO_UDC_HOTPLUG GPIO_USB_DETE + +/*====================================================================== + * LCD backlight + */ +#define GPIO_LCD_PWM (32*4+22) /* GPE22 PWM2 */ +#define LCD_PWM_CHN 2 /* pwm channel */ +#define LCD_PWM_FULL 101 +/* 100 level: 0,1,...,100 */ +#define __lcd_set_backlight_level(n) \ +do { \ + __gpio_as_output(GPIO_LCD_PWM); \ + __gpio_set_pin(GPIO_LCD_PWM); \ +} while (0) + +#define __lcd_close_backlight() \ +do { \ + __gpio_as_output(GPIO_LCD_PWM); \ + __gpio_clear_pin(GPIO_LCD_PWM); \ +} while (0) + +/*====================================================================== + * MMC/SD + */ + +#define MSC0_WP_PIN GPIO_SD0_WP +#define MSC0_HOTPLUG_PIN GPIO_SD0_CD_N +#define MSC0_HOTPLUG_IRQ (IRQ_GPIO_0 + GPIO_SD0_CD_N) + +#define MSC1_WP_PIN GPIO_SD1_WP +#define MSC1_HOTPLUG_PIN GPIO_SD1_CD_N +#define MSC1_HOTPLUG_IRQ (IRQ_GPIO_0 + GPIO_SD1_CD_N) + +#define __msc0_init_io() \ +do { \ + __gpio_as_output(GPIO_SD0_VCC_EN_N); \ + __gpio_as_input(GPIO_SD0_CD_N); \ +} while (0) + +#define __msc0_enable_power() \ +do { \ + __gpio_clear_pin(GPIO_SD0_VCC_EN_N); \ +} while (0) + +#define __msc0_disable_power() \ +do { \ + __gpio_set_pin(GPIO_SD0_VCC_EN_N); \ +} while (0) + +#define __msc0_card_detected(s) \ +({ \ + int detected = 1; \ + if (__gpio_get_pin(GPIO_SD0_CD_N)) \ + detected = 0; \ + detected; \ +}) + +#define __msc1_init_io() \ +do { \ + __gpio_as_output(GPIO_SD1_VCC_EN_N); \ + /* __gpio_as_input(GPIO_SD1_CD_N);*/ \ +} while (0) + +#define __msc1_enable_power() \ +do { \ + __gpio_clear_pin(GPIO_SD1_VCC_EN_N); \ +} while (0) + +#define __msc1_disable_power() \ +do { \ + __gpio_set_pin(GPIO_SD1_VCC_EN_N); \ +} while (0) + +#define __msc1_card_detected(s) \ +({ \ + int detected = 0; \ + if (__gpio_get_pin(GPIO_SD1_CD_N)) \ + detected = 1; \ + detected; \ +}) + +#endif /* __ASM_JZ4750d_CETUS_H__ */ diff --git a/include/asm-mips/mach-jz4750d/board-draco.h b/include/asm-mips/mach-jz4750d/board-draco.h new file mode 100644 index 0000000..fe04f79 --- /dev/null +++ b/include/asm-mips/mach-jz4750d/board-draco.h @@ -0,0 +1,126 @@ +/* + * linux/include/asm-mips/mach-jz4750d/board-draco.h + * + * JZ4750D-based DRACO board ver 1.x definition. + * + * Copyright (C) 2008 Ingenic Semiconductor Inc. + * + * Author: + * + * 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_JZ4750D_DRACO_H__ +#define __ASM_JZ4750D_DRACO_H__ + +//#define CONFIG_FPGA /* fuwa is an FPGA board */ + +/*====================================================================== + * Frequencies of on-board oscillators + */ +//#define JZ_EXTAL 48000000 /* Main extal freq: 12 MHz */ +#define JZ_EXTAL 24000000 +#define JZ_EXTAL2 32768 /* RTC extal freq: 32.768 KHz */ +//#define CFG_DIV 1 /* hclk=pclk=mclk=CFG_EXTAL/CFG_DIV, just for FPGA board */ + + +/*====================================================================== + * GPIO + */ +#define GPIO_SD0_VCC_EN_N (32*2+10) /* GPC10 */ +#define GPIO_SD0_CD_N (32*2+11) /* GPC11 */ +#define GPIO_SD0_WP (32*2+12) /* GPC12 */ +//#define GPIO_SD1_VCC_EN_N (32*2+13) /* GPC13 */ +#define GPIO_SD1_VCC_EN_N (32*4+4) /* GPE4 */ +#define GPIO_SD1_CD_N (32*2+14) /* GPC14 */ +#define GPIO_USB_DETE (32*5+12) /* GPF12 */ +#define GPIO_DC_DETE_N 103 /* GPD7 */ +#define GPIO_CHARG_STAT_N 111 /* GPD15 */ +#define GPIO_DISP_OFF_N 121 /* GPD25, LCD_REV */ +//#define GPIO_LED_EN 124 /* GPD28 */ + +#define GPIO_UDC_HOTPLUG GPIO_USB_DETE + +/*====================================================================== + * LCD backlight + */ +#define GPIO_LCD_PWM (32*4+20) /* GPE20 */ + +#define LCD_PWM_CHN 0 /* pwm channel */ +#define LCD_PWM_FULL 101 +/* 100 level: 0,1,...,100 */ +#define __lcd_set_backlight_level(n) \ +do { \ + __gpio_as_output(GPIO_LCD_PWM); \ + __gpio_set_pin(GPIO_LCD_PWM); \ +} while (0) + +#define __lcd_close_backlight() \ +do { \ + __gpio_as_output(GPIO_LCD_PWM); \ + __gpio_clear_pin(GPIO_LCD_PWM); \ +} while (0) + +/*====================================================================== + * MMC/SD + */ + +#define MSC0_WP_PIN GPIO_SD0_WP +#define MSC0_HOTPLUG_PIN GPIO_SD0_CD_N +#define MSC0_HOTPLUG_IRQ (IRQ_GPIO_0 + GPIO_SD0_CD_N) + +#define MSC1_WP_PIN GPIO_SD1_WP +#define MSC1_HOTPLUG_PIN GPIO_SD1_CD_N +#define MSC1_HOTPLUG_IRQ (IRQ_GPIO_0 + GPIO_SD1_CD_N) + +#define __msc0_init_io() \ +do { \ + __gpio_as_output(GPIO_SD0_VCC_EN_N); \ + __gpio_as_input(GPIO_SD0_CD_N); \ +} while (0) + +#define __msc0_enable_power() \ +do { \ + __gpio_clear_pin(GPIO_SD0_VCC_EN_N); \ +} while (0) + +#define __msc0_disable_power() \ +do { \ + __gpio_set_pin(GPIO_SD0_VCC_EN_N); \ +} while (0) + +#define __msc0_card_detected(s) \ +({ \ + int detected = 1; \ + if (__gpio_get_pin(GPIO_SD0_CD_N)) \ + detected = 0; \ + detected; \ +}) + +#define __msc1_init_io() \ +do { \ + __gpio_as_output(GPIO_SD1_VCC_EN_N); \ + /* __gpio_as_input(GPIO_SD1_CD_N);*/ \ +} while (0) + +#define __msc1_enable_power() \ +do { \ + __gpio_clear_pin(GPIO_SD1_VCC_EN_N); \ +} while (0) + +#define __msc1_disable_power() \ +do { \ + __gpio_set_pin(GPIO_SD1_VCC_EN_N); \ +} while (0) + +#define __msc1_card_detected(s) \ +({ \ + int detected = 0; \ + if (__gpio_get_pin(GPIO_SD1_CD_N)) \ + detected = 1; \ + detected; \ +}) + +#endif /* __ASM_JZ4750d_DRACO_H__ */ diff --git a/include/asm-mips/mach-jz4750d/board-fuwa1.h b/include/asm-mips/mach-jz4750d/board-fuwa1.h new file mode 100644 index 0000000..7b8c6c2 --- /dev/null +++ b/include/asm-mips/mach-jz4750d/board-fuwa1.h @@ -0,0 +1,126 @@ +/* + * linux/include/asm-mips/mach-jz4750d/board-fuwa1.h + * + * JZ4750D-based FUWA1 board ver 1.x definition. + * + * Copyright (C) 2008 Ingenic Semiconductor Inc. + * + * Author: + * + * 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_JZ4750D_FUWA1_H__ +#define __ASM_JZ4750D_FUWA1_H__ + +//#define CONFIG_FPGA /* fuwa is an FPGA board */ + +/*====================================================================== + * Frequencies of on-board oscillators + */ +//#define JZ_EXTAL 48000000 /* Main extal freq: 12 MHz */ +#define JZ_EXTAL 24000000 +#define JZ_EXTAL2 32768 /* RTC extal freq: 32.768 KHz */ +//#define CFG_DIV 1 /* hclk=pclk=mclk=CFG_EXTAL/CFG_DIV, just for FPGA board */ + + +/*====================================================================== + * GPIO + */ +#define GPIO_SD0_VCC_EN_N (32*2+10) /* GPC10 */ +#define GPIO_SD0_CD_N (32*2+11) /* GPC11 */ +#define GPIO_SD0_WP (32*2+12) /* GPC12 */ +//#define GPIO_SD1_VCC_EN_N (32*2+13) /* GPC13 */ +#define GPIO_SD1_VCC_EN_N (32*4+4) /* GPE4 */ +#define GPIO_SD1_CD_N (32*2+14) /* GPC14 */ +#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 121 /* GPD25, LCD_REV */ +//#define GPIO_LED_EN 124 /* GPD28 */ + +#define GPIO_UDC_HOTPLUG GPIO_USB_DETE + +/*====================================================================== + * LCD backlight + */ +#define GPIO_LCD_PWM (32*4+20) /* GPE20 */ + +#define LCD_PWM_CHN 0 /* pwm channel */ +#define LCD_PWM_FULL 101 +/* 100 level: 0,1,...,100 */ +#define __lcd_set_backlight_level(n) \ +do { \ + __gpio_as_output(GPIO_LCD_PWM); \ + __gpio_set_pin(GPIO_LCD_PWM); \ +} while (0) + +#define __lcd_close_backlight() \ +do { \ + __gpio_as_output(GPIO_LCD_PWM); \ + __gpio_clear_pin(GPIO_LCD_PWM); \ +} while (0) + +/*====================================================================== + * MMC/SD + */ + +#define MSC0_WP_PIN GPIO_SD0_WP +#define MSC0_HOTPLUG_PIN GPIO_SD0_CD_N +#define MSC0_HOTPLUG_IRQ (IRQ_GPIO_0 + GPIO_SD0_CD_N) + +#define MSC1_WP_PIN GPIO_SD1_WP +#define MSC1_HOTPLUG_PIN GPIO_SD1_CD_N +#define MSC1_HOTPLUG_IRQ (IRQ_GPIO_0 + GPIO_SD1_CD_N) + +#define __msc0_init_io() \ +do { \ + __gpio_as_output(GPIO_SD0_VCC_EN_N); \ + __gpio_as_input(GPIO_SD0_CD_N); \ +} while (0) + +#define __msc0_enable_power() \ +do { \ + __gpio_clear_pin(GPIO_SD0_VCC_EN_N); \ +} while (0) + +#define __msc0_disable_power() \ +do { \ + __gpio_set_pin(GPIO_SD0_VCC_EN_N); \ +} while (0) + +#define __msc0_card_detected(s) \ +({ \ + int detected = 1; \ + if (__gpio_get_pin(GPIO_SD0_CD_N)) \ + detected = 0; \ + detected; \ +}) + +#define __msc1_init_io() \ +do { \ + __gpio_as_output(GPIO_SD1_VCC_EN_N); \ + /* __gpio_as_input(GPIO_SD1_CD_N);*/ \ +} while (0) + +#define __msc1_enable_power() \ +do { \ + __gpio_clear_pin(GPIO_SD1_VCC_EN_N); \ +} while (0) + +#define __msc1_disable_power() \ +do { \ + __gpio_set_pin(GPIO_SD1_VCC_EN_N); \ +} while (0) + +#define __msc1_card_detected(s) \ +({ \ + int detected = 0; \ + if (__gpio_get_pin(GPIO_SD1_CD_N)) \ + detected = 1; \ + detected; \ +}) + +#endif /* __ASM_JZ4750d_FUWA1_H__ */ diff --git a/include/asm-mips/mach-jz4750d/clock.h b/include/asm-mips/mach-jz4750d/clock.h new file mode 100644 index 0000000..a2832d6 --- /dev/null +++ b/include/asm-mips/mach-jz4750d/clock.h @@ -0,0 +1,230 @@ +/* + * linux/include/asm-mips/mach-jz4750d/clock.h + * + * JZ4750D clocks definition. + * + * Copyright (C) 2008 Ingenic Semiconductor Inc. + * + * Author: + * + * 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_JZ4750D_CLOCK_H__ +#define __ASM_JZ4750D_CLOCK_H__ + +#ifndef JZ_EXTAL +#define JZ_EXTAL 12000000 /* 3.6864 MHz */ +#endif +#ifndef JZ_EXTAL2 +#define JZ_EXTAL2 32768 /* 32.768 KHz */ +#endif + +/* + * JZ4750D 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 h1clk; /* AHB1 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) +{ +#if defined(CONFIG_FPGA) + return JZ_EXTAL/CFG_DIV; +#else + 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; +#endif +} + +/* PLL output frequency for MSC/I2S/LCD/USB */ +static __inline__ unsigned int __cpm_get_pllout2(void) +{ +#if defined(CONFIG_FPGA) + return JZ_EXTAL/CFG_DIV; +#else + if (REG_CPM_CPCCR & CPM_CPCCR_PCS) + return __cpm_get_pllout(); + else + return __cpm_get_pllout()/2; +#endif +} + +/* CPU core clock */ +static __inline__ unsigned int __cpm_get_cclk(void) +{ + +#if defined(CONFIG_FGPA) + return JZ_EXTAL; +#else + int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; + return __cpm_get_pllout() / div[__cpm_get_cdiv()]; +#endif +} + +/* AHB system bus clock */ +static __inline__ unsigned int __cpm_get_hclk(void) +{ +#if defined(CONFIG_FPGA) + return JZ_EXTAL/CFG_DIV; +#else + int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; + + return __cpm_get_pllout() / div[__cpm_get_hdiv()]; +#endif + +} + +/* Memory bus clock */ +static __inline__ unsigned int __cpm_get_mclk(void) +{ +#if defined(CONFIG_FPGA) + return JZ_EXTAL/CFG_DIV; +#else + int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; + + return __cpm_get_pllout() / div[__cpm_get_mdiv()]; +#endif +} + +/* APB peripheral bus clock */ +static __inline__ unsigned int __cpm_get_pclk(void) +{ +#if defined(CONFIG_FPGA) + return JZ_EXTAL/CFG_DIV; +#else + int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; + + return __cpm_get_pllout() / div[__cpm_get_pdiv()]; +#endif +} + +/* LCDC module clock */ +static __inline__ unsigned int __cpm_get_h1clk(void) +{ + return __cpm_get_pllout2() / (__cpm_get_h1div() + 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 + * @n: the index of MMC/SD controller + */ +static __inline__ unsigned int __cpm_get_mscclk(int n) +{ + return __cpm_get_pllout2() / (__cpm_get_mscdiv(n) + 1); +} + +/* EXTAL clock */ +static __inline__ unsigned int __cpm_get_extalclk0(void) +{ + return JZ_EXTAL; +} + +/* EXTAL clock for UART,I2C,SSI,TCU,USB-PHY */ +static __inline__ unsigned int __cpm_get_extalclk(void) +{ +#if defined(CONFIG_FPGA) + return __cpm_get_pllout(); +#else + if (REG_CPM_CPCCR & CPM_CPCCR_ECS) + return __cpm_get_extalclk0()/2; + else + return __cpm_get_extalclk0(); +#endif + +} + +/* 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. + * @n: the index of MMC/SD controller + */ +static inline void __cpm_select_msc_clk(int n, int sd) +{ + unsigned int pllout2 = __cpm_get_pllout2(); + unsigned int div = 0; + + if (sd) { + div = pllout2 / 24000000; + } + else { + div = pllout2 / 16000000; + } + + REG_CPM_MSCCDR(n) = div - 1; + REG_CPM_CPCCR |= CPM_CPCCR_CE; +} + +/* + * Output 48MHz for high speed card. + */ +static inline void __cpm_select_msc_clk_high(int n, int sd) +{ + unsigned int pllout2 = __cpm_get_pllout2(); + unsigned int div = 0; + + div = pllout2 / 48000000; + + REG_CPM_MSCCDR(n) = div - 1; + REG_CPM_CPCCR |= CPM_CPCCR_CE; +} + +#endif /* __ASM_JZ4750D_CLOCK_H__ */ diff --git a/include/asm-mips/mach-jz4750d/dma.h b/include/asm-mips/mach-jz4750d/dma.h new file mode 100644 index 0000000..3cca844 --- /dev/null +++ b/include/asm-mips/mach-jz4750d/dma.h @@ -0,0 +1,307 @@ +/* + * linux/include/asm-mips/mach-jz4750d/dma.h + * + * JZ4750D DMA definition. + * + * Copyright (C) 2008 Ingenic Semiconductor Inc. + * + * Author: + * + * 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_JZ4750D_DMA_H__ +#define __ASM_JZ4750D_DMA_H__ + +#include +#include /* need byte IO */ +#include /* And spinlocks */ +#include +#include + +/* + * Descriptor structure for JZ4750D DMA engine + * Note: this structure must always be aligned to a 16-bytes boundary. + */ + +/* old descriptor 4-word */ +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; + +/* new descriptor 8-word */ +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 */ + volatile u32 dstrd; /* DMA source and target stride address */ + volatile u32 dreqt; /* DMA request type for current transfer */ + volatile u32 reserved0; /* Reserved */ + volatile u32 reserved1; /* Reserved */ +} jz_dma_desc_8word; + +/* DMA Device ID's follow */ +enum { + DMA_ID_EXT = 0, /* External request with DREQn */ + DMA_ID_NAND, /* NAND DMA request */ + DMA_ID_BCH_ENC, /* BCH Encoding DMA request */ + DMA_ID_BCH_DEC, /* BCH Decoding DMA request */ + DMA_ID_AUTO, /* Auto-request */ +// DMA_ID_TSSI_RX, /* TSSI receive fifo full request */ + DMA_ID_UART3_TX, /* UART3 transmit-fifo-empty request */ + DMA_ID_UART3_RX, /* UART3 receve-fifo-full request */ + DMA_ID_UART2_TX, /* UART2 transmit-fifo-empty request */ + DMA_ID_UART2_RX, /* UART2 receve-fifo-full request */ + DMA_ID_UART1_TX, /* UART1 transmit-fifo-empty request */ + DMA_ID_UART1_RX, /* UART1 receve-fifo-full request */ + DMA_ID_UART0_TX, /* UART0 transmit-fifo-empty request */ + DMA_ID_UART0_RX, /* UART0 receve-fifo-full request */ + DMA_ID_SSI0_TX, /* SSI0 transmit-fifo-full request */ + DMA_ID_SSI0_RX, /* SSI0 receive-fifo-empty request */ + DMA_ID_AIC_TX, /* AIC transmit-fifo-full request */ + DMA_ID_AIC_RX, /* AIC receive-fifo-empty request */ + DMA_ID_MSC0_TX, /* MSC0 transmit-fifo-full request */ + DMA_ID_MSC0_RX, /* MSC0 receive-fifo-empty request */ + DMA_ID_TCU_OVERFLOW, /* TCU channel n overflow interrupt */ + DMA_ID_SADC, /* SADC transfer request */ + DMA_ID_MSC1_TX, /* MSC1 transmit-fifo-full request */ + DMA_ID_MSC1_RX, /* MSC1 receive-fifo-empty request */ + DMA_ID_SSI1_TX, /* SSI1 transmit-fifo-full request */ + DMA_ID_SSI1_RX, /* SSI1 receive-fifo-empty request */ + DMA_ID_PCM_TX, /* PM transmit-fifo-full request */ + DMA_ID_PCM_RX, /* PM receive-fifo-empty request */ + 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_32BYTE_TX_CMD \ + DMAC_DCMD_SAI | \ + DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | \ + 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 + +#define DMA_AIC_16BYTE_TX_CMD_UC \ + DMAC_DCMD_SAI | \ + DMAC_DCMD_SWDH_32 | 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 void jz_set_dma_src_width(int dmanr, int nbit); +extern void jz_set_dma_dest_width(int dmanr, int nbit); +extern void jz_set_dma_block_size(int dmanr, int nbyte); +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((chan->io)/HALF_DMA_NUM) &= ~(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((chan->io)/HALF_DMA_NUM) &= ~(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_JZ4750D_DMA_H__ */ diff --git a/include/asm-mips/mach-jz4750d/jz4750d.h b/include/asm-mips/mach-jz4750d/jz4750d.h new file mode 100644 index 0000000..ac59226 --- /dev/null +++ b/include/asm-mips/mach-jz4750d/jz4750d.h @@ -0,0 +1,43 @@ +/* + * linux/include/asm-mips/mach-jz4750d/jz4750d.h + * + * JZ4750 common definition. + * + * Copyright (C) 2008 Ingenic Semiconductor Inc. + * + * Author: + * + * 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_JZ4750_H__ +#define __ASM_JZ4750_H__ + +#include +#include +#include +#include + +/*------------------------------------------------------------------ + * Platform definitions + */ +#ifdef CONFIG_JZ4750D_FUWA1 +#include +#endif + +#ifdef CONFIG_JZ4750D_CETUS +#include +#endif +/* Add other platform definition here ... */ + + +/*------------------------------------------------------------------ + * Follows are related to platform definitions + */ + +#include +#include + +#endif /* __ASM_JZ4750_H__ */ diff --git a/include/asm-mips/mach-jz4750d/misc.h b/include/asm-mips/mach-jz4750d/misc.h new file mode 100644 index 0000000..cd0b13c --- /dev/null +++ b/include/asm-mips/mach-jz4750d/misc.h @@ -0,0 +1,44 @@ +/* + * linux/include/asm-mips/mach-jz4750d/misc.h + * + * Ingenic's JZ4750D common include. + * + * Copyright (C) 2008 Ingenic Semiconductor Inc. + * + * Author: + * + * 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_JZ4750D_MISC_H__ +#define __ASM_JZ4750D_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_JZ4750D_MISC_H__ */ diff --git a/include/asm-mips/mach-jz4750d/ops.h b/include/asm-mips/mach-jz4750d/ops.h new file mode 100644 index 0000000..f28651f --- /dev/null +++ b/include/asm-mips/mach-jz4750d/ops.h @@ -0,0 +1,3430 @@ +/* + * linux/include/asm-mips/mach-jz4750d/ops.h + * + * JZ4750D register definition. + * + * Copyright (C) 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. + */ + + +#ifndef __JZ4750D_OPS_H__ +#define __JZ4750D_OPS_H__ + +/* + * Definition of Module Operations + */ + +/*************************************************************************** + * EMC + ***************************************************************************/ +#define is_share_mode() (1) + +/*************************************************************************** + * GPIO + ***************************************************************************/ + +//------------------------------------------------------ +// GPIO Pins Description +// +// PORT 0: +// +// PIN/BIT N FUNC0 FUNC1 NOTE +// 0 D0 - +// 1 D1 - +// 2 D2 - +// 3 D3 - +// 4 D4 - +// 5 D5 - +// 6 D6 - +// 7 D7 - +// 8 D8 - +// 9 D9 - +// 10 D10 - +// 11 D11 - +// 12 D12 - +// 13 D13 - +// 14 D14 - +// 15 D15 - +// 16 D16 - +// 17 D17 - +// 18 D18 - +// 19 D19 - +// 20 D20 - +// 21 D21 - +// 22 D22 - +// 23 D23 - +// 24 D24 - +// 25 D25 - +// 26 D26 - +// 27 D27 - +// 28 D28 - +// 29 D29 - +// 30 D30 - +// 31 D31 - +// +//------------------------------------------------------ +// PORT 1: +// +// PIN/BIT N FUNC0 FUNC1 FUNC2 NOTE +// 0 A0 - - +// 1 A1 - - +// 2 A2 - - +// 3 A3 - - +// 4 A4 - - +// 5 A5 - - +// 6 A6 - - +// 7 A7 - - +// 8 A8 - - +// 9 A9 - - +// 10 A10 - - +// 11 A11 - - +// 12 A12 - - +// 13 A13 - - +// 14 A14 - - +// 15 A15/CLE CL(unshare) MSC0_CLK +// 16 DCS0# - - +// 17 RAS# - - +// 18 CAS# - - +// 19 SDWE#/BUFD# - - +// 20 WE0# - - +// 21 WE1# - - +// 22 WE2# - - +// 23 WE3# - - +// 24 CKO - - Note1 +// 25 CKE - - +// 26 SSI_CLK MSC1_CLK - +// 27 SSI_DT MSC1_D1 - +// 28 SSI_DR MSC1_D0 - +// 29 SSI_CE0# MSC1_CMD - +// 30 SSI_GPC MSC1_D2 - +// 31 SSI_CE1# MSC1_D3 - +// +// Note1: BIT24: it is CKO when chip is reset +// +//------------------------------------------------------ +// PORT 2: +// +// PIN/BIT N FUNC0 FUNC1 FUNC2 NOTE +// 0 SD0 A20 - +// 1 SD1 A21 - +// 2 SD2 A22 - +// 3 SD3 A23 - +// 4 SD4 A24 - +// 5 SD5 A25 - +// 6 SD6 - - +// 7 SD7 - - +// 8 SD8 TSDI0 - +// 9 SD9 TSDI1 - +// 10 SD10 TSDI2 - +// 11 SD11 TSDI3 - +// 12 SD12 TSDI4 - +// 13 SD13 TSDI5 - +// 14 SD14 TSDI6 - +// 15 SD15 TSDI7 - +// 16 A16/ALE AL(unshare) MSC0_CMD +// 17 A17 MSC0_D3 - +// 18 A18 DREQ - +// 19 A19 DACK - +// 20 WAIT# - - Note2 +// 21 CS1# - - +// 22 CS2# - - +// 23 CS3# - - +// 24 CS4# - - +// 25 RD# - - +// 26 WR# - - +// 27 FRB# - - Note3 +// 28 FRE# MSC0_D0 - +// 29 FWE# MSC0_D1 - +// 30 - - - Note4 +// 31 - - - Note5 +// +// Note2: BIT20: it is WIAT# pin when chip is reset +// +// Note3: BIT27: when NAND is used, it should connect to NANF FRB#. +// +// Note4: BIT30: it is BOOT_SEL0 which would be set as input without pulling when chip is reset. +// +// Note5: BIT31: it is BOOT_SEL1 which would be set as input without pulling when chip is reset. +// +//------------------------------------------------------ +// PORT 3: +// +// PIN/BIT N FUNC0 FUNC1 NOTE +// 0 LCD_B2 - +// 1 LCD_B3 - +// 2 LCD_B4 - +// 3 LCD_B5 - +// 4 LCD_B6 - +// 5 LCD_B7 - +// 6 LCD_G2 - +// 7 LCD_G3 - +// 8 LCD_G4 - +// 9 LCD_G5 - +// 10 LCD_G6 - +// 11 LCD_G7 - +// 12 LCD_R2 - +// 13 LCD_R3 - +// 14 LCD_R4 - +// 15 LCD_R5 - +// 16 LCD_R6 - +// 17 LCD_R7 - +// 18 LCD_PCLK - +// 19 LCD_HSYNC - +// 20 LCD_VSYNC - +// 21 LCD_DE - +// 22 LCD_CLS LCD_R1 +// 23 LCD_SPL LCD_G0 +// 24 LCD_PS LCD_G1 +// 25 LCD_REV LCD_B1 +// 26 LCD_B0 - +// 27 LCD_R0 - +// 28 UART0_RXD TSCLK +// 29 UART0_TXD TSSTR +// 30 UART0_CTS TSFRM +// 31 UART0_RTS TSFAIL +// +//------------------------------------------------------ +// PORT 4: +// +// PIN/BIT N FUNC0 FUNC1 FUNC2 NOTE +// 0 CIM_D0 TSDI0 - +// 1 CIM_D1 TSDI1 - +// 2 CIM_D2 TSDI2 - +// 3 CIM_D3 TSDI3 - +// 4 CIM_D4 TSDI4 - +// 5 CIM_D5 TSDI5 - +// 6 CIM_D6 TSDI6 - +// 7 CIM_D7 TSDI7 - +// 8 CIM_MCLK TSFAIL - +// 9 CIM_PCLK TSCLK - +// 10 CIM_VSYNC TSSTR - +// 11 CIM_HSYNC TSFRM - +// 12 I2C_SDA - - +// 13 I2C_SCK - - +// 18 SDATO - - +// 19 SDATI - - +// 20 PWM0 - - +// 22 PWM2 SYNC - +// 23 PWM3 UART1_RxD BCLK +// 24 PWM4 - - +// 25 PWM5 UART1_TxD SCLK_RSTN +// 28 DCS1# - - +// 29 - - - Note6 +// 30 WKUP - - Note7 +// 31 - - - Note8 +// +// Note6: BIT29: it is BOOT_SEL2 which would be set as input without pulling when chip is reset. +// Note7: BIT30: it is only used as input and interrupt, and with no pull-up and pull-down +// Note8: BIT31: it is used to select the function of UART or JTAG set by PESEL[31] +// PESEL[31] = 0, select JTAG function +// PESEL[31] = 1, select UART function +// +//------------------------------------------------------ +// PORT 5: +// +// PIN/BIT N FUNC0 FUNC1 NOTE +// 10 SSI_CLK - +// 11 SSI_DT PWM1 +// 12 SSI_DR - +// 13 SSI_CE0# - +// 14 SSI_GPC - +// 15 SSI_CE2# - +// +////////////////////////////////////////////////////////// + +/*---------------------------------------------------------------- + * p is the port number (0,1,2,3,4,5) + * o is the pin offset (0-31) inside the port + * n is the absolute number of a pin (0-127), regardless of the port + */ + +//------------------------------------------- +// Function Pins Mode + +#define __gpio_as_func0(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXFUNS(p) = (1 << o); \ + REG_GPIO_PXTRGC(p) = (1 << o); \ + REG_GPIO_PXSELC(p) = (1 << o); \ +} while (0) + +#define __gpio_as_func1(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXFUNS(p) = (1 << o); \ + REG_GPIO_PXTRGC(p) = (1 << o); \ + REG_GPIO_PXSELS(p) = (1 << o); \ +} while (0) + +#define __gpio_as_func2(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXFUNS(p) = (1 << o); \ + REG_GPIO_PXTRGS(p) = (1 << o); \ + REG_GPIO_PXSELC(p) = (1 << o); \ +} while (0) + +/* + * D0 ~ D31, A0 ~ A14, DCS0#, RAS#, CAS#, + * RDWE#, WE0#, WE1#, WE2#, WE3#, CKO#, CKE# + */ +#define __gpio_as_sdram_32bit() \ +do { \ + REG_GPIO_PXFUNS(0) = 0xffffffff; \ + REG_GPIO_PXSELC(0) = 0xffffffff; \ + REG_GPIO_PXPES(0) = 0xffffffff; \ + REG_GPIO_PXFUNS(1) = 0x03ff7fff; \ + REG_GPIO_PXSELC(1) = 0x03ff7fff; \ + REG_GPIO_PXPES(1) = 0x03ff7fff; \ +} while (0) + + +/* + * D0 ~ D31, A0 ~ A14, DCS0#, RAS#, CAS#, + * RDWE#, WE0#, WE1#, WE2#, WE3#, CKO#, CKE# + * !!!!DCS1# + */ +#define __gpio_as_sdram_x2_32bit() \ +do { \ + REG_GPIO_PXFUNS(0) = 0xffffffff; \ + REG_GPIO_PXSELC(0) = 0xffffffff; \ + REG_GPIO_PXPES(0) = 0xffffffff; \ + REG_GPIO_PXFUNS(1) = 0x03ff7fff; \ + REG_GPIO_PXSELC(1) = 0x03ff7fff; \ + REG_GPIO_PXPES(1) = 0x03ff7fff; \ + REG_GPIO_PXFUNS(4) = 0x10000000; \ + REG_GPIO_PXSELC(4) = 0x10000000; \ + REG_GPIO_PXPES(4) = 0x10000000; \ +} while (0) + +/* + * D0 ~ D15, A0 ~ A14, DCS0#, RAS#, CAS#, + * RDWE#, WE0#, WE1#, WE2#, WE3#, CKO#, CKE# + */ +#define __gpio_as_sdram_16bit() \ +do { \ + /* 32/16-bit data normal order */ \ + REG_GPIO_PXFUNS(0) = 0x0000ffff; \ + REG_GPIO_PXSELC(0) = 0x0000ffff; \ + REG_GPIO_PXPES(0) = 0x0000ffff; \ + REG_GPIO_PXFUNS(1) = 0x03ff7fff; \ + REG_GPIO_PXSELC(1) = 0x03ff7fff; \ + REG_GPIO_PXPES(1) = 0x03ff7fff; \ +} while (0) + +/* + * D0 ~ D7, CS1#, CLE, ALE, FRE#, FWE#, FRB#, RDWE#/BUFD# + * @n: chip select number(1 ~ 4) + */ +#define __gpio_as_nand_8bit(n) \ +do { \ + /* 32/16-bit data bus */ \ + REG_GPIO_PXFUNS(0) = 0x000000ff; /* D0~D7 */ \ + REG_GPIO_PXSELC(0) = 0x000000ff; \ + REG_GPIO_PXPES(0) = 0x000000ff; \ + REG_GPIO_PXFUNS(1) = 0x00008000; /* CLE(A15) */ \ + REG_GPIO_PXSELC(1) = 0x00008000; \ + REG_GPIO_PXPES(1) = 0x00008000; \ + REG_GPIO_PXFUNS(2) = 0x00010000; /* ALE(A16) */ \ + REG_GPIO_PXSELC(2) = 0x00010000; \ + REG_GPIO_PXPES(2) = 0x00010000; \ + \ + REG_GPIO_PXFUNS(2) = 0x00200000 << ((n)-1); /* CSn */ \ + REG_GPIO_PXSELC(2) = 0x00200000 << ((n)-1); \ + REG_GPIO_PXPES(2) = 0x00200000 << ((n)-1); \ + \ + REG_GPIO_PXFUNS(1) = 0x00080000; /* RDWE#/BUFD# */ \ + REG_GPIO_PXSELC(1) = 0x00080000; \ + REG_GPIO_PXPES(1) = 0x00080000; \ + REG_GPIO_PXFUNS(2) = 0x30000000; /* FRE#, FWE# */ \ + REG_GPIO_PXSELC(2) = 0x30000000; \ + REG_GPIO_PXPES(2) = 0x30000000; \ + REG_GPIO_PXFUNC(2) = 0x08000000; /* FRB#(input) */ \ + REG_GPIO_PXSELC(2) = 0x08000000; \ + REG_GPIO_PXDIRC(2) = 0x08000000; \ + REG_GPIO_PXPES(2) = 0x08000000; \ +} while (0) + + +/* + * CS4#, RD#, WR#, WAIT#, A0 ~ A22, D0 ~ D7 + * @n: chip select number(1 ~ 4) + */ +#define __gpio_as_nor_8bit(n) \ +do { \ + /* 32/16-bit data bus */ \ + REG_GPIO_PXFUNS(0) = 0x000000ff; \ + REG_GPIO_PXSELC(0) = 0x000000ff; \ + REG_GPIO_PXPES(0) = 0x000000ff; \ + \ + REG_GPIO_PXFUNS(2) = 0x00200000 << ((n)-1); /* CSn */ \ + REG_GPIO_PXSELC(2) = 0x00200000 << ((n)-1); \ + REG_GPIO_PXPES(2) = 0x00200000 << ((n)-1); \ + \ + REG_GPIO_PXFUNS(1) = 0x0000ffff; /* A0~A15 */ \ + REG_GPIO_PXSELC(1) = 0x0000ffff; \ + REG_GPIO_PXPES(1) = 0x0000ffff; \ + REG_GPIO_PXFUNS(2) = 0x06110007; /* RD#, WR#, WAIT#, A20~A22 */ \ + REG_GPIO_PXSELC(2) = 0x06110007; \ + REG_GPIO_PXPES(2) = 0x06110007; \ + REG_GPIO_PXFUNS(2) = 0x000e0000; /* A17~A19 */ \ + REG_GPIO_PXSELS(2) = 0x000e0000; \ + REG_GPIO_PXPES(2) = 0x000e0000; \ +} while (0) + +/* + * CS4#, RD#, WR#, WAIT#, A0 ~ A22, D0 ~ D15 + * @n: chip select number(1 ~ 4) + */ +#define __gpio_as_nor_16bit(n) \ +do { \ + /* 32/16-bit data normal order */ \ + REG_GPIO_PXFUNS(0) = 0x0000ffff; \ + REG_GPIO_PXSELC(0) = 0x0000ffff; \ + REG_GPIO_PXPES(0) = 0x0000ffff; \ + \ + REG_GPIO_PXFUNS(2) = 0x00200000 << ((n)-1); /* CSn */ \ + REG_GPIO_PXSELC(2) = 0x00200000 << ((n)-1); \ + REG_GPIO_PXPES(2) = 0x00200000 << ((n)-1); \ + \ + REG_GPIO_PXFUNS(1) = 0x0000ffff; /* A0~A15 */ \ + REG_GPIO_PXSELC(1) = 0x0000ffff; \ + REG_GPIO_PXPES(1) = 0x0000ffff; \ + REG_GPIO_PXFUNS(2) = 0x06110007; /* RD#, WR#, WAIT#, A20~A22 */ \ + REG_GPIO_PXSELC(2) = 0x06110007; \ + REG_GPIO_PXPES(2) = 0x06110007; \ + REG_GPIO_PXFUNS(2) = 0x000e0000; /* A17~A19 */ \ + REG_GPIO_PXSELS(2) = 0x000e0000; \ + REG_GPIO_PXPES(2) = 0x000e0000; \ +} while (0) + +/* + * UART0_TxD, UART0_RxD + */ +#define __gpio_as_uart0() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x30000000; \ + REG_GPIO_PXSELC(3) = 0x30000000; \ + REG_GPIO_PXPES(3) = 0x30000000; \ +} while (0) + +/* + * UART0_TxD, UART0_RxD, UART0_CTS, UART0_RTS + */ +#define __gpio_as_uart0_ctsrts() \ +do { \ + REG_GPIO_PXFUNS(3) = 0xf0000000; \ + REG_GPIO_PXSELC(3) = 0xf0000000; \ + REG_GPIO_PXPES(3) = 0xf0000000; \ +} while (0) + +/* + * UART1_TxD, UART1_RxD + */ +#define __gpio_as_uart1() \ +do { \ + REG_GPIO_PXTRGC(4) = 0x02800000; \ + REG_GPIO_PXFUNS(4) = 0x02800000; \ + REG_GPIO_PXSELS(4) = 0x02800000; \ + REG_GPIO_PXPES(4) = 0x02800000; \ +} while (0) + +/* + * TSCLK, TSSTR, TSFRM, TSFAIL, TSDI0~7 + */ +#define __gpio_as_tssi() \ +do { \ + REG_GPIO_PXFUNS(2) = 0x0000ff00; \ + REG_GPIO_PXSELS(2) = 0x0000ff00; \ + REG_GPIO_PXPES(2) = 0x0000ff00; \ + REG_GPIO_PXFUNS(3) = 0xf0000000; \ + REG_GPIO_PXSELS(3) = 0xf0000000; \ + REG_GPIO_PXPES(3) = 0xf0000000; \ +} while (0) + +/* + * LCD_D0~LCD_D7, LCD_PCLK, LCD_HSYNC, LCD_VSYNC, LCD_DE + */ +#define __gpio_as_lcd_8bit() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x003c00ff; \ + REG_GPIO_PXTRGC(3) = 0x003c00ff; \ + REG_GPIO_PXSELC(3) = 0x003c00ff; \ + REG_GPIO_PXPES(3) = 0x003c00ff; \ +} while (0) + +/* + * LCD_D0~LCD_D15, LCD_PCLK, LCD_HSYNC, LCD_VSYNC, LCD_DE + */ +#define __gpio_as_lcd_16bit() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x003cffff; \ + REG_GPIO_PXTRGC(3) = 0x003cffff; \ + REG_GPIO_PXSELC(3) = 0x003cffff; \ + REG_GPIO_PXPES(3) = 0x003cffff; \ +} while (0) + +/* + * LCD_R2~LCD_R7, LCD_G2~LCD_G7, LCD_B2~LCD_B7, + * LCD_PCLK, LCD_HSYNC, LCD_VSYNC, LCD_DE + */ +#define __gpio_as_lcd_18bit() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x003fffff; \ + REG_GPIO_PXTRGC(3) = 0x003fffff; \ + REG_GPIO_PXSELC(3) = 0x003fffff; \ + REG_GPIO_PXPES(3) = 0x003fffff; \ +} while (0) + +/* + * LCD_R0~LCD_R7, LCD_G0~LCD_G7, LCD_B0~LCD_B7, + * LCD_PCLK, LCD_HSYNC, LCD_VSYNC, LCD_DE + */ +#define __gpio_as_lcd_24bit() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x0fffffff; \ + REG_GPIO_PXTRGC(3) = 0x0fffffff; \ + REG_GPIO_PXSELC(3) = 0x0c3fffff; \ + REG_GPIO_PXSELS(3) = 0x03c00000; \ + REG_GPIO_PXPES(3) = 0x0fffffff; \ +} while (0) + +/* + * LCD_CLS, LCD_SPL, LCD_PS, LCD_REV + */ +#define __gpio_as_lcd_special() \ +do { \ + REG_GPIO_PXFUNS(3) = 0x03C00000; \ + REG_GPIO_PXSELC(3) = 0x03C00000; \ + REG_GPIO_PXPES(3) = 0x03C00000; \ +} while (0) + +/* + * CIM_D0~CIM_D7, CIM_MCLK, CIM_PCLK, CIM_VSYNC, CIM_HSYNC + */ +#define __gpio_as_cim() \ +do { \ + REG_GPIO_PXFUNS(4) = 0x00000fff; \ + REG_GPIO_PXSELC(4) = 0x00000fff; \ + REG_GPIO_PXPES(4) = 0x00000fff; \ +} while (0) + +/* + * SDATO, SDATI, BCLK, SYNC, SCLK_RSTN(gpio sepc) or + * SDATA_OUT, SDATA_IN, BIT_CLK, SYNC, SCLK_RESET(aic spec) + */ +#define __gpio_as_aic() \ +do { \ + REG_GPIO_PXFUNS(4) = 0x16c00000; \ + REG_GPIO_PXTRGC(4) = 0x02c00000; \ + REG_GPIO_PXTRGS(4) = 0x14000000; \ + REG_GPIO_PXSELC(4) = 0x14c00000; \ + REG_GPIO_PXSELS(4) = 0x02000000; \ + REG_GPIO_PXPES(4) = 0x16c00000; \ +} while (0) + +/* + * MSC0_CMD, MSC0_CLK, MSC0_D0 ~ MSC0_D3 + */ +#define __gpio_as_msc0_4bit() \ +do { \ + REG_GPIO_PXFUNS(1) = 0x00008000; \ + REG_GPIO_PXTRGS(1) = 0x00008000; \ + REG_GPIO_PXSELC(1) = 0x00008000; \ + REG_GPIO_PXPES(1) = 0x00008000; \ + REG_GPIO_PXFUNS(2) = 0x38030000; \ + REG_GPIO_PXTRGS(2) = 0x00010000; \ + REG_GPIO_PXTRGC(2) = 0x38020000; \ + REG_GPIO_PXSELC(2) = 0x08010000; \ + REG_GPIO_PXSELS(2) = 0x30020000; \ + REG_GPIO_PXPES(2) = 0x38030000; \ +} while (0) + + +/* + * MSC1_CMD, MSC1_CLK, MSC1_D0 ~ MSC1_D3 + */ +#define __gpio_as_msc1_4bit() \ +do { \ + REG_GPIO_PXFUNS(1) = 0xfc000000; \ + REG_GPIO_PXTRGC(1) = 0xfc000000; \ + REG_GPIO_PXSELS(1) = 0xfc000000; \ + REG_GPIO_PXPES(1) = 0xfc000000; \ +} while (0) + +#define __gpio_as_msc __gpio_as_msc0_4bit /* default as msc0 4bit */ +#define __gpio_as_msc0 __gpio_as_msc0_4bit /* msc0 default as 4bit */ +#define __gpio_as_msc1 __gpio_as_msc1_4bit /* msc1 only support 4bit */ + +/* + * SSI_CE0, SSI_CE1, SSI_GPC, SSI_CLK, SSI_DT, SSI_DR + */ +#define __gpio_as_ssi() \ +do { \ + REG_GPIO_PXFUNS(1) = 0xfc000000; \ + REG_GPIO_PXTRGC(1) = 0xfc000000; \ + REG_GPIO_PXSELC(1) = 0xfc000000; \ + REG_GPIO_PXPES(1) = 0xfc000000; \ +} while (0) + +/* + * SSI_CE0, SSI_CE2, SSI_GPC, SSI_CLK, SSI_DT, SSI1_DR + */ +#define __gpio_as_ssi_1() \ +do { \ + REG_GPIO_PXFUNS(5) = 0x0000fc00; \ + REG_GPIO_PXTRGC(5) = 0x0000fc00; \ + REG_GPIO_PXSELC(5) = 0x0000fc00; \ + REG_GPIO_PXPES(5) = 0x0000fc00; \ +} while (0) + +/* + * I2C_SCK, I2C_SDA + */ +#define __gpio_as_i2c() \ +do { \ + REG_GPIO_PXFUNS(4) = 0x00003000; \ + REG_GPIO_PXSELC(4) = 0x00003000; \ + REG_GPIO_PXPES(4) = 0x00003000; \ +} while (0) + +/* + * PWM0 + */ +#define __gpio_as_pwm0() \ +do { \ + REG_GPIO_PXFUNS(4) = 0x00100000; \ + REG_GPIO_PXSELC(4) = 0x00100000; \ + REG_GPIO_PXPES(4) = 0x00100000; \ +} while (0) + +/* + * PWM1 + */ +#define __gpio_as_pwm1() \ +do { \ + REG_GPIO_PXFUNS(5) = 0x00000800; \ + REG_GPIO_PXSELC(5) = 0x00000800; \ + REG_GPIO_PXPES(5) = 0x00000800; \ +} while (0) + +/* + * PWM2 + */ +#define __gpio_as_pwm2() \ +do { \ + REG_GPIO_PXFUNS(4) = 0x00400000; \ + REG_GPIO_PXSELC(4) = 0x00400000; \ + REG_GPIO_PXPES(4) = 0x00400000; \ +} while (0) + +/* + * PWM3 + */ +#define __gpio_as_pwm3() \ +do { \ + REG_GPIO_PXFUNS(4) = 0x00800000; \ + REG_GPIO_PXSELC(4) = 0x00800000; \ + REG_GPIO_PXPES(4) = 0x00800000; \ +} while (0) + +/* + * PWM4 + */ +#define __gpio_as_pwm4() \ +do { \ + REG_GPIO_PXFUNS(4) = 0x01000000; \ + REG_GPIO_PXSELC(4) = 0x01000000; \ + REG_GPIO_PXPES(4) = 0x01000000; \ +} while (0) + +/* + * PWM5 + */ +#define __gpio_as_pwm5() \ +do { \ + REG_GPIO_PXFUNS(4) = 0x02000000; \ + REG_GPIO_PXSELC(4) = 0x02000000; \ + REG_GPIO_PXPES(4) = 0x02000000; \ +} while (0) + +/* + * n = 0 ~ 5 + */ +#define __gpio_as_pwm(n) __gpio_as_pwm##n() + +/* + * DREQ + */ +#define __gpio_as_dreq() \ +do { \ + REG_GPIO_PXFUNS(2) = 0x00040000; \ + REG_GPIO_PXSELS(2) = 0x00040000; \ + REG_GPIO_PXPES(2) = 0x00040000; \ +} while (0) + +/* + * DACK + */ +#define __gpio_as_dack() \ +do { \ + REG_GPIO_PXFUNS(2) = 0x00080000; \ + REG_GPIO_PXSELS(2) = 0x00080000; \ + REG_GPIO_PXPES(2) = 0x00080000; \ +} while (0) + +/* + * GPIO or Interrupt Mode + */ +#define __gpio_get_port(p) (REG_GPIO_PXPIN(p)) + +#define __gpio_port_as_output(p, o) \ +do { \ + REG_GPIO_PXFUNC(p) = (1 << (o)); \ + REG_GPIO_PXSELC(p) = (1 << (o)); \ + REG_GPIO_PXDIRS(p) = (1 << (o)); \ +} while (0) + +#define __gpio_port_as_input(p, o) \ +do { \ + REG_GPIO_PXFUNC(p) = (1 << (o)); \ + REG_GPIO_PXSELC(p) = (1 << (o)); \ + REG_GPIO_PXDIRC(p) = (1 << (o)); \ +} while (0) + +#define __gpio_as_output(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + __gpio_port_as_output(p, o); \ +} while (0) + +#define __gpio_as_input(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + __gpio_port_as_input(p, o); \ +} while (0) + +#define __gpio_set_pin(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXDATS(p) = (1 << o); \ +} while (0) + +#define __gpio_clear_pin(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXDATC(p) = (1 << o); \ +} while (0) + +#define __gpio_get_pin(n) \ +({ \ + unsigned int p, o, v; \ + p = (n) / 32; \ + o = (n) % 32; \ + if (__gpio_get_port(p) & (1 << o)) \ + v = 1; \ + else \ + v = 0; \ + v; \ +}) + +#define __gpio_as_irq_high_level(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXIMS(p) = (1 << o); \ + REG_GPIO_PXTRGC(p) = (1 << o); \ + REG_GPIO_PXFUNC(p) = (1 << o); \ + REG_GPIO_PXSELS(p) = (1 << o); \ + REG_GPIO_PXDIRS(p) = (1 << o); \ + REG_GPIO_PXFLGC(p) = (1 << o); \ + REG_GPIO_PXIMC(p) = (1 << o); \ +} while (0) + +#define __gpio_as_irq_low_level(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXIMS(p) = (1 << o); \ + REG_GPIO_PXTRGC(p) = (1 << o); \ + REG_GPIO_PXFUNC(p) = (1 << o); \ + REG_GPIO_PXSELS(p) = (1 << o); \ + REG_GPIO_PXDIRC(p) = (1 << o); \ + REG_GPIO_PXFLGC(p) = (1 << o); \ + REG_GPIO_PXIMC(p) = (1 << o); \ +} while (0) + +#define __gpio_as_irq_rise_edge(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXIMS(p) = (1 << o); \ + REG_GPIO_PXTRGS(p) = (1 << o); \ + REG_GPIO_PXFUNC(p) = (1 << o); \ + REG_GPIO_PXSELS(p) = (1 << o); \ + REG_GPIO_PXDIRS(p) = (1 << o); \ + REG_GPIO_PXFLGC(p) = (1 << o); \ + REG_GPIO_PXIMC(p) = (1 << o); \ +} while (0) + +#define __gpio_as_irq_fall_edge(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXIMS(p) = (1 << o); \ + REG_GPIO_PXTRGS(p) = (1 << o); \ + REG_GPIO_PXFUNC(p) = (1 << o); \ + REG_GPIO_PXSELS(p) = (1 << o); \ + REG_GPIO_PXDIRC(p) = (1 << o); \ + REG_GPIO_PXFLGC(p) = (1 << o); \ + REG_GPIO_PXIMC(p) = (1 << o); \ +} while (0) + +#define __gpio_mask_irq(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXIMS(p) = (1 << o); \ +} while (0) + +#define __gpio_unmask_irq(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXIMC(p) = (1 << o); \ +} while (0) + +#define __gpio_ack_irq(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXFLGC(p) = (1 << o); \ +} while (0) + +#define __gpio_get_irq() \ +({ \ + unsigned int p, i, tmp, v = 0; \ + for (p = 3; p >= 0; p--) { \ + tmp = REG_GPIO_PXFLG(p); \ + for (i = 0; i < 32; i++) \ + if (tmp & (1 << i)) \ + v = (32*p + i); \ + } \ + v; \ +}) + +#define __gpio_group_irq(n) \ +({ \ + register int tmp, i; \ + tmp = REG_GPIO_PXFLG((n)); \ + for (i=31;i>=0;i--) \ + if (tmp & (1 << i)) \ + break; \ + i; \ +}) + +#define __gpio_enable_pull(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXPEC(p) = (1 << o); \ +} while (0) + +#define __gpio_disable_pull(n) \ +do { \ + unsigned int p, o; \ + p = (n) / 32; \ + o = (n) % 32; \ + REG_GPIO_PXPES(p) = (1 << o); \ +} while (0) + + +/*************************************************************************** + * CPM + ***************************************************************************/ +#define __cpm_get_pllm() \ + ((REG_CPM_CPPCR & CPM_CPPCR_PLLM_MASK) >> CPM_CPPCR_PLLM_BIT) +#define __cpm_get_plln() \ + ((REG_CPM_CPPCR & CPM_CPPCR_PLLN_MASK) >> CPM_CPPCR_PLLN_BIT) +#define __cpm_get_pllod() \ + ((REG_CPM_CPPCR & CPM_CPPCR_PLLOD_MASK) >> CPM_CPPCR_PLLOD_BIT) + +#define __cpm_get_cdiv() \ + ((REG_CPM_CPCCR & CPM_CPCCR_CDIV_MASK) >> CPM_CPCCR_CDIV_BIT) +#define __cpm_get_hdiv() \ + ((REG_CPM_CPCCR & CPM_CPCCR_HDIV_MASK) >> CPM_CPCCR_HDIV_BIT) +#define __cpm_get_pdiv() \ + ((REG_CPM_CPCCR & CPM_CPCCR_PDIV_MASK) >> CPM_CPCCR_PDIV_BIT) +#define __cpm_get_mdiv() \ + ((REG_CPM_CPCCR & CPM_CPCCR_MDIV_MASK) >> CPM_CPCCR_MDIV_BIT) +#define __cpm_get_h1div() \ + ((REG_CPM_CPCCR & CPM_CPCCR_H1DIV_MASK) >> CPM_CPCCR_H1DIV_BIT) +#define __cpm_get_udiv() \ + ((REG_CPM_CPCCR & CPM_CPCCR_UDIV_MASK) >> CPM_CPCCR_UDIV_BIT) +#define __cpm_get_i2sdiv() \ + ((REG_CPM_I2SCDR & CPM_I2SCDR_I2SDIV_MASK) >> CPM_I2SCDR_I2SDIV_BIT) +#define __cpm_get_pixdiv() \ + ((REG_CPM_LPCDR & CPM_LPCDR_PIXDIV_MASK) >> CPM_LPCDR_PIXDIV_BIT) +#define __cpm_get_mscdiv(n) \ + ((REG_CPM_MSCCDR(n) & CPM_MSCCDR_MSCDIV_MASK) >> CPM_MSCCDR_MSCDIV_BIT) +#define __cpm_get_uhcdiv() \ + ((REG_CPM_UHCCDR & CPM_UHCCDR_UHCDIV_MASK) >> CPM_UHCCDR_UHCDIV_BIT) +#define __cpm_get_ssidiv() \ + ((REG_CPM_SSICCDR & CPM_SSICDR_SSICDIV_MASK) >> CPM_SSICDR_SSIDIV_BIT) +#define __cpm_get_pcmdiv(v) \ + ((REG_CPM_PCMCDR & CPM_PCMCDR_PCMCD_MASK) >> CPM_PCMCDR_PCMCD_BIT) + +#define __cpm_set_cdiv(v) \ + (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_CDIV_MASK) | ((v) << (CPM_CPCCR_CDIV_BIT))) +#define __cpm_set_hdiv(v) \ + (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_HDIV_MASK) | ((v) << (CPM_CPCCR_HDIV_BIT))) +#define __cpm_set_pdiv(v) \ + (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_PDIV_MASK) | ((v) << (CPM_CPCCR_PDIV_BIT))) +#define __cpm_set_mdiv(v) \ + (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_MDIV_MASK) | ((v) << (CPM_CPCCR_MDIV_BIT))) +#define __cpm_set_h1div(v) \ + (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_H1DIV_MASK) | ((v) << (CPM_CPCCR_H1DIV_BIT))) +#define __cpm_set_udiv(v) \ + (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_UDIV_MASK) | ((v) << (CPM_CPCCR_UDIV_BIT))) +#define __cpm_set_i2sdiv(v) \ + (REG_CPM_I2SCDR = (REG_CPM_I2SCDR & ~CPM_I2SCDR_I2SDIV_MASK) | ((v) << (CPM_I2SCDR_I2SDIV_BIT))) +#define __cpm_set_pixdiv(v) \ + (REG_CPM_LPCDR = (REG_CPM_LPCDR & ~CPM_LPCDR_PIXDIV_MASK) | ((v) << (CPM_LPCDR_PIXDIV_BIT))) +#define __cpm_set_mscdiv(n, v) \ + (REG_CPM_MSCCDR(n) = (REG_CPM_MSCCDR(n) & ~CPM_MSCCDR_MSCDIV_MASK) | ((v) << (CPM_MSCCDR_MSCDIV_BIT))) +#define __cpm_set_uhcdiv(v) \ + (REG_CPM_UHCCDR = (REG_CPM_UHCCDR & ~CPM_UHCCDR_UHCDIV_MASK) | ((v) << (CPM_UHCCDR_UHCDIV_BIT))) +#define __cpm_set_ssidiv(v) \ + (REG_CPM_SSICDR = (REG_CPM_SSICDR & ~CPM_SSICDR_SSIDIV_MASK) | ((v) << (CPM_SSICDR_SSIDIV_BIT))) +#define __cpm_set_pcmdiv(v) \ + (REG_CPM_PCMCDR = (REG_CPM_PCMCDR & ~CPM_PCMCDR_PCMCD_MASK) | ((v) << (CPM_PCMCDR_PCMCD_BIT))) + +#define __cpm_select_pcmclk_pll() (REG_CPM_PCMCDR |= CPM_PCMCDR_PCMS) +#define __cpm_select_pcmclk_exclk() (REG_CPM_PCMCDR &= ~CPM_PCMCDR_PCMS) +#define __cpm_select_tveclk_exclk() (REG_CPM_LPCDR |= CPM_CPCCR_LSCS) +#define __cpm_select_tveclk_pll() (REG_CPM_LPCDR &= ~CPM_LPCDR_LSCS) +#define __cpm_select_pixclk_lcd() (REG_CPM_LPCDR &= ~CPM_LPCDR_LTCS) +#define __cpm_select_pixclk_tve() (REG_CPM_LPCDR |= CPM_LPCDR_LTCS) +#define __cpm_select_i2sclk_exclk() (REG_CPM_CPCCR &= ~CPM_CPCCR_I2CS) +#define __cpm_select_i2sclk_pll() (REG_CPM_CPCCR |= CPM_CPCCR_I2CS) +#define __cpm_select_usbclk_exclk() (REG_CPM_CPCCR &= ~CPM_CPCCR_UCS) +#define __cpm_select_usbclk_pll() (REG_CPM_CPCCR |= CPM_CPCCR_UCS) + +#define __cpm_enable_cko() +#define __cpm_exclk_direct() (REG_CPM_CPCCR &= ~CPM_CPCCR_ECS) +#define __cpm_exclk_div2() (REG_CPM_CPCCR |= CPM_CPCCR_ECS) +#define __cpm_enable_pll_change() (REG_CPM_CPCCR |= CPM_CPCCR_CE) +#define __cpm_pllout_direct() (REG_CPM_CPCCR |= CPM_CPCCR_PCS) +#define __cpm_pllout_div2() (REG_CPM_CPCCR &= ~CPM_CPCCR_PCS) +#define __cpm_pll_enable() (REG_CPM_CPPCR |= CPM_CPPCR_PLLEN) + +#define __cpm_pll_is_off() (REG_CPM_CPPSR & CPM_CPPSR_PLLOFF) +#define __cpm_pll_is_on() (REG_CPM_CPPSR & CPM_CPPSR_PLLON) +#define __cpm_pll_bypass() (REG_CPM_CPPSR |= CPM_CPPSR_PLLBP) + +#define __cpm_get_cclk_doze_duty() \ + ((REG_CPM_LCR & CPM_LCR_DOZE_DUTY_MASK) >> CPM_LCR_DOZE_DUTY_BIT) +#define __cpm_set_cclk_doze_duty(v) \ + (REG_CPM_LCR = (REG_CPM_LCR & ~CPM_LCR_DOZE_DUTY_MASK) | ((v) << (CPM_LCR_DOZE_DUTY_BIT))) + +#define __cpm_doze_mode() (REG_CPM_LCR |= CPM_LCR_DOZE_ON) +#define __cpm_idle_mode() \ + (REG_CPM_LCR = (REG_CPM_LCR & ~CPM_LCR_LPM_MASK) | CPM_LCR_LPM_IDLE) +#define __cpm_sleep_mode() \ + (REG_CPM_LCR = (REG_CPM_LCR & ~CPM_LCR_LPM_MASK) | CPM_LCR_LPM_SLEEP) + +#define __cpm_stop_all() (REG_CPM_CLKGR = 0x1fffffff) +#define __cpm_stop_cimram() (REG_CPM_CLKGR |= CPM_CLKGR_CIMRAM) +#define __cpm_stop_idct() (REG_CPM_CLKGR |= CPM_CLKGR_IDCT) +#define __cpm_stop_db() (REG_CPM_CLKGR |= CPM_CLKGR_DB) +#define __cpm_stop_me() (REG_CPM_CLKGR |= CPM_CLKGR_ME) +#define __cpm_stop_mc() (REG_CPM_CLKGR |= CPM_CLKGR_MC) +#define __cpm_stop_tve() (REG_CPM_CLKGR |= CPM_CLKGR_TVE) +#define __cpm_stop_tssi() (REG_CPM_CLKGR |= CPM_CLKGR_TSSI) +#define __cpm_stop_owi() (REG_CPM_CLKGR |= CPM_CLKGR_OWI) +#define __cpm_stop_pcm() (REG_CPM_CLKGR |= CPM_CLKGR_PCM) +#define __cpm_stop_uart3() (REG_CPM_CLKGR |= CPM_CLKGR_UART3) +#define __cpm_stop_uart2() (REG_CPM_CLKGR |= CPM_CLKGR_UART2) +#define __cpm_stop_uart1() (REG_CPM_CLKGR |= CPM_CLKGR_UART1) +#define __cpm_stop_uhc() (REG_CPM_CLKGR |= CPM_CLKGR_UHC) +#define __cpm_stop_ipu() (REG_CPM_CLKGR |= CPM_CLKGR_IPU) +#define __cpm_stop_dmac() (REG_CPM_CLKGR |= CPM_CLKGR_DMAC) +#define __cpm_stop_udc() (REG_CPM_CLKGR |= CPM_CLKGR_UDC) +#define __cpm_stop_lcd() (REG_CPM_CLKGR |= CPM_CLKGR_LCD) +#define __cpm_stop_cim() (REG_CPM_CLKGR |= CPM_CLKGR_CIM) +#define __cpm_stop_sadc() (REG_CPM_CLKGR |= CPM_CLKGR_SADC) +#define __cpm_stop_msc(n) (REG_CPM_CLKGR |= CPM_CLKGR_MSC##n) +#define __cpm_stop_aic1() (REG_CPM_CLKGR |= CPM_CLKGR_AIC1) +#define __cpm_stop_aic2() (REG_CPM_CLKGR |= CPM_CLKGR_AIC2) +#define __cpm_stop_ssi(n) (REG_CPM_CLKGR |= CPM_CLKGR_SSI##n) +#define __cpm_stop_i2c() (REG_CPM_CLKGR |= CPM_CLKGR_I2C) +#define __cpm_stop_rtc() (REG_CPM_CLKGR |= CPM_CLKGR_RTC) +#define __cpm_stop_tcu() (REG_CPM_CLKGR |= CPM_CLKGR_TCU) +#define __cpm_stop_uart0() (REG_CPM_CLKGR |= CPM_CLKGR_UART0) + +#define __cpm_start_all() (REG_CPM_CLKGR = 0x0) +#define __cpm_start_cimram() (REG_CPM_CLKGR &= ~CPM_CLKGR_CIMRAM) +#define __cpm_start_idct() (REG_CPM_CLKGR &= ~CPM_CLKGR_IDCT) +#define __cpm_start_db() (REG_CPM_CLKGR &= ~CPM_CLKGR_DB) +#define __cpm_start_me() (REG_CPM_CLKGR &= ~CPM_CLKGR_ME) +#define __cpm_start_mc() (REG_CPM_CLKGR &= ~CPM_CLKGR_MC) +#define __cpm_start_tve() (REG_CPM_CLKGR &= ~CPM_CLKGR_TVE) +#define __cpm_start_tssi() (REG_CPM_CLKGR &= ~CPM_CLKGR_TSSI) +#define __cpm_start_owi() (REG_CPM_CLKGR &= ~CPM_CLKGR_OWI) +#define __cpm_start_pcm() (REG_CPM_CLKGR &= ~CPM_CLKGR_PCM) +#define __cpm_start_uart3() (REG_CPM_CLKGR &= ~CPM_CLKGR_UART3) +#define __cpm_start_uart2() (REG_CPM_CLKGR &= ~CPM_CLKGR_UART2) +#define __cpm_start_uart1() (REG_CPM_CLKGR &= ~CPM_CLKGR_UART1) +#define __cpm_start_uhc() (REG_CPM_CLKGR &= ~CPM_CLKGR_UHC) +#define __cpm_start_ipu() (REG_CPM_CLKGR &= ~CPM_CLKGR_IPU) +#define __cpm_start_dmac() (REG_CPM_CLKGR &= ~CPM_CLKGR_DMAC) +#define __cpm_start_udc() (REG_CPM_CLKGR &= ~CPM_CLKGR_UDC) +#define __cpm_start_lcd() (REG_CPM_CLKGR &= ~CPM_CLKGR_LCD) +#define __cpm_start_cim() (REG_CPM_CLKGR &= ~CPM_CLKGR_CIM) +#define __cpm_start_sadc() (REG_CPM_CLKGR &= ~CPM_CLKGR_SADC) +#define __cpm_start_msc(n) (REG_CPM_CLKGR &= ~CPM_CLKGR_MSC##n) +#define __cpm_start_aic1() (REG_CPM_CLKGR &= ~CPM_CLKGR_AIC1) +#define __cpm_start_aic2() (REG_CPM_CLKGR &= ~CPM_CLKGR_AIC2) +#define __cpm_start_ssi(n) (REG_CPM_CLKGR &= ~CPM_CLKGR_SSI##n) +#define __cpm_start_i2c() (REG_CPM_CLKGR &= ~CPM_CLKGR_I2C) +#define __cpm_start_rtc() (REG_CPM_CLKGR &= ~CPM_CLKGR_RTC) +#define __cpm_start_tcu() (REG_CPM_CLKGR &= ~CPM_CLKGR_TCU) +#define __cpm_start_uart0() (REG_CPM_CLKGR &= ~CPM_CLKGR_UART0) + +#define __cpm_get_o1st() \ + ((REG_CPM_OPCR & CPM_OPCR_O1ST_MASK) >> CPM_OPCR_O1ST_BIT) +#define __cpm_set_o1st(v) \ + (REG_CPM_OPCR = (REG_CPM_OPCR & ~CPM_OPCR_O1ST_MASK) | ((v) << (CPM_OPCR_O1ST_BIT))) +#define __cpm_enable_uhcphy() (REG_CPM_OPCR &= ~CPM_OPCR_UHCPHY_DISABLE) +#define __cpm_suspend_uhcphy() (REG_CPM_OPCR |= CPM_OPCR_UHCPHY_DISABLE) +#define __cpm_enable_udcphy() (REG_CPM_OPCR |= CPM_OPCR_UDCPHY_ENABLE) +#define __cpm_suspend_udcphy() (REG_CPM_OPCR &= ~CPM_OPCR_UDCPHY_ENABLE) +#define __cpm_enable_osc_in_sleep() (REG_CPM_OPCR |= CPM_OPCR_OSC_ENABLE) +#define __cpm_disable_osc_in_sleep() (REG_CPM_OPCR &= ~CPM_OPCR_OSC_ENABLE) +#define __cpm_select_rtcclk_rtc() (REG_CPM_OPCR |= CPM_OPCR_ERCS) +#define __cpm_select_rtcclk_exclk() (REG_CPM_OPCR &= ~CPM_OPCR_ERCS) + + +/*************************************************************************** + * TCU + ***************************************************************************/ +// where 'n' is the TCU channel +#define __tcu_select_extalclk(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~(TCU_TCSR_EXT_EN | TCU_TCSR_RTC_EN | TCU_TCSR_PCK_EN)) | TCU_TCSR_EXT_EN) +#define __tcu_select_rtcclk(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~(TCU_TCSR_EXT_EN | TCU_TCSR_RTC_EN | TCU_TCSR_PCK_EN)) | TCU_TCSR_RTC_EN) +#define __tcu_select_pclk(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~(TCU_TCSR_EXT_EN | TCU_TCSR_RTC_EN | TCU_TCSR_PCK_EN)) | TCU_TCSR_PCK_EN) +#define __tcu_disable_pclk(n) \ + REG_TCU_TCSR(n) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PCK_EN); +#define __tcu_select_clk_div1(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE1) +#define __tcu_select_clk_div4(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE4) +#define __tcu_select_clk_div16(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE16) +#define __tcu_select_clk_div64(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE64) +#define __tcu_select_clk_div256(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE256) +#define __tcu_select_clk_div1024(n) \ + (REG_TCU_TCSR((n)) = (REG_TCU_TCSR((n)) & ~TCU_TCSR_PRESCALE_MASK) | TCU_TCSR_PRESCALE1024) + +#define __tcu_enable_pwm_output(n) (REG_TCU_TCSR((n)) |= TCU_TCSR_PWM_EN) +#define __tcu_disable_pwm_output(n) (REG_TCU_TCSR((n)) &= ~TCU_TCSR_PWM_EN) + +#define __tcu_init_pwm_output_high(n) (REG_TCU_TCSR((n)) |= TCU_TCSR_PWM_INITL_HIGH) +#define __tcu_init_pwm_output_low(n) (REG_TCU_TCSR((n)) &= ~TCU_TCSR_PWM_INITL_HIGH) + +#define __tcu_set_pwm_output_shutdown_graceful(n) (REG_TCU_TCSR((n)) &= ~TCU_TCSR_PWM_SD) +#define __tcu_set_pwm_output_shutdown_abrupt(n) (REG_TCU_TCSR((n)) |= TCU_TCSR_PWM_SD) + +#define __tcu_clear_counter_to_zero(n) (REG_TCU_TCSR((n)) |= TCU_TCSR_CNT_CLRZ) + +#define __tcu_ost_enabled() (REG_TCU_TER & TCU_TER_OSTEN) +#define __tcu_enable_ost() (REG_TCU_TESR = TCU_TESR_OSTST) +#define __tcu_disable_ost() (REG_TCU_TECR = TCU_TECR_OSTCL) + +#define __tcu_counter_enabled(n) (REG_TCU_TER & (1 << (n))) +#define __tcu_start_counter(n) (REG_TCU_TESR |= (1 << (n))) +#define __tcu_stop_counter(n) (REG_TCU_TECR |= (1 << (n))) + +#define __tcu_half_match_flag(n) (REG_TCU_TFR & (1 << ((n) + 16))) +#define __tcu_full_match_flag(n) (REG_TCU_TFR & (1 << (n))) +#define __tcu_set_half_match_flag(n) (REG_TCU_TFSR = (1 << ((n) + 16))) +#define __tcu_set_full_match_flag(n) (REG_TCU_TFSR = (1 << (n))) +#define __tcu_clear_half_match_flag(n) (REG_TCU_TFCR = (1 << ((n) + 16))) +#define __tcu_clear_full_match_flag(n) (REG_TCU_TFCR = (1 << (n))) +#define __tcu_mask_half_match_irq(n) (REG_TCU_TMSR = (1 << ((n) + 16))) +#define __tcu_mask_full_match_irq(n) (REG_TCU_TMSR = (1 << (n))) +#define __tcu_unmask_half_match_irq(n) (REG_TCU_TMCR = (1 << ((n) + 16))) +#define __tcu_unmask_full_match_irq(n) (REG_TCU_TMCR = (1 << (n))) + +#define __tcu_ost_match_flag() (REG_TCU_TFR & TCU_TFR_OSTFLAG) +#define __tcu_set_ost_match_flag() (REG_TCU_TFSR = TCU_TFSR_OSTFST) +#define __tcu_clear_ost_match_flag() (REG_TCU_TFCR = TCU_TFCR_OSTFCL) +#define __tcu_ost_match_irq_masked() (REG_TCU_TMR & TCU_TMR_OSTMASK) +#define __tcu_mask_ost_match_irq() (REG_TCU_TMSR = TCU_TMSR_OSTMST) +#define __tcu_unmask_ost_match_irq() (REG_TCU_TMCR = TCU_TMCR_OSTMCL) + +#define __tcu_wdt_clock_stopped() (REG_TCU_TSR & TCU_TSSR_WDTSC) +#define __tcu_ost_clock_stopped() (REG_TCU_TSR & TCU_TSR_OST) +#define __tcu_timer_clock_stopped(n) (REG_TCU_TSR & (1 << (n))) + +#define __tcu_start_wdt_clock() (REG_TCU_TSCR = TCU_TSSR_WDTSC) +#define __tcu_start_ost_clock() (REG_TCU_TSCR = TCU_TSCR_OSTSC) +#define __tcu_start_timer_clock(n) (REG_TCU_TSCR = (1 << (n))) + +#define __tcu_stop_wdt_clock() (REG_TCU_TSSR = TCU_TSSR_WDTSC) +#define __tcu_stop_ost_clock() (REG_TCU_TSSR = TCU_TSSR_OSTSS) +#define __tcu_stop_timer_clock(n) (REG_TCU_TSSR = (1 << (n))) + +#define __tcu_get_count(n) (REG_TCU_TCNT((n))) +#define __tcu_set_count(n,v) (REG_TCU_TCNT((n)) = (v)) +#define __tcu_set_full_data(n,v) (REG_TCU_TDFR((n)) = (v)) +#define __tcu_set_half_data(n,v) (REG_TCU_TDHR((n)) = (v)) + +/* TCU2, counter 1, 2*/ +#define __tcu_read_real_value(n) (REG_TCU_TSTR & (1 << ((n) + 16))) +#define __tcu_read_false_value(n) (REG_TCU_TSTR & (1 << ((n) + 16))) +#define __tcu_counter_busy(n) (REG_TCU_TSTR & (1 << (n))) +#define __tcu_counter_ready(n) (REG_TCU_TSTR & (1 << (n))) + +#define __tcu_set_read_real_value(n) (REG_TCU_TSTSR = (1 << ((n) + 16))) +#define __tcu_set_read_false_value(n) (REG_TCU_TSTCR = (1 << ((n) + 16))) +#define __tcu_set_counter_busy(n) (REG_TCU_TSTSR = (1 << (n))) +#define __tcu_set_counter_ready(n) (REG_TCU_TSTCR = (1 << (n))) + +/* ost counter */ +#define __ostcu_set_pwm_output_shutdown_graceful() (REG_TCU_OSTCSR &= ~TCU_TCSR_PWM_SD) +#define __ostcu_set_ost_output_shutdown_abrupt() (REG_TCU_OSTCSR |= TCU_TCSR_PWM_SD) +#define __ostcu_select_clk_div1() \ + (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~TCU_OSTCSR_PRESCALE_MASK) | TCU_OSTCSR_PRESCALE1) +#define __ostcu_select_clk_div4() \ + (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~TCU_OSTCSR_PRESCALE_MASK) | TCU_OSTCSR_PRESCALE4) +#define __ostcu_select_clk_div16() \ + (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~TCU_OSTCSR_PRESCALE_MASK) | TCU_OSTCSR_PRESCALE16) +#define __ostcu_select_clk_div64() \ + (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~TCU_OSTCSR_PRESCALE_MASK) | TCU_OSTCSR_PRESCALE64) +#define __ostcu_select_clk_div256() \ + (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~TCU_OSTCSR_PRESCALE_MASK) | TCU_OSTCSR_PRESCALE256) +#define __ostcu_select_clk_div1024() \ + (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~TCU_OSTCSR_PRESCALE_MASK) | TCU_OSTCSR_PRESCALE1024) +#define __ostcu_select_rtcclk() \ + (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~(TCU_OSTCSR_EXT_EN | TCU_OSTCSR_RTC_EN | TCU_OSTCSR_PCK_EN)) | TCU_OSTCSR_RTC_EN) +#define __ostcu_select_extalclk() \ + (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~(TCU_OSTCSR_EXT_EN | TCU_OSTCSR_RTC_EN | TCU_OSTCSR_PCK_EN)) | TCU_OSTCSR_EXT_EN) +#define __ostcu_select_pclk() \ + (REG_TCU_OSTCSR = (REG_TCU_OSTCSR & ~(TCU_OSTCSR_EXT_EN | TCU_OSTCSR_RTC_EN | TCU_OSTCSR_PCK_EN)) | TCU_OSTCSR_PCK_EN) + + +/*************************************************************************** + * WDT + ***************************************************************************/ +#define __wdt_start() ( REG_WDT_TCER |= WDT_TCER_TCEN ) +#define __wdt_stop() ( REG_WDT_TCER &= ~WDT_TCER_TCEN ) +#define __wdt_set_count(v) ( REG_WDT_TCNT = (v) ) +#define __wdt_set_data(v) ( REG_WDT_TDR = (v) ) + +#define __wdt_select_extalclk() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~(WDT_TCSR_EXT_EN | WDT_TCSR_RTC_EN | WDT_TCSR_PCK_EN)) | WDT_TCSR_EXT_EN) +#define __wdt_select_rtcclk() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~(WDT_TCSR_EXT_EN | WDT_TCSR_RTC_EN | WDT_TCSR_PCK_EN)) | WDT_TCSR_RTC_EN) +#define __wdt_select_pclk() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~(WDT_TCSR_EXT_EN | WDT_TCSR_RTC_EN | WDT_TCSR_PCK_EN)) | WDT_TCSR_PCK_EN) + +#define __wdt_select_clk_div1() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE1) +#define __wdt_select_clk_div4() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE4) +#define __wdt_select_clk_div16() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE16) +#define __wdt_select_clk_div64() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE64) +#define __wdt_select_clk_div256() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE256) +#define __wdt_select_clk_div1024() \ + (REG_WDT_TCSR = (REG_WDT_TCSR & ~WDT_TCSR_PRESCALE_MASK) | WDT_TCSR_PRESCALE1024) + + +/*************************************************************************** + * UART + ***************************************************************************/ + +#define __uart_enable(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_FCR) |= UARTFCR_UUE | UARTFCR_FE ) +#define __uart_disable(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_FCR) = ~UARTFCR_UUE ) + +#define __uart_enable_transmit_irq(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) |= UARTIER_TIE ) +#define __uart_disable_transmit_irq(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) &= ~UARTIER_TIE ) + +#define __uart_enable_receive_irq(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) |= UARTIER_RIE | UARTIER_RLIE | UARTIER_RTIE ) +#define __uart_disable_receive_irq(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_IER) &= ~(UARTIER_RIE | UARTIER_RLIE | UARTIER_RTIE) ) + +#define __uart_enable_loopback(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_MCR) |= UARTMCR_LOOP ) +#define __uart_disable_loopback(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_MCR) &= ~UARTMCR_LOOP ) + +#define __uart_set_8n1(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_LCR) = UARTLCR_WLEN_8 ) + +#define __uart_set_baud(n, devclk, baud) \ + do { \ + REG8(UART_BASE + UART_OFF*(n) + OFF_LCR) |= UARTLCR_DLAB; \ + REG8(UART_BASE + UART_OFF*(n) + OFF_DLLR) = (devclk / 16 / baud) & 0xff; \ + REG8(UART_BASE + UART_OFF*(n) + OFF_DLHR) = ((devclk / 16 / baud) >> 8) & 0xff; \ + REG8(UART_BASE + UART_OFF*(n) + OFF_LCR) &= ~UARTLCR_DLAB; \ + } while (0) + +#define __uart_parity_error(n) \ + ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_PER) != 0 ) + +#define __uart_clear_errors(n) \ + ( REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) &= ~(UARTLSR_ORER | UARTLSR_BRK | UARTLSR_FER | UARTLSR_PER | UARTLSR_RFER) ) + +#define __uart_transmit_fifo_empty(n) \ + ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_TDRQ) != 0 ) + +#define __uart_transmit_end(n) \ + ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_TEMT) != 0 ) + +#define __uart_transmit_char(n, ch) \ + REG8(UART_BASE + UART_OFF*(n) + OFF_TDR) = (ch) + +#define __uart_receive_fifo_full(n) \ + ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_DR) != 0 ) + +#define __uart_receive_ready(n) \ + ( (REG8(UART_BASE + UART_OFF*(n) + OFF_LSR) & UARTLSR_DR) != 0 ) + +#define __uart_receive_char(n) \ + REG8(UART_BASE + UART_OFF*(n) + OFF_RDR) + +#define __uart_disable_irda() \ + ( REG8(IRDA_BASE + OFF_SIRCR) &= ~(SIRCR_TSIRE | SIRCR_RSIRE) ) +#define __uart_enable_irda() \ + /* Tx high pulse as 0, Rx low pulse as 0 */ \ + ( REG8(IRDA_BASE + OFF_SIRCR) = SIRCR_TSIRE | SIRCR_RSIRE | SIRCR_RXPL | SIRCR_TPWS ) + + +/*************************************************************************** + * DMAC + ***************************************************************************/ + +/* m is the DMA controller index (0, 1), n is the DMA channel index (0 - 11) */ + +#define __dmac_enable_module(m) \ + ( REG_DMAC_DMACR(m) |= DMAC_DMACR_DMAE | DMAC_DMACR_PR_012345 ) +#define __dmac_disable_module(m) \ + ( REG_DMAC_DMACR(m) &= ~DMAC_DMACR_DMAE ) + +/* p=0,1,2,3 */ +#define __dmac_set_priority(m,p) \ +do { \ + REG_DMAC_DMACR(m) &= ~DMAC_DMACR_PR_MASK; \ + REG_DMAC_DMACR(m) |= ((p) << DMAC_DMACR_PR_BIT); \ +} while (0) + +#define __dmac_test_halt_error(m) ( REG_DMAC_DMACR(m) & DMAC_DMACR_HLT ) +#define __dmac_test_addr_error(m) ( REG_DMAC_DMACR(m) & DMAC_DMACR_AR ) + +#define __dmac_channel_enable_clk(n) \ + REG_DMAC_DMACKE((n)/HALF_DMA_NUM) |= 1 << ((n)-(n)/HALF_DMA_NUM*HALF_DMA_NUM); + +#define __dmac_enable_descriptor(n) \ + ( REG_DMAC_DCCSR((n)) &= ~DMAC_DCCSR_NDES ) +#define __dmac_disable_descriptor(n) \ + ( REG_DMAC_DCCSR((n)) |= DMAC_DCCSR_NDES ) + +#define __dmac_enable_channel(n) \ +do { \ + REG_DMAC_DCCSR((n)) |= DMAC_DCCSR_EN; \ +} while (0) +#define __dmac_disable_channel(n) \ +do { \ + REG_DMAC_DCCSR((n)) &= ~DMAC_DCCSR_EN; \ +} while (0) +#define __dmac_channel_enabled(n) \ + ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_EN ) + +#define __dmac_channel_enable_irq(n) \ + ( REG_DMAC_DCMD((n)) |= DMAC_DCMD_TIE ) +#define __dmac_channel_disable_irq(n) \ + ( REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_TIE ) + +#define __dmac_channel_transmit_halt_detected(n) \ + ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_HLT ) +#define __dmac_channel_transmit_end_detected(n) \ + ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_TT ) +#define __dmac_channel_address_error_detected(n) \ + ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_AR ) +#define __dmac_channel_count_terminated_detected(n) \ + ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_CT ) +#define __dmac_channel_descriptor_invalid_detected(n) \ + ( REG_DMAC_DCCSR((n)) & DMAC_DCCSR_INV ) + +#define __dmac_channel_clear_transmit_halt(n) \ + do { \ + /* clear both channel halt error and globle halt error */ \ + REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_HLT; \ + REG_DMAC_DMACR(n/HALF_DMA_NUM) &= ~DMAC_DMACR_HLT; \ + } while (0) +#define __dmac_channel_clear_transmit_end(n) \ + ( REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_TT ) +#define __dmac_channel_clear_address_error(n) \ + do { \ + REG_DMAC_DDA(n) = 0; /* clear descriptor address register */ \ + REG_DMAC_DSAR(n) = 0; /* clear source address register */ \ + REG_DMAC_DTAR(n) = 0; /* clear target address register */ \ + /* clear both channel addr error and globle address error */ \ + REG_DMAC_DCCSR(n) &= ~DMAC_DCCSR_AR; \ + REG_DMAC_DMACR(n/HALF_DMA_NUM) &= ~DMAC_DMACR_AR; \ + } while (0) +#define __dmac_channel_clear_count_terminated(n) \ + ( REG_DMAC_DCCSR((n)) &= ~DMAC_DCCSR_CT ) +#define __dmac_channel_clear_descriptor_invalid(n) \ + ( REG_DMAC_DCCSR((n)) &= ~DMAC_DCCSR_INV ) + +#define __dmac_channel_set_transfer_unit_32bit(n) \ +do { \ + REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ + REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_32BIT; \ +} while (0) + +#define __dmac_channel_set_transfer_unit_16bit(n) \ +do { \ + REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ + REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_16BIT; \ +} while (0) + +#define __dmac_channel_set_transfer_unit_8bit(n) \ +do { \ + REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ + REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_8BIT; \ +} while (0) + +#define __dmac_channel_set_transfer_unit_16byte(n) \ +do { \ + REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ + REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_16BYTE; \ +} while (0) + +#define __dmac_channel_set_transfer_unit_32byte(n) \ +do { \ + REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DS_MASK; \ + REG_DMAC_DCMD((n)) |= DMAC_DCMD_DS_32BYTE; \ +} while (0) + +/* w=8,16,32 */ +#define __dmac_channel_set_dest_port_width(n,w) \ +do { \ + REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DWDH_MASK; \ + REG_DMAC_DCMD((n)) |= DMAC_DCMD_DWDH_##w; \ +} while (0) + +/* w=8,16,32 */ +#define __dmac_channel_set_src_port_width(n,w) \ +do { \ + REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_SWDH_MASK; \ + REG_DMAC_DCMD((n)) |= DMAC_DCMD_SWDH_##w; \ +} while (0) + +/* v=0-15 */ +#define __dmac_channel_set_rdil(n,v) \ +do { \ + REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_RDIL_MASK; \ + REG_DMAC_DCMD((n) |= ((v) << DMAC_DCMD_RDIL_BIT); \ +} while (0) + +#define __dmac_channel_dest_addr_fixed(n) \ + ( REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_DAI ) +#define __dmac_channel_dest_addr_increment(n) \ + ( REG_DMAC_DCMD((n)) |= DMAC_DCMD_DAI ) + +#define __dmac_channel_src_addr_fixed(n) \ + ( REG_DMAC_DCMD((n)) &= ~DMAC_DCMD_SAI ) +#define __dmac_channel_src_addr_increment(n) \ + ( REG_DMAC_DCMD((n)) |= DMAC_DCMD_SAI ) + +#define __dmac_channel_set_doorbell(n) \ + ( REG_DMAC_DMADBSR((n)/HALF_DMA_NUM) = (1 << ((n)-(n)/HALF_DMA_NUM*HALF_DMA_NUM)) ) + +#define __dmac_channel_irq_detected(n) ( REG_DMAC_DMAIPR((n)/HALF_DMA_NUM) & (1 << ((n)-(n)/HALF_DMA_NUM*HALF_DMA_NUM)) ) +#define __dmac_channel_ack_irq(n) ( REG_DMAC_DMAIPR((n)/HALF_DMA_NUM) &= ~(1 <<((n)-(n)/HALF_DMA_NUM*HALF_DMA_NUM)) ) + +static __inline__ int __dmac_get_irq(void) +{ + int i; + for (i = 0; i < MAX_DMA_NUM; i++) + if (__dmac_channel_irq_detected(i)) + return i; + return -1; +} + + +/*************************************************************************** + * AIC (AC'97 & I2S Controller) + ***************************************************************************/ + +#define __aic_enable() ( REG_AIC_FR |= AIC_FR_ENB ) +#define __aic_disable() ( REG_AIC_FR &= ~AIC_FR_ENB ) + +#define __aic_select_ac97() ( REG_AIC_FR &= ~AIC_FR_AUSEL ) +#define __aic_select_i2s() ( REG_AIC_FR |= AIC_FR_AUSEL ) + +#define __aic_play_zero() ( REG_AIC_FR &= ~AIC_FR_LSMP ) +#define __aic_play_lastsample() ( REG_AIC_FR |= AIC_FR_LSMP ) + +#define __i2s_as_master() ( REG_AIC_FR |= AIC_FR_BCKD | AIC_FR_SYNCD ) +#define __i2s_as_slave() ( REG_AIC_FR &= ~(AIC_FR_BCKD | AIC_FR_SYNCD) ) +#define __aic_reset_status() ( REG_AIC_FR & AIC_FR_RST ) + +#define __aic_reset() \ +do { \ + REG_AIC_FR |= AIC_FR_RST; \ +} while(0) + + +#define __aic_set_transmit_trigger(n) \ +do { \ + REG_AIC_FR &= ~AIC_FR_TFTH_MASK; \ + REG_AIC_FR |= ((n) << AIC_FR_TFTH_BIT); \ +} while(0) + +#define __aic_set_receive_trigger(n) \ +do { \ + REG_AIC_FR &= ~AIC_FR_RFTH_MASK; \ + REG_AIC_FR |= ((n) << AIC_FR_RFTH_BIT); \ +} while(0) + +#define __aic_enable_record() ( REG_AIC_CR |= AIC_CR_EREC ) +#define __aic_disable_record() ( REG_AIC_CR &= ~AIC_CR_EREC ) +#define __aic_enable_replay() ( REG_AIC_CR |= AIC_CR_ERPL ) +#define __aic_disable_replay() ( REG_AIC_CR &= ~AIC_CR_ERPL ) +#define __aic_enable_loopback() ( REG_AIC_CR |= AIC_CR_ENLBF ) +#define __aic_disable_loopback() ( REG_AIC_CR &= ~AIC_CR_ENLBF ) + +#define __aic_flush_fifo() ( REG_AIC_CR |= AIC_CR_FLUSH ) +#define __aic_unflush_fifo() ( REG_AIC_CR &= ~AIC_CR_FLUSH ) + +#define __aic_enable_transmit_intr() \ + ( REG_AIC_CR |= (AIC_CR_ETFS | AIC_CR_ETUR) ) +#define __aic_disable_transmit_intr() \ + ( REG_AIC_CR &= ~(AIC_CR_ETFS | AIC_CR_ETUR) ) +#define __aic_enable_receive_intr() \ + ( REG_AIC_CR |= (AIC_CR_ERFS | AIC_CR_EROR) ) +#define __aic_disable_receive_intr() \ + ( REG_AIC_CR &= ~(AIC_CR_ERFS | AIC_CR_EROR) ) + +#define __aic_enable_transmit_dma() ( REG_AIC_CR |= AIC_CR_TDMS ) +#define __aic_disable_transmit_dma() ( REG_AIC_CR &= ~AIC_CR_TDMS ) +#define __aic_enable_receive_dma() ( REG_AIC_CR |= AIC_CR_RDMS ) +#define __aic_disable_receive_dma() ( REG_AIC_CR &= ~AIC_CR_RDMS ) + +#define __aic_enable_mono2stereo() ( REG_AIC_CR |= AIC_CR_M2S ) +#define __aic_disable_mono2stereo() ( REG_AIC_CR &= ~AIC_CR_M2S ) +#define __aic_enable_byteswap() ( REG_AIC_CR |= AIC_CR_ENDSW ) +#define __aic_disable_byteswap() ( REG_AIC_CR &= ~AIC_CR_ENDSW ) +#define __aic_enable_unsignadj() ( REG_AIC_CR |= AIC_CR_AVSTSU ) +#define __aic_disable_unsignadj() ( REG_AIC_CR &= ~AIC_CR_AVSTSU ) + +#define AC97_PCM_XS_L_FRONT AIC_ACCR1_XS_SLOT3 +#define AC97_PCM_XS_R_FRONT AIC_ACCR1_XS_SLOT4 +#define AC97_PCM_XS_CENTER AIC_ACCR1_XS_SLOT6 +#define AC97_PCM_XS_L_SURR AIC_ACCR1_XS_SLOT7 +#define AC97_PCM_XS_R_SURR AIC_ACCR1_XS_SLOT8 +#define AC97_PCM_XS_LFE AIC_ACCR1_XS_SLOT9 + +#define AC97_PCM_RS_L_FRONT AIC_ACCR1_RS_SLOT3 +#define AC97_PCM_RS_R_FRONT AIC_ACCR1_RS_SLOT4 +#define AC97_PCM_RS_CENTER AIC_ACCR1_RS_SLOT6 +#define AC97_PCM_RS_L_SURR AIC_ACCR1_RS_SLOT7 +#define AC97_PCM_RS_R_SURR AIC_ACCR1_RS_SLOT8 +#define AC97_PCM_RS_LFE AIC_ACCR1_RS_SLOT9 + +#define __ac97_set_xs_none() ( REG_AIC_ACCR1 &= ~AIC_ACCR1_XS_MASK ) +#define __ac97_set_xs_mono() \ +do { \ + REG_AIC_ACCR1 &= ~AIC_ACCR1_XS_MASK; \ + REG_AIC_ACCR1 |= AC97_PCM_XS_R_FRONT; \ +} while(0) +#define __ac97_set_xs_stereo() \ +do { \ + REG_AIC_ACCR1 &= ~AIC_ACCR1_XS_MASK; \ + REG_AIC_ACCR1 |= AC97_PCM_XS_L_FRONT | AC97_PCM_XS_R_FRONT; \ +} while(0) + +/* In fact, only stereo is support now. */ +#define __ac97_set_rs_none() ( REG_AIC_ACCR1 &= ~AIC_ACCR1_RS_MASK ) +#define __ac97_set_rs_mono() \ +do { \ + REG_AIC_ACCR1 &= ~AIC_ACCR1_RS_MASK; \ + REG_AIC_ACCR1 |= AC97_PCM_RS_R_FRONT; \ +} while(0) +#define __ac97_set_rs_stereo() \ +do { \ + REG_AIC_ACCR1 &= ~AIC_ACCR1_RS_MASK; \ + REG_AIC_ACCR1 |= AC97_PCM_RS_L_FRONT | AC97_PCM_RS_R_FRONT; \ +} while(0) + +#define __ac97_warm_reset_codec() \ + do { \ + REG_AIC_ACCR2 |= AIC_ACCR2_SA; \ + REG_AIC_ACCR2 |= AIC_ACCR2_SS; \ + udelay(2); \ + REG_AIC_ACCR2 &= ~AIC_ACCR2_SS; \ + REG_AIC_ACCR2 &= ~AIC_ACCR2_SA; \ + } while (0) + +#define __ac97_cold_reset_codec() \ + do { \ + REG_AIC_ACCR2 |= AIC_ACCR2_SR; \ + udelay(2); \ + REG_AIC_ACCR2 &= ~AIC_ACCR2_SR; \ + } while (0) + +/* n=8,16,18,20 */ +#define __ac97_set_iass(n) \ + ( REG_AIC_ACCR2 = (REG_AIC_ACCR2 & ~AIC_ACCR2_IASS_MASK) | AIC_ACCR2_IASS_##n##BIT ) +#define __ac97_set_oass(n) \ + ( REG_AIC_ACCR2 = (REG_AIC_ACCR2 & ~AIC_ACCR2_OASS_MASK) | AIC_ACCR2_OASS_##n##BIT ) + +#define __i2s_select_i2s() ( REG_AIC_I2SCR &= ~AIC_I2SCR_AMSL ) +#define __i2s_select_msbjustified() ( REG_AIC_I2SCR |= AIC_I2SCR_AMSL ) + +/* n=8,16,18,20,24 */ +/*#define __i2s_set_sample_size(n) \ + ( REG_AIC_I2SCR |= (REG_AIC_I2SCR & ~AIC_I2SCR_WL_MASK) | AIC_I2SCR_WL_##n##BIT )*/ + +#define __i2s_set_oss_sample_size(n) \ + ( REG_AIC_CR = (REG_AIC_CR & ~AIC_CR_OSS_MASK) | AIC_CR_OSS_##n##BIT ) +#define __i2s_set_iss_sample_size(n) \ + ( REG_AIC_CR = (REG_AIC_CR & ~AIC_CR_ISS_MASK) | AIC_CR_ISS_##n##BIT ) + +#define __i2s_stop_bitclk() ( REG_AIC_I2SCR |= AIC_I2SCR_STPBK ) +#define __i2s_start_bitclk() ( REG_AIC_I2SCR &= ~AIC_I2SCR_STPBK ) + +#define __aic_transmit_request() ( REG_AIC_SR & AIC_SR_TFS ) +#define __aic_receive_request() ( REG_AIC_SR & AIC_SR_RFS ) +#define __aic_transmit_underrun() ( REG_AIC_SR & AIC_SR_TUR ) +#define __aic_receive_overrun() ( REG_AIC_SR & AIC_SR_ROR ) + +#define __aic_clear_errors() ( REG_AIC_SR &= ~(AIC_SR_TUR | AIC_SR_ROR) ) + +#define __aic_get_transmit_resident() \ + ( (REG_AIC_SR & AIC_SR_TFL_MASK) >> AIC_SR_TFL_BIT ) +#define __aic_get_receive_count() \ + ( (REG_AIC_SR & AIC_SR_RFL_MASK) >> AIC_SR_RFL_BIT ) + +#define __ac97_command_transmitted() ( REG_AIC_ACSR & AIC_ACSR_CADT ) +#define __ac97_status_received() ( REG_AIC_ACSR & AIC_ACSR_SADR ) +#define __ac97_status_receive_timeout() ( REG_AIC_ACSR & AIC_ACSR_RSTO ) +#define __ac97_codec_is_low_power_mode() ( REG_AIC_ACSR & AIC_ACSR_CLPM ) +#define __ac97_codec_is_ready() ( REG_AIC_ACSR & AIC_ACSR_CRDY ) +#define __ac97_slot_error_detected() ( REG_AIC_ACSR & AIC_ACSR_SLTERR ) +#define __ac97_clear_slot_error() ( REG_AIC_ACSR &= ~AIC_ACSR_SLTERR ) + +#define __i2s_is_busy() ( REG_AIC_I2SSR & AIC_I2SSR_BSY ) + +#define CODEC_READ_CMD (1 << 19) +#define CODEC_WRITE_CMD (0 << 19) +#define CODEC_REG_INDEX_BIT 12 +#define CODEC_REG_INDEX_MASK (0x7f << CODEC_REG_INDEX_BIT) /* 18:12 */ +#define CODEC_REG_DATA_BIT 4 +#define CODEC_REG_DATA_MASK (0x0ffff << 4) /* 19:4 */ + +#define __ac97_out_rcmd_addr(reg) \ +do { \ + REG_AIC_ACCAR = CODEC_READ_CMD | ((reg) << CODEC_REG_INDEX_BIT); \ +} while (0) + +#define __ac97_out_wcmd_addr(reg) \ +do { \ + REG_AIC_ACCAR = CODEC_WRITE_CMD | ((reg) << CODEC_REG_INDEX_BIT); \ +} while (0) + +#define __ac97_out_data(value) \ +do { \ + REG_AIC_ACCDR = ((value) << CODEC_REG_DATA_BIT); \ +} while (0) + +#define __ac97_in_data() \ + ( (REG_AIC_ACSDR & CODEC_REG_DATA_MASK) >> CODEC_REG_DATA_BIT ) + +#define __ac97_in_status_addr() \ + ( (REG_AIC_ACSAR & CODEC_REG_INDEX_MASK) >> CODEC_REG_INDEX_BIT ) + +#define __i2s_set_sample_rate(i2sclk, sync) \ + ( REG_AIC_I2SDIV = ((i2sclk) / (4*64)) / (sync) ) + +#define __aic_write_tfifo(v) ( REG_AIC_DR = (v) ) +#define __aic_read_rfifo() ( REG_AIC_DR ) + +#define __aic_internal_codec() ( REG_AIC_FR |= AIC_FR_ICDC ) +#define __aic_external_codec() ( REG_AIC_FR &= ~AIC_FR_ICDC ) + +// +// Define next ops for AC97 compatible +// + +#define AC97_ACSR AIC_ACSR + +#define __ac97_enable() __aic_enable(); __aic_select_ac97() +#define __ac97_disable() __aic_disable() +#define __ac97_reset() __aic_reset() + +#define __ac97_set_transmit_trigger(n) __aic_set_transmit_trigger(n) +#define __ac97_set_receive_trigger(n) __aic_set_receive_trigger(n) + +#define __ac97_enable_record() __aic_enable_record() +#define __ac97_disable_record() __aic_disable_record() +#define __ac97_enable_replay() __aic_enable_replay() +#define __ac97_disable_replay() __aic_disable_replay() +#define __ac97_enable_loopback() __aic_enable_loopback() +#define __ac97_disable_loopback() __aic_disable_loopback() + +#define __ac97_enable_transmit_dma() __aic_enable_transmit_dma() +#define __ac97_disable_transmit_dma() __aic_disable_transmit_dma() +#define __ac97_enable_receive_dma() __aic_enable_receive_dma() +#define __ac97_disable_receive_dma() __aic_disable_receive_dma() + +#define __ac97_transmit_request() __aic_transmit_request() +#define __ac97_receive_request() __aic_receive_request() +#define __ac97_transmit_underrun() __aic_transmit_underrun() +#define __ac97_receive_overrun() __aic_receive_overrun() + +#define __ac97_clear_errors() __aic_clear_errors() + +#define __ac97_get_transmit_resident() __aic_get_transmit_resident() +#define __ac97_get_receive_count() __aic_get_receive_count() + +#define __ac97_enable_transmit_intr() __aic_enable_transmit_intr() +#define __ac97_disable_transmit_intr() __aic_disable_transmit_intr() +#define __ac97_enable_receive_intr() __aic_enable_receive_intr() +#define __ac97_disable_receive_intr() __aic_disable_receive_intr() + +#define __ac97_write_tfifo(v) __aic_write_tfifo(v) +#define __ac97_read_rfifo() __aic_read_rfifo() + +// +// Define next ops for I2S compatible +// + +#define I2S_ACSR AIC_I2SSR + +#define __i2s_enable() __aic_enable(); __aic_select_i2s() +#define __i2s_disable() __aic_disable() +#define __i2s_reset() __aic_reset() + +#define __i2s_set_transmit_trigger(n) __aic_set_transmit_trigger(n) +#define __i2s_set_receive_trigger(n) __aic_set_receive_trigger(n) + +#define __i2s_enable_record() __aic_enable_record() +#define __i2s_disable_record() __aic_disable_record() +#define __i2s_enable_replay() __aic_enable_replay() +#define __i2s_disable_replay() __aic_disable_replay() +#define __i2s_enable_loopback() __aic_enable_loopback() +#define __i2s_disable_loopback() __aic_disable_loopback() + +#define __i2s_enable_transmit_dma() __aic_enable_transmit_dma() +#define __i2s_disable_transmit_dma() __aic_disable_transmit_dma() +#define __i2s_enable_receive_dma() __aic_enable_receive_dma() +#define __i2s_disable_receive_dma() __aic_disable_receive_dma() + +#define __i2s_transmit_request() __aic_transmit_request() +#define __i2s_receive_request() __aic_receive_request() +#define __i2s_transmit_underrun() __aic_transmit_underrun() +#define __i2s_receive_overrun() __aic_receive_overrun() + +#define __i2s_clear_errors() __aic_clear_errors() + +#define __i2s_get_transmit_resident() __aic_get_transmit_resident() +#define __i2s_get_receive_count() __aic_get_receive_count() + +#define __i2s_enable_transmit_intr() __aic_enable_transmit_intr() +#define __i2s_disable_transmit_intr() __aic_disable_transmit_intr() +#define __i2s_enable_receive_intr() __aic_enable_receive_intr() +#define __i2s_disable_receive_intr() __aic_disable_receive_intr() + +#define __i2s_write_tfifo(v) __aic_write_tfifo(v) +#define __i2s_read_rfifo() __aic_read_rfifo() + +#define __i2s_reset_codec() \ + do { \ + } while (0) + +/************************************************************************* + * PCM Controller operation + *************************************************************************/ + +#define __pcm_enable() ( REG_PCM_CTL |= PCM_CTL_PCMEN ) +#define __pcm_disable() ( REG_PCM_CTL &= ~PCM_CTL_PCMEN ) + +#define __pcm_clk_enable() ( REG_PCM_CTL |= PCM_CTL_CLKEN ) +#define __pcm_clk_disable() ( REG_PCM_CTL &= ~PCM_CTL_CLKEN ) + +#define __pcm_reset() ( REG_PCM_CTL |= PCM_CTL_RST ) +#define __pcm_flush_fifo() ( REG_PCM_CTL |= PCM_CTL_FLUSH ) + +#define __pcm_enable_record() ( REG_PCM_CTL |= PCM_CTL_EREC ) +#define __pcm_disable_record() ( REG_PCM_CTL &= ~PCM_CTL_EREC ) +#define __pcm_enable_playback() ( REG_PCM_CTL |= PCM_CTL_ERPL ) +#define __pcm_disable_playback() ( REG_PCM_CTL &= ~PCM_CTL_ERPL ) + +#define __pcm_enable_rxfifo() __pcm_enable_record() +#define __pcm_disable_rxfifo() __pcm_disable_record() +#define __pcm_enable_txfifo() __pcm_enable_playback() +#define __pcm_disable_txfifo() __pcm_disable_playback() + +#define __pcm_last_sample() ( REG_PCM_CTL |= PCM_CTL_LSMP ) +#define __pcm_zero_sample() ( REG_PCM_CTL &= ~PCM_CTL_LSMP ) + +#define __pcm_enable_transmit_dma() ( REG_PCM_CTL |= PCM_CTL_ETDMA ) +#define __pcm_disable_transmit_dma() ( REG_PCM_CTL &= ~PCM_CTL_ETDMA ) +#define __pcm_enable_receive_dma() ( REG_PCM_CTL |= PCM_CTL_ERDMA ) +#define __pcm_disable_receive_dma() ( REG_PCM_CTL &= ~PCM_CTL_ERDMA ) + +#define __pcm_as_master() ( REG_PCM_CFG &= PCM_CFG_MODE ) +#define __pcm_as_slave() ( REG_PCM_CFG |= ~PCM_CFG_MODE ) + +#define __pcm_set_transmit_trigger(n) \ +do { \ + REG_PCM_CFG &= ~PCM_CFG_TFTH_MASK; \ + REG_PCM_CFG |= ((n) << PCM_CFG_TFTH_BIT); \ +} while(0) + +#define __pcm_set_receive_trigger(n) \ +do { \ + REG_PCM_CFG &= ~PCM_CFG_RFTH_MASK; \ + REG_PCM_CFG |= ((n) << PCM_CFG_RFTH_BIT); \ +} while(0) + +#define __pcm_omsb_same_sync() ( REG_PCM_CFG &= ~PCM_CFG_OMSBPOS ) +#define __pcm_omsb_next_sync() ( REG_PCM_CFG |= PCM_CFG_OMSBPOS ) + +#define __pcm_imsb_same_sync() ( REG_PCM_CFG &= ~PCM_CFG_IMSBPOS ) +#define __pcm_imsb_next_sync() ( REG_PCM_CFG |= PCM_CFG_IMSBPOS ) + +/* set input sample size 8 or 16*/ +#define __pcm_set_iss(n) \ +( REG_PCM_CFG = (REG_PCM_CFG & ~PCM_CFG_ISS_MASK) | PCM_CFG_ISS_##n ) +/* set output sample size 8 or 16*/ +#define __pcm_set_oss(n) \ +( REG_PCM_CFG = (REG_PCM_CFG & ~PCM_CFG_OSS_MASK) | PCM_CFG_OSS_##n ) + +#define __pcm_set_valid_slot(n) \ +( REG_PCM_CFG = (REG_PCM_CFG & ~PCM_CFG_SLOT_MASK) | PCM_CFG_SLOT_##n ) + +#define __pcm_write_data(v) ( REG_PCM_DP = (v) ) +#define __pcm_read_data() ( REG_PCM_DP ) + +#define __pcm_enable_tfs_intr() ( REG_PCM_INTC |= PCM_INTC_ETFS ) +#define __pcm_disable_tfs_intr() ( REG_PCM_INTC &= ~PCM_INTC_ETFS ) + +#define __pcm_enable_tur_intr() ( REG_PCM_INTC |= PCM_INTC_ETUR ) +#define __pcm_disable_tur_intr() ( REG_PCM_INTC &= ~PCM_INTC_ETUR ) + +#define __pcm_enable_rfs_intr() ( REG_PCM_INTC |= PCM_INTC_ERFS ) +#define __pcm_disable_rfs_intr() ( REG_PCM_INTC &= ~PCM_INTC_ERFS ) + +#define __pcm_enable_ror_intr() ( REG_PCM_INTC |= PCM_INTC_EROR ) +#define __pcm_disable_ror_intr() ( REG_PCM_INTC &= ~PCM_INTC_EROR ) + +#define __pcm_ints_valid_tx() \ +( ((REG_PCM_INTS & PCM_INTS_TFL_MASK) >> PCM_INTS_TFL_BIT) ) +#define __pcm_ints_valid_rx() \ +( ((REG_PCM_INTS & PCM_INTS_RFL_MASK) >> PCM_INTS_RFL_BIT) ) + +#define __pcm_set_clk_div(n) \ +( REG_PCM_DIV = (REG_PCM_DIV & ~PCM_DIV_CLKDIV_MASK) | ((n) << PCM_DIV_CLKDIV_BIT) ) + +/* sysclk(cpm_pcm_sysclk) Hz is created by cpm logic, and pcmclk Hz is the pcm in/out clock wanted */ +#define __pcm_set_clk_rate(sysclk, pcmclk) \ +__pcm_set_clk_div(((sysclk) / (pcmclk) - 1)) + +#define __pcm_set_sync_div(n) \ +( REG_PCM_DIV = (REG_PCM_DIV & ~PCM_DIV_SYNDIV_MASK) | ((n) << PCM_DIV_SYNDIV_BIT) ) + +/* pcmclk is source clock Hz, and sync is the frame sync clock Hz wanted */ +#define __pcm_set_sync_rate(pcmclk, sync) \ +__pcm_set_sync_div(((pcmclk) / (8 * (sync)) - 1)) + + /* set sync length in pcmclk n = 0 ... 63 */ +#define __pcm_set_sync_len(n) \ +( REG_PCM_DIV = (REG_PCM_DIV & ~PCM_DIV_SYNL_MASK) | (n << PCM_DIV_SYNL_BIT) ) + + +/*************************************************************************** + * ICDC + ***************************************************************************/ +#define __i2s_internal_codec() __aic_internal_codec() +#define __i2s_external_codec() __aic_external_codec() + +#define __icdc_clk_ready() ( REG_ICDC_CKCFG & ICDC_CKCFG_CKRDY ) +#define __icdc_sel_adc() ( REG_ICDC_CKCFG |= ICDC_CKCFG_SELAD ) +#define __icdc_sel_dac() ( REG_ICDC_CKCFG &= ~ICDC_CKCFG_SELAD ) + +#define __icdc_set_rgwr() ( REG_ICDC_RGADW |= ICDC_RGADW_RGWR ) +#define __icdc_clear_rgwr() ( REG_ICDC_RGADW &= ~ICDC_RGADW_RGWR ) +#define __icdc_rgwr_ready() ( REG_ICDC_RGADW & ICDC_RGADW_RGWR ) + +#define __icdc_set_addr(n) \ +do { \ + REG_ICDC_RGADW &= ~ICDC_RGADW_RGADDR_MASK; \ + REG_ICDC_RGADW |= (n) << ICDC_RGADW_RGADDR_BIT; \ +} while(0) + +#define __icdc_set_cmd(n) \ +do { \ + REG_ICDC_RGADW &= ~ICDC_RGADW_RGDIN_MASK; \ + REG_ICDC_RGADW |= (n) << ICDC_RGADW_RGDIN_BIT; \ +} while(0) + +#define __icdc_irq_pending() ( REG_ICDC_RGDATA & ICDC_RGDATA_IRQ ) +#define __icdc_get_value() ( REG_ICDC_RGDATA & ICDC_RGDATA_RGDOUT_MASK ) + +/*************************************************************************** + * INTC + ***************************************************************************/ +#define __intc_unmask_irq(n) ( REG_INTC_IMCR = (1 << (n)) ) +#define __intc_mask_irq(n) ( REG_INTC_IMSR = (1 << (n)) ) +#define __intc_ack_irq(n) ( REG_INTC_IPR = (1 << (n)) ) /* A dummy ack, as the Pending Register is Read Only. Should we remove __intc_ack_irq() */ + + +/*************************************************************************** + * I2C + ***************************************************************************/ + +#define __i2c_enable() ( REG_I2C_CR |= I2C_CR_I2CE ) +#define __i2c_disable() ( REG_I2C_CR &= ~I2C_CR_I2CE ) + +#define __i2c_send_start() ( REG_I2C_CR |= I2C_CR_STA ) +#define __i2c_send_stop() ( REG_I2C_CR |= I2C_CR_STO ) +#define __i2c_send_ack() ( REG_I2C_CR &= ~I2C_CR_AC ) +#define __i2c_send_nack() ( REG_I2C_CR |= I2C_CR_AC ) + +#define __i2c_set_drf() ( REG_I2C_SR |= I2C_SR_DRF ) +#define __i2c_clear_drf() ( REG_I2C_SR &= ~I2C_SR_DRF ) +#define __i2c_check_drf() ( REG_I2C_SR & I2C_SR_DRF ) + +#define __i2c_received_ack() ( !(REG_I2C_SR & I2C_SR_ACKF) ) +#define __i2c_is_busy() ( REG_I2C_SR & I2C_SR_BUSY ) +#define __i2c_transmit_ended() ( REG_I2C_SR & I2C_SR_TEND ) + +#define __i2c_set_clk(dev_clk, i2c_clk) \ + ( REG_I2C_GR = (dev_clk) / (16*(i2c_clk)) - 1 ) + +#define __i2c_read() ( REG_I2C_DR ) +#define __i2c_write(val) ( REG_I2C_DR = (val) ) + + +/*************************************************************************** + * MSC + ***************************************************************************/ +/* n = 0, 1 (MSC0, MSC1) */ + +#define __msc_start_op(n) \ + ( REG_MSC_STRPCL(n) = MSC_STRPCL_START_OP | MSC_STRPCL_CLOCK_CONTROL_START ) + +#define __msc_set_resto(n, to) ( REG_MSC_RESTO(n) = to ) +#define __msc_set_rdto(n, to) ( REG_MSC_RDTO(n) = to ) +#define __msc_set_cmd(n, cmd) ( REG_MSC_CMD(n) = cmd ) +#define __msc_set_arg(n, arg) ( REG_MSC_ARG(n) = arg ) +#define __msc_set_nob(n, nob) ( REG_MSC_NOB(n) = nob ) +#define __msc_get_nob(n) ( REG_MSC_NOB(n) ) +#define __msc_set_blklen(n, len) ( REG_MSC_BLKLEN(n) = len ) +#define __msc_set_cmdat(n, cmdat) ( REG_MSC_CMDAT(n) = cmdat ) +#define __msc_set_cmdat_ioabort(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_IO_ABORT ) +#define __msc_clear_cmdat_ioabort(n) ( REG_MSC_CMDAT(n) &= ~MSC_CMDAT_IO_ABORT ) + +#define __msc_set_cmdat_bus_width1(n) \ +do { \ + REG_MSC_CMDAT(n) &= ~MSC_CMDAT_BUS_WIDTH_MASK; \ + REG_MSC_CMDAT(n) |= MSC_CMDAT_BUS_WIDTH_1BIT; \ +} while(0) + +#define __msc_set_cmdat_bus_width4(n) \ +do { \ + REG_MSC_CMDAT(n) &= ~MSC_CMDAT_BUS_WIDTH_MASK; \ + REG_MSC_CMDAT(n) |= MSC_CMDAT_BUS_WIDTH_4BIT; \ +} while(0) + +#define __msc_set_cmdat_dma_en(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_DMA_EN ) +#define __msc_set_cmdat_init(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_INIT ) +#define __msc_set_cmdat_busy(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_BUSY ) +#define __msc_set_cmdat_stream(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_STREAM_BLOCK ) +#define __msc_set_cmdat_block(n) ( REG_MSC_CMDAT(n) &= ~MSC_CMDAT_STREAM_BLOCK ) +#define __msc_set_cmdat_read(n) ( REG_MSC_CMDAT(n) &= ~MSC_CMDAT_WRITE_READ ) +#define __msc_set_cmdat_write(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_WRITE_READ ) +#define __msc_set_cmdat_data_en(n) ( REG_MSC_CMDAT(n) |= MSC_CMDAT_DATA_EN ) + +/* r is MSC_CMDAT_RESPONSE_FORMAT_Rx or MSC_CMDAT_RESPONSE_FORMAT_NONE */ +#define __msc_set_cmdat_res_format(n, r) \ +do { \ + REG_MSC_CMDAT(n) &= ~MSC_CMDAT_RESPONSE_FORMAT_MASK; \ + REG_MSC_CMDAT(n) |= (r); \ +} while(0) + +#define __msc_clear_cmdat(n) \ + REG_MSC_CMDAT(n) &= ~( MSC_CMDAT_IO_ABORT | MSC_CMDAT_DMA_EN | MSC_CMDAT_INIT| \ + MSC_CMDAT_BUSY | MSC_CMDAT_STREAM_BLOCK | MSC_CMDAT_WRITE_READ | \ + MSC_CMDAT_DATA_EN | MSC_CMDAT_RESPONSE_FORMAT_MASK ) + +#define __msc_get_imask(n) ( REG_MSC_IMASK(n) ) +#define __msc_mask_all_intrs(n) ( REG_MSC_IMASK(n) = 0xff ) +#define __msc_unmask_all_intrs(n) ( REG_MSC_IMASK(n) = 0x00 ) +#define __msc_mask_rd(n) ( REG_MSC_IMASK(n) |= MSC_IMASK_RXFIFO_RD_REQ ) +#define __msc_unmask_rd(n) ( REG_MSC_IMASK(n) &= ~MSC_IMASK_RXFIFO_RD_REQ ) +#define __msc_mask_wr(n) ( REG_MSC_IMASK(n) |= MSC_IMASK_TXFIFO_WR_REQ ) +#define __msc_unmask_wr(n) ( REG_MSC_IMASK(n) &= ~MSC_IMASK_TXFIFO_WR_REQ ) +#define __msc_mask_endcmdres(n) ( REG_MSC_IMASK(n) |= MSC_IMASK_END_CMD_RES ) +#define __msc_unmask_endcmdres(n) ( REG_MSC_IMASK(n) &= ~MSC_IMASK_END_CMD_RES ) +#define __msc_mask_datatrandone(n) ( REG_MSC_IMASK(n) |= MSC_IMASK_DATA_TRAN_DONE ) +#define __msc_unmask_datatrandone(n) ( REG_MSC_IMASK(n) &= ~MSC_IMASK_DATA_TRAN_DONE ) +#define __msc_mask_prgdone(n) ( REG_MSC_IMASK(n) |= MSC_IMASK_PRG_DONE ) +#define __msc_unmask_prgdone(n) ( REG_MSC_IMASK(n) &= ~MSC_IMASK_PRG_DONE ) + +/* m=0,1,2,3,4,5,6,7 */ +#define __msc_set_clkrt(n, m) \ +do { \ + REG_MSC_CLKRT(n) = m; \ +} while(0) + +#define __msc_get_ireg(n) ( REG_MSC_IREG(n) ) +#define __msc_ireg_rd(n) ( REG_MSC_IREG(n) & MSC_IREG_RXFIFO_RD_REQ ) +#define __msc_ireg_wr(n) ( REG_MSC_IREG(n) & MSC_IREG_TXFIFO_WR_REQ ) +#define __msc_ireg_end_cmd_res(n) ( REG_MSC_IREG(n) & MSC_IREG_END_CMD_RES ) +#define __msc_ireg_data_tran_done(n) ( REG_MSC_IREG(n) & MSC_IREG_DATA_TRAN_DONE ) +#define __msc_ireg_prg_done(n) ( REG_MSC_IREG(n) & MSC_IREG_PRG_DONE ) +#define __msc_ireg_clear_end_cmd_res(n) ( REG_MSC_IREG(n) = MSC_IREG_END_CMD_RES ) +#define __msc_ireg_clear_data_tran_done(n) ( REG_MSC_IREG(n) = MSC_IREG_DATA_TRAN_DONE ) +#define __msc_ireg_clear_prg_done(n) ( REG_MSC_IREG(n) = MSC_IREG_PRG_DONE ) + +#define __msc_get_stat(n) ( REG_MSC_STAT(n) ) +#define __msc_stat_not_end_cmd_res(n) ( (REG_MSC_STAT(n) & MSC_STAT_END_CMD_RES) == 0) +#define __msc_stat_crc_err(n) \ + ( REG_MSC_STAT(n) & (MSC_STAT_CRC_RES_ERR | MSC_STAT_CRC_READ_ERROR | MSC_STAT_CRC_WRITE_ERROR_YES) ) +#define __msc_stat_res_crc_err(n) ( REG_MSC_STAT(n) & MSC_STAT_CRC_RES_ERR ) +#define __msc_stat_rd_crc_err(n) ( REG_MSC_STAT(n) & MSC_STAT_CRC_READ_ERROR ) +#define __msc_stat_wr_crc_err(n) ( REG_MSC_STAT(n) & MSC_STAT_CRC_WRITE_ERROR_YES ) +#define __msc_stat_resto_err(n) ( REG_MSC_STAT(n) & MSC_STAT_TIME_OUT_RES ) +#define __msc_stat_rdto_err(n) ( REG_MSC_STAT(n) & MSC_STAT_TIME_OUT_READ ) + +#define __msc_rd_resfifo(n) ( REG_MSC_RES(n) ) +#define __msc_rd_rxfifo(n) ( REG_MSC_RXFIFO(n) ) +#define __msc_wr_txfifo(n, v) ( REG_MSC_TXFIFO(n) = v ) + +#define __msc_reset(n) \ +do { \ + REG_MSC_STRPCL(n) = MSC_STRPCL_RESET; \ + while (REG_MSC_STAT(n) & MSC_STAT_IS_RESETTING); \ +} while (0) + +#define __msc_start_clk(n) \ +do { \ + REG_MSC_STRPCL(n) = MSC_STRPCL_CLOCK_CONTROL_START; \ +} while (0) + +#define __msc_stop_clk(n) \ +do { \ + REG_MSC_STRPCL(n) = MSC_STRPCL_CLOCK_CONTROL_STOP; \ +} while (0) + +#define MMC_CLK 19169200 +#define SD_CLK 24576000 + +/* msc_clk should little than pclk and little than clk retrieve from card */ +#define __msc_calc_clk_divisor(type,dev_clk,msc_clk,lv) \ +do { \ + unsigned int rate, pclk, i; \ + pclk = dev_clk; \ + rate = type?SD_CLK:MMC_CLK; \ + if (msc_clk && msc_clk < pclk) \ + pclk = msc_clk; \ + i = 0; \ + while (pclk < rate) \ + { \ + i ++; \ + rate >>= 1; \ + } \ + lv = i; \ +} while(0) + +/* divide rate to little than or equal to 400kHz */ +#define __msc_calc_slow_clk_divisor(type, lv) \ +do { \ + unsigned int rate, i; \ + rate = (type?SD_CLK:MMC_CLK)/1000/400; \ + i = 0; \ + while (rate > 0) \ + { \ + rate >>= 1; \ + i ++; \ + } \ + lv = i; \ +} while(0) + + +/*************************************************************************** + * SSI (Synchronous Serial Interface) + ***************************************************************************/ +/* n = 0, 1 (SSI0, SSI1) */ +#define __ssi_enable(n) ( REG_SSI_CR0(n) |= SSI_CR0_SSIE ) +#define __ssi_disable(n) ( REG_SSI_CR0(n) &= ~SSI_CR0_SSIE ) +#define __ssi_select_ce(n) ( REG_SSI_CR0(n) &= ~SSI_CR0_FSEL ) + +#define __ssi_normal_mode(n) ( REG_SSI_ITR(n) &= ~SSI_ITR_IVLTM_MASK ) + +#define __ssi_select_ce2(n) \ +do { \ + REG_SSI_CR0(n) |= SSI_CR0_FSEL; \ + REG_SSI_CR1(n) &= ~SSI_CR1_MULTS; \ +} while (0) + +#define __ssi_select_gpc(n) \ +do { \ + REG_SSI_CR0(n) &= ~SSI_CR0_FSEL; \ + REG_SSI_CR1(n) |= SSI_CR1_MULTS; \ +} while (0) + +#define __ssi_underrun_auto_clear(n) \ +do { \ + REG_SSI_CR0(n) |= SSI_CR0_EACLRUN; \ +} while (0) + +#define __ssi_underrun_clear_manually(n) \ +do { \ + REG_SSI_CR0(n) &= ~SSI_CR0_EACLRUN; \ +} while (0) + +#define __ssi_enable_tx_intr(n) \ + ( REG_SSI_CR0(n) |= SSI_CR0_TIE | SSI_CR0_TEIE ) + +#define __ssi_disable_tx_intr(n) \ + ( REG_SSI_CR0(n) &= ~(SSI_CR0_TIE | SSI_CR0_TEIE) ) + +#define __ssi_enable_rx_intr(n) \ + ( REG_SSI_CR0(n) |= SSI_CR0_RIE | SSI_CR0_REIE ) + +#define __ssi_disable_rx_intr(n) \ + ( REG_SSI_CR0(n) &= ~(SSI_CR0_RIE | SSI_CR0_REIE) ) + +#define __ssi_enable_txfifo_half_empty_intr(n) \ + ( REG_SSI_CR0(n) |= SSI_CR0_TIE ) +#define __ssi_disable_txfifo_half_empty_intr(n) \ + ( REG_SSI_CR0(n) &= ~SSI_CR0_TIE ) +#define __ssi_enable_tx_error_intr(n) \ + ( REG_SSI_CR0(n) |= SSI_CR0_TEIE ) +#define __ssi_disable_tx_error_intr(n) \ + ( REG_SSI_CR0(n) &= ~SSI_CR0_TEIE ) +#define __ssi_enable_rxfifo_half_full_intr(n) \ + ( REG_SSI_CR0(n) |= SSI_CR0_RIE ) +#define __ssi_disable_rxfifo_half_full_intr(n) \ + ( REG_SSI_CR0(n) &= ~SSI_CR0_RIE ) +#define __ssi_enable_rx_error_intr(n) \ + ( REG_SSI_CR0(n) |= SSI_CR0_REIE ) +#define __ssi_disable_rx_error_intr(n) \ + ( REG_SSI_CR0(n) &= ~SSI_CR0_REIE ) + +#define __ssi_enable_loopback(n) ( REG_SSI_CR0(n) |= SSI_CR0_LOOP ) +#define __ssi_disable_loopback(n) ( REG_SSI_CR0(n) &= ~SSI_CR0_LOOP ) + +#define __ssi_enable_receive(n) ( REG_SSI_CR0(n) &= ~SSI_CR0_DISREV ) +#define __ssi_disable_receive(n) ( REG_SSI_CR0(n) |= SSI_CR0_DISREV ) + +#define __ssi_finish_receive(n) \ + ( REG_SSI_CR0(n) |= (SSI_CR0_RFINE | SSI_CR0_RFINC) ) + +#define __ssi_disable_recvfinish(n) \ + ( REG_SSI_CR0(n) &= ~(SSI_CR0_RFINE | SSI_CR0_RFINC) ) + +#define __ssi_flush_txfifo(n) ( REG_SSI_CR0(n) |= SSI_CR0_TFLUSH ) +#define __ssi_flush_rxfifo(n) ( REG_SSI_CR0(n) |= SSI_CR0_RFLUSH ) + +#define __ssi_flush_fifo(n) \ + ( REG_SSI_CR0(n) |= SSI_CR0_TFLUSH | SSI_CR0_RFLUSH ) + +#define __ssi_finish_transmit(n) ( REG_SSI_CR1(n) &= ~SSI_CR1_UNFIN ) +#define __ssi_wait_transmit(n) ( REG_SSI_CR1(n) |= SSI_CR1_UNFIN ) +#define __ssi_use_busy_wait_mode(n) __ssi_wait_transmit(n) +#define __ssi_unset_busy_wait_mode(n) __ssi_finish_transmit(n) + +#define __ssi_spi_format(n) \ + do { \ + REG_SSI_CR1(n) &= ~SSI_CR1_FMAT_MASK; \ + REG_SSI_CR1(n) |= SSI_CR1_FMAT_SPI; \ + REG_SSI_CR1(n) &= ~(SSI_CR1_TFVCK_MASK|SSI_CR1_TCKFI_MASK); \ + REG_SSI_CR1(n) |= (SSI_CR1_TFVCK_1 | SSI_CR1_TCKFI_1); \ + } while (0) + +/* TI's SSP format, must clear SSI_CR1.UNFIN */ +#define __ssi_ssp_format(n) \ + do { \ + REG_SSI_CR1(n) &= ~(SSI_CR1_FMAT_MASK | SSI_CR1_UNFIN); \ + REG_SSI_CR1(n) |= SSI_CR1_FMAT_SSP; \ + } while (0) + +/* National's Microwire format, must clear SSI_CR0.RFINE, and set max delay */ +#define __ssi_microwire_format(n) \ + do { \ + REG_SSI_CR1(n) &= ~SSI_CR1_FMAT_MASK; \ + REG_SSI_CR1(n) |= SSI_CR1_FMAT_MW1; \ + REG_SSI_CR1(n) &= ~(SSI_CR1_TFVCK_MASK|SSI_CR1_TCKFI_MASK); \ + REG_SSI_CR1(n) |= (SSI_CR1_TFVCK_3 | SSI_CR1_TCKFI_3); \ + REG_SSI_CR0(n) &= ~SSI_CR0_RFINE; \ + } while (0) + +/* CE# level (FRMHL), CE# in interval time (ITFRM), + clock phase and polarity (PHA POL), + interval time (SSIITR), interval characters/frame (SSIICR) */ + +/* frmhl,endian,mcom,flen,pha,pol MASK */ +#define SSICR1_MISC_MASK \ + ( SSI_CR1_FRMHL_MASK | SSI_CR1_LFST | SSI_CR1_MCOM_MASK \ + | SSI_CR1_FLEN_MASK | SSI_CR1_PHA | SSI_CR1_POL ) + +#define __ssi_spi_set_misc(n,frmhl,endian,flen,mcom,pha,pol) \ + do { \ + REG_SSI_CR1(n) &= ~SSICR1_MISC_MASK; \ + REG_SSI_CR1(n) |= ((frmhl) << 30) | ((endian) << 25) | \ + (((mcom) - 1) << 12) | (((flen) - 2) << 4) | \ + ((pha) << 1) | (pol); \ + } while(0) + +/* Transfer with MSB or LSB first */ +#define __ssi_set_msb(n) ( REG_SSI_CR1(n) &= ~SSI_CR1_LFST ) +#define __ssi_set_lsb(n) ( REG_SSI_CR1(n) |= SSI_CR1_LFST ) + +#define __ssi_set_frame_length(n, m) \ + REG_SSI_CR1(n) = (REG_SSI_CR1(n) & ~SSI_CR1_FLEN_MASK) | (((m) - 2) << 4) + +/* m = 1 - 16 */ +#define __ssi_set_microwire_command_length(n,m) \ + ( REG_SSI_CR1(n) = ((REG_SSI_CR1(n) & ~SSI_CR1_MCOM_MASK) | SSI_CR1_MCOM_##m##BIT) ) + +/* Set the clock phase for SPI */ +#define __ssi_set_spi_clock_phase(n, m) \ + ( REG_SSI_CR1(n) = ((REG_SSI_CR1(n) & ~SSI_CR1_PHA) | (((m)&0x1)<< 1))) + +/* Set the clock polarity for SPI */ +#define __ssi_set_spi_clock_polarity(n, p) \ + ( REG_SSI_CR1(n) = ((REG_SSI_CR1(n) & ~SSI_CR1_POL) | ((p)&0x1)) ) + +/* SSI tx trigger, m = i x 8 */ +#define __ssi_set_tx_trigger(n, m) \ + do { \ + REG_SSI_CR1(n) &= ~SSI_CR1_TTRG_MASK; \ + REG_SSI_CR1(n) |= ((m)/8)<> SSI_SR_TFIFONUM_BIT ) + +#define __ssi_get_rxfifo_count(n) \ + ( (REG_SSI_SR(n) & SSI_SR_RFIFONUM_MASK) >> SSI_SR_RFIFONUM_BIT ) + +#define __ssi_transfer_end(n) ( REG_SSI_SR(n) & SSI_SR_END ) +#define __ssi_is_busy(n) ( REG_SSI_SR(n) & SSI_SR_BUSY ) + +#define __ssi_txfifo_full(n) ( REG_SSI_SR(n) & SSI_SR_TFF ) +#define __ssi_rxfifo_empty(n) ( REG_SSI_SR(n) & SSI_SR_RFE ) +#define __ssi_rxfifo_half_full(n) ( REG_SSI_SR(n) & SSI_SR_RFHF ) +#define __ssi_txfifo_half_empty(n) ( REG_SSI_SR(n) & SSI_SR_TFHE ) +#define __ssi_underrun(n) ( REG_SSI_SR(n) & SSI_SR_UNDR ) +#define __ssi_overrun(n) ( REG_SSI_SR(n) & SSI_SR_OVER ) +#define __ssi_clear_underrun(n) ( REG_SSI_SR(n) = ~SSI_SR_UNDR ) +#define __ssi_clear_overrun(n) ( REG_SSI_SR(n) = ~SSI_SR_OVER ) +#define __ssi_clear_errors(n) ( REG_SSI_SR(n) &= ~(SSI_SR_UNDR | SSI_SR_OVER) ) + +#define __ssi_set_clk(n, dev_clk, ssi_clk) \ + ( REG_SSI_GR(n) = (dev_clk) / (2*(ssi_clk)) - 1 ) + +#define __ssi_receive_data(n) REG_SSI_DR(n) +#define __ssi_transmit_data(n, v) (REG_SSI_DR(n) = (v)) + + +/*************************************************************************** + * CIM + ***************************************************************************/ + +#define __cim_enable() ( REG_CIM_CTRL |= CIM_CTRL_ENA ) +#define __cim_disable() ( REG_CIM_CTRL &= ~CIM_CTRL_ENA ) + +/* n = 0, 1, 2, 3 */ +#define __cim_set_input_data_stream_order(n) \ + do { \ + REG_CIM_CFG &= CIM_CFG_ORDER_MASK; \ + REG_CIM_CFG |= ((n)<>CIM_SIZE_LPF_BIT) +#define __cim_get_pixel() ((REG_CIM_SIZE&CIM_SIZE_PPL_MASK)>>CIM_SIZE_PPL_BIT) + +#define __cim_set_v_offset(a) ( REG_CIM_OFFSET = (REG_CIM_OFFSET&(~CIM_OFFSET_V_MASK)) | ((a)<>CIM_OFFSET_V_BIT) +#define __cim_get_h_offset() ((REG_CIM_OFFSET&CIM_OFFSET_H_MASK)>>CIM_OFFSET_H_BIT) + +/************************************************************************* + * SLCD (Smart LCD Controller) + *************************************************************************/ +#define __slcd_set_data_18bit() \ + ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_18BIT ) +#define __slcd_set_data_16bit() \ + ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_16BIT ) +#define __slcd_set_data_8bit_x3() \ + ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_8BIT_x3 ) +#define __slcd_set_data_8bit_x2() \ + ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_8BIT_x2 ) +#define __slcd_set_data_8bit_x1() \ + ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_8BIT_x1 ) +#define __slcd_set_data_24bit() \ + ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_24BIT ) +#define __slcd_set_data_9bit_x2() \ + ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_DWIDTH_MASK) | SLCD_CFG_DWIDTH_9BIT_x2 ) + +#define __slcd_set_cmd_16bit() \ + ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_CWIDTH_MASK) | SLCD_CFG_CWIDTH_16BIT ) +#define __slcd_set_cmd_8bit() \ + ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_CWIDTH_MASK) | SLCD_CFG_CWIDTH_8BIT ) +#define __slcd_set_cmd_18bit() \ + ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_CWIDTH_MASK) | SLCD_CFG_CWIDTH_18BIT ) +#define __slcd_set_cmd_24bit() \ + ( REG_SLCD_CFG = (REG_SLCD_CFG & ~SLCD_CFG_CWIDTH_MASK) | SLCD_CFG_CWIDTH_24BIT ) + +#define __slcd_set_cs_high() ( REG_SLCD_CFG |= SLCD_CFG_CS_ACTIVE_HIGH ) +#define __slcd_set_cs_low() ( REG_SLCD_CFG &= ~SLCD_CFG_CS_ACTIVE_HIGH ) + +#define __slcd_set_rs_high() ( REG_SLCD_CFG |= SLCD_CFG_RS_CMD_HIGH ) +#define __slcd_set_rs_low() ( REG_SLCD_CFG &= ~SLCD_CFG_RS_CMD_HIGH ) + +#define __slcd_set_clk_falling() ( REG_SLCD_CFG &= ~SLCD_CFG_CLK_ACTIVE_RISING ) +#define __slcd_set_clk_rising() ( REG_SLCD_CFG |= SLCD_CFG_CLK_ACTIVE_RISING ) + +#define __slcd_set_parallel_type() ( REG_SLCD_CFG &= ~SLCD_CFG_TYPE_SERIAL ) +#define __slcd_set_serial_type() ( REG_SLCD_CFG |= SLCD_CFG_TYPE_SERIAL ) + +/* SLCD Control Register */ +#define __slcd_enable_dma() ( REG_SLCD_CTRL |= SLCD_CTRL_DMA_EN ) +#define __slcd_disable_dma() ( REG_SLCD_CTRL &= ~SLCD_CTRL_DMA_EN ) + +/* SLCD Status Register */ +#define __slcd_is_busy() ( REG_SLCD_STATE & SLCD_STATE_BUSY ) + +/* SLCD Data Register */ +#define __slcd_set_cmd_rs() ( REG_SLCD_DATA |= SLCD_DATA_RS_COMMAND) +#define __slcd_set_data_rs() ( REG_SLCD_DATA &= ~SLCD_DATA_RS_COMMAND) + + +/*************************************************************************** + * LCD + ***************************************************************************/ + +/*************************************************************************** + * LCD + ***************************************************************************/ +#define __lcd_as_smart_lcd() ( REG_LCD_CFG |= ( LCD_CFG_LCDPIN_SLCD | LCD_CFG_MODE_SLCD)) +#define __lcd_as_general_lcd() ( REG_LCD_CFG &= ~( LCD_CFG_LCDPIN_SLCD | LCD_CFG_MODE_SLCD)) + +#define __lcd_enable_tvepeh() ( REG_LCD_CFG |= LCD_CFG_TVEPEH ) +#define __lcd_disable_tvepeh() ( REG_LCD_CFG &= ~LCD_CFG_TVEPEH ) + +#define __lcd_enable_fuhold() ( REG_LCD_CFG |= LCD_CFG_FUHOLD ) +#define __lcd_disable_fuhold() ( REG_LCD_CFG &= ~LCD_CFG_FUHOLD ) + +#define __lcd_des_8word() ( REG_LCD_CFG |= LCD_CFG_NEWDES ) +#define __lcd_des_4word() ( REG_LCD_CFG &= ~LCD_CFG_NEWDES ) + +#define __lcd_enable_bypass_pal() ( REG_LCD_CFG |= LCD_CFG_PALBP ) +#define __lcd_disable_bypass_pal() ( REG_LCD_CFG &= ~LCD_CFG_PALBP ) + +#define __lcd_set_lcdpnl_term() ( REG_LCD_CFG |= LCD_CFG_TVEN ) +#define __lcd_set_tv_term() ( REG_LCD_CFG &= ~LCD_CFG_TVEN ) + +#define __lcd_enable_auto_recover() ( REG_LCD_CFG |= LCD_CFG_RECOVER ) +#define __lcd_disable_auto_recover() ( REG_LCD_CFG &= ~LCD_CFG_RECOVER ) + +#define __lcd_enable_dither() ( REG_LCD_CFG |= LCD_CFG_DITHER ) +#define __lcd_disable_dither() ( REG_LCD_CFG &= ~LCD_CFG_DITHER ) + +#define __lcd_disable_ps_mode() ( REG_LCD_CFG |= LCD_CFG_PSM ) +#define __lcd_enable_ps_mode() ( REG_LCD_CFG &= ~LCD_CFG_PSM ) + +#define __lcd_disable_cls_mode() ( REG_LCD_CFG |= LCD_CFG_CLSM ) +#define __lcd_enable_cls_mode() ( REG_LCD_CFG &= ~LCD_CFG_CLSM ) + +#define __lcd_disable_spl_mode() ( REG_LCD_CFG |= LCD_CFG_SPLM ) +#define __lcd_enable_spl_mode() ( REG_LCD_CFG &= ~LCD_CFG_SPLM ) + +#define __lcd_disable_rev_mode() ( REG_LCD_CFG |= LCD_CFG_REVM ) +#define __lcd_enable_rev_mode() ( REG_LCD_CFG &= ~LCD_CFG_REVM ) + +#define __lcd_disable_hsync_mode() ( REG_LCD_CFG |= LCD_CFG_HSYNM ) +#define __lcd_enable_hsync_mode() ( REG_LCD_CFG &= ~LCD_CFG_HSYNM ) + +#define __lcd_disable_pclk_mode() ( REG_LCD_CFG |= LCD_CFG_PCLKM ) +#define __lcd_enable_pclk_mode() ( REG_LCD_CFG &= ~LCD_CFG_PCLKM ) + +#define __lcd_normal_outdata() ( REG_LCD_CFG &= ~LCD_CFG_INVDAT ) +#define __lcd_inverse_outdata() ( REG_LCD_CFG |= LCD_CFG_INVDAT ) + +#define __lcd_sync_input() ( REG_LCD_CFG |= LCD_CFG_SYNDIR_IN ) +#define __lcd_sync_output() ( REG_LCD_CFG &= ~LCD_CFG_SYNDIR_IN ) + +#define __lcd_hsync_active_high() ( REG_LCD_CFG &= ~LCD_CFG_HSP ) +#define __lcd_hsync_active_low() ( REG_LCD_CFG |= LCD_CFG_HSP ) + +#define __lcd_pclk_rising() ( REG_LCD_CFG &= ~LCD_CFG_PCP ) +#define __lcd_pclk_falling() ( REG_LCD_CFG |= LCD_CFG_PCP ) + +#define __lcd_de_active_high() ( REG_LCD_CFG &= ~LCD_CFG_DEP ) +#define __lcd_de_active_low() ( REG_LCD_CFG |= LCD_CFG_DEP ) + +#define __lcd_vsync_rising() ( REG_LCD_CFG &= ~LCD_CFG_VSP ) +#define __lcd_vsync_falling() ( REG_LCD_CFG |= LCD_CFG_VSP ) + +#define __lcd_set_16_tftpnl() \ + ( REG_LCD_CFG = (REG_LCD_CFG & ~LCD_CFG_MODE_TFT_MASK) | LCD_CFG_MODE_TFT_16BIT ) + +#define __lcd_set_18_tftpnl() \ + ( REG_LCD_CFG = (REG_LCD_CFG & ~LCD_CFG_MODE_TFT_MASK) | LCD_CFG_MODE_TFT_18BIT ) + +#define __lcd_set_24_tftpnl() ( REG_LCD_CFG |= LCD_CFG_MODE_TFT_24BIT ) + +/* + * n=1,2,4,8 for single mono-STN + * n=4,8 for dual mono-STN + */ +#define __lcd_set_panel_datawidth(n) \ +do { \ + REG_LCD_CFG &= ~LCD_CFG_PDW_MASK; \ + REG_LCD_CFG |= LCD_CFG_PDW_n##; \ +} while (0) + +/* m = LCD_CFG_MODE_GENERUIC_TFT_xxx */ +#define __lcd_set_panel_mode(m) \ +do { \ + REG_LCD_CFG &= ~LCD_CFG_MODE_MASK; \ + REG_LCD_CFG |= (m); \ +} while(0) + +/* n=4,8,16 */ +#define __lcd_set_burst_length(n) \ +do { \ + REG_LCD_CTRL &= ~LCD_CTRL_BST_MASK; \ + REG_LCD_CTRL |= LCD_CTRL_BST_n##; \ +} while (0) + +#define __lcd_select_rgb565() ( REG_LCD_CTRL &= ~LCD_CTRL_RGB555 ) +#define __lcd_select_rgb555() ( REG_LCD_CTRL |= LCD_CTRL_RGB555 ) + +#define __lcd_set_ofup() ( REG_LCD_CTRL |= LCD_CTRL_OFUP ) +#define __lcd_clr_ofup() ( REG_LCD_CTRL &= ~LCD_CTRL_OFUP ) + +/* n=2,4,16 */ +#define __lcd_set_stn_frc(n) \ +do { \ + REG_LCD_CTRL &= ~LCD_CTRL_FRC_MASK; \ + REG_LCD_CTRL |= LCD_CTRL_FRC_n##; \ +} while (0) + +#define __lcd_enable_eof_intr() ( REG_LCD_CTRL |= LCD_CTRL_EOFM ) +#define __lcd_disable_eof_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_EOFM ) + +#define __lcd_enable_sof_intr() ( REG_LCD_CTRL |= LCD_CTRL_SOFM ) +#define __lcd_disable_sof_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_SOFM ) + +#define __lcd_enable_ofu_intr() ( REG_LCD_CTRL |= LCD_CTRL_OFUM ) +#define __lcd_disable_ofu_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_OFUM ) + +#define __lcd_enable_ifu0_intr() ( REG_LCD_CTRL |= LCD_CTRL_IFUM0 ) +#define __lcd_disable_ifu0_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_IFUM0 ) + +#define __lcd_enable_ifu1_intr() ( REG_LCD_CTRL |= LCD_CTRL_IFUM1 ) +#define __lcd_disable_ifu1_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_IFUM1 ) + +#define __lcd_enable_ldd_intr() ( REG_LCD_CTRL |= LCD_CTRL_LDDM ) +#define __lcd_disable_ldd_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_LDDM ) + +#define __lcd_enable_qd_intr() ( REG_LCD_CTRL |= LCD_CTRL_QDM ) +#define __lcd_disable_qd_intr() ( REG_LCD_CTRL &= ~LCD_CTRL_QDM ) + +#define __lcd_reverse_byte_endian() ( REG_LCD_CTRL |= LCD_CTRL_BEDN ) +#define __lcd_normal_byte_endian() ( REG_LCD_CTRL &= ~LCD_CTRL_BEDN ) + +#define __lcd_pixel_endian_little() ( REG_LCD_CTRL |= LCD_CTRL_PEDN ) +#define __lcd_pixel_endian_big() ( REG_LCD_CTRL &= ~LCD_CTRL_PEDN ) + +#define __lcd_set_dis() ( REG_LCD_CTRL |= LCD_CTRL_DIS ) +#define __lcd_clr_dis() ( REG_LCD_CTRL &= ~LCD_CTRL_DIS ) + +#define __lcd_set_ena() ( REG_LCD_CTRL |= LCD_CTRL_ENA ) +#define __lcd_clr_ena() ( REG_LCD_CTRL &= ~LCD_CTRL_ENA ) + +/* n=1,2,4,8,16 */ +#define __lcd_set_bpp(n) \ + ( REG_LCD_CTRL = (REG_LCD_CTRL & ~LCD_CTRL_BPP_MASK) | LCD_CTRL_BPP_##n ) + +/* LCD status register indication */ + +#define __lcd_quick_disable_done() ( REG_LCD_STATE & LCD_STATE_QD ) +#define __lcd_disable_done() ( REG_LCD_STATE & LCD_STATE_LDD ) +#define __lcd_infifo0_underrun() ( REG_LCD_STATE & LCD_STATE_IFU0 ) +#define __lcd_infifo1_underrun() ( REG_LCD_STATE & LCD_STATE_IFU1 ) +#define __lcd_outfifo_underrun() ( REG_LCD_STATE & LCD_STATE_OFU ) +#define __lcd_start_of_frame() ( REG_LCD_STATE & LCD_STATE_SOF ) +#define __lcd_end_of_frame() ( REG_LCD_STATE & LCD_STATE_EOF ) + +#define __lcd_clr_outfifounderrun() ( REG_LCD_STATE &= ~LCD_STATE_OFU ) +#define __lcd_clr_sof() ( REG_LCD_STATE &= ~LCD_STATE_SOF ) +#define __lcd_clr_eof() ( REG_LCD_STATE &= ~LCD_STATE_EOF ) + +/* OSD functions */ +#define __lcd_enable_osd() (REG_LCD_OSDC |= LCD_OSDC_OSDEN) +#define __lcd_enable_f0() (REG_LCD_OSDC |= LCD_OSDC_F0EN) +#define __lcd_enable_f1() (REG_LCD_OSDC |= LCD_OSDC_F1EN) +#define __lcd_enable_alpha() (REG_LCD_OSDC |= LCD_OSDC_ALPHAEN) +#define __lcd_enable_alphamd() (REG_LCD_OSDC |= LCD_OSDC_ALPHAMD) + +#define __lcd_disable_osd() (REG_LCD_OSDC &= ~LCD_OSDC_OSDEN) +#define __lcd_disable_f0() (REG_LCD_OSDC &= ~LCD_OSDC_F0EN) +#define __lcd_disable_f1() (REG_LCD_OSDC &= ~LCD_OSDC_F1EN) +#define __lcd_disable_alpha() (REG_LCD_OSDC &= ~LCD_OSDC_ALPHAEN) +#define __lcd_disable_alphamd() (REG_LCD_OSDC &= ~LCD_OSDC_ALPHAMD) + +/* OSD Controll Register */ +#define __lcd_fg1_use_ipu() (REG_LCD_OSDCTRL |= LCD_OSDCTRL_IPU) +#define __lcd_fg1_use_dma_chan1() (REG_LCD_OSDCTRL &= ~LCD_OSDCTRL_IPU) +#define __lcd_fg1_unuse_ipu() __lcd_fg1_use_dma_chan1() +#define __lcd_osd_rgb555_mode() ( REG_LCD_OSDCTRL |= LCD_OSDCTRL_RGB555 ) +#define __lcd_osd_rgb565_mode() ( REG_LCD_OSDCTRL &= ~LCD_OSDCTRL_RGB555 ) +#define __lcd_osd_change_size() ( REG_LCD_OSDCTRL |= LCD_OSDCTRL_CHANGES ) +#define __lcd_osd_bpp_15_16() \ + ( REG_LCD_OSDCTRL = (REG_LCD_OSDCTRL & ~LCD_OSDCTRL_OSDBPP_MASK) | LCD_OSDCTRL_OSDBPP_15_16 ) +#define __lcd_osd_bpp_18_24() \ + ( REG_LCD_OSDCTRL = (REG_LCD_OSDCTRL & ~LCD_OSDCTRL_OSDBPP_MASK) | LCD_OSDCTRL_OSDBPP_18_24 ) + +/* OSD State Register */ +#define __lcd_start_of_fg1() ( REG_LCD_STATE & LCD_OSDS_SOF1 ) +#define __lcd_end_of_fg1() ( REG_LCD_STATE & LCD_OSDS_EOF1 ) +#define __lcd_start_of_fg0() ( REG_LCD_STATE & LCD_OSDS_SOF0 ) +#define __lcd_end_of_fg0() ( REG_LCD_STATE & LCD_OSDS_EOF0 ) +#define __lcd_change_is_rdy() ( REG_LCD_STATE & LCD_OSDS_READY ) + +/* Foreground Color Key Register 0,1(foreground 0, foreground 1) */ +#define __lcd_enable_colorkey0() (REG_LCD_KEY0 |= LCD_KEY_KEYEN) +#define __lcd_enable_colorkey1() (REG_LCD_KEY1 |= LCD_KEY_KEYEN) +#define __lcd_enable_colorkey0_md() (REG_LCD_KEY0 |= LCD_KEY_KEYMD) +#define __lcd_enable_colorkey1_md() (REG_LCD_KEY1 |= LCD_KEY_KEYMD) +#define __lcd_set_colorkey0(key) (REG_LCD_KEY0 = (REG_LCD_KEY0&~0xFFFFFF)|(key)) +#define __lcd_set_colorkey1(key) (REG_LCD_KEY1 = (REG_LCD_KEY1&~0xFFFFFF)|(key)) + +#define __lcd_disable_colorkey0() (REG_LCD_KEY0 &= ~LCD_KEY_KEYEN) +#define __lcd_disable_colorkey1() (REG_LCD_KEY1 &= ~LCD_KEY_KEYEN) +#define __lcd_disable_colorkey0_md() (REG_LCD_KEY0 &= ~LCD_KEY_KEYMD) +#define __lcd_disable_colorkey1_md() (REG_LCD_KEY1 &= ~LCD_KEY_KEYMD) + +/* IPU Restart Register */ +#define __lcd_enable_ipu_restart() (REG_LCD_IPUR |= LCD_IPUR_IPUREN) +#define __lcd_disable_ipu_restart() (REG_LCD_IPUR &= ~LCD_IPUR_IPUREN) +#define __lcd_set_ipu_restart_triger(n) (REG_LCD_IPUR = (REG_LCD_IPUR&(~0xFFFFFF))|(n)) + +/* RGB Control Register */ +#define __lcd_enable_rgb_dummy() (REG_LCD_RGBC |= LCD_RGBC_RGBDM) +#define __lcd_disable_rgb_dummy() (REG_LCD_RGBC &= ~LCD_RGBC_RGBDM) + +#define __lcd_dummy_rgb() (REG_LCD_RGBC |= LCD_RGBC_DMM) +#define __lcd_rgb_dummy() (REG_LCD_RGBC &= ~LCD_RGBC_DMM) + +#define __lcd_rgb2ycc() (REG_LCD_RGBC |= LCD_RGBC_YCC) +#define __lcd_notrgb2ycc() (REG_LCD_RGBC &= ~LCD_RGBC_YCC) + +#define __lcd_odd_mode_rgb() \ + ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_ODDRGB_MASK) | LCD_RGBC_ODD_RGB ) +#define __lcd_odd_mode_rbg() \ + ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_ODDRGB_MASK) | LCD_RGBC_ODD_RBG ) +#define __lcd_odd_mode_grb() \ + ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_ODDRGB_MASK) | LCD_RGBC_ODD_GRB) + +#define __lcd_odd_mode_gbr() \ + ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_ODDRGB_MASK) | LCD_RGBC_ODD_GBR) +#define __lcd_odd_mode_brg() \ + ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_ODDRGB_MASK) | LCD_RGBC_ODD_BRG) +#define __lcd_odd_mode_bgr() \ + ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_ODDRGB_MASK) | LCD_RGBC_ODD_BGR) + +#define __lcd_even_mode_rgb() \ + ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_EVENRGB_MASK) | LCD_RGBC_EVEN_RGB ) +#define __lcd_even_mode_rbg() \ + ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_EVENRGB_MASK) | LCD_RGBC_EVEN_RBG ) +#define __lcd_even_mode_grb() \ + ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_EVENRGB_MASK) | LCD_RGBC_EVEN_GRB) + +#define __lcd_even_mode_gbr() \ + ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_EVENRGB_MASK) | LCD_RGBC_EVEN_GBR) +#define __lcd_even_mode_brg() \ + ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_EVENRGB_MASK) | LCD_RGBC_EVEN_BRG) +#define __lcd_even_mode_bgr() \ + ( REG_LCD_RGBC = (REG_LCD_RGBC & ~LCD_RGBC_EVENRGB_MASK) | LCD_RGBC_EVEN_BGR) + +/* Vertical Synchronize Register */ +#define __lcd_vsync_get_vps() \ + ( (REG_LCD_VSYNC & LCD_VSYNC_VPS_MASK) >> LCD_VSYNC_VPS_BIT ) + +#define __lcd_vsync_get_vpe() \ + ( (REG_LCD_VSYNC & LCD_VSYNC_VPE_MASK) >> LCD_VSYNC_VPE_BIT ) +#define __lcd_vsync_set_vpe(n) \ +do { \ + REG_LCD_VSYNC &= ~LCD_VSYNC_VPE_MASK; \ + REG_LCD_VSYNC |= (n) << LCD_VSYNC_VPE_BIT; \ +} while (0) + +#define __lcd_hsync_get_hps() \ + ( (REG_LCD_HSYNC & LCD_HSYNC_HPS_MASK) >> LCD_HSYNC_HPS_BIT ) +#define __lcd_hsync_set_hps(n) \ +do { \ + REG_LCD_HSYNC &= ~LCD_HSYNC_HPS_MASK; \ + REG_LCD_HSYNC |= (n) << LCD_HSYNC_HPS_BIT; \ +} while (0) + +#define __lcd_hsync_get_hpe() \ + ( (REG_LCD_HSYNC & LCD_HSYNC_HPE_MASK) >> LCD_VSYNC_HPE_BIT ) +#define __lcd_hsync_set_hpe(n) \ +do { \ + REG_LCD_HSYNC &= ~LCD_HSYNC_HPE_MASK; \ + REG_LCD_HSYNC |= (n) << LCD_HSYNC_HPE_BIT; \ +} while (0) + +#define __lcd_vat_get_ht() \ + ( (REG_LCD_VAT & LCD_VAT_HT_MASK) >> LCD_VAT_HT_BIT ) +#define __lcd_vat_set_ht(n) \ +do { \ + REG_LCD_VAT &= ~LCD_VAT_HT_MASK; \ + REG_LCD_VAT |= (n) << LCD_VAT_HT_BIT; \ +} while (0) + +#define __lcd_vat_get_vt() \ + ( (REG_LCD_VAT & LCD_VAT_VT_MASK) >> LCD_VAT_VT_BIT ) +#define __lcd_vat_set_vt(n) \ +do { \ + REG_LCD_VAT &= ~LCD_VAT_VT_MASK; \ + REG_LCD_VAT |= (n) << LCD_VAT_VT_BIT; \ +} while (0) + +#define __lcd_dah_get_hds() \ + ( (REG_LCD_DAH & LCD_DAH_HDS_MASK) >> LCD_DAH_HDS_BIT ) +#define __lcd_dah_set_hds(n) \ +do { \ + REG_LCD_DAH &= ~LCD_DAH_HDS_MASK; \ + REG_LCD_DAH |= (n) << LCD_DAH_HDS_BIT; \ +} while (0) + +#define __lcd_dah_get_hde() \ + ( (REG_LCD_DAH & LCD_DAH_HDE_MASK) >> LCD_DAH_HDE_BIT ) +#define __lcd_dah_set_hde(n) \ +do { \ + REG_LCD_DAH &= ~LCD_DAH_HDE_MASK; \ + REG_LCD_DAH |= (n) << LCD_DAH_HDE_BIT; \ +} while (0) + +#define __lcd_dav_get_vds() \ + ( (REG_LCD_DAV & LCD_DAV_VDS_MASK) >> LCD_DAV_VDS_BIT ) +#define __lcd_dav_set_vds(n) \ +do { \ + REG_LCD_DAV &= ~LCD_DAV_VDS_MASK; \ + REG_LCD_DAV |= (n) << LCD_DAV_VDS_BIT; \ +} while (0) + +#define __lcd_dav_get_vde() \ + ( (REG_LCD_DAV & LCD_DAV_VDE_MASK) >> LCD_DAV_VDE_BIT ) +#define __lcd_dav_set_vde(n) \ +do { \ + REG_LCD_DAV &= ~LCD_DAV_VDE_MASK; \ + REG_LCD_DAV |= (n) << LCD_DAV_VDE_BIT; \ +} while (0) + +/* DMA Command Register */ +#define __lcd_cmd0_set_sofint() ( REG_LCD_CMD0 |= LCD_CMD_SOFINT ) +#define __lcd_cmd0_clr_sofint() ( REG_LCD_CMD0 &= ~LCD_CMD_SOFINT ) +#define __lcd_cmd1_set_sofint() ( REG_LCD_CMD1 |= LCD_CMD_SOFINT ) +#define __lcd_cmd1_clr_sofint() ( REG_LCD_CMD1 &= ~LCD_CMD_SOFINT ) + +#define __lcd_cmd0_set_eofint() ( REG_LCD_CMD0 |= LCD_CMD_EOFINT ) +#define __lcd_cmd0_clr_eofint() ( REG_LCD_CMD0 &= ~LCD_CMD_EOFINT ) +#define __lcd_cmd1_set_eofint() ( REG_LCD_CMD1 |= LCD_CMD_EOFINT ) +#define __lcd_cmd1_clr_eofint() ( REG_LCD_CMD1 &= ~LCD_CMD_EOFINT ) + +#define __lcd_cmd0_set_pal() ( REG_LCD_CMD0 |= LCD_CMD_PAL ) +#define __lcd_cmd0_clr_pal() ( REG_LCD_CMD0 &= ~LCD_CMD_PAL ) + +#define __lcd_cmd0_get_len() \ + ( (REG_LCD_CMD0 & LCD_CMD_LEN_MASK) >> LCD_CMD_LEN_BIT ) +#define __lcd_cmd1_get_len() \ + ( (REG_LCD_CMD1 & LCD_CMD_LEN_MASK) >> LCD_CMD_LEN_BIT ) + +/************************************************************************* + * TVE (TV Encoder Controller) ops + *************************************************************************/ +/* TV Encoder Control register ops */ +#define __tve_soft_reset() (REG_TVE_CTRL |= TVE_CTRL_SWRST) + +#define __tve_output_colorbar() (REG_TVE_CTRL |= TVE_CTRL_CLBAR) +#define __tve_output_video() (REG_TVE_CTRL &= ~TVE_CTRL_CLBAR) + +#define __tve_input_cr_first() (REG_TVE_CTRL |= TVE_CTRL_CR1ST) +#define __tve_input_cb_first() (REG_TVE_CTRL &= ~TVE_CTRL_CR1ST) + +#define __tve_set_0_as_black() (REG_TVE_CTRL |= TVE_CTRL_ZBLACK) +#define __tve_set_16_as_black() (REG_TVE_CTRL &= ~TVE_CTRL_ZBLACK) + +#define __tve_ena_invert_top_bottom() (REG_TVE_CTRL |= TVE_CTRL_FINV) +#define __tve_dis_invert_top_bottom() (REG_TVE_CTRL &= ~TVE_CTRL_FINV) + +#define __tve_set_pal_mode() (REG_TVE_CTRL |= TVE_CTRL_PAL) +#define __tve_set_ntsc_mode() (REG_TVE_CTRL &= ~TVE_CTRL_PAL) + +#define __tve_set_pal_dura() (REG_TVE_CTRL |= TVE_CTRL_SYNCT) +#define __tve_set_ntsc_dura() (REG_TVE_CTRL &= ~TVE_CTRL_SYNCT) + +/* n = 0 ~ 3 */ +#define __tve_set_c_bandwidth(n) \ +do {\ + REG_TVE_CTRL &= ~TVE_CTRL_CBW_MASK;\ + REG_TVE_CTRL |= (n) << TVE_CTRL_CBW_BIT; \ +}while(0) + +/* n = 0 ~ 3 */ +#define __tve_set_c_gain(n) \ +do {\ + REG_TVE_CTRL &= ~TVE_CTRL_CGAIN_MASK;\ + (REG_TVE_CTRL |= (n) << TVE_CTRL_CGAIN_BIT; \ +}while(0) + +/* n = 0 ~ 7 */ +#define __tve_set_yc_delay(n) \ +do { \ + REG_TVE_CTRL &= ~TVE_CTRL_YCDLY_MASK \ + REG_TVE_CTRL |= ((n) << TVE_CTRL_YCDLY_BIT); \ +} while(0) + +#define __tve_disable_all_dacs() (REG_TVE_CTRL |= TVE_CTRL_DAPD) +#define __tve_disable_dac1() (REG_TVE_CTRL |= TVE_CTRL_DAPD1) +#define __tve_enable_dac1() (REG_TVE_CTRL &= ~TVE_CTRL_DAPD1) +#define __tve_disable_dac2() (REG_TVE_CTRL |= TVE_CTRL_DAPD2) +#define __tve_enable_dac2() (REG_TVE_CTRL &= ~TVE_CTRL_DAPD2) +#define __tve_disable_dac3() (REG_TVE_CTRL |= TVE_CTRL_DAPD3) +#define __tve_enable_dac3() (REG_TVE_CTRL &= ~TVE_CTRL_DAPD3) + +#define __tve_enable_svideo_fmt() (REG_TVE_CTRL |= TVE_CTRL_ECVBS) +#define __tve_enable_cvbs_fmt() (REG_TVE_CTRL &= ~TVE_CTRL_ECVBS) + +/* TV Encoder Frame Configure register ops */ +/* n = 0 ~ 255 */ +#define __tve_set_first_video_line(n) \ +do {\ + REG_TVE_FRCFG &= ~TVE_FRCFG_L1ST_MASK;\ + REG_TVE_FRCFG |= (n) << TVE_FRCFG_L1ST_BIT;\ +} while(0) +/* n = 0 ~ 1023 */ +#define __tve_set_line_num_per_frm(n) \ +do {\ + REG_TVE_FRCFG &= ~TVE_FRCFG_NLINE_MASK;\ + REG_TVE_CFG |= (n) << TVE_FRCFG_NLINE_BIT;\ +} while(0) +#define __tve_get_video_line_num()\ + (((REG_TVE_FRCFG & TVE_FRCFG_NLINE_MASK) >> TVE_FRCFG_NLINE_BIT) - 1 - 2 * ((REG_TVE_FRCFG & TVE_FRCFG_L1ST_MASK) >> TVE_FRCFG_L1ST_BIT)) + +/* TV Encoder Signal Level Configure register ops */ +/* n = 0 ~ 1023 */ +#define __tve_set_white_level(n) \ +do {\ + REG_TVE_SLCFG1 &= ~TVE_SLCFG1_WHITEL_MASK;\ + REG_TVE_SLCFG1 |= (n) << TVE_SLCFG1_WHITEL_BIT;\ +} while(0) +/* n = 0 ~ 1023 */ +#define __tve_set_black_level(n) \ +do {\ + REG_TVE_SLCFG1 &= ~TVE_SLCFG1_BLACKL_MASK;\ + REG_TVE_SLCFG1 |= (n) << TVE_SLCFG1_BLACKL_BIT;\ +} while(0) +/* n = 0 ~ 1023 */ +#define __tve_set_blank_level(n) \ +do {\ + REG_TVE_SLCFG2 &= ~TVE_SLCFG2_BLANKL_MASK;\ + REG_TVE_SLCFG2 |= (n) << TVE_SLCFG2_BLANKL_BIT;\ +} while(0) +/* n = 0 ~ 1023 */ +#define __tve_set_vbi_blank_level(n) \ +do {\ + REG_TVE_SLCFG2 &= ~TVE_SLCFG2_VBLANKL_MASK;\ + REG_TVE_SLCFG2 |= (n) << TVE_SLCFG2_VBLANKL_BIT;\ +} while(0) +/* n = 0 ~ 1023 */ +#define __tve_set_sync_level(n) \ +do {\ + REG_TVE_SLCFG3 &= ~TVE_SLCFG3_SYNCL_MASK;\ + REG_TVE_SLCFG3 |= (n) << TVE_SLCFG3_SYNCL_BIT;\ +} while(0) + +/* TV Encoder Signal Level Configure register ops */ +/* n = 0 ~ 31 */ +#define __tve_set_front_porch(n) \ +do {\ + REG_TVE_LTCFG1 &= ~TVE_LTCFG1_FRONTP_MASK;\ + REG_TVE_LTCFG1 |= (n) << TVE_LTCFG1_FRONTP_BIT; \ +} while(0) +/* n = 0 ~ 127 */ +#define __tve_set_hsync_width(n) \ +do {\ + REG_TVE_LTCFG1 &= ~TVE_LTCFG1_HSYNCW_MASK;\ + REG_TVE_LTCFG1 |= (n) << TVE_LTCFG1_HSYNCW_BIT; \ +} while(0) +/* n = 0 ~ 127 */ +#define __tve_set_back_porch(n) \ +do {\ + REG_TVE_LTCFG1 &= ~TVE_LTCFG1_BACKP_MASK;\ + REG_TVE_LTCFG1 |= (n) << TVE_LTCFG1_BACKP_BIT; \ +} while(0) +/* n = 0 ~ 2047 */ +#define __tve_set_active_linec(n) \ +do {\ + REG_TVE_LTCFG2 &= ~TVE_LTCFG2_ACTLIN_MASK;\ + REG_TVE_LTCFG2 |= (n) << TVE_LTCFG2_ACTLIN_BIT; \ +} while(0) +/* n = 0 ~ 31 */ +#define __tve_set_breezy_way(n) \ +do {\ + REG_TVE_LTCFG2 &= ~TVE_LTCFG2_PREBW_MASK;\ + REG_TVE_LTCFG2 |= (n) << TVE_LTCFG2_PREBW_BIT; \ +} while(0) + +/* n = 0 ~ 127 */ +#define __tve_set_burst_width(n) \ +do {\ + REG_TVE_LTCFG2 &= ~TVE_LTCFG2_BURSTW_MASK;\ + REG_TVE_LTCFG2 |= (n) << TVE_LTCFG2_BURSTW_BIT; \ +} while(0) + +/* TV Encoder Chrominance filter and Modulation register ops */ +/* n = 0 ~ (2^32-1) */ +#define __tve_set_c_sub_carrier_freq(n) REG_TVE_CFREQ = (n) +/* n = 0 ~ 255 */ +#define __tve_set_c_sub_carrier_init_phase(n) \ +do { \ + REG_TVE_CPHASE &= ~TVE_CPHASE_INITPH_MASK; \ + REG_TVE_CPHASE |= (n) << TVE_CPHASE_INITPH_BIT; \ +} while(0) +/* n = 0 ~ 255 */ +#define __tve_set_c_sub_carrier_act_phase(n) \ +do { \ + REG_TVE_CPHASE &= ~TVE_CPHASE_ACTPH_MASK; \ + REG_TVE_CPHASE |= (n) << TVE_CPHASE_ACTPH_BIT; \ +} while(0) +/* n = 0 ~ 255 */ +#define __tve_set_c_phase_rst_period(n) \ +do { \ + REG_TVE_CPHASE &= ~TVE_CPHASE_CCRSTP_MASK; \ + REG_TVE_CPHASE |= (n) << TVE_CPHASE_CCRSTP_BIT; \ +} while(0) +/* n = 0 ~ 255 */ +#define __tve_set_cb_burst_amp(n) \ +do { \ + REG_TVE_CBCRCFG &= ~TVE_CBCRCFG_CBBA_MASK; \ + REG_TVE_CBCRCFG |= (n) << TVE_CBCRCFG_CBBA_BIT; \ +} while(0) +/* n = 0 ~ 255 */ +#define __tve_set_cr_burst_amp(n) \ +do { \ + REG_TVE_CBCRCFG &= ~TVE_CBCRCFG_CRBA_MASK; \ + REG_TVE_CBCRCFG |= (n) << TVE_CBCRCFG_CRBA_BIT; \ +} while(0) +/* n = 0 ~ 255 */ +#define __tve_set_cb_gain_amp(n) \ +do { \ + REG_TVE_CBCRCFG &= ~TVE_CBCRCFG_CBGAIN_MASK; \ + REG_TVE_CBCRCFG |= (n) << TVE_CBCRCFG_CBGAIN_BIT; \ +} while(0) +/* n = 0 ~ 255 */ +#define __tve_set_cr_gain_amp(n) \ +do { \ + REG_TVE_CBCRCFG &= ~TVE_CBCRCFG_CRGAIN_MASK; \ + REG_TVE_CBCRCFG |= (n) << TVE_CBCRCFG_CRGAIN_BIT; \ +} while(0) + +/* TV Encoder Wide Screen Signal Control register ops */ +/* n = 0 ~ 7 */ +#define __tve_set_notch_freq(n) \ +do { \ + REG_TVE_WSSCR &= ~TVE_WSSCR_NCHFREQ_MASK; \ + REG_TVE_WSSCR |= (n) << TVE_WSSCR_NCHFREQ_BIT; \ +} while(0) +/* n = 0 ~ 7 */ +#define __tve_set_notch_width() (REG_TVE_WSSCR |= TVE_WSSCR_NCHW_BIT) +#define __tve_clear_notch_width() (REG_TVE_WSSCR &= ~TVE_WSSCR_NCHW_BIT) +#define __tve_enable_notch() (REG_TVE_WSSCR |= TVE_WSSCR_ENCH_BIT) +#define __tve_disable_notch() (REG_TVE_WSSCR &= ~TVE_WSSCR_ENCH_BIT) +/* n = 0 ~ 7 */ +#define __tve_set_wss_edge(n) \ +do { \ + REG_TVE_WSSCR &= ~TVE_WSSCR_WSSEDGE_MASK; \ + REG_TVE_WSSCR |= (n) << TVE_WSSCR_WSSEDGE_BIT; \ +} while(0) +#define __tve_set_wss_clkbyp() (REG_TVE_WSSCR |= TVE_WSSCR_WSSCKBP_BIT) +#define __tve_set_wss_type() (REG_TVE_WSSCR |= TVE_WSSCR_WSSTP_BIT) +#define __tve_enable_wssf1() (REG_TVE_WSSCR |= TVE_WSSCR_EWSS1_BIT) +#define __tve_enable_wssf0() (REG_TVE_WSSCR |= TVE_WSSCR_EWSS0_BIT) + +/* TV Encoder Wide Screen Signal Configure register 1, 2 and 3 ops */ +/* n = 0 ~ 1023 */ +#define __tve_set_wss_level(n) \ +do { \ + REG_TVE_WSSCFG1 &= ~TVE_WSSCFG1_WSSL_MASK; \ + REG_TVE_WSSCFG1 |= (n) << TVE_WSSCFG1_WSSL_BIT; \ +} while(0) +/* n = 0 ~ 4095 */ +#define __tve_set_wss_freq(n) \ +do { \ + REG_TVE_WSSCFG1 &= ~TVE_WSSCFG1_WSSFREQ_MASK; \ + REG_TVE_WSSCFG1 |= (n) << TVE_WSSCFG1_WSSFREQ_BIT; \ +} while(0) +/* n = 0, 1; l = 0 ~ 255 */ +#define __tve_set_wss_line(n,v) \ +do { \ + REG_TVE_WSSCFG##n &= ~TVE_WSSCFG_WSSLINE_MASK; \ + REG_TVE_WSSCFG##n |= (v) << TVE_WSSCFG_WSSLINE_BIT; \ +} while(0) +/* n = 0, 1; d = 0 ~ (2^20-1) */ +#define __tve_set_wss_data(n, v) \ +do { \ + REG_TVE_WSSCFG##n &= ~TVE_WSSCFG_WSSLINE_MASK; \ + REG_TVE_WSSCFG##n |= (v) << TVE_WSSCFG_WSSLINE_BIT; \ +} while(0) + +/*************************************************************************** + * RTC ops + ***************************************************************************/ + +#define __rtc_write_ready() ( (REG_RTC_RCR & RTC_RCR_WRDY) >> RTC_RCR_WRDY_BIT ) +#define __rtc_enabled() ( REG_RTC_RCR |= RTC_RCR_RTCE ) +#define __rtc_disabled() ( REG_RTC_RCR &= ~RTC_RCR_RTCE ) +#define __rtc_enable_alarm() ( REG_RTC_RCR |= RTC_RCR_AE ) +#define __rtc_disable_alarm() ( REG_RTC_RCR &= ~RTC_RCR_AE ) +#define __rtc_enable_alarm_irq() ( REG_RTC_RCR |= RTC_RCR_AIE ) +#define __rtc_disable_alarm_irq() ( REG_RTC_RCR &= ~RTC_RCR_AIE ) +#define __rtc_enable_1Hz_irq() ( REG_RTC_RCR |= RTC_RCR_1HZIE ) +#define __rtc_disable_1Hz_irq() ( REG_RTC_RCR &= ~RTC_RCR_1HZIE ) + +#define __rtc_get_1Hz_flag() ( (REG_RTC_RCR >> RTC_RCR_1HZ_BIT) & 0x1 ) +#define __rtc_clear_1Hz_flag() ( REG_RTC_RCR &= ~RTC_RCR_1HZ ) +#define __rtc_get_alarm_flag() ( (REG_RTC_RCR >> RTC_RCR_AF_BIT) & 0x1 ) +#define __rtc_clear_alarm_flag() ( REG_RTC_RCR &= ~RTC_RCR_AF ) + +#define __rtc_get_second() ( REG_RTC_RSR ) +#define __rtc_set_second(v) ( REG_RTC_RSR = v ) + +#define __rtc_get_alarm_second() ( REG_RTC_RSAR ) +#define __rtc_set_alarm_second(v) ( REG_RTC_RSAR = v ) + +#define __rtc_RGR_is_locked() ( (REG_RTC_RGR >> RTC_RGR_LOCK) ) +#define __rtc_lock_RGR() ( REG_RTC_RGR |= RTC_RGR_LOCK ) +#define __rtc_unlock_RGR() ( REG_RTC_RGR &= ~RTC_RGR_LOCK ) +#define __rtc_get_adjc_val() ( (REG_RTC_RGR & RTC_RGR_ADJC_MASK) >> RTC_RGR_ADJC_BIT ) +#define __rtc_set_adjc_val(v) \ + ( REG_RTC_RGR = ( (REG_RTC_RGR & ~RTC_RGR_ADJC_MASK) | (v << RTC_RGR_ADJC_BIT) )) +#define __rtc_get_nc1Hz_val() ( (REG_RTC_RGR & RTC_RGR_NC1HZ_MASK) >> RTC_RGR_NC1HZ_BIT ) +#define __rtc_set_nc1Hz_val(v) \ + ( REG_RTC_RGR = ( (REG_RTC_RGR & ~RTC_RGR_NC1HZ_MASK) | (v << RTC_RGR_NC1HZ_BIT) )) + +#define __rtc_power_down() ( REG_RTC_HCR |= RTC_HCR_PD ) + +#define __rtc_get_hwfcr_val() ( REG_RTC_HWFCR & RTC_HWFCR_MASK ) +#define __rtc_set_hwfcr_val(v) ( REG_RTC_HWFCR = (v) & RTC_HWFCR_MASK ) +#define __rtc_get_hrcr_val() ( REG_RTC_HRCR & RTC_HRCR_MASK ) +#define __rtc_set_hrcr_val(v) ( REG_RTC_HRCR = (v) & RTC_HRCR_MASK ) + +#define __rtc_enable_alarm_wakeup() ( REG_RTC_HWCR |= RTC_HWCR_EALM ) +#define __rtc_disable_alarm_wakeup() ( REG_RTC_HWCR &= ~RTC_HWCR_EALM ) + +#define __rtc_status_hib_reset_occur() ( (REG_RTC_HWRSR >> RTC_HWRSR_HR) & 0x1 ) +#define __rtc_status_ppr_reset_occur() ( (REG_RTC_HWRSR >> RTC_HWRSR_PPR) & 0x1 ) +#define __rtc_status_wakeup_pin_waken_up() ( (REG_RTC_HWRSR >> RTC_HWRSR_PIN) & 0x1 ) +#define __rtc_status_alarm_waken_up() ( (REG_RTC_HWRSR >> RTC_HWRSR_ALM) & 0x1 ) +#define __rtc_clear_hib_stat_all() ( REG_RTC_HWRSR = 0 ) + +#define __rtc_get_scratch_pattern() (REG_RTC_HSPR) +#define __rtc_set_scratch_pattern(n) (REG_RTC_HSPR = n ) + +/************************************************************************* + * BCH + *************************************************************************/ +#define __ecc_encoding_4bit() \ +do { \ + REG_BCH_CRS = BCH_CR_ENCE | BCH_CR_BRST | BCH_CR_BCHE; \ + REG_BCH_CRC = BCH_CR_BSEL8; \ +} while(0) +#define __ecc_decoding_4bit() \ +do { \ + REG_BCH_CRS = BCH_CR_BRST | BCH_CR_BCHE; \ + REG_BCH_CRC = BCH_CR_ENCE | BCH_CR_BSEL8; \ +} while(0) +#define __ecc_encoding_8bit() \ +do { \ + REG_BCH_CRS = BCH_CR_ENCE | BCH_CR_BRST | BCH_CR_BSEL8 | BCH_CR_BCHE; \ +} while(0) +#define __ecc_decoding_8bit() \ +do { \ + REG_BCH_CRS = BCH_CR_BRST | BCH_CR_BSEL8 | BCH_CR_BCHE; \ + REG_BCH_CRC = BCH_CR_ENCE; \ +} while(0) +#define __ecc_dma_enable() ( REG_BCH_CRS = BCH_CR_DMAE ) +#define __ecc_dma_disable() ( REG_BCH_CRC = BCH_CR_DMAE ) +#define __ecc_disable() ( REG_BCH_CRC = BCH_CR_BCHE ) +#define __ecc_encode_sync() while (!(REG_BCH_INTS & BCH_INTS_ENCF)) +#define __ecc_decode_sync() while (!(REG_BCH_INTS & BCH_INTS_DECF)) +#define __ecc_cnt_dec(n) \ +do { \ + REG_BCH_CNT &= ~(BCH_CNT_DEC_MASK << BCH_CNT_DEC_BIT); \ + REG_BCH_CNT = (n) << BCH_CNT_DEC_BIT; \ +} while(0) +#define __ecc_cnt_enc(n) \ +do { \ + REG_BCH_CNT &= ~(BCH_CNT_ENC_MASK << BCH_CNT_ENC_BIT); \ + REG_BCH_CNT = (n) << BCH_CNT_ENC_BIT; \ +} while(0) + +/*************************************************************************** + * OWI (one-wire bus) ops + ***************************************************************************/ + +/* OW control register ops */ +#define __owi_enable_all_interrupts() ( REG_OWI_CTL = (OWI_CTL_EBYTE | OWI_CTL_EBIT | OWI_CTL_ERST) ) +#define __owi_disable_all_interrupts() ( REG_OWI_CTL = 0 ) + +#define __owi_enable_byte_interrupt() ( REG_OWI_CTL |= OWI_CTL_EBYTE ) +#define __owi_disable_byte_interrupt() ( REG_OWI_CTL &= ~OWI_CTL_EBYTE ) +#define __owi_enable_bit_interrupt() ( REG_OWI_CTL |= OWI_CTL_EBIT ) +#define __owi_disable_bit_interrupt() ( REG_OWI_CTL &= ~OWI_CTL_EBIT ) +#define __owi_enable_rst_interrupt() ( REG_OWI_CTL |= OWI_CTL_ERST ) +#define __owi_disable_rst_interrupt() ( REG_OWI_CTL &=~OWI_CTL_ERST ) + +/* OW configure register ops */ +#define __owi_select_regular_mode() ( REG_OWI_CFG &= ~OWI_CFG_MODE ) +#define __owi_select_overdrive_mode() ( REG_OWI_CFG |= OWI_CFG_MODE ) + +#define __owi_set_rddata() ( REG_OWI_CFG |= OWI_CFG_RDDATA ) +#define __owi_clr_rddata() ( REG_OWI_CFG &= ~OWI_CFG_RDDATA ) +#define __owi_get_rddata() ( REG_OWI_CFG & OWI_CFG_RDDATA ) + +#define __owi_set_wrdata() ( REG_OWI_CFG |= OWI_CFG_WRDATA ) +#define __owi_clr_wrdata() ( REG_OWI_CFG &= ~OWI_CFG_WRDATA ) +#define __owi_get_wrdata() ( REG_OWI_CFG & OWI_CFG_WRDATA ) + +#define __owi_get_rdst() ( REG_OWI_CFG & OWI_CFG_RDST ) + +#define __owi_set_wr1rd() ( REG_OWI_CFG |= OWI_CFG_WR1RD ) +#define __owi_clr_wr1rd() ( REG_OWI_CFG &= ~OWI_CFG_WR1RD ) +#define __owi_get_wr1rd() ( REG_OWI_CFG & OWI_CFG_WR1RD ) + +#define __owi_set_wr0() ( REG_OWI_CFG |= OWI_CFG_WR0 ) +#define __owi_clr_wr0() ( REG_OWI_CFG &= ~OWI_CFG_WR0 ) +#define __owi_get_wr0() ( REG_OWI_CFG & OWI_CFG_WR0 ) + +#define __owi_set_rst() ( REG_OWI_CFG |= OWI_CFG_RST ) +#define __owi_clr_rst() ( REG_OWI_CFG &= ~OWI_CFG_RST ) +#define __owi_get_rst() ( REG_OWI_CFG & OWI_CFG_RST ) + +#define __owi_enable_ow_ops() ( REG_OWI_CFG |= OWI_CFG_ENA ) +#define __owi_disable_ow_ops() ( REG_OWI_CFG &= ~OWI_CFG_ENA ) +#define __owi_get_enable() ( REG_OWI_CFG & OWI_CFG_ENA ) + +#define __owi_wait_ops_rdy() \ + do { \ + while(__owi_get_enable()); \ + udelay(1); \ + } while(0); + +/* OW status register ops */ +#define __owi_clr_sts() ( REG_OWI_STS = 0 ) +#define __owi_get_sts_pst() ( REG_OWI_STS & OWI_STS_PST ) +#define __owi_get_sts_byte_rdy() ( REG_OWI_STS & OWI_STS_BYTE_RDY ) +#define __owi_get_sts_bit_rdy() ( REG_OWI_STS & OWI_STS_BIT_RDY ) +#define __owi_get_sts_pst_rdy() ( REG_OWI_STS & OWI_STS_PST_RDY ) + +/************************************************************************* + * TSSI MPEG 2-TS slave interface operation + *************************************************************************/ +#define __tssi_enable() ( REG_TSSI_ENA |= TSSI_ENA_ENA ) +#define __tssi_disable() ( REG_TSSI_ENA &= ~TSSI_ENA_ENA ) +#define __tssi_soft_reset() ( REG_TSSI_ENA |= TSSI_ENA_SFT_RST ) +#define __tssi_dma_enable() ( REG_TSSI_ENA |= TSSI_ENA_DMA_EN ) +#define __tssi_dma_disable() ( REG_TSSI_ENA &= ~TSSI_ENA_DMA_EN ) +#define __tssi_filter_enable() ( REG_TSSI_ENA |= TSSI_ENA_PID_EN ) +#define __tssi_filter_disable() ( REG_TSSI_ENA &= ~TSSI_ENA_PID_EN ) + +/* n = 4, 8, 16 */ +#define __tssi_set_tigger_num(n) \ + do { \ + REG_TSSI_CFG &= ~TSSI_CFG_TRIG_MASK; \ + REG_TSSI_CFG |= TSSI_CFG_TRIG_##n; \ + } while (0) + +#define __tssi_set_wd_1() ( REG_TSSI_CFG |= TSSI_CFG_END_WD ) +#define __tssi_set_wd_0() ( REG_TSSI_CFG &= ~TSSI_CFG_END_WD ) + +#define __tssi_set_bt_1() ( REG_TSSI_CFG |= TSSI_CFG_END_BD ) +#define __tssi_set_bt_0() ( REG_TSSI_CFG &= ~TSSI_CFG_END_BD ) + +#define __tssi_set_data_pola_high() ( REG_TSSI_CFG |= TSSI_CFG_TSDI_H ) +#define __tssi_set_data_pola_low() ( REG_TSSI_CFG &= ~TSSI_CFG_TSDI_H ) + +#define __tssi_set_data_use_data0() ( REG_TSSI_CFG |= TSSI_CFG_USE_0 ) +#define __tssi_set_data_use_data7() ( REG_TSSI_CFG &= ~TSSI_CFG_USE_0 ) + +#define __tssi_select_clk_fast() ( REG_TSSI_CFG &= ~TSSI_CFG_TSCLK_CH ) +#define __tssi_select_clk_slow() ( REG_TSSI_CFG |= TSSI_CFG_TSCLK_CH ) + +#define __tssi_select_serail_mode() ( REG_TSSI_CFG &= ~TSSI_CFG_PARAL ) +#define __tssi_select_paral_mode() ( REG_TSSI_CFG |= TSSI_CFG_PARAL ) + +#define __tssi_select_clk_nega_edge() ( REG_TSSI_CFG &= ~TSSI_CFG_TSCLK_P ) +#define __tssi_select_clk_posi_edge() ( REG_TSSI_CFG |= TSSI_CFG_TSCLK_P ) + +#define __tssi_select_frm_act_high() ( REG_TSSI_CFG |= TSSI_CFG_TSFRM_H ) +#define __tssi_select_frm_act_low() ( REG_TSSI_CFG &= ~TSSI_CFG_TSFRM_H ) + +#define __tssi_select_str_act_high() ( REG_TSSI_CFG |= TSSI_CFG_TSSTR_H ) +#define __tssi_select_str_act_low() ( REG_TSSI_CFG &= ~TSSI_CFG_TSSTR_H ) + +#define __tssi_select_fail_act_high() ( REG_TSSI_CFG |= TSSI_CFG_TSFAIL_H ) +#define __tssi_select_fail_act_low() ( REG_TSSI_CFG &= ~TSSI_CFG_TSFAIL_H ) + +#define __tssi_enable_ovrn_irq() ( REG_TSSI_CTRL &= ~TSSI_CTRL_OVRNM ) +#define __tssi_disable_ovrn_irq() ( REG_TSSI_CTRL |= TSSI_CTRL_OVRNM ) + +#define __tssi_enable_trig_irq() ( REG_TSSI_CTRL &= ~TSSI_CTRL_TRIGM ) +#define __tssi_disable_trig_irq() ( REG_TSSI_CTRL |= TSSI_CTRL_TRIGM ) + +#define __tssi_state_is_overrun() ( REG_TSSI_STAT & TSSI_STAT_OVRN ) +#define __tssi_state_trigger_meet() ( REG_TSSI_STAT & TSSI_STAT_TRIG ) +#define __tssi_clear_state() ( REG_TSSI_STAT = 0 ) /* write 0??? */ +#define __tssi_state_clear_overrun() ( REG_TSSI_STAT = TSSI_STAT_OVRN ) + +#define __tssi_enable_filte_pid0() ( REG_TSSI_PEN |= TSSI_PEN_PID0 ) +#define __tssi_disable_filte_pid0() ( REG_TSSI_PEN &= ~TSSI_PEN_PID0 ) + +/* m = 0, ..., 15 */ +#define __tssi_enable_pid_filter(m) \ + do { \ + int n = (m); \ + if ( n>=0 && n <(TSSI_PID_MAX*2) ) { \ + if ( n >= TSSI_PID_MAX ) n += 8; \ + REG_TSSI_PEN |= ( 1 << n ); \ + } \ + } while (0) + +/* m = 0, ..., 15 */ +#define __tssi_disable_pid_filter(m) \ + do { \ + int n = (m); \ + if ( n>=0 && n <(TSSI_PID_MAX*2) ) { \ + if ( n >= TSSI_PID_MAX ) n += 8; \ + REG_TSSI_PEN &= ~( 1 << n ); \ + } \ + } while (0) + +/* n = 0, ..., 7 */ +#define __tssi_set_pid0(n, pid0) \ + do { \ + REG_TSSI_PID(n) &= ~TSSI_PID_PID0_MASK; \ + REG_TSSI_PID(n) |= ((pid0)<=0 && n < TSSI_PID_MAX*2) { \ + if ( n < TSSI_PID_MAX ) \ + __tssi_set_pid0(n, pid); \ + else \ + __tssi_set_pid1(n-TSSI_PID_MAX, pid); \ + } \ + }while (0) + + +#if 0 +/************************************************************************* + * IPU (Image Processing Unit) + *************************************************************************/ +#define u32 volatile unsigned long + +#define write_reg(reg, val) \ +do { \ + *(u32 *)(reg) = (val); \ +} while(0) + +#define read_reg(reg, off) (*(u32 *)((reg)+(off))) + + +#define set_ipu_fmt(rgb_888_out_fmt, rgb_out_oft, out_fmt, yuv_pkg_out, in_oft, in_fmt ) \ +({ write_reg( (IPU_V_BASE + REG_D_FMT), ((in_fmt) & IN_FMT_MSK)< Unsigned toggle enable */ +#define AIC_CR_FLUSH (1 << 8) /* Flush FIFO */ +#define AIC_CR_EROR (1 << 6) /* Enable ROR interrupt */ +#define AIC_CR_ETUR (1 << 5) /* Enable TUR interrupt */ +#define AIC_CR_ERFS (1 << 4) /* Enable RFS interrupt */ +#define AIC_CR_ETFS (1 << 3) /* Enable TFS interrupt */ +#define AIC_CR_ENLBF (1 << 2) /* Enable Loopback Function */ +#define AIC_CR_ERPL (1 << 1) /* Enable Playback Function */ +#define AIC_CR_EREC (1 << 0) /* Enable Record Function */ + +/* AIC Controller AC-link Control Register 1 (AIC_ACCR1) */ + +#define AIC_ACCR1_RS_BIT 16 /* Receive Valid Slots */ +#define AIC_ACCR1_RS_MASK (0x3ff << AIC_ACCR1_RS_BIT) + #define AIC_ACCR1_RS_SLOT12 (1 << 25) /* Slot 12 valid bit */ + #define AIC_ACCR1_RS_SLOT11 (1 << 24) /* Slot 11 valid bit */ + #define AIC_ACCR1_RS_SLOT10 (1 << 23) /* Slot 10 valid bit */ + #define AIC_ACCR1_RS_SLOT9 (1 << 22) /* Slot 9 valid bit, LFE */ + #define AIC_ACCR1_RS_SLOT8 (1 << 21) /* Slot 8 valid bit, Surround Right */ + #define AIC_ACCR1_RS_SLOT7 (1 << 20) /* Slot 7 valid bit, Surround Left */ + #define AIC_ACCR1_RS_SLOT6 (1 << 19) /* Slot 6 valid bit, PCM Center */ + #define AIC_ACCR1_RS_SLOT5 (1 << 18) /* Slot 5 valid bit */ + #define AIC_ACCR1_RS_SLOT4 (1 << 17) /* Slot 4 valid bit, PCM Right */ + #define AIC_ACCR1_RS_SLOT3 (1 << 16) /* Slot 3 valid bit, PCM Left */ +#define AIC_ACCR1_XS_BIT 0 /* Transmit Valid Slots */ +#define AIC_ACCR1_XS_MASK (0x3ff << AIC_ACCR1_XS_BIT) + #define AIC_ACCR1_XS_SLOT12 (1 << 9) /* Slot 12 valid bit */ + #define AIC_ACCR1_XS_SLOT11 (1 << 8) /* Slot 11 valid bit */ + #define AIC_ACCR1_XS_SLOT10 (1 << 7) /* Slot 10 valid bit */ + #define AIC_ACCR1_XS_SLOT9 (1 << 6) /* Slot 9 valid bit, LFE */ + #define AIC_ACCR1_XS_SLOT8 (1 << 5) /* Slot 8 valid bit, Surround Right */ + #define AIC_ACCR1_XS_SLOT7 (1 << 4) /* Slot 7 valid bit, Surround Left */ + #define AIC_ACCR1_XS_SLOT6 (1 << 3) /* Slot 6 valid bit, PCM Center */ + #define AIC_ACCR1_XS_SLOT5 (1 << 2) /* Slot 5 valid bit */ + #define AIC_ACCR1_XS_SLOT4 (1 << 1) /* Slot 4 valid bit, PCM Right */ + #define AIC_ACCR1_XS_SLOT3 (1 << 0) /* Slot 3 valid bit, PCM Left */ + +/* AIC Controller AC-link Control Register 2 (AIC_ACCR2) */ + +#define AIC_ACCR2_ERSTO (1 << 18) /* Enable RSTO interrupt */ +#define AIC_ACCR2_ESADR (1 << 17) /* Enable SADR interrupt */ +#define AIC_ACCR2_ECADT (1 << 16) /* Enable CADT interrupt */ +#define AIC_ACCR2_OASS_BIT 8 /* Output Sample Size for AC-link */ +#define AIC_ACCR2_OASS_MASK (0x3 << AIC_ACCR2_OASS_BIT) + #define AIC_ACCR2_OASS_20BIT (0 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 20-bit */ + #define AIC_ACCR2_OASS_18BIT (1 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 18-bit */ + #define AIC_ACCR2_OASS_16BIT (2 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 16-bit */ + #define AIC_ACCR2_OASS_8BIT (3 << AIC_ACCR2_OASS_BIT) /* Output Audio Sample Size is 8-bit */ +#define AIC_ACCR2_IASS_BIT 6 /* Output Sample Size for AC-link */ +#define AIC_ACCR2_IASS_MASK (0x3 << AIC_ACCR2_IASS_BIT) + #define AIC_ACCR2_IASS_20BIT (0 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 20-bit */ + #define AIC_ACCR2_IASS_18BIT (1 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 18-bit */ + #define AIC_ACCR2_IASS_16BIT (2 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 16-bit */ + #define AIC_ACCR2_IASS_8BIT (3 << AIC_ACCR2_IASS_BIT) /* Input Audio Sample Size is 8-bit */ +#define AIC_ACCR2_SO (1 << 3) /* SDATA_OUT output value */ +#define AIC_ACCR2_SR (1 << 2) /* RESET# pin level */ +#define AIC_ACCR2_SS (1 << 1) /* SYNC pin level */ +#define AIC_ACCR2_SA (1 << 0) /* SYNC and SDATA_OUT alternation */ + +/* AIC Controller I2S/MSB-justified Control Register (AIC_I2SCR) */ + +#define AIC_I2SCR_STPBK (1 << 12) /* Stop BIT_CLK for I2S/MSB-justified */ +#define AIC_I2SCR_WL_BIT 1 /* Input/Output Sample Size for I2S/MSB-justified */ +#define AIC_I2SCR_WL_MASK (0x7 << AIC_I2SCR_WL_BIT) + #define AIC_I2SCR_WL_24BIT (0 << AIC_I2SCR_WL_BIT) /* Word Length is 24 bit */ + #define AIC_I2SCR_WL_20BIT (1 << AIC_I2SCR_WL_BIT) /* Word Length is 20 bit */ + #define AIC_I2SCR_WL_18BIT (2 << AIC_I2SCR_WL_BIT) /* Word Length is 18 bit */ + #define AIC_I2SCR_WL_16BIT (3 << AIC_I2SCR_WL_BIT) /* Word Length is 16 bit */ + #define AIC_I2SCR_WL_8BIT (4 << AIC_I2SCR_WL_BIT) /* Word Length is 8 bit */ +#define AIC_I2SCR_AMSL (1 << 0) /* 0:I2S, 1:MSB-justified */ + +/* AIC Controller FIFO Status Register (AIC_SR) */ + +#define AIC_SR_RFL_BIT 24 /* Receive FIFO Level */ +#define AIC_SR_RFL_MASK (0x3f << AIC_SR_RFL_BIT) +#define AIC_SR_TFL_BIT 8 /* Transmit FIFO level */ +#define AIC_SR_TFL_MASK (0x3f << AIC_SR_TFL_BIT) +#define AIC_SR_ROR (1 << 6) /* Receive FIFO Overrun */ +#define AIC_SR_TUR (1 << 5) /* Transmit FIFO Underrun */ +#define AIC_SR_RFS (1 << 4) /* Receive FIFO Service Request */ +#define AIC_SR_TFS (1 << 3) /* Transmit FIFO Service Request */ + +/* AIC Controller AC-link Status Register (AIC_ACSR) */ + +#define AIC_ACSR_SLTERR (1 << 21) /* Slot Error Flag */ +#define AIC_ACSR_CRDY (1 << 20) /* External CODEC Ready Flag */ +#define AIC_ACSR_CLPM (1 << 19) /* External CODEC low power mode flag */ +#define AIC_ACSR_RSTO (1 << 18) /* External CODEC regs read status timeout */ +#define AIC_ACSR_SADR (1 << 17) /* External CODEC regs status addr and data received */ +#define AIC_ACSR_CADT (1 << 16) /* Command Address and Data Transmitted */ + +/* AIC Controller I2S/MSB-justified Status Register (AIC_I2SSR) */ + +#define AIC_I2SSR_BSY (1 << 2) /* AIC Busy in I2S/MSB-justified format */ + +/* AIC Controller AC97 codec Command Address Register (AIC_ACCAR) */ + +#define AIC_ACCAR_CAR_BIT 0 +#define AIC_ACCAR_CAR_MASK (0xfffff << AIC_ACCAR_CAR_BIT) + +/* AIC Controller AC97 codec Command Data Register (AIC_ACCDR) */ + +#define AIC_ACCDR_CDR_BIT 0 +#define AIC_ACCDR_CDR_MASK (0xfffff << AIC_ACCDR_CDR_BIT) + +/* AIC Controller AC97 codec Status Address Register (AIC_ACSAR) */ + +#define AIC_ACSAR_SAR_BIT 0 +#define AIC_ACSAR_SAR_MASK (0xfffff << AIC_ACSAR_SAR_BIT) + +/* AIC Controller AC97 codec Status Data Register (AIC_ACSDR) */ + +#define AIC_ACSDR_SDR_BIT 0 +#define AIC_ACSDR_SDR_MASK (0xfffff << AIC_ACSDR_SDR_BIT) + +/* AIC Controller I2S/MSB-justified Clock Divider Register (AIC_I2SDIV) */ + +#define AIC_I2SDIV_DIV_BIT 0 +#define AIC_I2SDIV_DIV_MASK (0x7f << AIC_I2SDIV_DIV_BIT) + #define AIC_I2SDIV_BITCLK_3072KHZ (0x0C << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 3.072MHz */ + #define AIC_I2SDIV_BITCLK_2836KHZ (0x0D << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 2.836MHz */ + #define AIC_I2SDIV_BITCLK_1418KHZ (0x1A << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 1.418MHz */ + #define AIC_I2SDIV_BITCLK_1024KHZ (0x24 << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 1.024MHz */ + #define AIC_I2SDIV_BITCLK_7089KHZ (0x34 << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 708.92KHz */ + #define AIC_I2SDIV_BITCLK_512KHZ (0x48 << AIC_I2SDIV_DIV_BIT) /* BIT_CLK of 512.00KHz */ + + +/************************************************************************* + * ICDC (Internal CODEC) + *************************************************************************/ + +#define ICDC_CKCFG (ICDC_BASE + 0x00a0) /* Clock Configure Register */ +#define ICDC_RGADW (ICDC_BASE + 0x00a4) /* internal register access control */ +#define ICDC_RGDATA (ICDC_BASE + 0x00a8) /* internal register data output */ + +#define REG_ICDC_CKCFG REG32(ICDC_CKCFG) +#define REG_ICDC_RGADW REG32(ICDC_RGADW) +#define REG_ICDC_RGDATA REG32(ICDC_RGDATA) + +/* ICDC Clock Configure Register */ +#define ICDC_CKCFG_CKRDY (1 << 1) +#define ICDC_CKCFG_SELAD (1 << 0) + +/* ICDC internal register access control Register */ +#define ICDC_RGADW_RGWR (1 << 16) +#define ICDC_RGADW_RGADDR_BIT 8 +#define ICDC_RGADW_RGADDR_MASK (0x7f << ICDC_RGADW_RGADDR_BIT) +#define ICDC_RGADW_RGDIN_BIT 0 +#define ICDC_RGADW_RGDIN_MASK (0xff << ICDC_RGADW_RGDIN_BIT) + +/* ICDC internal register data output Register */ +#define ICDC_RGDATA_IRQ (1 << 8) +#define ICDC_RGDATA_RGDOUT_BIT 0 +#define ICDC_RGDATA_RGDOUT_MASK (0xff << ICDC_RGDATA_RGDOUT_BIT) + +/************************************************************************* + * PCM Controller + *************************************************************************/ + +#define PCM_CTL (PCM_BASE + 0x000) +#define PCM_CFG (PCM_BASE + 0x004) +#define PCM_DP (PCM_BASE + 0x008) +#define PCM_INTC (PCM_BASE + 0x00c) +#define PCM_INTS (PCM_BASE + 0x010) +#define PCM_DIV (PCM_BASE + 0x014) + +#define REG_PCM_CTL REG32(PCM_CTL) +#define REG_PCM_CFG REG32(PCM_CFG) +#define REG_PCM_DP REG32(PCM_DP) +#define REG_PCM_INTC REG32(PCM_INTC) +#define REG_PCM_INTS REG32(PCM_INTS) +#define REG_PCM_DIV REG32(PCM_DIV) + +/* PCM Controller control Register (PCM_CTL) */ + +#define PCM_CTL_ERDMA (1 << 9) /* Enable Receive DMA */ +#define PCM_CTL_ETDMA (1 << 8) /* Enable Transmit DMA */ +#define PCM_CTL_LSMP (1 << 7) /* Play Zero sample or last sample */ +#define PCM_CTL_ERPL (1 << 6) /* Enable Playing Back Function */ +#define PCM_CTL_EREC (1 << 5) /* Enable Recording Function */ +#define PCM_CTL_FLUSH (1 << 4) /* FIFO flush */ +#define PCM_CTL_RST (1 << 3) /* Reset PCM */ +#define PCM_CTL_CLKEN (1 << 1) /* Enable the clock division logic */ +#define PCM_CTL_PCMEN (1 << 0) /* Enable PCM module */ + +/* PCM Controller configure Register (PCM_CFG) */ + +#define PCM_CFG_SLOT_BIT 13 +#define PCM_CFG_SLOT_MASK (0x3 << PCM_CFG_SLOT_BIT) + #define PCM_CFG_SLOT_0 (0 << PCM_CFG_SLOT_BIT) /* Slot is 0 */ + #define PCM_CFG_SLOT_1 (1 << PCM_CFG_SLOT_BIT) /* Slot is 1 */ + #define PCM_CFG_SLOT_2 (2 << PCM_CFG_SLOT_BIT) /* Slot is 2 */ + #define PCM_CFG_SLOT_3 (3 << PCM_CFG_SLOT_BIT) /* Slot is 3 */ +#define PCM_CFG_ISS_BIT 12 +#define PCM_CFG_ISS_MASK (0x1 << PCM_CFG_ISS_BIT) + #define PCM_CFG_ISS_8 (0 << PCM_CFG_ISS_BIT) + #define PCM_CFG_ISS_16 (1 << PCM_CFG_ISS_BIT) +#define PCM_CFG_OSS_BIT 11 +#define PCM_CFG_OSS_MASK (0x1 << PCM_CFG_OSS_BIT) + #define PCM_CFG_OSS_8 (0 << PCM_CFG_OSS_BIT) + #define PCM_CFG_OSS_16 (1 << PCM_CFG_OSS_BIT) +#define PCM_CFG_IMSBPOS (1 << 10) +#define PCM_CFG_OMSBPOS (1 << 9) +#define PCM_CFG_RFTH_BIT 5 /* Receive FIFO Threshold */ +#define PCM_CFG_RFTH_MASK (0xf << PCM_CFG_RFTH_BIT) +#define PCM_CFG_TFTH_BIT 1 /* Transmit FIFO Threshold */ +#define PCM_CFG_TFTH_MASK (0xf << PCM_CFG_TFTH_BIT) +#define PCM_CFG_MODE (0x0 << 0) + +/* PCM Controller interrupt control Register (PCM_INTC) */ + +#define PCM_INTC_ETFS (1 << 3) +#define PCM_INTC_ETUR (1 << 2) +#define PCM_INTC_ERFS (1 << 1) +#define PCM_INTC_EROR (1 << 0) + +/* PCM Controller interrupt status Register (PCM_INTS) */ + +#define PCM_INTS_RSTS (1 << 14) /* Reset or flush has not complete */ +#define PCM_INTS_TFL_BIT 9 +#define PCM_INTS_TFL_MASK (0x1f << PCM_INTS_TFL_BIT) +#define PCM_INTS_TFS (1 << 8) /* Tranmit FIFO Service Request */ +#define PCM_INTS_TUR (1 << 7) /* Transmit FIFO Under Run */ +#define PCM_INTS_RFL_BIT 2 +#define PCM_INTS_RFL_MASK (0x1f << PCM_INTS_RFL_BIT) +#define PCM_INTS_RFS (1 << 1) /* Receive FIFO Service Request */ +#define PCM_INTS_ROR (1 << 0) /* Receive FIFO Over Run */ + +/* PCM Controller clock division Register (PCM_DIV) */ +#define PCM_DIV_SYNL_BIT 11 +#define PCM_DIV_SYNL_MASK (0x3f << PCM_DIV_SYNL_BIT) +#define PCM_DIV_SYNDIV_BIT 6 +#define PCM_DIV_SYNDIV_MASK (0x1f << PCM_DIV_SYNDIV_BIT) +#define PCM_DIV_CLKDIV_BIT 0 +#define PCM_DIV_CLKDIV_MASK (0x3f << PCM_DIV_CLKDIV_BIT) + + +/************************************************************************* + * I2C + *************************************************************************/ +#define I2C_DR (I2C_BASE + 0x000) +#define I2C_CR (I2C_BASE + 0x004) +#define I2C_SR (I2C_BASE + 0x008) +#define I2C_GR (I2C_BASE + 0x00C) + +#define REG_I2C_DR REG8(I2C_DR) +#define REG_I2C_CR REG8(I2C_CR) +#define REG_I2C_SR REG8(I2C_SR) +#define REG_I2C_GR REG16(I2C_GR) + +/* I2C Control Register (I2C_CR) */ + +#define I2C_CR_IEN (1 << 4) +#define I2C_CR_STA (1 << 3) +#define I2C_CR_STO (1 << 2) +#define I2C_CR_AC (1 << 1) +#define I2C_CR_I2CE (1 << 0) + +/* I2C Status Register (I2C_SR) */ + +#define I2C_SR_STX (1 << 4) +#define I2C_SR_BUSY (1 << 3) +#define I2C_SR_TEND (1 << 2) +#define I2C_SR_DRF (1 << 1) +#define I2C_SR_ACKF (1 << 0) + + +/************************************************************************* + * SSI (Synchronous Serial Interface) + *************************************************************************/ +/* n = 0, 1 (SSI0, SSI1) */ +#define SSI_DR(n) (SSI_BASE + 0x000 + (n)*0x2000) +#define SSI_CR0(n) (SSI_BASE + 0x004 + (n)*0x2000) +#define SSI_CR1(n) (SSI_BASE + 0x008 + (n)*0x2000) +#define SSI_SR(n) (SSI_BASE + 0x00C + (n)*0x2000) +#define SSI_ITR(n) (SSI_BASE + 0x010 + (n)*0x2000) +#define SSI_ICR(n) (SSI_BASE + 0x014 + (n)*0x2000) +#define SSI_GR(n) (SSI_BASE + 0x018 + (n)*0x2000) + +#define REG_SSI_DR(n) REG32(SSI_DR(n)) +#define REG_SSI_CR0(n) REG16(SSI_CR0(n)) +#define REG_SSI_CR1(n) REG32(SSI_CR1(n)) +#define REG_SSI_SR(n) REG32(SSI_SR(n)) +#define REG_SSI_ITR(n) REG16(SSI_ITR(n)) +#define REG_SSI_ICR(n) REG8(SSI_ICR(n)) +#define REG_SSI_GR(n) REG16(SSI_GR(n)) + +/* SSI Data Register (SSI_DR) */ + +#define SSI_DR_GPC_BIT 0 +#define SSI_DR_GPC_MASK (0x1ff << SSI_DR_GPC_BIT) + +#define SSI_MAX_FIFO_ENTRIES 128 /* 128 txfifo and 128 rxfifo */ + +/* SSI Control Register 0 (SSI_CR0) */ + +#define SSI_CR0_SSIE (1 << 15) +#define SSI_CR0_TIE (1 << 14) +#define SSI_CR0_RIE (1 << 13) +#define SSI_CR0_TEIE (1 << 12) +#define SSI_CR0_REIE (1 << 11) +#define SSI_CR0_LOOP (1 << 10) +#define SSI_CR0_RFINE (1 << 9) +#define SSI_CR0_RFINC (1 << 8) +#define SSI_CR0_EACLRUN (1 << 7) /* hardware auto clear underrun when TxFifo no empty */ +#define SSI_CR0_FSEL (1 << 6) +#define SSI_CR0_TFLUSH (1 << 2) +#define SSI_CR0_RFLUSH (1 << 1) +#define SSI_CR0_DISREV (1 << 0) + +/* SSI Control Register 1 (SSI_CR1) */ + +#define SSI_CR1_FRMHL_BIT 30 +#define SSI_CR1_FRMHL_MASK (0x3 << SSI_CR1_FRMHL_BIT) + #define SSI_CR1_FRMHL_CELOW_CE2LOW (0 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is low valid and SSI_CE2_ is low valid */ + #define SSI_CR1_FRMHL_CEHIGH_CE2LOW (1 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is high valid and SSI_CE2_ is low valid */ + #define SSI_CR1_FRMHL_CELOW_CE2HIGH (2 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is low valid and SSI_CE2_ is high valid */ + #define SSI_CR1_FRMHL_CEHIGH_CE2HIGH (3 << SSI_CR1_FRMHL_BIT) /* SSI_CE_ is high valid and SSI_CE2_ is high valid */ +#define SSI_CR1_TFVCK_BIT 28 +#define SSI_CR1_TFVCK_MASK (0x3 << SSI_CR1_TFVCK_BIT) + #define SSI_CR1_TFVCK_0 (0 << SSI_CR1_TFVCK_BIT) + #define SSI_CR1_TFVCK_1 (1 << SSI_CR1_TFVCK_BIT) + #define SSI_CR1_TFVCK_2 (2 << SSI_CR1_TFVCK_BIT) + #define SSI_CR1_TFVCK_3 (3 << SSI_CR1_TFVCK_BIT) +#define SSI_CR1_TCKFI_BIT 26 +#define SSI_CR1_TCKFI_MASK (0x3 << SSI_CR1_TCKFI_BIT) + #define SSI_CR1_TCKFI_0 (0 << SSI_CR1_TCKFI_BIT) + #define SSI_CR1_TCKFI_1 (1 << SSI_CR1_TCKFI_BIT) + #define SSI_CR1_TCKFI_2 (2 << SSI_CR1_TCKFI_BIT) + #define SSI_CR1_TCKFI_3 (3 << SSI_CR1_TCKFI_BIT) +#define SSI_CR1_LFST (1 << 25) +#define SSI_CR1_ITFRM (1 << 24) +#define SSI_CR1_UNFIN (1 << 23) +#define SSI_CR1_MULTS (1 << 22) +#define SSI_CR1_FMAT_BIT 20 +#define SSI_CR1_FMAT_MASK (0x3 << SSI_CR1_FMAT_BIT) + #define SSI_CR1_FMAT_SPI (0 << SSI_CR1_FMAT_BIT) /* Motorola¡¯s SPI format */ + #define SSI_CR1_FMAT_SSP (1 << SSI_CR1_FMAT_BIT) /* TI's SSP format */ + #define SSI_CR1_FMAT_MW1 (2 << SSI_CR1_FMAT_BIT) /* National Microwire 1 format */ + #define SSI_CR1_FMAT_MW2 (3 << SSI_CR1_FMAT_BIT) /* National Microwire 2 format */ +#define SSI_CR1_TTRG_BIT 16 /* SSI1 TX trigger */ +#define SSI_CR1_TTRG_MASK (0xf << SSI_CR1_TTRG_BIT) +#define SSI_CR1_MCOM_BIT 12 +#define SSI_CR1_MCOM_MASK (0xf << SSI_CR1_MCOM_BIT) + #define SSI_CR1_MCOM_1BIT (0x0 << SSI_CR1_MCOM_BIT) /* 1-bit command selected */ + #define SSI_CR1_MCOM_2BIT (0x1 << SSI_CR1_MCOM_BIT) /* 2-bit command selected */ + #define SSI_CR1_MCOM_3BIT (0x2 << SSI_CR1_MCOM_BIT) /* 3-bit command selected */ + #define SSI_CR1_MCOM_4BIT (0x3 << SSI_CR1_MCOM_BIT) /* 4-bit command selected */ + #define SSI_CR1_MCOM_5BIT (0x4 << SSI_CR1_MCOM_BIT) /* 5-bit command selected */ + #define SSI_CR1_MCOM_6BIT (0x5 << SSI_CR1_MCOM_BIT) /* 6-bit command selected */ + #define SSI_CR1_MCOM_7BIT (0x6 << SSI_CR1_MCOM_BIT) /* 7-bit command selected */ + #define SSI_CR1_MCOM_8BIT (0x7 << SSI_CR1_MCOM_BIT) /* 8-bit command selected */ + #define SSI_CR1_MCOM_9BIT (0x8 << SSI_CR1_MCOM_BIT) /* 9-bit command selected */ + #define SSI_CR1_MCOM_10BIT (0x9 << SSI_CR1_MCOM_BIT) /* 10-bit command selected */ + #define SSI_CR1_MCOM_11BIT (0xA << SSI_CR1_MCOM_BIT) /* 11-bit command selected */ + #define SSI_CR1_MCOM_12BIT (0xB << SSI_CR1_MCOM_BIT) /* 12-bit command selected */ + #define SSI_CR1_MCOM_13BIT (0xC << SSI_CR1_MCOM_BIT) /* 13-bit command selected */ + #define SSI_CR1_MCOM_14BIT (0xD << SSI_CR1_MCOM_BIT) /* 14-bit command selected */ + #define SSI_CR1_MCOM_15BIT (0xE << SSI_CR1_MCOM_BIT) /* 15-bit command selected */ + #define SSI_CR1_MCOM_16BIT (0xF << SSI_CR1_MCOM_BIT) /* 16-bit command selected */ +#define SSI_CR1_RTRG_BIT 8 /* SSI RX trigger */ +#define SSI_CR1_RTRG_MASK (0xf << SSI_CR1_RTRG_BIT) +#define SSI_CR1_FLEN_BIT 4 +#define SSI_CR1_FLEN_MASK (0xf << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_2BIT (0x0 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_3BIT (0x1 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_4BIT (0x2 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_5BIT (0x3 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_6BIT (0x4 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_7BIT (0x5 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_8BIT (0x6 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_9BIT (0x7 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_10BIT (0x8 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_11BIT (0x9 << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_12BIT (0xA << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_13BIT (0xB << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_14BIT (0xC << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_15BIT (0xD << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_16BIT (0xE << SSI_CR1_FLEN_BIT) + #define SSI_CR1_FLEN_17BIT (0xF << SSI_CR1_FLEN_BIT) +#define SSI_CR1_PHA (1 << 1) +#define SSI_CR1_POL (1 << 0) + +/* SSI Status Register (SSI_SR) */ + +#define SSI_SR_TFIFONUM_BIT 16 +#define SSI_SR_TFIFONUM_MASK (0xff << SSI_SR_TFIFONUM_BIT) +#define SSI_SR_RFIFONUM_BIT 8 +#define SSI_SR_RFIFONUM_MASK (0xff << SSI_SR_RFIFONUM_BIT) +#define SSI_SR_END (1 << 7) +#define SSI_SR_BUSY (1 << 6) +#define SSI_SR_TFF (1 << 5) +#define SSI_SR_RFE (1 << 4) +#define SSI_SR_TFHE (1 << 3) +#define SSI_SR_RFHF (1 << 2) +#define SSI_SR_UNDR (1 << 1) +#define SSI_SR_OVER (1 << 0) + +/* SSI Interval Time Control Register (SSI_ITR) */ + +#define SSI_ITR_CNTCLK (1 << 15) +#define SSI_ITR_IVLTM_BIT 0 +#define SSI_ITR_IVLTM_MASK (0x7fff << SSI_ITR_IVLTM_BIT) + + +/************************************************************************* + * MSC + ************************************************************************/ +/* n = 0, 1 (MSC0, MSC1) */ +#define MSC_STRPCL(n) (MSC_BASE + (n)*0x1000 + 0x000) +#define MSC_STAT(n) (MSC_BASE + (n)*0x1000 + 0x004) +#define MSC_CLKRT(n) (MSC_BASE + (n)*0x1000 + 0x008) +#define MSC_CMDAT(n) (MSC_BASE + (n)*0x1000 + 0x00C) +#define MSC_RESTO(n) (MSC_BASE + (n)*0x1000 + 0x010) +#define MSC_RDTO(n) (MSC_BASE + (n)*0x1000 + 0x014) +#define MSC_BLKLEN(n) (MSC_BASE + (n)*0x1000 + 0x018) +#define MSC_NOB(n) (MSC_BASE + (n)*0x1000 + 0x01C) +#define MSC_SNOB(n) (MSC_BASE + (n)*0x1000 + 0x020) +#define MSC_IMASK(n) (MSC_BASE + (n)*0x1000 + 0x024) +#define MSC_IREG(n) (MSC_BASE + (n)*0x1000 + 0x028) +#define MSC_CMD(n) (MSC_BASE + (n)*0x1000 + 0x02C) +#define MSC_ARG(n) (MSC_BASE + (n)*0x1000 + 0x030) +#define MSC_RES(n) (MSC_BASE + (n)*0x1000 + 0x034) +#define MSC_RXFIFO(n) (MSC_BASE + (n)*0x1000 + 0x038) +#define MSC_TXFIFO(n) (MSC_BASE + (n)*0x1000 + 0x03C) +#define MSC_LPM(n) (MSC_BASE + (n)*0x1000 + 0x040) + +#define REG_MSC_STRPCL(n) REG16(MSC_STRPCL(n)) +#define REG_MSC_STAT(n) REG32(MSC_STAT(n)) +#define REG_MSC_CLKRT(n) REG16(MSC_CLKRT(n)) +#define REG_MSC_CMDAT(n) REG32(MSC_CMDAT(n)) +#define REG_MSC_RESTO(n) REG16(MSC_RESTO(n)) +#define REG_MSC_RDTO(n) REG16(MSC_RDTO(n)) +#define REG_MSC_BLKLEN(n) REG16(MSC_BLKLEN(n)) +#define REG_MSC_NOB(n) REG16(MSC_NOB(n)) +#define REG_MSC_SNOB(n) REG16(MSC_SNOB(n)) +#define REG_MSC_IMASK(n) REG32(MSC_IMASK(n)) +#define REG_MSC_IREG(n) REG16(MSC_IREG(n)) +#define REG_MSC_CMD(n) REG8(MSC_CMD(n)) +#define REG_MSC_ARG(n) REG32(MSC_ARG(n)) +#define REG_MSC_RES(n) REG16(MSC_RES(n)) +#define REG_MSC_RXFIFO(n) REG32(MSC_RXFIFO(n)) +#define REG_MSC_TXFIFO(n) REG32(MSC_TXFIFO(n)) +#define REG_MSC_LPM(n) REG32(MSC_LPM(n)) + +/* MSC Clock and Control Register (MSC_STRPCL) */ +#define MSC_STRPCL_SEND_CCSD (1 << 15) /*send command completion signal disable to ceata */ +#define MSC_STRPCL_SEND_AS_CCSD (1 << 14) /*send internally generated stop after sending ccsd */ +#define MSC_STRPCL_EXIT_MULTIPLE (1 << 7) +#define MSC_STRPCL_EXIT_TRANSFER (1 << 6) +#define MSC_STRPCL_START_READWAIT (1 << 5) +#define MSC_STRPCL_STOP_READWAIT (1 << 4) +#define MSC_STRPCL_RESET (1 << 3) +#define MSC_STRPCL_START_OP (1 << 2) +#define MSC_STRPCL_CLOCK_CONTROL_BIT 0 +#define MSC_STRPCL_CLOCK_CONTROL_MASK (0x3 << MSC_STRPCL_CLOCK_CONTROL_BIT) + #define MSC_STRPCL_CLOCK_CONTROL_STOP (0x1 << MSC_STRPCL_CLOCK_CONTROL_BIT) /* Stop MMC/SD clock */ + #define MSC_STRPCL_CLOCK_CONTROL_START (0x2 << MSC_STRPCL_CLOCK_CONTROL_BIT) /* Start MMC/SD clock */ + +/* MSC Status Register (MSC_STAT) */ +#define MSC_STAT_AUTO_CMD_DONE (1 << 31) /*12 is internally generated by controller has finished */ +#define MSC_STAT_IS_RESETTING (1 << 15) +#define MSC_STAT_SDIO_INT_ACTIVE (1 << 14) +#define MSC_STAT_PRG_DONE (1 << 13) +#define MSC_STAT_DATA_TRAN_DONE (1 << 12) +#define MSC_STAT_END_CMD_RES (1 << 11) +#define MSC_STAT_DATA_FIFO_AFULL (1 << 10) +#define MSC_STAT_IS_READWAIT (1 << 9) +#define MSC_STAT_CLK_EN (1 << 8) +#define MSC_STAT_DATA_FIFO_FULL (1 << 7) +#define MSC_STAT_DATA_FIFO_EMPTY (1 << 6) +#define MSC_STAT_CRC_RES_ERR (1 << 5) +#define MSC_STAT_CRC_READ_ERROR (1 << 4) +#define MSC_STAT_CRC_WRITE_ERROR_BIT 2 +#define MSC_STAT_CRC_WRITE_ERROR_MASK (0x3 << MSC_STAT_CRC_WRITE_ERROR_BIT) + #define MSC_STAT_CRC_WRITE_ERROR_NO (0 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* No error on transmission of data */ + #define MSC_STAT_CRC_WRITE_ERROR (1 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* Card observed erroneous transmission of data */ + #define MSC_STAT_CRC_WRITE_ERROR_NOSTS (2 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* No CRC status is sent back */ +#define MSC_STAT_TIME_OUT_RES (1 << 1) +#define MSC_STAT_TIME_OUT_READ (1 << 0) + +/* MSC Bus Clock Control Register (MSC_CLKRT) */ +#define MSC_CLKRT_CLK_RATE_BIT 0 +#define MSC_CLKRT_CLK_RATE_MASK (0x7 << MSC_CLKRT_CLK_RATE_BIT) + #define MSC_CLKRT_CLK_RATE_DIV_1 (0x0 << MSC_CLKRT_CLK_RATE_BIT) /* CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_2 (0x1 << MSC_CLKRT_CLK_RATE_BIT) /* 1/2 of CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_4 (0x2 << MSC_CLKRT_CLK_RATE_BIT) /* 1/4 of CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_8 (0x3 << MSC_CLKRT_CLK_RATE_BIT) /* 1/8 of CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_16 (0x4 << MSC_CLKRT_CLK_RATE_BIT) /* 1/16 of CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_32 (0x5 << MSC_CLKRT_CLK_RATE_BIT) /* 1/32 of CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_64 (0x6 << MSC_CLKRT_CLK_RATE_BIT) /* 1/64 of CLK_SRC */ + #define MSC_CLKRT_CLK_RATE_DIV_128 (0x7 << MSC_CLKRT_CLK_RATE_BIT) /* 1/128 of CLK_SRC */ + +/* MSC Command Sequence Control Register (MSC_CMDAT) */ +#define MSC_CMDAT_CCS_EXPECTED (1 << 31) /* interrupts are enabled in ce-ata */ +#define MSC_CMDAT_READ_CEATA (1 << 30) +#define MSC_CMDAT_SDIO_PRDT (1 << 17) /* exact 2 cycle */ +#define MSC_CMDAT_SEND_AS_STOP (1 << 16) +#define MSC_CMDAT_RTRG_BIT 14 + #define MSC_CMDAT_RTRG_EQUALT_8 (0x0 << MSC_CMDAT_RTRG_BIT) + #define MSC_CMDAT_RTRG_EQUALT_16 (0x1 << MSC_CMDAT_RTRG_BIT) /* reset value */ + #define MSC_CMDAT_RTRG_EQUALT_24 (0x2 << MSC_CMDAT_RTRG_BIT) + +#define MSC_CMDAT_TTRG_BIT 12 + #define MSC_CMDAT_TTRG_LESS_8 (0x0 << MSC_CMDAT_TTRG_BIT) + #define MSC_CMDAT_TTRG_LESS_16 (0x1 << MSC_CMDAT_TTRG_BIT) /*reset value */ + #define MSC_CMDAT_TTRG_LESS_24 (0x2 << MSC_CMDAT_TTRG_BIT) +#define MSC_CMDAT_STOP_ABORT (1 << 11) +#define MSC_CMDAT_BUS_WIDTH_BIT 9 +#define MSC_CMDAT_BUS_WIDTH_MASK (0x3 << MSC_CMDAT_BUS_WIDTH_BIT) + #define MSC_CMDAT_BUS_WIDTH_1BIT (0x0 << MSC_CMDAT_BUS_WIDTH_BIT) /* 1-bit data bus */ + #define MSC_CMDAT_BUS_WIDTH_4BIT (0x2 << MSC_CMDAT_BUS_WIDTH_BIT) /* 4-bit data bus */ + #define MSC_CMDAT_BUS_WIDTH_8BIT (0x3 << MSC_CMDAT_BUS_WIDTH_BIT) /* 8-bit data bus */ +#define MSC_CMDAT_DMA_EN (1 << 8) +#define MSC_CMDAT_INIT (1 << 7) +#define MSC_CMDAT_BUSY (1 << 6) +#define MSC_CMDAT_STREAM_BLOCK (1 << 5) +#define MSC_CMDAT_WRITE (1 << 4) +#define MSC_CMDAT_READ (0 << 4) +#define MSC_CMDAT_DATA_EN (1 << 3) +#define MSC_CMDAT_RESPONSE_BIT 0 +#define MSC_CMDAT_RESPONSE_MASK (0x7 << MSC_CMDAT_RESPONSE_BIT) + #define MSC_CMDAT_RESPONSE_NONE (0x0 << MSC_CMDAT_RESPONSE_BIT) /* No response */ + #define MSC_CMDAT_RESPONSE_R1 (0x1 << MSC_CMDAT_RESPONSE_BIT) /* Format R1 and R1b */ + #define MSC_CMDAT_RESPONSE_R2 (0x2 << MSC_CMDAT_RESPONSE_BIT) /* Format R2 */ + #define MSC_CMDAT_RESPONSE_R3 (0x3 << MSC_CMDAT_RESPONSE_BIT) /* Format R3 */ + #define MSC_CMDAT_RESPONSE_R4 (0x4 << MSC_CMDAT_RESPONSE_BIT) /* Format R4 */ + #define MSC_CMDAT_RESPONSE_R5 (0x5 << MSC_CMDAT_RESPONSE_BIT) /* Format R5 */ + #define MSC_CMDAT_RESPONSE_R6 (0x6 << MSC_CMDAT_RESPONSE_BIT) /* Format R6 */ + +#define CMDAT_DMA_EN (1 << 8) +#define CMDAT_INIT (1 << 7) +#define CMDAT_BUSY (1 << 6) +#define CMDAT_STREAM (1 << 5) +#define CMDAT_WRITE (1 << 4) +#define CMDAT_DATA_EN (1 << 3) + +/* MSC Interrupts Mask Register (MSC_IMASK) */ +#define MSC_IMASK_AUTO_CMD_DONE (1 << 8) +#define MSC_IMASK_SDIO (1 << 7) +#define MSC_IMASK_TXFIFO_WR_REQ (1 << 6) +#define MSC_IMASK_RXFIFO_RD_REQ (1 << 5) +#define MSC_IMASK_END_CMD_RES (1 << 2) +#define MSC_IMASK_PRG_DONE (1 << 1) +#define MSC_IMASK_DATA_TRAN_DONE (1 << 0) + +/* MSC Interrupts Status Register (MSC_IREG) */ +#define MSC_IREG_AUTO_CMD_DONE (1 << 8) +#define MSC_IREG_SDIO (1 << 7) +#define MSC_IREG_TXFIFO_WR_REQ (1 << 6) +#define MSC_IREG_RXFIFO_RD_REQ (1 << 5) +#define MSC_IREG_END_CMD_RES (1 << 2) +#define MSC_IREG_PRG_DONE (1 << 1) +#define MSC_IREG_DATA_TRAN_DONE (1 << 0) + +/* MSC Low Power Mode Register (MSC_LPM) */ +#define MSC_SET_LPM (1 << 0) + +/************************************************************************* + * EMC (External Memory Controller) + *************************************************************************/ +#define EMC_BCR (EMC_BASE + 0x00) /* Bus Control Register */ +#define EMC_SMCR0 (EMC_BASE + 0x10) /* Static Memory Control Register 0 */ +#define EMC_SMCR1 (EMC_BASE + 0x14) /* Static Memory Control Register 1 */ +#define EMC_SMCR2 (EMC_BASE + 0x18) /* Static Memory Control Register 2 */ +#define EMC_SMCR3 (EMC_BASE + 0x1c) /* Static Memory Control Register 3 */ +#define EMC_SMCR4 (EMC_BASE + 0x20) /* Static Memory Control Register 4 */ +#define EMC_SACR0 (EMC_BASE + 0x30) /* Static Memory Bank 0 Addr Config Reg */ +#define EMC_SACR1 (EMC_BASE + 0x34) /* Static Memory Bank 1 Addr Config Reg */ +#define EMC_SACR2 (EMC_BASE + 0x38) /* Static Memory Bank 2 Addr Config Reg */ +#define EMC_SACR3 (EMC_BASE + 0x3c) /* Static Memory Bank 3 Addr Config Reg */ +#define EMC_SACR4 (EMC_BASE + 0x40) /* Static Memory Bank 4 Addr Config Reg */ + +#define EMC_NFCSR (EMC_BASE + 0x050) /* NAND Flash Control/Status Register */ + +#define EMC_DMCR (EMC_BASE + 0x80) /* DRAM Control Register */ +#define EMC_RTCSR (EMC_BASE + 0x84) /* Refresh Time Control/Status Register */ +#define EMC_RTCNT (EMC_BASE + 0x88) /* Refresh Timer Counter */ +#define EMC_RTCOR (EMC_BASE + 0x8c) /* Refresh Time Constant Register */ +#define EMC_DMAR0 (EMC_BASE + 0x90) /* SDRAM Bank 0 Addr Config Register */ +#define EMC_DMAR1 (EMC_BASE + 0x94) /* SDRAM Bank 1 Addr Config Register */ +#define EMC_SDMR0 (EMC_BASE + 0xa000) /* Mode Register of SDRAM bank 0 */ + +#define REG_EMC_BCR REG32(EMC_BCR) +#define REG_EMC_SMCR0 REG32(EMC_SMCR0) +#define REG_EMC_SMCR1 REG32(EMC_SMCR1) +#define REG_EMC_SMCR2 REG32(EMC_SMCR2) +#define REG_EMC_SMCR3 REG32(EMC_SMCR3) +#define REG_EMC_SMCR4 REG32(EMC_SMCR4) +#define REG_EMC_SACR0 REG32(EMC_SACR0) +#define REG_EMC_SACR1 REG32(EMC_SACR1) +#define REG_EMC_SACR2 REG32(EMC_SACR2) +#define REG_EMC_SACR3 REG32(EMC_SACR3) +#define REG_EMC_SACR4 REG32(EMC_SACR4) + +#define REG_EMC_NFCSR REG32(EMC_NFCSR) + +#define REG_EMC_DMCR REG32(EMC_DMCR) +#define REG_EMC_RTCSR REG16(EMC_RTCSR) +#define REG_EMC_RTCNT REG16(EMC_RTCNT) +#define REG_EMC_RTCOR REG16(EMC_RTCOR) +#define REG_EMC_DMAR0 REG32(EMC_DMAR0) +#define REG_EMC_DMAR1 REG32(EMC_DMAR1) + +/* Bus Control Register */ +#define EMC_BCR_BT_SEL_BIT 30 +#define EMC_BCR_BT_SEL_MASK (0x3 << EMC_BCR_BT_SEL_BIT) +#define EMC_BCR_PK_SEL (1 << 24) +#define EMC_BCR_BSR_MASK (1 << 2) /* Nand and SDRAM Bus Share Select: 0, share; 1, unshare */ + #define EMC_BCR_BSR_SHARE (0 << 2) + #define EMC_BCR_BSR_UNSHARE (1 << 2) +#define EMC_BCR_BRE (1 << 1) +#define EMC_BCR_ENDIAN (1 << 0) + +/* Static Memory Control Register */ +#define EMC_SMCR_STRV_BIT 24 +#define EMC_SMCR_STRV_MASK (0x0f << EMC_SMCR_STRV_BIT) +#define EMC_SMCR_TAW_BIT 20 +#define EMC_SMCR_TAW_MASK (0x0f << EMC_SMCR_TAW_BIT) +#define EMC_SMCR_TBP_BIT 16 +#define EMC_SMCR_TBP_MASK (0x0f << EMC_SMCR_TBP_BIT) +#define EMC_SMCR_TAH_BIT 12 +#define EMC_SMCR_TAH_MASK (0x07 << EMC_SMCR_TAH_BIT) +#define EMC_SMCR_TAS_BIT 8 +#define EMC_SMCR_TAS_MASK (0x07 << EMC_SMCR_TAS_BIT) +#define EMC_SMCR_BW_BIT 6 +#define EMC_SMCR_BW_MASK (0x03 << EMC_SMCR_BW_BIT) + #define EMC_SMCR_BW_8BIT (0 << EMC_SMCR_BW_BIT) + #define EMC_SMCR_BW_16BIT (1 << EMC_SMCR_BW_BIT) + #define EMC_SMCR_BW_32BIT (2 << EMC_SMCR_BW_BIT) +#define EMC_SMCR_BCM (1 << 3) +#define EMC_SMCR_BL_BIT 1 +#define EMC_SMCR_BL_MASK (0x03 << EMC_SMCR_BL_BIT) + #define EMC_SMCR_BL_4 (0 << EMC_SMCR_BL_BIT) + #define EMC_SMCR_BL_8 (1 << EMC_SMCR_BL_BIT) + #define EMC_SMCR_BL_16 (2 << EMC_SMCR_BL_BIT) + #define EMC_SMCR_BL_32 (3 << EMC_SMCR_BL_BIT) +#define EMC_SMCR_SMT (1 << 0) + +/* Static Memory Bank Addr Config Reg */ +#define EMC_SACR_BASE_BIT 8 +#define EMC_SACR_BASE_MASK (0xff << EMC_SACR_BASE_BIT) +#define EMC_SACR_MASK_BIT 0 +#define EMC_SACR_MASK_MASK (0xff << EMC_SACR_MASK_BIT) + +/* NAND Flash Control/Status Register */ +#define EMC_NFCSR_NFCE4 (1 << 7) /* NAND Flash Enable */ +#define EMC_NFCSR_NFE4 (1 << 6) /* NAND Flash FCE# Assertion Enable */ +#define EMC_NFCSR_NFCE3 (1 << 5) +#define EMC_NFCSR_NFE3 (1 << 4) +#define EMC_NFCSR_NFCE2 (1 << 3) +#define EMC_NFCSR_NFE2 (1 << 2) +#define EMC_NFCSR_NFCE1 (1 << 1) +#define EMC_NFCSR_NFE1 (1 << 0) + +/* DRAM Control Register */ +#define EMC_DMCR_BW_BIT 31 +#define EMC_DMCR_BW (1 << EMC_DMCR_BW_BIT) +#define EMC_DMCR_CA_BIT 26 +#define EMC_DMCR_CA_MASK (0x07 << EMC_DMCR_CA_BIT) + #define EMC_DMCR_CA_8 (0 << EMC_DMCR_CA_BIT) + #define EMC_DMCR_CA_9 (1 << EMC_DMCR_CA_BIT) + #define EMC_DMCR_CA_10 (2 << EMC_DMCR_CA_BIT) + #define EMC_DMCR_CA_11 (3 << EMC_DMCR_CA_BIT) + #define EMC_DMCR_CA_12 (4 << EMC_DMCR_CA_BIT) +#define EMC_DMCR_RMODE (1 << 25) +#define EMC_DMCR_RFSH (1 << 24) +#define EMC_DMCR_MRSET (1 << 23) +#define EMC_DMCR_RA_BIT 20 +#define EMC_DMCR_RA_MASK (0x03 << EMC_DMCR_RA_BIT) + #define EMC_DMCR_RA_11 (0 << EMC_DMCR_RA_BIT) + #define EMC_DMCR_RA_12 (1 << EMC_DMCR_RA_BIT) + #define EMC_DMCR_RA_13 (2 << EMC_DMCR_RA_BIT) +#define EMC_DMCR_BA_BIT 19 +#define EMC_DMCR_BA (1 << EMC_DMCR_BA_BIT) +#define EMC_DMCR_PDM (1 << 18) +#define EMC_DMCR_EPIN (1 << 17) +#define EMC_DMCR_MBSEL (1 << 16) +#define EMC_DMCR_TRAS_BIT 13 +#define EMC_DMCR_TRAS_MASK (0x07 << EMC_DMCR_TRAS_BIT) +#define EMC_DMCR_RCD_BIT 11 +#define EMC_DMCR_RCD_MASK (0x03 << EMC_DMCR_RCD_BIT) +#define EMC_DMCR_TPC_BIT 8 +#define EMC_DMCR_TPC_MASK (0x07 << EMC_DMCR_TPC_BIT) +#define EMC_DMCR_TRWL_BIT 5 +#define EMC_DMCR_TRWL_MASK (0x03 << EMC_DMCR_TRWL_BIT) +#define EMC_DMCR_TRC_BIT 2 +#define EMC_DMCR_TRC_MASK (0x07 << EMC_DMCR_TRC_BIT) +#define EMC_DMCR_TCL_BIT 0 +#define EMC_DMCR_TCL_MASK (0x03 << EMC_DMCR_TCL_BIT) + +/* Refresh Time Control/Status Register */ +#define EMC_RTCSR_SFR (1 << 8) /* self refresh flag */ +#define EMC_RTCSR_CMF (1 << 7) +#define EMC_RTCSR_CKS_BIT 0 +#define EMC_RTCSR_CKS_MASK (0x07 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_DISABLE (0 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_4 (1 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_16 (2 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_64 (3 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_256 (4 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_1024 (5 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_2048 (6 << EMC_RTCSR_CKS_BIT) + #define EMC_RTCSR_CKS_4096 (7 << EMC_RTCSR_CKS_BIT) + +/* SDRAM Bank Address Configuration Register */ +#define EMC_DMAR_BASE_BIT 8 +#define EMC_DMAR_BASE_MASK (0xff << EMC_DMAR_BASE_BIT) +#define EMC_DMAR_MASK_BIT 0 +#define EMC_DMAR_MASK_MASK (0xff << EMC_DMAR_MASK_BIT) + +/* Mode Register of SDRAM bank 0 */ +#define EMC_SDMR_BM (1 << 9) /* Write Burst Mode */ +#define EMC_SDMR_OM_BIT 7 /* Operating Mode */ +#define EMC_SDMR_OM_MASK (3 << EMC_SDMR_OM_BIT) + #define EMC_SDMR_OM_NORMAL (0 << EMC_SDMR_OM_BIT) +#define EMC_SDMR_CAS_BIT 4 /* CAS Latency */ +#define EMC_SDMR_CAS_MASK (7 << EMC_SDMR_CAS_BIT) + #define EMC_SDMR_CAS_1 (1 << EMC_SDMR_CAS_BIT) + #define EMC_SDMR_CAS_2 (2 << EMC_SDMR_CAS_BIT) + #define EMC_SDMR_CAS_3 (3 << EMC_SDMR_CAS_BIT) +#define EMC_SDMR_BT_BIT 3 /* Burst Type */ +#define EMC_SDMR_BT_MASK (1 << EMC_SDMR_BT_BIT) + #define EMC_SDMR_BT_SEQ (0 << EMC_SDMR_BT_BIT) /* Sequential */ + #define EMC_SDMR_BT_INT (1 << EMC_SDMR_BT_BIT) /* Interleave */ +#define EMC_SDMR_BL_BIT 0 /* Burst Length */ +#define EMC_SDMR_BL_MASK (7 << EMC_SDMR_BL_BIT) + #define EMC_SDMR_BL_1 (0 << EMC_SDMR_BL_BIT) + #define EMC_SDMR_BL_2 (1 << EMC_SDMR_BL_BIT) + #define EMC_SDMR_BL_4 (2 << EMC_SDMR_BL_BIT) + #define EMC_SDMR_BL_8 (3 << EMC_SDMR_BL_BIT) + +#define EMC_SDMR_CAS2_16BIT \ + (EMC_SDMR_CAS_2 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_2) +#define EMC_SDMR_CAS2_32BIT \ + (EMC_SDMR_CAS_2 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_4) +#define EMC_SDMR_CAS3_16BIT \ + (EMC_SDMR_CAS_3 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_2) +#define EMC_SDMR_CAS3_32BIT \ + (EMC_SDMR_CAS_3 | EMC_SDMR_BT_SEQ | EMC_SDMR_BL_4) + + +/************************************************************************* + * CIM + *************************************************************************/ +#define CIM_CFG (CIM_BASE + 0x0000) +#define CIM_CTRL (CIM_BASE + 0x0004) +#define CIM_STATE (CIM_BASE + 0x0008) +#define CIM_IID (CIM_BASE + 0x000C) +#define CIM_RXFIFO (CIM_BASE + 0x0010) +#define CIM_DA (CIM_BASE + 0x0020) +#define CIM_FA (CIM_BASE + 0x0024) +#define CIM_FID (CIM_BASE + 0x0028) +#define CIM_CMD (CIM_BASE + 0x002C) +#define CIM_SIZE (CIM_BASE + 0x0030) +#define CIM_OFFSET (CIM_BASE + 0x0034) +#define CIM_RAM_ADDR (CIM_BASE + 0x1000) + +#define REG_CIM_CFG REG32(CIM_CFG) +#define REG_CIM_CTRL REG32(CIM_CTRL) +#define REG_CIM_STATE REG32(CIM_STATE) +#define REG_CIM_IID REG32(CIM_IID) +#define REG_CIM_RXFIFO REG32(CIM_RXFIFO) +#define REG_CIM_DA REG32(CIM_DA) +#define REG_CIM_FA REG32(CIM_FA) +#define REG_CIM_FID REG32(CIM_FID) +#define REG_CIM_CMD REG32(CIM_CMD) +#define REG_CIM_SIZE REG32(CIM_SIZE) +#define REG_CIM_OFFSET REG32(CIM_OFFSET) + +#define CIM_CFG_ORDER_BIT 18 +#define CIM_CFG_ORDER_MASK (0x3 << CIM_CFG_ORDER_BIT) + #define CIM_CFG_ORDER_0 (0x0 << CIM_CFG_ORDER_BIT) /* Y0CbY1Cr; YCbCr */ + #define CIM_CFG_ORDER_1 (0x1 << CIM_CFG_ORDER_BIT) /* Y0CrY1Cb; YCrCb */ + #define CIM_CFG_ORDER_2 (0x2 << CIM_CFG_ORDER_BIT) /* CbY0CrY1; CbCrY */ + #define CIM_CFG_ORDER_3 (0x3 << CIM_CFG_ORDER_BIT) /* CrY0CbY1; CrCbY */ +#define CIM_CFG_DF_BIT 16 +#define CIM_CFG_DF_MASK (0x3 << CIM_CFG_DF_BIT) + #define CIM_CFG_DF_YUV444 (0x1 << CIM_CFG_DF_BIT) /* YCbCr444 */ + #define CIM_CFG_DF_YUV422 (0x2 << CIM_CFG_DF_BIT) /* YCbCr422 */ + #define CIM_CFG_DF_ITU656 (0x3 << CIM_CFG_DF_BIT) /* ITU656 YCbCr422 */ +#define CIM_CFG_INV_DAT (1 << 15) +#define CIM_CFG_VSP (1 << 14) /* VSYNC Polarity:0-rising edge active,1-falling edge active */ +#define CIM_CFG_HSP (1 << 13) /* HSYNC Polarity:0-rising edge active,1-falling edge active */ +#define CIM_CFG_PCP (1 << 12) /* PCLK working edge: 0-rising, 1-falling */ +#define CIM_CFG_DMA_BURST_TYPE_BIT 10 +#define CIM_CFG_DMA_BURST_TYPE_MASK (0x3 << CIM_CFG_DMA_BURST_TYPE_BIT) + #define CIM_CFG_DMA_BURST_INCR4 (0 << CIM_CFG_DMA_BURST_TYPE_BIT) + #define CIM_CFG_DMA_BURST_INCR8 (1 << CIM_CFG_DMA_BURST_TYPE_BIT) /* Suggested */ + #define CIM_CFG_DMA_BURST_INCR16 (2 << CIM_CFG_DMA_BURST_TYPE_BIT) /* Suggested High speed AHB*/ +#define CIM_CFG_DUMMY_ZERO (1 << 9) +#define CIM_CFG_EXT_VSYNC (1 << 8) /* Only for ITU656 Progressive mode */ +#define CIM_CFG_PACK_BIT 4 +#define CIM_CFG_PACK_MASK (0x7 << CIM_CFG_PACK_BIT) + #define CIM_CFG_PACK_0 (0 << CIM_CFG_PACK_BIT) /* 11 22 33 44 0xY0CbY1Cr */ + #define CIM_CFG_PACK_1 (1 << CIM_CFG_PACK_BIT) /* 22 33 44 11 0xCbY1CrY0 */ + #define CIM_CFG_PACK_2 (2 << CIM_CFG_PACK_BIT) /* 33 44 11 22 0xY1CrY0Cb */ + #define CIM_CFG_PACK_3 (3 << CIM_CFG_PACK_BIT) /* 44 11 22 33 0xCrY0CbY1 */ + #define CIM_CFG_PACK_4 (4 << CIM_CFG_PACK_BIT) /* 44 33 22 11 0xCrY1CbY0 */ + #define CIM_CFG_PACK_5 (5 << CIM_CFG_PACK_BIT) /* 33 22 11 44 0xY1CbY0Cr */ + #define CIM_CFG_PACK_6 (6 << CIM_CFG_PACK_BIT) /* 22 11 44 33 0xCbY0CrY1 */ + #define CIM_CFG_PACK_7 (7 << CIM_CFG_PACK_BIT) /* 11 44 33 22 0xY0CrY1Cb */ +#define CIM_CFG_BYPASS_BIT 2 +#define CIM_CFG_BYPASS_MASK (1 << CIM_CFG_BYPASS_BIT) + #define CIM_CFG_BYPASS (1 << CIM_CFG_BYPASS_BIT) +#define CIM_CFG_DSM_BIT 0 +#define CIM_CFG_DSM_MASK (0x3 << CIM_CFG_DSM_BIT) + #define CIM_CFG_DSM_CPM (0 << CIM_CFG_DSM_BIT) /* CCIR656 Progressive Mode */ + #define CIM_CFG_DSM_CIM (1 << CIM_CFG_DSM_BIT) /* CCIR656 Interlace Mode */ + #define CIM_CFG_DSM_GCM (2 << CIM_CFG_DSM_BIT) /* Gated Clock Mode */ + +/* CIM Control Register (CIM_CTRL) */ +#define CIM_CTRL_EEOF_LINE_BIT 20 +#define CIM_CTRL_EEOF_LINE_MASK (0xfff << CIM_CTRL_EEOF_LINE_BIT) +#define CIM_CTRL_FRC_BIT 16 +#define CIM_CTRL_FRC_MASK (0xf << CIM_CTRL_FRC_BIT) + #define CIM_CTRL_FRC_1 (0x0 << CIM_CTRL_FRC_BIT) /* Sample every frame */ + #define CIM_CTRL_FRC_2 (0x1 << CIM_CTRL_FRC_BIT) /* Sample 1/2 frame */ + #define CIM_CTRL_FRC_3 (0x2 << CIM_CTRL_FRC_BIT) /* Sample 1/3 frame */ + #define CIM_CTRL_FRC_4 (0x3 << CIM_CTRL_FRC_BIT) /* Sample 1/4 frame */ + #define CIM_CTRL_FRC_5 (0x4 << CIM_CTRL_FRC_BIT) /* Sample 1/5 frame */ + #define CIM_CTRL_FRC_6 (0x5 << CIM_CTRL_FRC_BIT) /* Sample 1/6 frame */ + #define CIM_CTRL_FRC_7 (0x6 << CIM_CTRL_FRC_BIT) /* Sample 1/7 frame */ + #define CIM_CTRL_FRC_8 (0x7 << CIM_CTRL_FRC_BIT) /* Sample 1/8 frame */ + #define CIM_CTRL_FRC_9 (0x8 << CIM_CTRL_FRC_BIT) /* Sample 1/9 frame */ + #define CIM_CTRL_FRC_10 (0x9 << CIM_CTRL_FRC_BIT) /* Sample 1/10 frame */ + #define CIM_CTRL_FRC_11 (0xA << CIM_CTRL_FRC_BIT) /* Sample 1/11 frame */ + #define CIM_CTRL_FRC_12 (0xB << CIM_CTRL_FRC_BIT) /* Sample 1/12 frame */ + #define CIM_CTRL_FRC_13 (0xC << CIM_CTRL_FRC_BIT) /* Sample 1/13 frame */ + #define CIM_CTRL_FRC_14 (0xD << CIM_CTRL_FRC_BIT) /* Sample 1/14 frame */ + #define CIM_CTRL_FRC_15 (0xE << CIM_CTRL_FRC_BIT) /* Sample 1/15 frame */ + #define CIM_CTRL_FRC_16 (0xF << CIM_CTRL_FRC_BIT) /* Sample 1/16 frame */ + +#define CIM_CTRL_DMA_EEOF (1 << 15) /* Enable EEOF interrupt */ +#define CIM_CTRL_WIN_EN (1 << 14) +#define CIM_CTRL_VDDM (1 << 13) /* VDD interrupt enable */ +#define CIM_CTRL_DMA_SOFM (1 << 12) +#define CIM_CTRL_DMA_EOFM (1 << 11) +#define CIM_CTRL_DMA_STOPM (1 << 10) +#define CIM_CTRL_RXF_TRIGM (1 << 9) +#define CIM_CTRL_RXF_OFM (1 << 8) +#define CIM_CTRL_DMA_SYNC (1 << 7) /*when change DA, do frame sync */ +#define CIM_CTRL_RXF_TRIG_BIT 3 +#define CIM_CTRL_RXF_TRIG_MASK (0xf << CIM_CTRL_RXF_TRIG_BIT) /* trigger value = (n+1)*burst_type */ + +#define CIM_CTRL_DMA_EN (1 << 2) /* Enable DMA */ +#define CIM_CTRL_RXF_RST (1 << 1) /* RxFIFO reset */ +#define CIM_CTRL_ENA (1 << 0) /* Enable CIM */ + +/* CIM State Register (CIM_STATE) */ +#define CIM_STATE_DMA_EEOF (1 << 7) /* DMA Line EEOf irq */ +#define CIM_STATE_DMA_SOF (1 << 6) /* DMA start irq */ +#define CIM_STATE_DMA_EOF (1 << 5) /* DMA end irq */ +#define CIM_STATE_DMA_STOP (1 << 4) /* DMA stop irq */ +#define CIM_STATE_RXF_OF (1 << 3) /* RXFIFO over flow irq */ +#define CIM_STATE_RXF_TRIG (1 << 2) /* RXFIFO triger meet irq */ +#define CIM_STATE_RXF_EMPTY (1 << 1) /* RXFIFO empty irq */ +#define CIM_STATE_VDD (1 << 0) /* CIM disabled irq */ + +/* CIM DMA Command Register (CIM_CMD) */ + +#define CIM_CMD_SOFINT (1 << 31) /* enable DMA start irq */ +#define CIM_CMD_EOFINT (1 << 30) /* enable DMA end irq */ +#define CIM_CMD_EEOFINT (1 << 29) /* enable DMA EEOF irq */ +#define CIM_CMD_STOP (1 << 28) /* enable DMA stop irq */ +#define CIM_CMD_OFRCV (1 << 27) /* enable recovery when TXFiFo overflow */ +#define CIM_CMD_LEN_BIT 0 +#define CIM_CMD_LEN_MASK (0xffffff << CIM_CMD_LEN_BIT) + +/* CIM Window-Image Size Register (CIM_SIZE) */ +#define CIM_SIZE_LPF_BIT 16 /* Lines per freame for csc output image */ +#define CIM_SIZE_LPF_MASK (0x1fff << CIM_SIZE_LPF_BIT) +#define CIM_SIZE_PPL_BIT 0 /* Pixels per line for csc output image, should be an even number */ +#define CIM_SIZE_PPL_MASK (0x1fff << CIM_SIZE_PPL_BIT) + +/* CIM Image Offset Register (CIM_OFFSET) */ +#define CIM_OFFSET_V_BIT 16 /* Vertical offset */ +#define CIM_OFFSET_V_MASK (0xfff << CIM_OFFSET_V_BIT) +#define CIM_OFFSET_H_BIT 0 /* Horizontal offset, should be an enen number */ +#define CIM_OFFSET_H_MASK (0xfff << CIM_OFFSET_H_BIT) /*OFFSET_H should be even number*/ + +/************************************************************************* + * SADC (Smart A/D Controller) + *************************************************************************/ + +#define SADC_ENA (SADC_BASE + 0x00) /* ADC Enable Register */ +#define SADC_CFG (SADC_BASE + 0x04) /* ADC Configure Register */ +#define SADC_CTRL (SADC_BASE + 0x08) /* ADC Control Register */ +#define SADC_STATE (SADC_BASE + 0x0C) /* ADC Status Register*/ +#define SADC_SAMETIME (SADC_BASE + 0x10) /* ADC Same Point Time Register */ +#define SADC_WAITTIME (SADC_BASE + 0x14) /* ADC Wait Time Register */ +#define SADC_TSDAT (SADC_BASE + 0x18) /* ADC Touch Screen Data Register */ +#define SADC_BATDAT (SADC_BASE + 0x1C) /* ADC PBAT Data Register */ +#define SADC_SADDAT (SADC_BASE + 0x20) /* ADC SADCIN Data Register */ +#define SADC_ADCLK (SADC_BASE + 0x28) /* ADC Clock Divide Register */ + +#define REG_SADC_ENA REG8(SADC_ENA) +#define REG_SADC_CFG REG32(SADC_CFG) +#define REG_SADC_CTRL REG8(SADC_CTRL) +#define REG_SADC_STATE REG8(SADC_STATE) +#define REG_SADC_SAMETIME REG16(SADC_SAMETIME) +#define REG_SADC_WAITTIME REG16(SADC_WAITTIME) +#define REG_SADC_TSDAT REG32(SADC_TSDAT) +#define REG_SADC_BATDAT REG16(SADC_BATDAT) +#define REG_SADC_SADDAT REG16(SADC_SADDAT) +#define REG_SADC_ADCLK REG32(SADC_ADCLK) + +/* ADC Enable Register */ +#define SADC_ENA_ADEN (1 << 7) /* Touch Screen Enable */ +#define SADC_ENA_ENTR_SLP (1 << 6) /* Touch Screen Enable */ +#define SADC_ENA_EXIT_SLP (1 << 5) /* Touch Screen Enable */ +#define SADC_ENA_TSEN (1 << 2) /* Touch Screen Enable */ +#define SADC_ENA_PBATEN (1 << 1) /* PBAT Enable */ +#define SADC_ENA_SADCINEN (1 << 0) /* SADCIN Enable */ + +/* ADC Configure Register */ +#define SADC_CFG_EXIN (1 << 30) +#define SADC_CFG_CLKOUT_NUM_BIT 16 +#define SADC_CFG_CLKOUT_NUM_MASK (0x7 << SADC_CFG_CLKOUT_NUM_BIT) +#define SADC_CFG_TS_DMA (1 << 15) /* Touch Screen DMA Enable */ +#define SADC_CFG_XYZ_BIT 13 /* XYZ selection */ +#define SADC_CFG_XYZ_MASK (0x3 << SADC_CFG_XYZ_BIT) + #define SADC_CFG_XY (0 << SADC_CFG_XYZ_BIT) + #define SADC_CFG_XYZ (1 << SADC_CFG_XYZ_BIT) + #define SADC_CFG_XYZ1Z2 (2 << SADC_CFG_XYZ_BIT) +#define SADC_CFG_SNUM_BIT 10 /* Sample Number */ +#define SADC_CFG_SNUM_MASK (0x7 << SADC_CFG_SNUM_BIT) + #define SADC_CFG_SNUM_1 (0x0 << SADC_CFG_SNUM_BIT) + #define SADC_CFG_SNUM_2 (0x1 << SADC_CFG_SNUM_BIT) + #define SADC_CFG_SNUM_3 (0x2 << SADC_CFG_SNUM_BIT) + #define SADC_CFG_SNUM_4 (0x3 << SADC_CFG_SNUM_BIT) + #define SADC_CFG_SNUM_5 (0x4 << SADC_CFG_SNUM_BIT) + #define SADC_CFG_SNUM_6 (0x5 << SADC_CFG_SNUM_BIT) + #define SADC_CFG_SNUM_8 (0x6 << SADC_CFG_SNUM_BIT) + #define SADC_CFG_SNUM_9 (0x7 << SADC_CFG_SNUM_BIT) +#define SADC_CFG_CLKDIV_BIT 5 /* AD Converter frequency clock divider */ +#define SADC_CFG_CLKDIV_MASK (0x1f << SADC_CFG_CLKDIV_BIT) +#define SADC_CFG_PBAT_HIGH (0 << 4) /* PBAT >= 2.5V */ +#define SADC_CFG_PBAT_LOW (1 << 4) /* PBAT < 2.5V */ +#define SADC_CFG_CMD_BIT 0 /* ADC Command */ +#define SADC_CFG_CMD_MASK (0xf << SADC_CFG_CMD_BIT) + #define SADC_CFG_CMD_X_SE (0x0 << SADC_CFG_CMD_BIT) /* X Single-End */ + #define SADC_CFG_CMD_Y_SE (0x1 << SADC_CFG_CMD_BIT) /* Y Single-End */ + #define SADC_CFG_CMD_X_DIFF (0x2 << SADC_CFG_CMD_BIT) /* X Differential */ + #define SADC_CFG_CMD_Y_DIFF (0x3 << SADC_CFG_CMD_BIT) /* Y Differential */ + #define SADC_CFG_CMD_Z1_DIFF (0x4 << SADC_CFG_CMD_BIT) /* Z1 Differential */ + #define SADC_CFG_CMD_Z2_DIFF (0x5 << SADC_CFG_CMD_BIT) /* Z2 Differential */ + #define SADC_CFG_CMD_Z3_DIFF (0x6 << SADC_CFG_CMD_BIT) /* Z3 Differential */ + #define SADC_CFG_CMD_Z4_DIFF (0x7 << SADC_CFG_CMD_BIT) /* Z4 Differential */ + #define SADC_CFG_CMD_TP_SE (0x8 << SADC_CFG_CMD_BIT) /* Touch Pressure */ + #define SADC_CFG_CMD_PBATH_SE (0x9 << SADC_CFG_CMD_BIT) /* PBAT >= 2.5V */ + #define SADC_CFG_CMD_PBATL_SE (0xa << SADC_CFG_CMD_BIT) /* PBAT < 2.5V */ + #define SADC_CFG_CMD_SADCIN_SE (0xb << SADC_CFG_CMD_BIT) /* Measure SADCIN */ + #define SADC_CFG_CMD_INT_PEN (0xc << SADC_CFG_CMD_BIT) /* INT_PEN Enable */ + +/* ADC Control Register */ +#define SADC_CTRL_SLPENDM (1 << 5) /* sleep Interrupt Mask */ +#define SADC_CTRL_PENDM (1 << 4) /* Pen Down Interrupt Mask */ +#define SADC_CTRL_PENUM (1 << 3) /* Pen Up Interrupt Mask */ +#define SADC_CTRL_TSRDYM (1 << 2) /* Touch Screen Data Ready Interrupt Mask */ +#define SADC_CTRL_PBATRDYM (1 << 1) /* PBAT Data Ready Interrupt Mask */ +#define SADC_CTRL_SRDYM (1 << 0) /* SADCIN Data Ready Interrupt Mask */ + +/* ADC Status Register */ +#define SADC_STATE_SLEEPND (1 << 5) /* Pen Down Interrupt Flag */ +#define SADC_STATE_PEND (1 << 4) /* Pen Down Interrupt Flag */ +#define SADC_STATE_PENU (1 << 3) /* Pen Up Interrupt Flag */ +#define SADC_STATE_TSRDY (1 << 2) /* Touch Screen Data Ready Interrupt Flag */ +#define SADC_STATE_PBATRDY (1 << 1) /* PBAT Data Ready Interrupt Flag */ +#define SADC_STATE_SRDY (1 << 0) /* SADCIN Data Ready Interrupt Flag */ + +/* ADC Touch Screen Data Register */ +#define SADC_TSDAT_DATA0_BIT 0 +#define SADC_TSDAT_DATA0_MASK (0xfff << SADC_TSDAT_DATA0_BIT) +#define SADC_TSDAT_TYPE0 (1 << 15) +#define SADC_TSDAT_DATA1_BIT 16 +#define SADC_TSDAT_DATA1_MASK (0xfff << SADC_TSDAT_DATA1_BIT) +#define SADC_TSDAT_TYPE1 (1 << 31) + +/* ADC Clock Divide Register */ +#define SADC_ADCLK_CLKDIV_10_BIT 16 +#define SADC_ADCLK_CLKDIV_10_MASK (0x7f << SADC_ADCLK_CLKDIV_10_BIT) +#define SADC_ADCLK_CLKDIV_BIT 0 +#define SADC_ADCLK_CLKDIV_MASK (0x3f << SADC_ADCLK_CLKDIV_BIT) + +/************************************************************************* + * SLCD (Smart LCD Controller) + *************************************************************************/ + +#define SLCD_CFG (SLCD_BASE + 0xA0) /* SLCD Configure Register */ +#define SLCD_CTRL (SLCD_BASE + 0xA4) /* SLCD Control Register */ +#define SLCD_STATE (SLCD_BASE + 0xA8) /* SLCD Status Register */ +#define SLCD_DATA (SLCD_BASE + 0xAC) /* SLCD Data Register */ + +#define REG_SLCD_CFG REG32(SLCD_CFG) +#define REG_SLCD_CTRL REG8(SLCD_CTRL) +#define REG_SLCD_STATE REG8(SLCD_STATE) +#define REG_SLCD_DATA REG32(SLCD_DATA) + +/* SLCD Configure Register */ +#define SLCD_CFG_DWIDTH_BIT 10 +#define SLCD_CFG_DWIDTH_MASK (0x7 << SLCD_CFG_DWIDTH_BIT) + #define SLCD_CFG_DWIDTH_18BIT (0 << SLCD_CFG_DWIDTH_BIT) + #define SLCD_CFG_DWIDTH_16BIT (1 << SLCD_CFG_DWIDTH_BIT) + #define SLCD_CFG_DWIDTH_8BIT_x3 (2 << SLCD_CFG_DWIDTH_BIT) + #define SLCD_CFG_DWIDTH_8BIT_x2 (3 << SLCD_CFG_DWIDTH_BIT) + #define SLCD_CFG_DWIDTH_8BIT_x1 (4 << SLCD_CFG_DWIDTH_BIT) + #define SLCD_CFG_DWIDTH_24BIT (5 << SLCD_CFG_DWIDTH_BIT) + #define SLCD_CFG_DWIDTH_9BIT_x2 (7 << SLCD_CFG_DWIDTH_BIT) +#define SLCD_CFG_CWIDTH_BIT (8) +#define SLCD_CFG_CWIDTH_MASK (0x7 << SLCD_CFG_CWIDTH_BIT) +#define SLCD_CFG_CWIDTH_16BIT (0 << SLCD_CFG_CWIDTH_BIT) +#define SLCD_CFG_CWIDTH_8BIT (1 << SLCD_CFG_CWIDTH_BIT) +#define SLCD_CFG_CWIDTH_18BIT (2 << SLCD_CFG_CWIDTH_BIT) +#define SLCD_CFG_CWIDTH_24BIT (3 << SLCD_CFG_CWIDTH_BIT) +#define SLCD_CFG_CS_ACTIVE_LOW (0 << 4) +#define SLCD_CFG_CS_ACTIVE_HIGH (1 << 4) +#define SLCD_CFG_RS_CMD_LOW (0 << 3) +#define SLCD_CFG_RS_CMD_HIGH (1 << 3) +#define SLCD_CFG_CLK_ACTIVE_FALLING (0 << 1) +#define SLCD_CFG_CLK_ACTIVE_RISING (1 << 1) +#define SLCD_CFG_TYPE_PARALLEL (0 << 0) +#define SLCD_CFG_TYPE_SERIAL (1 << 0) + +/* SLCD Control Register */ +#define SLCD_CTRL_DMA_MODE (1 << 2) +#define SLCD_CTRL_DMA_START (1 << 1) +#define SLCD_CTRL_DMA_EN (1 << 0) + +/* SLCD Status Register */ +#define SLCD_STATE_BUSY (1 << 0) + +/* SLCD Data Register */ +#define SLCD_DATA_RS_DATA (0 << 31) +#define SLCD_DATA_RS_COMMAND (1 << 31) + +/************************************************************************* + * LCD (LCD Controller) + *************************************************************************/ +#define LCD_CFG (LCD_BASE + 0x00) /* LCD Configure Register */ +#define LCD_CTRL (LCD_BASE + 0x30) /* LCD Control Register */ +#define LCD_STATE (LCD_BASE + 0x34) /* LCD Status Register */ + +#define LCD_OSDC (LCD_BASE + 0x100) /* LCD OSD Configure Register */ +#define LCD_OSDCTRL (LCD_BASE + 0x104) /* LCD OSD Control Register */ +#define LCD_OSDS (LCD_BASE + 0x108) /* LCD OSD Status Register */ +#define LCD_BGC (LCD_BASE + 0x10C) /* LCD Background Color Register */ +#define LCD_KEY0 (LCD_BASE + 0x110) /* LCD Foreground Color Key Register 0 */ +#define LCD_KEY1 (LCD_BASE + 0x114) /* LCD Foreground Color Key Register 1 */ +#define LCD_ALPHA (LCD_BASE + 0x118) /* LCD ALPHA Register */ +#define LCD_IPUR (LCD_BASE + 0x11C) /* LCD IPU Restart Register */ + +#define LCD_VAT (LCD_BASE + 0x0c) /* Virtual Area Setting Register */ +#define LCD_DAH (LCD_BASE + 0x10) /* Display Area Horizontal Start/End Point */ +#define LCD_DAV (LCD_BASE + 0x14) /* Display Area Vertical Start/End Point */ + +#define LCD_XYP0 (LCD_BASE + 0x120) /* Foreground 0 XY Position Register */ +#define LCD_XYP1 (LCD_BASE + 0x124) /* Foreground 1 XY Position Register */ +#define LCD_SIZE0 (LCD_BASE + 0x128) /* Foreground 0 Size Register */ +#define LCD_SIZE1 (LCD_BASE + 0x12C) /* Foreground 1 Size Register */ +#define LCD_RGBC (LCD_BASE + 0x90) /* RGB Controll Register */ + +#define LCD_VSYNC (LCD_BASE + 0x04) /* Vertical Synchronize Register */ +#define LCD_HSYNC (LCD_BASE + 0x08) /* Horizontal Synchronize Register */ +#define LCD_PS (LCD_BASE + 0x18) /* PS Signal Setting */ +#define LCD_CLS (LCD_BASE + 0x1c) /* CLS Signal Setting */ +#define LCD_SPL (LCD_BASE + 0x20) /* SPL Signal Setting */ +#define LCD_REV (LCD_BASE + 0x24) /* REV Signal Setting */ +#define LCD_IID (LCD_BASE + 0x38) /* Interrupt ID Register */ +#define LCD_DA0 (LCD_BASE + 0x40) /* Descriptor Address Register 0 */ +#define LCD_SA0 (LCD_BASE + 0x44) /* Source Address Register 0 */ +#define LCD_FID0 (LCD_BASE + 0x48) /* Frame ID Register 0 */ +#define LCD_CMD0 (LCD_BASE + 0x4c) /* DMA Command Register 0 */ +#define LCD_DA1 (LCD_BASE + 0x50) /* Descriptor Address Register 1 */ +#define LCD_SA1 (LCD_BASE + 0x54) /* Source Address Register 1 */ +#define LCD_FID1 (LCD_BASE + 0x58) /* Frame ID Register 1 */ +#define LCD_CMD1 (LCD_BASE + 0x5c) /* DMA Command Register 1 */ + +#define LCD_OFFS0 (LCD_BASE + 0x60) /* DMA Offsize Register 0 */ +#define LCD_PW0 (LCD_BASE + 0x64) /* DMA Page Width Register 0 */ +#define LCD_CNUM0 (LCD_BASE + 0x68) /* DMA Command Counter Register 0 */ +#define LCD_DESSIZE0 (LCD_BASE + 0x6C) /* Foreground Size in Descriptor 0 Register*/ +#define LCD_OFFS1 (LCD_BASE + 0x70) /* DMA Offsize Register 1 */ +#define LCD_PW1 (LCD_BASE + 0x74) /* DMA Page Width Register 1 */ +#define LCD_CNUM1 (LCD_BASE + 0x78) /* DMA Command Counter Register 1 */ +#define LCD_DESSIZE1 (LCD_BASE + 0x7C) /* Foreground Size in Descriptor 1 Register*/ + +#define REG_LCD_CFG REG32(LCD_CFG) +#define REG_LCD_CTRL REG32(LCD_CTRL) +#define REG_LCD_STATE REG32(LCD_STATE) + +#define REG_LCD_OSDC REG16(LCD_OSDC) +#define REG_LCD_OSDCTRL REG16(LCD_OSDCTRL) +#define REG_LCD_OSDS REG16(LCD_OSDS) +#define REG_LCD_BGC REG32(LCD_BGC) +#define REG_LCD_KEY0 REG32(LCD_KEY0) +#define REG_LCD_KEY1 REG32(LCD_KEY1) +#define REG_LCD_ALPHA REG8(LCD_ALPHA) +#define REG_LCD_IPUR REG32(LCD_IPUR) + +#define REG_LCD_VAT REG32(LCD_VAT) +#define REG_LCD_DAH REG32(LCD_DAH) +#define REG_LCD_DAV REG32(LCD_DAV) + +#define REG_LCD_XYP0 REG32(LCD_XYP0) +#define REG_LCD_XYP1 REG32(LCD_XYP1) +#define REG_LCD_SIZE0 REG32(LCD_SIZE0) +#define REG_LCD_SIZE1 REG32(LCD_SIZE1) +#define REG_LCD_RGBC REG16(LCD_RGBC) + +#define REG_LCD_VSYNC REG32(LCD_VSYNC) +#define REG_LCD_HSYNC REG32(LCD_HSYNC) +#define REG_LCD_PS REG32(LCD_PS) +#define REG_LCD_CLS REG32(LCD_CLS) +#define REG_LCD_SPL REG32(LCD_SPL) +#define REG_LCD_REV REG32(LCD_REV) +#define REG_LCD_IID REG32(LCD_IID) +#define REG_LCD_DA0 REG32(LCD_DA0) +#define REG_LCD_SA0 REG32(LCD_SA0) +#define REG_LCD_FID0 REG32(LCD_FID0) +#define REG_LCD_CMD0 REG32(LCD_CMD0) +#define REG_LCD_DA1 REG32(LCD_DA1) +#define REG_LCD_SA1 REG32(LCD_SA1) +#define REG_LCD_FID1 REG32(LCD_FID1) +#define REG_LCD_CMD1 REG32(LCD_CMD1) + +#define REG_LCD_OFFS0 REG32(LCD_OFFS0) +#define REG_LCD_PW0 REG32(LCD_PW0) +#define REG_LCD_CNUM0 REG32(LCD_CNUM0) +#define REG_LCD_DESSIZE0 REG32(LCD_DESSIZE0) +#define REG_LCD_OFFS1 REG32(LCD_OFFS1) +#define REG_LCD_PW1 REG32(LCD_PW1) +#define REG_LCD_CNUM1 REG32(LCD_CNUM1) +#define REG_LCD_DESSIZE1 REG32(LCD_DESSIZE1) + +/* LCD Configure Register */ +#define LCD_CFG_LCDPIN_BIT 31 /* LCD pins selection */ +#define LCD_CFG_LCDPIN_MASK (0x1 << LCD_CFG_LCDPIN_BIT) + #define LCD_CFG_LCDPIN_LCD (0x0 << LCD_CFG_LCDPIN_BIT) + #define LCD_CFG_LCDPIN_SLCD (0x1 << LCD_CFG_LCDPIN_BIT) +#define LCD_CFG_TVEPEH (1 << 30) /* TVE PAL enable extra halfline signal */ +#define LCD_CFG_FUHOLD (1 << 29) /* hold pixel clock when outFIFO underrun */ +#define LCD_CFG_NEWDES (1 << 28) /* use new descripter. old: 4words, new:8words */ +#define LCD_CFG_PALBP (1 << 27) /* bypass data format and alpha blending */ +#define LCD_CFG_TVEN (1 << 26) /* indicate the terminal is lcd or tv */ +#define LCD_CFG_RECOVER (1 << 25) /* Auto recover when output fifo underrun */ +#define LCD_CFG_DITHER (1 << 24) /* Dither function */ +#define LCD_CFG_PSM (1 << 23) /* PS signal mode */ +#define LCD_CFG_CLSM (1 << 22) /* CLS signal mode */ +#define LCD_CFG_SPLM (1 << 21) /* SPL signal mode */ +#define LCD_CFG_REVM (1 << 20) /* REV signal mode */ +#define LCD_CFG_HSYNM (1 << 19) /* HSYNC signal mode */ +#define LCD_CFG_PCLKM (1 << 18) /* PCLK signal mode */ +#define LCD_CFG_INVDAT (1 << 17) /* Inverse output data */ +#define LCD_CFG_SYNDIR_IN (1 << 16) /* VSYNC&HSYNC direction */ +#define LCD_CFG_PSP (1 << 15) /* PS pin reset state */ +#define LCD_CFG_CLSP (1 << 14) /* CLS pin reset state */ +#define LCD_CFG_SPLP (1 << 13) /* SPL pin reset state */ +#define LCD_CFG_REVP (1 << 12) /* REV pin reset state */ +#define LCD_CFG_HSP (1 << 11) /* HSYNC polarity:0-active high,1-active low */ +#define LCD_CFG_PCP (1 << 10) /* PCLK polarity:0-rising,1-falling */ +#define LCD_CFG_DEP (1 << 9) /* DE polarity:0-active high,1-active low */ +#define LCD_CFG_VSP (1 << 8) /* VSYNC polarity:0-rising,1-falling */ +#define LCD_CFG_MODE_TFT_18BIT (1 << 7) /* 18bit TFT */ +#define LCD_CFG_MODE_TFT_16BIT (0 << 7) /* 16bit TFT */ +#define LCD_CFG_MODE_TFT_24BIT (1 << 6) /* 24bit TFT */ +#define LCD_CFG_PDW_BIT 4 /* STN pins utilization */ +#define LCD_CFG_PDW_MASK (0x3 << LCD_DEV_PDW_BIT) +#define LCD_CFG_PDW_1 (0 << LCD_CFG_PDW_BIT) /* LCD_D[0] */ + #define LCD_CFG_PDW_2 (1 << LCD_CFG_PDW_BIT) /* LCD_D[0:1] */ + #define LCD_CFG_PDW_4 (2 << LCD_CFG_PDW_BIT) /* LCD_D[0:3]/LCD_D[8:11] */ + #define LCD_CFG_PDW_8 (3 << LCD_CFG_PDW_BIT) /* LCD_D[0:7]/LCD_D[8:15] */ +#define LCD_CFG_MODE_BIT 0 /* Display Device Mode Select */ +#define LCD_CFG_MODE_MASK (0x0f << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_GENERIC_TFT (0 << LCD_CFG_MODE_BIT) /* 16,18 bit TFT */ + #define LCD_CFG_MODE_SPECIAL_TFT_1 (1 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_SPECIAL_TFT_2 (2 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_SPECIAL_TFT_3 (3 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_NONINTER_CCIR656 (4 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_INTER_CCIR656 (6 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_SINGLE_CSTN (8 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_SINGLE_MSTN (9 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_DUAL_CSTN (10 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_DUAL_MSTN (11 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_SERIAL_TFT (12 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_LCM (13 << LCD_CFG_MODE_BIT) + #define LCD_CFG_MODE_SLCD LCD_CFG_MODE_LCM + +/* LCD Control Register */ +#define LCD_CTRL_BST_BIT 28 /* Burst Length Selection */ +#define LCD_CTRL_BST_MASK (0x03 << LCD_CTRL_BST_BIT) + #define LCD_CTRL_BST_4 (0 << LCD_CTRL_BST_BIT) /* 4-word */ + #define LCD_CTRL_BST_8 (1 << LCD_CTRL_BST_BIT) /* 8-word */ + #define LCD_CTRL_BST_16 (2 << LCD_CTRL_BST_BIT) /* 16-word */ + #define LCD_CTRL_BST_32 (3 << LCD_CTRL_BST_BIT) /* 32-word */ +#define LCD_CTRL_RGB565 (0 << 27) /* RGB565 mode(foreground 0 in OSD mode) */ +#define LCD_CTRL_RGB555 (1 << 27) /* RGB555 mode(foreground 0 in OSD mode) */ +#define LCD_CTRL_OFUP (1 << 26) /* Output FIFO underrun protection enable */ +#define LCD_CTRL_FRC_BIT 24 /* STN FRC Algorithm Selection */ +#define LCD_CTRL_FRC_MASK (0x03 << LCD_CTRL_FRC_BIT) + #define LCD_CTRL_FRC_16 (0 << LCD_CTRL_FRC_BIT) /* 16 grayscale */ + #define LCD_CTRL_FRC_4 (1 << LCD_CTRL_FRC_BIT) /* 4 grayscale */ + #define LCD_CTRL_FRC_2 (2 << LCD_CTRL_FRC_BIT) /* 2 grayscale */ +#define LCD_CTRL_PDD_BIT 16 /* Load Palette Delay Counter */ +#define LCD_CTRL_PDD_MASK (0xff << LCD_CTRL_PDD_BIT) +#define LCD_CTRL_VGA (1 << 15) /* VGA interface enable */ +#define LCD_CTRL_DACTE (1 << 14) /* DAC loop back test */ +#define LCD_CTRL_EOFM (1 << 13) /* EOF interrupt mask */ +#define LCD_CTRL_SOFM (1 << 12) /* SOF interrupt mask */ +#define LCD_CTRL_OFUM (1 << 11) /* Output FIFO underrun interrupt mask */ +#define LCD_CTRL_IFUM0 (1 << 10) /* Input FIFO 0 underrun interrupt mask */ +#define LCD_CTRL_IFUM1 (1 << 9) /* Input FIFO 1 underrun interrupt mask */ +#define LCD_CTRL_LDDM (1 << 8) /* LCD disable done interrupt mask */ +#define LCD_CTRL_QDM (1 << 7) /* LCD quick disable done interrupt mask */ +#define LCD_CTRL_BEDN (1 << 6) /* Endian selection */ +#define LCD_CTRL_PEDN (1 << 5) /* Endian in byte:0-msb first, 1-lsb first */ +#define LCD_CTRL_DIS (1 << 4) /* Disable indicate bit */ +#define LCD_CTRL_ENA (1 << 3) /* LCD enable bit */ +#define LCD_CTRL_BPP_BIT 0 /* Bits Per Pixel */ +#define LCD_CTRL_BPP_MASK (0x07 << LCD_CTRL_BPP_BIT) + #define LCD_CTRL_BPP_1 (0 << LCD_CTRL_BPP_BIT) /* 1 bpp */ + #define LCD_CTRL_BPP_2 (1 << LCD_CTRL_BPP_BIT) /* 2 bpp */ + #define LCD_CTRL_BPP_4 (2 << LCD_CTRL_BPP_BIT) /* 4 bpp */ + #define LCD_CTRL_BPP_8 (3 << LCD_CTRL_BPP_BIT) /* 8 bpp */ + #define LCD_CTRL_BPP_16 (4 << LCD_CTRL_BPP_BIT) /* 15/16 bpp */ + #define LCD_CTRL_BPP_18_24 (5 << LCD_CTRL_BPP_BIT) /* 18/24/32 bpp */ + #define LCD_CTRL_BPP_CMPS_24 (6 << LCD_CTRL_BPP_BIT) /* 24 compress bpp */ + +/* LCD Status Register */ +#define LCD_STATE_QD (1 << 7) /* Quick Disable Done */ +#define LCD_STATE_EOF (1 << 5) /* EOF Flag */ +#define LCD_STATE_SOF (1 << 4) /* SOF Flag */ +#define LCD_STATE_OFU (1 << 3) /* Output FIFO Underrun */ +#define LCD_STATE_IFU0 (1 << 2) /* Input FIFO 0 Underrun */ +#define LCD_STATE_IFU1 (1 << 1) /* Input FIFO 1 Underrun */ +#define LCD_STATE_LDD (1 << 0) /* LCD Disabled */ + +/* OSD Configure Register */ +#define LCD_OSDC_SOFM1 (1 << 15) /* Start of frame interrupt mask for foreground 1 */ +#define LCD_OSDC_EOFM1 (1 << 14) /* End of frame interrupt mask for foreground 1 */ +#define LCD_OSDC_SOFM0 (1 << 11) /* Start of frame interrupt mask for foreground 0 */ +#define LCD_OSDC_EOFM0 (1 << 10) /* End of frame interrupt mask for foreground 0 */ +#define LCD_OSDC_F1EN (1 << 4) /* enable foreground 1 */ +#define LCD_OSDC_F0EN (1 << 3) /* enable foreground 0 */ +#define LCD_OSDC_ALPHAEN (1 << 2) /* enable alpha blending */ +#define LCD_OSDC_ALPHAMD (1 << 1) /* alpha blending mode */ +#define LCD_OSDC_OSDEN (1 << 0) /* OSD mode enable */ + +/* OSD Controll Register */ +#define LCD_OSDCTRL_IPU (1 << 15) /* input data from IPU */ +#define LCD_OSDCTRL_RGB565 (0 << 4) /* foreground 1, 16bpp, 0-RGB565, 1-RGB555 */ +#define LCD_OSDCTRL_RGB555 (1 << 4) /* foreground 1, 16bpp, 0-RGB565, 1-RGB555 */ +#define LCD_OSDCTRL_CHANGES (1 << 3) /* Change size flag */ +#define LCD_OSDCTRL_OSDBPP_BIT 0 /* Bits Per Pixel of OSD Channel 1 */ +#define LCD_OSDCTRL_OSDBPP_MASK (0x7< + * + * 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__ */ diff --git a/include/asm-mips/mach-jz4750d/war.h b/include/asm-mips/mach-jz4750d/war.h new file mode 100644 index 0000000..3a5bc17 --- /dev/null +++ b/include/asm-mips/mach-jz4750d/war.h @@ -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 + */ +#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 */ diff --git a/include/asm-mips/sizes.h b/include/asm-mips/sizes.h new file mode 100644 index 0000000..503843d --- /dev/null +++ b/include/asm-mips/sizes.h @@ -0,0 +1,56 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* DO NOT EDIT!! - this file automatically generated + * from .s file by awk -f s2h.awk + */ +/* Size definitions + * Copyright (C) ARM Limited 1998. All rights reserved. + */ + +#ifndef __sizes_h +#define __sizes_h 1 + +/* handy sizes */ +#define SZ_16 0x00000010 +#define SZ_256 0x00000100 +#define SZ_512 0x00000200 + +#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 + +/* END */ diff --git a/include/linux/i2c-dev.h b/include/linux/i2c-dev.h index fd53bfd..b52bba3 100644 --- a/include/linux/i2c-dev.h +++ b/include/linux/i2c-dev.h @@ -49,7 +49,8 @@ #define I2C_PEC 0x0708 /* != 0 to use PEC with SMBus */ #define I2C_SMBUS 0x0720 /* SMBus transfer */ - +#define I2C_SET_SUB_ADDRESS 0x0730 /* SMBus transfer */ +#define I2C_SET_CLOCK 0x0731 /* SMBus transfer */ /* This is the structure as used in the I2C_SMBUS ioctl call */ struct i2c_smbus_ioctl_data { @@ -71,4 +72,5 @@ struct i2c_rdwr_ioctl_data { #define I2C_MAJOR 89 /* Device major number */ #endif +extern void i2c_jz_setclk(unsigned int i2cclk); #endif /* _LINUX_I2C_DEV_H */ diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 4030eba..4af8f35 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -53,6 +53,10 @@ extern void nand_wait_ready(struct mtd_info *mtd); * bits in one go. */ /* Select the chip by setting nCE to low */ +#define NAND_NCE1 0x08 +#define NAND_NCE2 0x10 +#define NAND_NCE3 0x20 +#define NAND_NCE4 0x40 #define NAND_NCE 0x01 /* Select the command latch by setting CLE to high */ #define NAND_CLE 0x02 diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h index b70313d..4dbcf23 100644 --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h @@ -38,6 +38,17 @@ struct mtd_partition { char *name; /* identifier string */ uint64_t size; /* partition size */ uint64_t offset; /* offset within the master MTD space */ + + char cpu_mode; + /* flag to specify whether the partition works with cpu mode, 0: + * dma mode, 1:cpu mode */ + char use_planes; + /* flag to specify whether multiple planes of NAND is used in + * the partition, 0:don't use planes, 1: use planes */ + uint32_t mtdblock_jz_invalid; + /* flag to specify whether the partition works over mtdblock-jz, + * 0: over mtdblock-jz, 1: not over mtdblock-jz */ + uint32_t mask_flags; /* master MTD flags to mask out for this partition */ struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/ }; diff --git a/include/linux/vt.h b/include/linux/vt.h index 02c1c02..edbb787 100644 --- a/include/linux/vt.h +++ b/include/linux/vt.h @@ -18,10 +18,16 @@ extern int unregister_vt_notifier(struct notifier_block *nb); * resizing). */ #define MIN_NR_CONSOLES 1 /* must be at least 1 */ -#define MAX_NR_CONSOLES 63 /* serial lines start at 64 */ -#define MAX_NR_USER_CONSOLES 63 /* must be root to allocate above this */ - /* Note: the ioctl VT_GETSTATE does not work for - consoles 16 and higher (since it returns a short) */ + +#if defined(CONFIG_JZSOC) +#define MAX_NR_CONSOLES 2 +#define MAX_NR_USER_CONSOLES 2 +#else +#define MAX_NR_CONSOLES 63 /* serial lines start at 64 */ +#define MAX_NR_USER_CONSOLES 63 /* must be root to allocate above this */ + /* Note: the ioctl VT_GETSTATE does not work for + consoles 16 and higher (since it returns a short) */ +#endif /* 0x56 is 'V', to avoid collision with termios and kd */ diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index be51ae2..0d0b953 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -30,6 +30,14 @@ struct mtd_oob_buf64 { __u64 usr_ptr; }; +struct mtd_page_buf { + uint32_t start; //page start address + uint32_t ooblength; + uint32_t datlength; + unsigned char __user *oobptr; + unsigned char __user *datptr; +}; + #define MTD_ABSENT 0 #define MTD_RAM 1 #define MTD_ROM 2 @@ -42,6 +50,8 @@ struct mtd_oob_buf64 { #define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */ #define MTD_NO_ERASE 0x1000 /* No erase necessary */ #define MTD_POWERUP_LOCK 0x2000 /* Always locked after reset */ +#define MTD_MTDBLOCK_JZ_INVALID 0x4000 /* Device doesn't works over mtdblock-jz */ +#define MTD_NAND_CPU_MODE 0x8000 /* Using cpu mode for NAND */ // Some common devices / combinations of capabilities #define MTD_CAP_ROM 0 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/oss/Kconfig b/sound/oss/Kconfig index bcf2a06..558e4aa 100644 --- a/sound/oss/Kconfig +++ b/sound/oss/Kconfig @@ -5,6 +5,91 @@ # # Prompt user for primary drivers. +config OSS_OBSOLETE + bool "Obsolete OSS drivers" + depends on SOUND_PRIME + help + This option enables support for obsolete OSS drivers that + are scheduled for removal in the near future. + + Please contact Adrian Bunk if you had to + say Y here because your hardware is not properly supported + by ALSA. + + If unsure, say N. + +config SOUND_JZ_AC97 + bool "Jz On-Chip AC97 driver" + depends on OSS_OBSOLETE + help + Say Y here if you have want to select the on-chip AC97 driver + on Jz4730/Jz4740/Jz5730. + +config SOUND_JZ_I2S + bool "Jz On-Chip I2S driver" + depends on OSS_OBSOLETE + help + Say Y here if you have want to select the on-chip I2S driver + on Jz4730/Jz4740/Jz5730. + +config SOUND_JZ_PCM + bool "Jz On-Chip PCM driver" + depends on SOC_JZ4750 + help + Say Y here if you have want to select the on-chip PCM driver + on Jz4750. + +choice + prompt "I2S codec type" + depends on SOUND_JZ_I2S + +config I2S_AK4642EN + bool "AK4642EN" + depends on SOC_JZ4730 + help + Answer Y if you have an external AK4642EN codec. + +config I2S_ICODEC + bool "Internal On-Chip codec" + depends on SOC_JZ4740 + help + Answer Y if you have an internal I2S codec. + +config I2S_DLV + bool "Internal On-Chip codec on Jz4750 or Jz4750d" + depends on SOC_JZ4750 || SOC_JZ4750D + help + Answer Y if you have an internal I2S codec on Jz4750 or Jz4750d. + +endchoice + +choice + prompt "PCM codec type" + depends on SOUND_JZ_PCM + +config PCM_TLV320AIC1106 + bool "TLV320AIC1106" + help + Answer Y if you have an TI tlv320aic 1106 codec. + +endchoice + +config SOUND_BT878 + tristate "BT878 audio dma" + depends on SOUND_PRIME && PCI && OSS_OBSOLETE + ---help--- + Audio DMA support for bt878 based grabber boards. As you might have + already noticed, bt878 is listed with two functions in /proc/pci. + Function 0 does the video stuff (bt848 compatible), function 1 does + the same for audio data. This is a driver for the audio part of + the chip. If you say 'Y' here you get a oss-compatible dsp device + where you can record from. If you want just watch TV you probably + don't need this driver as most TV cards handle sound with a short + cable from the TV card to your sound card's line-in. + + To compile this driver as a module, choose M here: the module will + be called btaudio. + config SOUND_BCM_CS4297A tristate "Crystal Sound CS4297a (for Swarm)" depends on SIBYTE_SWARM @@ -15,6 +100,13 @@ config SOUND_BCM_CS4297A note that CONFIG_KGDB should not be enabled at the same time, since it also attempts to use this UART port. +config SOUND_ICH + tristate "Intel ICH (i8xx) audio support" + depends on SOUND_PRIME && PCI && OSS_OBSOLETE + help + Support for integral audio in Intel's I/O Controller Hub (ICH) + chipset, as used on the 810/820/840 motherboards. + config SOUND_VWSND tristate "SGI Visual Workstation Sound" depends on X86_VISWS @@ -24,6 +116,14 @@ config SOUND_VWSND for more info on this driver's capabilities. +config SOUND_VRC5477 + tristate "NEC Vrc5477 AC97 sound" + depends on SOUND_PRIME && DDB5477 + help + Say Y here to enable sound support for the NEC Vrc5477 chip, an + integrated, multi-function controller chip for MIPS CPUs. Works + with the AC97 codec. + config SOUND_AU1550_AC97 tristate "Au1550/Au1200 AC97 Sound" depends on SOC_AU1550 || SOC_AU1200 @@ -503,7 +603,7 @@ config SOUND_AEDSP16 questions. Read the file and the head of - as well as + as well as to get more information about this driver and its configuration. @@ -553,6 +653,13 @@ config SOUND_WAVEARTIST Say Y here to include support for the Rockwell WaveArtist sound system. This driver is mainly for the NetWinder. +config SOUND_TVMIXER + tristate "TV card (bt848) mixer support" + depends on SOUND_PRIME && I2C && VIDEO_V4L1 && OSS_OBSOLETE + help + Support for audio mixer facilities on the BT848 TV frame-grabber + card. + config SOUND_KAHLUA tristate "XpressAudio Sound Blaster emulation" depends on SOUND_SB diff --git a/sound/oss/Makefile b/sound/oss/Makefile index e0ae4d4..37d5233 100644 --- a/sound/oss/Makefile +++ b/sound/oss/Makefile @@ -9,6 +9,12 @@ obj-$(CONFIG_SOUND_OSS) += sound.o # Please leave it as is, cause the link order is significant ! +obj-$(CONFIG_SOUND_JZ_AC97) += jz_ac97.o ac97_codec.o +obj-$(CONFIG_I2S_AK4642EN) += ak4642en.o +obj-$(CONFIG_I2S_ICODEC) += jzcodec.o jz_i2s.o +obj-$(CONFIG_I2S_DLV) += jzdlv.o jz_i2s.o +obj-$(CONFIG_SOUND_JZ_PCM) += jz_pcm_tlv320aic1106_dma.o + obj-$(CONFIG_SOUND_SH_DAC_AUDIO) += sh_dac_audio.o obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o obj-$(CONFIG_SOUND_PSS) += pss.o ad1848.o mpu401.o diff --git a/sound/oss/ak4642en.c b/sound/oss/ak4642en.c new file mode 100644 index 0000000..b649a7e --- /dev/null +++ b/sound/oss/ak4642en.c @@ -0,0 +1,712 @@ +/* + * linux/sound/oss/ak4642en.c + * + * AKM ak4642en codec chip driver to I2S interface + * + * Copyright (c) 2005-2007 Ingenic Semiconductor Inc. + * Author: + * + * 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. + * + * Because the normal application of AUDIO devices are focused on Little_endian, + * then we only perform the little endian data format in driver. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sound_config.h" + +extern mixer_info info; +extern _old_mixer_info old_info; +extern int abnormal_data_count; + +extern void (*clear_codec_mode)(void); +extern void (*set_codec_gpio_pin)(void); +extern void (*each_time_init_codec)(void); +extern void (*set_codec_record)(void); +extern void (*set_codec_replay)(void); +extern void (*clear_codec_record)(void); +extern void (*clear_codec_replay)(void); +extern void (*set_codec_speed)(int range); +extern void (*codec_mixer_old_info_id_name)(void); +extern void (*codec_mixer_info_id_name)(void); +extern void (*set_codec_volume)(int val); +extern void (*set_codec_mic)(int val); +extern void (*i2s_resume_codec)(void); +extern void (*i2s_suspend_codec)(int wr,int rd); +extern void (*set_replay_hp_or_speaker)(void); + +#define I2S_PDN 68 +#define JACK_PLUG_PIN 83 +#define JACK_PLUG_IRQ (IRQ_GPIO_0 + JACK_PLUG_PIN) + +static int jack_plug_level, old_level; +static unsigned int i2c_addr = 0x26; //AK4642EN device address at I2C bus +static unsigned int i2c_clk = 100000;//AK4642EN 400kHz max,but 100kHz here +static unsigned int spk_hp = 0; +static int codec_volume; + +void set_ak4642en_gpio_pin(void); +void each_time_init_ak4642en(void); +void set_ak4642en_replay(void); +void set_ak4642en_record(void); +void turn_on_ak4642en(void); +void turn_off_ak4642en(void); +void set_ak4642en_speed(int rate); +void reset_ak4642en(void); +void ak4642en_mixer_old_info_id_name(void); +void ak4642en_mixer_info_id_name(void); +void set_ak4642en_bass(int val); +void set_ak4642en_volume(int val); +void set_ak4642en_mic(int val); +void resume_ak4642en(void); +void suspend_ak4642en(int wr,int rd); + +static void write_reg(u8 reg, u8 val) +{ + i2c_open(); + i2c_setclk(i2c_clk); + i2c_write((i2c_addr >> 1), &val, reg, 1); + i2c_close(); +} + +#if 0 +static u8 read_reg(u8 reg) +{ + u8 val; + i2c_open(); + i2c_setclk(i2c_clk); + i2c_read((i2c_addr >> 1), &val, reg, 1); + i2c_close(); + return val; +} + +static u16 i2s_codec_read(u8 reg) +{ + u16 value; + value = read_reg(reg); + return value; +} +#endif + +static void i2s_codec_write(u8 reg, u16 data) +{ + u8 val = data & 0xff; + write_reg(reg, val); +} + +void set_ak4642en_gpio_pin(void) +{ + //set AIC pin to I2S slave mode,only GPIO70,71,77,78 + __gpio_as_output(68); + __gpio_clear_pin(68); + __gpio_as_output(69); + __gpio_clear_pin(69); + __gpio_as_output(70); + __gpio_clear_pin(70); + __gpio_as_input(71); + __gpio_clear_pin(71); + __gpio_as_input(77); + __gpio_clear_pin(77); + __gpio_as_input(78); + __gpio_clear_pin(78); + REG_GPIO_GPALR(2) &= 0xC3FF0CFF; + REG_GPIO_GPALR(2) |= 0x14005000; + //set SCC clock initialization + REG_SCC1_CR(SCC1_BASE) = 0x00000000; + udelay(2); + REG_SCC1_CR(SCC1_BASE) |= 1 << 31; + udelay(2); + + __gpio_as_output(I2S_PDN); + __gpio_set_pin(I2S_PDN); + udelay(5); + __gpio_clear_pin(I2S_PDN); + ndelay(300);//>150ns + __gpio_set_pin(I2S_PDN); + mdelay(1); + //set PLL Master mode + i2s_codec_write(0x01, 0x0008);//master + i2s_codec_write(0x04, 0x006b);//ref:12MHz;BITCLK:64fs;I2S compli + i2s_codec_write(0x05, 0x000b);//sync:48KHz; + i2s_codec_write(0x00, 0x0040);//PMVCM + i2s_codec_write(0x01, 0x0009);//master,PLL enable + mdelay(40); + jack_plug_level = 10; + old_level = 100; + spk_hp = 0; + __gpio_disable_pull(JACK_PLUG_PIN); + udelay(10); + __gpio_as_input(JACK_PLUG_PIN); + jack_plug_level = __gpio_get_pin(JACK_PLUG_PIN); + //i suppose jack_plug_lvel is 1 indicate with HPO + if (jack_plug_level > 1 || jack_plug_level <0) + printk("Audio ak4642en codec Jack plug level is wrong!\n"); + if (jack_plug_level) + __gpio_as_irq_fall_edge(JACK_PLUG_PIN); + else + __gpio_as_irq_rise_edge(JACK_PLUG_PIN); +} + +void clear_ak4642en_mode(void) +{ + spk_hp = 0; + i2s_codec_write(0x01, 0x0008);//master,PLL disable + //free_irq(JACK_PLUG_IRQ, i2s_controller); + __gpio_clear_pin(I2S_PDN); + udelay(2); + REG_SCC1_CR(SCC1_BASE) &= 0 << 31; + udelay(2); +} + +void set_ak4642en_replay(void) +{ + //for poll + /*jack_plug_level is H for SPK,is L for HP*/ + jack_plug_level = __gpio_get_pin(JACK_PLUG_PIN); + if(old_level == jack_plug_level) + return; + old_level = jack_plug_level; + if(spk_hp == 1) + { + if(jack_plug_level == 1) + { + //now HeadPhone output,so clear SPK + i2s_codec_write(0x02, 0x0020); + i2s_codec_write(0x02, 0x0000); + i2s_codec_write(0x00, 0x0040); + } + else + { + //now Speaker output,so clear HP + i2s_codec_write(0x01, 0x0039); + i2s_codec_write(0x01, 0x0009); + i2s_codec_write(0x00, 0x0040); + i2s_codec_write(0x0e, 0x0000); + i2s_codec_write(0x0f, 0x0008); + } + } + spk_hp = 1; + if(jack_plug_level == 1) + { + //for HeadPhone output + i2s_codec_write(0x00, 0x0060); // + i2s_codec_write(0x0f, 0x0009); //5-10 + + i2s_codec_write(0x00, 0x0064); // + i2s_codec_write(0x09, 0x0091);// volume control 0dB + i2s_codec_write(0x0c, 0x0091);// 0dB(right) + //eq off + i2s_codec_write(0x11, 0x0000);//5-10 + i2s_codec_write(0x01, 0x0039); // + + i2s_codec_write(0x01, 0x0079); // + } + else + { + //for Speaker output + i2s_codec_write(0x00, 0x0040); + i2s_codec_write(0x02, 0x0020); + + i2s_codec_write(0x03, 0x0018);//5-10 + i2s_codec_write(0x06, 0x003c); + + i2s_codec_write(0x08, 0x00A1);//5-10 + + i2s_codec_write(0x0b, 0x0040); //5-10 + + i2s_codec_write(0x07, 0x002d); //5-10 + i2s_codec_write(0x09, 0x0091); + i2s_codec_write(0x0c, 0x0091); + //HP volume output value + + i2s_codec_write(0x0a, codec_volume);//5-10 + i2s_codec_write(0x0d, codec_volume);//5-10 + + i2s_codec_write(0x00, 0x0074); + i2s_codec_write(0x02, 0x00a0); + } +} + +void set_ak4642en_record(void) +{ + abnormal_data_count = 0; + i2s_codec_write(0x02, 0x0004); + i2s_codec_write(0x03, 0x0038);// recording volume add + i2s_codec_write(0x06, 0x0000);//for ALC short waiting time + i2s_codec_write(0x08, 0x00e1); + i2s_codec_write(0x0b, 0x0000); + i2s_codec_write(0x07, 0x0021); // ALC on + + i2s_codec_write(0x10, 0x0000);//0x0001 + //i2s_codec_write(0x10, 0x0001);//0x0001 + i2s_codec_write(0x01, 0x0039); //for open pop noise + i2s_codec_write(0x01, 0x0079); + i2s_codec_write(0x00, 0x0065); + mdelay(300); +} + +void clear_ak4642en_replay(void) +{ + //for poll + old_level = 100; + spk_hp = 0; + if(jack_plug_level == 1) + { + //for HeadPhone output + i2s_codec_write(0x01, 0x0039); // for close pop noise + mdelay(300); + i2s_codec_write(0x01, 0x0009); //PLL on I2S + i2s_codec_write(0x07, 0x0001); + i2s_codec_write(0x11, 0x0000); + i2s_codec_write(0x00, 0x0040); + i2s_codec_write(0x0f, 0x0008); // for open pop noise + } + else + { + //for Speaker output + i2s_codec_write(0x02, 0x0020); + i2s_codec_write(0x07, 0x0001); + i2s_codec_write(0x11, 0x0000); + i2s_codec_write(0x02, 0x0000); + i2s_codec_write(0x00, 0x0040); // for close pop noise + } +} + +void clear_ak4642en_record(void) +{ + //for Mic input(Stereo) + i2s_codec_write(0x02, 0x0001); + i2s_codec_write(0x07, 0x0001); + i2s_codec_write(0x11, 0x0000); +} + +void each_time_init_ak4642en(void) +{ + __i2s_disable(); + __i2s_as_slave(); + __i2s_set_sample_size(16); +} + +void set_ak4642en_speed(int rate) +{ + //codec work at frequency + unsigned short speed = 0; + unsigned short val = 0; + switch (rate) + { + case 8000: + speed = 0x00; + if(jack_plug_level == 1) //speaker + { + i2s_codec_write(0x16, 0x0000); + i2s_codec_write(0x17, 0x0000); + i2s_codec_write(0x18, 0x0000); + i2s_codec_write(0x19, 0x0000); + i2s_codec_write(0x1A, 0x0000); + i2s_codec_write(0x1B, 0x0000); + i2s_codec_write(0x1C, 0x0027);//800hz + i2s_codec_write(0x1D, 0x0018); + i2s_codec_write(0x1E, 0x00b2); + i2s_codec_write(0x1F, 0x002f); + i2s_codec_write(0x11, 0x0010); //eq on + } + break; + case 12000: + speed = 0x01; + if(jack_plug_level == 1) + { + i2s_codec_write(0x16, 0x0000); + i2s_codec_write(0x17, 0x0000); + i2s_codec_write(0x18, 0x0000); + i2s_codec_write(0x19, 0x0000); + i2s_codec_write(0x1A, 0x0000); + i2s_codec_write(0x1B, 0x0000); + i2s_codec_write(0x1C, 0x0064); + i2s_codec_write(0x1D, 0x001a); + i2s_codec_write(0x1E, 0x0038); + i2s_codec_write(0x1F, 0x002b); + i2s_codec_write(0x11, 0x0010); //eq on + } + break; + case 16000: + speed = 0x02; + if(jack_plug_level == 1) + { + i2s_codec_write(0x16, 0x00af); + i2s_codec_write(0x17, 0x0020); + i2s_codec_write(0x18, 0x0043); + i2s_codec_write(0x19, 0x001a); + i2s_codec_write(0x1A, 0x00af); + i2s_codec_write(0x1B, 0x0020); + i2s_codec_write(0x1C, 0x00a0); + i2s_codec_write(0x1D, 0x001b); + i2s_codec_write(0x1E, 0x00c0); + i2s_codec_write(0x1F, 0x0028); + i2s_codec_write(0x11, 0x0018); //eq on + } + break; + case 24000: + speed = 0x03; + if(jack_plug_level == 1) + { + i2s_codec_write(0x16, 0x0086); + i2s_codec_write(0x17, 0x0015); + i2s_codec_write(0x18, 0x005d); + i2s_codec_write(0x19, 0x0006); + i2s_codec_write(0x1A, 0x0086); + i2s_codec_write(0x1B, 0x0015); + i2s_codec_write(0x1C, 0x00f5); + i2s_codec_write(0x1D, 0x001c); + i2s_codec_write(0x1E, 0x0016); + i2s_codec_write(0x1F, 0x0026); + i2s_codec_write(0x11, 0x0018); //eq on + } + break; + case 7350: + speed = 0x04; + if(jack_plug_level == 1) + { + i2s_codec_write(0x16, 0x0000); + i2s_codec_write(0x17, 0x0000); + i2s_codec_write(0x18, 0x0000); + i2s_codec_write(0x19, 0x0000); + i2s_codec_write(0x1A, 0x0000); + i2s_codec_write(0x1B, 0x0000); + i2s_codec_write(0x1C, 0x0027); + i2s_codec_write(0x1D, 0x0018); + i2s_codec_write(0x1E, 0x00b2); + i2s_codec_write(0x1F, 0x002f); + i2s_codec_write(0x11, 0x0010); //eq on + } + break; + case 11025: + speed = 0x05; + if(jack_plug_level == 1) + { + i2s_codec_write(0x16, 0x0059); + i2s_codec_write(0x17, 0x000d); + i2s_codec_write(0x18, 0x00cb); + i2s_codec_write(0x19, 0x0037); + i2s_codec_write(0x1A, 0x0059); + i2s_codec_write(0x1B, 0x000d); + i2s_codec_write(0x1C, 0x0046); + i2s_codec_write(0x1D, 0x001e); + i2s_codec_write(0x1E, 0x0074); + i2s_codec_write(0x1F, 0x0023); + i2s_codec_write(0x11, 0x0018); //eq on + } + break; + case 14700: + speed = 0x06; + if(jack_plug_level == 1) + { + i2s_codec_write(0x16, 0x0000); + i2s_codec_write(0x17, 0x0000); + i2s_codec_write(0x18, 0x0000); + i2s_codec_write(0x19, 0x0000); + i2s_codec_write(0x1A, 0x0000); + i2s_codec_write(0x1B, 0x0000); + i2s_codec_write(0x1C, 0x004a); + i2s_codec_write(0x1D, 0x001b); + i2s_codec_write(0x1E, 0x006c); + i2s_codec_write(0x1F, 0x0029); + i2s_codec_write(0x11, 0x0010); //eq on + } + break; + case 22050: + speed = 0x07; + if(jack_plug_level == 1) + { + i2s_codec_write(0x16, 0x002d); + i2s_codec_write(0x17, 0x0017); + i2s_codec_write(0x18, 0x0050); + i2s_codec_write(0x19, 0x0009); + i2s_codec_write(0x1A, 0x002d); + i2s_codec_write(0x1B, 0x0017); + i2s_codec_write(0x1C, 0x00d7); + i2s_codec_write(0x1D, 0x001c); + i2s_codec_write(0x1E, 0x0093); + i2s_codec_write(0x1F, 0x0026); + i2s_codec_write(0x11, 0x0018); //eq on + } + break; + case 32000: + speed = 0x0a; + if(jack_plug_level == 1) + { + i2s_codec_write(0x16, 0x0012); + i2s_codec_write(0x17, 0x0011); + i2s_codec_write(0x18, 0x006e); + i2s_codec_write(0x19, 0x003e); + i2s_codec_write(0x1A, 0x0012); + i2s_codec_write(0x1B, 0x0011); + i2s_codec_write(0x1C, 0x00aa); + i2s_codec_write(0x1D, 0x001d); + i2s_codec_write(0x1E, 0x00ab); + i2s_codec_write(0x1F, 0x0024); + i2s_codec_write(0x11, 0x0018); //eq on + } + break; + case 48000: + speed = 0x0b; + if(jack_plug_level == 1) + { + i2s_codec_write(0x16, 0x0082); + i2s_codec_write(0x17, 0x000c); + i2s_codec_write(0x18, 0x004b); + i2s_codec_write(0x19, 0x0036); + i2s_codec_write(0x1A, 0x0082); + i2s_codec_write(0x1B, 0x000c); + i2s_codec_write(0x1C, 0x0068); + i2s_codec_write(0x1D, 0x001e); + i2s_codec_write(0x1E, 0x0030); + i2s_codec_write(0x1F, 0x0023); + i2s_codec_write(0x11, 0x0018); //eq on + } + break; + case 29400: + speed = 0x0e; + if(jack_plug_level == 1) + { + i2s_codec_write(0x16, 0x003d); + i2s_codec_write(0x17, 0x0012); + i2s_codec_write(0x18, 0x0083); + i2s_codec_write(0x19, 0x0000); + i2s_codec_write(0x1A, 0x003d); + i2s_codec_write(0x1B, 0x0012); + i2s_codec_write(0x1C, 0x0079); + i2s_codec_write(0x1D, 0x001d); + i2s_codec_write(0x1E, 0x000d); + i2s_codec_write(0x1F, 0x0025); + i2s_codec_write(0x11, 0x0018); //eq on + } + break; + case 44100: + speed = 0x0f; + if(jack_plug_level == 1) + { + i2s_codec_write(0x16, 0x0059); + i2s_codec_write(0x17, 0x000d); + i2s_codec_write(0x18, 0x00cb); + i2s_codec_write(0x19, 0x0037); + i2s_codec_write(0x1A, 0x0059); + i2s_codec_write(0x1B, 0x000d); + i2s_codec_write(0x1C, 0x0046); + i2s_codec_write(0x1D, 0x001e); + i2s_codec_write(0x1E, 0x0074); + i2s_codec_write(0x1F, 0x0023); + i2s_codec_write(0x11, 0x0018); //eq on + } + break; + default: + break; + } + val = speed & 0x08; + val = val << 2; + speed = speed & 0x07; + val = val | speed; + i2s_codec_write(0x05, val); +} + +void ak4642en_mixer_old_info_id_name(void) +{ + strncpy(info.id, "AK4642EN", sizeof(info.id)); + strncpy(info.name,"AKM AK4642en codec", sizeof(info.name)); +} + +void ak4642en_mixer_info_id_name(void) +{ + strncpy(old_info.id, "AK4642EN", sizeof(old_info.id)); + strncpy(old_info.name,"AKM AK4642en codec", sizeof(old_info.name)); +} + +void set_ak4642en_volume(int val) +{ + if ( val == 0 ) + codec_volume = 255; + else if ( val > 1 && val <= 10) + codec_volume = 92; + else if ( val > 10 && val <= 20 ) + codec_volume = 67; + else if ( val > 20 && val <= 30 ) + codec_volume = 50; + else if ( val > 30 && val <= 40 ) + codec_volume = 40; + else if ( val > 40 && val <= 50 ) + codec_volume = 30; + else if ( val > 50 && val <= 60 ) + codec_volume = 22; + else if ( val > 60&& val <= 70 ) + codec_volume = 15; + else if ( val > 70 && val <= 80 ) + codec_volume = 8; + else if ( val > 80 && val <= 90 ) + codec_volume = 4; + else if ( val > 90 && val <= 100 ) + codec_volume = 2; + + i2s_codec_write(0x0a, codec_volume); + i2s_codec_write(0x0d, codec_volume); +} + +void set_ak4642en_mic(int val) +{ + int mic_gain; + mic_gain = 241 * val /100; + i2s_codec_write(0x09, mic_gain); + i2s_codec_write(0x0c, mic_gain); +} + +void resume_ak4642en(void) +{ + __gpio_as_output(17); + __gpio_set_pin(17); //enable ak4642 + __gpio_as_output(68); + __gpio_clear_pin(68); + __gpio_as_output(69); + __gpio_clear_pin(69); + __gpio_as_output(70); + __gpio_clear_pin(70); + __gpio_as_input(71); + __gpio_clear_pin(71); + __gpio_as_input(77); + __gpio_clear_pin(77); + __gpio_as_input(78); + __gpio_clear_pin(78); + REG_GPIO_GPALR(2) &= 0xC3FF0CFF; + REG_GPIO_GPALR(2) |= 0x14005000; + //set SCC clock initialization + REG_SCC1_CR(SCC1_BASE) = 0x00000000; + udelay(2); + REG_SCC1_CR(SCC1_BASE) |= 1 << 31; + udelay(2); + __gpio_as_output(I2S_PDN); + __gpio_set_pin(I2S_PDN); + udelay(5); + __gpio_clear_pin(I2S_PDN); + ndelay(300);//>150ns + __gpio_set_pin(I2S_PDN); + mdelay(1); + //set PLL Master mode + i2s_codec_write(0x01, 0x0008);//master + i2s_codec_write(0x04, 0x006b);//ref:12MHz;BITCLK:64fs;I2S compli + i2s_codec_write(0x05, 0x000b);//sync:48KHz; + i2s_codec_write(0x00, 0x0040);//PMVCM + i2s_codec_write(0x01, 0x0009);//master,PLL enable + jack_plug_level = 10; + old_level = 100; + spk_hp = 0; + __gpio_as_input(JACK_PLUG_PIN); + jack_plug_level = __gpio_get_pin(JACK_PLUG_PIN); + //i suppose jack_plug_lvel is 1 indicate with HPO + if(jack_plug_level > 1 || jack_plug_level <0) + printk("Audio ak4642en codec Jack plug level is wrong!\n"); + if(jack_plug_level) + __gpio_as_irq_fall_edge(JACK_PLUG_PIN); + else + __gpio_as_irq_rise_edge(JACK_PLUG_PIN); + + i2s_codec_write(0x00, 0x0065); //for resume power + i2s_codec_write(0x01, 0x0039); //for open pop noise + i2s_codec_write(0x01, 0x0079); + i2s_codec_write(0x0a, codec_volume); + i2s_codec_write(0x0d, codec_volume); +} + +void suspend_ak4642en(int wr,int rd) +{ + if(wr) //playing + { + if(jack_plug_level == 0) + { + i2s_codec_write(0x01, 0x0039); // for close pop noise + mdelay(500); + i2s_codec_write(0x01, 0x0009); //PLL on I2S + i2s_codec_write(0x07, 0x0001); + i2s_codec_write(0x11, 0x0000); + i2s_codec_write(0x00, 0x0040); + i2s_codec_write(0x0f, 0x0008); // for open pop noise + + } + else + { + //for Speaker output + i2s_codec_write(0x02, 0x0020); + i2s_codec_write(0x07, 0x0001); + i2s_codec_write(0x11, 0x0000); + i2s_codec_write(0x02, 0x0000); + i2s_codec_write(0x00, 0x0040); // for close pop noise + } + } + + if(rd) // recording + { + i2s_codec_write(0x02, 0x0001); // 5-11 a1 + i2s_codec_write(0x07, 0x0001); + i2s_codec_write(0x11, 0x0000); + mdelay(300); + } + __gpio_as_output(17); + __gpio_clear_pin(17);//disable ak4642 + __i2s_disable(); +} + +static int __init init_ak4642en(void) +{ + set_codec_gpio_pin = set_ak4642en_gpio_pin; + each_time_init_codec = each_time_init_ak4642en; + clear_codec_mode = clear_ak4642en_mode; + + set_codec_record = set_ak4642en_record; + set_codec_replay = set_ak4642en_replay; + set_replay_hp_or_speaker = set_ak4642en_replay; + + set_codec_speed = set_ak4642en_speed; + clear_codec_record = clear_ak4642en_record; + clear_codec_replay = clear_ak4642en_replay; + + codec_mixer_old_info_id_name = ak4642en_mixer_old_info_id_name; + codec_mixer_info_id_name = ak4642en_mixer_info_id_name; + + set_codec_volume = set_ak4642en_volume; + + set_codec_mic = set_ak4642en_mic; + + i2s_resume_codec = resume_ak4642en; + i2s_suspend_codec = suspend_ak4642en; + printk("---> ak4642en initialization!\n"); + return 0; +} + +static void __exit cleanup_ak4642en(void) +{ + spk_hp = 0; + i2s_codec_write(0x01, 0x0008);//master,PLL disable + //free_irq(JACK_PLUG_IRQ, i2s_controller); + __gpio_clear_pin(I2S_PDN); + udelay(2); + REG_SCC1_CR(SCC1_BASE) &= 0 << 31; + udelay(2); +} + +module_init(init_ak4642en); +module_exit(cleanup_ak4642en); diff --git a/sound/oss/jz_ac97.c b/sound/oss/jz_ac97.c new file mode 100644 index 0000000..698a003 --- /dev/null +++ b/sound/oss/jz_ac97.c @@ -0,0 +1,2252 @@ +/* + * linux/drivers/sound/jz_ac97.c + * + * Jz On-Chip AC97 audio driver. + * + * 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. + * + * Because the normal application of AUDIO devices are focused on Little_endian, + * then we only perform the little endian data format in driver. + * + */ + +#define __NO_VERSION__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include "sound_config.h" + +#define DMA_ID_AC97_TX DMA_ID_AIC_TX +#define DMA_ID_AC97_RX DMA_ID_AIC_RX + +/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */ +#define NR_AC97 2 + +#define STANDARD_SPEED 48000 +#define MAX_RETRY 100 + +static unsigned int k_8000[] = { + 0, 42, 85, 128, 170, 213, +}; + +static unsigned int reload_8000[] = { + 1, 0, 0, 0, 0, 0, +}; + +static unsigned int k_11025[] = { + 0, 58, 117, 176, 234, 37, 96, 154, + 213, 16, 74, 133, 192, 250, 53, 112, + 170, 229, 32, 90, 149, 208, 10, 69, + 128, 186, 245, 48, 106, 165, 224, 26, + 85, 144, 202, 5, 64, 122, 181, 240, + 42, 101, 160, 218, 21, 80, 138, 197, +}; + +static unsigned int reload_11025[] = { + 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, + 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, +}; + +static unsigned int k_16000[] = { + 0, 85, 170, +}; + +static unsigned int reload_16000[] = { + 1, 0, 0, +}; + +static unsigned int k_22050[] = { + 0, 117, 234, 96, 213, 74, 192, 53, + 170, 32, 149, 10, 128, 245, 106, 224, + 85, 202, 64, 181, 42, 160, 21, 138, +}; + +static unsigned int reload_22050[] = { + 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, +}; + +static unsigned int k_24000[] = { + 0, 128, +}; + +static unsigned int reload_24000[] = { + 1, 0, +}; + +static unsigned int k_32000[] = { + 0, 170, 85, +}; + +static unsigned int reload_32000[] = { + 1, 0, 1, +}; + +static unsigned int k_44100[] = { + 0, 234, 213, 192, 170, 149, 128, 106, + 85, 64, 42, 21, +}; + +static unsigned int reload_44100[] = { + 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static unsigned int k_48000[] = { + 0, +}; + +static unsigned int reload_48000[] = { + 1, +}; + + +static unsigned int f_scale_counts[8] = { + 6, 48, 3, 24, 2, 3, 12, 1, +}; + +static int jz_audio_rate; +static char jz_audio_format; +static char jz_audio_channels; +static int jz_audio_k; /* rate expand multiple */ +static int jz_audio_q; /* rate expand compensate */ +static int jz_audio_count; /* total count of voice data */ +static int last_jz_audio_count; + +static int jz_audio_fragments;//unused fragment amount +static int jz_audio_fragstotal; +static int jz_audio_fragsize; +static int jz_audio_dma_tran_count;//bytes count of one DMA transfer + +static unsigned int f_scale_count; +static unsigned int *f_scale_array; +static unsigned int *f_scale_reload; +static unsigned int f_scale_idx; + +static void (*old_mksound)(unsigned int hz, unsigned int ticks); +extern void (*kd_mksound)(unsigned int hz, unsigned int ticks); +extern void jz_set_dma_block_size(int dmanr, int nbyte); +extern void jz_set_dma_dest_width(int dmanr, int nbit); +extern void jz_set_dma_src_width(int dmanr, int nbit); + +static void jz_update_filler(int bits, int channels); + +static void Init_In_Out_queue(int fragstotal,int fragsize); +static void Free_In_Out_queue(int fragstotal,int fragsize); + +static irqreturn_t +jz_ac97_replay_dma_irq(int irqnr, void *dev_id); +static irqreturn_t +jz_ac97_record_dma_irq(int irqnr, void *dev_id); + +static void +(*replay_filler)(unsigned long src_start, int count, int id); +static int +(*record_filler)(unsigned long dst_start, int count, int id); + +static struct file_operations jz_ac97_audio_fops; + +DECLARE_WAIT_QUEUE_HEAD (rx_wait_queue); +DECLARE_WAIT_QUEUE_HEAD (tx_wait_queue); + +struct jz_ac97_controller_info +{ + int io_base; + int dma1; /* play */ + int dma2; /* record */ + + char *name; + + int dev_audio; + struct ac97_codec *ac97_codec[NR_AC97]; + + unsigned short ac97_features; + + int opened1; + int opened2; + + unsigned char *tmp1; /* tmp buffer for sample conversions */ + unsigned char *tmp2; + + spinlock_t lock; + spinlock_t ioctllock; + wait_queue_head_t dac_wait; + wait_queue_head_t adc_wait; + + int nextIn; // byte index to next-in to DMA buffer + int nextOut; // byte index to next-out from DMA buffer + int count; // current byte count in DMA buffer + int finish; // current transfered byte count in DMA buffer + unsigned total_bytes; // total bytes written or read + unsigned blocks; + unsigned error; // over/underrun + + /* We use two devices, because we can do simultaneous play and record. + This keeps track of which device is being used for what purpose; + these are the actual device numbers. */ + int dev_for_play; + int dev_for_record; + + int playing; + int recording; + int patched; + unsigned long rec_buf_size; + unsigned long playback_buf_size; + +#ifdef CONFIG_PM + struct pm_dev *pm; +#endif +}; + +static struct jz_ac97_controller_info *ac97_controller = NULL; + +static int jz_readAC97Reg(struct ac97_codec *dev, u8 reg); +static int jz_writeAC97Reg(struct ac97_codec *dev, u8 reg, u16 data); +static u16 ac97_codec_read(struct ac97_codec *codec, u8 reg); +static void ac97_codec_write(struct ac97_codec *codec, u8 reg, u16 data); + +#define QUEUE_MAX 2 + +typedef struct buffer_queue_s { + int count; + int *id; + spinlock_t lock; +} buffer_queue_t; + +static unsigned long *out_dma_buf = NULL; +static unsigned long *out_dma_pbuf = NULL; +static unsigned long *out_dma_buf_data_count = NULL; +static unsigned long *in_dma_buf = NULL; +static unsigned long *in_dma_pbuf = NULL; +static unsigned long *in_dma_buf_data_count = NULL; + +static buffer_queue_t out_empty_queue; +static buffer_queue_t out_full_queue; +static buffer_queue_t out_busy_queue; + +static buffer_queue_t in_empty_queue; +static buffer_queue_t in_full_queue; +static buffer_queue_t in_busy_queue; + +static int first_record_call = 0; + +static inline int get_buffer_id(struct buffer_queue_s *q) +{ + int r, i; + unsigned long flags; + spin_lock_irqsave(&q->lock, flags); + if (q->count == 0) { + spin_unlock_irqrestore(&q->lock, flags); + return -1; + } + r = *(q->id + 0); + for (i=0;i < q->count-1;i++) + *(q->id + i) = *(q->id + (i+1)); + q->count --; + spin_unlock_irqrestore(&q->lock, flags); + return r; +} + +static inline void put_buffer_id(struct buffer_queue_s *q, int id) +{ + unsigned long flags; + spin_lock_irqsave(&q->lock, flags); + *(q->id + q->count) = id; + q->count ++; + spin_unlock_irqrestore(&q->lock, flags); +} + +static inline int elements_in_queue(struct buffer_queue_s *q) +{ + int r; + unsigned long flags; + spin_lock_irqsave(&q->lock, flags); + r = q->count; + spin_unlock_irqrestore(&q->lock, flags); + return r; +} + +/**************************************************************************** + * Architecture related routines + ****************************************************************************/ +static inline +void audio_start_dma(int chan, void *dev_id, unsigned long phyaddr,int count, int mode) +{ + unsigned long flags; + struct jz_ac97_controller_info * controller = + (struct jz_ac97_controller_info *) dev_id; + //for DSP_GETOPTR + spin_lock_irqsave(&controller->ioctllock, flags); + jz_audio_dma_tran_count = count; + spin_unlock_irqrestore(&controller->ioctllock, flags); + + flags = claim_dma_lock(); + disable_dma(chan); + clear_dma_ff(chan); + set_dma_mode(chan, mode); + set_dma_addr(chan, phyaddr); + if (count == 0) { + count++; + printk(KERN_DEBUG "%s: JzSOC DMA controller can't set dma count zero!\n", + __FUNCTION__); + } + set_dma_count(chan, count); + enable_dma(chan); + release_dma_lock(flags); +} + +static irqreturn_t +jz_ac97_record_dma_irq (int irq, void *dev_id) +{ + struct jz_ac97_controller_info * controller = + (struct jz_ac97_controller_info *) dev_id; + int dma = controller->dma2; + int id1, id2; + unsigned long flags; + + disable_dma(dma); + if (__dmac_channel_address_error_detected(dma)) { + printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); + __dmac_channel_clear_address_error(dma); + } + if (__dmac_channel_transmit_end_detected(dma)) { + __dmac_channel_clear_transmit_end(dma); + + //for DSP_GETIPTR + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes += jz_audio_dma_tran_count; + controller->blocks ++; + spin_unlock_irqrestore(&controller->ioctllock, flags); + + id1 = get_buffer_id(&in_busy_queue); + put_buffer_id(&in_full_queue, id1); + wake_up(&rx_wait_queue); + wake_up(&controller->adc_wait); + if ((id2 = get_buffer_id(&in_empty_queue)) >= 0) { + put_buffer_id(&in_busy_queue, id2); + + *(in_dma_buf_data_count + id2) = *(in_dma_buf_data_count + id1); + audio_start_dma(dma,dev_id, + *(in_dma_pbuf + id2), + *(in_dma_buf_data_count + id2), + DMA_MODE_READ); + } else { + in_busy_queue.count = 0; + } + } + return IRQ_HANDLED; +} + +static irqreturn_t +jz_ac97_replay_dma_irq (int irq, void *dev_id) +{ + struct jz_ac97_controller_info * controller = + (struct jz_ac97_controller_info *) dev_id; + int dma = controller->dma1, id; + unsigned long flags; + + disable_dma(dma); + if (__dmac_channel_address_error_detected(dma)) { + printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); + __dmac_channel_clear_address_error(dma); + } + if (__dmac_channel_transmit_end_detected(dma)) { + __dmac_channel_clear_transmit_end(dma); + //for DSP_GETOPTR + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes += jz_audio_dma_tran_count; + controller->blocks ++; + spin_unlock_irqrestore(&controller->ioctllock, flags); + if ((id = get_buffer_id(&out_busy_queue)) < 0) { + printk(KERN_DEBUG "Strange DMA finish interrupt for AC97 module\n"); + } + + put_buffer_id(&out_empty_queue, id); + if ((id = get_buffer_id(&out_full_queue)) >= 0) { + put_buffer_id(&out_busy_queue, id); + if(*(out_dma_buf_data_count + id) > 0) { + audio_start_dma(dma, dev_id, *(out_dma_pbuf + id), + *(out_dma_buf_data_count + id), + DMA_MODE_WRITE); + } + } else { + out_busy_queue.count = 0; + } + if (elements_in_queue(&out_empty_queue) > 0) { + wake_up(&tx_wait_queue); + wake_up(&controller->dac_wait); + } + } + return IRQ_HANDLED; +} + +/* + * Initialize the onchip AC97 controller + */ +static void jz_ac97_initHw(struct jz_ac97_controller_info *controller) +{ + __ac97_disable(); + __ac97_reset(); + __ac97_enable(); + + __ac97_cold_reset_codec(); + /* wait for a long time to let ac97 controller reset completely, + * otherwise, registers except ACFR will be clear by reset, can't be + * set correctly. + */ + udelay(160); + + __ac97_disable_record(); + __ac97_disable_replay(); + __ac97_disable_loopback(); + + /* Check the trigger threshold reset value to detect version */ + if (((REG_AIC_FR & AIC_FR_TFTH_MASK) >> AIC_FR_TFTH_BIT) == 8) { + printk("JzAC97: patched controller detected.\n"); + controller->patched = 1; + } else { + printk("JzAC97: standard controller detected.\n"); + controller->patched = 0; + } + + /* Set FIFO data size. Which shows valid data bits. + * + */ + __ac97_set_oass(8); + __ac97_set_iass(8); + + __ac97_set_xs_stereo(); + __ac97_set_rs_stereo(); +} + +/* + * Initialize all of in(out)_empty_queue value + */ +static void Init_In_Out_queue(int fragstotal,int fragsize) +{ + int i; + if(out_dma_buf || in_dma_buf) + return; + in_empty_queue.count = fragstotal; + out_empty_queue.count = fragstotal; + + out_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!out_dma_buf) + goto all_mem_err; + out_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!out_dma_pbuf) + goto all_mem_err; + out_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!out_dma_buf_data_count) + goto all_mem_err; + in_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!in_dma_buf) + goto all_mem_err; + in_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!in_dma_pbuf) + goto all_mem_err; + in_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!in_dma_buf_data_count) + goto all_mem_err; + in_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!in_empty_queue.id) + goto all_mem_err; + in_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!in_full_queue.id) + goto all_mem_err; + in_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!in_busy_queue.id) + goto all_mem_err; + out_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!out_empty_queue.id) + goto all_mem_err; + out_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!out_full_queue.id) + goto all_mem_err; + out_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!out_busy_queue.id) + goto all_mem_err; + + for (i=0;i < fragstotal;i++) { + *(in_empty_queue.id + i) = i; + *(out_empty_queue.id + i) = i; + } + + in_full_queue.count = 0; + in_busy_queue.count = 0; + out_busy_queue.count = 0; + out_full_queue.count = 0; + /*alloc DMA buffer*/ + for (i = 0; i < jz_audio_fragstotal; i++) { + *(out_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); + if (*(out_dma_buf + i) == 0) { + printk(" can't allocate required DMA(OUT) buffers.\n"); + goto mem_failed_out; + } + *(out_dma_pbuf + i) = virt_to_phys((void *)(*(out_dma_buf + i))); + } + + for (i = 0; i < jz_audio_fragstotal; i++) { + *(in_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); + if (*(in_dma_buf + i) == 0) { + printk(" can't allocate required DMA(IN) buffers.\n"); + goto mem_failed_in; + } + *(in_dma_pbuf + i) = virt_to_phys((void *)(*(in_dma_buf + i))); + dma_cache_wback_inv(*(in_dma_buf + i), 4096*8);//fragsize + *(in_dma_buf + i) = KSEG1ADDR(*(in_dma_buf + i)); + } + return ; + + all_mem_err: + printk("error:allocate memory occur error!\n"); + return ; + +mem_failed_out: + + for (i = 0; i < jz_audio_fragstotal; i++) { + if(*(out_dma_buf + i)) + free_pages(*(out_dma_buf + i), get_order(fragsize)); + } + return ; + +mem_failed_in: + + for (i = 0; i < jz_audio_fragstotal; i++) { + if(*(in_dma_buf + i)) + free_pages(*(in_dma_buf + i), get_order(fragsize)); + } + return ; + +} +static void Free_In_Out_queue(int fragstotal,int fragsize) +{ + int i; + if(out_dma_buf != NULL) + { + for (i = 0; i < jz_audio_fragstotal; i++) + { + if(*(out_dma_buf + i)) + free_pages(*(out_dma_buf + i), get_order(fragsize)); + *(out_dma_buf + i) = 0; + } + kfree(out_dma_buf); + out_dma_buf = NULL; + } + if(out_dma_pbuf) + { + kfree(out_dma_pbuf); + out_dma_pbuf = NULL; + } + if(out_dma_buf_data_count) + { + kfree(out_dma_buf_data_count); + out_dma_buf_data_count = NULL; + } + if(in_dma_buf) + { + for (i = 0; i < jz_audio_fragstotal; i++) + { + if(*(in_dma_buf + i)) + free_pages(*(in_dma_buf + i), get_order(fragsize)); + *(in_dma_buf + i) = 0; + } + kfree(in_dma_buf); + in_dma_buf = NULL; + } + if(in_dma_pbuf) + { + kfree(in_dma_pbuf); + in_dma_pbuf = NULL; + } + if(in_dma_buf_data_count) + { + kfree(in_dma_buf_data_count); + in_dma_buf_data_count = NULL; + } + if(in_empty_queue.id) + { + kfree(in_empty_queue.id); + in_empty_queue.id = NULL; + } + if(in_full_queue.id) + { + kfree(in_full_queue.id); + in_full_queue.id = NULL; + } + if(in_busy_queue.id) + { + kfree(in_busy_queue.id); + in_busy_queue.id = NULL; + } + if(out_empty_queue.id) + { + kfree(out_empty_queue.id); + out_empty_queue.id = NULL; + } + if(out_full_queue.id) + { + kfree(out_full_queue.id); + out_full_queue.id = NULL; + } + if(out_busy_queue.id) + { + kfree(out_busy_queue.id); + out_busy_queue.id = NULL; + } + + in_empty_queue.count = fragstotal; + out_empty_queue.count = fragstotal; + in_full_queue.count = 0; + in_busy_queue.count = 0; + out_busy_queue.count = 0; + out_full_queue.count = 0; + return ; +} + +/* + * Reset everything + */ +static void +jz_ac97_full_reset(struct jz_ac97_controller_info *controller) +{ + jz_ac97_initHw(controller); +} + + +static void +jz_ac97_mksound(unsigned int hz, unsigned int ticks) +{ +// printk("BEEP - %d %d!\n", hz, ticks); +} + +static int jz_audio_set_speed(int dev, int rate) +{ + /* 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 99999999 ? */ + u32 dacp; + struct ac97_codec *codec=ac97_controller->ac97_codec[0]; + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + + + /* Power down the DAC */ + dacp=ac97_codec_read(codec, AC97_POWER_CONTROL); + ac97_codec_write(codec, AC97_POWER_CONTROL, dacp|0x0200); + /* Load the rate; only 48Khz playback available, read always zero */ + if ((ac97_controller->patched) && (ac97_controller->ac97_features & 1)) + ac97_codec_write(codec, AC97_PCM_FRONT_DAC_RATE, rate); + else + ac97_codec_write(codec, AC97_PCM_FRONT_DAC_RATE, 48000); + + /* Power it back up */ + ac97_codec_write(codec, AC97_POWER_CONTROL, dacp); + + jz_audio_rate = rate; + jz_audio_k = STANDARD_SPEED / rate; + if (rate * jz_audio_k != STANDARD_SPEED) + jz_audio_q = rate / ((STANDARD_SPEED / jz_audio_k) - rate ); + else + jz_audio_q = 0x1fffffff; /* a very big value, don't compensate */ + + switch (rate) { + case 8000: + f_scale_count = f_scale_counts[0]; + f_scale_array = k_8000; + f_scale_reload = reload_8000; + break; + case 11025: + f_scale_count = f_scale_counts[1]; + f_scale_array = k_11025; + f_scale_reload = reload_11025; + break; + case 16000: + f_scale_count = f_scale_counts[2]; + f_scale_array = k_16000; + f_scale_reload = reload_16000; + break; + case 22050: + f_scale_count = f_scale_counts[3]; + f_scale_array = k_22050; + f_scale_reload = reload_22050; + break; + case 24000: + f_scale_count = f_scale_counts[4]; + f_scale_array = k_24000; + f_scale_reload = reload_24000; + break; + case 32000: + f_scale_count = f_scale_counts[5]; + f_scale_array = k_32000; + f_scale_reload = reload_32000; + break; + case 44100: + f_scale_count = f_scale_counts[6]; + f_scale_array = k_44100; + f_scale_reload = reload_44100; + break; + case 48000: + f_scale_count = f_scale_counts[7]; + f_scale_array = k_48000; + f_scale_reload = reload_48000; + break; + } + f_scale_idx = 0; + + return jz_audio_rate; +} + +static int record_fill_1x8_u(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + unsigned char data; + volatile unsigned char *s = (unsigned char *)(*(in_dma_buf + id)); + volatile unsigned char *dp = (unsigned char *)dst_start; + + while (count > 0) { + count -= 2; /* count in dword */ + if ((jz_audio_count++ % jz_audio_k) == 0) { + cnt++; + data = *(s++); + *(dp ++) = data + 0x80; + s++; /* skip the other channel */ + } else { + s += 2; /* skip the redundancy */ + } + if (jz_audio_count - last_jz_audio_count >= jz_audio_q) { + jz_audio_count++; + last_jz_audio_count = jz_audio_count; + count -= 2; + s += 2; + } + } + return cnt; +} + +static int record_fill_2x8_u(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + unsigned char d1, d2; + volatile unsigned char *s = (unsigned char *)(*(in_dma_buf + id)); + volatile unsigned char *dp = (unsigned char *)dst_start; + + while (count > 0) { + count -= 2; + if ((jz_audio_count++ % jz_audio_k) == 0) { + cnt += 2; + d1 = *(s++); + *(dp ++) = d1 + 0x80; + d2 = *(s++); + *(dp ++) = d2 + 0x80; + } else { + s += 2; /* skip the redundancy */ + } + if (jz_audio_count - last_jz_audio_count >= jz_audio_q * 2) { + jz_audio_count += 2; + last_jz_audio_count = jz_audio_count; + count -= 2; + s += 2; + } + } + return cnt; +} + +static int record_fill_1x16_s(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + unsigned short d1; + unsigned short *s = (unsigned short *)(*(in_dma_buf + id)); + unsigned short *dp = (unsigned short *)dst_start; + + while (count > 0) { + count -= 2; /* count in dword */ + if ((jz_audio_count++ % jz_audio_k) == 0) { + cnt += 2; /* count in byte */ + d1 = *(s++); + *(dp ++) = d1; + s++; /* skip the other channel */ + } else { + s += 2; /* skip the redundancy */ + } + if (jz_audio_count - last_jz_audio_count >= jz_audio_q * 2) { + jz_audio_count += 2; + last_jz_audio_count = jz_audio_count; + count -= 2; + s += 2; + } + } + return cnt; +} + +static int record_fill_2x16_s(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + unsigned short d1, d2; + unsigned short *s = (unsigned short *)(*(in_dma_buf + id)); + unsigned short *dp = (unsigned short *)dst_start; + + while (count > 0) { + count -= 2; /* count in dword */ + if ((jz_audio_count++ % jz_audio_k) == 0) { + cnt += 4; /* count in byte */ + d1 = *(s++); + *(dp ++) = d1; + d2 = *(s++); + *(dp ++) = d2; + } else + s += 2; /* skip the redundancy */ + + if (jz_audio_count - last_jz_audio_count >= jz_audio_q * 4) { + jz_audio_count += 4; + last_jz_audio_count = jz_audio_count; + count -= 2; + s += 2; + } + } + return cnt; +} + + +static void replay_fill_1x8_u(unsigned long src_start, int count, int id) +{ + int i, cnt = 0; + unsigned char data; + unsigned char *s = (unsigned char *)src_start; + unsigned char *dp = (unsigned char *)(*(out_dma_buf + id)); + + while (count > 0) { + count--; + jz_audio_count++; + cnt += jz_audio_k; + data = *(s++) - 0x80; + for (i=0;i 0) { + count -= 2; + jz_audio_count += 2; + cnt += 2 * jz_audio_k; + d1 = *(s++) - 0x80; + d2 = *(s++) - 0x80; + for (i=0;i= jz_audio_q * 2) { + cnt += 2 * jz_audio_k; + last_jz_audio_count = jz_audio_count; + for (i=0;i= 0) { + if (f_scale_reload[f_scale_idx]) { + d1 = d2; + d2 = *s++; + if (!count) + break; + count -= 2; + } + d = d1 + (((d2 - d1) * f_scale_array[f_scale_idx]) >> 8); + *dp++ = d; + *dp++ = d; + cnt += 4; + f_scale_idx ++; + if (f_scale_idx >= f_scale_count) + f_scale_idx = 0; + } + *(out_dma_buf_data_count + id) = cnt; +} + +static void replay_fill_2x16_s(unsigned long src_start, int count, int id) +{ + int cnt = 0; + static short d11, d12, d21, d22, d1, d2; + short *s = (short *)src_start; + short *dp = (short *)(*(out_dma_buf + id)); + + d12 = *s++; + d22 = *s++; + count -= 4; + while (count >= 0) { + register unsigned int kvalue; + kvalue = f_scale_array[f_scale_idx]; + if (f_scale_reload[f_scale_idx]) { + d11 = d12; + d12 = *s++; + d21 = d22; + d22 = *s++; + if (!count) + break; + count -= 4; + } + d1 = d11 + (((d12 - d11)*kvalue) >> 8); + d2 = d21 + (((d22 - d21)*kvalue) >> 8); + *dp++ = d1; + *dp++ = d2; + cnt += 4; + f_scale_idx ++; + if (f_scale_idx >= f_scale_count) + f_scale_idx = 0; + } + *(out_dma_buf_data_count + id) = cnt; +} + +static unsigned int jz_audio_set_format(int dev, unsigned int fmt) +{ + switch (fmt) { + case AFMT_U8: + case AFMT_S16_LE: + jz_audio_format = fmt; + jz_update_filler(fmt, jz_audio_channels); + case AFMT_QUERY: + break; + } + return jz_audio_format; +} + +static short jz_audio_set_channels(int dev, short channels) +{ + switch (channels) { + case 1: + __ac97_set_xs_stereo(); // always stereo when recording + __ac97_set_rs_stereo(); // always stereo when recording + jz_audio_channels = channels; + jz_update_filler(jz_audio_format, jz_audio_channels); + break; + case 2: + __ac97_set_xs_stereo(); + __ac97_set_rs_stereo(); + jz_audio_channels = channels; + jz_update_filler(jz_audio_format, jz_audio_channels); + break; + case 0: + break; + } + return jz_audio_channels; +} + + +static void jz_audio_reset(void) +{ + __ac97_disable_replay(); + __ac97_disable_receive_dma(); + __ac97_disable_record(); + __ac97_disable_transmit_dma(); +} + +static int jz_audio_release(struct inode *inode, struct file *file); +static int jz_audio_open(struct inode *inode, struct file *file); +static int jz_audio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static unsigned int jz_audio_poll(struct file *file, + struct poll_table_struct *wait); +static ssize_t jz_audio_write(struct file *file, const char *buffer, + size_t count, loff_t *ppos); +static ssize_t jz_audio_read(struct file *file, char *buffer, + size_t count, loff_t *ppos); + +/* static struct file_operations jz_ac97_audio_fops */ +static struct file_operations jz_ac97_audio_fops = +{ + owner: THIS_MODULE, + open: jz_audio_open, + release: jz_audio_release, + write: jz_audio_write, + read: jz_audio_read, + poll: jz_audio_poll, + ioctl: jz_audio_ioctl +}; + +/* Read / Write AC97 codec registers */ +static inline int jz_out_command_ready(void) +{ + int t2 = 1000; + int done = 0; + + while (! done && t2-- > 0) { + if (REG32(AC97_ACSR) & AIC_ACSR_CADT) { + REG32(AC97_ACSR) &= ~AIC_ACSR_CADT; + done = 1; + } + else + udelay (1); + } + return done; +} + +static inline int jz_in_status_ready(void) +{ + int t2 = 1000; + int done = 0; + + while (! done && t2-- > 0) { + if (REG32(AC97_ACSR) & AIC_ACSR_SADR) { + REG32(AC97_ACSR) &= ~AIC_ACSR_SADR; + done = 1; + } + else { + if (REG32(AC97_ACSR) & AIC_ACSR_RSTO) { + REG32(AC97_ACSR) &= ~AIC_ACSR_RSTO; + printk(KERN_DEBUG "%s: RSTO receive status timeout.\n", + __FUNCTION__); + done = 0; + break; + } + udelay (1); + } + } + return done; +} + +static int jz_readAC97Reg (struct ac97_codec *dev, u8 reg) +{ + u16 value; + + if (reg < 128) { + u8 ret_reg; + __ac97_out_rcmd_addr(reg);//output read addr + if (jz_out_command_ready())//judge if send completely? + while (jz_in_status_ready()) {//judge if receive completely? + ret_reg = __ac97_in_status_addr();//slot1:send addr + value = __ac97_in_data(); + if (ret_reg == reg) + return value; + else { +// printk(KERN_DEBUG "%s: index (0x%02x)->(0x%02x) 0x%x\n", __FUNCTION__, reg, ret_reg, value); + return -EINVAL; + } + } + } + value = __ac97_in_data(); + printk (KERN_DEBUG "timeout while reading AC97 codec (0x%x)\n", reg); + return -EINVAL; +} + +static u16 ac97_codec_read(struct ac97_codec *codec, u8 reg) +{ + int res = jz_readAC97Reg(codec, reg); + int count = 0; + while (res == -EINVAL) { + udelay(1000); + __ac97_warm_reset_codec(); + udelay(1000); + res = jz_readAC97Reg(codec, reg); + count ++; + if (count > MAX_RETRY){ + printk(KERN_WARNING"After try %d when read AC97 codec 0x%x, can't success, give up operate!!\n", + MAX_RETRY, reg); + break; + } + } + return (u16)res; +} + +static int jz_writeAC97Reg (struct ac97_codec *dev, u8 reg, u16 value) +{ + //unsigned long flags; + int done = 0; + + //save_and_cli(flags); + + __ac97_out_wcmd_addr(reg); + __ac97_out_data(value); + if (jz_out_command_ready()) + done = 1; + else + printk (KERN_DEBUG "Tiemout AC97 codec write (0x%X<==0x%X)\n", reg, value); + //restore_flags(flags); + return done; +} +static void ac97_codec_write(struct ac97_codec *codec, u8 reg, u16 data) +{ + int done = jz_writeAC97Reg(codec, reg, data); + int count = 0; + while (done == 0) { + count ++; + udelay (2000); + __ac97_warm_reset_codec(); + udelay(2000); + done = jz_writeAC97Reg(codec, reg, data); + if ( count > MAX_RETRY ){ + printk (KERN_DEBUG " After try %d when write AC97 codec (0x%x), can't sucess, give up!! \n", + MAX_RETRY, reg); + break; + } + } +} + +/* OSS /dev/mixer file operation methods */ + +static int jz_ac97_open_mixdev(struct inode *inode, struct file *file) +{ + int i; + int minor = MINOR(inode->i_rdev); + struct jz_ac97_controller_info *controller = ac97_controller; + + for (i = 0; i < NR_AC97; i++) + if (controller->ac97_codec[i] != NULL && + controller->ac97_codec[i]->dev_mixer == minor) + goto match; + + if (!controller) + return -ENODEV; + + match: + file->private_data = controller->ac97_codec[i]; + + return 0; +} + +static int jz_ac97_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct ac97_codec *codec = (struct ac97_codec *)file->private_data; + + return codec->mixer_ioctl(codec, cmd, arg); +} + +static loff_t jz_ac97_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +static /*const*/ struct file_operations jz_ac97_mixer_fops = { + owner: THIS_MODULE, + llseek: jz_ac97_llseek, + ioctl: jz_ac97_ioctl_mixdev, + open: jz_ac97_open_mixdev, +}; + +/* AC97 codec initialisation. */ +static int __init jz_ac97_codec_init(struct jz_ac97_controller_info *controller) +{ + int num_ac97 = 0; + int ready_2nd = 0; + struct ac97_codec *codec; + unsigned short eid; + int i = 0; + + if (__ac97_codec_is_low_power_mode()) { + printk(KERN_DEBUG "AC97 codec is low power mode, warm reset ...\n"); + __ac97_warm_reset_codec(); + udelay(10); + } + i = 0; + while (!__ac97_codec_is_ready()) { + i++; + if ( i > 100 ) { + printk(KERN_WARNING "AC97 codec not ready, failed init ..\n"); + return -ENODEV; + } + udelay(10); + } + i = 0; + + /* Reset the mixer. */ + for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + if ((codec = kmalloc(sizeof(struct ac97_codec), + GFP_KERNEL)) == NULL) + return -ENOMEM; + memset(codec, 0, sizeof(struct ac97_codec)); + + /* initialize some basic codec information, + other fields will be filled + in ac97_probe_codec */ + codec->private_data = controller; + codec->id = num_ac97; + + codec->codec_read = ac97_codec_read; + codec->codec_write = ac97_codec_write; + + if (ac97_probe_codec(codec) == 0) + break; + + eid = ac97_codec_read(codec, AC97_EXTENDED_ID); + if (eid == 0xFFFF) { + printk(KERN_WARNING "Jz AC97: no codec attached?\n"); + kfree(codec); + break; + } + + controller->ac97_features = eid; + + if (!(eid & 0x0001)) + printk(KERN_WARNING "AC97 codec: only 48Khz playback available.\n"); + else { + printk(KERN_WARNING "AC97 codec: supports variable sample rate.\n"); + /* Enable HPEN: UCB1400 only */ + ac97_codec_write(codec, 0x6a, + ac97_codec_read(codec, 0x6a) | 0x40); + + /* Enable variable rate mode */ + ac97_codec_write(codec, AC97_EXTENDED_STATUS, 9); + ac97_codec_write(codec, + AC97_EXTENDED_STATUS, + ac97_codec_read(codec, AC97_EXTENDED_STATUS) | 0xE800); + /* power up everything, modify this + when implementing power saving */ + ac97_codec_write(codec, + AC97_POWER_CONTROL, + ac97_codec_read(codec, AC97_POWER_CONTROL) & ~0x7f00); + /* wait for analog ready */ + for (i=10; i && ((ac97_codec_read(codec, AC97_POWER_CONTROL) & 0xf) != 0xf); i--) { +// current->state = TASK_UNINTERRUPTIBLE; +// schedule_timeout(HZ/20); + } + + if (!(ac97_codec_read(codec, AC97_EXTENDED_STATUS) & 1)) { + printk(KERN_WARNING "Jz AC97: Codec refused to allow VRA, using 48Khz only.\n"); + controller->ac97_features &= ~1; + } + } + + if ((codec->dev_mixer = + register_sound_mixer(&jz_ac97_mixer_fops, -1)) < 0) { + printk(KERN_ERR "Jz AC97: couldn't register mixer!\n"); + kfree(codec); + break; + } + + controller->ac97_codec[num_ac97] = codec; + + /* if there is no secondary codec at all, don't probe any more */ + if (!ready_2nd) + return num_ac97+1; + } + return num_ac97; +} + +static void jz_update_filler(int format, int channels) +{ +#define TYPE(fmt,ch) (((fmt)<<3) | ((ch)&7)) /* up to 8 chans supported. */ + + + switch (TYPE(format, channels)) { + default: + + case TYPE(AFMT_U8, 1): + if ((ac97_controller->patched) && + (ac97_controller->ac97_features & 1)) { + __aic_enable_mono2stereo(); + __aic_enable_unsignadj(); + jz_set_dma_block_size(ac97_controller->dma1, 16); + jz_set_dma_block_size(ac97_controller->dma2, 16); + } else { + __aic_disable_mono2stereo(); + __aic_disable_unsignadj(); + jz_set_dma_block_size(ac97_controller->dma1, 4); + jz_set_dma_block_size(ac97_controller->dma2, 4); + } + replay_filler = replay_fill_1x8_u; + record_filler = record_fill_1x8_u; + __ac97_set_oass(8); + __ac97_set_iass(8); + jz_set_dma_dest_width(ac97_controller->dma1, 8); + jz_set_dma_src_width(ac97_controller->dma2, 8); + break; + case TYPE(AFMT_U8, 2): + if ((ac97_controller->patched) && + (ac97_controller->ac97_features & 1)) { + __aic_enable_mono2stereo(); + __aic_enable_unsignadj(); + jz_set_dma_block_size(ac97_controller->dma1, 16); + jz_set_dma_block_size(ac97_controller->dma2, 16); + } else { + __aic_disable_mono2stereo(); + __aic_disable_unsignadj(); + jz_set_dma_block_size(ac97_controller->dma1, 4); + jz_set_dma_block_size(ac97_controller->dma2, 4); + } + replay_filler = replay_fill_2x8_u; + record_filler = record_fill_2x8_u; + __ac97_set_oass(8); + __ac97_set_iass(8); + jz_set_dma_dest_width(ac97_controller->dma1, 8); + jz_set_dma_src_width(ac97_controller->dma2, 8); + break; + case TYPE(AFMT_S16_LE, 1): + if ((ac97_controller->patched) && + (ac97_controller->ac97_features & 1)) { + __aic_enable_mono2stereo(); + jz_set_dma_block_size(ac97_controller->dma1, 16); + jz_set_dma_block_size(ac97_controller->dma2, 16); + } else { + __aic_disable_mono2stereo(); + jz_set_dma_block_size(ac97_controller->dma1, 4); + jz_set_dma_block_size(ac97_controller->dma2, 4); + } + __aic_disable_unsignadj(); + replay_filler = replay_fill_1x16_s; + record_filler = record_fill_1x16_s; + __ac97_set_oass(16); + __ac97_set_iass(16); + + jz_set_dma_dest_width(ac97_controller->dma1, 16); + jz_set_dma_src_width(ac97_controller->dma2, 16); + + break; + + case TYPE(AFMT_S16_LE, 2): + if ((ac97_controller->patched) && + (ac97_controller->ac97_features & 1)) { + jz_set_dma_block_size(ac97_controller->dma1, 16); + jz_set_dma_block_size(ac97_controller->dma2, 16); + } else { + jz_set_dma_block_size(ac97_controller->dma1, 4); + jz_set_dma_block_size(ac97_controller->dma2, 4); + } + __aic_disable_mono2stereo(); + __aic_disable_unsignadj(); + replay_filler = replay_fill_2x16_s; + record_filler = record_fill_2x16_s; + __ac97_set_oass(16); + __ac97_set_iass(16); + jz_set_dma_dest_width(ac97_controller->dma1, 16); + jz_set_dma_src_width(ac97_controller->dma2, 16); + break; + } +} + +#ifdef CONFIG_PROC_FS + +extern struct proc_dir_entry *proc_jz_root; + +static int jz_ac97_init_proc(struct jz_ac97_controller_info *controller) +{ + if (!create_proc_read_entry ("ac97", 0, proc_jz_root, + ac97_read_proc, controller->ac97_codec[0])) + return -EIO; + + return 0; +} + +static void jz_ac97_cleanup_proc(struct jz_ac97_controller_info *controller) +{ +} + +#endif + +static void __init attach_jz_ac97(struct jz_ac97_controller_info *controller) +{ + char *name; + int adev; + + name = controller->name; + jz_ac97_initHw(controller); + + /* register /dev/audio ? */ + adev = register_sound_dsp(&jz_ac97_audio_fops, -1); + if (adev < 0) + goto audio_failed; + + /* initialize AC97 codec and register /dev/mixer */ + if (jz_ac97_codec_init(controller) <= 0) + goto mixer_failed; + +#ifdef CONFIG_PROC_FS + if (jz_ac97_init_proc(controller) < 0) { + printk(KERN_ERR "%s: can't create AC97 proc filesystem.\n", name); + goto proc_failed; + } +#endif + + controller->tmp1 = (void *)__get_free_pages(GFP_KERNEL, 8);//4 + if (!controller->tmp1) { + printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name); + goto tmp1_failed; + } + + controller->tmp2 = (void *)__get_free_pages(GFP_KERNEL, 8);//4 + if (!controller->tmp2) { + printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name); + goto tmp2_failed; + } + + if ((controller->dma1 = jz_request_dma(DMA_ID_AC97_TX, "audio dac", + jz_ac97_replay_dma_irq, + IRQF_DISABLED, controller)) < 0) { + printk(KERN_ERR "%s: can't reqeust DMA DAC channel.\n", name); + goto dma1_failed; + } + if ((controller->dma2 = jz_request_dma(DMA_ID_AC97_RX, "audio adc", + jz_ac97_record_dma_irq, + IRQF_DISABLED, controller)) < 0) { + printk(KERN_ERR "%s: can't reqeust DMA ADC channel.\n", name); + goto dma2_failed; + } + + printk("Jz On-Chip AC97 controller registered (DAC: DMA%d/IRQ%d, ADC: DMA%d/IRQ%d)\n", + controller->dma1, get_dma_done_irq(controller->dma1), + controller->dma2, get_dma_done_irq(controller->dma2)); + + old_mksound = kd_mksound; /* see vt.c */ + kd_mksound = jz_ac97_mksound; + controller->dev_audio = adev; + return; + + dma2_failed: + jz_free_dma(controller->dma1); + dma1_failed: + free_pages((unsigned long)controller->tmp2, 8);//4 + tmp2_failed: + free_pages((unsigned long)controller->tmp1, 8);//4 + tmp1_failed: +#ifdef CONFIG_PROC_FS + jz_ac97_cleanup_proc(controller); +#endif + proc_failed: + /* unregister mixer dev */ + mixer_failed: + unregister_sound_dsp(adev); + audio_failed: + return; +} + +static int __init probe_jz_ac97(struct jz_ac97_controller_info **controller) +{ + if ((*controller = kmalloc(sizeof(struct jz_ac97_controller_info), + GFP_KERNEL)) == NULL) { + printk(KERN_ERR "Jz AC97 Controller: out of memory.\n"); + return -ENOMEM; + } + memset( *controller, 0, sizeof(struct jz_ac97_controller_info) ); + (*controller)->name = "Jz AC97 controller"; + (*controller)->opened1 = 0; + (*controller)->opened2 = 0; + + init_waitqueue_head(&(*controller)->adc_wait); + init_waitqueue_head(&(*controller)->dac_wait); + spin_lock_init(&(*controller)->lock); + init_waitqueue_head(&rx_wait_queue); + init_waitqueue_head(&tx_wait_queue); + + return 0; +} + +static void __exit unload_jz_ac97(struct jz_ac97_controller_info *controller) +{ + int adev = controller->dev_audio; + + jz_ac97_full_reset (controller); + + controller->dev_audio = -1; + + if (old_mksound) + kd_mksound = old_mksound; /* Our driver support bell for kb, see vt.c */ + +#ifdef CONFIG_PROC_FS + jz_ac97_cleanup_proc(controller); +#endif + + jz_free_dma(controller->dma1); + jz_free_dma(controller->dma2); + + free_pages((unsigned long)controller->tmp1, 8);//4 + free_pages((unsigned long)controller->tmp2, 8);//4 + + if (adev >= 0) { + //unregister_sound_mixer(audio_devs[adev]->mixer_dev); + unregister_sound_dsp(controller->dev_audio); + } +} + +#ifdef CONFIG_PM + +static int reserve_mastervol, reserve_micvol; +static int reserve_power1, reserve_power2; +static int reserve_power3, reserve_power4; +static int reserve_power5, reserve_power6; + +static int jz_ac97_suspend(struct jz_ac97_controller_info *controller, int state) +{ + struct ac97_codec *codec = controller->ac97_codec[0]; + + /* save codec states */ + reserve_mastervol = ac97_codec_read(codec, 0x0002); + reserve_micvol = ac97_codec_read(codec, 0x000e); + + reserve_power1 = ac97_codec_read(codec, 0x0026); + reserve_power2 = ac97_codec_read(codec, 0x006c); + reserve_power3 = ac97_codec_read(codec, 0x005c); + reserve_power4 = ac97_codec_read(codec, 0x0064); + reserve_power5 = ac97_codec_read(codec, 0x0066); + reserve_power6 = ac97_codec_read(codec, 0x006a); + + /* put codec into power-saving mode */ + ac97_codec_write(codec, 0x5c, 0xffff); + ac97_codec_write(codec, 0x64, 0x0000); + ac97_codec_write(codec, 0x66, 0x0000); + ac97_codec_write(codec, 0x6a, 0x0000); + + ac97_codec_write(codec, 0x6c, 0x0030); + ac97_codec_write(codec, 0x26, 0x3b00); + + ac97_save_state(codec); + __ac97_disable(); + + return 0; +} + +static int jz_ac97_resume(struct jz_ac97_controller_info *controller) +{ + struct ac97_codec *codec = controller->ac97_codec[0]; + + jz_ac97_full_reset(controller); + ac97_probe_codec(codec); + ac97_restore_state(codec); + + ac97_codec_write(codec, 0x0026, reserve_power1); + ac97_codec_write(codec, 0x006c, reserve_power2); + ac97_codec_write(codec, 0x005c, reserve_power3); + ac97_codec_write(codec, 0x0064, reserve_power4); + ac97_codec_write(codec, 0x0066, reserve_power5); + ac97_codec_write(codec, 0x006a, reserve_power6); + + ac97_codec_write(codec, 0x0002, reserve_mastervol); + ac97_codec_write(codec, 0x000e, reserve_micvol); + + /* Enable variable rate mode */ + ac97_codec_write(codec, AC97_EXTENDED_STATUS, 9); + ac97_codec_write(codec, + AC97_EXTENDED_STATUS, + ac97_codec_read(codec, AC97_EXTENDED_STATUS) | 0xE800); + + return 0; +} + +static int jz_ac97_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data) +{ + int ret; + struct jz_ac97_controller_info *controller = pm_dev->data; + + if (!controller) return -EINVAL; + + switch (req) { + case PM_SUSPEND: + ret = jz_ac97_suspend(controller, (int)data); + break; + + case PM_RESUME: + ret = jz_ac97_resume(controller); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} +#endif /* CONFIG_PM */ + +static int __init init_jz_ac97(void) +{ + int errno; + + if ((errno = probe_jz_ac97(&ac97_controller)) < 0) + return errno; + attach_jz_ac97(ac97_controller); + + out_empty_queue.id = NULL; + out_full_queue.id = NULL; + out_busy_queue.id = NULL; + + in_empty_queue.id = NULL; + in_full_queue.id = NULL; + in_busy_queue.id = NULL; + +#ifdef CONFIG_PM + ac97_controller->pm = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, + jz_ac97_pm_callback); + if (ac97_controller->pm) + ac97_controller->pm->data = ac97_controller; +#endif + + return 0; +} + +static void __exit cleanup_jz_ac97(void) +{ + unload_jz_ac97(ac97_controller); +} + +module_init(init_jz_ac97); +module_exit(cleanup_jz_ac97); + + +static int drain_adc(struct jz_ac97_controller_info *ctrl, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count; + unsigned tmo; + + add_wait_queue(&ctrl->adc_wait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&ctrl->lock, flags); + count = get_dma_residue(ctrl->dma2); + spin_unlock_irqrestore(&ctrl->lock, flags); + if (count <= 0) + break; + + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&ctrl->adc_wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (PAGE_SIZE * HZ) / STANDARD_SPEED; + tmo *= jz_audio_k * (jz_audio_format == AFMT_S16_LE) ? 2 : 4; + tmo /= jz_audio_channels; + } + remove_wait_queue(&ctrl->adc_wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +static int drain_dac(struct jz_ac97_controller_info *ctrl, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count; + unsigned tmo; + + add_wait_queue(&(ctrl->dac_wait), &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&ctrl->lock, flags); + count = get_dma_residue(ctrl->dma1); + spin_unlock_irqrestore(&ctrl->lock, flags); + if (count <= 0 && elements_in_queue(&out_full_queue) <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&ctrl->dac_wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (PAGE_SIZE * HZ) / STANDARD_SPEED; + tmo *= jz_audio_k * (jz_audio_format == AFMT_S16_LE) ? 2 : 4; + tmo /= jz_audio_channels; + } + remove_wait_queue(&ctrl->dac_wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* + * Audio operation routines implementation + */ +static int jz_audio_release(struct inode *inode, struct file *file) +{ + struct jz_ac97_controller_info *controller = + (struct jz_ac97_controller_info *) file->private_data; + unsigned long flags; + + if (file->f_mode & FMODE_WRITE) { + controller->opened1 = 0; + drain_dac(controller, file->f_flags & O_NONBLOCK); + disable_dma(controller->dma1); + set_dma_count(controller->dma1, 0); + __ac97_disable_transmit_dma(); + __ac97_disable_replay(); + + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes = 0; + controller->count = 0; + controller->finish = 0; + jz_audio_dma_tran_count = 0; + controller->blocks = 0; + controller->nextOut = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + + } + if (file->f_mode & FMODE_READ) { + controller->opened2 = 0; + first_record_call = 1; + drain_adc(controller, file->f_flags & O_NONBLOCK); + disable_dma(controller->dma2); + set_dma_count(controller->dma2, 0); + __ac97_disable_receive_dma(); + __ac97_disable_record(); + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes = 0; + jz_audio_dma_tran_count = 0; + controller->count = 0; + controller->finish = 0; + controller->blocks = 0; + controller->nextIn = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + + } + Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); + return 0; +} + +static int jz_audio_open(struct inode *inode, struct file *file) +{ + struct jz_ac97_controller_info *controller= ac97_controller; + + if (controller == NULL) + return -ENODEV; + + if (file->f_mode & FMODE_WRITE) { + if (controller->opened1 == 1) + return -EBUSY; + controller->opened1 = 1; + //for ioctl + controller->total_bytes = 0; + jz_audio_dma_tran_count = 0; + controller->count = 0; + controller->finish = 0; + controller->blocks = 0; + controller->nextOut = 0; + jz_audio_set_channels(controller->dev_audio, 1); + jz_audio_set_format(controller->dev_audio, AFMT_U8); + jz_audio_set_speed(controller->dev_audio, 8000); + } + + if (file->f_mode & FMODE_READ) { + if (controller->opened2 == 1) + return -EBUSY; + controller->opened2 = 1; + first_record_call = 1; + printk(KERN_DEBUG "You'd better apply 48000Hz 16-bit Stereo for record.\n"); + //for ioctl + controller->total_bytes = 0; + jz_audio_dma_tran_count = 0; + controller->count = 0; + controller->finish = 0; + controller->blocks = 0; + controller->nextIn = 0; + jz_audio_set_channels(controller->dev_audio, 2); + jz_audio_set_format(controller->dev_audio, AFMT_S16_LE); + jz_audio_set_speed(controller->dev_audio, 48000); + } + + last_jz_audio_count = jz_audio_count = 0; + file->private_data = controller; + + jz_audio_fragsize = 8 * PAGE_SIZE; + jz_audio_fragstotal = 4; + Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); + return 0; +} + +static int jz_audio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct jz_ac97_controller_info *controller = + (struct jz_ac97_controller_info *) file->private_data; + int val=0,fullc,busyc,unfinish; + unsigned int flags; + count_info cinfo; + + switch (cmd) + { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_RESET: + jz_audio_reset(); + return 0; + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(controller, file->f_flags & O_NONBLOCK); + return 0; + + case SNDCTL_DSP_SPEED: /* set smaple rate */ + { + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) + jz_audio_set_speed(controller->dev_audio, val); + return put_user(val, (int *)arg); + } + + case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ + if (get_user(val, (int *)arg)) + return -EFAULT; + jz_audio_set_channels(controller->dev_audio, val ? 2 : 1); + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + //return put_user(4*PAGE_SIZE, (int *)arg); + return put_user(jz_audio_fragsize , (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/ + return put_user(AFMT_U8 | AFMT_S16_LE, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Select sample format */ + if (get_user(val, (int *)arg)) + return -EFAULT; + + if (val != AFMT_QUERY) { + jz_audio_set_format(controller->dev_audio,val); + } else { + if (file->f_mode & FMODE_READ) + val = (jz_audio_format == 16) ? + AFMT_S16_LE : AFMT_U8; + else + val = (jz_audio_format == 16) ? + AFMT_S16_LE : AFMT_U8; + } + return put_user(val, (int *)arg); + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + jz_audio_set_channels(controller->dev_audio, val); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_POST: + /* FIXME: the same as RESET ?? */ + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + return 0; + + case SNDCTL_DSP_SETFRAGMENT: + if(out_dma_buf || in_dma_buf) + return -EBUSY; + get_user(val, (long *) arg); + jz_audio_fragsize = 1 << (val & 0xFFFF);//16 least bits + + if (jz_audio_fragsize < 4 * PAGE_SIZE) + jz_audio_fragsize = 4 * PAGE_SIZE; + if (jz_audio_fragsize > (16 * PAGE_SIZE)) //16 PAGE_SIZE + jz_audio_fragsize = 16 * PAGE_SIZE; + jz_audio_fragstotal = (val >> 16) & 0x7FFF; + if (jz_audio_fragstotal < 2) + jz_audio_fragstotal = 2; + if (jz_audio_fragstotal > 32) + jz_audio_fragstotal = 32; + + Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_REALTIME|DSP_CAP_BATCH, (int *)arg); + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return -EINVAL; + + case SNDCTL_DSP_GETOSPACE: + { + audio_buf_info abinfo; + int i, bytes = 0; + + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + //unused fragment amount + spin_lock_irqsave(&controller->ioctllock, flags); + jz_audio_fragments = elements_in_queue(&out_empty_queue); + for (i = 0; i < jz_audio_fragments; i++) + bytes += jz_audio_fragsize; + bytes /= jz_audio_k; + spin_unlock_irqrestore(&controller->ioctllock, flags); + //bytes /= jz_audio_b; + abinfo.fragments = jz_audio_fragments; + abinfo.fragstotal = jz_audio_fragstotal; + abinfo.fragsize = jz_audio_fragsize; + abinfo.bytes = bytes; + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + } + + case SNDCTL_DSP_GETISPACE: + { + audio_buf_info abinfo; + int i, bytes = 0; + + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + //unused fragment amount + jz_audio_fragments = elements_in_queue(&in_empty_queue); + for (i = 0; i < jz_audio_fragments; i++) + bytes += jz_audio_fragsize; + + abinfo.fragments = jz_audio_fragments; + abinfo.fragstotal = jz_audio_fragstotal; + abinfo.fragsize = jz_audio_fragsize; + abinfo.bytes = bytes; + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + } + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && in_dma_buf) //record is at working + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && out_dma_buf) //playback is at working + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + /*if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) + return ret; + start_adc(state); + } else + stop_adc(state); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) + return ret; + start_dac(state); + } else + stop_dac(state); + }*/ + return 0; + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + + spin_lock_irqsave(&controller->ioctllock, flags); + //controller->total_bytes += get_dma_residue(controller->dma2); + cinfo.bytes = controller->total_bytes; + cinfo.blocks = controller->blocks; + cinfo.ptr = controller->nextIn; + controller->blocks = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + spin_lock_irqsave(&controller->ioctllock, flags); + //controller->total_bytes += get_dma_residue(controller->dma1); + cinfo.bytes = controller->total_bytes; + cinfo.blocks = controller->blocks; + cinfo.ptr = controller->nextOut; + controller->blocks = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETODELAY: + { + int id, i; + + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&controller->ioctllock, flags); + unfinish = 0; + fullc = elements_in_queue(&out_full_queue); + busyc = elements_in_queue(&out_busy_queue); + + for(i = 0;i < fullc ;i ++) + { + id = *(out_full_queue.id + i); + unfinish += *(out_dma_buf_data_count + id); + } + for(i = 0;i < busyc ;i ++) + { + id = *(out_busy_queue.id + i); + unfinish += get_dma_residue(controller->dma1); + } + spin_unlock_irqrestore(&controller->ioctllock, flags); + unfinish /= jz_audio_k;//jz_audio_k is jz_audio_b + return put_user(val, (int *)arg); + } + case SOUND_PCM_READ_RATE: + return put_user(jz_audio_rate, (int *)arg); + case SOUND_PCM_READ_CHANNELS: + return put_user(jz_audio_channels, (int *)arg); + case SOUND_PCM_READ_BITS: + return put_user((jz_audio_format & (AFMT_S8 | AFMT_U8)) ? 8 : 16, (int *)arg); + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + return -EINVAL; +} + +static unsigned int jz_audio_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct jz_ac97_controller_info *controller = + (struct jz_ac97_controller_info *) file->private_data; + unsigned long flags; + unsigned int mask = 0; + + if (file->f_mode & FMODE_WRITE) { + if (elements_in_queue(&out_empty_queue) > 0) + return POLLOUT | POLLWRNORM; + poll_wait(file, &controller->dac_wait, wait); + } + if (file->f_mode & FMODE_READ) { + if (elements_in_queue(&in_full_queue) > 0) + return POLLIN | POLLRDNORM; + poll_wait(file, &controller->adc_wait, wait); + } + spin_lock_irqsave(&controller->lock, flags); + if (file->f_mode & FMODE_WRITE) { + if (elements_in_queue(&out_empty_queue) > 0) + mask |= POLLOUT | POLLWRNORM; + } + else if (file->f_mode & FMODE_READ) { + if (elements_in_queue(&in_full_queue) > 0) + mask |= POLLIN | POLLRDNORM; + } + spin_unlock_irqrestore(&controller->lock, flags); + return mask; +} + +static ssize_t jz_audio_read(struct file *file, char *buffer, + size_t count, loff_t *ppos) +{ + struct jz_ac97_controller_info *controller = + (struct jz_ac97_controller_info *) file->private_data; + int id, ret = 0, left_count, copy_count, cnt = 0; + unsigned long flags; + + if (count < 0) + return -EINVAL; + + spin_lock_irqsave(&controller->ioctllock, flags); + controller->nextIn = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); + + if (count < 2*PAGE_SIZE / jz_audio_k) { + copy_count = count * 16 / (jz_audio_channels * jz_audio_format); + } else + copy_count = ((2*PAGE_SIZE / jz_audio_k + 3) / 4) * 4; + left_count = count; + + if (first_record_call) { + first_record_call = 0; + if ((id = get_buffer_id(&in_empty_queue)) >= 0) { + put_buffer_id(&in_busy_queue, id); + *(in_dma_buf_data_count + id) = copy_count * (jz_audio_format/8); + __ac97_enable_receive_dma(); + __ac97_enable_record(); + audio_start_dma(controller->dma2,file->private_data, + *(in_dma_pbuf + id), + *(in_dma_buf_data_count + id), + DMA_MODE_READ); + interruptible_sleep_on(&rx_wait_queue); + } else + BUG(); + } + + while (left_count > 0) { + if (elements_in_queue(&in_full_queue) <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + else + interruptible_sleep_on(&rx_wait_queue); + } + if (signal_pending(current)) + return ret ? ret: -ERESTARTSYS; + + if ((id = get_buffer_id(&in_full_queue)) >= 0) { + cnt = record_filler((unsigned long)controller->tmp2+ret, copy_count, id); + put_buffer_id(&in_empty_queue, id); + } else + BUG(); + + if (elements_in_queue(&in_busy_queue) == 0) { + if ((id=get_buffer_id(&in_empty_queue)) >= 0) { + put_buffer_id(&in_busy_queue, id); + *(in_dma_buf_data_count + id) = copy_count * (jz_audio_format/8); + __ac97_enable_receive_dma(); + __ac97_enable_record(); + audio_start_dma(controller->dma2,file->private_data, + *(in_dma_pbuf + id), + *(in_dma_buf_data_count + id), + DMA_MODE_READ); + } + } + + if (ret + cnt > count) + cnt = count - ret; + + if (copy_to_user(buffer+ret, controller->tmp2+ret, cnt)) + return ret ? ret : -EFAULT; + + ret += cnt; + spin_lock_irqsave(&controller->ioctllock, flags); + controller->nextIn += ret; + spin_unlock_irqrestore(&controller->ioctllock, flags); + left_count -= cnt; + } + if (ret != count) + printk(KERN_DEBUG "%s: count=%d ret=%d jz_audio_count=%d\n", + __FUNCTION__, count, ret, jz_audio_count); + return ret; +} + +static ssize_t jz_audio_write(struct file *file, const char *buffer, + size_t count, loff_t *ppos) +{ + struct jz_ac97_controller_info *controller = + (struct jz_ac97_controller_info *) file->private_data; + int id, ret = 0, left_count, copy_count; + unsigned int flags; + + if (count <= 0) + return -EINVAL; + + spin_lock_irqsave(&controller->ioctllock, flags); + controller->nextOut = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); + + /* The data buffer size of the user space is always a PAGE_SIZE + * scale, so the process can be simplified. + */ + + if ((ac97_controller->patched) && (ac97_controller->ac97_features & 1)) + copy_count = count; + else { + if (count < 2*PAGE_SIZE / jz_audio_k) + copy_count = count; + else + copy_count = ((2*PAGE_SIZE / jz_audio_k + 3) / 4) * 4; + } + left_count = count; + + if (!(ac97_controller->patched) || !(ac97_controller->ac97_features & 1)) { + if (copy_from_user(controller->tmp1, buffer, count)) { + printk(KERN_DEBUG "%s: copy_from_user failed.\n", __FUNCTION__); + return ret ? ret : -EFAULT; + } + } + + while (left_count > 0) { + if (elements_in_queue(&out_empty_queue) == 0) { + if (file->f_flags & O_NONBLOCK) + return ret; + else + interruptible_sleep_on(&tx_wait_queue); + } + + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + + /* the end fragment size in this write */ + if (ret + copy_count > count) + copy_count = count - ret; + + if ((id = get_buffer_id(&out_empty_queue)) >= 0) { + if ((ac97_controller->patched) && + (ac97_controller->ac97_features & 1)) { + if (copy_from_user((char *)(*(out_dma_buf + id)), buffer+ret, copy_count)) { + printk(KERN_DEBUG "%s: copy_from_user failed.\n", __FUNCTION__); + return ret ? ret : -EFAULT; + } + *(out_dma_buf_data_count + id) = copy_count; + } else + replay_filler( + (unsigned long)controller->tmp1 + ret, + copy_count, id); + //when 0,kernel will panic + if(*(out_dma_buf_data_count + id) > 0) { + put_buffer_id(&out_full_queue, id); + dma_cache_wback_inv(*(out_dma_buf + id), *(out_dma_buf_data_count + id)); + } else {//when 0,i need refill in empty queue + put_buffer_id(&out_empty_queue, id); + } + } else + BUG(); + left_count = left_count - copy_count; + ret += copy_count; + + spin_lock_irqsave(&controller->ioctllock, flags); + controller->nextOut += ret; + spin_unlock_irqrestore(&controller->ioctllock, flags); + + if (elements_in_queue(&out_busy_queue) == 0) { + if ((id=get_buffer_id(&out_full_queue)) >= 0) { + put_buffer_id(&out_busy_queue, id); + + __ac97_enable_transmit_dma(); + __ac97_enable_replay(); + if(*(out_dma_buf_data_count + id) > 0) { + audio_start_dma(controller->dma1,file->private_data, + *(out_dma_pbuf + id), + *(out_dma_buf_data_count + id), + DMA_MODE_WRITE); + } + } + } + } + + if (ret != count) + printk(KERN_DEBUG "%s: count=%d ret=%d jz_audio_count=%d\n", + __FUNCTION__, count, ret, jz_audio_count); + return ret; +} + +/* this function for the other codec function */ +struct ac97_codec * find_ac97_codec(void) +{ + if ( ac97_controller ) + return ac97_controller->ac97_codec[0]; + return 0; + +} + diff --git a/sound/oss/jz_i2s.c b/sound/oss/jz_i2s.c new file mode 100644 index 0000000..ab67cae --- /dev/null +++ b/sound/oss/jz_i2s.c @@ -0,0 +1,2894 @@ +/* + * linux/drivers/sound/Jz_i2s.c + * + * JzSOC On-Chip I2S audio driver. + * + * Copyright (C) 2005 by Junzheng Corp. + * Modified by cjfeng on Aug 9,2007,and not any bug on Jz4730 using + * dma channel 4&3,noah is tested. + * + * 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. + * + * Because the normal application of AUDIO devices are focused on Little_endian, + * then we only perform the little endian data format in driver. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sound_config.h" + +#if defined(CONFIG_I2S_DLV) +#include "jzdlv.h" +#endif + +#define DPRINTK(args...) printk(args) +#define DMA_ID_I2S_TX DMA_ID_AIC_TX +#define DMA_ID_I2S_RX DMA_ID_AIC_RX +#define NR_I2S 2 +#define JZCODEC_RW_BUFFER_SIZE 5 +#define JZCODEC_RW_BUFFER_TOTAL 4 + +#define USE_NONE 1 +#define USE_MIC 2 +#define USE_LINEIN 3 + +typedef struct hpvol_shift_s +{ + int hpvol; + int shift; +} hpvol_shift_t; + +mixer_info info; +_old_mixer_info old_info; +int codec_volue_shift; +hpvol_shift_t hpvol_shift_table[72]; +int abnormal_data_count; +unsigned long i2s_clk; + +void (*set_codec_mode)(void) = NULL; +void (*clear_codec_mode)(void) = NULL; +void (*set_codec_gpio_pin)(void) = NULL; +void (*each_time_init_codec)(void) = NULL; +int (*set_codec_startup_param)(void) = NULL; +void (*set_codec_volume_table)(void) = NULL; +void (*set_codec_record)(int mode) = NULL; +void (*set_codec_replay)(void) = NULL; +void (*set_codec_replay_record)(int mode) = NULL; +void (*turn_on_codec)(void) = NULL; +void (*turn_off_codec)(void) = NULL; +void (*set_codec_speed)(int rate) = NULL; +void (*reset_codec)(void) = NULL; +void (*codec_mixer_old_info_id_name)(void) = NULL; +void (*codec_mixer_info_id_name)(void) = NULL; +void (*set_codec_bass)(int val) = NULL; +void (*set_codec_volume)(int val) = NULL; +void (*set_codec_mic)(int val) = NULL; +void (*set_codec_line)(int val) = NULL; +void (*i2s_resume_codec)(void) = NULL; +void (*i2s_suspend_codec)(int wr,int rd) = NULL; +void (*init_codec_pin)(void) = NULL; +void (*set_codec_some_func)(void) = NULL; +void (*clear_codec_record)(void) = NULL; +void (*clear_codec_replay)(void) = NULL; +void (*set_replay_hp_or_speaker)(void) = NULL; +void (*set_codec_direct_mode)(void) = NULL; +void (*clear_codec_direct_mode)(void) = NULL; +void (*set_codec_linein2hp)(void) = NULL; +void (*clear_codec_linein2hp)(void) = NULL; + +static int jz_audio_rate; +static int jz_audio_format; +static int jz_audio_volume; +static int jz_audio_channels; +static int jz_audio_b; /* bits expand multiple */ +static int jz_audio_fragments; /* unused fragment amount */ +static int jz_audio_fragstotal; +static int jz_audio_fragsize; +static int jz_audio_speed; + +static int codec_bass_gain; +static int audio_mix_modcnt; +static int jz_audio_dma_tran_count; /* bytes count of one DMA transfer */ +#if defined(CONFIG_I2S_DLV) +int jz_mic_only = 1; +static int jz_codec_config = 0; +static unsigned long ramp_up_start; +static unsigned long ramp_up_end; +static unsigned long gain_up_start; +static unsigned long gain_up_end; +static unsigned long ramp_down_start; +static unsigned long ramp_down_end; +static unsigned long gain_down_start; +static unsigned long gain_down_end; +#endif + +static int codec_mic_gain; +static int pop_dma_flag; +static int last_dma_buffer_id; +static int drain_flag; +static int use_mic_line_flag; + +static void (*old_mksound)(unsigned int hz, unsigned int ticks); +extern void (*kd_mksound)(unsigned int hz, unsigned int ticks); +static void jz_update_filler(int bits, int channels); + +static int Init_In_Out_queue(int fragstotal,int fragsize); +static int Free_In_Out_queue(int fragstotal,int fragsize); +static irqreturn_t jz_i2s_replay_dma_irq(int irqnr, void *ref); +static irqreturn_t jz_i2s_record_dma_irq(int irqnr, void *ref); +static void (*replay_filler)(signed long src_start, int count, int id); +static int (*record_filler)(unsigned long dst_start, int count, int id); +#if defined(CONFIG_I2S_ICODEC) +static void write_mute_to_dma_buffer(signed long l_sample, signed long r_sample); +#endif +static void jz_audio_reset(void); +static struct file_operations jz_i2s_audio_fops; + +static DECLARE_WAIT_QUEUE_HEAD (rx_wait_queue); +static DECLARE_WAIT_QUEUE_HEAD (tx_wait_queue); +static DECLARE_WAIT_QUEUE_HEAD (drain_wait_queue); +static DECLARE_WAIT_QUEUE_HEAD (pop_wait_queue); + +struct jz_i2s_controller_info +{ + int io_base; + int dma1; /* for play */ + int dma2; /* for record */ + char *name; + int dev_audio; + struct i2s_codec *i2s_codec[NR_I2S]; + int opened1; + int opened2; + unsigned char *tmp1; /* tmp buffer for sample conversions */ + unsigned char *tmp2; + spinlock_t lock; + spinlock_t ioctllock; + + wait_queue_head_t dac_wait; + wait_queue_head_t adc_wait; + int nextIn; /* byte index to next-in to DMA buffer */ + int nextOut; /* byte index to next-out from DMA buffer */ + int count; /* current byte count in DMA buffer */ + int finish; /* current transfered byte count in DMA buffer */ + unsigned total_bytes; /* total bytes written or read */ + unsigned blocks; + unsigned error; /* over/underrun */ +#ifdef CONFIG_PM + struct pm_dev *pm; +#endif +}; + + +static struct jz_i2s_controller_info *i2s_controller = NULL; +struct i2s_codec +{ + /* I2S controller connected with */ + void *private_data; + char *name; + int id; + int dev_mixer; + /* controller specific lower leverl i2s accessing routines */ + u16 (*codec_read) (u8 reg); /* the function accessing Codec REGs */ + void (*codec_write) (u8 reg, u16 val); + /* Wait for codec-ready */ + void (*codec_wait) (struct i2s_codec *codec); + /* OSS mixer masks */ + int modcnt; + int supported_mixers; + int stereo_mixers; + int record_sources; + int bit_resolution; + /* OSS mixer interface */ + int (*read_mixer) (struct i2s_codec *codec, int oss_channel); + void (*write_mixer)(struct i2s_codec *codec, int oss_channel, + unsigned int left, unsigned int right); + int (*recmask_io) (struct i2s_codec *codec, int rw, int mask); + int (*mixer_ioctl)(struct i2s_codec *codec, unsigned int cmd, unsigned long arg); + /* saved OSS mixer states */ + unsigned int mixer_state[SOUND_MIXER_NRDEVICES]; +}; + + +typedef struct buffer_queue_s +{ + int count; + int *id; + int lock; +} buffer_queue_t; + +typedef struct left_right_sample_s +{ + signed long left; + signed long right; +} left_right_sample_t; + +static unsigned long pop_turn_onoff_buf; +static unsigned long pop_turn_onoff_pbuf; + +static unsigned long *out_dma_buf = NULL; +static unsigned long *out_dma_pbuf = NULL; +static unsigned long *out_dma_buf_data_count = NULL; +static unsigned long *in_dma_buf = NULL; +static unsigned long *in_dma_pbuf = NULL; +static unsigned long *in_dma_buf_data_count = NULL; + +static buffer_queue_t out_empty_queue; +static buffer_queue_t out_full_queue; +static buffer_queue_t out_busy_queue; +static buffer_queue_t in_empty_queue; +static buffer_queue_t in_full_queue; +static buffer_queue_t in_busy_queue; +static int first_record_call = 0; + +static left_right_sample_t save_last_samples[64]; + +static inline int get_buffer_id(struct buffer_queue_s *q) +{ + int r; + unsigned long flags; + int i; + + spin_lock_irqsave(&q->lock, flags); + if (q->count == 0) { + spin_unlock_irqrestore(&q->lock, flags); + return -1; + } + r = *(q->id + 0); + for (i=0;i < q->count-1;i++) + *(q->id + i) = *(q->id + (i+1)); + q->count --; + spin_unlock_irqrestore(&q->lock, flags); + + return r; +} + +static inline void put_buffer_id(struct buffer_queue_s *q, int id) +{ + unsigned long flags; + + spin_lock_irqsave(&q->lock, flags); + *(q->id + q->count) = id; + q->count ++; + spin_unlock_irqrestore(&q->lock, flags); +} + +static inline int elements_in_queue(struct buffer_queue_s *q) +{ + int r; + unsigned long flags; + + spin_lock_irqsave(&q->lock, flags); + r = q->count; + spin_unlock_irqrestore(&q->lock, flags); + + return r; +} + +static inline void audio_start_dma(int chan, void *dev_id, unsigned long phyaddr,int count, int mode) +{ + unsigned long flags; + struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; + + spin_lock_irqsave(&controller->ioctllock, flags); + jz_audio_dma_tran_count = count / jz_audio_b; + spin_unlock_irqrestore(&controller->ioctllock, flags); + flags = claim_dma_lock(); + disable_dma(chan); + clear_dma_ff(chan); +#if 1 + jz_set_oss_dma(chan, mode, jz_audio_format); +#else + set_dma_mode(chan, mode); +#endif + set_dma_addr(chan, phyaddr); + if (count == 0) { + count++; + printk("JzSOC DMA controller can't set dma 0 count!\n"); + } + set_dma_count(chan, count); + enable_dma(chan); + release_dma_lock(flags); +} + +static irqreturn_t jz_i2s_record_dma_irq (int irq, void *dev_id) +{ + int id1, id2; + unsigned long flags; + struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; + int dma = controller->dma2; + + disable_dma(dma); + if (__dmac_channel_address_error_detected(dma)) { + printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); + __dmac_channel_clear_address_error(dma); + } + if (__dmac_channel_transmit_end_detected(dma)) { + __dmac_channel_clear_transmit_end(dma); + + if(drain_flag == 1) + wake_up(&drain_wait_queue); + /* for DSP_GETIPTR */ + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes += jz_audio_dma_tran_count; + controller->blocks ++; + spin_unlock_irqrestore(&controller->ioctllock, flags); + id1 = get_buffer_id(&in_busy_queue); + put_buffer_id(&in_full_queue, id1); + + wake_up(&rx_wait_queue); + wake_up(&controller->adc_wait); + if ((id2 = get_buffer_id(&in_empty_queue)) >= 0) { + put_buffer_id(&in_busy_queue, id2); + *(in_dma_buf_data_count + id2) = *(in_dma_buf_data_count + id1); + dma_cache_wback_inv(*(in_dma_buf + id2), *(in_dma_buf_data_count + id2)); + audio_start_dma(dma,dev_id, + *(in_dma_pbuf + id2), + *(in_dma_buf_data_count + id2), + DMA_MODE_READ); + } else + in_busy_queue.count = 0; + } + + return IRQ_HANDLED; +} + +static irqreturn_t jz_i2s_replay_dma_irq (int irq, void *dev_id) +{ + int id; + unsigned long flags; + struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; + int dma = controller->dma1; + + disable_dma(dma); + if (__dmac_channel_address_error_detected(dma)) { + printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); + __dmac_channel_clear_address_error(dma); + } + if (__dmac_channel_transmit_end_detected(dma)) { + __dmac_channel_clear_transmit_end(dma); + + if(pop_dma_flag == 1) { + pop_dma_flag = 0; + wake_up(&pop_wait_queue); + } else { + if(drain_flag == 1) { + /* Is replay dma buffer over ? */ + if(elements_in_queue(&out_full_queue) <= 0) { + drain_flag = 0; + wake_up(&drain_wait_queue); + } + } + + /* for DSP_GETOPTR */ + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes += jz_audio_dma_tran_count; + controller->blocks ++; + spin_unlock_irqrestore(&controller->ioctllock, flags); + if ((id = get_buffer_id(&out_busy_queue)) < 0) + printk(KERN_DEBUG "Strange DMA finish interrupt for I2S module\n"); + put_buffer_id(&out_empty_queue, id); + if ((id = get_buffer_id(&out_full_queue)) >= 0) { + put_buffer_id(&out_busy_queue, id); + if(*(out_dma_buf_data_count + id) > 0) { + audio_start_dma(dma, dev_id, *(out_dma_pbuf + id), + *(out_dma_buf_data_count + id), + DMA_MODE_WRITE); + last_dma_buffer_id = id; + } + } else + out_busy_queue.count = 0; + + if (elements_in_queue(&out_empty_queue) > 0) { + wake_up(&tx_wait_queue); + wake_up(&controller->dac_wait); + } + } + } + + return IRQ_HANDLED; +} + +static void jz_i2s_initHw(int set) +{ +#if defined(CONFIG_MIPS_JZ_URANUS) + i2s_clk = 48000000; +#else + i2s_clk = __cpm_get_i2sclk(); +#endif + __i2s_disable(); + if(set) + __i2s_reset(); + schedule_timeout(5); + if(each_time_init_codec) + each_time_init_codec(); + __i2s_disable_record(); + __i2s_disable_replay(); + __i2s_disable_loopback(); + __i2s_set_transmit_trigger(4); + __i2s_set_receive_trigger(3); +} + +static int Init_In_Out_queue(int fragstotal,int fragsize) +{ + int i; + + /* recording */ + in_empty_queue.count = fragstotal; + in_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!in_dma_buf) + goto all_mem_err; + in_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!in_dma_pbuf) + goto all_mem_err; + in_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!in_dma_buf_data_count) + goto all_mem_err; + in_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!in_empty_queue.id) + goto all_mem_err; + in_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!in_full_queue.id) + goto all_mem_err; + in_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!in_busy_queue.id) + goto all_mem_err; + + for (i=0;i < fragstotal;i++) + *(in_empty_queue.id + i) = i; + in_full_queue.count = 0; + in_busy_queue.count = 0; + + for (i = 0; i < fragstotal; i++) { + *(in_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); + if (*(in_dma_buf + i) == 0) + goto mem_failed_in; + *(in_dma_pbuf + i) = virt_to_phys((void *)(*(in_dma_buf + i))); + dma_cache_wback_inv(*(in_dma_buf + i), fragsize); + } + + /* playing */ + out_empty_queue.count = fragstotal; + out_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!out_dma_buf) + goto all_mem_err; + out_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!out_dma_pbuf) + goto all_mem_err; + out_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + + if (!out_dma_buf_data_count) + goto all_mem_err; + out_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!out_empty_queue.id) + goto all_mem_err; + out_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!out_full_queue.id) + goto all_mem_err; + out_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!out_busy_queue.id) + goto all_mem_err; + for (i=0;i < fragstotal;i++) + *(out_empty_queue.id + i) = i; + + out_busy_queue.count = 0; + out_full_queue.count = 0; + /* alloc DMA buffer */ + for (i = 0; i < fragstotal; i++) { + *(out_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); + if (*(out_dma_buf + i) == 0) { + printk(" can't allocate required DMA(OUT) buffers.\n"); + goto mem_failed_out; + } + *(out_dma_pbuf + i) = virt_to_phys((void *)(*(out_dma_buf + i))); + } + + return 1; +all_mem_err: + printk("error:allocate memory occur error 1!\n"); + return 0; +mem_failed_out: + printk("error:allocate memory occur error 2!\n"); + for (i = 0; i < fragstotal; i++) { + if(*(out_dma_buf + i)) + free_pages(*(out_dma_buf + i), get_order(fragsize)); + } + + return 0; +mem_failed_in: + printk("error:allocate memory occur error 3!\n"); + for (i = 0; i < fragstotal; i++) { + if(*(in_dma_buf + i)) + free_pages(*(in_dma_buf + i), get_order(fragsize)); + } + return 0; +} + +static int Free_In_Out_queue(int fragstotal,int fragsize) +{ + int i; + /* playing */ + if(out_dma_buf != NULL) { + for (i = 0; i < fragstotal; i++) { + if(*(out_dma_buf + i)) + free_pages(*(out_dma_buf + i), get_order(fragsize)); + *(out_dma_buf + i) = 0; + } + kfree(out_dma_buf); + out_dma_buf = NULL; + } + if(out_dma_pbuf) { + kfree(out_dma_pbuf); + out_dma_pbuf = NULL; + } + if(out_dma_buf_data_count) { + kfree(out_dma_buf_data_count); + out_dma_buf_data_count = NULL; + } + if(out_empty_queue.id) { + kfree(out_empty_queue.id); + out_empty_queue.id = NULL; + } + if(out_full_queue.id) { + kfree(out_full_queue.id); + out_full_queue.id = NULL; + } + if(out_busy_queue.id) { + kfree(out_busy_queue.id); + out_busy_queue.id = NULL; + } + out_empty_queue.count = fragstotal; + out_busy_queue.count = 0; + out_full_queue.count = 0; + + /* recording */ + if(in_dma_buf) { + for (i = 0; i < fragstotal; i++) { + if(*(in_dma_buf + i)) { + dma_cache_wback_inv(*(in_dma_buf + i), fragsize); + free_pages(*(in_dma_buf + i), get_order(fragsize)); + } + *(in_dma_buf + i) = 0; + } + kfree(in_dma_buf); + in_dma_buf = NULL; + } + if(in_dma_pbuf) { + kfree(in_dma_pbuf); + in_dma_pbuf = NULL; + } + if(in_dma_buf_data_count) { + kfree(in_dma_buf_data_count); + in_dma_buf_data_count = NULL; + } + if(in_empty_queue.id) { + kfree(in_empty_queue.id); + in_empty_queue.id = NULL; + } + if(in_full_queue.id) { + kfree(in_full_queue.id); + in_full_queue.id = NULL; + } + if(in_busy_queue.id) { + kfree(in_busy_queue.id); + in_busy_queue.id = NULL; + } + + in_empty_queue.count = fragstotal; + in_full_queue.count = 0; + in_busy_queue.count = 0; + + return 1; +} + +static void jz_i2s_full_reset(struct jz_i2s_controller_info *controller) +{ + jz_i2s_initHw(0); +} + +static int jz_audio_set_speed(int dev, int rate) +{ + /* 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 99999999 ? */ + jz_audio_speed = rate; +#if defined(CONFIG_I2S_DLV) + if (rate > 96000) + rate = 96000; +#else + if (rate > 48000) + rate = 48000; +#endif + if (rate < 8000) + rate = 8000; + jz_audio_rate = rate; + + if(set_codec_speed) + set_codec_speed(rate); + + return jz_audio_rate; +} + + +static int record_fill_1x8_u(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + unsigned long data; + volatile unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); + volatile unsigned char *dp = (unsigned char*)dst_start; + + while (count > 0) { + count -= 2; /* count in dword */ + cnt++; + data = *(s++); + *(dp ++) = ((data << 16) >> 24) + 0x80; + s++; /* skip the other channel */ + } + + return cnt; +} + + +static int record_fill_2x8_u(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + unsigned long d1, d2; + volatile unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); + volatile unsigned char *dp = (unsigned char*)dst_start; + + while (count > 0) { + count -= 2; + cnt += 2; + d1 = *(s++); + *(dp ++) = ((d1 << 16) >> 24) + 0x80; + d2 = *(s++); + *(dp ++) = ((d2 << 16) >> 24) + 0x80; + } + + return cnt; +} + + +static int record_fill_1x16_s(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + unsigned long d1; + unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); + unsigned short *dp = (unsigned short *)dst_start; + + while (count > 0) { + count -= 2; /* count in dword */ + cnt += 2; /* count in byte */ + d1 = *(s++); + *(dp ++) = (d1 << 16) >> 16; + s++; /* skip the other channel */ + } + + return cnt; +} + + +static int record_fill_2x16_s(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + unsigned long d1, d2; + unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); + unsigned short *dp = (unsigned short *)dst_start; + while (count > 0) { + count -= 2; /* count in dword */ + cnt += 4; /* count in byte */ + d1 = *(s++); + d2 = *(s++); + if(abnormal_data_count > 0) { + d1 = d2 = 0; + abnormal_data_count --; + } + *(dp ++) = (d1 << 16) >> 16; + *(dp ++) = (d2 << 16) >> 16; + } + + return cnt; +} + +static void replay_fill_1x8_u(signed long src_start, int count, int id) +{ + int cnt = 0; + unsigned char data; + unsigned long ddata; + volatile unsigned char *s = (unsigned char *)src_start; + volatile unsigned long *dp = (unsigned long*)(*(out_dma_buf + id)); + + while (count > 0) { + count--; + cnt += 1; + data = *(s++) - 0x80; + ddata = (unsigned long) data << 8; + *(dp ++) = ddata; + *(dp ++) = ddata; + + /* save last left and right */ + if(count == 1) { + save_last_samples[id].left = ddata; + save_last_samples[id].right = ddata; + } + } + cnt = cnt * 2 * jz_audio_b; + *(out_dma_buf_data_count + id) = cnt; +} + + +static void replay_fill_2x8_u(signed long src_start, int count, int id) +{ + int cnt = 0; + unsigned char d1; + unsigned long dd1; + volatile unsigned char *s = (unsigned char *)src_start; + volatile unsigned long *dp = (unsigned long*)(*(out_dma_buf + id)); + + while (count > 0) { + count -= 1; + cnt += 1 ; + d1 = *(s++) - 0x80; + dd1 = (unsigned long) d1 << 8; + *(dp ++) = dd1; + /* save last left */ + if(count == 2) + save_last_samples[id].left = dd1; + /* save last right */ + if(count == 1) + save_last_samples[id].right = dd1; + } + cnt *= jz_audio_b; + *(out_dma_buf_data_count + id) = cnt; +} + + +static void replay_fill_1x16_s(signed long src_start, int count, int id) +{ + int cnt = 0; + signed short d1; + signed long l1; + volatile signed short *s = (signed short *)src_start; + volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); + + while (count > 0) { + count -= 2; + cnt += 2 ; + d1 = *(s++); + l1 = (signed long)d1; +#if defined(CONFIG_I2S_ICODEC) + l1 >>= codec_volue_shift; +#endif + *(dp ++) = l1; + *(dp ++) = l1; + + /* save last left and right */ + if(count == 1) { + save_last_samples[id].left = l1; + save_last_samples[id].right = l1; + } + } + cnt = cnt * 2 * jz_audio_b; + *(out_dma_buf_data_count + id) = cnt; +} + +static void replay_fill_2x16_s(signed long src_start, int count, int id) +{ + int cnt = 0; + signed short d1; + signed long l1; + volatile signed short *s = (signed short *)src_start; + volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); +#if defined(CONFIG_I2S_ICODEC) + int mute_cnt = 0; + signed long tmp1,tmp2; + volatile signed long *before_dp; + int sam_rate = jz_audio_rate / 20; + + tmp1 = tmp2 = 0; + while (count > 0) { + count -= 2; + cnt += 2; + d1 = *(s++); + + l1 = (signed long)d1; + l1 >>= codec_volue_shift; + + if(l1 == 0) { + mute_cnt ++; + if(mute_cnt >= sam_rate) { + before_dp = dp - 10; + *(before_dp) = (signed long)1; + before_dp = dp - 11; + *(before_dp) = (signed long)1; + mute_cnt = 0; + } + } else + mute_cnt = 0; + + *(dp ++) = l1; + + tmp1 = tmp2; + tmp2 = l1; + } + + /* save last left */ + save_last_samples[id].left = tmp1; + /* save last right */ + save_last_samples[id].right = tmp2; +#endif +#if defined(CONFIG_I2S_DLV) + while (count > 0) { + count -= 2; + cnt += 2; + d1 = *(s++); + + l1 = (signed long)d1; + + *(dp ++) = l1; + } +#endif + cnt *= jz_audio_b; + *(out_dma_buf_data_count + id) = cnt; +} + +static void replay_fill_2x18_s(unsigned long src_start, int count, int id) +{ + int cnt = 0; + signed long d1; + signed long l1; + volatile signed long *s = (signed long *)src_start; + volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); + while (count > 0) { + count -= 4; + cnt += 4; + d1 = *(s++); + l1 = (signed long)d1; + *(dp ++) = l1; + } + + cnt *= jz_audio_b; + *(out_dma_buf_data_count + id) = cnt; +} + +static unsigned int jz_audio_set_format(int dev, unsigned int fmt) +{ + switch (fmt) { + case AFMT_U8: + __i2s_set_oss_sample_size(8); + __i2s_set_iss_sample_size(8); + jz_audio_format = fmt; + jz_update_filler(jz_audio_format,jz_audio_channels); + break; + case AFMT_S16_LE: +#if defined(CONFIG_I2S_DLV) + /* DAC path and ADC path */ + write_codec_file(2, 0x00); + //write_codec_file(2, 0x60); +#endif + jz_audio_format = fmt; + jz_update_filler(jz_audio_format,jz_audio_channels); + __i2s_set_oss_sample_size(16); + __i2s_set_iss_sample_size(16); + break; + case 18: + __i2s_set_oss_sample_size(18); + jz_audio_format = fmt; + jz_update_filler(jz_audio_format,jz_audio_channels); + break; + case AFMT_QUERY: + break; + } + + return jz_audio_format; +} + + +static short jz_audio_set_channels(int dev, short channels) +{ + switch (channels) { + case 1: + if(set_codec_some_func) + set_codec_some_func(); + jz_audio_channels = channels; + jz_update_filler(jz_audio_format, jz_audio_channels); +#if defined(CONFIG_I2S_DLV) + write_codec_file_bit(1, 1, 6);//CR1.MONO->1 for Mono +#endif + break; + case 2: + jz_audio_channels = channels; + jz_update_filler(jz_audio_format, jz_audio_channels); +#if defined(CONFIG_I2S_DLV) + write_codec_file_bit(1, 0, 6);//CR1.MONO->0 for Stereo +#endif + break; + case 0: + break; + } + + return jz_audio_channels; +} + +static void init_codec(void) +{ + /* inititalize internal I2S codec */ + if(init_codec_pin) + init_codec_pin(); + +#if defined(CONFIG_I2S_ICDC) + /* initialize AIC but not reset it */ + jz_i2s_initHw(0); +#endif + if(reset_codec) + reset_codec(); +} + +static void jz_audio_reset(void) +{ + __i2s_disable_replay(); + __i2s_disable_receive_dma(); + __i2s_disable_record(); + __i2s_disable_transmit_dma(); +#if defined(CONFIG_I2S_DLV) + REG_AIC_I2SCR = 0x10; +#endif + init_codec(); +} + +static int jz_audio_release(struct inode *inode, struct file *file); +static int jz_audio_open(struct inode *inode, struct file *file); +static int jz_audio_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg); +static unsigned int jz_audio_poll(struct file *file,struct poll_table_struct *wait); +static ssize_t jz_audio_write(struct file *file, const char *buffer,size_t count, loff_t *ppos); +static ssize_t jz_audio_read(struct file *file, char *buffer,size_t count, loff_t *ppos); + +/* static struct file_operations jz_i2s_audio_fops */ +static struct file_operations jz_i2s_audio_fops = +{ + owner: THIS_MODULE, + open: jz_audio_open, + release: jz_audio_release, + write: jz_audio_write, + read: jz_audio_read, + poll: jz_audio_poll, + ioctl: jz_audio_ioctl +}; + +static int jz_i2s_open_mixdev(struct inode *inode, struct file *file) +{ + int i; + int minor = MINOR(inode->i_rdev); + struct jz_i2s_controller_info *controller = i2s_controller; + + for (i = 0; i < NR_I2S; i++) + if (controller->i2s_codec[i] != NULL && controller->i2s_codec[i]->dev_mixer == minor) + goto match; + + if (!controller) + return -ENODEV; +match: + file->private_data = controller->i2s_codec[i]; + + return 0; +} + +static int jz_i2s_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct i2s_codec *codec = (struct i2s_codec *)file->private_data; + return codec->mixer_ioctl(codec, cmd, arg); +} + +static loff_t jz_i2s_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +static struct file_operations jz_i2s_mixer_fops = +{ + owner: THIS_MODULE, + llseek: jz_i2s_llseek, + ioctl: jz_i2s_ioctl_mixdev, + open: jz_i2s_open_mixdev, +}; + +static int i2s_mixer_ioctl(struct i2s_codec *codec, unsigned int cmd, unsigned long arg) +{ + int ret; + long val = 0; + switch (cmd) { + case SOUND_MIXER_INFO: + + if(codec_mixer_info_id_name) + codec_mixer_info_id_name(); + info.modify_counter = audio_mix_modcnt; + + return copy_to_user((void *)arg, &info, sizeof(info)); + case SOUND_OLD_MIXER_INFO: + + if(codec_mixer_old_info_id_name) + codec_mixer_old_info_id_name(); + + return copy_to_user((void *)arg, &old_info, sizeof(info)); + case SOUND_MIXER_READ_STEREODEVS: + + return put_user(0, (long *) arg); + case SOUND_MIXER_READ_CAPS: + + val = SOUND_CAP_EXCL_INPUT; + return put_user(val, (long *) arg); + case SOUND_MIXER_READ_DEVMASK: + break; + case SOUND_MIXER_READ_RECMASK: + break; + case SOUND_MIXER_READ_RECSRC: + break; + case SOUND_MIXER_WRITE_SPEAKER: + + ret = get_user(val, (long *) arg); + if (ret) + return ret; + val = val & 0xff; + if(val < 0) + val = 0; + if(val > 100) + val = 100; + switch(val) { + case 100: + if(set_codec_direct_mode) + set_codec_direct_mode(); + break; + case 0: + if(clear_codec_direct_mode) + clear_codec_direct_mode(); + break; + } + break; + case SOUND_MIXER_WRITE_BASS: + + ret = get_user(val, (long *) arg); + if (ret) + return ret; + + val = val & 0xff; + if(val < 0) + val = 0; + if(val > 100) + val = 100; + codec_bass_gain = val; + if(set_codec_bass) + set_codec_bass(val); + + return 0; + case SOUND_MIXER_READ_BASS: + + val = codec_bass_gain; + ret = val << 8; + val = val | ret; + + return put_user(val, (long *) arg); + case SOUND_MIXER_WRITE_VOLUME: + ret = get_user(val, (long *) arg); + if (ret) + return ret; + val = val & 0xff; + if(val < 0) + val = 0; + if(val > 100) + val = 100; + + jz_audio_volume = val; + if(set_codec_volume) + set_codec_volume(val); + + return 0; + case SOUND_MIXER_READ_VOLUME: + + val = jz_audio_volume; + ret = val << 8; + val = val | ret; + + return put_user(val, (long *) arg); + + case SOUND_MIXER_WRITE_MIC: + + ret = get_user(val, (long *) arg); + if (ret) + return ret; + + val = val & 0xff; + if(val < 0) + val = 0; + if(val > 100) + val = 100; + codec_mic_gain = val; + use_mic_line_flag = USE_MIC; + if(set_codec_mic) + set_codec_mic(val); + + return 0; + case SOUND_MIXER_READ_MIC: + + val = codec_mic_gain; + ret = val << 8; + val = val | ret; + + return put_user(val, (long *) arg); + + case SOUND_MIXER_WRITE_LINE: + + ret = get_user(val, (long *) arg); + if (ret) + return ret; + + val = val & 0xff; + if(val < 0) + val = 0; + if(val > 100) + val = 100; + use_mic_line_flag = USE_LINEIN; + codec_mic_gain = val; + if(set_codec_line) + set_codec_line(val); + + return 0; + case SOUND_MIXER_READ_LINE: + + val = codec_mic_gain; + ret = val << 8; + val = val | ret; + + return put_user(val, (long *) arg); + default: + return -ENOSYS; + } + audio_mix_modcnt ++; + return 0; +} + + +int i2s_probe_codec(struct i2s_codec *codec) +{ + /* generic OSS to I2S wrapper */ + codec->mixer_ioctl = i2s_mixer_ioctl; + return 1; +} + + +/* I2S codec initialisation. */ +static int __init jz_i2s_codec_init(struct jz_i2s_controller_info *controller) +{ + int num_i2s = 0; + struct i2s_codec *codec; + + for (num_i2s = 0; num_i2s < NR_I2S; num_i2s++) { + if ((codec = kmalloc(sizeof(struct i2s_codec),GFP_KERNEL)) == NULL) + return -ENOMEM; + memset(codec, 0, sizeof(struct i2s_codec)); + codec->private_data = controller; + codec->id = num_i2s; + + if (i2s_probe_codec(codec) == 0) + break; + if ((codec->dev_mixer = register_sound_mixer(&jz_i2s_mixer_fops, -1)) < 0) { + printk(KERN_ERR "Jz I2S: couldn't register mixer!\n"); + kfree(codec); + break; + } + controller->i2s_codec[num_i2s] = codec; + } + return num_i2s; +} + + +static void jz_update_filler(int format, int channels) +{ +#define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3)) + + switch (TYPE(format, channels)) + { + + case TYPE(AFMT_U8, 1): + jz_audio_b = 4; /* 4bytes * 8bits =32bits */ + replay_filler = replay_fill_1x8_u; + record_filler = record_fill_1x8_u; + break; + case TYPE(AFMT_U8, 2): + jz_audio_b = 4; + replay_filler = replay_fill_2x8_u; + record_filler = record_fill_2x8_u; + break; + case TYPE(AFMT_S16_LE, 1): + jz_audio_b = 2; /* 2bytes * 16bits =32bits */ + replay_filler = replay_fill_1x16_s; + record_filler = record_fill_1x16_s; + break; + case TYPE(AFMT_S16_LE, 2): + jz_audio_b = 2; + replay_filler = replay_fill_2x16_s; + record_filler = record_fill_2x16_s; + break; + case TYPE(18, 2): + jz_audio_b = 1; + replay_filler = replay_fill_2x18_s; + record_filler = record_fill_2x16_s; + break; + default: + ; + } +} + + +#ifdef CONFIG_PROC_FS +extern struct proc_dir_entry *proc_jz_root; +int i2s_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + return 0; +} + +static int jz_i2s_init_proc(struct jz_i2s_controller_info *controller) +{ + if (!create_proc_read_entry ("i2s", 0, proc_jz_root, i2s_read_proc, controller->i2s_codec[0])) + return -EIO; + return 0; +} + +static void jz_i2s_cleanup_proc(struct jz_i2s_controller_info *controller) +{ +} +#endif + +static void __init attach_jz_i2s(struct jz_i2s_controller_info *controller) +{ + char *name; + int adev; /* No of Audio device. */ + + name = controller->name; + /* initialize AIC controller and reset it */ + jz_i2s_initHw(1); + adev = register_sound_dsp(&jz_i2s_audio_fops, -1); + if (adev < 0) + goto audio_failed; + /* initialize I2S codec and register /dev/mixer */ + if (jz_i2s_codec_init(controller) <= 0) + goto mixer_failed; + +#ifdef CONFIG_PROC_FS + if (jz_i2s_init_proc(controller) < 0) { + printk(KERN_ERR "%s: can't create I2S proc filesystem.\n", name); + goto proc_failed; + } +#endif + + controller->tmp1 = (void *)__get_free_pages(GFP_KERNEL, 8); + if (!controller->tmp1) { + printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name); + goto tmp1_failed; + } + controller->tmp2 = (void *)__get_free_pages(GFP_KERNEL, 8); + if (!controller->tmp2) { + printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name); + goto tmp2_failed; + } + if ((controller->dma2 = jz_request_dma(DMA_ID_I2S_RX, "audio adc", jz_i2s_record_dma_irq, IRQF_DISABLED, controller)) < 0) { + printk(KERN_ERR "%s: can't reqeust DMA ADC channel.\n", name); + goto dma2_failed; + } + if ((controller->dma1 = jz_request_dma(DMA_ID_I2S_TX, "audio dac", jz_i2s_replay_dma_irq, IRQF_DISABLED, controller)) < 0) { + printk(KERN_ERR "%s: can't reqeust DMA DAC channel.\n", name); + goto dma1_failed; + } + printk("JzSOC On-Chip I2S controller registered (DAC: DMA(play):%d/IRQ%d,\n ADC: DMA(record):%d/IRQ%d)\n", controller->dma1, get_dma_done_irq(controller->dma1), controller->dma2, get_dma_done_irq(controller->dma2)); + + controller->dev_audio = adev; + pop_turn_onoff_buf = __get_free_pages(GFP_KERNEL | GFP_DMA, 8); + if(!pop_turn_onoff_buf) + printk("pop_turn_onoff_buf alloc is wrong!\n"); + pop_turn_onoff_pbuf = virt_to_phys((void *)pop_turn_onoff_buf); + + return; +dma2_failed: + jz_free_dma(controller->dma1); +dma1_failed: + free_pages((unsigned long)controller->tmp2, 8); +tmp2_failed: + free_pages((unsigned long)controller->tmp1, 8); +tmp1_failed: + +#ifdef CONFIG_PROC_FS + jz_i2s_cleanup_proc(controller); +#endif +proc_failed: + /* unregister mixer dev */ +mixer_failed: + unregister_sound_dsp(adev); +audio_failed: + return; +} + +static int __init probe_jz_i2s(struct jz_i2s_controller_info **controller) +{ + if ((*controller = kmalloc(sizeof(struct jz_i2s_controller_info), + GFP_KERNEL)) == NULL) { + printk(KERN_ERR "Jz I2S Controller: out of memory.\n"); + return -ENOMEM; + } + (*controller)->name = "Jz I2S controller"; + (*controller)->opened1 = 0; + (*controller)->opened2 = 0; + init_waitqueue_head(&(*controller)->adc_wait); + init_waitqueue_head(&(*controller)->dac_wait); + spin_lock_init(&(*controller)->lock); + init_waitqueue_head(&rx_wait_queue); + init_waitqueue_head(&tx_wait_queue); + init_waitqueue_head(&pop_wait_queue); + init_waitqueue_head(&drain_wait_queue); + + return 0; +} + +static void __exit unload_jz_i2s(struct jz_i2s_controller_info *controller) +{ + int adev = controller->dev_audio; + + jz_i2s_full_reset(controller); + controller->dev_audio = -1; + if (old_mksound) + kd_mksound = old_mksound;/* Our driver support bell for kb, see vt.c */ + +#ifdef CONFIG_PROC_FS + jz_i2s_cleanup_proc(controller); +#endif + + jz_free_dma(controller->dma1); + jz_free_dma(controller->dma2); + free_pages((unsigned long)controller->tmp1, 8); + free_pages((unsigned long)controller->tmp2, 8); + free_pages((unsigned long)pop_turn_onoff_buf, 8); + + if (adev >= 0) { + /* unregister_sound_mixer(audio_devs[adev]->mixer_dev); */ + unregister_sound_dsp(controller->dev_audio); + } +} + +#ifdef CONFIG_PM +static int jz_i2s_suspend(struct jz_i2s_controller_info *controller, int state) +{ + if(i2s_suspend_codec) + i2s_suspend_codec(controller->opened1,controller->opened2); + printk("Aic and codec are suspended!\n"); + return 0; +} + +static int jz_i2s_resume(struct jz_i2s_controller_info *controller) +{ + if(i2s_resume_codec) + i2s_resume_codec(); + +#if defined(CONFIG_I2S_AK4642EN) + jz_i2s_initHw(0); + jz_audio_reset(); + __i2s_enable(); + jz_audio_set_speed(controller->dev_audio,jz_audio_speed); + /* playing */ + if(controller->opened1) { + if(set_codec_replay) + set_codec_replay(); + int dma = controller->dma1; + int id; + unsigned long flags; + disable_dma(dma); + if(__dmac_channel_address_error_detected(dma)) { + printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); + __dmac_channel_clear_address_error(dma); + } + if(__dmac_channel_transmit_end_detected(dma)) + __dmac_channel_clear_transmit_end(dma); + + /* for DSP_GETOPTR */ + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes += jz_audio_dma_tran_count; + controller->blocks ++; + spin_unlock_irqrestore(&controller->ioctllock, flags); + while((id = get_buffer_id(&out_busy_queue)) >= 0) + put_buffer_id(&out_empty_queue, id); + + out_busy_queue.count=0; + if((id = get_buffer_id(&out_full_queue)) >= 0) { + put_buffer_id(&out_empty_queue, id); + } + if (elements_in_queue(&out_empty_queue) > 0) { + wake_up(&tx_wait_queue); + wake_up(&controller->dac_wait); + } else + printk("pm out_empty_queue empty"); + } + + /* recording */ + if(controller->opened2) { + if(set_codec_record) + set_codec_record(use_mic_line_flag); + int dma = controller->dma2; + int id1, id2; + unsigned long flags; + disable_dma(dma); + if (__dmac_channel_address_error_detected(dma)) { + printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); + __dmac_channel_clear_address_error(dma); + } + if (__dmac_channel_transmit_end_detected(dma)) { + __dmac_channel_clear_transmit_end(dma); + } + /* for DSP_GETIPTR */ + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes += jz_audio_dma_tran_count; + controller->blocks ++; + spin_unlock_irqrestore(&controller->ioctllock, flags); + id1 = get_buffer_id(&in_busy_queue); + put_buffer_id(&in_full_queue, id1); + wake_up(&rx_wait_queue); + wake_up(&controller->adc_wait); + if ((id2 = get_buffer_id(&in_empty_queue)) >= 0) { + put_buffer_id(&in_full_queue, id2); + } + in_busy_queue.count = 0; + } +#endif + + return 0; +} + +static int jz_i2s_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data) +{ + int ret; + struct jz_i2s_controller_info *controller = pm_dev->data; + + if (!controller) return -EINVAL; + + switch (req) { + case PM_SUSPEND: + ret = jz_i2s_suspend(controller, (int)data); + break; + case PM_RESUME: + ret = jz_i2s_resume(controller); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} +#endif /* CONFIG_PM */ + +#if defined(CONFIG_I2S_DLV) +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) { + + 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 __init init_jz_i2s(void) +{ + int errno; +#if defined(CONFIG_I2S_DLV) + int retval; + ramp_up_start = 0; + ramp_up_end = 0; + gain_up_start = 0; + gain_up_end = 0; + ramp_down_start = 0; + ramp_down_end = 0; + gain_down_start = 0; + gain_down_end = 0; +#endif + use_mic_line_flag = USE_NONE; + abnormal_data_count = 0; + if(set_codec_mode) + set_codec_mode(); + + drain_flag = 0; + if ((errno = probe_jz_i2s(&i2s_controller)) < 0) + return errno; + if(set_codec_gpio_pin) + set_codec_gpio_pin(); + + attach_jz_i2s(i2s_controller); + if(set_codec_startup_param) + set_codec_startup_param(); + if(set_codec_volume_table) + set_codec_volume_table(); + +#if defined(CONFIG_I2S_DLV) + jz_codec_config = 0; + 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; + } +#endif + + out_empty_queue.id = NULL; + out_full_queue.id = NULL; + out_busy_queue.id = NULL; + in_empty_queue.id = NULL; + in_full_queue.id = NULL; + in_busy_queue.id = NULL; + + jz_audio_fragsize = JZCODEC_RW_BUFFER_SIZE * PAGE_SIZE; + jz_audio_fragstotal = JZCODEC_RW_BUFFER_TOTAL ; + Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); + +#ifdef CONFIG_PM + i2s_controller->pm = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, + jz_i2s_pm_callback); + if (i2s_controller->pm) + i2s_controller->pm->data = i2s_controller; +#endif +#if defined(CONFIG_I2S_DLV) + __cpm_start_idct(); + __cpm_start_db(); + __cpm_start_me(); + __cpm_start_mc(); + __cpm_start_ipu(); +#endif + printk("JZ I2S OSS audio driver initialized\n"); + + return 0; +} + +static void __exit cleanup_jz_i2s(void) +{ +#ifdef CONFIG_PM + /* pm_unregister(i2s_controller->pm); */ +#endif +#if defined(CONFIG_I2S_DLV) + free_irq(IRQ_AIC, NULL); +#endif + unload_jz_i2s(i2s_controller); + Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); + if(clear_codec_mode) + clear_codec_mode(); +} + +module_init(init_jz_i2s); +module_exit(cleanup_jz_i2s); + +#if defined(CONFIG_SOC_JZ4730) +static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count,con; + + if(elements_in_queue(&in_busy_queue) > 0) { + if (nonblock) + return -EBUSY; + drain_flag = 1; + sleep_on(&drain_wait_queue); + drain_flag = 0; + } else { + add_wait_queue(&ctrl->adc_wait, &wait); + for (con = 0; con < 1000; con ++) { + udelay(1); + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&ctrl->lock, flags); + count = get_dma_residue(ctrl->dma2); + spin_unlock_irqrestore(&ctrl->lock, flags); + if (count <= 0) + break; + if (nonblock) { + remove_wait_queue(&ctrl->adc_wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + } + remove_wait_queue(&ctrl->adc_wait, &wait); + current->state = TASK_RUNNING; + } + return 0; +} + +static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count; + + if(elements_in_queue(&out_full_queue) > 0) { + if (nonblock) + return -EBUSY; + + drain_flag = 1; + sleep_on(&drain_wait_queue); + drain_flag = 0; + } else { + add_wait_queue(&(ctrl->dac_wait), &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if(elements_in_queue(&out_full_queue) <= 0) { + spin_lock_irqsave(&ctrl->lock, flags); + count = get_dma_residue(ctrl->dma1); + spin_unlock_irqrestore(&ctrl->lock, flags); + if(count <= 0) + break; + } + if (nonblock) { + remove_wait_queue(&ctrl->dac_wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + } + remove_wait_queue(&ctrl->dac_wait, &wait); + current->state = TASK_RUNNING; + } + + return 0; +} +#endif + +#if defined(CONFIG_SOC_JZ4740) +#define MAXDELAY 50000 +static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock) +{ + int count,ele,i=0; + int tfl; + + for (;;) { + if(!nonblock) {//blocked + if ( i < MAXDELAY ) { + udelay(10); + i++; + } else + break; + + ele = elements_in_queue(&out_full_queue); + if(ele <= 0) { + udelay(10); + spin_lock(&ctrl->lock); + count = get_dma_residue(ctrl->dma1); + spin_unlock(&ctrl->lock); + if (count <= 0) + break; + } + } else {//non-blocked + mdelay(100); + ele = elements_in_queue(&out_full_queue); + + if(ele <= 0) { + mdelay(100); + + spin_lock(&ctrl->lock); + count = get_dma_residue(ctrl->dma1); + spin_unlock(&ctrl->lock); + if (count <= 0) + break; + } + } + } + + /* wait for TX fifo */ + while (1) { + tfl = __aic_get_transmit_resident(); + if (tfl == 0) + break; + udelay(2); + } + + return 0; +} + +static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock) +{ + int count,i=0; + + for (;;) { + if ( i < MAXDELAY ) + { + udelay(10); + i++; + } + else + break; + spin_lock(&ctrl->lock); + count = get_dma_residue(ctrl->dma2); + spin_unlock(&ctrl->lock); + if (count <= 0) + break; + + if (nonblock) { + return -EBUSY; + } + } + + return 0; +} +#endif + +#if defined(CONFIG_SOC_JZ4750) || defined(CONFIG_SOC_JZ4750D) +#define MAXDELAY 50000 +static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock) +{ + //DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count,i=0; + + //add_wait_queue(&ctrl->adc_wait, &wait); + for (;;) { + if (i < MAXDELAY) { + udelay(10); + i++; + } else + break; + //set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&ctrl->lock, flags); + //spin_lock(&ctrl->lock); + count = get_dma_residue(ctrl->dma2); + spin_unlock_irqrestore(&ctrl->lock, flags); + //spin_unlock(&ctrl->lock); + if (count <= 0) + break; + + /*if (signal_pending(current)) + break;*/ + if (nonblock) { + //remove_wait_queue(&ctrl->adc_wait, &wait); + //current->state = TASK_RUNNING; + return -EBUSY; + } + } + //remove_wait_queue(&ctrl->adc_wait, &wait); + //current->state = TASK_RUNNING; + /*if (signal_pending(current)) + return -ERESTARTSYS;*/ + return 0; +} +static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock) +{ + unsigned long flags; + int count,ele,busyele,emptyele,i=0; + + for (;;) { + if(!nonblock) {//blocked + if (i < MAXDELAY) { + udelay(10); + i++; + } else + break; + + ele = elements_in_queue(&out_full_queue); + if(ele <= 0) { + udelay(200); + + busyele = elements_in_queue(&out_busy_queue); + emptyele = elements_in_queue(&out_empty_queue); + if (busyele <= 0 && emptyele >= jz_audio_fragstotal) { + spin_lock_irqsave(&ctrl->lock, flags); + count = get_dma_residue(ctrl->dma1); + spin_unlock_irqrestore(&ctrl->lock, flags); + if (count <= 0) + break; + } + } + } else {//non-blocked + //mdelay(100); + ele = elements_in_queue(&out_full_queue); + + if(ele <= 0) { + //mdelay(100); + busyele = elements_in_queue(&out_busy_queue); + emptyele = elements_in_queue(&out_empty_queue); + + if (busyele <= 0 && emptyele >= jz_audio_fragstotal) { + spin_lock_irqsave(&ctrl->lock, flags); + count = get_dma_residue(ctrl->dma1); + spin_unlock_irqrestore(&ctrl->lock, flags); + if (count <= 0) + break; + } + } + } + } + + return 0; +} +#endif + +static int jz_audio_release(struct inode *inode, struct file *file) +{ + unsigned long flags; +#if defined(CONFIG_I2S_DLV) + unsigned long tfl; +#endif + struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; +#if defined(CONFIG_I2S_DLV) + jz_codec_config = 0; +#endif + if (controller == NULL) + return -ENODEV; + + pop_dma_flag = 0; + + if (controller->opened1 == 1 && controller->opened2 == 1) { + controller->opened1 = 0; + __i2s_enable_transmit_dma(); + __i2s_enable_replay(); + drain_dac(controller, file->f_flags & O_NONBLOCK); +#if defined(CONFIG_I2S_DLV) + /* wait for fifo empty */ + write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + gain_down_start = jiffies; + sleep_on(&pop_wait_queue); + //gain_down_end = jiffies; + /*while (1) { + tfl = REG_AIC_SR & 0x00003f00; + if (tfl == 0) { + udelay(500); + break; + } + mdelay(2); + }*/ + mdelay(100); +#endif + disable_dma(controller->dma1); + set_dma_count(controller->dma1, 0); + __i2s_disable_transmit_dma(); + __i2s_disable_replay(); + +#if defined(CONFIG_I2S_ICODEC) + if(clear_codec_replay) + clear_codec_replay(); +#endif + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes = 0; + controller->count = 0; + controller->finish = 0; + jz_audio_dma_tran_count = 0; + controller->blocks = 0; + controller->nextOut = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + + controller->opened2 = 0; + first_record_call = 1; + __i2s_enable_receive_dma(); + __i2s_enable_record(); + drain_adc(controller, file->f_flags & O_NONBLOCK); + disable_dma(controller->dma2); + set_dma_count(controller->dma2, 0); + __i2s_disable_receive_dma(); + __i2s_disable_record(); +#if defined(CONFIG_I2S_ICODEC) + if(clear_codec_record) + clear_codec_record(); +#endif + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes = 0; + jz_audio_dma_tran_count = 0; + controller->count = 0; + controller->finish = 0; + controller->blocks = 0; + controller->nextIn = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); +#if defined(CONFIG_I2S_DLV) + write_codec_file_bit(5, 1, 6);//SB_OUT->1 + ramp_down_start = jiffies; + sleep_on(&pop_wait_queue); + //ramp_down_end = jiffies; + if (use_mic_line_flag == USE_LINEIN) { + unset_record_line_input_audio_with_audio_data_replay(); + //printk("3 use_mic_line_flag=%d\n",use_mic_line_flag); + } + if (use_mic_line_flag == USE_MIC) { + unset_record_mic_input_audio_with_audio_data_replay(); + //printk("4 use_mic_line_flag=%d\n",use_mic_line_flag); + } + +#if 0 + unset_record_playing_audio_mixed_with_mic_input_audio(); +#endif +#endif + __i2s_disable(); + if(turn_off_codec) + turn_off_codec(); + abnormal_data_count = 0; + } else if (controller->opened1 == 1) { + //controller->opened1 = 0; + __i2s_enable_transmit_dma(); + __i2s_enable_replay(); + drain_dac(controller, file->f_flags & O_NONBLOCK); + /* add some mute to anti-pop */ +#if defined(CONFIG_I2S_ICODEC) + //write_mute_to_dma_buffer(save_last_samples[last_dma_buffer_id].left,save_last_samples[last_dma_buffer_id].right); +#endif +#if defined(CONFIG_I2S_DLV) + write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + gain_down_start = jiffies; + sleep_on(&pop_wait_queue); + //gain_down_end = jiffies; + while (1) { + tfl = REG_AIC_SR & 0x00003f00; + if (tfl == 0) { + udelay(500); + break; + } + mdelay(2); + } +#endif + disable_dma(controller->dma1); + set_dma_count(controller->dma1, 0); + __i2s_disable_transmit_dma(); + __i2s_disable_replay(); +#if defined(CONFIG_I2S_ICODEC) + if(clear_codec_replay) + clear_codec_replay(); +#endif + __aic_flush_fifo(); + + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes = 0; + controller->count = 0; + controller->finish = 0; + jz_audio_dma_tran_count = 0; + controller->blocks = 0; + controller->nextOut = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); +#if defined(CONFIG_I2S_DLV) + write_codec_file_bit(5, 1, 6);//SB_OUT->1 + ramp_down_start = jiffies; + sleep_on(&pop_wait_queue); + //ramp_down_end = jiffies; + unset_audio_data_replay(); +#endif + __i2s_disable(); +#if defined(CONFIG_I2S_ICODEC) + if(turn_off_codec) + turn_off_codec(); +#endif + } else if (controller->opened2 == 1) { + controller->opened2 = 0; + first_record_call = 1; + __i2s_enable_receive_dma(); + __i2s_enable_record(); + drain_adc(controller, file->f_flags & O_NONBLOCK); + disable_dma(controller->dma2); + set_dma_count(controller->dma2, 0); + __i2s_disable_receive_dma(); + __i2s_disable_record(); +#if defined(CONFIG_I2S_ICODEC) + if(clear_codec_record) + clear_codec_record(); +#endif + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes = 0; + jz_audio_dma_tran_count = 0; + controller->count = 0; + controller->finish = 0; + controller->blocks = 0; + controller->nextIn = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); +#if defined(CONFIG_I2S_DLV) +#if 0 + /* unset Record MIC input audio with direct playback */ + unset_record_mic_input_audio_with_direct_playback(); +#endif +#if 1 + /* unset Record MIC input audio without playback */ + unset_record_mic_input_audio_without_playback(); +#endif +#if 0 + /* tested */ + /* unset Record LINE input audio without playback */ + unset_record_line_input_audio_without_playback(); +#endif +#endif + __i2s_disable(); +#if defined(CONFIG_I2S_ICODEC) + if(turn_off_codec) + turn_off_codec(); +#endif + abnormal_data_count = 0; + } + +#if defined(CONFIG_I2S_DLV) + write_codec_file(9, 0xff); + write_codec_file(8, 0x3f); +/* __cpm_stop_idct(); + __cpm_stop_db(); + __cpm_stop_me(); + __cpm_stop_mc(); + __cpm_stop_ipu();*/ +#endif + + if (controller->opened1 == 1 && controller->opened2 == 1) { + controller->opened1 = 0; + controller->opened2 = 0; + //print_pop_duration(); + //__dmac_disable_module(0); + } else if ( controller->opened1 == 1 ) { + controller->opened1 = 0; + //print_pop_duration(); + } else if ( controller->opened2 == 1 ) { + controller->opened2 = 0; + } + + return 0; +} + +static int jz_audio_open(struct inode *inode, struct file *file) +{ + int i; + struct jz_i2s_controller_info *controller = i2s_controller; +#if defined(CONFIG_I2S_DLV) + jz_codec_config = 0; +#endif + if (controller == NULL) + return -ENODEV; + + mdelay(2); +#if defined(CONFIG_I2S_DLV) + REG_DMAC_DMACKE(0) = 0x3f; +#endif + pop_dma_flag = 0; + if (controller->opened1 == 1 || controller->opened2 == 1 ) { + printk("\naudio is busy!\n"); + return -EBUSY; + } + + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + if (controller->opened1 == 1) + return -EBUSY; + controller->opened1 = 1; + /* for ioctl */ + controller->total_bytes = 0; + jz_audio_dma_tran_count = 0; + controller->count = 0; + controller->finish = 0; + controller->blocks = 0; + controller->nextOut = 0; + + for(i=0;i < 64;i++) { + save_last_samples[i].left = 0; + save_last_samples[i].right = 0; + } + + out_empty_queue.count = jz_audio_fragstotal; + for (i=0;i < jz_audio_fragstotal;i++) + *(out_empty_queue.id + i) = i; + out_busy_queue.count = 0; + out_full_queue.count = 0; + last_dma_buffer_id = 0; + + if (controller->opened2 == 1) + return -EBUSY; + + controller->opened2 = 1; + first_record_call = 1; + /* for ioctl */ + controller->total_bytes = 0; + jz_audio_dma_tran_count = 0; + controller->count = 0; + controller->finish = 0; + controller->blocks = 0; + controller->nextIn = 0; + + in_empty_queue.count = jz_audio_fragstotal; + for (i=0;i < jz_audio_fragstotal;i++) + *(in_empty_queue.id + i) = i; + + in_full_queue.count = 0; + in_busy_queue.count = 0; + } else if (file->f_mode & FMODE_WRITE) { + if (controller->opened1 == 1) + return -EBUSY; + + controller->opened1 = 1; + /* for ioctl */ + controller->total_bytes = 0; + jz_audio_dma_tran_count = 0; + controller->count = 0; + controller->finish = 0; + controller->blocks = 0; + controller->nextOut = 0; + + for(i=0;i < 64;i++) { + save_last_samples[i].left = 0; + save_last_samples[i].right = 0; + } + + out_empty_queue.count = jz_audio_fragstotal; + for (i=0;i < jz_audio_fragstotal;i++) + *(out_empty_queue.id + i) = i; + out_busy_queue.count = 0; + out_full_queue.count = 0; + last_dma_buffer_id = 0; + } else if (file->f_mode & FMODE_READ) { + if (controller->opened2 == 1) + return -EBUSY; + + controller->opened2 = 1; + first_record_call = 1; + /* for ioctl */ + controller->total_bytes = 0; + jz_audio_dma_tran_count = 0; + controller->count = 0; + controller->finish = 0; + controller->blocks = 0; + controller->nextIn = 0; + + in_empty_queue.count = jz_audio_fragstotal; + for (i=0;i < jz_audio_fragstotal;i++) + *(in_empty_queue.id + i) = i; + + in_full_queue.count = 0; + in_busy_queue.count = 0; + } + + file->private_data = controller; + jz_audio_reset(); + REG_AIC_FR |= (1 << 6); + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { +#if defined(CONFIG_I2S_ICODEC) + if (set_codec_replay_record) + set_codec_replay_record(use_mic_line_flag); +#endif +#if defined(CONFIG_I2S_DLV) + if (use_mic_line_flag == USE_NONE) { + printk("you select mic or line recording please.or use mic recording!\n"); + use_mic_line_flag = USE_MIC; + } + if (use_mic_line_flag == USE_LINEIN) { + /* Record LINE input audio with Audio data replay (full duplex for linein) */ + /* codec_test_line */ + set_record_line_input_audio_with_audio_data_replay(); + + } + if (use_mic_line_flag == USE_MIC) { + /* Record MIC input audio with Audio data replay (full duplex) */ + /* codec_test_mic */ + set_record_mic_input_audio_with_audio_data_replay(); + } +#if 0 + /* Record playing audio mixed with MIC input audio */ + set_record_playing_audio_mixed_with_mic_input_audio(); +#endif + +#endif + } else if (file->f_mode & FMODE_WRITE) { +#if defined(CONFIG_I2S_ICODEC) + if(set_codec_replay) + set_codec_replay(); +#endif +#if defined(CONFIG_I2S_DLV) + //mdelay(10); + /* Audio data replay */ + set_audio_data_replay(); +#endif + } else if (file->f_mode & FMODE_READ) { +#if defined(CONFIG_I2S_ICODEC) + abnormal_data_count = 0; + if(set_codec_record) + set_codec_record(use_mic_line_flag); +#endif +#if defined(CONFIG_I2S_DLV) +#if 0 + /* Record MIC input audio with direct playback */ + set_record_mic_input_audio_with_direct_playback(); +#endif + +#if 1 + /* set Record MIC input audio without playback */ + set_record_mic_input_audio_without_playback(); +#endif +#if 0 + /* tested */ + /* set Record LINE input audio without playback */ + set_record_line_input_audio_without_playback(); +#endif + mdelay(1); +#endif + } + +#if defined(CONFIG_I2S_DLV) + __aic_reset(); + + mdelay(10); + REG_AIC_I2SCR = 0x10; + mdelay(20); + __aic_flush_fifo(); +#endif + + __i2s_enable(); + +#if defined(CONFIG_I2S_DLV) + ndelay(100); + + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { +#if defined(CONFIG_I2S_DLV) + //set SB_ADC or SB_DAC + __dmac_enable_module(0); + write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 + ramp_up_start = jiffies; + sleep_on(&pop_wait_queue); + //ramp_up_end = jiffies; +#endif + } else if (file->f_mode & FMODE_WRITE) { +#if defined(CONFIG_I2S_DLV) + write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 + ramp_up_start = jiffies; + /*while (!(REG_RTC_RCR & RTC_RCR_WRDY)); + REG_RTC_RCR = 0x1; + while (!(REG_RTC_RCR & RTC_RCR_WRDY)); + REG_RTC_RGR = 1;*/ + sleep_on(&pop_wait_queue); + //ramp_up_end = jiffies; + write_codec_file_bit(5, 1, 4);//SB_ADC->1 +#endif + } else if (file->f_mode & FMODE_READ) { +#if defined(CONFIG_I2S_DLV) + if (jz_mic_only) + write_codec_file_bit(5, 1, 7);//SB_DAC->1 + else + write_codec_file_bit(5, 0, 7);//SB_DAC->0 + mdelay(500); +#endif + } +#endif + return 0; +} + + +static int jz_audio_ioctl(struct inode *inode, struct file *file, +unsigned int cmd, unsigned long arg) +{ + int val,fullc,busyc,unfinish,newfragstotal,newfragsize; + unsigned int flags; + struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; + count_info cinfo; + audio_buf_info abinfo; + int id, i; + + val = 0; + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + case SNDCTL_DSP_RESET: +#if 0 + jz_audio_reset(); + __i2s_disable_replay(); + __i2s_disable_receive_dma(); + __i2s_disable_record(); + __i2s_disable_transmit_dma(); +#endif + return 0; + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(controller, file->f_flags & O_NONBLOCK); + return 0; + case SNDCTL_DSP_SPEED: + /* set smaple rate */ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) + jz_audio_set_speed(controller->dev_audio, val); + + return put_user(val, (int *)arg); + case SNDCTL_DSP_STEREO: + /* set stereo or mono channel */ + if (get_user(val, (int *)arg)) + return -EFAULT; + jz_audio_set_channels(controller->dev_audio, val ? 2 : 1); + + return 0; + case SNDCTL_DSP_GETBLKSIZE: + //return put_user(jz_audio_fragsize / jz_audio_b, (int *)arg); + return put_user(jz_audio_fragsize, (int *)arg); + case SNDCTL_DSP_GETFMTS: + /* Returns a mask of supported sample format*/ + return put_user(AFMT_U8 | AFMT_S16_LE, (int *)arg); + case SNDCTL_DSP_SETFMT: + /* Select sample format */ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != AFMT_QUERY) + jz_audio_set_format(controller->dev_audio,val); + else { + if (file->f_mode & FMODE_READ) + val = (jz_audio_format == 16) ? AFMT_S16_LE : AFMT_U8; + else + val = (jz_audio_format == 16) ? AFMT_S16_LE : AFMT_U8; + } + + return put_user(val, (int *)arg); + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + jz_audio_set_channels(controller->dev_audio, val); + + return put_user(val, (int *)arg); + case SNDCTL_DSP_POST: + /* FIXME: the same as RESET ?? */ + return 0; + case SNDCTL_DSP_SUBDIVIDE: + return 0; + case SNDCTL_DSP_SETFRAGMENT: + get_user(val, (long *) arg); + newfragsize = 1 << (val & 0xFFFF); + if (newfragsize < 4 * PAGE_SIZE) + newfragsize = 4 * PAGE_SIZE; + if (newfragsize > (16 * PAGE_SIZE)) + newfragsize = 16 * PAGE_SIZE; + + newfragstotal = (val >> 16) & 0x7FFF; + if (newfragstotal < 2) + newfragstotal = 2; + if (newfragstotal > 32) + newfragstotal = 32; + if((jz_audio_fragstotal == newfragstotal) && (jz_audio_fragsize == newfragsize)) + return 0; + Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); + mdelay(500); + jz_audio_fragstotal = newfragstotal; + jz_audio_fragsize = newfragsize; + + Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); + mdelay(10); + + return 0; + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_REALTIME|DSP_CAP_BATCH, (int *)arg); + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + case SNDCTL_DSP_SETDUPLEX: + return -EINVAL; + case SNDCTL_DSP_GETOSPACE: + { + int i; + unsigned long bytes = 0; + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + spin_lock_irqsave(&controller->ioctllock, flags); + jz_audio_fragments = elements_in_queue(&out_empty_queue); + for (i = 0; i < jz_audio_fragments; i++) + bytes += jz_audio_fragsize; + + if (jz_audio_channels == 2) + bytes /= jz_audio_b; + else if (jz_audio_channels == 1) + bytes /= 4; + else + printk("SNDCTL_DSP_GETOSPACE : channels is wrong 1!\n"); + + spin_unlock_irqrestore(&controller->ioctllock, flags); + /* unused fragment amount */ + abinfo.fragments = jz_audio_fragments; + /* amount of fragments */ + abinfo.fragstotal = jz_audio_fragstotal; + /* fragment size in bytes */ + if (jz_audio_channels == 2) + abinfo.fragsize = jz_audio_fragsize / jz_audio_b; + else if (jz_audio_channels == 1) + abinfo.fragsize = jz_audio_fragsize / 4; + else + printk("SNDCTL_DSP_GETOSPACE : channels is wrong 2!\n"); + + /* write size count without blocking in bytes */ + abinfo.bytes = (int)bytes; + + return copy_to_user((void *)arg, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + } + case SNDCTL_DSP_GETISPACE: + { + int i; + unsigned long bytes = 0; + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + jz_audio_fragments = elements_in_queue(&in_empty_queue); + for (i = 0; i < jz_audio_fragments; i++) + bytes += jz_audio_fragsize; + + if (jz_audio_channels == 2) + bytes /= jz_audio_b; + else if (jz_audio_channels == 1) + bytes /= 4; + else + printk("SNDCTL_DSP_GETISPACE : channels is wrong 1!\n"); + + abinfo.fragments = jz_audio_fragments; + abinfo.fragstotal = jz_audio_fragstotal; + + if (jz_audio_channels == 2) + abinfo.fragsize = jz_audio_fragsize / jz_audio_b; + else if (jz_audio_channels == 1) + abinfo.fragsize = jz_audio_fragsize / 4; + else + printk("SNDCTL_DSP_GETISPACE : channels is wrong 2!\n"); + + abinfo.bytes = (int)bytes; + + return copy_to_user((void *)arg, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + } + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && in_dma_buf) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && out_dma_buf) + val |= PCM_ENABLE_OUTPUT; + + return put_user(val, (int *)arg); + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + return 0; + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + + spin_lock_irqsave(&controller->ioctllock, flags); + cinfo.bytes = controller->total_bytes; + cinfo.blocks = controller->blocks; + cinfo.ptr = controller->nextIn; + controller->blocks = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + spin_lock_irqsave(&controller->ioctllock, flags); + cinfo.bytes = controller->total_bytes; + cinfo.blocks = controller->blocks; + cinfo.ptr = controller->nextOut; + controller->blocks = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + + return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + spin_lock_irqsave(&controller->ioctllock, flags); + unfinish = 0; + fullc = elements_in_queue(&out_full_queue); + busyc = elements_in_queue(&out_busy_queue); + for(i = 0;i < fullc ;i ++) { + id = *(out_full_queue.id + i); + unfinish += *(out_dma_buf_data_count + id); + } + for(i = 0;i < busyc ;i ++) { + id = *(out_busy_queue.id + i); + unfinish += get_dma_residue(controller->dma1); + } + spin_unlock_irqrestore(&controller->ioctllock, flags); + + if (jz_audio_channels == 2) + unfinish /= jz_audio_b; + else if (jz_audio_channels == 1) + unfinish /= 4; + else + printk("SNDCTL_DSP_GETODELAY : channels is wrong !\n"); + + return put_user(unfinish, (int *) arg); + case SOUND_PCM_READ_RATE: + return put_user(jz_audio_rate, (int *)arg); + case SOUND_PCM_READ_CHANNELS: + return put_user(jz_audio_channels, (int *)arg); + case SOUND_PCM_READ_BITS: + return put_user((jz_audio_format & (AFMT_S8 | AFMT_U8)) ? 8 : 16, (int *)arg); + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + return -EINVAL; +} + + +static unsigned int jz_audio_poll(struct file *file,struct poll_table_struct *wait) +{ + struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; + unsigned long flags; + unsigned int mask = 0; + + if (file->f_mode & FMODE_WRITE) { + if (elements_in_queue(&out_empty_queue) > 0) + return POLLOUT | POLLWRNORM; + + poll_wait(file, &controller->dac_wait, wait); + } + + if (file->f_mode & FMODE_READ) { + if (elements_in_queue(&in_full_queue) > 0) + return POLLIN | POLLRDNORM; + + poll_wait(file, &controller->adc_wait, wait); + } + + spin_lock_irqsave(&controller->lock, flags); + if (file->f_mode & FMODE_WRITE) { + if (elements_in_queue(&out_empty_queue) > 0) + mask |= POLLOUT | POLLWRNORM; + } else if (file->f_mode & FMODE_READ) { + if (elements_in_queue(&in_full_queue) > 0) + mask |= POLLIN | POLLRDNORM; + } + spin_unlock_irqrestore(&controller->lock, flags); + + return mask; +} + +static ssize_t jz_audio_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; + int id, ret = 0, left_count, copy_count, cnt = 0; + unsigned long flags; + + if (count < 0) + return -EINVAL; + + __i2s_enable_receive_dma(); + __i2s_enable_record(); + + spin_lock_irqsave(&controller->ioctllock, flags); + controller->nextIn = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + + copy_count = jz_audio_fragsize / 4; + + left_count = count; + if (first_record_call) { + first_record_call = 0; + audio_read_back_first: + if ((id = get_buffer_id(&in_empty_queue)) >= 0) { + put_buffer_id(&in_busy_queue, id); + spin_lock(&controller->lock); + *(in_dma_buf_data_count + id) = copy_count * 4; + + spin_unlock(&controller->lock); + __i2s_enable_receive_dma(); + __i2s_enable_record(); + dma_cache_wback_inv(*(in_dma_buf + id), *(in_dma_buf_data_count + id)); + audio_start_dma(controller->dma2,file->private_data, + *(in_dma_pbuf + id), + *(in_dma_buf_data_count + id), + DMA_MODE_READ); + sleep_on(&rx_wait_queue); + } else + goto audio_read_back_first; + } + + while (left_count > 0) { + audio_read_back_second: + if (elements_in_queue(&in_full_queue) <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + else + sleep_on(&rx_wait_queue); + } + + if ((id = get_buffer_id(&in_full_queue)) >= 0) { + spin_lock(&controller->lock); + cnt = record_filler((unsigned long)controller->tmp2+ret, copy_count, id); + spin_unlock(&controller->lock); + put_buffer_id(&in_empty_queue, id); + } else + goto audio_read_back_second; + + if (elements_in_queue(&in_busy_queue) == 0) { + if ((id=get_buffer_id(&in_empty_queue)) >= 0) { + put_buffer_id(&in_busy_queue, id); + spin_lock(&controller->lock); + *(in_dma_buf_data_count + id) = copy_count * 4; + spin_unlock(&controller->lock); + + dma_cache_wback_inv(*(in_dma_buf + id), *(in_dma_buf_data_count + id)); + audio_start_dma(controller->dma2,file->private_data, + *(in_dma_pbuf + id), + *(in_dma_buf_data_count + id), + DMA_MODE_READ); + } + } + if (ret + cnt > count) { + spin_lock(&controller->lock); + cnt = count - ret; + spin_unlock(&controller->lock); + } + if (copy_to_user(buffer+ret, controller->tmp2+ret, cnt)) + return ret ? ret : -EFAULT; + + spin_lock(&controller->lock); + ret += cnt; + spin_unlock(&controller->lock); + + spin_lock_irqsave(&controller->ioctllock, flags); + controller->nextIn += ret; + spin_unlock_irqrestore(&controller->ioctllock, flags); + + spin_lock(&controller->lock); + left_count -= cnt; + spin_unlock(&controller->lock); + } + return ret; +} + +static ssize_t jz_audio_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + int id, ret = 0, left_count, copy_count = 0; + unsigned int flags; + struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; + + if (count <= 0) + return -EINVAL; + + if(set_replay_hp_or_speaker) + set_replay_hp_or_speaker(); + + __i2s_enable_transmit_dma(); + __i2s_enable_replay(); + + spin_lock_irqsave(&controller->ioctllock, flags); + controller->nextOut = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + if (jz_audio_channels == 2) + copy_count = jz_audio_fragsize / jz_audio_b; + else if(jz_audio_channels == 1) + copy_count = jz_audio_fragsize / 4; + left_count = count; + if (copy_from_user(controller->tmp1, buffer, count)) { + printk("copy_from_user failed:%d",ret); + return ret ? ret : -EFAULT; + } + + while (left_count > 0) { + audio_write_back: + if (file->f_flags & O_NONBLOCK) + udelay(2); + if (elements_in_queue(&out_empty_queue) == 0) { + if (file->f_flags & O_NONBLOCK) + return ret; + else + sleep_on(&tx_wait_queue); + } + /* the end fragment size in this write */ + if (ret + copy_count > count) + copy_count = count - ret; + if ((id = get_buffer_id(&out_empty_queue)) >= 0) { + replay_filler((signed long)controller->tmp1 + ret, copy_count, id); + if(*(out_dma_buf_data_count + id) > 0) { + put_buffer_id(&out_full_queue, id); + dma_cache_wback_inv(*(out_dma_buf + id), *(out_dma_buf_data_count + id)); + } else + put_buffer_id(&out_empty_queue, id); + } else + goto audio_write_back; + + left_count = left_count - copy_count; + ret += copy_count; + + spin_lock_irqsave(&controller->ioctllock, flags); + controller->nextOut += ret; + spin_unlock_irqrestore(&controller->ioctllock, flags); + + if (elements_in_queue(&out_busy_queue) == 0) { + if ((id=get_buffer_id(&out_full_queue)) >= 0) { + put_buffer_id(&out_busy_queue, id); + if(*(out_dma_buf_data_count + id) > 0) { + audio_start_dma(controller->dma1, + file->private_data, + *(out_dma_pbuf + id), + *(out_dma_buf_data_count + id), + DMA_MODE_WRITE); + last_dma_buffer_id = id; +#if defined(CONFIG_I2S_DLV) + if (jz_codec_config == 0) { + write_codec_file_bit(1, 0, 5); + gain_up_start = jiffies; + sleep_on(&pop_wait_queue); + //gain_up_end = jiffies; + jz_codec_config = 1; + //SB_ADC->1 + //write_codec_file_bit(5, 1, 4); + //while(1); + } +#endif + } + } + } + } + + return ret; +} + +#if defined(CONFIG_I2S_ICODEC) +static void write_mute_to_dma_buffer(signed long l_sample, signed long r_sample) +{ + int i,step_len; + unsigned long *pop_buf = (unsigned long*)pop_turn_onoff_buf; + unsigned int sample_oss = (REG_AIC_CR & 0x00380000) >> 19; + unsigned long l_sample_count,r_sample_count,sample_count; + struct jz_i2s_controller_info *controller = i2s_controller; + signed int left_sam=0,right_sam=0,l_val,r_val; + + switch (sample_oss) { + case 0x0: + break; + case 0x1: + left_sam = (signed int)l_sample; + right_sam = (signed int)r_sample; + break; + case 0x2: + break; + case 0x3: + break; + case 0x4: + break; + } + + if(left_sam == 0 && right_sam == 0) + return; + + switch (sample_oss) { + case 0x0: + break; + case 0x1: + step_len = jz_audio_speed / 10 * 3; + step_len = step_len / 2; + step_len = 0x7fff / step_len + 1; + + l_sample_count = 0; + l_val = left_sam; + + while(1) { + if(l_val > 0) { + if(l_val >= step_len) { + l_val -= step_len; + l_sample_count ++; + } else + break; + } + + if(l_val < 0) { + if(l_val <= -step_len) { + l_val += step_len; + l_sample_count ++; + } else + break; + } + + if(l_val == 0) + break; + } + + r_sample_count = 0; + r_val = right_sam; + while(1) { + if(r_val > 0) { + if(r_val >= step_len) { + r_val -= step_len; + r_sample_count ++; + } else + break; + } + + if(r_val < 0) { + if(r_val <= -step_len) { + r_val += step_len; + r_sample_count ++; + } else + break; + } + + if(r_val == 0) + break; + } + /* fill up */ + if(l_sample_count > r_sample_count) + sample_count = l_sample_count; + else + sample_count = r_sample_count; + + l_val = left_sam; + r_val = right_sam; + for(i=0;i <= sample_count;i++) { + + *pop_buf = (unsigned long)l_val; + pop_buf ++; + + if(l_val > step_len) + l_val -= step_len; + else if(l_val < -step_len) + l_val += step_len; + else if(l_val >= -step_len && l_val <= step_len) + l_val = 0; + + *pop_buf = (unsigned long)r_val; + pop_buf ++; + if(r_val > step_len) + r_val -= step_len; + else if(r_val < -step_len) + r_val += step_len; + else if(r_val >= -step_len && r_val <= step_len) + r_val = 0; + } + + *pop_buf = 0; + pop_buf ++; + *pop_buf = 0; + + pop_buf ++; + sample_count += 2; + dma_cache_wback_inv(pop_turn_onoff_buf, sample_count*8); + + pop_dma_flag = 1; + audio_start_dma(controller->dma1,controller,pop_turn_onoff_pbuf,sample_count*8,DMA_MODE_WRITE); + sleep_on(&pop_wait_queue); + pop_dma_flag = 0; + break; + case 0x2: + break; + case 0x3: + break; + case 0x4: + break; + } +} +#endif diff --git a/sound/oss/jz_i2s_4750.c b/sound/oss/jz_i2s_4750.c new file mode 100644 index 0000000..d99fa8d --- /dev/null +++ b/sound/oss/jz_i2s_4750.c @@ -0,0 +1,3010 @@ +/* + * linux/drivers/sound/Jz_i2s.c + * + * JzSOC On-Chip I2S audio driver. + * + * Copyright (C) 2005 by Junzheng Corp. + * Modified by cjfeng on Aug 9,2007,and not any bug on Jz4730 using + * dma channel 4&3,noah is tested. + * + * 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. + * + * Because the normal application of AUDIO devices are focused on Little_endian, + * then we only perform the little endian data format in driver. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sound_config.h" + +#define DPRINTK(args...) printk(args) +#define DMA_ID_I2S_TX DMA_ID_AIC_TX +#define DMA_ID_I2S_RX DMA_ID_AIC_RX +#define NR_I2S 2 +#define MAXDELAY 50000 +#define JZCODEC_RW_BUFFER_SIZE 2 +#define JZCODEC_RW_BUFFER_TOTAL 6 + +typedef struct hpvol_shift_s +{ + int hpvol; + int shift; +} hpvol_shift_t; + +mixer_info info; +_old_mixer_info old_info; +int codec_volue_shift; +hpvol_shift_t hpvol_shift_table[72]; +int abnormal_data_count; +unsigned long i2s_clk; + +void (*set_codec_mode)(void) = NULL; +void (*clear_codec_mode)(void) = NULL; +void (*set_codec_gpio_pin)(void) = NULL; +void (*each_time_init_codec)(void) = NULL; +int (*set_codec_startup_param)(void) = NULL; +void (*set_codec_volume_table)(void) = NULL; +void (*set_codec_record)(void) = NULL; +void (*set_codec_replay)(void) = NULL; +void (*set_codec_replay_record)(void); +void (*turn_on_codec)(void) = NULL; +void (*turn_off_codec)(void) = NULL; +void (*set_codec_speed)(int rate) = NULL; +void (*reset_codec)(void) = NULL; +void (*codec_mixer_old_info_id_name)(void) = NULL; +void (*codec_mixer_info_id_name)(void) = NULL; +void (*set_codec_bass)(int val) = NULL; +void (*set_codec_volume)(int val) = NULL; +void (*set_codec_mic)(int val) = NULL; +void (*i2s_resume_codec)(void) = NULL; +void (*i2s_suspend_codec)(int wr,int rd) = NULL; +void (*init_codec_pin)(void) = NULL; +void (*set_codec_some_func)(void) = NULL; +void (*clear_codec_record)(void) = NULL; +void (*clear_codec_replay)(void) = NULL; +void (*set_replay_hp_or_speaker)(void) = NULL; +void (*set_codec_direct_mode)(void) = NULL; +void (*clear_codec_direct_mode)(void) = NULL; + +static int jz_audio_rate; +static int jz_audio_format; +static int jz_audio_volume; +static int jz_audio_channels; +static int jz_audio_b; /* bits expand multiple */ +static int jz_audio_fragments; /* unused fragment amount */ +static int jz_audio_fragstotal; +static int jz_audio_fragsize; +static int jz_audio_speed; + +static int codec_bass_gain; +static int audio_mix_modcnt; +static int jz_audio_dma_tran_count; /* bytes count of one DMA transfer */ +static int jz_mic_only = 1; +static int jz_codec_config = 0; +static unsigned long ramp_up_start; +static unsigned long ramp_up_end; +static unsigned long gain_up_start; +static unsigned long gain_up_end; +static unsigned long ramp_down_start; +static unsigned long ramp_down_end; +static unsigned long gain_down_start; +static unsigned long gain_down_end; + +static int codec_mic_gain; +static int pop_dma_flag; +static int last_dma_buffer_id; +static int drain_flag; + +static void (*old_mksound)(unsigned int hz, unsigned int ticks); +extern void (*kd_mksound)(unsigned int hz, unsigned int ticks); +static void jz_update_filler(int bits, int channels); + +static int Init_In_Out_queue(int fragstotal,int fragsize); +static int Free_In_Out_queue(int fragstotal,int fragsize); +static irqreturn_t jz_i2s_replay_dma_irq(int irqnr, void *ref); +static irqreturn_t jz_i2s_record_dma_irq(int irqnr, void *ref); +static void (*replay_filler)(signed long src_start, int count, int id); +static int (*record_filler)(unsigned long dst_start, int count, int id); +#if defined(CONFIG_I2S_ICODEC) +static void write_mute_to_dma_buffer(signed long l_sample, signed long r_sample); +#endif +static void jz_audio_reset(void); +static struct file_operations jz_i2s_audio_fops; + +static DECLARE_WAIT_QUEUE_HEAD (rx_wait_queue); +static DECLARE_WAIT_QUEUE_HEAD (tx_wait_queue); +static DECLARE_WAIT_QUEUE_HEAD (drain_wait_queue); +static DECLARE_WAIT_QUEUE_HEAD (pop_wait_queue); + +struct jz_i2s_controller_info +{ + int io_base; + int dma1; /* for play */ + int dma2; /* for record */ + char *name; + int dev_audio; + struct i2s_codec *i2s_codec[NR_I2S]; + int opened1; + int opened2; + unsigned char *tmp1; /* tmp buffer for sample conversions */ + unsigned char *tmp2; + spinlock_t lock; + spinlock_t ioctllock; + + wait_queue_head_t dac_wait; + wait_queue_head_t adc_wait; + int nextIn; /* byte index to next-in to DMA buffer */ + int nextOut; /* byte index to next-out from DMA buffer */ + int count; /* current byte count in DMA buffer */ + int finish; /* current transfered byte count in DMA buffer */ + unsigned total_bytes; /* total bytes written or read */ + unsigned blocks; + unsigned error; /* over/underrun */ +#ifdef CONFIG_PM + struct pm_dev *pm; +#endif +}; + + +static struct jz_i2s_controller_info *i2s_controller = NULL; +struct i2s_codec +{ + /* I2S controller connected with */ + void *private_data; + char *name; + int id; + int dev_mixer; + /* controller specific lower leverl i2s accessing routines */ + u16 (*codec_read) (u8 reg); /* the function accessing Codec REGs */ + void (*codec_write) (u8 reg, u16 val); + /* Wait for codec-ready */ + void (*codec_wait) (struct i2s_codec *codec); + /* OSS mixer masks */ + int modcnt; + int supported_mixers; + int stereo_mixers; + int record_sources; + int bit_resolution; + /* OSS mixer interface */ + int (*read_mixer) (struct i2s_codec *codec, int oss_channel); + void (*write_mixer)(struct i2s_codec *codec, int oss_channel, + unsigned int left, unsigned int right); + int (*recmask_io) (struct i2s_codec *codec, int rw, int mask); + int (*mixer_ioctl)(struct i2s_codec *codec, unsigned int cmd, unsigned long arg); + /* saved OSS mixer states */ + unsigned int mixer_state[SOUND_MIXER_NRDEVICES]; +}; + + +typedef struct buffer_queue_s +{ + int count; + int *id; + int lock; +} buffer_queue_t; + +typedef struct left_right_sample_s +{ + signed long left; + signed long right; +} left_right_sample_t; + +static unsigned long pop_turn_onoff_buf; +static unsigned long pop_turn_onoff_pbuf; + +static unsigned long *out_dma_buf = NULL; +static unsigned long *out_dma_pbuf = NULL; +static unsigned long *out_dma_buf_data_count = NULL; +static unsigned long *in_dma_buf = NULL; +static unsigned long *in_dma_pbuf = NULL; +static unsigned long *in_dma_buf_data_count = NULL; + +static buffer_queue_t out_empty_queue; +static buffer_queue_t out_full_queue; +static buffer_queue_t out_busy_queue; +static buffer_queue_t in_empty_queue; +static buffer_queue_t in_full_queue; +static buffer_queue_t in_busy_queue; +static int first_record_call = 0; + +static left_right_sample_t save_last_samples[64]; +static int read_codec_file(int addr) +{ + while (__icdc_rgwr_ready()); + __icdc_set_addr(addr); + mdelay(1); + return(__icdc_get_value()); +} + +#if 0 /* mask warning */ +static void printk_codec_files(void) +{ + int cnt; + + printk("\n"); + + 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); + + for (cnt = 0; cnt <= 27 ; cnt++) { + printk(" ( %d : 0x%x ) ",cnt ,read_codec_file(cnt)); + } + printk("\n"); +} +#endif + +static 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); +} + +static 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 */ + + while (__icdc_rgwr_ready()); + __icdc_set_addr(addr); + val &= ~(1 << mask_bit); + if (bitval == 1) + val |= 1 << mask_bit; + + __icdc_set_cmd(val); /* write */ + mdelay(1); + __icdc_set_rgwr(); + mdelay(1); + + while (__icdc_rgwr_ready()); + __icdc_set_addr(addr); + val = __icdc_get_value(); /* read */ + + if (((val >> mask_bit) & bitval) == bitval) + return 1; + else + return 0; +} + +#if 0 /* mask warning */ +/* set Audio data replay */ +static void set_audio_data_replay(void) +{ + /* DAC path */ + write_codec_file(9, 0xff); + //write_codec_file(8, 0x30); + write_codec_file(8, 0x20); + mdelay(10); + 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(1, 1, 7);//CR1.SB_MICBIAS->1 + //mdelay(300); +} +#endif + +/* unset Audio data replay */ +static void unset_audio_data_replay(void) +{ + //write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + //mdelay(800); + //write_codec_file_bit(5, 1, 6);//SB_OUT->1 + //mdelay(800); + 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 +} + +#if 0 /* mask warning */ +/* set Record MIC input audio without playback */ +static void set_record_mic_input_audio_without_playback(void) +{ + /* ADC path for MIC IN */ + jz_mic_only = 1; + write_codec_file(9, 0xff); + write_codec_file(8, 0x3f); + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 + mdelay(10); + write_codec_file_bit(1, 1, 2); + //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 + + write_codec_file(22, 0x40);//mic 1 + 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); +} +#endif + +#if 0 /* mask warning */ +/* unset Record MIC input audio without playback */ +static void unset_record_mic_input_audio_without_playback(void) +{ + /* ADC path for MIC IN */ + jz_mic_only = 0; + 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 + write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 + write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} +#endif + +#if 0 /* mask warning */ +/* set Record LINE input audio without playback */ +static void set_record_line_input_audio_without_playback(void) +{ + /* ADC path for LINE IN */ + jz_mic_only = 1; + write_codec_file(9, 0xff); + write_codec_file(8, 0x3f); + mdelay(10); + write_codec_file(22, 0xf6);//line in 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, 0, 3);//PMR1.SB_LIN->0 + 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 + mdelay(10); + 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); +} +#endif + +#if 0 /* mask warning */ +/* unset Record LINE input audio without playback */ +static void unset_record_line_input_audio_without_playback(void) +{ + /* ADC path for LINE IN */ + write_codec_file_bit(5, 1, 4);//SB_ADC->1 + write_codec_file_bit(5, 1, 3);//ONR1.SB_LIN->1 + + write_codec_file(22, 0xc0);//CR3.SB_MIC1 + write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 + write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} +#endif + +#if 0 /* mask warning */ +/* set Playback LINE input audio direct only */ +static void set_playback_line_input_audio_direct_only(void) +{ + jz_audio_reset();//or init_codec() + REG_AIC_I2SCR = 0x10; + write_codec_file(9, 0xff); + write_codec_file(8, 0x3f); + mdelay(10); + write_codec_file(22, 0xf6);//line in 1 + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 + mdelay(10); + write_codec_file_bit(1, 1, 2);//CR1.HP_BYPASS->1 + write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 + write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0 + write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 + write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0 + + 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, 1, 7);//PMR1.SB_DAC->1 + //write_codec_file_bit(5, 1, 4);//PMR1.SB_ADC->1 +} +#endif + +#if 0 /* mask warning */ +/* unset Playback LINE input audio direct only */ +static void unset_playback_line_input_audio_direct_only(void) +{ + write_codec_file_bit(6, 0, 3);//GIM->0 + write_codec_file_bit(1, 0, 2);//PMR1.BYPASS->0 + write_codec_file_bit(5, 1, 3);//PMR1.SB_LINE->1 + write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 + mdelay(100); + write_codec_file_bit(5, 1, 5);//PMR1.SB_MIX->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} +#endif + +#if 0 /* mask warning */ +/* set Record MIC input audio with direct playback */ +static void set_record_mic_input_audio_with_direct_playback(void) +{ + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 + jz_mic_only = 0; + write_codec_file(9, 0xff); + write_codec_file(8, 0x3f); + mdelay(10); + + write_codec_file(22, 0x60);//mic 1 + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->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, 7);//CR1.SB_MICBIAS->0 + write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 + + 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); +} +#endif + +#if 0 /* mask warning */ +/* unset Record MIC input audio with direct playback */ +static void unset_record_mic_input_audio_with_direct_playback(void) +{ + /* ADC path for MIC IN */ + jz_mic_only = 0; + 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 + write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 + write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} +#endif + +#if 0 /* mask warning */ +/* set Record playing audio mixed with MIC input audio */ +static void set_record_playing_audio_mixed_with_mic_input_audio(void) +{ + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 + write_codec_file(9, 0xff); + //write_codec_file(8, 0x30); + write_codec_file(8, 0x20); + mdelay(10); + + write_codec_file(22, 0x63);//mic 1 + + write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 + write_codec_file_bit(6, 1, 3);// gain set + + 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, 7);//CR1.SB_MICBIAS->0 + write_codec_file_bit(22, 0, 7);//CR3.SB_MIC->0 + write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 + write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 + write_codec_file_bit(5, 0, 4);//PMR1.SB_MIX->0 +} +#endif + +#if 0 /* mask warning */ +/* unset Record playing audio mixed with MIC input audio */ +static void unset_record_playing_audio_mixed_with_mic_input_audio(void) +{ + /* ADC path */ + write_codec_file_bit(5, 1, 4);//SB_ADC->1 + write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 + //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 + write_codec_file(22, 0xc0);//CR3.SB_MIC1->1 + //write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + //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, 5);//SB_MIX->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} +#endif + +#if 0 /* mask warning */ +/* set Record MIC input audio with Audio data replay (full duplex) */ +static void set_record_mic_input_audio_with_audio_data_replay(void) +{ + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 + write_codec_file(9, 0xff); + //write_codec_file(8, 0x30); + write_codec_file(8, 0x20); + 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(22, 0, 7);//CR3.SB_MIC->0 + write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0 + + write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 + write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 +} +#endif + +#if 0 /* mask warning */ +/* unset Record MIC input audio with Audio data replay (full duplex) */ +static void unset_record_mic_input_audio_with_audio_data_replay(void) +{ + /* ADC path */ + write_codec_file_bit(5, 1, 4);//SB_ADC->1 + write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 + //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 + write_codec_file(22, 0xc0);//CR3.SB_MIC1->1 + write_codec_file_bit(5, 1, 7);//SB_DAC->1 + write_codec_file_bit(5, 1, 5);//SB_MIX->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} +#endif + +#if 0 /* mask warning */ +/* set Record LINE input audio with Audio data replay (full duplex for linein) */ +static void set_record_line_input_audio_with_audio_data_replay(void) +{ + write_codec_file(9, 0xff); + //write_codec_file(8, 0x30); + write_codec_file(8, 0x20); + write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 + write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0 + write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 + write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 + //write_codec_file_bit(22, 1, 7);//CR3.SB_MIC->1 + write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 + write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 + + + //jz_mic_only = 1; + write_codec_file(22, 0xc6);//line in 1 + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 + write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 + write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 +} +#endif + +#if 0 /* mask warning */ +/* unset Record LINE input audio with Audio data replay (full duplex for linein) */ +static void unset_record_line_input_audio_with_audio_data_replay(void) +{ + /* ADC path */ + write_codec_file_bit(5, 1, 4);//SB_ADC->1 + write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 + //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 + write_codec_file(22, 0xc0);//CR3.SB_MIC1->1 + write_codec_file_bit(5, 1, 7);//SB_DAC->1 + write_codec_file_bit(5, 1, 5);//SB_MIX->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} +#endif + +static inline int get_buffer_id(struct buffer_queue_s *q) +{ + int r; + unsigned long flags; + int i; + + spin_lock_irqsave(&q->lock, flags); + if (q->count == 0) { + spin_unlock_irqrestore(&q->lock, flags); + return -1; + } + r = *(q->id + 0); + for (i=0;i < q->count-1;i++) + *(q->id + i) = *(q->id + (i+1)); + q->count --; + spin_unlock_irqrestore(&q->lock, flags); + + return r; +} + +static inline void put_buffer_id(struct buffer_queue_s *q, int id) +{ + unsigned long flags; + + spin_lock_irqsave(&q->lock, flags); + *(q->id + q->count) = id; + q->count ++; + spin_unlock_irqrestore(&q->lock, flags); +} + +static inline int elements_in_queue(struct buffer_queue_s *q) +{ + int r; + unsigned long flags; + + spin_lock_irqsave(&q->lock, flags); + r = q->count; + spin_unlock_irqrestore(&q->lock, flags); + + return r; +} + +static inline void audio_start_dma(int chan, void *dev_id, unsigned long phyaddr,int count, int mode) +{ + unsigned long flags; + struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; + + spin_lock_irqsave(&controller->ioctllock, flags); + jz_audio_dma_tran_count = count / jz_audio_b; + spin_unlock_irqrestore(&controller->ioctllock, flags); + flags = claim_dma_lock(); + disable_dma(chan); + clear_dma_ff(chan); + //set_dma_mode(chan, mode); + jz_set_oss_dma(chan, mode, jz_audio_format); + set_dma_addr(chan, phyaddr); + if (count == 0) { + count++; + printk("JzSOC DMA controller can't set dma 0 count!\n"); + } + set_dma_count(chan, count); + enable_dma(chan); + release_dma_lock(flags); +} + +static irqreturn_t jz_i2s_record_dma_irq (int irq, void *dev_id) +{ + int id1, id2; + unsigned long flags; + struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; + int dma = controller->dma2; + + disable_dma(dma); + if (__dmac_channel_address_error_detected(dma)) { + printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); + __dmac_channel_clear_address_error(dma); + } + if (__dmac_channel_transmit_end_detected(dma)) { + __dmac_channel_clear_transmit_end(dma); + + if(drain_flag == 1) + wake_up(&drain_wait_queue); + /* for DSP_GETIPTR */ + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes += jz_audio_dma_tran_count; + controller->blocks ++; + spin_unlock_irqrestore(&controller->ioctllock, flags); + id1 = get_buffer_id(&in_busy_queue); + put_buffer_id(&in_full_queue, id1); + + wake_up(&rx_wait_queue); + wake_up(&controller->adc_wait); + if ((id2 = get_buffer_id(&in_empty_queue)) >= 0) { + put_buffer_id(&in_busy_queue, id2); + *(in_dma_buf_data_count + id2) = *(in_dma_buf_data_count + id1); + dma_cache_wback_inv(*(in_dma_buf + id2), *(in_dma_buf_data_count + id2)); + audio_start_dma(dma,dev_id, + *(in_dma_pbuf + id2), + *(in_dma_buf_data_count + id2), + DMA_MODE_READ); + } else + in_busy_queue.count = 0; + } + + return IRQ_HANDLED; +} + +static irqreturn_t jz_i2s_replay_dma_irq (int irq, void *dev_id) +{ + int id; + unsigned long flags; + struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; + int dma = controller->dma1; + + disable_dma(dma); + if (__dmac_channel_address_error_detected(dma)) { + printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); + __dmac_channel_clear_address_error(dma); + } + if (__dmac_channel_transmit_end_detected(dma)) { + __dmac_channel_clear_transmit_end(dma); + + if(pop_dma_flag == 1) { + pop_dma_flag = 0; + wake_up(&pop_wait_queue); + } else { + if(drain_flag == 1) { + /* Is replay dma buffer over ? */ + if(elements_in_queue(&out_full_queue) <= 0) { + drain_flag = 0; + wake_up(&drain_wait_queue); + } + } + + /* for DSP_GETOPTR */ + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes += jz_audio_dma_tran_count; + controller->blocks ++; + spin_unlock_irqrestore(&controller->ioctllock, flags); + if ((id = get_buffer_id(&out_busy_queue)) < 0) + printk(KERN_DEBUG "Strange DMA finish interrupt for I2S module\n"); + put_buffer_id(&out_empty_queue, id); + if ((id = get_buffer_id(&out_full_queue)) >= 0) { + put_buffer_id(&out_busy_queue, id); + if(*(out_dma_buf_data_count + id) > 0) { + audio_start_dma(dma, dev_id, *(out_dma_pbuf + id), + *(out_dma_buf_data_count + id), + DMA_MODE_WRITE); + last_dma_buffer_id = id; + } + } else + out_busy_queue.count = 0; + + if (elements_in_queue(&out_empty_queue) > 0) { + wake_up(&tx_wait_queue); + wake_up(&controller->dac_wait); + } + } + } + + return IRQ_HANDLED; +} + +static void jz_i2s_initHw(int set) +{ +#if defined(CONFIG_MIPS_JZ_URANUS) + i2s_clk = 48000000; +#else + i2s_clk = __cpm_get_i2sclk(); +#endif + __i2s_disable(); + if(set) + __i2s_reset(); + schedule_timeout(5); + if(each_time_init_codec) + each_time_init_codec(); + __i2s_disable_record(); + __i2s_disable_replay(); + __i2s_disable_loopback(); + __i2s_set_transmit_trigger(4); + __i2s_set_receive_trigger(3); +} + +static int Init_In_Out_queue(int fragstotal,int fragsize) +{ + int i; + + /* recording */ + in_empty_queue.count = fragstotal; + in_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!in_dma_buf) + goto all_mem_err; + in_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!in_dma_pbuf) + goto all_mem_err; + in_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!in_dma_buf_data_count) + goto all_mem_err; + in_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!in_empty_queue.id) + goto all_mem_err; + in_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!in_full_queue.id) + goto all_mem_err; + in_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!in_busy_queue.id) + goto all_mem_err; + + for (i=0;i < fragstotal;i++) + *(in_empty_queue.id + i) = i; + in_full_queue.count = 0; + in_busy_queue.count = 0; + + for (i = 0; i < fragstotal; i++) { + *(in_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); + if (*(in_dma_buf + i) == 0) + goto mem_failed_in; + *(in_dma_pbuf + i) = virt_to_phys((void *)(*(in_dma_buf + i))); + dma_cache_wback_inv(*(in_dma_buf + i), fragsize); + } + + /* playing */ + out_empty_queue.count = fragstotal; + out_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!out_dma_buf) + goto all_mem_err; + out_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!out_dma_pbuf) + goto all_mem_err; + out_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + + if (!out_dma_buf_data_count) + goto all_mem_err; + out_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!out_empty_queue.id) + goto all_mem_err; + out_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!out_full_queue.id) + goto all_mem_err; + out_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!out_busy_queue.id) + goto all_mem_err; + for (i=0;i < fragstotal;i++) + *(out_empty_queue.id + i) = i; + + out_busy_queue.count = 0; + out_full_queue.count = 0; + /* alloc DMA buffer */ + for (i = 0; i < fragstotal; i++) { + *(out_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); + if (*(out_dma_buf + i) == 0) { + printk(" can't allocate required DMA(OUT) buffers.\n"); + goto mem_failed_out; + } + *(out_dma_pbuf + i) = virt_to_phys((void *)(*(out_dma_buf + i))); + } + + return 1; +all_mem_err: + printk("error:allocate memory occur error 1!\n"); + return 0; +mem_failed_out: + printk("error:allocate memory occur error 2!\n"); + for (i = 0; i < fragstotal; i++) { + if(*(out_dma_buf + i)) + free_pages(*(out_dma_buf + i), get_order(fragsize)); + } + + return 0; +mem_failed_in: + printk("error:allocate memory occur error 3!\n"); + for (i = 0; i < fragstotal; i++) { + if(*(in_dma_buf + i)) + free_pages(*(in_dma_buf + i), get_order(fragsize)); + } + return 0; +} + +static int Free_In_Out_queue(int fragstotal,int fragsize) +{ + int i; + /* playing */ + if(out_dma_buf != NULL) { + for (i = 0; i < fragstotal; i++) { + if(*(out_dma_buf + i)) + free_pages(*(out_dma_buf + i), get_order(fragsize)); + *(out_dma_buf + i) = 0; + } + kfree(out_dma_buf); + out_dma_buf = NULL; + } + if(out_dma_pbuf) { + kfree(out_dma_pbuf); + out_dma_pbuf = NULL; + } + if(out_dma_buf_data_count) { + kfree(out_dma_buf_data_count); + out_dma_buf_data_count = NULL; + } + if(out_empty_queue.id) { + kfree(out_empty_queue.id); + out_empty_queue.id = NULL; + } + if(out_full_queue.id) { + kfree(out_full_queue.id); + out_full_queue.id = NULL; + } + if(out_busy_queue.id) { + kfree(out_busy_queue.id); + out_busy_queue.id = NULL; + } + out_empty_queue.count = fragstotal; + out_busy_queue.count = 0; + out_full_queue.count = 0; + + /* recording */ + if(in_dma_buf) { + for (i = 0; i < fragstotal; i++) { + if(*(in_dma_buf + i)) { + dma_cache_wback_inv(*(in_dma_buf + i), fragsize); + free_pages(*(in_dma_buf + i), get_order(fragsize)); + } + *(in_dma_buf + i) = 0; + } + kfree(in_dma_buf); + in_dma_buf = NULL; + } + if(in_dma_pbuf) { + kfree(in_dma_pbuf); + in_dma_pbuf = NULL; + } + if(in_dma_buf_data_count) { + kfree(in_dma_buf_data_count); + in_dma_buf_data_count = NULL; + } + if(in_empty_queue.id) { + kfree(in_empty_queue.id); + in_empty_queue.id = NULL; + } + if(in_full_queue.id) { + kfree(in_full_queue.id); + in_full_queue.id = NULL; + } + if(in_busy_queue.id) { + kfree(in_busy_queue.id); + in_busy_queue.id = NULL; + } + + in_empty_queue.count = fragstotal; + in_full_queue.count = 0; + in_busy_queue.count = 0; + + return 1; +} + +static void jz_i2s_full_reset(struct jz_i2s_controller_info *controller) +{ + jz_i2s_initHw(0); +} + +static int jz_audio_set_speed(int dev, int rate) +{ + /* 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 99999999 ? */ + jz_audio_speed = rate; + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + jz_audio_rate = rate; + + if(set_codec_speed) + set_codec_speed(rate); + + return jz_audio_rate; +} + + +static int record_fill_1x8_u(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + unsigned long data; + volatile unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); + volatile unsigned char *dp = (unsigned char*)dst_start; + + while (count > 0) { + count -= 2; /* count in dword */ + cnt++; + data = *(s++); + *(dp ++) = ((data << 16) >> 24) + 0x80; + s++; /* skip the other channel */ + } + + return cnt; +} + + +static int record_fill_2x8_u(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + unsigned long d1, d2; + volatile unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); + volatile unsigned char *dp = (unsigned char*)dst_start; + + while (count > 0) { + count -= 2; + cnt += 2; + d1 = *(s++); + *(dp ++) = ((d1 << 16) >> 24) + 0x80; + d2 = *(s++); + *(dp ++) = ((d2 << 16) >> 24) + 0x80; + } + + return cnt; +} + + +static int record_fill_1x16_s(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + unsigned long d1; + unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); + unsigned short *dp = (unsigned short *)dst_start; + + while (count > 0) { + count -= 2; /* count in dword */ + cnt += 2; /* count in byte */ + d1 = *(s++); + *(dp ++) = (d1 << 16) >> 16; + s++; /* skip the other channel */ + } + + return cnt; +} + + +static int record_fill_2x16_s(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + unsigned long d1, d2; + unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); + unsigned short *dp = (unsigned short *)dst_start; + while (count > 0) { + count -= 2; /* count in dword */ + cnt += 4; /* count in byte */ + d1 = *(s++); + d2 = *(s++); + if(abnormal_data_count > 0) { + d1 = d2 = 0; + abnormal_data_count --; + } + *(dp ++) = (d1 << 16) >> 16; + *(dp ++) = (d2 << 16) >> 16; + } + + return cnt; +} + +static void replay_fill_1x8_u(signed long src_start, int count, int id) +{ + int cnt = 0; + unsigned char data; + unsigned long ddata; + volatile unsigned char *s = (unsigned char *)src_start; + volatile unsigned long *dp = (unsigned long*)(*(out_dma_buf + id)); + + while (count > 0) { + count--; + cnt += 1; + data = *(s++) - 0x80; + ddata = (unsigned long) data << 8; + *(dp ++) = ddata; + *(dp ++) = ddata; + + /* save last left and right */ + if(count == 1) { + save_last_samples[id].left = ddata; + save_last_samples[id].right = ddata; + } + } + cnt = cnt * 2 * jz_audio_b; + *(out_dma_buf_data_count + id) = cnt; +} + + +static void replay_fill_2x8_u(signed long src_start, int count, int id) +{ + int cnt = 0; + unsigned char d1; + unsigned long dd1; + volatile unsigned char *s = (unsigned char *)src_start; + volatile unsigned long *dp = (unsigned long*)(*(out_dma_buf + id)); + + while (count > 0) { + count -= 1; + cnt += 1 ; + d1 = *(s++) - 0x80; + dd1 = (unsigned long) d1 << 8; + *(dp ++) = dd1; + /* save last left */ + if(count == 2) + save_last_samples[id].left = dd1; + /* save last right */ + if(count == 1) + save_last_samples[id].right = dd1; + } + cnt *= jz_audio_b; + *(out_dma_buf_data_count + id) = cnt; +} + + +static void replay_fill_1x16_s(signed long src_start, int count, int id) +{ + int cnt = 0; + signed short d1; + signed long l1; + volatile signed short *s = (signed short *)src_start; + volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); + + while (count > 0) { + count -= 2; + cnt += 2 ; + d1 = *(s++); + l1 = (signed long)d1; + *(dp ++) = l1; + *(dp ++) = l1; + + /* save last left and right */ + if(count == 1) { + save_last_samples[id].left = l1; + save_last_samples[id].right = l1; + } + } + cnt = cnt * 2 * jz_audio_b; + *(out_dma_buf_data_count + id) = cnt; +} + +#if 0 +static void replay_fill_2x16_s(signed long src_start, int count, int id) +{ + int cnt = 0; + signed short d1; + signed long l1; + int mute_cnt = 0; + signed long tmp1,tmp2; + volatile signed short *s = (signed short *)src_start; + volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); +#if defined(CONFIG_I2S_ICDC) + volatile signed long *before_dp; + int sam_rate = jz_audio_rate / 20; + + tmp1 = tmp2 = 0; + while (count > 0) { + count -= 2; + cnt += 2; + d1 = *(s++); + + l1 = (signed long)d1; + l1 >>= codec_volue_shift; + + if(l1 == 0) { + mute_cnt ++; + if(mute_cnt >= sam_rate) { + before_dp = dp - 10; + *(before_dp) = (signed long)1; + before_dp = dp - 11; + *(before_dp) = (signed long)1; + mute_cnt = 0; + } + } else + mute_cnt = 0; + + *(dp ++) = l1; + + tmp1 = tmp2; + tmp2 = l1; + } + + /* save last left */ + save_last_samples[id].left = tmp1; + /* save last right */ + save_last_samples[id].right = tmp2; +#endif +#if defined(CONFIG_I2S_DLV) + while (count > 0) { + count -= 2; + cnt += 2; + d1 = *(s++); + + l1 = (signed long)d1; + + *(dp ++) = l1; + } +#endif + cnt *= jz_audio_b; + *(out_dma_buf_data_count + id) = cnt; +} +#else +static void replay_fill_2x16_s(signed long src_start, int count, int id) +{ + int cnt = 0; + signed short d1; + signed long l1; + +#if 0 + volatile signed short *s = (signed short *)src_start; + volatile signed short *dp = (signed short*)(*(out_dma_buf + id)); + memcpy((char*)dp, (char*)s, count); + *(out_dma_buf_data_count + id) = count; +#else + volatile signed short *s = (signed short *)src_start; + volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); + while (count > 0) { + count -= 2; + cnt += 2; + d1 = *(s++); + + l1 = (signed long)d1; + + *(dp ++) = l1; + } + cnt *= jz_audio_b; + *(out_dma_buf_data_count + id) = cnt; +#endif +} +#endif + + +static unsigned int jz_audio_set_format(int dev, unsigned int fmt) +{ + switch (fmt) { + case AFMT_U8: + __i2s_set_oss_sample_size(8); + __i2s_set_iss_sample_size(8); + jz_audio_format = fmt; + jz_update_filler(jz_audio_format, jz_audio_channels); + break; + case AFMT_S16_LE: +#if defined(CONFIG_I2S_DLV) + /* DAC path and ADC path */ + write_codec_file(2, 0x00); + //write_codec_file(2, 0x60); +#endif + jz_audio_format = fmt; + jz_update_filler(jz_audio_format,jz_audio_channels); + /* print all files */ + __i2s_set_oss_sample_size(16); + __i2s_set_iss_sample_size(16); + break; + + case AFMT_QUERY: + break; + } + + return jz_audio_format; +} + + +static short jz_audio_set_channels(int dev, short channels) +{ + switch (channels) { + case 1: + if(set_codec_some_func) + set_codec_some_func(); + jz_audio_channels = channels; + jz_update_filler(jz_audio_format, jz_audio_channels); +#if defined(CONFIG_I2S_DLV) + write_codec_file_bit(1, 1, 6);//CR1.MONO->1 for Mono +#endif + break; + case 2: + jz_audio_channels = channels; + jz_update_filler(jz_audio_format, jz_audio_channels); +#if defined(CONFIG_I2S_DLV) + write_codec_file_bit(1, 0, 6);//CR1.MONO->0 for Stereo +#endif + break; + case 0: + break; + } + + return jz_audio_channels; +} + +static void init_codec(void) +{ + /* inititalize internal I2S codec */ + if(init_codec_pin) + init_codec_pin(); + +#if defined(CONFIG_I2S_ICDC) + /* initialize AIC but not reset it */ + jz_i2s_initHw(0); +#endif + if(reset_codec) + reset_codec(); +} + +static void jz_audio_reset(void) +{ + __i2s_disable_replay(); + __i2s_disable_receive_dma(); + __i2s_disable_record(); + __i2s_disable_transmit_dma(); +#if defined(CONFIG_I2S_DLV) + REG_AIC_I2SCR = 0x10; +#endif + init_codec(); +} + +static int jz_audio_release(struct inode *inode, struct file *file); +static int jz_audio_open(struct inode *inode, struct file *file); +static int jz_audio_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg); +static unsigned int jz_audio_poll(struct file *file,struct poll_table_struct *wait); +static ssize_t jz_audio_write(struct file *file, const char *buffer,size_t count, loff_t *ppos); +static ssize_t jz_audio_read(struct file *file, char *buffer,size_t count, loff_t *ppos); + +/* static struct file_operations jz_i2s_audio_fops */ +static struct file_operations jz_i2s_audio_fops = +{ + owner: THIS_MODULE, + open: jz_audio_open, + release: jz_audio_release, + write: jz_audio_write, + read: jz_audio_read, + poll: jz_audio_poll, + ioctl: jz_audio_ioctl +}; + +static int jz_i2s_open_mixdev(struct inode *inode, struct file *file) +{ + int i; + int minor = MINOR(inode->i_rdev); + struct jz_i2s_controller_info *controller = i2s_controller; + + for (i = 0; i < NR_I2S; i++) + if (controller->i2s_codec[i] != NULL && controller->i2s_codec[i]->dev_mixer == minor) + goto match; + + if (!controller) + return -ENODEV; +match: + file->private_data = controller->i2s_codec[i]; + + return 0; +} + +static int jz_i2s_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct i2s_codec *codec = (struct i2s_codec *)file->private_data; + return codec->mixer_ioctl(codec, cmd, arg); +} + +static loff_t jz_i2s_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +static struct file_operations jz_i2s_mixer_fops = +{ + owner: THIS_MODULE, + llseek: jz_i2s_llseek, + ioctl: jz_i2s_ioctl_mixdev, + open: jz_i2s_open_mixdev, +}; + +static int i2s_mixer_ioctl(struct i2s_codec *codec, unsigned int cmd, unsigned long arg) +{ + int ret; + long val = 0; + switch (cmd) { + case SOUND_MIXER_INFO: + + if(codec_mixer_info_id_name) + codec_mixer_info_id_name(); + info.modify_counter = audio_mix_modcnt; + + return copy_to_user((void *)arg, &info, sizeof(info)); + case SOUND_OLD_MIXER_INFO: + + if(codec_mixer_old_info_id_name) + codec_mixer_old_info_id_name(); + + return copy_to_user((void *)arg, &old_info, sizeof(info)); + case SOUND_MIXER_READ_STEREODEVS: + + return put_user(0, (long *) arg); + case SOUND_MIXER_READ_CAPS: + + val = SOUND_CAP_EXCL_INPUT; + return put_user(val, (long *) arg); + case SOUND_MIXER_READ_DEVMASK: + break; + case SOUND_MIXER_READ_RECMASK: + break; + case SOUND_MIXER_READ_RECSRC: + break; + case SOUND_MIXER_WRITE_SPEAKER: + + ret = get_user(val, (long *) arg); + if (ret) + return ret; + val = val & 0xff; + if(val < 0) + val = 0; + if(val > 100) + val = 100; + switch(val) { + case 100: + if(set_codec_direct_mode) + set_codec_direct_mode(); + break; + case 0: + if(clear_codec_direct_mode) + clear_codec_direct_mode(); + break; + } + break; + case SOUND_MIXER_WRITE_BASS: + + ret = get_user(val, (long *) arg); + if (ret) + return ret; + + val = val & 0xff; + if(val < 0) + val = 0; + if(val > 100) + val = 100; + codec_bass_gain = val; + if(set_codec_bass) + set_codec_bass(val); + + return 0; + case SOUND_MIXER_READ_BASS: + + val = codec_bass_gain; + ret = val << 8; + val = val | ret; + + return put_user(val, (long *) arg); + case SOUND_MIXER_WRITE_VOLUME: + ret = get_user(val, (long *) arg); + if (ret) + return ret; + val = val & 0xff; + if(val < 0) + val = 0; + if(val > 100) + val = 100; + if (val > 31) + val = 31; + jz_audio_volume = val; + + if(set_codec_volume) + set_codec_volume(val); + return 0; + case SOUND_MIXER_READ_VOLUME: + + val = jz_audio_volume; + ret = val << 8; + val = val | ret; + + return put_user(val, (long *) arg); + case SOUND_MIXER_WRITE_MIC: + + ret = get_user(val, (long *) arg); + if (ret) + return ret; + + val = val & 0xff; + if(val < 0) + val = 0; + if(val > 100) + val = 100; + codec_mic_gain = val; + if(set_codec_mic) + set_codec_mic(val); + + return 0; + case SOUND_MIXER_READ_MIC: + + val = codec_mic_gain; + ret = val << 8; + val = val | ret; + + return put_user(val, (long *) arg); + default: + return -ENOSYS; + } + audio_mix_modcnt ++; + return 0; +} + + +int i2s_probe_codec(struct i2s_codec *codec) +{ + /* generic OSS to I2S wrapper */ + codec->mixer_ioctl = i2s_mixer_ioctl; + return 1; +} + + +/* I2S codec initialisation. */ +static int __init jz_i2s_codec_init(struct jz_i2s_controller_info *controller) +{ + int num_i2s = 0; + struct i2s_codec *codec; + + for (num_i2s = 0; num_i2s < NR_I2S; num_i2s++) { + if ((codec = kmalloc(sizeof(struct i2s_codec),GFP_KERNEL)) == NULL) + return -ENOMEM; + memset(codec, 0, sizeof(struct i2s_codec)); + codec->private_data = controller; + codec->id = num_i2s; + + if (i2s_probe_codec(codec) == 0) + break; + if ((codec->dev_mixer = register_sound_mixer(&jz_i2s_mixer_fops, -1)) < 0) { + printk(KERN_ERR "Jz I2S: couldn't register mixer!\n"); + kfree(codec); + break; + } + controller->i2s_codec[num_i2s] = codec; + } + return num_i2s; +} + + +static void jz_update_filler(int format, int channels) +{ +#define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3)) + + switch (TYPE(format, channels)) + { + + case TYPE(AFMT_U8, 1): + jz_audio_b = 4; /* 4bytes * 8bits =32bits */ + replay_filler = replay_fill_1x8_u; + record_filler = record_fill_1x8_u; + break; + case TYPE(AFMT_U8, 2): + jz_audio_b = 4; + replay_filler = replay_fill_2x8_u; + record_filler = record_fill_2x8_u; + break; + case TYPE(AFMT_S16_LE, 1): + jz_audio_b = 2; /* 2bytes * 16bits =32bits */ + replay_filler = replay_fill_1x16_s; + record_filler = record_fill_1x16_s; + break; + case TYPE(AFMT_S16_LE, 2): + jz_audio_b = 2; + replay_filler = replay_fill_2x16_s; + record_filler = record_fill_2x16_s; + break; + default: + ; + } +} + + +#ifdef CONFIG_PROC_FS +extern struct proc_dir_entry *proc_jz_root; +int i2s_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + return 0; +} + +static int jz_i2s_init_proc(struct jz_i2s_controller_info *controller) +{ + if (!create_proc_read_entry ("i2s", 0, proc_jz_root, i2s_read_proc, controller->i2s_codec[0])) + return -EIO; + return 0; +} + +static void jz_i2s_cleanup_proc(struct jz_i2s_controller_info *controller) +{ +} +#endif + +static void __init attach_jz_i2s(struct jz_i2s_controller_info *controller) +{ + char *name; + int adev; /* No of Audio device. */ + + name = controller->name; + /* initialize AIC controller and reset it */ + jz_i2s_initHw(1); + adev = register_sound_dsp(&jz_i2s_audio_fops, -1); + if (adev < 0) + goto audio_failed; + /* initialize I2S codec and register /dev/mixer */ + if (jz_i2s_codec_init(controller) <= 0) + goto mixer_failed; + +#ifdef CONFIG_PROC_FS + if (jz_i2s_init_proc(controller) < 0) { + printk(KERN_ERR "%s: can't create I2S proc filesystem.\n", name); + goto proc_failed; + } +#endif + + controller->tmp1 = (void *)__get_free_pages(GFP_KERNEL, 8); + if (!controller->tmp1) { + printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name); + goto tmp1_failed; + } + controller->tmp2 = (void *)__get_free_pages(GFP_KERNEL, 8); + if (!controller->tmp2) { + printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name); + goto tmp2_failed; + } + if ((controller->dma2 = jz_request_dma(DMA_ID_I2S_RX, "audio adc", jz_i2s_record_dma_irq, IRQF_DISABLED, controller)) < 0) { + printk(KERN_ERR "%s: can't reqeust DMA ADC channel.\n", name); + goto dma2_failed; + } + if ((controller->dma1 = jz_request_dma(DMA_ID_I2S_TX, "audio dac", jz_i2s_replay_dma_irq, IRQF_DISABLED, controller)) < 0) { + printk(KERN_ERR "%s: can't reqeust DMA DAC channel.\n", name); + goto dma1_failed; + } + printk("JzSOC On-Chip I2S controller registered (DAC: DMA(play):%d/IRQ%d,\n ADC: DMA(record):%d/IRQ%d)\n", controller->dma1, get_dma_done_irq(controller->dma1), controller->dma2, get_dma_done_irq(controller->dma2)); + + controller->dev_audio = adev; + pop_turn_onoff_buf = __get_free_pages(GFP_KERNEL | GFP_DMA, 8); + if(!pop_turn_onoff_buf) + printk("pop_turn_onoff_buf alloc is wrong!\n"); + pop_turn_onoff_pbuf = virt_to_phys((void *)pop_turn_onoff_buf); + + return; +dma2_failed: + jz_free_dma(controller->dma1); +dma1_failed: + free_pages((unsigned long)controller->tmp2, 8); +tmp2_failed: + free_pages((unsigned long)controller->tmp1, 8); +tmp1_failed: + +#ifdef CONFIG_PROC_FS + jz_i2s_cleanup_proc(controller); +#endif +proc_failed: + /* unregister mixer dev */ +mixer_failed: + unregister_sound_dsp(adev); +audio_failed: + return; +} + +static int __init probe_jz_i2s(struct jz_i2s_controller_info **controller) +{ + if ((*controller = kmalloc(sizeof(struct jz_i2s_controller_info), + GFP_KERNEL)) == NULL) { + printk(KERN_ERR "Jz I2S Controller: out of memory.\n"); + return -ENOMEM; + } + (*controller)->name = "Jz I2S controller"; + (*controller)->opened1 = 0; + (*controller)->opened2 = 0; + init_waitqueue_head(&(*controller)->adc_wait); + init_waitqueue_head(&(*controller)->dac_wait); + spin_lock_init(&(*controller)->lock); + init_waitqueue_head(&rx_wait_queue); + init_waitqueue_head(&tx_wait_queue); + init_waitqueue_head(&pop_wait_queue); + init_waitqueue_head(&drain_wait_queue); + + return 0; +} + +static void __exit unload_jz_i2s(struct jz_i2s_controller_info *controller) +{ + int adev = controller->dev_audio; + + jz_i2s_full_reset(controller); + controller->dev_audio = -1; + if (old_mksound) + kd_mksound = old_mksound;/* Our driver support bell for kb, see vt.c */ + +#ifdef CONFIG_PROC_FS + jz_i2s_cleanup_proc(controller); +#endif + + jz_free_dma(controller->dma1); + jz_free_dma(controller->dma2); + free_pages((unsigned long)controller->tmp1, 8); + free_pages((unsigned long)controller->tmp2, 8); + free_pages((unsigned long)pop_turn_onoff_buf, 8); + + if (adev >= 0) { + /* unregister_sound_mixer(audio_devs[adev]->mixer_dev); */ + unregister_sound_dsp(controller->dev_audio); + } +} + +#ifdef CONFIG_PM +static int jz_i2s_suspend(struct jz_i2s_controller_info *controller, int state) +{ + if(i2s_suspend_codec) + i2s_suspend_codec(controller->opened1,controller->opened2); + printk("Aic and codec are suspended!\n"); + return 0; +} + +static int jz_i2s_resume(struct jz_i2s_controller_info *controller) +{ + if(i2s_resume_codec) + i2s_resume_codec(); + +#if defined(CONFIG_I2S_AK4642EN) + jz_i2s_initHw(0); + jz_audio_reset(); + __i2s_enable(); + jz_audio_set_speed(controller->dev_audio,jz_audio_speed); + /* playing */ + if(controller->opened1) { + if(set_codec_replay) + set_codec_replay(); + int dma = controller->dma1; + int id; + unsigned long flags; + disable_dma(dma); + if(__dmac_channel_address_error_detected(dma)) { + printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); + __dmac_channel_clear_address_error(dma); + } + if(__dmac_channel_transmit_end_detected(dma)) + __dmac_channel_clear_transmit_end(dma); + + /* for DSP_GETOPTR */ + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes += jz_audio_dma_tran_count; + controller->blocks ++; + spin_unlock_irqrestore(&controller->ioctllock, flags); + while((id = get_buffer_id(&out_busy_queue)) >= 0) + put_buffer_id(&out_empty_queue, id); + + out_busy_queue.count=0; + if((id = get_buffer_id(&out_full_queue)) >= 0) { + put_buffer_id(&out_empty_queue, id); + } + if (elements_in_queue(&out_empty_queue) > 0) { + wake_up(&tx_wait_queue); + wake_up(&controller->dac_wait); + } else + printk("pm out_empty_queue empty"); + } + + /* recording */ + if(controller->opened2) { + if(set_codec_record) + set_codec_record(); + int dma = controller->dma2; + int id1, id2; + unsigned long flags; + disable_dma(dma); + if (__dmac_channel_address_error_detected(dma)) { + printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); + __dmac_channel_clear_address_error(dma); + } + if (__dmac_channel_transmit_end_detected(dma)) { + __dmac_channel_clear_transmit_end(dma); + } + /* for DSP_GETIPTR */ + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes += jz_audio_dma_tran_count; + controller->blocks ++; + spin_unlock_irqrestore(&controller->ioctllock, flags); + id1 = get_buffer_id(&in_busy_queue); + put_buffer_id(&in_full_queue, id1); + wake_up(&rx_wait_queue); + wake_up(&controller->adc_wait); + if ((id2 = get_buffer_id(&in_empty_queue)) >= 0) { + put_buffer_id(&in_full_queue, id2); + } + in_busy_queue.count = 0; + } +#endif + + return 0; +} + +static int jz_i2s_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data) +{ + int ret; + struct jz_i2s_controller_info *controller = pm_dev->data; + + if (!controller) return -EINVAL; + + switch (req) { + case PM_SUSPEND: + ret = jz_i2s_suspend(controller, (int)data); + break; + case PM_RESUME: + ret = jz_i2s_resume(controller); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} +#endif /* CONFIG_PM */ +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) { + + 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; +} + +static int __init init_jz_i2s(void) +{ + int errno, retval; +#if defined(CONFIG_I2S_DLV) + + ramp_up_start = 0; + ramp_up_end = 0; + gain_up_start = 0; + gain_up_end = 0; + ramp_down_start = 0; + ramp_down_end = 0; + gain_down_start = 0; + gain_down_end = 0; +#endif + + abnormal_data_count = 0; + if(set_codec_mode) + set_codec_mode(); + + drain_flag = 0; + if ((errno = probe_jz_i2s(&i2s_controller)) < 0) + return errno; + if(set_codec_gpio_pin) + set_codec_gpio_pin(); + + attach_jz_i2s(i2s_controller); + if(set_codec_startup_param) + set_codec_startup_param(); +#if defined(CONFIG_I2S_DLV) + jz_codec_config = 0; + 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; + } +#endif + if(set_codec_volume_table) + set_codec_volume_table(); + + out_empty_queue.id = NULL; + out_full_queue.id = NULL; + out_busy_queue.id = NULL; + in_empty_queue.id = NULL; + in_full_queue.id = NULL; + in_busy_queue.id = NULL; + + jz_audio_fragsize = JZCODEC_RW_BUFFER_SIZE * PAGE_SIZE; + jz_audio_fragstotal = JZCODEC_RW_BUFFER_TOTAL ; + Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); + +#ifdef CONFIG_PM + i2s_controller->pm = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, + jz_i2s_pm_callback); + if (i2s_controller->pm) + i2s_controller->pm->data = i2s_controller; +#endif + +#if defined(CONFIG_I2S_DLV) + __cpm_start_idct(); + __cpm_start_db(); + __cpm_start_me(); + __cpm_start_mc(); + __cpm_start_ipu(); +#endif + + printk("JZ I2S OSS audio driver initialized\n"); + + return 0; +} + +static void __exit cleanup_jz_i2s(void) +{ +#ifdef CONFIG_PM + /* pm_unregister(i2s_controller->pm); */ +#endif +#if defined(CONFIG_I2S_DLV) + free_irq(IRQ_AIC, NULL); +#endif + unload_jz_i2s(i2s_controller); + Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); + if(clear_codec_mode) + clear_codec_mode(); +} + +module_init(init_jz_i2s); +module_exit(cleanup_jz_i2s); + +#if defined(CONFIG_SOC_JZ4730) +static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count,con; + + if(elements_in_queue(&in_busy_queue) > 0) { + if (nonblock) + return -EBUSY; + drain_flag = 1; + sleep_on(&drain_wait_queue); + drain_flag = 0; + } else { + add_wait_queue(&ctrl->adc_wait, &wait); + for (con = 0; con < 1000; con ++) { + udelay(1); + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&ctrl->lock, flags); + count = get_dma_residue(ctrl->dma2); + spin_unlock_irqrestore(&ctrl->lock, flags); + if (count <= 0) + break; + if (nonblock) { + remove_wait_queue(&ctrl->adc_wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + } + remove_wait_queue(&ctrl->adc_wait, &wait); + current->state = TASK_RUNNING; + } + return 0; +} + +static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count; + + if(elements_in_queue(&out_full_queue) > 0) { + if (nonblock) + return -EBUSY; + + drain_flag = 1; + sleep_on(&drain_wait_queue); + drain_flag = 0; + } else { + add_wait_queue(&(ctrl->dac_wait), &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if(elements_in_queue(&out_full_queue) <= 0) { + spin_lock_irqsave(&ctrl->lock, flags); + count = get_dma_residue(ctrl->dma1); + spin_unlock_irqrestore(&ctrl->lock, flags); + if(count <= 0) + break; + } + if (nonblock) { + remove_wait_queue(&ctrl->dac_wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + } + remove_wait_queue(&ctrl->dac_wait, &wait); + current->state = TASK_RUNNING; + } + + return 0; +} +#endif + +#if defined(CONFIG_SOC_JZ4750) || defined(CONFIG_SOC_JZ4750D) +static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock) +{ + //DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count,i=0; + + //add_wait_queue(&ctrl->adc_wait, &wait); + for (;;) { + if (i < MAXDELAY) { + udelay(10); + i++; + } else + break; + //set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&ctrl->lock, flags); + //spin_lock(&ctrl->lock); + count = get_dma_residue(ctrl->dma2); + spin_unlock_irqrestore(&ctrl->lock, flags); + //spin_unlock(&ctrl->lock); + if (count <= 0) + break; + + /*if (signal_pending(current)) + break;*/ + if (nonblock) { + //remove_wait_queue(&ctrl->adc_wait, &wait); + //current->state = TASK_RUNNING; + return -EBUSY; + } + } + //remove_wait_queue(&ctrl->adc_wait, &wait); + //current->state = TASK_RUNNING; + /*if (signal_pending(current)) + return -ERESTARTSYS;*/ + return 0; +} +static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock) +{ + unsigned long flags; + int count,ele,busyele,emptyele,i=0; + + for (;;) { + if(!nonblock) {//blocked + if (i < MAXDELAY) { + udelay(10); + i++; + } else + break; + + ele = elements_in_queue(&out_full_queue); + if(ele <= 0) { + udelay(200); + + busyele = elements_in_queue(&out_busy_queue); + emptyele = elements_in_queue(&out_empty_queue); + if (busyele <= 0 && emptyele >= jz_audio_fragstotal) { + spin_lock_irqsave(&ctrl->lock, flags); + count = get_dma_residue(ctrl->dma1); + spin_unlock_irqrestore(&ctrl->lock, flags); + if (count <= 0) + break; + } + } + } else {//non-blocked + //mdelay(100); + ele = elements_in_queue(&out_full_queue); + + if(ele <= 0) { + //mdelay(100); + busyele = elements_in_queue(&out_busy_queue); + emptyele = elements_in_queue(&out_empty_queue); + + if (busyele <= 0 && emptyele >= jz_audio_fragstotal) { + spin_lock_irqsave(&ctrl->lock, flags); + count = get_dma_residue(ctrl->dma1); + spin_unlock_irqrestore(&ctrl->lock, flags); + if (count <= 0) + break; + } + } + } + } + + return 0; +} +#endif + +#if defined(CONFIG_SOC_JZ4740) +#define MAXDELAY 50000 +static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock) +{ + int count,ele,i=0; + + for (;;) { + if(!nonblock) {//blocked + if ( i < MAXDELAY ) { + udelay(10); + i++; + } else + break; + + ele = elements_in_queue(&out_full_queue); + if(ele <= 0) { + udelay(10); + spin_lock(&ctrl->lock); + count = get_dma_residue(ctrl->dma1); + spin_unlock(&ctrl->lock); + if (count <= 0) + break; + } + } else {//non-blocked + mdelay(100); + ele = elements_in_queue(&out_full_queue); + + if(ele <= 0) { + mdelay(100); + + spin_lock(&ctrl->lock); + count = get_dma_residue(ctrl->dma1); + spin_unlock(&ctrl->lock); + if (count <= 0) + break; + } + } + } + + return 0; +} + +static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock) +{ + int count,i=0; + + for (;;) { + if ( i < MAXDELAY ) + { + udelay(10); + i++; + } + else + break; + spin_lock(&ctrl->lock); + count = get_dma_residue(ctrl->dma2); + spin_unlock(&ctrl->lock); + if (count <= 0) + break; + + if (nonblock) { + return -EBUSY; + } + } + + return 0; +} +#endif + +static int jz_audio_release(struct inode *inode, struct file *file) +{ + unsigned long flags; + struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; + unsigned long tfl; + + if (controller == NULL) + return -ENODEV; + + pop_dma_flag = 0; + if (controller->opened1 == 1) { + controller->opened1 = 0; + __i2s_enable_transmit_dma(); + __i2s_enable_replay(); + drain_dac(controller, file->f_flags & O_NONBLOCK); + /* add some mute to anti-pop */ +#if defined(CONFIG_I2S_DLV) + /* wait for fifo empty */ + write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + gain_down_start = jiffies; + sleep_on(&pop_wait_queue); + //gain_down_end = jiffies; + while (1) { + tfl = REG_AIC_SR & 0x00003f00; + if (tfl == 0) { + udelay(500); + break; + } + mdelay(2); + } +#endif + disable_dma(controller->dma1); + set_dma_count(controller->dma1, 0); + __i2s_disable_transmit_dma(); + __i2s_disable_replay(); + __aic_flush_fifo(); + if(clear_codec_replay) + clear_codec_replay(); + __aic_flush_fifo(); + + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes = 0; + controller->count = 0; + controller->finish = 0; + jz_audio_dma_tran_count = 0; + controller->blocks = 0; + controller->nextOut = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + +#if defined(CONFIG_I2S_DLV) + write_codec_file_bit(5, 1, 6);//SB_OUT->1 + ramp_down_start = jiffies; + sleep_on(&pop_wait_queue); + //ramp_down_end = jiffies; + unset_audio_data_replay(); +#endif + __i2s_disable(); + if(turn_off_codec) + turn_off_codec(); + } + + if (controller->opened2 == 1) { + controller->opened2 = 0; + first_record_call = 1; + __i2s_enable_receive_dma(); + __i2s_enable_record(); + drain_adc(controller, file->f_flags & O_NONBLOCK); + disable_dma(controller->dma2); + set_dma_count(controller->dma2, 0); + __i2s_disable_receive_dma(); + __i2s_disable_record(); + + if(clear_codec_record) + clear_codec_record(); + + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes = 0; + jz_audio_dma_tran_count = 0; + controller->count = 0; + controller->finish = 0; + controller->blocks = 0; + controller->nextIn = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + __i2s_disable(); + if(turn_off_codec) + turn_off_codec(); + abnormal_data_count = 0; + } + +#if defined(CONFIG_I2S_DLV) + write_codec_file(9, 0xff); + write_codec_file(8, 0x3f); +#endif + return 0; +} + +static int jz_audio_open(struct inode *inode, struct file *file) +{ + int i; + struct jz_i2s_controller_info *controller = i2s_controller; + + if (controller == NULL) + return -ENODEV; + + mdelay(2); + REG_DMAC_DMACKE(0) = 0x3f; + pop_dma_flag = 0; + if (controller->opened1 == 1 || controller->opened2 == 1 ) { + printk("\naudio is busy!\n"); + return -EBUSY; + } + jz_codec_config = 0; + + ramp_up_start = 0; + ramp_up_end = 0; + gain_up_start = 0; + gain_up_end = 0; + ramp_down_start = 0; + ramp_down_end = 0; + gain_down_start = 0; + gain_down_end = 0; + if (file->f_mode & FMODE_WRITE) { + if (controller->opened1 == 1) + return -EBUSY; + + controller->opened1 = 1; + /* for ioctl */ + controller->total_bytes = 0; + jz_audio_dma_tran_count = 0; + controller->count = 0; + controller->finish = 0; + controller->blocks = 0; + controller->nextOut = 0; + + for(i=0;i < 64;i++) { + save_last_samples[i].left = 0; + save_last_samples[i].right = 0; + } + + out_empty_queue.count = jz_audio_fragstotal; + for (i=0;i < jz_audio_fragstotal;i++) + *(out_empty_queue.id + i) = i; + out_busy_queue.count = 0; + out_full_queue.count = 0; + last_dma_buffer_id = 0; + } + + if (file->f_mode & FMODE_READ) { + if (controller->opened2 == 1) + return -EBUSY; + + controller->opened2 = 1; + first_record_call = 1; + /* for ioctl */ + controller->total_bytes = 0; + jz_audio_dma_tran_count = 0; + controller->count = 0; + controller->finish = 0; + controller->blocks = 0; + controller->nextIn = 0; + + in_empty_queue.count = jz_audio_fragstotal; + for (i=0;i < jz_audio_fragstotal;i++) + *(in_empty_queue.id + i) = i; + + in_full_queue.count = 0; + in_busy_queue.count = 0; + } + + file->private_data = controller; + jz_audio_reset(); + REG_AIC_FR |= (1 << 6); + + if (file->f_mode & FMODE_WRITE) { + if(set_codec_replay) + set_codec_replay(); + } + + if (file->f_mode & FMODE_READ) { + abnormal_data_count = 0; + if(set_codec_record) + set_codec_record(); + } + +#if defined(CONFIG_I2S_DLV) + __aic_reset(); + + mdelay(10); + REG_AIC_I2SCR = 0x10; + mdelay(20); + __aic_flush_fifo(); +#endif + + __i2s_enable(); + +#if defined(CONFIG_I2S_DLV) + if (file->f_mode & FMODE_WRITE) { + + write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 + ramp_up_start = jiffies; + /*while (!(REG_RTC_RCR & RTC_RCR_WRDY)); + REG_RTC_RCR = 0x1; + while (!(REG_RTC_RCR & RTC_RCR_WRDY)); + REG_RTC_RGR = 1;*/ + sleep_on(&pop_wait_queue); + //ramp_up_end = jiffies; + write_codec_file_bit(5, 1, 4);//SB_ADC->1 + } else if (file->f_mode & FMODE_READ) { + if (jz_mic_only) + write_codec_file_bit(5, 1, 7);//SB_DAC->1 + else + write_codec_file_bit(5, 0, 7);//SB_DAC->0 + mdelay(500); + } + +#endif + + return 0; +} + + +static int jz_audio_ioctl(struct inode *inode, struct file *file, +unsigned int cmd, unsigned long arg) +{ + int val,fullc,busyc,unfinish,newfragstotal,newfragsize; + unsigned int flags; + struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; + count_info cinfo; + audio_buf_info abinfo; + int id, i; + + val = 0; + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + case SNDCTL_DSP_RESET: +#if 0 + jz_audio_reset(); + __i2s_disable_replay(); + __i2s_disable_receive_dma(); + __i2s_disable_record(); + __i2s_disable_transmit_dma(); +#endif + return 0; + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(controller, file->f_flags & O_NONBLOCK); + return 0; + case SNDCTL_DSP_SPEED: + /* set smaple rate */ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) + jz_audio_set_speed(controller->dev_audio, val); + + return put_user(val, (int *)arg); + case SNDCTL_DSP_STEREO: + /* set stereo or mono channel */ + if (get_user(val, (int *)arg)) + return -EFAULT; + jz_audio_set_channels(controller->dev_audio, val ? 2 : 1); + + return 0; + case SNDCTL_DSP_GETBLKSIZE: + //return put_user(jz_audio_fragsize / jz_audio_b, (int *)arg); + return put_user(jz_audio_fragsize, (int *)arg); + case SNDCTL_DSP_GETFMTS: + /* Returns a mask of supported sample format*/ + return put_user(AFMT_U8 | AFMT_S16_LE, (int *)arg); + case SNDCTL_DSP_SETFMT: + /* Select sample format */ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != AFMT_QUERY) + jz_audio_set_format(controller->dev_audio,val); + else { + if (file->f_mode & FMODE_READ) + val = (jz_audio_format == 16) ? AFMT_S16_LE : AFMT_U8; + else + val = (jz_audio_format == 16) ? AFMT_S16_LE : AFMT_U8; + } + + return put_user(val, (int *)arg); + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + jz_audio_set_channels(controller->dev_audio, val); + + return put_user(val, (int *)arg); + case SNDCTL_DSP_POST: + /* FIXME: the same as RESET ?? */ + return 0; + case SNDCTL_DSP_SUBDIVIDE: + return 0; + case SNDCTL_DSP_SETFRAGMENT: + get_user(val, (long *) arg); + newfragsize = 1 << (val & 0xFFFF); + if (newfragsize < 4 * PAGE_SIZE) + newfragsize = 4 * PAGE_SIZE; + if (newfragsize > (16 * PAGE_SIZE)) + newfragsize = 16 * PAGE_SIZE; + + newfragstotal = (val >> 16) & 0x7FFF; + if (newfragstotal < 2) + newfragstotal = 2; + if (newfragstotal > 32) + newfragstotal = 32; + if((jz_audio_fragstotal == newfragstotal) && (jz_audio_fragsize == newfragsize)) + return 0; + Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); + mdelay(500); + jz_audio_fragstotal = newfragstotal; + jz_audio_fragsize = newfragsize; + + Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); + mdelay(10); + + return 0; + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_REALTIME|DSP_CAP_BATCH, (int *)arg); + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + case SNDCTL_DSP_SETDUPLEX: + return -EINVAL; + case SNDCTL_DSP_GETOSPACE: + { + int i, bytes = 0; + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + spin_lock_irqsave(&controller->ioctllock, flags); + jz_audio_fragments = elements_in_queue(&out_empty_queue); + for (i = 0; i < jz_audio_fragments; i++) + bytes += jz_audio_fragsize; + + if (jz_audio_channels == 2) + bytes /= jz_audio_b; + else if (jz_audio_channels == 1) + bytes /= 4; + else + printk("SNDCTL_DSP_GETOSPACE : channels is wrong 1!\n"); + + + spin_unlock_irqrestore(&controller->ioctllock, flags); + /* unused fragment amount */ + abinfo.fragments = jz_audio_fragments; + /* amount of fragments */ + abinfo.fragstotal = jz_audio_fragstotal; + /* fragment size in bytes */ + if (jz_audio_channels == 2) + abinfo.fragsize = jz_audio_fragsize / jz_audio_b; + else if (jz_audio_channels == 1) + abinfo.fragsize = jz_audio_fragsize / 4; + else + printk("SNDCTL_DSP_GETOSPACE : channels is wrong 2!\n"); + + /* write size count without blocking in bytes */ + abinfo.bytes = bytes; + + return copy_to_user((void *)arg, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + } + case SNDCTL_DSP_GETISPACE: + { + int i, bytes = 0; + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + jz_audio_fragments = elements_in_queue(&in_empty_queue); + for (i = 0; i < jz_audio_fragments; i++) + bytes += jz_audio_fragsize; + + if (jz_audio_channels == 2) + bytes /= jz_audio_b; + else if (jz_audio_channels == 1) + bytes /= 4; + else + printk("SNDCTL_DSP_GETISPACE : channels is wrong 1!\n"); + + abinfo.fragments = jz_audio_fragments; + abinfo.fragstotal = jz_audio_fragstotal; + + if (jz_audio_channels == 2) + abinfo.fragsize = jz_audio_fragsize / jz_audio_b; + else if (jz_audio_channels == 1) + abinfo.fragsize = jz_audio_fragsize / 4; + else + printk("SNDCTL_DSP_GETISPACE : channels is wrong 2!\n"); + + abinfo.bytes = bytes; + + return copy_to_user((void *)arg, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + } + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && in_dma_buf) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && out_dma_buf) + val |= PCM_ENABLE_OUTPUT; + + return put_user(val, (int *)arg); + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + return 0; + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + + spin_lock_irqsave(&controller->ioctllock, flags); + cinfo.bytes = controller->total_bytes; + cinfo.blocks = controller->blocks; + cinfo.ptr = controller->nextIn; + controller->blocks = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + spin_lock_irqsave(&controller->ioctllock, flags); + cinfo.bytes = controller->total_bytes; + cinfo.blocks = controller->blocks; + cinfo.ptr = controller->nextOut; + controller->blocks = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + + return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + spin_lock_irqsave(&controller->ioctllock, flags); + unfinish = 0; + fullc = elements_in_queue(&out_full_queue); + busyc = elements_in_queue(&out_busy_queue); + for(i = 0;i < fullc ;i ++) { + id = *(out_full_queue.id + i); + unfinish += *(out_dma_buf_data_count + id); + } + for(i = 0;i < busyc ;i ++) { + id = *(out_busy_queue.id + i); + unfinish += get_dma_residue(controller->dma1); + } + spin_unlock_irqrestore(&controller->ioctllock, flags); + + if (jz_audio_channels == 2) + unfinish /= jz_audio_b; + else if (jz_audio_channels == 1) + unfinish /= 4; + else + printk("SNDCTL_DSP_GETODELAY : channels is wrong !\n"); + + return put_user(unfinish, (int *) arg); + case SOUND_PCM_READ_RATE: + return put_user(jz_audio_rate, (int *)arg); + case SOUND_PCM_READ_CHANNELS: + return put_user(jz_audio_channels, (int *)arg); + case SOUND_PCM_READ_BITS: + return put_user((jz_audio_format & (AFMT_S8 | AFMT_U8)) ? 8 : 16, (int *)arg); + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + return -EINVAL; +} + + +static unsigned int jz_audio_poll(struct file *file,struct poll_table_struct *wait) +{ + struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; + unsigned long flags; + unsigned int mask = 0; + + if (file->f_mode & FMODE_WRITE) { + if (elements_in_queue(&out_empty_queue) > 0) + return POLLOUT | POLLWRNORM; + + poll_wait(file, &controller->dac_wait, wait); + } + + if (file->f_mode & FMODE_READ) { + if (elements_in_queue(&in_full_queue) > 0) + return POLLIN | POLLRDNORM; + + poll_wait(file, &controller->adc_wait, wait); + } + + spin_lock_irqsave(&controller->lock, flags); + if (file->f_mode & FMODE_WRITE) { + if (elements_in_queue(&out_empty_queue) > 0) + mask |= POLLOUT | POLLWRNORM; + } else if (file->f_mode & FMODE_READ) { + if (elements_in_queue(&in_full_queue) > 0) + mask |= POLLIN | POLLRDNORM; + } + spin_unlock_irqrestore(&controller->lock, flags); + + return mask; +} + +static ssize_t jz_audio_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; + int id, ret = 0, left_count, copy_count, cnt = 0; + unsigned long flags; + + if (count < 0) + return -EINVAL; + + __i2s_enable_receive_dma(); + __i2s_enable_record(); + + spin_lock_irqsave(&controller->ioctllock, flags); + controller->nextIn = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + + copy_count = jz_audio_fragsize / 4; + + left_count = count; + if (first_record_call) { + first_record_call = 0; + audio_read_back_first: + if ((id = get_buffer_id(&in_empty_queue)) >= 0) { + put_buffer_id(&in_busy_queue, id); + spin_lock(&controller->lock); + *(in_dma_buf_data_count + id) = copy_count * 4; + + spin_unlock(&controller->lock); + __i2s_enable_receive_dma(); + __i2s_enable_record(); + dma_cache_wback_inv(*(in_dma_buf + id), *(in_dma_buf_data_count + id)); + audio_start_dma(controller->dma2,file->private_data, + *(in_dma_pbuf + id), + *(in_dma_buf_data_count + id), + DMA_MODE_READ); + sleep_on(&rx_wait_queue); + } else + goto audio_read_back_first; + } + + while (left_count > 0) { + audio_read_back_second: + if (elements_in_queue(&in_full_queue) <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + else + sleep_on(&rx_wait_queue); + } + + if ((id = get_buffer_id(&in_full_queue)) >= 0) { + spin_lock(&controller->lock); + cnt = record_filler((unsigned long)controller->tmp2+ret, copy_count, id); + spin_unlock(&controller->lock); + put_buffer_id(&in_empty_queue, id); + } else + goto audio_read_back_second; + + if (elements_in_queue(&in_busy_queue) == 0) { + if ((id=get_buffer_id(&in_empty_queue)) >= 0) { + put_buffer_id(&in_busy_queue, id); + spin_lock(&controller->lock); + *(in_dma_buf_data_count + id) = copy_count * 4; + spin_unlock(&controller->lock); + + dma_cache_wback_inv(*(in_dma_buf + id), *(in_dma_buf_data_count + id)); + audio_start_dma(controller->dma2,file->private_data, + *(in_dma_pbuf + id), + *(in_dma_buf_data_count + id), + DMA_MODE_READ); + } + } + if (ret + cnt > count) { + spin_lock(&controller->lock); + cnt = count - ret; + spin_unlock(&controller->lock); + } + if (copy_to_user(buffer+ret, controller->tmp2+ret, cnt)) + return ret ? ret : -EFAULT; + + spin_lock(&controller->lock); + ret += cnt; + spin_unlock(&controller->lock); + + spin_lock_irqsave(&controller->ioctllock, flags); + controller->nextIn += ret; + spin_unlock_irqrestore(&controller->ioctllock, flags); + + spin_lock(&controller->lock); + left_count -= cnt; + spin_unlock(&controller->lock); + } + return ret; +} + +static ssize_t jz_audio_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + int id, ret = 0, left_count, copy_count = 0; + unsigned int flags; + struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; + + if (count <= 0) + return -EINVAL; + + if(set_replay_hp_or_speaker) + set_replay_hp_or_speaker(); + + __i2s_enable_transmit_dma(); + __i2s_enable_replay(); + + spin_lock_irqsave(&controller->ioctllock, flags); + controller->nextOut = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + if (jz_audio_channels == 2) + copy_count = jz_audio_fragsize / jz_audio_b; + else if(jz_audio_channels == 1) + copy_count = jz_audio_fragsize / 4; + left_count = count; + if (copy_from_user(controller->tmp1, buffer, count)) { + printk("copy_from_user failed:%d",ret); + return ret ? ret : -EFAULT; + } + + while (left_count > 0) { + audio_write_back: + /*if (file->f_flags & O_NONBLOCK) + udelay(2);*/ + if (elements_in_queue(&out_empty_queue) == 0) { + if (file->f_flags & O_NONBLOCK) + return ret; + else + sleep_on(&tx_wait_queue); + } + /* the end fragment size in this write */ + if (ret + copy_count > count) + copy_count = count - ret; + if ((id = get_buffer_id(&out_empty_queue)) >= 0) { + replay_filler((signed long)controller->tmp1 + ret, copy_count, id); + if(*(out_dma_buf_data_count + id) > 0) { + put_buffer_id(&out_full_queue, id); + dma_cache_wback_inv(*(out_dma_buf + id), + *(out_dma_buf_data_count + id)); + } else + put_buffer_id(&out_empty_queue, id); + } else + goto audio_write_back; + + left_count = left_count - copy_count; + ret += copy_count; + + spin_lock_irqsave(&controller->ioctllock, flags); + controller->nextOut += ret; + spin_unlock_irqrestore(&controller->ioctllock, flags); + + if (elements_in_queue(&out_busy_queue) == 0) { + if ((id=get_buffer_id(&out_full_queue)) >= 0) { + put_buffer_id(&out_busy_queue, id); + + if(*(out_dma_buf_data_count + id) > 0) { + audio_start_dma(controller->dma1, + file->private_data, + *(out_dma_pbuf + id), + *(out_dma_buf_data_count + id), + DMA_MODE_WRITE); + last_dma_buffer_id = id; + if (jz_codec_config == 0) { + write_codec_file_bit(1, 0, 5); + gain_up_start = jiffies; + sleep_on(&pop_wait_queue); + //gain_up_end = jiffies; + jz_codec_config = 1; + //SB_ADC->1 + //write_codec_file_bit(5, 1, 4); + //while(1); + } + } + } + } + } + + return ret; +} + +#if defined(CONFIG_I2S_ICODEC) +static void write_mute_to_dma_buffer(signed long l_sample, signed long r_sample) +{ + int i,step_len; + unsigned long *pop_buf = (unsigned long*)pop_turn_onoff_buf; + unsigned int sample_oss = (REG_AIC_CR & 0x00380000) >> 19; + unsigned long l_sample_count,r_sample_count,sample_count; + struct jz_i2s_controller_info *controller = i2s_controller; + signed int left_sam=0,right_sam=0,l_val,r_val; + + switch (sample_oss) { + case 0x0: + break; + case 0x1: + left_sam = (signed int)l_sample; + right_sam = (signed int)r_sample; + break; + case 0x2: + break; + case 0x3: + break; + case 0x4: + break; + } + + if(left_sam == 0 && right_sam == 0) + return; + + switch (sample_oss) { + case 0x0: + break; + case 0x1: + step_len = jz_audio_speed / 10 * 3; + step_len = step_len / 2; + step_len = 0x7fff / step_len + 1; + + l_sample_count = 0; + l_val = left_sam; + + while(1) { + if(l_val > 0) { + if(l_val >= step_len) { + l_val -= step_len; + l_sample_count ++; + } else + break; + } + + if(l_val < 0) { + if(l_val <= -step_len) { + l_val += step_len; + l_sample_count ++; + } else + break; + } + + if(l_val == 0) + break; + } + + r_sample_count = 0; + r_val = right_sam; + while(1) { + if(r_val > 0) { + if(r_val >= step_len) { + r_val -= step_len; + r_sample_count ++; + } else + break; + } + + if(r_val < 0) { + if(r_val <= -step_len) { + r_val += step_len; + r_sample_count ++; + } else + break; + } + + if(r_val == 0) + break; + } + /* fill up */ + if(l_sample_count > r_sample_count) + sample_count = l_sample_count; + else + sample_count = r_sample_count; + + l_val = left_sam; + r_val = right_sam; + for(i=0;i <= sample_count;i++) { + + *pop_buf = (unsigned long)l_val; + pop_buf ++; + + if(l_val > step_len) + l_val -= step_len; + else if(l_val < -step_len) + l_val += step_len; + else if(l_val >= -step_len && l_val <= step_len) + l_val = 0; + + *pop_buf = (unsigned long)r_val; + pop_buf ++; + if(r_val > step_len) + r_val -= step_len; + else if(r_val < -step_len) + r_val += step_len; + else if(r_val >= -step_len && r_val <= step_len) + r_val = 0; + } + + *pop_buf = 0; + pop_buf ++; + *pop_buf = 0; + + pop_buf ++; + sample_count += 2; + dma_cache_wback_inv(pop_turn_onoff_buf, sample_count*8); + + pop_dma_flag = 1; + audio_start_dma(controller->dma1,controller,pop_turn_onoff_pbuf,sample_count*8,DMA_MODE_WRITE); + sleep_on(&pop_wait_queue); + pop_dma_flag = 0; + break; + case 0x2: + break; + case 0x3: + break; + case 0x4: + break; + } +} +#endif diff --git a/sound/oss/jz_i2s_dlv_dma_test.c b/sound/oss/jz_i2s_dlv_dma_test.c new file mode 100644 index 0000000..49a64a2 --- /dev/null +++ b/sound/oss/jz_i2s_dlv_dma_test.c @@ -0,0 +1,2808 @@ +/* + * linux/drivers/sound/jz_i2s_dlv.c + * + * JzSOC On-Chip I2S audio driver. + * + * Copyright (C) 2005 by Junzheng Corp. + * Modified by cjfeng on Aug 9,2007,and not any bug on Jz4730 using + * dma channel 4&3,noah is tested. + * + * 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. + * + * Because the normal application of AUDIO devices are focused on Little_endian, + * then we only perform the little endian data format in driver. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sound_config.h" + +#define DPRINTK(args...) printk(args) +#define DMA_ID_I2S_TX DMA_ID_AIC_TX +#define DMA_ID_I2S_RX DMA_ID_AIC_RX + +#define NR_I2S 2 +#define MAXDELAY 50000 +#define JZCODEC_RW_BUFFER_SIZE 3 +#define JZCODEC_RW_BUFFER_TOTAL 3 +#define JZCODEC_USER_BUFFER 6 + +#define USE_NONE 1 +#define USE_MIC 2 +#define USE_LINEIN 3 + +static int jz_audio_rate; +static char jz_audio_format; +static char jz_audio_volume; +static char jz_audio_channels; +static int jz_audio_b; /* bits expand multiple */ +static int jz_audio_fragments;//unused fragment amount +static int jz_audio_fragstotal; +static int jz_audio_fragsize; +static int jz_audio_speed; +static int audio_mix_modcnt; +static int codec_volue_shift; +static int jz_audio_dma_tran_count;//bytes count of one DMA transfer +static int jz_mic_only = 1; +static int jz_codec_config = 0; +static int use_mic_line_flag; +static unsigned long ramp_up_start; +static unsigned long ramp_up_end; +static unsigned long gain_up_start; +static unsigned long gain_up_end; +static unsigned long ramp_down_start; +static unsigned long ramp_down_end; +static unsigned long gain_down_start; +static unsigned long gain_down_end; + + +static void jz_update_filler(int bits, int channels); +static int Init_In_Out_queue(int fragstotal,int fragsize); +static int Free_In_Out_queue(int fragstotal,int fragsize); +static irqreturn_t jz_i2s_replay_dma_irq(int irqnr, void *ref); +static irqreturn_t jz_i2s_record_dma_irq(int irqnr, void *ref); +static void (*replay_filler)(signed long src_start, int count, int id); +static int (*record_filler)(unsigned long dst_start, int count, int id); +static void jz_audio_reset(void); + +static struct file_operations jz_i2s_audio_fops; + +static DECLARE_WAIT_QUEUE_HEAD (rx_wait_queue); +static DECLARE_WAIT_QUEUE_HEAD (tx_wait_queue); +static DECLARE_WAIT_QUEUE_HEAD (pop_wait_queue); + +struct jz_i2s_controller_info +{ + int io_base; + int dma1; /* play */ + int dma2; /* record */ + char *name; + int dev_audio; + struct i2s_codec *i2s_codec[NR_I2S]; + int opened1; + int opened2; + unsigned char *tmp1; /* tmp buffer for sample conversions */ + unsigned char *tmp2; + spinlock_t lock; + spinlock_t ioctllock; + + wait_queue_head_t dac_wait; + wait_queue_head_t adc_wait; + int nextIn; // byte index to next-in to DMA buffer + int nextOut; // byte index to next-out from DMA buffer + int count; // current byte count in DMA buffer + int finish; // current transfered byte count in DMA buffer + unsigned total_bytes; // total bytes written or read + unsigned blocks; + unsigned error; // over/underrun +#ifdef CONFIG_PM + struct pm_dev *pm; +#endif +}; + + +static struct jz_i2s_controller_info *i2s_controller = NULL; +struct i2s_codec +{ + /* I2S controller connected with */ + void *private_data; + char *name; + int id; + int dev_mixer; + /* controller specific lower leverl i2s accessing routines */ + u16 (*codec_read) (u8 reg);//the function accessing Codec REGs fcj add + void (*codec_write) (u8 reg, u16 val);//to AK4642EN,val is 8bit + /* Wait for codec-ready. Ok to sleep here. */ + void (*codec_wait) (struct i2s_codec *codec); + /* OSS mixer masks */ + int modcnt; + int supported_mixers; + int stereo_mixers; + int record_sources; + int bit_resolution; + /* OSS mixer interface */ + int (*read_mixer) (struct i2s_codec *codec, int oss_channel); + void (*write_mixer)(struct i2s_codec *codec, int oss_channel, + unsigned int left, unsigned int right); + int (*recmask_io) (struct i2s_codec *codec, int rw, int mask); + int (*mixer_ioctl)(struct i2s_codec *codec, unsigned int cmd, unsigned long arg); + /* saved OSS mixer states */ + unsigned int mixer_state[SOUND_MIXER_NRDEVICES]; +}; + + +typedef struct buffer_queue_s +{ + int count; + int *id; + int lock; +} buffer_queue_t; + +typedef struct left_right_sample_s +{ + signed long left; + signed long right; +} left_right_sample_t; + +typedef struct hpvol_shift_s +{ + int hpvol; + int shift; +} hpvol_shift_t; + +static unsigned long *out_dma_buf = NULL; +static unsigned long *out_dma_pbuf = NULL; +static unsigned long *out_dma_buf_data_count = NULL; +static unsigned long *in_dma_buf = NULL; +static unsigned long *in_dma_pbuf = NULL; +static unsigned long *in_dma_buf_data_count = NULL; + +static buffer_queue_t out_empty_queue; +static buffer_queue_t out_full_queue; +static buffer_queue_t out_busy_queue; +static buffer_queue_t in_empty_queue; +static buffer_queue_t in_full_queue; +static buffer_queue_t in_busy_queue; +static int first_record_call = 0; + +static 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; + + printk("\n"); + + 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); + + for (cnt = 0; cnt <= 27 ; cnt++) { + printk(" ( %d : 0x%x ) ",cnt ,read_codec_file(cnt)); + } + printk("\n"); +} + +static 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); +} + +static 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 */ + + while (__icdc_rgwr_ready()); + __icdc_set_addr(addr); + val &= ~(1 << mask_bit); + if (bitval == 1) + val |= 1 << mask_bit; + + __icdc_set_cmd(val); /* write */ + mdelay(1); + __icdc_set_rgwr(); + mdelay(1); + + while (__icdc_rgwr_ready()); + __icdc_set_addr(addr); + val = __icdc_get_value(); /* read */ + + if (((val >> mask_bit) & bitval) == bitval) + return 1; + else + return 0; +} + +/* set Audio data replay */ +static void set_audio_data_replay() +{ + /* DAC path */ + write_codec_file(9, 0xff); + //write_codec_file(8, 0x30); + write_codec_file(8, 0x20); + mdelay(10); + 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(1, 1, 7);//CR1.SB_MICBIAS->1 + //mdelay(300); +} + +/* unset Audio data replay */ +static void unset_audio_data_replay(void) +{ + //write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + //mdelay(800); + //write_codec_file_bit(5, 1, 6);//SB_OUT->1 + //mdelay(800); + 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 +} + +/* set Record MIC input audio without playback */ +static void set_record_mic_input_audio_without_playback(void) +{ + /* ADC path for MIC IN */ + jz_mic_only = 1; + write_codec_file(9, 0xff); + write_codec_file(8, 0x3f); + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 + mdelay(10); + write_codec_file_bit(1, 1, 2); + //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 + + write_codec_file(22, 0x40);//mic 1 + 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 */ + jz_mic_only = 0; + 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 + write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 + write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} + +/* set Record LINE input audio without playback */ +static void set_record_line_input_audio_without_playback(void) +{ + /* ADC path for LINE IN */ + jz_mic_only = 1; + write_codec_file(9, 0xff); + write_codec_file(8, 0x3f); + mdelay(10); + write_codec_file(22, 0xf6);//line in 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, 0, 3);//PMR1.SB_LIN->0 + 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 + mdelay(10); + 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 LINE input audio without playback */ +static void unset_record_line_input_audio_without_playback(void) +{ + /* ADC path for LINE IN */ + write_codec_file_bit(5, 1, 4);//SB_ADC->1 + write_codec_file_bit(5, 1, 3);//ONR1.SB_LIN->1 + + write_codec_file(22, 0xc0);//CR3.SB_MIC1 + write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 + write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} + +/* set Playback LINE input audio direct only */ +static void set_playback_line_input_audio_direct_only() +{ + jz_audio_reset();//or init_codec() + REG_AIC_I2SCR = 0x10; + write_codec_file(9, 0xff); + write_codec_file(8, 0x3f); + mdelay(10); + write_codec_file(22, 0xf6);//line in 1 + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 + mdelay(10); + write_codec_file_bit(1, 1, 2);//CR1.HP_BYPASS->1 + write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 + write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0 + write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 + write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0 + + 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, 1, 7);//PMR1.SB_DAC->1 + //write_codec_file_bit(5, 1, 4);//PMR1.SB_ADC->1 +} + +/* unset Playback LINE input audio direct only */ +static void unset_playback_line_input_audio_direct_only() +{ + write_codec_file_bit(6, 0, 3);//GIM->0 + write_codec_file_bit(1, 0, 2);//PMR1.BYPASS->0 + write_codec_file_bit(5, 1, 3);//PMR1.SB_LINE->1 + write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 + mdelay(100); + write_codec_file_bit(5, 1, 5);//PMR1.SB_MIX->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} + +/* set Record MIC input audio with direct playback */ +static void set_record_mic_input_audio_with_direct_playback(void) +{ + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 + jz_mic_only = 0; + write_codec_file(9, 0xff); + write_codec_file(8, 0x3f); + mdelay(10); + + write_codec_file(22, 0x60);//mic 1 + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->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, 7);//CR1.SB_MICBIAS->0 + write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 + + 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 with direct playback */ +static void unset_record_mic_input_audio_with_direct_playback(void) +{ + /* ADC path for MIC IN */ + jz_mic_only = 0; + 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 + write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 + write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} + +/* set Record playing audio mixed with MIC input audio */ +static void set_record_playing_audio_mixed_with_mic_input_audio(void) +{ + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 + write_codec_file(9, 0xff); + //write_codec_file(8, 0x30); + write_codec_file(8, 0x20); + mdelay(10); + + write_codec_file(22, 0x63);//mic 1 + + write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 + write_codec_file_bit(6, 1, 3);// gain set + + 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, 7);//CR1.SB_MICBIAS->0 + write_codec_file_bit(22, 0, 7);//CR3.SB_MIC->0 + write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 + write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 + write_codec_file_bit(5, 0, 4);//PMR1.SB_MIX->0 +} + +/* unset Record playing audio mixed with MIC input audio */ +static void unset_record_playing_audio_mixed_with_mic_input_audio(void) +{ + /* ADC path */ + write_codec_file_bit(5, 1, 4);//SB_ADC->1 + write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 + //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 + write_codec_file(22, 0xc0);//CR3.SB_MIC1->1 + //write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + //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, 5);//SB_MIX->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} + +/* set Record MIC input audio with Audio data replay (full duplex) */ +static void set_record_mic_input_audio_with_audio_data_replay(void) +{ + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 + write_codec_file(9, 0xff); + //write_codec_file(8, 0x30); + write_codec_file(8, 0x20); + 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(22, 0, 7);//CR3.SB_MIC->0 + write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0 + + write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 + write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 +} + +/* unset Record MIC input audio with Audio data replay (full duplex) */ +static void unset_record_mic_input_audio_with_audio_data_replay(void) +{ + /* ADC path */ + write_codec_file_bit(5, 1, 4);//SB_ADC->1 + write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 + //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 + write_codec_file(22, 0xc0);//CR3.SB_MIC1->1 + write_codec_file_bit(5, 1, 7);//SB_DAC->1 + write_codec_file_bit(5, 1, 5);//SB_MIX->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} + +///////// +/* set Record LINE input audio with Audio data replay (full duplex for linein) */ +static void set_record_line_input_audio_with_audio_data_replay(void) +{ + write_codec_file(9, 0xff); + //write_codec_file(8, 0x30); + write_codec_file(8, 0x20); + write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 + write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0 + write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 + write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 + //write_codec_file_bit(22, 1, 7);//CR3.SB_MIC->1 + write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 + write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 + + + //jz_mic_only = 1; + write_codec_file(22, 0xc6);//line in 1 + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 + write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 + write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 +} + +/* unset Record LINE input audio with Audio data replay (full duplex for linein) */ +static void unset_record_line_input_audio_with_audio_data_replay(void) +{ + /* ADC path */ + write_codec_file_bit(5, 1, 4);//SB_ADC->1 + write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 + //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 + write_codec_file(22, 0xc0);//CR3.SB_MIC1->1 + write_codec_file_bit(5, 1, 7);//SB_DAC->1 + write_codec_file_bit(5, 1, 5);//SB_MIX->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} +///////// + +static inline int get_buffer_id(struct buffer_queue_s *q) +{ + int r; + unsigned long flags; + int i; + spin_lock_irqsave(&q->lock, flags); + //spin_lock(&q->lock); + if (q->count == 0) { + spin_unlock_irqrestore(&q->lock, flags); + //spin_unlock(&q->lock); + return -1; + } + r = *(q->id + 0); + for (i=0;i < q->count-1;i++) + *(q->id + i) = *(q->id + (i+1)); + q->count --; + spin_unlock_irqrestore(&q->lock, flags); + //spin_unlock(&q->lock); + return r; +} + +static inline void put_buffer_id(struct buffer_queue_s *q, int id) +{ + unsigned long flags; + spin_lock_irqsave(&q->lock, flags); + //spin_lock(&q->lock); + *(q->id + q->count) = id; + q->count ++; + spin_unlock_irqrestore(&q->lock, flags); + //spin_unlock(&q->lock); +} + +static inline int elements_in_queue(struct buffer_queue_s *q) +{ + int r; + unsigned long flags; + spin_lock_irqsave(&q->lock, flags); + //spin_lock(&q->lock); + r = q->count; + spin_unlock_irqrestore(&q->lock, flags); + //spin_unlock(&q->lock); + return r; +} + +static inline void audio_start_dma(int chan, void *dev_id, unsigned long phyaddr,int count, int mode) +{ + unsigned long flags; + struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; + //for DSP_GETOPTR + //spin_lock_irqsave(&controller->ioctllock, flags); + spin_lock(&controller->ioctllock); + jz_audio_dma_tran_count = count; + //spin_unlock_irqrestore(&controller->ioctllock, flags); + spin_unlock(&controller->ioctllock); + flags = claim_dma_lock(); + //__dmac_disable_module(0); + disable_dma(chan); + clear_dma_ff(chan); + + set_dma_mode(chan, mode); + set_dma_addr(chan, phyaddr); + if (count == 0) + { + count++; + printk("JzSOC DMA controller can't set dma 0 count!\n"); + } + + set_dma_count(chan, count); + enable_dma(chan); + //__dmac_enable_module(0); + release_dma_lock(flags); + //dump_jz_dma_channel(chan); + + //printk_codec_files(); +} + +static irqreturn_t jz_i2s_record_dma_irq (int irq, void *dev_id) +{ + int id1, id2; + + struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; + int dma = controller->dma2; + + disable_dma(dma); + if (__dmac_channel_address_error_detected(dma)) { + printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); + __dmac_channel_clear_address_error(dma); + } + if (__dmac_channel_transmit_end_detected(dma)) { + __dmac_channel_clear_transmit_end(dma); + //for DSP_GETIPTR + //spin_lock_irqsave(&controller->ioctllock, flags); + spin_lock(&controller->ioctllock); + controller->total_bytes += jz_audio_dma_tran_count; + controller->blocks ++; + //spin_unlock_irqrestore(&controller->ioctllock, flags); + spin_unlock(&controller->ioctllock); + id1 = get_buffer_id(&in_busy_queue); + put_buffer_id(&in_full_queue, id1); + + wake_up(&rx_wait_queue); + wake_up(&controller->adc_wait); + if ((id2 = get_buffer_id(&in_empty_queue)) >= 0) { + put_buffer_id(&in_busy_queue, id2); + *(in_dma_buf_data_count + id2) = *(in_dma_buf_data_count + id1); + //write back + dma_cache_wback_inv(*(in_dma_buf + id2), *(in_dma_buf_data_count + id2)); + audio_start_dma(dma,dev_id, + *(in_dma_pbuf + id2), + *(in_dma_buf_data_count + id2), + DMA_MODE_READ); + } + else + in_busy_queue.count = 0; + } + return IRQ_HANDLED; + +} + +static irqreturn_t jz_i2s_replay_dma_irq (int irq, void *dev_id) +{ + int id; + unsigned long flags; + struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; + int dma = controller->dma1; + disable_dma(dma); + + if (__dmac_channel_address_error_detected(dma)) { + printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); + __dmac_channel_clear_address_error(dma); + } + if (__dmac_channel_transmit_end_detected(dma)) { + __dmac_channel_clear_transmit_end(dma); + + + //for DSP_GETOPTR + spin_lock_irqsave(&controller->ioctllock, flags); + //spin_lock(&controller->ioctllock); + controller->total_bytes += jz_audio_dma_tran_count; + controller->blocks ++; + spin_unlock_irqrestore(&controller->ioctllock, flags); + //spin_unlock(&controller->ioctllock); + if ((id = get_buffer_id(&out_busy_queue)) < 0) + printk(KERN_DEBUG "Strange DMA finish interrupt for I2S module\n"); + put_buffer_id(&out_empty_queue, id); + if ((id = get_buffer_id(&out_full_queue)) >= 0) { + put_buffer_id(&out_busy_queue, id); //very busy + if(*(out_dma_buf_data_count + id) > 0) { + audio_start_dma(dma, dev_id, *(out_dma_pbuf + id), + *(out_dma_buf_data_count + id), + DMA_MODE_WRITE); + } + } else + out_busy_queue.count = 0; + + if (elements_in_queue(&out_empty_queue) > 0) { + wake_up(&tx_wait_queue); + wake_up(&controller->dac_wait); + } + } + return IRQ_HANDLED; +} + +static void jz_i2s_initHw(int set) +{ + __i2s_disable(); + //schedule_timeout(5); + +#if defined(CONFIG_I2S_DLV) + __i2s_disable(); + __i2s_as_slave(); + __aic_internal_codec(); + //__i2s_set_oss_sample_size(16); + //__i2s_set_iss_sample_size(16); + +#endif + __i2s_disable_record(); + __i2s_disable_replay(); + __i2s_disable_loopback(); + __i2s_set_transmit_trigger(7); + __i2s_set_receive_trigger(8); +} + +static int Init_In_Out_queue(int fragstotal,int fragsize) +{ + int i; + // recording + in_empty_queue.count = fragstotal; + in_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!in_dma_buf) + goto all_mem_err; + in_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!in_dma_pbuf) + goto all_mem_err; + in_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!in_dma_buf_data_count) + goto all_mem_err; + in_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!in_empty_queue.id) + goto all_mem_err; + in_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!in_full_queue.id) + goto all_mem_err; + in_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!in_busy_queue.id) + goto all_mem_err; + + for (i=0;i < fragstotal;i++) + *(in_empty_queue.id + i) = i; + in_full_queue.count = 0; + in_busy_queue.count = 0; + + for (i = 0; i < fragstotal; i++) { + *(in_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); + if (*(in_dma_buf + i) == 0) + goto mem_failed_in; + *(in_dma_pbuf + i) = virt_to_phys((void *)(*(in_dma_buf + i))); + dma_cache_wback_inv(*(in_dma_buf + i), fragsize); + } + + //playing + out_empty_queue.count = fragstotal; + out_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!out_dma_buf) + goto all_mem_err; + out_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!out_dma_pbuf) + goto all_mem_err; + out_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + + if (!out_dma_buf_data_count) + goto all_mem_err; + out_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!out_empty_queue.id) + goto all_mem_err; + out_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!out_full_queue.id) + goto all_mem_err; + out_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!out_busy_queue.id) + goto all_mem_err; + for (i=0;i < fragstotal;i++) + *(out_empty_queue.id + i) = i; + + out_busy_queue.count = 0; + out_full_queue.count = 0; + /*alloc DMA buffer*/ + for (i = 0; i < fragstotal; i++) { + *(out_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); + if (*(out_dma_buf + i) == 0) { + printk(" can't allocate required DMA(OUT) buffers.\n"); + goto mem_failed_out; + } + *(out_dma_pbuf + i) = virt_to_phys((void *)(*(out_dma_buf + i))); + } + + return 1; +all_mem_err: + printk("error:allocate memory occur error 1!\n"); + return 0; +mem_failed_out: + printk("error:allocate memory occur error 2!\n"); + for (i = 0; i < fragstotal; i++) { + if(*(out_dma_buf + i)) + free_pages(*(out_dma_buf + i), get_order(fragsize)); + } + + return 0; +mem_failed_in: + printk("error:allocate memory occur error 3!\n"); + for (i = 0; i < fragstotal; i++) { + if(*(in_dma_buf + i)) + free_pages(*(in_dma_buf + i), get_order(fragsize)); + } + return 0; +} + +static int Free_In_Out_queue(int fragstotal,int fragsize) +{ + int i; + //playing + if(out_dma_buf != NULL) { + for (i = 0; i < fragstotal; i++) { + if(*(out_dma_buf + i)) + free_pages(*(out_dma_buf + i), get_order(fragsize)); + *(out_dma_buf + i) = 0; //release page error + } + kfree(out_dma_buf); + out_dma_buf = NULL; + } + if(out_dma_pbuf) { + kfree(out_dma_pbuf); + out_dma_pbuf = NULL; + } + if(out_dma_buf_data_count) { + kfree(out_dma_buf_data_count); + out_dma_buf_data_count = NULL; + } + if(out_empty_queue.id) { + kfree(out_empty_queue.id); + out_empty_queue.id = NULL; + } + if(out_full_queue.id) { + kfree(out_full_queue.id); + out_full_queue.id = NULL; + } + if(out_busy_queue.id) { + kfree(out_busy_queue.id); + out_busy_queue.id = NULL; + } + out_empty_queue.count = fragstotal; + out_busy_queue.count = 0; + out_full_queue.count = 0; + + // recording + if(in_dma_buf) { + for (i = 0; i < fragstotal; i++) { + if(*(in_dma_buf + i)) { + dma_cache_wback_inv(*(in_dma_buf + i), fragsize); + free_pages(*(in_dma_buf + i), get_order(fragsize)); + } + *(in_dma_buf + i) = 0; //release page error + } + kfree(in_dma_buf); + in_dma_buf = NULL; + } + if(in_dma_pbuf) { + kfree(in_dma_pbuf); + in_dma_pbuf = NULL; + } + if(in_dma_buf_data_count) { + kfree(in_dma_buf_data_count); + in_dma_buf_data_count = NULL; + } + if(in_empty_queue.id) { + kfree(in_empty_queue.id); + in_empty_queue.id = NULL; + } + if(in_full_queue.id) { + kfree(in_full_queue.id); + in_full_queue.id = NULL; + } + if(in_busy_queue.id) { + kfree(in_busy_queue.id); + in_busy_queue.id = NULL; + } + + in_empty_queue.count = fragstotal; + in_full_queue.count = 0; + in_busy_queue.count = 0; + + return 1; +} + +static void jz_i2s_full_reset(struct jz_i2s_controller_info *controller) +{ + jz_i2s_initHw(0); +} + +static int jz_audio_set_speed(int dev, int rate) +{ + /* 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 99999999 ? */ + int speed = 0, val; + + jz_audio_speed = rate; + if (rate > 96000) + rate = 96000; + if (rate < 8000) + rate = 8000; + jz_audio_rate = rate; + +#if defined(CONFIG_I2S_DLV) + speed = 0; + switch (rate) { + 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: + break; + } + + val = read_codec_file(4); + val = (speed << 4) | speed; + write_codec_file(4, val); + +#if 0 + for (i = 0; i <= 27 ; i++) { + printk(" ( CCC %d : 0x%x ) ",i ,read_codec_file(i)); + } +#endif + +#endif + return jz_audio_rate; +} + +static int record_fill_1x8_u(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + unsigned long data; + volatile unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); + volatile unsigned char *dp = (unsigned char*)dst_start; + while (count > 0) { + count -= 2; /* count in dword */ + cnt++; + data = *(s++); + *(dp ++) = ((data << 16) >> 24) + 0x80; + s++; /* skip the other channel */ + } + return cnt; +} + +static int record_fill_2x8_u(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + unsigned long d1, d2; + volatile unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); + volatile unsigned char *dp = (unsigned char*)dst_start; + while (count > 0) { + count -= 2; + cnt += 2; + d1 = *(s++); + *(dp ++) = ((d1 << 16) >> 24) + 0x80; + d2 = *(s++); + *(dp ++) = ((d2 << 16) >> 24) + 0x80; + } + return cnt; +} + +static int record_fill_1x16_s(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + unsigned long d1; + unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); + unsigned short *dp = (unsigned short *)dst_start; + + while (count > 0) { + count -= 2; /* count in dword */ + cnt += 2; /* count in byte */ + d1 = *(s++); + *(dp ++) = (d1 << 16) >> 16; + s++; /* skip the other channel */ + } + return cnt; +} + + +static int record_fill_2x16_s(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + unsigned long d1, d2; + unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); + unsigned short *dp = (unsigned short *)dst_start; + + while (count > 0) { + count -= 2; /* count in dword */ + cnt += 4; /* count in byte */ + d1 = *(s++); + *(dp ++) = (d1 << 16) >> 16; + d2 = *(s++); + *(dp ++) = (d2 << 16) >> 16; + } + return cnt; +} + +static int record_fill_2x24_s(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + unsigned long d1, d2; + unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); + unsigned short *dp = (unsigned short *)dst_start; + + while (count > 0) { + count -= 4; /* count in dword */ + cnt += 4; /* count in byte */ + d1 = *(s++); + *(dp ++) = (d1 << 24) >> 24; + d2 = *(s++); + *(dp ++) = (d2 << 24) >> 24; + } + //printk("--- rec 24 ---\n"); + return cnt; +} + +static void replay_fill_1x8_u(unsigned long src_start, int count, int id) +{ + int cnt = 0; + unsigned char data; + unsigned long ddata; + volatile unsigned char *s = (unsigned char *)src_start; + volatile unsigned long *dp = (unsigned long*)(*(out_dma_buf + id)); + while (count > 0) { + count--; + cnt += 1; + data = *(s++) - 0x80; + ddata = (unsigned long) data << 8; + *(dp ++) = ddata; + *(dp ++) = ddata; + } + cnt = cnt * 2 * jz_audio_b; + *(out_dma_buf_data_count + id) = cnt; +} + +static void replay_fill_2x8_u(unsigned long src_start, int count, int id) +{ + int cnt = 0; + unsigned char d1; + unsigned long dd1; + volatile unsigned char *s = (unsigned char *)src_start; + volatile unsigned long *dp = (unsigned long*)(*(out_dma_buf + id)); + while (count > 0) { + count -= 1; + cnt += 1 ; + d1 = *(s++) - 0x80; + dd1 = (unsigned long) d1 << 8; + *(dp ++) = dd1; + } + cnt *= jz_audio_b; + *(out_dma_buf_data_count + id) = cnt; +} + +static void replay_fill_1x16_s(signed long src_start, int count, int id) +{ + int cnt = 0; + signed short d1; + signed long l1; + volatile signed short *s = (signed short *)src_start; + volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); + + while (count > 0) { + count -= 2; + cnt += 2 ; + d1 = *(s++); + l1 = (unsigned long)d1; + *(dp ++) = l1; + *(dp ++) = l1; + } + cnt = cnt * 2 * jz_audio_b; + *(out_dma_buf_data_count + id) = cnt; +} + +static void replay_fill_2x16_s(signed long src_start, int count, int id) +{ + int cnt = 0; + signed short d1; + signed long l1; + volatile signed short *s = (signed short *)src_start; + volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); + + while (count > 0) { + count -= 2; + cnt += 2; + d1 = *(s++); + + l1 = (signed long)d1; + + *(dp ++) = l1; + } + + cnt *= jz_audio_b; + *(out_dma_buf_data_count + id) = cnt; +} + +static unsigned int jz_audio_set_format(int dev, unsigned int fmt) +{ + switch (fmt) { + case AFMT_U8: + __i2s_set_oss_sample_size(8); + __i2s_set_iss_sample_size(8); + jz_audio_format = fmt; + jz_update_filler(jz_audio_format, jz_audio_channels); + break; + case AFMT_S16_LE: + /* DAC path and ADC path */ + write_codec_file(2, 0x00); + //write_codec_file(2, 0x60); + + jz_audio_format = fmt; + jz_update_filler(jz_audio_format, jz_audio_channels); + + /* print all files */ + __i2s_set_oss_sample_size(16); + __i2s_set_iss_sample_size(16); + break; + case 18: + /* DAC path and ADC path */ + write_codec_file(2, 0x28); + //write_codec_file(2, 0x60); + __i2s_set_oss_sample_size(18); + __i2s_set_iss_sample_size(16); + jz_audio_format = fmt; + jz_update_filler(jz_audio_format,jz_audio_channels); + break; + case 24: + /* DAC path and ADC path */ + write_codec_file(2, 0x78); + //write_codec_file(2, 0x60); + __i2s_set_oss_sample_size(24); + __i2s_set_iss_sample_size(24); + jz_audio_format = fmt; + jz_update_filler(jz_audio_format,jz_audio_channels); + break; + case AFMT_QUERY: + break; + } + return jz_audio_format; +} + +static short jz_audio_set_channels(int dev, short channels) +{ + switch (channels) { + case 1: + /* print all files */ + jz_audio_channels = channels; + jz_update_filler(jz_audio_format, jz_audio_channels); + write_codec_file_bit(1, 1, 6);//CR1.MONO->1 for Mono + break; + case 2: + /* print all files */ + jz_audio_channels = channels; + jz_update_filler(jz_audio_format, jz_audio_channels); + write_codec_file_bit(1, 0, 6);//CR1.MONO->0 for Stereo + break; + case 0: + break; + } + return jz_audio_channels; +} + +static void init_codec(void) +{ +#if defined(CONFIG_I2S_DLV) + /* 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 +#endif +} + +static void jz_audio_reset(void) +{ + __i2s_disable_replay(); + __i2s_disable_receive_dma(); + __i2s_disable_record(); + __i2s_disable_transmit_dma(); + REG_AIC_I2SCR = 0x10; + init_codec(); +} + +static int jz_audio_release(struct inode *inode, struct file *file); +static int jz_audio_open(struct inode *inode, struct file *file); +static int jz_audio_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg); +static unsigned int jz_audio_poll(struct file *file,struct poll_table_struct *wait); +static ssize_t jz_audio_write(struct file *file, const char *buffer,size_t count, loff_t *ppos); +static ssize_t jz_audio_read(struct file *file, char *buffer,size_t count, loff_t *ppos); +/* static struct file_operations jz_i2s_audio_fops */ +static struct file_operations jz_i2s_audio_fops = +{ + owner: THIS_MODULE, + open: jz_audio_open, + release: jz_audio_release, + write: jz_audio_write, + read: jz_audio_read, + poll: jz_audio_poll, + ioctl: jz_audio_ioctl +}; + +static int jz_i2s_open_mixdev(struct inode *inode, struct file *file) +{ + int i; + int minor = MINOR(inode->i_rdev); + struct jz_i2s_controller_info *controller = i2s_controller; + + for (i = 0; i < NR_I2S; i++) + if (controller->i2s_codec[i] != NULL && controller->i2s_codec[i]->dev_mixer == minor) + goto match; + + if (!controller) + return -ENODEV; +match: + file->private_data = controller->i2s_codec[i]; + return 0; +} + +static int jz_i2s_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct i2s_codec *codec = (struct i2s_codec *)file->private_data; + return codec->mixer_ioctl(codec, cmd, arg); +} + +static loff_t jz_i2s_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +static struct file_operations jz_i2s_mixer_fops = +{ + owner: THIS_MODULE, + llseek: jz_i2s_llseek, + ioctl: jz_i2s_ioctl_mixdev, + open: jz_i2s_open_mixdev, +}; + +static int i2s_mixer_ioctl(struct i2s_codec *codec, unsigned int cmd, unsigned long arg) +{ + int ret; + u8 cur_vol; + long val = 0; + + switch (cmd) { + case SOUND_MIXER_INFO: + { + mixer_info info; +#if defined(CONFIG_I2S_DLV) + strncpy(info.id, "DLV", sizeof(info.id)); + strncpy(info.name,"Jz4750 internal codec", sizeof(info.name)); +#endif + + info.modify_counter = audio_mix_modcnt; + return copy_to_user((void *)arg, &info, sizeof(info)); + } + case SOUND_OLD_MIXER_INFO: + { + _old_mixer_info info; +#if defined(CONFIG_I2S_DLV) + strncpy(info.id, "DLV", sizeof(info.id)); + strncpy(info.name,"Jz4750 internal codec", sizeof(info.name)); +#endif + + return copy_to_user((void *)arg, &info, sizeof(info)); + } + case SOUND_MIXER_READ_STEREODEVS: + return put_user(0, (long *) arg); + case SOUND_MIXER_READ_CAPS: + val = SOUND_CAP_EXCL_INPUT; + return put_user(val, (long *) arg); + case SOUND_MIXER_READ_DEVMASK: + break; + case SOUND_MIXER_READ_RECMASK: + break; + case SOUND_MIXER_READ_RECSRC: + break; + case SOUND_MIXER_WRITE_SPEAKER: + ret = get_user(val, (long *) arg); + if (ret) + return ret; + val = val & 0xff; + if(val < 0) + val = 0; + if(val > 100) + val = 100; + + REG_CPM_I2SCDR = val; + printk_codec_files(); + break; + case SOUND_MIXER_WRITE_BASS: + ret = get_user(val, (long *) arg); + if (ret) + return ret; + + val = val & 0xff; + if(val < 0) + val = 0; + if(val > 100) + val = 100; +#if defined(CONFIG_I2S_DLV) +#endif + return 0; + + case SOUND_MIXER_READ_BASS: +#if defined(CONFIG_I2S_DLV) +#endif + return put_user(val, (long *) arg); + + case SOUND_MIXER_WRITE_VOLUME: + ret = get_user(val, (long *) arg); + if (ret) + return ret; + val = val & 0xff; + if(val < 0) + val = 0; + if(val == 100) { + //REG_CPM_CLKGR |= 0x60; + printk("gate clock : 0x%08x\n", REG_CPM_CLKGR); + return 100; + } + if(val == 99) { + REG_CPM_CLKGR &= ~0x60; + return 99; + } + if(val == 98) { + REG_CPM_CPCCR |= 0x80400000; + return 98; + } + if(val == 97) { + REG_CPM_CPCCR &= ~0x8000000; + REG_CPM_CPCCR |= 0x0040000; + return 97; + } + if(val > 31) + val = 31; + + jz_audio_volume = val; + //printk("\njz_audio_volume=%d\n",jz_audio_volume); +#if defined(CONFIG_I2S_DLV) + cur_vol = val; + write_codec_file(17, cur_vol | 0xc0); + write_codec_file(18, cur_vol); +#endif + return 0; + case SOUND_MIXER_READ_VOLUME: +#if defined(CONFIG_I2S_DLV) + val = jz_audio_volume | jz_audio_volume << 8; +#endif + return put_user(val, (long *) arg); + case SOUND_MIXER_WRITE_LINE: + ret = get_user(val, (long *) arg); + if (ret) + return ret; + + val = val & 0xff; + if(val < 0) + val = 0; + if (val == 100) { + printk("Playback LINE input audio direct only is starting...\n"); + printk_codec_files(); + set_playback_line_input_audio_direct_only(); + printk_codec_files(); + return 100; + } + if (val == 99) { + printk("Playback LINE input audio direct only is end\n"); + unset_playback_line_input_audio_direct_only(); + printk_codec_files(); + return 99; + } + + if(val > 31) + val = 31; +#if defined(CONFIG_I2S_DLV) + use_mic_line_flag = USE_LINEIN; + /* set gain */ + cur_vol = val; + cur_vol &= 0x1f; + write_codec_file(11, cur_vol);//GO1L + write_codec_file(12, cur_vol);//GO1R +#endif + return 0; + break; + case SOUND_MIXER_WRITE_MIC: + ret = get_user(val, (long *) arg); + if (ret) + return ret; + + val = val & 0xff; + if(val < 0) + val = 0; + + if(val > 0xf) + val = 0xf; +#if defined(CONFIG_I2S_DLV) + use_mic_line_flag = USE_MIC; + /* set gain */ + //write_codec_file_bit(6, 1, 3);//GIM + cur_vol = val; + cur_vol |= cur_vol << 4; + write_codec_file(19, cur_vol);//GIL,GIR +#endif + return 0; + + case SOUND_MIXER_READ_MIC: +#if defined(CONFIG_I2S_DLV) +#endif + + return put_user(val, (long *) arg); + default: + return -ENOSYS; + } + audio_mix_modcnt ++; + + return 0; +} + +int i2s_probe_codec(struct i2s_codec *codec) +{ + /* generic OSS to I2S wrapper */ + codec->mixer_ioctl = i2s_mixer_ioctl; + return 1; +} + + +/* I2S codec initialisation. */ +static int __init jz_i2s_codec_init(struct jz_i2s_controller_info *controller) +{ + int num_i2s = 0; + struct i2s_codec *codec; + + for (num_i2s = 0; num_i2s < NR_I2S; num_i2s++) { + if ((codec = kmalloc(sizeof(struct i2s_codec),GFP_KERNEL)) == NULL) + return -ENOMEM; + memset(codec, 0, sizeof(struct i2s_codec)); + codec->private_data = controller; + codec->id = num_i2s; + + if (i2s_probe_codec(codec) == 0) + break; + if ((codec->dev_mixer = register_sound_mixer(&jz_i2s_mixer_fops, -1)) < 0) { + printk(KERN_ERR "Jz I2S: couldn't register mixer!\n"); + kfree(codec); + break; + } + controller->i2s_codec[num_i2s] = codec; + } + return num_i2s; +} + +static void replay_fill_2x18_s(unsigned long src_start, int count, int id) +{ + int cnt = 0; + signed long d1; + signed long l1; + volatile signed long *s = (signed long *)src_start; + volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); + while (count > 0) { + count -= 4; + cnt += 4; + d1 = *(s++); + l1 = (signed long)d1; + *(dp ++) = l1; + } + + cnt *= jz_audio_b; + *(out_dma_buf_data_count + id) = cnt; +} + +static void replay_fill_2x24_s(unsigned long src_start, int count, int id) +{ + int cnt = 0; + signed long d1; + signed long l1; + volatile signed long *s = (signed long *)src_start; + volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); + while (count > 0) { + count -= 4; + cnt += 4; + d1 = *(s++); + l1 = (signed long)d1; + *(dp ++) = l1; + } + + cnt *= jz_audio_b; + *(out_dma_buf_data_count + id) = cnt; + //printk("--- play 24 ---\n"); +} + +static void jz_update_filler(int format, int channels) +{ + #define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3)) + + switch (TYPE(format, channels)) + { + default: + case TYPE(AFMT_U8, 1): + jz_audio_b = 4;//4bytes * 8bits =32bits + replay_filler = replay_fill_1x8_u; + record_filler = record_fill_1x8_u; + break; + case TYPE(AFMT_U8, 2): + jz_audio_b = 4; + replay_filler = replay_fill_2x8_u; + record_filler = record_fill_2x8_u; + break; + case TYPE(AFMT_S16_LE, 1): + //2bytes * 16bits =32bits + jz_audio_b = 2; + replay_filler = replay_fill_1x16_s; + record_filler = record_fill_1x16_s; + break; + case TYPE(AFMT_S16_LE, 2): + jz_audio_b = 2; + replay_filler = replay_fill_2x16_s; + record_filler = record_fill_2x16_s; + break; + case TYPE(18, 2): + jz_audio_b = 1; + replay_filler = replay_fill_2x18_s; + record_filler = record_fill_2x16_s; + break; + case TYPE(24, 2): + jz_audio_b = 1; + replay_filler = replay_fill_2x24_s; + record_filler = record_fill_2x24_s; + //printk("--- 24 ---\n"); + break; + } +} + + +#ifdef CONFIG_PROC_FS +extern struct proc_dir_entry *proc_jz_root; + +int i2s_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + return 0; +} + +static int jz_i2s_init_proc(struct jz_i2s_controller_info *controller) +{ + if (!create_proc_read_entry ("i2s", 0, proc_jz_root, i2s_read_proc, controller->i2s_codec[0])) + return -EIO; + return 0; +} + +static void jz_i2s_cleanup_proc(struct jz_i2s_controller_info *controller) +{ + ; +} +#endif + +static void __init attach_jz_i2s(struct jz_i2s_controller_info *controller) +{ + char *name; + int adev;//No of Audio device. + name = controller->name; + jz_i2s_initHw(1);//initialize AIC controller and reset it + /* register /dev/audio */ + adev = register_sound_dsp(&jz_i2s_audio_fops, -1); + if (adev < 0) + goto audio_failed; + /* initialize I2S codec and register /dev/mixer */ + if (jz_i2s_codec_init(controller) <= 0) + goto mixer_failed; + +#ifdef CONFIG_PROC_FS + if (jz_i2s_init_proc(controller) < 0) { + printk(KERN_ERR "%s: can't create I2S proc filesystem.\n", name); + goto proc_failed; + } +#endif + + controller->tmp1 = (void *)__get_free_pages(GFP_KERNEL, JZCODEC_USER_BUFFER); + if (!controller->tmp1) { + printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name); + goto tmp1_failed; + } + + controller->tmp2 = (void *)__get_free_pages(GFP_KERNEL, JZCODEC_USER_BUFFER); + if (!controller->tmp2) { + printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name); + goto tmp2_failed; + } + + if ((controller->dma1 = jz_request_dma(DMA_ID_I2S_TX, "audio dac", jz_i2s_replay_dma_irq, IRQF_DISABLED, controller)) < 0) { + printk(KERN_ERR "%s: can't reqeust DMA DAC channel.\n", name); + goto dma1_failed; + } + if ((controller->dma2 = jz_request_dma(DMA_ID_I2S_RX, "audio adc", jz_i2s_record_dma_irq, IRQF_DISABLED, controller)) < 0) { + printk(KERN_ERR "%s: can't reqeust DMA ADC channel.\n", name); + goto dma2_failed; + } + printk("JzSOC On-Chip I2S controller registered (DAC: DMA(play):%d/IRQ%d,\n ADC: DMA(record):%d/IRQ%d)\n", controller->dma1, get_dma_done_irq(controller->dma1), controller->dma2, get_dma_done_irq(controller->dma2)); + + controller->dev_audio = adev; + + return; +dma2_failed: + jz_free_dma(controller->dma2); +dma1_failed: + jz_free_dma(controller->dma1); + free_pages((unsigned long)controller->tmp2, JZCODEC_USER_BUFFER); +tmp2_failed: +tmp1_failed: + free_pages((unsigned long)controller->tmp1, JZCODEC_USER_BUFFER); + +#ifdef CONFIG_PROC_FS + jz_i2s_cleanup_proc(controller); +#endif +proc_failed: + /* unregister mixer dev */ +mixer_failed: + unregister_sound_dsp(adev); +audio_failed: + return; +} + + +static int __init probe_jz_i2s(struct jz_i2s_controller_info **controller) +{ + if ((*controller = kmalloc(sizeof(struct jz_i2s_controller_info), + GFP_KERNEL)) == NULL) { + printk(KERN_ERR "Jz I2S Controller: out of memory.\n"); + return -ENOMEM; + } + (*controller)->name = "Jz I2S controller"; + (*controller)->opened1 = 0; + (*controller)->opened2 = 0; + init_waitqueue_head(&(*controller)->adc_wait); + init_waitqueue_head(&(*controller)->dac_wait); + spin_lock_init(&(*controller)->lock); + init_waitqueue_head(&rx_wait_queue); + init_waitqueue_head(&tx_wait_queue); + init_waitqueue_head(&pop_wait_queue); + + return 0; +} + +static void __exit unload_jz_i2s(struct jz_i2s_controller_info *controller) +{ + int adev = controller->dev_audio; + jz_i2s_full_reset (controller); + controller->dev_audio = -1; + +#ifdef CONFIG_PROC_FS + jz_i2s_cleanup_proc(controller); +#endif + + jz_free_dma(controller->dma1); + jz_free_dma(controller->dma2); + free_pages((unsigned long)controller->tmp1, JZCODEC_USER_BUFFER); + free_pages((unsigned long)controller->tmp2, JZCODEC_USER_BUFFER); + if (adev >= 0) { + //unregister_sound_mixer(audio_devs[adev]->mixer_dev); + unregister_sound_dsp(controller->dev_audio); + } +} + + +#ifdef CONFIG_PM +static int jz_i2s_suspend(struct jz_i2s_controller_info *controller, int state) +{ + return 0; +} + +static int jz_i2s_resume(struct jz_i2s_controller_info *controller) +{ + return 0; +} + +static int jz_i2s_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data) +{ + int ret; + struct jz_i2s_controller_info *controller = pm_dev->data; + + if (!controller) return -EINVAL; + + switch (req) { + case PM_SUSPEND: + ret = jz_i2s_suspend(controller, (int)data); + break; + + case PM_RESUME: + ret = jz_i2s_resume(controller); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} +#endif /* CONFIG_PM */ + + +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) { + + 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; +} + +static int __init init_jz_i2s(void) +{ + int errno, retval; + +#if defined(CONFIG_I2S_DLV) + ramp_up_start = 0; + ramp_up_end = 0; + gain_up_start = 0; + gain_up_end = 0; + ramp_down_start = 0; + ramp_down_end = 0; + gain_down_start = 0; + gain_down_end = 0; + + //REG_CPM_CPCCR &= ~(1 << 31); + //REG_CPM_CPCCR &= ~(1 << 30); + use_mic_line_flag = USE_NONE; + 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(9, 0xff); + write_codec_file(8, 0x3f); + mdelay(10); +#endif + + if ((errno = probe_jz_i2s(&i2s_controller)) < 0) + return errno; + attach_jz_i2s(i2s_controller); +#if defined(CONFIG_I2S_DLV) + __i2s_disable_transmit_intr(); + __i2s_disable_receive_intr(); + jz_codec_config = 0; + 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; + } +#endif + + out_empty_queue.id = NULL; + out_full_queue.id = NULL; + out_busy_queue.id = NULL; + in_empty_queue.id = NULL; + in_full_queue.id = NULL; + in_busy_queue.id = NULL; + + jz_audio_fragsize = JZCODEC_RW_BUFFER_SIZE * PAGE_SIZE; + jz_audio_fragstotal = JZCODEC_RW_BUFFER_TOTAL ; + Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); + +#ifdef CONFIG_PM + i2s_controller->pm = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, + jz_i2s_pm_callback); + if (i2s_controller->pm) + i2s_controller->pm->data = i2s_controller; +#endif + + __cpm_start_idct(); + __cpm_start_db(); + __cpm_start_me(); + __cpm_start_mc(); + __cpm_start_ipu(); + + return 0; +} + + +static void __exit cleanup_jz_i2s(void) +{ + unload_jz_i2s(i2s_controller); +#if defined(CONFIG_I2S_DLV) + free_irq(IRQ_AIC, NULL); +#endif + Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); +} + +module_init(init_jz_i2s); +module_exit(cleanup_jz_i2s); + +static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock) +{ + //DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count,i=0; + + //add_wait_queue(&ctrl->adc_wait, &wait); + for (;;) { + if (i < MAXDELAY) { + udelay(10); + i++; + } else + break; + //set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&ctrl->lock, flags); + //spin_lock(&ctrl->lock); + count = get_dma_residue(ctrl->dma2); + spin_unlock_irqrestore(&ctrl->lock, flags); + //spin_unlock(&ctrl->lock); + if (count <= 0) + break; + + /*if (signal_pending(current)) + break;*/ + if (nonblock) { + //remove_wait_queue(&ctrl->adc_wait, &wait); + //current->state = TASK_RUNNING; + return -EBUSY; + } + } + //remove_wait_queue(&ctrl->adc_wait, &wait); + //current->state = TASK_RUNNING; + /*if (signal_pending(current)) + return -ERESTARTSYS;*/ + return 0; +} +static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock) +{ + unsigned long flags; + int count,ele,busyele,emptyele,i=0; + + for (;;) { + if(!nonblock) {//blocked + if (i < MAXDELAY) { + udelay(10); + i++; + } else + break; + + ele = elements_in_queue(&out_full_queue); + if(ele <= 0) { + udelay(200); + + busyele = elements_in_queue(&out_busy_queue); + emptyele = elements_in_queue(&out_empty_queue); + if (busyele <= 0 && emptyele >= jz_audio_fragstotal) { + spin_lock_irqsave(&ctrl->lock, flags); + count = get_dma_residue(ctrl->dma1); + spin_unlock_irqrestore(&ctrl->lock, flags); + if (count <= 0) + break; + } + } + } else {//non-blocked + mdelay(100); + ele = elements_in_queue(&out_full_queue); + + if(ele <= 0) { + mdelay(100); + busyele = elements_in_queue(&out_busy_queue); + emptyele = elements_in_queue(&out_empty_queue); + + if (busyele <= 0 && emptyele >= jz_audio_fragstotal) { + spin_lock_irqsave(&ctrl->lock, flags); + count = get_dma_residue(ctrl->dma1); + spin_unlock_irqrestore(&ctrl->lock, flags); + if (count <= 0) + break; + } + } + } + } + + return 0; +} + +static void print_pop_duration(void) +{ + long diff; +#if 0 + printk(" ramp_up_start=0x%x\n",ramp_up_start); + printk(" ramp_up_end =0x%x\n",ramp_up_end); + printk(" gain_up_start=0x%x\n",gain_up_start); + printk(" gain_up_end =0x%x\n",gain_up_end); + + printk("ramp_down_start=0x%x\n",ramp_down_start); + printk("ramp_down_end =0x%x\n",ramp_down_end); + printk("gain_down_start=0x%x\n",gain_down_start); + printk("gain_down_end =0x%x\n",gain_down_end); +#endif + + diff = (long)ramp_up_end - (long)ramp_up_start; + printk("ramp up duration: %d ms\n",diff * 1000 / HZ); + diff = (long)gain_up_end - (long)gain_up_start; + printk("gain up duration: %d ms\n",diff * 1000 / HZ); + diff = (long)gain_down_end - (long)gain_down_start; + printk("gain down duration: %d ms\n",diff); + diff = (long)ramp_down_end - (long)ramp_down_start; + printk("ramp down duration: %d ms\n",diff * 1000 / HZ); +} + +static int jz_audio_release(struct inode *inode, struct file *file) +{ + struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; + unsigned long tfl; + + jz_codec_config = 0; + if (controller == NULL) + return -ENODEV; + + if (controller->opened1 == 1 && controller->opened2 == 1) { + __i2s_enable_transmit_dma(); + __i2s_enable_replay(); + + drain_dac(controller, file->f_flags & O_NONBLOCK); + /* wait for fifo empty */ + write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + gain_down_start = jiffies; + sleep_on(&pop_wait_queue); + //gain_down_end = jiffies; + /*while (1) { + tfl = REG_AIC_SR & 0x00003f00; + if (tfl == 0) { + udelay(500); + break; + } + mdelay(2); + }*/ + mdelay(100); + disable_dma(controller->dma1); + set_dma_count(controller->dma1, 0); + + __i2s_disable_transmit_dma(); + __i2s_disable_replay(); + + //spin_lock_irqsave(&controller->ioctllock, flags); + spin_lock(&controller->ioctllock); + controller->total_bytes = 0; + controller->count = 0; + controller->finish = 0; + jz_audio_dma_tran_count = 0; + controller->blocks = 0; + controller->nextOut = 0; + //spin_unlock_irqrestore(&controller->ioctllock, flags); + spin_unlock(&controller->ioctllock); + + ///////////////////////// + first_record_call = 1; + __i2s_enable_receive_dma(); + __i2s_enable_record(); + drain_adc(controller, file->f_flags & O_NONBLOCK); + disable_dma(controller->dma2); + set_dma_count(controller->dma2, 0); + __i2s_disable_receive_dma(); + __i2s_disable_record(); + //__aic_flush_fifo(); + //spin_lock_irqsave(&controller->ioctllock, flags); + spin_lock(&controller->ioctllock); + controller->total_bytes = 0; + jz_audio_dma_tran_count = 0; + controller->count = 0; + controller->finish = 0; + controller->blocks = 0; + controller->nextIn = 0; + //spin_unlock_irqrestore(&controller->ioctllock, flags); + spin_unlock(&controller->ioctllock); +#if defined(CONFIG_I2S_DLV) + write_codec_file_bit(5, 1, 6);//SB_OUT->1 + ramp_down_start = jiffies; + sleep_on(&pop_wait_queue); + //ramp_down_end = jiffies; + if (use_mic_line_flag == USE_LINEIN) { + unset_record_line_input_audio_with_audio_data_replay(); + //printk("3 use_mic_line_flag=%d\n",use_mic_line_flag); + } + if (use_mic_line_flag == USE_MIC) { + unset_record_mic_input_audio_with_audio_data_replay(); + //printk("4 use_mic_line_flag=%d\n",use_mic_line_flag); + } + +#if 0 + unset_record_playing_audio_mixed_with_mic_input_audio(); +#endif + __i2s_disable(); +#endif + } else if ( controller->opened1 == 1 ) { + __i2s_enable_transmit_dma(); + __i2s_enable_replay(); + drain_dac(controller, file->f_flags & O_NONBLOCK); + /* wait for fifo empty */ + write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + gain_down_start = jiffies; + sleep_on(&pop_wait_queue); + //gain_down_end = jiffies; + while (1) { + tfl = REG_AIC_SR & 0x00003f00; + if (tfl == 0) { + udelay(500); + break; + } + mdelay(2); + } + disable_dma(controller->dma1); + set_dma_count(controller->dma1, 0); + + __i2s_disable_transmit_dma(); + __i2s_disable_replay(); + + __aic_flush_fifo(); + + //spin_lock_irqsave(&controller->ioctllock, flags); + spin_lock(&controller->ioctllock); + controller->total_bytes = 0; + controller->count = 0; + controller->finish = 0; + jz_audio_dma_tran_count = 0; + controller->blocks = 0; + controller->nextOut = 0; + //spin_unlock_irqrestore(&controller->ioctllock, flags); + spin_unlock(&controller->ioctllock); +#if defined(CONFIG_I2S_DLV) + write_codec_file_bit(5, 1, 6);//SB_OUT->1 + ramp_down_start = jiffies; + sleep_on(&pop_wait_queue); + //ramp_down_end = jiffies; + unset_audio_data_replay(); +#endif + __i2s_disable(); + } else if ( controller->opened2 == 1 ) { + //controller->opened2 = 0; + first_record_call = 1; + __i2s_enable_receive_dma(); + __i2s_enable_record(); + drain_adc(controller, file->f_flags & O_NONBLOCK); + disable_dma(controller->dma2); + set_dma_count(controller->dma2, 0); + __i2s_disable_receive_dma(); + __i2s_disable_record(); + + //spin_lock_irqsave(&controller->ioctllock, flags); + spin_lock(&controller->ioctllock); + controller->total_bytes = 0; + jz_audio_dma_tran_count = 0; + controller->count = 0; + controller->finish = 0; + controller->blocks = 0; + controller->nextIn = 0; + //spin_unlock_irqrestore(&controller->ioctllock, flags); + spin_unlock(&controller->ioctllock); +#if defined(CONFIG_I2S_DLV) +#if 0 + /* unset Record MIC input audio with direct playback */ + unset_record_mic_input_audio_with_direct_playback(); +#endif +#if 1 + /* unset Record MIC input audio without playback */ + unset_record_mic_input_audio_without_playback(); +#endif +#if 0 + /* tested */ + /* unset Record LINE input audio without playback */ + unset_record_line_input_audio_without_playback(); +#endif +#endif + __i2s_disable(); + } + + if (controller->opened1 == 1 && controller->opened2 == 1) { + controller->opened1 = 0; + controller->opened2 = 0; + print_pop_duration(); + //__dmac_disable_module(0); + } else if ( controller->opened1 == 1 ) { + controller->opened1 = 0; + print_pop_duration(); + } else if ( controller->opened2 == 1 ) { + controller->opened2 = 0; + } + write_codec_file(9, 0xff); + write_codec_file(8, 0x3f); + +/* __cpm_stop_idct(); + __cpm_stop_db(); + __cpm_stop_me(); + __cpm_stop_mc(); + __cpm_stop_ipu();*/ + printk("close audio and clear ipu gate : 0x%08x\n", REG_CPM_CLKGR); + return 0; +} + +static int jz_audio_open(struct inode *inode, struct file *file) +{ + int i; + struct jz_i2s_controller_info *controller = i2s_controller; + if (controller == NULL) + return -ENODEV; + mdelay(2); + REG_DMAC_DMACKE(0) = 0x3f; + if (controller->opened1 == 1 || controller->opened2 == 1 ) { + printk("\naudio is busy!\n"); + return -EBUSY; + } + jz_codec_config = 0; + + ramp_up_start = 0; + ramp_up_end = 0; + gain_up_start = 0; + gain_up_end = 0; + ramp_down_start = 0; + ramp_down_end = 0; + gain_down_start = 0; + gain_down_end = 0; + + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { + if (controller->opened1 == 1) + return -EBUSY; + controller->opened1 = 1; + /* for ioctl */ + controller->total_bytes = 0; + jz_audio_dma_tran_count = 0; + controller->count = 0; + controller->finish = 0; + controller->blocks = 0; + controller->nextOut = 0; + + out_empty_queue.count = jz_audio_fragstotal; + for (i=0;i < jz_audio_fragstotal;i++) + *(out_empty_queue.id + i) = i; + out_busy_queue.count = 0; + out_full_queue.count = 0; + + if (controller->opened2 == 1) + return -EBUSY; + + controller->opened2 = 1; + first_record_call = 1; + /* for ioctl */ + controller->total_bytes = 0; + jz_audio_dma_tran_count = 0; + controller->count = 0; + controller->finish = 0; + controller->blocks = 0; + controller->nextIn = 0; + + in_empty_queue.count = jz_audio_fragstotal; + for (i=0;i < jz_audio_fragstotal;i++) + *(in_empty_queue.id + i) = i; + + in_full_queue.count = 0; + in_busy_queue.count = 0; + } else if (file->f_mode & FMODE_WRITE) { + if (controller->opened1 == 1) + return -EBUSY; + controller->opened1 = 1; + //for ioctl + controller->total_bytes = 0; + jz_audio_dma_tran_count = 0; + controller->count = 0; + controller->finish = 0; + controller->blocks = 0; + controller->nextOut = 0; + + out_empty_queue.count = jz_audio_fragstotal; + for (i=0;i < jz_audio_fragstotal;i++) + *(out_empty_queue.id + i) = i; + out_busy_queue.count = 0; + out_full_queue.count = 0; + } else if (file->f_mode & FMODE_READ) { + if (controller->opened2 == 1) + return -EBUSY; + + controller->opened2 = 1; + first_record_call = 1; + //for ioctl + controller->total_bytes = 0; + jz_audio_dma_tran_count = 0; + controller->count = 0; + controller->finish = 0; + controller->blocks = 0; + controller->nextIn = 0; + + in_empty_queue.count = jz_audio_fragstotal; + for (i=0;i < jz_audio_fragstotal;i++) + *(in_empty_queue.id + i) = i; + + in_full_queue.count = 0; + in_busy_queue.count = 0; + } + + file->private_data = controller; + jz_audio_reset();//11.2 + + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { +#if defined(CONFIG_I2S_DLV) + + if (use_mic_line_flag == USE_LINEIN) { + /* Record LINE input audio with Audio data replay (full duplex for linein) */ + /* codec_test_line */ + set_record_line_input_audio_with_audio_data_replay(); + + } + if (use_mic_line_flag == USE_MIC) { + /* Record MIC input audio with Audio data replay (full duplex) */ + /* codec_test_mic */ + set_record_mic_input_audio_with_audio_data_replay(); + } +#if 0 + /* Record playing audio mixed with MIC input audio */ + set_record_playing_audio_mixed_with_mic_input_audio(); +#endif + +#endif + } else if (file->f_mode & FMODE_WRITE) { +#if defined(CONFIG_I2S_DLV) + //mdelay(10); + /* Audio data replay */ + set_audio_data_replay(); +#endif + } else if (file->f_mode & FMODE_READ) { +#if defined(CONFIG_I2S_DLV) +#if 0 + /* Record MIC input audio with direct playback */ + set_record_mic_input_audio_with_direct_playback(); +#endif + +#if 1 + /* set Record MIC input audio without playback */ + set_record_mic_input_audio_without_playback(); +#endif +#if 0 + /* tested */ + /* set Record LINE input audio without playback */ + set_record_line_input_audio_without_playback(); +#endif + mdelay(1); +#endif + } + + __aic_reset(); + + mdelay(10); + REG_AIC_I2SCR = 0x10; + mdelay(20); + __aic_flush_fifo(); + __i2s_enable(); + ndelay(100); + + if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { +#if defined(CONFIG_I2S_DLV) + //set SB_ADC or SB_DAC + __dmac_enable_module(0); + write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 + ramp_up_start = jiffies; + sleep_on(&pop_wait_queue); + //ramp_up_end = jiffies; +#endif + } else if (file->f_mode & FMODE_WRITE) { +#if defined(CONFIG_I2S_DLV) + write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 + ramp_up_start = jiffies; + /*while (!(REG_RTC_RCR & RTC_RCR_WRDY)); + REG_RTC_RCR = 0x1; + while (!(REG_RTC_RCR & RTC_RCR_WRDY)); + REG_RTC_RGR = 1;*/ + sleep_on(&pop_wait_queue); + //ramp_up_end = jiffies; + write_codec_file_bit(5, 1, 4);//SB_ADC->1 +#endif + } else if (file->f_mode & FMODE_READ) { +#if defined(CONFIG_I2S_DLV) + if (jz_mic_only) + write_codec_file_bit(5, 1, 7);//SB_DAC->1 + else + write_codec_file_bit(5, 0, 7);//SB_DAC->0 + mdelay(500); +#endif + } + + + printk("open audio and set ipu gate : 0x%08x\n", REG_CPM_CLKGR); + return 0; +} + + +static int jz_audio_ioctl(struct inode *inode, struct file *file, +unsigned int cmd, unsigned long arg) +{ + int val,fullc,busyc,unfinish,newfragstotal,newfragsize; + unsigned int flags; + struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; + + audio_buf_info abinfo; + int i, bytes, id; + count_info cinfo; + val = 0; + bytes = 0; + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + case SNDCTL_DSP_RESET: + return 0; + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + drain_dac(controller, file->f_flags & O_NONBLOCK); + return 0; + case SNDCTL_DSP_SPEED: + /* set smaple rate */ + { + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) + jz_audio_set_speed(controller->dev_audio, val); + return put_user(val, (int *)arg); + } + case SNDCTL_DSP_STEREO: + /* set stereo or mono channel */ + if (get_user(val, (int *)arg)) + return -EFAULT; + jz_audio_set_channels(controller->dev_audio, val ? 2 : 1); + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + //return put_user(4*PAGE_SIZE, (int *)arg); + return put_user(jz_audio_fragsize , (int *)arg); + case SNDCTL_DSP_GETFMTS: + /* Returns a mask of supported sample format*/ + return put_user(AFMT_U8 | AFMT_S16_LE, (int *)arg); + + case SNDCTL_DSP_SETFMT: + /* Select sample format */ + { + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + jz_audio_set_format(controller->dev_audio,val); + } else { + if (file->f_mode & FMODE_READ) + val = (jz_audio_format == 16) ? AFMT_S16_LE : AFMT_U8; + else + val = (jz_audio_format == 16) ? AFMT_S16_LE : AFMT_U8; + + } + return put_user(val, (int *)arg); + } + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + jz_audio_set_channels(controller->dev_audio, val); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_POST: + /* FIXME: the same as RESET ?? */ + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + return 0; + + case SNDCTL_DSP_SETFRAGMENT: + get_user(val, (long *) arg); + newfragsize = 1 << (val & 0xFFFF);//16 least bits + + if (newfragsize < 4 * PAGE_SIZE) + newfragsize = 4 * PAGE_SIZE; + if (newfragsize > (16 * PAGE_SIZE)) //16 PAGE_SIZE + newfragsize = 16 * PAGE_SIZE; + + newfragstotal = (val >> 16) & 0x7FFF; + if (newfragstotal < 2) + newfragstotal = 2; + if (newfragstotal > 32) + newfragstotal = 32; + if((jz_audio_fragstotal == newfragstotal) && (jz_audio_fragsize == newfragsize)) + return 0; + Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); + mdelay(500); + jz_audio_fragstotal = newfragstotal; + jz_audio_fragsize = newfragsize; + + Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); + mdelay(10); + + return 0; + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_REALTIME|DSP_CAP_BATCH, (int *)arg); + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + case SNDCTL_DSP_SETDUPLEX: + return -EINVAL; + case SNDCTL_DSP_GETOSPACE: + { + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + //unused fragment amount + spin_lock_irqsave(&controller->ioctllock, flags); + jz_audio_fragments = elements_in_queue(&out_empty_queue); + for (i = 0; i < jz_audio_fragments; i++) + bytes += jz_audio_fragsize; + bytes /= jz_audio_b; + spin_unlock_irqrestore(&controller->ioctllock, flags); + abinfo.fragments = jz_audio_fragments; + abinfo.fragstotal = jz_audio_fragstotal; + abinfo.fragsize = jz_audio_fragsize; + abinfo.bytes = bytes; + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + } + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + + bytes = 0; + //unused fragment amount + jz_audio_fragments = elements_in_queue(&in_empty_queue); + for (i = 0; i < jz_audio_fragments; i++) + bytes += jz_audio_fragsize; + bytes /= jz_audio_b; + abinfo.fragments = jz_audio_fragments; + abinfo.fragstotal = jz_audio_fragstotal; + abinfo.fragsize = jz_audio_fragsize; + abinfo.bytes = bytes; + return copy_to_user((void *)arg, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && in_dma_buf) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && out_dma_buf) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + return 0; + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&controller->ioctllock, flags); + //controller->total_bytes += get_dma_residue(controller->dma2); + cinfo.bytes = controller->total_bytes; + cinfo.blocks = controller->blocks; + cinfo.ptr = controller->nextIn; + controller->blocks = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&controller->ioctllock, flags); + //controller->total_bytes += get_dma_residue(controller->dma1); + cinfo.bytes = controller->total_bytes; + cinfo.blocks = controller->blocks; + cinfo.ptr = controller->nextOut; + controller->blocks = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + spin_lock_irqsave(&controller->ioctllock, flags); + unfinish = 0; + fullc = elements_in_queue(&out_full_queue); + busyc = elements_in_queue(&out_busy_queue); + for(i = 0;i < fullc ;i ++) { + id = *(out_full_queue.id + i); + unfinish += *(out_dma_buf_data_count + id); + } + for(i = 0;i < busyc ;i ++) { + id = *(out_busy_queue.id + i); + unfinish += get_dma_residue(controller->dma1); + } + spin_unlock_irqrestore(&controller->ioctllock, flags); + unfinish /= jz_audio_b; + return put_user(unfinish, (int *) arg); + case SOUND_PCM_READ_RATE: + return put_user(jz_audio_rate, (int *)arg); + case SOUND_PCM_READ_CHANNELS: + return put_user(jz_audio_channels, (int *)arg); + case SOUND_PCM_READ_BITS: + return put_user((jz_audio_format & (AFMT_S8 | AFMT_U8)) ? 8 : 16, + (int *)arg); + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + return -EINVAL; +} + + +static unsigned int jz_audio_poll(struct file *file,struct poll_table_struct *wait) +{ + struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; + unsigned long flags; + unsigned int mask = 0; + + if (file->f_mode & FMODE_WRITE) { + if (elements_in_queue(&out_empty_queue) > 0) + return POLLOUT | POLLWRNORM; + poll_wait(file, &controller->dac_wait, wait); + } + + if (file->f_mode & FMODE_READ) { + if (elements_in_queue(&in_full_queue) > 0) + return POLLIN | POLLRDNORM; + poll_wait(file, &controller->adc_wait, wait); + } + spin_lock_irqsave(&controller->lock, flags); + if (file->f_mode & FMODE_WRITE) { + if (elements_in_queue(&out_empty_queue) > 0) + mask |= POLLOUT | POLLWRNORM; + } else if (file->f_mode & FMODE_READ) { + if (elements_in_queue(&in_full_queue) > 0) + mask |= POLLIN | POLLRDNORM; + } + spin_unlock_irqrestore(&controller->lock, flags); + return mask; +} + +static ssize_t jz_audio_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; + int id, ret = 0, left_count, copy_count, cnt = 0; + + if (count < 0) + return -EINVAL; + + __i2s_enable_receive_dma(); + __i2s_enable_record(); + //spin_lock_irqsave(&controller->ioctllock, flags); + spin_lock(&controller->ioctllock); + controller->nextIn = 0; + //spin_unlock_irqrestore(&controller->ioctllock, flags); + spin_unlock(&controller->ioctllock); + + spin_lock(&controller->lock); + + copy_count = jz_audio_fragsize / 4; + + left_count = count; + spin_unlock(&controller->lock); + + if (first_record_call) { + first_record_call = 0; + audio_read_back_first: + if ((id = get_buffer_id(&in_empty_queue)) >= 0) { + put_buffer_id(&in_busy_queue, id); + spin_lock(&controller->lock); + *(in_dma_buf_data_count + id) = copy_count * 4; + spin_unlock(&controller->lock); + __i2s_enable_receive_dma(); + __i2s_enable_record(); + + //write back + dma_cache_wback_inv(*(in_dma_buf + id), + *(in_dma_buf_data_count + id)); + audio_start_dma(controller->dma2,file->private_data, + *(in_dma_pbuf + id), + *(in_dma_buf_data_count + id), + DMA_MODE_READ); + //interruptible_sleep_on(&rx_wait_queue); + sleep_on(&rx_wait_queue); + } else + goto audio_read_back_first; + } + + while (left_count > 0) { + audio_read_back_second: + if (elements_in_queue(&in_full_queue) <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + else + sleep_on(&rx_wait_queue); + } + /*if (signal_pending(current)) + return ret ? ret: -ERESTARTSYS; */ + if ((id = get_buffer_id(&in_full_queue)) >= 0) { + spin_lock(&controller->lock); + cnt = record_filler((unsigned long)controller->tmp2+ret, + copy_count, id); + spin_unlock(&controller->lock); + put_buffer_id(&in_empty_queue, id); + } else + goto audio_read_back_second; + + if (elements_in_queue(&in_busy_queue) == 0) { + if ((id=get_buffer_id(&in_empty_queue)) >= 0) { + put_buffer_id(&in_busy_queue, id); + spin_lock(&controller->lock); + *(in_dma_buf_data_count + id) = copy_count * 4; + spin_unlock(&controller->lock); + //write back + dma_cache_wback_inv(*(in_dma_buf + id), + *(in_dma_buf_data_count + id)); + audio_start_dma(controller->dma2,file->private_data, + *(in_dma_pbuf + id), + *(in_dma_buf_data_count + id), + DMA_MODE_READ); + } + } + if (ret + cnt > count) { + spin_lock(&controller->lock); + cnt = count - ret; + spin_unlock(&controller->lock); + } + if (copy_to_user(buffer+ret, controller->tmp2+ret, cnt)) + return ret ? ret : -EFAULT; + spin_lock(&controller->lock); + ret += cnt; + spin_unlock(&controller->lock); + //spin_lock_irqsave(&controller->ioctllock, flags); + spin_lock(&controller->ioctllock); + controller->nextIn += ret; + //spin_unlock_irqrestore(&controller->ioctllock, flags); + spin_unlock(&controller->ioctllock); + spin_lock(&controller->lock); + left_count -= cnt; + spin_unlock(&controller->lock); + }//while (left_count > 0) + return ret; +} + +static ssize_t jz_audio_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + int id, ret = 0, left_count, copy_count = 0, val; + unsigned int flags; + struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; + + if (count <= 0) + return -EINVAL; + + __i2s_enable_transmit_dma(); + __i2s_enable_replay(); + + spin_lock_irqsave(&controller->ioctllock, flags); + //spin_lock(&controller->ioctllock); + controller->nextOut = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + //spin_unlock(&controller->ioctllock); + if (jz_audio_channels == 2) + copy_count = jz_audio_fragsize / jz_audio_b; + else if(jz_audio_channels == 1) + copy_count = jz_audio_fragsize / 4; + + left_count = count; + if (copy_from_user(controller->tmp1, buffer, count)) { + printk("copy_from_user failed:%d",ret); + return ret ? ret : -EFAULT; + } + + while (left_count > 0) { + audio_write_back: + if (file->f_flags & O_NONBLOCK) + udelay(2); + if (elements_in_queue(&out_empty_queue) == 0) { + // all are full + if (file->f_flags & O_NONBLOCK) + return ret;//no waiting,no block + else { + sleep_on(&tx_wait_queue);//blocked + } + } + /*if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS;*/ + /* the end fragment size in this write */ + if (ret + copy_count > count) + copy_count = count - ret; + if ((id = get_buffer_id(&out_empty_queue)) >= 0) { + //replay_filler((unsigned long)controller->tmp1 + ret, copy_count, id); + replay_filler((signed long)controller->tmp1 + ret, copy_count, id); + if(*(out_dma_buf_data_count + id) > 0) { + put_buffer_id(&out_full_queue, id); //busy in + dma_cache_wback_inv(*(out_dma_buf + id), + *(out_dma_buf_data_count + id)); + } else + put_buffer_id(&out_empty_queue, id); //spare + } else + goto audio_write_back; + + left_count = left_count - copy_count; + ret += copy_count; + //spin_lock_irqsave(&controller->ioctllock, flags); + spin_lock(&controller->ioctllock); + controller->nextOut += ret; + //spin_unlock_irqrestore(&controller->ioctllock, flags); + spin_unlock(&controller->ioctllock); + if (elements_in_queue(&out_busy_queue) == 0) { + if ((id = get_buffer_id(&out_full_queue)) >= 0) { + put_buffer_id(&out_busy_queue, id); //first once,next spare + + if(*(out_dma_buf_data_count + id) > 0) { + audio_start_dma(controller->dma1, + file->private_data, + *(out_dma_pbuf + id), + *(out_dma_buf_data_count + id), + DMA_MODE_WRITE); + if (jz_codec_config == 0) { + write_codec_file_bit(1, 0, 5); + gain_up_start = jiffies; + sleep_on(&pop_wait_queue); + //gain_up_end = jiffies; + jz_codec_config = 1; + //SB_ADC->1 + //write_codec_file_bit(5, 1, 4); + //while(1); + } + } + } + } + } + return ret; +} diff --git a/sound/oss/jz_i2s_for_4750.c b/sound/oss/jz_i2s_for_4750.c new file mode 100644 index 0000000..2ca3342 --- /dev/null +++ b/sound/oss/jz_i2s_for_4750.c @@ -0,0 +1,2981 @@ +/* + * linux/drivers/sound/Jz_i2s.c + * + * JzSOC On-Chip I2S audio driver. + * + * Copyright (C) 2005 by Junzheng Corp. + * Modified by cjfeng on Aug 9,2007,and not any bug on Jz4730 using + * dma channel 4&3,noah is tested. + * + * 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. + * + * Because the normal application of AUDIO devices are focused on Little_endian, + * then we only perform the little endian data format in driver. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sound_config.h" + +#define DPRINTK(args...) printk(args) +#define DMA_ID_I2S_TX DMA_ID_AIC_TX +#define DMA_ID_I2S_RX DMA_ID_AIC_RX +#define NR_I2S 2 +#define MAXDELAY 50000 +#define JZCODEC_RW_BUFFER_SIZE 2 +#define JZCODEC_RW_BUFFER_TOTAL 6 + +typedef struct hpvol_shift_s +{ + int hpvol; + int shift; +} hpvol_shift_t; + +mixer_info info; +_old_mixer_info old_info; +int codec_volue_shift; +hpvol_shift_t hpvol_shift_table[72]; +int abnormal_data_count; +unsigned long i2s_clk; + +void (*set_codec_mode)(void) = NULL; +void (*clear_codec_mode)(void) = NULL; +void (*set_codec_gpio_pin)(void) = NULL; +void (*each_time_init_codec)(void) = NULL; +int (*set_codec_startup_param)(void) = NULL; +void (*set_codec_volume_table)(void) = NULL; +void (*set_codec_record)(void) = NULL; +void (*set_codec_replay)(void) = NULL; +void (*set_codec_replay_record)(void); +void (*turn_on_codec)(void) = NULL; +void (*turn_off_codec)(void) = NULL; +void (*set_codec_speed)(int rate) = NULL; +void (*reset_codec)(void) = NULL; +void (*codec_mixer_old_info_id_name)(void) = NULL; +void (*codec_mixer_info_id_name)(void) = NULL; +void (*set_codec_bass)(int val) = NULL; +void (*set_codec_volume)(int val) = NULL; +void (*set_codec_mic)(int val) = NULL; +void (*i2s_resume_codec)(void) = NULL; +void (*i2s_suspend_codec)(int wr,int rd) = NULL; +void (*init_codec_pin)(void) = NULL; +void (*set_codec_some_func)(void) = NULL; +void (*clear_codec_record)(void) = NULL; +void (*clear_codec_replay)(void) = NULL; +void (*set_replay_hp_or_speaker)(void) = NULL; +void (*set_codec_direct_mode)(void) = NULL; +void (*clear_codec_direct_mode)(void) = NULL; + +static int jz_audio_rate; +static int jz_audio_format; +static int jz_audio_volume; +static int jz_audio_channels; +static int jz_audio_b; /* bits expand multiple */ +static int jz_audio_fragments; /* unused fragment amount */ +static int jz_audio_fragstotal; +static int jz_audio_fragsize; +static int jz_audio_speed; + +static int codec_bass_gain; +static int audio_mix_modcnt; +static int jz_audio_dma_tran_count; /* bytes count of one DMA transfer */ +static int jz_mic_only = 1; +static int jz_codec_config = 0; +static unsigned long ramp_up_start; +static unsigned long ramp_up_end; +static unsigned long gain_up_start; +static unsigned long gain_up_end; +static unsigned long ramp_down_start; +static unsigned long ramp_down_end; +static unsigned long gain_down_start; +static unsigned long gain_down_end; + +static int codec_mic_gain; +static int pop_dma_flag; +static int last_dma_buffer_id; +static int drain_flag; + +static void (*old_mksound)(unsigned int hz, unsigned int ticks); +extern void (*kd_mksound)(unsigned int hz, unsigned int ticks); +static void jz_update_filler(int bits, int channels); + +static int Init_In_Out_queue(int fragstotal,int fragsize); +static int Free_In_Out_queue(int fragstotal,int fragsize); +static irqreturn_t jz_i2s_replay_dma_irq(int irqnr, void *ref); +static irqreturn_t jz_i2s_record_dma_irq(int irqnr, void *ref); +static void (*replay_filler)(signed long src_start, int count, int id); +static int (*record_filler)(unsigned long dst_start, int count, int id); +#if defined(CONFIG_I2S_ICODEC) +static void write_mute_to_dma_buffer(signed long l_sample, signed long r_sample); +#endif +static void jz_audio_reset(void); +static struct file_operations jz_i2s_audio_fops; + +static DECLARE_WAIT_QUEUE_HEAD (rx_wait_queue); +static DECLARE_WAIT_QUEUE_HEAD (tx_wait_queue); +static DECLARE_WAIT_QUEUE_HEAD (drain_wait_queue); +static DECLARE_WAIT_QUEUE_HEAD (pop_wait_queue); + +struct jz_i2s_controller_info +{ + int io_base; + int dma1; /* for play */ + int dma2; /* for record */ + char *name; + int dev_audio; + struct i2s_codec *i2s_codec[NR_I2S]; + int opened1; + int opened2; + unsigned char *tmp1; /* tmp buffer for sample conversions */ + unsigned char *tmp2; + spinlock_t lock; + spinlock_t ioctllock; + + wait_queue_head_t dac_wait; + wait_queue_head_t adc_wait; + int nextIn; /* byte index to next-in to DMA buffer */ + int nextOut; /* byte index to next-out from DMA buffer */ + int count; /* current byte count in DMA buffer */ + int finish; /* current transfered byte count in DMA buffer */ + unsigned total_bytes; /* total bytes written or read */ + unsigned blocks; + unsigned error; /* over/underrun */ +#ifdef CONFIG_PM + struct pm_dev *pm; +#endif +}; + + +static struct jz_i2s_controller_info *i2s_controller = NULL; +struct i2s_codec +{ + /* I2S controller connected with */ + void *private_data; + char *name; + int id; + int dev_mixer; + /* controller specific lower leverl i2s accessing routines */ + u16 (*codec_read) (u8 reg); /* the function accessing Codec REGs */ + void (*codec_write) (u8 reg, u16 val); + /* Wait for codec-ready */ + void (*codec_wait) (struct i2s_codec *codec); + /* OSS mixer masks */ + int modcnt; + int supported_mixers; + int stereo_mixers; + int record_sources; + int bit_resolution; + /* OSS mixer interface */ + int (*read_mixer) (struct i2s_codec *codec, int oss_channel); + void (*write_mixer)(struct i2s_codec *codec, int oss_channel, + unsigned int left, unsigned int right); + int (*recmask_io) (struct i2s_codec *codec, int rw, int mask); + int (*mixer_ioctl)(struct i2s_codec *codec, unsigned int cmd, unsigned long arg); + /* saved OSS mixer states */ + unsigned int mixer_state[SOUND_MIXER_NRDEVICES]; +}; + + +typedef struct buffer_queue_s +{ + int count; + int *id; + int lock; +} buffer_queue_t; + +typedef struct left_right_sample_s +{ + signed long left; + signed long right; +} left_right_sample_t; + +static unsigned long pop_turn_onoff_buf; +static unsigned long pop_turn_onoff_pbuf; + +static unsigned long *out_dma_buf = NULL; +static unsigned long *out_dma_pbuf = NULL; +static unsigned long *out_dma_buf_data_count = NULL; +static unsigned long *in_dma_buf = NULL; +static unsigned long *in_dma_pbuf = NULL; +static unsigned long *in_dma_buf_data_count = NULL; + +static buffer_queue_t out_empty_queue; +static buffer_queue_t out_full_queue; +static buffer_queue_t out_busy_queue; +static buffer_queue_t in_empty_queue; +static buffer_queue_t in_full_queue; +static buffer_queue_t in_busy_queue; +static int first_record_call = 0; + +static left_right_sample_t save_last_samples[64]; +static 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; + + printk("\n"); + + 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); + + for (cnt = 0; cnt <= 27 ; cnt++) { + printk(" ( %d : 0x%x ) ",cnt ,read_codec_file(cnt)); + } + printk("\n"); +} + +static 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); +} + +static 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 */ + + while (__icdc_rgwr_ready()); + __icdc_set_addr(addr); + val &= ~(1 << mask_bit); + if (bitval == 1) + val |= 1 << mask_bit; + + __icdc_set_cmd(val); /* write */ + mdelay(1); + __icdc_set_rgwr(); + mdelay(1); + + while (__icdc_rgwr_ready()); + __icdc_set_addr(addr); + val = __icdc_get_value(); /* read */ + + if (((val >> mask_bit) & bitval) == bitval) + return 1; + else + return 0; +} + +/* set Audio data replay */ +static void set_audio_data_replay() +{ + /* DAC path */ + write_codec_file(9, 0xff); + //write_codec_file(8, 0x30); + write_codec_file(8, 0x20); + mdelay(10); + 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(1, 1, 7);//CR1.SB_MICBIAS->1 + //mdelay(300); +} + +/* unset Audio data replay */ +static void unset_audio_data_replay(void) +{ + //write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + //mdelay(800); + //write_codec_file_bit(5, 1, 6);//SB_OUT->1 + //mdelay(800); + 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 +} + +/* set Record MIC input audio without playback */ +static void set_record_mic_input_audio_without_playback(void) +{ + /* ADC path for MIC IN */ + jz_mic_only = 1; + write_codec_file(9, 0xff); + write_codec_file(8, 0x3f); + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 + mdelay(10); + write_codec_file_bit(1, 1, 2); + //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 + + write_codec_file(22, 0x40);//mic 1 + 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 */ + jz_mic_only = 0; + 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 + write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 + write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} + +/* set Record LINE input audio without playback */ +static void set_record_line_input_audio_without_playback(void) +{ + /* ADC path for LINE IN */ + jz_mic_only = 1; + write_codec_file(9, 0xff); + write_codec_file(8, 0x3f); + mdelay(10); + write_codec_file(22, 0xf6);//line in 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, 0, 3);//PMR1.SB_LIN->0 + 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 + mdelay(10); + 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 LINE input audio without playback */ +static void unset_record_line_input_audio_without_playback(void) +{ + /* ADC path for LINE IN */ + write_codec_file_bit(5, 1, 4);//SB_ADC->1 + write_codec_file_bit(5, 1, 3);//ONR1.SB_LIN->1 + + write_codec_file(22, 0xc0);//CR3.SB_MIC1 + write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 + write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} + +/* set Playback LINE input audio direct only */ +static void set_playback_line_input_audio_direct_only() +{ + jz_audio_reset();//or init_codec() + REG_AIC_I2SCR = 0x10; + write_codec_file(9, 0xff); + write_codec_file(8, 0x3f); + mdelay(10); + write_codec_file(22, 0xf6);//line in 1 + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 + mdelay(10); + write_codec_file_bit(1, 1, 2);//CR1.HP_BYPASS->1 + write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 + write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0 + write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 + write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0 + + 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, 1, 7);//PMR1.SB_DAC->1 + //write_codec_file_bit(5, 1, 4);//PMR1.SB_ADC->1 +} + +/* unset Playback LINE input audio direct only */ +static void unset_playback_line_input_audio_direct_only() +{ + write_codec_file_bit(6, 0, 3);//GIM->0 + write_codec_file_bit(1, 0, 2);//PMR1.BYPASS->0 + write_codec_file_bit(5, 1, 3);//PMR1.SB_LINE->1 + write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 + mdelay(100); + write_codec_file_bit(5, 1, 5);//PMR1.SB_MIX->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} + +/* set Record MIC input audio with direct playback */ +static void set_record_mic_input_audio_with_direct_playback(void) +{ + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 + jz_mic_only = 0; + write_codec_file(9, 0xff); + write_codec_file(8, 0x3f); + mdelay(10); + + write_codec_file(22, 0x60);//mic 1 + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->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, 7);//CR1.SB_MICBIAS->0 + write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 + + 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 with direct playback */ +static void unset_record_mic_input_audio_with_direct_playback(void) +{ + /* ADC path for MIC IN */ + jz_mic_only = 0; + 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 + write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 + write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} + +/* set Record playing audio mixed with MIC input audio */ +static void set_record_playing_audio_mixed_with_mic_input_audio(void) +{ + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 + write_codec_file(9, 0xff); + //write_codec_file(8, 0x30); + write_codec_file(8, 0x20); + mdelay(10); + + write_codec_file(22, 0x63);//mic 1 + + write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 + write_codec_file_bit(6, 1, 3);// gain set + + 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, 7);//CR1.SB_MICBIAS->0 + write_codec_file_bit(22, 0, 7);//CR3.SB_MIC->0 + write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 + write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 + write_codec_file_bit(5, 0, 4);//PMR1.SB_MIX->0 +} + +/* unset Record playing audio mixed with MIC input audio */ +static void unset_record_playing_audio_mixed_with_mic_input_audio(void) +{ + /* ADC path */ + write_codec_file_bit(5, 1, 4);//SB_ADC->1 + write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 + //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 + write_codec_file(22, 0xc0);//CR3.SB_MIC1->1 + //write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + //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, 5);//SB_MIX->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} + +/* set Record MIC input audio with Audio data replay (full duplex) */ +static void set_record_mic_input_audio_with_audio_data_replay(void) +{ + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 + write_codec_file(9, 0xff); + //write_codec_file(8, 0x30); + write_codec_file(8, 0x20); + 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(22, 0, 7);//CR3.SB_MIC->0 + write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0 + + write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 + write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 +} + +/* unset Record MIC input audio with Audio data replay (full duplex) */ +static void unset_record_mic_input_audio_with_audio_data_replay(void) +{ + /* ADC path */ + write_codec_file_bit(5, 1, 4);//SB_ADC->1 + write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 + //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 + write_codec_file(22, 0xc0);//CR3.SB_MIC1->1 + write_codec_file_bit(5, 1, 7);//SB_DAC->1 + write_codec_file_bit(5, 1, 5);//SB_MIX->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} + +///////// +/* set Record LINE input audio with Audio data replay (full duplex for linein) */ +static void set_record_line_input_audio_with_audio_data_replay(void) +{ + write_codec_file(9, 0xff); + //write_codec_file(8, 0x30); + write_codec_file(8, 0x20); + write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 + write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0 + write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 + write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 + //write_codec_file_bit(22, 1, 7);//CR3.SB_MIC->1 + write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 + write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 + + + //jz_mic_only = 1; + write_codec_file(22, 0xc6);//line in 1 + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 + write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 + write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 +} + +/* unset Record LINE input audio with Audio data replay (full duplex for linein) */ +static void unset_record_line_input_audio_with_audio_data_replay(void) +{ + /* ADC path */ + write_codec_file_bit(5, 1, 4);//SB_ADC->1 + write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 + //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 + write_codec_file(22, 0xc0);//CR3.SB_MIC1->1 + write_codec_file_bit(5, 1, 7);//SB_DAC->1 + write_codec_file_bit(5, 1, 5);//SB_MIX->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} +///////// +static inline int get_buffer_id(struct buffer_queue_s *q) +{ + int r; + unsigned long flags; + int i; + + spin_lock_irqsave(&q->lock, flags); + if (q->count == 0) { + spin_unlock_irqrestore(&q->lock, flags); + return -1; + } + r = *(q->id + 0); + for (i=0;i < q->count-1;i++) + *(q->id + i) = *(q->id + (i+1)); + q->count --; + spin_unlock_irqrestore(&q->lock, flags); + + return r; +} + +static inline void put_buffer_id(struct buffer_queue_s *q, int id) +{ + unsigned long flags; + + spin_lock_irqsave(&q->lock, flags); + *(q->id + q->count) = id; + q->count ++; + spin_unlock_irqrestore(&q->lock, flags); +} + +static inline int elements_in_queue(struct buffer_queue_s *q) +{ + int r; + unsigned long flags; + + spin_lock_irqsave(&q->lock, flags); + r = q->count; + spin_unlock_irqrestore(&q->lock, flags); + + return r; +} + +static inline void audio_start_dma(int chan, void *dev_id, unsigned long phyaddr,int count, int mode) +{ + unsigned long flags; + struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; + + spin_lock_irqsave(&controller->ioctllock, flags); + jz_audio_dma_tran_count = count / jz_audio_b; + spin_unlock_irqrestore(&controller->ioctllock, flags); + flags = claim_dma_lock(); + disable_dma(chan); + clear_dma_ff(chan); + //set_dma_mode(chan, mode); + jz_set_oss_dma(chan, mode, jz_audio_format); + set_dma_addr(chan, phyaddr); + if (count == 0) { + count++; + printk("JzSOC DMA controller can't set dma 0 count!\n"); + } + set_dma_count(chan, count); + enable_dma(chan); + release_dma_lock(flags); +} + +static irqreturn_t jz_i2s_record_dma_irq (int irq, void *dev_id) +{ + int id1, id2; + unsigned long flags; + struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; + int dma = controller->dma2; + + disable_dma(dma); + if (__dmac_channel_address_error_detected(dma)) { + printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); + __dmac_channel_clear_address_error(dma); + } + if (__dmac_channel_transmit_end_detected(dma)) { + __dmac_channel_clear_transmit_end(dma); + + if(drain_flag == 1) + wake_up(&drain_wait_queue); + /* for DSP_GETIPTR */ + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes += jz_audio_dma_tran_count; + controller->blocks ++; + spin_unlock_irqrestore(&controller->ioctllock, flags); + id1 = get_buffer_id(&in_busy_queue); + put_buffer_id(&in_full_queue, id1); + + wake_up(&rx_wait_queue); + wake_up(&controller->adc_wait); + if ((id2 = get_buffer_id(&in_empty_queue)) >= 0) { + put_buffer_id(&in_busy_queue, id2); + *(in_dma_buf_data_count + id2) = *(in_dma_buf_data_count + id1); + dma_cache_wback_inv(*(in_dma_buf + id2), *(in_dma_buf_data_count + id2)); + audio_start_dma(dma,dev_id, + *(in_dma_pbuf + id2), + *(in_dma_buf_data_count + id2), + DMA_MODE_READ); + } else + in_busy_queue.count = 0; + } + + return IRQ_HANDLED; +} + +static irqreturn_t jz_i2s_replay_dma_irq (int irq, void *dev_id) +{ + int id; + unsigned long flags; + struct jz_i2s_controller_info * controller = (struct jz_i2s_controller_info *) dev_id; + int dma = controller->dma1; + + disable_dma(dma); + if (__dmac_channel_address_error_detected(dma)) { + printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); + __dmac_channel_clear_address_error(dma); + } + if (__dmac_channel_transmit_end_detected(dma)) { + __dmac_channel_clear_transmit_end(dma); + + if(pop_dma_flag == 1) { + pop_dma_flag = 0; + wake_up(&pop_wait_queue); + } else { + if(drain_flag == 1) { + /* Is replay dma buffer over ? */ + if(elements_in_queue(&out_full_queue) <= 0) { + drain_flag = 0; + wake_up(&drain_wait_queue); + } + } + + /* for DSP_GETOPTR */ + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes += jz_audio_dma_tran_count; + controller->blocks ++; + spin_unlock_irqrestore(&controller->ioctllock, flags); + if ((id = get_buffer_id(&out_busy_queue)) < 0) + printk(KERN_DEBUG "Strange DMA finish interrupt for I2S module\n"); + put_buffer_id(&out_empty_queue, id); + if ((id = get_buffer_id(&out_full_queue)) >= 0) { + put_buffer_id(&out_busy_queue, id); + if(*(out_dma_buf_data_count + id) > 0) { + audio_start_dma(dma, dev_id, *(out_dma_pbuf + id), + *(out_dma_buf_data_count + id), + DMA_MODE_WRITE); + last_dma_buffer_id = id; + } + } else + out_busy_queue.count = 0; + + if (elements_in_queue(&out_empty_queue) > 0) { + wake_up(&tx_wait_queue); + wake_up(&controller->dac_wait); + } + } + } + + return IRQ_HANDLED; +} + +static void jz_i2s_initHw(int set) +{ +#if defined(CONFIG_MIPS_JZ_URANUS) + i2s_clk = 48000000; +#else + i2s_clk = __cpm_get_i2sclk(); +#endif + __i2s_disable(); + if(set) + __i2s_reset(); + schedule_timeout(5); + if(each_time_init_codec) + each_time_init_codec(); + __i2s_disable_record(); + __i2s_disable_replay(); + __i2s_disable_loopback(); + __i2s_set_transmit_trigger(4); + __i2s_set_receive_trigger(3); +} + +static int Init_In_Out_queue(int fragstotal,int fragsize) +{ + int i; + + /* recording */ + in_empty_queue.count = fragstotal; + in_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!in_dma_buf) + goto all_mem_err; + in_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!in_dma_pbuf) + goto all_mem_err; + in_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!in_dma_buf_data_count) + goto all_mem_err; + in_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!in_empty_queue.id) + goto all_mem_err; + in_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!in_full_queue.id) + goto all_mem_err; + in_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!in_busy_queue.id) + goto all_mem_err; + + for (i=0;i < fragstotal;i++) + *(in_empty_queue.id + i) = i; + in_full_queue.count = 0; + in_busy_queue.count = 0; + + for (i = 0; i < fragstotal; i++) { + *(in_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); + if (*(in_dma_buf + i) == 0) + goto mem_failed_in; + *(in_dma_pbuf + i) = virt_to_phys((void *)(*(in_dma_buf + i))); + dma_cache_wback_inv(*(in_dma_buf + i), fragsize); + } + + /* playing */ + out_empty_queue.count = fragstotal; + out_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!out_dma_buf) + goto all_mem_err; + out_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!out_dma_pbuf) + goto all_mem_err; + out_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + + if (!out_dma_buf_data_count) + goto all_mem_err; + out_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!out_empty_queue.id) + goto all_mem_err; + out_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!out_full_queue.id) + goto all_mem_err; + out_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!out_busy_queue.id) + goto all_mem_err; + for (i=0;i < fragstotal;i++) + *(out_empty_queue.id + i) = i; + + out_busy_queue.count = 0; + out_full_queue.count = 0; + /* alloc DMA buffer */ + for (i = 0; i < fragstotal; i++) { + *(out_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); + if (*(out_dma_buf + i) == 0) { + printk(" can't allocate required DMA(OUT) buffers.\n"); + goto mem_failed_out; + } + *(out_dma_pbuf + i) = virt_to_phys((void *)(*(out_dma_buf + i))); + } + + return 1; +all_mem_err: + printk("error:allocate memory occur error 1!\n"); + return 0; +mem_failed_out: + printk("error:allocate memory occur error 2!\n"); + for (i = 0; i < fragstotal; i++) { + if(*(out_dma_buf + i)) + free_pages(*(out_dma_buf + i), get_order(fragsize)); + } + + return 0; +mem_failed_in: + printk("error:allocate memory occur error 3!\n"); + for (i = 0; i < fragstotal; i++) { + if(*(in_dma_buf + i)) + free_pages(*(in_dma_buf + i), get_order(fragsize)); + } + return 0; +} + +static int Free_In_Out_queue(int fragstotal,int fragsize) +{ + int i; + /* playing */ + if(out_dma_buf != NULL) { + for (i = 0; i < fragstotal; i++) { + if(*(out_dma_buf + i)) + free_pages(*(out_dma_buf + i), get_order(fragsize)); + *(out_dma_buf + i) = 0; + } + kfree(out_dma_buf); + out_dma_buf = NULL; + } + if(out_dma_pbuf) { + kfree(out_dma_pbuf); + out_dma_pbuf = NULL; + } + if(out_dma_buf_data_count) { + kfree(out_dma_buf_data_count); + out_dma_buf_data_count = NULL; + } + if(out_empty_queue.id) { + kfree(out_empty_queue.id); + out_empty_queue.id = NULL; + } + if(out_full_queue.id) { + kfree(out_full_queue.id); + out_full_queue.id = NULL; + } + if(out_busy_queue.id) { + kfree(out_busy_queue.id); + out_busy_queue.id = NULL; + } + out_empty_queue.count = fragstotal; + out_busy_queue.count = 0; + out_full_queue.count = 0; + + /* recording */ + if(in_dma_buf) { + for (i = 0; i < fragstotal; i++) { + if(*(in_dma_buf + i)) { + dma_cache_wback_inv(*(in_dma_buf + i), fragsize); + free_pages(*(in_dma_buf + i), get_order(fragsize)); + } + *(in_dma_buf + i) = 0; + } + kfree(in_dma_buf); + in_dma_buf = NULL; + } + if(in_dma_pbuf) { + kfree(in_dma_pbuf); + in_dma_pbuf = NULL; + } + if(in_dma_buf_data_count) { + kfree(in_dma_buf_data_count); + in_dma_buf_data_count = NULL; + } + if(in_empty_queue.id) { + kfree(in_empty_queue.id); + in_empty_queue.id = NULL; + } + if(in_full_queue.id) { + kfree(in_full_queue.id); + in_full_queue.id = NULL; + } + if(in_busy_queue.id) { + kfree(in_busy_queue.id); + in_busy_queue.id = NULL; + } + + in_empty_queue.count = fragstotal; + in_full_queue.count = 0; + in_busy_queue.count = 0; + + return 1; +} + +static void jz_i2s_full_reset(struct jz_i2s_controller_info *controller) +{ + jz_i2s_initHw(0); +} + +static int jz_audio_set_speed(int dev, int rate) +{ + /* 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 99999999 ? */ + jz_audio_speed = rate; + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + jz_audio_rate = rate; + + if(set_codec_speed) + set_codec_speed(rate); + + return jz_audio_rate; +} + + +static int record_fill_1x8_u(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + unsigned long data; + volatile unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); + volatile unsigned char *dp = (unsigned char*)dst_start; + + while (count > 0) { + count -= 2; /* count in dword */ + cnt++; + data = *(s++); + *(dp ++) = ((data << 16) >> 24) + 0x80; + s++; /* skip the other channel */ + } + + return cnt; +} + + +static int record_fill_2x8_u(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + unsigned long d1, d2; + volatile unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); + volatile unsigned char *dp = (unsigned char*)dst_start; + + while (count > 0) { + count -= 2; + cnt += 2; + d1 = *(s++); + *(dp ++) = ((d1 << 16) >> 24) + 0x80; + d2 = *(s++); + *(dp ++) = ((d2 << 16) >> 24) + 0x80; + } + + return cnt; +} + + +static int record_fill_1x16_s(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + unsigned long d1; + unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); + unsigned short *dp = (unsigned short *)dst_start; + + while (count > 0) { + count -= 2; /* count in dword */ + cnt += 2; /* count in byte */ + d1 = *(s++); + *(dp ++) = (d1 << 16) >> 16; + s++; /* skip the other channel */ + } + + return cnt; +} + + +static int record_fill_2x16_s(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + unsigned long d1, d2; + unsigned long *s = (unsigned long*)(*(in_dma_buf + id)); + unsigned short *dp = (unsigned short *)dst_start; + while (count > 0) { + count -= 2; /* count in dword */ + cnt += 4; /* count in byte */ + d1 = *(s++); + d2 = *(s++); + if(abnormal_data_count > 0) { + d1 = d2 = 0; + abnormal_data_count --; + } + *(dp ++) = (d1 << 16) >> 16; + *(dp ++) = (d2 << 16) >> 16; + } + + return cnt; +} + +static void replay_fill_1x8_u(signed long src_start, int count, int id) +{ + int cnt = 0; + unsigned char data; + unsigned long ddata; + volatile unsigned char *s = (unsigned char *)src_start; + volatile unsigned long *dp = (unsigned long*)(*(out_dma_buf + id)); + + while (count > 0) { + count--; + cnt += 1; + data = *(s++) - 0x80; + ddata = (unsigned long) data << 8; + *(dp ++) = ddata; + *(dp ++) = ddata; + + /* save last left and right */ + if(count == 1) { + save_last_samples[id].left = ddata; + save_last_samples[id].right = ddata; + } + } + cnt = cnt * 2 * jz_audio_b; + *(out_dma_buf_data_count + id) = cnt; +} + + +static void replay_fill_2x8_u(signed long src_start, int count, int id) +{ + int cnt = 0; + unsigned char d1; + unsigned long dd1; + volatile unsigned char *s = (unsigned char *)src_start; + volatile unsigned long *dp = (unsigned long*)(*(out_dma_buf + id)); + + while (count > 0) { + count -= 1; + cnt += 1 ; + d1 = *(s++) - 0x80; + dd1 = (unsigned long) d1 << 8; + *(dp ++) = dd1; + /* save last left */ + if(count == 2) + save_last_samples[id].left = dd1; + /* save last right */ + if(count == 1) + save_last_samples[id].right = dd1; + } + cnt *= jz_audio_b; + *(out_dma_buf_data_count + id) = cnt; +} + + +static void replay_fill_1x16_s(signed long src_start, int count, int id) +{ + int cnt = 0; + signed short d1; + signed long l1; + volatile signed short *s = (signed short *)src_start; + volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); + + while (count > 0) { + count -= 2; + cnt += 2 ; + d1 = *(s++); + l1 = (signed long)d1; + *(dp ++) = l1; + *(dp ++) = l1; + + /* save last left and right */ + if(count == 1) { + save_last_samples[id].left = l1; + save_last_samples[id].right = l1; + } + } + cnt = cnt * 2 * jz_audio_b; + *(out_dma_buf_data_count + id) = cnt; +} + +#if 0 +static void replay_fill_2x16_s(signed long src_start, int count, int id) +{ + int cnt = 0; + signed short d1; + signed long l1; + int mute_cnt = 0; + signed long tmp1,tmp2; + volatile signed short *s = (signed short *)src_start; + volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); +#if defined(CONFIG_I2S_ICDC) + volatile signed long *before_dp; + int sam_rate = jz_audio_rate / 20; + + tmp1 = tmp2 = 0; + while (count > 0) { + count -= 2; + cnt += 2; + d1 = *(s++); + + l1 = (signed long)d1; + l1 >>= codec_volue_shift; + + if(l1 == 0) { + mute_cnt ++; + if(mute_cnt >= sam_rate) { + before_dp = dp - 10; + *(before_dp) = (signed long)1; + before_dp = dp - 11; + *(before_dp) = (signed long)1; + mute_cnt = 0; + } + } else + mute_cnt = 0; + + *(dp ++) = l1; + + tmp1 = tmp2; + tmp2 = l1; + } + + /* save last left */ + save_last_samples[id].left = tmp1; + /* save last right */ + save_last_samples[id].right = tmp2; +#endif +#if defined(CONFIG_I2S_DLV) + while (count > 0) { + count -= 2; + cnt += 2; + d1 = *(s++); + + l1 = (signed long)d1; + + *(dp ++) = l1; + } +#endif + cnt *= jz_audio_b; + *(out_dma_buf_data_count + id) = cnt; +} +#else +static void replay_fill_2x16_s(signed long src_start, int count, int id) +{ + int cnt = 0; + signed short d1; + signed long l1; + int mute_cnt = 0; + signed long tmp1,tmp2; + +#if 0 + volatile signed short *s = (signed short *)src_start; + volatile signed short *dp = (signed short*)(*(out_dma_buf + id)); + memcpy((char*)dp, (char*)s, count); + *(out_dma_buf_data_count + id) = count; +#else + volatile signed short *s = (signed short *)src_start; + volatile signed long *dp = (signed long*)(*(out_dma_buf + id)); + while (count > 0) { + count -= 2; + cnt += 2; + d1 = *(s++); + + l1 = (signed long)d1; + + *(dp ++) = l1; + } + cnt *= jz_audio_b; + *(out_dma_buf_data_count + id) = cnt; +#endif +} +#endif + + +static unsigned int jz_audio_set_format(int dev, unsigned int fmt) +{ + switch (fmt) { + case AFMT_U8: + __i2s_set_oss_sample_size(8); + __i2s_set_iss_sample_size(8); + jz_audio_format = fmt; + jz_update_filler(jz_audio_format, jz_audio_channels); + break; + case AFMT_S16_LE: +#if defined(CONFIG_I2S_DLV) + /* DAC path and ADC path */ + write_codec_file(2, 0x00); + //write_codec_file(2, 0x60); +#endif + jz_audio_format = fmt; + jz_update_filler(jz_audio_format,jz_audio_channels); + /* print all files */ + __i2s_set_oss_sample_size(16); + __i2s_set_iss_sample_size(16); + break; + + case AFMT_QUERY: + break; + } + + return jz_audio_format; +} + + +static short jz_audio_set_channels(int dev, short channels) +{ + switch (channels) { + case 1: + if(set_codec_some_func) + set_codec_some_func(); + jz_audio_channels = channels; + jz_update_filler(jz_audio_format, jz_audio_channels); +#if defined(CONFIG_I2S_DLV) + write_codec_file_bit(1, 1, 6);//CR1.MONO->1 for Mono +#endif + break; + case 2: + jz_audio_channels = channels; + jz_update_filler(jz_audio_format, jz_audio_channels); +#if defined(CONFIG_I2S_DLV) + write_codec_file_bit(1, 0, 6);//CR1.MONO->0 for Stereo +#endif + break; + case 0: + break; + } + + return jz_audio_channels; +} + +static void init_codec(void) +{ + /* inititalize internal I2S codec */ + if(init_codec_pin) + init_codec_pin(); + +#if defined(CONFIG_I2S_ICDC) + /* initialize AIC but not reset it */ + jz_i2s_initHw(0); +#endif + if(reset_codec) + reset_codec(); +} + +static void jz_audio_reset(void) +{ + __i2s_disable_replay(); + __i2s_disable_receive_dma(); + __i2s_disable_record(); + __i2s_disable_transmit_dma(); +#if defined(CONFIG_I2S_DLV) + REG_AIC_I2SCR = 0x10; +#endif + init_codec(); +} + +static int jz_audio_release(struct inode *inode, struct file *file); +static int jz_audio_open(struct inode *inode, struct file *file); +static int jz_audio_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg); +static unsigned int jz_audio_poll(struct file *file,struct poll_table_struct *wait); +static ssize_t jz_audio_write(struct file *file, const char *buffer,size_t count, loff_t *ppos); +static ssize_t jz_audio_read(struct file *file, char *buffer,size_t count, loff_t *ppos); + +/* static struct file_operations jz_i2s_audio_fops */ +static struct file_operations jz_i2s_audio_fops = +{ + owner: THIS_MODULE, + open: jz_audio_open, + release: jz_audio_release, + write: jz_audio_write, + read: jz_audio_read, + poll: jz_audio_poll, + ioctl: jz_audio_ioctl +}; + +static int jz_i2s_open_mixdev(struct inode *inode, struct file *file) +{ + int i; + int minor = MINOR(inode->i_rdev); + struct jz_i2s_controller_info *controller = i2s_controller; + + for (i = 0; i < NR_I2S; i++) + if (controller->i2s_codec[i] != NULL && controller->i2s_codec[i]->dev_mixer == minor) + goto match; + + if (!controller) + return -ENODEV; +match: + file->private_data = controller->i2s_codec[i]; + + return 0; +} + +static int jz_i2s_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct i2s_codec *codec = (struct i2s_codec *)file->private_data; + return codec->mixer_ioctl(codec, cmd, arg); +} + +static loff_t jz_i2s_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +static struct file_operations jz_i2s_mixer_fops = +{ + owner: THIS_MODULE, + llseek: jz_i2s_llseek, + ioctl: jz_i2s_ioctl_mixdev, + open: jz_i2s_open_mixdev, +}; + +static int i2s_mixer_ioctl(struct i2s_codec *codec, unsigned int cmd, unsigned long arg) +{ + int ret; + long val = 0; + switch (cmd) { + case SOUND_MIXER_INFO: + + if(codec_mixer_info_id_name) + codec_mixer_info_id_name(); + info.modify_counter = audio_mix_modcnt; + + return copy_to_user((void *)arg, &info, sizeof(info)); + case SOUND_OLD_MIXER_INFO: + + if(codec_mixer_old_info_id_name) + codec_mixer_old_info_id_name(); + + return copy_to_user((void *)arg, &old_info, sizeof(info)); + case SOUND_MIXER_READ_STEREODEVS: + + return put_user(0, (long *) arg); + case SOUND_MIXER_READ_CAPS: + + val = SOUND_CAP_EXCL_INPUT; + return put_user(val, (long *) arg); + case SOUND_MIXER_READ_DEVMASK: + break; + case SOUND_MIXER_READ_RECMASK: + break; + case SOUND_MIXER_READ_RECSRC: + break; + case SOUND_MIXER_WRITE_SPEAKER: + + ret = get_user(val, (long *) arg); + if (ret) + return ret; + val = val & 0xff; + if(val < 0) + val = 0; + if(val > 100) + val = 100; + switch(val) { + case 100: + if(set_codec_direct_mode) + set_codec_direct_mode(); + break; + case 0: + if(clear_codec_direct_mode) + clear_codec_direct_mode(); + break; + } + break; + case SOUND_MIXER_WRITE_BASS: + + ret = get_user(val, (long *) arg); + if (ret) + return ret; + + val = val & 0xff; + if(val < 0) + val = 0; + if(val > 100) + val = 100; + codec_bass_gain = val; + if(set_codec_bass) + set_codec_bass(val); + + return 0; + case SOUND_MIXER_READ_BASS: + + val = codec_bass_gain; + ret = val << 8; + val = val | ret; + + return put_user(val, (long *) arg); + case SOUND_MIXER_WRITE_VOLUME: + ret = get_user(val, (long *) arg); + if (ret) + return ret; + val = val & 0xff; + if(val < 0) + val = 0; + if(val > 100) + val = 100; + if (val > 31) + val = 31; + jz_audio_volume = val; + + if(set_codec_volume) + set_codec_volume(val); + return 0; + case SOUND_MIXER_READ_VOLUME: + + val = jz_audio_volume; + ret = val << 8; + val = val | ret; + + return put_user(val, (long *) arg); + case SOUND_MIXER_WRITE_MIC: + + ret = get_user(val, (long *) arg); + if (ret) + return ret; + + val = val & 0xff; + if(val < 0) + val = 0; + if(val > 100) + val = 100; + codec_mic_gain = val; + if(set_codec_mic) + set_codec_mic(val); + + return 0; + case SOUND_MIXER_READ_MIC: + + val = codec_mic_gain; + ret = val << 8; + val = val | ret; + + return put_user(val, (long *) arg); + default: + return -ENOSYS; + } + audio_mix_modcnt ++; + return 0; +} + + +int i2s_probe_codec(struct i2s_codec *codec) +{ + /* generic OSS to I2S wrapper */ + codec->mixer_ioctl = i2s_mixer_ioctl; + return 1; +} + + +/* I2S codec initialisation. */ +static int __init jz_i2s_codec_init(struct jz_i2s_controller_info *controller) +{ + int num_i2s = 0; + struct i2s_codec *codec; + + for (num_i2s = 0; num_i2s < NR_I2S; num_i2s++) { + if ((codec = kmalloc(sizeof(struct i2s_codec),GFP_KERNEL)) == NULL) + return -ENOMEM; + memset(codec, 0, sizeof(struct i2s_codec)); + codec->private_data = controller; + codec->id = num_i2s; + + if (i2s_probe_codec(codec) == 0) + break; + if ((codec->dev_mixer = register_sound_mixer(&jz_i2s_mixer_fops, -1)) < 0) { + printk(KERN_ERR "Jz I2S: couldn't register mixer!\n"); + kfree(codec); + break; + } + controller->i2s_codec[num_i2s] = codec; + } + return num_i2s; +} + + +static void jz_update_filler(int format, int channels) +{ +#define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3)) + + switch (TYPE(format, channels)) + { + + case TYPE(AFMT_U8, 1): + jz_audio_b = 4; /* 4bytes * 8bits =32bits */ + replay_filler = replay_fill_1x8_u; + record_filler = record_fill_1x8_u; + break; + case TYPE(AFMT_U8, 2): + jz_audio_b = 4; + replay_filler = replay_fill_2x8_u; + record_filler = record_fill_2x8_u; + break; + case TYPE(AFMT_S16_LE, 1): + jz_audio_b = 2; /* 2bytes * 16bits =32bits */ + replay_filler = replay_fill_1x16_s; + record_filler = record_fill_1x16_s; + break; + case TYPE(AFMT_S16_LE, 2): + jz_audio_b = 2; + replay_filler = replay_fill_2x16_s; + record_filler = record_fill_2x16_s; + break; + default: + ; + } +} + + +#ifdef CONFIG_PROC_FS +extern struct proc_dir_entry *proc_jz_root; +int i2s_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + return 0; +} + +static int jz_i2s_init_proc(struct jz_i2s_controller_info *controller) +{ + if (!create_proc_read_entry ("i2s", 0, proc_jz_root, i2s_read_proc, controller->i2s_codec[0])) + return -EIO; + return 0; +} + +static void jz_i2s_cleanup_proc(struct jz_i2s_controller_info *controller) +{ +} +#endif + +static void __init attach_jz_i2s(struct jz_i2s_controller_info *controller) +{ + char *name; + int adev; /* No of Audio device. */ + + name = controller->name; + /* initialize AIC controller and reset it */ + jz_i2s_initHw(1); + adev = register_sound_dsp(&jz_i2s_audio_fops, -1); + if (adev < 0) + goto audio_failed; + /* initialize I2S codec and register /dev/mixer */ + if (jz_i2s_codec_init(controller) <= 0) + goto mixer_failed; + +#ifdef CONFIG_PROC_FS + if (jz_i2s_init_proc(controller) < 0) { + printk(KERN_ERR "%s: can't create I2S proc filesystem.\n", name); + goto proc_failed; + } +#endif + + controller->tmp1 = (void *)__get_free_pages(GFP_KERNEL, 8); + if (!controller->tmp1) { + printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name); + goto tmp1_failed; + } + controller->tmp2 = (void *)__get_free_pages(GFP_KERNEL, 8); + if (!controller->tmp2) { + printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name); + goto tmp2_failed; + } + if ((controller->dma2 = jz_request_dma(DMA_ID_I2S_RX, "audio adc", jz_i2s_record_dma_irq, IRQF_DISABLED, controller)) < 0) { + printk(KERN_ERR "%s: can't reqeust DMA ADC channel.\n", name); + goto dma2_failed; + } + if ((controller->dma1 = jz_request_dma(DMA_ID_I2S_TX, "audio dac", jz_i2s_replay_dma_irq, IRQF_DISABLED, controller)) < 0) { + printk(KERN_ERR "%s: can't reqeust DMA DAC channel.\n", name); + goto dma1_failed; + } + printk("JzSOC On-Chip I2S controller registered (DAC: DMA(play):%d/IRQ%d,\n ADC: DMA(record):%d/IRQ%d)\n", controller->dma1, get_dma_done_irq(controller->dma1), controller->dma2, get_dma_done_irq(controller->dma2)); + + controller->dev_audio = adev; + pop_turn_onoff_buf = __get_free_pages(GFP_KERNEL | GFP_DMA, 8); + if(!pop_turn_onoff_buf) + printk("pop_turn_onoff_buf alloc is wrong!\n"); + pop_turn_onoff_pbuf = virt_to_phys((void *)pop_turn_onoff_buf); + + return; +dma2_failed: + jz_free_dma(controller->dma1); +dma1_failed: + free_pages((unsigned long)controller->tmp2, 8); +tmp2_failed: + free_pages((unsigned long)controller->tmp1, 8); +tmp1_failed: + +#ifdef CONFIG_PROC_FS + jz_i2s_cleanup_proc(controller); +#endif +proc_failed: + /* unregister mixer dev */ +mixer_failed: + unregister_sound_dsp(adev); +audio_failed: + return; +} + +static int __init probe_jz_i2s(struct jz_i2s_controller_info **controller) +{ + if ((*controller = kmalloc(sizeof(struct jz_i2s_controller_info), + GFP_KERNEL)) == NULL) { + printk(KERN_ERR "Jz I2S Controller: out of memory.\n"); + return -ENOMEM; + } + (*controller)->name = "Jz I2S controller"; + (*controller)->opened1 = 0; + (*controller)->opened2 = 0; + init_waitqueue_head(&(*controller)->adc_wait); + init_waitqueue_head(&(*controller)->dac_wait); + spin_lock_init(&(*controller)->lock); + init_waitqueue_head(&rx_wait_queue); + init_waitqueue_head(&tx_wait_queue); + init_waitqueue_head(&pop_wait_queue); + init_waitqueue_head(&drain_wait_queue); + + return 0; +} + +static void __exit unload_jz_i2s(struct jz_i2s_controller_info *controller) +{ + int adev = controller->dev_audio; + + jz_i2s_full_reset(controller); + controller->dev_audio = -1; + if (old_mksound) + kd_mksound = old_mksound;/* Our driver support bell for kb, see vt.c */ + +#ifdef CONFIG_PROC_FS + jz_i2s_cleanup_proc(controller); +#endif + + jz_free_dma(controller->dma1); + jz_free_dma(controller->dma2); + free_pages((unsigned long)controller->tmp1, 8); + free_pages((unsigned long)controller->tmp2, 8); + free_pages((unsigned long)pop_turn_onoff_buf, 8); + + if (adev >= 0) { + /* unregister_sound_mixer(audio_devs[adev]->mixer_dev); */ + unregister_sound_dsp(controller->dev_audio); + } +} + +#ifdef CONFIG_PM +static int jz_i2s_suspend(struct jz_i2s_controller_info *controller, int state) +{ + if(i2s_suspend_codec) + i2s_suspend_codec(controller->opened1,controller->opened2); + printk("Aic and codec are suspended!\n"); + return 0; +} + +static int jz_i2s_resume(struct jz_i2s_controller_info *controller) +{ + if(i2s_resume_codec) + i2s_resume_codec(); + +#if defined(CONFIG_I2S_AK4642EN) + jz_i2s_initHw(0); + jz_audio_reset(); + __i2s_enable(); + jz_audio_set_speed(controller->dev_audio,jz_audio_speed); + /* playing */ + if(controller->opened1) { + if(set_codec_replay) + set_codec_replay(); + int dma = controller->dma1; + int id; + unsigned long flags; + disable_dma(dma); + if(__dmac_channel_address_error_detected(dma)) { + printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); + __dmac_channel_clear_address_error(dma); + } + if(__dmac_channel_transmit_end_detected(dma)) + __dmac_channel_clear_transmit_end(dma); + + /* for DSP_GETOPTR */ + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes += jz_audio_dma_tran_count; + controller->blocks ++; + spin_unlock_irqrestore(&controller->ioctllock, flags); + while((id = get_buffer_id(&out_busy_queue)) >= 0) + put_buffer_id(&out_empty_queue, id); + + out_busy_queue.count=0; + if((id = get_buffer_id(&out_full_queue)) >= 0) { + put_buffer_id(&out_empty_queue, id); + } + if (elements_in_queue(&out_empty_queue) > 0) { + wake_up(&tx_wait_queue); + wake_up(&controller->dac_wait); + } else + printk("pm out_empty_queue empty"); + } + + /* recording */ + if(controller->opened2) { + if(set_codec_record) + set_codec_record(); + int dma = controller->dma2; + int id1, id2; + unsigned long flags; + disable_dma(dma); + if (__dmac_channel_address_error_detected(dma)) { + printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); + __dmac_channel_clear_address_error(dma); + } + if (__dmac_channel_transmit_end_detected(dma)) { + __dmac_channel_clear_transmit_end(dma); + } + /* for DSP_GETIPTR */ + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes += jz_audio_dma_tran_count; + controller->blocks ++; + spin_unlock_irqrestore(&controller->ioctllock, flags); + id1 = get_buffer_id(&in_busy_queue); + put_buffer_id(&in_full_queue, id1); + wake_up(&rx_wait_queue); + wake_up(&controller->adc_wait); + if ((id2 = get_buffer_id(&in_empty_queue)) >= 0) { + put_buffer_id(&in_full_queue, id2); + } + in_busy_queue.count = 0; + } +#endif + + return 0; +} + +static int jz_i2s_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data) +{ + int ret; + struct jz_i2s_controller_info *controller = pm_dev->data; + + if (!controller) return -EINVAL; + + switch (req) { + case PM_SUSPEND: + ret = jz_i2s_suspend(controller, (int)data); + break; + case PM_RESUME: + ret = jz_i2s_resume(controller); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} +#endif /* CONFIG_PM */ +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) { + + 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; +} + +static int __init init_jz_i2s(void) +{ + int errno, retval; +#if defined(CONFIG_I2S_DLV) + + ramp_up_start = 0; + ramp_up_end = 0; + gain_up_start = 0; + gain_up_end = 0; + ramp_down_start = 0; + ramp_down_end = 0; + gain_down_start = 0; + gain_down_end = 0; +#endif + + abnormal_data_count = 0; + if(set_codec_mode) + set_codec_mode(); + + drain_flag = 0; + if ((errno = probe_jz_i2s(&i2s_controller)) < 0) + return errno; + if(set_codec_gpio_pin) + set_codec_gpio_pin(); + + attach_jz_i2s(i2s_controller); + if(set_codec_startup_param) + set_codec_startup_param(); +#if defined(CONFIG_I2S_DLV) + jz_codec_config = 0; + 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; + } +#endif + if(set_codec_volume_table) + set_codec_volume_table(); + + out_empty_queue.id = NULL; + out_full_queue.id = NULL; + out_busy_queue.id = NULL; + in_empty_queue.id = NULL; + in_full_queue.id = NULL; + in_busy_queue.id = NULL; + + jz_audio_fragsize = JZCODEC_RW_BUFFER_SIZE * PAGE_SIZE; + jz_audio_fragstotal = JZCODEC_RW_BUFFER_TOTAL ; + Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); + +#ifdef CONFIG_PM + i2s_controller->pm = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, + jz_i2s_pm_callback); + if (i2s_controller->pm) + i2s_controller->pm->data = i2s_controller; +#endif + +#if defined(CONFIG_I2S_DLV) + __cpm_start_idct(); + __cpm_start_db(); + __cpm_start_me(); + __cpm_start_mc(); + __cpm_start_ipu(); +#endif + + printk("JZ I2S OSS audio driver initialized\n"); + + return 0; +} + +static void __exit cleanup_jz_i2s(void) +{ +#ifdef CONFIG_PM + /* pm_unregister(i2s_controller->pm); */ +#endif +#if defined(CONFIG_I2S_DLV) + free_irq(IRQ_AIC, NULL); +#endif + unload_jz_i2s(i2s_controller); + Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); + if(clear_codec_mode) + clear_codec_mode(); +} + +module_init(init_jz_i2s); +module_exit(cleanup_jz_i2s); + +#if defined(CONFIG_SOC_JZ4730) +static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count,con; + + if(elements_in_queue(&in_busy_queue) > 0) { + if (nonblock) + return -EBUSY; + drain_flag = 1; + sleep_on(&drain_wait_queue); + drain_flag = 0; + } else { + add_wait_queue(&ctrl->adc_wait, &wait); + for (con = 0; con < 1000; con ++) { + udelay(1); + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&ctrl->lock, flags); + count = get_dma_residue(ctrl->dma2); + spin_unlock_irqrestore(&ctrl->lock, flags); + if (count <= 0) + break; + if (nonblock) { + remove_wait_queue(&ctrl->adc_wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + } + remove_wait_queue(&ctrl->adc_wait, &wait); + current->state = TASK_RUNNING; + } + return 0; +} + +static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count; + + if(elements_in_queue(&out_full_queue) > 0) { + if (nonblock) + return -EBUSY; + + drain_flag = 1; + sleep_on(&drain_wait_queue); + drain_flag = 0; + } else { + add_wait_queue(&(ctrl->dac_wait), &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if(elements_in_queue(&out_full_queue) <= 0) { + spin_lock_irqsave(&ctrl->lock, flags); + count = get_dma_residue(ctrl->dma1); + spin_unlock_irqrestore(&ctrl->lock, flags); + if(count <= 0) + break; + } + if (nonblock) { + remove_wait_queue(&ctrl->dac_wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + } + remove_wait_queue(&ctrl->dac_wait, &wait); + current->state = TASK_RUNNING; + } + + return 0; +} +#endif + +#if defined(CONFIG_SOC_JZ4750) +static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock) +{ + //DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count,i=0; + + //add_wait_queue(&ctrl->adc_wait, &wait); + for (;;) { + if (i < MAXDELAY) { + udelay(10); + i++; + } else + break; + //set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&ctrl->lock, flags); + //spin_lock(&ctrl->lock); + count = get_dma_residue(ctrl->dma2); + spin_unlock_irqrestore(&ctrl->lock, flags); + //spin_unlock(&ctrl->lock); + if (count <= 0) + break; + + /*if (signal_pending(current)) + break;*/ + if (nonblock) { + //remove_wait_queue(&ctrl->adc_wait, &wait); + //current->state = TASK_RUNNING; + return -EBUSY; + } + } + //remove_wait_queue(&ctrl->adc_wait, &wait); + //current->state = TASK_RUNNING; + /*if (signal_pending(current)) + return -ERESTARTSYS;*/ + return 0; +} +static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock) +{ + unsigned long flags; + int count,ele,busyele,emptyele,i=0; + + for (;;) { + if(!nonblock) {//blocked + if (i < MAXDELAY) { + udelay(10); + i++; + } else + break; + + ele = elements_in_queue(&out_full_queue); + if(ele <= 0) { + udelay(200); + + busyele = elements_in_queue(&out_busy_queue); + emptyele = elements_in_queue(&out_empty_queue); + if (busyele <= 0 && emptyele >= jz_audio_fragstotal) { + spin_lock_irqsave(&ctrl->lock, flags); + count = get_dma_residue(ctrl->dma1); + spin_unlock_irqrestore(&ctrl->lock, flags); + if (count <= 0) + break; + } + } + } else {//non-blocked + //mdelay(100); + ele = elements_in_queue(&out_full_queue); + + if(ele <= 0) { + //mdelay(100); + busyele = elements_in_queue(&out_busy_queue); + emptyele = elements_in_queue(&out_empty_queue); + + if (busyele <= 0 && emptyele >= jz_audio_fragstotal) { + spin_lock_irqsave(&ctrl->lock, flags); + count = get_dma_residue(ctrl->dma1); + spin_unlock_irqrestore(&ctrl->lock, flags); + if (count <= 0) + break; + } + } + } + } + + return 0; +} +#endif + +#if defined(CONFIG_SOC_JZ4740) +#define MAXDELAY 50000 +static int drain_dac(struct jz_i2s_controller_info *ctrl, int nonblock) +{ + int count,ele,i=0; + + for (;;) { + if(!nonblock) {//blocked + if ( i < MAXDELAY ) { + udelay(10); + i++; + } else + break; + + ele = elements_in_queue(&out_full_queue); + if(ele <= 0) { + udelay(10); + spin_lock(&ctrl->lock); + count = get_dma_residue(ctrl->dma1); + spin_unlock(&ctrl->lock); + if (count <= 0) + break; + } + } else {//non-blocked + mdelay(100); + ele = elements_in_queue(&out_full_queue); + + if(ele <= 0) { + mdelay(100); + + spin_lock(&ctrl->lock); + count = get_dma_residue(ctrl->dma1); + spin_unlock(&ctrl->lock); + if (count <= 0) + break; + } + } + } + + return 0; +} + +static int drain_adc(struct jz_i2s_controller_info *ctrl, int nonblock) +{ + int count,i=0; + + for (;;) { + if ( i < MAXDELAY ) + { + udelay(10); + i++; + } + else + break; + spin_lock(&ctrl->lock); + count = get_dma_residue(ctrl->dma2); + spin_unlock(&ctrl->lock); + if (count <= 0) + break; + + if (nonblock) { + return -EBUSY; + } + } + + return 0; +} +#endif + +static int jz_audio_release(struct inode *inode, struct file *file) +{ + unsigned long flags; + struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; + unsigned long tfl; + + if (controller == NULL) + return -ENODEV; + + pop_dma_flag = 0; + if (controller->opened1 == 1) { + controller->opened1 = 0; + __i2s_enable_transmit_dma(); + __i2s_enable_replay(); + drain_dac(controller, file->f_flags & O_NONBLOCK); + /* add some mute to anti-pop */ +#if defined(CONFIG_I2S_DLV) + /* wait for fifo empty */ + write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + gain_down_start = jiffies; + sleep_on(&pop_wait_queue); + //gain_down_end = jiffies; + while (1) { + tfl = REG_AIC_SR & 0x00003f00; + if (tfl == 0) { + udelay(500); + break; + } + mdelay(2); + } +#endif + disable_dma(controller->dma1); + set_dma_count(controller->dma1, 0); + __i2s_disable_transmit_dma(); + __i2s_disable_replay(); + __aic_flush_fifo(); + if(clear_codec_replay) + clear_codec_replay(); + __aic_flush_fifo(); + + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes = 0; + controller->count = 0; + controller->finish = 0; + jz_audio_dma_tran_count = 0; + controller->blocks = 0; + controller->nextOut = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + +#if defined(CONFIG_I2S_DLV) + write_codec_file_bit(5, 1, 6);//SB_OUT->1 + ramp_down_start = jiffies; + sleep_on(&pop_wait_queue); + //ramp_down_end = jiffies; + unset_audio_data_replay(); +#endif + __i2s_disable(); + if(turn_off_codec) + turn_off_codec(); + } + + if (controller->opened2 == 1) { + controller->opened2 = 0; + first_record_call = 1; + __i2s_enable_receive_dma(); + __i2s_enable_record(); + drain_adc(controller, file->f_flags & O_NONBLOCK); + disable_dma(controller->dma2); + set_dma_count(controller->dma2, 0); + __i2s_disable_receive_dma(); + __i2s_disable_record(); + + if(clear_codec_record) + clear_codec_record(); + + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes = 0; + jz_audio_dma_tran_count = 0; + controller->count = 0; + controller->finish = 0; + controller->blocks = 0; + controller->nextIn = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + __i2s_disable(); + if(turn_off_codec) + turn_off_codec(); + abnormal_data_count = 0; + } + +#if defined(CONFIG_I2S_DLV) + write_codec_file(9, 0xff); + write_codec_file(8, 0x3f); +#endif + return 0; +} + +static int jz_audio_open(struct inode *inode, struct file *file) +{ + int i; + struct jz_i2s_controller_info *controller = i2s_controller; + + if (controller == NULL) + return -ENODEV; + + mdelay(2); + REG_DMAC_DMACKE(0) = 0x3f; + pop_dma_flag = 0; + if (controller->opened1 == 1 || controller->opened2 == 1 ) { + printk("\naudio is busy!\n"); + return -EBUSY; + } + jz_codec_config = 0; + + ramp_up_start = 0; + ramp_up_end = 0; + gain_up_start = 0; + gain_up_end = 0; + ramp_down_start = 0; + ramp_down_end = 0; + gain_down_start = 0; + gain_down_end = 0; + if (file->f_mode & FMODE_WRITE) { + if (controller->opened1 == 1) + return -EBUSY; + + controller->opened1 = 1; + /* for ioctl */ + controller->total_bytes = 0; + jz_audio_dma_tran_count = 0; + controller->count = 0; + controller->finish = 0; + controller->blocks = 0; + controller->nextOut = 0; + + for(i=0;i < 64;i++) { + save_last_samples[i].left = 0; + save_last_samples[i].right = 0; + } + + out_empty_queue.count = jz_audio_fragstotal; + for (i=0;i < jz_audio_fragstotal;i++) + *(out_empty_queue.id + i) = i; + out_busy_queue.count = 0; + out_full_queue.count = 0; + last_dma_buffer_id = 0; + } + + if (file->f_mode & FMODE_READ) { + if (controller->opened2 == 1) + return -EBUSY; + + controller->opened2 = 1; + first_record_call = 1; + /* for ioctl */ + controller->total_bytes = 0; + jz_audio_dma_tran_count = 0; + controller->count = 0; + controller->finish = 0; + controller->blocks = 0; + controller->nextIn = 0; + + in_empty_queue.count = jz_audio_fragstotal; + for (i=0;i < jz_audio_fragstotal;i++) + *(in_empty_queue.id + i) = i; + + in_full_queue.count = 0; + in_busy_queue.count = 0; + } + + file->private_data = controller; + jz_audio_reset(); + REG_AIC_FR |= (1 << 6); + + if (file->f_mode & FMODE_WRITE) { + if(set_codec_replay) + set_codec_replay(); + } + + if (file->f_mode & FMODE_READ) { + abnormal_data_count = 0; + if(set_codec_record) + set_codec_record(); + } + +#if defined(CONFIG_I2S_DLV) + __aic_reset(); + + mdelay(10); + REG_AIC_I2SCR = 0x10; + mdelay(20); + __aic_flush_fifo(); +#endif + + __i2s_enable(); + +#if defined(CONFIG_I2S_DLV) + if (file->f_mode & FMODE_WRITE) { + + write_codec_file_bit(5, 0, 6);//PMR1.SB_OUT->0 + ramp_up_start = jiffies; + /*while (!(REG_RTC_RCR & RTC_RCR_WRDY)); + REG_RTC_RCR = 0x1; + while (!(REG_RTC_RCR & RTC_RCR_WRDY)); + REG_RTC_RGR = 1;*/ + sleep_on(&pop_wait_queue); + //ramp_up_end = jiffies; + write_codec_file_bit(5, 1, 4);//SB_ADC->1 + } else if (file->f_mode & FMODE_READ) { + if (jz_mic_only) + write_codec_file_bit(5, 1, 7);//SB_DAC->1 + else + write_codec_file_bit(5, 0, 7);//SB_DAC->0 + mdelay(500); + } + +#endif + + return 0; +} + + +static int jz_audio_ioctl(struct inode *inode, struct file *file, +unsigned int cmd, unsigned long arg) +{ + int val,fullc,busyc,unfinish,newfragstotal,newfragsize; + unsigned int flags; + struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; + count_info cinfo; + audio_buf_info abinfo; + int id, i; + + val = 0; + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + case SNDCTL_DSP_RESET: +#if 0 + jz_audio_reset(); + __i2s_disable_replay(); + __i2s_disable_receive_dma(); + __i2s_disable_record(); + __i2s_disable_transmit_dma(); +#endif + return 0; + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(controller, file->f_flags & O_NONBLOCK); + return 0; + case SNDCTL_DSP_SPEED: + /* set smaple rate */ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) + jz_audio_set_speed(controller->dev_audio, val); + + return put_user(val, (int *)arg); + case SNDCTL_DSP_STEREO: + /* set stereo or mono channel */ + if (get_user(val, (int *)arg)) + return -EFAULT; + jz_audio_set_channels(controller->dev_audio, val ? 2 : 1); + + return 0; + case SNDCTL_DSP_GETBLKSIZE: + //return put_user(jz_audio_fragsize / jz_audio_b, (int *)arg); + return put_user(jz_audio_fragsize, (int *)arg); + case SNDCTL_DSP_GETFMTS: + /* Returns a mask of supported sample format*/ + return put_user(AFMT_U8 | AFMT_S16_LE, (int *)arg); + case SNDCTL_DSP_SETFMT: + /* Select sample format */ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != AFMT_QUERY) + jz_audio_set_format(controller->dev_audio,val); + else { + if (file->f_mode & FMODE_READ) + val = (jz_audio_format == 16) ? AFMT_S16_LE : AFMT_U8; + else + val = (jz_audio_format == 16) ? AFMT_S16_LE : AFMT_U8; + } + + return put_user(val, (int *)arg); + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + jz_audio_set_channels(controller->dev_audio, val); + + return put_user(val, (int *)arg); + case SNDCTL_DSP_POST: + /* FIXME: the same as RESET ?? */ + return 0; + case SNDCTL_DSP_SUBDIVIDE: + return 0; + case SNDCTL_DSP_SETFRAGMENT: + get_user(val, (long *) arg); + newfragsize = 1 << (val & 0xFFFF); + if (newfragsize < 4 * PAGE_SIZE) + newfragsize = 4 * PAGE_SIZE; + if (newfragsize > (16 * PAGE_SIZE)) + newfragsize = 16 * PAGE_SIZE; + + newfragstotal = (val >> 16) & 0x7FFF; + if (newfragstotal < 2) + newfragstotal = 2; + if (newfragstotal > 32) + newfragstotal = 32; + if((jz_audio_fragstotal == newfragstotal) && (jz_audio_fragsize == newfragsize)) + return 0; + Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); + mdelay(500); + jz_audio_fragstotal = newfragstotal; + jz_audio_fragsize = newfragsize; + + Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); + mdelay(10); + + return 0; + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_REALTIME|DSP_CAP_BATCH, (int *)arg); + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + case SNDCTL_DSP_SETDUPLEX: + return -EINVAL; + case SNDCTL_DSP_GETOSPACE: + { + int i, bytes = 0; + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + spin_lock_irqsave(&controller->ioctllock, flags); + jz_audio_fragments = elements_in_queue(&out_empty_queue); + for (i = 0; i < jz_audio_fragments; i++) + bytes += jz_audio_fragsize; + + if (jz_audio_channels == 2) + bytes /= jz_audio_b; + else if (jz_audio_channels == 1) + bytes /= 4; + else + printk("SNDCTL_DSP_GETOSPACE : channels is wrong 1!\n"); + + + spin_unlock_irqrestore(&controller->ioctllock, flags); + /* unused fragment amount */ + abinfo.fragments = jz_audio_fragments; + /* amount of fragments */ + abinfo.fragstotal = jz_audio_fragstotal; + /* fragment size in bytes */ + if (jz_audio_channels == 2) + abinfo.fragsize = jz_audio_fragsize / jz_audio_b; + else if (jz_audio_channels == 1) + abinfo.fragsize = jz_audio_fragsize / 4; + else + printk("SNDCTL_DSP_GETOSPACE : channels is wrong 2!\n"); + + /* write size count without blocking in bytes */ + abinfo.bytes = bytes; + + return copy_to_user((void *)arg, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + } + case SNDCTL_DSP_GETISPACE: + { + int i, bytes = 0; + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + jz_audio_fragments = elements_in_queue(&in_empty_queue); + for (i = 0; i < jz_audio_fragments; i++) + bytes += jz_audio_fragsize; + + if (jz_audio_channels == 2) + bytes /= jz_audio_b; + else if (jz_audio_channels == 1) + bytes /= 4; + else + printk("SNDCTL_DSP_GETISPACE : channels is wrong 1!\n"); + + abinfo.fragments = jz_audio_fragments; + abinfo.fragstotal = jz_audio_fragstotal; + + if (jz_audio_channels == 2) + abinfo.fragsize = jz_audio_fragsize / jz_audio_b; + else if (jz_audio_channels == 1) + abinfo.fragsize = jz_audio_fragsize / 4; + else + printk("SNDCTL_DSP_GETISPACE : channels is wrong 2!\n"); + + abinfo.bytes = bytes; + + return copy_to_user((void *)arg, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + } + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && in_dma_buf) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && out_dma_buf) + val |= PCM_ENABLE_OUTPUT; + + return put_user(val, (int *)arg); + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + return 0; + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + + spin_lock_irqsave(&controller->ioctllock, flags); + cinfo.bytes = controller->total_bytes; + cinfo.blocks = controller->blocks; + cinfo.ptr = controller->nextIn; + controller->blocks = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + spin_lock_irqsave(&controller->ioctllock, flags); + cinfo.bytes = controller->total_bytes; + cinfo.blocks = controller->blocks; + cinfo.ptr = controller->nextOut; + controller->blocks = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + + return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + spin_lock_irqsave(&controller->ioctllock, flags); + unfinish = 0; + fullc = elements_in_queue(&out_full_queue); + busyc = elements_in_queue(&out_busy_queue); + for(i = 0;i < fullc ;i ++) { + id = *(out_full_queue.id + i); + unfinish += *(out_dma_buf_data_count + id); + } + for(i = 0;i < busyc ;i ++) { + id = *(out_busy_queue.id + i); + unfinish += get_dma_residue(controller->dma1); + } + spin_unlock_irqrestore(&controller->ioctllock, flags); + + if (jz_audio_channels == 2) + unfinish /= jz_audio_b; + else if (jz_audio_channels == 1) + unfinish /= 4; + else + printk("SNDCTL_DSP_GETODELAY : channels is wrong !\n"); + + return put_user(unfinish, (int *) arg); + case SOUND_PCM_READ_RATE: + return put_user(jz_audio_rate, (int *)arg); + case SOUND_PCM_READ_CHANNELS: + return put_user(jz_audio_channels, (int *)arg); + case SOUND_PCM_READ_BITS: + return put_user((jz_audio_format & (AFMT_S8 | AFMT_U8)) ? 8 : 16, (int *)arg); + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + return -EINVAL; +} + + +static unsigned int jz_audio_poll(struct file *file,struct poll_table_struct *wait) +{ + struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; + unsigned long flags; + unsigned int mask = 0; + + if (file->f_mode & FMODE_WRITE) { + if (elements_in_queue(&out_empty_queue) > 0) + return POLLOUT | POLLWRNORM; + + poll_wait(file, &controller->dac_wait, wait); + } + + if (file->f_mode & FMODE_READ) { + if (elements_in_queue(&in_full_queue) > 0) + return POLLIN | POLLRDNORM; + + poll_wait(file, &controller->adc_wait, wait); + } + + spin_lock_irqsave(&controller->lock, flags); + if (file->f_mode & FMODE_WRITE) { + if (elements_in_queue(&out_empty_queue) > 0) + mask |= POLLOUT | POLLWRNORM; + } else if (file->f_mode & FMODE_READ) { + if (elements_in_queue(&in_full_queue) > 0) + mask |= POLLIN | POLLRDNORM; + } + spin_unlock_irqrestore(&controller->lock, flags); + + return mask; +} + +static ssize_t jz_audio_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; + int id, ret = 0, left_count, copy_count, cnt = 0; + unsigned long flags; + + if (count < 0) + return -EINVAL; + + __i2s_enable_receive_dma(); + __i2s_enable_record(); + + spin_lock_irqsave(&controller->ioctllock, flags); + controller->nextIn = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + + copy_count = jz_audio_fragsize / 4; + + left_count = count; + if (first_record_call) { + first_record_call = 0; + audio_read_back_first: + if ((id = get_buffer_id(&in_empty_queue)) >= 0) { + put_buffer_id(&in_busy_queue, id); + spin_lock(&controller->lock); + *(in_dma_buf_data_count + id) = copy_count * 4; + + spin_unlock(&controller->lock); + __i2s_enable_receive_dma(); + __i2s_enable_record(); + dma_cache_wback_inv(*(in_dma_buf + id), *(in_dma_buf_data_count + id)); + audio_start_dma(controller->dma2,file->private_data, + *(in_dma_pbuf + id), + *(in_dma_buf_data_count + id), + DMA_MODE_READ); + sleep_on(&rx_wait_queue); + } else + goto audio_read_back_first; + } + + while (left_count > 0) { + audio_read_back_second: + if (elements_in_queue(&in_full_queue) <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + else + sleep_on(&rx_wait_queue); + } + + if ((id = get_buffer_id(&in_full_queue)) >= 0) { + spin_lock(&controller->lock); + cnt = record_filler((unsigned long)controller->tmp2+ret, copy_count, id); + spin_unlock(&controller->lock); + put_buffer_id(&in_empty_queue, id); + } else + goto audio_read_back_second; + + if (elements_in_queue(&in_busy_queue) == 0) { + if ((id=get_buffer_id(&in_empty_queue)) >= 0) { + put_buffer_id(&in_busy_queue, id); + spin_lock(&controller->lock); + *(in_dma_buf_data_count + id) = copy_count * 4; + spin_unlock(&controller->lock); + + dma_cache_wback_inv(*(in_dma_buf + id), *(in_dma_buf_data_count + id)); + audio_start_dma(controller->dma2,file->private_data, + *(in_dma_pbuf + id), + *(in_dma_buf_data_count + id), + DMA_MODE_READ); + } + } + if (ret + cnt > count) { + spin_lock(&controller->lock); + cnt = count - ret; + spin_unlock(&controller->lock); + } + if (copy_to_user(buffer+ret, controller->tmp2+ret, cnt)) + return ret ? ret : -EFAULT; + + spin_lock(&controller->lock); + ret += cnt; + spin_unlock(&controller->lock); + + spin_lock_irqsave(&controller->ioctllock, flags); + controller->nextIn += ret; + spin_unlock_irqrestore(&controller->ioctllock, flags); + + spin_lock(&controller->lock); + left_count -= cnt; + spin_unlock(&controller->lock); + } + return ret; +} + +static ssize_t jz_audio_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + int id, ret = 0, left_count, copy_count = 0; + unsigned int flags; + struct jz_i2s_controller_info *controller = (struct jz_i2s_controller_info *) file->private_data; + + if (count <= 0) + return -EINVAL; + + if(set_replay_hp_or_speaker) + set_replay_hp_or_speaker(); + + __i2s_enable_transmit_dma(); + __i2s_enable_replay(); + + spin_lock_irqsave(&controller->ioctllock, flags); + controller->nextOut = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + if (jz_audio_channels == 2) + copy_count = jz_audio_fragsize / jz_audio_b; + else if(jz_audio_channels == 1) + copy_count = jz_audio_fragsize / 4; + left_count = count; + if (copy_from_user(controller->tmp1, buffer, count)) { + printk("copy_from_user failed:%d",ret); + return ret ? ret : -EFAULT; + } + + while (left_count > 0) { + audio_write_back: + /*if (file->f_flags & O_NONBLOCK) + udelay(2);*/ + if (elements_in_queue(&out_empty_queue) == 0) { + if (file->f_flags & O_NONBLOCK) + return ret; + else + sleep_on(&tx_wait_queue); + } + /* the end fragment size in this write */ + if (ret + copy_count > count) + copy_count = count - ret; + if ((id = get_buffer_id(&out_empty_queue)) >= 0) { + replay_filler((signed long)controller->tmp1 + ret, copy_count, id); + if(*(out_dma_buf_data_count + id) > 0) { + put_buffer_id(&out_full_queue, id); + dma_cache_wback_inv(*(out_dma_buf + id), + *(out_dma_buf_data_count + id)); + } else + put_buffer_id(&out_empty_queue, id); + } else + goto audio_write_back; + + left_count = left_count - copy_count; + ret += copy_count; + + spin_lock_irqsave(&controller->ioctllock, flags); + controller->nextOut += ret; + spin_unlock_irqrestore(&controller->ioctllock, flags); + + if (elements_in_queue(&out_busy_queue) == 0) { + if ((id=get_buffer_id(&out_full_queue)) >= 0) { + put_buffer_id(&out_busy_queue, id); + + if(*(out_dma_buf_data_count + id) > 0) { + audio_start_dma(controller->dma1, + file->private_data, + *(out_dma_pbuf + id), + *(out_dma_buf_data_count + id), + DMA_MODE_WRITE); + last_dma_buffer_id = id; + if (jz_codec_config == 0) { + write_codec_file_bit(1, 0, 5); + gain_up_start = jiffies; + sleep_on(&pop_wait_queue); + //gain_up_end = jiffies; + jz_codec_config = 1; + //SB_ADC->1 + //write_codec_file_bit(5, 1, 4); + //while(1); + } + } + } + } + } + + return ret; +} + +#if defined(CONFIG_I2S_ICODEC) +static void write_mute_to_dma_buffer(signed long l_sample, signed long r_sample) +{ + int i,step_len; + unsigned long *pop_buf = (unsigned long*)pop_turn_onoff_buf; + unsigned int sample_oss = (REG_AIC_CR & 0x00380000) >> 19; + unsigned long l_sample_count,r_sample_count,sample_count; + struct jz_i2s_controller_info *controller = i2s_controller; + signed int left_sam=0,right_sam=0,l_val,r_val; + + switch (sample_oss) { + case 0x0: + break; + case 0x1: + left_sam = (signed int)l_sample; + right_sam = (signed int)r_sample; + break; + case 0x2: + break; + case 0x3: + break; + case 0x4: + break; + } + + if(left_sam == 0 && right_sam == 0) + return; + + switch (sample_oss) { + case 0x0: + break; + case 0x1: + step_len = jz_audio_speed / 10 * 3; + step_len = step_len / 2; + step_len = 0x7fff / step_len + 1; + + l_sample_count = 0; + l_val = left_sam; + + while(1) { + if(l_val > 0) { + if(l_val >= step_len) { + l_val -= step_len; + l_sample_count ++; + } else + break; + } + + if(l_val < 0) { + if(l_val <= -step_len) { + l_val += step_len; + l_sample_count ++; + } else + break; + } + + if(l_val == 0) + break; + } + + r_sample_count = 0; + r_val = right_sam; + while(1) { + if(r_val > 0) { + if(r_val >= step_len) { + r_val -= step_len; + r_sample_count ++; + } else + break; + } + + if(r_val < 0) { + if(r_val <= -step_len) { + r_val += step_len; + r_sample_count ++; + } else + break; + } + + if(r_val == 0) + break; + } + /* fill up */ + if(l_sample_count > r_sample_count) + sample_count = l_sample_count; + else + sample_count = r_sample_count; + + l_val = left_sam; + r_val = right_sam; + for(i=0;i <= sample_count;i++) { + + *pop_buf = (unsigned long)l_val; + pop_buf ++; + + if(l_val > step_len) + l_val -= step_len; + else if(l_val < -step_len) + l_val += step_len; + else if(l_val >= -step_len && l_val <= step_len) + l_val = 0; + + *pop_buf = (unsigned long)r_val; + pop_buf ++; + if(r_val > step_len) + r_val -= step_len; + else if(r_val < -step_len) + r_val += step_len; + else if(r_val >= -step_len && r_val <= step_len) + r_val = 0; + } + + *pop_buf = 0; + pop_buf ++; + *pop_buf = 0; + + pop_buf ++; + sample_count += 2; + dma_cache_wback_inv(pop_turn_onoff_buf, sample_count*8); + + pop_dma_flag = 1; + audio_start_dma(controller->dma1,controller,pop_turn_onoff_pbuf,sample_count*8,DMA_MODE_WRITE); + sleep_on(&pop_wait_queue); + pop_dma_flag = 0; + break; + case 0x2: + break; + case 0x3: + break; + case 0x4: + break; + } +} +#endif diff --git a/sound/oss/jz_pcm_tlv320aic1106_dma.c b/sound/oss/jz_pcm_tlv320aic1106_dma.c new file mode 100644 index 0000000..1a1f4cf --- /dev/null +++ b/sound/oss/jz_pcm_tlv320aic1106_dma.c @@ -0,0 +1,1839 @@ +/* + * linux/drivers/sound/jz_pcm_tlv320aic1106.c + * + * JzSOC On-Chip PCM audio driver. + * + * Copyright (C) 2005 by Ingenic Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Because the normal application of AUDIO devices are focused on Little_endian, + * then we only perform the little endian data format in driver. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sound_config.h" + +#define NR_PCM 2 +#define MAXDELAY 50000 +#define JZCODEC_RW_BUFFER_SIZE 4 +#define JZCODEC_RW_BUFFER_TOTAL 3 +#define JZCODEC_USER_BUFFER 6 + +#define MODE_is_8 0 // 1 is 8 bits, and 0 is 16 bits + +static int jz_audio_rate; +static char jz_audio_format; +static char jz_audio_volume; +static char jz_audio_channels; +static int jz_audio_fragments;//unused fragment amount +static int jz_audio_fragstotal; +static int jz_audio_fragsize; +static int jz_audio_speed; +static int jz_audio_dma_tran_count;//bytes count of one DMA transfer + +static void jz_update_filler(int bits, int channels); +static int Init_In_Out_queue(int fragstotal,int fragsize); +static int Free_In_Out_queue(int fragstotal,int fragsize); +static irqreturn_t jz_pcm_irq(int irqnr, void *ref); +static irqreturn_t jz_pcm_replay_dma_irq(int irqnr, void *ref); +static irqreturn_t jz_pcm_record_dma_irq(int irqnr, void *ref); +static void (*replay_filler)(unsigned long src_start, int count, int id); +static int (*record_filler)(unsigned long dst_start, int count, int id); +static void dump_pcmc_reg(void); + +static struct file_operations jz_pcm_audio_fops; +static DECLARE_WAIT_QUEUE_HEAD (rx_wait_queue); +static DECLARE_WAIT_QUEUE_HEAD (tx_wait_queue); + +struct jz_pcm_controller_info { + int io_base; + int dma1; /* play */ + int dma2; /* record */ + char *name; + int dev_audio; + struct pcm_codec *pcm_codec[NR_PCM]; + int opened1; + int opened2; + unsigned char *tmp1; /* tmp buffer for sample conversions */ + unsigned char *tmp2; + spinlock_t lock; + spinlock_t ioctllock; + + wait_queue_head_t dac_wait; + wait_queue_head_t adc_wait; + int nextIn; // byte index to next-in to DMA buffer + int nextOut; // byte index to next-out from DMA buffer + int count; // current byte count in DMA buffer + int finish; // current transfered byte count in DMA buffer + unsigned long total_bytes; // total bytes written or read + unsigned long blocks; + unsigned long error; // over/underrun +}; +static struct jz_pcm_controller_info *pcm_controller = NULL; + +struct pcm_codec { + /* PCM controller connected with */ + void *private_data; + char *name; + int id; + int dev_mixer; + /* controller specific lower leverl pcm accessing routines */ + u16 (*codec_read) (u8 reg);//the function accessing Codec REGs + void (*codec_write) (u8 reg, u16 val); + /* Wait for codec-ready. Ok to sleep here. */ + void (*codec_wait) (struct pcm_codec *codec); + /* OSS mixer masks */ + int modcnt; + int supported_mixers; + int stereo_mixers; + int record_sources; + int bit_resolution; + /* OSS mixer interface */ + int (*read_mixer) (struct pcm_codec *codec, int oss_channel); + void (*write_mixer)(struct pcm_codec *codec, int oss_channel, + unsigned int left, unsigned int right); + int (*recmask_io) (struct pcm_codec *codec, int rw, int mask); + int (*mixer_ioctl)(struct pcm_codec *codec, unsigned int cmd, unsigned long arg); + /* saved OSS mixer states */ + unsigned int mixer_state[SOUND_MIXER_NRDEVICES]; +}; + +typedef struct buffer_queue_s { + int count; + int *id; + int lock; +} buffer_queue_t; + +static unsigned long *out_dma_buf = NULL; +static unsigned long *out_dma_pbuf = NULL; +static unsigned long *out_dma_buf_data_count = NULL; +static unsigned long *in_dma_buf = NULL; +static unsigned long *in_dma_pbuf = NULL; +static unsigned long *in_dma_buf_data_count = NULL; + +static buffer_queue_t out_empty_queue; +static buffer_queue_t out_full_queue; +static buffer_queue_t out_busy_queue; +static buffer_queue_t in_empty_queue; +static buffer_queue_t in_full_queue; +static buffer_queue_t in_busy_queue; +static int first_record_call = 0; + +static inline int get_buffer_id(struct buffer_queue_s *q) +{ + int r, i; + unsigned long flags; + + spin_lock_irqsave(&q->lock, flags); + if (q->count == 0) { + spin_unlock_irqrestore(&q->lock, flags); + return -1; + } + r = *(q->id + 0); + for (i=0;i < q->count-1;i++) + *(q->id + i) = *(q->id + (i+1)); + q->count --; + spin_unlock_irqrestore(&q->lock, flags); + + return r; +} + +static inline void put_buffer_id(struct buffer_queue_s *q, int id) +{ + unsigned long flags; + + spin_lock_irqsave(&q->lock, flags); + *(q->id + q->count) = id; + q->count ++; + spin_unlock_irqrestore(&q->lock, flags); +} + +static inline int elements_in_queue(struct buffer_queue_s *q) +{ + int r; + unsigned long flags; + + spin_lock_irqsave(&q->lock, flags); + r = q->count; + spin_unlock_irqrestore(&q->lock, flags); + + return r; +} + +static inline void audio_start_dma(int chan, void *dev_id, unsigned long phyaddr,int count, int mode) +{ + unsigned long flags; + struct jz_pcm_controller_info * controller = (struct jz_pcm_controller_info *) dev_id; + + //for DSP_GETOPTR + //spin_lock_irqsave(&controller->ioctllock, flags); + spin_lock(&controller->ioctllock); + jz_audio_dma_tran_count = count; + //spin_unlock_irqrestore(&controller->ioctllock, flags); + spin_unlock(&controller->ioctllock); + flags = claim_dma_lock(); + __dmac_disable_module(0);//!!!!!!!!! + disable_dma(chan); + clear_dma_ff(chan); + set_dma_mode(chan, mode); +#if MODE_is_8 + __dmac_channel_set_src_port_width(chan, 8); + __dmac_channel_set_dest_port_width(chan, 8); + __dmac_channel_set_transfer_unit_8bit(chan); +#else + __dmac_channel_set_src_port_width(chan, 16); + __dmac_channel_set_dest_port_width(chan, 16); + __dmac_channel_set_transfer_unit_16bit(chan); +#endif + + set_dma_addr(chan, phyaddr); + if (count == 0) { + count++; + printk("JzSOC DMA controller can't set dma 0 count!\n"); + } + + set_dma_count(chan, count); + enable_dma(chan); + __dmac_enable_module(0); + release_dma_lock(flags); +#if 0 + //for DSP_GETOPTR on FPGA + struct jz_dma_chan *tmpchan = &jz_dma_table[1]; + + spin_lock(&controller->ioctllock); + jz_audio_dma_tran_count = count; + spin_unlock(&controller->ioctllock); + flags = claim_dma_lock(); + disable_dma(chan); + clear_dma_ff(chan); + set_dma_mode(chan, mode); +#if MODE_is_8 + __dmac_channel_set_src_port_width(chan, 8); + __dmac_channel_set_dest_port_width(chan, 8); + __dmac_channel_set_transfer_unit_8bit(chan); +#else + __dmac_channel_set_src_port_width(chan, 16); + __dmac_channel_set_dest_port_width(chan, 16); + __dmac_channel_set_transfer_unit_16bit(chan); +#endif + set_dma_addr(chan, phyaddr); + if (count == 0) { + count++; + printk("JzSOC DMA controller can't set dma 0 count!\n"); + } + set_dma_count(chan, count); + enable_dma(chan); + release_dma_lock(flags); +#if 0 + __pcm_enable_tfs_intr(); + __pcm_enable_tur_intr(); + __pcm_enable_rfs_intr(); + __pcm_enable_ror_intr(); +#endif +#endif +} + +static irqreturn_t jz_pcm_record_dma_irq(int irq, void *dev_id) +{ + int id1, id2; + struct jz_pcm_controller_info * controller = (struct jz_pcm_controller_info *) dev_id; + int dma = controller->dma2; + + disable_dma(dma); + if (__dmac_channel_address_error_detected(dma)) { + printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); + __dmac_channel_clear_address_error(dma); + } + if (__dmac_channel_transmit_end_detected(dma)) { + __dmac_channel_clear_transmit_end(dma); + //for DSP_GETIPTR + spin_lock(&controller->ioctllock); + controller->total_bytes += jz_audio_dma_tran_count; + controller->blocks ++; + spin_unlock(&controller->ioctllock); + id1 = get_buffer_id(&in_busy_queue); + put_buffer_id(&in_full_queue, id1); + + wake_up(&rx_wait_queue); + wake_up(&controller->adc_wait); + if ((id2 = get_buffer_id(&in_empty_queue)) >= 0) { + put_buffer_id(&in_busy_queue, id2); + *(in_dma_buf_data_count + id2) = *(in_dma_buf_data_count + id1); + + dma_cache_wback_inv(*(in_dma_buf + id2), *(in_dma_buf_data_count + id2)); + audio_start_dma(dma,dev_id, + *(in_dma_pbuf + id2), + *(in_dma_buf_data_count + id2), + DMA_MODE_READ); + } else + in_busy_queue.count = 0; + } + + return IRQ_HANDLED; +} + +static irqreturn_t jz_pcm_irq(int irq, void *dev_id) +{ + struct jz_pcm_controller_info * controller = (struct jz_pcm_controller_info *) dev_id; + printk("pcm interrupt REG_PCM_INTS : 0x%08x\n",REG_PCM_INTS); + + return IRQ_HANDLED; +} + +static irqreturn_t jz_pcm_replay_dma_irq(int irq, void *dev_id) +{ + int id; + unsigned long flags; + struct jz_pcm_controller_info * controller = (struct jz_pcm_controller_info *) dev_id; + int dma = controller->dma1; + + disable_dma(dma); + if (__dmac_channel_address_error_detected(dma)) { + printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); + __dmac_channel_clear_address_error(dma); + } + if (__dmac_channel_transmit_end_detected(dma)) { + __dmac_channel_clear_transmit_end(dma); + //for DSP_GETOPTR + spin_lock_irqsave(&controller->ioctllock, flags); + controller->total_bytes += jz_audio_dma_tran_count; + controller->blocks ++; + spin_unlock_irqrestore(&controller->ioctllock, flags); + if ((id = get_buffer_id(&out_busy_queue)) < 0) + printk(KERN_DEBUG "Strange DMA finish interrupt for PCM module\n"); + put_buffer_id(&out_empty_queue, id); + if ((id = get_buffer_id(&out_full_queue)) >= 0) { + put_buffer_id(&out_busy_queue, id); //very busy + if(*(out_dma_buf_data_count + id) > 0) { + audio_start_dma(dma, dev_id, *(out_dma_pbuf + id), + *(out_dma_buf_data_count + id), + DMA_MODE_WRITE); + } + } else + out_busy_queue.count = 0; + + if (elements_in_queue(&out_empty_queue) > 0) { + wake_up(&tx_wait_queue); + wake_up(&controller->dac_wait); + } + } + + return IRQ_HANDLED; +} + +static int Init_In_Out_queue(int fragstotal,int fragsize) +{ + int i; + // recording + in_empty_queue.count = fragstotal; + in_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!in_dma_buf) + goto all_mem_err; + in_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!in_dma_pbuf) + goto all_mem_err; + in_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!in_dma_buf_data_count) + goto all_mem_err; + in_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!in_empty_queue.id) + goto all_mem_err; + in_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!in_full_queue.id) + goto all_mem_err; + in_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!in_busy_queue.id) + goto all_mem_err; + + for (i=0;i < fragstotal;i++) + *(in_empty_queue.id + i) = i; + in_full_queue.count = 0; + in_busy_queue.count = 0; + + for (i = 0; i < fragstotal; i++) { + *(in_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); + if (*(in_dma_buf + i) == 0) + goto mem_failed_in; + *(in_dma_pbuf + i) = virt_to_phys((void *)(*(in_dma_buf + i))); + dma_cache_wback_inv(*(in_dma_buf + i), fragsize); + } + + //playing + out_empty_queue.count = fragstotal; + out_dma_buf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!out_dma_buf) + goto all_mem_err; + out_dma_pbuf = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + if (!out_dma_pbuf) + goto all_mem_err; + out_dma_buf_data_count = (unsigned long *)kmalloc(sizeof(unsigned long) * fragstotal, GFP_KERNEL); + + if (!out_dma_buf_data_count) + goto all_mem_err; + out_empty_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!out_empty_queue.id) + goto all_mem_err; + out_full_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!out_full_queue.id) + goto all_mem_err; + out_busy_queue.id = (int *)kmalloc(sizeof(int) * fragstotal, GFP_KERNEL); + if (!out_busy_queue.id) + goto all_mem_err; + for (i=0;i < fragstotal;i++) + *(out_empty_queue.id + i) = i; + + out_busy_queue.count = 0; + out_full_queue.count = 0; + /*alloc DMA buffer*/ + for (i = 0; i < fragstotal; i++) { + *(out_dma_buf + i) = __get_free_pages(GFP_KERNEL | GFP_DMA, get_order(fragsize)); + if (*(out_dma_buf + i) == 0) { + printk(" can't allocate required DMA(OUT) buffers.\n"); + goto mem_failed_out; + } + *(out_dma_pbuf + i) = virt_to_phys((void *)(*(out_dma_buf + i))); + } + + return 1; +all_mem_err: + printk("error:allocate memory occur error 1!\n"); + return 0; +mem_failed_out: + printk("error:allocate memory occur error 2!\n"); + for (i = 0; i < fragstotal; i++) { + if(*(out_dma_buf + i)) + free_pages(*(out_dma_buf + i), get_order(fragsize)); + } + + return 0; +mem_failed_in: + printk("error:allocate memory occur error 3!\n"); + for (i = 0; i < fragstotal; i++) { + if(*(in_dma_buf + i)) + free_pages(*(in_dma_buf + i), get_order(fragsize)); + } + return 0; +} + +static int Free_In_Out_queue(int fragstotal,int fragsize) +{ + int i; + //playing + if(out_dma_buf != NULL) { + for (i = 0; i < fragstotal; i++) { + if(*(out_dma_buf + i)) + free_pages(*(out_dma_buf + i), get_order(fragsize)); + *(out_dma_buf + i) = 0; //release page error + } + kfree(out_dma_buf); + out_dma_buf = NULL; + } + if(out_dma_pbuf) { + kfree(out_dma_pbuf); + out_dma_pbuf = NULL; + } + if(out_dma_buf_data_count) { + kfree(out_dma_buf_data_count); + out_dma_buf_data_count = NULL; + } + if(out_empty_queue.id) { + kfree(out_empty_queue.id); + out_empty_queue.id = NULL; + } + if(out_full_queue.id) { + kfree(out_full_queue.id); + out_full_queue.id = NULL; + } + if(out_busy_queue.id) { + kfree(out_busy_queue.id); + out_busy_queue.id = NULL; + } + out_empty_queue.count = fragstotal; + out_busy_queue.count = 0; + out_full_queue.count = 0; + + // recording + if(in_dma_buf) { + for (i = 0; i < fragstotal; i++) { + if(*(in_dma_buf + i)) { + dma_cache_wback_inv(*(in_dma_buf + i), fragsize); + free_pages(*(in_dma_buf + i), get_order(fragsize)); + } + *(in_dma_buf + i) = 0; //release page error + } + kfree(in_dma_buf); + in_dma_buf = NULL; + } + if(in_dma_pbuf) { + kfree(in_dma_pbuf); + in_dma_pbuf = NULL; + } + if(in_dma_buf_data_count) { + kfree(in_dma_buf_data_count); + in_dma_buf_data_count = NULL; + } + if(in_empty_queue.id) { + kfree(in_empty_queue.id); + in_empty_queue.id = NULL; + } + if(in_full_queue.id) { + kfree(in_full_queue.id); + in_full_queue.id = NULL; + } + if(in_busy_queue.id) { + kfree(in_busy_queue.id); + in_busy_queue.id = NULL; + } + + in_empty_queue.count = fragstotal; + in_full_queue.count = 0; + in_busy_queue.count = 0; + + return 1; +} + +static int jz_audio_set_speed(int dev, int rate) +{ + /* 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000 */ + long codec_speed; + long speed = 0; + + jz_audio_speed = rate; + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + jz_audio_rate = rate; + + /*switch (rate) { + case 8000: + speed = 0; + break; + case 11025: + speed = 1; + break; + case 12000: + speed = 2; + break; + case 16000: + speed = 3; + break; + case 22050: + speed = 4; + break; + case 24000: + speed = 5; + break; + case 32000: + speed = 6; + break; + case 44100: + speed = 7; + break; + case 48000: + speed = 8; + break; + default: + break; + } + printk("set PCMIN or PCMOUT speed.\n"); + __pcm_set_clk_rate(speed); + __pcm_set_sync_rate(256);*/ + + return jz_audio_rate; +} + + +static int record_fill_1x8_u(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + volatile unsigned char *s = (unsigned char*)(*(in_dma_buf + id)); + volatile unsigned char *dp = (unsigned char*)dst_start; + + while (count > 0) { + *dp = *s; + dp ++; + s++; + count --; + cnt ++; + } + + return cnt; +} + +static int record_fill_2x8_u(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + volatile unsigned char *s = (unsigned char*)(*(in_dma_buf + id)); + volatile unsigned char *dp = (unsigned char*)dst_start; + +#if 1 + while (count > 0) { + *dp = *s; + s ++; + dp ++; + count --; + cnt ++; + } +#else + while (count > 0) { + *dp = *s; + s += 2; //skip right sample + dp ++; + count -= 2; + cnt ++; + } +#endif + return cnt; +} + +static int record_fill_1x16_s(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + unsigned short d1; + unsigned short *s = (unsigned short*)(*(in_dma_buf + id)); + unsigned short *dp = (unsigned short *)dst_start; + + while (count > 0) { + *dp = *s; + s ++; + dp ++; + count -= 2; + cnt += 2; + } + + return cnt; +} + +static int record_fill_2x16_s(unsigned long dst_start, int count, int id) +{ + int cnt = 0; + unsigned short *s = (unsigned short*)(*(in_dma_buf + id)); + unsigned short *dp = (unsigned short *)dst_start; + +#if 1 + while (count > 0) { + *dp = *s; + s ++; + dp ++; + count -= 2; + cnt += 2; + } +#else + while (count > 0) { + *dp = *s; + s += 2; //skip right sample + dp ++; + count -= 4; + cnt += 2;/* count in byte */ + } +#endif + + return cnt; +} + +static void replay_fill_1x8_u(unsigned long src_start, int count, int id) +{ +#if 0 + volatile unsigned char *s = (unsigned char *)src_start; + volatile unsigned char *dp = (unsigned char *)(*(out_dma_buf + id)); + + *(out_dma_buf_data_count + id) = count; + while (count > 0) { + *dp = *s; + dp ++; + s ++; + count --; + } +#else + volatile u8 *s = (u8 *)src_start; + volatile u8 *dp = (u8 *)(*(out_dma_buf + id)); + + *(out_dma_buf_data_count + id) = count; + while (count > 0) { + *dp = *s; + dp ++; + s ++; + count --; + } +#endif +} + +static void replay_fill_2x8_u(unsigned long src_start, int count, int id) +{ + volatile unsigned char *s = (unsigned char *)src_start; + volatile unsigned char *dp = (unsigned char *)(*(out_dma_buf + id)); + +#if 1 + *(out_dma_buf_data_count + id) = count; + while (count > 0) { + *dp = *s; + dp ++; + s ++; + count --; + } +#else + while (count > 0) { + *dp = *s; + s += 2; //skip right sample + dp ++; + count -= 2; + cnt ++; + } + *(out_dma_buf_data_count + id) = cnt; +#endif +} + + +static void replay_fill_1x16_s(unsigned long src_start, int count, int id) +{ + int cnt = 0; + unsigned short d1, l1; + signed short sam_to; + volatile unsigned short *s = (unsigned short *)src_start; + volatile unsigned short *dp = (unsigned short*)(*(out_dma_buf + id)); + + while (count > 0) { + d1 = *s; + + sam_to = (signed short)d1; + if (sam_to >= 0) { + sam_to = 0xfff * sam_to / 0x7fff; + } else { + sam_to = 0 - sam_to; + sam_to = 0xfff * sam_to / 0x7fff; + sam_to = 0 - sam_to; + } + d1 = (unsigned short)sam_to; + d1 = d1 << 3; + l1 = d1 | jz_audio_volume; + + *dp = l1; + s ++; + dp ++; + count -= 2; + cnt += 2 ; + } + *(out_dma_buf_data_count + id) = cnt; +} + +static void replay_fill_2x16_s(unsigned long src_start, int count, int id) +{ + int cnt = 0; + unsigned short d1, l1; + volatile unsigned short *s = (unsigned short *)src_start; + volatile unsigned short *dp = (unsigned short*)(*(out_dma_buf + id)); + +#if 1 + while (count > 0) { + d1 = *s; + l1 = (d1 & 0xfff8) | jz_audio_volume; + *dp = l1; + s ++; + dp ++; + count -= 2; + cnt += 2 ; + } + *(out_dma_buf_data_count + id) = cnt; +#else + while (count > 0) { + d1 = *s; + l1 = (d1 & 0xfff8) | jz_audio_volume; + *dp = l1; + s += 2; //skip right sample + dp ++; + count -= 4; + cnt += 2; + } + *(out_dma_buf_data_count + id) = cnt; +#endif +} + +static unsigned int jz_audio_set_format(int dev, unsigned int fmt) +{ + switch (fmt) { + case AFMT_U8: + case AFMT_S16_LE: + jz_audio_format = fmt; + jz_update_filler(jz_audio_format, jz_audio_channels); + case AFMT_QUERY: + break; + } + + return jz_audio_format; +} + +static short jz_audio_set_channels(int dev, short channels) +{ + switch (channels) { + case 1: + case 2: + jz_audio_channels = channels; + jz_update_filler(jz_audio_format, jz_audio_channels); + break; + default: + printk("channel number is wrong. %d\n",__LINE__); + } + + return jz_audio_channels; +} + +static void init_codec(void) +{ + printk("set PCM codec RESET pin on LOW, while MCLK occur.\n"); + printk("set LINSEL on LOW(8bits) or HIGH(13bits+3bits for PCMOUT gain).\n"); +} + +static void jz_audio_reset(void) +{ + __pcm_flush_fifo(); + __pcm_disable_txfifo(); + __pcm_disable_rxfifo(); + __pcm_set_transmit_trigger(7); + __pcm_set_receive_trigger(7); + __pcm_omsb_next_sync(); + __pcm_imsb_next_sync(); +#if MODE_is_8 + __pcm_set_iss(8);//8bits decided by LINSEL + __pcm_set_oss(8);//8bits decided by LINSEL +#else + __pcm_set_iss(16);//16bits decided by LINSEL + __pcm_set_oss(16); +#endif + __pcm_disable_tfs_intr(); + __pcm_disable_tur_intr(); + __pcm_disable_rfs_intr(); + __pcm_disable_ror_intr(); + + __pcm_disable_receive_dma(); + __pcm_disable_transmit_dma(); + + //init_codec(); +} + +static int jz_audio_release(struct inode *inode, struct file *file); +static int jz_audio_open(struct inode *inode, struct file *file); +static int jz_audio_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg); +static unsigned int jz_audio_poll(struct file *file,struct poll_table_struct *wait); +static ssize_t jz_audio_write(struct file *file, const char *buffer,size_t count, loff_t *ppos); +static ssize_t jz_audio_read(struct file *file, char *buffer,size_t count, loff_t *ppos); + +/* static struct file_operations jz_pcm_audio_fops */ +static struct file_operations jz_pcm_audio_fops = +{ + owner: THIS_MODULE, + open: jz_audio_open, + release: jz_audio_release, + write: jz_audio_write, + read: jz_audio_read, + poll: jz_audio_poll, + ioctl: jz_audio_ioctl +}; + +static int jz_pcm_open_mixdev(struct inode *inode, struct file *file) +{ + int i; + int minor = MINOR(inode->i_rdev); + struct jz_pcm_controller_info *controller = pcm_controller; + + for (i = 0; i < NR_PCM; i++) + if (controller->pcm_codec[i] != NULL && controller->pcm_codec[i]->dev_mixer == minor) + goto match; + + if (!controller) + return -ENODEV; +match: + file->private_data = controller->pcm_codec[i]; + + return 0; +} + +static int jz_pcm_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct pcm_codec *codec = (struct pcm_codec *)file->private_data; + return codec->mixer_ioctl(codec, cmd, arg); +} + +static loff_t jz_pcm_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +static struct file_operations jz_pcm_mixer_fops = +{ + owner: THIS_MODULE, + llseek: jz_pcm_llseek, + ioctl: jz_pcm_ioctl_mixdev, + open: jz_pcm_open_mixdev, +}; + +static int pcm_mixer_ioctl(struct pcm_codec *codec, unsigned int cmd, unsigned long arg) +{ + int ret; + long val = 0; + + switch (cmd) { + case SOUND_MIXER_READ_STEREODEVS: + return put_user(0, (long *) arg); + case SOUND_MIXER_READ_CAPS: + val = SOUND_CAP_EXCL_INPUT; + return put_user(val, (long *) arg); + case SOUND_MIXER_READ_DEVMASK: + break; + case SOUND_MIXER_READ_RECMASK: + break; + case SOUND_MIXER_READ_RECSRC: + break; + case SOUND_MIXER_WRITE_VOLUME: + ret = get_user(val, (long *) arg); + if (ret) + return ret; + val = val & 0xff; + if(val < 0) + val = 0; + if(val > 100) + val = 100; + if (val == 100) { + dump_pcmc_reg(); + return 100; + } + val = val / 10; + switch (val) { + case 0: + case 1: + jz_audio_volume = 7; + break; + case 2: + jz_audio_volume = 6; + break; + case 3: + jz_audio_volume = 5; + break; + case 4: + jz_audio_volume = 4; + break; + case 5: + jz_audio_volume = 3; + break; + case 6: + jz_audio_volume = 2; + break; + case 7: + case 8: + jz_audio_volume = 1; + break; + case 9: + case 10: + jz_audio_volume = 0; + break; + } + return 0; + case SOUND_MIXER_READ_VOLUME: + val = jz_audio_volume; + ret = val << 8; + val = val | ret; + return put_user(val, (long *) arg); + case SOUND_MIXER_WRITE_MIC: + printk("Can not justify Mic gain to the PCM codec.!\n"); + return -ENOSYS; + + case SOUND_MIXER_READ_MIC: + printk("Can not justify Mic gain to the PCM codec.!\n"); + return -ENOSYS; + default: + return -ENOSYS; + } + + return 0; +} + + +int pcm_probe_codec(struct pcm_codec *codec) +{ + /* generic OSS to PCM wrapper */ + codec->mixer_ioctl = pcm_mixer_ioctl; + return 1; +} + +/* PCM codec initialisation. */ +static int __init jz_pcm_codec_init(struct jz_pcm_controller_info *controller) +{ + int num_pcm = 0; + struct pcm_codec *codec; + + for (num_pcm = 0; num_pcm < NR_PCM; num_pcm++) { + if ((codec = kmalloc(sizeof(struct pcm_codec),GFP_KERNEL)) == NULL) + return -ENOMEM; + memset(codec, 0, sizeof(struct pcm_codec)); + codec->private_data = controller; + codec->id = num_pcm; + + if (pcm_probe_codec(codec) == 0) + break; + if ((codec->dev_mixer = register_sound_mixer(&jz_pcm_mixer_fops, -1)) < 0) { + printk(KERN_ERR "Jz PCM: couldn't register mixer!\n"); + kfree(codec); + break; + } + controller->pcm_codec[num_pcm] = codec; + } + + return num_pcm; +} + +static void jz_update_filler(int format, int channels) +{ +#define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3)) + + switch (TYPE(format, channels)) { + default: + case TYPE(AFMT_U8, 1): + replay_filler = replay_fill_1x8_u; + record_filler = record_fill_1x8_u; + break; + case TYPE(AFMT_U8, 2): + /*replay_filler = replay_fill_2x8_u; + record_filler = record_fill_2x8_u;*/ + printk("channel is 2. Line:%d\n",__LINE__); + break; + case TYPE(AFMT_S16_LE, 1): + replay_filler = replay_fill_1x16_s; + record_filler = record_fill_1x16_s; + break; + case TYPE(AFMT_S16_LE, 2): + /*replay_filler = replay_fill_2x16_s; + record_filler = record_fill_2x16_s;*/ + printk("channel is 2. Line:%d\n",__LINE__); + break; + } +} + +static void __init attach_jz_pcm(struct jz_pcm_controller_info *controller) +{ + char *name; + int adev;//No of Audio device. + int err; + + name = controller->name; + /* register /dev/audio */ + adev = register_sound_dsp(&jz_pcm_audio_fops, -1); + if (adev < 0) + goto audio_failed; + /* initialize PCM codec and register /dev/mixer */ + if (jz_pcm_codec_init(controller) <= 0) + goto mixer_failed; + + controller->tmp1 = (void *)__get_free_pages(GFP_KERNEL, JZCODEC_USER_BUFFER); + if (!controller->tmp1) { + printk(KERN_ERR "%s: can't allocate tmp buffers.\n", controller->name); + goto tmp1_failed; + } + printk("DMA_ID_PCM_TX=0x%08x DMA_ID_PCM_RX=0x%08x\n",DMA_ID_PCM_TX ,DMA_ID_PCM_RX); + if ((controller->dma1 = jz_request_dma(DMA_ID_PCM_TX, "audio dac", jz_pcm_replay_dma_irq, IRQF_DISABLED, controller)) < 0) { + printk(KERN_ERR "%s: can't reqeust DMA DAC channel.\n", name); + goto dma1_failed; + } + if ((controller->dma2 = jz_request_dma(DMA_ID_PCM_RX, "audio adc", jz_pcm_record_dma_irq, IRQF_DISABLED, controller)) < 0) { + printk(KERN_ERR "%s: can't reqeust DMA ADC channel.\n", name); + goto dma2_failed; + } + printk("JzSOC On-Chip PCM controller registered (DAC: DMA(play):%d/IRQ%d,\n ADC: DMA(record):%d/IRQ%d)\n", controller->dma1, get_dma_done_irq(controller->dma1), controller->dma2, get_dma_done_irq(controller->dma2)); + + err = request_irq(IRQ_PCM, jz_pcm_irq, IRQF_DISABLED, "pcm irq", controller); + if (err < 0) + printk("can't allocate pcm irq.\n"); + controller->dev_audio = adev; + + return; +dma2_failed: + jz_free_dma(controller->dma2); +dma1_failed: + jz_free_dma(controller->dma1); +tmp1_failed: + free_pages((unsigned long)controller->tmp1, JZCODEC_USER_BUFFER); + +mixer_failed: + unregister_sound_dsp(adev); +audio_failed: + return; +} + + +static int __init probe_jz_pcm(struct jz_pcm_controller_info **controller) +{ + if ((*controller = kmalloc(sizeof(struct jz_pcm_controller_info), + GFP_KERNEL)) == NULL) { + printk(KERN_ERR "Jz PCM Controller: out of memory.\n"); + return -ENOMEM; + } + + (*controller)->name = "Jz PCM controller"; + (*controller)->opened1 = 0; + (*controller)->opened2 = 0; + init_waitqueue_head(&(*controller)->adc_wait); + init_waitqueue_head(&(*controller)->dac_wait); + spin_lock_init(&(*controller)->lock); + init_waitqueue_head(&rx_wait_queue); + init_waitqueue_head(&tx_wait_queue); + + return 0; +} + +static void __exit unload_jz_pcm(struct jz_pcm_controller_info *controller) +{ + int adev = controller->dev_audio; + + __pcm_reset(); + schedule_timeout(5); + __pcm_disable(); + __pcm_clk_disable(); + __pcm_flush_fifo(); + + controller->dev_audio = -1; + jz_free_dma(controller->dma1); + jz_free_dma(controller->dma2); + free_pages((unsigned long)controller->tmp1, JZCODEC_USER_BUFFER); + + if (adev >= 0) + unregister_sound_dsp(controller->dev_audio); +} + +static int __init init_jz_pcm(void) +{ + int errno; + /* 24M OSC ---> CPCCR.ECS ---> PCMCDR.PCMS ---> cpm_pcm_sysclk(X) */ + long X = 12000000; //in Hz /* 6.144 MHz <= X <= 264.192 MHz */ + +#if 0 + /* pcm_sys_clk is from PLL divsion */ + REG_CPM_PCMCDR = 0x8000001b; + REG_CPM_CPCCR |= 0x00400000; +#endif + /* pcm_sys_clk is from external clock */ + /* reset codec GPF4 */ + __gpio_as_output(32 * 5 + 4); + __gpio_set_pin(32 * 5 + 4); + mdelay(1); + + __pcm_reset(); + schedule_timeout(5); + __pcm_clk_disable(); + + /* set CPM to output cpm-pcm-sysclk ,assume cpm-pcm-sysclk is X Hz */ + /* PCMCLK must be 2048000 Hz, it is 256 mutil of PCMSYNC */ + //__pcm_set_clk_rate(X, 2048000); + __pcm_set_clk_rate(X, 2000000); + /* PCMSYNC must be 8000 Hz. 2048000 / 256 = 8000 */ + __pcm_set_sync_rate(2048000, 8000); + //__pcm_set_sync_rate(2000000, 8000); + __pcm_set_sync_len(0); + + __pcm_flush_fifo(); + __pcm_disable_txfifo(); + __pcm_disable_rxfifo(); + __pcm_set_transmit_trigger(7); + __pcm_set_receive_trigger(7); + __pcm_omsb_next_sync(); + __pcm_imsb_next_sync(); + +#if MODE_is_8 + __pcm_set_iss(8);//8bits decided by LINSEL + __pcm_set_oss(8); +#else + __pcm_set_iss(16);//16bits decided by LINSEL + __pcm_set_oss(16); +#endif + __pcm_set_valid_slot(1); + + __pcm_disable_tfs_intr(); + __pcm_disable_tur_intr(); + __pcm_disable_rfs_intr(); + __pcm_disable_ror_intr(); + + __pcm_disable_receive_dma(); + __pcm_disable_transmit_dma(); + + __pcm_last_sample(); + __pcm_as_master(); + + __pcm_enable(); + __pcm_clk_enable(); + + if ((errno = probe_jz_pcm(&pcm_controller)) < 0) + return errno; + attach_jz_pcm(pcm_controller); + + out_empty_queue.id = NULL; + out_full_queue.id = NULL; + out_busy_queue.id = NULL; + in_empty_queue.id = NULL; + in_full_queue.id = NULL; + in_busy_queue.id = NULL; + + jz_audio_fragsize = JZCODEC_RW_BUFFER_SIZE * PAGE_SIZE; + jz_audio_fragstotal = JZCODEC_RW_BUFFER_TOTAL ; + Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); + + __gpio_clear_pin(32 * 5 + 4); + udelay(100); + + __gpio_set_pin(32 * 5 + 4); + dump_pcmc_reg(); + + return 0; +} + + +static void __exit cleanup_jz_pcm(void) +{ + unload_jz_pcm(pcm_controller); + Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); +} + +module_init(init_jz_pcm); +module_exit(cleanup_jz_pcm); + +static int drain_adc(struct jz_pcm_controller_info *ctrl, int nonblock) +{ + unsigned long flags; + int count, i=0; + + for (;;) { + if ( i < MAXDELAY ) { + udelay(10); + i++; + } else + break; + + spin_lock_irqsave(&ctrl->lock, flags); + count = get_dma_residue(ctrl->dma2); + spin_unlock_irqrestore(&ctrl->lock, flags); + if (count <= 0) + break; + + if (nonblock) + return -EBUSY; + } + + return 0; +} +static int drain_dac(struct jz_pcm_controller_info *ctrl, int nonblock) +{ + unsigned long flags; + int count, ele, i=0; + + for (;;) { + if(!nonblock) {//blocked + if ( i < MAXDELAY ) { + udelay(10); + i++; + } else + break; + + ele = elements_in_queue(&out_full_queue); + if(ele <= 0) { + udelay(10); + spin_lock_irqsave(&ctrl->lock, flags); + count = get_dma_residue(ctrl->dma1); + spin_unlock_irqrestore(&ctrl->lock, flags); + if (count <= 0) + break; + } + } else {//non-blocked + mdelay(100); + ele = elements_in_queue(&out_full_queue); + + if(ele <= 0) { + mdelay(100); + + spin_lock_irqsave(&ctrl->lock, flags); + count = get_dma_residue(ctrl->dma1); + spin_unlock_irqrestore(&ctrl->lock, flags); + if (count <= 0) + break; + } + } + } + + return 0; +} + +static int jz_audio_release(struct inode *inode, struct file *file) +{ + struct jz_pcm_controller_info *controller = (struct jz_pcm_controller_info *) file->private_data; + + if (controller == NULL) + return -ENODEV; + + if ( controller->opened1 == 1 ) { + __pcm_enable_transmit_dma(); + __pcm_enable_txfifo(); + drain_dac(controller, file->f_flags & O_NONBLOCK); + disable_dma(controller->dma1); + set_dma_count(controller->dma1, 0); + + __pcm_disable_transmit_dma(); + __pcm_disable_txfifo(); + + spin_lock(&controller->ioctllock); + controller->total_bytes = 0; + controller->count = 0; + controller->finish = 0; + jz_audio_dma_tran_count = 0; + controller->blocks = 0; + controller->nextOut = 0; + spin_unlock(&controller->ioctllock); + //__pcm_disable(); + controller->opened1 = 0; + } + + if ( controller->opened2 == 1 ) { + first_record_call = 1; + __pcm_enable_receive_dma(); + __pcm_enable_rxfifo(); + drain_adc(controller, file->f_flags & O_NONBLOCK); + disable_dma(controller->dma2); + set_dma_count(controller->dma2, 0); + __pcm_disable_receive_dma(); + __pcm_disable_rxfifo(); + + spin_lock(&controller->ioctllock); + controller->total_bytes = 0; + jz_audio_dma_tran_count = 0; + controller->count = 0; + controller->finish = 0; + controller->blocks = 0; + controller->nextIn = 0; + spin_unlock(&controller->ioctllock); + //__pcm_disable(); + controller->opened2 = 0; + } + __pcm_disable_tfs_intr(); + __pcm_disable_tur_intr(); + __pcm_disable_rfs_intr(); + __pcm_disable_ror_intr(); + + return 0; +} + +static int jz_audio_open(struct inode *inode, struct file *file) +{ + int i; + struct jz_pcm_controller_info *controller = pcm_controller; + + if (controller == NULL) + return -ENODEV; + + if (controller->opened1 == 1 || controller->opened2 == 1 ) { + printk("\naudio is busy!\n"); + return -EBUSY; + } + REG_DMAC_DMACKE(0) = 0x3f; + REG_DMAC_DMACKE(1) = 0x3f; + if (file->f_mode & FMODE_WRITE) { + if (controller->opened1 == 1) + return -EBUSY; + controller->opened1 = 1; + //for ioctl + controller->total_bytes = 0; + jz_audio_dma_tran_count = 0; + controller->count = 0; + controller->finish = 0; + controller->blocks = 0; + controller->nextOut = 0; + + out_empty_queue.count = jz_audio_fragstotal; + for (i=0;i < jz_audio_fragstotal;i++) + *(out_empty_queue.id + i) = i; + out_busy_queue.count = 0; + out_full_queue.count = 0; + /* set PCMOUT params */ + } + + if (file->f_mode & FMODE_READ) { + if (controller->opened2 == 1) + return -EBUSY; + controller->opened2 = 1; + first_record_call = 1; + //for ioctl + controller->total_bytes = 0; + jz_audio_dma_tran_count = 0; + controller->count = 0; + controller->finish = 0; + controller->blocks = 0; + controller->nextIn = 0; + in_empty_queue.count = jz_audio_fragstotal; + for (i=0;i < jz_audio_fragstotal;i++) + *(in_empty_queue.id + i) = i; + in_full_queue.count = 0; + in_busy_queue.count = 0; + /* set PCMIN params */ + } + + file->private_data = controller; + jz_audio_reset(); + __pcm_enable(); + + return 0; +} + +static int jz_audio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int val,fullc,busyc,unfinish,newfragstotal,newfragsize; + unsigned int flags; + audio_buf_info abinfo; + int i, bytes, id; + count_info cinfo; + struct jz_pcm_controller_info *controller = (struct jz_pcm_controller_info *) file->private_data; + + val = 0; + bytes = 0; + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + case SNDCTL_DSP_RESET: + return 0; + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + drain_dac(controller, file->f_flags & O_NONBLOCK); + return 0; + case SNDCTL_DSP_SPEED: + { + if (get_user(val, (int *)arg)) + return -EFAULT; + /* set smaple rate */ + if (val >= 0) + jz_audio_set_speed(controller->dev_audio, val); + return put_user(val, (int *)arg); + } + case SNDCTL_DSP_STEREO: + /* set stereo or mono channel */ + if (get_user(val, (int *)arg)) + return -EFAULT; + + jz_audio_set_channels(controller->dev_audio, val ? 2 : 1); + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + //return put_user(4*PAGE_SIZE, (int *)arg); + return put_user(jz_audio_fragsize , (int *)arg); + case SNDCTL_DSP_GETFMTS: + /* Returns a mask of supported sample format*/ + return put_user(AFMT_U8 | AFMT_S16_LE, (int *)arg); + + case SNDCTL_DSP_SETFMT: + { + if (get_user(val, (int *)arg)) + return -EFAULT; + /* Select sample format */ + if (val != AFMT_QUERY) + jz_audio_set_format(controller->dev_audio,val); + else + if (file->f_mode & FMODE_READ) + val = (jz_audio_format == 16) ? AFMT_S16_LE : AFMT_U8; + else + val = (jz_audio_format == 16) ? AFMT_S16_LE : AFMT_U8; + return put_user(val, (int *)arg); + } + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + printk("%s:%s:%d\n",__FILE__,__FUNCTION__,__LINE__); + jz_audio_set_channels(controller->dev_audio, val); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_POST: + /* FIXME: the same as RESET ?? */ + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + return 0; + + case SNDCTL_DSP_SETFRAGMENT: + get_user(val, (long *) arg); + newfragsize = 1 << (val & 0xFFFF);//16 least bits + + if (newfragsize < 4 * PAGE_SIZE) + newfragsize = 4 * PAGE_SIZE; + if (newfragsize > (16 * PAGE_SIZE)) //16 PAGE_SIZE + newfragsize = 16 * PAGE_SIZE; + + newfragstotal = (val >> 16) & 0x7FFF; + if (newfragstotal < 2) + newfragstotal = 2; + if (newfragstotal > 32) + newfragstotal = 32; + if((jz_audio_fragstotal == newfragstotal) && (jz_audio_fragsize == newfragsize)) + return 0; + Free_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); + mdelay(500); + jz_audio_fragstotal = newfragstotal; + jz_audio_fragsize = newfragsize; + + Init_In_Out_queue(jz_audio_fragstotal,jz_audio_fragsize); + mdelay(10); + + return 0; + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_REALTIME|DSP_CAP_BATCH, (int *)arg); + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + case SNDCTL_DSP_SETDUPLEX: + return -EINVAL; + case SNDCTL_DSP_GETOSPACE: + { + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + //unused fragment amount + spin_lock_irqsave(&controller->ioctllock, flags); + jz_audio_fragments = elements_in_queue(&out_empty_queue); + for (i = 0; i < jz_audio_fragments; i++) + bytes += jz_audio_fragsize; + + spin_unlock_irqrestore(&controller->ioctllock, flags); + abinfo.fragments = jz_audio_fragments; + abinfo.fragstotal = jz_audio_fragstotal; + abinfo.fragsize = jz_audio_fragsize; + abinfo.bytes = bytes; + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + } + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + bytes = 0; + //unused fragment amount + jz_audio_fragments = elements_in_queue(&in_empty_queue); + for (i = 0; i < jz_audio_fragments; i++) + bytes += jz_audio_fragsize; + + abinfo.fragments = jz_audio_fragments; + abinfo.fragstotal = jz_audio_fragstotal; + abinfo.fragsize = jz_audio_fragsize; + abinfo.bytes = bytes; + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && in_dma_buf) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && out_dma_buf) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + return 0; + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + + spin_lock_irqsave(&controller->ioctllock, flags); + cinfo.bytes = controller->total_bytes; + cinfo.blocks = controller->blocks; + cinfo.ptr = controller->nextIn; + controller->blocks = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&controller->ioctllock, flags); + cinfo.bytes = controller->total_bytes; + cinfo.blocks = controller->blocks; + cinfo.ptr = controller->nextOut; + controller->blocks = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&controller->ioctllock, flags); + unfinish = 0; + fullc = elements_in_queue(&out_full_queue); + busyc = elements_in_queue(&out_busy_queue); + for(i = 0;i < fullc ;i ++) { + id = *(out_full_queue.id + i); + unfinish += *(out_dma_buf_data_count + id); + } + for(i = 0;i < busyc ;i ++) { + id = *(out_busy_queue.id + i); + unfinish += get_dma_residue(controller->dma1); + } + spin_unlock_irqrestore(&controller->ioctllock, flags); + return put_user(unfinish, (int *) arg); + case SOUND_PCM_READ_RATE: + return put_user(jz_audio_rate, (int *)arg); + case SOUND_PCM_READ_CHANNELS: + return put_user(jz_audio_channels, (int *)arg); + case SOUND_PCM_READ_BITS: + return put_user((jz_audio_format & (AFMT_S8 | AFMT_U8)) ? 8 : 16, (int *)arg); + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + return -EINVAL; +} + +static unsigned int jz_audio_poll(struct file *file,struct poll_table_struct *wait) +{ + unsigned long flags; + unsigned int mask = 0; + struct jz_pcm_controller_info *controller = (struct jz_pcm_controller_info *) file->private_data; + + if (file->f_mode & FMODE_WRITE) { + if (elements_in_queue(&out_empty_queue) > 0) + return POLLOUT | POLLWRNORM; + poll_wait(file, &controller->dac_wait, wait); + } + + if (file->f_mode & FMODE_READ) { + if (elements_in_queue(&in_full_queue) > 0) + return POLLIN | POLLRDNORM; + poll_wait(file, &controller->adc_wait, wait); + } + + spin_lock_irqsave(&controller->lock, flags); + if (file->f_mode & FMODE_WRITE) { + if (elements_in_queue(&out_empty_queue) > 0) + mask |= POLLOUT | POLLWRNORM; + } else if (file->f_mode & FMODE_READ) { + if (elements_in_queue(&in_full_queue) > 0) + mask |= POLLIN | POLLRDNORM; + } + spin_unlock_irqrestore(&controller->lock, flags); + + return mask; +} + +static ssize_t jz_audio_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + int id, ret = 0, left_count, copy_count, cnt = 0; + struct jz_pcm_controller_info *controller = (struct jz_pcm_controller_info *) file->private_data; + + if (count < 0) + return -EINVAL; + + __pcm_enable_receive_dma(); + __pcm_enable_rxfifo(); + + spin_lock(&controller->ioctllock); + controller->nextIn = 0; + spin_unlock(&controller->ioctllock); + + spin_lock(&controller->lock); + +#if 0 + if (count < 2 * PAGE_SIZE ) + copy_count = count * 16 / (jz_audio_channels * jz_audio_format); + else + copy_count = 2 * PAGE_SIZE ; +#else + if (count <= jz_audio_fragsize) + copy_count = count; + else + copy_count = jz_audio_fragsize; +#endif + + left_count = count; + spin_unlock(&controller->lock); + + if (first_record_call) { + first_record_call = 0; +audio_read_back_first: + if ((id = get_buffer_id(&in_empty_queue)) >= 0) { + put_buffer_id(&in_busy_queue, id); + + spin_lock(&controller->lock); + *(in_dma_buf_data_count + id) = copy_count; + spin_unlock(&controller->lock); + + dma_cache_wback_inv(*(in_dma_buf + id), *(in_dma_buf_data_count + id)); + audio_start_dma(controller->dma2,file->private_data, + *(in_dma_pbuf + id), + *(in_dma_buf_data_count + id), + DMA_MODE_READ); + + sleep_on(&rx_wait_queue); + } else + goto audio_read_back_first; + } + + while (left_count > 0) { +audio_read_back_second: + if (elements_in_queue(&in_full_queue) <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + else + sleep_on(&rx_wait_queue); + } + + if ((id = get_buffer_id(&in_full_queue)) >= 0) { + spin_lock(&controller->lock); + cnt = record_filler((unsigned long)controller->tmp1+ret, copy_count, id); + spin_unlock(&controller->lock); + put_buffer_id(&in_empty_queue, id); + } else + goto audio_read_back_second; + + if (elements_in_queue(&in_busy_queue) == 0) { + if ((id=get_buffer_id(&in_empty_queue)) >= 0) { + put_buffer_id(&in_busy_queue, id); + spin_lock(&controller->lock); + *(in_dma_buf_data_count + id) = copy_count; + spin_unlock(&controller->lock); + + dma_cache_wback_inv(*(in_dma_buf + id), *(in_dma_buf_data_count + id)); + audio_start_dma(controller->dma2,file->private_data, + *(in_dma_pbuf + id), + *(in_dma_buf_data_count + id), + DMA_MODE_READ); + } + } + if (ret + cnt > count) { + spin_lock(&controller->lock); + cnt = count - ret; + spin_unlock(&controller->lock); + } + if (copy_to_user(buffer+ret, controller->tmp1+ret, cnt)) + return ret ? ret : -EFAULT; + spin_lock(&controller->lock); + ret += cnt; + spin_unlock(&controller->lock); + + spin_lock(&controller->ioctllock); + controller->nextIn += ret; + spin_unlock(&controller->ioctllock); + + spin_lock(&controller->lock); + left_count -= cnt; + spin_unlock(&controller->lock); + } + + return ret; +} + +static ssize_t jz_audio_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + int id, ret, left_count, copy_count; + unsigned int flags; + struct jz_pcm_controller_info *controller = (struct jz_pcm_controller_info *) file->private_data; + + if (count <= 0) + return -EINVAL; + + __pcm_enable_transmit_dma(); + __pcm_enable_txfifo(); + + spin_lock_irqsave(&controller->ioctllock, flags); + controller->nextOut = 0; + spin_unlock_irqrestore(&controller->ioctllock, flags); + +#if 0 + if (count < 2 * PAGE_SIZE ) + copy_count = count; + else + copy_count = 2 * PAGE_SIZE; +#else + if (count <= jz_audio_fragsize) + copy_count = count; + else + copy_count = jz_audio_fragsize; +#endif + + left_count = count; + ret = 0; + + if (copy_from_user(controller->tmp1, buffer, count)) { + printk("copy_from_user failed:%d",ret); + return ret ? ret : -EFAULT; + } + + while (left_count > 0) { +audio_write_back: + if (elements_in_queue(&out_empty_queue) == 0) { + // all are full + if (file->f_flags & O_NONBLOCK) + return ret; + else + sleep_on(&tx_wait_queue); + } + /* the end fragment size in this write */ + if (ret + copy_count > count) + copy_count = count - ret; + if ((id = get_buffer_id(&out_empty_queue)) >= 0) { + replay_filler((unsigned long)controller->tmp1 + ret, copy_count, id); + if(*(out_dma_buf_data_count + id) > 0) { + put_buffer_id(&out_full_queue, id); //busy in + dma_cache_wback_inv(*(out_dma_buf + id), *(out_dma_buf_data_count + id)); + } else + put_buffer_id(&out_empty_queue, id); //spare + } else + goto audio_write_back; + + left_count = left_count - copy_count; + ret += copy_count;//all is in byte + + spin_lock(&controller->ioctllock); + controller->nextOut += ret; + spin_unlock(&controller->ioctllock); + + if (elements_in_queue(&out_busy_queue) == 0) { + if ((id=get_buffer_id(&out_full_queue)) >= 0) { + put_buffer_id(&out_busy_queue, id); + + if(*(out_dma_buf_data_count + id) > 0) { + audio_start_dma(controller->dma1, + file->private_data, + *(out_dma_pbuf + id), + *(out_dma_buf_data_count + id), + DMA_MODE_WRITE); + + } + } + } + } + + return ret; +} + +static void dump_pcmc_reg(void) +{ + printk("REG_DMAC_DMACKE(0) : 0x%08x\n",REG_DMAC_DMACKE(0)); + printk("REG_DMAC_DMACKE(1) : 0x%08x\n",REG_DMAC_DMACKE(1)); + printk("REG_CPM_CLKGR=0x%08x\n",REG_CPM_CLKGR); + printk("REG_CPM_CPCCR : 0x%08x\n",REG_CPM_CPCCR); + printk("REG_CPM_PCMCDR : 0x%08x\n",REG_CPM_PCMCDR); + printk("REG_PCM_CLT : 0x%08x\n",REG_PCM_CTL); + printk("REG_PCM_CFG : 0x%08x\n",REG_PCM_CFG); + printk("REG_PCM_INTC : 0x%08x\n",REG_PCM_INTC); + printk("REG_PCM_INTS : 0x%08x\n",REG_PCM_INTS); + printk("REG_PCM_DIV : 0x%08x\n",REG_PCM_DIV); +} diff --git a/sound/oss/jzcodec.c b/sound/oss/jzcodec.c new file mode 100644 index 0000000..680b1d2 --- /dev/null +++ b/sound/oss/jzcodec.c @@ -0,0 +1,443 @@ +/* + * linux/drivers/sound/jzcodec.c + * + * JzSOC internal audio driver. + * + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sound_config.h" + +#define USE_NONE 1 +#define USE_MIC 2 +#define USE_LINEIN 3 + +typedef struct hpvol_shift_s +{ + int hpvol; + int shift; +} hpvol_shift_t; + +extern mixer_info info; +extern _old_mixer_info old_info; +extern int codec_volue_shift; +extern hpvol_shift_t hpvol_shift_table[72]; +extern int abnormal_data_count; + +extern void (*set_codec_mode)(void); +extern void (*each_time_init_codec)(void); +extern int (*set_codec_startup_param)(void); +extern void (*set_codec_volume_table)(void); +extern void (*set_codec_record)(int mode); +extern void (*set_codec_replay)(void); +extern void (*set_codec_replay_record)(int mode); +extern void (*turn_on_codec)(void); +extern void (*turn_off_codec)(void); +extern void (*set_codec_speed)(int rate); +extern void (*reset_codec)(void); +extern void (*codec_mixer_old_info_id_name)(void); +extern void (*codec_mixer_info_id_name)(void); +extern void (*set_codec_bass)(int val); +extern void (*set_codec_volume)(int val); +extern void (*set_codec_mic)(int val); +extern void (*set_codec_line)(int val); +extern void (*i2s_resume_codec)(void); +extern void (*i2s_suspend_codec)(void); +extern void (*set_codec_direct_mode)(void); +extern void (*clear_codec_direct_mode)(void); + +static int jzcodec_reg[2]; + +void set_jzcodec_mode(void); +void each_time_init_jzcodec(void); +int set_jzcodec_startup_param(void); +void set_jzcodec_volume_table(void); +void set_jzcodec_replay(void); +void set_jzcodec_record(int mode); +void turn_on_jzcodec(void); +void turn_off_jzcodec(void); +void set_jzcodec_speed(int rate); +void reset_jzcodec(void); +void jzcodec_mixer_old_info_id_name(void); +void jzcodec_mixer_info_id_name(void); +void set_jzcodec_bass(int val); +void set_jzcodec_volume(int val); +void set_jzcodec_mic(int val); +void set_jzcodec_line(int val); +void in_codec_app1(void); +void in_codec_app12(void); +void HP_turn_on(void); +void HP_turn_off(void); +void resume_jzcodec(void); +void suspend_jzcodec(void); +void set_jzcodec_replay_record(int mode); + +void set_jzcodec_mode(void) +{ + __i2s_internal_codec(); + __i2s_as_slave(); + __i2s_select_i2s(); + __aic_select_i2s(); + + REG_ICDC_CDCCR1 = 0x001b2303; + schedule_timeout(1); + REG_ICDC_CDCCR1 = 0x001b2302; +} + +void each_time_init_jzcodec(void) +{ + __i2s_disable(); + __i2s_as_slave(); + __i2s_set_oss_sample_size(16); + __i2s_set_iss_sample_size(16); +} + +int set_jzcodec_startup_param(void) +{ + REG_ICDC_CDCCR1 = 0x001b2300; + schedule_timeout(50); + REG_ICDC_CDCCR1 = 0x00033300; + schedule_timeout(5); + REG_ICDC_CDCCR1 = 0x17026300; + + return 1; +} +void set_jzcodec_volume_table(void) +{ + int errno,hpvol_step,sample_shift; + + codec_volue_shift = 0; + hpvol_step = 3; + sample_shift = 0; + for(errno = 0;errno < 72;errno++) { + hpvol_shift_table[errno].hpvol = hpvol_step; + hpvol_shift_table[errno].shift = sample_shift; + hpvol_step --; + if(hpvol_step <= 0) { + hpvol_step = 3; + sample_shift ++; + } + } +} + +void set_jzcodec_replay(void) +{ + in_codec_app1(); +} + +void set_jzcodec_record(int mode) +{ + switch (mode) { + case USE_NONE: + case USE_MIC: + in_codec_app12(); + abnormal_data_count = 0; + break; + case USE_LINEIN: + REG_ICDC_CDCCR1 = 0x27022000;//for linein + mdelay(300); + break; + } +} + +void set_jzcodec_replay_record(int mode) +{ + long val = 0; + REG_ICDC_CDCCR1 = 0x00037302; + mdelay(2); + REG_ICDC_CDCCR1 = 0x03006000; + mdelay(2); + + switch (mode) { + case USE_NONE: + case USE_MIC: + REG_ICDC_CDCCR1 = 0x17022000;//for mic + break; + case USE_LINEIN: + REG_ICDC_CDCCR1 = 0x27022000;//for linein + break; + } + + val = REG_ICDC_CDCCR2; + val &= 0x0000ff00; + val |= 0x00170030; + REG_ICDC_CDCCR2 = val; +} + +void turn_on_jzcodec(void) +{ + HP_turn_on(); +} + +void set_jzcodec_direct_mode(void) +{ + long val = 0; + REG_ICDC_CDCCR1 = 0x14000000; + val &= 0x0000ff00; + val |= 0x001f0033; + REG_ICDC_CDCCR2 = val; + mdelay(300); +} + +void clear_jzcodec_direct_mode(void) +{ + HP_turn_off(); +} + +void turn_off_jzcodec(void) +{ + HP_turn_off(); +} + +void set_jzcodec_speed(int rate) +{ + long codec_speed,speed = 0; + switch (rate) { + case 8000: + speed = 0; + break; + case 11025: + speed = 1; + break; + case 12000: + speed = 2; + break; + case 16000: + speed = 3; + break; + case 22050: + speed = 4; + break; + case 24000: + speed = 5; + break; + case 32000: + speed = 6; + break; + case 44100: + speed = 7; + break; + case 48000: + speed = 8; + break; + default: + break; + } + + codec_speed = REG_ICDC_CDCCR2; + codec_speed |= 0x00000f00; + + speed = speed << 8; + speed |= 0xfffff0ff; + codec_speed &= speed; + REG_ICDC_CDCCR2 = codec_speed; +} + +void reset_jzcodec(void) +{ + REG_ICDC_CDCCR1 |= 1; + mdelay(1); + REG_ICDC_CDCCR1 &= 0xfffffffe; +} + +void jzcodec_mixer_old_info_id_name(void) +{ + strncpy(info.id, "JZCODEC", sizeof(info.id)); + strncpy(info.name,"Jz internal codec", sizeof(info.name)); +} + +void jzcodec_mixer_info_id_name(void) +{ + strncpy(old_info.id, "JZCODEC", sizeof(old_info.id)); + strncpy(old_info.name,"Jz internal codec", sizeof(old_info.name)); +} + +void set_jzcodec_bass(int val) +{ + int bass_gain = 0; + if(val < 25) + bass_gain = 0; + if(val >= 25 && val < 50) + bass_gain = 1; + if(val >= 50 && val < 75) + bass_gain = 2; + if(val >= 75 && val <= 100 ) + bass_gain = 3; + + REG_ICDC_CDCCR2 = ((REG_ICDC_CDCCR2 & ~(0x3 << 4)) | (bass_gain << 4)); +} + +void set_jzcodec_volume(int val) +{ + unsigned int sample_oss; + int index,shift_max,vol_scale,vol_step,codec_volume; + shift_max = 0; + sample_oss = (REG_AIC_CR & 0x00380000) >> 19; + + switch(sample_oss) + { + case 0x0: + shift_max = 4; /* 8 bits */ + break; + case 0x1: + shift_max = 10; /* 16 bits */ + break; + case 0x2: + shift_max = 12; /* 18 bits */ + break; + case 0x3: + shift_max = 15; /* 20 bits */ + break; + case 0x4: + shift_max = 19; /* 24 bits */ + break; + } + + vol_scale = 3 * (shift_max + 1); + vol_step = 100 / vol_scale; + + for(index = 0;index <= 100;index += vol_step) + if( val <= index ) + break; + + if(index == 0) + index = vol_step; + index = index / vol_step; + if(index > vol_scale) + index = vol_scale; + + index = vol_scale - index; + codec_volume = hpvol_shift_table[index].hpvol; + codec_volue_shift = hpvol_shift_table[index].shift; + codec_volume = 3; + REG_ICDC_CDCCR2 = ((REG_ICDC_CDCCR2 & ~(0x3)) | codec_volume); +} + +void set_jzcodec_mic(int val) +{ + long mic_gain; + + mic_gain = 31 * val /100; + REG_ICDC_CDCCR2 = (REG_ICDC_CDCCR2 | (0x3 << 4)); + REG_ICDC_CDCCR2 = ((REG_ICDC_CDCCR2 & ~(0x1f << 16)) | (mic_gain << 16)); +} + +void set_jzcodec_line(int val) +{ + long line_gain; + + line_gain = 31 * val /100; + REG_ICDC_CDCCR2 = ((REG_ICDC_CDCCR2 & ~(0x1f << 16)) | (line_gain << 16)); +} + +void HP_turn_on(void) +{ + long val = 0; + /* simple and slow anti-pop */ + REG_ICDC_CDCCR1 = 0x00037302; + mdelay(2); + REG_ICDC_CDCCR1 = 0x03006000; + mdelay(2); + REG_ICDC_CDCCR1 = 0x03002000; + + val = REG_ICDC_CDCCR2; + val &= 0x0000ff00; + val |= 0x001f0033; + REG_ICDC_CDCCR2 = val; +} + +void HP_turn_off(void) +{ + mdelay(20); + REG_ICDC_CDCCR1 = 0x00033300; +} + +void in_codec_app1(void) +{ + /* test is OK */ + HP_turn_on(); +} + +void in_codec_app12(void) +{ + REG_ICDC_CDCCR1 = 0x14024300; + mdelay(300); +} + +void resume_jzcodec(void) +{ + REG_ICDC_CDCCR2 = 0x00170800; + mdelay(2); + REG_ICDC_CDCCR1 = 0x001f2102; + mdelay(5); + REG_ICDC_CDCCR1 = 0x00033302; + mdelay(550); + REG_ICDC_CDCCR1 = 0x00033300; + REG_ICDC_CDCCR1 = jzcodec_reg[0]; + REG_ICDC_CDCCR2 = jzcodec_reg[1]; +} + +void suspend_jzcodec(void) +{ + + jzcodec_reg[0] = REG_ICDC_CDCCR1; + jzcodec_reg[1] = REG_ICDC_CDCCR2; + + REG_ICDC_CDCCR1 = 0x001b2302; + mdelay(1); + REG_ICDC_CDCCR1 = 0x001b2102; +} + +static int __init init_jzcodec(void) +{ + set_codec_mode = set_jzcodec_mode; + each_time_init_codec = each_time_init_jzcodec; + + set_codec_startup_param = set_jzcodec_startup_param; + set_codec_volume_table = set_jzcodec_volume_table; + set_codec_record = set_jzcodec_record; + set_codec_replay = set_jzcodec_replay; + set_codec_replay_record = set_jzcodec_replay_record; + turn_on_codec = turn_on_jzcodec; + turn_off_codec = turn_off_jzcodec; + set_codec_speed = set_jzcodec_speed; + reset_codec = reset_jzcodec; + codec_mixer_old_info_id_name = jzcodec_mixer_old_info_id_name; + codec_mixer_info_id_name = jzcodec_mixer_info_id_name; + set_codec_bass = set_jzcodec_bass; + set_codec_volume = set_jzcodec_volume; + set_codec_mic = set_jzcodec_mic; + set_codec_line = set_jzcodec_line; + i2s_resume_codec = resume_jzcodec; + i2s_suspend_codec = suspend_jzcodec; + set_codec_direct_mode = set_jzcodec_direct_mode; + clear_codec_direct_mode = clear_jzcodec_direct_mode; + + return 0; +} + + +static void __exit cleanup_jzcodec(void) +{ + REG_ICDC_CDCCR1 = 0x001b2302; + +} + +module_init(init_jzcodec); +module_exit(cleanup_jzcodec); diff --git a/sound/oss/jzdlv.c b/sound/oss/jzdlv.c new file mode 100644 index 0000000..772b12f --- /dev/null +++ b/sound/oss/jzdlv.c @@ -0,0 +1,644 @@ +/* + * linux/drivers/sound/jzcodec.c + * + * JzSOC internal audio driver. + * + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sound_config.h" +#include "jzdlv.h" + +#define USE_NONE 1 +#define USE_MIC 2 +#define USE_LINEIN 3 + +extern mixer_info info; +extern _old_mixer_info old_info; +extern int codec_volue_shift; + +extern void (*set_codec_mode)(void); +extern void (*each_time_init_codec)(void); +extern int (*set_codec_startup_param)(void); +extern void (*set_codec_record)(void); +extern void (*set_codec_replay)(void); +extern void (*set_codec_replay_record)(void); +extern void (*turn_on_codec)(void); +extern void (*turn_off_codec)(void); +extern void (*set_codec_speed)(int rate); +extern void (*reset_codec)(void); +extern void (*codec_mixer_old_info_id_name)(void); +extern void (*codec_mixer_info_id_name)(void); +extern void (*set_codec_bass)(int val); +extern void (*set_codec_volume)(int val); +extern void (*set_codec_mic)(int val); +extern void (*set_codec_line)(int val); +extern void (*i2s_resume_codec)(void); +extern void (*i2s_suspend_codec)(void); +extern void (*set_codec_direct_mode)(void); +extern void (*clear_codec_direct_mode)(void); + + +void set_dlv_mode(void); +void each_time_init_jzcodec(void); +int set_dlv_startup_param(void); +void set_dlvjzcodec_volume_table(void); +void set_dlv_replay(void); +void set_dlv_record(void); +void set_dlv_speed(int rate); +void reset_dlv(void); +void jzcodec_mixer_old_info_id_name(void); +void jzcodec_mixer_info_id_name(void); +void set_dlv_volume(int val); +void set_dlv_mic(int val); + +extern int jz_mic_only; +int read_codec_file(int addr) +{ + while (__icdc_rgwr_ready()); + __icdc_set_addr(addr); + mdelay(1); + return(__icdc_get_value()); +} + +#if 0 +void printk_codec_files(void) +{ + int cnt; + + printk("\n"); + + 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); + + for (cnt = 0; cnt <= 27 ; cnt++) { + printk(" ( %d : 0x%x ) ",cnt ,read_codec_file(cnt)); + } + printk("\n"); +} +#endif + +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); +} + +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 */ + + while (__icdc_rgwr_ready()); + __icdc_set_addr(addr); + val &= ~(1 << mask_bit); + if (bitval == 1) + val |= 1 << mask_bit; + + __icdc_set_cmd(val); /* write */ + mdelay(1); + __icdc_set_rgwr(); + mdelay(1); + + while (__icdc_rgwr_ready()); + __icdc_set_addr(addr); + val = __icdc_get_value(); /* read */ + + if (((val >> mask_bit) & bitval) == bitval) + return 1; + else + return 0; +} +void set_dlv_mode(void) +{ + /*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(9, 0xff); + write_codec_file(8, 0x3f); + mdelay(10); +} +void reset_dlv_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 +} + +void each_time_init_dlv(void) +{ + __i2s_disable(); + __i2s_as_slave(); + __aic_internal_codec(); + __i2s_set_oss_sample_size(16); + __i2s_set_iss_sample_size(16); +} + +int set_dlv_startup_param(void) +{ + __i2s_disable_transmit_intr(); + __i2s_disable_receive_intr(); + + return 1; +} +/* set Audio data replay */ +void set_audio_data_replay(void) +{ + /* DAC path */ + write_codec_file(9, 0xff); + //write_codec_file(8, 0x30); + write_codec_file(8, 0x20); + mdelay(10); + 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(1, 1, 7);//CR1.SB_MICBIAS->1 + //mdelay(300); +} + +#if 1 /* mask warning */ +/* set Record MIC input audio without playback */ +void set_record_mic_input_audio_without_playback(void) +{ + /* ADC path for MIC IN */ + jz_mic_only = 1; + write_codec_file(9, 0xff); + write_codec_file(8, 0x3f); + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 + mdelay(10); + write_codec_file_bit(1, 1, 2); + //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 + + write_codec_file(22, 0x40);//mic 1 + 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); +} +#endif + +#if 1 /* mask warning */ +/* unset Record MIC input audio without playback */ +void unset_record_mic_input_audio_without_playback(void) +{ + /* ADC path for MIC IN */ + jz_mic_only = 0; + 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 + write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 + write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} +#endif + +#if 0 /* mask warning */ +/* set Record LINE input audio without playback */ +void set_record_line_input_audio_without_playback(void) +{ + /* ADC path for LINE IN */ + jz_mic_only = 1; + write_codec_file(9, 0xff); + write_codec_file(8, 0x3f); + mdelay(10); + write_codec_file(22, 0xf6);//line in 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, 0, 3);//PMR1.SB_LIN->0 + 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 + mdelay(10); + 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); +} +#endif + +#if 0 /* mask warning */ +/* unset Record LINE input audio without playback */ +void unset_record_line_input_audio_without_playback(void) +{ + /* ADC path for LINE IN */ + write_codec_file_bit(5, 1, 4);//SB_ADC->1 + write_codec_file_bit(5, 1, 3);//ONR1.SB_LIN->1 + + write_codec_file(22, 0xc0);//CR3.SB_MIC1 + write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 + write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} +#endif + +#if 0 /* mask warning */ +/* set Playback LINE input audio direct only */ +void set_playback_line_input_audio_direct_only(void) +{ + jz_audio_reset();//or init_codec() + REG_AIC_I2SCR = 0x10; + write_codec_file(9, 0xff); + write_codec_file(8, 0x3f); + mdelay(10); + write_codec_file(22, 0xf6);//line in 1 + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 + mdelay(10); + write_codec_file_bit(1, 1, 2);//CR1.HP_BYPASS->1 + write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 + write_codec_file_bit(1, 0, 3);//CR1.DACSEL->0 + write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 + write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0 + + 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, 1, 7);//PMR1.SB_DAC->1 + //write_codec_file_bit(5, 1, 4);//PMR1.SB_ADC->1 +} +#endif + +#if 0 /* mask warning */ +/* unset Playback LINE input audio direct only */ +void unset_playback_line_input_audio_direct_only(void) +{ + write_codec_file_bit(6, 0, 3);//GIM->0 + write_codec_file_bit(1, 0, 2);//PMR1.BYPASS->0 + write_codec_file_bit(5, 1, 3);//PMR1.SB_LINE->1 + write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 + mdelay(100); + write_codec_file_bit(5, 1, 5);//PMR1.SB_MIX->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} +#endif + +#if 0 /* mask warning */ +/* set Record MIC input audio with direct playback */ +void set_record_mic_input_audio_with_direct_playback(void) +{ + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 + jz_mic_only = 0; + write_codec_file(9, 0xff); + write_codec_file(8, 0x3f); + mdelay(10); + + write_codec_file(22, 0x60);//mic 1 + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->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, 7);//CR1.SB_MICBIAS->0 + write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 + + 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); +} +#endif + +#if 0 /* mask warning */ +/* unset Record MIC input audio with direct playback */ +void unset_record_mic_input_audio_with_direct_playback(void) +{ + /* ADC path for MIC IN */ + jz_mic_only = 0; + 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 + write_codec_file_bit(5, 1, 6);//PMR1.SB_OUT->1 + write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} +#endif + +#if 0 /* mask warning */ +/* set Record playing audio mixed with MIC input audio */ +void set_record_playing_audio_mixed_with_mic_input_audio(void) +{ + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 + write_codec_file(9, 0xff); + //write_codec_file(8, 0x30); + write_codec_file(8, 0x20); + mdelay(10); + + write_codec_file(22, 0x63);//mic 1 + + write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 + write_codec_file_bit(6, 1, 3);// gain set + + 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, 7);//CR1.SB_MICBIAS->0 + write_codec_file_bit(22, 0, 7);//CR3.SB_MIC->0 + write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 + write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 + write_codec_file_bit(5, 0, 4);//PMR1.SB_MIX->0 +} +#endif + +#if 0 /* mask warning */ +/* unset Record playing audio mixed with MIC input audio */ +void unset_record_playing_audio_mixed_with_mic_input_audio(void) +{ + /* ADC path */ + write_codec_file_bit(5, 1, 4);//SB_ADC->1 + write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 + //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 + write_codec_file(22, 0xc0);//CR3.SB_MIC1->1 + //write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + //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, 5);//SB_MIX->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} +#endif + +#if 1 /* mask warning */ +/* set Record MIC input audio with Audio data replay (full duplex) */ +void set_record_mic_input_audio_with_audio_data_replay(void) +{ + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 + write_codec_file(9, 0xff); + //write_codec_file(8, 0x30); + write_codec_file(8, 0x20); + 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(22, 0, 7);//CR3.SB_MIC->0 + write_codec_file_bit(1, 0, 7);//CR1.SB_MICBIAS->0 + + write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 + write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 +} +#endif + +#if 1 /* mask warning */ +/* unset Record MIC input audio with Audio data replay (full duplex) */ +void unset_record_mic_input_audio_with_audio_data_replay(void) +{ + /* ADC path */ + write_codec_file_bit(5, 1, 4);//SB_ADC->1 + write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 + //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 + write_codec_file(22, 0xc0);//CR3.SB_MIC1->1 + write_codec_file_bit(5, 1, 7);//SB_DAC->1 + write_codec_file_bit(5, 1, 5);//SB_MIX->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} +#endif + +#if 1 /* mask warning */ +/* set Record LINE input audio with Audio data replay (full duplex for linein) */ +void set_record_line_input_audio_with_audio_data_replay(void) +{ + write_codec_file(9, 0xff); + //write_codec_file(8, 0x30); + write_codec_file(8, 0x20); + write_codec_file_bit(1, 0, 4);//CR1.HP_DIS->0 + write_codec_file_bit(5, 0, 3);//PMR1.SB_LIN->0 + write_codec_file_bit(5, 1, 0);//PMR1.SB_IND->1 + write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 + //write_codec_file_bit(22, 1, 7);//CR3.SB_MIC->1 + write_codec_file_bit(1, 1, 3);//CR1.DACSEL->1 + write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 + + + //jz_mic_only = 1; + write_codec_file(22, 0xc6);//line in 1 + write_codec_file_bit(23, 0, 7);//AGC1.AGC_EN->0 + write_codec_file_bit(1, 0, 2);//CR1.BYPASS->0 + write_codec_file_bit(5, 0, 5);//PMR1.SB_MIX->0 +} +#endif + +#if 1 /* mask warning */ +/* unset Record LINE input audio with Audio data replay (full duplex for linein) */ +void unset_record_line_input_audio_with_audio_data_replay(void) +{ + /* ADC path */ + write_codec_file_bit(5, 1, 4);//SB_ADC->1 + write_codec_file_bit(1, 1, 7);//CR1.SB_MICBIAS->1 + //write_codec_file_bit(1, 1, 6);//CR1.MONO->1 + write_codec_file(22, 0xc0);//CR3.SB_MIC1->1 + write_codec_file_bit(5, 1, 7);//SB_DAC->1 + write_codec_file_bit(5, 1, 5);//SB_MIX->1 + write_codec_file_bit(6, 1, 0);//SB_SLEEP->1 + write_codec_file_bit(6, 1, 1);//SB->1 +} +#endif + +#if 1 +/* unset Audio data replay */ +void unset_audio_data_replay(void) +{ + //write_codec_file_bit(1, 1, 5);//DAC_MUTE->1 + //mdelay(800); + //write_codec_file_bit(5, 1, 6);//SB_OUT->1 + //mdelay(800); + 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 +} +#endif + +void set_dlv_replay(void) +{ + set_audio_data_replay(); +} + +void set_dlv_speed(int rate) +{ + int speed = 0, val; + speed = 0; + switch (rate) { + 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: + break; + } + + val = read_codec_file(4); + val = (speed << 4) | speed; + write_codec_file(4, val); +} + +void reset_jzcodec(void) +{ + +} + +void dlv_mixer_old_info_id_name(void) +{ + strncpy(info.id, "JZDLV", sizeof(info.id)); + strncpy(info.name,"Jz internal codec dlv on jz4750", sizeof(info.name)); +} + +void dlv_mixer_info_id_name(void) +{ + strncpy(old_info.id, "JZDLV", sizeof(old_info.id)); + strncpy(old_info.name,"Jz internal codec dlv on jz4750", sizeof(old_info.name)); +} + +void set_dlv_mic(int val) +{ + int cur_vol ; + /* set gain */ + //write_codec_file_bit(6, 1, 3);//GIM + cur_vol = 31 * val / 100; + cur_vol |= cur_vol << 4; + write_codec_file(19, cur_vol);//GIL,GIR +} + +void set_dlv_line(int val) +{ + int cur_vol; + /* set gain */ + cur_vol = 31 * val / 100; + cur_vol &= 0x1f; + write_codec_file(11, cur_vol);//GO1L + write_codec_file(12, cur_vol);//GO1R +} + +void set_dlv_volume(int val) +{ + unsigned long cur_vol; + cur_vol = 31 * (100 - val) / 100; + write_codec_file(17, cur_vol | 0xc0); + write_codec_file(18, cur_vol); +} + +static int __init init_dlv(void) +{ + set_codec_mode = set_dlv_mode; + each_time_init_codec = each_time_init_dlv; + reset_codec = reset_dlv_codec; + set_codec_startup_param = set_dlv_startup_param; + + set_codec_replay = set_dlv_replay; + + set_codec_speed = set_dlv_speed; + + codec_mixer_old_info_id_name = dlv_mixer_old_info_id_name; + codec_mixer_info_id_name = dlv_mixer_info_id_name; + + set_codec_volume = set_dlv_volume; + set_codec_mic = set_dlv_mic; + set_codec_line = set_dlv_line; + + return 0; +} + + +static void __exit cleanup_dlv(void) +{ + +} + +module_init(init_dlv); +module_exit(cleanup_dlv); diff --git a/sound/oss/jzdlv.h b/sound/oss/jzdlv.h new file mode 100644 index 0000000..450e7ea --- /dev/null +++ b/sound/oss/jzdlv.h @@ -0,0 +1,21 @@ +/* header file for dlv */ +void write_codec_file(int addr, int val); +int read_codec_file(int addr); +void printk_codec_files(void); +int write_codec_file_bit(int addr, int bitval, int mask_bit); +void set_audio_data_replay(void); +void unset_audio_data_replay(void); +void set_record_mic_input_audio_without_playback(void); +void unset_record_mic_input_audio_without_playback(void); +void set_record_line_input_audio_without_playback(void); +void unset_record_line_input_audio_without_playback(void); +void set_playback_line_input_audio_direct_only(void); +void unset_playback_line_input_audio_direct_only(void); +void set_record_mic_input_audio_with_direct_playback(void); +void unset_record_mic_input_audio_with_direct_playback(void); +void set_record_playing_audio_mixed_with_mic_input_audio(void); +void unset_record_playing_audio_mixed_with_mic_input_audio(void); +void set_record_mic_input_audio_with_audio_data_replay(void); +void unset_record_mic_input_audio_with_audio_data_replay(void); +void set_record_line_input_audio_with_audio_data_replay(void); +void unset_record_line_input_audio_with_audio_data_replay(void); diff --git a/sound/oss/os.h b/sound/oss/os.h index a1a962d..2fb00ab 100644 --- a/sound/oss/os.h +++ b/sound/oss/os.h @@ -9,7 +9,6 @@ #ifdef __KERNEL__ #include #include -#include #include #include #include diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index d3e786a..e827f85 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -35,6 +35,8 @@ 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" +source "sound/soc/jz4750/Kconfig" # Supported codecs source "sound/soc/codecs/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 6f1e28d..6d72e6d 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -13,3 +13,5 @@ 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/ +obj-$(CONFIG_SND_SOC) += jz4750/ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index bbc97fd..c5dea8c 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -176,3 +176,14 @@ 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..7f53f57 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -4,6 +4,8 @@ snd-soc-ad73311-objs := ad73311.o snd-soc-ak4104-objs := ak4104.o snd-soc-ak4535-objs := ak4535.o snd-soc-cs4270-objs := cs4270.o +snd-soc-jzcodec-objs := jzcodec.o +snd-soc-jzdlv-objs := jzdlv.o snd-soc-l3-objs := l3.o snd-soc-pcm3008-objs := pcm3008.o snd-soc-spdif-objs := spdif_transciever.o @@ -41,6 +43,8 @@ obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o +obj-$(CONFIG_SND_SOC_ICODEC) += snd-soc-jzcodec.o +obj-$(CONFIG_SND_SOC_DLV) += snd-soc-jzdlv.o obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o diff --git a/sound/soc/codecs/jzcodec.c b/sound/soc/codecs/jzcodec.c new file mode 100644 index 0000000..f922845 --- /dev/null +++ b/sound/soc/codecs/jzcodec.c @@ -0,0 +1,729 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../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]); + } + + /* 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]); + } + + 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); + + /* bit size. codec side */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + } + /* 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_PAUSE_RELEASE: + break; + + case SNDRV_PCM_TRIGGER_START: + //case SNDRV_PCM_TRIGGER_RESUME: + + 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_PAUSE_PUSH: + break; + + case SNDRV_PCM_TRIGGER_STOP: + //case SNDRV_PCM_TRIGGER_SUSPEND: + + 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_codec_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_codec_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_codec_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 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; +} + +#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_codec_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"); diff --git a/sound/soc/codecs/jzcodec.h b/sound/soc/codecs/jzcodec.h new file mode 100644 index 0000000..7e4dedf --- /dev/null +++ b/sound/soc/codecs/jzcodec.h @@ -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_codec_dai jzcodec_dai; +extern struct snd_soc_codec_device soc_codec_dev_jzcodec; + +#endif diff --git a/sound/soc/codecs/jzdlv.c b/sound/soc/codecs/jzdlv.c new file mode 100644 index 0000000..2ed2b28 --- /dev/null +++ b/sound/soc/codecs/jzdlv.c @@ -0,0 +1,970 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../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; +} + +/* 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, 31, 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++) { + if ((err = snd_ctl_add(codec->card, + snd_soc_cnew(&jzdlv_snd_controls[i], codec, NULL))) < 0) + return err; + } + + return 0; +} + +static const struct snd_soc_dapm_widget jzdlv_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 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) +{ + int i,cnt; + + cnt = ARRAY_SIZE(jzdlv_dapm_widgets); + for(i = 0; i < ARRAY_SIZE(jzdlv_dapm_widgets); i++) { + snd_soc_dapm_new_control(codec, &jzdlv_dapm_widgets[i]); + } + + /* 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]); + } + + 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_PAUSE_RELEASE: + break; + case SNDRV_PCM_TRIGGER_START: + //case SNDRV_PCM_TRIGGER_RESUME: + + init_codec(); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + set_audio_data_replay(); + write_codec_file_bit(5, 0, 7);//PMR1.SB_DAC->0 + mdelay(10); + REG_AIC_I2SCR = 0x10; + mdelay(20); + __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_PAUSE_PUSH: + break; + case SNDRV_PCM_TRIGGER_STOP: + //case SNDRV_PCM_TRIGGER_SUSPEND: + + 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_codec_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_codec_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_codec_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) + +#define JZDLV_FORMATS (SNDRV_PCM_FORMAT_S8 | SNDRV_PCM_FMTBIT_S16_LE) + +struct snd_soc_codec_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 u16 jzdlv_reg_pm[JZDLV_CACHEREGNUM]; +static int jzdlv_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; +#if 0 + 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) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + //struct snd_soc_codec *codec = socdev->codec; + //u16 reg_val; +#if 0 + 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"); diff --git a/sound/soc/codecs/jzdlv.h b/sound/soc/codecs/jzdlv.h new file mode 100644 index 0000000..a901a69 --- /dev/null +++ b/sound/soc/codecs/jzdlv.h @@ -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_codec_dai jzdlv_dai; +extern struct snd_soc_codec_device soc_codec_dev_jzdlv; + +#endif diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig new file mode 100644 index 0000000..0bfbffe --- /dev/null +++ b/sound/soc/jz4740/Kconfig @@ -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. diff --git a/sound/soc/jz4740/Makefile b/sound/soc/jz4740/Makefile new file mode 100644 index 0000000..4c79b13 --- /dev/null +++ b/sound/soc/jz4740/Makefile @@ -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 diff --git a/sound/soc/jz4740/jz4740-ac97.c b/sound/soc/jz4740/jz4740-ac97.c new file mode 100644 index 0000000..84da470 --- /dev/null +++ b/sound/soc/jz4740/jz4740-ac97.c @@ -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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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"); diff --git a/sound/soc/jz4740/jz4740-ac97.h b/sound/soc/jz4740/jz4740-ac97.h new file mode 100644 index 0000000..e80e2a0 --- /dev/null +++ b/sound/soc/jz4740/jz4740-ac97.h @@ -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 diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c new file mode 100644 index 0000000..de40d4d --- /dev/null +++ b/sound/soc/jz4740/jz4740-i2s.c @@ -0,0 +1,296 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "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_cpu_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_cpu_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; + if (channels == 1) + __aic_enable_mono2stereo(); + else + __aic_disable_mono2stereo(); + } else + 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_cpu_dai *dai) +{ + if (!dai->active) + return 0; + + return 0; +} + +static int jz4740_i2s_resume(struct platform_device *pdev, + struct snd_soc_cpu_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_cpu_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"); diff --git a/sound/soc/jz4740/jz4740-i2s.h b/sound/soc/jz4740/jz4740-i2s.h new file mode 100644 index 0000000..f4cbcec --- /dev/null +++ b/sound/soc/jz4740/jz4740-i2s.h @@ -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_cpu_dai jz4740_i2s_dai; + +#endif diff --git a/sound/soc/jz4740/jz4740-pcm.c b/sound/soc/jz4740/jz4740-pcm.c new file mode 100644 index 0000000..516dd1f --- /dev/null +++ b/sound/soc/jz4740/jz4740-pcm.c @@ -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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#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; + + 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; + + 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_codec_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"); diff --git a/sound/soc/jz4740/jz4740-pcm.h b/sound/soc/jz4740/jz4740-pcm.h new file mode 100644 index 0000000..68ea842 --- /dev/null +++ b/sound/soc/jz4740/jz4740-pcm.h @@ -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 + +#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 diff --git a/sound/soc/jz4740/pavo.c b/sound/soc/jz4740/pavo.c new file mode 100644 index 0000000..9d6056e --- /dev/null +++ b/sound/soc/jz4740/pavo.c @@ -0,0 +1,360 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + + /* 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); +} + +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_codec_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_cpu_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; + + snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0); + snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0); + + /* 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]); + } + + /* 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); + 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"); diff --git a/sound/soc/jz4750/Kconfig b/sound/soc/jz4750/Kconfig new file mode 100644 index 0000000..7cf9b16 --- /dev/null +++ b/sound/soc/jz4750/Kconfig @@ -0,0 +1,34 @@ +config SND_JZ4750_SOC + tristate "SoC Audio for Ingenic jz4750 chip" + depends on (JZ4750_APUS || JZ4750_FUWA) && 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. diff --git a/sound/soc/jz4750/Makefile b/sound/soc/jz4750/Makefile new file mode 100644 index 0000000..ba5cbfa --- /dev/null +++ b/sound/soc/jz4750/Makefile @@ -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 diff --git a/sound/soc/jz4750/apus.c b/sound/soc/jz4750/apus.c new file mode 100644 index 0000000..def2104 --- /dev/null +++ b/sound/soc/jz4750/apus.c @@ -0,0 +1,357 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../codecs/jzdlv.h" +#include "jz4750-pcm.h" +#include "jz4750-i2s.h" + +#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; + /* 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 APUS_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 APUS_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 APUS_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 (apus_spk_func == APUS_SPK_ON) + spk = 1; + + /* 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); +} + +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 void 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; +} + +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_codec_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_cpu_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, 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); + 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, 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, 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; +} + +/* 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 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 apus_enum[] = { + SOC_ENUM_SINGLE_EXT(5, jack_function), + SOC_ENUM_SINGLE_EXT(2, spk_function), +}; + +static const struct snd_kcontrol_new jzdlv_apus_controls[] = { + 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_set_endpoint(codec, "LLINEIN", 0); + snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0); + + /* 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 */ + for(i = 0; i < ARRAY_SIZE(jzdlv_dapm_widgets); i++) { + snd_soc_dapm_new_control(codec, &jzdlv_dapm_widgets[i]); + } + + /* Set up apus 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); + 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"); diff --git a/sound/soc/jz4750/jz4750-ac97.c b/sound/soc/jz4750/jz4750-ac97.c new file mode 100644 index 0000000..84da470 --- /dev/null +++ b/sound/soc/jz4750/jz4750-ac97.c @@ -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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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"); diff --git a/sound/soc/jz4750/jz4750-ac97.h b/sound/soc/jz4750/jz4750-ac97.h new file mode 100644 index 0000000..cd116e9 --- /dev/null +++ b/sound/soc/jz4750/jz4750-ac97.h @@ -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 diff --git a/sound/soc/jz4750/jz4750-i2s.c b/sound/soc/jz4750/jz4750-i2s.c new file mode 100644 index 0000000..887443e --- /dev/null +++ b/sound/soc/jz4750/jz4750-i2s.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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_cpu_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_cpu_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; + + 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_cpu_dai *dai) +{ + if (!dai->active) + return 0; + + return 0; +} + +static int jz4750_i2s_resume(struct platform_device *pdev, + struct snd_soc_cpu_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_cpu_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"); diff --git a/sound/soc/jz4750/jz4750-i2s.h b/sound/soc/jz4750/jz4750-i2s.h new file mode 100644 index 0000000..c227cae --- /dev/null +++ b/sound/soc/jz4750/jz4750-i2s.h @@ -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_cpu_dai jz4750_i2s_dai; + +#endif diff --git a/sound/soc/jz4750/jz4750-pcm.c b/sound/soc/jz4750/jz4750-pcm.c new file mode 100644 index 0000000..e50d1c8 --- /dev/null +++ b/sound/soc/jz4750/jz4750-pcm.c @@ -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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#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_PAUSE | + SNDRV_PCM_INFO_RESUME | + 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_codec_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"); diff --git a/sound/soc/jz4750/jz4750-pcm.h b/sound/soc/jz4750/jz4750-pcm.h new file mode 100644 index 0000000..d7d4f72 --- /dev/null +++ b/sound/soc/jz4750/jz4750-pcm.h @@ -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 + +#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