summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKe Qinghua <qinghua.ke@freescale.com>2014-04-25 15:24:45 +0800
committerKe Qinghua <qinghua.ke@freescale.com>2014-04-25 15:24:45 +0800
commit581789ede2f71d716cf6a04181b9006105d2fad8 (patch)
treebae7e83fe491993d8f35483011aad8f222dc0bfb
parentd782a300614be14690be40ddfd11cba5beb81419 (diff)
parentc78c4d7c0c515e98fe81bf0f5e3a183cf52baeea (diff)
Merge remote-tracking branch 'remotes/fsl-linux-sdk/imx_3.10.31_1.1.0_alpha' into imx_3.10.y_android
-rw-r--r--arch/arm/boot/dts/Makefile2
-rw-r--r--arch/arm/boot/dts/imx6sx-sdb-m4.dts29
-rw-r--r--arch/arm/boot/dts/imx6sx-sdb.dts1
-rw-r--r--arch/arm/configs/imx_v7_defconfig1
-rw-r--r--arch/arm/mach-imx/clk-gate2.c47
-rw-r--r--arch/arm/mach-imx/clk-imx6q.c1
-rw-r--r--arch/arm/mach-imx/clk-imx6sl.c3
-rw-r--r--arch/arm/mach-imx/clk-imx6sx.c36
-rw-r--r--arch/arm/mach-imx/clk.h13
-rw-r--r--drivers/media/platform/mxc/output/mxc_vout.c18
-rw-r--r--drivers/net/ethernet/freescale/fec_main.c4
-rw-r--r--include/dt-bindings/clock/imx6sx-clock.h3
-rw-r--r--sound/soc/fsl/fsl_spdif.c106
13 files changed, 175 insertions, 89 deletions
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index da04fb12faa9..54f4d3db0ac4 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -140,6 +140,7 @@ dtb-$(CONFIG_ARCH_MXC) += \
imx6sx-17x17-arm2-sai.dtb \
imx6sx-17x17-arm2-ssi.dtb \
imx6sx-17x17-arm2-spdif.dtb \
+ imx6sx-17x17-arm2-mlb.dtb \
imx6sx-19x19-arm2.dtb \
imx6sx-19x19-arm2-csi.dtb \
imx6sx-19x19-arm2-lcdif1.dtb \
@@ -148,6 +149,7 @@ dtb-$(CONFIG_ARCH_MXC) += \
imx6sx-sdb-lcdif1.dtb \
imx6sx-sdb-sai.dtb \
imx6sx-sdb-emmc.dtb \
+ imx6sx-sdb-m4.dtb \
vf610-twr.dtb
dtb-$(CONFIG_ARCH_MXS) += imx23-evk.dtb \
imx23-olinuxino.dtb \
diff --git a/arch/arm/boot/dts/imx6sx-sdb-m4.dts b/arch/arm/boot/dts/imx6sx-sdb-m4.dts
new file mode 100644
index 000000000000..2d706853438c
--- /dev/null
+++ b/arch/arm/boot/dts/imx6sx-sdb-m4.dts
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "imx6sx-sdb.dts"
+
+/*
+ * The flollowing modules are conflicting with M4, disable them when m4
+ * is running.
+ */
+&flexcan1 {
+ status = "disabled";
+};
+
+&flexcan2 {
+ status = "disabled";
+};
+
+&i2c3 {
+ status = "disabled";
+};
+
+&uart2 {
+ status = "disabled";
+};
diff --git a/arch/arm/boot/dts/imx6sx-sdb.dts b/arch/arm/boot/dts/imx6sx-sdb.dts
index 26b2895cbbb6..c36489196069 100644
--- a/arch/arm/boot/dts/imx6sx-sdb.dts
+++ b/arch/arm/boot/dts/imx6sx-sdb.dts
@@ -472,6 +472,7 @@
vbus-supply = <&reg_usb_otg1_vbus>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usbotg1_1>;
+ imx6-usb-charger-detection;
status = "okay";
};
diff --git a/arch/arm/configs/imx_v7_defconfig b/arch/arm/configs/imx_v7_defconfig
index d4a2666d9de1..abc9957ae4a9 100644
--- a/arch/arm/configs/imx_v7_defconfig
+++ b/arch/arm/configs/imx_v7_defconfig
@@ -257,6 +257,7 @@ CONFIG_USB_MXS_PHY=y
CONFIG_USB_GADGET=y
CONFIG_USB_ZERO=m
CONFIG_USB_ETH=m
+CONFIG_USB_G_NCM=m
CONFIG_USB_MASS_STORAGE=m
CONFIG_USB_G_SERIAL=m
CONFIG_MMC=y
diff --git a/arch/arm/mach-imx/clk-gate2.c b/arch/arm/mach-imx/clk-gate2.c
index dcdc1a1d8a1b..52d872e7d326 100644
--- a/arch/arm/mach-imx/clk-gate2.c
+++ b/arch/arm/mach-imx/clk-gate2.c
@@ -28,48 +28,61 @@
* parent - fixed parent. No clk_set_parent support
*/
-#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw)
+struct clk_gate2 {
+ struct clk_hw hw;
+ void __iomem *reg;
+ u8 bit_idx;
+ u8 flags;
+ spinlock_t *lock;
+ unsigned int *share_count;
+};
+
+#define to_clk_gate2(_hw) container_of(_hw, struct clk_gate2, hw)
static int clk_gate2_enable(struct clk_hw *hw)
{
- struct clk_gate *gate = to_clk_gate(hw);
+ struct clk_gate2 *gate = to_clk_gate2(hw);
u32 reg;
unsigned long flags = 0;
- if (gate->lock)
- spin_lock_irqsave(gate->lock, flags);
+ spin_lock_irqsave(gate->lock, flags);
+
+ if (gate->share_count && (*gate->share_count)++ > 0)
+ goto out;
reg = readl(gate->reg);
reg |= 3 << gate->bit_idx;
writel(reg, gate->reg);
- if (gate->lock)
- spin_unlock_irqrestore(gate->lock, flags);
+out:
+ spin_unlock_irqrestore(gate->lock, flags);
return 0;
}
static void clk_gate2_disable(struct clk_hw *hw)
{
- struct clk_gate *gate = to_clk_gate(hw);
+ struct clk_gate2 *gate = to_clk_gate2(hw);
u32 reg;
unsigned long flags = 0;
- if (gate->lock)
- spin_lock_irqsave(gate->lock, flags);
+ spin_lock_irqsave(gate->lock, flags);
+
+ if (gate->share_count && --(*gate->share_count) > 0)
+ goto out;
reg = readl(gate->reg);
reg &= ~(3 << gate->bit_idx);
writel(reg, gate->reg);
- if (gate->lock)
- spin_unlock_irqrestore(gate->lock, flags);
+out:
+ spin_unlock_irqrestore(gate->lock, flags);
}
static int clk_gate2_is_enabled(struct clk_hw *hw)
{
u32 reg;
- struct clk_gate *gate = to_clk_gate(hw);
+ struct clk_gate2 *gate = to_clk_gate2(hw);
reg = readl(gate->reg);
@@ -88,21 +101,23 @@ static struct clk_ops clk_gate2_ops = {
struct clk *clk_register_gate2(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 bit_idx,
- u8 clk_gate2_flags, spinlock_t *lock)
+ u8 clk_gate2_flags, spinlock_t *lock,
+ unsigned int *share_count)
{
- struct clk_gate *gate;
+ struct clk_gate2 *gate;
struct clk *clk;
struct clk_init_data init;
- gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
+ gate = kzalloc(sizeof(struct clk_gate2), GFP_KERNEL);
if (!gate)
return ERR_PTR(-ENOMEM);
- /* struct clk_gate assignments */
+ /* struct clk_gate2 assignments */
gate->reg = reg;
gate->bit_idx = bit_idx;
gate->flags = clk_gate2_flags;
gate->lock = lock;
+ gate->share_count = share_count;
init.name = name;
init.ops = &clk_gate2_ops;
diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c
index 6efe6f2316f5..77d425c4a3a5 100644
--- a/arch/arm/mach-imx/clk-imx6q.c
+++ b/arch/arm/mach-imx/clk-imx6q.c
@@ -691,6 +691,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
clk_set_parent(clk[ssi3_sel], clk[pll4_audio_div]);
clk_set_parent(clk[esai_sel], clk[pll4_audio_div]);
clk_set_parent(clk[spdif_sel], clk[pll3_pfd3_454m]);
+ clk_set_rate(clk[spdif_podf], 227368421);
clk_set_parent(clk[spdif1_sel], clk[pll3_usb_otg]);
clk_set_rate(clk[spdif1_sel], 7500000);
diff --git a/arch/arm/mach-imx/clk-imx6sl.c b/arch/arm/mach-imx/clk-imx6sl.c
index 95f8b0b8d029..c9eb48a09159 100644
--- a/arch/arm/mach-imx/clk-imx6sl.c
+++ b/arch/arm/mach-imx/clk-imx6sl.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ * Copyright (C) 2013-2014 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -458,6 +458,7 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
/* Audio clocks */
clk_set_parent(clks[IMX6SL_CLK_SPDIF0_SEL], clks[IMX6SL_CLK_PLL3_PFD3]);
+ clk_set_rate(clks[IMX6SL_CLK_SPDIF0_PODF], 227368421);
/* set extern_audio to be sourced from PLL4/audio PLL */
clk_set_parent(clks[IMX6SL_CLK_EXTERN_AUDIO_SEL], clks[IMX6SL_CLK_PLL4_AUDIO_DIV]);
diff --git a/arch/arm/mach-imx/clk-imx6sx.c b/arch/arm/mach-imx/clk-imx6sx.c
index 6a5a8b543c23..6c8a7506abf3 100644
--- a/arch/arm/mach-imx/clk-imx6sx.c
+++ b/arch/arm/mach-imx/clk-imx6sx.c
@@ -90,7 +90,11 @@ static int const clks_init_on[] __initconst = {
IMX6SX_CLK_WAKEUP, IMX6SX_CLK_MMDC_P0_FAST, IMX6SX_CLK_MMDC_P0_IPG,
IMX6SX_CLK_ROM, IMX6SX_CLK_ARM, IMX6SX_CLK_IPG, IMX6SX_CLK_OCRAM,
IMX6SX_CLK_PER2_MAIN, IMX6SX_CLK_PERCLK, IMX6SX_CLK_M4,
- IMX6SX_CLK_QSPI1, IMX6SX_CLK_QSPI2,
+ IMX6SX_CLK_QSPI1, IMX6SX_CLK_QSPI2, IMX6SX_CLK_UART_IPG,
+ IMX6SX_CLK_UART_SERIAL, IMX6SX_CLK_I2C3, IMX6SX_CLK_ECSPI5,
+ IMX6SX_CLK_CAN1_IPG, IMX6SX_CLK_CAN1_SERIAL, IMX6SX_CLK_CAN2_IPG,
+ IMX6SX_CLK_CAN2_SERIAL, IMX6SX_CLK_CANFD, IMX6SX_CLK_EPIT1,
+ IMX6SX_CLK_EPIT2,
};
static struct clk_div_table clk_enet_ref_table[] = {
@@ -115,6 +119,9 @@ static struct clk_div_table video_div_table[] = {
{ }
};
+static u32 share_count_asrc;
+static u32 share_count_audio;
+
static void __init imx6sx_clocks_init(struct device_node *ccm_node)
{
struct device_node *np;
@@ -319,9 +326,8 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node)
clks[IMX6SX_CLK_AIPS_TZ1] = imx_clk_gate2("aips_tz1", "ahb", base + 0x68, 0);
clks[IMX6SX_CLK_AIPS_TZ2] = imx_clk_gate2("aips_tz2", "ahb", base + 0x68, 2);
clks[IMX6SX_CLK_APBH_DMA] = imx_clk_gate2("apbh_dma", "usdhc3", base + 0x68, 4);
- clks[IMX6SX_CLK_ASRC_GATE] = imx_clk_gate2("asrc_gate", "ahb", base + 0x68, 6);
- clks[IMX6SX_CLK_ASRC_MEM] = imx_clk_fixed_factor("asrc_mem", "asrc_gate", 1, 1);
- clks[IMX6SX_CLK_ASRC_IPG] = imx_clk_fixed_factor("asrc_ipg", "asrc_gate", 1, 1);
+ clks[IMX6SX_CLK_ASRC_MEM] = imx_clk_gate2_shared("asrc_mem", "ahb", base + 0x68, 6, &share_count_asrc);
+ clks[IMX6SX_CLK_ASRC_IPG] = imx_clk_gate2_shared("asrc_ipg", "ahb", base + 0x68, 6, &share_count_asrc);
clks[IMX6SX_CLK_CAAM_MEM] = imx_clk_gate2("caam_mem", "ahb", base + 0x68, 8);
clks[IMX6SX_CLK_CAAM_ACLK] = imx_clk_gate2("caam_aclk", "ahb", base + 0x68, 10);
clks[IMX6SX_CLK_CAAM_IPG] = imx_clk_gate2("caam_ipg", "ipg", base + 0x68, 12);
@@ -394,9 +400,8 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node)
clks[IMX6SX_CLK_ROM] = imx_clk_gate2("rom", "ahb", base + 0x7c, 0);
clks[IMX6SX_CLK_SDMA] = imx_clk_gate2("sdma", "ahb", base + 0x7c, 6);
clks[IMX6SX_CLK_SPBA] = imx_clk_gate2("spba", "ipg", base + 0x7c, 12);
- clks[IMX6SX_CLK_AUDIO_GATE] = imx_clk_gate2("audio_gate", "audio_podf", base + 0x7c, 14);
- clks[IMX6SX_CLK_AUDIO] = imx_clk_fixed_factor("audio", "audio_gate", 1, 1);
- clks[IMX6SX_CLK_SPDIF] = imx_clk_fixed_factor("spdif", "audio_gate", 1, 1);
+ clks[IMX6SX_CLK_AUDIO] = imx_clk_gate2_shared("audio", "audio_podf", base + 0x7c, 14, &share_count_audio);
+ clks[IMX6SX_CLK_SPDIF] = imx_clk_gate2_shared("spdif", "spdif_podf", base + 0x7c, 14, &share_count_audio);
clks[IMX6SX_CLK_SSI1_IPG] = imx_clk_gate2("ssi1_ipg", "ipg", base + 0x7c, 18);
clks[IMX6SX_CLK_SSI2_IPG] = imx_clk_gate2("ssi2_ipg", "ipg", base + 0x7c, 20);
clks[IMX6SX_CLK_SSI3_IPG] = imx_clk_gate2("ssi3_ipg", "ipg", base + 0x7c, 22);
@@ -479,20 +484,11 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node)
/* Audio clocks */
clk_set_rate(clks[IMX6SX_CLK_PLL4_AUDIO_DIV], 393216000);
- /*
- * IMPORTANT:
- * SPDIF and AUDIO clocks are sharing the same gate on i.MX6 Solo X
- * while their rates and gates are being handled by separate drivers.
- * To keep them safe, we here merge them into one clock and use one
- * exact rate so there'd not be any conflict during usages of them.
- *
- * But this results a limitation that if using these two simultaneous,
- * make sure to keep them identical as what the code does over here.
- */
- clk_set_parent(clks[IMX6SX_CLK_SPDIF_SEL], clks[IMX6SX_CLK_PLL3_USB_OTG]);
+ clk_set_parent(clks[IMX6SX_CLK_SPDIF_SEL], clks[IMX6SX_CLK_PLL4_AUDIO_DIV]);
+ clk_set_rate(clks[IMX6SX_CLK_SPDIF_PODF], 98304000);
+
clk_set_parent(clks[IMX6SX_CLK_AUDIO_SEL], clks[IMX6SX_CLK_PLL3_USB_OTG]);
- clk_set_rate(clks[IMX6SX_CLK_SPDIF_PODF], 48000000);
- clk_set_rate(clks[IMX6SX_CLK_AUDIO_PODF], 48000000);
+ clk_set_rate(clks[IMX6SX_CLK_AUDIO_PODF], 24000000);
clk_set_parent(clks[IMX6SX_CLK_SSI1_SEL], clks[IMX6SX_CLK_PLL4_AUDIO_DIV]);
clk_set_parent(clks[IMX6SX_CLK_SSI2_SEL], clks[IMX6SX_CLK_PLL4_AUDIO_DIV]);
diff --git a/arch/arm/mach-imx/clk.h b/arch/arm/mach-imx/clk.h
index e9f235bef175..319a9660e4c2 100644
--- a/arch/arm/mach-imx/clk.h
+++ b/arch/arm/mach-imx/clk.h
@@ -30,7 +30,8 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
struct clk *clk_register_gate2(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 bit_idx,
- u8 clk_gate_flags, spinlock_t *lock);
+ u8 clk_gate_flags, spinlock_t *lock,
+ unsigned int *share_count);
struct clk * imx_obtain_fixed_clock(
const char *name, unsigned long rate);
@@ -39,7 +40,15 @@ static inline struct clk *imx_clk_gate2(const char *name, const char *parent,
void __iomem *reg, u8 shift)
{
return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg,
- shift, 0, &imx_ccm_lock);
+ shift, 0, &imx_ccm_lock, NULL);
+}
+
+static inline struct clk *imx_clk_gate2_shared(const char *name,
+ const char *parent, void __iomem *reg, u8 shift,
+ unsigned int *share_count)
+{
+ return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg,
+ shift, 0, &imx_ccm_lock, share_count);
}
struct clk *imx_clk_pfd(const char *name, const char *parent_name,
diff --git a/drivers/media/platform/mxc/output/mxc_vout.c b/drivers/media/platform/mxc/output/mxc_vout.c
index bd7d1f0446f3..6ebecf38e790 100644
--- a/drivers/media/platform/mxc/output/mxc_vout.c
+++ b/drivers/media/platform/mxc/output/mxc_vout.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2011-2014 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
@@ -577,7 +577,7 @@ static void disp_work_func(struct work_struct *work)
unsigned long flags = 0;
struct ipu_pos ipos;
int ret = 0;
- u32 in_fmt = 0;
+ u32 in_fmt = 0, in_width = 0, in_height = 0;
u32 vdi_cnt = 0;
u32 vdi_frame;
u32 index = 0;
@@ -689,7 +689,11 @@ vdi_frame_rate_double:
}
vout->task.input.paddr = vout->vdoa_task.output.paddr;
in_fmt = vout->task.input.format;
+ in_width = vout->task.input.width;
+ in_height = vout->task.input.height;
vout->task.input.format = vout->vdoa_task.output.format;
+ vout->task.input.width = vout->vdoa_task.output.width;
+ vout->task.input.height = vout->vdoa_task.output.height;
if (vout->task.input.deinterlace.enable) {
tiled_interlaced = 1;
vout->task.input.deinterlace.enable = 0;
@@ -698,8 +702,11 @@ vdi_frame_rate_double:
"tiled queue task\n");
}
ret = ipu_queue_task(&vout->task);
- if ((!vout->tiled_bypass_pp) && tiled_fmt)
+ if ((!vout->tiled_bypass_pp) && tiled_fmt) {
vout->task.input.format = in_fmt;
+ vout->task.input.width = in_width;
+ vout->task.input.height = in_height;
+ }
if (tiled_interlaced)
vout->task.input.deinterlace.enable = 1;
if (ret < 0) {
@@ -1101,6 +1108,7 @@ static inline int vdoaipu_try_task(struct mxc_vout_output *vout)
{
int ret;
int is_1080p_stream;
+ int in_width, in_height;
size_t size;
struct ipu_task *ipu_task = &vout->task;
struct ipu_crop *icrop = &ipu_task->input.crop;
@@ -1146,6 +1154,8 @@ static inline int vdoaipu_try_task(struct mxc_vout_output *vout)
if (is_1080p_stream)
ipu_task->input.crop.h = VALID_HEIGHT_1080P;
in_fmt = ipu_task->input.format;
+ in_width = ipu_task->input.width;
+ in_height = ipu_task->input.height;
ipu_task->input.format = vdoa_task->output.format;
ipu_task->input.height = vdoa_task->output.height;
ipu_task->input.width = vdoa_task->output.width;
@@ -1155,6 +1165,8 @@ static inline int vdoaipu_try_task(struct mxc_vout_output *vout)
if (deinterlace)
ipu_task->input.deinterlace.enable = 1;
ipu_task->input.format = in_fmt;
+ ipu_task->input.width = in_width;
+ ipu_task->input.height = in_height;
return ret;
}
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index b13675cf8975..3447f7c2248d 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -1224,7 +1224,7 @@ fec_enet_interrupt(int irq, void *dev_id)
do {
if (unlikely(!fec_enet_collect_events(fep)))
- return IRQ_NONE;
+ return ret;
if (fep->work_ts && fep->bufdesc_ex) {
ret = IRQ_HANDLED;
@@ -1251,7 +1251,7 @@ fec_enet_interrupt(int irq, void *dev_id)
complete(&fep->mdio_done);
fep->work_mdio = 0;
}
- } while (fec_enet_collect_events(fep));
+ } while (1);
return ret;
}
diff --git a/include/dt-bindings/clock/imx6sx-clock.h b/include/dt-bindings/clock/imx6sx-clock.h
index 4fe0e4686794..0cc658b7e436 100644
--- a/include/dt-bindings/clock/imx6sx-clock.h
+++ b/include/dt-bindings/clock/imx6sx-clock.h
@@ -249,7 +249,6 @@
#define IMX6SX_CLK_ASRC_MEM 236
#define IMX6SX_CLK_SAI1_IPG 237
#define IMX6SX_CLK_SAI2_IPG 238
-#define IMX6SX_CLK_AUDIO_GATE 239
-#define IMX6SX_CLK_CLK_END 240
+#define IMX6SX_CLK_CLK_END 239
#endif /* __DT_BINDINGS_CLOCK_IMX6SX_H */
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c
index 99480fe77f4f..b47c436e03a9 100644
--- a/sound/soc/fsl/fsl_spdif.c
+++ b/sound/soc/fsl/fsl_spdif.c
@@ -1,7 +1,7 @@
/*
* Freescale S/PDIF ALSA SoC Digital Audio Interface (DAI) driver
*
- * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ * Copyright (C) 2013-2014 Freescale Semiconductor, Inc.
*
* Based on stmp3xxx_spdif_dai.c
* Vladimir Barinov <vbarinov@embeddedalley.com>
@@ -78,10 +78,12 @@ struct fsl_spdif_priv {
struct regmap *regmap;
bool dpll_locked;
u8 txclk_div[SPDIF_TXRATE_MAX];
+ u8 sysclk_df[SPDIF_TXRATE_MAX];
u8 txclk_src[SPDIF_TXRATE_MAX];
u8 rxclk_src;
struct clk *txclk[SPDIF_TXRATE_MAX];
struct clk *rxclk;
+ struct clk *coreclk;
struct clk *sysclk;
struct clk *dmaclk;
struct snd_dmaengine_dai_dma_data dma_params_tx;
@@ -353,8 +355,7 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
struct platform_device *pdev = spdif_priv->pdev;
unsigned long csfs = 0;
u32 stc, mask, rate;
- u8 clk, div;
- int ret;
+ u8 clk, div, df;
switch (sample_rate) {
case 32000:
@@ -386,19 +387,10 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
return -EINVAL;
}
- /*
- * The S/PDIF block needs a clock of 64 * fs * div. The S/PDIF block
- * will divide by (div). So request 64 * fs * (div+1) which will
- * get rounded.
- */
- ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (div + 1));
- if (ret) {
- dev_err(&pdev->dev, "failed to set tx clock rate\n");
- return ret;
- }
+ df = spdif_priv->sysclk_df[rate];
dev_dbg(&pdev->dev, "expected clock rate = %d\n",
- (64 * sample_rate * div));
+ (64 * sample_rate * div * df));
dev_dbg(&pdev->dev, "actual clock rate = %ld\n",
clk_get_rate(spdif_priv->txclk[rate]));
@@ -410,6 +402,13 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DIV_MASK;
regmap_update_bits(regmap, REG_SPDIF_STC, mask, stc);
+ /* The min of df should be 2 if valid */
+ if (df >= 2) {
+ regmap_update_bits(regmap, REG_SPDIF_STC,
+ STC_SYSCLK_DIV_MASK, STC_SYSCLK_DIV(df));
+ dev_dbg(&pdev->dev, "use df %d clk %d\n", df, clk);
+ }
+
dev_dbg(&pdev->dev, "set sample rate to %d\n", sample_rate);
return 0;
@@ -426,6 +425,7 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
int ret;
pm_runtime_get_sync(cpu_dai->dev);
+ clk_prepare_enable(spdif_priv->coreclk);
clk_prepare_enable(spdif_priv->dmaclk);
/* Reset module and interrupts only for first initialization */
@@ -494,6 +494,7 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
}
clk_disable_unprepare(spdif_priv->dmaclk);
+ clk_disable_unprepare(spdif_priv->coreclk);
pm_runtime_put_sync(cpu_dai->dev);
}
@@ -1010,40 +1011,51 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
enum spdif_txrate index)
{
const u32 rate[] = { 32000, 44100, 48000 };
- u64 rate_ideal, rate_actual, sub;
+ bool is_sysclk = clk == spdif_priv->sysclk;
+ u64 rate_actual, sub;
+ u32 df_min, df_max, df;
u32 div, arate;
- for (div = 1; div <= 128; div++) {
- rate_ideal = rate[index] * (div + 1) * 64;
- rate_actual = clk_round_rate(clk, rate_ideal);
+ /* The sysclk has an extra divisor [2, 512] */
+ df_min = is_sysclk ? 2 : 1;
+ df_max = is_sysclk ? 512 : 1;
- arate = rate_actual / 64;
- arate /= div;
+ for (df = df_min; df <= df_max; df++) {
+ for (div = 1; div <= 128; div++) {
+ rate_actual = clk_get_rate(clk);
- if (arate == rate[index]) {
- /* We are lucky */
- savesub = 0;
- spdif_priv->txclk_div[index] = div;
- break;
- } else if (arate / rate[index] == 1) {
- /* A little bigger than expect */
- sub = (arate - rate[index]) * 100000;
- do_div(sub, rate[index]);
- if (sub < savesub) {
+ arate = rate_actual / 64;
+ arate /= df * div;
+
+ if (arate == rate[index]) {
+ /* We are lucky */
+ savesub = 0;
+ spdif_priv->txclk_div[index] = div;
+ spdif_priv->sysclk_df[index] = df;
+ goto out;
+ } else if (arate / rate[index] == 1) {
+ /* A little bigger than expect */
+ sub = (arate - rate[index]) * 100000;
+ do_div(sub, rate[index]);
+ if (sub >= savesub)
+ continue;
savesub = sub;
spdif_priv->txclk_div[index] = div;
- }
- } else if (rate[index] / arate == 1) {
- /* A little smaller than expect */
- sub = (rate[index] - arate) * 100000;
- do_div(sub, rate[index]);
- if (sub < savesub) {
+ spdif_priv->sysclk_df[index] = df;
+ } else if (rate[index] / arate == 1) {
+ /* A little smaller than expect */
+ sub = (rate[index] - arate) * 100000;
+ do_div(sub, rate[index]);
+ if (sub >= savesub)
+ continue;
savesub = sub;
spdif_priv->txclk_div[index] = div;
+ spdif_priv->sysclk_df[index] = df;
}
}
}
+out:
return savesub;
}
@@ -1055,6 +1067,7 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
struct device *dev = &pdev->dev;
u64 savesub = 100000, ret;
struct clk *clk;
+ long rate_final;
char tmp[16];
int i;
@@ -1068,13 +1081,6 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
if (!clk_get_rate(clk))
continue;
- /* TODO: We here ignore sysclk source due to imperfect clock
- * selecting mechanism: sysclk is a bit different which we can
- * not change its clock rate but use another inner divider to
- * derive a proper clock rate. */
- if (i == SPDIF_CLK_SRC_SYSCLK)
- continue;
-
ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index);
if (savesub == ret)
continue;
@@ -1092,6 +1098,13 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
spdif_priv->txclk_src[index], rate[index]);
dev_dbg(&pdev->dev, "use divisor %d for %dHz sample rate\n",
spdif_priv->txclk_div[index], rate[index]);
+ if (spdif_priv->txclk[index] == spdif_priv->sysclk)
+ dev_dbg(&pdev->dev, "use sysclk_df %d for %dHz sample rate\n",
+ spdif_priv->sysclk_df[index], rate[index]);
+ rate_final = clk_get_rate(spdif_priv->txclk[index]);
+ rate_final /= 64 * spdif_priv->txclk_div[index] * spdif_priv->sysclk_df[index];
+ dev_dbg(&pdev->dev, "The best rate for %dHz sample rate is %ldHz\n",
+ rate[index], rate_final);
return 0;
}
@@ -1162,6 +1175,13 @@ static int fsl_spdif_probe(struct platform_device *pdev)
return PTR_ERR(spdif_priv->sysclk);
}
+ /* Get core clock for data register access via DMA */
+ spdif_priv->coreclk = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(spdif_priv->coreclk)) {
+ dev_err(&pdev->dev, "no core clock in devicetree\n");
+ return PTR_ERR(spdif_priv->coreclk);
+ }
+
/* Get dma clock for dma script operation */
spdif_priv->dmaclk = devm_clk_get(&pdev->dev, "dma");
if (IS_ERR(spdif_priv->dmaclk)) {