diff options
104 files changed, 7884 insertions, 446 deletions
diff --git a/arch/arm/mach-davinci/include/mach/asp.h b/arch/arm/mach-davinci/include/mach/asp.h index 834725f1e81..b12c69edeea 100644 --- a/arch/arm/mach-davinci/include/mach/asp.h +++ b/arch/arm/mach-davinci/include/mach/asp.h @@ -63,6 +63,49 @@ struct snd_platform_data { unsigned sram_size_playback; unsigned sram_size_capture; + /* + * If McBSP peripheral gets the clock from an external pin, + * there are three chooses, that are MCBSP_CLKX, MCBSP_CLKR + * and MCBSP_CLKS. + * Depending on different hardware connections it is possible + * to use this setting to change the behaviour of McBSP + * driver. The dm365_clk_input_pin enum is available for dm365 + */ + int clk_input_pin; + + /* + * This flag works when both clock and FS are outputs for the cpu + * and makes clock more accurate (FS is not symmetrical and the + * clock is very fast. + * The clock becoming faster is named + * i2s continuous serial clock (I2S_SCK) and it is an externally + * visible bit clock. + * + * first line : WordSelect + * second line : ContinuousSerialClock + * third line: SerialData + * + * SYMMETRICAL APPROACH: + * _______________________ LEFT + * _| RIGHT |______________________| + * _ _ _ _ _ _ _ _ + * _| |_| |_ x16 _| |_| |_| |_| |_ x16 _| |_| |_ + * _ _ _ _ _ _ _ _ + * _/ \_/ \_ ... _/ \_/ \_/ \_/ \_ ... _/ \_/ \_ + * \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ + * + * ACCURATE CLOCK APPROACH: + * ______________ LEFT + * _| RIGHT |_______________________________| + * _ _ _ _ _ _ _ _ _ + * _| |_ x16 _| |_| |_ x16 _| |_| |_| |_| |_| |_| | + * _ _ _ _ dummy cycles + * _/ \_ ... _/ \_/ \_ ... _/ \__________________ + * \_/ \_/ \_/ \_/ + * + */ + bool i2s_accurate_sck; + /* McASP specific fields */ int tdm_slots; u8 op_mode; @@ -78,6 +121,11 @@ enum { MCASP_VERSION_2, /* DA8xx/OMAPL1x */ }; +enum dm365_clk_input_pin { + MCBSP_CLKR = 0, /* DM365 */ + MCBSP_CLKS, +}; + #define INACTIVE_MODE 0 #define TX_MODE 1 #define RX_MODE 2 diff --git a/arch/arm/mach-ep93xx/clock.c b/arch/arm/mach-ep93xx/clock.c index e29bdef9b2e..803162492c4 100644 --- a/arch/arm/mach-ep93xx/clock.c +++ b/arch/arm/mach-ep93xx/clock.c @@ -43,7 +43,8 @@ static unsigned long get_uart_rate(struct clk *clk); static int set_keytchclk_rate(struct clk *clk, unsigned long rate); static int set_div_rate(struct clk *clk, unsigned long rate); - +static int set_i2s_sclk_rate(struct clk *clk, unsigned long rate); +static int set_i2s_lrclk_rate(struct clk *clk, unsigned long rate); static struct clk clk_xtali = { .rate = EP93XX_EXT_CLK_RATE, @@ -112,6 +113,29 @@ static struct clk clk_video = { .set_rate = set_div_rate, }; +static struct clk clk_i2s_mclk = { + .sw_locked = 1, + .enable_reg = EP93XX_SYSCON_I2SCLKDIV, + .enable_mask = EP93XX_SYSCON_CLKDIV_ENABLE, + .set_rate = set_div_rate, +}; + +static struct clk clk_i2s_sclk = { + .sw_locked = 1, + .parent = &clk_i2s_mclk, + .enable_reg = EP93XX_SYSCON_I2SCLKDIV, + .enable_mask = EP93XX_SYSCON_I2SCLKDIV_SENA, + .set_rate = set_i2s_sclk_rate, +}; + +static struct clk clk_i2s_lrclk = { + .sw_locked = 1, + .parent = &clk_i2s_sclk, + .enable_reg = EP93XX_SYSCON_I2SCLKDIV, + .enable_mask = EP93XX_SYSCON_I2SCLKDIV_SENA, + .set_rate = set_i2s_lrclk_rate, +}; + /* DMA Clocks */ static struct clk clk_m2p0 = { .parent = &clk_h, @@ -191,6 +215,9 @@ static struct clk_lookup clocks[] = { INIT_CK("ep93xx-keypad", NULL, &clk_keypad), INIT_CK("ep93xx-fb", NULL, &clk_video), INIT_CK("ep93xx-spi.0", NULL, &clk_spi), + INIT_CK("ep93xx-i2s", "mclk", &clk_i2s_mclk), + INIT_CK("ep93xx-i2s", "sclk", &clk_i2s_sclk), + INIT_CK("ep93xx-i2s", "lrclk", &clk_i2s_lrclk), INIT_CK(NULL, "pwm_clk", &clk_pwm), INIT_CK(NULL, "m2p0", &clk_m2p0), INIT_CK(NULL, "m2p1", &clk_m2p1), @@ -401,6 +428,44 @@ static int set_div_rate(struct clk *clk, unsigned long rate) return 0; } +static int set_i2s_sclk_rate(struct clk *clk, unsigned long rate) +{ + unsigned val = __raw_readl(clk->enable_reg); + + if (rate == clk_i2s_mclk.rate / 2) + ep93xx_syscon_swlocked_write(val & ~EP93XX_I2SCLKDIV_SDIV, + clk->enable_reg); + else if (rate == clk_i2s_mclk.rate / 4) + ep93xx_syscon_swlocked_write(val | EP93XX_I2SCLKDIV_SDIV, + clk->enable_reg); + else + return -EINVAL; + + clk_i2s_sclk.rate = rate; + return 0; +} + +static int set_i2s_lrclk_rate(struct clk *clk, unsigned long rate) +{ + unsigned val = __raw_readl(clk->enable_reg) & + ~EP93XX_I2SCLKDIV_LRDIV_MASK; + + if (rate == clk_i2s_sclk.rate / 32) + ep93xx_syscon_swlocked_write(val | EP93XX_I2SCLKDIV_LRDIV32, + clk->enable_reg); + else if (rate == clk_i2s_sclk.rate / 64) + ep93xx_syscon_swlocked_write(val | EP93XX_I2SCLKDIV_LRDIV64, + clk->enable_reg); + else if (rate == clk_i2s_sclk.rate / 128) + ep93xx_syscon_swlocked_write(val | EP93XX_I2SCLKDIV_LRDIV128, + clk->enable_reg); + else + return -EINVAL; + + clk_i2s_lrclk.rate = rate; + return 0; +} + int clk_set_rate(struct clk *clk, unsigned long rate) { if (clk->set_rate) diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c index 9092677f63e..b4ee5409eb7 100644 --- a/arch/arm/mach-ep93xx/core.c +++ b/arch/arm/mach-ep93xx/core.c @@ -714,6 +714,73 @@ void ep93xx_keypad_release_gpio(struct platform_device *pdev) } EXPORT_SYMBOL(ep93xx_keypad_release_gpio); +/************************************************************************* + * EP93xx I2S audio peripheral handling + *************************************************************************/ +static struct resource ep93xx_i2s_resource[] = { + { + .start = EP93XX_I2S_PHYS_BASE, + .end = EP93XX_I2S_PHYS_BASE + 0x100 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device ep93xx_i2s_device = { + .name = "ep93xx-i2s", + .id = -1, + .num_resources = ARRAY_SIZE(ep93xx_i2s_resource), + .resource = ep93xx_i2s_resource, +}; + +void __init ep93xx_register_i2s(void) +{ + platform_device_register(&ep93xx_i2s_device); +} + +#define EP93XX_SYSCON_DEVCFG_I2S_MASK (EP93XX_SYSCON_DEVCFG_I2SONSSP | \ + EP93XX_SYSCON_DEVCFG_I2SONAC97) + +#define EP93XX_I2SCLKDIV_MASK (EP93XX_SYSCON_I2SCLKDIV_ORIDE | \ + EP93XX_SYSCON_I2SCLKDIV_SPOL) + +int ep93xx_i2s_acquire(unsigned i2s_pins, unsigned i2s_config) +{ + unsigned val; + + /* Sanity check */ + if (i2s_pins & ~EP93XX_SYSCON_DEVCFG_I2S_MASK) + return -EINVAL; + if (i2s_config & ~EP93XX_I2SCLKDIV_MASK) + return -EINVAL; + + /* Must have only one of I2SONSSP/I2SONAC97 set */ + if ((i2s_pins & EP93XX_SYSCON_DEVCFG_I2SONSSP) == + (i2s_pins & EP93XX_SYSCON_DEVCFG_I2SONAC97)) + return -EINVAL; + + ep93xx_devcfg_clear_bits(EP93XX_SYSCON_DEVCFG_I2S_MASK); + ep93xx_devcfg_set_bits(i2s_pins); + + /* + * This is potentially racy with the clock api for i2s_mclk, sclk and + * lrclk. Since the i2s driver is the only user of those clocks we + * rely on it to prevent parallel use of this function and the + * clock api for the i2s clocks. + */ + val = __raw_readl(EP93XX_SYSCON_I2SCLKDIV); + val &= ~EP93XX_I2SCLKDIV_MASK; + val |= i2s_config; < |