summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXinyu Chen <xinyu.chen@freescale.com>2012-04-09 13:34:16 +0800
committerXinyu Chen <xinyu.chen@freescale.com>2012-04-09 13:34:16 +0800
commit52c5341f1302a0b328e7dd5890c12729406256fc (patch)
tree687adeb27a395f512c2a0fa373bd173c437f9321
parentbee5f57f1e3e5174aa5390a330052e772afdc453 (diff)
parent3ec8c998827be5729ff98a0d7c097f7ad9ddbc6e (diff)
Merge remote branch 'fsl-linux-sdk/imx_3.0.15' into imx_3.0.15_android
-rw-r--r--arch/arm/configs/imx6_defconfig9
-rw-r--r--arch/arm/include/asm/assembler.h13
-rw-r--r--arch/arm/include/asm/domain.h8
-rw-r--r--arch/arm/include/asm/futex.h8
-rw-r--r--arch/arm/include/asm/uaccess.h16
-rw-r--r--arch/arm/lib/getuser.S12
-rw-r--r--arch/arm/lib/putuser.S28
-rw-r--r--arch/arm/lib/uaccess.S82
-rw-r--r--arch/arm/mach-mx6/Kconfig1
-rw-r--r--arch/arm/mach-mx6/board-mx6q_arm2.c1
-rw-r--r--arch/arm/mach-mx6/board-mx6q_sabreauto.c33
-rw-r--r--arch/arm/mach-mx6/board-mx6q_sabreauto.h3
-rw-r--r--arch/arm/mach-mx6/board-mx6q_sabrelite.c1
-rw-r--r--arch/arm/mach-mx6/board-mx6q_sabresd.c71
-rw-r--r--arch/arm/mach-mx6/board-mx6q_sabresd.h3
-rw-r--r--arch/arm/mach-mx6/board-mx6solo_sabreauto.h3
-rw-r--r--arch/arm/mach-mx6/clock.c128
-rw-r--r--arch/arm/mach-mx6/cpu_op-mx6.c54
-rw-r--r--arch/arm/mach-mx6/devices-imx6q.h2
-rw-r--r--arch/arm/mach-mx6/irq.c12
-rw-r--r--arch/arm/mach-mx6/usb_dr.c4
-rw-r--r--arch/arm/mach-mx6/usb_h1.c4
-rwxr-xr-xarch/arm/plat-mxc/devices/Kconfig3
-rwxr-xr-xarch/arm/plat-mxc/devices/Makefile1
-rw-r--r--arch/arm/plat-mxc/devices/platform-imx-vdoa.c60
-rwxr-xr-xarch/arm/plat-mxc/include/mach/devices-common.h9
-rw-r--r--arch/arm/plat-mxc/include/mach/dma.h1
-rwxr-xr-xarch/arm/plat-mxc/include/mach/ipu-v3.h15
-rw-r--r--drivers/cpufreq/cpufreq_interactive.c16
-rw-r--r--drivers/dma/imx-sdma.c85
-rw-r--r--drivers/mxc/dam/dam.c6
-rw-r--r--drivers/mxc/ipu3/Makefile2
-rw-r--r--drivers/mxc/ipu3/ipu_common.c165
-rw-r--r--drivers/mxc/ipu3/ipu_device.c2388
-rw-r--r--drivers/mxc/ipu3/ipu_disp.c4
-rw-r--r--drivers/mxc/ipu3/ipu_ic.c37
-rw-r--r--drivers/mxc/ipu3/ipu_param_mem.h49
-rw-r--r--drivers/mxc/ipu3/ipu_prv.h11
-rw-r--r--drivers/mxc/ipu3/ipu_regs.h13
-rw-r--r--drivers/mxc/ipu3/vdoa.c541
-rw-r--r--drivers/mxc/ipu3/vdoa.h69
-rwxr-xr-xdrivers/net/fec.c24
-rw-r--r--drivers/rtc/rtc-snvs.c6
-rw-r--r--drivers/tty/serial/imx.c12
-rwxr-xr-xdrivers/usb/otg/fsl_otg.c7
-rw-r--r--include/linux/fsl_devices.h5
-rw-r--r--include/linux/ipu.h10
-rw-r--r--include/linux/mxc_asrc.h1
-rw-r--r--sound/soc/codecs/wm8962.c404
-rw-r--r--sound/soc/imx/imx-cs42888.c131
-rw-r--r--sound/soc/imx/imx-esai.h41
-rw-r--r--sound/soc/imx/imx-pcm-dma-mx2.c211
-rw-r--r--sound/soc/imx/imx-pcm.h70
-rw-r--r--sound/soc/imx/imx-wm8962.c254
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(&param, 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, &param);
+ 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, &params);
- 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, &params);
+ 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, &params);
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, &param);
+
+ 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(&params, 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(&params, 0, 138, 12, (height / 2) - 1);
ipu_ch_param_set_field(&params, 1, 102, 14, (stride * 2) - 1);
} else {
+ /* note: for vdoa+vdi- ch8/9/10, always use band mode */
ipu_ch_param_set_field(&params, 0, 138, 12, height - 1);
ipu_ch_param_set_field(&params, 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(&params, 0, 107, 3, 3); /* bits/pixel */
ipu_ch_param_set_field(&params, 1, 85, 4, 0x8); /* pix format */
if ((ch == 8) || (ch == 9) || (ch == 10)) {
- ipu_ch_param_set_field(&params, 1, 78, 7, 15); /* burst size */
+ if (ipu->vdoa_en) {
+ ipu_ch_param_set_field(&params, 1, 78, 7, 31);
+ } else {
+ ipu_ch_param_set_field(&params, 1, 78, 7, 15);
+ }
} else {
ipu_ch_param_set_field(&params, 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(&params, 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(&params, 1, 78, 7, 63);
+ } else {
+ ipu_ch_param_set_field(&params, 1, 78, 7, 15);
+ /* top/bottom field in one buffer*/
+ uv_stride = uv_stride*2;
+ }
} else {
ipu_ch_param_set_field(&params, 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;
}