diff options
author | Ke Qinghua <qinghua.ke@freescale.com> | 2014-04-25 15:24:45 +0800 |
---|---|---|
committer | Ke Qinghua <qinghua.ke@freescale.com> | 2014-04-25 15:24:45 +0800 |
commit | 581789ede2f71d716cf6a04181b9006105d2fad8 (patch) | |
tree | bae7e83fe491993d8f35483011aad8f222dc0bfb | |
parent | d782a300614be14690be40ddfd11cba5beb81419 (diff) | |
parent | c78c4d7c0c515e98fe81bf0f5e3a183cf52baeea (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/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/boot/dts/imx6sx-sdb-m4.dts | 29 | ||||
-rw-r--r-- | arch/arm/boot/dts/imx6sx-sdb.dts | 1 | ||||
-rw-r--r-- | arch/arm/configs/imx_v7_defconfig | 1 | ||||
-rw-r--r-- | arch/arm/mach-imx/clk-gate2.c | 47 | ||||
-rw-r--r-- | arch/arm/mach-imx/clk-imx6q.c | 1 | ||||
-rw-r--r-- | arch/arm/mach-imx/clk-imx6sl.c | 3 | ||||
-rw-r--r-- | arch/arm/mach-imx/clk-imx6sx.c | 36 | ||||
-rw-r--r-- | arch/arm/mach-imx/clk.h | 13 | ||||
-rw-r--r-- | drivers/media/platform/mxc/output/mxc_vout.c | 18 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/fec_main.c | 4 | ||||
-rw-r--r-- | include/dt-bindings/clock/imx6sx-clock.h | 3 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_spdif.c | 106 |
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 = <®_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)) { |