diff options
author | Xinyu Chen <xinyu.chen@freescale.com> | 2012-04-09 13:34:16 +0800 |
---|---|---|
committer | Xinyu Chen <xinyu.chen@freescale.com> | 2012-04-09 13:34:16 +0800 |
commit | 52c5341f1302a0b328e7dd5890c12729406256fc (patch) | |
tree | 687adeb27a395f512c2a0fa373bd173c437f9321 | |
parent | bee5f57f1e3e5174aa5390a330052e772afdc453 (diff) | |
parent | 3ec8c998827be5729ff98a0d7c097f7ad9ddbc6e (diff) |
Merge remote branch 'fsl-linux-sdk/imx_3.0.15' into imx_3.0.15_android
54 files changed, 4082 insertions, 1065 deletions
diff --git a/arch/arm/configs/imx6_defconfig b/arch/arm/configs/imx6_defconfig index 7da0c3da67e0..8a7b660163c4 100644 --- a/arch/arm/configs/imx6_defconfig +++ b/arch/arm/configs/imx6_defconfig @@ -463,10 +463,10 @@ CONFIG_CPU_FREQ_STAT=y CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y # CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set -# CONFIG_CPU_FREQ_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y -# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set +CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y CONFIG_CPU_FREQ_GOV_INTERACTIVE=y CONFIG_CPU_FREQ_IMX=y @@ -563,7 +563,8 @@ CONFIG_DEFAULT_TCP_CONG="cubic" # CONFIG_NET_DSA is not set # CONFIG_VLAN_8021Q is not set # CONFIG_DECNET is not set -# CONFIG_LLC2 is not set +CONFIG_LLC=y +CONFIG_LLC2=y # CONFIG_IPX is not set # CONFIG_ATALK is not set # CONFIG_X25 is not set @@ -2482,7 +2483,7 @@ CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 CONFIG_ENABLE_WARN_DEPRECATED=y CONFIG_ENABLE_MUST_CHECK=y CONFIG_FRAME_WARN=1024 -# CONFIG_MAGIC_SYSRQ is not set +CONFIG_MAGIC_SYSRQ=y # CONFIG_STRIP_ASM_SYMS is not set # CONFIG_UNUSED_SYMBOLS is not set CONFIG_DEBUG_FS=y diff --git a/arch/arm/include/asm/assembler.h b/arch/arm/include/asm/assembler.h index 4e25f1888356..fed335efda25 100644 --- a/arch/arm/include/asm/assembler.h +++ b/arch/arm/include/asm/assembler.h @@ -231,7 +231,7 @@ */ #ifdef CONFIG_THUMB2_KERNEL - .macro usraccoff, instr, reg, ptr, inc, off, cond, abort, t=T() + .macro usraccoff, instr, reg, ptr, inc, off, cond, abort, t=TUSER() 9999: .if \inc == 1 \instr\cond\()b\()\t\().w \reg, [\ptr, #\off] @@ -271,7 +271,7 @@ #else /* !CONFIG_THUMB2_KERNEL */ - .macro usracc, instr, reg, ptr, inc, cond, rept, abort, t=T() + .macro usracc, instr, reg, ptr, inc, cond, rept, abort, t=TUSER() .rept \rept 9999: .if \inc == 1 @@ -298,4 +298,13 @@ .macro ldrusr, reg, ptr, inc, cond=al, rept=1, abort=9001f usracc ldr, \reg, \ptr, \inc, \cond, \rept, \abort .endm + +/* Utility macro for declaring string literals */ + .macro string name:req, string + .type \name , #object +\name: + .asciz "\string" + .size \name , . - \name + .endm + #endif /* __ASM_ASSEMBLER_H__ */ diff --git a/arch/arm/include/asm/domain.h b/arch/arm/include/asm/domain.h index af18ceaacf5d..b5dc173d336f 100644 --- a/arch/arm/include/asm/domain.h +++ b/arch/arm/include/asm/domain.h @@ -83,9 +83,9 @@ * instructions (inline assembly) */ #ifdef CONFIG_CPU_USE_DOMAINS -#define T(instr) #instr "t" +#define TUSER(instr) #instr "t" #else -#define T(instr) #instr +#define TUSER(instr) #instr #endif #else /* __ASSEMBLY__ */ @@ -95,9 +95,9 @@ * instructions */ #ifdef CONFIG_CPU_USE_DOMAINS -#define T(instr) instr ## t +#define TUSER(instr) instr ## t #else -#define T(instr) instr +#define TUSER(instr) instr #endif #endif /* __ASSEMBLY__ */ diff --git a/arch/arm/include/asm/futex.h b/arch/arm/include/asm/futex.h index 253cc86318bf..7be54690aeec 100644 --- a/arch/arm/include/asm/futex.h +++ b/arch/arm/include/asm/futex.h @@ -75,9 +75,9 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, #define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg) \ __asm__ __volatile__( \ - "1: " T(ldr) " %1, [%3]\n" \ + "1: " TUSER(ldr) " %1, [%3]\n" \ " " insn "\n" \ - "2: " T(str) " %0, [%3]\n" \ + "2: " TUSER(str) " %0, [%3]\n" \ " mov %0, #0\n" \ __futex_atomic_ex_table("%5") \ : "=&r" (ret), "=&r" (oldval), "=&r" (tmp) \ @@ -95,10 +95,10 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, return -EFAULT; __asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n" - "1: " T(ldr) " %1, [%4]\n" + "1: " TUSER(ldr) " %1, [%4]\n" " teq %1, %2\n" " it eq @ explicit IT needed for the 2b label\n" - "2: " T(streq) " %3, [%4]\n" + "2: " TUSER(streq) " %3, [%4]\n" __futex_atomic_ex_table("%5") : "+r" (ret), "=&r" (val) : "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT) diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h index b293616a1a1a..2958976d867b 100644 --- a/arch/arm/include/asm/uaccess.h +++ b/arch/arm/include/asm/uaccess.h @@ -227,7 +227,7 @@ do { \ #define __get_user_asm_byte(x,addr,err) \ __asm__ __volatile__( \ - "1: " T(ldrb) " %1,[%2],#0\n" \ + "1: " TUSER(ldrb) " %1,[%2],#0\n" \ "2:\n" \ " .pushsection .fixup,\"ax\"\n" \ " .align 2\n" \ @@ -263,7 +263,7 @@ do { \ #define __get_user_asm_word(x,addr,err) \ __asm__ __volatile__( \ - "1: " T(ldr) " %1,[%2],#0\n" \ + "1: " TUSER(ldr) " %1,[%2],#0\n" \ "2:\n" \ " .pushsection .fixup,\"ax\"\n" \ " .align 2\n" \ @@ -308,7 +308,7 @@ do { \ #define __put_user_asm_byte(x,__pu_addr,err) \ __asm__ __volatile__( \ - "1: " T(strb) " %1,[%2],#0\n" \ + "1: " TUSER(strb) " %1,[%2],#0\n" \ "2:\n" \ " .pushsection .fixup,\"ax\"\n" \ " .align 2\n" \ @@ -341,7 +341,7 @@ do { \ #define __put_user_asm_word(x,__pu_addr,err) \ __asm__ __volatile__( \ - "1: " T(str) " %1,[%2],#0\n" \ + "1: " TUSER(str) " %1,[%2],#0\n" \ "2:\n" \ " .pushsection .fixup,\"ax\"\n" \ " .align 2\n" \ @@ -366,10 +366,10 @@ do { \ #define __put_user_asm_dword(x,__pu_addr,err) \ __asm__ __volatile__( \ - ARM( "1: " T(str) " " __reg_oper1 ", [%1], #4\n" ) \ - ARM( "2: " T(str) " " __reg_oper0 ", [%1]\n" ) \ - THUMB( "1: " T(str) " " __reg_oper1 ", [%1]\n" ) \ - THUMB( "2: " T(str) " " __reg_oper0 ", [%1, #4]\n" ) \ + ARM( "1: " TUSER(str) " " __reg_oper1 ", [%1], #4\n" ) \ + ARM( "2: " TUSER(str) " " __reg_oper0 ", [%1]\n" ) \ + THUMB( "1: " TUSER(str) " " __reg_oper1 ", [%1]\n" ) \ + THUMB( "2: " TUSER(str) " " __reg_oper0 ", [%1, #4]\n" ) \ "3:\n" \ " .pushsection .fixup,\"ax\"\n" \ " .align 2\n" \ diff --git a/arch/arm/lib/getuser.S b/arch/arm/lib/getuser.S index 1b049cd7a49a..11093a7c3e32 100644 --- a/arch/arm/lib/getuser.S +++ b/arch/arm/lib/getuser.S @@ -31,18 +31,18 @@ #include <asm/domain.h> ENTRY(__get_user_1) -1: T(ldrb) r2, [r0] +1: TUSER(ldrb) r2, [r0] mov r0, #0 mov pc, lr ENDPROC(__get_user_1) ENTRY(__get_user_2) #ifdef CONFIG_THUMB2_KERNEL -2: T(ldrb) r2, [r0] -3: T(ldrb) r3, [r0, #1] +2: TUSER(ldrb) r2, [r0] +3: TUSER(ldrb) r3, [r0, #1] #else -2: T(ldrb) r2, [r0], #1 -3: T(ldrb) r3, [r0] +2: TUSER(ldrb) r2, [r0], #1 +3: TUSER(ldrb) r3, [r0] #endif #ifndef __ARMEB__ orr r2, r2, r3, lsl #8 @@ -54,7 +54,7 @@ ENTRY(__get_user_2) ENDPROC(__get_user_2) ENTRY(__get_user_4) -4: T(ldr) r2, [r0] +4: TUSER(ldr) r2, [r0] mov r0, #0 mov pc, lr ENDPROC(__get_user_4) diff --git a/arch/arm/lib/putuser.S b/arch/arm/lib/putuser.S index c023fc11e86c..7db25990c589 100644 --- a/arch/arm/lib/putuser.S +++ b/arch/arm/lib/putuser.S @@ -31,7 +31,7 @@ #include <asm/domain.h> ENTRY(__put_user_1) -1: T(strb) r2, [r0] +1: TUSER(strb) r2, [r0] mov r0, #0 mov pc, lr ENDPROC(__put_user_1) @@ -40,19 +40,19 @@ ENTRY(__put_user_2) mov ip, r2, lsr #8 #ifdef CONFIG_THUMB2_KERNEL #ifndef __ARMEB__ -2: T(strb) r2, [r0] -3: T(strb) ip, [r0, #1] +2: TUSER(strb) r2, [r0] +3: TUSER(strb) ip, [r0, #1] #else -2: T(strb) ip, [r0] -3: T(strb) r2, [r0, #1] +2: TUSER(strb) ip, [r0] +3: TUSER(strb) r2, [r0, #1] #endif #else /* !CONFIG_THUMB2_KERNEL */ #ifndef __ARMEB__ -2: T(strb) r2, [r0], #1 -3: T(strb) ip, [r0] +2: TUSER(strb) r2, [r0], #1 +3: TUSER(strb) ip, [r0] #else -2: T(strb) ip, [r0], #1 -3: T(strb) r2, [r0] +2: TUSER(strb) ip, [r0], #1 +3: TUSER(strb) r2, [r0] #endif #endif /* CONFIG_THUMB2_KERNEL */ mov r0, #0 @@ -60,18 +60,18 @@ ENTRY(__put_user_2) ENDPROC(__put_user_2) ENTRY(__put_user_4) -4: T(str) r2, [r0] +4: TUSER(str) r2, [r0] mov r0, #0 mov pc, lr ENDPROC(__put_user_4) ENTRY(__put_user_8) #ifdef CONFIG_THUMB2_KERNEL -5: T(str) r2, [r0] -6: T(str) r3, [r0, #4] +5: TUSER(str) r2, [r0] +6: TUSER(str) r3, [r0, #4] #else -5: T(str) r2, [r0], #4 -6: T(str) r3, [r0] +5: TUSER(str) r2, [r0], #4 +6: TUSER(str) r3, [r0] #endif mov r0, #0 mov pc, lr diff --git a/arch/arm/lib/uaccess.S b/arch/arm/lib/uaccess.S index d0ece2aeb70d..5c908b1cb8ed 100644 --- a/arch/arm/lib/uaccess.S +++ b/arch/arm/lib/uaccess.S @@ -32,11 +32,11 @@ rsb ip, ip, #4 cmp ip, #2 ldrb r3, [r1], #1 -USER( T(strb) r3, [r0], #1) @ May fault +USER( TUSER( strb) r3, [r0], #1) @ May fault ldrgeb r3, [r1], #1 -USER( T(strgeb) r3, [r0], #1) @ May fault +USER( TUSER( strgeb) r3, [r0], #1) @ May fault ldrgtb r3, [r1], #1 -USER( T(strgtb) r3, [r0], #1) @ May fault +USER( TUSER( strgtb) r3, [r0], #1) @ May fault sub r2, r2, ip b .Lc2u_dest_aligned @@ -59,7 +59,7 @@ ENTRY(__copy_to_user) addmi ip, r2, #4 bmi .Lc2u_0nowords ldr r3, [r1], #4 -USER( T(str) r3, [r0], #4) @ May fault +USER( TUSER( str) r3, [r0], #4) @ May fault mov ip, r0, lsl #32 - PAGE_SHIFT @ On each page, use a ld/st??t instruction rsb ip, ip, #0 movs ip, ip, lsr #32 - PAGE_SHIFT @@ -88,18 +88,18 @@ USER( T(str) r3, [r0], #4) @ May fault stmneia r0!, {r3 - r4} @ Shouldnt fault tst ip, #4 ldrne r3, [r1], #4 - T(strne) r3, [r0], #4 @ Shouldnt fault + TUSER( strne) r3, [r0], #4 @ Shouldnt fault ands ip, ip, #3 beq .Lc2u_0fupi .Lc2u_0nowords: teq ip, #0 beq .Lc2u_finished .Lc2u_nowords: cmp ip, #2 ldrb r3, [r1], #1 -USER( T(strb) r3, [r0], #1) @ May fault +USER( TUSER( strb) r3, [r0], #1) @ May fault ldrgeb r3, [r1], #1 -USER( T(strgeb) r3, [r0], #1) @ May fault +USER( TUSER( strgeb) r3, [r0], #1) @ May fault ldrgtb r3, [r1], #1 -USER( T(strgtb) r3, [r0], #1) @ May fault +USER( TUSER( strgtb) r3, [r0], #1) @ May fault b .Lc2u_finished .Lc2u_not_enough: @@ -120,7 +120,7 @@ USER( T(strgtb) r3, [r0], #1) @ May fault mov r3, r7, pull #8 ldr r7, [r1], #4 orr r3, r3, r7, push #24 -USER( T(str) r3, [r0], #4) @ May fault +USER( TUSER( str) r3, [r0], #4) @ May fault mov ip, r0, lsl #32 - PAGE_SHIFT rsb ip, ip, #0 movs ip, ip, lsr #32 - PAGE_SHIFT @@ -155,18 +155,18 @@ USER( T(str) r3, [r0], #4) @ May fault movne r3, r7, pull #8 ldrne r7, [r1], #4 orrne r3, r3, r7, push #24 - T(strne) r3, [r0], #4 @ Shouldnt fault + TUSER( strne) r3, [r0], #4 @ Shouldnt fault ands ip, ip, #3 beq .Lc2u_1fupi .Lc2u_1nowords: mov r3, r7, get_byte_1 teq ip, #0 beq .Lc2u_finished cmp ip, #2 -USER( T(strb) r3, [r0], #1) @ May fault +USER( TUSER( strb) r3, [r0], #1) @ May fault movge r3, r7, get_byte_2 -USER( T(strgeb) r3, [r0], #1) @ May fault +USER( TUSER( strgeb) r3, [r0], #1) @ May fault movgt r3, r7, get_byte_3 -USER( T(strgtb) r3, [r0], #1) @ May fault +USER( TUSER( strgtb) r3, [r0], #1) @ May fault b .Lc2u_finished .Lc2u_2fupi: subs r2, r2, #4 @@ -175,7 +175,7 @@ USER( T(strgtb) r3, [r0], #1) @ May fault mov r3, r7, pull #16 ldr r7, [r1], #4 orr r3, r3, r7, push #16 -USER( T(str) r3, [r0], #4) @ May fault +USER( TUSER( str) r3, [r0], #4) @ May fault mov ip, r0, lsl #32 - PAGE_SHIFT rsb ip, ip, #0 movs ip, ip, lsr #32 - PAGE_SHIFT @@ -210,18 +210,18 @@ USER( T(str) r3, [r0], #4) @ May fault movne r3, r7, pull #16 ldrne r7, [r1], #4 orrne r3, r3, r7, push #16 - T(strne) r3, [r0], #4 @ Shouldnt fault + TUSER( strne) r3, [r0], #4 @ Shouldnt fault ands ip, ip, #3 beq .Lc2u_2fupi .Lc2u_2nowords: mov r3, r7, get_byte_2 teq ip, #0 beq .Lc2u_finished cmp ip, #2 -USER( T(strb) r3, [r0], #1) @ May fault +USER( TUSER( strb) r3, [r0], #1) @ May fault movge r3, r7, get_byte_3 -USER( T(strgeb) r3, [r0], #1) @ May fault +USER( TUSER( strgeb) r3, [r0], #1) @ May fault ldrgtb r3, [r1], #0 -USER( T(strgtb) r3, [r0], #1) @ May fault +USER( TUSER( strgtb) r3, [r0], #1) @ May fault b .Lc2u_finished .Lc2u_3fupi: subs r2, r2, #4 @@ -230,7 +230,7 @@ USER( T(strgtb) r3, [r0], #1) @ May fault mov r3, r7, pull #24 ldr r7, [r1], #4 orr r3, r3, r7, push #8 -USER( T(str) r3, [r0], #4) @ May fault +USER( TUSER( str) r3, [r0], #4) @ May fault mov ip, r0, lsl #32 - PAGE_SHIFT rsb ip, ip, #0 movs ip, ip, lsr #32 - PAGE_SHIFT @@ -265,18 +265,18 @@ USER( T(str) r3, [r0], #4) @ May fault movne r3, r7, pull #24 ldrne r7, [r1], #4 orrne r3, r3, r7, push #8 - T(strne) r3, [r0], #4 @ Shouldnt fault + TUSER( strne) r3, [r0], #4 @ Shouldnt fault ands ip, ip, #3 beq .Lc2u_3fupi .Lc2u_3nowords: mov r3, r7, get_byte_3 teq ip, #0 beq .Lc2u_finished cmp ip, #2 -USER( T(strb) r3, [r0], #1) @ May fault +USER( TUSER( strb) r3, [r0], #1) @ May fault ldrgeb r3, [r1], #1 -USER( T(strgeb) r3, [r0], #1) @ May fault +USER( TUSER( strgeb) r3, [r0], #1) @ May fault ldrgtb r3, [r1], #0 -USER( T(strgtb) r3, [r0], #1) @ May fault +USER( TUSER( strgtb) r3, [r0], #1) @ May fault b .Lc2u_finished ENDPROC(__copy_to_user) @@ -295,11 +295,11 @@ ENDPROC(__copy_to_user) .Lcfu_dest_not_aligned: rsb ip, ip, #4 cmp ip, #2 -USER( T(ldrb) r3, [r1], #1) @ May fault +USER( TUSER( ldrb) r3, [r1], #1) @ May fault strb r3, [r0], #1 -USER( T(ldrgeb) r3, [r1], #1) @ May fault +USER( TUSER( ldrgeb) r3, [r1], #1) @ May fault strgeb r3, [r0], #1 -USER( T(ldrgtb) r3, [r1], #1) @ May fault +USER( TUSER( ldrgtb) r3, [r1], #1) @ May fault strgtb r3, [r0], #1 sub r2, r2, ip b .Lcfu_dest_aligned @@ -322,7 +322,7 @@ ENTRY(__copy_from_user) .Lcfu_0fupi: subs r2, r2, #4 addmi ip, r2, #4 bmi .Lcfu_0nowords -USER( T(ldr) r3, [r1], #4) +USER( TUSER( ldr) r3, [r1], #4) str r3, [r0], #4 mov ip, r1, lsl #32 - PAGE_SHIFT @ On each page, use a ld/st??t instruction rsb ip, ip, #0 @@ -351,18 +351,18 @@ USER( T(ldr) r3, [r1], #4) ldmneia r1!, {r3 - r4} @ Shouldnt fault stmneia r0!, {r3 - r4} tst ip, #4 - T(ldrne) r3, [r1], #4 @ Shouldnt fault + TUSER( ldrne) r3, [r1], #4 @ Shouldnt fault strne r3, [r0], #4 ands ip, ip, #3 beq .Lcfu_0fupi .Lcfu_0nowords: teq ip, #0 beq .Lcfu_finished .Lcfu_nowords: cmp ip, #2 -USER( T(ldrb) r3, [r1], #1) @ May fault +USER( TUSER( ldrb) r3, [r1], #1) @ May fault strb r3, [r0], #1 -USER( T(ldrgeb) r3, [r1], #1) @ May fault +USER( TUSER( ldrgeb) r3, [r1], #1) @ May fault strgeb r3, [r0], #1 -USER( T(ldrgtb) r3, [r1], #1) @ May fault +USER( TUSER( ldrgtb) r3, [r1], #1) @ May fault strgtb r3, [r0], #1 b .Lcfu_finished @@ -375,7 +375,7 @@ USER( T(ldrgtb) r3, [r1], #1) @ May fault .Lcfu_src_not_aligned: bic r1, r1, #3 -USER( T(ldr) r7, [r1], #4) @ May fault +USER( TUSER( ldr) r7, [r1], #4) @ May fault cmp ip, #2 bgt .Lcfu_3fupi beq .Lcfu_2fupi @@ -383,7 +383,7 @@ USER( T(ldr) r7, [r1], #4) @ May fault addmi ip, r2, #4 bmi .Lcfu_1nowords mov r3, r7, pull #8 -USER( T(ldr) r7, [r1], #4) @ May fault +USER( TUSER( ldr) r7, [r1], #4) @ May fault orr r3, r3, r7, push #24 str r3, [r0], #4 mov ip, r1, lsl #32 - PAGE_SHIFT @@ -418,7 +418,7 @@ USER( T(ldr) r7, [r1], #4) @ May fault stmneia r0!, {r3 - r4} tst ip, #4 movne r3, r7, pull #8 -USER( T(ldrne) r7, [r1], #4) @ May fault +USER( TUSER( ldrne) r7, [r1], #4) @ May fault orrne r3, r3, r7, push #24 strne r3, [r0], #4 ands ip, ip, #3 @@ -438,7 +438,7 @@ USER( T(ldrne) r7, [r1], #4) @ May fault addmi ip, r2, #4 bmi .Lcfu_2nowords mov r3, r7, pull #16 -USER( T(ldr) r7, [r1], #4) @ May fault +USER( TUSER( ldr) r7, [r1], #4) @ May fault orr r3, r3, r7, push #16 str r3, [r0], #4 mov ip, r1, lsl #32 - PAGE_SHIFT @@ -474,7 +474,7 @@ USER( T(ldr) r7, [r1], #4) @ May fault stmneia r0!, {r3 - r4} tst ip, #4 movne r3, r7, pull #16 -USER( T(ldrne) r7, [r1], #4) @ May fault +USER( TUSER( ldrne) r7, [r1], #4) @ May fault orrne r3, r3, r7, push #16 strne r3, [r0], #4 ands ip, ip, #3 @@ -486,7 +486,7 @@ USER( T(ldrne) r7, [r1], #4) @ May fault strb r3, [r0], #1 movge r3, r7, get_byte_3 strgeb r3, [r0], #1 -USER( T(ldrgtb) r3, [r1], #0) @ May fault +USER( TUSER( ldrgtb) r3, [r1], #0) @ May fault strgtb r3, [r0], #1 b .Lcfu_finished @@ -494,7 +494,7 @@ USER( T(ldrgtb) r3, [r1], #0) @ May fault addmi ip, r2, #4 bmi .Lcfu_3nowords mov r3, r7, pull #24 -USER( T(ldr) r7, [r1], #4) @ May fault +USER( TUSER( ldr) r7, [r1], #4) @ May fault orr r3, r3, r7, push #8 str r3, [r0], #4 mov ip, r1, lsl #32 - PAGE_SHIFT @@ -529,7 +529,7 @@ USER( T(ldr) r7, [r1], #4) @ May fault stmneia r0!, {r3 - r4} tst ip, #4 movne r3, r7, pull #24 -USER( T(ldrne) r7, [r1], #4) @ May fault +USER( TUSER( ldrne) r7, [r1], #4) @ May fault orrne r3, r3, r7, push #8 strne r3, [r0], #4 ands ip, ip, #3 @@ -539,9 +539,9 @@ USER( T(ldrne) r7, [r1], #4) @ May fault beq .Lcfu_finished cmp ip, #2 strb r3, [r0], #1 -USER( T(ldrgeb) r3, [r1], #1) @ May fault +USER( TUSER( ldrgeb) r3, [r1], #1) @ May fault strgeb r3, [r0], #1 -USER( T(ldrgtb) r3, [r1], #1) @ May fault +USER( TUSER( ldrgtb) r3, [r1], #1) @ May fault strgtb r3, [r0], #1 b .Lcfu_finished ENDPROC(__copy_from_user) diff --git a/arch/arm/mach-mx6/Kconfig b/arch/arm/mach-mx6/Kconfig index 3c083feb1742..1df24213195b 100644 --- a/arch/arm/mach-mx6/Kconfig +++ b/arch/arm/mach-mx6/Kconfig @@ -14,6 +14,7 @@ config ARCH_MX6Q select IMX_HAVE_PLATFORM_MXC_PWM select IMX_HAVE_PLATFORM_LDB select IMX_HAVE_PLATFORM_IMX_SPDIF + select IMX_HAVE_PLATFORM_IMX_VDOA config FORCE_MAX_ZONEORDER int "MAX_ORDER" diff --git a/arch/arm/mach-mx6/board-mx6q_arm2.c b/arch/arm/mach-mx6/board-mx6q_arm2.c index 8333e222095e..cf6a76076c4f 100644 --- a/arch/arm/mach-mx6/board-mx6q_arm2.c +++ b/arch/arm/mach-mx6/board-mx6q_arm2.c @@ -2083,6 +2083,7 @@ static void __init mx6_arm2_init(void) if (!disable_mipi_dsi) imx6q_add_mipi_dsi(&mipi_dsi_pdata); + imx6q_add_vdoa(); imx6q_add_lcdif(&lcdif_data); imx6q_add_ldb(&ldb_data); imx6q_add_v4l2_output(0); diff --git a/arch/arm/mach-mx6/board-mx6q_sabreauto.c b/arch/arm/mach-mx6/board-mx6q_sabreauto.c index 4a96434e2d02..bc32f5674cbf 100644 --- a/arch/arm/mach-mx6/board-mx6q_sabreauto.c +++ b/arch/arm/mach-mx6/board-mx6q_sabreauto.c @@ -137,15 +137,15 @@ static int mma8451_position = 3; static struct clk *sata_clk; static int mipi_sensor; -static int uart2_en; static int can0_enable; +static int uart3_en; -static int __init uart2_enable(char *p) +static int __init uart3_enable(char *p) { - uart2_en = 1; + uart3_en = 1; return 0; } -early_param("uart2", uart2_enable); +early_param("uart3", uart3_enable); enum sd_pad_mode { SD_PAD_MODE_LOW_SPEED, @@ -295,8 +295,8 @@ mx6q_sabreauto_anatop_thermal_data __initconst = { static inline void mx6q_sabreauto_init_uart(void) { - imx6q_add_imx_uart(0, NULL); imx6q_add_imx_uart(1, NULL); + imx6q_add_imx_uart(2, NULL); imx6q_add_imx_uart(3, NULL); } @@ -534,9 +534,11 @@ static int max7310_u43_setup(struct i2c_client *client, int max7310_gpio_value[] = { 0, 0, 0, 0, 0, 0, 0, 0, }; - int n; + if (uart3_en) + max7310_gpio_value[3] = 1; + for (n = 0; n < ARRAY_SIZE(max7310_gpio_value); ++n) { gpio_request(gpio_base + n, "MAX7310 U43 GPIO Expander"); if (max7310_gpio_value[n] < 0) @@ -1318,15 +1320,14 @@ static void __init mx6_board_init(void) BUG_ON(!i2c3_pads); mxc_iomux_v3_setup_multiple_pads(i2c3_pads, i2c3_pads_cnt); - if (!uart2_en) { - if (can0_enable) { - BUG_ON(!can0_pads); - mxc_iomux_v3_setup_multiple_pads(can0_pads, - can0_pads_cnt); - } - BUG_ON(!can1_pads); - mxc_iomux_v3_setup_multiple_pads(can1_pads, can1_pads_cnt); + if (can0_enable) { + BUG_ON(!can0_pads); + mxc_iomux_v3_setup_multiple_pads(can0_pads, + can0_pads_cnt); } + BUG_ON(!can1_pads); + mxc_iomux_v3_setup_multiple_pads(can1_pads, can1_pads_cnt); + /* assert i2c-rst */ gpio_request(SABREAUTO_I2C_EXP_RST, "i2c-rst"); @@ -1374,6 +1375,7 @@ static void __init mx6_board_init(void) for (i = 0; i < (ARRAY_SIZE(sabr_fb_data) + 1) / 2; i++) imx6q_add_ipuv3fb(i, &sabr_fb_data[i]); + imx6q_add_vdoa(); imx6q_add_mipi_dsi(&mipi_dsi_pdata); imx6q_add_lcdif(&lcdif_data); imx6q_add_ldb(&ldb_data); @@ -1456,7 +1458,8 @@ static void __init mx6_board_init(void) imx6q_add_viim(); imx6q_add_imx2_wdt(0, NULL); imx6q_add_dma(); - imx6q_add_gpmi(&mx6q_gpmi_nand_platform_data); + if (!uart3_en) + imx6q_add_gpmi(&mx6q_gpmi_nand_platform_data); imx6q_add_dvfs_core(&sabreauto_dvfscore_data); diff --git a/arch/arm/mach-mx6/board-mx6q_sabreauto.h b/arch/arm/mach-mx6/board-mx6q_sabreauto.h index 21e7be512238..d2a097ec5e17 100644 --- a/arch/arm/mach-mx6/board-mx6q_sabreauto.h +++ b/arch/arm/mach-mx6/board-mx6q_sabreauto.h @@ -187,6 +187,9 @@ static iomux_v3_cfg_t mx6q_sabreauto_pads[] = { MX6Q_PAD_GPIO_8__UART2_RXD, MX6Q_PAD_SD4_DAT6__UART2_CTS, MX6Q_PAD_SD4_DAT5__UART2_RTS, + /* UART 3 */ + MX6Q_PAD_SD4_CLK__UART3_TXD, + MX6Q_PAD_SD4_CMD__UART3_RXD, /*USBs OC pin */ MX6Q_PAD_EIM_WAIT__GPIO_5_0, /*HOST1_OC*/ MX6Q_PAD_SD4_DAT0__GPIO_2_8, /*OTG_OC*/ diff --git a/arch/arm/mach-mx6/board-mx6q_sabrelite.c b/arch/arm/mach-mx6/board-mx6q_sabrelite.c index 8f29e032d74a..c93da957070e 100644 --- a/arch/arm/mach-mx6/board-mx6q_sabrelite.c +++ b/arch/arm/mach-mx6/board-mx6q_sabrelite.c @@ -1126,6 +1126,7 @@ static void __init mx6_sabrelite_board_init(void) for (i = 0; i < ARRAY_SIZE(sabrelite_fb_data); i++) imx6q_add_ipuv3fb(i, &sabrelite_fb_data[i]); + imx6q_add_vdoa(); imx6q_add_lcdif(&lcdif_data); imx6q_add_ldb(&ldb_data); imx6q_add_v4l2_output(0); diff --git a/arch/arm/mach-mx6/board-mx6q_sabresd.c b/arch/arm/mach-mx6/board-mx6q_sabresd.c index c06a20674840..b7af6301058a 100644 --- a/arch/arm/mach-mx6/board-mx6q_sabresd.c +++ b/arch/arm/mach-mx6/board-mx6q_sabresd.c @@ -55,6 +55,7 @@ #include <linux/mfd/max17135.h> #include <linux/mfd/wm8994/pdata.h> #include <linux/mfd/wm8994/gpio.h> +#include <sound/wm8962.h> #include <mach/common.h> #include <mach/hardware.h> @@ -237,17 +238,29 @@ static inline void mx6q_sabresd_init_uart(void) static int mx6q_sabresd_fec_phy_init(struct phy_device *phydev) { - /* prefer master mode, disable 1000 Base-T capable */ - phy_write(phydev, 0x9, 0x1c00); + unsigned short val; - /* min rx data delay */ - phy_write(phydev, 0x0b, 0x8105); - phy_write(phydev, 0x0c, 0x0000); + /* To enable AR8031 ouput a 125MHz clk from CLK_25M */ + phy_write(phydev, 0xd, 0x7); + phy_write(phydev, 0xe, 0x8016); + phy_write(phydev, 0xd, 0x4007); + val = phy_read(phydev, 0xe); - /* max rx/tx clock delay, min rx/tx control delay */ - phy_write(phydev, 0x0b, 0x8104); - phy_write(phydev, 0x0c, 0xf0f0); - phy_write(phydev, 0x0b, 0x104); + val &= 0xffe3; + val |= 0x18; + phy_write(phydev, 0xe, val); + + /* Introduce tx clock delay */ + phy_write(phydev, 0x1d, 0x5); + val = phy_read(phydev, 0x1e); + val |= 0x0100; + phy_write(phydev, 0x1e, val); + + /*check phy power*/ + val = phy_read(phydev, 0x0); + + if (val & BMCR_PDOWN) + phy_write(phydev, 0x0, (val & ~BMCR_PDOWN)); return 0; } @@ -322,7 +335,7 @@ static struct mxc_audio_platform_data wm8958_data = { .hp_active_low = 1, }; -static struct wm8994_pdata wm8958_pdata = { +static struct wm8994_pdata wm8958_config_data = { .gpio_defaults = { [0] = WM8994_GP_FN_GPIO | WM8994_GPN_DB, [1] = WM8994_GP_FN_GPIO | WM8994_GPN_DB | WM8994_GPN_PD, @@ -366,12 +379,21 @@ static struct platform_device mx6_sabresd_audio_wm8962_device = { .name = "imx-wm8962", }; +static struct wm8962_pdata wm8962_config_data = { + .gpio_init = { + [2] = WM8962_GPIO_FN_DMICCLK, + [4] = 0x8000 | WM8962_GPIO_FN_DMICDAT, + }, +}; + static struct mxc_audio_platform_data wm8962_data = { .ssi_num = 1, .src_port = 2, .ext_port = 3, .hp_gpio = SABRESD_HEADPHONE_DET, .hp_active_low = 1, + .mic_gpio = SABRESD_MICROPHONE_DET, + .mic_active_low = 1, }; static int mxc_wm8962_init(void) @@ -399,6 +421,11 @@ static struct regulator_consumer_supply sabresd_vwm8962_consumers[] = { }; static struct regulator_init_data sabresd_vwm8962_init = { + .constraints = { + .name = "SPKVDD", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .boot_on = 1, + }, .num_consumer_supplies = ARRAY_SIZE(sabresd_vwm8962_consumers), .consumer_supplies = sabresd_vwm8962_consumers, }; @@ -689,7 +716,12 @@ static int __init max17135_regulator_init(struct max17135 *max17135) } } - regulator_has_full_constraints(); + /* + * TODO: We cannot enable full constraints for now, since + * it results in the PFUZE regulators being disabled + * at the end of boot, which disables critical regulators. + */ + /*regulator_has_full_constraints();*/ return 0; } @@ -1438,6 +1470,17 @@ static struct mipi_csi2_platform_data mipi_csi2_pdata = { .pixel_clk = "emi_clk", }; +#define SNVS_LPCR 0x38 +static void mx6_snvs_poweroff(void) +{ + + void __iomem *mx6_snvs_base = MX6_IO_ADDRESS(MX6Q_SNVS_BASE_ADDR); + u32 value; + value = readl(mx6_snvs_base + SNVS_LPCR); + /*set TOP and DP_EN bit*/ + writel(value | 0x60, mx6_snvs_base + SNVS_LPCR); +} + /*! * Board specific initialization. */ @@ -1499,6 +1542,7 @@ static void __init mx6_sabresd_board_init(void) for (i = 0; i < (ARRAY_SIZE(sabresd_fb_data) + 1) / 2; i++) imx6q_add_ipuv3fb(i, &sabresd_fb_data[i]); + imx6q_add_vdoa(); imx6q_add_lcdif(&lcdif_data); imx6q_add_ldb(&ldb_data); imx6q_add_v4l2_output(0); @@ -1508,9 +1552,10 @@ static void __init mx6_sabresd_board_init(void) if (board_is_mx6_reva()) { strcpy(mxc_i2c0_board_info[0].type, "wm8958"); - mxc_i2c0_board_info[0].platform_data = &wm8958_pdata; + mxc_i2c0_board_info[0].platform_data = &wm8958_config_data; } else { strcpy(mxc_i2c0_board_info[0].type, "wm8962"); + mxc_i2c0_board_info[0].platform_data = &wm8962_config_data; } imx6q_add_imx_i2c(0, &mx6q_sabresd_i2c_data); imx6q_add_imx_i2c(1, &mx6q_sabresd_i2c_data); @@ -1633,6 +1678,8 @@ static void __init mx6_sabresd_board_init(void) /* Register charger chips */ platform_device_register(&sabresd_max8903_charger_1); + pm_power_off = mx6_snvs_poweroff; + } extern void __iomem *twd_base; diff --git a/arch/arm/mach-mx6/board-mx6q_sabresd.h b/arch/arm/mach-mx6/board-mx6q_sabresd.h index 69c46f9727fc..f4c04d3111b5 100644 --- a/arch/arm/mach-mx6/board-mx6q_sabresd.h +++ b/arch/arm/mach-mx6/board-mx6q_sabresd.h @@ -242,9 +242,10 @@ static iomux_v3_cfg_t mx6q_sabresd_pads[] = { MX6Q_PAD_ENET_RXD0__GPIO_1_27, /* UOK_B */ MX6Q_PAD_EIM_CS1__GPIO_2_24, /* DOK_B */ - /* WM8958 */ + /* Audio Codec */ MX6Q_PAD_KEY_COL2__GPIO_4_10, /* CODEC_PWR_EN */ MX6Q_PAD_SD3_RST__GPIO_7_8, /* HEADPHONE_DET */ + MX6Q_PAD_GPIO_9__GPIO_1_9, /* MICROPHONE_DET */ /*GPS AUX_3V15_EN*/ MX6Q_PAD_NANDF_WP_B__GPIO_6_9, diff --git a/arch/arm/mach-mx6/board-mx6solo_sabreauto.h b/arch/arm/mach-mx6/board-mx6solo_sabreauto.h index 25a1bdc64911..3829c7ac38e6 100644 --- a/arch/arm/mach-mx6/board-mx6solo_sabreauto.h +++ b/arch/arm/mach-mx6/board-mx6solo_sabreauto.h @@ -188,6 +188,9 @@ static iomux_v3_cfg_t mx6dl_sabreauto_pads[] = { MX6DL_PAD_GPIO_8__UART2_RXD, MX6DL_PAD_SD4_DAT6__UART2_CTS, MX6DL_PAD_SD4_DAT5__UART2_RTS, + /* UART 3 */ + MX6DL_PAD_SD4_CLK__UART3_TXD, + MX6DL_PAD_SD4_CMD__UART3_RXD, /*USBs OC pin */ MX6DL_PAD_EIM_WAIT__GPIO_5_0, /*HOST1_OC*/ MX6DL_PAD_SD4_DAT0__GPIO_2_8, /*OTG_OC*/ diff --git a/arch/arm/mach-mx6/clock.c b/arch/arm/mach-mx6/clock.c index c333c274713d..d84e3111390b 100644 --- a/arch/arm/mach-mx6/clock.c +++ b/arch/arm/mach-mx6/clock.c @@ -29,6 +29,8 @@ #include <mach/common.h> #include <mach/clock.h> #include <mach/mxc_dvfs.h> +#include <mach/ahci_sata.h> +#include <mach/mxc_hdmi.h> #include "crm_regs.h" #include "cpu_op-mx6.h" #include "regs-anadig.h" @@ -51,6 +53,9 @@ void __iomem *apll_base; static struct clk pll1_sys_main_clk; static struct clk pll2_528_bus_main_clk; static struct clk pll2_pfd_400M; +static struct clk pll2_pfd_352M; +static struct clk pll3_pfd_540M; +static struct clk pll2_pfd_594M; static struct clk pll3_usb_otg_main_clk; static struct clk pll4_audio_main_clk; static struct clk pll5_video_main_clk; @@ -64,11 +69,14 @@ static struct clk usdhc3_clk; static struct cpu_op *cpu_op_tbl; static int cpu_op_nr; +static bool pll1_enabled; +static bool arm_needs_pll2_400; #define SPIN_DELAY 1200000 /* in nanoseconds */ #define AUDIO_VIDEO_MIN_CLK_FREQ 650000000 #define AUDIO_VIDEO_MAX_CLK_FREQ 1300000000 +DEFINE_SPINLOCK(clk_lock); /* We need to check the exp status again after timer expiration, * as there might be interrupt coming between the first time exp @@ -192,6 +200,7 @@ static void _clk_disable_inwait(struct clk *clk) __raw_writel(reg, clk->enable_reg); } + /* * For the 4-to-1 muxed input clock */ @@ -512,13 +521,26 @@ static int _clk_pll1_main_set_rate(struct clk *clk, unsigned long rate) return 0; } +static void _clk_pll1_disable(struct clk *clk) +{ + pll1_enabled = false; + _clk_pll_disable(clk); +} + +static int _clk_pll1_enable(struct clk *clk) +{ + _clk_pll_enable(clk); + pll1_enabled = true; + return 0; +} + static struct clk pll1_sys_main_clk = { __INIT_CLK_DEBUG(pll1_sys_main_clk) .parent = &osc_clk, .get_rate = _clk_pll1_main_get_rate, .set_rate = _clk_pll1_main_set_rate, - .enable = _clk_pll_enable, - .disable = _clk_pll_disable, + .enable = _clk_pll1_enable, + .disable = _clk_pll1_disable, }; static int _clk_pll1_sw_set_parent(struct clk *clk, struct clk *parent) @@ -545,7 +567,6 @@ static int _clk_pll1_sw_set_parent(struct clk *clk, struct clk *parent) reg = __raw_readl(MXC_CCM_CCSR); reg |= MXC_CCM_CCSR_STEP_SEL; __raw_writel(reg, MXC_CCM_CCSR); - } reg = __raw_readl(MXC_CCM_CCSR); reg |= MXC_CCM_CCSR_PLL1_SW_CLK_SEL; @@ -610,13 +631,19 @@ static struct clk pll2_528_bus_main_clk = { .disable = _clk_pll_disable, }; +static void _clk_pll2_pfd_400M_disable(struct clk *clk) +{ + if (!arm_needs_pll2_400) + _clk_pfd_disable(clk); +} + static struct clk pll2_pfd_400M = { __INIT_CLK_DEBUG(pll2_pfd_400M) .parent = &pll2_528_bus_main_clk, .enable_reg = (void *)PFD_528_BASE_ADDR, .enable_shift = ANADIG_PFD2_FRAC_OFFSET, .enable = _clk_pfd_enable, - .disable = _clk_pfd_disable, + .disable = _clk_pll2_pfd_400M_disable, .get_rate = pfd_get_rate, .set_rate = pfd_set_rate, .get_rate = pfd_get_rate, @@ -1152,8 +1179,8 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate) { int i; u32 div; - u32 parent_rate; - + unsigned long parent_rate; + unsigned long flags; for (i = 0; i < cpu_op_nr; i++) { if (rate == cpu_op_tbl[i].cpu_rate) @@ -1162,30 +1189,67 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate) if (i >= cpu_op_nr) return -EINVAL; - if (cpu_op_tbl[i].pll_rate != clk_get_rate(&pll1_sys_main_clk)) { - /* Change the PLL1 rate. */ - if (pll2_pfd_400M.usecount != 0) + spin_lock_irqsave(&clk_lock, flags); + + if (rate <= clk_get_rate(&pll2_pfd_400M)) { + /* Source pll1_sw_clk from step_clk which is sourced from + * PLL2_PFD_400M. + */ + if (pll1_sw_clk.parent != &pll2_pfd_400M) { + pll2_pfd_400M.enable(&pll2_pfd_400M); + arm_needs_pll2_400 = true; pll1_sw_clk.set_parent(&pll1_sw_clk, &pll2_pfd_400M); - else - pll1_sw_clk.set_parent(&pll1_sw_clk, &osc_clk); - pll1_sys_main_clk.set_rate(&pll1_sys_main_clk, cpu_op_tbl[i].pll_rate); - pll1_sw_clk.set_parent(&pll1_sw_clk, &pll1_sys_main_clk); + pll1_sw_clk.parent = &pll2_pfd_400M; + } + } else { + if (pll1_sw_clk.parent != &pll1_sys_main_clk) { + /* pll1_sw_clk was being sourced from pll2_400M. */ + /* Enable PLL1 and set pll1_sw_clk parent as PLL1 */ + if (!pll1_enabled) + pll1_sys_main_clk.enable(&pll1_sys_main_clk); + pll1_sw_clk.set_parent(&pll1_sw_clk, &pll1_sys_main_clk); + pll1_sw_clk.parent = &pll1_sys_main_clk; + arm_needs_pll2_400 = false; + if (pll2_pfd_400M.usecount == 0) + pll2_pfd_400M.disable(&pll2_pfd_400M); + } + if (cpu_op_tbl[i].pll_rate != clk_get_rate(&pll1_sys_main_clk)) { + /* Change the PLL1 rate. */ + if (pll2_pfd_400M.usecount != 0) + pll1_sw_clk.set_parent(&pll1_sw_clk, &pll2_pfd_400M); + else + pll1_sw_clk.set_parent(&pll1_sw_clk, &osc_clk); + pll1_sys_main_clk.set_rate(&pll1_sys_main_clk, cpu_op_tbl[i].pll_rate); + pll1_sw_clk.set_parent(&pll1_sw_clk, &pll1_sys_main_clk); + } } - parent_rate = clk_get_rate(clk->parent); div = parent_rate / rate; - if (div == 0) div = 1; if ((parent_rate / div) > rate) div++; - if (div > 8) + if (div > 8) { + spin_unlock_irqrestore(&clk_lock, flags); return -1; + } + + /* Need PLL1-MAIN to be ON to write to ARM-PODF bit. */ + if (!pll1_enabled) + pll1_sys_main_clk.enable(&pll1_sys_main_clk); __raw_writel(div - 1, MXC_CCM_CACRR); + while (__raw_readl(MXC_CCM_CDHIPR)) + ; + + if (pll1_sys_main_clk.usecount == 1 && arm_needs_pll2_400) + pll1_sys_main_clk.disable(&pll1_sys_main_clk); + + spin_unlock_irqrestore(&clk_lock, flags); + return 0; } @@ -1839,8 +1903,8 @@ static struct clk vdo_axi_clk = { static struct clk vdoa_clk = { __INIT_CLK_DEBUG(vdoa_clk) .id = 0, - .parent = &axi_clk, - .secondary = &mx6fast1_clk, + .parent = &vdo_axi_clk, + .secondary = &ipg_clk, .enable_reg = MXC_CCM_CCGR2, .enable_shift = MXC_CCM_CCGRx_CG13_OFFSET, .enable = _clk_enable, @@ -2007,6 +2071,7 @@ static struct clk vpu_clk[] = { .set_rate = _clk_vpu_axi_set_rate, .get_rate = _clk_vpu_axi_get_rate, .secondary = &vpu_clk[1], + .flags = AHB_MED_SET_POINT | CPU_FREQ_TRIG_UPDATE, }, { .parent = &mmdc_ch0_axi_clk[0], @@ -2016,7 +2081,6 @@ static struct clk vpu_clk[] = { .parent = &mx6fast1_clk, .secondary = &ocram_clk, }, - }; static int _clk_ipu1_set_parent(struct clk *clk, struct clk *parent) @@ -2097,7 +2161,7 @@ static struct clk ipu1_clk = { .round_rate = _clk_ipu_round_rate, .set_rate = _clk_ipu1_set_rate, .get_rate = _clk_ipu1_get_rate, - .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, + .flags = AHB_MED_SET_POINT | CPU_FREQ_TRIG_UPDATE, }; static int _clk_ipu2_set_parent(struct clk *clk, struct clk *parent) @@ -2158,7 +2222,7 @@ static struct clk ipu2_clk = { .round_rate = _clk_ipu_round_rate, .set_rate = _clk_ipu2_set_rate, .get_rate = _clk_ipu2_get_rate, - .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, + .flags = AHB_MED_SET_POINT | CPU_FREQ_TRIG_UPDATE, }; static struct clk usdhc_dep_clk = { @@ -2729,7 +2793,7 @@ static struct clk ldb_di0_clk = { .set_rate = _clk_ldb_di0_set_rate, .round_rate = _clk_ldb_di_round_rate, .get_rate = _clk_ldb_di0_get_rate, - .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, + .flags = AHB_MED_SET_POINT | CPU_FREQ_TRIG_UPDATE, }; static unsigned long _clk_ldb_di1_get_rate(struct clk *clk) @@ -2796,7 +2860,7 @@ static struct clk ldb_di1_clk = { .set_rate = _clk_ldb_di1_set_rate, .round_rate = _clk_ldb_di_round_rate, .get_rate = _clk_ldb_di1_get_rate, - .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, + .flags = AHB_MED_SET_POINT | CPU_FREQ_TRIG_UPDATE, }; @@ -2989,7 +3053,7 @@ static struct clk ipu1_di_clk[] = { .set_rate = _clk_ipu1_di0_set_rate, .round_rate = _clk_ipu_di_round_rate, .get_rate = _clk_ipu1_di0_get_rate, - .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, + .flags = AHB_MED_SET_POINT | CPU_FREQ_TRIG_UPDATE, }, { __INIT_CLK_DEBUG(ipu1_di_clk_1) @@ -3003,7 +3067,7 @@ static struct clk ipu1_di_clk[] = { .set_rate = _clk_ipu1_di1_set_rate, .round_rate = _clk_ipu_di_round_rate, .get_rate = _clk_ipu1_di1_get_rate, - .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, + .flags = AHB_MED_SET_POINT | CPU_FREQ_TRIG_UPDATE, }, }; @@ -3166,7 +3230,7 @@ static struct clk ipu2_di_clk[] = { .set_rate = _clk_ipu2_di0_set_rate, .round_rate = _clk_ipu_di_round_rate, .get_rate = _clk_ipu2_di0_get_rate, - .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, + .flags = AHB_MED_SET_POINT | CPU_FREQ_TRIG_UPDATE, }, { __INIT_CLK_DEBUG(ipu2_di_clk_1) @@ -3180,7 +3244,7 @@ static struct clk ipu2_di_clk[] = { .set_rate = _clk_ipu2_di1_set_rate, .round_rate = _clk_ipu_di_round_rate, .get_rate = _clk_ipu2_di1_get_rate, - .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, + .flags = AHB_MED_SET_POINT | CPU_FREQ_TRIG_UPDATE, }, }; @@ -4254,9 +4318,9 @@ static struct clk gpu3d_core_clk[] = { __INIT_CLK_DEBUG(gpu3d_core_clk) .parent = &pll2_pfd_594M, .enable = _clk_enable, + .disable = _clk_disable, .enable_reg = MXC_CCM_CCGR1, .enable_shift = MXC_CCM_CCGRx_CG13_OFFSET, - .disable = _clk_disable, .set_parent = _clk_gpu3d_core_set_parent, .set_rate = _clk_gpu3d_core_set_rate, .get_rate = _clk_gpu3d_core_get_rate, @@ -4656,7 +4720,7 @@ static struct clk usboh3_clk[] = { .enable_shift = MXC_CCM_CCGRx_CG0_OFFSET, .disable = _clk_disable, .secondary = &usboh3_clk[1], - .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, + .flags = AHB_MED_SET_POINT | CPU_FREQ_TRIG_UPDATE, }, { .parent = &mmdc_ch0_axi_clk[0], @@ -5049,7 +5113,7 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK(NULL, "hdmi_isfr_clk", hdmi_clk[0]), _REGISTER_CLOCK(NULL, "hdmi_iahb_clk", hdmi_clk[1]), _REGISTER_CLOCK(NULL, "mipi_pllref_clk", mipi_pllref_clk), - _REGISTER_CLOCK(NULL, NULL, vdoa_clk), + _REGISTER_CLOCK(NULL, "vdoa", vdoa_clk), _REGISTER_CLOCK(NULL, NULL, aips_tz2_clk), _REGISTER_CLOCK(NULL, NULL, aips_tz1_clk), _REGISTER_CLOCK(NULL, "clko_clk", clko_clk), @@ -5201,9 +5265,9 @@ int __init mx6_clocks_init(unsigned long ckil, unsigned long osc, 3 << MXC_CCM_CCGRx_CG9_OFFSET | 3 << MXC_CCM_CCGRx_CG8_OFFSET, MXC_CCM_CCGR2); __raw_writel(1 << MXC_CCM_CCGRx_CG14_OFFSET | - 3 << MXC_CCM_CCGRx_CG13_OFFSET | + 1 << MXC_CCM_CCGRx_CG13_OFFSET | 3 << MXC_CCM_CCGRx_CG12_OFFSET | - 3 << MXC_CCM_CCGRx_CG11_OFFSET | + 1 << MXC_CCM_CCGRx_CG11_OFFSET | 3 << MXC_CCM_CCGRx_CG10_OFFSET, MXC_CCM_CCGR3); __raw_writel(3 << MXC_CCM_CCGRx_CG7_OFFSET | 1 << MXC_CCM_CCGRx_CG6_OFFSET | diff --git a/arch/arm/mach-mx6/cpu_op-mx6.c b/arch/arm/mach-mx6/cpu_op-mx6.c index 4fe3086dd7cc..80b5e055d089 100644 --- a/arch/arm/mach-mx6/cpu_op-mx6.c +++ b/arch/arm/mach-mx6/cpu_op-mx6.c @@ -40,14 +40,14 @@ static struct cpu_op mx6_cpu_op_1_2G[] = { .cpu_rate = 672000000, .cpu_voltage = 1100000,}, { - .pll_rate = 792000000, + .pll_rate = 396000000, .cpu_rate = 396000000, - .cpu_podf = 1, + .cpu_podf = 0, .cpu_voltage = 950000,}, { - .pll_rate = 792000000, + .pll_rate = 396000000, .cpu_rate = 198000000, - .cpu_podf = 3, + .cpu_podf = 1, .cpu_voltage = 850000,}, }; @@ -68,14 +68,14 @@ static struct cpu_op mx6_cpu_op_1G[] = { .cpu_rate = 672000000, .cpu_voltage = 1100000,}, { - .pll_rate = 792000000, + .pll_rate = 396000000, .cpu_rate = 396000000, - .cpu_podf = 1, + .cpu_podf = 0, .cpu_voltage = 950000,}, { - .pll_rate = 792000000, + .pll_rate = 396000000, .cpu_rate = 198000000, - .cpu_podf = 3, + .cpu_podf = 1, .cpu_voltage = 850000,}, }; @@ -86,14 +86,14 @@ static struct cpu_op mx6_cpu_op[] = { .cpu_podf = 0, .cpu_voltage = 1100000,}, { - .pll_rate = 792000000, + .pll_rate = 396000000, .cpu_rate = 396000000, - .cpu_podf = 1, + .cpu_podf = 0, .cpu_voltage = 950000,}, { - .pll_rate = 792000000, + .pll_rate = 396000000, .cpu_rate = 198000000, - .cpu_podf = 3, + .cpu_podf = 1, .cpu_voltage = 850000,}, }; @@ -110,14 +110,14 @@ static struct cpu_op mx6dl_cpu_op_1_2G[] = { .cpu_podf = 0, .cpu_voltage = 1100000,}, { - .pll_rate = 792000000, + .pll_rate = 396000000, .cpu_rate = 396000000, - .cpu_podf = 1, + .cpu_podf = 0, .cpu_voltage = 1000000,}, { - .pll_rate = 792000000, + .pll_rate = 396000000, .cpu_rate = 198000000, - .cpu_podf = 3, + .cpu_podf = 1, .cpu_voltage = 1000000,}, }; /* working point(wp): 0 - 1GHz; 1 - 800MHz, 2 - 400MHz, 3 - 200MHz */ @@ -133,14 +133,14 @@ static struct cpu_op mx6dl_cpu_op_1G[] = { .cpu_podf = 0, .cpu_voltage = 1100000,}, { - .pll_rate = 792000000, + .pll_rate = 396000000, .cpu_rate = 396000000, - .cpu_podf = 1, + .cpu_podf = 0, .cpu_voltage = 1000000,}, { - .pll_rate = 792000000, + .pll_rate = 396000000, .cpu_rate = 198000000, - .cpu_podf = 3, + .cpu_podf = 1, .cpu_voltage = 1000000,}, }; @@ -151,35 +151,35 @@ static struct cpu_op mx6dl_cpu_op[] = { .cpu_podf = 0, .cpu_voltage = 1100000,}, { - .pll_rate = 792000000, + .pll_rate = 396000000, .cpu_rate = 396000000, - .cpu_podf = 1, + .cpu_podf = 0, .cpu_voltage = 1000000,}, { - .pll_rate = 792000000, + .pll_rate = 396000000, .cpu_rate = 198000000, - .cpu_podf = 3, + .cpu_podf = 1, .cpu_voltage = 1000000,}, }; static struct dvfs_op dvfs_core_setpoint_1_2G[] = { {33, 14, 33, 10, 128, 0x10}, /* 1.2GHz*/ {30, 12, 33, 100, 200, 0x10}, /* 800MHz */ - {28, 12, 33, 100, 200, 0x10}, /* 624MHz */ + {28, 12, 33, 100, 200, 0x10}, /* 672MHz */ {26, 8, 33, 100, 200, 0x10}, /* 400MHz */ {20, 0, 33, 20, 10, 0x10} }; /* 200MHz*/ static struct dvfs_op dvfs_core_setpoint_1G[] = { {33, 14, 33, 10, 128, 0x10}, /* 1GHz*/ {30, 12, 33, 100, 200, 0x10}, /* 800MHz */ - {28, 12, 33, 100, 200, 0x10}, /* 624MHz */ + {28, 12, 33, 100, 200, 0x10}, /* 672MHz */ {26, 8, 33, 100, 200, 0x10}, /* 400MHz */ {20, 0, 33, 20, 10, 0x10} }; /* 200MHz*/ static struct dvfs_op dvfs_core_setpoint[] = { {33, 14, 33, 10, 128, 0x08}, /* 800MHz */ {26, 8, 33, 100, 200, 0x08}, /* 400MHz */ - {20, 0, 33, 20, 10, 0x08} }; /* 200MHz*/ + {20, 0, 33, 100, 10, 0x08} }; /* 200MHz*/ static struct dvfs_op *mx6_get_dvfs_core_table(int *wp) { diff --git a/arch/arm/mach-mx6/devices-imx6q.h b/arch/arm/mach-mx6/devices-imx6q.h index ab245158cfb7..02ac1f409799 100644 --- a/arch/arm/mach-mx6/devices-imx6q.h +++ b/arch/arm/mach-mx6/devices-imx6q.h @@ -208,3 +208,5 @@ extern const struct imx_pxp_data imx6dl_pxp_data __initconst; extern const struct imx_epdc_data imx6dl_epdc_data __initconst; #define imx6dl_add_imx_epdc(pdata) \ imx_add_imx_epdc(&imx6dl_epdc_data, pdata) +extern const struct imx_vdoa_data imx6q_vdoa_data __initconst; +#define imx6q_add_vdoa() imx_add_vdoa(&imx6q_vdoa_data) diff --git a/arch/arm/mach-mx6/irq.c b/arch/arm/mach-mx6/irq.c index 753767bfd7c4..2434c1b11606 100644 --- a/arch/arm/mach-mx6/irq.c +++ b/arch/arm/mach-mx6/irq.c @@ -57,12 +57,20 @@ static struct irq_tuner mxc_irq_tuner[] = { .up_threshold = 0, .enable = 1,}, { + .irq_number = 54, /* uSDHC1 */ + .up_threshold = 4, + .enable = 1,}, + { + .irq_number = 55, /* uSDHC2 */ + .up_threshold = 4, + .enable = 1,}, + { .irq_number = 56, /* uSDHC3 */ - .up_threshold = 8, + .up_threshold = 4, .enable = 1,}, { .irq_number = 57, /* uSDHC4 */ - .up_threshold = 8, + .up_threshold = 4, .enable = 1,}, { .irq_number = 71, /* SATA */ diff --git a/arch/arm/mach-mx6/usb_dr.c b/arch/arm/mach-mx6/usb_dr.c index 3a8a3a7ec242..24c8cb9c4d31 100644 --- a/arch/arm/mach-mx6/usb_dr.c +++ b/arch/arm/mach-mx6/usb_dr.c @@ -415,14 +415,14 @@ static void _host_platform_rh_resume(struct fsl_usb2_platform_data *pdata) while ((UOG_PORTSC1 & PORTSC_PORT_FORCE_RESUME) && (index < 1000)) { - msleep(1); + udelay(500); index++; } if (index >= 1000) printk(KERN_INFO "%s big error\n", __func__); - msleep(1); + udelay(500); fsl_platform_otg_set_usb_phy_dis(pdata, 1); } diff --git a/arch/arm/mach-mx6/usb_h1.c b/arch/arm/mach-mx6/usb_h1.c index 4d9d1527a0b1..478ead40cf0d 100644 --- a/arch/arm/mach-mx6/usb_h1.c +++ b/arch/arm/mach-mx6/usb_h1.c @@ -243,14 +243,14 @@ static void usbh1_platform_rh_resume(struct fsl_usb2_platform_data *pdata) while ((UH1_PORTSC1 & PORTSC_PORT_FORCE_RESUME) && (index < 1000)) { - msleep(1); + udelay(500); index++; } if (index >= 1000) printk(KERN_INFO "%s big error\n", __func__); - msleep(1); + udelay(500); fsl_platform_h1_set_usb_phy_dis(pdata, 1); } diff --git a/arch/arm/plat-mxc/devices/Kconfig b/arch/arm/plat-mxc/devices/Kconfig index 3eead8ebda92..350845eb64f0 100755 --- a/arch/arm/plat-mxc/devices/Kconfig +++ b/arch/arm/plat-mxc/devices/Kconfig @@ -169,3 +169,6 @@ config IMX_HAVE_PLATFORM_IMX_MIPI_DSI config IMX_HAVE_PLATFORM_IMX_MIPI_CSI2 bool + +config IMX_HAVE_PLATFORM_IMX_VDOA + bool diff --git a/arch/arm/plat-mxc/devices/Makefile b/arch/arm/plat-mxc/devices/Makefile index 71cfefa7cccb..f2741caecfca 100755 --- a/arch/arm/plat-mxc/devices/Makefile +++ b/arch/arm/plat-mxc/devices/Makefile @@ -61,3 +61,4 @@ obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_HDMI) += platform-imx-hdmi-soc-dai.o obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_ASRC) += platform-imx-asrc.o obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_MIPI_DSI) += platform-imx-mipi_dsi.o obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_MIPI_CSI2) += platform-imx-mipi_csi2.o +obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_VDOA) += platform-imx-vdoa.o diff --git a/arch/arm/plat-mxc/devices/platform-imx-vdoa.c b/arch/arm/plat-mxc/devices/platform-imx-vdoa.c new file mode 100644 index 000000000000..057fda6b389e --- /dev/null +++ b/arch/arm/plat-mxc/devices/platform-imx-vdoa.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2012 Freescale Semiconductor, 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 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <mach/hardware.h> +#include <mach/devices-common.h> + +#define imx_vdoa_data_entry_single(soc, _id, _hwid, size) \ + { \ + .id = _id, \ + .iobase = soc ## _VDOA ## _hwid ## _BASE_ADDR, \ + .iosize = size, \ + .irq = soc ## _INT_VDOA ## _hwid, \ + } + +#define imx_vdoa_data_entry(soc, _id, _hwid, _size) \ + [_id] = imx_vdoa_data_entry_single(soc, _id, _hwid, _size) + +#ifdef CONFIG_SOC_IMX6Q +#define MX6Q_VDOA_BASE_ADDR VDOA_BASE_ADDR +#define SOC_VDOA_BASE_ADDR MX6Q_VDOA_BASE_ADDR +#define MX6Q_INT_VDOA MXC_INT_VDOA +const struct imx_vdoa_data imx6q_vdoa_data __initconst = + imx_vdoa_data_entry_single(MX6Q, 0, , SZ_4K); +#endif + +struct platform_device *__init imx_add_vdoa( + const struct imx_vdoa_data *data) +{ + struct resource res[] = { + { + .start = data->iobase, + .end = data->iobase + data->iosize - 1, + .flags = IORESOURCE_MEM, + }, { + .start = data->irq, + .end = data->irq, + .flags = IORESOURCE_IRQ, + }, + }; + + return imx_add_platform_device("mxc_vdoa", -1, + res, ARRAY_SIZE(res), NULL, 0); +} diff --git a/arch/arm/plat-mxc/include/mach/devices-common.h b/arch/arm/plat-mxc/include/mach/devices-common.h index 2f5f04c39638..9f87b009f129 100755 --- a/arch/arm/plat-mxc/include/mach/devices-common.h +++ b/arch/arm/plat-mxc/include/mach/devices-common.h @@ -633,3 +633,12 @@ struct imx_rngb_data { struct platform_device *__init imx_add_rngb( const struct imx_rngb_data *data); + +struct imx_vdoa_data { + int id; + resource_size_t iobase; + resource_size_t iosize; + resource_size_t irq; +}; +struct platform_device *__init imx_add_vdoa( + const struct imx_vdoa_data *data); diff --git a/arch/arm/plat-mxc/include/mach/dma.h b/arch/arm/plat-mxc/include/mach/dma.h index ee3b962eeb4d..ba00959c19fa 100644 --- a/arch/arm/plat-mxc/include/mach/dma.h +++ b/arch/arm/plat-mxc/include/mach/dma.h @@ -55,6 +55,7 @@ enum imx_dma_prio { struct imx_dma_data { int dma_request; /* DMA request line */ + int dma_request_p2p; enum sdma_peripheral_type peripheral_type; int priority; }; diff --git a/arch/arm/plat-mxc/include/mach/ipu-v3.h b/arch/arm/plat-mxc/include/mach/ipu-v3.h index 02c699d9e067..1ddd69ed1bb1 100755 --- a/arch/arm/plat-mxc/include/mach/ipu-v3.h +++ b/arch/arm/plat-mxc/include/mach/ipu-v3.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> - * Copyright (C) 2011 Freescale Semiconductor, Inc. + * Copyright (C) 2011-2012 Freescale 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 @@ -71,10 +71,19 @@ typedef enum { CSI_PRP_ENC_MEM = _MAKE_CHAN(19, NO_DMA, NO_DMA, NO_DMA, 20), CSI_PRP_VF_MEM = _MAKE_CHAN(20, NO_DMA, NO_DMA, NO_DMA, 21), + /* for vdi mem->vdi->ic->mem , add graphics plane and alpha*/ MEM_VDI_PRP_VF_MEM_P = _MAKE_CHAN(21, 8, 14, 17, 21), MEM_VDI_PRP_VF_MEM = _MAKE_CHAN(22, 9, 14, 17, 21), MEM_VDI_PRP_VF_MEM_N = _MAKE_CHAN(23, 10, 14, 17, 21), + /* for vdi mem->vdi->mem */ + MEM_VDI_MEM_P = _MAKE_CHAN(24, 8, NO_DMA, NO_DMA, 5), + MEM_VDI_MEM = _MAKE_CHAN(25, 9, NO_DMA, NO_DMA, 5), + MEM_VDI_MEM_N = _MAKE_CHAN(26, 10, NO_DMA, NO_DMA, 5), + + /* fake channel for vdoa to link with IPU */ + MEM_VDOA_MEM = _MAKE_CHAN(27, NO_DMA, NO_DMA, NO_DMA, NO_DMA), + MEM_PP_ADC = CHAN_NONE, ADC_SYS2 = CHAN_NONE, @@ -308,6 +317,7 @@ enum ipu_irq_line { IPU_IRQ_CSI1_OUT_EOF = 1, IPU_IRQ_CSI2_OUT_EOF = 2, IPU_IRQ_CSI3_OUT_EOF = 3, + IPU_IRQ_VDIC_OUT_EOF = 5, IPU_IRQ_VDI_P_IN_EOF = 8, IPU_IRQ_VDI_C_IN_EOF = 9, IPU_IRQ_VDI_N_IN_EOF = 10, @@ -616,6 +626,7 @@ uint32_t ipu_get_cur_buffer_idx(struct ipu_soc *ipu, ipu_channel_t channel, ipu_ int32_t ipu_enable_channel(struct ipu_soc *ipu, ipu_channel_t channel); int32_t ipu_disable_channel(struct ipu_soc *ipu, ipu_channel_t channel, bool wait_for_stop); int32_t ipu_swap_channel(struct ipu_soc *ipu, ipu_channel_t from_ch, ipu_channel_t to_ch); +uint32_t ipu_channel_status(struct ipu_soc *ipu, ipu_channel_t channel); int32_t ipu_enable_csi(struct ipu_soc *ipu, uint32_t csi); int32_t ipu_disable_csi(struct ipu_soc *ipu, uint32_t csi); @@ -632,6 +643,8 @@ int ipu_request_irq(struct ipu_soc *ipu, uint32_t irq, void ipu_free_irq(struct ipu_soc *ipu, uint32_t irq, void *dev_id); bool ipu_get_irq_status(struct ipu_soc *ipu, uint32_t irq); void ipu_set_csc_coefficients(struct ipu_soc *ipu, ipu_channel_t channel, int32_t param[][3]); +int32_t ipu_set_channel_bandmode(struct ipu_soc *ipu, ipu_channel_t channel, + ipu_buffer_t type, uint32_t band_height); /* two stripe calculations */ struct stripe_param{ diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 1fa2889d4dbc..68232e39f6bc 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -77,7 +77,7 @@ static unsigned long min_sample_time; /* * The sample rate of the timer used to increase frequency */ -#define DEFAULT_TIMER_RATE (20 * USEC_PER_MSEC) +#define DEFAULT_TIMER_RATE (50 * USEC_PER_MSEC) #define CPUFREQ_IRQ_LEN 60 #define CPUFREQ_NOTE_LEN 120 static unsigned long timer_rate; @@ -377,7 +377,6 @@ static void cpufreq_interactive_idle_end(void) static int cpufreq_interactive_up_task(void *data) { unsigned int cpu; - cpumask_t tmp_mask; unsigned long flags; struct cpufreq_interactive_cpuinfo *pcpu; @@ -396,11 +395,10 @@ static int cpufreq_interactive_up_task(void *data) } set_current_state(TASK_RUNNING); - tmp_mask = up_cpumask; cpumask_clear(&up_cpumask); spin_unlock_irqrestore(&up_cpumask_lock, flags); - for_each_cpu(cpu, &tmp_mask) { + for_each_online_cpu(cpu) { unsigned int j; unsigned int max_freq = 0; @@ -412,7 +410,7 @@ static int cpufreq_interactive_up_task(void *data) mutex_lock(&set_speed_lock); - for_each_cpu(j, pcpu->policy->cpus) { + for_each_online_cpu(j) { struct cpufreq_interactive_cpuinfo *pjcpu = &per_cpu(cpuinfo, j); @@ -438,16 +436,14 @@ static int cpufreq_interactive_up_task(void *data) static void cpufreq_interactive_freq_down(struct work_struct *work) { unsigned int cpu; - cpumask_t tmp_mask; unsigned long flags; struct cpufreq_interactive_cpuinfo *pcpu; spin_lock_irqsave(&down_cpumask_lock, flags); - tmp_mask = down_cpumask; cpumask_clear(&down_cpumask); spin_unlock_irqrestore(&down_cpumask_lock, flags); - for_each_cpu(cpu, &tmp_mask) { + for_each_online_cpu(cpu) { unsigned int j; unsigned int max_freq = 0; @@ -459,7 +455,7 @@ static void cpufreq_interactive_freq_down(struct work_struct *work) mutex_lock(&set_speed_lock); - for_each_cpu(j, pcpu->policy->cpus) { + for_each_online_cpu(j) { struct cpufreq_interactive_cpuinfo *pjcpu = &per_cpu(cpuinfo, j); @@ -575,7 +571,7 @@ static ssize_t show_irq_param(struct kobject *kobj, j += scnprintf(&buf[j], CPUFREQ_NOTE_LEN, "Change irq setting by echo a data, format: 0xAABBBC, AA:irq number, BBB:up_threshold, C:enable\n"); for (i = 0; i < MAX_CPUFREQ_IRQ_NUMBER; i++) { if (irq_tuner_ins[i].irq_number != 0) - j += scnprintf(&buf[j], CPUFREQ_IRQ_LEN, "irq number: %d, up_threshold %d, %s\n", irq_tuner_ins[i].irq_number, irq_tuner_ins[i].up_threshold, irq_tuner_ins[i].enable ? "enabled" : "disabled"); + j += scnprintf(&buf[j], CPUFREQ_IRQ_LEN, "irq number: 0x%x, up_threshold 0x%x, %s\n", irq_tuner_ins[i].irq_number, irq_tuner_ins[i].up_threshold, irq_tuner_ins[i].enable ? "enabled" : "disabled"); } return j; diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index f8232cd68453..6f168fe568a8 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -7,7 +7,7 @@ * * Based on code from Freescale: * - * Copyright 2004-2011 Freescale Semiconductor, Inc. + * Copyright 2004-2012 Freescale Semiconductor, Inc. * * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License @@ -253,9 +253,11 @@ struct sdma_channel { unsigned int num_bd; struct sdma_buffer_descriptor *bd; dma_addr_t bd_phys; - unsigned int pc_from_device, pc_to_device; + unsigned int pc_from_device; + unsigned int pc_to_device; + unsigned int device_to_device; unsigned long flags; - dma_addr_t per_address; + dma_addr_t per_address, per_address2; u32 event_mask0, event_mask1; u32 watermark_level; u32 shp_addr, per_addr; @@ -439,7 +441,6 @@ static void sdma_event_disable(struct sdma_channel *sdmac, unsigned int event) static void sdma_handle_channel_loop(struct sdma_channel *sdmac) { struct sdma_buffer_descriptor *bd; - /* * loop mode. Iterate over descriptors, re-setup them and * call callback function. @@ -542,6 +543,7 @@ static void sdma_get_pc(struct sdma_channel *sdmac, sdmac->pc_from_device = 0; sdmac->pc_to_device = 0; + sdmac->device_to_device = 0; switch (peripheral_type) { case IMX_DMATYPE_MEMORY: @@ -607,6 +609,7 @@ static void sdma_get_pc(struct sdma_channel *sdmac, sdmac->pc_from_device = per_2_emi; sdmac->pc_to_device = emi_2_per; + sdmac->device_to_device = per_2_per; } static int sdma_load_context(struct sdma_channel *sdmac) @@ -618,11 +621,16 @@ static int sdma_load_context(struct sdma_channel *sdmac) struct sdma_buffer_descriptor *bd0 = sdma->channel[0].bd; int ret; - if (sdmac->direction == DMA_DEV_TO_MEM) { + + if (sdmac->direction == DMA_DEV_TO_MEM) load_address = sdmac->pc_from_device; - } else { + else if (sdmac->direction == DMA_DEV_TO_DEV) + load_address = sdmac->device_to_device; + else if (sdmac->direction == DMA_MEM_TO_DEV) load_address = sdmac->pc_to_device; - } + else + load_address = sdmac->pc_to_device; + if (load_address < 0) return load_address; @@ -701,20 +709,52 @@ static int sdma_config_channel(struct sdma_channel *sdmac) (sdmac->peripheral_type != IMX_DMATYPE_DSP)) { /* Handle multiple event channels differently */ if (sdmac->event_id1) { - sdmac->event_mask1 = 1 << (sdmac->event_id1 % 32); - if (sdmac->event_id1 > 31) - sdmac->watermark_level |= 1 << 29; - sdmac->event_mask0 = 1 << (sdmac->event_id0 % 32); - if (sdmac->event_id0 > 31) + if (sdmac->event_id0 > 31) { sdmac->watermark_level |= 1 << 28; + sdmac->event_mask0 |= 0; + sdmac->event_mask1 |= + 1 << ((sdmac->event_id0)%32); + } else { + sdmac->event_mask0 |= + 1 << ((sdmac->event_id0)%32); + sdmac->event_mask1 |= 0; + } + if (sdmac->event_id1 > 31) { + sdmac->watermark_level |= 1 << 29; + sdmac->event_mask0 |= 0; + sdmac->event_mask1 |= + 1 << ((sdmac->event_id1)%32); + } else { + sdmac->event_mask0 |= + 1 << ((sdmac->event_id1)%32); + sdmac->event_mask1 |= 0; + } + sdmac->watermark_level |= (unsigned int)(3<<11); + sdmac->watermark_level |= (unsigned int)(1<<31); + sdmac->watermark_level |= (unsigned int)(2<<24); } else { - sdmac->event_mask0 = 1 << sdmac->event_id0; - sdmac->event_mask1 = 1 << (sdmac->event_id0 - 32); + if (sdmac->event_id0 > 31) { + sdmac->event_mask0 = 0; + sdmac->event_mask1 = + 1 << ((sdmac->event_id0)%32); + } else { + sdmac->event_mask0 = + 1 << ((sdmac->event_id0)%32); + sdmac->event_mask1 = 0; + } } /* Watermark Level */ sdmac->watermark_level |= sdmac->watermark_level; /* Address */ + switch (sdmac->direction) { + case DMA_DEV_TO_DEV: + sdmac->per_addr = sdmac->per_address; + sdmac->shp_addr = sdmac->per_address2; + break; + default: sdmac->shp_addr = sdmac->per_address; + break; + } } else { sdmac->watermark_level = 0; /* FIXME: M3_BASE_ADDRESS */ } @@ -837,6 +877,10 @@ static int sdma_alloc_chan_resources(struct dma_chan *chan) sdmac->peripheral_type = data->peripheral_type; sdmac->event_id0 = data->dma_request; + if (data->dma_request_p2p > 0) + sdmac->event_id1 = data->dma_request_p2p; + else + sdmac->event_id1 = 0; ret = sdma_request_channel(sdmac); if (ret) return ret; @@ -1061,7 +1105,18 @@ static int sdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, sdma_disable_channel(sdmac); return 0; case DMA_SLAVE_CONFIG: - if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { + + sdmac->direction = dmaengine_cfg->direction; + if (dmaengine_cfg->direction == DMA_DEV_TO_DEV) { + sdmac->per_address = dmaengine_cfg->src_addr; + sdmac->per_address2 = dmaengine_cfg->dst_addr; + sdmac->watermark_level = 0; + sdmac->watermark_level |= + dmaengine_cfg->src_maxburst; + sdmac->watermark_level |= + dmaengine_cfg->dst_maxburst << 16; + sdmac->word_size = dmaengine_cfg->dst_addr_width; + } else if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { sdmac->per_address = dmaengine_cfg->src_addr; sdmac->watermark_level = dmaengine_cfg->src_maxburst; sdmac->word_size = dmaengine_cfg->src_addr_width; diff --git a/drivers/mxc/dam/dam.c b/drivers/mxc/dam/dam.c index 9e57e1a260ea..cf8c305cccb6 100644 --- a/drivers/mxc/dam/dam.c +++ b/drivers/mxc/dam/dam.c @@ -1,5 +1,5 @@ /* - * Copyright 2004-2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2004-2012 Freescale Semiconductor, Inc. All Rights Reserved. */ /* @@ -34,7 +34,9 @@ #define ModifyRegister32(a, b, c) (c = (((c)&(~(a))) | (b))) -#define DAM_VIRT_BASE_ADDR IO_ADDRESS(AUDMUX_BASE_ADDR) +#if defined(CONFIG_ARCH_MX6) +#define DAM_VIRT_BASE_ADDR IO_ADDRESS(MX6Q_AUDMUX_BASE_ADDR) +#endif #ifndef _reg_DAM_PTCR1 #define _reg_DAM_PTCR1 (*((volatile unsigned long *) \ diff --git a/drivers/mxc/ipu3/Makefile b/drivers/mxc/ipu3/Makefile index aa3e7b1bb501..8dcad3cdf836 100644 --- a/drivers/mxc/ipu3/Makefile +++ b/drivers/mxc/ipu3/Makefile @@ -1,4 +1,4 @@ obj-$(CONFIG_MXC_IPU_V3) = mxc_ipu.o -mxc_ipu-objs := ipu_common.o ipu_ic.o ipu_disp.o ipu_capture.o ipu_device.o ipu_calc_stripes_sizes.o +mxc_ipu-objs := ipu_common.o ipu_ic.o ipu_disp.o ipu_capture.o ipu_device.o ipu_calc_stripes_sizes.o vdoa.o diff --git a/drivers/mxc/ipu3/ipu_common.c b/drivers/mxc/ipu3/ipu_common.c index a97166a21a58..f03980bdc819 100644 --- a/drivers/mxc/ipu3/ipu_common.c +++ b/drivers/mxc/ipu3/ipu_common.c @@ -53,7 +53,13 @@ static inline uint32_t channel_2_dma(ipu_channel_t ch, ipu_buffer_t type) static inline int _ipu_is_ic_chan(uint32_t dma_chan) { - return ((dma_chan >= 11) && (dma_chan <= 22) && (dma_chan != 17) && (dma_chan != 18)); + return (((dma_chan >= 11) && (dma_chan <= 22) && (dma_chan != 17) && + (dma_chan != 18))); +} + +static inline int _ipu_is_vdi_out_chan(uint32_t dma_chan) +{ + return (dma_chan == 5); } static inline int _ipu_is_ic_graphic_chan(uint32_t dma_chan) @@ -611,6 +617,12 @@ void ipu_dump_registers(struct ipu_soc *ipu) ipu_cm_read(ipu, IPU_FS_PROC_FLOW3)); dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n", ipu_cm_read(ipu, IPU_FS_DISP_FLOW1)); + dev_dbg(ipu->dev, "IPU_VDIC_VDI_FSIZE = \t0x%08X\n", + ipu_vdi_read(ipu, VDI_FSIZE)); + dev_dbg(ipu->dev, "IPU_VDIC_VDI_C = \t0x%08X\n", + ipu_vdi_read(ipu, VDI_C)); + dev_dbg(ipu->dev, "IPU_IC_CONF = \t0x%08X\n", + ipu_ic_read(ipu, IC_CONF)); } /*! @@ -691,7 +703,8 @@ int32_t ipu_init_channel(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel ret = -EINVAL; goto err; } - if (ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) { + if ((ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) || + (ipu->using_ic_dirct_ch == MEM_VDI_MEM)) { ret = -EINVAL; goto err; } @@ -731,7 +744,8 @@ int32_t ipu_init_channel(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel ret = -EINVAL; goto err; } - if (ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) { + if ((ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) || + (ipu->using_ic_dirct_ch == MEM_VDI_MEM)) { ret = -EINVAL; goto err; } @@ -780,6 +794,7 @@ int32_t ipu_init_channel(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel break; case MEM_VDI_PRP_VF_MEM: if ((ipu->using_ic_dirct_ch == CSI_PRP_VF_MEM) || + (ipu->using_ic_dirct_ch == MEM_VDI_MEM) || (ipu->using_ic_dirct_ch == CSI_PRP_ENC_MEM)) { ret = -EINVAL; goto err; @@ -797,9 +812,22 @@ int32_t ipu_init_channel(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel _ipu_vdi_init(ipu, channel, params); break; case MEM_VDI_PRP_VF_MEM_P: + case MEM_VDI_PRP_VF_MEM_N: + case MEM_VDI_MEM_P: + case MEM_VDI_MEM_N: _ipu_vdi_init(ipu, channel, params); break; - case MEM_VDI_PRP_VF_MEM_N: + case MEM_VDI_MEM: + if ((ipu->using_ic_dirct_ch == CSI_PRP_VF_MEM) || + (ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) || + (ipu->using_ic_dirct_ch == CSI_PRP_ENC_MEM)) { + ret = -EINVAL; + goto err; + } + ipu->using_ic_dirct_ch = MEM_VDI_MEM; + ipu->ic_use_count++; + ipu->vdi_use_count++; + _ipu_ic_init_prpvf(ipu, params, false); _ipu_vdi_init(ipu, channel, params); break; case MEM_ROT_VF_MEM: @@ -928,7 +956,7 @@ void ipu_uninit_channel(struct ipu_soc *ipu, ipu_channel_t channel) _ipu_lock(ipu); if ((ipu->channel_init_mask & (1L << IPU_CHAN_ID(channel))) == 0) { - dev_err(ipu->dev, "Channel already uninitialized %d\n", + dev_dbg(ipu->dev, "Channel already uninitialized %d\n", IPU_CHAN_ID(channel)); _ipu_unlock(ipu); return; @@ -1017,8 +1045,18 @@ void ipu_uninit_channel(struct ipu_soc *ipu, ipu_channel_t channel) reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); ipu_cm_write(ipu, reg & ~FS_VF_IN_VALID, IPU_FS_PROC_FLOW1); break; + case MEM_VDI_MEM: + ipu->ic_use_count--; + ipu->vdi_use_count--; + if (ipu->using_ic_dirct_ch == MEM_VDI_MEM) + ipu->using_ic_dirct_ch = 0; + _ipu_ic_uninit_prpvf(ipu); + _ipu_vdi_uninit(ipu); + break; case MEM_VDI_PRP_VF_MEM_P: case MEM_VDI_PRP_VF_MEM_N: + case MEM_VDI_MEM_P: + case MEM_VDI_MEM_N: break; case MEM_ROT_VF_MEM: ipu->rot_use_count--; @@ -1198,6 +1236,12 @@ int32_t ipu_init_channel_buffer(struct ipu_soc *ipu, ipu_channel_t channel, return -EINVAL; } + if (_ipu_is_vdi_out_chan(dma_chan) && + ((width < 16) || (height < 16) || (width % 2) || (height % 4))) { + dev_err(ipu->dev, "vdi width/height limited err\n"); + return -EINVAL; + } + /* IPUv3EX and IPUv3M support triple buffer */ if ((!_ipu_is_trb_chan(dma_chan)) && phyaddr_2) { dev_err(ipu->dev, "Chan%d doesn't support triple buffer " @@ -1234,7 +1278,7 @@ int32_t ipu_init_channel_buffer(struct ipu_soc *ipu, ipu_channel_t channel, _ipu_ch_param_set_rotation(ipu, dma_chan, rot_mode); /* IC and ROT channels have restriction of 8 or 16 pix burst length */ - if (_ipu_is_ic_chan(dma_chan)) { + if (_ipu_is_ic_chan(dma_chan) || _ipu_is_vdi_out_chan(dma_chan)) { if ((width % 16) == 0) _ipu_ch_param_set_burst_size(ipu, dma_chan, 16); else @@ -1252,7 +1296,8 @@ int32_t ipu_init_channel_buffer(struct ipu_soc *ipu, ipu_channel_t channel, ipu->chan_is_interlaced[dma_chan]) _ipu_ch_param_set_interlaced_scan(ipu, dma_chan); - if (_ipu_is_ic_chan(dma_chan) || _ipu_is_irt_chan(dma_chan)) { + if (_ipu_is_ic_chan(dma_chan) || _ipu_is_irt_chan(dma_chan) || + _ipu_is_vdi_out_chan(dma_chan)) { burst_size = _ipu_ch_param_get_burst_size(ipu, dma_chan); _ipu_ic_idma_init(ipu, dma_chan, width, height, burst_size, rot_mode); @@ -1434,6 +1479,46 @@ int32_t ipu_update_channel_buffer(struct ipu_soc *ipu, ipu_channel_t channel, } EXPORT_SYMBOL(ipu_update_channel_buffer); +/*! + * This function is called to update the band mode setting for + * a logical IPU channel. + * + * @param ipu ipu handler + * + * @param channel Input parameter for the logical channel ID. + * + * @param type Input parameter which buffer to initialize. + * + * @param band_height Input parameter for band lines: + * shoule be log2(4/8/16/32/64/128/256). + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int32_t ipu_set_channel_bandmode(struct ipu_soc *ipu, ipu_channel_t channel, + ipu_buffer_t type, uint32_t band_height) +{ + uint32_t reg; + int ret = 0; + uint32_t dma_chan = channel_2_dma(channel, type); + + if ((2 > band_height) || (8 < band_height)) + return -EINVAL; + + _ipu_lock(ipu); + + reg = ipu_idmac_read(ipu, IDMAC_BAND_EN(dma_chan)); + reg |= 1 << (dma_chan % 32); + ipu_idmac_write(ipu, reg, IDMAC_BAND_EN(dma_chan)); + + _ipu_ch_param_set_bandmode(ipu, dma_chan, band_height); + dev_dbg(ipu->dev, "dma_chan:%d, band_height:%d.\n\n", + dma_chan, 1 << band_height); + _ipu_unlock(ipu); + + return ret; +} +EXPORT_SYMBOL(ipu_set_channel_bandmode); /*! * This function is called to initialize a buffer for logical IPU channel. @@ -1688,6 +1773,17 @@ int32_t ipu_link_channels(struct ipu_soc *ipu, ipu_channel_t src_ch, ipu_channel proc_dest_sel[IPU_CHAN_ID(dest_ch)] << FS_PRPVF_ROT_DEST_SEL_OFFSET; break; + case MEM_VDOA_MEM: + fs_proc_flow3 &= ~FS_VDOA_DEST_SEL_MASK; + if (MEM_VDI_MEM == dest_ch) + fs_proc_flow3 |= FS_VDOA_DEST_SEL_VDI; + else if (MEM_PP_MEM == dest_ch) + fs_proc_flow3 |= FS_VDOA_DEST_SEL_IC; + else { + retval = -EINVAL; + goto err; + } + break; default: retval = -EINVAL; goto err; @@ -1696,8 +1792,11 @@ int32_t ipu_link_channels(struct ipu_soc *ipu, ipu_channel_t src_ch, ipu_channel switch (dest_ch) { case MEM_PP_MEM: fs_proc_flow1 &= ~FS_PP_SRC_SEL_MASK; - fs_proc_flow1 |= - proc_src_sel[IPU_CHAN_ID(src_ch)] << FS_PP_SRC_SEL_OFFSET; + if (MEM_VDOA_MEM == src_ch) + fs_proc_flow1 |= FS_PP_SRC_SEL_VDOA; + else + fs_proc_flow1 |= proc_src_sel[IPU_CHAN_ID(src_ch)] << + FS_PP_SRC_SEL_OFFSET; break; case MEM_ROT_PP_MEM: fs_proc_flow1 &= ~FS_PP_ROT_SRC_SEL_MASK; @@ -1766,6 +1865,15 @@ int32_t ipu_link_channels(struct ipu_soc *ipu, ipu_channel_t src_ch, ipu_channel disp_src_sel[IPU_CHAN_ID(src_ch)] << FS_DP_ASYNC1_SRC_SEL_OFFSET; break; + case MEM_VDI_MEM: + fs_proc_flow1 &= ~FS_VDI_SRC_SEL_MASK; + if (MEM_VDOA_MEM == src_ch) + fs_proc_flow1 |= FS_VDI_SRC_SEL_VDOA; + else { + retval = -EINVAL; + goto err; + } + break; default: retval = -EINVAL; goto err; @@ -1851,6 +1959,9 @@ int32_t ipu_unlink_channels(struct ipu_soc *ipu, ipu_channel_t src_ch, ipu_chann case MEM_ROT_VF_MEM: fs_proc_flow2 &= ~FS_PRPVF_ROT_DEST_SEL_MASK; break; + case MEM_VDOA_MEM: + fs_proc_flow3 &= ~FS_VDOA_DEST_SEL_MASK; + break; default: retval = -EINVAL; goto err; @@ -1896,6 +2007,9 @@ int32_t ipu_unlink_channels(struct ipu_soc *ipu, ipu_channel_t src_ch, ipu_chann case MEM_FG_ASYNC0: fs_disp_flow1 &= ~FS_DP_ASYNC1_SRC_SEL_MASK; break; + case MEM_VDI_MEM: + fs_proc_flow1 &= ~FS_VDI_SRC_SEL_MASK; + break; default: retval = -EINVAL; goto err; @@ -2040,7 +2154,8 @@ int32_t ipu_enable_channel(struct ipu_soc *ipu, ipu_channel_t channel) } if (_ipu_is_ic_chan(in_dma) || _ipu_is_ic_chan(out_dma) || - _ipu_is_irt_chan(in_dma) || _ipu_is_irt_chan(out_dma)) + _ipu_is_irt_chan(in_dma) || _ipu_is_irt_chan(out_dma) || + _ipu_is_vdi_out_chan(out_dma)) _ipu_ic_enable_task(ipu, channel); ipu->channel_enable_mask |= 1L << IPU_CHAN_ID(channel); @@ -2152,7 +2267,7 @@ int32_t ipu_disable_channel(struct ipu_soc *ipu, ipu_channel_t channel, bool wai _ipu_lock(ipu); if ((ipu->channel_enable_mask & (1L << IPU_CHAN_ID(channel))) == 0) { - dev_err(ipu->dev, "Channel already disabled %d\n", + dev_dbg(ipu->dev, "Channel already disabled %d\n", IPU_CHAN_ID(channel)); _ipu_unlock(ipu); return -EACCES; @@ -2262,7 +2377,8 @@ int32_t ipu_disable_channel(struct ipu_soc *ipu, ipu_channel_t channel, bool wai /* Disable IC task */ if (_ipu_is_ic_chan(in_dma) || _ipu_is_ic_chan(out_dma) || - _ipu_is_irt_chan(in_dma) || _ipu_is_irt_chan(out_dma)) + _ipu_is_irt_chan(in_dma) || _ipu_is_irt_chan(out_dma) || + _ipu_is_vdi_out_chan(out_dma)) _ipu_ic_disable_task(ipu, channel); /* Disable DMA channel(s) */ @@ -2701,6 +2817,31 @@ uint32_t _ipu_channel_status(struct ipu_soc *ipu, ipu_channel_t channel) return stat; } +/*! + * This function check for a logical channel status + * + * @param ipu ipu handler + * @param channel Input parameter for the logical channel ID. + * + * @return This function returns 0 on idle and 1 on busy. + * + */ +uint32_t ipu_channel_status(struct ipu_soc *ipu, ipu_channel_t channel) +{ + uint32_t dma_status; + + _ipu_lock(ipu); + _ipu_get(ipu); + dma_status = ipu_is_channel_busy(ipu, channel); + _ipu_put(ipu); + _ipu_unlock(ipu); + + dev_dbg(ipu->dev, "%s, dma_status:%d.\n", __func__, dma_status); + + return dma_status; +} +EXPORT_SYMBOL(ipu_channel_status); + int32_t ipu_swap_channel(struct ipu_soc *ipu, ipu_channel_t from_ch, ipu_channel_t to_ch) { uint32_t reg; diff --git a/drivers/mxc/ipu3/ipu_device.c b/drivers/mxc/ipu3/ipu_device.c index 15d7d1119c79..c7d37df019ed 100644 --- a/drivers/mxc/ipu3/ipu_device.c +++ b/drivers/mxc/ipu3/ipu_device.c @@ -34,6 +34,7 @@ #include <linux/io.h> #include <linux/kthread.h> #include <linux/vmalloc.h> +#include <linux/cpumask.h> #include <mach/ipu-v3.h> #include <asm/outercache.h> #include <asm/cacheflush.h> @@ -41,33 +42,140 @@ #include "ipu_prv.h" #include "ipu_regs.h" #include "ipu_param_mem.h" +#include "vdoa.h" + +#define CHECK_RETCODE(cont, str, err, label, ret) \ +do { \ + if (cont) { \ + dev_err(t->dev, "ERR:[0x%p]-no:0x%x "#str" ret:%d," \ + "line:%d\n", t, t->task_no, ret, __LINE__);\ + if (ret != -EACCES) { \ + t->state = err; \ + goto label; \ + } \ + } \ +} while (0) + +#define CHECK_RETCODE_CONT(cont, str, err, ret) \ +do { \ + if (cont) { \ + dev_err(t->dev, "ERR:[0x%p]-no:0x%x"#str" ret:%d," \ + "line:%d\n", t, t->task_no, ret, __LINE__);\ + if (ret != -EACCES) { \ + if (t->state == STATE_OK) \ + t->state = err; \ + } \ + } \ +} while (0) + +#undef DBG_IPU_PERF +#ifdef DBG_IPU_PERF +#define CHECK_PERF(ts) \ +do { \ + getnstimeofday(ts); \ +} while (0) + +#define DECLARE_PERF_VAR \ + struct timespec ts_queue; \ + struct timespec ts_dotask; \ + struct timespec ts_waitirq; \ + struct timespec ts_sche; \ + struct timespec ts_rel; \ + struct timespec ts_frame + +#define PRINT_TASK_STATISTICS \ +do { \ + ts_queue = timespec_sub(tsk->ts_dotask, tsk->ts_queue); \ + ts_dotask = timespec_sub(tsk->ts_waitirq, tsk->ts_dotask); \ + ts_waitirq = timespec_sub(tsk->ts_inirq, tsk->ts_waitirq); \ + ts_sche = timespec_sub(tsk->ts_wakeup, tsk->ts_inirq); \ + ts_rel = timespec_sub(tsk->ts_rel, tsk->ts_wakeup); \ + ts_frame = timespec_sub(tsk->ts_rel, tsk->ts_queue); \ + dev_dbg(tsk->dev, "[0x%p] no-0x%x, ts_q:%ldus, ts_do:%ldus," \ + "ts_waitirq:%ldus,ts_sche:%ldus, ts_rel:%ldus," \ + "ts_frame: %ldus\n", tsk, tsk->task_no, \ + ts_queue.tv_nsec / NSEC_PER_USEC + ts_queue.tv_sec * USEC_PER_SEC,\ + ts_dotask.tv_nsec / NSEC_PER_USEC + ts_dotask.tv_sec * USEC_PER_SEC,\ + ts_waitirq.tv_nsec / NSEC_PER_USEC + ts_waitirq.tv_sec * USEC_PER_SEC,\ + ts_sche.tv_nsec / NSEC_PER_USEC + ts_sche.tv_sec * USEC_PER_SEC,\ + ts_rel.tv_nsec / NSEC_PER_USEC + ts_rel.tv_sec * USEC_PER_SEC,\ + ts_frame.tv_nsec / NSEC_PER_USEC + ts_frame.tv_sec * USEC_PER_SEC); \ + if ((ts_frame.tv_nsec/NSEC_PER_USEC + ts_frame.tv_sec*USEC_PER_SEC) > \ + 80000) \ + dev_dbg(tsk->dev, "ts_frame larger than 80ms [0x%p] no-0x%x.\n"\ + , tsk, tsk->task_no); \ +} while (0) +#else +#define CHECK_PERF(ts) +#define DECLARE_PERF_VAR +#define PRINT_TASK_STATISTICS +#endif -#undef TIME_CALC +#define IPU_PP_CH_VF (IPU_TASK_ID_VF - 1) +#define IPU_PP_CH_PP (IPU_TASK_ID_PP - 1) +#define MAX_PP_CH (IPU_TASK_ID_MAX - 1) +#define VDOA_DEF_TIMEOUT_MS (HZ/2) /* Strucutures and variables for exporting MXC IPU as device*/ typedef enum { STATE_OK = 0, + STATE_QUEUE, + STATE_IN_PROGRESS, + STATE_ERR, + STATE_TIMEOUT, + STATE_RES_TIMEOUT, STATE_NO_IPU, STATE_NO_IRQ, + STATE_IPU_BUSY, STATE_IRQ_FAIL, STATE_IRQ_TIMEOUT, + STATE_ENABLE_CHAN_FAIL, + STATE_DISABLE_CHAN_FAIL, + STATE_SEL_BUF_FAIL, STATE_INIT_CHAN_FAIL, STATE_LINK_CHAN_FAIL, + STATE_UNLINK_CHAN_FAIL, STATE_INIT_CHAN_BUF_FAIL, + STATE_INIT_CHAN_BAND_FAIL, + STATE_SYS_NO_MEM, + STATE_VDOA_IRQ_TIMEOUT, + STATE_VDOA_IRQ_FAIL, + STATE_VDOA_TASK_FAIL, } ipu_state_t; +enum { + INPUT_CHAN_VDI_P = 1, + INPUT_CHAN, + INPUT_CHAN_VDI_N, +}; + struct ipu_state_msg { int state; char *msg; } state_msg[] = { {STATE_OK, "ok"}, + {STATE_QUEUE, "split queue"}, + {STATE_IN_PROGRESS, "split in progress"}, + {STATE_ERR, "error"}, + {STATE_TIMEOUT, "split task timeout"}, + {STATE_RES_TIMEOUT, "wait resource timeout"}, {STATE_NO_IPU, "no ipu found"}, {STATE_NO_IRQ, "no irq found for task"}, + {STATE_IPU_BUSY, "ipu busy"}, {STATE_IRQ_FAIL, "request irq failed"}, {STATE_IRQ_TIMEOUT, "wait for irq timeout"}, + {STATE_ENABLE_CHAN_FAIL, "ipu enable channel fail"}, + {STATE_DISABLE_CHAN_FAIL, "ipu disable channel fail"}, + {STATE_SEL_BUF_FAIL, "ipu select buf fail"}, {STATE_INIT_CHAN_FAIL, "ipu init channel fail"}, {STATE_LINK_CHAN_FAIL, "ipu link channel fail"}, + {STATE_UNLINK_CHAN_FAIL, "ipu unlink channel fail"}, {STATE_INIT_CHAN_BUF_FAIL, "ipu init channel buffer fail"}, + {STATE_INIT_CHAN_BAND_FAIL, "ipu init channel band mode fail"}, + {STATE_SYS_NO_MEM, "sys no mem: -ENOMEM"}, + {STATE_VDOA_IRQ_TIMEOUT, "wait for vdoa irq timeout"}, + {STATE_VDOA_IRQ_FAIL, "vdoa irq fail"}, + {STATE_VDOA_TASK_FAIL, "vdoa task fail"}, }; struct stripe_setting { @@ -94,14 +202,32 @@ struct task_set { #define IC_MODE 0x1 #define ROT_MODE 0x2 #define VDI_MODE 0x4 +#define IPU_PREPROCESS_MODE_MASK (IC_MODE | ROT_MODE | VDI_MODE) +/* VDOA_MODE means this task use vdoa, and VDOA has two modes: + * BAND MODE and non-BAND MODE. Non-band mode will do transfer data + * to memory. BAND mode needs hareware sync with IPU, it is used default + * if connected to VDIC. + */ +#define VDOA_MODE 0x8 +#define VDOA_BAND_MODE 0x10 u8 mode; #define IC_VF 0x1 #define IC_PP 0x2 #define ROT_VF 0x4 #define ROT_PP 0x8 #define VDI_VF 0x10 +#define VDOA_ONLY 0x20 u8 task; - +#define NO_SPLIT 0x0 +#define RL_SPLIT 0x1 +#define UD_SPLIT 0x2 +#define LEFT_STRIPE 0x1 +#define RIGHT_STRIPE 0x2 +#define UP_STRIPE 0x4 +#define DOWN_STRIPE 0x8 +#define SPLIT_MASK 0xF + u8 split_mode; + u8 band_lines; ipu_channel_t ic_chan; ipu_channel_t rot_chan; ipu_channel_t vdi_ic_p_chan; @@ -131,25 +257,13 @@ struct task_set { u32 r_stride; dma_addr_t r_paddr; -#define NO_SPLIT 0x0 -#define RL_SPLIT 0x1 -#define UD_SPLIT 0x2 -#define LEFT_STRIPE 0x1 -#define RIGHT_STRIPE 0x2 -#define UP_STRIPE 0x4 -#define DOWN_STRIPE 0x8 - u8 split_mode; struct stripe_setting sp_setting; }; struct ipu_split_task { struct ipu_task task; struct ipu_task_entry *parent_task; - struct task_struct *thread; - volatile bool could_finish; - wait_queue_head_t waitq; - int ret; - + struct ipu_task_entry *child_task; u32 task_no; }; @@ -159,36 +273,95 @@ struct ipu_task_entry { bool overlay_en; struct ipu_overlay overlay; - - u8 priority; - u8 task_id; #define DEF_TIMEOUT_MS 1000 +#define DEF_DELAY_MS 20 int timeout; + int irq; + + u8 task_id; + u8 ipu_id; + u8 task_in_list; + u8 split_done; + struct mutex split_lock; + wait_queue_head_t split_waitq; struct list_head node; + struct list_head split_list; + struct ipu_soc *ipu; struct device *dev; struct task_set set; - struct completion comp; + wait_queue_head_t task_waitq; + struct completion irq_comp; + struct kref refcount; ipu_state_t state; - u32 task_no; + atomic_t done; + atomic_t res_free; + atomic_t res_get; + + struct ipu_task_entry *parent; + char *vditmpbuf[2]; + u32 old_save_lines; + u32 old_size; + bool buf1filled; + bool buf0filled; + + vdoa_handle_t vdoa_handle; + struct vdoa_output_mem { + void *vaddr; + dma_addr_t paddr; + int size; + } vdoa_dma; + +#ifdef DBG_IPU_PERF + struct timespec ts_queue; + struct timespec ts_dotask; + struct timespec ts_waitirq; + struct timespec ts_inirq; + struct timespec ts_wakeup; + struct timespec ts_rel; +#endif +}; + +struct ipu_channel_tabel { + struct mutex lock; + u8 used[MXC_IPU_MAX_NUM][MAX_PP_CH]; + u8 vdoa_used; }; + +struct ipu_thread_data { + struct ipu_soc *ipu; + u32 id; + u32 is_vdoa; +}; + struct ipu_alloc_list { struct list_head list; dma_addr_t phy_addr; void *cpu_addr; u32 size; }; -LIST_HEAD(ipu_alloc_list); +static LIST_HEAD(ipu_alloc_list); +static DEFINE_MUTEX(ipu_alloc_lock); +static struct ipu_channel_tabel ipu_ch_tbl; +static LIST_HEAD(ipu_task_list); +static DEFINE_SPINLOCK(ipu_task_list_lock); +static DECLARE_WAIT_QUEUE_HEAD(thread_waitq); +static DECLARE_WAIT_QUEUE_HEAD(res_waitq); +static atomic_t req_cnt; static int major; -static u32 frame_no; +static int thread_id; +static atomic_t frame_no; static struct class *ipu_class; static struct device *ipu_dev; -static char *vditmpbuf[2]; -static bool buf1filled, buf0filled; -static u32 old_save_lines, old_size; -int ipu_queue_sp_task(struct ipu_split_task *sp_task); +static int debug; +module_param(debug, int, 0600); +#ifdef DBG_IPU_PERF +static struct timespec ts_frame_max; +static u32 ts_frame_avg; +static atomic_t frame_cnt; +#endif static bool deinterlace_3_field(struct ipu_task_entry *t) { @@ -196,6 +369,42 @@ static bool deinterlace_3_field(struct ipu_task_entry *t) (t->input.deinterlace.motion != HIGH_MOTION)); } +static u32 tiled_filed_size(struct ipu_task_entry *t) +{ + u32 y_size; + u32 field_size; + + /* note: page_align is required by VPU hw ouput buffer */ + y_size = t->input.width * t->input.height/2; + field_size = ALIGN(y_size, SZ_4K) + ALIGN(y_size/2, SZ_4K); + + return field_size; +} + +static bool only_ic(u8 mode) +{ + mode = mode & IPU_PREPROCESS_MODE_MASK; + return ((mode == IC_MODE) || (mode == VDI_MODE)); +} + +static bool only_rot(u8 mode) +{ + mode = mode & IPU_PREPROCESS_MODE_MASK; + return (mode == ROT_MODE); +} + +static bool ic_and_rot(u8 mode) +{ + mode = mode & IPU_PREPROCESS_MODE_MASK; + return ((mode == (IC_MODE | ROT_MODE)) || + (mode == (VDI_MODE | ROT_MODE))); +} + +static bool need_split(struct ipu_task_entry *t) +{ + return ((t->set.split_mode != NO_SPLIT) || (t->task_no & SPLIT_MASK)); +} + unsigned int fmt_to_bpp(unsigned int pixelformat) { u32 bpp; @@ -259,6 +468,8 @@ cs_t colorspaceofpixel(int fmt) case IPU_PIX_FMT_YUV422P: case IPU_PIX_FMT_YUV444: case IPU_PIX_FMT_NV12: + case IPU_PIX_FMT_TILED_NV12: + case IPU_PIX_FMT_TILED_NV12F: return YUV_CS; break; default: @@ -283,9 +494,9 @@ int need_csc(int ifmt, int ofmt) } EXPORT_SYMBOL_GPL(need_csc); -static int soc_max_in_width(void) +static int soc_max_in_width(u32 is_vdoa) { - return 4096; + return is_vdoa ? 8192 : 4096; } static int soc_max_in_height(void) @@ -305,97 +516,10 @@ static int soc_max_out_height(void) return 1024; } -static int list_size(struct list_head *head) -{ - struct list_head *p, *n; - int size = 0; - - list_for_each_safe(p, n, head) - size++; - - return size; -} - -static int get_task_size(struct ipu_soc *ipu, int id) -{ - struct list_head *task_list; - - if (id == IPU_TASK_ID_VF) - task_list = &ipu->task_list[0]; - else if (id == IPU_TASK_ID_PP) - task_list = &ipu->task_list[1]; - else { - printk(KERN_ERR "query error task id\n"); - return -EINVAL; - } - - return list_size(task_list); -} - -static struct ipu_soc *most_free_ipu_task(struct ipu_task_entry *t) -{ - unsigned int task_num[2][2] = { - {0xffffffff, 0xffffffff}, - {0xffffffff, 0xffffffff} }; - struct ipu_soc *ipu; - int ipu_idx, task_id; - int i; - - /* decide task_id */ - if (t->task_id >= IPU_TASK_ID_MAX) - t->task_id %= IPU_TASK_ID_MAX; - /* must use task_id VF for VDI task*/ - if ((t->set.mode & VDI_MODE) && - (t->task_id != IPU_TASK_ID_VF)) - t->task_id = IPU_TASK_ID_VF; - - for (i = 0; i < MXC_IPU_MAX_NUM; i++) { - ipu = ipu_get_soc(i); - if (!IS_ERR(ipu)) { - task_num[i][0] = get_task_size(ipu, IPU_TASK_ID_VF); - task_num[i][1] = get_task_size(ipu, IPU_TASK_ID_PP); - } - } - - task_id = t->task_id; - if (t->task_id == IPU_TASK_ID_VF) { - if (task_num[0][0] < task_num[1][0]) - ipu_idx = 0; - else - ipu_idx = 1; - } else if (t->task_id == IPU_TASK_ID_PP) { - if (task_num[0][1] < task_num[1][1]) - ipu_idx = 0; - else - ipu_idx = 1; - } else { - unsigned int min; - ipu_idx = 0; - task_id = IPU_TASK_ID_VF; - min = task_num[0][0]; - if (task_num[0][1] < min) { - min = task_num[0][1]; - task_id = IPU_TASK_ID_PP; - } - if (task_num[1][0] < min) { - min = task_num[1][0]; - ipu_idx = 1; - task_id = IPU_TASK_ID_VF; - } - if (task_num[1][1] < min) { - ipu_idx = 1; - task_id = IPU_TASK_ID_PP; - } - } - - t->task_id = task_id; - ipu = ipu_get_soc(ipu_idx); - - return ipu; -} - static void dump_task_info(struct ipu_task_entry *t) { + if (!debug) + return; dev_dbg(t->dev, "[0x%p]input:\n", (void *)t); dev_dbg(t->dev, "[0x%p]\tformat = 0x%x\n", (void *)t, t->input.format); dev_dbg(t->dev, "[0x%p]\twidth = %d\n", (void *)t, t->input.width); @@ -548,20 +672,46 @@ static void dump_check_warn(struct device *dev, int warn) dev_warn(dev, "overlay u/v offset not 8 align\n"); } -static int set_crop(struct ipu_crop *crop, int width, int height) +static int set_crop(struct ipu_crop *crop, int width, int height, int fmt) { - if (crop->w || crop->h) { - if (((crop->w + crop->pos.x) > width) - || ((crop->h + crop->pos.y) > height)) - return -EINVAL; + if ((IPU_PIX_FMT_TILED_NV12 == fmt) || + (IPU_PIX_FMT_TILED_NV12F == fmt)) { + if (crop->w || crop->h) { + if (((crop->w + crop->pos.x) > width) + || ((crop->h + crop->pos.y) > height) + || (0 != (crop->w % IPU_PIX_FMT_TILED_NV12_MBALIGN)) + || (0 != (crop->h % IPU_PIX_FMT_TILED_NV12_MBALIGN)) + || (0 != (crop->pos.x % IPU_PIX_FMT_TILED_NV12_MBALIGN)) + || (0 != (crop->pos.y % IPU_PIX_FMT_TILED_NV12_MBALIGN)) + ) { + pr_err("set_crop error MB align.\n"); + return -EINVAL; + } + } else { + crop->pos.x = 0; + crop->pos.y = 0; + crop->w = width; + crop->h = height; + if ((0 != (crop->w % IPU_PIX_FMT_TILED_NV12_MBALIGN)) + || (0 != (crop->h % IPU_PIX_FMT_TILED_NV12_MBALIGN))) { + pr_err("set_crop error w/h MB align.\n"); + return -EINVAL; + } + } } else { - crop->pos.x = 0; - crop->pos.y = 0; - crop->w = width; - crop->h = height; + if (crop->w || crop->h) { + if (((crop->w + crop->pos.x) > width) + || ((crop->h + crop->pos.y) > height)) + return -EINVAL; + } else { + crop->pos.x = 0; + crop->pos.y = 0; + crop->w = width; + crop->h = height; + } + crop->w -= crop->w%8; + crop->h -= crop->h%8; } - crop->w -= crop->w%8; - crop->h -= crop->h%8; return 0; } @@ -603,6 +753,26 @@ static void update_offset(unsigned int fmt, *uoff = (width * (height - pos_y) - pos_x) + width * pos_y/2 + pos_x; break; + case IPU_PIX_FMT_TILED_NV12: + /* + * tiled format, progressive: + * assuming that line is aligned with MB height (aligned to 16) + * offset = line * stride + (pixel / MB_width) * pixels_in_MB + * = line * stride + (pixel / 16) * 256 + * = line * stride + pixel * 16 + */ + *off = pos_y * width + (pos_x << 4); + *uoff = ALIGN(width * height, SZ_4K) + (*off >> 1); + break; + case IPU_PIX_FMT_TILED_NV12F: + /* + * tiled format, interlaced: + * same as above, only number of pixels in MB is 128, + * instead of 256 + */ + *off = (pos_y >> 1) * width + (pos_x << 3); + *uoff = ALIGN(width * height/2, SZ_4K) + (*off >> 1); + break; default: *off = (pos_y * width + pos_x) * fmt_to_bpp(fmt)/8; break; @@ -698,9 +868,21 @@ static int check_task(struct ipu_task_entry *t) { int tmp; int ret = IPU_CHECK_OK; + int timeout; + + if ((IPU_PIX_FMT_TILED_NV12 == t->overlay.format) || + (IPU_PIX_FMT_TILED_NV12F == t->overlay.format) || + (IPU_PIX_FMT_TILED_NV12 == t->output.format) || + (IPU_PIX_FMT_TILED_NV12F == t->output.format) || + ((IPU_PIX_FMT_TILED_NV12F == t->input.format) && + !t->input.deinterlace.enable)) { + ret = IPU_CHECK_ERR_NOT_SUPPORT; + goto done; + } /* check input */ - ret = set_crop(&t->input.crop, t->input.width, t->input.height); + ret = set_crop(&t->input.crop, t->input.width, t->input.height, + t->input.format); if (ret < 0) { ret = IPU_CHECK_ERR_INPUT_CROP; goto done; @@ -711,7 +893,8 @@ static int check_task(struct ipu_task_entry *t) &t->set.i_voff, &t->set.istride); /* check output */ - ret = set_crop(&t->output.crop, t->output.width, t->output.height); + ret = set_crop(&t->output.crop, t->output.width, t->output.height, + t->output.format); if (ret < 0) { ret = IPU_CHECK_ERR_OUTPUT_CROP; goto done; @@ -722,13 +905,32 @@ static int check_task(struct ipu_task_entry *t) &t->set.o_off, &t->set.o_uoff, &t->set.o_voff, &t->set.ostride); + if ((IPU_PIX_FMT_TILED_NV12 == t->input.format) || + (IPU_PIX_FMT_TILED_NV12F == t->input.format)) { + if ((t->input.crop.w > soc_max_in_width(1)) || + (t->input.crop.h > soc_max_in_height())) { + ret = IPU_CHECK_ERR_INPUT_OVER_LIMIT; + goto done; + } + /* output fmt: NV12 and YUYV, now don't support resize */ + if (((IPU_PIX_FMT_NV12 != t->output.format) && + (IPU_PIX_FMT_YUYV != t->output.format)) || + (t->input.crop.w != t->output.crop.w) || + (t->input.crop.h != t->output.crop.h)) { + ret = IPU_CHECK_ERR_NOT_SUPPORT; + goto done; + } + } + /* check overlay if there is */ if (t->overlay_en) { if (t->input.deinterlace.enable) { ret = IPU_CHECK_ERR_OVERLAY_WITH_VDI; goto done; } - ret = set_crop(&t->overlay.crop, t->overlay.width, t->overlay.height); + + ret = set_crop(&t->overlay.crop, t->overlay.width, + t->overlay.height, t->overlay.format); if (ret < 0) { ret = IPU_CHECK_ERR_OVERLAY_CROP; goto done; @@ -759,10 +961,13 @@ static int check_task(struct ipu_task_entry *t) } /* input overflow? */ - if ((t->input.crop.w > soc_max_in_width()) || - (t->input.crop.h > soc_max_in_height())) { - ret = IPU_CHECK_ERR_INPUT_OVER_LIMIT; - goto done; + if (!((IPU_PIX_FMT_TILED_NV12 == t->input.format) || + (IPU_PIX_FMT_TILED_NV12F == t->input.format))) { + if ((t->input.crop.w > soc_max_in_width(0)) || + (t->input.crop.h > soc_max_in_height())) { + ret = IPU_CHECK_ERR_INPUT_OVER_LIMIT; + goto done; + } } /* check task mode */ @@ -802,13 +1007,33 @@ static int check_task(struct ipu_task_entry *t) t->set.mode &= ~IC_MODE; t->set.mode |= VDI_MODE; } + if ((IPU_PIX_FMT_TILED_NV12 == t->input.format) || + (IPU_PIX_FMT_TILED_NV12F == t->input.format)) { + if (t->set.mode & ROT_MODE) { + ret = IPU_CHECK_ERR_NOT_SUPPORT; + goto done; + } + t->set.mode |= VDOA_MODE; + if (IPU_PIX_FMT_TILED_NV12F == t->input.format) + t->set.mode |= VDOA_BAND_MODE; + t->set.mode &= ~IC_MODE; + } - if (t->set.mode & (IC_MODE | VDI_MODE)) { + if ((t->set.mode & (IC_MODE | VDI_MODE)) && + (IPU_PIX_FMT_TILED_NV12F != t->input.format)) { if (t->output.crop.w > soc_max_out_width()) t->set.split_mode |= RL_SPLIT; if (t->output.crop.h > soc_max_out_height()) t->set.split_mode |= UD_SPLIT; if (t->set.split_mode) { + if ((t->set.split_mode == RL_SPLIT) || + (t->set.split_mode == UD_SPLIT)) + timeout = DEF_TIMEOUT_MS * 2 + DEF_DELAY_MS; + else + timeout = DEF_TIMEOUT_MS * 4 + DEF_DELAY_MS; + if (t->timeout < timeout) + t->timeout = timeout; + ret = update_split_setting(t); if (ret > IPU_CHECK_ERR_MIN) goto done; @@ -836,10 +1061,12 @@ static int check_task(struct ipu_task_entry *t) done: /* dump msg */ - if (ret > IPU_CHECK_ERR_MIN) - dump_check_err(t->dev, ret); - else if (ret != IPU_CHECK_OK) - dump_check_warn(t->dev, ret); + if (debug) { + if (ret > IPU_CHECK_ERR_MIN) + dump_check_err(t->dev, ret); + else if (ret != IPU_CHECK_OK) + dump_check_warn(t->dev, ret); + } return ret; } @@ -852,101 +1079,305 @@ static int prepare_task(struct ipu_task_entry *t) if (ret > IPU_CHECK_ERR_MIN) return -EINVAL; + if (t->set.mode & VDI_MODE) { + if (t->task_id != IPU_TASK_ID_VF) + t->task_id = IPU_TASK_ID_VF; + t->set.task = VDI_VF; + if (t->set.mode & ROT_MODE) + t->set.task |= ROT_VF; + } + + if (VDOA_MODE == t->set.mode) { + if (t->set.task != 0) { + dev_err(t->dev, "ERR: vdoa only task:0x%x, [0x%p].\n", + t->set.task, t); + BUG(); + } + t->set.task |= VDOA_ONLY; + } + + if (VDOA_BAND_MODE & t->set.mode) { + /* to save band size: 1<<3 = 8 lines */ + t->set.band_lines = 3; + } + dump_task_info(t); return ret; } -/* should call from a process context */ -static int queue_task(struct ipu_task_entry *t) +static uint32_t ic_vf_pp_is_busy(struct ipu_soc *ipu, bool is_vf) { - int ret = 0; - struct ipu_soc *ipu; - struct list_head *task_list = NULL; - struct mutex *task_lock = NULL; - wait_queue_head_t *waitq = NULL; - - ipu = most_free_ipu_task(t); - t->dev = ipu->dev; - - dev_dbg(t->dev, "[0x%p]Queue task: id %d\n", (void *)t, t->task_id); - - init_completion(&t->comp); - - t->set.task = 0; - switch (t->task_id) { - case IPU_TASK_ID_VF: - task_list = &ipu->task_list[0]; - task_lock = &ipu->task_lock[0]; - waitq = &ipu->waitq[0]; - if (t->set.mode & IC_MODE) - t->set.task |= IC_VF; - else if (t->set.mode & VDI_MODE) - t->set.task |= VDI_VF; - if (t->set.mode & ROT_MODE) - t->set.task |= ROT_VF; - break; - case IPU_TASK_ID_PP: - task_list = &ipu->task_list[1]; - task_lock = &ipu->task_lock[1]; - waitq = &ipu->waitq[1]; - if (t->set.mode & IC_MODE) - t->set.task |= IC_PP; - if (t->set.mode & ROT_MODE) - t->set.task |= ROT_PP; - break; - default: - dev_err(t->dev, "[0x%p]should never come here\n", (void *)t); + uint32_t status; + uint32_t status_vf; + uint32_t status_rot; + + if (is_vf) { + status = ipu_channel_status(ipu, MEM_VDI_PRP_VF_MEM); + status_vf = ipu_channel_status(ipu, MEM_PRP_VF_MEM); + status_rot = ipu_channel_status(ipu, MEM_ROT_VF_MEM); + return status || status_vf || status_rot; + } else { + status = ipu_channel_status(ipu, MEM_PP_MEM); + status_rot = ipu_channel_status(ipu, MEM_ROT_PP_MEM); + return status || status_rot; } +} - dev_dbg(t->dev, "[0x%p]choose task_id[%d] mode[0x%x]\n", - (void *)t, t->task_id, t->set.task); - dev_dbg(t->dev, "[0x%p]\tIPU_TASK_ID_VF = %d\n", - (void *)t, IPU_TASK_ID_VF); - dev_dbg(t->dev, "[0x%p]\tIPU_TASK_ID_PP = %d\n", - (void *)t, IPU_TASK_ID_PP); - dev_dbg(t->dev, "[0x%p]\tIC_VF = 0x%x\n", (void *)t, IC_VF); - dev_dbg(t->dev, "[0x%p]\tIC_PP = 0x%x\n", (void *)t, IC_PP); - dev_dbg(t->dev, "[0x%p]\tROT_VF = 0x%x\n", (void *)t, ROT_VF); - dev_dbg(t->dev, "[0x%p]\tROT_PP = 0x%x\n", (void *)t, ROT_PP); - dev_dbg(t->dev, "[0x%p]\tVDI_VF = 0x%x\n", (void *)t, VDI_VF); +static int _get_vdoa_ipu_res(struct ipu_task_entry *t) +{ + int i; + struct ipu_soc *ipu; + u8 *used; + uint32_t found_ipu = 0; + uint32_t found_vdoa = 0; + struct ipu_channel_tabel *tbl = &ipu_ch_tbl; + + mutex_lock(&tbl->lock); + if (t->set.mode & VDOA_MODE) { + if (NULL != t->vdoa_handle) + found_vdoa = 1; + else { + found_vdoa = tbl->vdoa_used ? 0 : 1; + if (found_vdoa) { + tbl->vdoa_used = 1; + vdoa_get_handle(&t->vdoa_handle); + } else + /* first get vdoa->ipu resource sequence */ + goto out; + if (t->set.task & VDOA_ONLY) + goto out; + } + } - /* add and wait task */ - mutex_lock(task_lock); - list_add_tail(&t->node, task_list); - mutex_unlock(task_lock); + for (i = 0; i < MXC_IPU_MAX_NUM; i++) { + ipu = ipu_get_soc(i); + if (IS_ERR(ipu)) + BUG(); + + used = &tbl->used[i][IPU_PP_CH_VF]; + if (t->set.mode & VDI_MODE) { + if (0 == *used) { + *used = 1; + found_ipu = 1; + break; + } + } else if ((t->set.mode & IC_MODE) || only_rot(t->set.mode)) { + if (0 == *used) { + t->task_id = IPU_TASK_ID_VF; + if (t->set.mode & IC_MODE) + t->set.task |= IC_VF; + if (t->set.mode & ROT_MODE) + t->set.task |= ROT_VF; + *used = 1; + found_ipu = 1; + break; + } + } else + BUG(); + } + if (found_ipu) + goto next; - wake_up_interruptible(waitq); + for (i = 0; i < MXC_IPU_MAX_NUM; i++) { + ipu = ipu_get_soc(i); + if (IS_ERR(ipu)) + BUG(); + + if ((t->set.mode & IC_MODE) || only_rot(t->set.mode)) { + used = &tbl->used[i][IPU_PP_CH_PP]; + if (0 == *used) { + t->task_id = IPU_TASK_ID_PP; + if (t->set.mode & IC_MODE) + t->set.task |= IC_PP; + if (t->set.mode & ROT_MODE) + t->set.task |= ROT_PP; + *used = 1; + found_ipu = 1; + break; + } + } + } - wait_for_completion(&t->comp); +next: + if (found_ipu) { + t->ipu = ipu; + t->ipu_id = i; + t->dev = ipu->dev; + if (atomic_inc_return(&t->res_get) == 2) + BUG(); + } +out: + dev_dbg(t->dev, + "%s:no:0x%x,found_vdoa:%d, found_ipu:%d\n", + __func__, t->task_no, found_vdoa, found_ipu); + mutex_unlock(&tbl->lock); + if (t->set.task & VDOA_ONLY) + return found_vdoa; + else if (t->set.mode & VDOA_MODE) + return found_vdoa && found_ipu; + else + return found_ipu; +} - dev_dbg(t->dev, "[0x%p]Queue task finished\n", (void *)t); +static void put_vdoa_ipu_res(struct ipu_task_entry *tsk, int vdoa_only) +{ + int ret; + int rel_vdoa = 0, rel_ipu = 0; + struct ipu_channel_tabel *tbl = &ipu_ch_tbl; - if (t->state != STATE_OK) { - dev_err(t->dev, "[0x%p]state %d: %s\n", - (void *)t, t->state, state_msg[t->state].msg); - ret = -ECANCELED; + if (!tsk) + BUG(); + mutex_lock(&tbl->lock); + if (tsk->set.mode & VDOA_MODE) { + if (!tbl->vdoa_used && tsk->vdoa_handle) + BUG(); + if (tbl->vdoa_used && tsk->vdoa_handle) { + tbl->vdoa_used = 0; + vdoa_put_handle(&tsk->vdoa_handle); + if (tsk->ipu) + tsk->ipu->vdoa_en = 0; + rel_vdoa = 1; + if (vdoa_only || (tsk->set.task & VDOA_ONLY)) + goto out; + } } - return ret; + if (tsk) { + tbl->used[tsk->ipu_id][tsk->task_id - 1] = 0; + rel_ipu = 1; + ret = atomic_inc_return(&tsk->res_free); + if (ret == 2) + BUG(); + } +out: + dev_dbg(tsk->dev, + "%s:no:0x%x,rel_vdoa:%d, rel_ipu:%d\n", + __func__, tsk->task_no, rel_vdoa, rel_ipu); + mutex_unlock(&tbl->lock); } -static bool need_split(struct ipu_task_entry *t) +static int get_vdoa_ipu_res(struct ipu_task_entry *t) { - return (t->set.split_mode != NO_SPLIT); + int ret; + uint32_t found = 0; + + found = _get_vdoa_ipu_res(t); + if (!found) { + t->ipu_id = -1; + t->ipu = NULL; + /* blocking to get resource */ + ret = atomic_inc_return(&req_cnt); + dev_dbg(t->dev, + "wait_res:no:0x%x,req_cnt:%d\n", t->task_no, ret); + ret = wait_event_timeout(res_waitq, _get_vdoa_ipu_res(t), + msecs_to_jiffies(t->timeout - DEF_DELAY_MS)); + if (ret == 0) { + dev_err(t->dev, "ERR[0x%p,no-0x%x] wait_res timeout:%dms!\n", + t, t->task_no, t->timeout - DEF_DELAY_MS); + ret = -ETIMEDOUT; + t->state = STATE_RES_TIMEOUT; + goto out; + } else { + if (!(t->set.task & VDOA_ONLY) && (!t->ipu)) + BUG(); + ret = atomic_read(&req_cnt); + if (ret > 0) + ret = atomic_dec_return(&req_cnt); + else + BUG(); + dev_dbg(t->dev, "no-0x%x,[0x%p],req_cnt:%d, got_res!\n", + t->task_no, t, ret); + found = 1; + } + } + +out: + return found; +} + +static struct ipu_task_entry *create_task_entry(struct ipu_task *task) +{ + struct ipu_task_entry *tsk; + + tsk = kzalloc(sizeof(struct ipu_task_entry), GFP_KERNEL); + if (!tsk) + return ERR_PTR(-ENOMEM); + kref_init(&tsk->refcount); + tsk->state = -EINVAL; + tsk->ipu_id = -1; + tsk->dev = ipu_dev; + tsk->input = task->input; + tsk->output = task->output; + tsk->overlay_en = task->overlay_en; + if (tsk->overlay_en) + tsk->overlay = task->overlay; + if (tsk->timeout && (tsk->timeout > DEF_TIMEOUT_MS)) + tsk->timeout = task->timeout; + else + tsk->timeout = DEF_TIMEOUT_MS; + + return tsk; } -static int split_task_thread(void *data) +static void task_mem_free(struct kref *ref) { - struct ipu_split_task *t = data; + struct ipu_task_entry *tsk = + container_of(ref, struct ipu_task_entry, refcount); - t->ret = ipu_queue_sp_task(t); + memset(tsk, 0, sizeof(*tsk)); + kfree(tsk); +} - t->could_finish = true; +int create_split_child_task(struct ipu_split_task *sp_task) +{ + int ret = 0; + struct ipu_task_entry *tsk; + + tsk = create_task_entry(&sp_task->task); + if (IS_ERR(tsk)) + return PTR_ERR(tsk); + + sp_task->child_task = tsk; + tsk->task_no = sp_task->task_no; + + ret = prepare_task(tsk); + if (ret < 0) + goto err; + + tsk->parent = sp_task->parent_task; + tsk->set.sp_setting = sp_task->parent_task->set.sp_setting; + + list_add(&tsk->node, &tsk->parent->split_list); + dev_dbg(tsk->dev, "[0x%p] sp_tsk Q list,no-0x%x\n", tsk, tsk->task_no); + tsk->state = STATE_QUEUE; + CHECK_PERF(&tsk->ts_queue); +err: + return ret; +} - wake_up_interruptible(&t->waitq); +static inline int sp_task_check_done(struct ipu_split_task *sp_task, + struct ipu_task_entry *parent, int num, int *idx) +{ + int i; + int ret = 0; + struct ipu_task_entry *tsk; + struct mutex *lock = &parent->split_lock; + + *idx = -EINVAL; + mutex_lock(lock); + for (i = 0; i < num; i++) { + tsk = sp_task[i].child_task; + if (tsk && tsk->split_done) { + *idx = i; + ret = 1; + goto out; + } + } - do_exit(0); +out: + mutex_unlock(lock); + return ret; } static int create_split_task( @@ -955,6 +1386,7 @@ static int create_split_task( { struct ipu_task *task = &(sp_task->task); struct ipu_task_entry *t = sp_task->parent_task; + int ret; sp_task->task_no |= stripe; @@ -963,9 +1395,12 @@ static int create_split_task( task->overlay_en = t->overlay_en; if (task->overlay_en) task->overlay = t->overlay; - task->priority = t->priority; task->task_id = t->task_id; - task->timeout = t->timeout; + if ((t->set.split_mode == RL_SPLIT) || + (t->set.split_mode == UD_SPLIT)) + task->timeout = t->timeout / 2; + else + task->timeout = t->timeout / 4; task->input.crop.w = t->set.sp_setting.iw; task->input.crop.h = t->set.sp_setting.ih; @@ -1120,206 +1555,262 @@ static int create_split_task( - t->set.sp_setting.o_right_pos - t->set.sp_setting.ow; break; default: - dev_err(t->dev, "should not be here\n"); + dev_err(t->dev, "ERR:should not be here\n"); break; } - /*check split task deinterlace enable*/ - if (t->input.deinterlace.enable) { - sp_task->ret = ipu_queue_sp_task(sp_task); - } else { - sp_task->thread = kthread_run(split_task_thread, sp_task, - "ipu_split_task"); - if (IS_ERR(sp_task->thread)) { - dev_err(t->dev, "split thread can not create\n"); - return PTR_ERR(sp_task->thread); - } - } - - return 0; + ret = create_split_child_task(sp_task); + if (ret < 0) + dev_err(t->dev, "ERR:create_split_child_task() ret:%d\n", ret); + return ret; } -static int queue_split_task(struct ipu_task_entry *t) +static int queue_split_task(struct ipu_task_entry *t, + struct ipu_split_task *sp_task, uint32_t size) { - struct ipu_split_task sp_task[4]; - int i, ret = 0, size; - - dev_dbg(t->dev, "Split task 0x%p\n", (void *)t); - - if ((t->set.split_mode == RL_SPLIT) || (t->set.split_mode == UD_SPLIT)) - size = 2; - else - size = 4; + int err[4]; + int ret = 0; + int i, j; + struct ipu_task_entry *tsk = NULL; + struct mutex *lock = &t->split_lock; - for (i = 0; i < size; i++) { - memset(&sp_task[i], 0, sizeof(struct ipu_split_task)); - init_waitqueue_head(&(sp_task[i].waitq)); - sp_task[i].could_finish = false; - sp_task[i].parent_task = t; - sp_task[i].task_no = t->task_no; + dev_dbg(t->dev, "Split task 0x%p, no-0x%x, size:%d\n", + t, t->task_no, size); + mutex_init(lock); + init_waitqueue_head(&t->split_waitq); + INIT_LIST_HEAD(&t->split_list); + for (j = 0; j < size; j++) { + memset(&sp_task[j], 0, sizeof(*sp_task)); + sp_task[j].parent_task = t; + sp_task[j].task_no = t->task_no; } if (t->set.split_mode == RL_SPLIT) { - create_split_task(LEFT_STRIPE, &sp_task[0]); - create_split_task(RIGHT_STRIPE, &sp_task[1]); + i = 0; + err[i] = create_split_task(RIGHT_STRIPE, &sp_task[i]); + if (err[i] < 0) + goto err_start; + i = 1; + err[i] = create_split_task(LEFT_STRIPE, &sp_task[i]); } else if (t->set.split_mode == UD_SPLIT) { - create_split_task(UP_STRIPE, &sp_task[0]); - create_split_task(DOWN_STRIPE, &sp_task[1]); - } else { - create_split_task(LEFT_STRIPE | UP_STRIPE, &sp_task[0]); - create_split_task(LEFT_STRIPE | DOWN_STRIPE, &sp_task[1]); - create_split_task(RIGHT_STRIPE | UP_STRIPE, &sp_task[2]); - create_split_task(RIGHT_STRIPE | DOWN_STRIPE, &sp_task[3]); - } - - /*check split task deinterlace enable*/ - if (t->input.deinterlace.enable) { - return ret; + i = 0; + err[i] = create_split_task(DOWN_STRIPE, &sp_task[i]); + if (err[i] < 0) + goto err_start; + i = 1; + err[i] = create_split_task(UP_STRIPE, &sp_task[i]); } else { - for (i = 0; i < size; i++) { - wait_event_interruptible(sp_task[i].waitq, sp_task[i].could_finish); - if (sp_task[i].ret < 0) { - ret = sp_task[i].ret; + i = 0; + err[i] = create_split_task(RIGHT_STRIPE | DOWN_STRIPE, &sp_task[i]); + if (err[i] < 0) + goto err_start; + i = 1; + err[i] = create_split_task(LEFT_STRIPE | DOWN_STRIPE, &sp_task[i]); + if (err[i] < 0) + goto err_start; + i = 2; + err[i] = create_split_task(RIGHT_STRIPE | UP_STRIPE, &sp_task[i]); + if (err[i] < 0) + goto err_start; + i = 3; + err[i] = create_split_task(LEFT_STRIPE | UP_STRIPE, &sp_task[i]); + } + +err_start: + for (j = 0; j < (i + 1); j++) { + if (err[j] < 0) { + if (sp_task[j].child_task) dev_err(t->dev, - "split task %d fail with ret %d\n", - i, ret); - } + "sp_task[%d],no-0x%x fail state:%d, queue err:%d.\n", + j, sp_task[j].child_task->task_no, + sp_task[j].child_task->state, err[j]); + goto err_exit; } - return ret; + dev_dbg(t->dev, "[0x%p] sp_task[%d], no-0x%x state:%s, queue ret:%d.\n", + sp_task[j].child_task, j, sp_task[j].child_task->task_no, + state_msg[sp_task[j].child_task->state].msg, err[j]); } -} -static struct ipu_task_entry *create_task_entry(struct ipu_task *task) -{ - struct ipu_task_entry *tsk; - - tsk = kzalloc(sizeof(struct ipu_task_entry), GFP_KERNEL); - if (!tsk) - return ERR_PTR(-ENOMEM); + return ret; - tsk->dev = ipu_dev; - tsk->input = task->input; - tsk->output = task->output; - tsk->overlay_en = task->overlay_en; - if (tsk->overlay_en) - tsk->overlay = task->overlay; - tsk->priority = task->priority; - tsk->task_id = task->task_id; - if (task->timeout && (task->timeout > DEF_TIMEOUT_MS)) - tsk->timeout = task->timeout; - else - tsk->timeout = DEF_TIMEOUT_MS; +err_exit: + for (j = 0; j < (i + 1); j++) { + if (err[j] < 0 && !ret) + ret = err[j]; + tsk = sp_task[j].child_task; + if (!tsk) + continue; + kfree(tsk); + memset(tsk, 0, sizeof(*tsk)); + } + t->state = STATE_ERR; + return ret; - return tsk; } -int ipu_check_task(struct ipu_task *task) +static int init_tiled_buf(struct ipu_soc *ipu, struct ipu_task_entry *t, + ipu_channel_t channel, uint32_t ch_type) { - struct ipu_task_entry *tsk; int ret = 0; + int i; + uint32_t ipu_fmt; + dma_addr_t inbuf_base = 0; + u32 field_size; + struct vdoa_params param; + struct vdoa_ipu_buf buf; + struct ipu_soc *ipu_idx; + u32 ipu_stride, obuf_size; + u32 height, width; + ipu_buffer_t type; + + if ((IPU_PIX_FMT_YUYV != t->output.format) && + (IPU_PIX_FMT_NV12 != t->output.format)) { + dev_err(t->dev, "ERR:[0x%d] output format\n", t->task_no); + return -EINVAL; + } - tsk = create_task_entry(task); - if (IS_ERR(tsk)) - return PTR_ERR(tsk); - - ret = check_task(tsk); - - task->input = tsk->input; - task->output = tsk->output; - task->overlay = tsk->overlay; - - dump_task_info(tsk); - - kfree(tsk); - - return ret; -} -EXPORT_SYMBOL_GPL(ipu_check_task); - -int ipu_queue_sp_task(struct ipu_split_task *sp_task) -{ - struct ipu_task_entry *tsk; - int ret; - - tsk = create_task_entry(&sp_task->task); - if (IS_ERR(tsk)) - return PTR_ERR(tsk); + memset(¶m, 0, sizeof(param)); + /* init channel tiled bufs */ + if (deinterlace_3_field(t) && + (IPU_PIX_FMT_TILED_NV12F == t->input.format)) { + field_size = tiled_filed_size(t); + if (INPUT_CHAN_VDI_P == ch_type) { + inbuf_base = t->input.paddr + field_size; + param.vfield_buf.prev_veba = inbuf_base + t->set.i_off; + } else if (INPUT_CHAN == ch_type) { + inbuf_base = t->input.paddr_n; + param.vfield_buf.cur_veba = inbuf_base + t->set.i_off; + } else if (INPUT_CHAN_VDI_N == ch_type) { + inbuf_base = t->input.paddr_n + field_size; + param.vfield_buf.next_veba = inbuf_base + t->set.i_off; + } else + return -EINVAL; + height = t->input.crop.h >> 1; /* field format for vdoa */ + width = t->input.crop.w; + param.vfield_buf.vubo = t->set.i_uoff; + param.interlaced = 1; + param.scan_order = 1; + type = IPU_INPUT_BUFFER; + } else if ((IPU_PIX_FMT_TILED_NV12 == t->input.format) && + (INPUT_CHAN == ch_type)) { + height = t->input.crop.h; + width = t->input.crop.w; + param.vframe_buf.veba = t->input.paddr + t->set.i_off; + param.vframe_buf.vubo = t->set.i_uoff; + type = IPU_INPUT_BUFFER; + } else + return -EINVAL; - tsk->task_no = sp_task->task_no; + param.band_mode = (t->set.mode & VDOA_BAND_MODE) ? 1 : 0; + if (param.band_mode && (t->set.band_lines != 3) && + (t->set.band_lines != 4) && (t->set.band_lines != 5)) + return -EINVAL; + else if (param.band_mode) + param.band_lines = (1 << t->set.band_lines); + for (i = 0; i < MXC_IPU_MAX_NUM; i++) { + ipu_idx = ipu_get_soc(i); + if (!IS_ERR(ipu_idx) && ipu_idx == ipu) + break; + } + if (t->set.task & VDOA_ONLY) + /* dummy, didn't need ipu res */ + i = 0; + if (MXC_IPU_MAX_NUM == i) { + dev_err(t->dev, "ERR:[0x%p] get ipu num\n", t); + return -EINVAL; + } - ret = prepare_task(tsk); - if (ret < 0) + param.ipu_num = i; + param.vpu_stride = t->input.width; + param.height = height; + param.width = width; + if (IPU_PIX_FMT_NV12 == t->output.format) + param.pfs = VDOA_PFS_NV12; + else + param.pfs = VDOA_PFS_YUYV; + ipu_fmt = (param.pfs == VDOA_PFS_YUYV) ? IPU_PIX_FMT_YUYV : + IPU_PIX_FMT_NV12; + ipu_stride = param.width * bytes_per_pixel(ipu_fmt); + obuf_size = PAGE_ALIGN(param.width * param.height * + fmt_to_bpp(ipu_fmt)/8); + dev_dbg(t->dev, "band_mode:%d, band_lines:%d\n", + param.band_mode, param.band_lines); + if (!param.band_mode) { + /* note: if only for tiled -> raster convert and + no other post-processing, we don't need alloc buf + and use output buffer directly. + */ + if (t->set.task & VDOA_ONLY) + param.ieba0 = t->output.paddr; + else { + dev_err(t->dev, "ERR:[0x%d] vdoa task\n", t->task_no); + return -EINVAL; + } + } else { + if (IPU_PIX_FMT_TILED_NV12F != t->input.format) { + dev_err(t->dev, "ERR [0x%d] vdoa task\n", t->task_no); + return -EINVAL; + } + } + vdoa_setup(t->vdoa_handle, ¶m); + vdoa_get_output_buf(t->vdoa_handle, &buf); + if (t->set.task & VDOA_ONLY) goto done; - tsk->set.sp_setting = sp_task->parent_task->set.sp_setting; + ret = ipu_init_channel_buffer(ipu, + channel, + type, + ipu_fmt, + width, + height, + ipu_stride, + IPU_ROTATE_NONE, + buf.ieba0, + buf.ieba1, + 0, + buf.iubo, + 0); + if (ret < 0) { + t->state = STATE_INIT_CHAN_BUF_FAIL; + goto done; + } - ret = queue_task(tsk); + if (param.band_mode) { + ret = ipu_set_channel_bandmode(ipu, channel, + type, t->set.band_lines); + if (ret < 0) { + t->state = STATE_INIT_CHAN_BAND_FAIL; + goto done; + } + } done: - kfree(tsk); return ret; } -int ipu_queue_task(struct ipu_task *task) +static int init_tiled_ch_bufs(struct ipu_soc *ipu, struct ipu_task_entry *t) { - struct ipu_task_entry *tsk; - int ret; - u32 tmp_task_no; -#ifdef TIME_CALC - struct timespec begin; - struct timespec end; - long time_per_frame; - - getnstimeofday(&begin); -#endif - - tsk = create_task_entry(task); - if (IS_ERR(tsk)) - return PTR_ERR(tsk); - - ret = prepare_task(tsk); - if (ret < 0) - goto done; - - /* task_no last for bits for split task type*/ - tmp_task_no = frame_no++ % 1024; - tsk->task_no = tmp_task_no << 4; + int ret = 0; - if (need_split(tsk)) - ret = queue_split_task(tsk); - else - ret = queue_task(tsk); + if (IPU_PIX_FMT_TILED_NV12 == t->input.format) { + ret = init_tiled_buf(ipu, t, t->set.ic_chan, INPUT_CHAN); + CHECK_RETCODE(ret < 0, "init tiled_ch", t->state, done, ret); + } else if (IPU_PIX_FMT_TILED_NV12F == t->input.format) { + ret = init_tiled_buf(ipu, t, t->set.ic_chan, INPUT_CHAN); + CHECK_RETCODE(ret < 0, "init tiled_ch-c", t->state, done, ret); + ret = init_tiled_buf(ipu, t, t->set.vdi_ic_p_chan, + INPUT_CHAN_VDI_P); + CHECK_RETCODE(ret < 0, "init tiled_ch-p", t->state, done, ret); + ret = init_tiled_buf(ipu, t, t->set.vdi_ic_n_chan, + INPUT_CHAN_VDI_N); + CHECK_RETCODE(ret < 0, "init tiled_ch-n", t->state, done, ret); + } else { + ret = -EINVAL; + BUG(); + } -#ifdef TIME_CALC - getnstimeofday(&end); - if (end.tv_sec > begin.tv_sec) { - time_per_frame = (end.tv_sec - begin.tv_sec) * NSEC_PER_SEC; - time_per_frame += end.tv_nsec; - } else - time_per_frame = end.tv_nsec; - time_per_frame -= begin.tv_nsec; - dev_info(tsk->dev, "task take time %ld us\n", time_per_frame/1000); -#endif done: - kfree(tsk); return ret; } -EXPORT_SYMBOL_GPL(ipu_queue_task); - -static bool only_ic(u8 mode) -{ - return ((mode == IC_MODE) || (mode == VDI_MODE)); -} - -static bool only_rot(u8 mode) -{ - return (mode == ROT_MODE); -} - -static bool ic_and_rot(u8 mode) -{ - return ((mode == (IC_MODE | ROT_MODE)) || - (mode == (VDI_MODE | ROT_MODE))); -} static int init_ic(struct ipu_soc *ipu, struct ipu_task_entry *t) { @@ -1383,14 +1874,25 @@ static int init_ic(struct ipu_soc *ipu, struct ipu_task_entry *t) } } + if (t->set.mode & VDOA_MODE) + ipu->vdoa_en = 1; + /* init channels */ - ret = ipu_init_channel(ipu, t->set.ic_chan, ¶ms); - if (ret < 0) { - t->state = STATE_INIT_CHAN_FAIL; - goto done; + if (!(t->set.task & VDOA_ONLY)) { + ret = ipu_init_channel(ipu, t->set.ic_chan, ¶ms); + if (ret < 0) { + t->state = STATE_INIT_CHAN_FAIL; + goto done; + } } if (deinterlace_3_field(t)) { + if (IPU_DEINTERLACE_FIELD_TOP == t->input.deinterlace.field_fmt) + params.mem_prp_vf_mem.field_fmt = V4L2_FIELD_INTERLACED_TB; + else if (IPU_DEINTERLACE_FIELD_BOTTOM == t->input.deinterlace.field_fmt) + params.mem_prp_vf_mem.field_fmt = V4L2_FIELD_INTERLACED_BT; + else + BUG(); ret = ipu_init_channel(ipu, t->set.vdi_ic_p_chan, ¶ms); if (ret < 0) { t->state = STATE_INIT_CHAN_FAIL; @@ -1404,39 +1906,51 @@ static int init_ic(struct ipu_soc *ipu, struct ipu_task_entry *t) } /* init channel bufs */ - if (deinterlace_3_field(t)) { - inbuf_p = t->input.paddr + t->set.istride + t->set.i_off; - inbuf = t->input.paddr_n + t->set.i_off; - inbuf_n = t->input.paddr_n + t->set.istride + t->set.i_off; - } else - inbuf = t->input.paddr + t->set.i_off; + if ((IPU_PIX_FMT_TILED_NV12 == t->input.format) || + (IPU_PIX_FMT_TILED_NV12F == t->input.format)) { + ret = init_tiled_ch_bufs(ipu, t); + if (ret < 0) + goto done; + } else { + if ((deinterlace_3_field(t)) && + (IPU_PIX_FMT_TILED_NV12F != t->input.format)) { + inbuf_p = t->input.paddr + t->set.istride + + t->set.i_off; + inbuf = t->input.paddr_n + t->set.i_off; + inbuf_n = t->input.paddr_n + t->set.istride + + t->set.i_off; + } else + inbuf = t->input.paddr + t->set.i_off; - if (t->overlay_en) { - ovbuf = t->overlay.paddr + t->set.ov_off; - if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) - ov_alp_buf = t->overlay.alpha.loc_alp_paddr - + t->set.ov_alpha_off; + if (t->overlay_en) + ovbuf = t->overlay.paddr + t->set.ov_off; } + if (t->overlay_en && (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL)) + ov_alp_buf = t->overlay.alpha.loc_alp_paddr + + t->set.ov_alpha_off; - ret = ipu_init_channel_buffer(ipu, - t->set.ic_chan, - IPU_INPUT_BUFFER, - t->input.format, - t->input.crop.w, - t->input.crop.h, - t->set.istride, - IPU_ROTATE_NONE, - inbuf, - 0, - 0, - t->set.i_uoff, - t->set.i_voff); - if (ret < 0) { - t->state = STATE_INIT_CHAN_BUF_FAIL; - goto done; + if ((IPU_PIX_FMT_TILED_NV12 != t->input.format) && + (IPU_PIX_FMT_TILED_NV12F != t->input.format)) { + ret = ipu_init_channel_buffer(ipu, + t->set.ic_chan, + IPU_INPUT_BUFFER, + t->input.format, + t->input.crop.w, + t->input.crop.h, + t->set.istride, + IPU_ROTATE_NONE, + inbuf, + 0, + 0, + t->set.i_uoff, + t->set.i_voff); + if (ret < 0) { + t->state = STATE_INIT_CHAN_BUF_FAIL; + goto done; + } } - - if (deinterlace_3_field(t)) { + if (deinterlace_3_field(t) && + (IPU_PIX_FMT_TILED_NV12F != t->input.format)) { ret = ipu_init_channel_buffer(ipu, t->set.vdi_ic_p_chan, IPU_INPUT_BUFFER, @@ -1492,43 +2006,51 @@ static int init_ic(struct ipu_soc *ipu, struct ipu_task_entry *t) t->state = STATE_INIT_CHAN_BUF_FAIL; goto done; } + } - if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) { - ret = ipu_init_channel_buffer(ipu, - t->set.ic_chan, - IPU_ALPHA_IN_BUFFER, - IPU_PIX_FMT_GENERIC, - t->overlay.crop.w, - t->overlay.crop.h, - t->set.ov_alpha_stride, - IPU_ROTATE_NONE, - ov_alp_buf, - 0, - 0, - 0, 0); - if (ret < 0) { - t->state = STATE_INIT_CHAN_BUF_FAIL; - goto done; - } + if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) { + ret = ipu_init_channel_buffer(ipu, + t->set.ic_chan, + IPU_ALPHA_IN_BUFFER, + IPU_PIX_FMT_GENERIC, + t->overlay.crop.w, + t->overlay.crop.h, + t->set.ov_alpha_stride, + IPU_ROTATE_NONE, + ov_alp_buf, + 0, + 0, + 0, 0); + if (ret < 0) { + t->state = STATE_INIT_CHAN_BUF_FAIL; + goto done; } } - ret = ipu_init_channel_buffer(ipu, - t->set.ic_chan, - IPU_OUTPUT_BUFFER, - out_fmt, - out_w, - out_h, - out_stride, - out_rot, - outbuf, - 0, - 0, - out_uoff, - out_voff); - if (ret < 0) { - t->state = STATE_INIT_CHAN_BUF_FAIL; - goto done; + if (!(t->set.task & VDOA_ONLY)) { + ret = ipu_init_channel_buffer(ipu, + t->set.ic_chan, + IPU_OUTPUT_BUFFER, + out_fmt, + out_w, + out_h, + out_stride, + out_rot, + outbuf, + 0, + 0, + out_uoff, + out_voff); + if (ret < 0) { + t->state = STATE_INIT_CHAN_BUF_FAIL; + goto done; + } + } + + if ((t->set.mode & VDOA_BAND_MODE) && (t->set.task & VDI_VF)) { + ret = ipu_link_channels(ipu, MEM_VDOA_MEM, t->set.ic_chan); + CHECK_RETCODE(ret < 0, "ipu_link_ch vdoa_ic", + STATE_LINK_CHAN_FAIL, done, ret); } done: @@ -1537,6 +2059,13 @@ done: static void uninit_ic(struct ipu_soc *ipu, struct ipu_task_entry *t) { + int ret; + + if ((t->set.mode & VDOA_BAND_MODE) && (t->set.task & VDI_VF)) { + ret = ipu_unlink_channels(ipu, MEM_VDOA_MEM, t->set.ic_chan); + CHECK_RETCODE_CONT(ret < 0, "ipu_unlink_ch vdoa_ic", + STATE_UNLINK_CHAN_FAIL, ret); + } ipu_uninit_channel(ipu, t->set.ic_chan); if (deinterlace_3_field(t)) { ipu_uninit_channel(ipu, t->set.vdi_ic_p_chan); @@ -1648,6 +2177,9 @@ static int get_irq(struct ipu_task_entry *t) case MEM_PP_MEM: irq = IPU_IRQ_PP_OUT_EOF; break; + case MEM_VDI_MEM: + irq = IPU_IRQ_VDIC_OUT_EOF; + break; default: irq = -EINVAL; } @@ -1657,8 +2189,13 @@ static int get_irq(struct ipu_task_entry *t) static irqreturn_t task_irq_handler(int irq, void *dev_id) { - struct completion *comp = dev_id; - complete(comp); + struct ipu_task_entry *prev_tsk = dev_id; + + CHECK_PERF(&prev_tsk->ts_inirq); + complete(&prev_tsk->irq_comp); + dev_dbg(prev_tsk->dev, "[0x%p] no-0x%x in-irq!", + prev_tsk, prev_tsk->task_no); + return IRQ_HANDLED; } @@ -1671,17 +2208,20 @@ static void vdi_split_process(struct ipu_soc *ipu, struct ipu_task_entry *t) u32 task_no; u32 i, offset_addr; unsigned char *base_off; + struct ipu_task_entry *parent = t->parent; + if (!parent) + BUG(); stripe_mode = t->task_no & 0xf; task_no = t->task_no >> 4; base_off = (char *) __va(t->output.paddr); if (base_off == NULL) { - dev_err(t->dev, "[0x%p]Falied get vitual address\n", (void *)t); + dev_err(t->dev, "ERR[0x%p]Falied get vitual address\n", t); return; } - vdi_save_lines = (t->output.crop.h - t->set.sp_setting.ud_split_line)/2 ; + vdi_save_lines = (t->output.crop.h - t->set.sp_setting.ud_split_line)/2; vdi_size = vdi_save_lines * t->output.crop.w * 2; if (vdi_save_lines <= 0) { @@ -1690,68 +2230,68 @@ static void vdi_split_process(struct ipu_soc *ipu, struct ipu_task_entry *t) } /*check vditmpbuf buffer have alloced or buffer size is changed */ - if ((vdi_save_lines != old_save_lines) || (vdi_size != old_size)) { - if (vditmpbuf[0] != NULL) - kfree(vditmpbuf[0]); - if (vditmpbuf[1] != NULL) - kfree(vditmpbuf[1]); - - vditmpbuf[0] = (char *)kmalloc(vdi_size, GFP_KERNEL); - if (vditmpbuf[0] == NULL) { + if ((vdi_save_lines != parent->old_save_lines) || + (vdi_size != parent->old_size)) { + if (parent->vditmpbuf[0] != NULL) + kfree(parent->vditmpbuf[0]); + if (parent->vditmpbuf[1] != NULL) + kfree(parent->vditmpbuf[1]); + + parent->vditmpbuf[0] = kmalloc(vdi_size, GFP_KERNEL); + if (parent->vditmpbuf[0] == NULL) { dev_err(t->dev, - "[0x%p]Falied Alloc vditmpbuf[0]\n", (void *)t); + "[0x%p]Falied Alloc vditmpbuf[0]\n", (void *)t); return; } - memset(vditmpbuf[0], 0, vdi_size); + memset(parent->vditmpbuf[0], 0, vdi_size); - vditmpbuf[1] = (char *)kmalloc(vdi_size, GFP_KERNEL); - if (vditmpbuf[1] == NULL) { + parent->vditmpbuf[1] = kmalloc(vdi_size, GFP_KERNEL); + if (parent->vditmpbuf[1] == NULL) { dev_err(t->dev, - "[0x%p]Falied Alloc vditmpbuf[1]\n", (void *)t); + "[0x%p]Falied Alloc vditmpbuf[1]\n", (void *)t); return; } - memset(vditmpbuf[1], 0, vdi_size); + memset(parent->vditmpbuf[1], 0, vdi_size); - old_save_lines = vdi_save_lines; - old_size = vdi_size; + parent->old_save_lines = vdi_save_lines; + parent->old_size = vdi_size; } /* UP stripe or UP&LEFT stripe */ if ((stripe_mode == UP_STRIPE) || (stripe_mode == (UP_STRIPE | LEFT_STRIPE))) { - if (!buf0filled) { - + if (!parent->buf0filled) { offset_addr = t->set.o_off + t->set.sp_setting.ud_split_line*t->set.ostride; dmac_flush_range(base_off + offset_addr, base_off + offset_addr + vdi_size); outer_flush_range(t->output.paddr + offset_addr, - t->output.paddr + offset_addr + vdi_size); + t->output.paddr + offset_addr + vdi_size); for (i = 0; i < vdi_save_lines; i++) - memcpy(vditmpbuf[0] + i*t->output.crop.w*2, - base_off + offset_addr + i*t->set.ostride, - t->output.crop.w*2); - buf0filled = true; + memcpy(parent->vditmpbuf[0] + i*t->output.crop.w*2, + base_off + offset_addr + + i*t->set.ostride, t->output.crop.w*2); + parent->buf0filled = true; } else { - offset_addr = t->set.o_off + - (t->output.crop.h - vdi_save_lines)*t->set.ostride; + offset_addr = t->set.o_off + (t->output.crop.h - + vdi_save_lines) * t->set.ostride; for (i = 0; i < vdi_save_lines; i++) memcpy(base_off + offset_addr + i*t->set.ostride, - vditmpbuf[0] + i*t->output.crop.w*2, + parent->vditmpbuf[0] + i*t->output.crop.w*2, t->output.crop.w*2); dmac_flush_range(base_off + offset_addr, base_off + offset_addr + i*t->set.ostride); outer_flush_range(t->output.paddr + offset_addr, t->output.paddr + offset_addr + i*t->set.ostride); - buf0filled = false; + parent->buf0filled = false; } } /*Down stripe or Down&Left stripe*/ else if ((stripe_mode == DOWN_STRIPE) || (stripe_mode == (DOWN_STRIPE | LEFT_STRIPE))) { - if (!buf0filled) { + if (!parent->buf0filled) { offset_addr = t->set.o_off + vdi_save_lines*t->set.ostride; dmac_flush_range(base_off + offset_addr, base_off + offset_addr + vdi_size); @@ -1759,27 +2299,27 @@ static void vdi_split_process(struct ipu_soc *ipu, struct ipu_task_entry *t) t->output.paddr + offset_addr + vdi_size); for (i = 0; i < vdi_save_lines; i++) - memcpy(vditmpbuf[0] + i*t->output.crop.w*2, + memcpy(parent->vditmpbuf[0] + i*t->output.crop.w*2, base_off + offset_addr + i*t->set.ostride, t->output.crop.w*2); - buf0filled = true; + parent->buf0filled = true; } else { offset_addr = t->set.o_off; for (i = 0; i < vdi_save_lines; i++) memcpy(base_off + offset_addr + i*t->set.ostride, - vditmpbuf[0] + i*t->output.crop.w*2, + parent->vditmpbuf[0] + i*t->output.crop.w*2, t->output.crop.w*2); dmac_flush_range(base_off + offset_addr, base_off + offset_addr + i*t->set.ostride); outer_flush_range(t->output.paddr + offset_addr, t->output.paddr + offset_addr + i*t->set.ostride); - buf0filled = false; + parent->buf0filled = false; } } /*Up&Right stripe*/ else if (stripe_mode == (UP_STRIPE | RIGHT_STRIPE)) { - if (!buf1filled) { + if (!parent->buf1filled) { offset_addr = t->set.o_off + t->set.sp_setting.ud_split_line*t->set.ostride; dmac_flush_range(base_off + offset_addr, @@ -1788,28 +2328,28 @@ static void vdi_split_process(struct ipu_soc *ipu, struct ipu_task_entry *t) t->output.paddr + offset_addr + vdi_size); for (i = 0; i < vdi_save_lines; i++) - memcpy(vditmpbuf[1] + i*t->output.crop.w*2, + memcpy(parent->vditmpbuf[1] + i*t->output.crop.w*2, base_off + offset_addr + i*t->set.ostride, t->output.crop.w*2); - buf1filled = true; + parent->buf1filled = true; } else { offset_addr = t->set.o_off + (t->output.crop.h - vdi_save_lines)*t->set.ostride; for (i = 0; i < vdi_save_lines; i++) memcpy(base_off + offset_addr + i*t->set.ostride, - vditmpbuf[1] + i*t->output.crop.w*2, + parent->vditmpbuf[1] + i*t->output.crop.w*2, t->output.crop.w*2); dmac_flush_range(base_off + offset_addr, base_off + offset_addr + i*t->set.ostride); outer_flush_range(t->output.paddr + offset_addr, t->output.paddr + offset_addr + i*t->set.ostride); - buf1filled = false; + parent->buf1filled = false; } } /*Down stripe or Down&Right stript*/ else if (stripe_mode == (DOWN_STRIPE | RIGHT_STRIPE)) { - if (!buf1filled) { + if (!parent->buf1filled) { offset_addr = t->set.o_off + vdi_save_lines*t->set.ostride; dmac_flush_range(base_off + offset_addr, base_off + offset_addr + vdi_save_lines*t->set.ostride); @@ -1817,39 +2357,131 @@ static void vdi_split_process(struct ipu_soc *ipu, struct ipu_task_entry *t) t->output.paddr + offset_addr + vdi_save_lines*t->set.ostride); for (i = 0; i < vdi_save_lines; i++) - memcpy(vditmpbuf[1] + i*t->output.crop.w*2, + memcpy(parent->vditmpbuf[1] + i*t->output.crop.w*2, base_off + offset_addr + i*t->set.ostride, t->output.crop.w*2); - buf1filled = true; + parent->buf1filled = true; } else { offset_addr = t->set.o_off; for (i = 0; i < vdi_save_lines; i++) memcpy(base_off + offset_addr + i*t->set.ostride, - vditmpbuf[1] + i*t->output.crop.w*2, + parent->vditmpbuf[1] + i*t->output.crop.w*2, t->output.crop.w*2); dmac_flush_range(base_off + offset_addr, base_off + offset_addr + vdi_save_lines*t->set.ostride); outer_flush_range(t->output.paddr + offset_addr, t->output.paddr + offset_addr + vdi_save_lines*t->set.ostride); - buf1filled = false; + parent->buf1filled = false; + } + } +} + +static void do_task_release(struct ipu_task_entry *t, int fail) +{ + int ret; + struct ipu_soc *ipu = t->ipu; + + if (t->input.deinterlace.enable && !fail && + (t->task_no & (UP_STRIPE | DOWN_STRIPE))) + vdi_split_process(ipu, t); + + ipu_free_irq(ipu, t->irq, t); + + if (t->vdoa_dma.vaddr) + dma_free_coherent(t->dev, + t->vdoa_dma.size, + t->vdoa_dma.vaddr, + t->vdoa_dma.paddr); + + if (only_ic(t->set.mode)) { + ret = ipu_disable_channel(ipu, t->set.ic_chan, true); + CHECK_RETCODE_CONT(ret < 0, "ipu_disable_ch only_ic", + STATE_DISABLE_CHAN_FAIL, ret); + if (deinterlace_3_field(t)) { + ret = ipu_disable_channel(ipu, t->set.vdi_ic_p_chan, + true); + CHECK_RETCODE_CONT(ret < 0, "ipu_disable_ch only_ic_p", + STATE_DISABLE_CHAN_FAIL, ret); + ret = ipu_disable_channel(ipu, t->set.vdi_ic_n_chan, + true); + CHECK_RETCODE_CONT(ret < 0, "ipu_disable_ch only_ic_n", + STATE_DISABLE_CHAN_FAIL, ret); + } + } else if (only_rot(t->set.mode)) { + ret = ipu_disable_channel(ipu, t->set.rot_chan, true); + CHECK_RETCODE_CONT(ret < 0, "ipu_disable_ch only_rot", + STATE_DISABLE_CHAN_FAIL, ret); + } else if (ic_and_rot(t->set.mode)) { + ret = ipu_unlink_channels(ipu, t->set.ic_chan, t->set.rot_chan); + CHECK_RETCODE_CONT(ret < 0, "ipu_unlink_ch", + STATE_UNLINK_CHAN_FAIL, ret); + ret = ipu_disable_channel(ipu, t->set.rot_chan, true); + CHECK_RETCODE_CONT(ret < 0, "ipu_disable_ch ic_and_rot-rot", + STATE_DISABLE_CHAN_FAIL, ret); + ret = ipu_disable_channel(ipu, t->set.ic_chan, true); + CHECK_RETCODE_CONT(ret < 0, "ipu_disable_ch ic_and_rot-ic", + STATE_DISABLE_CHAN_FAIL, ret); + if (deinterlace_3_field(t)) { + ret = ipu_disable_channel(ipu, t->set.vdi_ic_p_chan, + true); + CHECK_RETCODE_CONT(ret < 0, "ipu_disable_ch icrot-ic-p", + STATE_DISABLE_CHAN_FAIL, ret); + ret = ipu_disable_channel(ipu, t->set.vdi_ic_n_chan, + true); + CHECK_RETCODE_CONT(ret < 0, "ipu_disable_ch icrot-ic-n", + STATE_DISABLE_CHAN_FAIL, ret); } } + + if (only_ic(t->set.mode)) + uninit_ic(ipu, t); + else if (only_rot(t->set.mode)) + uninit_rot(ipu, t); + else if (ic_and_rot(t->set.mode)) { + uninit_ic(ipu, t); + uninit_rot(ipu, t); + } + + t->state = STATE_OK; + CHECK_PERF(&t->ts_rel); + return; +} + +static void do_task_vdoa_only(struct ipu_task_entry *t) +{ + int ret; + + ret = init_tiled_ch_bufs(NULL, t); + CHECK_RETCODE(ret < 0, "do_vdoa_only", STATE_ERR, out, ret); + ret = vdoa_start(t->vdoa_handle, VDOA_DEF_TIMEOUT_MS); + vdoa_stop(t->vdoa_handle); + CHECK_RETCODE(ret < 0, "vdoa_wait4complete, do_vdoa_only", + STATE_VDOA_IRQ_TIMEOUT, out, ret); + + t->state = STATE_OK; +out: + return; } -static void do_task(struct ipu_soc *ipu, struct ipu_task_entry *t) +static void do_task(struct ipu_task_entry *t) { - struct completion comp; int r_size; int irq; int ret; + uint32_t busy; + struct ipu_soc *ipu = t->ipu; + + CHECK_PERF(&t->ts_dotask); if (!ipu) { t->state = STATE_NO_IPU; return; } - dev_dbg(ipu->dev, "[0x%p]Do task: id %d\n", (void *)t, t->task_id); + init_completion(&t->irq_comp); + dev_dbg(ipu->dev, "[0x%p]Do task no:0x%x: id %d\n", (void *)t, + t->task_no, t->task_id); dump_task_info(t); if (t->set.task & IC_PP) { @@ -1859,12 +2491,23 @@ static void do_task(struct ipu_soc *ipu, struct ipu_task_entry *t) t->set.ic_chan = MEM_PRP_VF_MEM; dev_dbg(ipu->dev, "[0x%p]ic channel MEM_PRP_VF_MEM\n", (void *)t); } else if (t->set.task & VDI_VF) { - t->set.ic_chan = MEM_VDI_PRP_VF_MEM; - if (deinterlace_3_field(t)) { - t->set.vdi_ic_p_chan = MEM_VDI_PRP_VF_MEM_P; - t->set.vdi_ic_n_chan = MEM_VDI_PRP_VF_MEM_N; + if (t->set.mode & VDOA_BAND_MODE) { + t->set.ic_chan = MEM_VDI_MEM; + if (deinterlace_3_field(t)) { + t->set.vdi_ic_p_chan = MEM_VDI_MEM_P; + t->set.vdi_ic_n_chan = MEM_VDI_MEM_N; + } + dev_dbg(ipu->dev, "[0x%p]ic ch MEM_VDI_MEM\n", + (void *)t); + } else { + t->set.ic_chan = MEM_VDI_PRP_VF_MEM; + if (deinterlace_3_field(t)) { + t->set.vdi_ic_p_chan = MEM_VDI_PRP_VF_MEM_P; + t->set.vdi_ic_n_chan = MEM_VDI_PRP_VF_MEM_N; + } + dev_dbg(ipu->dev, + "[0x%p]ic ch MEM_VDI_PRP_VF_MEM\n", t); } - dev_dbg(ipu->dev, "[0x%p]ic channel MEM_VDI_PRP_VF_MEM\n", (void *)t); } if (t->set.task & ROT_PP) { @@ -1875,17 +2518,38 @@ static void do_task(struct ipu_soc *ipu, struct ipu_task_entry *t) dev_dbg(ipu->dev, "[0x%p]rot channel MEM_ROT_VF_MEM\n", (void *)t); } + if (t->task_id == IPU_TASK_ID_VF) + busy = ic_vf_pp_is_busy(ipu, true); + else if (t->task_id == IPU_TASK_ID_PP) + busy = ic_vf_pp_is_busy(ipu, false); + else + BUG(); + if (busy) { + dev_err(ipu->dev, "ERR[0x%p-no:0x%x]ipu task_id:%d busy!\n", + (void *)t, t->task_no, t->task_id); + t->state = STATE_IPU_BUSY; + BUG(); + return; + } + + irq = get_irq(t); + if (irq < 0) { + t->state = STATE_NO_IRQ; + return; + } + t->irq = irq; + /* channel setup */ if (only_ic(t->set.mode)) { dev_dbg(t->dev, "[0x%p]only ic mode\n", (void *)t); ret = init_ic(ipu, t); - if (ret < 0) - goto chan_done; + CHECK_RETCODE(ret < 0, "init_ic only_ic", + t->state, chan_setup, ret); } else if (only_rot(t->set.mode)) { dev_dbg(t->dev, "[0x%p]only rot mode\n", (void *)t); ret = init_rot(ipu, t); - if (ret < 0) - goto chan_done; + CHECK_RETCODE(ret < 0, "init_rot only_rot", + t->state, chan_setup, ret); } else if (ic_and_rot(t->set.mode)) { int rot_idx = (t->task_id == IPU_TASK_ID_VF) ? 0 : 1; @@ -1917,10 +2581,9 @@ static void do_task(struct ipu_soc *ipu, struct ipu_task_entry *t) r_size, &ipu->rot_dma[rot_idx].paddr, GFP_DMA | GFP_KERNEL); - if (ipu->rot_dma[rot_idx].vaddr == NULL) { - ret = -ENOMEM; - goto chan_done; - } + CHECK_RETCODE(ipu->rot_dma[rot_idx].vaddr == NULL, + "ic_and_rot", STATE_SYS_NO_MEM, + chan_setup, -ENOMEM); } t->set.r_paddr = ipu->rot_dma[rot_idx].paddr; @@ -1932,180 +2595,636 @@ static void do_task(struct ipu_soc *ipu, struct ipu_task_entry *t) dev_dbg(t->dev, "[0x%p]\trstride = %d\n", (void *)t, t->set.r_stride); ret = init_ic(ipu, t); - if (ret < 0) - goto chan_done; + CHECK_RETCODE(ret < 0, "init_ic ic_and_rot", + t->state, chan_setup, ret); ret = init_rot(ipu, t); - if (ret < 0) - goto chan_done; + CHECK_RETCODE(ret < 0, "init_rot ic_and_rot", + t->state, chan_setup, ret); ret = ipu_link_channels(ipu, t->set.ic_chan, t->set.rot_chan); - if (ret < 0) { - t->state = STATE_LINK_CHAN_FAIL; - goto chan_done; - } + CHECK_RETCODE(ret < 0, "ipu_link_ch ic_and_rot", + STATE_LINK_CHAN_FAIL, chan_setup, ret); } else { - dev_err(t->dev, "[0x%p]do_task: should not be here\n", (void *)t); + dev_err(t->dev, "ERR [0x%p]do task: should not be here\n", t); + t->state = STATE_ERR; + BUG(); return; } - /* channel setup */ - /* irq setup */ - irq = get_irq(t); - if (irq < 0) { - t->state = STATE_NO_IRQ; - goto chan_done; - } - - dev_dbg(t->dev, "[0x%p]task irq is %d\n", (void *)t, irq); - - init_completion(&comp); - ret = ipu_request_irq(ipu, irq, task_irq_handler, 0, NULL, &comp); - if (ret < 0) { - t->state = STATE_IRQ_FAIL; - goto chan_done; - } + ret = ipu_request_irq(ipu, irq, task_irq_handler, 0, NULL, t); + CHECK_RETCODE(ret < 0, "ipu_req_irq", + STATE_IRQ_FAIL, chan_setup, ret); /* enable/start channel */ if (only_ic(t->set.mode)) { - ipu_enable_channel(ipu, t->set.ic_chan); + ret = ipu_enable_channel(ipu, t->set.ic_chan); + CHECK_RETCODE(ret < 0, "ipu_enable_ch only_ic", + STATE_ENABLE_CHAN_FAIL, chan_en, ret); if (deinterlace_3_field(t)) { - ipu_enable_channel(ipu, t->set.vdi_ic_p_chan); - ipu_enable_channel(ipu, t->set.vdi_ic_n_chan); + ret = ipu_enable_channel(ipu, t->set.vdi_ic_p_chan); + CHECK_RETCODE(ret < 0, "ipu_enable_ch only_ic_p", + STATE_ENABLE_CHAN_FAIL, chan_en, ret); + ret = ipu_enable_channel(ipu, t->set.vdi_ic_n_chan); + CHECK_RETCODE(ret < 0, "ipu_enable_ch only_ic_n", + STATE_ENABLE_CHAN_FAIL, chan_en, ret); } - ipu_select_buffer(ipu, t->set.ic_chan, IPU_OUTPUT_BUFFER, 0); + ret = ipu_select_buffer(ipu, t->set.ic_chan, IPU_OUTPUT_BUFFER, + 0); + CHECK_RETCODE(ret < 0, "ipu_sel_buf only_ic", + STATE_SEL_BUF_FAIL, chan_buf, ret); if (t->overlay_en) { - ipu_select_buffer(ipu, t->set.ic_chan, IPU_GRAPH_IN_BUFFER, 0); - if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) - ipu_select_buffer(ipu, t->set.ic_chan, IPU_ALPHA_IN_BUFFER, 0); + ret = ipu_select_buffer(ipu, t->set.ic_chan, + IPU_GRAPH_IN_BUFFER, 0); + CHECK_RETCODE(ret < 0, "ipu_sel_buf only_ic_g", + STATE_SEL_BUF_FAIL, chan_buf, ret); + if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) { + ret = ipu_select_buffer(ipu, t->set.ic_chan, + IPU_ALPHA_IN_BUFFER, 0); + CHECK_RETCODE(ret < 0, "ipu_sel_buf only_ic_a", + STATE_SEL_BUF_FAIL, chan_buf, + ret); + } + } + if (!(t->set.mode & VDOA_BAND_MODE)) { + if (deinterlace_3_field(t)) + ipu_select_multi_vdi_buffer(ipu, 0); + else { + ret = ipu_select_buffer(ipu, t->set.ic_chan, + IPU_INPUT_BUFFER, 0); + CHECK_RETCODE(ret < 0, "ipu_sel_buf only_ic_i", + STATE_SEL_BUF_FAIL, chan_buf, ret); + } } - if (deinterlace_3_field(t)) - ipu_select_multi_vdi_buffer(ipu, 0); - else - ipu_select_buffer(ipu, t->set.ic_chan, IPU_INPUT_BUFFER, 0); } else if (only_rot(t->set.mode)) { - ipu_enable_channel(ipu, t->set.rot_chan); - ipu_select_buffer(ipu, t->set.rot_chan, IPU_OUTPUT_BUFFER, 0); - ipu_select_buffer(ipu, t->set.rot_chan, IPU_INPUT_BUFFER, 0); + ret = ipu_enable_channel(ipu, t->set.rot_chan); + CHECK_RETCODE(ret < 0, "ipu_enable_ch only_rot", + STATE_ENABLE_CHAN_FAIL, chan_en, ret); + ret = ipu_select_buffer(ipu, t->set.rot_chan, + IPU_OUTPUT_BUFFER, 0); + CHECK_RETCODE(ret < 0, "ipu_sel_buf only_rot_o", + STATE_SEL_BUF_FAIL, chan_buf, ret); + ret = ipu_select_buffer(ipu, t->set.rot_chan, + IPU_INPUT_BUFFER, 0); + CHECK_RETCODE(ret < 0, "ipu_sel_buf only_rot_i", + STATE_SEL_BUF_FAIL, chan_buf, ret); } else if (ic_and_rot(t->set.mode)) { - ipu_enable_channel(ipu, t->set.rot_chan); - ipu_enable_channel(ipu, t->set.ic_chan); + ret = ipu_enable_channel(ipu, t->set.rot_chan); + CHECK_RETCODE(ret < 0, "ipu_enable_ch ic_and_rot-rot", + STATE_ENABLE_CHAN_FAIL, chan_en, ret); + ret = ipu_enable_channel(ipu, t->set.ic_chan); + CHECK_RETCODE(ret < 0, "ipu_enable_ch ic_and_rot-ic", + STATE_ENABLE_CHAN_FAIL, chan_en, ret); if (deinterlace_3_field(t)) { - ipu_enable_channel(ipu, t->set.vdi_ic_p_chan); - ipu_enable_channel(ipu, t->set.vdi_ic_n_chan); + ret = ipu_enable_channel(ipu, t->set.vdi_ic_p_chan); + CHECK_RETCODE(ret < 0, "ipu_enable_ch ic_and_rot-p", + STATE_ENABLE_CHAN_FAIL, chan_en, ret); + ret = ipu_enable_channel(ipu, t->set.vdi_ic_n_chan); + CHECK_RETCODE(ret < 0, "ipu_enable_ch ic_and_rot-n", + STATE_ENABLE_CHAN_FAIL, chan_en, ret); } - ipu_select_buffer(ipu, t->set.rot_chan, IPU_OUTPUT_BUFFER, 0); + ret = ipu_select_buffer(ipu, t->set.rot_chan, + IPU_OUTPUT_BUFFER, 0); + CHECK_RETCODE(ret < 0, "ipu_sel_buf ic_and_rot-rot-o", + STATE_SEL_BUF_FAIL, chan_buf, ret); if (t->overlay_en) { - ipu_select_buffer(ipu, t->set.ic_chan, IPU_GRAPH_IN_BUFFER, 0); - if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) - ipu_select_buffer(ipu, t->set.ic_chan, IPU_ALPHA_IN_BUFFER, 0); + ret = ipu_select_buffer(ipu, t->set.ic_chan, + IPU_GRAPH_IN_BUFFER, 0); + CHECK_RETCODE(ret < 0, "ipu_sel_buf ic_and_rot-ic-g", + STATE_SEL_BUF_FAIL, chan_buf, ret); + if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) { + ret = ipu_select_buffer(ipu, t->set.ic_chan, + IPU_ALPHA_IN_BUFFER, 0); + CHECK_RETCODE(ret < 0, "ipu_sel_buf icrot-ic-a", + STATE_SEL_BUF_FAIL, + chan_buf, ret); + } } - ipu_select_buffer(ipu, t->set.ic_chan, IPU_OUTPUT_BUFFER, 0); + ret = ipu_select_buffer(ipu, t->set.ic_chan, + IPU_OUTPUT_BUFFER, 0); + CHECK_RETCODE(ret < 0, "ipu_sel_buf ic_and_rot-ic-o", + STATE_SEL_BUF_FAIL, chan_buf, ret); if (deinterlace_3_field(t)) ipu_select_multi_vdi_buffer(ipu, 0); + else { + ret = ipu_select_buffer(ipu, t->set.ic_chan, + IPU_INPUT_BUFFER, 0); + CHECK_RETCODE(ret < 0, "ipu_sel_buf ic_and_rot-ic-i", + STATE_SEL_BUF_FAIL, chan_buf, ret); + } + } + + if (need_split(t)) + t->state = STATE_IN_PROGRESS; + + if (t->set.mode & VDOA_BAND_MODE) { + ret = vdoa_start(t->vdoa_handle, VDOA_DEF_TIMEOUT_MS); + CHECK_RETCODE(ret < 0, "vdoa_wait4complete, do_vdoa_band", + STATE_VDOA_IRQ_TIMEOUT, chan_rel, ret); + } + + CHECK_PERF(&t->ts_waitirq); + ret = wait_for_completion_timeout(&t->irq_comp, + msecs_to_jiffies(t->timeout - DEF_DELAY_MS)); + CHECK_PERF(&t->ts_wakeup); + CHECK_RETCODE(ret == 0, "wait_for_comp_timeout", + STATE_IRQ_TIMEOUT, chan_rel, ret); + dev_dbg(t->dev, "[0x%p] no-0x%x ipu irq done!", t, t->task_no); + +chan_rel: +chan_buf: +chan_en: +chan_setup: + if (t->set.mode & VDOA_BAND_MODE) + vdoa_stop(t->vdoa_handle); + do_task_release(t, t->state >= STATE_ERR); + return; +} + +static void do_task_vdoa_vdi(struct ipu_task_entry *t) +{ + int i; + int ret; + u32 stripe_width; + + /* FIXME: crop mode not support now */ + stripe_width = t->input.width >> 1; + t->input.crop.pos.x = 0; + t->input.crop.pos.y = 0; + t->input.crop.w = stripe_width; + t->input.crop.h = t->input.height; + t->output.crop.w = stripe_width; + t->output.crop.h = t->input.height; + + for (i = 0; i < 2; i++) { + t->input.crop.pos.x = t->input.crop.pos.x + i * stripe_width; + t->output.crop.pos.x = t->output.crop.pos.x + i * stripe_width; + /* check input */ + ret = set_crop(&t->input.crop, t->input.width, t->input.height, + t->input.format); + if (ret < 0) { + ret = STATE_ERR; + goto done; + } else + update_offset(t->input.format, + t->input.width, t->input.height, + t->input.crop.pos.x, + t->input.crop.pos.y, + &t->set.i_off, &t->set.i_uoff, + &t->set.i_voff, &t->set.istride); + dev_dbg(t->dev, "i_off:0x%x, i_uoff:0x%x, istride:%d.\n", + t->set.i_off, t->set.i_uoff, t->set.istride); + /* check output */ + ret = set_crop(&t->output.crop, t->input.width, + t->output.height, t->output.format); + if (ret < 0) { + ret = STATE_ERR; + goto done; + } else + update_offset(t->output.format, + t->output.width, t->output.height, + t->output.crop.pos.x, + t->output.crop.pos.y, + &t->set.o_off, &t->set.o_uoff, + &t->set.o_voff, &t->set.ostride); + + dev_dbg(t->dev, "o_off:0x%x, o_uoff:0x%x, ostride:%d.\n", + t->set.o_off, t->set.o_uoff, t->set.ostride); + + do_task(t); + } + + return; +done: + dev_err(t->dev, "ERR %s set_crop.\n", __func__); + t->state = ret; + return; +} + +static void get_res_do_task(struct ipu_task_entry *t) +{ + uint32_t found; + uint32_t split_child; + struct mutex *lock; + + found = get_vdoa_ipu_res(t); + if (!found) { + BUG(); + } else { + if (t->set.task & VDOA_ONLY) + do_task_vdoa_only(t); + else if ((IPU_PIX_FMT_TILED_NV12F == t->input.format) && + (t->set.mode & VDOA_BAND_MODE) && + (t->input.crop.w > soc_max_out_width())) + do_task_vdoa_vdi(t); else - ipu_select_buffer(ipu, t->set.ic_chan, IPU_INPUT_BUFFER, 0); + do_task(t); + put_vdoa_ipu_res(t, 0); + } + if (t->state != STATE_OK) { + dev_err(t->dev, "ERR:[0x%p] no-0x%x state: %s\n", + t, t->task_no, state_msg[t->state].msg); } - ret = wait_for_completion_timeout(&comp, msecs_to_jiffies(t->timeout)); - if (ret == 0) - t->state = STATE_IRQ_TIMEOUT; + split_child = need_split(t) && t->parent; + if (split_child) { + lock = &t->parent->split_lock; + mutex_lock(lock); + t->split_done = 1; + mutex_unlock(lock); + wake_up(&t->parent->split_waitq); + } - /* split mode and VDI mode */ - if (t->input.deinterlace.enable && - (t->task_no & (UP_STRIPE | DOWN_STRIPE))) - vdi_split_process(ipu, t); + return; +} - ipu_free_irq(ipu, irq, &comp); +static void wait_split_task_complete(struct ipu_task_entry *parent, + struct ipu_split_task *sp_task, uint32_t size) +{ + struct ipu_task_entry *tsk = NULL; + int ret = 0, rc; + int j, idx = -1; + unsigned long flags; + struct mutex *lock = &parent->split_lock; + int k, busy_vf, busy_pp; + struct ipu_soc *ipu; + DECLARE_PERF_VAR; + + for (j = 0; j < size; j++) { + rc = wait_event_timeout( + parent->split_waitq, + sp_task_check_done(sp_task, parent, size, &idx), + msecs_to_jiffies(parent->timeout - DEF_DELAY_MS)); + if (!rc) { + dev_err(parent->dev, + "ERR:[0x%p] no-0x%x, split_task timeout,j:%d," + "size:%d.\n", + parent, parent->task_no, j, size); + ret = -ETIMEDOUT; + goto out; + } else { + if (idx < 0) + BUG(); + tsk = sp_task[idx].child_task; + mutex_lock(lock); + if (!tsk->split_done || !tsk->ipu) + BUG(); + tsk->split_done = 0; + mutex_unlock(lock); + + dev_dbg(tsk->dev, + "[0x%p] no-0x%x sp_tsk[%d] done,state:%d.\n", + tsk, tsk->task_no, idx, tsk->state); + #ifdef DBG_IPU_PERF + CHECK_PERF(&tsk->ts_rel); + PRINT_TASK_STATISTICS; + #endif + } + } - if (only_ic(t->set.mode)) { - ipu_disable_channel(ipu, t->set.ic_chan, true); - if (deinterlace_3_field(t)) { - ipu_disable_channel(ipu, t->set.vdi_ic_p_chan, true); - ipu_disable_channel(ipu, t->set.vdi_ic_n_chan, true); +out: + if (ret == -ETIMEDOUT) { + /* debug */ + for (k = 0; k < MXC_IPU_MAX_NUM; k++) { + ipu = ipu_get_soc(k); + if (IS_ERR(ipu)) { + BUG(); + } else { + busy_vf = ic_vf_pp_is_busy(ipu, true); + busy_pp = ic_vf_pp_is_busy(ipu, false); + dev_err(parent->dev, + "ERR:ipu[%d] busy_vf:%d, busy_pp:%d.\n", + k, busy_vf, busy_pp); + } } - } else if (only_rot(t->set.mode)) - ipu_disable_channel(ipu, t->set.rot_chan, true); - else if (ic_and_rot(t->set.mode)) { - ipu_unlink_channels(ipu, t->set.ic_chan, t->set.rot_chan); - ipu_disable_channel(ipu, t->set.rot_chan, true); - ipu_disable_channel(ipu, t->set.ic_chan, true); - if (deinterlace_3_field(t)) { - ipu_disable_channel(ipu, t->set.vdi_ic_p_chan, true); - ipu_disable_channel(ipu, t->set.vdi_ic_n_chan, true); + for (k = 0; k < size; k++) { + tsk = sp_task[k].child_task; + if (!tsk) + continue; + dev_err(parent->dev, + "ERR: sp_task[%d][0x%p] no-0x%x done:%d," + "state:%s,on_list:%d, ipu:0x%p,timeout!\n", + k, tsk, tsk->task_no, tsk->split_done, + state_msg[tsk->state].msg, tsk->task_in_list, + tsk->ipu); } } -chan_done: - if (only_ic(t->set.mode)) - uninit_ic(ipu, t); - else if (only_rot(t->set.mode)) - uninit_rot(ipu, t); - else if (ic_and_rot(t->set.mode)) { - uninit_ic(ipu, t); - uninit_rot(ipu, t); + for (j = 0; j < size; j++) { + tsk = sp_task[j].child_task; + if (!tsk) + continue; + spin_lock_irqsave(&ipu_task_list_lock, flags); + if (tsk->task_in_list) { + list_del(&tsk->node); + tsk->task_in_list = 0; + dev_dbg(tsk->dev, + "[0x%p] no-0x%x,id:%d sp_tsk timeout list_del.\n", + tsk, tsk->task_no, tsk->task_id); + } + spin_unlock_irqrestore(&ipu_task_list_lock, flags); + if (!tsk->ipu) + continue; + if (STATE_IN_PROGRESS == tsk->state) { + do_task_release(tsk, 1); + put_vdoa_ipu_res(tsk, 0); + } + if (tsk->state != STATE_OK) { + dev_err(tsk->dev, + "ERR:[0x%p] no-0x%x,id:%d, sp_tsk state: %s\n", + tsk, tsk->task_no, tsk->task_id, + state_msg[tsk->state].msg); + } + kref_put(&tsk->refcount, task_mem_free); } + + kfree(parent->vditmpbuf[0]); + kfree(parent->vditmpbuf[1]); + + if (ret < 0) + parent->state = STATE_TIMEOUT; + else + parent->state = STATE_OK; return; } -static int thread_loop(struct ipu_soc *ipu, int id) +static inline int find_task(struct ipu_task_entry **t, int thread_id) { + int found; + unsigned long flags; struct ipu_task_entry *tsk; - struct list_head *task_list = &ipu->task_list[id]; - struct mutex *task_lock = &ipu->task_lock[id]; - int ret; - - while (!kthread_should_stop()) { - int found = 0; - - ret = wait_event_interruptible(ipu->waitq[id], !list_empty(task_list)); - if (0 != ret) - continue; + struct list_head *task_list = &ipu_task_list; + + *t = NULL; + spin_lock_irqsave(&ipu_task_list_lock, flags); + found = !list_empty(task_list); + if (found) { + tsk = list_first_entry(task_list, struct ipu_task_entry, node); + if (tsk->task_in_list) { + list_del(&tsk->node); + tsk->task_in_list = 0; + *t = tsk; + kref_get(&tsk->refcount); + dev_dbg(tsk->dev, + "thread_id:%d,[0x%p] task_no:0x%x,mode:0x%x list_del\n", + thread_id, tsk, tsk->task_no, tsk->set.mode); + } else + BUG(); + } + spin_unlock_irqrestore(&ipu_task_list_lock, flags); - mutex_lock(task_lock); + return found; +} - list_for_each_entry(tsk, task_list, node) { - if (tsk->priority == IPU_TASK_PRIORITY_HIGH) { - found = 1; - break; - } +static int ipu_task_thread(void *argv) +{ + struct ipu_task_entry *tsk; + struct ipu_task_entry *sp_tsk0; + struct ipu_split_task sp_task[4]; + /* priority lower than irq_thread */ + const struct sched_param param = { + .sched_priority = MAX_USER_RT_PRIO/2 - 1, + }; + int ret; + int curr_thread_id; + uint32_t size; + unsigned long flags; + unsigned int cpu; + struct cpumask cpu_mask; + struct ipu_thread_data *data = (struct ipu_thread_data *)argv; + + thread_id++; + curr_thread_id = thread_id; + sched_setscheduler(current, SCHED_FIFO, ¶m); + + if (!data->is_vdoa) { + cpu = cpumask_first(cpu_online_mask); + cpumask_set_cpu(cpu, &cpu_mask); + ret = sched_setaffinity(data->ipu->thread[data->id]->pid, + &cpu_mask); + if (ret < 0) { + pr_err("%s: sched_setaffinity fail:%d.\n", __func__, ret); + BUG(); } + pr_debug("%s: sched_setaffinity cpu:%d.\n", __func__, cpu); + } - if (!found) - tsk = list_first_entry(task_list, struct ipu_task_entry, node); + while (!kthread_should_stop()) { + int split_fail = 0; + int split_parent; + int split_child; + + wait_event(thread_waitq, find_task(&tsk, curr_thread_id)); + + if (!tsk) + BUG(); + + /* note: other threads run split child task */ + split_parent = need_split(tsk) && !tsk->parent; + split_child = need_split(tsk) && tsk->parent; + if (split_parent) { + if ((tsk->set.split_mode == RL_SPLIT) || + (tsk->set.split_mode == UD_SPLIT)) + size = 2; + else + size = 4; + ret = queue_split_task(tsk, sp_task, size); + if (ret < 0) { + split_fail = 1; + } else { + struct list_head *pos; + + spin_lock_irqsave(&ipu_task_list_lock, flags); + + sp_tsk0 = list_first_entry(&tsk->split_list, + struct ipu_task_entry, node); + list_del(&sp_tsk0->node); + + list_for_each(pos, &tsk->split_list) { + struct ipu_task_entry *tmp; + + tmp = list_entry(pos, + struct ipu_task_entry, node); + tmp->task_in_list = 1; + dev_dbg(tmp->dev, + "[0x%p] no-0x%x,id:%d sp_tsk " + "add_to_list.\n", tmp, + tmp->task_no, tmp->task_id); + } + /* add to global list */ + list_splice(&tsk->split_list, &ipu_task_list); + + spin_unlock_irqrestore(&ipu_task_list_lock, + flags); + /* let the parent thread do the first sp_task */ + /* FIXME: ensure the correct sequence for split + 4size: 5/6->9/a*/ + if (!sp_tsk0) + BUG(); + wake_up(&thread_waitq); + get_res_do_task(sp_tsk0); + dev_dbg(sp_tsk0->dev, + "thread:%d complete tsk no:0x%x.\n", + curr_thread_id, sp_tsk0->task_no); + ret = atomic_read(&req_cnt); + if (ret > 0) { + wake_up(&res_waitq); + dev_dbg(sp_tsk0->dev, + "sp_tsk0 sche thread:%d no:0x%x," + "req_cnt:%d\n", curr_thread_id, + sp_tsk0->task_no, ret); + /* For other threads to get_res */ + schedule(); + } + } + } else + get_res_do_task(tsk); - mutex_unlock(task_lock); + /* wait for all 4 sp_task finished here or timeout + and then release all resources */ + if (split_parent && !split_fail) + wait_split_task_complete(tsk, sp_task, size); - do_task(ipu, tsk); + if (!split_child) { + atomic_inc(&tsk->done); + wake_up(&tsk->task_waitq); + } - mutex_lock(task_lock); - list_del(&tsk->node); - mutex_unlock(task_lock); + dev_dbg(tsk->dev, "thread:%d complete tsk no:0x%x-[0x%p].\n", + curr_thread_id, tsk->task_no, tsk); + ret = atomic_read(&req_cnt); + if (ret > 0) { + wake_up(&res_waitq); + dev_dbg(tsk->dev, "sche thread:%d no:0x%x,req_cnt:%d\n", + curr_thread_id, tsk->task_no, ret); + /* note: give cpu to other threads to get_res */ + schedule(); + } - complete(&tsk->comp); + kref_put(&tsk->refcount, task_mem_free); } + pr_info("%s exit.\n", __func__); + BUG(); return 0; } -static int task_vf_thread(void *data) +int ipu_check_task(struct ipu_task *task) { - struct ipu_soc *ipu = data; + struct ipu_task_entry *tsk; + int ret = 0; + + tsk = create_task_entry(task); + if (IS_ERR(tsk)) + return PTR_ERR(tsk); - thread_loop(ipu, 0); + ret = check_task(tsk); - return 0; + task->input = tsk->input; + task->output = tsk->output; + task->overlay = tsk->overlay; + dump_task_info(tsk); + + kref_put(&tsk->refcount, task_mem_free); + if (ret != 0) + pr_debug("%s ret:%d.\n", __func__, ret); + return ret; } +EXPORT_SYMBOL_GPL(ipu_check_task); -static int task_pp_thread(void *data) +int ipu_queue_task(struct ipu_task *task) { - struct ipu_soc *ipu = data; + struct ipu_task_entry *tsk; + unsigned long flags; + int ret; + u32 tmp_task_no; + DECLARE_PERF_VAR; - thread_loop(ipu, 1); + tsk = create_task_entry(task); + if (IS_ERR(tsk)) + return PTR_ERR(tsk); - return 0; + CHECK_PERF(&tsk->ts_queue); + ret = prepare_task(tsk); + if (ret < 0) + goto done; + + if (need_split(tsk)) { + CHECK_PERF(&tsk->ts_dotask); + CHECK_PERF(&tsk->ts_waitirq); + CHECK_PERF(&tsk->ts_inirq); + CHECK_PERF(&tsk->ts_wakeup); + } + + /* task_no last four bits for split task type*/ + tmp_task_no = atomic_inc_return(&frame_no); + tsk->task_no = tmp_task_no << 4; + init_waitqueue_head(&tsk->task_waitq); + + spin_lock_irqsave(&ipu_task_list_lock, flags); + list_add_tail(&tsk->node, &ipu_task_list); + tsk->task_in_list = 1; + dev_dbg(tsk->dev, "[0x%p,no-0x%x] list_add_tail\n", tsk, tsk->task_no); + spin_unlock_irqrestore(&ipu_task_list_lock, flags); + wake_up(&thread_waitq); + + ret = wait_event_timeout(tsk->task_waitq, atomic_read(&tsk->done), + msecs_to_jiffies(tsk->timeout)); + if (0 == ret) { + /* note: the timeout should larger than the internal timeout!*/ + ret = -ETIMEDOUT; + dev_err(tsk->dev, "ERR: [0x%p] no-0x%x, timeout:%dms!\n", + tsk, tsk->task_no, tsk->timeout); + } else { + if (STATE_OK != tsk->state) { + dev_err(tsk->dev, "ERR: [0x%p] no-0x%x,state %d: %s\n", + tsk, tsk->task_no, tsk->state, + state_msg[tsk->state].msg); + ret = -ECANCELED; + } else + ret = 0; + } + + spin_lock_irqsave(&ipu_task_list_lock, flags); + if (tsk->task_in_list) { + list_del(&tsk->node); + tsk->task_in_list = 0; + dev_dbg(tsk->dev, "[0x%p] no:0x%x list_del\n", + tsk, tsk->task_no); + } + spin_unlock_irqrestore(&ipu_task_list_lock, flags); + +#ifdef DBG_IPU_PERF + CHECK_PERF(&tsk->ts_rel); + PRINT_TASK_STATISTICS; + if (ts_frame_avg == 0) + ts_frame_avg = ts_frame.tv_nsec / NSEC_PER_USEC + + ts_frame.tv_sec * USEC_PER_SEC; + else + ts_frame_avg = (ts_frame_avg + ts_frame.tv_nsec / NSEC_PER_USEC + + ts_frame.tv_sec * USEC_PER_SEC)/2; + if (timespec_compare(&ts_frame, &ts_frame_max) > 0) + ts_frame_max = ts_frame; + + atomic_inc(&frame_cnt); + + if ((atomic_read(&frame_cnt) % 1000) == 0) + pr_debug("ipu_dev: max frame time:%ldus, avg frame time:%dus," + "frame_cnt:%d\n", ts_frame_max.tv_nsec / NSEC_PER_USEC + + ts_frame_max.tv_sec * USEC_PER_SEC, + ts_frame_avg, atomic_read(&frame_cnt)); +#endif +done: + if (ret < 0) + dev_err(tsk->dev, "ERR: no-0x%x,ipu_queue_task err:%d\n", + tsk->task_no, ret); + + kref_put(&tsk->refcount, task_mem_free); + + return ret; } +EXPORT_SYMBOL_GPL(ipu_queue_task); static int mxc_ipu_open(struct inode *inode, struct file *file) { @@ -2165,8 +3284,9 @@ static long mxc_ipu_ioctl(struct file *file, kfree(mem); return -ENOMEM; } - + mutex_lock(&ipu_alloc_lock); list_add(&mem->list, &ipu_alloc_list); + mutex_unlock(&ipu_alloc_lock); dev_dbg(ipu_dev, "allocated %d bytes @ 0x%08X\n", mem->size, mem->phy_addr); @@ -2185,6 +3305,7 @@ static long mxc_ipu_ioctl(struct file *file, return -EFAULT; ret = -EINVAL; + mutex_lock(&ipu_alloc_lock); list_for_each_entry(mem, &ipu_alloc_list, list) { if (mem->phy_addr == offset) { list_del(&mem->list); @@ -2197,6 +3318,10 @@ static long mxc_ipu_ioctl(struct file *file, break; } } + mutex_unlock(&ipu_alloc_lock); + if (0 == ret) + dev_dbg(ipu_dev, "free %d bytes @ 0x%08X\n", + mem->size, mem->phy_addr); break; } @@ -2213,6 +3338,7 @@ static int mxc_ipu_mmap(struct file *file, struct vm_area_struct *vma) unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; struct ipu_alloc_list *mem; + mutex_lock(&ipu_alloc_lock); list_for_each_entry(mem, &ipu_alloc_list, list) { if (offset == mem->phy_addr) { found = true; @@ -2220,6 +3346,7 @@ static int mxc_ipu_mmap(struct file *file, struct vm_area_struct *vma) break; } } + mutex_unlock(&ipu_alloc_lock); if (!found) return -EINVAL; @@ -2253,7 +3380,9 @@ static struct file_operations mxc_ipu_fops = { int register_ipu_device(struct ipu_soc *ipu, int id) { - int i, ret = 0; + int ret = 0; + static int idx; + static struct ipu_thread_data thread_data[5]; if (!major) { major = register_chrdev(0, "mxc_ipu", &mxc_ipu_fops); @@ -2278,30 +3407,33 @@ int register_ipu_device(struct ipu_soc *ipu, int id) ipu_dev->dma_mask = kmalloc(sizeof(*ipu_dev->dma_mask), GFP_KERNEL); *ipu_dev->dma_mask = DMA_BIT_MASK(32); ipu_dev->coherent_dma_mask = DMA_BIT_MASK(32); - } - for (i = 0; i < 2; i++) { - INIT_LIST_HEAD(&ipu->task_list[i]); - init_waitqueue_head(&ipu->waitq[i]); - mutex_init(&ipu->task_lock[i]); - - ipu->rot_dma[i].size = 0; + mutex_init(&ipu_ch_tbl.lock); } + ipu->rot_dma[0].size = 0; + ipu->rot_dma[1].size = 0; - ipu->thread[0] = kthread_run(task_vf_thread, ipu, - "ipu%d_process-vf", id); + thread_data[idx].ipu = ipu; + thread_data[idx].id = 0; + thread_data[idx].is_vdoa = 0; + ipu->thread[0] = kthread_run(ipu_task_thread, &thread_data[idx++], + "ipu%d_task", id); if (IS_ERR(ipu->thread[0])) { ret = PTR_ERR(ipu->thread[0]); goto kthread0_fail; } - ipu->thread[1] = kthread_run(task_pp_thread, ipu, - "ipu%d_process-pp", id); + thread_data[idx].ipu = ipu; + thread_data[idx].id = 1; + thread_data[idx].is_vdoa = 0; + ipu->thread[1] = kthread_run(ipu_task_thread, &thread_data[idx++], + "ipu%d_task", id); if (IS_ERR(ipu->thread[1])) { ret = PTR_ERR(ipu->thread[1]); goto kthread1_fail; } + return ret; kthread1_fail: @@ -2312,7 +3444,6 @@ kthread0_fail: dev_create_fail: if (id == 0) { class_destroy(ipu_class); - unregister_chrdev(major, "mxc_ipu"); } ipu_class_fail: if (id == 0) @@ -2327,7 +3458,6 @@ void unregister_ipu_device(struct ipu_soc *ipu, int id) kthread_stop(ipu->thread[0]); kthread_stop(ipu->thread[1]); - for (i = 0; i < 2; i++) { if (ipu->rot_dma[i].vaddr) dma_free_coherent(ipu_dev, diff --git a/drivers/mxc/ipu3/ipu_disp.c b/drivers/mxc/ipu3/ipu_disp.c index a34d0601f2bf..229545c9cb34 100644 --- a/drivers/mxc/ipu3/ipu_disp.c +++ b/drivers/mxc/ipu3/ipu_disp.c @@ -1292,8 +1292,8 @@ int32_t ipu_init_sync_panel(struct ipu_soc *ipu, int disp, uint32_t pixel_clk, /*clear DI*/ di_gen = ipu_di_read(ipu, disp, DI_GENERAL); - ipu_di_write(ipu, disp, - di_gen & (0x3 << 20), DI_GENERAL); + di_gen &= (0x3 << 20); + ipu_di_write(ipu, disp, di_gen, DI_GENERAL); if (sig.interlaced) { if (g_ipu_hw_rev >= 2) { diff --git a/drivers/mxc/ipu3/ipu_ic.c b/drivers/mxc/ipu3/ipu_ic.c index e93d2a36ec86..e21d6134c84a 100644 --- a/drivers/mxc/ipu3/ipu_ic.c +++ b/drivers/mxc/ipu3/ipu_ic.c @@ -1,5 +1,5 @@ /* - * Copyright 2005-2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved. */ /* @@ -97,6 +97,9 @@ void _ipu_ic_enable_task(struct ipu_soc *ipu, ipu_channel_t channel) case MEM_VDI_PRP_VF_MEM: ic_conf |= IC_CONF_PRPVF_EN; break; + case MEM_VDI_MEM: + ic_conf |= IC_CONF_PRPVF_EN | IC_CONF_RWS_EN ; + break; case MEM_ROT_VF_MEM: ic_conf |= IC_CONF_PRPVF_ROT_EN; break; @@ -132,6 +135,9 @@ void _ipu_ic_disable_task(struct ipu_soc *ipu, ipu_channel_t channel) case MEM_VDI_PRP_VF_MEM: ic_conf &= ~IC_CONF_PRPVF_EN; break; + case MEM_VDI_MEM: + ic_conf &= ~(IC_CONF_PRPVF_EN | IC_CONF_RWS_EN); + break; case MEM_ROT_VF_MEM: ic_conf &= ~IC_CONF_PRPVF_ROT_EN; break; @@ -158,6 +164,7 @@ void _ipu_vdi_init(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel_param { uint32_t reg; uint32_t pixel_fmt; + uint32_t pix_per_burst; reg = ((params->mem_prp_vf_mem.in_height-1) << 16) | (params->mem_prp_vf_mem.in_width-1); @@ -168,10 +175,13 @@ void _ipu_vdi_init(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel_param if (params->mem_prp_vf_mem.in_pixel_fmt == IPU_PIX_FMT_UYVY || params->mem_prp_vf_mem.in_pixel_fmt == - IPU_PIX_FMT_YUYV) + IPU_PIX_FMT_YUYV) { pixel_fmt = VDI_C_CH_422; - else + pix_per_burst = 32; + } else { pixel_fmt = VDI_C_CH_420; + pix_per_burst = 64; + } reg = ipu_vdi_read(ipu, VDI_C); reg |= pixel_fmt; @@ -185,6 +195,21 @@ void _ipu_vdi_init(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel_param case MEM_VDI_PRP_VF_MEM_N: reg |= VDI_C_BURST_SIZE3_4 | VDI_C_VWM3_SET_1 | VDI_C_VWM3_CLR_2; break; + + case MEM_VDI_MEM: + reg |= (((pix_per_burst >> 2) - 1) & VDI_C_BURST_SIZE_MASK) + << VDI_C_BURST_SIZE2_OFFSET; + break; + case MEM_VDI_MEM_P: + reg |= (((pix_per_burst >> 2) - 1) & VDI_C_BURST_SIZE_MASK) + << VDI_C_BURST_SIZE1_OFFSET; + reg |= VDI_C_VWM1_SET_2 | VDI_C_VWM1_CLR_2; + break; + case MEM_VDI_MEM_N: + reg |= (((pix_per_burst >> 2) - 1) & VDI_C_BURST_SIZE_MASK) + << VDI_C_BURST_SIZE3_OFFSET; + reg |= VDI_C_VWM3_SET_2 | VDI_C_VWM3_CLR_2; + break; default: break; } @@ -644,12 +669,16 @@ int _ipu_ic_idma_init(struct ipu_soc *ipu, int dma_chan, ic_idmac_1 |= IC_IDMAC_1_CB4_BURST_16; else ic_idmac_1 &= ~IC_IDMAC_1_CB4_BURST_16; + } else if (dma_chan == 5) { /* VDIC OUTPUT - CB7 */ + if (burst_size == 16) + ic_idmac_1 |= IC_IDMAC_1_CB7_BURST_16; + else + ic_idmac_1 &= ~IC_IDMAC_1_CB7_BURST_16; } ipu_ic_write(ipu, ic_idmac_1, IC_IDMAC_1); ipu_ic_write(ipu, ic_idmac_2, IC_IDMAC_2); ipu_ic_write(ipu, ic_idmac_3, IC_IDMAC_3); - return 0; } diff --git a/drivers/mxc/ipu3/ipu_param_mem.h b/drivers/mxc/ipu3/ipu_param_mem.h index 223a1cdf5fa4..8ae0a5edd269 100644 --- a/drivers/mxc/ipu3/ipu_param_mem.h +++ b/drivers/mxc/ipu3/ipu_param_mem.h @@ -1,5 +1,5 @@ /* - * Copyright 2005-2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved. */ /* @@ -194,8 +194,20 @@ static inline void _ipu_ch_param_dump(struct ipu_soc *ipu, int ch) ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 125, 13)); dev_dbg(ipu->dev, "FH %d, ", ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 138, 12)); + dev_dbg(ipu->dev, "EBA0 0x%x\n", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 0, 29) << 3); + dev_dbg(ipu->dev, "EBA1 0x%x\n", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 29, 29) << 3); dev_dbg(ipu->dev, "Stride %d\n", ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 102, 14)); + dev_dbg(ipu->dev, "scan_order %d\n", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 113, 1)); + dev_dbg(ipu->dev, "uv_stride %d\n", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 128, 14)); + dev_dbg(ipu->dev, "u_offset 0x%x\n", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 46, 22) << 3); + dev_dbg(ipu->dev, "v_offset 0x%x\n", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 68, 22) << 3); dev_dbg(ipu->dev, "Width0 %d+1, ", ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 116, 3)); @@ -246,10 +258,11 @@ static inline void _ipu_ch_param_init(struct ipu_soc *ipu, int ch, ipu_ch_param_set_field(¶ms, 0, 125, 13, width - 1); - if ((ch == 8) || (ch == 9) || (ch == 10)) { + if (((ch == 8) || (ch == 9) || (ch == 10)) && !ipu->vdoa_en) { ipu_ch_param_set_field(¶ms, 0, 138, 12, (height / 2) - 1); ipu_ch_param_set_field(¶ms, 1, 102, 14, (stride * 2) - 1); } else { + /* note: for vdoa+vdi- ch8/9/10, always use band mode */ ipu_ch_param_set_field(¶ms, 0, 138, 12, height - 1); ipu_ch_param_set_field(¶ms, 1, 102, 14, stride - 1); } @@ -340,7 +353,11 @@ static inline void _ipu_ch_param_init(struct ipu_soc *ipu, int ch, ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */ ipu_ch_param_set_field(¶ms, 1, 85, 4, 0x8); /* pix format */ if ((ch == 8) || (ch == 9) || (ch == 10)) { - ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + if (ipu->vdoa_en) { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); + } else { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); + } } else { ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ } @@ -404,8 +421,14 @@ static inline void _ipu_ch_param_init(struct ipu_soc *ipu, int ch, uv_stride = stride; u_offset = (u == 0) ? stride * height : u; if ((ch == 8) || (ch == 9) || (ch == 10)) { - ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ - uv_stride = uv_stride*2; + if (ipu->vdoa_en) { + /* one field buffer, memory width 64bits */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 63); + } else { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); + /* top/bottom field in one buffer*/ + uv_stride = uv_stride*2; + } } else { ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ } @@ -819,4 +842,20 @@ static inline void _ipu_ch_params_set_alpha_width(struct ipu_soc *ipu, uint32_t ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 125, 3, alpha_width - 1); }; +static inline void _ipu_ch_param_set_bandmode(struct ipu_soc *ipu, + uint32_t ch, uint32_t band_height) +{ + int32_t sub_ch = 0; + + ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, ch), + 0, 114, 3, band_height - 1); + sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch); + if (sub_ch <= 0) + return; + ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, sub_ch), + 0, 114, 3, band_height - 1); + + dev_dbg(ipu->dev, "BNDM 0x%x, ", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 114, 3)); +} #endif diff --git a/drivers/mxc/ipu3/ipu_prv.h b/drivers/mxc/ipu3/ipu_prv.h index 68cb87a910fa..526a2c0a36f0 100644 --- a/drivers/mxc/ipu3/ipu_prv.h +++ b/drivers/mxc/ipu3/ipu_prv.h @@ -1,5 +1,5 @@ /* - * Copyright 2005-2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved. */ /* @@ -128,16 +128,15 @@ struct ipu_soc { u32 idma_enable_reg[2]; u32 buf_ready_reg[10]; - /*ipu processing driver*/ - struct list_head task_list[2]; - struct mutex task_lock[2]; - wait_queue_head_t waitq[2]; - struct task_struct *thread[2]; struct rot_mem { void *vaddr; dma_addr_t paddr; int size; } rot_dma[2]; + + int vdoa_en; + struct task_struct *thread[2]; + }; struct ipu_channel { diff --git a/drivers/mxc/ipu3/ipu_regs.h b/drivers/mxc/ipu3/ipu_regs.h index c63c93231135..c06ac9ff8486 100644 --- a/drivers/mxc/ipu3/ipu_regs.h +++ b/drivers/mxc/ipu3/ipu_regs.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved. */ /* @@ -368,12 +368,17 @@ enum { FS_PP_ROT_SRC_SEL_MASK = 0x000F0000, FS_PP_ROT_SRC_SEL_OFFSET = 16, FS_PP_SRC_SEL_MASK = 0x0000F000, + FS_PP_SRC_SEL_VDOA = 0x00008000, FS_PP_SRC_SEL_OFFSET = 12, FS_PRP_SRC_SEL_MASK = 0x0F000000, FS_PRP_SRC_SEL_OFFSET = 24, FS_VF_IN_VALID = 0x80000000, FS_ENC_IN_VALID = 0x40000000, FS_VDI_SRC_SEL_MASK = 0x30000000, + FS_VDI_SRC_SEL_VDOA = 0x20000000, + FS_VDOA_DEST_SEL_MASK = 0x00030000, + FS_VDOA_DEST_SEL_VDI = 0x00020000, + FS_VDOA_DEST_SEL_IC = 0x00010000, FS_VDI_SRC_SEL_OFFSET = 28, @@ -659,9 +664,15 @@ enum { VDI_C_BURST_SIZE1_4 = 0x00000030, VDI_C_BURST_SIZE2_4 = 0x00000300, VDI_C_BURST_SIZE3_4 = 0x00003000, + VDI_C_BURST_SIZE_MASK = 0xF, + VDI_C_BURST_SIZE1_OFFSET = 4, + VDI_C_BURST_SIZE2_OFFSET = 8, + VDI_C_BURST_SIZE3_OFFSET = 12, VDI_C_VWM1_SET_1 = 0x00000000, + VDI_C_VWM1_SET_2 = 0x00010000, VDI_C_VWM1_CLR_2 = 0x00080000, VDI_C_VWM3_SET_1 = 0x00000000, + VDI_C_VWM3_SET_2 = 0x00400000, VDI_C_VWM3_CLR_2 = 0x02000000, VDI_C_TOP_FIELD_MAN_1 = 0x40000000, VDI_C_TOP_FIELD_AUTO_1 = 0x80000000, diff --git a/drivers/mxc/ipu3/vdoa.c b/drivers/mxc/ipu3/vdoa.c new file mode 100644 index 000000000000..1a5266d7d7a8 --- /dev/null +++ b/drivers/mxc/ipu3/vdoa.c @@ -0,0 +1,541 @@ +/* + * Copyright (C) 2012 Freescale Semiconductor, 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 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include <linux/types.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/ipu.h> +#include <linux/iram_alloc.h> + +#include "vdoa.h" +/* FIXME: use cmdline to specify the iram size */ +/* 6band(3field* double buffer) * (width*2) * bandline(8) + = 6x1024x2x8 = 96k or 72k(1.5byte) */ +#define VDOA_IRAM_SIZE (1024*96) + +#define VDOAC_BAND_HEIGHT_32LINES (32) +#define VDOAC_BAND_HEIGHT_16LINES (16) +#define VDOAC_BAND_HEIGHT_8LINES (8) +#define VDOAC_THREE_FRAMES (0x1 << 2) +#define VDOAC_SYNC_BAND_MODE (0x1 << 3) +#define VDOAC_SCAN_ORDER_INTERLACED (0x1 << 4) +#define VDOAC_PFS_YUYV (0x1 << 5) +#define VDOAC_IPU_SEL_1 (0x1 << 6) +#define VDOAFP_FH_MASK (0x1FFF) +#define VDOAFP_FH_SHIFT (16) +#define VDOAFP_FW_MASK (0x3FFF) +#define VDOAFP_FW_SHIFT (0) +#define VDOASL_VSLY_MASK (0x3FFF) +#define VDOASL_VSLY_SHIFT (16) +#define VDOASL_ISLY_MASK (0x7FFF) +#define VDOASL_ISLY_SHIFT (0) +#define VDOASRR_START_XFER (0x2) +#define VDOASRR_SWRST (0x1) +#define VDOAIEIST_TRANSFER_ERR (0x2) +#define VDOAIEIST_TRANSFER_END (0x1) + +#define VDOAC (0x0) /* Control Register */ +#define VDOASRR (0x4) /* Start and Reset Register */ +#define VDOAIE (0x8) /* Interrupt Enable Register */ +#define VDOAIST (0xc) /* Interrupt Status Register */ +#define VDOAFP (0x10) /* Frame Parameters Register */ +#define VDOAIEBA00 (0x14) /* External Buffer n Frame m Address Register */ +#define VDOAIEBA01 (0x18) /* External Buffer n Frame m Address Register */ +#define VDOAIEBA02 (0x1c) /* External Buffer n Frame m Address Register */ +#define VDOAIEBA10 (0x20) /* External Buffer n Frame m Address Register */ +#define VDOAIEBA11 (0x24) /* External Buffer n Frame m Address Register */ +#define VDOAIEBA12 (0x28) /* External Buffer n Frame m Address Register */ +#define VDOASL (0x2c) /* IPU Stride Line Register */ +#define VDOAIUBO (0x30) /* IPU Chroma Buffer Offset Register */ +#define VDOAVEBA0 (0x34) /* External Buffer m Address Register */ +#define VDOAVEBA1 (0x38) /* External Buffer m Address Register */ +#define VDOAVEBA2 (0x3c) /* External Buffer m Address Register */ +#define VDOAVUBO (0x40) /* VPU Chroma Buffer Offset */ +#define VDOASR (0x44) /* Status Register */ +#define VDOATD (0x48) /* Test Debug Register */ + + +enum { + VDOA_INIT = 0x1, + VDOA_GET = 0x2, + VDOA_SETUP = 0x4, + VDOA_GET_OBUF = 0x8, + VDOA_START = 0x10, + VDOA_INIRQ = 0x20, + VDOA_STOP = 0x40, + VDOA_PUT = VDOA_INIT, +}; + +enum { + VDOA_NULL = 0, + VDOA_FRAME = 1, + VDOA_PREV_FIELD = 2, + VDOA_CURR_FIELD = 3, + VDOA_NEXT_FIELD = 4, +}; + +#define CHECK_STATE(expect, retcode) \ +do { \ + if (!((expect) & vdoa->state)) { \ + dev_err(vdoa->dev, "ERR: %s state:0x%x, expect:0x%x.\n",\ + __func__, vdoa->state, (expect)); \ + retcode; \ + } \ +} while (0) + +#define CHECK_NULL_PTR(ptr) \ +do { \ + pr_debug("vdoa_ptr:0x%p in %s state:0x%x.\n", \ + vdoa, __func__, vdoa->state); \ + if (NULL == (ptr)) { \ + pr_err("ERR vdoa: %s state:0x%x null ptr.\n", \ + __func__, vdoa->state); \ + } \ +} while (0) + +struct vdoa_info { + int state; + struct device *dev; + struct clk *clk; + void __iomem *reg_base; + void __iomem *iram_base; + unsigned long iram_paddr; + int irq; + int field; + struct completion comp; +}; + +static struct vdoa_info *g_vdoa; +static DEFINE_MUTEX(vdoa_lock); + +static inline void vdoa_read_register(struct vdoa_info *vdoa, + u32 reg, u32 *val) +{ + *val = ioread32(vdoa->reg_base + reg); + dev_dbg(vdoa->dev, "read_reg:0x%02x, val:0x%08x.\n", reg, *val); +} + +static inline void vdoa_write_register(struct vdoa_info *vdoa, + u32 reg, u32 val) +{ + iowrite32(val, vdoa->reg_base + reg); + dev_dbg(vdoa->dev, "\t\twrite_reg:0x%02x, val:0x%08x.\n", reg, val); +} + +static void dump_registers(struct vdoa_info *vdoa) +{ + int i; + u32 data; + + for (i = VDOAC; i < VDOATD; i += 4) + vdoa_read_register(vdoa, i, &data); +} + +void vdoa_setup(vdoa_handle_t handle, struct vdoa_params *params) +{ + int band_size; + int ipu_stride; + u32 data; + struct vdoa_info *vdoa = (struct vdoa_info *)handle; + + CHECK_NULL_PTR(vdoa); + CHECK_STATE(VDOA_GET | VDOA_GET_OBUF | VDOA_STOP, return); + if (VDOA_GET == vdoa->state) { + dev_dbg(vdoa->dev, "w:%d, h:%d.\n", + params->width, params->height); + data = (params->band_lines == VDOAC_BAND_HEIGHT_32LINES) ? 2 : + ((params->band_lines == VDOAC_BAND_HEIGHT_16LINES) ? + 1 : 0); + data |= params->scan_order ? VDOAC_SCAN_ORDER_INTERLACED : 0; + data |= params->band_mode ? VDOAC_SYNC_BAND_MODE : 0; + data |= params->pfs ? VDOAC_PFS_YUYV : 0; + data |= params->ipu_num ? VDOAC_IPU_SEL_1 : 0; + vdoa_write_register(vdoa, VDOAC, data); + + data = ((params->width & VDOAFP_FW_MASK) << VDOAFP_FW_SHIFT) | + ((params->height & VDOAFP_FH_MASK) << VDOAFP_FH_SHIFT); + vdoa_write_register(vdoa, VDOAFP, data); + + ipu_stride = params->pfs ? params->width << 1 : params->width; + data = ((params->vpu_stride & VDOASL_VSLY_MASK) << + VDOASL_VSLY_SHIFT) | + ((ipu_stride & VDOASL_ISLY_MASK) << VDOASL_ISLY_SHIFT); + vdoa_write_register(vdoa, VDOASL, data); + + dev_dbg(vdoa->dev, "band_mode:%d, band_line:%d, base:0x%lx.\n", + params->band_mode, params->band_lines, vdoa->iram_paddr); + } + /* + * band size = (luma_per_line + chroma_per_line) * bandLines + * = width * (3/2 or 2) * bandLines + * double buffer mode used. + */ + if (params->pfs) + band_size = (params->width << 1) * params->band_lines; + else + band_size = ((params->width * 3) >> 1) * + params->band_lines; + if (params->interlaced) { + if (params->vfield_buf.prev_veba) { + if (params->band_mode) { + vdoa_write_register(vdoa, VDOAIEBA00, + vdoa->iram_paddr); + vdoa_write_register(vdoa, VDOAIEBA10, + vdoa->iram_paddr + band_size); + } else + vdoa_write_register(vdoa, VDOAIEBA00, + params->ieba0); + vdoa_write_register(vdoa, VDOAVEBA0, + params->vfield_buf.prev_veba); + vdoa->field = VDOA_PREV_FIELD; + } + if (params->vfield_buf.cur_veba) { + if (params->band_mode) { + vdoa_write_register(vdoa, VDOAIEBA01, + vdoa->iram_paddr + band_size * 2); + vdoa_write_register(vdoa, VDOAIEBA11, + vdoa->iram_paddr + band_size * 3); + } else + vdoa_write_register(vdoa, VDOAIEBA01, + params->ieba1); + vdoa_write_register(vdoa, VDOAVEBA1, + params->vfield_buf.cur_veba); + vdoa->field = VDOA_CURR_FIELD; + } + if (params->vfield_buf.next_veba) { + if (params->band_mode) { + vdoa_write_register(vdoa, VDOAIEBA02, + vdoa->iram_paddr + band_size * 4); + vdoa_write_register(vdoa, VDOAIEBA12, + vdoa->iram_paddr + band_size * 5); + } else + vdoa_write_register(vdoa, VDOAIEBA02, + params->ieba2); + vdoa_write_register(vdoa, VDOAVEBA2, + params->vfield_buf.next_veba); + vdoa->field = VDOA_NEXT_FIELD; + vdoa_read_register(vdoa, VDOAC, &data); + data |= VDOAC_THREE_FRAMES; + vdoa_write_register(vdoa, VDOAC, data); + } + + if (!params->pfs) + vdoa_write_register(vdoa, VDOAIUBO, + params->width * params->band_lines); + vdoa_write_register(vdoa, VDOAVUBO, + params->vfield_buf.vubo); + dev_dbg(vdoa->dev, "total band_size:0x%x.\n", band_size*6); + } else if (params->band_mode) { + /* used for progressive frame resize on PrP channel */ + BUG(); /* currently not support */ + /* progressvie frame: band mode */ + vdoa_write_register(vdoa, VDOAIEBA00, vdoa->iram_paddr); + vdoa_write_register(vdoa, VDOAIEBA10, + vdoa->iram_paddr + band_size); + if (!params->pfs) + vdoa_write_register(vdoa, VDOAIUBO, + params->width * params->band_lines); + dev_dbg(vdoa->dev, "total band_size:0x%x\n", band_size*2); + } else { + /* progressive frame: mem->mem, non-band mode */ + vdoa->field = VDOA_FRAME; + vdoa_write_register(vdoa, VDOAVEBA0, params->vframe_buf.veba); + vdoa_write_register(vdoa, VDOAVUBO, params->vframe_buf.vubo); + vdoa_write_register(vdoa, VDOAIEBA00, params->ieba0); + if (!params->pfs) + /* note: iubo is relative value, based on ieba0 */ + vdoa_write_register(vdoa, VDOAIUBO, + params->width * params->height); + } + vdoa->state = VDOA_SETUP; +} + +void vdoa_get_output_buf(vdoa_handle_t handle, struct vdoa_ipu_buf *buf) +{ + u32 data; + struct vdoa_info *vdoa = (struct vdoa_info *)handle; + + CHECK_NULL_PTR(vdoa); + CHECK_STATE(VDOA_SETUP, return); + vdoa->state = VDOA_GET_OBUF; + memset(buf, 0, sizeof(*buf)); + + vdoa_read_register(vdoa, VDOAC, &data); + switch (vdoa->field) { + case VDOA_FRAME: + case VDOA_PREV_FIELD: + vdoa_read_register(vdoa, VDOAIEBA00, &buf->ieba0); + if (data & VDOAC_SYNC_BAND_MODE) + vdoa_read_register(vdoa, VDOAIEBA10, &buf->ieba1); + break; + case VDOA_CURR_FIELD: + vdoa_read_register(vdoa, VDOAIEBA01, &buf->ieba0); + vdoa_read_register(vdoa, VDOAIEBA11, &buf->ieba1); + break; + case VDOA_NEXT_FIELD: + vdoa_read_register(vdoa, VDOAIEBA02, &buf->ieba0); + vdoa_read_register(vdoa, VDOAIEBA12, &buf->ieba1); + break; + default: + BUG(); + break; + } + if (!(data & VDOAC_PFS_YUYV)) + vdoa_read_register(vdoa, VDOAIUBO, &buf->iubo); +} + +int vdoa_start(vdoa_handle_t handle, int timeout_ms) +{ + int ret; + struct vdoa_info *vdoa = (struct vdoa_info *)handle; + + CHECK_NULL_PTR(vdoa); + CHECK_STATE(VDOA_GET_OBUF, return -EINVAL); + init_completion(&vdoa->comp); + vdoa_write_register(vdoa, VDOAIST, + VDOAIEIST_TRANSFER_ERR | VDOAIEIST_TRANSFER_END); + vdoa_write_register(vdoa, VDOAIE, + VDOAIEIST_TRANSFER_ERR | VDOAIEIST_TRANSFER_END); + + enable_irq(vdoa->irq); + vdoa_write_register(vdoa, VDOASRR, VDOASRR_START_XFER); + dump_registers(vdoa); + + vdoa->state = VDOA_START; + ret = wait_for_completion_timeout(&vdoa->comp, + msecs_to_jiffies(timeout_ms)); + + return ret > 0 ? 0 : -ETIMEDOUT; +} + +void vdoa_stop(vdoa_handle_t handle) +{ + struct vdoa_info *vdoa = (struct vdoa_info *)handle; + + CHECK_NULL_PTR(vdoa); + CHECK_STATE(VDOA_START | VDOA_INIRQ, return); + vdoa->state = VDOA_STOP; + + disable_irq(vdoa->irq); + + vdoa_write_register(vdoa, VDOASRR, VDOASRR_SWRST); +} + +void vdoa_get_handle(vdoa_handle_t *handle) +{ + struct vdoa_info *vdoa = g_vdoa; + + CHECK_NULL_PTR(handle); + *handle = (vdoa_handle_t *)NULL; + CHECK_STATE(VDOA_INIT, return); + mutex_lock(&vdoa_lock); + clk_enable(vdoa->clk); + vdoa->state = VDOA_GET; + vdoa->field = VDOA_NULL; + vdoa_write_register(vdoa, VDOASRR, VDOASRR_SWRST); + + *handle = (vdoa_handle_t *)vdoa; +} + +void vdoa_put_handle(vdoa_handle_t *handle) +{ + struct vdoa_info *vdoa = (struct vdoa_info *)(*handle); + + CHECK_NULL_PTR(vdoa); + CHECK_STATE(VDOA_STOP, return); + if (vdoa != g_vdoa) + BUG(); + + clk_disable(vdoa->clk); + vdoa->state = VDOA_PUT; + *handle = (vdoa_handle_t *)NULL; + mutex_unlock(&vdoa_lock); +} + +static irqreturn_t vdoa_irq_handler(int irq, void *data) +{ + u32 status, mask, val; + struct vdoa_info *vdoa = data; + + CHECK_NULL_PTR(vdoa); + CHECK_STATE(VDOA_START, return IRQ_HANDLED); + vdoa->state = VDOA_INIRQ; + vdoa_read_register(vdoa, VDOAIST, &status); + vdoa_read_register(vdoa, VDOAIE, &mask); + val = status & mask; + vdoa_write_register(vdoa, VDOAIST, val); + if (VDOAIEIST_TRANSFER_ERR & val) + dev_err(vdoa->dev, "vdoa Transfer err irq!\n"); + if (VDOAIEIST_TRANSFER_END & val) + dev_dbg(vdoa->dev, "vdoa Transfer end irq!\n"); + if (0 == val) { + dev_err(vdoa->dev, "vdoa unknown irq!\n"); + BUG(); + } + + complete(&vdoa->comp); + return IRQ_HANDLED; +} + +static int vdoa_probe(struct platform_device *pdev) +{ + int ret; + struct vdoa_info *vdoa; + struct resource *res; + struct resource *res_irq; + struct device *dev; + char clk[] = "vdoa"; + + vdoa = kzalloc(sizeof(struct vdoa_info), GFP_KERNEL); + if (!vdoa) { + ret = -ENOMEM; + goto alloc_failed; + } + vdoa->dev = dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "get IORESOURCE_MEM error\n"); + ret = -ENODEV; + goto res_mem_failed; + } + + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (!res) { + dev_err(dev, "request mem region error\n"); + ret = -EBUSY; + goto req_mem_region; + } + vdoa->reg_base = ioremap(res->start, resource_size(res)); + if (!vdoa->reg_base) { + dev_err(dev, "map vdoa registers error\n"); + ret = -EIO; + goto err_ioremap; + } + + res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res_irq) { + dev_err(dev, "failed to get irq resource\n"); + ret = -ENODEV; + goto err_get_irq; + } + vdoa->irq = res_irq->start; + ret = request_irq(vdoa->irq, vdoa_irq_handler, 0, "vdoa", vdoa); + if (ret) { + dev_err(dev, "request vdoa interrupt failed\n"); + ret = -EBUSY; + goto err_req_irq; + } + disable_irq(vdoa->irq); + + vdoa->clk = clk_get(dev, clk); + if (IS_ERR(vdoa->clk)) { + dev_err(dev, "failed to get vdoa_clk\n"); + ret = PTR_ERR(vdoa->clk); + goto err_clk; + } + clk_enable(vdoa->clk); + + vdoa->iram_base = iram_alloc(VDOA_IRAM_SIZE, &vdoa->iram_paddr); + if (!vdoa->iram_base) { + dev_err(dev, "failed to get iram memory:0x%x\n", + VDOA_IRAM_SIZE); + ret = -ENOMEM; + goto err_iram_alloc; + } + dev_dbg(dev, "iram_base:0x%p,iram_paddr:0x%lx,size:0x%x\n", + vdoa->iram_base, vdoa->iram_paddr, VDOA_IRAM_SIZE); + + vdoa->state = VDOA_INIT; + dev_set_drvdata(dev, vdoa); + g_vdoa = vdoa; + dev_info(dev, "i.MX Video Data Order Adapter(VDOA) driver probed\n"); + return 0; + +err_iram_alloc: + clk_put(vdoa->clk); +err_clk: +err_req_irq: +err_get_irq: + iounmap(vdoa->reg_base); +err_ioremap: + release_mem_region(res->start, resource_size(res)); +req_mem_region: +res_mem_failed: + kfree(vdoa); +alloc_failed: + return ret; +} + +static int __devexit vdoa_remove(struct platform_device *pdev) +{ + int ret = 0; + struct resource *res; + struct vdoa_info *vdoa = dev_get_drvdata(&pdev->dev); + + clk_put(vdoa->clk); + clk_disable(vdoa->clk); + iram_free(vdoa->iram_paddr, VDOA_IRAM_SIZE); + iounmap(vdoa->reg_base); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "get IORESOURCE_MEM error\n"); + ret = -ENODEV; + goto res_mem_failed; + } + release_mem_region(res->start, resource_size(res)); + kfree(vdoa); + dev_set_drvdata(&pdev->dev, NULL); + +res_mem_failed: + return ret; +} + +static struct platform_driver vdoa_driver = { + .driver = { + .name = "mxc_vdoa", + }, + .probe = vdoa_probe, + .remove = __devexit_p(vdoa_remove), +}; + +static int __init vdoa_init(void) +{ + int err; + + err = platform_driver_register(&vdoa_driver); + if (err) { + pr_err("vdoa_driver register failed\n"); + return -ENODEV; + } + return 0; +} + +static void __exit vdoa_cleanup(void) +{ + platform_driver_unregister(&vdoa_driver); +} + +module_init(vdoa_init); +module_exit(vdoa_cleanup); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("i.MX Video Data Order Adapter(VDOA) driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mxc/ipu3/vdoa.h b/drivers/mxc/ipu3/vdoa.h new file mode 100644 index 000000000000..82b0ee1f2c3c --- /dev/null +++ b/drivers/mxc/ipu3/vdoa.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2012 Freescale Semiconductor, 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 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __VDOA_H__ +#define __VDOA_H__ + +#define VDOA_PFS_YUYV (1) +#define VDOA_PFS_NV12 (0) + + +struct vfield_buf { + u32 prev_veba; + u32 cur_veba; + u32 next_veba; + u32 vubo; +}; + +struct vframe_buf { + u32 veba; + u32 vubo; +}; + +struct vdoa_params { + u32 width; + u32 height; + int vpu_stride; + int interlaced; + int scan_order; + int ipu_num; + int band_lines; + int band_mode; + int pfs; + u32 ieba0; + u32 ieba1; + u32 ieba2; + struct vframe_buf vframe_buf; + struct vfield_buf vfield_buf; +}; +struct vdoa_ipu_buf { + u32 ieba0; + u32 ieba1; + u32 iubo; +}; + +struct vdoa_info; +typedef void *vdoa_handle_t; + +void vdoa_setup(vdoa_handle_t handle, struct vdoa_params *params); +void vdoa_get_output_buf(vdoa_handle_t handle, struct vdoa_ipu_buf *buf); +int vdoa_start(vdoa_handle_t handle, int timeout_ms); +void vdoa_stop(vdoa_handle_t handle); +void vdoa_get_handle(vdoa_handle_t *handle); +void vdoa_put_handle(vdoa_handle_t *handle); +#endif diff --git a/drivers/net/fec.c b/drivers/net/fec.c index 872b7c4c5cc6..2078ef8d25f0 100755 --- a/drivers/net/fec.c +++ b/drivers/net/fec.c @@ -276,12 +276,14 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) unsigned long estatus; unsigned long flags; + spin_lock_irqsave(&fep->hw_lock, flags); if (!fep->link) { /* Link is down or autonegotiation is in progress. */ + netif_stop_queue(ndev); + spin_unlock_irqrestore(&fep->hw_lock, flags); return NETDEV_TX_BUSY; } - spin_lock_irqsave(&fep->hw_lock, flags); /* Fill in a Tx ring entry */ bdp = fep->cur_tx; @@ -292,6 +294,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) * This should not happen, since ndev->tbusy should be set. */ printk("%s: tx queue full!.\n", ndev->name); + netif_stop_queue(ndev); spin_unlock_irqrestore(&fep->hw_lock, flags); return NETDEV_TX_BUSY; } @@ -311,8 +314,8 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) if (((unsigned long) bufaddr) & FEC_ALIGNMENT) { unsigned int index; index = bdp - fep->tx_bd_base; - memcpy(fep->tx_bounce[index], (void *)skb->data, skb->len); - bufaddr = fep->tx_bounce[index]; + bufaddr = PTR_ALIGN(fep->tx_bounce[index], FEC_ALIGNMENT + 1); + memcpy(bufaddr, (void *)skb->data, skb->len); } if (fep->ptimer_present) { @@ -382,7 +385,8 @@ fec_timeout(struct net_device *ndev) ndev->stats.tx_errors++; fec_restart(ndev, fep->full_duplex); - netif_wake_queue(ndev); + if (fep->link && !fep->tx_full) + netif_wake_queue(ndev); } static void @@ -409,6 +413,8 @@ fec_enet_tx(struct net_device *ndev) bdp->cbd_bufaddr = 0; skb = fep->tx_skbuff[fep->skb_dirty]; + if (!skb) + break; /* Check for errors. */ if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | BD_ENET_TX_RL | BD_ENET_TX_UN | @@ -730,9 +736,11 @@ static void fec_enet_adjust_link(struct net_device *ndev) /* Link on or off change */ if (phy_dev->link != fep->link) { fep->link = phy_dev->link; - if (phy_dev->link) + if (phy_dev->link) { fec_restart(ndev, phy_dev->duplex); - else + if (!fep->tx_full) + netif_wake_queue(ndev); + } else fec_stop(ndev); status_change = 1; } @@ -1071,6 +1079,10 @@ static int fec_enet_alloc_buffers(struct net_device *ndev) bdp = fep->tx_bd_base; for (i = 0; i < TX_RING_SIZE; i++) { fep->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL); + if (!fep->tx_bounce[i]) { + fec_enet_free_buffers(ndev); + return -ENOMEM; + } bdp->cbd_sc = 0; bdp->cbd_bufaddr = 0; diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c index aa3dedb6b10f..2051e8691a64 100644 --- a/drivers/rtc/rtc-snvs.c +++ b/drivers/rtc/rtc-snvs.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Freescale Semiconductor, Inc. + * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. */ /* @@ -565,6 +565,10 @@ static int snvs_rtc_probe(struct platform_device *pdev) pdata->irq = platform_get_irq(pdev, 0); platform_set_drvdata(pdev, pdata); + + /* Added to support sysfs wakealarm attribute */ + pdev->dev.power.can_wakeup = 1; + /* initialize glitch detect */ __raw_writel(SNVS_LPPGDR_INIT, ioaddr + SNVS_LPPGDR); udelay(100); diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index fa453518139f..f8cd9bd076ce 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -553,11 +553,15 @@ static irqreturn_t imx_rxint(int irq, void *dev_id) continue; } + spin_unlock_irqrestore(&sport->port.lock, flags); if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx)) continue; + spin_lock_irqsave(&sport->port.lock, flags); - if (rx & (URXD_PRERR | URXD_OVRRUN | URXD_FRMERR) ) { - if (rx & URXD_PRERR) + if (unlikely(rx & URXD_ERR)) { + if (rx & URXD_BRK) + sport->port.icount.brk++; + else if (rx & URXD_PRERR) sport->port.icount.parity++; else if (rx & URXD_FRMERR) sport->port.icount.frame++; @@ -572,7 +576,9 @@ static irqreturn_t imx_rxint(int irq, void *dev_id) rx &= sport->port.read_status_mask; - if (rx & URXD_PRERR) + if (rx & URXD_BRK) + flg = TTY_BREAK; + else if (rx & URXD_PRERR) flg = TTY_PARITY; else if (rx & URXD_FRMERR) flg = TTY_FRAME; diff --git a/drivers/usb/otg/fsl_otg.c b/drivers/usb/otg/fsl_otg.c index 79b7e7bf2c25..2e7d97e81ef5 100755 --- a/drivers/usb/otg/fsl_otg.c +++ b/drivers/usb/otg/fsl_otg.c @@ -670,7 +670,9 @@ static int fsl_otg_set_peripheral(struct otg_transceiver *otg_p, struct usb_gadget *gadget) { struct fsl_otg *otg_dev = container_of(otg_p, struct fsl_otg, otg); + struct fsl_usb2_platform_data *pdata; + pdata = otg_dev->otg.dev->platform_data; VDBG("otg_dev 0x%x\n", (int)otg_dev); VDBG("fsl_otg_dev 0x%x\n", (int)fsl_otg_dev); @@ -683,10 +685,11 @@ static int fsl_otg_set_peripheral(struct otg_transceiver *otg_p, usb_gadget_vbus_disconnect(otg_dev->otg.gadget); otg_dev->otg.gadget = 0; otg_dev->fsm.b_bus_req = 0; + pdata->port_enables = 0; otg_statemachine(&otg_dev->fsm); return 0; } - + pdata->port_enables = 1; otg_p->gadget = gadget; otg_p->gadget->is_a_peripheral = !otg_dev->fsm.id; @@ -748,7 +751,7 @@ static void fsl_otg_event(struct work_struct *work) if (pdata->wake_up_enable) pdata->wake_up_enable(pdata, false); otg_drv_vbus(fsm, 0); - if (og->host_first_call == false) { + if ((og->host_first_call == false) && (pdata->port_enables == 1)) { fsl_otg_wait_dischrg_vbus(pdata); fsl_otg_wait_stable_vbus(false); } else { diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index ba66c938ae0a..bec8fe9cd838 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -355,7 +355,10 @@ struct mxc_audio_platform_data { struct clk *ssi_clk[2]; int hp_gpio; - int hp_active_low; /* headphone irq is active loaw */ + int hp_active_low; /* headphone irq is active low */ + + int mic_gpio; + int mic_active_low; /* micphone irq is active low */ int sysclk; const char *codec_name; diff --git a/include/linux/ipu.h b/include/linux/ipu.h index 051624854558..973cfe9b45a5 100644 --- a/include/linux/ipu.h +++ b/include/linux/ipu.h @@ -1,5 +1,5 @@ /* - * Copyright 2005-2011 Freescale Semiconductor, Inc. + * Copyright 2005-2012 Freescale Semiconductor, Inc. */ /* @@ -124,6 +124,10 @@ typedef enum { #define IPU_PIX_FMT_VYU444 fourcc('V', '4', '4', '4') /*!< 24 VYU 4:4:4 */ /* two planes -- one Y, one Cb + Cr interleaved */ #define IPU_PIX_FMT_NV12 fourcc('N', 'V', '1', '2') /* 12 Y/CbCr 4:2:0 */ +/* two planes -- 12 tiled Y/CbCr 4:2:0 */ +#define IPU_PIX_FMT_TILED_NV12 fourcc('T', 'N', 'V', 'P') +#define IPU_PIX_FMT_TILED_NV12F fourcc('T', 'N', 'V', 'F') + /*! @} */ /*! @name YUV Planar Formats */ /*! @{ */ @@ -136,7 +140,7 @@ typedef enum { #define IPU_PIX_FMT_YVU422P fourcc('Y', 'V', '1', '6') /*!< 16 YVU 4:2:2 */ #define IPU_PIX_FMT_YUV422P fourcc('4', '2', '2', 'P') /*!< 16 YUV 4:2:2 */ /*! @} */ - +#define IPU_PIX_FMT_TILED_NV12_MBALIGN (16) /* IPU device */ typedef enum { RGB_CS, @@ -244,6 +248,8 @@ enum { IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER, IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER, IPU_CHECK_ERR_SPLIT_WITH_ROT, + IPU_CHECK_ERR_NOT_SUPPORT, + IPU_CHECK_ERR_NOT16ALIGN, }; /* IOCTL commands */ diff --git a/include/linux/mxc_asrc.h b/include/linux/mxc_asrc.h index 6de6a7e3cd86..4ccf8cb496ca 100644 --- a/include/linux/mxc_asrc.h +++ b/include/linux/mxc_asrc.h @@ -204,7 +204,6 @@ struct asrc_pair_params { struct dma_block input_dma[ASRC_DMA_BUFFER_NUM]; struct dma_block output_dma_total; struct dma_block output_dma[ASRC_DMA_BUFFER_NUM]; - struct semaphore busy_lock; struct dma_async_tx_descriptor *desc_in; struct dma_async_tx_descriptor *desc_out; }; diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 7b14746d9336..b8be06b3344a 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -63,6 +63,8 @@ struct wm8962_priv { int fll_fref; int fll_fout; + u16 dsp2_ena; + struct delayed_work mic_work; struct snd_soc_jack *jack; @@ -78,6 +80,8 @@ struct wm8962_priv { #ifdef CONFIG_GPIOLIB struct gpio_chip gpio_chip; #endif + + int irq; }; /* We can't use the same notifier block for more than one supply and @@ -835,7 +839,7 @@ static const struct wm8962_reg_access { [40] = { 0x00FF, 0x01FF, 0x0000 }, /* R40 - SPKOUTL volume */ [41] = { 0x00FF, 0x01FF, 0x0000 }, /* R41 - SPKOUTR volume */ - [47] = { 0x000F, 0x0000, 0x0000 }, /* R47 - Thermal Shutdown Status */ + [47] = { 0x000F, 0x0000, 0xFFFF }, /* R47 - Thermal Shutdown Status*/ [48] = { 0x7EC7, 0x7E07, 0xFFFF }, /* R48 - Additional Control (4) */ [49] = { 0x00D3, 0x00D7, 0xFFFF }, /* R49 - Class D Control 1 */ [51] = { 0x0047, 0x0047, 0x0000 }, /* R51 - Class D Control 2 */ @@ -963,7 +967,7 @@ static const struct wm8962_reg_access { [584] = { 0x002D, 0x002D, 0x0000 }, /* R584 - IRQ Debounce */ [586] = { 0xC000, 0xC000, 0x0000 }, /* R586 - MICINT Source Pol */ [768] = { 0x0001, 0x0001, 0x0000 }, /* R768 - DSP2 Power Management */ - [1037] = { 0x0000, 0x003F, 0x0000 }, /* R1037 - DSP2_ExecControl */ + [1037] = { 0x0000, 0x003F, 0xFFFF }, /* R1037 - DSP2_ExecControl */ [4096] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4096 - Write Sequencer 0 */ [4097] = { 0x00FF, 0x00FF, 0x0000 }, /* R4097 - Write Sequencer 1 */ [4098] = { 0x070F, 0x070F, 0x0000 }, /* R4098 - Write Sequencer 2 */ @@ -1969,7 +1973,7 @@ static int wm8962_reset(struct snd_soc_codec *codec) static const DECLARE_TLV_DB_SCALE(inpga_tlv, -2325, 75, 0); static const DECLARE_TLV_DB_SCALE(mixin_tlv, -1500, 300, 0); static const unsigned int mixinpga_tlv[] = { - TLV_DB_RANGE_HEAD(7), + TLV_DB_RANGE_HEAD(5), 0, 1, TLV_DB_SCALE_ITEM(0, 600, 0), 2, 2, TLV_DB_SCALE_ITEM(1300, 1300, 0), 3, 4, TLV_DB_SCALE_ITEM(1800, 200, 0), @@ -1984,10 +1988,127 @@ static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0); static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); static const DECLARE_TLV_DB_SCALE(hp_tlv, -700, 100, 0); static const unsigned int classd_tlv[] = { - TLV_DB_RANGE_HEAD(7), + TLV_DB_RANGE_HEAD(2), 0, 6, TLV_DB_SCALE_ITEM(0, 150, 0), 7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0), }; +static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); + +static int wm8962_dsp2_write_config(struct snd_soc_codec *codec) +{ + return 0; +} + +static int wm8962_dsp2_set_enable(struct snd_soc_codec *codec, u16 val) +{ + u16 adcl = snd_soc_read(codec, WM8962_LEFT_ADC_VOLUME); + u16 adcr = snd_soc_read(codec, WM8962_RIGHT_ADC_VOLUME); + u16 dac = snd_soc_read(codec, WM8962_ADC_DAC_CONTROL_1); + + /* Mute the ADCs and DACs */ + snd_soc_write(codec, WM8962_LEFT_ADC_VOLUME, 0); + snd_soc_write(codec, WM8962_RIGHT_ADC_VOLUME, WM8962_ADC_VU); + snd_soc_update_bits(codec, WM8962_ADC_DAC_CONTROL_1, + WM8962_DAC_MUTE, WM8962_DAC_MUTE); + + snd_soc_write(codec, WM8962_SOUNDSTAGE_ENABLES_0, val); + + /* Restore the ADCs and DACs */ + snd_soc_write(codec, WM8962_LEFT_ADC_VOLUME, adcl); + snd_soc_write(codec, WM8962_RIGHT_ADC_VOLUME, adcr); + snd_soc_update_bits(codec, WM8962_ADC_DAC_CONTROL_1, + WM8962_DAC_MUTE, dac); + + return 0; +} + +static int wm8962_dsp2_start(struct snd_soc_codec *codec) +{ + struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); + + wm8962_dsp2_write_config(codec); + + snd_soc_write(codec, WM8962_DSP2_EXECCONTROL, WM8962_DSP2_RUNR); + + wm8962_dsp2_set_enable(codec, wm8962->dsp2_ena); + + return 0; +} + +static int wm8962_dsp2_stop(struct snd_soc_codec *codec) +{ + wm8962_dsp2_set_enable(codec, 0); + + snd_soc_write(codec, WM8962_DSP2_EXECCONTROL, WM8962_DSP2_STOP); + + return 0; +} + +#define WM8962_DSP2_ENABLE(xname, xshift) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = wm8962_dsp2_ena_info, \ + .get = wm8962_dsp2_ena_get, .put = wm8962_dsp2_ena_put, \ + .private_value = xshift } + +static int wm8962_dsp2_ena_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + + return 0; +} + +static int wm8962_dsp2_ena_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int shift = kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = !!(wm8962->dsp2_ena & 1 << shift); + + return 0; +} + +static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int shift = kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); + int old = wm8962->dsp2_ena; + int ret = 0; + int dsp2_running = snd_soc_read(codec, WM8962_DSP2_POWER_MANAGEMENT) & + WM8962_DSP2_ENA; + + mutex_lock(&codec->mutex); + + if (ucontrol->value.integer.value[0]) + wm8962->dsp2_ena |= 1 << shift; + else + wm8962->dsp2_ena &= ~(1 << shift); + + if (wm8962->dsp2_ena == old) + goto out; + + ret = 1; + + if (dsp2_running) { + if (wm8962->dsp2_ena) + wm8962_dsp2_set_enable(codec, wm8962->dsp2_ena); + else + wm8962_dsp2_stop(codec); + } + +out: + mutex_unlock(&codec->mutex); + + return ret; +} /* The VU bits for the headphones are in a different register to the mute * bits and only take effect on the PGA if it is actually powered. @@ -2054,6 +2175,14 @@ static const char *cap_hpf_mode_text[] = { static const struct soc_enum cap_hpf_mode = SOC_ENUM_SINGLE(WM8962_ADC_DAC_CONTROL_2, 10, 2, cap_hpf_mode_text); + +static const char *cap_lhpf_mode_text[] = { + "LPF", "HPF" +}; + +static const struct soc_enum cap_lhpf_mode = + SOC_ENUM_SINGLE(WM8962_LHPF1, 1, 2, cap_lhpf_mode_text); + static const struct snd_kcontrol_new wm8962_snd_controls[] = { SOC_DOUBLE("Input Mixer Switch", WM8962_INPUT_MIXER_CONTROL_1, 3, 2, 1, 1), @@ -2082,6 +2211,8 @@ SOC_DOUBLE_R("Capture ZC Switch", WM8962_LEFT_INPUT_VOLUME, SOC_SINGLE("Capture HPF Switch", WM8962_ADC_DAC_CONTROL_1, 0, 1, 1), SOC_ENUM("Capture HPF Mode", cap_hpf_mode), SOC_SINGLE("Capture HPF Cutoff", WM8962_ADC_DAC_CONTROL_2, 7, 7, 0), +SOC_SINGLE("Capture LHPF Switch", WM8962_LHPF1, 0, 1, 0), +SOC_ENUM("Capture LHPF Mode", cap_lhpf_mode), SOC_DOUBLE_R_TLV("Sidetone Volume", WM8962_DAC_DSP_MIXING_1, WM8962_DAC_DSP_MIXING_2, 4, 12, 0, st_tlv), @@ -2127,6 +2258,23 @@ SOC_SINGLE_TLV("HPMIXR MIXINR Volume", WM8962_HEADPHONE_MIXER_4, SOC_SINGLE_TLV("Speaker Boost Volume", WM8962_CLASS_D_CONTROL_2, 0, 7, 0, classd_tlv), + +SOC_SINGLE("EQ Switch", WM8962_EQ1, WM8962_EQ_ENA_SHIFT, 1, 0), +SOC_DOUBLE_R_TLV("EQ1 Volume", WM8962_EQ2, WM8962_EQ22, + WM8962_EQL_B1_GAIN_SHIFT, 31, 0, eq_tlv), +SOC_DOUBLE_R_TLV("EQ2 Volume", WM8962_EQ2, WM8962_EQ22, + WM8962_EQL_B2_GAIN_SHIFT, 31, 0, eq_tlv), +SOC_DOUBLE_R_TLV("EQ3 Volume", WM8962_EQ2, WM8962_EQ22, + WM8962_EQL_B3_GAIN_SHIFT, 31, 0, eq_tlv), +SOC_DOUBLE_R_TLV("EQ4 Volume", WM8962_EQ3, WM8962_EQ23, + WM8962_EQL_B4_GAIN_SHIFT, 31, 0, eq_tlv), +SOC_DOUBLE_R_TLV("EQ5 Volume", WM8962_EQ3, WM8962_EQ23, + WM8962_EQL_B5_GAIN_SHIFT, 31, 0, eq_tlv), + +WM8962_DSP2_ENABLE("VSS Switch", WM8962_VSS_ENA_SHIFT), +WM8962_DSP2_ENABLE("HPF1 Switch", WM8962_HPF1_ENA_SHIFT), +WM8962_DSP2_ENABLE("HPF2 Switch", WM8962_HPF2_ENA_SHIFT), +WM8962_DSP2_ENABLE("HD Bass Switch", WM8962_HDBASS_ENA_SHIFT), }; static const struct snd_kcontrol_new wm8962_spk_mono_controls[] = { @@ -2192,6 +2340,8 @@ static int sysclk_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = w->codec; + struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); + unsigned long timeout; int src; int fll; @@ -2211,9 +2361,20 @@ static int sysclk_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - if (fll) + if (fll) { + try_wait_for_completion(&wm8962->fll_lock); + snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1, WM8962_FLL_ENA, WM8962_FLL_ENA); + + timeout = msecs_to_jiffies(5); + timeout = wait_for_completion_timeout(&wm8962->fll_lock, + timeout); + + if (wm8962->irq && timeout == 0) + dev_err(codec->dev, + "Timed out starting FLL\n"); + } break; case SND_SOC_DAPM_POST_PMD: @@ -2373,6 +2534,31 @@ static int out_pga_event(struct snd_soc_dapm_widget *w, } } +static int dsp2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (wm8962->dsp2_ena) + wm8962_dsp2_start(codec); + break; + + case SND_SOC_DAPM_PRE_PMD: + if (wm8962->dsp2_ena) + wm8962_dsp2_stop(codec); + break; + + default: + BUG(); + return -EINVAL; + } + + return 0; +} + static const char *st_text[] = { "None", "Right", "Left" }; static const struct soc_enum str_enum = @@ -2487,7 +2673,7 @@ SND_SOC_DAPM_INPUT("IN4R"), SND_SOC_DAPM_INPUT("Beep"), SND_SOC_DAPM_INPUT("DMICDAT"), -SND_SOC_DAPM_MICBIAS("MICBIAS", WM8962_PWR_MGMT_1, 1, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS", WM8962_PWR_MGMT_1, 1, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("Class G", WM8962_CHARGE_PUMP_B, 0, 1, NULL, 0), SND_SOC_DAPM_SUPPLY("SYSCLK", WM8962_CLOCKING2, 5, 0, sysclk_event, @@ -2495,6 +2681,11 @@ SND_SOC_DAPM_SUPPLY("SYSCLK", WM8962_CLOCKING2, 5, 0, sysclk_event, SND_SOC_DAPM_SUPPLY("Charge Pump", WM8962_CHARGE_PUMP_1, 0, 0, cp_event, SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY("TOCLK", WM8962_ADDITIONAL_CONTROL_1, 0, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("TEMP_HP", WM8962_ADDITIONAL_CONTROL_4, 2, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("TEMP_SPK", WM8962_ADDITIONAL_CONTROL_4, 1, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY_S("DSP2", 1, WM8962_DSP2_POWER_MANAGEMENT, + WM8962_DSP2_ENA_SHIFT, 0, dsp2_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_MIXER("INPGAL", WM8962_LEFT_INPUT_PGA_CONTROL, 4, 0, inpgal, ARRAY_SIZE(inpgal)), @@ -2505,7 +2696,7 @@ SND_SOC_DAPM_MIXER("MIXINL", WM8962_PWR_MGMT_1, 5, 0, SND_SOC_DAPM_MIXER("MIXINR", WM8962_PWR_MGMT_1, 4, 0, mixinr, ARRAY_SIZE(mixinr)), -SND_SOC_DAPM_AIF_IN("DMIC", NULL, 0, WM8962_PWR_MGMT_1, 10, 0), +SND_SOC_DAPM_AIF_IN("DMIC_ENA", NULL, 0, WM8962_PWR_MGMT_1, 10, 0), SND_SOC_DAPM_ADC("ADCL", "Capture", WM8962_PWR_MGMT_1, 3, 0), SND_SOC_DAPM_ADC("ADCR", "Capture", WM8962_PWR_MGMT_1, 2, 0), @@ -2584,17 +2775,19 @@ static const struct snd_soc_dapm_route wm8962_intercon[] = { { "MICBIAS", NULL, "SYSCLK" }, - { "DMIC", NULL, "DMICDAT" }, + { "DMIC_ENA", NULL, "DMICDAT" }, { "ADCL", NULL, "SYSCLK" }, { "ADCL", NULL, "TOCLK" }, { "ADCL", NULL, "MIXINL" }, - { "ADCL", NULL, "DMIC" }, + { "ADCL", NULL, "DMIC_ENA" }, + { "ADCL", NULL, "DSP2" }, { "ADCR", NULL, "SYSCLK" }, { "ADCR", NULL, "TOCLK" }, { "ADCR", NULL, "MIXINR" }, - { "ADCR", NULL, "DMIC" }, + { "ADCR", NULL, "DMIC_ENA" }, + { "ADCR", NULL, "DSP2" }, { "STL", "Left", "ADCL" }, { "STL", "Right", "ADCR" }, @@ -2606,11 +2799,13 @@ static const struct snd_soc_dapm_route wm8962_intercon[] = { { "DACL", NULL, "TOCLK" }, { "DACL", NULL, "Beep" }, { "DACL", NULL, "STL" }, + { "DACL", NULL, "DSP2" }, { "DACR", NULL, "SYSCLK" }, { "DACR", NULL, "TOCLK" }, { "DACR", NULL, "Beep" }, { "DACR", NULL, "STR" }, + { "DACR", NULL, "DSP2" }, { "HPMIXL", "IN4L Switch", "IN4L" }, { "HPMIXL", "IN4R Switch", "IN4R" }, @@ -2646,6 +2841,9 @@ static const struct snd_soc_dapm_route wm8962_intercon[] = { { "HPOUTL", NULL, "HPOUT" }, { "HPOUTR", NULL, "HPOUT" }, + + { "HPOUTL", NULL, "TEMP_HP" }, + { "HPOUTR", NULL, "TEMP_HP" }, }; static const struct snd_soc_dapm_route wm8962_spk_mono_intercon[] = { @@ -2662,6 +2860,7 @@ static const struct snd_soc_dapm_route wm8962_spk_mono_intercon[] = { { "Speaker Output", NULL, "Speaker PGA" }, { "Speaker Output", NULL, "SYSCLK" }, { "Speaker Output", NULL, "TOCLK" }, + { "Speaker Output", NULL, "TEMP_SPK" }, { "SPKOUT", NULL, "Speaker Output" }, }; @@ -2690,10 +2889,12 @@ static const struct snd_soc_dapm_route wm8962_spk_stereo_intercon[] = { { "SPKOUTL Output", NULL, "SPKOUTL PGA" }, { "SPKOUTL Output", NULL, "SYSCLK" }, { "SPKOUTL Output", NULL, "TOCLK" }, + { "SPKOUTL Output", NULL, "TEMP_SPK" }, { "SPKOUTR Output", NULL, "SPKOUTR PGA" }, { "SPKOUTR Output", NULL, "SYSCLK" }, { "SPKOUTR Output", NULL, "TOCLK" }, + { "SPKOUTR Output", NULL, "TEMP_SPK" }, { "SPKOUTL", NULL, "SPKOUTL Output" }, { "SPKOUTR", NULL, "SPKOUTR Output" }, @@ -2770,18 +2971,44 @@ static const int bclk_divs[] = { 1, -1, 2, 3, 4, -1, 6, 8, -1, 12, 16, 24, -1, 32, 32, 32 }; +static const int sysclk_rates[] = { + 64, 128, 192, 256, 384, 512, 768, 1024, 1408, 1536, +}; + static void wm8962_configure_bclk(struct snd_soc_codec *codec) { struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); int dspclk, i; int clocking2 = 0; + int clocking4 = 0; int aif2 = 0; - if (!wm8962->bclk) { - dev_dbg(codec->dev, "No BCLK rate configured\n"); + if (!wm8962->sysclk_rate) { + dev_dbg(codec->dev, "No SYSCLK configured\n"); + return; + } + + if (!wm8962->bclk || !wm8962->lrclk) { + dev_dbg(codec->dev, "No audio clocks configured\n"); return; } + for (i = 0; i < ARRAY_SIZE(sysclk_rates); i++) { + if (sysclk_rates[i] == wm8962->sysclk_rate / wm8962->lrclk) { + clocking4 |= i << WM8962_SYSCLK_RATE_SHIFT; + break; + } + } + + if (i == ARRAY_SIZE(sysclk_rates)) { + dev_err(codec->dev, "Unsupported sysclk ratio %d\n", + wm8962->sysclk_rate / wm8962->lrclk); + return; + } + + snd_soc_update_bits(codec, WM8962_CLOCKING_4, + WM8962_SYSCLK_RATE_MASK, clocking4); + dspclk = snd_soc_read(codec, WM8962_CLOCKING1); if (dspclk < 0) { dev_err(codec->dev, "Failed to read DSPCLK: %d\n", dspclk); @@ -2851,6 +3078,8 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec, /* VMID 2*50k */ snd_soc_update_bits(codec, WM8962_PWR_MGMT_1, WM8962_VMID_SEL_MASK, 0x80); + + wm8962_configure_bclk(codec); break; case SND_SOC_BIAS_STANDBY: @@ -2879,12 +3108,6 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec, WM8962_BIAS_ENA | 0x180); msleep(5); - - snd_soc_update_bits(codec, WM8962_CLOCKING2, - WM8962_CLKREG_OVD, - WM8962_CLKREG_OVD); - - wm8962_configure_bclk(codec); } /* VMID 2*250k */ @@ -2925,10 +3148,6 @@ static const struct { { 96000, 6 }, }; -static const int sysclk_rates[] = { - 64, 128, 192, 256, 384, 512, 768, 1024, 1408, 1536, -}; - static int wm8962_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -2936,52 +3155,44 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_codec *codec = rtd->codec; struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); - int rate = params_rate(params); int i; int aif0 = 0; int adctl3 = 0; - int clocking4 = 0; + + if (codec->dapm.bias_level != SND_SOC_BIAS_OFF) + return 0; wm8962->bclk = snd_soc_params_to_bclk(params); + if (params_channels(params) == 1) + wm8962->bclk *= 2; + wm8962->lrclk = params_rate(params); for (i = 0; i < ARRAY_SIZE(sr_vals); i++) { - if (sr_vals[i].rate == rate) { + if (sr_vals[i].rate == wm8962->lrclk) { adctl3 |= sr_vals[i].reg; break; } } if (i == ARRAY_SIZE(sr_vals)) { - dev_err(codec->dev, "Unsupported rate %dHz\n", rate); + dev_err(codec->dev, "Unsupported rate %dHz\n", wm8962->lrclk); return -EINVAL; } - if (rate % 8000 == 0) + if (wm8962->lrclk % 8000 == 0) adctl3 |= WM8962_SAMPLE_RATE_INT_MODE; - for (i = 0; i < ARRAY_SIZE(sysclk_rates); i++) { - if (sysclk_rates[i] == wm8962->sysclk_rate / rate) { - clocking4 |= i << WM8962_SYSCLK_RATE_SHIFT; - break; - } - } - if (i == ARRAY_SIZE(sysclk_rates)) { - dev_err(codec->dev, "Unsupported sysclk ratio %d\n", - wm8962->sysclk_rate / rate); - return -EINVAL; - } - switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: break; case SNDRV_PCM_FORMAT_S20_3LE: - aif0 |= 0x40; + aif0 |= 0x4; break; case SNDRV_PCM_FORMAT_S24_LE: - aif0 |= 0x80; + aif0 |= 0x8; break; case SNDRV_PCM_FORMAT_S32_LE: - aif0 |= 0xc0; + aif0 |= 0xc; break; default: return -EINVAL; @@ -2992,8 +3203,6 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream, snd_soc_update_bits(codec, WM8962_ADDITIONAL_CONTROL_3, WM8962_SAMPLE_RATE_INT_MODE | WM8962_SAMPLE_RATE_MASK, adctl3); - snd_soc_update_bits(codec, WM8962_CLOCKING_4, - WM8962_SYSCLK_RATE_MASK, clocking4); wm8962_configure_bclk(codec); @@ -3262,22 +3471,39 @@ static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source, snd_soc_write(codec, WM8962_FLL_CONTROL_7, fll_div.lambda); snd_soc_write(codec, WM8962_FLL_CONTROL_8, fll_div.n); + try_wait_for_completion(&wm8962->fll_lock); + snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1, WM8962_FLL_FRAC | WM8962_FLL_REFCLK_SRC_MASK | WM8962_FLL_ENA, fll1); dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout); - /* This should be a massive overestimate */ - timeout = msecs_to_jiffies(1); + ret = 0; + + if (fll1 & WM8962_FLL_ENA) { + /* This should be a massive overestimate but go even + * higher if we'll error out + */ + if (wm8962->irq) + timeout = msecs_to_jiffies(5); + else + timeout = msecs_to_jiffies(1); + + timeout = wait_for_completion_timeout(&wm8962->fll_lock, + timeout); - wait_for_completion_timeout(&wm8962->fll_lock, timeout); + if (timeout == 0 && wm8962->irq) { + dev_err(codec->dev, "FLL lock timed out"); + ret = -ETIMEDOUT; + } + } wm8962->fll_fref = Fref; wm8962->fll_fout = Fout; wm8962->fll_src = source; - return 0; + return ret; } static int wm8962_mute(struct snd_soc_dai *dai, int mute) @@ -3317,7 +3543,7 @@ static struct snd_soc_dai_driver wm8962_dai = { }, .capture = { .stream_name = "Capture", - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = WM8962_RATES, .formats = WM8962_FORMATS, @@ -3362,12 +3588,19 @@ static irqreturn_t wm8962_irq(int irq, void *data) struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); int mask; int active; + int reg; mask = snd_soc_read(codec, WM8962_INTERRUPT_STATUS_2_MASK); active = snd_soc_read(codec, WM8962_INTERRUPT_STATUS_2); active &= ~mask; + if (!active) + return IRQ_NONE; + + /* Acknowledge the interrupts */ + snd_soc_write(codec, WM8962_INTERRUPT_STATUS_2, active); + if (active & WM8962_FLL_LOCK_EINT) { dev_dbg(codec->dev, "FLL locked\n"); complete(&wm8962->fll_lock); @@ -3376,9 +3609,21 @@ static irqreturn_t wm8962_irq(int irq, void *data) if (active & WM8962_FIFOS_ERR_EINT) dev_err(codec->dev, "FIFO error\n"); - if (active & WM8962_TEMP_SHUT_EINT) + if (active & WM8962_TEMP_SHUT_EINT) { dev_crit(codec->dev, "Thermal shutdown\n"); + reg = snd_soc_read(codec, WM8962_THERMAL_SHUTDOWN_STATUS); + + if (reg & WM8962_TEMP_ERR_HP) + dev_crit(codec->dev, "Headphone thermal error\n"); + if (reg & WM8962_TEMP_WARN_HP) + dev_crit(codec->dev, "Headphone thermal warning\n"); + if (reg & WM8962_TEMP_ERR_SPK) + dev_crit(codec->dev, "Speaker thermal error\n"); + if (reg & WM8962_TEMP_WARN_SPK) + dev_crit(codec->dev, "Speaker thermal warning\n"); + } + if (active & (WM8962_MICSCD_EINT | WM8962_MICD_EINT)) { dev_dbg(codec->dev, "Microphone event detected\n"); @@ -3392,9 +3637,6 @@ static irqreturn_t wm8962_irq(int irq, void *data) msecs_to_jiffies(250)); } - /* Acknowledge the interrupts */ - snd_soc_write(codec, WM8962_INTERRUPT_STATUS_2, active); - return IRQ_HANDLED; } @@ -3434,34 +3676,17 @@ int wm8962_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) snd_soc_jack_report(wm8962->jack, 0, SND_JACK_MICROPHONE | SND_JACK_BTN_0); - return 0; -} -EXPORT_SYMBOL_GPL(wm8962_mic_detect); - -#ifdef CONFIG_PM -static int wm8962_resume(struct snd_soc_codec *codec) -{ - u16 *reg_cache = codec->reg_cache; - int i; - - /* Restore the registers */ - for (i = 1; i < codec->driver->reg_cache_size; i++) { - switch (i) { - case WM8962_SOFTWARE_RESET: - continue; - default: - break; - } - - if (reg_cache[i] != wm8962_reg[i]) - snd_soc_write(codec, i, reg_cache[i]); + if (jack) { + snd_soc_dapm_force_enable_pin(&codec->dapm, "SYSCLK"); + snd_soc_dapm_force_enable_pin(&codec->dapm, "MICBIAS"); + } else { + snd_soc_dapm_disable_pin(&codec->dapm, "SYSCLK"); + snd_soc_dapm_disable_pin(&codec->dapm, "MICBIAS"); } return 0; } -#else -#define wm8962_resume NULL -#endif +EXPORT_SYMBOL_GPL(wm8962_mic_detect); #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) static int beep_rates[] = { @@ -3738,8 +3963,6 @@ static int wm8962_probe(struct snd_soc_codec *codec) int ret; struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); struct wm8962_pdata *pdata = dev_get_platdata(codec->dev); - struct i2c_client *i2c = container_of(codec->dev, struct i2c_client, - dev); u16 *reg_cache = codec->reg_cache; int i, trigger, irq_pol; bool dmicclk, dmicdat; @@ -3829,6 +4052,10 @@ static int wm8962_probe(struct snd_soc_codec *codec) */ snd_soc_update_bits(codec, WM8962_CLOCKING2, WM8962_SYSCLK_ENA, 0); + /* Ensure we have soft control over all registers */ + snd_soc_update_bits(codec, WM8962_CLOCKING2, + WM8962_CLKREG_OVD, WM8962_CLKREG_OVD); + /* Ensure that the oscillator and PLLs are disabled */ snd_soc_update_bits(codec, WM8962_PLL2, WM8962_OSC_ENA | WM8962_PLL2_ENA | WM8962_PLL3_ENA, @@ -3883,6 +4110,9 @@ static int wm8962_probe(struct snd_soc_codec *codec) snd_soc_update_bits(codec, WM8962_HPOUTR_VOLUME, WM8962_HPOUT_VU, WM8962_HPOUT_VU); + /* Stereo control for EQ */ + snd_soc_update_bits(codec, WM8962_EQ1, WM8962_EQ_SHARED_COEFF, 0); + wm8962_add_widgets(codec); /* Save boards having to disable DMIC when not in use */ @@ -3911,7 +4141,7 @@ static int wm8962_probe(struct snd_soc_codec *codec) wm8962_init_beep(codec); wm8962_init_gpio(codec); - if (i2c->irq) { + if (wm8962->irq) { if (pdata && pdata->irq_active_low) { trigger = IRQF_TRIGGER_LOW; irq_pol = WM8962_IRQ_POL; @@ -3923,12 +4153,13 @@ static int wm8962_probe(struct snd_soc_codec *codec) snd_soc_update_bits(codec, WM8962_INTERRUPT_CONTROL, WM8962_IRQ_POL, irq_pol); - ret = request_threaded_irq(i2c->irq, NULL, wm8962_irq, + ret = request_threaded_irq(wm8962->irq, NULL, wm8962_irq, trigger | IRQF_ONESHOT, "wm8962", codec); if (ret != 0) { dev_err(codec->dev, "Failed to request IRQ %d: %d\n", - i2c->irq, ret); + wm8962->irq, ret); + wm8962->irq = 0; /* Non-fatal */ } else { /* Enable some IRQs by default */ @@ -3953,12 +4184,10 @@ err: static int wm8962_remove(struct snd_soc_codec *codec) { struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); - struct i2c_client *i2c = container_of(codec->dev, struct i2c_client, - dev); int i; - if (i2c->irq) - free_irq(i2c->irq, codec); + if (wm8962->irq) + free_irq(wm8962->irq, codec); cancel_delayed_work_sync(&wm8962->mic_work); @@ -3975,7 +4204,6 @@ static int wm8962_remove(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_wm8962 = { .probe = wm8962_probe, .remove = wm8962_remove, - .resume = wm8962_resume, .set_bias_level = wm8962_set_bias_level, .reg_cache_size = WM8962_MAX_REGISTER + 1, .reg_word_size = sizeof(u16), @@ -3998,6 +4226,8 @@ static __devinit int wm8962_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, wm8962); + wm8962->irq = i2c->irq; + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8962, &wm8962_dai, 1); if (ret < 0) diff --git a/sound/soc/imx/imx-cs42888.c b/sound/soc/imx/imx-cs42888.c index dc46fa676386..c58af0c46f25 100644 --- a/sound/soc/imx/imx-cs42888.c +++ b/sound/soc/imx/imx-cs42888.c @@ -21,12 +21,14 @@ #include <linux/regulator/consumer.h> #include <linux/fsl_devices.h> #include <linux/gpio.h> +#include <linux/mxc_asrc.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/soc-dai.h> +#include <sound/pcm_params.h> #include <mach/hardware.h> #include <mach/clock.h> @@ -34,6 +36,99 @@ #include "imx-esai.h" #include "../codecs/cs42888.h" +#include "imx-pcm.h" + +#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_IMX_HAVE_PLATFORM_IMX_ASRC) +struct asrc_esai { + unsigned int cpu_dai_rates; + unsigned int codec_dai_rates; + enum asrc_pair_index asrc_index; + unsigned int output_sample_rate; +}; +static struct asrc_esai asrc_esai_data; +static bool asrc_support = 1; + +static int get_format_width(struct snd_pcm_hw_params *params) +{ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + case SNDRV_PCM_FORMAT_U8: + return 8; + case SNDRV_PCM_FORMAT_U16: + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + return 16; + case SNDRV_PCM_FORMAT_S20_3LE: + case SNDRV_PCM_FORMAT_S20_3BE: + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_3BE: + case SNDRV_PCM_FORMAT_S24_BE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_U24_BE: + case SNDRV_PCM_FORMAT_U24_LE: + case SNDRV_PCM_FORMAT_U24_3BE: + case SNDRV_PCM_FORMAT_U24_3LE: + return 24; + case SNDRV_PCM_FORMAT_S32: + case SNDRV_PCM_FORMAT_U32: + return 32; + default: + pr_err("Format is not support!\r\n"); + return -EINVAL; + } +} + +static int config_asrc(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + unsigned int rate = params_rate(params); + unsigned int channel = params_channels(params); + unsigned int wordwidth = get_format_width(params); + struct imx_pcm_runtime_data *pcm_data = + substream->runtime->private_data; + struct asrc_config config = {0}; + int ret = 0; + + if (rate <= 32000 || rate == asrc_esai_data.output_sample_rate) + return -EINVAL; + + if (channel != 2) + return -EINVAL; + + if (wordwidth != 24) + return -EINVAL; + + ret = asrc_req_pair(channel, &asrc_esai_data.asrc_index); + if (ret < 0) { + pr_err("Fail to request asrc pair\n"); + asrc_release_pair(asrc_esai_data.asrc_index); + asrc_finish_conv(asrc_esai_data.asrc_index); + return -EINVAL; + } + + config.pair = asrc_esai_data.asrc_index; + config.channel_num = channel; + config.input_sample_rate = rate; + config.output_sample_rate = asrc_esai_data.output_sample_rate; + config.inclk = OUTCLK_ASRCK1_CLK; + config.word_width = wordwidth; + config.outclk = OUTCLK_ESAI_TX; + + ret = asrc_config_pair(&config); + if (ret < 0) { + pr_err("Fail to config asrc\n"); + asrc_release_pair(asrc_esai_data.asrc_index); + asrc_finish_conv(asrc_esai_data.asrc_index); + return ret; + } + pcm_data->asrc_index = asrc_esai_data.asrc_index; + pcm_data->asrc_enable = 1; + + return 0; +} +#else +static bool asrc_support; +#endif struct imx_priv_state { int hw; @@ -42,6 +137,7 @@ struct imx_priv_state { static struct imx_priv_state hw_state; unsigned int mclk_freq; + static int imx_3stack_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -51,6 +147,14 @@ static int imx_3stack_startup(struct snd_pcm_substream *substream) hw_state.hw = 0; } + if (asrc_support) { + struct snd_soc_dai *codec_dai = rtd->codec_dai; + asrc_esai_data.cpu_dai_rates = + cpu_dai->driver->playback.rates; + asrc_esai_data.codec_dai_rates = + codec_dai->driver->playback.rates; + } + return 0; } @@ -58,6 +162,24 @@ static void imx_3stack_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + + if (asrc_support) { + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct imx_pcm_runtime_data *pcm_data = + substream->runtime->private_data; + if (pcm_data->asrc_enable) { + asrc_release_pair(asrc_esai_data.asrc_index); + asrc_finish_conv(asrc_esai_data.asrc_index); + } + pcm_data->asrc_enable = 0; + asrc_esai_data.asrc_index = -1; + + codec_dai->driver->playback.rates = + asrc_esai_data.codec_dai_rates; + cpu_dai->driver->playback.rates = + asrc_esai_data.cpu_dai_rates; + } + if (!cpu_dai->active) hw_state.hw = 0; } @@ -75,6 +197,12 @@ static int imx_3stack_surround_hw_params(struct snd_pcm_substream *substream, if (hw_state.hw) return 0; hw_state.hw = 1; + + if (asrc_support && + (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) && + !config_asrc(substream, params)) { + rate = asrc_esai_data.output_sample_rate; + } if (cpu_is_mx53() || machine_is_mx6q_sabreauto()) { switch (rate) { case 32000: @@ -211,6 +339,9 @@ static int imx_3stack_cs42888_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; + if (asrc_support) + asrc_esai_data.output_sample_rate = 44100; + snd_soc_dapm_new_controls(&codec->dapm, imx_3stack_dapm_widgets, ARRAY_SIZE(imx_3stack_dapm_widgets)); diff --git a/sound/soc/imx/imx-esai.h b/sound/soc/imx/imx-esai.h index 97cf1c44c004..d27cbd00ae96 100644 --- a/sound/soc/imx/imx-esai.h +++ b/sound/soc/imx/imx-esai.h @@ -1,7 +1,7 @@ /* * imx-esai.h -- ESAI driver header file for Freescale IMX * - * Copyright 2008-2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008-2012 Freescale Semiconductor, Inc. All Rights Reserved. */ /* @@ -19,25 +19,26 @@ #ifdef IMX_ESAI_DUMP #define ESAI_DUMP() \ do {pr_info("dump @ %s\n", __func__); \ - pr_info("ecr %x\n", readl(esai->base + ESAI_ECR)); \ - pr_info("esr %x\n", readl(esai->base + ESAI_ESR)); \ - pr_info("tfcr %x\n", readl(esai->base + ESAI_TFCR)); \ - pr_info("tfsr %x\n", readl(esai->base + ESAI_TFSR)); \ - pr_info("rfcr %x\n", readl(esai->base + ESAI_RFCR)); \ - pr_info("rfsr %x\n", readl(esai->base + ESAI_RFSR)); \ - pr_info("tsr %x\n", readl(esai->base + ESAI_TSR)); \ - pr_info("saisr %x\n", readl(esai->base + ESAI_SAISR)); \ - pr_info("saicr %x\n", readl(esai->base + ESAI_SAICR)); \ - pr_info("tcr %x\n", readl(esai->base + ESAI_TCR)); \ - pr_info("tccr %x\n", readl(esai->base + ESAI_TCCR)); \ - pr_info("rcr %x\n", readl(esai->base + ESAI_RCR)); \ - pr_info("rccr %x\n", readl(esai->base + ESAI_RCCR)); \ - pr_info("tsma %x\n", readl(esai->base + ESAI_TSMA)); \ - pr_info("tsmb %x\n", readl(esai->base + ESAI_TSMB)); \ - pr_info("rsma %x\n", readl(esai->base + ESAI_RSMA)); \ - pr_info("rsmb %x\n", readl(esai->base + ESAI_RSMB)); \ - pr_info("prrc %x\n", readl(esai->base + ESAI_PRRC)); \ - pr_info("pcrc %x\n", readl(esai->base + ESAI_PCRC)); } while (0); + pr_info("ESAI_ECR 0x%08x\n", readl(esai->base + ESAI_ECR)); \ + pr_info("ESAI_ESR 0x%08x\n", readl(esai->base + ESAI_ESR)); \ + pr_info("ESAI_TFCR 0x%08x\n", readl(esai->base + ESAI_TFCR)); \ + pr_info("ESAI_TFSR 0x%08x\n", readl(esai->base + ESAI_TFSR)); \ + pr_info("ESAI_RFCR 0x%08x\n", readl(esai->base + ESAI_RFCR)); \ + pr_info("ESAI_RFSR 0x%08x\n", readl(esai->base + ESAI_RFSR)); \ + pr_info("ESAI_TSR 0x%08x\n", readl(esai->base + ESAI_TSR)); \ + pr_info("ESAI_SAISR 0x%08x\n", readl(esai->base + ESAI_SAISR)); \ + pr_info("ESAI_SAICR 0x%08x\n", readl(esai->base + ESAI_SAICR)); \ + pr_info("ESAI_TCR 0x%08x\n", readl(esai->base + ESAI_TCR)); \ + pr_info("ESAI_TCCR 0x%08x\n", readl(esai->base + ESAI_TCCR)); \ + pr_info("ESAI_RCR 0x%08x\n", readl(esai->base + ESAI_RCR)); \ + pr_info("ESAI_RCCR 0x%08x\n", readl(esai->base + ESAI_RCCR)); \ + pr_info("ESAI_TSMA 0x%08x\n", readl(esai->base + ESAI_TSMA)); \ + pr_info("ESAI_TSMB 0x%08x\n", readl(esai->base + ESAI_TSMB)); \ + pr_info("ESAI_RSMA 0x%08x\n", readl(esai->base + ESAI_RSMA)); \ + pr_info("ESAI_RSMB 0x%08x\n", readl(esai->base + ESAI_RSMB)); \ + pr_info("ESAI_PRRC 0x%08x\n", readl(esai->base + ESAI_PRRC)); \ + pr_info("ESAI_PCRC 0x%08x\n", readl(esai->base + ESAI_PCRC)); \ + } while (0); #else #define ESAI_DUMP() #endif diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/imx/imx-pcm-dma-mx2.c index 7ff31a894fa4..386bd26564a8 100644 --- a/sound/soc/imx/imx-pcm-dma-mx2.c +++ b/sound/soc/imx/imx-pcm-dma-mx2.c @@ -21,6 +21,8 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/dmaengine.h> +#include <linux/delay.h> +#include <linux/mxc_asrc.h> #include <sound/core.h> #include <sound/initval.h> @@ -31,19 +33,8 @@ #include <mach/dma.h> #include "imx-ssi.h" +#include "imx-pcm.h" -struct imx_pcm_runtime_data { - int period_bytes; - int periods; - int dma; - unsigned long offset; - unsigned long size; - void *buf; - int period_time; - struct dma_async_tx_descriptor *desc; - struct dma_chan *dma_chan; - struct imx_dma_data dma_data; -}; static void audio_dma_irq(void *data) { @@ -68,6 +59,106 @@ static bool filter(struct dma_chan *chan, void *param) return true; } +#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_IMX_HAVE_PLATFORM_IMX_ASRC) +static bool asrc_filter(struct dma_chan *chan, void *param) +{ + struct imx_pcm_runtime_data *iprtd = param; + if (!imx_dma_is_general_purpose(chan)) + return false; + chan->private = &iprtd->asrc_dma_data; + return true; +} +static bool asrc_p2p_filter(struct dma_chan *chan, void *param) +{ + struct imx_pcm_runtime_data *iprtd = param; + if (!imx_dma_is_general_purpose(chan)) + return false; + chan->private = &iprtd->asrc_p2p_dma_data; + return true; +} +static int imx_ssi_asrc_dma_alloc(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct imx_pcm_dma_params *dma_params; + struct snd_pcm_runtime *runtime = substream->runtime; + struct imx_pcm_runtime_data *iprtd = runtime->private_data; + struct dma_slave_config slave_config; + + dma_cap_mask_t mask; + enum dma_slave_buswidth buswidth; + int ret; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + case SNDRV_PCM_FORMAT_S24_LE: + buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + default: + goto error; + } + + dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + /*config m2p dma channel*/ + iprtd->asrc_dma_data.peripheral_type = IMX_DMATYPE_ASRC; + iprtd->asrc_dma_data.priority = DMA_PRIO_HIGH; + iprtd->asrc_dma_data.dma_request = + asrc_get_dma_request(iprtd->asrc_index, 1); + iprtd->asrc_dma_chan = dma_request_channel(mask, asrc_filter, iprtd); + + if (!iprtd->asrc_dma_chan) + goto error; + + slave_config.direction = DMA_TO_DEVICE; + slave_config.dst_addr = asrc_get_per_addr(iprtd->asrc_index, 1); + slave_config.dst_addr_width = buswidth; + slave_config.dst_maxburst = dma_params->burstsize * buswidth; + + ret = dmaengine_slave_config(iprtd->asrc_dma_chan, &slave_config); + if (ret) + goto error; + /*config p2p dma channel*/ + iprtd->asrc_p2p_dma_data.peripheral_type = IMX_DMATYPE_ASRC; + iprtd->asrc_p2p_dma_data.priority = DMA_PRIO_HIGH; + iprtd->asrc_p2p_dma_data.dma_request = + asrc_get_dma_request(iprtd->asrc_index, 0); + iprtd->asrc_p2p_dma_data.dma_request_p2p = dma_params->dma; + iprtd->asrc_p2p_dma_chan = + dma_request_channel(mask, asrc_p2p_filter, iprtd); + if (!iprtd->asrc_p2p_dma_chan) + goto error; + + slave_config.direction = DMA_DEV_TO_DEV;; + slave_config.src_addr = asrc_get_per_addr(iprtd->asrc_index, 0); + slave_config.src_addr_width = buswidth; + slave_config.src_maxburst = dma_params->burstsize * buswidth; + slave_config.dst_addr = dma_params->dma_addr; + slave_config.dst_addr_width = buswidth; + slave_config.dst_maxburst = dma_params->burstsize * buswidth; + + ret = dmaengine_slave_config(iprtd->asrc_p2p_dma_chan, &slave_config); + if (ret) + goto error; + + return 0; +error: + if (iprtd->asrc_dma_chan) { + dma_release_channel(iprtd->asrc_dma_chan); + iprtd->asrc_dma_chan = NULL; + } + if (iprtd->asrc_p2p_dma_chan) { + dma_release_channel(iprtd->asrc_p2p_dma_chan); + iprtd->asrc_p2p_dma_chan = NULL; + } + return -EINVAL; +} +#endif static int imx_ssi_dma_alloc(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) @@ -137,17 +228,36 @@ static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream, int ret; dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - ret = imx_ssi_dma_alloc(substream, params); - if (ret) - return ret; - chan = iprtd->dma_chan; + + if (iprtd->asrc_enable) { + ret = imx_ssi_asrc_dma_alloc(substream, params); + if (ret) + return ret; + chan = iprtd->asrc_p2p_dma_chan; + iprtd->asrc_p2p_desc = + chan->device->device_prep_dma_cyclic(chan, 0xffff, + 64, + 64, + DMA_DEV_TO_DEV); + if (!iprtd->asrc_p2p_desc) { + dev_err(&chan->dev->device, + "cannot prepare slave dma\n"); + return -EINVAL; + } + chan = iprtd->asrc_dma_chan; + } else { + ret = imx_ssi_dma_alloc(substream, params); + if (ret) + return ret; + chan = iprtd->dma_chan; + } iprtd->size = params_buffer_bytes(params); iprtd->periods = params_periods(params); iprtd->period_bytes = params_period_bytes(params); iprtd->offset = 0; iprtd->period_time = HZ / (params_rate(params) / - params_period_size(params)); + params_period_size(params)); snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); @@ -155,19 +265,38 @@ static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream, iprtd->buf = (unsigned int *)substream->dma_buffer.area; - iprtd->desc = chan->device->device_prep_dma_cyclic(chan, dma_addr, + if (iprtd->asrc_enable) { + iprtd->asrc_desc = + chan->device->device_prep_dma_cyclic(chan, dma_addr, + iprtd->period_bytes * iprtd->periods, + iprtd->period_bytes, + substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); + if (!iprtd->asrc_desc) { + dev_err(&chan->dev->device, + "cannot prepare slave dma\n"); + return -EINVAL; + } + + iprtd->asrc_desc->callback = audio_dma_irq; + iprtd->asrc_desc->callback_param = substream; + } else { + iprtd->desc = chan->device->device_prep_dma_cyclic( + chan, dma_addr, iprtd->period_bytes * iprtd->periods, iprtd->period_bytes, substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (!iprtd->desc) { - dev_err(&chan->dev->device, "cannot prepare slave dma\n"); - return -EINVAL; + if (!iprtd->desc) { + dev_err(&chan->dev->device, + "cannot prepare slave dma\n"); + return -EINVAL; + } + + iprtd->desc->callback = audio_dma_irq; + iprtd->desc->callback_param = substream; } - iprtd->desc->callback = audio_dma_irq; - iprtd->desc->callback_param = substream; - return 0; } @@ -176,9 +305,21 @@ static int snd_imx_pcm_hw_free(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct imx_pcm_runtime_data *iprtd = runtime->private_data; - if (iprtd->dma_chan) { - dma_release_channel(iprtd->dma_chan); + if (iprtd->asrc_enable) { + if (iprtd->asrc_dma_chan) { + dma_release_channel(iprtd->asrc_dma_chan); + iprtd->asrc_dma_chan = NULL; + } + if (iprtd->asrc_p2p_dma_chan) { + dma_release_channel(iprtd->asrc_p2p_dma_chan); + iprtd->asrc_p2p_dma_chan = NULL; + } iprtd->dma_chan = NULL; + } else { + if (iprtd->dma_chan) { + dma_release_channel(iprtd->dma_chan); + iprtd->dma_chan = NULL; + } } return 0; @@ -203,15 +344,25 @@ static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - dmaengine_submit(iprtd->desc); - + if (iprtd->asrc_enable) { + dmaengine_submit(iprtd->asrc_p2p_desc); + dmaengine_submit(iprtd->asrc_desc); + asrc_start_conv(iprtd->asrc_index); + } else { + dmaengine_submit(iprtd->desc); + } break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - dmaengine_terminate_all(iprtd->dma_chan); - + if (iprtd->asrc_enable) { + dmaengine_terminate_all(iprtd->asrc_dma_chan); + dmaengine_terminate_all(iprtd->asrc_p2p_dma_chan); + asrc_stop_conv(iprtd->asrc_index); + } else { + dmaengine_terminate_all(iprtd->dma_chan); + } break; default: return -EINVAL; diff --git a/sound/soc/imx/imx-pcm.h b/sound/soc/imx/imx-pcm.h new file mode 100644 index 000000000000..be0d5ffbe5d1 --- /dev/null +++ b/sound/soc/imx/imx-pcm.h @@ -0,0 +1,70 @@ +/* + * MXC ALSA Soc Driver + * + * Copyright (C) 2012 Freescale 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _IMX_PCM_H +#define _IMX_PCM_H + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/dmaengine.h> + +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include <mach/dma.h> + +#include "imx-ssi.h" + +struct imx_pcm_runtime_data { + int period_bytes; + int periods; + int dma; + unsigned long offset; + unsigned long size; + void *buf; + int period_time; + struct dma_async_tx_descriptor *desc; + struct dma_chan *dma_chan; + struct imx_dma_data dma_data; + +#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_IMX_HAVE_PLATFORM_IMX_ASRC) + int asrc_index; + int asrc_enable; + struct dma_async_tx_descriptor *asrc_desc; + struct dma_chan *asrc_dma_chan; + struct imx_dma_data asrc_dma_data; + struct dma_async_tx_descriptor *asrc_p2p_desc; + struct dma_chan *asrc_p2p_dma_chan; + struct imx_dma_data asrc_p2p_dma_data; +#endif +}; +#endif diff --git a/sound/soc/imx/imx-wm8962.c b/sound/soc/imx/imx-wm8962.c index 3154cbcd7dfc..2885d3a47889 100644 --- a/sound/soc/imx/imx-wm8962.c +++ b/sound/soc/imx/imx-wm8962.c @@ -37,6 +37,7 @@ #include <mach/dma.h> #include <mach/clock.h> #include <mach/audmux.h> +#include <mach/gpio.h> #include "imx-ssi.h" #include "../codecs/wm8962.h" @@ -45,31 +46,17 @@ struct imx_priv { int sysclk; /*mclk from the outside*/ int codec_sysclk; int dai_hifi; + int hp_irq; + int hp_status; + int amic_irq; + int amic_status; struct platform_device *pdev; }; - +unsigned int sample_format = SNDRV_PCM_FMTBIT_S16_LE; static struct imx_priv card_priv; static struct snd_soc_card snd_soc_card_imx; struct clk *wm8962_mclk; -static struct snd_soc_jack hs_jack; - -/* Headphones jack detection DAPM pins */ -static struct snd_soc_jack_pin hs_jack_pins[] = { - { - .pin = "Headphone Jack", - .mask = SND_JACK_HEADPHONE, - }, -}; - -/* Headphones jack detection gpios */ -static struct snd_soc_jack_gpio hs_jack_gpios[] = { - [0] = { - /* gpio is set on per-platform basis */ - .name = "hp-gpio", - .report = SND_JACK_HEADPHONE, - .debounce_time = 200, - }, -}; +static struct snd_soc_codec *gcodec; static int imx_hifi_startup(struct snd_pcm_substream *substream) { @@ -97,6 +84,7 @@ static int imx_hifi_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 *codec = rtd->codec; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct imx_priv *priv = &card_priv; @@ -104,6 +92,10 @@ static int imx_hifi_hw_params(struct snd_pcm_substream *substream, unsigned int sample_rate = 44100; int ret = 0; u32 dai_format; + unsigned int pll_out; + + if (codec->dapm.bias_level != SND_SOC_BIAS_OFF) + return 0; dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM; @@ -125,16 +117,22 @@ static int imx_hifi_hw_params(struct snd_pcm_substream *substream, return ret; sample_rate = params_rate(params); + sample_format = params_format(params); + + if (sample_format == SNDRV_PCM_FORMAT_S24_LE) + pll_out = sample_rate * 192; + else + pll_out = sample_rate * 256; - ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL_INT, + ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL_MCLK, WM8962_FLL_MCLK, priv->sysclk, - sample_rate * 768); + pll_out); if (ret < 0) pr_err("Failed to start FLL: %d\n", ret); ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_FLL, - sample_rate * 768, + pll_out, SND_SOC_CLOCK_IN); if (ret < 0) { pr_err("Failed to set SYSCLK: %d\n", ret); @@ -145,17 +143,15 @@ static int imx_hifi_hw_params(struct snd_pcm_substream *substream, } static const struct snd_kcontrol_new controls[] = { - SOC_DAPM_PIN_SWITCH("Main Speaker"), - SOC_DAPM_PIN_SWITCH("DMIC"), + SOC_DAPM_PIN_SWITCH("Ext Spk"), }; /* imx card dapm widgets */ static const struct snd_soc_dapm_widget imx_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), - - SND_SOC_DAPM_MIC("AMIC", NULL), - SND_SOC_DAPM_SPK("Ext Spk", NULL), + SND_SOC_DAPM_MIC("AMIC", NULL), + SND_SOC_DAPM_MIC("DMIC", NULL), }; /* imx machine connections to the codec pins */ @@ -169,14 +165,150 @@ static const struct snd_soc_dapm_route audio_map[] = { { "MICBIAS", NULL, "AMIC" }, { "IN3R", NULL, "MICBIAS" }, + + { "DMIC", NULL, "MICBIAS" }, + { "DMICDAT", NULL, "DMIC" }, + }; +static void headphone_detect_handler(struct work_struct *wor) +{ + struct imx_priv *priv = &card_priv; + struct platform_device *pdev = priv->pdev; + struct mxc_audio_platform_data *plat = pdev->dev.platform_data; + char *envp[3]; + char *buf; + + /*sysfs_notify(&pdev->dev.kobj, NULL, "headphone");*/ + priv->hp_status = gpio_get_value(plat->hp_gpio); + + /* setup a message for userspace headphone in */ + buf = kmalloc(32, GFP_ATOMIC); + if (!buf) { + pr_err("%s kmalloc failed\n", __func__); + return; + } + + if (priv->hp_status != plat->hp_active_low) + snprintf(buf, 32, "STATE=%d", 2); + else + snprintf(buf, 32, "STATE=%d", 0); + + envp[0] = "NAME=headphone"; + envp[1] = buf; + envp[2] = NULL; + kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, envp); + kfree(buf); + + enable_irq(priv->hp_irq); + + return; +} + +static DECLARE_DELAYED_WORK(hp_event, headphone_detect_handler); + +static irqreturn_t imx_headphone_detect_handler(int irq, void *data) +{ + disable_irq_nosync(irq); + schedule_delayed_work(&hp_event, msecs_to_jiffies(200)); + return IRQ_HANDLED; +} + +static ssize_t show_headphone(struct device_driver *dev, char *buf) +{ + struct imx_priv *priv = &card_priv; + struct platform_device *pdev = priv->pdev; + struct mxc_audio_platform_data *plat = pdev->dev.platform_data; + + /* determine whether hp is plugged in */ + priv->hp_status = gpio_get_value(plat->hp_gpio); + + if (priv->hp_status != plat->hp_active_low) + strcpy(buf, "headphone\n"); + else + strcpy(buf, "speaker\n"); + + return strlen(buf); +} + +static DRIVER_ATTR(headphone, S_IRUGO | S_IWUSR, show_headphone, NULL); + +static void amic_detect_handler(struct work_struct *work) +{ + struct imx_priv *priv = &card_priv; + struct platform_device *pdev = priv->pdev; + struct mxc_audio_platform_data *plat = pdev->dev.platform_data; + char *envp[3]; + char *buf; + + /* sysfs_notify(&pdev->dev.kobj, NULL, "amic"); */ + priv->amic_status = gpio_get_value(plat->mic_gpio); + + /* if amic is inserted, disable dmic */ + if (priv->amic_status != plat->mic_active_low) + snd_soc_dapm_nc_pin(&gcodec->dapm, "DMIC"); + else + snd_soc_dapm_enable_pin(&gcodec->dapm, "DMIC"); + + /* setup a message for userspace headphone in */ + buf = kmalloc(32, GFP_ATOMIC); + if (!buf) { + pr_err("%s kmalloc failed\n", __func__); + return; + } + + if (priv->amic_status == 0) + snprintf(buf, 32, "STATE=%d", 2); + else + snprintf(buf, 32, "STATE=%d", 0); + + envp[0] = "NAME=amic"; + envp[1] = buf; + envp[2] = NULL; + kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, envp); + kfree(buf); + + enable_irq(priv->amic_irq); +} + +static DECLARE_DELAYED_WORK(amic_event, amic_detect_handler); + +static irqreturn_t imx_amic_detect_handler(int irq, void *data) +{ + disable_irq_nosync(irq); + schedule_delayed_work(&amic_event, msecs_to_jiffies(200)); + return IRQ_HANDLED; +} + +static ssize_t show_amic(struct device_driver *dev, char *buf) +{ + struct imx_priv *priv = &card_priv; + struct platform_device *pdev = priv->pdev; + struct mxc_audio_platform_data *plat = pdev->dev.platform_data; + + /* determine whether amic is plugged in */ + priv->amic_status = gpio_get_value(plat->hp_gpio); + + if (priv->amic_status != plat->mic_active_low) + strcpy(buf, "amic\n"); + else + strcpy(buf, "dmic\n"); + + return strlen(buf); +} + +static DRIVER_ATTR(amic, S_IRUGO | S_IWUSR, show_amic, NULL); + static int imx_wm8962_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; - int ret; + struct imx_priv *priv = &card_priv; + struct platform_device *pdev = priv->pdev; + struct mxc_audio_platform_data *plat = pdev->dev.platform_data; + + gcodec = rtd->codec; -/* Add imx specific widgets */ + /* Add imx specific widgets */ snd_soc_dapm_new_controls(&codec->dapm, imx_dapm_widgets, ARRAY_SIZE(imx_dapm_widgets)); @@ -188,24 +320,15 @@ static int imx_wm8962_init(struct snd_soc_pcm_runtime *rtd) snd_soc_dapm_sync(&codec->dapm); - if (hs_jack_gpios[0].gpio != -1) { - /* Jack detection API stuff */ - ret = snd_soc_jack_new(codec, "Headphone Jack", - SND_JACK_HEADPHONE, &hs_jack); - if (ret) - return ret; + if (plat->mic_gpio != -1) { - ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), - hs_jack_pins); - if (ret) { - printk(KERN_ERR "failed to call snd_soc_jack_add_pins\n"); - return ret; - } + priv->amic_status = gpio_get_value(plat->mic_gpio); - ret = snd_soc_jack_add_gpios(&hs_jack, - ARRAY_SIZE(hs_jack_gpios), hs_jack_gpios); - if (ret) - printk(KERN_WARNING "failed to call snd_soc_jack_add_gpios\n"); + /* if amic is inserted, disable DMIC */ + if (priv->amic_status != plat->mic_active_low) + snd_soc_dapm_nc_pin(&gcodec->dapm, "DMIC"); + else + snd_soc_dapm_enable_pin(&gcodec->dapm, "DMIC"); } return 0; @@ -283,8 +406,43 @@ static int __devinit imx_wm8962_probe(struct platform_device *pdev) } priv->sysclk = plat->sysclk; - hs_jack_gpios[0].gpio = plat->hp_gpio; - hs_jack_gpios[0].invert = plat->hp_active_low; + priv->hp_irq = gpio_to_irq(plat->hp_gpio); + priv->amic_irq = gpio_to_irq(plat->mic_gpio); + + if (plat->hp_gpio != -1) { + ret = request_irq(priv->hp_irq, + imx_headphone_detect_handler, + IRQ_TYPE_EDGE_BOTH, pdev->name, priv); + + if (ret < 0) { + ret = -EINVAL; + return ret; + } + + ret = driver_create_file(pdev->dev.driver, + &driver_attr_headphone); + if (ret < 0) { + ret = -EINVAL; + return ret; + } + } + + if (plat->mic_gpio != -1) { + ret = request_irq(priv->amic_irq, + imx_amic_detect_handler, + IRQ_TYPE_EDGE_BOTH, pdev->name, priv); + + if (ret < 0) { + ret = -EINVAL; + return ret; + } + + ret = driver_create_file(pdev->dev.driver, &driver_attr_amic); + if (ret < 0) { + ret = -EINVAL; + return ret; + } + } return ret; } |