summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXinyu Chen <xinyu.chen@freescale.com>2012-05-02 11:02:43 +0800
committerXinyu Chen <xinyu.chen@freescale.com>2012-05-02 11:02:43 +0800
commit3b9257f71a2a0dc0e0061ce2d455c504515c26d9 (patch)
tree6d19fa9ec5cf7973d4b599fce2a8e2861bd0dce9
parent5f5288b61bc0596dbe1436a65f0bdf52b8d855cb (diff)
parent1b64ead9cdb4eae25789cfc5bbb7d8ad07dee402 (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.h13
-rw-r--r--arch/arm/include/asm/pgtable.h3
-rw-r--r--arch/arm/mach-mx6/board-mx6q_sabreauto.c2
-rw-r--r--arch/arm/mach-mx6/board-mx6q_sabreauto.h2
-rw-r--r--arch/arm/mach-mx6/bus_freq.c53
-rw-r--r--arch/arm/mach-mx6/clock.c13
-rw-r--r--arch/arm/mach-mx6/mx6_ddr_freq.S79
-rw-r--r--arch/arm/mach-mx6/mx6_mmdc.c84
-rw-r--r--arch/arm/mm/dma-mapping.c12
-rw-r--r--arch/arm/plat-mxc/ahci_sata.c13
-rwxr-xr-xarch/arm/plat-mxc/clock.c14
-rwxr-xr-xarch/arm/plat-mxc/include/mach/clock.h3
-rw-r--r--drivers/media/video/mxc/output/mxc_vout.c140
-rw-r--r--drivers/mxc/ipu3/ipu_device.c4
-rw-r--r--drivers/mxc/ipu3/vdoa.c1
-rw-r--r--drivers/video/mxc/mxc_ipuv3_fb.c8
-rw-r--r--sound/soc/codecs/mxc_hdmi.c3
-rw-r--r--sound/soc/codecs/mxc_spdif.c33
-rw-r--r--sound/soc/codecs/wm8962.c8
-rw-r--r--sound/soc/imx/Makefile5
-rw-r--r--sound/soc/imx/hdmi_pcm.S238
-rw-r--r--sound/soc/imx/imx-hdmi-dma.c407
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;