diff options
author | Xinyu Chen <xinyu.chen@freescale.com> | 2012-05-02 11:02:43 +0800 |
---|---|---|
committer | Xinyu Chen <xinyu.chen@freescale.com> | 2012-05-02 11:02:43 +0800 |
commit | 3b9257f71a2a0dc0e0061ce2d455c504515c26d9 (patch) | |
tree | 6d19fa9ec5cf7973d4b599fce2a8e2861bd0dce9 | |
parent | 5f5288b61bc0596dbe1436a65f0bdf52b8d855cb (diff) | |
parent | 1b64ead9cdb4eae25789cfc5bbb7d8ad07dee402 (diff) |
Merge remote branch 'fsl-linux-sdk/imx_3.0.15_12.04.01' into imx_3.0.15_android
Conflicts:
sound/soc/imx/Makefile
-rw-r--r-- | arch/arm/include/asm/dma-mapping.h | 13 | ||||
-rw-r--r-- | arch/arm/include/asm/pgtable.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-mx6/board-mx6q_sabreauto.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-mx6/board-mx6q_sabreauto.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-mx6/bus_freq.c | 53 | ||||
-rw-r--r-- | arch/arm/mach-mx6/clock.c | 13 | ||||
-rw-r--r-- | arch/arm/mach-mx6/mx6_ddr_freq.S | 79 | ||||
-rw-r--r-- | arch/arm/mach-mx6/mx6_mmdc.c | 84 | ||||
-rw-r--r-- | arch/arm/mm/dma-mapping.c | 12 | ||||
-rw-r--r-- | arch/arm/plat-mxc/ahci_sata.c | 13 | ||||
-rwxr-xr-x | arch/arm/plat-mxc/clock.c | 14 | ||||
-rwxr-xr-x | arch/arm/plat-mxc/include/mach/clock.h | 3 | ||||
-rw-r--r-- | drivers/media/video/mxc/output/mxc_vout.c | 140 | ||||
-rw-r--r-- | drivers/mxc/ipu3/ipu_device.c | 4 | ||||
-rw-r--r-- | drivers/mxc/ipu3/vdoa.c | 1 | ||||
-rw-r--r-- | drivers/video/mxc/mxc_ipuv3_fb.c | 8 | ||||
-rw-r--r-- | sound/soc/codecs/mxc_hdmi.c | 3 | ||||
-rw-r--r-- | sound/soc/codecs/mxc_spdif.c | 33 | ||||
-rw-r--r-- | sound/soc/codecs/wm8962.c | 8 | ||||
-rw-r--r-- | sound/soc/imx/Makefile | 5 | ||||
-rw-r--r-- | sound/soc/imx/hdmi_pcm.S | 238 | ||||
-rw-r--r-- | sound/soc/imx/imx-hdmi-dma.c | 407 |
22 files changed, 971 insertions, 167 deletions
diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h index 4fff837363ed..d9b4badee6b2 100644 --- a/arch/arm/include/asm/dma-mapping.h +++ b/arch/arm/include/asm/dma-mapping.h @@ -186,6 +186,19 @@ static inline void dma_free_noncoherent(struct device *dev, size_t size, extern void *dma_alloc_coherent(struct device *, size_t, dma_addr_t *, gfp_t); /** + * dma_alloc_writethrough - allocate consistent memory for DMA + * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices + * @size: required memory size + * @handle: bus-specific DMA address + * + * Allocate some writethrough cached, for a device for + * performing DMA. This function allocates pages, and will + * return the CPU-viewed address, and sets @handle to be the + * device-viewed address. + */ +extern void *dma_alloc_writethrough(struct device *, size_t, dma_addr_t *, gfp_t); + +/** * dma_free_coherent - free memory allocated by dma_alloc_coherent * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices * @size: size of memory originally requested in dma_alloc_coherent diff --git a/arch/arm/include/asm/pgtable.h b/arch/arm/include/asm/pgtable.h index 5750704e0271..6682ab931451 100644 --- a/arch/arm/include/asm/pgtable.h +++ b/arch/arm/include/asm/pgtable.h @@ -232,6 +232,9 @@ extern pgprot_t pgprot_kernel; #define pgprot_writecombine(prot) \ __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_BUFFERABLE) +#define pgprot_writethrough(prot) \ + __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_WRITETHROUGH) + #ifdef CONFIG_ARM_DMA_MEM_BUFFERABLE #define pgprot_dmacoherent(prot) \ __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_BUFFERABLE | L_PTE_XN) diff --git a/arch/arm/mach-mx6/board-mx6q_sabreauto.c b/arch/arm/mach-mx6/board-mx6q_sabreauto.c index 571ae7ce03f2..ba4ad5b05f63 100644 --- a/arch/arm/mach-mx6/board-mx6q_sabreauto.c +++ b/arch/arm/mach-mx6/board-mx6q_sabreauto.c @@ -259,6 +259,7 @@ static int plt_sd3_pad_change(int clock) static const struct esdhc_platform_data mx6q_sabreauto_sd3_data __initconst = { .cd_gpio = SABREAUTO_SD3_CD, .wp_gpio = SABREAUTO_SD3_WP, + .keep_power_at_suspend = 1, .support_18v = 1, .support_8bit = 1, .delay_line = 0, @@ -268,6 +269,7 @@ static const struct esdhc_platform_data mx6q_sabreauto_sd3_data __initconst = { static const struct esdhc_platform_data mx6q_sabreauto_sd1_data __initconst = { .cd_gpio = SABREAUTO_SD1_CD, .wp_gpio = SABREAUTO_SD1_WP, + .keep_power_at_suspend = 1, }; diff --git a/arch/arm/mach-mx6/board-mx6q_sabreauto.h b/arch/arm/mach-mx6/board-mx6q_sabreauto.h index 4c74a6aba961..bffd109115c2 100644 --- a/arch/arm/mach-mx6/board-mx6q_sabreauto.h +++ b/arch/arm/mach-mx6/board-mx6q_sabreauto.h @@ -179,7 +179,7 @@ static iomux_v3_cfg_t mx6q_sabreauto_pads[] = { MX6Q_PAD_EIM_EB0__GPIO_2_28, /* USBOTG ID pin */ - MX6Q_PAD_ENET_RX_ER__ENET_RX_ER, + MX6Q_PAD_ENET_RX_ER__ANATOP_USBOTG_ID, /* VIDEO adv7180 INTRQ */ MX6Q_PAD_ENET_RXD0__GPIO_1_27, diff --git a/arch/arm/mach-mx6/bus_freq.c b/arch/arm/mach-mx6/bus_freq.c index e4244afe7ae7..b721bc0479f0 100644 --- a/arch/arm/mach-mx6/bus_freq.c +++ b/arch/arm/mach-mx6/bus_freq.c @@ -53,6 +53,7 @@ DEFINE_SPINLOCK(ddr_freq_lock); int low_bus_freq_mode; +int audio_bus_freq_mode; int high_bus_freq_mode; int med_bus_freq_mode; @@ -65,6 +66,7 @@ int bus_freq_scaling_is_active; int lp_high_freq; int lp_med_freq; +int lp_audio_freq; unsigned int ddr_low_rate; unsigned int ddr_med_rate; unsigned int ddr_normal_rate; @@ -97,6 +99,9 @@ static void reduce_bus_freq_handler(struct work_struct *work) if (low_bus_freq_mode || !low_freq_bus_used()) return; + if (audio_bus_freq_mode && lp_audio_freq) + return; + while (!mutex_trylock(&bus_freq_mutex)) msleep(1); @@ -106,26 +111,42 @@ static void reduce_bus_freq_handler(struct work_struct *work) mutex_unlock(&bus_freq_mutex); return; } - clk_enable(pll3); + if (audio_bus_freq_mode && lp_audio_freq) { + mutex_unlock(&bus_freq_mutex); + return; + } + + clk_enable(pll3); - update_ddr_freq(24000000); + if (lp_audio_freq) { + /* Need to ensure that PLL2_PFD_400M is kept ON. */ + clk_enable(pll2_400); + update_ddr_freq(50000000); + audio_bus_freq_mode = 1; + low_bus_freq_mode = 0; + } else { + update_ddr_freq(24000000); + if (audio_bus_freq_mode) + clk_disable(pll2_400); + low_bus_freq_mode = 1; + audio_bus_freq_mode = 0; + } if (med_bus_freq_mode) clk_disable(pll2_400); - low_bus_freq_mode = 1; high_bus_freq_mode = 0; med_bus_freq_mode = 0; - /* Power gate the PU LDO. */ - org_ldo = reg = __raw_readl(ANADIG_REG_CORE); - reg &= ~(ANADIG_REG_TARGET_MASK << ANADIG_REG1_PU_TARGET_OFFSET); - __raw_writel(reg, ANADIG_REG_CORE); - - mutex_unlock(&bus_freq_mutex); + if (cpu_is_mx6q()) { + /* Power gate the PU LDO. */ + org_ldo = reg = __raw_readl(ANADIG_REG_CORE); + reg &= ~(ANADIG_REG_TARGET_MASK << ANADIG_REG1_PU_TARGET_OFFSET); + __raw_writel(reg, ANADIG_REG_CORE); + } clk_disable(pll3); - + mutex_unlock(&bus_freq_mutex); } @@ -176,26 +197,30 @@ int set_high_bus_freq(int high_bus_freq) clk_enable(pll3); /* Enable the PU LDO */ - if (low_bus_freq_mode) + if (cpu_is_mx6q() && low_bus_freq_mode) __raw_writel(org_ldo, ANADIG_REG_CORE); if (high_bus_freq) { update_ddr_freq(ddr_normal_rate); if (med_bus_freq_mode) clk_disable(pll2_400); - low_bus_freq_mode = 0; high_bus_freq_mode = 1; med_bus_freq_mode = 0; } else { clk_enable(pll2_400); update_ddr_freq(ddr_med_rate); - low_bus_freq_mode = 0; high_bus_freq_mode = 0; med_bus_freq_mode = 1; } + if (audio_bus_freq_mode) + clk_disable(pll2_400); + low_bus_freq_mode = 0; + audio_bus_freq_mode = 0; + + low_bus_freq_mode = 0; - mutex_unlock(&bus_freq_mutex); clk_disable(pll3); + mutex_unlock(&bus_freq_mutex); return 0; } diff --git a/arch/arm/mach-mx6/clock.c b/arch/arm/mach-mx6/clock.c index 16927e5e182b..12a8fcf8464c 100644 --- a/arch/arm/mach-mx6/clock.c +++ b/arch/arm/mach-mx6/clock.c @@ -47,7 +47,7 @@ extern struct regulator *cpu_regulator; extern struct cpu_op *(*get_cpu_op)(int *op); extern int lp_high_freq; extern int lp_med_freq; -extern int mx6q_revision(void); +extern int lp_audio_freq; void __iomem *apll_base; static struct clk pll1_sys_main_clk; @@ -2494,6 +2494,7 @@ static struct clk ssi1_clk = { #else .secondary = &mmdc_ch0_axi_clk[0], #endif + .flags = AHB_AUDIO_SET_POINT | CPU_FREQ_TRIG_UPDATE, }; static unsigned long _clk_ssi2_get_rate(struct clk *clk) @@ -2567,6 +2568,7 @@ static struct clk ssi2_clk = { #else .secondary = &mmdc_ch0_axi_clk[0], #endif + .flags = AHB_AUDIO_SET_POINT | CPU_FREQ_TRIG_UPDATE, }; static unsigned long _clk_ssi3_get_rate(struct clk *clk) @@ -2639,6 +2641,7 @@ static struct clk ssi3_clk = { #else .secondary = &mmdc_ch0_axi_clk[0], #endif + .flags = AHB_AUDIO_SET_POINT | CPU_FREQ_TRIG_UPDATE, }; static unsigned long _clk_ldb_di_round_rate(struct clk *clk, @@ -5220,10 +5223,6 @@ int __init mx6_clocks_init(unsigned long ckil, unsigned long osc, /* S/PDIF */ clk_set_parent(&spdif0_clk[0], &pll3_pfd_454M); - /* pxp & epdc */ - clk_set_parent(&ipu2_clk, &pll2_pfd_400M); - clk_set_rate(&ipu2_clk, 200000000); - if (mx6q_revision() == IMX_CHIP_REVISION_1_0) { gpt_clk[0].parent = &ipg_perclk; gpt_clk[0].get_rate = NULL; @@ -5234,6 +5233,9 @@ int __init mx6_clocks_init(unsigned long ckil, unsigned long osc, } if (cpu_is_mx6dl()) { + /* pxp & epdc */ + clk_set_parent(&ipu2_clk, &pll2_pfd_400M); + clk_set_rate(&ipu2_clk, 200000000); if (epdc_enabled) clk_set_parent(&ipu2_di_clk[1], &pll5_video_main_clk); else @@ -5245,6 +5247,7 @@ int __init mx6_clocks_init(unsigned long ckil, unsigned long osc, lp_high_freq = 0; lp_med_freq = 0; + lp_audio_freq = 0; /* Turn OFF all unnecessary PHYs. */ if (cpu_is_mx6q()) { diff --git a/arch/arm/mach-mx6/mx6_ddr_freq.S b/arch/arm/mach-mx6/mx6_ddr_freq.S index 766d867ee1c4..85af2a402e68 100644 --- a/arch/arm/mach-mx6/mx6_ddr_freq.S +++ b/arch/arm/mach-mx6/mx6_ddr_freq.S @@ -191,16 +191,16 @@ switch_pre_periph_clk_50: orr r0, r0, #0xC0000 str r0, [r6, #0x18] - /* Set the MMDC_DIV=4, AXI_DIV = 4, AHB_DIV=6 (need to maintain GPT divider). */ + /* Set the MMDC_DIV=4, AXI_DIV = 4, AHB_DIV=8 (need to maintain GPT divider). */ ldr r0, [r6, #0x14] ldr r2, =0x3F1C00 bic r0, r0, r2 orr r0, r0, #0x180000 - orr r0, r0, #0x10000 + orr r0, r0, #0x30000 /* If changing AHB divider remember to change the IPGPER divider too below. */ - orr r0, r0, #0xC00 + orr r0, r0, #0x1C00 str r0, [r6, #0x14] wait_div_update_50: @@ -348,7 +348,7 @@ ddr_freq_change: /* set CON_REG */ ldr r0, =0x8000 - str r0, [r5, #0x1C] + str r0, [r5, #0x1C] poll_conreq_set_1: ldr r0, [r5, #0x1C] and r0, r0, #0x4000 @@ -587,7 +587,7 @@ poll_conreq_clear_1: dll_on_mode: /* assert DVFS - enter self refresh mode */ ldr r0, [r5, #0x404] - orr r0, r0, #0x200000 + orr r0, r0, #0x200000 str r0, [r5, #0x404] /* de-assert CON_REQ */ @@ -632,7 +632,7 @@ poll_dvfs_clear_2: /* if DLL is currently off, turn it back on */ cmp r9, #0 - beq update_calibration + beq update_calibration_only ldr r0, =0xa5390003 str r0, [r5, #0x800] @@ -697,16 +697,17 @@ update_iomux1: @//setmem /32 0x021b0014 = 0x01ff00db @// MMDC0_MDCFG2 - tRRD - 4ck; tWTR - 4ck; tRTP - 4ck; tDLLK - 512ck @//setmem /32 0x021b0018 = 0x00081740 @// MMDC0_MDMISC, RALAT=0x5 (original value) */ + ldr r9, [r8] @size of array + add r8, r8, #8 @skip first eight bytes in array + ldr r0, [r8, #0x0] @ offset + ldr r3, [r8, #0x4] @ value + str r3, [r5, r0] + add r8, r8, #8 - ldr r0, [r5, #0x0C] - bic r0, r0, #0xf - orr r0, r0, #0x5 - str r0, [r5, #0x0C] - - ldr r0, [r5, #0x10] - bic r0, r0, #0x7 - orr r0, r0, #0x4 - str r0, [r5, #0x10] + ldr r0, [r8, #0x0] @ offset + ldr r3, [r8, #0x4] @ value + str r3, [r5, r0] + add r8, r8, #8 /* update MISC register: WALAT, RALAT */ ldr r0, =0x00081740 @@ -762,11 +763,17 @@ cont8: @//setmem /32 0x021b001c = 0x04088032 @//setmem /32 0x021b001c = 0x0408803a */ - ldr r0, =0x04088032 - str r0, [r5, #0x1C] + /* MR2 - CS0 */ + ldr r0, [r8, #0x0] @ offset + ldr r3, [r8, #0x4] @ value + str r3, [r5, r0] + add r8, r8, #8 - ldr r0, =0x0408803a - str r0, [r5, #0x1C] + /*MR2 - CS1 */ + ldr r0, [r8, #0x0] @ offset + ldr r3, [r8, #0x4] @ value + str r3, [r5, r0] + add r8, r8, #8 ldr r0, =0x00428031 str r0, [r5, #0x1C] @@ -778,11 +785,17 @@ cont8: @// setmem /32 0x021b001c = 0x08408030 @// setmem /32 0x021b001c = 0x08408038 */ - ldr r0, =0x08408030 - str r0, [r5, #0x1C] + /* MR1 - CS0 */ + ldr r0, [r8, #0x0] @ offset + ldr r3, [r8, #0x4] @ value + str r3, [r5, r0] + add r8, r8, #8 - ldr r0, =0x08408038 - str r0, [r5, #0x1C] + /*MR1 - CS1 */ + ldr r0, [r8, #0x0] @ offset + ldr r3, [r8, #0x4] @ value + str r3, [r5, r0] + add r8, r8, #8 /* issue a zq command @// setmem /32 0x021b001c = 0x04000040 @@ -798,8 +811,11 @@ cont8: @//setmem /32 0x021b0818 = 0x00022225 @// DDR_PHY_P0_MPODTCTRL @//setmem /32 0x021b4818 = 0x00022225 @// DDR_PHY_P1_MPODTCTRL */ - ldr r0, =0x00022225 - str r0, [r5, #0x818] + ldr r0, [r8, #0x0] @ offset + ldr r3, [r8, #0x4] @ value + str r3, [r5, r0] + add r8, r8, #8 + ldr r2, =0x4818 str r0, [r5, r2] @@ -827,10 +843,19 @@ cont15: orr r0, r0, #0x5500 str r0, [r5, #0x4] + b update_calibration + +update_calibration_only: + ldr r1, [r8] @ size of array + sub r1, r1, #7 @ first 7 entries are not related to calibration + add r8, r8, #64 @ Skip the first 7 entries that are needed only when DLL was OFF + count entry. + b update_calib + update_calibration: /* Write the new calibration values. */ - ldr r1, [r8] @size of array - add r8, r8, #8 @skip first eight bytes in array + mov r1, r9 @ size of array + sub r1, r1, #7 @ first 7 entries are not related to calibration + update_calib: ldr r0, [r8, #0x0] @ offset ldr r3, [r8, #0x4] @ value diff --git a/arch/arm/mach-mx6/mx6_mmdc.c b/arch/arm/mach-mx6/mx6_mmdc.c index 2b5ce842489e..2498f01e0889 100644 --- a/arch/arm/mach-mx6/mx6_mmdc.c +++ b/arch/arm/mach-mx6/mx6_mmdc.c @@ -64,12 +64,22 @@ static int ddr_settings_size; static int iomux_settings_size; static volatile unsigned int cpus_in_wfe; static volatile bool wait_for_ddr_freq_update; - +static int curr_ddr_rate; #define MIN_DLL_ON_FREQ 333000000 #define MAX_DLL_OFF_FREQ 125000000 -unsigned long ddr3_mmdc_regs_offsets[][2] = { +unsigned long ddr3_dll_mx6q[][2] = { + {0x0c, 0x0}, + {0x10, 0x0}, + {0x1C, 0x04088032}, + {0x1C, 0x0408803a}, + {0x1C, 0x08408030}, + {0x1C, 0x08408038}, + {0x818, 0x0}, +}; + +unsigned long ddr3_calibration[][2] = { {0x83c, 0x0}, {0x840, 0x0}, {0x483c, 0x0}, @@ -80,6 +90,16 @@ unsigned long ddr3_mmdc_regs_offsets[][2] = { {0x4850, 0x0}, }; +unsigned long ddr3_dll_mx6dl[][2] = { + {0x0c, 0x0}, + {0x10, 0x0}, + {0x1C, 0x04008032}, + {0x1C, 0x0400803a}, + {0x1C, 0x07208030}, + {0x1C, 0x07208038}, + {0x818, 0x0}, +}; + unsigned long iomux_offsets_mx6q[][2] = { {0x5A8, 0x0}, {0x5B0, 0x0}, @@ -90,6 +110,7 @@ unsigned long iomux_offsets_mx6q[][2] = { {0x5B8, 0x0}, {0x5C0, 0x0}, }; + unsigned long iomux_offsets_mx6dl[][2] = { {0x4BC, 0x0}, {0x4C0, 0x0}, @@ -146,13 +167,14 @@ irqreturn_t wait_in_wfe_irq(int irq, void *dev_id) wfe(); *((char *)(&cpus_in_wfe) + (u8)me) = 0; + return IRQ_HANDLED; } /* Change the DDR frequency. */ int update_ddr_freq(int ddr_rate) { - int i; + int i, j; unsigned int reg; bool dll_off = false; unsigned int online_cpus = 0; @@ -162,17 +184,26 @@ int update_ddr_freq(int ddr_rate) if (!can_change_ddr_freq()) return -1; + if (ddr_rate == curr_ddr_rate) + return 0; + if (low_bus_freq_mode) dll_off = true; iram_ddr_settings[0][0] = ddr_settings_size; iram_iomux_settings[0][0] = iomux_settings_size; - if (ddr_rate == ddr_med_rate) { - for (i = 0; i < iram_ddr_settings[0][0]; i++) { + if (ddr_rate == ddr_med_rate && cpu_is_mx6q()) { + for (i = 0; i < ARRAY_SIZE(ddr3_dll_mx6q); i++) { iram_ddr_settings[i + 1][0] = - ddr3_400[i][0]; + normal_mmdc_settings[i][0]; iram_ddr_settings[i + 1][1] = - ddr3_400[i][1]; + normal_mmdc_settings[i][1]; + } + for (j = 0, i = ARRAY_SIZE(ddr3_dll_mx6q); i < iram_ddr_settings[0][0]; j++, i++) { + iram_ddr_settings[i + 1][0] = + ddr3_400[j][0]; + iram_ddr_settings[i + 1][1] = + ddr3_400[j][1]; } } else if (ddr_rate == ddr_normal_rate) { for (i = 0; i < iram_ddr_settings[0][0]; i++) { @@ -190,7 +221,6 @@ int update_ddr_freq(int ddr_rate) *((char *)(&cpus_in_wfe) + (u8)me) = 0xff; wait_for_ddr_freq_update = true; - for_each_online_cpu(cpu) { *((char *)(&online_cpus) + (u8)cpu) = 0xff; if (cpu != me) { @@ -206,6 +236,8 @@ int update_ddr_freq(int ddr_rate) /* Now we can change the DDR frequency. */ mx6_change_ddr_freq(ddr_rate, iram_ddr_settings, dll_off, iram_iomux_settings); + curr_ddr_rate = ddr_rate; + /* DDR frequency change is done . */ wait_for_ddr_freq_update = false; @@ -229,19 +261,37 @@ int init_mmdc_settings(void) gic_dist_base = ioremap(IC_DISTRIBUTOR_BASE_ADDR, SZ_16K); gic_cpu_base = ioremap(IC_INTERFACES_BASE_ADDR, SZ_16K); - normal_mmdc_settings = ddr3_mmdc_regs_offsets; - ddr_settings_size = ARRAY_SIZE(ddr3_mmdc_regs_offsets); + if (cpu_is_mx6q()) + ddr_settings_size = ARRAY_SIZE(ddr3_dll_mx6q) + ARRAY_SIZE(ddr3_calibration); + if (cpu_is_mx6dl()) + ddr_settings_size = ARRAY_SIZE(ddr3_dll_mx6dl) + ARRAY_SIZE(ddr3_calibration); + + normal_mmdc_settings = kmalloc((ddr_settings_size * 8), GFP_KERNEL); + if (cpu_is_mx6q()) { + memcpy(normal_mmdc_settings, ddr3_dll_mx6q, sizeof(ddr3_dll_mx6q)); + memcpy(((char *)normal_mmdc_settings + sizeof(ddr3_dll_mx6q)), ddr3_calibration, sizeof(ddr3_calibration)); + } + if (cpu_is_mx6dl()) { + memcpy(normal_mmdc_settings, ddr3_dll_mx6dl, sizeof(ddr3_dll_mx6dl)); + memcpy(((char *)normal_mmdc_settings + sizeof(ddr3_dll_mx6dl)), ddr3_calibration, sizeof(ddr3_calibration)); + } /* Store the original DDR settings at boot. */ for (i = 0; i < ddr_settings_size; i++) { - normal_mmdc_settings[i][1] = - __raw_readl(mmdc_base - + normal_mmdc_settings[i][0]); + /*Writes via command mode register cannot be read back. + * Hence hardcode them in the initial static array. + * This may require modification on a per customer basis. + */ + if (normal_mmdc_settings[i][0] != 0x1C) + normal_mmdc_settings[i][1] = + __raw_readl(mmdc_base + + normal_mmdc_settings[i][0]); } + /* Store the size of the array in iRAM also, * increase the size by 8 bytes. */ - iram_ddr_settings = iram_alloc(ddr_settings_size + 8, &iram_paddr); + iram_ddr_settings = iram_alloc((ddr_settings_size * 8) + 8, &iram_paddr); if (iram_ddr_settings == NULL) { printk(KERN_DEBUG "%s: failed to allocate iRAM memory for ddr settings\n", @@ -249,10 +299,11 @@ int init_mmdc_settings(void) return ENOMEM; } + iomux_settings_size = ARRAY_SIZE(iomux_offsets_mx6q); /* Store the size of the iomux settings in iRAM also, * increase the size by 8 bytes. */ - iram_iomux_settings = iram_alloc(iomux_settings_size + 8, &iram_paddr); + iram_iomux_settings = iram_alloc((iomux_settings_size * 8) + 8, &iram_paddr); if (iram_iomux_settings == NULL) { printk(KERN_DEBUG "%s: failed to allocate iRAM memory for iomuxr settings\n", @@ -262,7 +313,6 @@ int init_mmdc_settings(void) /* Store the IOMUX settings at boot. */ if (cpu_is_mx6q()) { - iomux_settings_size = ARRAY_SIZE(iomux_offsets_mx6q); for (i = 0; i < iomux_settings_size; i++) { iomux_offsets_mx6q[i][1] = __raw_readl(iomux_base @@ -272,8 +322,8 @@ int init_mmdc_settings(void) } irq_used = irqs_used_mx6q; } + if (cpu_is_mx6dl()) { - iomux_settings_size = ARRAY_SIZE(iomux_offsets_mx6dl); for (i = 0; i < iomux_settings_size; i++) { iomux_offsets_mx6dl[i][1] = __raw_readl(iomux_base diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index f96d2c730020..79e05f69631c 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -357,6 +357,18 @@ dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_ } EXPORT_SYMBOL(dma_alloc_writecombine); +/* + * Allocate DMA-writethrough memory space and return both the kernel remapped + * virtual and bus address for that space. + */ +void * +dma_alloc_writethrough(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp) +{ + return __dma_alloc(dev, size, handle, gfp, + pgprot_writethrough(pgprot_kernel)); +} +EXPORT_SYMBOL(dma_alloc_writethrough); + static int dma_mmap(struct device *dev, struct vm_area_struct *vma, void *cpu_addr, dma_addr_t dma_addr, size_t size) { diff --git a/arch/arm/plat-mxc/ahci_sata.c b/arch/arm/plat-mxc/ahci_sata.c index 0d78a123a469..cee34b6f70d9 100644 --- a/arch/arm/plat-mxc/ahci_sata.c +++ b/arch/arm/plat-mxc/ahci_sata.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. All Rights Reserved. */ /* @@ -18,11 +18,13 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include <asm/errno.h> #include <linux/io.h> #include <linux/clk.h> #include <linux/kernel.h> #include <linux/delay.h> -#include <asm/errno.h> + +#include <mach/hardware.h> #include <mach/ahci_sata.h> int write_phy_ctl_ack_polling(u32 data, void __iomem *mmio, @@ -140,6 +142,13 @@ int sata_init(void __iomem *addr, unsigned long timer1ms) u32 tmpdata; int iterations = 20; + /* + * Make sure that SATA PHY is enabled + * The PDDQ mode is disabled. + */ + tmpdata = readl(addr + PORT_PHY_CTL); + writel(tmpdata & (~PORT_PHY_CTL_PDDQ_LOC), addr + PORT_PHY_CTL); + /* Reset HBA */ writel(HOST_RESET, addr + HOST_CTL); diff --git a/arch/arm/plat-mxc/clock.c b/arch/arm/plat-mxc/clock.c index f6e14a3cf2f5..43d33376b52d 100755 --- a/arch/arm/plat-mxc/clock.c +++ b/arch/arm/plat-mxc/clock.c @@ -47,6 +47,8 @@ extern int dvfs_core_is_active; extern int lp_high_freq; extern int lp_med_freq; +extern int lp_audio_freq; +extern int audio_bus_freq_mode; extern int low_bus_freq_mode; extern int high_bus_freq_mode; extern int med_bus_freq_mode; @@ -115,13 +117,19 @@ int clk_enable(struct clk *clk) lp_high_freq++; else if (clk->flags & AHB_MED_SET_POINT) lp_med_freq++; + else if (clk->flags & AHB_AUDIO_SET_POINT) + lp_audio_freq++; if ((clk->flags & CPU_FREQ_TRIG_UPDATE) && (clk_get_usecount(clk) == 0)) { if (!(clk->flags & (AHB_HIGH_SET_POINT | AHB_MED_SET_POINT))) { - if (low_freq_bus_used() && !low_bus_freq_mode) - set_low_bus_freq(); + if (low_freq_bus_used()) { + if ((clk->flags & AHB_AUDIO_SET_POINT) & !audio_bus_freq_mode) + set_low_bus_freq(); + else if (!low_bus_freq_mode) + set_low_bus_freq(); + } } else { if ((clk->flags & AHB_MED_SET_POINT) && !med_bus_freq_mode) @@ -164,6 +172,8 @@ void clk_disable(struct clk *clk) lp_high_freq--; else if (clk->flags & AHB_MED_SET_POINT) lp_med_freq--; + else if (clk->flags & AHB_AUDIO_SET_POINT) + lp_audio_freq--; mutex_lock(&clocks_mutex); __clk_disable(clk); diff --git a/arch/arm/plat-mxc/include/mach/clock.h b/arch/arm/plat-mxc/include/mach/clock.h index a66b0018c944..e150579e5ced 100755 --- a/arch/arm/plat-mxc/include/mach/clock.h +++ b/arch/arm/plat-mxc/include/mach/clock.h @@ -1,5 +1,5 @@ /* - * Copyright 2005-2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2005-2012 Freescale Semiconductor, Inc. * Copyright 2008 Juergen Beisert, kernel@pengutronix.de * * This program is free software; you can redistribute it and/or @@ -71,6 +71,7 @@ int clk_get_usecount(struct clk *clk); #define CPU_FREQ_TRIG_UPDATE (1 << 3) /* CPUFREQ trig update */ #define AHB_HIGH_SET_POINT (1 << 4) /* Requires max AHB clock */ #define AHB_MED_SET_POINT (1 << 5) /* Requires med AHB clock */ +#define AHB_AUDIO_SET_POINT (1 << 6) /* Requires LOW AHB, but higher DDR clock */ unsigned long mxc_decode_pll(unsigned int pll, u32 f_ref); diff --git a/drivers/media/video/mxc/output/mxc_vout.c b/drivers/media/video/mxc/output/mxc_vout.c index 7542ca5f2062..aebe57fda0c5 100644 --- a/drivers/media/video/mxc/output/mxc_vout.c +++ b/drivers/media/video/mxc/output/mxc_vout.c @@ -56,9 +56,18 @@ struct mxc_vout_fb { bool disp_support_windows; }; +struct dma_mem { + void *vaddr; + dma_addr_t paddr; + size_t size; +}; + struct mxc_vout_output { int open_cnt; struct fb_info *fbi; + unsigned long fb_smem_start; + unsigned long fb_smem_len; + struct fb_var_screeninfo fb_var; struct video_device *vfd; struct mutex mutex; struct mutex task_lock; @@ -77,15 +86,13 @@ struct mxc_vout_output { bool disp_support_csc; bool fmt_init; + bool release; + bool save_var; bool bypass_pp; bool is_vdoaipu_task; struct ipu_task task; struct ipu_task vdoa_task; - struct vdoa_mem { - void *vaddr; - dma_addr_t paddr; - size_t size; - } vdoa_dma; + struct dma_mem vdoa_dma; bool timer_stop; struct timer_list timer; @@ -428,6 +435,7 @@ static int show_buf(struct mxc_vout_output *vout, int idx, int ret; u32 is_1080p; u32 yres = 0; + u32 fb_base = 0; memcpy(&var, &fbi->var, sizeof(var)); @@ -437,11 +445,13 @@ static int show_buf(struct mxc_vout_output *vout, int idx, * NOTE: should not do other fb operation during v4l2 */ console_lock(); + fb_base = fbi->fix.smem_start; fbi->fix.smem_start = vout->task.output.paddr; fbi->var.yoffset = ipos->y + 1; var.xoffset = ipos->x; var.yoffset = ipos->y; ret = fb_pan_display(fbi, &var); + fbi->fix.smem_start = fb_base; console_unlock(); } else { console_lock(); @@ -472,6 +482,7 @@ static void disp_work_func(struct work_struct *work) int ret = 0; u32 is_1080p; u32 ocrop_h = 0; + u32 tiled_interlaced = 0; v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "disp work begin one frame\n"); @@ -532,6 +543,9 @@ static void disp_work_func(struct work_struct *work) } if (vout->is_vdoaipu_task) { vout->vdoa_task.input.paddr = vout->task.input.paddr; + if (deinterlace_3_field(vout)) + vout->vdoa_task.input.paddr_n = + vout->task.input.paddr_n; vout->vdoa_task.output.paddr = vout->vdoa_dma.paddr; ret = ipu_queue_task(&vout->vdoa_task); if (ret < 0) { @@ -539,8 +553,14 @@ static void disp_work_func(struct work_struct *work) goto err; } vout->task.input.paddr = vout->vdoa_task.output.paddr; + if (vout->task.input.deinterlace.enable) { + tiled_interlaced = 1; + vout->task.input.deinterlace.enable = 0; + } } ret = ipu_queue_task(&vout->task); + if (tiled_interlaced) + vout->task.input.deinterlace.enable = 1; if (ret < 0) { mutex_unlock(&vout->task_lock); goto err; @@ -593,6 +613,10 @@ static void disp_work_func(struct work_struct *work) spin_unlock_irqrestore(q->irqlock, flags); v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "disp work finish one frame\n"); + if (!vout->save_var) { + memcpy(&vout->fb_var, &vout->fbi->var, sizeof(vout->fb_var)); + vout->save_var = true; + } return; err: @@ -838,7 +862,7 @@ static int mxc_vidioc_g_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *f) { struct mxc_vout_output *vout = fh; - struct v4l2_rect *rect = NULL; + struct v4l2_rect rect; f->fmt.pix.width = vout->task.input.width; f->fmt.pix.height = vout->task.input.height; @@ -846,11 +870,13 @@ static int mxc_vidioc_g_fmt_vid_out(struct file *file, void *fh, f->fmt.pix.sizeimage = get_frame_size(vout); if (f->fmt.pix.priv) { - rect = (struct v4l2_rect *)f->fmt.pix.priv; - rect->left = vout->task.input.crop.pos.x; - rect->top = vout->task.input.crop.pos.y; - rect->width = vout->task.input.crop.w; - rect->height = vout->task.input.crop.h; + rect.left = vout->task.input.crop.pos.x; + rect.top = vout->task.input.crop.pos.y; + rect.width = vout->task.input.crop.w; + rect.height = vout->task.input.crop.h; + if (copy_to_user((void __user *)f->fmt.pix.priv, + &rect, sizeof(rect))) + return -EFAULT; } v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "frame_size:0x%x, pix_fmt:0x%x\n", @@ -909,7 +935,10 @@ static inline int vdoaipu_try_task(struct mxc_vout_output *vout) size_t size; struct ipu_task *ipu_task = &vout->task; struct ipu_task *vdoa_task = &vout->vdoa_task; + u32 deinterlace = 0; + if (vout->task.input.deinterlace.enable) + deinterlace = 1; is_1080p_stream = CHECK_TILED_1080P_STREAM(vout); if (is_1080p_stream) ipu_task->input.crop.h = VALID_HEIGHT_1080P; @@ -950,9 +979,12 @@ static inline int vdoaipu_try_task(struct mxc_vout_output *vout) vout->vdoa_dma.vaddr = dma_alloc_coherent(vout->vbq.dev, vout->vdoa_dma.size, &vout->vdoa_dma.paddr, - GFP_DMA | GFP_KERNEL); - if (!vout->vdoa_dma.vaddr) + GFP_KERNEL); + if (!vout->vdoa_dma.vaddr) { + v4l2_err(vout->vfd->v4l2_dev, + "cannot get vdoa dma buf size:0x%x\n", size); return -ENOMEM; + } v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "alloc vdoa_dma.size:0x%x, paddr:0x%x\n", vout->vdoa_dma.size, @@ -971,7 +1003,11 @@ static inline int vdoaipu_try_task(struct mxc_vout_output *vout) ipu_task->input.width = vdoa_task->output.width; ipu_task->input.crop.w = icrop_w; } + if (deinterlace) + ipu_task->input.deinterlace.enable = 0; ret = ipu_try_task(vout); + if (deinterlace) + ipu_task->input.deinterlace.enable = 1; return ret; } @@ -1031,17 +1067,22 @@ static int mxc_vout_try_task(struct mxc_vout_output *vout) static int mxc_vout_try_format(struct mxc_vout_output *vout, struct v4l2_format *f) { int ret = 0; - struct v4l2_rect *rect = NULL; + struct v4l2_rect rect; u32 o_height = 0; u32 ocrop_h = 0; u32 is_1080p; + if (f->fmt.pix.priv && copy_from_user(&rect, + (void __user *)f->fmt.pix.priv, sizeof(rect))) + return -EFAULT; + vout->task.input.width = f->fmt.pix.width; vout->task.input.height = f->fmt.pix.height; vout->task.input.format = f->fmt.pix.pixelformat; if (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format) { - if (vout->task.input.width > MAX_INTERLACED_WIDTH) + if ((vout->task.input.width > MAX_INTERLACED_WIDTH) || + (vout->task.input.deinterlace.motion == HIGH_MOTION)) return -EINVAL; v4l2_info(vout->vfd->v4l2_dev, "tiled fmt enable deinterlace.\n"); @@ -1060,13 +1101,13 @@ static int mxc_vout_try_format(struct mxc_vout_output *vout, struct v4l2_format "V4L2_FIELD_ALTERNATE field format not supported yet!\n"); break; case V4L2_FIELD_INTERLACED_TB: - v4l2_info(vout->vfd->v4l2_dev, "Enable deinterlace.\n"); + v4l2_info(vout->vfd->v4l2_dev, "Enable deinterlace TB.\n"); vout->task.input.deinterlace.enable = true; vout->task.input.deinterlace.field_fmt = IPU_DEINTERLACE_FIELD_TOP; break; case V4L2_FIELD_INTERLACED_BT: - v4l2_info(vout->vfd->v4l2_dev, "Enable deinterlace.\n"); + v4l2_info(vout->vfd->v4l2_dev, "Enable deinterlace BT.\n"); vout->task.input.deinterlace.enable = true; vout->task.input.deinterlace.field_fmt = IPU_DEINTERLACE_FIELD_BOTTOM; @@ -1076,11 +1117,10 @@ static int mxc_vout_try_format(struct mxc_vout_output *vout, struct v4l2_format } if (f->fmt.pix.priv) { - rect = (struct v4l2_rect *)f->fmt.pix.priv; - vout->task.input.crop.pos.x = rect->left; - vout->task.input.crop.pos.y = rect->top; - vout->task.input.crop.w = rect->width; - vout->task.input.crop.h = rect->height; + vout->task.input.crop.pos.x = rect.left; + vout->task.input.crop.pos.y = rect.top; + vout->task.input.crop.w = rect.width; + vout->task.input.crop.h = rect.height; } else { vout->task.input.crop.pos.x = 0; vout->task.input.crop.pos.y = 0; @@ -1099,9 +1139,12 @@ static int mxc_vout_try_format(struct mxc_vout_output *vout, struct v4l2_format ret = mxc_vout_try_task(vout); if (!ret) { - if (rect) { - rect->width = vout->task.input.crop.w; - rect->height = vout->task.input.crop.h; + if (f->fmt.pix.priv) { + rect.width = vout->task.input.crop.w; + rect.height = vout->task.input.crop.h; + if (copy_to_user((void __user *)f->fmt.pix.priv, + &rect, sizeof(rect))) + ret = -EFAULT; } else { f->fmt.pix.width = vout->task.input.crop.w; f->fmt.pix.height = vout->task.input.crop.h; @@ -1521,8 +1564,16 @@ static int config_disp_output(struct mxc_vout_output *vout) struct fb_var_screeninfo var; int i, display_buf_size, fb_num, ret; u32 is_1080p; + u32 fb_base; + u32 is_bg; memcpy(&var, &fbi->var, sizeof(var)); + fb_base = fbi->fix.smem_start; + is_bg = get_ipu_channel(fbi); + if (is_bg == MEM_BG_SYNC) { + memcpy(&vout->fb_var, &fbi->var, sizeof(var)); + vout->save_var = true; + } var.xres = vout->task.output.width; var.yres = vout->task.output.height; @@ -1557,8 +1608,10 @@ static int config_disp_output(struct mxc_vout_output *vout) var.xres, var.yres); ret = set_window_position(vout, &vout->win_pos); - if (ret < 0) + if (ret < 0) { + v4l2_err(vout->vfd->v4l2_dev, "ERR: set_win_pos ret:%d\n", ret); return ret; + } /* Init display channel through fb API */ var.yoffset = 0; @@ -1568,8 +1621,11 @@ static int config_disp_output(struct mxc_vout_output *vout) ret = fb_set_var(fbi, &var); fbi->flags &= ~FBINFO_MISC_USEREVENT; console_unlock(); - if (ret < 0) + if (ret < 0) { + v4l2_err(vout->vfd->v4l2_dev, + "ERR:%s fb_set_var ret:%d\n", __func__, ret); return ret; + } is_1080p = CHECK_TILED_1080P_DISPLAY(vout); if (is_1080p) @@ -1579,11 +1635,22 @@ static int config_disp_output(struct mxc_vout_output *vout) for (i = 0; i < fb_num; i++) vout->disp_bufs[i] = fbi->fix.smem_start + i * display_buf_size; + vout->fb_smem_len = fbi->fix.smem_len; + vout->fb_smem_start = fbi->fix.smem_start; + if (fb_base != fbi->fix.smem_start) { + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "realloc fb mem size:0x%x@0x%lx,old paddr @0x%x\n", + fbi->fix.smem_len, fbi->fix.smem_start, fb_base); + if (is_bg) + vout->save_var = false; + } + console_lock(); fbi->flags |= FBINFO_MISC_USEREVENT; ret = fb_blank(fbi, FB_BLANK_UNBLANK); fbi->flags &= ~FBINFO_MISC_USEREVENT; console_unlock(); + vout->release = false; return ret; } @@ -1592,7 +1659,10 @@ static void release_disp_output(struct mxc_vout_output *vout) { struct fb_info *fbi = vout->fbi; struct mxcfb_pos pos; + int ret; + if (vout->release) + return; console_lock(); fbi->flags |= FBINFO_MISC_USEREVENT; fb_blank(fbi, FB_BLANK_POWERDOWN); @@ -1604,20 +1674,24 @@ static void release_disp_output(struct mxc_vout_output *vout) pos.y = 0; set_window_position(vout, &pos); - /* fix if ic bypass crack smem_start */ - if (vout->bypass_pp) { + if (get_ipu_channel(fbi) == MEM_BG_SYNC) { console_lock(); - fbi->fix.smem_start = vout->disp_bufs[0]; + fbi->fix.smem_start = vout->fb_smem_start; + fbi->fix.smem_len = vout->fb_smem_len; + vout->fb_var.activate |= FB_ACTIVATE_FORCE; + fbi->flags |= FBINFO_MISC_USEREVENT; + ret = fb_set_var(fbi, &vout->fb_var); + fbi->flags &= ~FBINFO_MISC_USEREVENT; console_unlock(); - } - - if (get_ipu_channel(fbi) == MEM_BG_SYNC) { + if (ret < 0) + v4l2_err(vout->vfd->v4l2_dev, "ERR: fb_set_var.\n"); console_lock(); fbi->flags |= FBINFO_MISC_USEREVENT; fb_blank(fbi, FB_BLANK_UNBLANK); fbi->flags &= ~FBINFO_MISC_USEREVENT; console_unlock(); } + vout->release = true; } static int mxc_vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) diff --git a/drivers/mxc/ipu3/ipu_device.c b/drivers/mxc/ipu3/ipu_device.c index 46a9645fcf25..6810cea8f503 100644 --- a/drivers/mxc/ipu3/ipu_device.c +++ b/drivers/mxc/ipu3/ipu_device.c @@ -2577,7 +2577,7 @@ static void do_task(struct ipu_task_entry *t) ipu->rot_dma[rot_idx].vaddr = dma_alloc_coherent(t->dev, r_size, &ipu->rot_dma[rot_idx].paddr, - GFP_DMA | GFP_KERNEL); + GFP_KERNEL); CHECK_RETCODE(ipu->rot_dma[rot_idx].vaddr == NULL, "ic_and_rot", STATE_SYS_NO_MEM, chan_setup, -ENOMEM); @@ -3276,7 +3276,7 @@ static long mxc_ipu_ioctl(struct file *file, mem->cpu_addr = dma_alloc_coherent(ipu_dev, size, &mem->phy_addr, - GFP_DMA); + GFP_KERNEL); if (mem->cpu_addr == NULL) { kfree(mem); return -ENOMEM; diff --git a/drivers/mxc/ipu3/vdoa.c b/drivers/mxc/ipu3/vdoa.c index 2d3aaef16622..5800fc606f8c 100644 --- a/drivers/mxc/ipu3/vdoa.c +++ b/drivers/mxc/ipu3/vdoa.c @@ -451,7 +451,6 @@ static int vdoa_probe(struct platform_device *pdev) 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) { diff --git a/drivers/video/mxc/mxc_ipuv3_fb.c b/drivers/video/mxc/mxc_ipuv3_fb.c index b010e943f61d..a087f3323233 100644 --- a/drivers/video/mxc/mxc_ipuv3_fb.c +++ b/drivers/video/mxc/mxc_ipuv3_fb.c @@ -361,13 +361,13 @@ static int mxcfb_set_par(struct fb_info *fbi) dma_alloc_coherent(fbi->device, alpha_mem_len, &mxc_fbi->alpha_phy_addr0, - GFP_DMA | GFP_KERNEL); + GFP_KERNEL); mxc_fbi->alpha_virt_addr1 = dma_alloc_coherent(fbi->device, alpha_mem_len, &mxc_fbi->alpha_phy_addr1, - GFP_DMA | GFP_KERNEL); + GFP_KERNEL); if (mxc_fbi->alpha_virt_addr0 == NULL || mxc_fbi->alpha_virt_addr1 == NULL) { dev_err(fbi->device, "mxcfb: dma alloc for" @@ -1037,7 +1037,7 @@ static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg) mem->cpu_addr = dma_alloc_coherent(fbi->device, size, &mem->phy_addr, - GFP_DMA); + GFP_KERNEL); if (mem->cpu_addr == NULL) { kfree(mem); return -ENOMEM; @@ -1577,7 +1577,7 @@ static int mxcfb_map_video_memory(struct fb_info *fbi) fbi->screen_base = dma_alloc_writecombine(fbi->device, fbi->fix.smem_len, (dma_addr_t *)&fbi->fix.smem_start, - GFP_DMA); + GFP_KERNEL); if (fbi->screen_base == 0) { dev_err(fbi->device, "Unable to allocate framebuffer memory\n"); fbi->fix.smem_len = 0; diff --git a/sound/soc/codecs/mxc_hdmi.c b/sound/soc/codecs/mxc_hdmi.c index 4d9a5719eeae..62decb89d876 100644 --- a/sound/soc/codecs/mxc_hdmi.c +++ b/sound/soc/codecs/mxc_hdmi.c @@ -150,9 +150,6 @@ static void mxc_hdmi_get_playback_sample_size(void) /* always assume basic audio support */ playback_sample_size[i++] = 16; - if (edid_cfg.sample_sizes & 0x2) - playback_sample_size[i++] = 20; - if (edid_cfg.sample_sizes & 0x4) playback_sample_size[i++] = 24; diff --git a/sound/soc/codecs/mxc_spdif.c b/sound/soc/codecs/mxc_spdif.c index 4d8e83b2032d..2bcb4d68274b 100644 --- a/sound/soc/codecs/mxc_spdif.c +++ b/sound/soc/codecs/mxc_spdif.c @@ -1055,8 +1055,17 @@ static struct snd_kcontrol_new mxc_spdif_ctrls[] = { static int mxc_spdif_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + struct snd_soc_codec *codec = dai->codec; + struct mxc_spdif_priv *spdif_priv = snd_soc_codec_get_drvdata(codec); + struct mxc_spdif_platform_data *plat_data = spdif_priv->plat_data; int ret; + /* enable spdif_xtal_clk */ + clk_enable(plat_data->spdif_core_clk); + spdif_softreset(); + /* disable all the interrupts */ + spdif_intr_enable(0xffffff, 0); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ret = mxc_spdif_playback_startup(substream, dai); else @@ -1068,12 +1077,18 @@ static int mxc_spdif_startup(struct snd_pcm_substream *substream, static void mxc_spdif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + struct snd_soc_codec *codec = dai->codec; + struct mxc_spdif_priv *spdif_priv = snd_soc_codec_get_drvdata(codec); + struct mxc_spdif_platform_data *plat_data = spdif_priv->plat_data; int ret; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ret = mxc_spdif_playback_shutdown(substream, dai); else ret = mxc_spdif_capture_shutdown(substream, dai); + /* disable spdif_core clock */ + clk_put(plat_data->spdif_clk); + clk_disable(plat_data->spdif_core_clk); } static int mxc_spdif_prepare(struct snd_pcm_substream *substream, @@ -1244,16 +1259,14 @@ static int __devinit mxc_spdif_probe(struct platform_device *pdev) } plat_data->spdif_clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(plat_data->spdif_clk)) { + ret = PTR_ERR(plat_data->spdif_clk); + dev_err(&pdev->dev, "can't get clock: %d\n", ret); + goto failed_clk; + } atomic_set(&spdif_priv->dpll_locked, 0); - /* spdif_xtal_clk */ - clk_enable(plat_data->spdif_core_clk); - spdif_softreset(); - - /* disable all the interrupts */ - spdif_intr_enable(0xffffff, 0); - /* spdif interrupt register and disable */ irq = platform_get_irq(pdev, 0); if ((irq <= 0) || request_irq(irq, spdif_isr, 0, "spdif", spdif_priv)) { @@ -1277,7 +1290,7 @@ static int __devinit mxc_spdif_probe(struct platform_device *pdev) card_err: clk_put(plat_data->spdif_clk); clk_disable(plat_data->spdif_core_clk); - +failed_clk: platform_set_drvdata(pdev, NULL); kfree(spdif_priv); @@ -1287,13 +1300,9 @@ card_err: static int __devexit mxc_spdif_remove(struct platform_device *pdev) { struct mxc_spdif_priv *spdif_priv = platform_get_drvdata(pdev); - struct mxc_spdif_platform_data *plat_data = spdif_priv->plat_data; snd_soc_unregister_codec(&pdev->dev); - clk_put(plat_data->spdif_clk); - clk_disable(plat_data->spdif_core_clk); - platform_set_drvdata(pdev, NULL); kfree(spdif_priv); diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 824a6fa8f487..ccf8883a7866 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3206,6 +3206,14 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream, wm8962_configure_bclk(codec); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (snd_soc_dapm_get_pin_status(&codec->dapm, "DMIC")) + snd_soc_update_bits(codec, WM8962_THREED1, WM8962_ADC_MONOMIX_MASK, 0); + else + snd_soc_update_bits(codec, WM8962_THREED1, + WM8962_ADC_MONOMIX_MASK, WM8962_ADC_MONOMIX); + } + return 0; } diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile index 7b2fce6a37fc..2a67c3250355 100644 --- a/sound/soc/imx/Makefile +++ b/sound/soc/imx/Makefile @@ -19,8 +19,7 @@ snd-soc-imx-wm8962-objs := imx-wm8962.o snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o snd-soc-imx-cs42888-objs := imx-cs42888.o snd-soc-imx-spdif-objs := imx-spdif.o -snd-soc-imx-hdmi-objs := imx-hdmi.o imx-hdmi-dai.o imx-hdmi-dma.o -snd-soc-imx-wm8958-objs := imx-wm8958.o +snd-soc-imx-hdmi-objs := imx-hdmi.o imx-hdmi-dai.o imx-hdmi-dma.o hdmi_pcm.o obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o @@ -32,3 +31,5 @@ obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o obj-$(CONFIG_SND_SOC_IMX_CS42888) += snd-soc-imx-cs42888.o obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o obj-$(CONFIG_SND_SOC_IMX_HDMI) += snd-soc-imx-hdmi.o + +AFLAGS_hdmi_pcm.o := -march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=softfp diff --git a/sound/soc/imx/hdmi_pcm.S b/sound/soc/imx/hdmi_pcm.S new file mode 100644 index 000000000000..6e97e1a41170 --- /dev/null +++ b/sound/soc/imx/hdmi_pcm.S @@ -0,0 +1,238 @@ +/* +* Copyright (C) 2010-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. +*/ + +.section .text + +.global hdmi_dma_copy_16_neon_lut +.global hdmi_dma_copy_16_neon_fast +.global hdmi_dma_copy_24_neon_lut +.global hdmi_dma_copy_24_neon_fast + + +/* hdmi_dma_copy_16_neon_lut + * Convert pcm sample to iec sample. Pcm sample is 16 bits. + * Frame index is between 0 and 47 inclusively. Channel count can be 1, 2, 4 or 8. + * Frame count should be multipliable by 4. Sample count should be multipliable by 8. + * + * C Prototype + * void hdmi_dma_copy_16_neon_lut(unsigned short *src, unsigned int *dst, + * int samples, unsigned char *lookup_table); + * Return value + * None + * Parameters + * src Source PCM16 samples + * dst Dest buffer to store pcm with header + * samples Contains sample count (=frame_count * channel_count) + * lookup_table Preconstructed header table. Channels interleaved. + */ + +hdmi_dma_copy_16_neon_lut: + mov r12, #1 /* construct vector(1) */ + vdup.8 d6, r12 + +hdmi_dma_copy_16_neon_lut_start: + + /* get 8 samples to q0 */ + vld1.16 {d0, d1}, [r0]! /* TODO: aligned */ + +/* pld [r1, #(64*4)] */ + + /* xor every bit */ + vcnt.8 q1, q0 /* count of 1s */ + vpadd.i8 d2, d2, d3 /* only care about the least bit in every element */ + vand d2, d2, d6 /* clear other bits while keep the least bit */ + vshl.u8 d2, d2, #3 /* bit p: d2 = d2 << 3 */ + + /* get packet header */ + vld1.8 {d5}, [r3]! + veor d4, d5, d2 /* xor bit c */ + + /* store: (d4 << 16 | q0) << 8 */ + vmovl.u8 q2, d4 /* expand from char to short */ + vzip.16 q0, q2 + vshl.u32 q0, q0, #8 + vshl.u32 q1, q2, #8 + vst1.32 {d0, d1, d2, d3}, [r1]! + + /* decrease sample count */ + subs r2, r2, #8 + bne hdmi_dma_copy_16_neon_lut_start + + mov pc, lr + +/* hdmi_dma_copy_16_neon_fast + * Convert pcm sample to iec sample. Pcm sample is 16 bits. + * Frame index is between 48 and 191 inclusively. Channel count can be 1, 2, 4 or 8. + * Frame count should be multipliable by 4. Sample count should be multipliable by 8. + * + * C Prototype + * void hdmi_dma_copy_16_neon_fast(unsigned short *src, unsigned int *dst, int samples); + * Return value + * None + * Parameters + * src Source PCM16 samples + * dst Dest buffer to store pcm with header + * samples Contains sample count (=frame_count * channel_count) + */ + +hdmi_dma_copy_16_neon_fast: + mov r12, #1 /* construct vector(1) */ + vdup.8 d6, r12 + +hdmi_dma_copy_16_neon_fast_start: + /* get 8 samples to q0 */ + vld1.16 {d0, d1}, [r0]! /* TODO: aligned */ + +/* pld [r1, #(64*4)] */ + + /* xor every bit */ + vcnt.8 q1, q0 /* count of 1s */ + vpadd.i8 d2, d2, d3 + vand d2, d2, d6 /* clear other bits while keep the least bit */ + vshl.u8 d4, d2, #3 /* bit p: d2 = d2 << 3 */ /* finally we construct packet header */ + + /* get packet header: always 0 */ + + /* store: (d4 << 16 | q0) << 8 */ + vmovl.u8 q2, d4 /* expand from char to short */ + vzip.16 q0, q2 + vshl.u32 q0, q0, #8 + vshl.u32 q1, q2, #8 + vst1.32 {d0, d1, d2, d3}, [r1]! + + /* decrease sample count */ + subs r2, r2, #8 + bne hdmi_dma_copy_16_neon_fast_start + + mov pc, lr + + + +/* hdmi_dma_copy_24_neon_lut + * Convert pcm sample to iec sample. Pcm sample is 24 bits. + * Frame index is between 0 and 47 inclusively. Channel count can be 1, 2, 4 or 8. + * Frame count should be multipliable by 4. Sample count should be multipliable by 8. + * + * C Prototype + * void hdmi_dma_copy_24_neon_lut(unsigned int *src, unsigned int *dst, + * int samples, unsigned char *lookup_table); + * Return value + * None + * Parameters + * src Source PCM24 samples + * dst Dest buffer to store pcm with header + * samples Contains sample count (=frame_count * channel_count) + * lookup_table Preconstructed header table. Channels interleaved. + */ + +hdmi_dma_copy_24_neon_lut: + vpush {d8} + + mov r12, #1 /* construct vector(1) */ + vdup.8 d8, r12 + +hdmi_dma_copy_24_neon_lut_start: + + /* get 8 samples to q0 and q1 */ + vld1.32 {d0, d1, d2, d3}, [r0]! /* TODO: aligned */ + +/* pld [r1, #(64*4)] */ + + /* xor every bit */ + vcnt.8 q2, q0 /* count of 1s */ + vpadd.i8 d4, d4, d5 /* only care about the least bit in every element */ + vcnt.8 q3, q1 + vpadd.i8 d6, d6, d7 + vpadd.i8 d4, d4, d6 /* d4: contains xor result and other dirty bits */ + vand d4, d4, d8 /* clear other bits while keep the least bit */ + vshl.u8 d4, d4, #3 /* bit p: d4 = d4 << 3 */ + + /* get packet header */ + vld1.8 {d5}, [r3]! /* d5: original header */ + veor d5, d5, d4 /* fix bit p */ + + /* store: (d5 << 24 | q0) */ + vmovl.u8 q3, d5 /* expand from char to short */ + vmovl.u16 q2, d6 /* expand from short to int */ + vmovl.u16 q3, d7 + vshl.u32 q2, q2, #24 + vshl.u32 q3, q3, #24 + vorr q0, q0, q2 + vorr q1, q1, q3 + vst1.32 {d0, d1, d2, d3}, [r1]! + + /* decrease sample count */ + subs r2, r2, #8 + bne hdmi_dma_copy_24_neon_lut_start + + vpop {d8} + mov pc, lr + +/* hdmi_dma_copy_24_neon_fast + * Convert pcm sample to iec sample. Pcm sample is 24 bits. + * Frame index is between 48 and 191 inclusively. Channel count can be 1, 2, 4 or 8. + * Frame count should be multipliable by 4. Sample count should be multipliable by 8. + * + * C Prototype + * void hdmi_dma_copy_24_neon_fast(unsigned int *src, unsigned int *dst, int samples); + * Return value + * None + * Parameters + * src Source PCM24 samples + * dst Dest buffer to store pcm with header + * samples Contains sample count (=frame_count * channel_count) + */ + +hdmi_dma_copy_24_neon_fast: + vpush {d8} + + mov r12, #1 /* construct vector(1) */ + vdup.8 d8, r12 + +hdmi_dma_copy_24_neon_fast_start: + /* get 8 samples to q0 and q1 */ + vld1.32 {d0, d1, d2, d3}, [r0]! /* TODO: aligned */ + +/* pld [r1, #(64*4)] */ + + /* xor every bit */ + vcnt.8 q2, q0 /* count of 1s */ + vpadd.i8 d4, d4, d5 /* only care about the least bit in every element */ + vcnt.8 q3, q1 + vpadd.i8 d6, d6, d7 + vpadd.i8 d4, d4, d6 /* d4: contains xor result and other dirty bits */ + vand d4, d4, d8 /* clear other bits while keep the least bit */ + vshl.u8 d4, d4, #3 /* bit p: d4 = d4 << 3 */ + + /* store: (d4 << 24 | q0) */ + vmovl.u8 q3, d4 /* expand from char to short */ + vmovl.u16 q2, d6 /* expand from short to int */ + vmovl.u16 q3, d7 + vshl.u32 q2, q2, #24 + vshl.u32 q3, q3, #24 + vorr q0, q0, q2 + vorr q1, q1, q3 + vst1.32 {d0, d1, d2, d3}, [r1]! + + /* decrease sample count */ + subs r2, r2, #8 + bne hdmi_dma_copy_24_neon_fast_start + + vpop {d8} + mov pc, lr + diff --git a/sound/soc/imx/imx-hdmi-dma.c b/sound/soc/imx/imx-hdmi-dma.c index 1bd36ca1ea57..da9c2f776988 100644 --- a/sound/soc/imx/imx-hdmi-dma.c +++ b/sound/soc/imx/imx-hdmi-dma.c @@ -35,6 +35,7 @@ #include <mach/mxc_hdmi.h> #include "imx-hdmi.h" + #define HDMI_DMA_BURST_UNSPECIFIED_LEGNTH 0 #define HDMI_DMA_BURST_INCR4 1 #define HDMI_DMA_BURST_INCR8 2 @@ -44,7 +45,8 @@ struct imx_hdmi_dma_runtime_data { struct snd_pcm_substream *tx_substream; unsigned long buffer_bytes; - void *buf; + struct snd_dma_buffer hw_buffer; + unsigned long appl_bytes; int period_time; int periods; @@ -70,6 +72,19 @@ struct imx_hdmi_dma_runtime_data { spinlock_t irq_lock; }; +/* bit 0:0:0:b:p(0):c:(u)0:(v)0*/ +/* max 8 channels supported; channels are interleaved*/ +static unsigned char g_packet_head_table[48*8]; + +void hdmi_dma_copy_16_neon_lut(unsigned short *src, unsigned int *dst, + int samples, unsigned char *lookup_table); +void hdmi_dma_copy_16_neon_fast(unsigned short *src, unsigned int *dst, + int samples); +void hdmi_dma_copy_24_neon_lut(unsigned int *src, unsigned int *dst, + int samples, unsigned char *lookup_table); +void hdmi_dma_copy_24_neon_fast(unsigned int *src, unsigned int *dst, + int samples); + hdmi_audio_header_t iec_header; /* @@ -113,6 +128,7 @@ hdmi_audio_header_t iec_header; */ #define HDMI_DMA_PERIOD_BYTES (6144) #define HDMI_DMA_BUF_SIZE (64 * 1024) +#define HDMI_PCM_BUF_SIZE (64 * 1024) struct imx_hdmi_dma_runtime_data *hdmi_dma_priv; @@ -152,7 +168,7 @@ static void dumprtd(struct imx_hdmi_dma_runtime_data *rtd) pr_debug("period_bytes = %d\n", rtd->period_bytes); pr_debug("dma period_bytes = %d\n", rtd->dma_period_bytes); pr_debug("buffer_ratio = %d\n", rtd->buffer_ratio); - pr_debug("dma buf addr = 0x%08x\n", (int)rtd->buf); + pr_debug("hw dma buffer = 0x%08x\n", (int)rtd->hw_buffer.addr); pr_debug("dma buf size = %d\n", (int)rtd->buffer_bytes); pr_debug("sample_rate = %d\n", (int)rtd->rate); } @@ -216,12 +232,33 @@ static void hdmi_dma_clear_irq_status(u8 status) static void hdmi_dma_irq_mask(int mask) { - if (mask) - hdmi_writeb(0xff, HDMI_AHB_DMA_MASK); - else - hdmi_writeb((u8)~HDMI_AHB_DMA_DONE, HDMI_AHB_DMA_MASK); + u8 regvalue; + regvalue = hdmi_readb(HDMI_AHB_DMA_MASK); + + if (mask) { + regvalue |= HDMI_AHB_DMA_DONE; + hdmi_writeb(regvalue, HDMI_AHB_DMA_MASK); + } else { + regvalue &= (u8)~HDMI_AHB_DMA_DONE; + hdmi_writeb(regvalue, HDMI_AHB_DMA_MASK); + } } +static void hdmi_mask(int mask) +{ + u8 regvalue; + regvalue = hdmi_readb(HDMI_AHB_DMA_MASK); + + if (mask) { + regvalue |= HDMI_AHB_DMA_ERROR | HDMI_AHB_DMA_FIFO_EMPTY; + hdmi_writeb(regvalue, HDMI_AHB_DMA_MASK); + } else { + regvalue &= (u8)~(HDMI_AHB_DMA_ERROR | HDMI_AHB_DMA_FIFO_EMPTY); + hdmi_writeb(regvalue, HDMI_AHB_DMA_MASK); + } +} + + static void hdmi_dma_irq_mute(int mute) { if (mute) @@ -230,12 +267,21 @@ static void hdmi_dma_irq_mute(int mute) hdmi_writeb(0x00, HDMI_IH_MUTE_AHBDMAAUD_STAT0); } +int odd_ones(unsigned a) +{ + a ^= a >> 8; + a ^= a >> 4; + a ^= a >> 2; + a ^= a >> 1; + + return a & 1; +} + /* Add frame information for one pcm subframe */ static u32 hdmi_dma_add_frame_info(struct imx_hdmi_dma_runtime_data *rtd, u32 pcm_data, int subframe_idx) { hdmi_audio_dma_data_t subframe; - int i; subframe.U = 0; iec_header.B.channel = subframe_idx; @@ -249,9 +295,7 @@ static u32 hdmi_dma_add_frame_info(struct imx_hdmi_dma_runtime_data *rtd, else subframe.B.c = 0; - /* fill p (parity) */ - for (i = 0 ; i < rtd->sample_bits ; i++) - subframe.B.p ^= (pcm_data >> i) & 0x01; + subframe.B.p = odd_ones(pcm_data); subframe.B.p ^= subframe.B.c; subframe.B.p ^= subframe.B.u; subframe.B.p ^= subframe.B.v; @@ -274,12 +318,239 @@ static void hdmi_dma_incr_frame_idx(struct imx_hdmi_dma_runtime_data *rtd) rtd->frame_idx = 0; } +static void init_table(int channels) +{ + int i; + int ch = 0; + unsigned char *p = g_packet_head_table; + + for (i = 0; i < 48; i++) { + int b = 0; + if (i == 0) + b = 1; + + for (ch = 0; ch < channels; ch++) { + int c = 0; + if (i < 42) { + iec_header.B.channel = ch+1; + c = (iec_header.U >> i) & 0x1; + } + /* preset bit p as c */ + *p++ = (b << 4) | (c << 2) | (c << 3); + } + } +} + +#if 1 + +/* C code optimization for IEC head */ +static void hdmi_dma_copy_16_c_lut(unsigned short *src, unsigned int *dst, int samples, unsigned char *lookup_table) +{ + int i; + unsigned int sample; + unsigned int p; + unsigned int head; + + for (i = 0; i < samples; i++) { + /* get source sample */ + sample = *src++; + + /* xor every bit */ + p = sample ^ (sample >> 8); + p ^= (p >> 4); + p ^= (p >> 2); + p ^= (p >> 1); + p &= 1; /* only want last bit */ + p <<= 3; /* bit p */ + + /* get packet header */ + head = *lookup_table++; + + /* fix head */ + head ^= p; + + /* store */ + *dst++ = (head << 24) | (sample << 8); + } +} + +static void hdmi_dma_copy_16_c_fast(unsigned short *src, unsigned int *dst, int samples) +{ + int i; + unsigned int sample; + unsigned int p; + + for (i = 0; i < samples; i++) { + /* get source sample */ + sample = *src++; + + /* xor every bit */ + p = sample ^ (sample >> 8); + p ^= (p >> 4); + p ^= (p >> 2); + p ^= (p >> 1); + p &= 1; /* only want last bit */ + p <<= 3; /* bit p */ + + /* store */ + *dst++ = (p << 24) | (sample << 8); + } +} + +static void hdmi_dma_copy_16(unsigned short *src, unsigned int *dest, int framecount, int channelcount) +{ + /* split input frames into 192-frame each */ + int count_in_192 = (framecount + 191) / 192; + int i; + + for (i = 0; i < count_in_192; i++) { + int count; + int samples; + + /* handles frame index [0, 48) */ + count = (framecount < 48) ? framecount : 48; + samples = count * channelcount; + hdmi_dma_copy_16_c_lut(src, dest, samples, g_packet_head_table); + framecount -= count; + if (framecount == 0) + break; + + src += samples; + dest += samples; + + /* handles frame index [48, 192) */ + count = (framecount < 192 - 48) ? framecount : 192 - 48; + samples = count * channelcount; + hdmi_dma_copy_16_c_fast(src, dest, samples); + framecount -= count; + src += samples; + dest += samples; + } +} + +#else + +/* NEON optimization for IEC head*/ + +/* Convert pcm samples to iec samples suitable for HDMI transfer. +* PCM sample is 16 bits length. +* Frame index always starts from 0. +* Channel count can be 1, 2, 4, 6, or 8 +* Sample count (frame_count * channel_count) is multipliable by 8. +*/ +static void hdmi_dma_copy_16(u16 *src, u32 *dest, int framecount, int channelcount) +{ + /* split input frames into 192-frame each */ + int count_in_192 = (framecount + 191) / 192; + int i; + + for (i = 0; i < count_in_192; i++) { + int count; + int samples; + + /* handles frame index [0, 48) */ + count = (framecount < 48) ? framecount : 48; + samples = count * channelcount; + hdmi_dma_copy_16_neon_lut(src, dest, samples, g_packet_head_table); + framecount -= count; + if (framecount == 0) + break; + + src += samples; + dest += samples; + + /* handles frame index [48, 192) */ + count = (framecount < 192 - 48) ? framecount : 192 - 48; + samples = count * channelcount; + hdmi_dma_copy_16_neon_fast(src, dest, samples); + framecount -= count; + src += samples; + dest += samples; + } +} + +/* Convert pcm samples to iec samples suitable for HDMI transfer. +* PCM sample is 24 bits length. +* Frame index always starts from 0. +* Channel count can be 1, 2, 4, 6, or 8 +* Sample count (frame_count * channel_count) is multipliable by 8. +*/ +static void hdmi_dma_copy_24(u32 *src, u32 *dest, int framecount, int channelcount) +{ + /* split input frames into 192-frame each */ + int count_in_192 = (framecount + 191) / 192; + int i; + + for (i = 0; i < count_in_192; i++) { + int count; + int samples; + + /* handles frame index [0, 48) */ + count = (framecount < 48) ? framecount : 48; + samples = count * channelcount; + hdmi_dma_copy_24_neon_lut(src, dest, samples, g_packet_head_table); + framecount -= count; + if (framecount == 0) + break; + + src += samples; + dest += samples; + + /* handles frame index [48, 192) */ + count = (framecount < 192 - 48) ? framecount : 192 - 48; + samples = count * channelcount; + hdmi_dma_copy_24_neon_fast(src, dest, samples); + framecount -= count; + src += samples; + dest += samples; + } +} +#endif + +static void hdmi_dma_mmap_copy(struct snd_pcm_substream *substream, + int offset, int count) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct imx_hdmi_dma_runtime_data *rtd = runtime->private_data; + u32 framecount; + u32 *src32, *dest; + u16 *src16; + + framecount = count/(rtd->sample_align * rtd->channels); + + /* hw_buffer is the destination for pcm data plus frame info. */ + dest = (u32 *)(rtd->hw_buffer.area + (offset * rtd->buffer_ratio)); + + switch (rtd->format) { + + case SNDRV_PCM_FORMAT_S16_LE: + /* dma_buffer is the mmapped buffer we are copying pcm from. */ + src16 = (u16 *)(runtime->dma_area + offset); + hdmi_dma_copy_16(src16, dest, framecount, rtd->channels); + break; + +/* 24bit not support now. */ +/* + case SNDRV_PCM_FORMAT_S24_LE: + src32 = (u32 *)(runtime->dma_area + offset); + hdmi_dma_copy_24(src32, dest, framecount, rtd->channels); + break; +*/ + default: + pr_err("%s HDMI Audio invalid sample format (%d)\n", + __func__, rtd->format); + return; + } +} + static irqreturn_t hdmi_dma_isr(int irq, void *dev_id) { struct imx_hdmi_dma_runtime_data *rtd = dev_id; struct snd_pcm_substream *substream = rtd->tx_substream; - unsigned int status; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned long offset, count, space_to_end, appl_bytes; unsigned long flags; + unsigned int status; spin_lock_irqsave(&rtd->irq_lock, flags); @@ -291,18 +562,35 @@ static irqreturn_t hdmi_dma_isr(int irq, void *dev_id) rtd->offset += rtd->period_bytes; rtd->offset %= rtd->period_bytes * rtd->periods; + /* For mmap access, need to copy data from dma_buffer + * to hw_buffer and add the frame info. */ + if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { + appl_bytes = frames_to_bytes(runtime, + runtime->control->appl_ptr); + offset = rtd->appl_bytes % rtd->buffer_bytes; + space_to_end = rtd->buffer_bytes - offset; + count = appl_bytes - rtd->appl_bytes; + rtd->appl_bytes = appl_bytes; + + if (count <= space_to_end) { + hdmi_dma_mmap_copy(substream, offset, count); + } else { + hdmi_dma_mmap_copy(substream, offset, space_to_end); + hdmi_dma_mmap_copy(substream, 0, count - space_to_end); + } + } snd_pcm_period_elapsed(substream); - hdmi_dma_set_addr(substream->dma_buffer.addr + - (rtd->offset * rtd->buffer_ratio), + hdmi_dma_set_addr(rtd->hw_buffer.addr + + (rtd->offset * rtd->buffer_ratio), rtd->dma_period_bytes); - hdmi_dma_start(); } hdmi_dma_irq_mute(0); spin_unlock_irqrestore(&rtd->irq_lock, flags); + return IRQ_HANDLED; } @@ -364,27 +652,25 @@ static void hdmi_dma_enable_channels(int channels) } } -static void hdmi_dma_set_thrsld(void) +static void hdmi_dma_configure_dma(int channels) { - int rev = hdmi_readb(HDMI_REVISION_ID); + hdmi_dma_enable_hlock(1); - switch (rev) { - case 0x0a: + switch (channels) { + case 2: + hdmi_dma_set_incr_type(HDMI_DMA_BURST_INCR4); hdmi_writeb(126, HDMI_AHB_DMA_THRSLD); break; - default: - hdmi_writeb(64, HDMI_AHB_DMA_THRSLD); + case 4: + case 6: + case 8: + hdmi_dma_set_incr_type(HDMI_DMA_BURST_INCR4); + hdmi_writeb(124, HDMI_AHB_DMA_THRSLD); break; + default: + pr_err("%s %dunsupport channel!\r\n", __func__, __LINE__); } - pr_debug("HDMI_AHB_DMA_THRSLD 0x%02x\n", hdmi_readb(HDMI_AHB_DMA_THRSLD)); -} - -static void hdmi_dma_configure_dma(int channels) -{ - hdmi_dma_enable_hlock(1); - hdmi_dma_set_incr_type(HDMI_DMA_BURST_UNSPECIFIED_LEGNTH); - hdmi_dma_set_thrsld(); hdmi_dma_enable_channels(channels); } @@ -485,8 +771,9 @@ static int hdmi_dma_copy(struct snd_pcm_substream *substream, int channel, int subframe_idx; u32 pcm_data; - /* Copy pcm data from userspace and add frame info. */ - hw_buf = (u32 *)(rtd->buf + (pos_bytes * rtd->buffer_ratio)); + /* Copy pcm data from userspace and add frame info. + * Destination is hw_buffer. */ + hw_buf = (u32 *)(rtd->hw_buffer.area + (pos_bytes * rtd->buffer_ratio)); while (count > 0) { for (subframe_idx = 1 ; subframe_idx <= rtd->channels ; subframe_idx++) { @@ -521,7 +808,6 @@ static int hdmi_dma_hw_params(struct snd_pcm_substream *substream, rtd->offset = 0; rtd->period_time = HZ / (params_rate(params) / params_period_size(params)); - rtd->buf = (unsigned int *)substream->dma_buffer.area; switch (rtd->format) { case SNDRV_PCM_FORMAT_S16_LE: @@ -546,12 +832,15 @@ static int hdmi_dma_hw_params(struct snd_pcm_substream *substream, snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); hdmi_dma_configure_dma(rtd->channels); - hdmi_dma_set_addr(substream->dma_buffer.addr, rtd->dma_period_bytes); + hdmi_dma_set_addr(rtd->hw_buffer.addr, rtd->dma_period_bytes); dumprtd(rtd); hdmi_dma_update_iec_header(substream); + /* Init par for mmap optimizate */ + init_table(rtd->channels); + return 0; } @@ -565,10 +854,18 @@ static int hdmi_dma_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: rtd->frame_idx = 0; + if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { + rtd->appl_bytes = frames_to_bytes(runtime, + runtime->control->appl_ptr); + + hdmi_dma_mmap_copy(substream, 0, rtd->appl_bytes); + + } dumpregs(); - hdmi_dma_irq_mask(0); + hdmi_dma_priv->tx_active = true; hdmi_dma_start(); + hdmi_dma_irq_mask(0); hdmi_set_dma_mode(1); break; @@ -599,17 +896,19 @@ static snd_pcm_uframes_t hdmi_dma_pointer(struct snd_pcm_substream *substream) static struct snd_pcm_hardware snd_imx_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, .formats = MXC_HDMI_FORMATS_PLAYBACK, .rate_min = 32000, .channels_min = 2, .channels_max = 8, - .buffer_bytes_max = HDMI_DMA_BUF_SIZE / 2, + .buffer_bytes_max = HDMI_PCM_BUF_SIZE, .period_bytes_min = HDMI_DMA_PERIOD_BYTES / 2, .period_bytes_max = HDMI_DMA_PERIOD_BYTES / 2, - .periods_min = 4, - .periods_max = 255, + .periods_min = 8, + .periods_max = 8, .fifo_size = 0, }; @@ -627,6 +926,8 @@ static void hdmi_dma_irq_enable(struct imx_hdmi_dma_runtime_data *rtd) hdmi_dma_irq_mute(0); hdmi_dma_irq_mask(0); + hdmi_mask(0); + spin_unlock_irqrestore(&hdmi_dma_priv->irq_lock, flags); } @@ -642,6 +943,8 @@ static void hdmi_dma_irq_disable(struct imx_hdmi_dma_runtime_data *rtd) hdmi_irq_disable(rtd->irq); hdmi_dma_clear_irq_status(0xff); + hdmi_mask(1); + spin_unlock_irqrestore(&rtd->irq_lock, flags); } @@ -700,20 +1003,34 @@ static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) { struct snd_pcm_substream *substream = pcm->streams[stream].substream; struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = HDMI_DMA_BUF_SIZE; + struct snd_dma_buffer *hw_buffer = &hdmi_dma_priv->hw_buffer; - buf->area = dma_alloc_writecombine(pcm->card->dev, size, + /* The 'dma_buffer' is the buffer alsa knows about. + * It contains only raw audio. */ + buf->area = dma_alloc_writethrough(pcm->card->dev, + HDMI_PCM_BUF_SIZE, &buf->addr, GFP_KERNEL); + + buf->bytes = HDMI_PCM_BUF_SIZE; if (!buf->area) return -ENOMEM; buf->dev.type = SNDRV_DMA_TYPE_DEV; buf->dev.dev = pcm->card->dev; buf->private_data = NULL; - buf->bytes = size; hdmi_dma_priv->tx_substream = substream; + /* For mmap access, isr will copy from + * the dma_buffer to the hw_buffer */ + hw_buffer->area = dma_alloc_writethrough(pcm->card->dev, + HDMI_DMA_BUF_SIZE, + &hw_buffer->addr, GFP_KERNEL); + if (!hw_buffer->area) + return -ENOMEM; + + hw_buffer->bytes = HDMI_DMA_BUF_SIZE; + return 0; } @@ -744,6 +1061,7 @@ static void imx_hdmi_dma_pcm_free(struct snd_pcm *pcm) struct snd_dma_buffer *buf; int stream; + /* free each dma_buffer */ for (stream = 0; stream < 2; stream++) { substream = pcm->streams[stream].substream; if (!substream) @@ -757,6 +1075,14 @@ static void imx_hdmi_dma_pcm_free(struct snd_pcm *pcm) buf->area, buf->addr); buf->area = NULL; } + + /* free the hw_buffer */ + buf = &hdmi_dma_priv->hw_buffer; + if (buf->area) { + dma_free_writecombine(pcm->card->dev, buf->bytes, + buf->area, buf->addr); + buf->area = NULL; + } } static struct snd_soc_platform_driver imx_soc_platform_mx2 = { @@ -773,6 +1099,7 @@ static int __devinit imx_soc_platform_probe(struct platform_device *pdev) hdmi_dma_priv = kzalloc(sizeof(*hdmi_dma_priv), GFP_KERNEL); if (hdmi_dma_priv == NULL) return -ENOMEM; + /*To alloc a buffer non cacheable for hdmi script use*/ hdmi_dma_priv->tx_active = false; spin_lock_init(&hdmi_dma_priv->irq_lock); @@ -797,7 +1124,6 @@ static int __devinit imx_soc_platform_probe(struct platform_device *pdev) goto e_clk_get2; } - /* The HDMI block's irq line is shared with HDMI video. */ if (request_irq(hdmi_dma_priv->irq, hdmi_dma_isr, IRQF_SHARED, "hdmi dma", hdmi_dma_priv)) { dev_err(&pdev->dev, "MXC hdmi: failed to request irq %d\n", @@ -805,7 +1131,6 @@ static int __devinit imx_soc_platform_probe(struct platform_device *pdev) ret = -EBUSY; goto e_irq; } - ret = snd_soc_register_platform(&pdev->dev, &imx_soc_platform_mx2); if (ret) goto e_irq; |