[alsa-devel] [PATCH v2 0/6] Add S/PDIF common driver for Samsung SoCs
Hi,
This patch-set is a 2nd version for new S/PDIF common driver that supports S/PDIF PCM audio on S5PC100, S5PC110 and S5PV210.
This patch-set is based on two different branches that :- o ARM patches are based on Kukjin Kim's git branch 'for-next' (commit id - 64dcc6aef86593e478cf297a8508e55b54cceaf8) o ASoC patches are based on Mark Brown's git branch 'for-next' (commit id - fe3e2e7ff2da41bd7a985c4c206e05a95ebe7a6b)
This patch-set updates followings from the 1st submit :- o Add EPLL enable and get_rate function as common EPLL function o Add EPLL rate change warning o Fix S/PDIF's audio clock divider set dynamically at CPU driver o Move S/PDIF specific register macros and structure into CPU driver
This patch-set contains followings :- o To Kukjin Kim and Ben Dooks, - [PATCH v2 1/6] ARM: S5P: Reduce duplicated EPLL control codes - [PATCH v2 2/6] ARM: S5PV210: Fix wrong EPLL rate getting on setup clocks - [PATCH v2 3/6] ARM: S5PV210: Add EPLL clock operations - [PATCH v2 4/6] ARM: S5P: Add EPLL rate change warning o To Jassi Brar, Mark Brown and Liam Girdwood, - [PATCH v2 5/6] ASoC: SAMSUNG: Add S/PDIF CPU driver - [PATCH v2 6/6] ASoC: SAMSUNG: Add Machine driver for S/PDIF PCM audio
Best Regards, Claude(Seungwhan Youn)
S5P Samsung SoCs has a EPLL to support various PLL clock sources for other H/W blocks. Until now, to control EPLL, each of SoCs make their own functions in 'mach-s5pxxx/clock.c'. But some of functions, 'xxx_epll_get_rate()' and 'xxx_epll_enable()', are exactly same in all S5P SoCs, so this patch move these duplicated codes to common EPLL functions that use platform wide.
Signed-off-by: Seungwhan Youn sw.youn@samsung.com --- arch/arm/mach-s5p64x0/clock-s5p6440.c | 16 ++++++++-------- arch/arm/mach-s5p64x0/clock-s5p6450.c | 16 ++++++++-------- arch/arm/mach-s5p64x0/clock.c | 18 ------------------ arch/arm/mach-s5p64x0/include/mach/regs-clock.h | 4 ++-- arch/arm/mach-s5pc100/clock.c | 22 ++-------------------- arch/arm/plat-s5p/clock.c | 20 ++++++++++++++++++++ arch/arm/plat-s5p/include/plat/s5p-clock.h | 4 ++++ 7 files changed, 44 insertions(+), 56 deletions(-)
diff --git a/arch/arm/mach-s5p64x0/clock-s5p6440.c b/arch/arm/mach-s5p64x0/clock-s5p6440.c index f93dcd8..1e3f517 100644 --- a/arch/arm/mach-s5p64x0/clock-s5p6440.c +++ b/arch/arm/mach-s5p64x0/clock-s5p6440.c @@ -55,8 +55,8 @@ static int s5p6440_epll_set_rate(struct clk *clk, unsigned long rate) if (clk->rate == rate) /* Return if nothing changed */ return 0;
- epll_con = __raw_readl(S5P64X0_EPLL_CON); - epll_con_k = __raw_readl(S5P64X0_EPLL_CON_K); + epll_con = __raw_readl(S5P_EPLL_CON); + epll_con_k = __raw_readl(S5P_EPLL_CON_K);
epll_con_k &= ~(PLL90XX_KDIV_MASK); epll_con &= ~(PLL90XX_MDIV_MASK | PLL90XX_PDIV_MASK | PLL90XX_SDIV_MASK); @@ -76,8 +76,8 @@ static int s5p6440_epll_set_rate(struct clk *clk, unsigned long rate) return -EINVAL; }
- __raw_writel(epll_con, S5P64X0_EPLL_CON); - __raw_writel(epll_con_k, S5P64X0_EPLL_CON_K); + __raw_writel(epll_con, S5P_EPLL_CON); + __raw_writel(epll_con_k, S5P_EPLL_CON_K);
clk->rate = rate;
@@ -85,7 +85,7 @@ static int s5p6440_epll_set_rate(struct clk *clk, unsigned long rate) }
static struct clk_ops s5p6440_epll_ops = { - .get_rate = s5p64x0_epll_get_rate, + .get_rate = s5p_epll_get_rate, .set_rate = s5p6440_epll_set_rate, };
@@ -548,7 +548,7 @@ void __init_or_cpufreq s5p6440_setup_clocks(void)
/* Set S5P6440 functions for clk_fout_epll */
- clk_fout_epll.enable = s5p64x0_epll_enable; + clk_fout_epll.enable = s5p_epll_enable; clk_fout_epll.ops = &s5p6440_epll_ops;
clk_48m.enable = s5p64x0_clk48m_ctrl; @@ -561,8 +561,8 @@ void __init_or_cpufreq s5p6440_setup_clocks(void)
apll = s5p_get_pll45xx(xtal, __raw_readl(S5P64X0_APLL_CON), pll_4502); mpll = s5p_get_pll45xx(xtal, __raw_readl(S5P64X0_MPLL_CON), pll_4502); - epll = s5p_get_pll90xx(xtal, __raw_readl(S5P64X0_EPLL_CON), - __raw_readl(S5P64X0_EPLL_CON_K)); + epll = s5p_get_pll90xx(xtal, __raw_readl(S5P_EPLL_CON), + __raw_readl(S5P_EPLL_CON_K));
clk_fout_apll.rate = apll; clk_fout_mpll.rate = mpll; diff --git a/arch/arm/mach-s5p64x0/clock-s5p6450.c b/arch/arm/mach-s5p64x0/clock-s5p6450.c index f9afb05..ee03ec3 100644 --- a/arch/arm/mach-s5p64x0/clock-s5p6450.c +++ b/arch/arm/mach-s5p64x0/clock-s5p6450.c @@ -56,8 +56,8 @@ static int s5p6450_epll_set_rate(struct clk *clk, unsigned long rate) if (clk->rate == rate) /* Return if nothing changed */ return 0;
- epll_con = __raw_readl(S5P64X0_EPLL_CON); - epll_con_k = __raw_readl(S5P64X0_EPLL_CON_K); + epll_con = __raw_readl(S5P_EPLL_CON); + epll_con_k = __raw_readl(S5P_EPLL_CON_K);
epll_con_k &= ~(PLL90XX_KDIV_MASK); epll_con &= ~(PLL90XX_MDIV_MASK | PLL90XX_PDIV_MASK | PLL90XX_SDIV_MASK); @@ -77,8 +77,8 @@ static int s5p6450_epll_set_rate(struct clk *clk, unsigned long rate) return -EINVAL; }
- __raw_writel(epll_con, S5P64X0_EPLL_CON); - __raw_writel(epll_con_k, S5P64X0_EPLL_CON_K); + __raw_writel(epll_con, S5P_EPLL_CON); + __raw_writel(epll_con_k, S5P_EPLL_CON_K);
clk->rate = rate;
@@ -86,7 +86,7 @@ static int s5p6450_epll_set_rate(struct clk *clk, unsigned long rate) }
static struct clk_ops s5p6450_epll_ops = { - .get_rate = s5p64x0_epll_get_rate, + .get_rate = s5p_epll_get_rate, .set_rate = s5p6450_epll_set_rate, };
@@ -581,7 +581,7 @@ void __init_or_cpufreq s5p6450_setup_clocks(void)
/* Set S5P6450 functions for clk_fout_epll */
- clk_fout_epll.enable = s5p64x0_epll_enable; + clk_fout_epll.enable = s5p_epll_enable; clk_fout_epll.ops = &s5p6450_epll_ops;
clk_48m.enable = s5p64x0_clk48m_ctrl; @@ -594,8 +594,8 @@ void __init_or_cpufreq s5p6450_setup_clocks(void)
apll = s5p_get_pll45xx(xtal, __raw_readl(S5P64X0_APLL_CON), pll_4502); mpll = s5p_get_pll45xx(xtal, __raw_readl(S5P64X0_MPLL_CON), pll_4502); - epll = s5p_get_pll90xx(xtal, __raw_readl(S5P64X0_EPLL_CON), - __raw_readl(S5P64X0_EPLL_CON_K)); + epll = s5p_get_pll90xx(xtal, __raw_readl(S5P_EPLL_CON), + __raw_readl(S5P_EPLL_CON_K)); dpll = s5p_get_pll46xx(xtal, __raw_readl(S5P6450_DPLL_CON), __raw_readl(S5P6450_DPLL_CON_K), pll_4650c);
diff --git a/arch/arm/mach-s5p64x0/clock.c b/arch/arm/mach-s5p64x0/clock.c index 523ba80..b52c6e2 100644 --- a/arch/arm/mach-s5p64x0/clock.c +++ b/arch/arm/mach-s5p64x0/clock.c @@ -73,24 +73,6 @@ static const u32 clock_table[][3] = { {L2 * 1000, (3 << ARM_DIV_RATIO_SHIFT), (0 << S5P64X0_CLKDIV0_HCLK_SHIFT)}, };
-int s5p64x0_epll_enable(struct clk *clk, int enable) -{ - unsigned int ctrlbit = clk->ctrlbit; - unsigned int epll_con = __raw_readl(S5P64X0_EPLL_CON) & ~ctrlbit; - - if (enable) - __raw_writel(epll_con | ctrlbit, S5P64X0_EPLL_CON); - else - __raw_writel(epll_con, S5P64X0_EPLL_CON); - - return 0; -} - -unsigned long s5p64x0_epll_get_rate(struct clk *clk) -{ - return clk->rate; -} - unsigned long s5p64x0_armclk_get_rate(struct clk *clk) { unsigned long rate = clk_get_rate(clk->parent); diff --git a/arch/arm/mach-s5p64x0/include/mach/regs-clock.h b/arch/arm/mach-s5p64x0/include/mach/regs-clock.h index 58e1bc8..ac30123 100644 --- a/arch/arm/mach-s5p64x0/include/mach/regs-clock.h +++ b/arch/arm/mach-s5p64x0/include/mach/regs-clock.h @@ -19,8 +19,8 @@
#define S5P64X0_APLL_CON S5P_CLKREG(0x0C) #define S5P64X0_MPLL_CON S5P_CLKREG(0x10) -#define S5P64X0_EPLL_CON S5P_CLKREG(0x14) -#define S5P64X0_EPLL_CON_K S5P_CLKREG(0x18) +#define S5P_EPLL_CON S5P_CLKREG(0x14) +#define S5P_EPLL_CON_K S5P_CLKREG(0x18)
#define S5P64X0_CLK_SRC0 S5P_CLKREG(0x1C)
diff --git a/arch/arm/mach-s5pc100/clock.c b/arch/arm/mach-s5pc100/clock.c index 306ae74..42c2636 100644 --- a/arch/arm/mach-s5pc100/clock.c +++ b/arch/arm/mach-s5pc100/clock.c @@ -273,24 +273,6 @@ static struct clksrc_clk clk_div_hdmi = { .reg_div = { .reg = S5P_CLK_DIV3, .shift = 28, .size = 4 }, };
-static int s5pc100_epll_enable(struct clk *clk, int enable) -{ - unsigned int ctrlbit = clk->ctrlbit; - unsigned int epll_con = __raw_readl(S5P_EPLL_CON) & ~ctrlbit; - - if (enable) - __raw_writel(epll_con | ctrlbit, S5P_EPLL_CON); - else - __raw_writel(epll_con, S5P_EPLL_CON); - - return 0; -} - -static unsigned long s5pc100_epll_get_rate(struct clk *clk) -{ - return clk->rate; -} - static u32 epll_div[][4] = { { 32750000, 131, 3, 4 }, { 32768000, 131, 3, 4 }, @@ -347,7 +329,7 @@ static int s5pc100_epll_set_rate(struct clk *clk, unsigned long rate) }
static struct clk_ops s5pc100_epll_ops = { - .get_rate = s5pc100_epll_get_rate, + .get_rate = s5p_epll_get_rate, .set_rate = s5pc100_epll_set_rate, };
@@ -1261,7 +1243,7 @@ void __init_or_cpufreq s5pc100_setup_clocks(void) unsigned int ptr;
/* Set S5PC100 functions for clk_fout_epll */ - clk_fout_epll.enable = s5pc100_epll_enable; + clk_fout_epll.enable = s5p_epll_enable; clk_fout_epll.ops = &s5pc100_epll_ops;
printk(KERN_DEBUG "%s: registering clocks\n", __func__); diff --git a/arch/arm/plat-s5p/clock.c b/arch/arm/plat-s5p/clock.c index 8188009..8d081d9 100644 --- a/arch/arm/plat-s5p/clock.c +++ b/arch/arm/plat-s5p/clock.c @@ -21,6 +21,8 @@ #include <linux/io.h> #include <asm/div64.h>
+#include <mach/regs-clock.h> + #include <plat/clock.h> #include <plat/clock-clksrc.h> #include <plat/s5p-clock.h> @@ -148,6 +150,24 @@ int s5p_gatectrl(void __iomem *reg, struct clk *clk, int enable) return 0; }
+int s5p_epll_enable(struct clk *clk, int enable) +{ + unsigned int ctrlbit = clk->ctrlbit; + unsigned int epll_con = __raw_readl(S5P_EPLL_CON) & ~ctrlbit; + + if (enable) + __raw_writel(epll_con | ctrlbit, S5P_EPLL_CON); + else + __raw_writel(epll_con, S5P_EPLL_CON); + + return 0; +} + +unsigned long s5p_epll_get_rate(struct clk *clk) +{ + return clk->rate; +} + static struct clk *s5p_clks[] __initdata = { &clk_ext_xtal_mux, &clk_48m, diff --git a/arch/arm/plat-s5p/include/plat/s5p-clock.h b/arch/arm/plat-s5p/include/plat/s5p-clock.h index 17036c8..2b6dcff 100644 --- a/arch/arm/plat-s5p/include/plat/s5p-clock.h +++ b/arch/arm/plat-s5p/include/plat/s5p-clock.h @@ -43,4 +43,8 @@ extern struct clksrc_sources clk_src_dpll;
extern int s5p_gatectrl(void __iomem *reg, struct clk *clk, int enable);
+/* Common EPLL operations for S5P platform */ +extern int s5p_epll_enable(struct clk *clk, int enable); +extern unsigned long s5p_epll_get_rate(struct clk *clk); + #endif /* __ASM_PLAT_S5P_CLOCK_H */
Seungwhan Youn wrote:
S5P Samsung SoCs has a EPLL to support various PLL clock sources for other H/W blocks. Until now, to control EPLL, each of SoCs make their own
functions
in 'mach-s5pxxx/clock.c'. But some of functions, 'xxx_epll_get_rate()' and 'xxx_epll_enable()', are exactly same in all S5P SoCs, so this patch move these duplicated codes to common EPLL functions that use platform wide.
Signed-off-by: Seungwhan Youn sw.youn@samsung.com
arch/arm/mach-s5p64x0/clock-s5p6440.c | 16 ++++++++-------- arch/arm/mach-s5p64x0/clock-s5p6450.c | 16 ++++++++-------- arch/arm/mach-s5p64x0/clock.c | 18 ------------------ arch/arm/mach-s5p64x0/include/mach/regs-clock.h | 4 ++-- arch/arm/mach-s5pc100/clock.c | 22
++--------------------
arch/arm/plat-s5p/clock.c | 20 ++++++++++++++++++++ arch/arm/plat-s5p/include/plat/s5p-clock.h | 4 ++++ 7 files changed, 44 insertions(+), 56 deletions(-)
Hi,
Thanks for your updating EPLL control.
I think, if we use just S5P64X0_EPLL_CON in the mach-s5p64x0/include/mach/regs-clock.h, no need to change many codes such as this patch. But need to re-define for compatibility like following in the mach-s5p64x0. ... +/* compatibility defines */ +#define S5P_EPLL_CON S5P64X0_EPLL_CON ...
Maybe as you know, S5PV310 EPLL configuration definition is different from the others. So need to add S5P_EPLL_CON definition for building.
And actually, we need to sort out clock definitions for Samsung SoCs. Will do it soon.
Anyway, could you please re-work this?
diff --git a/arch/arm/mach-s5p64x0/clock-s5p6440.c b/arch/arm/mach- s5p64x0/clock-s5p6440.c index f93dcd8..1e3f517 100644 --- a/arch/arm/mach-s5p64x0/clock-s5p6440.c +++ b/arch/arm/mach-s5p64x0/clock-s5p6440.c @@ -55,8 +55,8 @@ static int s5p6440_epll_set_rate(struct clk *clk,
unsigned long
rate) if (clk->rate == rate) /* Return if nothing changed */ return 0;
- epll_con = __raw_readl(S5P64X0_EPLL_CON);
- epll_con_k = __raw_readl(S5P64X0_EPLL_CON_K);
epll_con = __raw_readl(S5P_EPLL_CON);
epll_con_k = __raw_readl(S5P_EPLL_CON_K);
epll_con_k &= ~(PLL90XX_KDIV_MASK); epll_con &= ~(PLL90XX_MDIV_MASK | PLL90XX_PDIV_MASK |
PLL90XX_SDIV_MASK); @@ -76,8 +76,8 @@ static int s5p6440_epll_set_rate(struct clk *clk,
unsigned long
rate) return -EINVAL; }
- __raw_writel(epll_con, S5P64X0_EPLL_CON);
- __raw_writel(epll_con_k, S5P64X0_EPLL_CON_K);
__raw_writel(epll_con, S5P_EPLL_CON);
__raw_writel(epll_con_k, S5P_EPLL_CON_K);
clk->rate = rate;
@@ -85,7 +85,7 @@ static int s5p6440_epll_set_rate(struct clk *clk,
unsigned long
rate) }
static struct clk_ops s5p6440_epll_ops = {
- .get_rate = s5p64x0_epll_get_rate,
- .get_rate = s5p_epll_get_rate, .set_rate = s5p6440_epll_set_rate,
};
@@ -548,7 +548,7 @@ void __init_or_cpufreq s5p6440_setup_clocks(void)
/* Set S5P6440 functions for clk_fout_epll */
- clk_fout_epll.enable = s5p64x0_epll_enable;
clk_fout_epll.enable = s5p_epll_enable; clk_fout_epll.ops = &s5p6440_epll_ops;
clk_48m.enable = s5p64x0_clk48m_ctrl;
@@ -561,8 +561,8 @@ void __init_or_cpufreq s5p6440_setup_clocks(void)
apll = s5p_get_pll45xx(xtal, __raw_readl(S5P64X0_APLL_CON),
pll_4502);
mpll = s5p_get_pll45xx(xtal, __raw_readl(S5P64X0_MPLL_CON), pll_4502);
- epll = s5p_get_pll90xx(xtal, __raw_readl(S5P64X0_EPLL_CON),
__raw_readl(S5P64X0_EPLL_CON_K));
epll = s5p_get_pll90xx(xtal, __raw_readl(S5P_EPLL_CON),
__raw_readl(S5P_EPLL_CON_K));
clk_fout_apll.rate = apll; clk_fout_mpll.rate = mpll;
diff --git a/arch/arm/mach-s5p64x0/clock-s5p6450.c b/arch/arm/mach- s5p64x0/clock-s5p6450.c index f9afb05..ee03ec3 100644 --- a/arch/arm/mach-s5p64x0/clock-s5p6450.c +++ b/arch/arm/mach-s5p64x0/clock-s5p6450.c @@ -56,8 +56,8 @@ static int s5p6450_epll_set_rate(struct clk *clk,
unsigned long
rate) if (clk->rate == rate) /* Return if nothing changed */ return 0;
- epll_con = __raw_readl(S5P64X0_EPLL_CON);
- epll_con_k = __raw_readl(S5P64X0_EPLL_CON_K);
epll_con = __raw_readl(S5P_EPLL_CON);
epll_con_k = __raw_readl(S5P_EPLL_CON_K);
epll_con_k &= ~(PLL90XX_KDIV_MASK); epll_con &= ~(PLL90XX_MDIV_MASK | PLL90XX_PDIV_MASK |
PLL90XX_SDIV_MASK); @@ -77,8 +77,8 @@ static int s5p6450_epll_set_rate(struct clk *clk,
unsigned long
rate) return -EINVAL; }
- __raw_writel(epll_con, S5P64X0_EPLL_CON);
- __raw_writel(epll_con_k, S5P64X0_EPLL_CON_K);
__raw_writel(epll_con, S5P_EPLL_CON);
__raw_writel(epll_con_k, S5P_EPLL_CON_K);
clk->rate = rate;
@@ -86,7 +86,7 @@ static int s5p6450_epll_set_rate(struct clk *clk,
unsigned long
rate) }
static struct clk_ops s5p6450_epll_ops = {
- .get_rate = s5p64x0_epll_get_rate,
- .get_rate = s5p_epll_get_rate, .set_rate = s5p6450_epll_set_rate,
};
@@ -581,7 +581,7 @@ void __init_or_cpufreq s5p6450_setup_clocks(void)
/* Set S5P6450 functions for clk_fout_epll */
- clk_fout_epll.enable = s5p64x0_epll_enable;
clk_fout_epll.enable = s5p_epll_enable; clk_fout_epll.ops = &s5p6450_epll_ops;
clk_48m.enable = s5p64x0_clk48m_ctrl;
@@ -594,8 +594,8 @@ void __init_or_cpufreq s5p6450_setup_clocks(void)
apll = s5p_get_pll45xx(xtal, __raw_readl(S5P64X0_APLL_CON),
pll_4502);
mpll = s5p_get_pll45xx(xtal, __raw_readl(S5P64X0_MPLL_CON), pll_4502);
- epll = s5p_get_pll90xx(xtal, __raw_readl(S5P64X0_EPLL_CON),
__raw_readl(S5P64X0_EPLL_CON_K));
- epll = s5p_get_pll90xx(xtal, __raw_readl(S5P_EPLL_CON),
dpll = s5p_get_pll46xx(xtal, __raw_readl(S5P6450_DPLL_CON), __raw_readl(S5P6450_DPLL_CON_K),__raw_readl(S5P_EPLL_CON_K));
pll_4650c);
diff --git a/arch/arm/mach-s5p64x0/clock.c b/arch/arm/mach-s5p64x0/clock.c index 523ba80..b52c6e2 100644 --- a/arch/arm/mach-s5p64x0/clock.c +++ b/arch/arm/mach-s5p64x0/clock.c @@ -73,24 +73,6 @@ static const u32 clock_table[][3] = { {L2 * 1000, (3 << ARM_DIV_RATIO_SHIFT), (0 << S5P64X0_CLKDIV0_HCLK_SHIFT)}, };
-int s5p64x0_epll_enable(struct clk *clk, int enable) -{
- unsigned int ctrlbit = clk->ctrlbit;
- unsigned int epll_con = __raw_readl(S5P64X0_EPLL_CON) & ~ctrlbit;
- if (enable)
__raw_writel(epll_con | ctrlbit, S5P64X0_EPLL_CON);
- else
__raw_writel(epll_con, S5P64X0_EPLL_CON);
- return 0;
-}
-unsigned long s5p64x0_epll_get_rate(struct clk *clk) -{
- return clk->rate;
-}
unsigned long s5p64x0_armclk_get_rate(struct clk *clk) { unsigned long rate = clk_get_rate(clk->parent); diff --git a/arch/arm/mach-s5p64x0/include/mach/regs-clock.h
b/arch/arm/mach-
s5p64x0/include/mach/regs-clock.h index 58e1bc8..ac30123 100644 --- a/arch/arm/mach-s5p64x0/include/mach/regs-clock.h +++ b/arch/arm/mach-s5p64x0/include/mach/regs-clock.h @@ -19,8 +19,8 @@
#define S5P64X0_APLL_CON S5P_CLKREG(0x0C) #define S5P64X0_MPLL_CON S5P_CLKREG(0x10) -#define S5P64X0_EPLL_CON S5P_CLKREG(0x14) -#define S5P64X0_EPLL_CON_K S5P_CLKREG(0x18) +#define S5P_EPLL_CON S5P_CLKREG(0x14) +#define S5P_EPLL_CON_K S5P_CLKREG(0x18)
#define S5P64X0_CLK_SRC0 S5P_CLKREG(0x1C)
diff --git a/arch/arm/mach-s5pc100/clock.c b/arch/arm/mach-s5pc100/clock.c index 306ae74..42c2636 100644 --- a/arch/arm/mach-s5pc100/clock.c +++ b/arch/arm/mach-s5pc100/clock.c @@ -273,24 +273,6 @@ static struct clksrc_clk clk_div_hdmi = { .reg_div = { .reg = S5P_CLK_DIV3, .shift = 28, .size = 4 }, };
-static int s5pc100_epll_enable(struct clk *clk, int enable) -{
- unsigned int ctrlbit = clk->ctrlbit;
- unsigned int epll_con = __raw_readl(S5P_EPLL_CON) & ~ctrlbit;
- if (enable)
__raw_writel(epll_con | ctrlbit, S5P_EPLL_CON);
- else
__raw_writel(epll_con, S5P_EPLL_CON);
- return 0;
-}
-static unsigned long s5pc100_epll_get_rate(struct clk *clk) -{
- return clk->rate;
-}
static u32 epll_div[][4] = { { 32750000, 131, 3, 4 }, { 32768000, 131, 3, 4 }, @@ -347,7 +329,7 @@ static int s5pc100_epll_set_rate(struct clk *clk,
unsigned
long rate) }
static struct clk_ops s5pc100_epll_ops = {
- .get_rate = s5pc100_epll_get_rate,
- .get_rate = s5p_epll_get_rate, .set_rate = s5pc100_epll_set_rate,
};
@@ -1261,7 +1243,7 @@ void __init_or_cpufreq s5pc100_setup_clocks(void) unsigned int ptr;
/* Set S5PC100 functions for clk_fout_epll */
- clk_fout_epll.enable = s5pc100_epll_enable;
clk_fout_epll.enable = s5p_epll_enable; clk_fout_epll.ops = &s5pc100_epll_ops;
printk(KERN_DEBUG "%s: registering clocks\n", __func__);
diff --git a/arch/arm/plat-s5p/clock.c b/arch/arm/plat-s5p/clock.c index 8188009..8d081d9 100644 --- a/arch/arm/plat-s5p/clock.c +++ b/arch/arm/plat-s5p/clock.c @@ -21,6 +21,8 @@ #include <linux/io.h> #include <asm/div64.h>
+#include <mach/regs-clock.h>
#include <plat/clock.h> #include <plat/clock-clksrc.h> #include <plat/s5p-clock.h> @@ -148,6 +150,24 @@ int s5p_gatectrl(void __iomem *reg, struct clk *clk,
int
enable) return 0; }
+int s5p_epll_enable(struct clk *clk, int enable) +{
- unsigned int ctrlbit = clk->ctrlbit;
- unsigned int epll_con = __raw_readl(S5P_EPLL_CON) & ~ctrlbit;
- if (enable)
__raw_writel(epll_con | ctrlbit, S5P_EPLL_CON);
- else
__raw_writel(epll_con, S5P_EPLL_CON);
- return 0;
+}
+unsigned long s5p_epll_get_rate(struct clk *clk) +{
- return clk->rate;
+}
static struct clk *s5p_clks[] __initdata = { &clk_ext_xtal_mux, &clk_48m, diff --git a/arch/arm/plat-s5p/include/plat/s5p-clock.h b/arch/arm/plat- s5p/include/plat/s5p-clock.h index 17036c8..2b6dcff 100644 --- a/arch/arm/plat-s5p/include/plat/s5p-clock.h +++ b/arch/arm/plat-s5p/include/plat/s5p-clock.h @@ -43,4 +43,8 @@ extern struct clksrc_sources clk_src_dpll;
extern int s5p_gatectrl(void __iomem *reg, struct clk *clk, int enable);
+/* Common EPLL operations for S5P platform */ +extern int s5p_epll_enable(struct clk *clk, int enable); +extern unsigned long s5p_epll_get_rate(struct clk *clk);
#endif /* __ASM_PLAT_S5P_CLOCK_H */
Thanks.
Best regards, Kgene. -- Kukjin Kim kgene.kim@samsung.com, Senior Engineer, SW Solution Development Team, Samsung Electronics Co., Ltd.
On Wed, Oct 13, 2010 at 5:05 PM, Kukjin Kim kgene.kim@samsung.com wrote:
Seungwhan Youn wrote:
S5P Samsung SoCs has a EPLL to support various PLL clock sources for other H/W blocks. Until now, to control EPLL, each of SoCs make their own
functions
in 'mach-s5pxxx/clock.c'. But some of functions, 'xxx_epll_get_rate()' and 'xxx_epll_enable()', are exactly same in all S5P SoCs, so this patch move these duplicated codes to common EPLL functions that use platform wide.
Signed-off-by: Seungwhan Youn sw.youn@samsung.com
arch/arm/mach-s5p64x0/clock-s5p6440.c | 16 ++++++++-------- arch/arm/mach-s5p64x0/clock-s5p6450.c | 16 ++++++++-------- arch/arm/mach-s5p64x0/clock.c | 18 ------------------ arch/arm/mach-s5p64x0/include/mach/regs-clock.h | 4 ++-- arch/arm/mach-s5pc100/clock.c | 22
++--------------------
arch/arm/plat-s5p/clock.c | 20 ++++++++++++++++++++ arch/arm/plat-s5p/include/plat/s5p-clock.h | 4 ++++ 7 files changed, 44 insertions(+), 56 deletions(-)
Hi,
Thanks for your updating EPLL control.
I think, if we use just S5P64X0_EPLL_CON in the mach-s5p64x0/include/mach/regs-clock.h, no need to change many codes such as this patch. But need to re-define for compatibility like following in the mach-s5p64x0. ... +/* compatibility defines */ +#define S5P_EPLL_CON S5P64X0_EPLL_CON ...
Maybe as you know, S5PV310 EPLL configuration definition is different from the others. So need to add S5P_EPLL_CON definition for building.
And actually, we need to sort out clock definitions for Samsung SoCs. Will do it soon.
Anyway, could you please re-work this?
Okay, I'll re-work and re-send this right now.
Thank you, Claude
This patch fix wrong EPLL getting on setup clocks on S5PV210.
Signed-off-by: Seungwhan Youn sw.youn@samsung.com --- arch/arm/mach-s5pv210/clock.c | 3 ++- arch/arm/mach-s5pv210/include/mach/regs-clock.h | 1 + 2 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/arch/arm/mach-s5pv210/clock.c b/arch/arm/mach-s5pv210/clock.c index 3f3b7ba..f1ec5bb 100644 --- a/arch/arm/mach-s5pv210/clock.c +++ b/arch/arm/mach-s5pv210/clock.c @@ -1040,7 +1040,8 @@ void __init_or_cpufreq s5pv210_setup_clocks(void)
apll = s5p_get_pll45xx(xtal, __raw_readl(S5P_APLL_CON), pll_4508); mpll = s5p_get_pll45xx(xtal, __raw_readl(S5P_MPLL_CON), pll_4502); - epll = s5p_get_pll45xx(xtal, __raw_readl(S5P_EPLL_CON), pll_4500); + epll = s5p_get_pll46xx(xtal, __raw_readl(S5P_EPLL_CON), + __raw_readl(S5P_EPLL_CON1), pll_4600); vpllsrc = clk_get_rate(&clk_vpllsrc.clk); vpll = s5p_get_pll45xx(vpllsrc, __raw_readl(S5P_VPLL_CON), pll_4502);
diff --git a/arch/arm/mach-s5pv210/include/mach/regs-clock.h b/arch/arm/mach-s5pv210/include/mach/regs-clock.h index 929fd3a..eaca7bc 100644 --- a/arch/arm/mach-s5pv210/include/mach/regs-clock.h +++ b/arch/arm/mach-s5pv210/include/mach/regs-clock.h @@ -25,6 +25,7 @@ #define S5P_APLL_CON S5P_CLKREG(0x100) #define S5P_MPLL_CON S5P_CLKREG(0x108) #define S5P_EPLL_CON S5P_CLKREG(0x110) +#define S5P_EPLL_CON1 S5P_CLKREG(0x114) #define S5P_VPLL_CON S5P_CLKREG(0x120)
#define S5P_CLK_SRC0 S5P_CLKREG(0x200)
This patch adds EPLL specific clock get_rate/set_rate operations on S5PV210.
Signed-off-by: Seungwhan Youn sw.youn@samsung.com --- arch/arm/mach-s5pv210/clock.c | 77 +++++++++++++++++++++++++++++++++++++++++ 1 files changed, 77 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-s5pv210/clock.c b/arch/arm/mach-s5pv210/clock.c index f1ec5bb..fdfadac 100644 --- a/arch/arm/mach-s5pv210/clock.c +++ b/arch/arm/mach-s5pv210/clock.c @@ -1003,6 +1003,79 @@ static struct clksrc_clk *sysclks[] = { &clk_sclk_spdif, };
+static u32 epll_div[][6] = { + { 48000000, 0, 48, 3, 3, 0 }, + { 96000000, 0, 48, 3, 2, 0 }, + { 144000000, 1, 72, 3, 2, 0 }, + { 192000000, 0, 48, 3, 1, 0 }, + { 288000000, 1, 72, 3, 1, 0 }, + { 32750000, 1, 65, 3, 4, 35127 }, + { 32768000, 1, 65, 3, 4, 35127 }, + { 45158400, 0, 45, 3, 3, 10355 }, + { 45000000, 0, 45, 3, 3, 10355 }, + { 45158000, 0, 45, 3, 3, 10355 }, + { 49125000, 0, 49, 3, 3, 9961 }, + { 49152000, 0, 49, 3, 3, 9961 }, + { 67737600, 1, 67, 3, 3, 48366 }, + { 67738000, 1, 67, 3, 3, 48366 }, + { 73800000, 1, 73, 3, 3, 47710 }, + { 73728000, 1, 73, 3, 3, 47710 }, + { 36000000, 1, 32, 3, 4, 0 }, + { 60000000, 1, 60, 3, 3, 0 }, + { 72000000, 1, 72, 3, 3, 0 }, + { 80000000, 1, 80, 3, 3, 0 }, + { 84000000, 0, 42, 3, 2, 0 }, + { 50000000, 0, 50, 3, 3, 0 }, +}; + +static int s5pv210_epll_set_rate(struct clk *clk, unsigned long rate) +{ + unsigned int epll_con, epll_con_k; + unsigned int i; + + /* Return if nothing changed */ + if (clk->rate == rate) + return 0; + + epll_con = __raw_readl(S5P_EPLL_CON); + epll_con_k = __raw_readl(S5P_EPLL_CON1); + + epll_con_k &= ~PLL46XX_KDIV_MASK; + epll_con &= ~(1 << 27 | + PLL46XX_MDIV_MASK << PLL46XX_MDIV_SHIFT | + PLL46XX_PDIV_MASK << PLL46XX_PDIV_SHIFT | + PLL46XX_SDIV_MASK << PLL46XX_SDIV_SHIFT); + + for (i = 0; i < ARRAY_SIZE(epll_div); i++) { + if (epll_div[i][0] == rate) { + epll_con_k |= epll_div[i][5] << 0; + epll_con |= (epll_div[i][1] << 27 | + epll_div[i][2] << PLL46XX_MDIV_SHIFT | + epll_div[i][3] << PLL46XX_PDIV_SHIFT | + epll_div[i][4] << PLL46XX_SDIV_SHIFT); + break; + } + } + + if (i == ARRAY_SIZE(epll_div)) { + printk(KERN_ERR "%s: Invalid Clock EPLL Frequency\n", + __func__); + return -EINVAL; + } + + __raw_writel(epll_con, S5P_EPLL_CON); + __raw_writel(epll_con_k, S5P_EPLL_CON1); + + clk->rate = rate; + + return 0; +} + +static struct clk_ops s5pv210_epll_ops = { + .set_rate = s5pv210_epll_set_rate, + .get_rate = s5p_epll_get_rate, +}; + void __init_or_cpufreq s5pv210_setup_clocks(void) { struct clk *xtal_clk; @@ -1022,6 +1095,10 @@ void __init_or_cpufreq s5pv210_setup_clocks(void) unsigned int ptr; u32 clkdiv0, clkdiv1;
+ /* Set functions for clk_fout_epll */ + clk_fout_epll.enable = s5p_epll_enable; + clk_fout_epll.ops = &s5pv210_epll_ops; + printk(KERN_DEBUG "%s: registering clocks\n", __func__);
clkdiv0 = __raw_readl(S5P_CLK_DIV0);
Seungwhan Youn wrote:
This patch adds EPLL specific clock get_rate/set_rate operations on S5PV210.
Signed-off-by: Seungwhan Youn sw.youn@samsung.com
arch/arm/mach-s5pv210/clock.c | 77 +++++++++++++++++++++++++++++++++++++++++ 1 files changed, 77 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-s5pv210/clock.c b/arch/arm/mach-s5pv210/clock.c index f1ec5bb..fdfadac 100644 --- a/arch/arm/mach-s5pv210/clock.c +++ b/arch/arm/mach-s5pv210/clock.c @@ -1003,6 +1003,79 @@ static struct clksrc_clk *sysclks[] = { &clk_sclk_spdif, };
+static u32 epll_div[][6] = {
- { 48000000, 0, 48, 3, 3, 0 },
- { 96000000, 0, 48, 3, 2, 0 },
- { 144000000, 1, 72, 3, 2, 0 },
- { 192000000, 0, 48, 3, 1, 0 },
- { 288000000, 1, 72, 3, 1, 0 },
- { 32750000, 1, 65, 3, 4, 35127 },
- { 32768000, 1, 65, 3, 4, 35127 },
- { 45158400, 0, 45, 3, 3, 10355 },
- { 45000000, 0, 45, 3, 3, 10355 },
- { 45158000, 0, 45, 3, 3, 10355 },
- { 49125000, 0, 49, 3, 3, 9961 },
- { 49152000, 0, 49, 3, 3, 9961 },
- { 67737600, 1, 67, 3, 3, 48366 },
- { 67738000, 1, 67, 3, 3, 48366 },
- { 73800000, 1, 73, 3, 3, 47710 },
- { 73728000, 1, 73, 3, 3, 47710 },
- { 36000000, 1, 32, 3, 4, 0 },
- { 60000000, 1, 60, 3, 3, 0 },
- { 72000000, 1, 72, 3, 3, 0 },
- { 80000000, 1, 80, 3, 3, 0 },
- { 84000000, 0, 42, 3, 2, 0 },
- { 50000000, 0, 50, 3, 3, 0 },
+};
+static int s5pv210_epll_set_rate(struct clk *clk, unsigned long rate) +{
- unsigned int epll_con, epll_con_k;
- unsigned int i;
- /* Return if nothing changed */
- if (clk->rate == rate)
return 0;
- epll_con = __raw_readl(S5P_EPLL_CON);
- epll_con_k = __raw_readl(S5P_EPLL_CON1);
- epll_con_k &= ~PLL46XX_KDIV_MASK;
- epll_con &= ~(1 << 27 |
PLL46XX_MDIV_MASK << PLL46XX_MDIV_SHIFT |
PLL46XX_PDIV_MASK << PLL46XX_PDIV_SHIFT |
PLL46XX_SDIV_MASK << PLL46XX_SDIV_SHIFT);
- for (i = 0; i < ARRAY_SIZE(epll_div); i++) {
if (epll_div[i][0] == rate) {
epll_con_k |= epll_div[i][5] << 0;
epll_con |= (epll_div[i][1] << 27 |
epll_div[i][2] <<
PLL46XX_MDIV_SHIFT |
epll_div[i][3] <<
PLL46XX_PDIV_SHIFT |
epll_div[i][4] <<
PLL46XX_SDIV_SHIFT);
break;
}
- }
- if (i == ARRAY_SIZE(epll_div)) {
printk(KERN_ERR "%s: Invalid Clock EPLL Frequency\n",
__func__);
return -EINVAL;
- }
- __raw_writel(epll_con, S5P_EPLL_CON);
- __raw_writel(epll_con_k, S5P_EPLL_CON1);
- clk->rate = rate;
- return 0;
+}
+static struct clk_ops s5pv210_epll_ops = {
- .set_rate = s5pv210_epll_set_rate,
- .get_rate = s5p_epll_get_rate,
+};
void __init_or_cpufreq s5pv210_setup_clocks(void) { struct clk *xtal_clk; @@ -1022,6 +1095,10 @@ void __init_or_cpufreq s5pv210_setup_clocks(void) unsigned int ptr; u32 clkdiv0, clkdiv1;
/* Set functions for clk_fout_epll */
clk_fout_epll.enable = s5p_epll_enable;
clk_fout_epll.ops = &s5pv210_epll_ops;
printk(KERN_DEBUG "%s: registering clocks\n", __func__);
clkdiv0 = __raw_readl(S5P_CLK_DIV0);
--
I think...we need to sort out epll_set_rate() like epll_get_rate() later.
Will apply anyway.
Thanks.
Best regards, Kgene. -- Kukjin Kim kgene.kim@samsung.com, Senior Engineer, SW Solution Development Team, Samsung Electronics Co., Ltd.
This patch adds warning about changing EPLL rate to notice that other driver that controls H/W, which is using EPLL, will has unknown effects by this EPLL rate change.
Signed-off-by: Seungwhan Youn sw.youn@samsung.com --- arch/arm/mach-s5p64x0/clock-s5p6440.c | 3 +++ arch/arm/mach-s5p64x0/clock-s5p6450.c | 3 +++ arch/arm/mach-s5pc100/clock.c | 3 +++ arch/arm/mach-s5pv210/clock.c | 3 +++ 4 files changed, 12 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-s5p64x0/clock-s5p6440.c b/arch/arm/mach-s5p64x0/clock-s5p6440.c index 1e3f517..0b539d5 100644 --- a/arch/arm/mach-s5p64x0/clock-s5p6440.c +++ b/arch/arm/mach-s5p64x0/clock-s5p6440.c @@ -79,6 +79,9 @@ static int s5p6440_epll_set_rate(struct clk *clk, unsigned long rate) __raw_writel(epll_con, S5P_EPLL_CON); __raw_writel(epll_con_k, S5P_EPLL_CON_K);
+ printk(KERN_WARNING "EPLL Rate changes from %lu to %lu\n", + clk->rate, rate); + clk->rate = rate;
return 0; diff --git a/arch/arm/mach-s5p64x0/clock-s5p6450.c b/arch/arm/mach-s5p64x0/clock-s5p6450.c index ee03ec3..838eeff 100644 --- a/arch/arm/mach-s5p64x0/clock-s5p6450.c +++ b/arch/arm/mach-s5p64x0/clock-s5p6450.c @@ -80,6 +80,9 @@ static int s5p6450_epll_set_rate(struct clk *clk, unsigned long rate) __raw_writel(epll_con, S5P_EPLL_CON); __raw_writel(epll_con_k, S5P_EPLL_CON_K);
+ printk(KERN_WARNING "EPLL Rate changes from %lu to %lu\n", + clk->rate, rate); + clk->rate = rate;
return 0; diff --git a/arch/arm/mach-s5pc100/clock.c b/arch/arm/mach-s5pc100/clock.c index 42c2636..9e192a0 100644 --- a/arch/arm/mach-s5pc100/clock.c +++ b/arch/arm/mach-s5pc100/clock.c @@ -323,6 +323,9 @@ static int s5pc100_epll_set_rate(struct clk *clk, unsigned long rate)
__raw_writel(epll_con, S5P_EPLL_CON);
+ printk(KERN_WARNING "EPLL Rate changes from %lu to %lu\n", + clk->rate, rate); + clk->rate = rate;
return 0; diff --git a/arch/arm/mach-s5pv210/clock.c b/arch/arm/mach-s5pv210/clock.c index fdfadac..25eb839 100644 --- a/arch/arm/mach-s5pv210/clock.c +++ b/arch/arm/mach-s5pv210/clock.c @@ -1066,6 +1066,9 @@ static int s5pv210_epll_set_rate(struct clk *clk, unsigned long rate) __raw_writel(epll_con, S5P_EPLL_CON); __raw_writel(epll_con_k, S5P_EPLL_CON1);
+ printk(KERN_WARNING "EPLL Rate changes from %lu to %lu\n", + clk->rate, rate); + clk->rate = rate;
return 0;
Seungwhan Youn wrote:
This patch adds warning about changing EPLL rate to notice that other driver that controls H/W, which is using EPLL, will has unknown effects by this EPLL rate change.
Signed-off-by: Seungwhan Youn sw.youn@samsung.com
arch/arm/mach-s5p64x0/clock-s5p6440.c | 3 +++ arch/arm/mach-s5p64x0/clock-s5p6450.c | 3 +++ arch/arm/mach-s5pc100/clock.c | 3 +++ arch/arm/mach-s5pv210/clock.c | 3 +++ 4 files changed, 12 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-s5p64x0/clock-s5p6440.c b/arch/arm/mach- s5p64x0/clock-s5p6440.c index 1e3f517..0b539d5 100644 --- a/arch/arm/mach-s5p64x0/clock-s5p6440.c +++ b/arch/arm/mach-s5p64x0/clock-s5p6440.c @@ -79,6 +79,9 @@ static int s5p6440_epll_set_rate(struct clk *clk,
unsigned long
rate) __raw_writel(epll_con, S5P_EPLL_CON); __raw_writel(epll_con_k, S5P_EPLL_CON_K);
- printk(KERN_WARNING "EPLL Rate changes from %lu to %lu\n",
clk->rate, rate);
Do we really need KERN_WARNING here? How about just KERN_INFO?...In my opinion, to change EPLL value seems to be nearer 'information' message more than 'warning'.
(others, same)
Thanks.
Best regards, Kgene. -- Kukjin Kim kgene.kim@samsung.com, Senior Engineer, SW Solution Development Team, Samsung Electronics Co., Ltd.
On Wed, Oct 13, 2010 at 04:55:55PM +0900, Kukjin Kim wrote:
Seungwhan Youn wrote:
This patch adds warning about changing EPLL rate to notice that other driver that controls H/W, which is using EPLL, will has unknown effects by this EPLL rate change.
- printk(KERN_WARNING "EPLL Rate changes from %lu to %lu\n",
clk->rate, rate);
Do we really need KERN_WARNING here? How about just KERN_INFO?...In my opinion, to change EPLL value seems to be nearer 'information' message more than 'warning'.
Personally I don't think it really makes much odds so long as the message is displayed on the console by default.
Mark Brown wrote:
On Wed, Oct 13, 2010 at 04:55:55PM +0900, Kukjin Kim wrote:
Seungwhan Youn wrote:
This patch adds warning about changing EPLL rate to notice that other driver that controls H/W, which is using EPLL, will has unknown
effects
by this EPLL rate change.
- printk(KERN_WARNING "EPLL Rate changes from %lu to %lu\n",
clk->rate, rate);
Do we really need KERN_WARNING here? How about just KERN_INFO?...In my opinion, to change EPLL value seems to
be
nearer 'information' message more than 'warning'.
Personally I don't think it really makes much odds so long as the message is displayed on the console by default.
Hmm...yeah I mean just EPLL is used only in the audio and SPI now. However we don't know which driver will use it later. And as you said, need to show it. Ok...will apply.
Mr. Youn, no need to re-work this.
Thanks.
Best regards, Kgene. -- Kukjin Kim kgene.kim@samsung.com, Senior Engineer, SW Solution Development Team, Samsung Electronics Co., Ltd.
On Wed, Oct 13, 2010 at 05:21:48PM +0900, Kukjin Kim wrote:
Mark Brown wrote:
Personally I don't think it really makes much odds so long as the message is displayed on the console by default.
Hmm...yeah I mean just EPLL is used only in the audio and SPI now. However we don't know which driver will use it later. And as you said, need to show it. Ok...will apply.
FWIW the particular favourite that users run into with some of the CPUs is USB.
This patch adds S/PDIF CPU driver for various Samsung SoCs.
Signed-off-by: Seungwhan Youn sw.youn@samsung.com --- sound/soc/s3c24xx/Kconfig | 4 + sound/soc/s3c24xx/Makefile | 2 + sound/soc/s3c24xx/spdif.c | 501 ++++++++++++++++++++++++++++++++++++++++++++ sound/soc/s3c24xx/spdif.h | 19 ++ 4 files changed, 526 insertions(+), 0 deletions(-) create mode 100644 sound/soc/s3c24xx/spdif.c create mode 100644 sound/soc/s3c24xx/spdif.h
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig index 7d8235d..e2cba93 100644 --- a/sound/soc/s3c24xx/Kconfig +++ b/sound/soc/s3c24xx/Kconfig @@ -36,6 +36,10 @@ config SND_S3C_SOC_AC97 tristate select SND_SOC_AC97_BUS
+config SND_S5P_SOC_SPDIF + tristate + select SND_SOC_SPDIF + config SND_S3C24XX_SOC_NEO1973_WM8753 tristate "SoC I2S Audio support for NEO1973 - WM8753" depends on SND_S3C24XX_SOC && MACH_NEO1973_GTA01 diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile index dd412a9..0a02735 100644 --- a/sound/soc/s3c24xx/Makefile +++ b/sound/soc/s3c24xx/Makefile @@ -7,6 +7,7 @@ snd-soc-s3c-ac97-objs := s3c-ac97.o snd-soc-s3c64xx-i2s-v4-objs := s3c64xx-i2s-v4.o snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o snd-soc-s3c-pcm-objs := s3c-pcm.o +snd-soc-samsung-spdif-objs := spdif.o
obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o @@ -16,6 +17,7 @@ obj-$(CONFIG_SND_S3C64XX_SOC_I2S) += snd-soc-s3c64xx-i2s.o obj-$(CONFIG_SND_S3C64XX_SOC_I2S_V4) += snd-soc-s3c64xx-i2s-v4.o obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o obj-$(CONFIG_SND_S3C_SOC_PCM) += snd-soc-s3c-pcm.o +obj-$(CONFIG_SND_S5P_SOC_SPDIF) += snd-soc-samsung-spdif.o
# S3C24XX Machine Support snd-soc-jive-wm8750-objs := jive_wm8750.o diff --git a/sound/soc/s3c24xx/spdif.c b/sound/soc/s3c24xx/spdif.c new file mode 100644 index 0000000..ce554e9 --- /dev/null +++ b/sound/soc/s3c24xx/spdif.c @@ -0,0 +1,501 @@ +/* sound/soc/s3c24xx/spdif.c + * + * ALSA SoC Audio Layer - Samsung S/PDIF Controller driver + * + * Copyright (c) 2010 Samsung Electronics Co. Ltd + * http://www.samsung.com/ + * + * 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 <linux/clk.h> +#include <linux/io.h> + +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include <plat/audio.h> +#include <mach/dma.h> + +#include "s3c-dma.h" +#include "spdif.h" + +/* Registers */ +#define CLKCON 0x00 +#define CON 0x04 +#define BSTAS 0x08 +#define CSTAS 0x0C +#define DATA_OUTBUF 0x10 +#define DCNT 0x14 +#define BSTAS_S 0x18 +#define DCNT_S 0x1C + +#define CLKCTL_MASK 0x7 +#define CLKCTL_MCLK_EXT (0x1 << 2) +#define CLKCTL_PWR_ON (0x1 << 0) + +#define CON_MASK 0x3ffffff +#define CON_FIFO_TH_SHIFT 19 +#define CON_FIFO_TH_MASK (0x7 << 19) +#define CON_USERDATA_23RDBIT (0x1 << 12) + +#define CON_SW_RESET (0x1 << 5) + +#define CON_MCLKDIV_MASK (0x3 << 3) +#define CON_MCLKDIV_256FS (0x0 << 3) +#define CON_MCLKDIV_384FS (0x1 << 3) +#define CON_MCLKDIV_512FS (0x2 << 3) + +#define CON_PCM_MASK (0x3 << 1) +#define CON_PCM_16BIT (0x0 << 1) +#define CON_PCM_20BIT (0x1 << 1) +#define CON_PCM_24BIT (0x2 << 1) + +#define CON_PCM_DATA (0x1 << 0) + +#define CSTAS_MASK 0x3fffffff +#define CSTAS_SAMP_FREQ_MASK (0xF << 24) +#define CSTAS_SAMP_FREQ_44 (0x0 << 24) +#define CSTAS_SAMP_FREQ_48 (0x2 << 24) +#define CSTAS_SAMP_FREQ_32 (0x3 << 24) +#define CSTAS_SAMP_FREQ_96 (0xA << 24) + +#define CSTAS_CATEGORY_MASK (0xFF << 8) +#define CSTAS_CATEGORY_CODE_CDP (0x01 << 8) + +#define CSTAS_NO_COPYRIGHT (0x1 << 2) + +/** + * struct samsung_spdif_info - Samsung S/PDIF Controller information + * @lock: Spin lock for S/PDIF. + * @dev: The parent device passed to use from the probe. + * @regs: The pointer to the device register block. + * @clk_rate: Current clock rate for calcurate ratio. + * @pclk: The peri-clock pointer for spdif master operation. + * @sclk: The source clock pointer for making sync signals. + * @save_clkcon: Backup clkcon reg. in suspend. + * @save_con: Backup con reg. in suspend. + * @save_cstas: Backup cstas reg. in suspend. + * @dma_playback: DMA information for playback channel. + */ +struct samsung_spdif_info { + spinlock_t lock; + struct device *dev; + void __iomem *regs; + unsigned long clk_rate; + struct clk *pclk; + struct clk *sclk; + u32 saved_clkcon; + u32 saved_con; + u32 saved_cstas; + struct s3c_dma_params *dma_playback; +}; + +static struct s3c2410_dma_client spdif_dma_client_out = { + .name = "S/PDIF Stereo out", +}; + +static struct s3c_dma_params spdif_stereo_out; +static struct samsung_spdif_info spdif_info; + +static inline struct samsung_spdif_info *to_info(struct snd_soc_dai *cpu_dai) +{ + return snd_soc_dai_get_drvdata(cpu_dai); +} + +static void spdif_snd_txctrl(struct samsung_spdif_info *spdif, int on) +{ + void __iomem *regs = spdif->regs; + u32 clkcon; + + dev_dbg(spdif->dev, "Entered %s\n", __func__); + + clkcon = readl(regs + CLKCON) & CLKCTL_MASK; + if (on) + writel(clkcon | CLKCTL_PWR_ON, regs + CLKCON); + else + writel(clkcon & ~CLKCTL_PWR_ON, regs + CLKCON); +} + +static int spdif_set_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + struct samsung_spdif_info *spdif = to_info(cpu_dai); + u32 clkcon; + + dev_dbg(spdif->dev, "Entered %s\n", __func__); + + clkcon = readl(spdif->regs + CLKCON); + + if (clk_id == SND_SOC_SPDIF_INT_MCLK) + clkcon &= ~CLKCTL_MCLK_EXT; + else + clkcon |= CLKCTL_MCLK_EXT; + + writel(clkcon, spdif->regs + CLKCON); + + spdif->clk_rate = freq; + + return 0; +} + +static int spdif_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct samsung_spdif_info *spdif = to_info(rtd->cpu_dai); + unsigned long flags; + + dev_dbg(spdif->dev, "Entered %s\n", __func__); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + spin_lock_irqsave(&spdif->lock, flags); + spdif_snd_txctrl(spdif, 1); + spin_unlock_irqrestore(&spdif->lock, flags); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + spin_lock_irqsave(&spdif->lock, flags); + spdif_snd_txctrl(spdif, 0); + spin_unlock_irqrestore(&spdif->lock, flags); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int spdif_sysclk_ratios[] = { + 512, 384, 256, +}; + +static int spdif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *socdai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct samsung_spdif_info *spdif = to_info(rtd->cpu_dai); + void __iomem *regs = spdif->regs; + struct s3c_dma_params *dma_data; + u32 con, clkcon, cstas; + unsigned long flags; + int i, ratio; + + dev_dbg(spdif->dev, "Entered %s\n", __func__); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dma_data = spdif->dma_playback; + else { + dev_err(spdif->dev, "Capture is not supported\n"); + return -EINVAL; + } + + snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_data); + + spin_lock_irqsave(&spdif->lock, flags); + + con = readl(regs + CON) & CON_MASK; + cstas = readl(regs + CSTAS) & CSTAS_MASK; + clkcon = readl(regs + CLKCON) & CLKCTL_MASK; + + con &= ~CON_FIFO_TH_MASK; + con |= (0x7 << CON_FIFO_TH_SHIFT); + con |= CON_USERDATA_23RDBIT; + con |= CON_PCM_DATA; + + con &= ~CON_PCM_MASK; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + con |= CON_PCM_16BIT; + break; + default: + dev_err(spdif->dev, "Unsupported data size.\n"); + goto err; + } + + ratio = spdif->clk_rate / params_rate(params); + for (i = 0; i < ARRAY_SIZE(spdif_sysclk_ratios); i++) + if (ratio == spdif_sysclk_ratios[i]) + break; + if (i == ARRAY_SIZE(spdif_sysclk_ratios)) { + dev_err(spdif->dev, "Invalid clock ratio %ld/%d\n", + spdif->clk_rate, params_rate(params)); + goto err; + } + + con &= ~CON_MCLKDIV_MASK; + switch (ratio) { + case 256: + con |= CON_MCLKDIV_256FS; + break; + case 384: + con |= CON_MCLKDIV_384FS; + break; + case 512: + con |= CON_MCLKDIV_512FS; + break; + } + + cstas &= ~CSTAS_SAMP_FREQ_MASK; + switch (params_rate(params)) { + case 44100: + cstas |= CSTAS_SAMP_FREQ_44; + break; + case 48000: + cstas |= CSTAS_SAMP_FREQ_48; + break; + case 32000: + cstas |= CSTAS_SAMP_FREQ_32; + break; + case 96000: + cstas |= CSTAS_SAMP_FREQ_96; + break; + default: + dev_err(spdif->dev, "Invalid sampling rate %d\n", + params_rate(params)); + goto err; + } + + cstas &= ~CSTAS_CATEGORY_MASK; + cstas |= CSTAS_CATEGORY_CODE_CDP; + cstas |= CSTAS_NO_COPYRIGHT; + + writel(con, regs + CON); + writel(cstas, regs + CSTAS); + writel(clkcon, regs + CLKCON); + + spin_unlock_irqrestore(&spdif->lock, flags); + + return 0; +err: + spin_unlock_irqrestore(&spdif->lock, flags); + return -EINVAL; +} + +static void spdif_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct samsung_spdif_info *spdif = to_info(rtd->cpu_dai); + void __iomem *regs = spdif->regs; + u32 con, clkcon; + + dev_dbg(spdif->dev, "Entered %s\n", __func__); + + con = readl(regs + CON) & CON_MASK; + clkcon = readl(regs + CLKCON) & CLKCTL_MASK; + + writel(con | CON_SW_RESET, regs + CON); + cpu_relax(); + + writel(clkcon & ~CLKCTL_PWR_ON, regs + CLKCON); +} + +#ifdef CONFIG_PM +static int spdif_suspend(struct snd_soc_dai *cpu_dai) +{ + struct samsung_spdif_info *spdif = to_info(cpu_dai); + u32 con = spdif->saved_con; + + dev_dbg(spdif->dev, "Entered %s\n", __func__); + + spdif->saved_clkcon = readl(spdif->regs + CLKCON) & CLKCTL_MASK; + spdif->saved_con = readl(spdif->regs + CON) & CON_MASK; + spdif->saved_cstas = readl(spdif->regs + CSTAS) & CSTAS_MASK; + + writel(con | CON_SW_RESET, spdif->regs + CON); + cpu_relax(); + + return 0; +} + +static int spdif_resume(struct snd_soc_dai *cpu_dai) +{ + struct samsung_spdif_info *spdif = to_info(cpu_dai); + + dev_dbg(spdif->dev, "Entered %s\n", __func__); + + writel(spdif->saved_clkcon, spdif->regs + CLKCON); + writel(spdif->saved_con, spdif->regs + CON); + writel(spdif->saved_cstas, spdif->regs + CSTAS); + + return 0; +} +#else +#define spdif_suspend NULL +#define spdif_resume NULL +#endif + +static struct snd_soc_dai_ops spdif_dai_ops = { + .set_sysclk = spdif_set_sysclk, + .trigger = spdif_trigger, + .hw_params = spdif_hw_params, + .shutdown = spdif_shutdown, +}; + +struct snd_soc_dai_driver samsung_spdif_dai = { + .name = "samsung-spdif", + .playback = { + .stream_name = "S/PDIF Playback", + .channels_min = 2, + .channels_max = 2, + .rates = (SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, }, + .ops = &spdif_dai_ops, + .suspend = spdif_suspend, + .resume = spdif_resume, +}; + +static __devinit int spdif_probe(struct platform_device *pdev) +{ + struct s3c_audio_pdata *spdif_pdata; + struct resource *mem_res, *dma_res; + struct samsung_spdif_info *spdif; + int ret; + + spdif_pdata = pdev->dev.platform_data; + + dev_dbg(&pdev->dev, "Entered %s\n", __func__); + + dma_res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!dma_res) { + dev_err(&pdev->dev, "Unable to get dma resource.\n"); + return -ENXIO; + } + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem_res) { + dev_err(&pdev->dev, "Unable to get register resource.\n"); + return -ENXIO; + } + + if (spdif_pdata && spdif_pdata->cfg_gpio + && spdif_pdata->cfg_gpio(pdev)) { + dev_err(&pdev->dev, "Unable to configure GPIO pins\n"); + return -EINVAL; + } + + spdif = &spdif_info; + spdif->dev = &pdev->dev; + + spin_lock_init(&spdif->lock); + + spdif->pclk = clk_get(&pdev->dev, "spdif"); + if (IS_ERR(spdif->pclk)) { + dev_err(&pdev->dev, "failed to get peri-clock\n"); + ret = -ENOENT; + goto err0; + } + clk_enable(spdif->pclk); + + spdif->sclk = clk_get(&pdev->dev, "sclk_spdif"); + if (IS_ERR(spdif->sclk)) { + dev_err(&pdev->dev, "failed to get internal source clock\n"); + ret = -ENOENT; + goto err1; + } + clk_enable(spdif->sclk); + + /* Request S/PDIF Register's memory region */ + if (!request_mem_region(mem_res->start, + resource_size(mem_res), "samsung-spdif")) { + dev_err(&pdev->dev, "Unable to request register region\n"); + ret = -EBUSY; + goto err2; + } + + spdif->regs = ioremap(mem_res->start, 0x100); + if (spdif->regs == NULL) { + dev_err(&pdev->dev, "Cannot ioremap registers\n"); + ret = -ENXIO; + goto err3; + } + + dev_set_drvdata(&pdev->dev, spdif); + + ret = snd_soc_register_dai(&pdev->dev, &samsung_spdif_dai); + if (ret != 0) { + dev_err(&pdev->dev, "fail to register dai\n"); + goto err4; + } + + spdif_stereo_out.dma_size = 2; + spdif_stereo_out.client = &spdif_dma_client_out; + spdif_stereo_out.dma_addr = mem_res->start + DATA_OUTBUF; + spdif_stereo_out.channel = dma_res->start; + + spdif->dma_playback = &spdif_stereo_out; + + return 0; + +err4: + iounmap(spdif->regs); +err3: + release_mem_region(mem_res->start, resource_size(mem_res)); +err2: + clk_disable(spdif->sclk); + clk_put(spdif->sclk); +err1: + clk_disable(spdif->pclk); + clk_put(spdif->pclk); +err0: + return ret; +} + +static __devexit int spdif_remove(struct platform_device *pdev) +{ + struct samsung_spdif_info *spdif = &spdif_info; + struct resource *mem_res; + + snd_soc_unregister_dai(&pdev->dev); + + iounmap(spdif->regs); + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (mem_res) + release_mem_region(mem_res->start, resource_size(mem_res)); + + clk_disable(spdif->sclk); + clk_put(spdif->sclk); + clk_disable(spdif->pclk); + clk_put(spdif->pclk); + + return 0; +} + +static struct platform_driver samsung_spdif_driver = { + .probe = spdif_probe, + .remove = spdif_remove, + .driver = { + .name = "samsung-spdif", + .owner = THIS_MODULE, + }, +}; + +static int __init spdif_init(void) +{ + return platform_driver_register(&samsung_spdif_driver); +} +module_init(spdif_init); + +static void __exit spdif_exit(void) +{ + platform_driver_unregister(&samsung_spdif_driver); +} +module_exit(spdif_exit); + +MODULE_AUTHOR("Seungwhan Youn, sw.youn@samsung.com"); +MODULE_DESCRIPTION("Samsung S/PDIF Controller Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:samsung-spdif"); diff --git a/sound/soc/s3c24xx/spdif.h b/sound/soc/s3c24xx/spdif.h new file mode 100644 index 0000000..3ed5559 --- /dev/null +++ b/sound/soc/s3c24xx/spdif.h @@ -0,0 +1,19 @@ +/* sound/soc/s3c24xx/spdif.h + * + * ALSA SoC Audio Layer - Samsung S/PDIF Controller driver + * + * Copyright (c) 2010 Samsung Electronics Co. Ltd + * http://www.samsung.com/ + * + * 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. + */ + +#ifndef __SND_SOC_SAMSUNG_SPDIF_H +#define __SND_SOC_SAMSUNG_SPDIF_H __FILE__ + +#define SND_SOC_SPDIF_INT_MCLK 0 +#define SND_SOC_SPDIF_EXT_MCLK 1 + +#endif /* __SND_SOC_SAMSUNG_SPDIF_H */
On Tue, Oct 12, 2010 at 08:51:23PM +0900, Seungwhan Youn wrote:
This patch adds S/PDIF CPU driver for various Samsung SoCs.
Signed-off-by: Seungwhan Youn sw.youn@samsung.com
Applied this and the machine driver, thanks!
On Fri, Oct 15, 2010 at 7:05 PM, Mark Brown broonie@opensource.wolfsonmicro.com wrote:
On Tue, Oct 12, 2010 at 08:51:23PM +0900, Seungwhan Youn wrote:
This patch adds S/PDIF CPU driver for various Samsung SoCs.
Signed-off-by: Seungwhan Youn sw.youn@samsung.com
Applied this and the machine driver, thanks!
Great... probably monday late I'll submit patches upgrading these SPDIF and other Samsung ASoC support. Hope to have tidy Samsung ASoC in 37.
This patch add S/PDIF machine driver to support S/PDIF PCM audio on SMDKC100, SMDKC110 and SMDKV210 boards.
Signed-off-by: Seungwhan Youn sw.youn@samsung.com --- sound/soc/s3c24xx/Kconfig | 7 ++ sound/soc/s3c24xx/Makefile | 2 + sound/soc/s3c24xx/smdk_spdif.c | 223 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 232 insertions(+), 0 deletions(-) create mode 100644 sound/soc/s3c24xx/smdk_spdif.c
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig index e2cba93..8a6b53c 100644 --- a/sound/soc/s3c24xx/Kconfig +++ b/sound/soc/s3c24xx/Kconfig @@ -161,3 +161,10 @@ config SND_S5PV210_SOC_GONI_WM8994 help Say Y if you want to add support for SoC audio on goni with the WM8994. + +config SND_SOC_SMDK_SPDIF + tristate "SoC S/PDIF Audio support for SMDK" + depends on SND_S3C24XX_SOC && (MACH_SMDKC100 || MACH_SMDKC110 || MACH_SMDKV210) + select SND_S5P_SOC_SPDIF + help + Say Y if you want to add support for SoC S/PDIF audio on the SMDK. diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile index 0a02735..ee8f41d 100644 --- a/sound/soc/s3c24xx/Makefile +++ b/sound/soc/s3c24xx/Makefile @@ -35,6 +35,7 @@ snd-soc-smdk-wm9713-objs := smdk_wm9713.o snd-soc-s3c64xx-smartq-wm8987-objs := smartq_wm8987.o snd-soc-aquila-wm8994-objs := aquila_wm8994.o snd-soc-goni-wm8994-objs := goni_wm8994.o +snd-soc-smdk-spdif-objs := smdk_spdif.o
obj-$(CONFIG_SND_S3C24XX_SOC_JIVE_WM8750) += snd-soc-jive-wm8750.o obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o @@ -51,3 +52,4 @@ obj-$(CONFIG_SND_SOC_SMDK_WM9713) += snd-soc-smdk-wm9713.o obj-$(CONFIG_SND_S3C64XX_SOC_SMARTQ) += snd-soc-s3c64xx-smartq-wm8987.o obj-$(CONFIG_SND_S5PC110_SOC_AQUILA_WM8994) += snd-soc-aquila-wm8994.o obj-$(CONFIG_SND_S5PV210_SOC_GONI_WM8994) += snd-soc-goni-wm8994.o +obj-$(CONFIG_SND_SOC_SMDK_SPDIF) += snd-soc-smdk-spdif.o diff --git a/sound/soc/s3c24xx/smdk_spdif.c b/sound/soc/s3c24xx/smdk_spdif.c new file mode 100644 index 0000000..5daf32f --- /dev/null +++ b/sound/soc/s3c24xx/smdk_spdif.c @@ -0,0 +1,223 @@ +/* + * smdk_spdif.c -- S/PDIF audio for SMDK + * + * Copyright 2010 Samsung Electronics Co. Ltd. + * + * 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. + * + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/clk.h> + +#include <plat/devs.h> + +#include <sound/soc.h> + +#include "s3c-dma.h" +#include "spdif.h" + +/* Audio clock settings are belonged to board specific part. Every + * board can set audio source clock setting which is matched with H/W + * like this function-'set_audio_clock_heirachy'. + */ +static int set_audio_clock_heirachy(struct platform_device *pdev) +{ + struct clk *fout_epll, *mout_epll, *sclk_audio0, *sclk_spdif; + int ret; + + fout_epll = clk_get(NULL, "fout_epll"); + if (IS_ERR(fout_epll)) { + printk(KERN_WARNING "%s: Cannot find fout_epll.\n", + __func__); + return -EINVAL; + } + + mout_epll = clk_get(NULL, "mout_epll"); + if (IS_ERR(fout_epll)) { + printk(KERN_WARNING "%s: Cannot find mout_epll.\n", + __func__); + ret = -EINVAL; + goto out1; + } + + sclk_audio0 = clk_get(&pdev->dev, "sclk_audio"); + if (IS_ERR(sclk_audio0)) { + printk(KERN_WARNING "%s: Cannot find sclk_audio.\n", + __func__); + ret = -EINVAL; + goto out2; + } + + sclk_spdif = clk_get(NULL, "sclk_spdif"); + if (IS_ERR(fout_epll)) { + printk(KERN_WARNING "%s: Cannot find sclk_spdif.\n", + __func__); + ret = -EINVAL; + goto out3; + } + + /* Set audio clock heirachy for S/PDIF */ + clk_set_parent(mout_epll, fout_epll); + clk_set_parent(sclk_audio0, mout_epll); + clk_set_parent(sclk_spdif, sclk_audio0); + + clk_put(sclk_spdif); +out3: + clk_put(sclk_audio0); +out2: + clk_put(mout_epll); +out1: + clk_put(fout_epll); + + return ret; +} + +/* We should haved to set clock directly on this part because of clock + * scheme of Samsudng SoCs did not support to set rates from abstrct + * clock of it's heirachy. + */ +static int set_audio_clock_rate(unsigned long epll_rate, + unsigned long audio_rate) +{ + struct clk *fout_epll, *sclk_spdif; + + fout_epll = clk_get(NULL, "fout_epll"); + if (IS_ERR(fout_epll)) { + printk(KERN_ERR "%s: failed to get fout_epll\n", __func__); + return -ENOENT; + } + + clk_set_rate(fout_epll, epll_rate); + clk_put(fout_epll); + + sclk_spdif = clk_get(NULL, "sclk_spdif"); + if (IS_ERR(sclk_spdif)) { + printk(KERN_ERR "%s: failed to get sclk_spdif\n", __func__); + return -ENOENT; + } + + clk_set_rate(sclk_spdif, audio_rate); + clk_put(sclk_spdif); + + return 0; +} + +static int smdk_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + unsigned long pll_out, rclk_rate; + int ret, ratio; + + switch (params_rate(params)) { + case 44100: + pll_out = 45158400; + break; + case 32000: + case 48000: + case 96000: + pll_out = 49152000; + break; + default: + return -EINVAL; + } + + /* Setting ratio to 512fs helps to use S/PDIF with HDMI without + * modify S/PDIF ASoC machine driver. + */ + ratio = 512; + rclk_rate = params_rate(params) * ratio; + + /* Set audio source clock rates */ + ret = set_audio_clock_rate(pll_out, rclk_rate); + if (ret < 0) + return ret; + + /* Set S/PDIF uses internal source clock */ + ret = snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK, + rclk_rate, SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + return ret; +} + +static struct snd_soc_ops smdk_spdif_ops = { + .hw_params = smdk_hw_params, +}; + +static struct snd_soc_card smdk; + +static struct snd_soc_dai_link smdk_dai = { + .name = "S/PDIF", + .stream_name = "S/PDIF PCM Playback", + .platform_name = "s3c24xx-pcm-audio", + .cpu_dai_name = "samsung-spdif", + .codec_dai_name = "dit-hifi", + .codec_name = "spdif-dit", + .ops = &smdk_spdif_ops, +}; + +static struct snd_soc_card smdk = { + .name = "SMDK-S/PDIF", + .dai_link = &smdk_dai, + .num_links = 1, +}; + +static struct platform_device *smdk_snd_spdif_dit_device; +static struct platform_device *smdk_snd_spdif_device; + +static int __init smdk_init(void) +{ + int ret; + + smdk_snd_spdif_dit_device = platform_device_alloc("spdif-dit", -1); + if (!smdk_snd_spdif_dit_device) + return -ENOMEM; + + ret = platform_device_add(smdk_snd_spdif_dit_device); + if (ret) + goto err2; + + smdk_snd_spdif_device = platform_device_alloc("soc-audio", -1); + if (!smdk_snd_spdif_device) { + ret = -ENOMEM; + goto err2; + } + + platform_set_drvdata(smdk_snd_spdif_device, &smdk); + + ret = platform_device_add(smdk_snd_spdif_device); + if (ret) + goto err1; + + /* Set audio clock heirachy manually */ + ret = set_audio_clock_heirachy(smdk_snd_spdif_device); + if (ret) + goto err1; + + return 0; +err1: + platform_device_put(smdk_snd_spdif_device); +err2: + platform_device_put(smdk_snd_spdif_dit_device); + return ret; +} + +static void __exit smdk_exit(void) +{ + platform_device_unregister(smdk_snd_spdif_device); +} + +module_init(smdk_init); +module_exit(smdk_exit); + +MODULE_AUTHOR("Seungwhan Youn, sw.youn@samsung.com"); +MODULE_DESCRIPTION("ALSA SoC SMDK+S/PDIF"); +MODULE_LICENSE("GPL");
On Tue, Oct 12, 2010 at 8:19 PM, Seungwhan Youn sw.youn@samsung.com wrote:
Hi,
This patch-set is a 2nd version for new S/PDIF common driver that supports S/PDIF PCM audio on S5PC100, S5PC110 and S5PV210.
This patch-set is based on two different branches that :- o ARM patches are based on Kukjin Kim's git branch 'for-next' (commit id - 64dcc6aef86593e478cf297a8508e55b54cceaf8) o ASoC patches are based on Mark Brown's git branch 'for-next' (commit id - fe3e2e7ff2da41bd7a985c4c206e05a95ebe7a6b)
This patch-set updates followings from the 1st submit :- o Add EPLL enable and get_rate function as common EPLL function o Add EPLL rate change warning o Fix S/PDIF's audio clock divider set dynamically at CPU driver o Move S/PDIF specific register macros and structure into CPU driver
This patch-set contains followings :- o To Kukjin Kim and Ben Dooks, - [PATCH v2 1/6] ARM: S5P: Reduce duplicated EPLL control codes - [PATCH v2 2/6] ARM: S5PV210: Fix wrong EPLL rate getting on setup clocks - [PATCH v2 3/6] ARM: S5PV210: Add EPLL clock operations - [PATCH v2 4/6] ARM: S5P: Add EPLL rate change warning o To Jassi Brar, Mark Brown and Liam Girdwood, - [PATCH v2 5/6] ASoC: SAMSUNG: Add S/PDIF CPU driver - [PATCH v2 6/6] ASoC: SAMSUNG: Add Machine driver for S/PDIF PCM audio
Though we need to also provide iecset control support, maybe later as incremental patches.
All Acked-by: Jassi Brar jassi.brar@samsung.com
participants (5)
-
Jassi Brar
-
Kukjin Kim
-
Mark Brown
-
Seungwhan Youn
-
Seungwhan Youn