diff options
| author | Takashi Iwai <tiwai@suse.de> | 2010-08-05 05:17:01 -0400 |
|---|---|---|
| committer | Takashi Iwai <tiwai@suse.de> | 2010-08-05 05:17:01 -0400 |
| commit | e71981343ad29b5d929f82ac56c0b27b8ea0e540 (patch) | |
| tree | 47135be4252faecbc0e5508658a58f8afd197fff | |
| parent | 2603798070a80d76e7e6d2992ba4ec74addcec90 (diff) | |
| parent | bda7d2a862e6b788bca2d02d38a07966a9c92e48 (diff) | |
Merge branch 'topic/asoc' into for-linus
127 files changed, 9110 insertions, 659 deletions
diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c index 212d97084bd7..bc384d3561da 100644 --- a/arch/arm/mach-davinci/board-da830-evm.c +++ b/arch/arm/mach-davinci/board-da830-evm.c | |||
| @@ -208,7 +208,7 @@ static struct snd_platform_data da830_evm_snd_data = { | |||
| 208 | .num_serializer = ARRAY_SIZE(da830_iis_serializer_direction), | 208 | .num_serializer = ARRAY_SIZE(da830_iis_serializer_direction), |
| 209 | .tdm_slots = 2, | 209 | .tdm_slots = 2, |
| 210 | .serial_dir = da830_iis_serializer_direction, | 210 | .serial_dir = da830_iis_serializer_direction, |
| 211 | .eventq_no = EVENTQ_0, | 211 | .asp_chan_q = EVENTQ_0, |
| 212 | .version = MCASP_VERSION_2, | 212 | .version = MCASP_VERSION_2, |
| 213 | .txnumevt = 1, | 213 | .txnumevt = 1, |
| 214 | .rxnumevt = 1, | 214 | .rxnumevt = 1, |
diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index b280efb1fa12..e8c819090268 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c | |||
| @@ -343,7 +343,7 @@ static struct snd_platform_data da850_evm_snd_data = { | |||
| 343 | .num_serializer = ARRAY_SIZE(da850_iis_serializer_direction), | 343 | .num_serializer = ARRAY_SIZE(da850_iis_serializer_direction), |
| 344 | .tdm_slots = 2, | 344 | .tdm_slots = 2, |
| 345 | .serial_dir = da850_iis_serializer_direction, | 345 | .serial_dir = da850_iis_serializer_direction, |
| 346 | .eventq_no = EVENTQ_1, | 346 | .asp_chan_q = EVENTQ_1, |
| 347 | .version = MCASP_VERSION_2, | 347 | .version = MCASP_VERSION_2, |
| 348 | .txnumevt = 1, | 348 | .txnumevt = 1, |
| 349 | .rxnumevt = 1, | 349 | .rxnumevt = 1, |
diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c index 6d8889342c9f..87521f2d69c7 100644 --- a/arch/arm/mach-davinci/board-dm646x-evm.c +++ b/arch/arm/mach-davinci/board-dm646x-evm.c | |||
| @@ -323,7 +323,7 @@ static struct snd_platform_data dm646x_evm_snd_data[] = { | |||
| 323 | .num_serializer = ARRAY_SIZE(dm646x_iis_serializer_direction), | 323 | .num_serializer = ARRAY_SIZE(dm646x_iis_serializer_direction), |
| 324 | .tdm_slots = 2, | 324 | .tdm_slots = 2, |
| 325 | .serial_dir = dm646x_iis_serializer_direction, | 325 | .serial_dir = dm646x_iis_serializer_direction, |
| 326 | .eventq_no = EVENTQ_0, | 326 | .asp_chan_q = EVENTQ_0, |
| 327 | }, | 327 | }, |
| 328 | { | 328 | { |
| 329 | .tx_dma_offset = 0x400, | 329 | .tx_dma_offset = 0x400, |
| @@ -332,7 +332,7 @@ static struct snd_platform_data dm646x_evm_snd_data[] = { | |||
| 332 | .num_serializer = ARRAY_SIZE(dm646x_dit_serializer_direction), | 332 | .num_serializer = ARRAY_SIZE(dm646x_dit_serializer_direction), |
| 333 | .tdm_slots = 32, | 333 | .tdm_slots = 32, |
| 334 | .serial_dir = dm646x_dit_serializer_direction, | 334 | .serial_dir = dm646x_dit_serializer_direction, |
| 335 | .eventq_no = EVENTQ_0, | 335 | .asp_chan_q = EVENTQ_0, |
| 336 | }, | 336 | }, |
| 337 | }; | 337 | }; |
| 338 | 338 | ||
diff --git a/arch/arm/mach-davinci/include/mach/asp.h b/arch/arm/mach-davinci/include/mach/asp.h index 834725f1e81d..9aa240909a2c 100644 --- a/arch/arm/mach-davinci/include/mach/asp.h +++ b/arch/arm/mach-davinci/include/mach/asp.h | |||
| @@ -52,7 +52,8 @@ | |||
| 52 | struct snd_platform_data { | 52 | struct snd_platform_data { |
| 53 | u32 tx_dma_offset; | 53 | u32 tx_dma_offset; |
| 54 | u32 rx_dma_offset; | 54 | u32 rx_dma_offset; |
| 55 | enum dma_event_q eventq_no; /* event queue number */ | 55 | enum dma_event_q asp_chan_q; /* event queue number for ASP channel */ |
| 56 | enum dma_event_q ram_chan_q; /* event queue number for RAM channel */ | ||
| 56 | unsigned int codec_fmt; | 57 | unsigned int codec_fmt; |
| 57 | /* | 58 | /* |
| 58 | * Allowing this is more efficient and eliminates left and right swaps | 59 | * Allowing this is more efficient and eliminates left and right swaps |
| @@ -63,6 +64,49 @@ struct snd_platform_data { | |||
| 63 | unsigned sram_size_playback; | 64 | unsigned sram_size_playback; |
| 64 | unsigned sram_size_capture; | 65 | unsigned sram_size_capture; |
| 65 | 66 | ||
| 67 | /* | ||
| 68 | * If McBSP peripheral gets the clock from an external pin, | ||
| 69 | * there are three chooses, that are MCBSP_CLKX, MCBSP_CLKR | ||
| 70 | * and MCBSP_CLKS. | ||
| 71 | * Depending on different hardware connections it is possible | ||
| 72 | * to use this setting to change the behaviour of McBSP | ||
| 73 | * driver. The dm365_clk_input_pin enum is available for dm365 | ||
| 74 | */ | ||
| 75 | int clk_input_pin; | ||
| 76 | |||
| 77 | /* | ||
| 78 | * This flag works when both clock and FS are outputs for the cpu | ||
| 79 | * and makes clock more accurate (FS is not symmetrical and the | ||
| 80 | * clock is very fast. | ||
| 81 | * The clock becoming faster is named | ||
| 82 | * i2s continuous serial clock (I2S_SCK) and it is an externally | ||
| 83 | * visible bit clock. | ||
| 84 | * | ||
| 85 | * first line : WordSelect | ||
| 86 | * second line : ContinuousSerialClock | ||
| 87 | * third line: SerialData | ||
| 88 | * | ||
| 89 | * SYMMETRICAL APPROACH: | ||
| 90 | * _______________________ LEFT | ||
| 91 | * _| RIGHT |______________________| | ||
| 92 | * _ _ _ _ _ _ _ _ | ||
| 93 | * _| |_| |_ x16 _| |_| |_| |_| |_ x16 _| |_| |_ | ||
| 94 | * _ _ _ _ _ _ _ _ | ||
| 95 | * _/ \_/ \_ ... _/ \_/ \_/ \_/ \_ ... _/ \_/ \_ | ||
| 96 | * \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ | ||
| 97 | * | ||
| 98 | * ACCURATE CLOCK APPROACH: | ||
| 99 | * ______________ LEFT | ||
| 100 | * _| RIGHT |_______________________________| | ||
| 101 | * _ _ _ _ _ _ _ _ _ | ||
| 102 | * _| |_ x16 _| |_| |_ x16 _| |_| |_| |_| |_| |_| | | ||
| 103 | * _ _ _ _ dummy cycles | ||
| 104 | * _/ \_ ... _/ \_/ \_ ... _/ \__________________ | ||
| 105 | * \_/ \_/ \_/ \_/ | ||
| 106 | * | ||
| 107 | */ | ||
| 108 | bool i2s_accurate_sck; | ||
| 109 | |||
| 66 | /* McASP specific fields */ | 110 | /* McASP specific fields */ |
| 67 | int tdm_slots; | 111 | int tdm_slots; |
| 68 | u8 op_mode; | 112 | u8 op_mode; |
| @@ -78,6 +122,11 @@ enum { | |||
| 78 | MCASP_VERSION_2, /* DA8xx/OMAPL1x */ | 122 | MCASP_VERSION_2, /* DA8xx/OMAPL1x */ |
| 79 | }; | 123 | }; |
| 80 | 124 | ||
| 125 | enum dm365_clk_input_pin { | ||
| 126 | MCBSP_CLKR = 0, /* DM365 */ | ||
| 127 | MCBSP_CLKS, | ||
| 128 | }; | ||
| 129 | |||
| 81 | #define INACTIVE_MODE 0 | 130 | #define INACTIVE_MODE 0 |
| 82 | #define TX_MODE 1 | 131 | #define TX_MODE 1 |
| 83 | #define RX_MODE 2 | 132 | #define RX_MODE 2 |
diff --git a/arch/arm/mach-ep93xx/clock.c b/arch/arm/mach-ep93xx/clock.c index e29bdef9b2e2..803162492c4a 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); | |||
| 43 | 43 | ||
| 44 | static int set_keytchclk_rate(struct clk *clk, unsigned long rate); | 44 | static int set_keytchclk_rate(struct clk *clk, unsigned long rate); |
| 45 | static int set_div_rate(struct clk *clk, unsigned long rate); | 45 | static int set_div_rate(struct clk *clk, unsigned long rate); |
| 46 | 46 | static int set_i2s_sclk_rate(struct clk *clk, unsigned long rate); | |
| 47 | static int set_i2s_lrclk_rate(struct clk *clk, unsigned long rate); | ||
| 47 | 48 | ||
| 48 | static struct clk clk_xtali = { | 49 | static struct clk clk_xtali = { |
| 49 | .rate = EP93XX_EXT_CLK_RATE, | 50 | .rate = EP93XX_EXT_CLK_RATE, |
| @@ -112,6 +113,29 @@ static struct clk clk_video = { | |||
| 112 | .set_rate = set_div_rate, | 113 | .set_rate = set_div_rate, |
| 113 | }; | 114 | }; |
| 114 | 115 | ||
| 116 | static struct clk clk_i2s_mclk = { | ||
| 117 | .sw_locked = 1, | ||
| 118 | .enable_reg = EP93XX_SYSCON_I2SCLKDIV, | ||
| 119 | .enable_mask = EP93XX_SYSCON_CLKDIV_ENABLE, | ||
| 120 | .set_rate = set_div_rate, | ||
| 121 | }; | ||
| 122 | |||
| 123 | static struct clk clk_i2s_sclk = { | ||
| 124 | .sw_locked = 1, | ||
| 125 | .parent = &clk_i2s_mclk, | ||
| 126 | .enable_reg = EP93XX_SYSCON_I2SCLKDIV, | ||
| 127 | .enable_mask = EP93XX_SYSCON_I2SCLKDIV_SENA, | ||
| 128 | .set_rate = set_i2s_sclk_rate, | ||
| 129 | }; | ||
| 130 | |||
| 131 | static struct clk clk_i2s_lrclk = { | ||
| 132 | .sw_locked = 1, | ||
| 133 | .parent = &clk_i2s_sclk, | ||
| 134 | .enable_reg = EP93XX_SYSCON_I2SCLKDIV, | ||
| 135 | .enable_mask = EP93XX_SYSCON_I2SCLKDIV_SENA, | ||
| 136 | .set_rate = set_i2s_lrclk_rate, | ||
| 137 | }; | ||
| 138 | |||
| 115 | /* DMA Clocks */ | 139 | /* DMA Clocks */ |
| 116 | static struct clk clk_m2p0 = { | 140 | static struct clk clk_m2p0 = { |
| 117 | .parent = &clk_h, | 141 | .parent = &clk_h, |
| @@ -191,6 +215,9 @@ static struct clk_lookup clocks[] = { | |||
| 191 | INIT_CK("ep93xx-keypad", NULL, &clk_keypad), | 215 | INIT_CK("ep93xx-keypad", NULL, &clk_keypad), |
| 192 | INIT_CK("ep93xx-fb", NULL, &clk_video), | 216 | INIT_CK("ep93xx-fb", NULL, &clk_video), |
| 193 | INIT_CK("ep93xx-spi.0", NULL, &clk_spi), | 217 | INIT_CK("ep93xx-spi.0", NULL, &clk_spi), |
| 218 | INIT_CK("ep93xx-i2s", "mclk", &clk_i2s_mclk), | ||
| 219 | INIT_CK("ep93xx-i2s", "sclk", &clk_i2s_sclk), | ||
| 220 | INIT_CK("ep93xx-i2s", "lrclk", &clk_i2s_lrclk), | ||
| 194 | INIT_CK(NULL, "pwm_clk", &clk_pwm), | 221 | INIT_CK(NULL, "pwm_clk", &clk_pwm), |
| 195 | INIT_CK(NULL, "m2p0", &clk_m2p0), | 222 | INIT_CK(NULL, "m2p0", &clk_m2p0), |
| 196 | INIT_CK(NULL, "m2p1", &clk_m2p1), | 223 | INIT_CK(NULL, "m2p1", &clk_m2p1), |
| @@ -401,6 +428,44 @@ static int set_div_rate(struct clk *clk, unsigned long rate) | |||
| 401 | return 0; | 428 | return 0; |
| 402 | } | 429 | } |
| 403 | 430 | ||
| 431 | static int set_i2s_sclk_rate(struct clk *clk, unsigned long rate) | ||
| 432 | { | ||
| 433 | unsigned val = __raw_readl(clk->enable_reg); | ||
| 434 | |||
| 435 | if (rate == clk_i2s_mclk.rate / 2) | ||
| 436 | ep93xx_syscon_swlocked_write(val & ~EP93XX_I2SCLKDIV_SDIV, | ||
| 437 | clk->enable_reg); | ||
| 438 | else if (rate == clk_i2s_mclk.rate / 4) | ||
| 439 | ep93xx_syscon_swlocked_write(val | EP93XX_I2SCLKDIV_SDIV, | ||
| 440 | clk->enable_reg); | ||
| 441 | else | ||
| 442 | return -EINVAL; | ||
| 443 | |||
| 444 | clk_i2s_sclk.rate = rate; | ||
| 445 | return 0; | ||
| 446 | } | ||
| 447 | |||
| 448 | static int set_i2s_lrclk_rate(struct clk *clk, unsigned long rate) | ||
| 449 | { | ||
| 450 | unsigned val = __raw_readl(clk->enable_reg) & | ||
| 451 | ~EP93XX_I2SCLKDIV_LRDIV_MASK; | ||
| 452 | |||
| 453 | if (rate == clk_i2s_sclk.rate / 32) | ||
| 454 | ep93xx_syscon_swlocked_write(val | EP93XX_I2SCLKDIV_LRDIV32, | ||
| 455 | clk->enable_reg); | ||
| 456 | else if (rate == clk_i2s_sclk.rate / 64) | ||
| 457 | ep93xx_syscon_swlocked_write(val | EP93XX_I2SCLKDIV_LRDIV64, | ||
| 458 | clk->enable_reg); | ||
| 459 | else if (rate == clk_i2s_sclk.rate / 128) | ||
| 460 | ep93xx_syscon_swlocked_write(val | EP93XX_I2SCLKDIV_LRDIV128, | ||
| 461 | clk->enable_reg); | ||
| 462 | else | ||
| 463 | return -EINVAL; | ||
| 464 | |||
| 465 | clk_i2s_lrclk.rate = rate; | ||
| 466 | return 0; | ||
| 467 | } | ||
| 468 | |||
| 404 | int clk_set_rate(struct clk *clk, unsigned long rate) | 469 | int clk_set_rate(struct clk *clk, unsigned long rate) |
| 405 | { | 470 | { |
| 406 | if (clk->set_rate) | 471 | if (clk->set_rate) |
diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c index 9092677f63eb..b4ee5409eb72 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) | |||
| 714 | } | 714 | } |
| 715 | EXPORT_SYMBOL(ep93xx_keypad_release_gpio); | 715 | EXPORT_SYMBOL(ep93xx_keypad_release_gpio); |
| 716 | 716 | ||
| 717 | /************************************************************************* | ||
| 718 | * EP93xx I2S audio peripheral handling | ||
| 719 | *************************************************************************/ | ||
| 720 | static struct resource ep93xx_i2s_resource[] = { | ||
| 721 | { | ||
| 722 | .start = EP93XX_I2S_PHYS_BASE, | ||
| 723 | .end = EP93XX_I2S_PHYS_BASE + 0x100 - 1, | ||
| 724 | .flags = IORESOURCE_MEM, | ||
| 725 | }, | ||
| 726 | }; | ||
| 727 | |||
| 728 | static struct platform_device ep93xx_i2s_device = { | ||
| 729 | .name = "ep93xx-i2s", | ||
| 730 | .id = -1, | ||
| 731 | .num_resources = ARRAY_SIZE(ep93xx_i2s_resource), | ||
| 732 | .resource = ep93xx_i2s_resource, | ||
| 733 | }; | ||
| 734 | |||
| 735 | void __init ep93xx_register_i2s(void) | ||
| 736 | { | ||
| 737 | platform_device_register(&ep93xx_i2s_device); | ||
| 738 | } | ||
| 739 | |||
| 740 | #define EP93XX_SYSCON_DEVCFG_I2S_MASK (EP93XX_SYSCON_DEVCFG_I2SONSSP | \ | ||
| 741 | EP93XX_SYSCON_DEVCFG_I2SONAC97) | ||
| 742 | |||
| 743 | #define EP93XX_I2SCLKDIV_MASK (EP93XX_SYSCON_I2SCLKDIV_ORIDE | \ | ||
| 744 | EP93XX_SYSCON_I2SCLKDIV_SPOL) | ||
| 745 | |||
| 746 | int ep93xx_i2s_acquire(unsigned i2s_pins, unsigned i2s_config) | ||
| 747 | { | ||
| 748 | unsigned val; | ||
| 749 | |||
| 750 | /* Sanity check */ | ||
| 751 | if (i2s_pins & ~EP93XX_SYSCON_DEVCFG_I2S_MASK) | ||
| 752 | return -EINVAL; | ||
| 753 | if (i2s_config & ~EP93XX_I2SCLKDIV_MASK) | ||
| 754 | return -EINVAL; | ||
| 755 | |||
| 756 | /* Must have only one of I2SONSSP/I2SONAC97 set */ | ||
| 757 | if ((i2s_pins & EP93XX_SYSCON_DEVCFG_I2SONSSP) == | ||
| 758 | (i2s_pins & EP93XX_SYSCON_DEVCFG_I2SONAC97)) | ||
| 759 | return -EINVAL; | ||
| 760 | |||
| 761 | ep93xx_devcfg_clear_bits(EP93XX_SYSCON_DEVCFG_I2S_MASK); | ||
| 762 | ep93xx_devcfg_set_bits(i2s_pins); | ||
| 763 | |||
| 764 | /* | ||
| 765 | * This is potentially racy with the clock api for i2s_mclk, sclk and | ||
| 766 | * lrclk. Since the i2s driver is the only user of those clocks we | ||
| 767 | * rely on it to prevent parallel use of this function and the | ||
| 768 | * clock api for the i2s clocks. | ||
| 769 | */ | ||
| 770 | val = __raw_readl(EP93XX_SYSCON_I2SCLKDIV); | ||
| 771 | val &= ~EP93XX_I2SCLKDIV_MASK; | ||
| 772 | val |= i2s_config; | ||
| 773 | ep93xx_syscon_swlocked_write(val, EP93XX_SYSCON_I2SCLKDIV); | ||
| 774 | |||
| 775 | return 0; | ||
| 776 | } | ||
| 777 | EXPORT_SYMBOL(ep93xx_i2s_acquire); | ||
| 778 | |||
| 779 | void ep93xx_i2s_release(void) | ||
| 780 | { | ||
| 781 | ep93xx_devcfg_clear_bits(EP93XX_SYSCON_DEVCFG_I2S_MASK); | ||
| 782 | } | ||
| 783 | EXPORT_SYMBOL(ep93xx_i2s_release); | ||
| 717 | 784 | ||
| 718 | extern void ep93xx_gpio_init(void); | 785 | extern void ep93xx_gpio_init(void); |
| 719 | 786 | ||
diff --git a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h index b1e096f0c2d2..c54b3e56ba63 100644 --- a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h +++ b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h | |||
| @@ -93,6 +93,7 @@ | |||
| 93 | /* APB peripherals */ | 93 | /* APB peripherals */ |
| 94 | #define EP93XX_TIMER_BASE EP93XX_APB_IOMEM(0x00010000) | 94 | #define EP93XX_TIMER_BASE EP93XX_APB_IOMEM(0x00010000) |
| 95 | 95 | ||
| 96 | #define EP93XX_I2S_PHYS_BASE EP93XX_APB_PHYS(0x00020000) | ||
| 96 | #define EP93XX_I2S_BASE EP93XX_APB_IOMEM(0x00020000) | 97 | #define EP93XX_I2S_BASE EP93XX_APB_IOMEM(0x00020000) |
| 97 | 98 | ||
| 98 | #define EP93XX_SECURITY_BASE EP93XX_APB_IOMEM(0x00030000) | 99 | #define EP93XX_SECURITY_BASE EP93XX_APB_IOMEM(0x00030000) |
| @@ -194,6 +195,15 @@ | |||
| 194 | #define EP93XX_SYSCON_CLKDIV_ESEL (1<<14) | 195 | #define EP93XX_SYSCON_CLKDIV_ESEL (1<<14) |
| 195 | #define EP93XX_SYSCON_CLKDIV_PSEL (1<<13) | 196 | #define EP93XX_SYSCON_CLKDIV_PSEL (1<<13) |
| 196 | #define EP93XX_SYSCON_CLKDIV_PDIV_SHIFT 8 | 197 | #define EP93XX_SYSCON_CLKDIV_PDIV_SHIFT 8 |
| 198 | #define EP93XX_SYSCON_I2SCLKDIV EP93XX_SYSCON_REG(0x8c) | ||
| 199 | #define EP93XX_SYSCON_I2SCLKDIV_SENA (1<<31) | ||
| 200 | #define EP93XX_SYSCON_I2SCLKDIV_ORIDE (1<<29) | ||
| 201 | #define EP93XX_SYSCON_I2SCLKDIV_SPOL (1<<19) | ||
| 202 | #define EP93XX_I2SCLKDIV_SDIV (1 << 16) | ||
| 203 | #define EP93XX_I2SCLKDIV_LRDIV32 (0 << 17) | ||
| 204 | #define EP93XX_I2SCLKDIV_LRDIV64 (1 << 17) | ||
| 205 | #define EP93XX_I2SCLKDIV_LRDIV128 (2 << 17) | ||
| 206 | #define EP93XX_I2SCLKDIV_LRDIV_MASK (3 << 17) | ||
| 197 | #define EP93XX_SYSCON_KEYTCHCLKDIV EP93XX_SYSCON_REG(0x90) | 207 | #define EP93XX_SYSCON_KEYTCHCLKDIV EP93XX_SYSCON_REG(0x90) |
| 198 | #define EP93XX_SYSCON_KEYTCHCLKDIV_TSEN (1<<31) | 208 | #define EP93XX_SYSCON_KEYTCHCLKDIV_TSEN (1<<31) |
| 199 | #define EP93XX_SYSCON_KEYTCHCLKDIV_ADIV (1<<16) | 209 | #define EP93XX_SYSCON_KEYTCHCLKDIV_ADIV (1<<16) |
diff --git a/arch/arm/mach-ep93xx/include/mach/platform.h b/arch/arm/mach-ep93xx/include/mach/platform.h index 9a4413dd44bb..99eff877a146 100644 --- a/arch/arm/mach-ep93xx/include/mach/platform.h +++ b/arch/arm/mach-ep93xx/include/mach/platform.h | |||
| @@ -55,6 +55,9 @@ void ep93xx_pwm_release_gpio(struct platform_device *pdev); | |||
| 55 | void ep93xx_register_keypad(struct ep93xx_keypad_platform_data *data); | 55 | void ep93xx_register_keypad(struct ep93xx_keypad_platform_data *data); |
| 56 | int ep93xx_keypad_acquire_gpio(struct platform_device *pdev); | 56 | int ep93xx_keypad_acquire_gpio(struct platform_device *pdev); |
| 57 | void ep93xx_keypad_release_gpio(struct platform_device *pdev); | 57 | void ep93xx_keypad_release_gpio(struct platform_device *pdev); |
| 58 | void ep93xx_register_i2s(void); | ||
| 59 | int ep93xx_i2s_acquire(unsigned i2s_pins, unsigned i2s_config); | ||
| 60 | void ep93xx_i2s_release(void); | ||
| 58 | 61 | ||
| 59 | void ep93xx_init_devices(void); | 62 | void ep93xx_init_devices(void); |
| 60 | extern struct sys_timer ep93xx_timer; | 63 | extern struct sys_timer ep93xx_timer; |
diff --git a/arch/arm/mach-ep93xx/snappercl15.c b/arch/arm/mach-ep93xx/snappercl15.c index 38deaee40397..a12c89301297 100644 --- a/arch/arm/mach-ep93xx/snappercl15.c +++ b/arch/arm/mach-ep93xx/snappercl15.c | |||
| @@ -157,6 +157,7 @@ static void __init snappercl15_init_machine(void) | |||
| 157 | ep93xx_register_i2c(&snappercl15_i2c_gpio_data, snappercl15_i2c_data, | 157 | ep93xx_register_i2c(&snappercl15_i2c_gpio_data, snappercl15_i2c_data, |
| 158 | ARRAY_SIZE(snappercl15_i2c_data)); | 158 | ARRAY_SIZE(snappercl15_i2c_data)); |
| 159 | ep93xx_register_fb(&snappercl15_fb_info); | 159 | ep93xx_register_fb(&snappercl15_fb_info); |
| 160 | ep93xx_register_i2s(); | ||
| 160 | platform_device_register(&snappercl15_nand_device); | 161 | platform_device_register(&snappercl15_nand_device); |
| 161 | } | 162 | } |
| 162 | 163 | ||
diff --git a/arch/arm/mach-kirkwood/common.c b/arch/arm/mach-kirkwood/common.c index 6072eaa5e66a..e1f3efedbcf1 100644 --- a/arch/arm/mach-kirkwood/common.c +++ b/arch/arm/mach-kirkwood/common.c | |||
| @@ -25,6 +25,7 @@ | |||
| 25 | #include <asm/mach/time.h> | 25 | #include <asm/mach/time.h> |
| 26 | #include <mach/kirkwood.h> | 26 | #include <mach/kirkwood.h> |
| 27 | #include <mach/bridge-regs.h> | 27 | #include <mach/bridge-regs.h> |
| 28 | #include <plat/audio.h> | ||
| 28 | #include <plat/cache-feroceon-l2.h> | 29 | #include <plat/cache-feroceon-l2.h> |
| 29 | #include <plat/ehci-orion.h> | 30 | #include <plat/ehci-orion.h> |
| 30 | #include <plat/mvsdio.h> | 31 | #include <plat/mvsdio.h> |
| @@ -864,6 +865,42 @@ struct sys_timer kirkwood_timer = { | |||
| 864 | .init = kirkwood_timer_init, | 865 | .init = kirkwood_timer_init, |
| 865 | }; | 866 | }; |
| 866 | 867 | ||
| 868 | /***************************************************************************** | ||
| 869 | * Audio | ||
| 870 | ****************************************************************************/ | ||
| 871 | static struct resource kirkwood_i2s_resources[] = { | ||
| 872 | [0] = { | ||
| 873 | .start = AUDIO_PHYS_BASE, | ||
| 874 | .end = AUDIO_PHYS_BASE + SZ_16K - 1, | ||
| 875 | .flags = IORESOURCE_MEM, | ||
| 876 | }, | ||
| 877 | [1] = { | ||
| 878 | .start = IRQ_KIRKWOOD_I2S, | ||
| 879 | .end = IRQ_KIRKWOOD_I2S, | ||
| 880 | .flags = IORESOURCE_IRQ, | ||
| 881 | }, | ||
| 882 | }; | ||
| 883 | |||
| 884 | static struct kirkwood_asoc_platform_data kirkwood_i2s_data = { | ||
| 885 | .dram = &kirkwood_mbus_dram_info, | ||
| 886 | .burst = 128, | ||
| 887 | }; | ||
| 888 | |||
| 889 | static struct platform_device kirkwood_i2s_device = { | ||
| 890 | .name = "kirkwood-i2s", | ||
| 891 | .id = -1, | ||
| 892 | .num_resources = ARRAY_SIZE(kirkwood_i2s_resources), | ||
| 893 | .resource = kirkwood_i2s_resources, | ||
| 894 | .dev = { | ||
| 895 | .platform_data = &kirkwood_i2s_data, | ||
| 896 | }, | ||
| 897 | }; | ||
| 898 | |||
| 899 | void __init kirkwood_audio_init(void) | ||
| 900 | { | ||
| 901 | kirkwood_clk_ctrl |= CGC_AUDIO; | ||
| 902 | platform_device_register(&kirkwood_i2s_device); | ||
| 903 | } | ||
| 867 | 904 | ||
| 868 | /***************************************************************************** | 905 | /***************************************************************************** |
| 869 | * General | 906 | * General |
| @@ -923,6 +960,7 @@ void __init kirkwood_init(void) | |||
| 923 | kirkwood_spi_plat_data.tclk = kirkwood_tclk; | 960 | kirkwood_spi_plat_data.tclk = kirkwood_tclk; |
| 924 | kirkwood_uart0_data[0].uartclk = kirkwood_tclk; | 961 | kirkwood_uart0_data[0].uartclk = kirkwood_tclk; |
| 925 | kirkwood_uart1_data[0].uartclk = kirkwood_tclk; | 962 | kirkwood_uart1_data[0].uartclk = kirkwood_tclk; |
| 963 | kirkwood_i2s_data.tclk = kirkwood_tclk; | ||
| 926 | 964 | ||
| 927 | /* | 965 | /* |
| 928 | * Disable propagation of mbus errors to the CPU local bus, | 966 | * Disable propagation of mbus errors to the CPU local bus, |
diff --git a/arch/arm/mach-kirkwood/common.h b/arch/arm/mach-kirkwood/common.h index 05e8a8a5692e..c9ab1cb3b39f 100644 --- a/arch/arm/mach-kirkwood/common.h +++ b/arch/arm/mach-kirkwood/common.h | |||
| @@ -17,6 +17,7 @@ struct mv_sata_platform_data; | |||
| 17 | struct mvsdio_platform_data; | 17 | struct mvsdio_platform_data; |
| 18 | struct mtd_partition; | 18 | struct mtd_partition; |
| 19 | struct mtd_info; | 19 | struct mtd_info; |
| 20 | struct kirkwood_asoc_platform_data; | ||
| 20 | 21 | ||
| 21 | /* | 22 | /* |
| 22 | * Basic Kirkwood init functions used early by machine-setup. | 23 | * Basic Kirkwood init functions used early by machine-setup. |
| @@ -43,6 +44,7 @@ void kirkwood_uart0_init(void); | |||
| 43 | void kirkwood_uart1_init(void); | 44 | void kirkwood_uart1_init(void); |
| 44 | void kirkwood_nand_init(struct mtd_partition *parts, int nr_parts, int delay); | 45 | void kirkwood_nand_init(struct mtd_partition *parts, int nr_parts, int delay); |
| 45 | void kirkwood_nand_init_rnb(struct mtd_partition *parts, int nr_parts, int (*dev_ready)(struct mtd_info *)); | 46 | void kirkwood_nand_init_rnb(struct mtd_partition *parts, int nr_parts, int (*dev_ready)(struct mtd_info *)); |
| 47 | void kirkwood_audio_init(void); | ||
| 46 | 48 | ||
| 47 | extern int kirkwood_tclk; | 49 | extern int kirkwood_tclk; |
| 48 | extern struct sys_timer kirkwood_timer; | 50 | extern struct sys_timer kirkwood_timer; |
diff --git a/arch/arm/mach-kirkwood/include/mach/kirkwood.h b/arch/arm/mach-kirkwood/include/mach/kirkwood.h index a15cf0ee22bd..838151d0744b 100644 --- a/arch/arm/mach-kirkwood/include/mach/kirkwood.h +++ b/arch/arm/mach-kirkwood/include/mach/kirkwood.h | |||
| @@ -96,6 +96,9 @@ | |||
| 96 | 96 | ||
| 97 | #define SDIO_PHYS_BASE (KIRKWOOD_REGS_PHYS_BASE | 0x90000) | 97 | #define SDIO_PHYS_BASE (KIRKWOOD_REGS_PHYS_BASE | 0x90000) |
| 98 | 98 | ||
| 99 | #define AUDIO_PHYS_BASE (KIRKWOOD_REGS_PHYS_BASE | 0xA0000) | ||
| 100 | #define AUDIO_VIRT_BASE (KIRKWOOD_REGS_VIRT_BASE | 0xA0000) | ||
| 101 | |||
| 99 | /* | 102 | /* |
| 100 | * Supported devices and revisions. | 103 | * Supported devices and revisions. |
| 101 | */ | 104 | */ |
diff --git a/arch/arm/mach-kirkwood/openrd-setup.c b/arch/arm/mach-kirkwood/openrd-setup.c index ad3f1ec33796..e36067ad3aca 100644 --- a/arch/arm/mach-kirkwood/openrd-setup.c +++ b/arch/arm/mach-kirkwood/openrd-setup.c | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include <linux/mtd/partitions.h> | 15 | #include <linux/mtd/partitions.h> |
| 16 | #include <linux/ata_platform.h> | 16 | #include <linux/ata_platform.h> |
| 17 | #include <linux/mv643xx_eth.h> | 17 | #include <linux/mv643xx_eth.h> |
| 18 | #include <linux/i2c.h> | ||
| 18 | #include <asm/mach-types.h> | 19 | #include <asm/mach-types.h> |
| 19 | #include <asm/mach/arch.h> | 20 | #include <asm/mach/arch.h> |
| 20 | #include <mach/kirkwood.h> | 21 | #include <mach/kirkwood.h> |
| @@ -60,6 +61,12 @@ static unsigned int openrd_mpp_config[] __initdata = { | |||
| 60 | 0 | 61 | 0 |
| 61 | }; | 62 | }; |
| 62 | 63 | ||
| 64 | static struct i2c_board_info i2c_board_info[] __initdata = { | ||
| 65 | { | ||
| 66 | I2C_BOARD_INFO("cs42l51", 0x4a), | ||
| 67 | }, | ||
| 68 | }; | ||
| 69 | |||
| 63 | static void __init openrd_init(void) | 70 | static void __init openrd_init(void) |
| 64 | { | 71 | { |
| 65 | /* | 72 | /* |
| @@ -80,6 +87,12 @@ static void __init openrd_init(void) | |||
| 80 | kirkwood_sdio_init(&openrd_mvsdio_data); | 87 | kirkwood_sdio_init(&openrd_mvsdio_data); |
| 81 | 88 | ||
| 82 | kirkwood_i2c_init(); | 89 | kirkwood_i2c_init(); |
| 90 | |||
| 91 | if (machine_is_openrd_client()) { | ||
| 92 | i2c_register_board_info(0, i2c_board_info, | ||
| 93 | ARRAY_SIZE(i2c_board_info)); | ||
| 94 | kirkwood_audio_init(); | ||
| 95 | } | ||
| 83 | } | 96 | } |
| 84 | 97 | ||
| 85 | static int __init openrd_pci_init(void) | 98 | static int __init openrd_pci_init(void) |
diff --git a/arch/arm/mach-omap2/mcbsp.c b/arch/arm/mach-omap2/mcbsp.c index c29337074ad3..8fb5e5345557 100644 --- a/arch/arm/mach-omap2/mcbsp.c +++ b/arch/arm/mach-omap2/mcbsp.c | |||
| @@ -133,7 +133,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = { | |||
| 133 | .rx_irq = INT_24XX_MCBSP1_IRQ_RX, | 133 | .rx_irq = INT_24XX_MCBSP1_IRQ_RX, |
| 134 | .tx_irq = INT_24XX_MCBSP1_IRQ_TX, | 134 | .tx_irq = INT_24XX_MCBSP1_IRQ_TX, |
| 135 | .ops = &omap2_mcbsp_ops, | 135 | .ops = &omap2_mcbsp_ops, |
| 136 | .buffer_size = 0x6F, | 136 | .buffer_size = 0x80, /* The FIFO has 128 locations */ |
| 137 | }, | 137 | }, |
| 138 | { | 138 | { |
| 139 | .phys_base = OMAP34XX_MCBSP2_BASE, | 139 | .phys_base = OMAP34XX_MCBSP2_BASE, |
| @@ -143,7 +143,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = { | |||
| 143 | .rx_irq = INT_24XX_MCBSP2_IRQ_RX, | 143 | .rx_irq = INT_24XX_MCBSP2_IRQ_RX, |
| 144 | .tx_irq = INT_24XX_MCBSP2_IRQ_TX, | 144 | .tx_irq = INT_24XX_MCBSP2_IRQ_TX, |
| 145 | .ops = &omap2_mcbsp_ops, | 145 | .ops = &omap2_mcbsp_ops, |
| 146 | .buffer_size = 0x3FF, | 146 | .buffer_size = 0x500, /* The FIFO has 1024 + 256 locations */ |
| 147 | }, | 147 | }, |
| 148 | { | 148 | { |
| 149 | .phys_base = OMAP34XX_MCBSP3_BASE, | 149 | .phys_base = OMAP34XX_MCBSP3_BASE, |
| @@ -153,7 +153,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = { | |||
| 153 | .rx_irq = INT_24XX_MCBSP3_IRQ_RX, | 153 | .rx_irq = INT_24XX_MCBSP3_IRQ_RX, |
| 154 | .tx_irq = INT_24XX_MCBSP3_IRQ_TX, | 154 | .tx_irq = INT_24XX_MCBSP3_IRQ_TX, |
| 155 | .ops = &omap2_mcbsp_ops, | 155 | .ops = &omap2_mcbsp_ops, |
| 156 | .buffer_size = 0x6F, | 156 | .buffer_size = 0x80, /* The FIFO has 128 locations */ |
| 157 | }, | 157 | }, |
| 158 | { | 158 | { |
| 159 | .phys_base = OMAP34XX_MCBSP4_BASE, | 159 | .phys_base = OMAP34XX_MCBSP4_BASE, |
| @@ -162,7 +162,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = { | |||
| 162 | .rx_irq = INT_24XX_MCBSP4_IRQ_RX, | 162 | .rx_irq = INT_24XX_MCBSP4_IRQ_RX, |
| 163 | .tx_irq = INT_24XX_MCBSP4_IRQ_TX, | 163 | .tx_irq = INT_24XX_MCBSP4_IRQ_TX, |
| 164 | .ops = &omap2_mcbsp_ops, | 164 | .ops = &omap2_mcbsp_ops, |
| 165 | .buffer_size = 0x6F, | 165 | .buffer_size = 0x80, /* The FIFO has 128 locations */ |
| 166 | }, | 166 | }, |
| 167 | { | 167 | { |
| 168 | .phys_base = OMAP34XX_MCBSP5_BASE, | 168 | .phys_base = OMAP34XX_MCBSP5_BASE, |
| @@ -171,7 +171,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = { | |||
| 171 | .rx_irq = INT_24XX_MCBSP5_IRQ_RX, | 171 | .rx_irq = INT_24XX_MCBSP5_IRQ_RX, |
| 172 | .tx_irq = INT_24XX_MCBSP5_IRQ_TX, | 172 | .tx_irq = INT_24XX_MCBSP5_IRQ_TX, |
| 173 | .ops = &omap2_mcbsp_ops, | 173 | .ops = &omap2_mcbsp_ops, |
| 174 | .buffer_size = 0x6F, | 174 | .buffer_size = 0x80, /* The FIFO has 128 locations */ |
| 175 | }, | 175 | }, |
| 176 | }; | 176 | }; |
| 177 | #define OMAP34XX_MCBSP_PDATA_SZ ARRAY_SIZE(omap34xx_mcbsp_pdata) | 177 | #define OMAP34XX_MCBSP_PDATA_SZ ARRAY_SIZE(omap34xx_mcbsp_pdata) |
diff --git a/arch/arm/plat-mxc/include/mach/ssi.h b/arch/arm/plat-mxc/include/mach/ssi.h index c34ded523f10..63f3c2804239 100644 --- a/arch/arm/plat-mxc/include/mach/ssi.h +++ b/arch/arm/plat-mxc/include/mach/ssi.h | |||
| @@ -10,6 +10,9 @@ struct imx_ssi_platform_data { | |||
| 10 | unsigned int flags; | 10 | unsigned int flags; |
| 11 | #define IMX_SSI_DMA (1 << 0) | 11 | #define IMX_SSI_DMA (1 << 0) |
| 12 | #define IMX_SSI_USE_AC97 (1 << 1) | 12 | #define IMX_SSI_USE_AC97 (1 << 1) |
| 13 | #define IMX_SSI_NET (1 << 2) | ||
| 14 | #define IMX_SSI_SYN (1 << 3) | ||
| 15 | #define IMX_SSI_USE_I2S_SLAVE (1 << 4) | ||
| 13 | void (*ac97_reset) (struct snd_ac97 *ac97); | 16 | void (*ac97_reset) (struct snd_ac97 *ac97); |
| 14 | void (*ac97_warm_reset)(struct snd_ac97 *ac97); | 17 | void (*ac97_warm_reset)(struct snd_ac97 *ac97); |
| 15 | }; | 18 | }; |
diff --git a/arch/arm/plat-omap/include/plat/mcbsp.h b/arch/arm/plat-omap/include/plat/mcbsp.h index 975744f10a58..b4ff6a11a8f2 100644 --- a/arch/arm/plat-omap/include/plat/mcbsp.h +++ b/arch/arm/plat-omap/include/plat/mcbsp.h | |||
| @@ -473,6 +473,7 @@ void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold); | |||
| 473 | void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold); | 473 | void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold); |
| 474 | u16 omap_mcbsp_get_max_tx_threshold(unsigned int id); | 474 | u16 omap_mcbsp_get_max_tx_threshold(unsigned int id); |
| 475 | u16 omap_mcbsp_get_max_rx_threshold(unsigned int id); | 475 | u16 omap_mcbsp_get_max_rx_threshold(unsigned int id); |
| 476 | u16 omap_mcbsp_get_fifo_size(unsigned int id); | ||
| 476 | u16 omap_mcbsp_get_tx_delay(unsigned int id); | 477 | u16 omap_mcbsp_get_tx_delay(unsigned int id); |
| 477 | u16 omap_mcbsp_get_rx_delay(unsigned int id); | 478 | u16 omap_mcbsp_get_rx_delay(unsigned int id); |
| 478 | int omap_mcbsp_get_dma_op_mode(unsigned int id); | 479 | int omap_mcbsp_get_dma_op_mode(unsigned int id); |
| @@ -483,6 +484,7 @@ static inline void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold) | |||
| 483 | { } | 484 | { } |
| 484 | static inline u16 omap_mcbsp_get_max_tx_threshold(unsigned int id) { return 0; } | 485 | static inline u16 omap_mcbsp_get_max_tx_threshold(unsigned int id) { return 0; } |
| 485 | static inline u16 omap_mcbsp_get_max_rx_threshold(unsigned int id) { return 0; } | 486 | static inline u16 omap_mcbsp_get_max_rx_threshold(unsigned int id) { return 0; } |
| 487 | static inline u16 omap_mcbsp_get_fifo_size(unsigned int id) { return 0; } | ||
| 486 | static inline u16 omap_mcbsp_get_tx_delay(unsigned int id) { return 0; } | 488 | static inline u16 omap_mcbsp_get_tx_delay(unsigned int id) { return 0; } |
| 487 | static inline u16 omap_mcbsp_get_rx_delay(unsigned int id) { return 0; } | 489 | static inline u16 omap_mcbsp_get_rx_delay(unsigned int id) { return 0; } |
| 488 | static inline int omap_mcbsp_get_dma_op_mode(unsigned int id) { return 0; } | 490 | static inline int omap_mcbsp_get_dma_op_mode(unsigned int id) { return 0; } |
diff --git a/arch/arm/plat-omap/mcbsp.c b/arch/arm/plat-omap/mcbsp.c index 7e669c9744d8..e31496e35b0f 100644 --- a/arch/arm/plat-omap/mcbsp.c +++ b/arch/arm/plat-omap/mcbsp.c | |||
| @@ -481,9 +481,9 @@ int omap_st_is_enabled(unsigned int id) | |||
| 481 | EXPORT_SYMBOL(omap_st_is_enabled); | 481 | EXPORT_SYMBOL(omap_st_is_enabled); |
| 482 | 482 | ||
| 483 | /* | 483 | /* |
| 484 | * omap_mcbsp_set_tx_threshold configures how to deal | 484 | * omap_mcbsp_set_rx_threshold configures the transmit threshold in words. |
| 485 | * with transmit threshold. the threshold value and handler can be | 485 | * The threshold parameter is 1 based, and it is converted (threshold - 1) |
| 486 | * configure in here. | 486 | * for the THRSH2 register. |
| 487 | */ | 487 | */ |
| 488 | void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold) | 488 | void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold) |
| 489 | { | 489 | { |
| @@ -498,14 +498,15 @@ void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold) | |||
| 498 | } | 498 | } |
| 499 | mcbsp = id_to_mcbsp_ptr(id); | 499 | mcbsp = id_to_mcbsp_ptr(id); |
| 500 | 500 | ||
| 501 | MCBSP_WRITE(mcbsp, THRSH2, threshold); | 501 | if (threshold && threshold <= mcbsp->max_tx_thres) |
| 502 | MCBSP_WRITE(mcbsp, THRSH2, threshold - 1); | ||
| 502 | } | 503 | } |
| 503 | EXPORT_SYMBOL(omap_mcbsp_set_tx_threshold); | 504 | EXPORT_SYMBOL(omap_mcbsp_set_tx_threshold); |
| 504 | 505 | ||
| 505 | /* | 506 | /* |
| 506 | * omap_mcbsp_set_rx_threshold configures how to deal | 507 | * omap_mcbsp_set_rx_threshold configures the receive threshold in words. |
| 507 | * with receive threshold. the threshold value and handler can be | 508 | * The threshold parameter is 1 based, and it is converted (threshold - 1) |
| 508 | * configure in here. | 509 | * for the THRSH1 register. |
| 509 | */ | 510 | */ |
| 510 | void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold) | 511 | void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold) |
| 511 | { | 512 | { |
| @@ -520,7 +521,8 @@ void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold) | |||
| 520 | } | 521 | } |
| 521 | mcbsp = id_to_mcbsp_ptr(id); | 522 | mcbsp = id_to_mcbsp_ptr(id); |
| 522 | 523 | ||
| 523 | MCBSP_WRITE(mcbsp, THRSH1, threshold); | 524 | if (threshold && threshold <= mcbsp->max_rx_thres) |
| 525 | MCBSP_WRITE(mcbsp, THRSH1, threshold - 1); | ||
| 524 | } | 526 | } |
| 525 | EXPORT_SYMBOL(omap_mcbsp_set_rx_threshold); | 527 | EXPORT_SYMBOL(omap_mcbsp_set_rx_threshold); |
| 526 | 528 | ||
| @@ -560,8 +562,20 @@ u16 omap_mcbsp_get_max_rx_threshold(unsigned int id) | |||
| 560 | } | 562 | } |
| 561 | EXPORT_SYMBOL(omap_mcbsp_get_max_rx_threshold); | 563 | EXPORT_SYMBOL(omap_mcbsp_get_max_rx_threshold); |
| 562 | 564 | ||
| 563 | #define MCBSP2_FIFO_SIZE 0x500 /* 1024 + 256 locations */ | 565 | u16 omap_mcbsp_get_fifo_size(unsigned int id) |
| 564 | #define MCBSP1345_FIFO_SIZE 0x80 /* 128 locations */ | 566 | { |
| 567 | struct omap_mcbsp *mcbsp; | ||
| 568 | |||
| 569 | if (!omap_mcbsp_check_valid_id(id)) { | ||
| 570 | printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1); | ||
| 571 | return -ENODEV; | ||
| 572 | } | ||
| 573 | mcbsp = id_to_mcbsp_ptr(id); | ||
| 574 | |||
| 575 | return mcbsp->pdata->buffer_size; | ||
| 576 | } | ||
| 577 | EXPORT_SYMBOL(omap_mcbsp_get_fifo_size); | ||
| 578 | |||
| 565 | /* | 579 | /* |
| 566 | * omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO | 580 | * omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO |
| 567 | */ | 581 | */ |
| @@ -580,10 +594,7 @@ u16 omap_mcbsp_get_tx_delay(unsigned int id) | |||
| 580 | buffstat = MCBSP_READ(mcbsp, XBUFFSTAT); | 594 | buffstat = MCBSP_READ(mcbsp, XBUFFSTAT); |
| 581 | 595 | ||
| 582 | /* Number of slots are different in McBSP ports */ | 596 | /* Number of slots are different in McBSP ports */ |
| 583 | if (mcbsp->id == 2) | 597 | return mcbsp->pdata->buffer_size - buffstat; |
| 584 | return MCBSP2_FIFO_SIZE - buffstat; | ||
| 585 | else | ||
| 586 | return MCBSP1345_FIFO_SIZE - buffstat; | ||
| 587 | } | 598 | } |
| 588 | EXPORT_SYMBOL(omap_mcbsp_get_tx_delay); | 599 | EXPORT_SYMBOL(omap_mcbsp_get_tx_delay); |
| 589 | 600 | ||
| @@ -1683,8 +1694,16 @@ static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp) | |||
| 1683 | { | 1694 | { |
| 1684 | mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT; | 1695 | mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT; |
| 1685 | if (cpu_is_omap34xx()) { | 1696 | if (cpu_is_omap34xx()) { |
| 1686 | mcbsp->max_tx_thres = max_thres(mcbsp); | 1697 | /* |
| 1687 | mcbsp->max_rx_thres = max_thres(mcbsp); | 1698 | * Initially configure the maximum thresholds to a safe value. |
| 1699 | * The McBSP FIFO usage with these values should not go under | ||
| 1700 | * 16 locations. | ||
| 1701 | * If the whole FIFO without safety buffer is used, than there | ||
| 1702 | * is a possibility that the DMA will be not able to push the | ||
| 1703 | * new data on time, causing channel shifts in runtime. | ||
| 1704 | */ | ||
| 1705 | mcbsp->max_tx_thres = max_thres(mcbsp) - 0x10; | ||
| 1706 | mcbsp->max_rx_thres = max_thres(mcbsp) - 0x10; | ||
| 1688 | /* | 1707 | /* |
| 1689 | * REVISIT: Set dmap_op_mode to THRESHOLD as default | 1708 | * REVISIT: Set dmap_op_mode to THRESHOLD as default |
| 1690 | * for mcbsp2 instances. | 1709 | * for mcbsp2 instances. |
diff --git a/arch/arm/plat-orion/include/plat/audio.h b/arch/arm/plat-orion/include/plat/audio.h new file mode 100644 index 000000000000..9cf1f781329b --- /dev/null +++ b/arch/arm/plat-orion/include/plat/audio.h | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | #ifndef __PLAT_AUDIO_H | ||
| 2 | #define __PLAT_AUDIO_H | ||
| 3 | |||
| 4 | #include <linux/mbus.h> | ||
| 5 | |||
| 6 | struct kirkwood_asoc_platform_data { | ||
| 7 | u32 tclk; | ||
| 8 | struct mbus_dram_target_info *dram; | ||
| 9 | int burst; | ||
| 10 | }; | ||
| 11 | #endif | ||
diff --git a/include/sound/sh_fsi.h b/include/sound/sh_fsi.h index c0227361a876..9d51d6f35893 100644 --- a/include/sound/sh_fsi.h +++ b/include/sound/sh_fsi.h | |||
| @@ -12,6 +12,9 @@ | |||
| 12 | * published by the Free Software Foundation. | 12 | * published by the Free Software Foundation. |
| 13 | */ | 13 | */ |
| 14 | 14 | ||
| 15 | #define FSI_PORT_A 0 | ||
| 16 | #define FSI_PORT_B 1 | ||
| 17 | |||
| 15 | /* flags format | 18 | /* flags format |
| 16 | 19 | ||
| 17 | * 0xABCDEEFF | 20 | * 0xABCDEEFF |
| @@ -55,12 +58,14 @@ | |||
| 55 | #define SH_FSI_GET_IFMT(x) ((x >> 8) & SH_FSI_FMT_MASK) | 58 | #define SH_FSI_GET_IFMT(x) ((x >> 8) & SH_FSI_FMT_MASK) |
| 56 | #define SH_FSI_GET_OFMT(x) ((x >> 0) & SH_FSI_FMT_MASK) | 59 | #define SH_FSI_GET_OFMT(x) ((x >> 0) & SH_FSI_FMT_MASK) |
| 57 | 60 | ||
| 58 | #define SH_FSI_FMT_MONO (1 << 0) | 61 | #define SH_FSI_FMT_MONO 0 |
| 59 | #define SH_FSI_FMT_MONO_DELAY (1 << 1) | 62 | #define SH_FSI_FMT_MONO_DELAY 1 |
| 60 | #define SH_FSI_FMT_PCM (1 << 2) | 63 | #define SH_FSI_FMT_PCM 2 |
| 61 | #define SH_FSI_FMT_I2S (1 << 3) | 64 | #define SH_FSI_FMT_I2S 3 |
| 62 | #define SH_FSI_FMT_TDM (1 << 4) | 65 | #define SH_FSI_FMT_TDM 4 |
| 63 | #define SH_FSI_FMT_TDM_DELAY (1 << 5) | 66 | #define SH_FSI_FMT_TDM_DELAY 5 |
| 67 | #define SH_FSI_FMT_SPDIF 6 | ||
| 68 | |||
| 64 | 69 | ||
| 65 | #define SH_FSI_IFMT_TDM_CH(x) \ | 70 | #define SH_FSI_IFMT_TDM_CH(x) \ |
| 66 | (SH_FSI_IFMT(TDM) | SH_FSI_SET_CH_I(x)) | 71 | (SH_FSI_IFMT(TDM) | SH_FSI_SET_CH_I(x)) |
| @@ -72,9 +77,41 @@ | |||
| 72 | #define SH_FSI_OFMT_TDM_DELAY_CH(x) \ | 77 | #define SH_FSI_OFMT_TDM_DELAY_CH(x) \ |
| 73 | (SH_FSI_OFMT(TDM_DELAY) | SH_FSI_SET_CH_O(x)) | 78 | (SH_FSI_OFMT(TDM_DELAY) | SH_FSI_SET_CH_O(x)) |
| 74 | 79 | ||
| 80 | |||
| 81 | /* | ||
| 82 | * set_rate return value | ||
| 83 | * | ||
| 84 | * see ACKMD/BPFMD on | ||
| 85 | * ACK_MD (FSI2) | ||
| 86 | * CKG1 (FSI) | ||
| 87 | * | ||
| 88 | * err: return value < 0 | ||
| 89 | * | ||
| 90 | * 0x-00000AB | ||
| 91 | * | ||
| 92 | * A: ACKMD value | ||
| 93 | * B: BPFMD value | ||
| 94 | */ | ||
| 95 | |||
| 96 | #define SH_FSI_ACKMD_MASK (0xF << 0) | ||
| 97 | #define SH_FSI_ACKMD_512 (1 << 0) | ||
| 98 | #define SH_FSI_ACKMD_256 (2 << 0) | ||
| 99 | #define SH_FSI_ACKMD_128 (3 << 0) | ||
| 100 | #define SH_FSI_ACKMD_64 (4 << 0) | ||
| 101 | #define SH_FSI_ACKMD_32 (5 << 0) | ||
| 102 | |||
| 103 | #define SH_FSI_BPFMD_MASK (0xF << 4) | ||
| 104 | #define SH_FSI_BPFMD_512 (1 << 4) | ||
| 105 | #define SH_FSI_BPFMD_256 (2 << 4) | ||
| 106 | #define SH_FSI_BPFMD_128 (3 << 4) | ||
| 107 | #define SH_FSI_BPFMD_64 (4 << 4) | ||
| 108 | #define SH_FSI_BPFMD_32 (5 << 4) | ||
| 109 | #define SH_FSI_BPFMD_16 (6 << 4) | ||
| 110 | |||
| 75 | struct sh_fsi_platform_info { | 111 | struct sh_fsi_platform_info { |
| 76 | unsigned long porta_flags; | 112 | unsigned long porta_flags; |
| 77 | unsigned long portb_flags; | 113 | unsigned long portb_flags; |
| 114 | int (*set_rate)(int is_porta, int rate); /* for master mode */ | ||
| 78 | }; | 115 | }; |
| 79 | 116 | ||
| 80 | extern struct snd_soc_dai fsi_soc_dai[2]; | 117 | extern struct snd_soc_dai fsi_soc_dai[2]; |
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 66ff4c124dbd..c5d9987bc897 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h | |||
| @@ -273,6 +273,8 @@ | |||
| 273 | #define SND_SOC_DAPM_POST_PMD 0x8 /* after widget power down */ | 273 | #define SND_SOC_DAPM_POST_PMD 0x8 /* after widget power down */ |
| 274 | #define SND_SOC_DAPM_PRE_REG 0x10 /* before audio path setup */ | 274 | #define SND_SOC_DAPM_PRE_REG 0x10 /* before audio path setup */ |
| 275 | #define SND_SOC_DAPM_POST_REG 0x20 /* after audio path setup */ | 275 | #define SND_SOC_DAPM_POST_REG 0x20 /* after audio path setup */ |
| 276 | #define SND_SOC_DAPM_PRE_POST_PMD \ | ||
| 277 | (SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD) | ||
| 276 | 278 | ||
| 277 | /* convenience event type detection */ | 279 | /* convenience event type detection */ |
| 278 | #define SND_SOC_DAPM_EVENT_ON(e) \ | 280 | #define SND_SOC_DAPM_EVENT_ON(e) \ |
diff --git a/include/sound/soc.h b/include/sound/soc.h index 697e7ffe39d7..65e9d03ed4f5 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h | |||
| @@ -170,6 +170,21 @@ | |||
| 170 | .get = xhandler_get, .put = xhandler_put, \ | 170 | .get = xhandler_get, .put = xhandler_put, \ |
| 171 | .private_value = (unsigned long)&xenum } | 171 | .private_value = (unsigned long)&xenum } |
| 172 | 172 | ||
| 173 | #define SOC_DOUBLE_R_SX_TLV(xname, xreg_left, xreg_right, xshift,\ | ||
| 174 | xmin, xmax, tlv_array) \ | ||
| 175 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ | ||
| 176 | .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ | ||
| 177 | SNDRV_CTL_ELEM_ACCESS_READWRITE, \ | ||
| 178 | .tlv.p = (tlv_array), \ | ||
| 179 | .info = snd_soc_info_volsw_2r_sx, \ | ||
| 180 | .get = snd_soc_get_volsw_2r_sx, \ | ||
| 181 | .put = snd_soc_put_volsw_2r_sx, \ | ||
| 182 | .private_value = (unsigned long)&(struct soc_mixer_control) \ | ||
| 183 | {.reg = xreg_left, \ | ||
| 184 | .rreg = xreg_right, .shift = xshift, \ | ||
| 185 | .min = xmin, .max = xmax} } | ||
| 186 | |||
| 187 | |||
| 173 | /* | 188 | /* |
| 174 | * Simplified versions of above macros, declaring a struct and calculating | 189 | * Simplified versions of above macros, declaring a struct and calculating |
| 175 | * ARRAY_SIZE internally | 190 | * ARRAY_SIZE internally |
| @@ -329,6 +344,12 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, | |||
| 329 | struct snd_ctl_elem_value *ucontrol); | 344 | struct snd_ctl_elem_value *ucontrol); |
| 330 | int snd_soc_limit_volume(struct snd_soc_codec *codec, | 345 | int snd_soc_limit_volume(struct snd_soc_codec *codec, |
| 331 | const char *name, int max); | 346 | const char *name, int max); |
| 347 | int snd_soc_info_volsw_2r_sx(struct snd_kcontrol *kcontrol, | ||
| 348 | struct snd_ctl_elem_info *uinfo); | ||
| 349 | int snd_soc_get_volsw_2r_sx(struct snd_kcontrol *kcontrol, | ||
| 350 | struct snd_ctl_elem_value *ucontrol); | ||
| 351 | int snd_soc_put_volsw_2r_sx(struct snd_kcontrol *kcontrol, | ||
| 352 | struct snd_ctl_elem_value *ucontrol); | ||
| 332 | 353 | ||
| 333 | /** | 354 | /** |
| 334 | * struct snd_soc_jack_pin - Describes a pin to update based on jack detection | 355 | * struct snd_soc_jack_pin - Describes a pin to update based on jack detection |
diff --git a/include/sound/tlv320dac33-plat.h b/include/sound/tlv320dac33-plat.h index 3f428d53195b..6c6649656798 100644 --- a/include/sound/tlv320dac33-plat.h +++ b/include/sound/tlv320dac33-plat.h | |||
| @@ -15,6 +15,8 @@ | |||
| 15 | 15 | ||
| 16 | struct tlv320dac33_platform_data { | 16 | struct tlv320dac33_platform_data { |
| 17 | int power_gpio; | 17 | int power_gpio; |
| 18 | int mode1_latency; /* latency caused by the i2c writes in us */ | ||
| 19 | int auto_fifo_config; /* FIFO config based on the period size */ | ||
| 18 | int keep_bclk; /* Keep the BCLK running in FIFO modes */ | 20 | int keep_bclk; /* Keep the BCLK running in FIFO modes */ |
| 19 | u8 burst_bclkdiv; | 21 | u8 burst_bclkdiv; |
| 20 | }; | 22 | }; |
diff --git a/include/sound/uda134x.h b/include/sound/uda134x.h index 509efb050176..e475659bd3be 100644 --- a/include/sound/uda134x.h +++ b/include/sound/uda134x.h | |||
| @@ -18,6 +18,18 @@ struct uda134x_platform_data { | |||
| 18 | struct l3_pins l3; | 18 | struct l3_pins l3; |
| 19 | void (*power) (int); | 19 | void (*power) (int); |
| 20 | int model; | 20 | int model; |
| 21 | /* | ||
| 22 | ALSA SOC usually puts the device in standby mode when it's not used | ||
| 23 | for sometime. If you unset is_powered_on_standby the driver will | ||
| 24 | turn off the ADC/DAC when this callback is invoked and turn it back | ||
| 25 | on when needed. Unfortunately this will result in a very light bump | ||
| 26 | (it can be audible only with good earphones). If this bothers you | ||
| 27 | set is_powered_on_standby, you will have slightly higher power | ||
| 28 | consumption. Please note that sending the L3 command for ADC is | ||
| 29 | enough to make the bump, so it doesn't make difference if you | ||
| 30 | completely take off power from the codec. | ||
| 31 | */ | ||
| 32 | int is_powered_on_standby; | ||
| 21 | #define UDA134X_UDA1340 1 | 33 | #define UDA134X_UDA1340 1 |
| 22 | #define UDA134X_UDA1341 2 | 34 | #define UDA134X_UDA1341 2 |
| 23 | #define UDA134X_UDA1344 3 | 35 | #define UDA134X_UDA1344 3 |
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index b1749bc67979..3e598e756e54 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig | |||
| @@ -28,9 +28,13 @@ source "sound/soc/atmel/Kconfig" | |||
| 28 | source "sound/soc/au1x/Kconfig" | 28 | source "sound/soc/au1x/Kconfig" |
| 29 | source "sound/soc/blackfin/Kconfig" | 29 | source "sound/soc/blackfin/Kconfig" |
| 30 | source "sound/soc/davinci/Kconfig" | 30 | source "sound/soc/davinci/Kconfig" |
| 31 | source "sound/soc/ep93xx/Kconfig" | ||
| 31 | source "sound/soc/fsl/Kconfig" | 32 | source "sound/soc/fsl/Kconfig" |
| 32 | source "sound/soc/imx/Kconfig" | 33 | source "sound/soc/imx/Kconfig" |
| 34 | source "sound/soc/jz4740/Kconfig" | ||
| 35 | source "sound/soc/nuc900/Kconfig" | ||
| 33 | source "sound/soc/omap/Kconfig" | 36 | source "sound/soc/omap/Kconfig" |
| 37 | source "sound/soc/kirkwood/Kconfig" | ||
| 34 | source "sound/soc/pxa/Kconfig" | 38 | source "sound/soc/pxa/Kconfig" |
| 35 | source "sound/soc/s3c24xx/Kconfig" | 39 | source "sound/soc/s3c24xx/Kconfig" |
| 36 | source "sound/soc/s6000/Kconfig" | 40 | source "sound/soc/s6000/Kconfig" |
diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 1470141d4167..eb183443eee4 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile | |||
| @@ -6,9 +6,13 @@ obj-$(CONFIG_SND_SOC) += atmel/ | |||
| 6 | obj-$(CONFIG_SND_SOC) += au1x/ | 6 | obj-$(CONFIG_SND_SOC) += au1x/ |
| 7 | obj-$(CONFIG_SND_SOC) += blackfin/ | 7 | obj-$(CONFIG_SND_SOC) += blackfin/ |
| 8 | obj-$(CONFIG_SND_SOC) += davinci/ | 8 | obj-$(CONFIG_SND_SOC) += davinci/ |
| 9 | obj-$(CONFIG_SND_SOC) += ep93xx/ | ||
| 9 | obj-$(CONFIG_SND_SOC) += fsl/ | 10 | obj-$(CONFIG_SND_SOC) += fsl/ |
| 10 | obj-$(CONFIG_SND_SOC) += imx/ | 11 | obj-$(CONFIG_SND_SOC) += imx/ |
| 12 | obj-$(CONFIG_SND_SOC) += jz4740/ | ||
| 13 | obj-$(CONFIG_SND_SOC) += nuc900/ | ||
| 11 | obj-$(CONFIG_SND_SOC) += omap/ | 14 | obj-$(CONFIG_SND_SOC) += omap/ |
| 15 | obj-$(CONFIG_SND_SOC) += kirkwood/ | ||
| 12 | obj-$(CONFIG_SND_SOC) += pxa/ | 16 | obj-$(CONFIG_SND_SOC) += pxa/ |
| 13 | obj-$(CONFIG_SND_SOC) += s3c24xx/ | 17 | obj-$(CONFIG_SND_SOC) += s3c24xx/ |
| 14 | obj-$(CONFIG_SND_SOC) += s6000/ | 18 | obj-$(CONFIG_SND_SOC) += s6000/ |
diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c index f6b3cc04b34b..dc5249fba85c 100644 --- a/sound/soc/atmel/atmel-pcm.c +++ b/sound/soc/atmel/atmel-pcm.c | |||
| @@ -77,7 +77,6 @@ struct atmel_runtime_data { | |||
| 77 | size_t period_size; | 77 | size_t period_size; |
| 78 | 78 | ||
| 79 | dma_addr_t period_ptr; /* physical address of next period */ | 79 | dma_addr_t period_ptr; /* physical address of next period */ |
| 80 | int periods; /* period index of period_ptr */ | ||
| 81 | 80 | ||
| 82 | /* PDC register save */ | 81 | /* PDC register save */ |
| 83 | u32 pdc_xpr_save; | 82 | u32 pdc_xpr_save; |
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 0b59806905d1..c85844d4845b 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c | |||
| @@ -549,7 +549,6 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, | |||
| 549 | printk(KERN_WARNING "atmel_ssc_dai: unsupported DAI format 0x%x\n", | 549 | printk(KERN_WARNING "atmel_ssc_dai: unsupported DAI format 0x%x\n", |
| 550 | ssc_p->daifmt); | 550 | ssc_p->daifmt); |
| 551 | return -EINVAL; | 551 | return -EINVAL; |
| 552 | break; | ||
| 553 | } | 552 | } |
| 554 | pr_debug("atmel_ssc_hw_params: " | 553 | pr_debug("atmel_ssc_hw_params: " |
| 555 | "RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n", | 554 | "RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n", |
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c index a61ccd2d505f..d14a5a91a465 100644 --- a/sound/soc/au1x/psc-ac97.c +++ b/sound/soc/au1x/psc-ac97.c | |||
| @@ -375,12 +375,10 @@ static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev) | |||
| 375 | } | 375 | } |
| 376 | 376 | ||
| 377 | ret = -EBUSY; | 377 | ret = -EBUSY; |
| 378 | wd->ioarea = request_mem_region(r->start, r->end - r->start + 1, | 378 | if (!request_mem_region(r->start, resource_size(r), pdev->name)) |
| 379 | "au1xpsc_ac97"); | ||
| 380 | if (!wd->ioarea) | ||
| 381 | goto out0; | 379 | goto out0; |
| 382 | 380 | ||
| 383 | wd->mmio = ioremap(r->start, 0xffff); | 381 | wd->mmio = ioremap(r->start, resource_size(r)); |
| 384 | if (!wd->mmio) | 382 | if (!wd->mmio) |
| 385 | goto out1; | 383 | goto out1; |
| 386 | 384 | ||
| @@ -410,8 +408,7 @@ static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev) | |||
| 410 | 408 | ||
| 411 | snd_soc_unregister_dai(&au1xpsc_ac97_dai); | 409 | snd_soc_unregister_dai(&au1xpsc_ac97_dai); |
| 412 | out1: | 410 | out1: |
| 413 | release_resource(wd->ioarea); | 411 | release_mem_region(r->start, resource_size(r)); |
| 414 | kfree(wd->ioarea); | ||
| 415 | out0: | 412 | out0: |
| 416 | kfree(wd); | 413 | kfree(wd); |
| 417 | return ret; | 414 | return ret; |
| @@ -420,6 +417,7 @@ out0: | |||
| 420 | static int __devexit au1xpsc_ac97_drvremove(struct platform_device *pdev) | 417 | static int __devexit au1xpsc_ac97_drvremove(struct platform_device *pdev) |
| 421 | { | 418 | { |
| 422 | struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev); | 419 | struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev); |
| 420 | struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 423 | 421 | ||
| 424 | if (wd->dmapd) | 422 | if (wd->dmapd) |
| 425 | au1xpsc_pcm_destroy(wd->dmapd); | 423 | au1xpsc_pcm_destroy(wd->dmapd); |
| @@ -433,8 +431,7 @@ static int __devexit au1xpsc_ac97_drvremove(struct platform_device *pdev) | |||
| 433 | au_sync(); | 431 | au_sync(); |
| 434 | 432 | ||
| 435 | iounmap(wd->mmio); | 433 | iounmap(wd->mmio); |
| 436 | release_resource(wd->ioarea); | 434 | release_mem_region(r->start, resource_size(r)); |
| 437 | kfree(wd->ioarea); | ||
| 438 | kfree(wd); | 435 | kfree(wd); |
| 439 | 436 | ||
| 440 | au1xpsc_ac97_workdata = NULL; /* MDEV */ | 437 | au1xpsc_ac97_workdata = NULL; /* MDEV */ |
diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c index 24454c98d0ee..6083fe7799fa 100644 --- a/sound/soc/au1x/psc-i2s.c +++ b/sound/soc/au1x/psc-i2s.c | |||
| @@ -321,12 +321,10 @@ static int __devinit au1xpsc_i2s_drvprobe(struct platform_device *pdev) | |||
| 321 | } | 321 | } |
| 322 | 322 | ||
| 323 | ret = -EBUSY; | 323 | ret = -EBUSY; |
| 324 | wd->ioarea = request_mem_region(r->start, r->end - r->start + 1, | 324 | if (!request_mem_region(r->start, resource_size(r), pdev->name)) |
| 325 | "au1xpsc_i2s"); | ||
| 326 | if (!wd->ioarea) | ||
| 327 | goto out0; | 325 | goto out0; |
| 328 | 326 | ||
| 329 | wd->mmio = ioremap(r->start, 0xffff); | 327 | wd->mmio = ioremap(r->start, resource_size(r)); |
| 330 | if (!wd->mmio) | 328 | if (!wd->mmio) |
| 331 | goto out1; | 329 | goto out1; |
| 332 | 330 | ||
| @@ -362,8 +360,7 @@ static int __devinit au1xpsc_i2s_drvprobe(struct platform_device *pdev) | |||
| 362 | 360 | ||
| 363 | snd_soc_unregister_dai(&au1xpsc_i2s_dai); | 361 | snd_soc_unregister_dai(&au1xpsc_i2s_dai); |
| 364 | out1: | 362 | out1: |
| 365 | release_resource(wd->ioarea); | 363 | release_mem_region(r->start, resource_size(r)); |
| 366 | kfree(wd->ioarea); | ||
| 367 | out0: | 364 | out0: |
| 368 | kfree(wd); | 365 | kfree(wd); |
| 369 | return ret; | 366 | return ret; |
| @@ -372,6 +369,7 @@ out0: | |||
| 372 | static int __devexit au1xpsc_i2s_drvremove(struct platform_device *pdev) | 369 | static int __devexit au1xpsc_i2s_drvremove(struct platform_device *pdev) |
| 373 | { | 370 | { |
| 374 | struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev); | 371 | struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev); |
| 372 | struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 375 | 373 | ||
| 376 | if (wd->dmapd) | 374 | if (wd->dmapd) |
| 377 | au1xpsc_pcm_destroy(wd->dmapd); | 375 | au1xpsc_pcm_destroy(wd->dmapd); |
| @@ -384,8 +382,7 @@ static int __devexit au1xpsc_i2s_drvremove(struct platform_device *pdev) | |||
| 384 | au_sync(); | 382 | au_sync(); |
| 385 | 383 | ||
| 386 | iounmap(wd->mmio); | 384 | iounmap(wd->mmio); |
| 387 | release_resource(wd->ioarea); | 385 | release_mem_region(r->start, resource_size(r)); |
| 388 | kfree(wd->ioarea); | ||
| 389 | kfree(wd); | 386 | kfree(wd); |
| 390 | 387 | ||
| 391 | au1xpsc_i2s_workdata = NULL; /* MDEV */ | 388 | au1xpsc_i2s_workdata = NULL; /* MDEV */ |
diff --git a/sound/soc/au1x/psc.h b/sound/soc/au1x/psc.h index 32d3807d3f5a..093775d4dc3e 100644 --- a/sound/soc/au1x/psc.h +++ b/sound/soc/au1x/psc.h | |||
| @@ -32,7 +32,6 @@ struct au1xpsc_audio_data { | |||
| 32 | unsigned long rate; | 32 | unsigned long rate; |
| 33 | 33 | ||
| 34 | unsigned long pm[2]; | 34 | unsigned long pm[2]; |
| 35 | struct resource *ioarea; | ||
| 36 | struct mutex lock; | 35 | struct mutex lock; |
| 37 | struct platform_device *dmapd; | 36 | struct platform_device *dmapd; |
| 38 | }; | 37 | }; |
diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c index 523b7fc33f4e..c0eba5109980 100644 --- a/sound/soc/blackfin/bf5xx-ac97.c +++ b/sound/soc/blackfin/bf5xx-ac97.c | |||
| @@ -255,8 +255,7 @@ EXPORT_SYMBOL_GPL(soc_ac97_ops); | |||
| 255 | #ifdef CONFIG_PM | 255 | #ifdef CONFIG_PM |
| 256 | static int bf5xx_ac97_suspend(struct snd_soc_dai *dai) | 256 | static int bf5xx_ac97_suspend(struct snd_soc_dai *dai) |
| 257 | { | 257 | { |
| 258 | struct sport_device *sport = | 258 | struct sport_device *sport = dai->private_data; |
| 259 | (struct sport_device *)dai->private_data; | ||
| 260 | 259 | ||
| 261 | pr_debug("%s : sport %d\n", __func__, dai->id); | 260 | pr_debug("%s : sport %d\n", __func__, dai->id); |
| 262 | if (!dai->active) | 261 | if (!dai->active) |
| @@ -271,8 +270,7 @@ static int bf5xx_ac97_suspend(struct snd_soc_dai *dai) | |||
| 271 | static int bf5xx_ac97_resume(struct snd_soc_dai *dai) | 270 | static int bf5xx_ac97_resume(struct snd_soc_dai *dai) |
| 272 | { | 271 | { |
| 273 | int ret; | 272 | int ret; |
| 274 | struct sport_device *sport = | 273 | struct sport_device *sport = dai->private_data; |
| 275 | (struct sport_device *)dai->private_data; | ||
| 276 | 274 | ||
| 277 | pr_debug("%s : sport %d\n", __func__, dai->id); | 275 | pr_debug("%s : sport %d\n", __func__, dai->id); |
| 278 | if (!dai->active) | 276 | if (!dai->active) |
diff --git a/sound/soc/blackfin/bf5xx-tdm.c b/sound/soc/blackfin/bf5xx-tdm.c index 4b360124083e..24c14269f4bc 100644 --- a/sound/soc/blackfin/bf5xx-tdm.c +++ b/sound/soc/blackfin/bf5xx-tdm.c | |||
| @@ -210,8 +210,7 @@ static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai, | |||
| 210 | #ifdef CONFIG_PM | 210 | #ifdef CONFIG_PM |
| 211 | static int bf5xx_tdm_suspend(struct snd_soc_dai *dai) | 211 | static int bf5xx_tdm_suspend(struct snd_soc_dai *dai) |
| 212 | { | 212 | { |
| 213 | struct sport_device *sport = | 213 | struct sport_device *sport = dai->private_data; |
| 214 | (struct sport_device *)dai->private_data; | ||
| 215 | 214 | ||
| 216 | if (!dai->active) | 215 | if (!dai->active) |
| 217 | return 0; | 216 | return 0; |
| @@ -225,8 +224,7 @@ static int bf5xx_tdm_suspend(struct snd_soc_dai *dai) | |||
| 225 | static int bf5xx_tdm_resume(struct snd_soc_dai *dai) | 224 | static int bf5xx_tdm_resume(struct snd_soc_dai *dai) |
| 226 | { | 225 | { |
| 227 | int ret; | 226 | int ret; |
| 228 | struct sport_device *sport = | 227 | struct sport_device *sport = dai->private_data; |
| 229 | (struct sport_device *)dai->private_data; | ||
| 230 | 228 | ||
| 231 | if (!dai->active) | 229 | if (!dai->active) |
| 232 | return 0; | 230 | return 0; |
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 5da30eb6ad00..83f5c67d3c41 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
| @@ -22,9 +22,11 @@ config SND_SOC_ALL_CODECS | |||
| 22 | select SND_SOC_AK4642 if I2C | 22 | select SND_SOC_AK4642 if I2C |
| 23 | select SND_SOC_AK4671 if I2C | 23 | select SND_SOC_AK4671 if I2C |
| 24 | select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC | 24 | select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC |
| 25 | select SND_SOC_CS42L51 if I2C | ||
| 25 | select SND_SOC_CS4270 if I2C | 26 | select SND_SOC_CS4270 if I2C |
| 26 | select SND_SOC_MAX9877 if I2C | ||
| 27 | select SND_SOC_DA7210 if I2C | 27 | select SND_SOC_DA7210 if I2C |
| 28 | select SND_SOC_JZ4740 if SOC_JZ4740 | ||
| 29 | select SND_SOC_MAX9877 if I2C | ||
| 28 | select SND_SOC_PCM3008 | 30 | select SND_SOC_PCM3008 |
| 29 | select SND_SOC_SPDIF | 31 | select SND_SOC_SPDIF |
| 30 | select SND_SOC_SSM2602 if I2C | 32 | select SND_SOC_SSM2602 if I2C |
| @@ -48,6 +50,7 @@ config SND_SOC_ALL_CODECS | |||
| 48 | select SND_SOC_WM8727 | 50 | select SND_SOC_WM8727 |
| 49 | select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI | 51 | select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI |
| 50 | select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI | 52 | select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI |
| 53 | select SND_SOC_WM8741 if SND_SOC_I2C_AND_SPI | ||
| 51 | select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI | 54 | select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI |
| 52 | select SND_SOC_WM8753 if SND_SOC_I2C_AND_SPI | 55 | select SND_SOC_WM8753 if SND_SOC_I2C_AND_SPI |
| 53 | select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI | 56 | select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI |
| @@ -120,13 +123,13 @@ config SND_SOC_AK4671 | |||
| 120 | config SND_SOC_CQ0093VC | 123 | config SND_SOC_CQ0093VC |
| 121 | tristate | 124 | tristate |
| 122 | 125 | ||
| 126 | config SND_SOC_CS42L51 | ||
| 127 | tristate | ||
| 128 | |||
| 123 | # Cirrus Logic CS4270 Codec | 129 | # Cirrus Logic CS4270 Codec |
| 124 | config SND_SOC_CS4270 | 130 | config SND_SOC_CS4270 |
| 125 | tristate | 131 | tristate |
| 126 | 132 | ||
| 127 | config SND_SOC_DA7210 | ||
| 128 | tristate | ||
| 129 | |||
| 130 | # Cirrus Logic CS4270 Codec VD = 3.3V Errata | 133 | # Cirrus Logic CS4270 Codec VD = 3.3V Errata |
| 131 | # Select if you are affected by the errata where the part will not function | 134 | # Select if you are affected by the errata where the part will not function |
| 132 | # if MCLK divide-by-1.5 is selected and VD is set to 3.3V. The driver will | 135 | # if MCLK divide-by-1.5 is selected and VD is set to 3.3V. The driver will |
| @@ -138,9 +141,15 @@ config SND_SOC_CS4270_VD33_ERRATA | |||
| 138 | config SND_SOC_CX20442 | 141 | config SND_SOC_CX20442 |
| 139 | tristate | 142 | tristate |
| 140 | 143 | ||
| 144 | config SND_SOC_JZ4740_CODEC | ||
| 145 | tristate | ||
| 146 | |||
| 141 | config SND_SOC_L3 | 147 | config SND_SOC_L3 |
| 142 | tristate | 148 | tristate |
| 143 | 149 | ||
| 150 | config SND_SOC_DA7210 | ||
| 151 | tristate | ||
| 152 | |||
| 144 | config SND_SOC_PCM3008 | 153 | config SND_SOC_PCM3008 |
| 145 | tristate | 154 | tristate |
| 146 | 155 | ||
| @@ -206,6 +215,9 @@ config SND_SOC_WM8728 | |||
| 206 | config SND_SOC_WM8731 | 215 | config SND_SOC_WM8731 |
| 207 | tristate | 216 | tristate |
| 208 | 217 | ||
| 218 | config SND_SOC_WM8741 | ||
| 219 | tristate | ||
| 220 | |||
| 209 | config SND_SOC_WM8750 | 221 | config SND_SOC_WM8750 |
| 210 | tristate | 222 | tristate |
| 211 | 223 | ||
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 91429eab0707..53524095759c 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile | |||
| @@ -9,6 +9,7 @@ snd-soc-ak4535-objs := ak4535.o | |||
| 9 | snd-soc-ak4642-objs := ak4642.o | 9 | snd-soc-ak4642-objs := ak4642.o |
| 10 | snd-soc-ak4671-objs := ak4671.o | 10 | snd-soc-ak4671-objs := ak4671.o |
| 11 | snd-soc-cq93vc-objs := cq93vc.o | 11 | snd-soc-cq93vc-objs := cq93vc.o |
| 12 | snd-soc-cs42l51-objs := cs42l51.o | ||
| 12 | snd-soc-cs4270-objs := cs4270.o | 13 | snd-soc-cs4270-objs := cs4270.o |
| 13 | snd-soc-cx20442-objs := cx20442.o | 14 | snd-soc-cx20442-objs := cx20442.o |
| 14 | snd-soc-da7210-objs := da7210.o | 15 | snd-soc-da7210-objs := da7210.o |
| @@ -34,6 +35,7 @@ snd-soc-wm8711-objs := wm8711.o | |||
| 34 | snd-soc-wm8727-objs := wm8727.o | 35 | snd-soc-wm8727-objs := wm8727.o |
| 35 | snd-soc-wm8728-objs := wm8728.o | 36 | snd-soc-wm8728-objs := wm8728.o |
| 36 | snd-soc-wm8731-objs := wm8731.o | 37 | snd-soc-wm8731-objs := wm8731.o |
| 38 | snd-soc-wm8741-objs := wm8741.o | ||
| 37 | snd-soc-wm8750-objs := wm8750.o | 39 | snd-soc-wm8750-objs := wm8750.o |
| 38 | snd-soc-wm8753-objs := wm8753.o | 40 | snd-soc-wm8753-objs := wm8753.o |
| 39 | snd-soc-wm8776-objs := wm8776.o | 41 | snd-soc-wm8776-objs := wm8776.o |
| @@ -56,6 +58,7 @@ snd-soc-wm9705-objs := wm9705.o | |||
| 56 | snd-soc-wm9712-objs := wm9712.o | 58 | snd-soc-wm9712-objs := wm9712.o |
| 57 | snd-soc-wm9713-objs := wm9713.o | 59 | snd-soc-wm9713-objs := wm9713.o |
| 58 | snd-soc-wm-hubs-objs := wm_hubs.o | 60 | snd-soc-wm-hubs-objs := wm_hubs.o |
| 61 | snd-soc-jz4740-codec-objs := jz4740.o | ||
| 59 | 62 | ||
| 60 | # Amp | 63 | # Amp |
| 61 | snd-soc-max9877-objs := max9877.o | 64 | snd-soc-max9877-objs := max9877.o |
| @@ -74,10 +77,12 @@ obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o | |||
| 74 | obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o | 77 | obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o |
| 75 | obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o | 78 | obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o |
| 76 | obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o | 79 | obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o |
| 80 | obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o | ||
| 77 | obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o | 81 | obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o |
| 78 | obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o | 82 | obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o |
| 79 | obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o | 83 | obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o |
| 80 | obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o | 84 | obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o |
| 85 | obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o | ||
| 81 | obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o | 86 | obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o |
| 82 | obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o | 87 | obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o |
| 83 | obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o | 88 | obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o |
| @@ -99,6 +104,7 @@ obj-$(CONFIG_SND_SOC_WM8711) += snd-soc-wm8711.o | |||
| 99 | obj-$(CONFIG_SND_SOC_WM8727) += snd-soc-wm8727.o | 104 | obj-$(CONFIG_SND_SOC_WM8727) += snd-soc-wm8727.o |
| 100 | obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o | 105 | obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o |
| 101 | obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o | 106 | obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o |
| 107 | obj-$(CONFIG_SND_SOC_WM8741) += snd-soc-wm8741.o | ||
| 102 | obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o | 108 | obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o |
| 103 | obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o | 109 | obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o |
| 104 | obj-$(CONFIG_SND_SOC_WM8776) += snd-soc-wm8776.o | 110 | obj-$(CONFIG_SND_SOC_WM8776) += snd-soc-wm8776.o |
diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c index 217538423225..a01006c8c606 100644 --- a/sound/soc/codecs/ad1836.c +++ b/sound/soc/codecs/ad1836.c | |||
| @@ -272,6 +272,7 @@ static int ad1836_register(struct ad1836_priv *ad1836) | |||
| 272 | 272 | ||
| 273 | if (ad1836_codec) { | 273 | if (ad1836_codec) { |
| 274 | dev_err(codec->dev, "Another ad1836 is registered\n"); | 274 | dev_err(codec->dev, "Another ad1836 is registered\n"); |
| 275 | kfree(ad1836); | ||
| 275 | return -EINVAL; | 276 | return -EINVAL; |
| 276 | } | 277 | } |
| 277 | 278 | ||
diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c index c8ca1142b2f4..1def75e4862f 100644 --- a/sound/soc/codecs/ad193x.c +++ b/sound/soc/codecs/ad193x.c | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | 24 | ||
| 25 | /* codec private data */ | 25 | /* codec private data */ |
| 26 | struct ad193x_priv { | 26 | struct ad193x_priv { |
| 27 | unsigned int sysclk; | ||
| 27 | struct snd_soc_codec codec; | 28 | struct snd_soc_codec codec; |
| 28 | u8 reg_cache[AD193X_NUM_REGS]; | 29 | u8 reg_cache[AD193X_NUM_REGS]; |
| 29 | }; | 30 | }; |
| @@ -251,15 +252,32 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai, | |||
| 251 | return 0; | 252 | return 0; |
| 252 | } | 253 | } |
| 253 | 254 | ||
| 255 | static int ad193x_set_dai_sysclk(struct snd_soc_dai *codec_dai, | ||
| 256 | int clk_id, unsigned int freq, int dir) | ||
| 257 | { | ||
| 258 | struct snd_soc_codec *codec = codec_dai->codec; | ||
| 259 | struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec); | ||
| 260 | switch (freq) { | ||
| 261 | case 12288000: | ||
| 262 | case 18432000: | ||
| 263 | case 24576000: | ||
| 264 | case 36864000: | ||
| 265 | ad193x->sysclk = freq; | ||
| 266 | return 0; | ||
| 267 | } | ||
| 268 | return -EINVAL; | ||
| 269 | } | ||
| 270 | |||
| 254 | static int ad193x_hw_params(struct snd_pcm_substream *substream, | 271 | static int ad193x_hw_params(struct snd_pcm_substream *substream, |
| 255 | struct snd_pcm_hw_params *params, | 272 | struct snd_pcm_hw_params *params, |
| 256 | struct snd_soc_dai *dai) | 273 | struct snd_soc_dai *dai) |
| 257 | { | 274 | { |
| 258 | int word_len = 0, reg = 0; | 275 | int word_len = 0, reg = 0, master_rate = 0; |
| 259 | 276 | ||
| 260 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 277 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
| 261 | struct snd_soc_device *socdev = rtd->socdev; | 278 | struct snd_soc_device *socdev = rtd->socdev; |
| 262 | struct snd_soc_codec *codec = socdev->card->codec; | 279 | struct snd_soc_codec *codec = socdev->card->codec; |
| 280 | struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec); | ||
| 263 | 281 | ||
| 264 | /* bit size */ | 282 | /* bit size */ |
| 265 | switch (params_format(params)) { | 283 | switch (params_format(params)) { |
| @@ -275,6 +293,25 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream, | |||
| 275 | break; | 293 | break; |
| 276 | } | 294 | } |
| 277 | 295 | ||
| 296 | switch (ad193x->sysclk) { | ||
| 297 | case 12288000: | ||
| 298 | master_rate = AD193X_PLL_INPUT_256; | ||
| 299 | break; | ||
| 300 | case 18432000: | ||
| 301 | master_rate = AD193X_PLL_INPUT_384; | ||
| 302 | break; | ||
| 303 | case 24576000: | ||
| 304 | master_rate = AD193X_PLL_INPUT_512; | ||
| 305 | break; | ||
| 306 | case 36864000: | ||
| 307 | master_rate = AD193X_PLL_INPUT_768; | ||
| 308 | break; | ||
| 309 | } | ||
| 310 | |||
| 311 | reg = snd_soc_read(codec, AD193X_PLL_CLK_CTRL0); | ||
| 312 | reg = (reg & AD193X_PLL_INPUT_MASK) | master_rate; | ||
| 313 | snd_soc_write(codec, AD193X_PLL_CLK_CTRL0, reg); | ||
| 314 | |||
| 278 | reg = snd_soc_read(codec, AD193X_DAC_CTRL2); | 315 | reg = snd_soc_read(codec, AD193X_DAC_CTRL2); |
| 279 | reg = (reg & (~AD193X_DAC_WORD_LEN_MASK)) | word_len; | 316 | reg = (reg & (~AD193X_DAC_WORD_LEN_MASK)) | word_len; |
| 280 | snd_soc_write(codec, AD193X_DAC_CTRL2, reg); | 317 | snd_soc_write(codec, AD193X_DAC_CTRL2, reg); |
| @@ -348,6 +385,7 @@ static int ad193x_bus_probe(struct device *dev, void *ctrl_data, int bus_type) | |||
| 348 | /* pll input: mclki/xi */ | 385 | /* pll input: mclki/xi */ |
| 349 | snd_soc_write(codec, AD193X_PLL_CLK_CTRL0, 0x99); /* mclk=24.576Mhz: 0x9D; mclk=12.288Mhz: 0x99 */ | 386 | snd_soc_write(codec, AD193X_PLL_CLK_CTRL0, 0x99); /* mclk=24.576Mhz: 0x9D; mclk=12.288Mhz: 0x99 */ |
| 350 | snd_soc_write(codec, AD193X_PLL_CLK_CTRL1, 0x04); | 387 | snd_soc_write(codec, AD193X_PLL_CLK_CTRL1, 0x04); |
| 388 | ad193x->sysclk = 12288000; | ||
| 351 | 389 | ||
| 352 | ret = snd_soc_register_codec(codec); | 390 | ret = snd_soc_register_codec(codec); |
| 353 | if (ret != 0) { | 391 | if (ret != 0) { |
| @@ -383,6 +421,7 @@ static struct snd_soc_dai_ops ad193x_dai_ops = { | |||
| 383 | .hw_params = ad193x_hw_params, | 421 | .hw_params = ad193x_hw_params, |
| 384 | .digital_mute = ad193x_mute, | 422 | .digital_mute = ad193x_mute, |
| 385 | .set_tdm_slot = ad193x_set_tdm_slot, | 423 | .set_tdm_slot = ad193x_set_tdm_slot, |
| 424 | .set_sysclk = ad193x_set_dai_sysclk, | ||
| 386 | .set_fmt = ad193x_set_dai_fmt, | 425 | .set_fmt = ad193x_set_dai_fmt, |
| 387 | }; | 426 | }; |
| 388 | 427 | ||
diff --git a/sound/soc/codecs/ad193x.h b/sound/soc/codecs/ad193x.h index a03c880d52f9..654ba64ae04c 100644 --- a/sound/soc/codecs/ad193x.h +++ b/sound/soc/codecs/ad193x.h | |||
| @@ -11,6 +11,11 @@ | |||
| 11 | 11 | ||
| 12 | #define AD193X_PLL_CLK_CTRL0 0x800 | 12 | #define AD193X_PLL_CLK_CTRL0 0x800 |
| 13 | #define AD193X_PLL_POWERDOWN 0x01 | 13 | #define AD193X_PLL_POWERDOWN 0x01 |
| 14 | #define AD193X_PLL_INPUT_MASK (~0x6) | ||
| 15 | #define AD193X_PLL_INPUT_256 (0 << 1) | ||
| 16 | #define AD193X_PLL_INPUT_384 (1 << 1) | ||
| 17 | #define AD193X_PLL_INPUT_512 (2 << 1) | ||
| 18 | #define AD193X_PLL_INPUT_768 (3 << 1) | ||
| 14 | #define AD193X_PLL_CLK_CTRL1 0x801 | 19 | #define AD193X_PLL_CLK_CTRL1 0x801 |
| 15 | #define AD193X_DAC_CTRL0 0x802 | 20 | #define AD193X_DAC_CTRL0 0x802 |
| 16 | #define AD193X_DAC_POWERDOWN 0x01 | 21 | #define AD193X_DAC_POWERDOWN 0x01 |
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 7528a54102b5..3d7dc55305ec 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c | |||
| @@ -22,20 +22,13 @@ | |||
| 22 | * AK4643 is tested. | 22 | * AK4643 is tested. |
| 23 | */ | 23 | */ |
| 24 | 24 | ||
| 25 | #include <linux/module.h> | ||
| 26 | #include <linux/moduleparam.h> | ||
| 27 | #include <linux/init.h> | ||
| 28 | #include <linux/delay.h> | 25 | #include <linux/delay.h> |
| 29 | #include <linux/pm.h> | ||
| 30 | #include <linux/i2c.h> | 26 | #include <linux/i2c.h> |
| 31 | #include <linux/platform_device.h> | 27 | #include <linux/platform_device.h> |
| 32 | #include <linux/slab.h> | 28 | #include <linux/slab.h> |
| 33 | #include <sound/core.h> | ||
| 34 | #include <sound/pcm.h> | ||
| 35 | #include <sound/pcm_params.h> | ||
| 36 | #include <sound/soc.h> | ||
| 37 | #include <sound/soc-dapm.h> | 29 | #include <sound/soc-dapm.h> |
| 38 | #include <sound/initval.h> | 30 | #include <sound/initval.h> |
| 31 | #include <sound/tlv.h> | ||
| 39 | 32 | ||
| 40 | #include "ak4642.h" | 33 | #include "ak4642.h" |
| 41 | 34 | ||
| @@ -111,6 +104,23 @@ | |||
| 111 | 104 | ||
| 112 | struct snd_soc_codec_device soc_codec_dev_ak4642; | 105 | struct snd_soc_codec_device soc_codec_dev_ak4642; |
| 113 | 106 | ||
| 107 | /* | ||
| 108 | * Playback Volume (table 39) | ||
| 109 | * | ||
| 110 | * max : 0x00 : +12.0 dB | ||
| 111 | * ( 0.5 dB step ) | ||
| 112 | * min : 0xFE : -115.0 dB | ||
| 113 | * mute: 0xFF | ||
| 114 | */ | ||
| 115 | static const DECLARE_TLV_DB_SCALE(out_tlv, -11500, 50, 1); | ||
| 116 | |||
| 117 | static const struct snd_kcontrol_new ak4642_snd_controls[] = { | ||
| 118 | |||
| 119 | SOC_DOUBLE_R_TLV("Digital Playback Volume", L_DVC, R_DVC, | ||
| 120 | 0, 0xFF, 1, out_tlv), | ||
| 121 | }; | ||
| 122 | |||
| 123 | |||
| 114 | /* codec private data */ | 124 | /* codec private data */ |
| 115 | struct ak4642_priv { | 125 | struct ak4642_priv { |
| 116 | struct snd_soc_codec codec; | 126 | struct snd_soc_codec codec; |
| @@ -204,7 +214,6 @@ static int ak4642_dai_startup(struct snd_pcm_substream *substream, | |||
| 204 | * | 214 | * |
| 205 | * PLL, Master Mode | 215 | * PLL, Master Mode |
| 206 | * Audio I/F Format :MSB justified (ADC & DAC) | 216 | * Audio I/F Format :MSB justified (ADC & DAC) |
| 207 | * Digital Volume: -8dB | ||
| 208 | * Bass Boost Level : Middle | 217 | * Bass Boost Level : Middle |
| 209 | * | 218 | * |
| 210 | * This operation came from example code of | 219 | * This operation came from example code of |
| @@ -214,8 +223,6 @@ static int ak4642_dai_startup(struct snd_pcm_substream *substream, | |||
| 214 | ak4642_write(codec, 0x0e, 0x19); | 223 | ak4642_write(codec, 0x0e, 0x19); |
| 215 | ak4642_write(codec, 0x09, 0x91); | 224 | ak4642_write(codec, 0x09, 0x91); |
| 216 | ak4642_write(codec, 0x0c, 0x91); | 225 | ak4642_write(codec, 0x0c, 0x91); |
| 217 | ak4642_write(codec, 0x0a, 0x28); | ||
| 218 | ak4642_write(codec, 0x0d, 0x28); | ||
| 219 | ak4642_write(codec, 0x00, 0x64); | 226 | ak4642_write(codec, 0x00, 0x64); |
| 220 | snd_soc_update_bits(codec, PW_MGMT2, PMHP_MASK, PMHP); | 227 | snd_soc_update_bits(codec, PW_MGMT2, PMHP_MASK, PMHP); |
| 221 | snd_soc_update_bits(codec, PW_MGMT2, HPMTN, HPMTN); | 228 | snd_soc_update_bits(codec, PW_MGMT2, HPMTN, HPMTN); |
| @@ -491,8 +498,10 @@ static int ak4642_i2c_probe(struct i2c_client *i2c, | |||
| 491 | codec->control_data = i2c; | 498 | codec->control_data = i2c; |
| 492 | 499 | ||
| 493 | ret = ak4642_init(ak4642); | 500 | ret = ak4642_init(ak4642); |
| 494 | if (ret < 0) | 501 | if (ret < 0) { |
| 495 | printk(KERN_ERR "failed to initialise AK4642\n"); | 502 | printk(KERN_ERR "failed to initialise AK4642\n"); |
| 503 | kfree(ak4642); | ||
| 504 | } | ||
| 496 | 505 | ||
| 497 | return ret; | 506 | return ret; |
| 498 | } | 507 | } |
| @@ -548,6 +557,9 @@ static int ak4642_probe(struct platform_device *pdev) | |||
| 548 | goto pcm_err; | 557 | goto pcm_err; |
| 549 | } | 558 | } |
| 550 | 559 | ||
| 560 | snd_soc_add_controls(ak4642_codec, ak4642_snd_controls, | ||
| 561 | ARRAY_SIZE(ak4642_snd_controls)); | ||
| 562 | |||
| 551 | dev_info(&pdev->dev, "AK4642 Audio Codec %s", AK4642_VERSION); | 563 | dev_info(&pdev->dev, "AK4642 Audio Codec %s", AK4642_VERSION); |
| 552 | return ret; | 564 | return ret; |
| 553 | 565 | ||
diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c new file mode 100644 index 000000000000..dd9b8550c402 --- /dev/null +++ b/sound/soc/codecs/cs42l51.c | |||
| @@ -0,0 +1,763 @@ | |||
| 1 | /* | ||
| 2 | * cs42l51.c | ||
| 3 | * | ||
| 4 | * ASoC Driver for Cirrus Logic CS42L51 codecs | ||
| 5 | * | ||
| 6 | * Copyright (c) 2010 Arnaud Patard <apatard@mandriva.com> | ||
| 7 | * | ||
| 8 | * Based on cs4270.c - Copyright (c) Freescale Semiconductor | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or modify | ||
| 11 | * it under the terms of the GNU General Public License version 2 as | ||
| 12 | * published by the Free Software Foundation. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, | ||
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | * GNU General Public License for more details. | ||
| 18 | * | ||
| 19 | * For now: | ||
| 20 | * - Only I2C is support. Not SPI | ||
| 21 | * - master mode *NOT* supported | ||
| 22 | */ | ||
| 23 | |||
| 24 | #include <linux/module.h> | ||
| 25 | #include <linux/platform_device.h> | ||
| 26 | #include <linux/slab.h> | ||
| 27 | #include <sound/core.h> | ||
| 28 | #include <sound/soc.h> | ||
| 29 | #include <sound/soc-dapm.h> | ||
| 30 | #include <sound/tlv.h> | ||
| 31 | #include <sound/initval.h> | ||
| 32 | #include <sound/pcm_params.h> | ||
| 33 | #include <sound/pcm.h> | ||
| 34 | #include <linux/i2c.h> | ||
| 35 | |||
| 36 | #include "cs42l51.h" | ||
| 37 | |||
| 38 | enum master_slave_mode { | ||
| 39 | MODE_SLAVE, | ||
| 40 | MODE_SLAVE_AUTO, | ||
| 41 | MODE_MASTER, | ||
| 42 | }; | ||
| 43 | |||
| 44 | struct cs42l51_private { | ||
| 45 | unsigned int mclk; | ||
| 46 | unsigned int audio_mode; /* The mode (I2S or left-justified) */ | ||
| 47 | enum master_slave_mode func; | ||
| 48 | struct snd_soc_codec codec; | ||
| 49 | u8 reg_cache[CS42L51_NUMREGS]; | ||
| 50 | }; | ||
| 51 | |||
| 52 | static struct snd_soc_codec *cs42l51_codec; | ||
| 53 | |||
| 54 | #define CS42L51_FORMATS ( \ | ||
| 55 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ | ||
| 56 | SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \ | ||
| 57 | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \ | ||
| 58 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE) | ||
| 59 | |||
| 60 | static int cs42l51_fill_cache(struct snd_soc_codec *codec) | ||
| 61 | { | ||
| 62 | u8 *cache = codec->reg_cache + 1; | ||
| 63 | struct i2c_client *i2c_client = codec->control_data; | ||
| 64 | s32 length; | ||
| 65 | |||
| 66 | length = i2c_smbus_read_i2c_block_data(i2c_client, | ||
| 67 | CS42L51_FIRSTREG | 0x80, CS42L51_NUMREGS, cache); | ||
| 68 | if (length != CS42L51_NUMREGS) { | ||
| 69 | dev_err(&i2c_client->dev, | ||
| 70 | "I2C read failure, addr=0x%x (ret=%d vs %d)\n", | ||
| 71 | i2c_client->addr, length, CS42L51_NUMREGS); | ||
| 72 | return -EIO; | ||
| 73 | } | ||
| 74 | |||
| 75 | return 0; | ||
| 76 | } | ||
| 77 | |||
| 78 | static int cs42l51_i2c_probe(struct i2c_client *i2c_client, | ||
| 79 | const struct i2c_device_id *id) | ||
| 80 | { | ||
| 81 | struct snd_soc_codec *codec; | ||
| 82 | struct cs42l51_private *cs42l51; | ||
| 83 | int ret = 0; | ||
| 84 | int reg; | ||
| 85 | |||
| 86 | if (cs42l51_codec) | ||
| 87 | return -EBUSY; | ||
| 88 | |||
| 89 | /* Verify that we have a CS42L51 */ | ||
| 90 | ret = i2c_smbus_read_byte_data(i2c_client, CS42L51_CHIP_REV_ID); | ||
| 91 | if (ret < 0) { | ||
| 92 | dev_err(&i2c_client->dev, "failed to read I2C\n"); | ||
| 93 | goto error; | ||
| 94 | } | ||
| 95 | |||
| 96 | if ((ret != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_A)) && | ||
| 97 | (ret != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_B))) { | ||
| 98 | dev_err(&i2c_client->dev, "Invalid chip id\n"); | ||
| 99 | ret = -ENODEV; | ||
| 100 | goto error; | ||
| 101 | } | ||
| 102 | |||
| 103 | dev_info(&i2c_client->dev, "found device cs42l51 rev %d\n", | ||
| 104 | ret & 7); | ||
| 105 | |||
| 106 | cs42l51 = kzalloc(sizeof(struct cs42l51_private), GFP_KERNEL); | ||
| 107 | if (!cs42l51) { | ||
| 108 | dev_err(&i2c_client->dev, "could not allocate codec\n"); | ||
| 109 | return -ENOMEM; | ||
| 110 | } | ||
| 111 | codec = &cs42l51->codec; | ||
| 112 | |||
| 113 | mutex_init(&codec->mutex); | ||
| 114 | INIT_LIST_HEAD(&codec->dapm_widgets); | ||
| 115 | INIT_LIST_HEAD(&codec->dapm_paths); | ||
| 116 | |||
| 117 | codec->dev = &i2c_client->dev; | ||
| 118 | codec->name = "CS42L51"; | ||
| 119 | codec->owner = THIS_MODULE; | ||
| 120 | codec->dai = &cs42l51_dai; | ||
| 121 | codec->num_dai = 1; | ||
| 122 | snd_soc_codec_set_drvdata(codec, cs42l51); | ||
| 123 | |||
| 124 | codec->control_data = i2c_client; | ||
| 125 | codec->reg_cache = cs42l51->reg_cache; | ||
| 126 | codec->reg_cache_size = CS42L51_NUMREGS; | ||
| 127 | i2c_set_clientdata(i2c_client, codec); | ||
| 128 | |||
| 129 | ret = cs42l51_fill_cache(codec); | ||
| 130 | if (ret < 0) { | ||
| 131 | dev_err(&i2c_client->dev, "failed to fill register cache\n"); | ||
| 132 | goto error_alloc; | ||
| 133 | } | ||
| 134 | |||
| 135 | ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C); | ||
| 136 | if (ret < 0) { | ||
| 137 | dev_err(&i2c_client->dev, "Failed to set cache I/O: %d\n", ret); | ||
| 138 | goto error_alloc; | ||
| 139 | } | ||
| 140 | |||
| 141 | /* | ||
| 142 | * DAC configuration | ||
| 143 | * - Use signal processor | ||
| 144 | * - auto mute | ||
| 145 | * - vol changes immediate | ||
| 146 | * - no de-emphasize | ||
| 147 | */ | ||
| 148 | reg = CS42L51_DAC_CTL_DATA_SEL(1) | ||
| 149 | | CS42L51_DAC_CTL_AMUTE | CS42L51_DAC_CTL_DACSZ(0); | ||
| 150 | ret = snd_soc_write(codec, CS42L51_DAC_CTL, reg); | ||
| 151 | if (ret < 0) | ||
| 152 | goto error_alloc; | ||
| 153 | |||
| 154 | cs42l51_dai.dev = codec->dev; | ||
| 155 | cs42l51_codec = codec; | ||
| 156 | |||
| 157 | ret = snd_soc_register_codec(codec); | ||
| 158 | if (ret != 0) { | ||
| 159 | dev_err(codec->dev, "Failed to register codec: %d\n", ret); | ||
| 160 | goto error_alloc; | ||
| 161 | } | ||
| 162 | |||
| 163 | ret = snd_soc_register_dai(&cs42l51_dai); | ||
| 164 | if (ret < 0) { | ||
| 165 | dev_err(&i2c_client->dev, "failed to register DAIe\n"); | ||
| 166 | goto error_reg; | ||
| 167 | } | ||
| 168 | |||
| 169 | return 0; | ||
| 170 | |||
| 171 | error_reg: | ||
| 172 | snd_soc_unregister_codec(codec); | ||
| 173 | error_alloc: | ||
| 174 | kfree(cs42l51); | ||
| 175 | error: | ||
| 176 | return ret; | ||
| 177 | } | ||
| 178 | |||
| 179 | static int cs42l51_i2c_remove(struct i2c_client *client) | ||
| 180 | { | ||
| 181 | struct cs42l51_private *cs42l51 = i2c_get_clientdata(client); | ||
| 182 | snd_soc_unregister_dai(&cs42l51_dai); | ||
| 183 | snd_soc_unregister_codec(cs42l51_codec); | ||
| 184 | cs42l51_codec = NULL; | ||
| 185 | kfree(cs42l51); | ||
| 186 | return 0; | ||
| 187 | } | ||
| 188 | |||
| 189 | |||
| 190 | static const struct i2c_device_id cs42l51_id[] = { | ||
| 191 | {"cs42l51", 0}, | ||
| 192 | {} | ||
| 193 | }; | ||
| 194 | MODULE_DEVICE_TABLE(i2c, cs42l51_id); | ||
| 195 | |||
| 196 | static struct i2c_driver cs42l51_i2c_driver = { | ||
| 197 | .driver = { | ||
| 198 | .name = "CS42L51 I2C", | ||
| 199 | .owner = THIS_MODULE, | ||
| 200 | }, | ||
| 201 | .id_table = cs42l51_id, | ||
| 202 | .probe = cs42l51_i2c_probe, | ||
| 203 | .remove = cs42l51_i2c_remove, | ||
| 204 | }; | ||
| 205 | |||
| 206 | static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol, | ||
| 207 | struct snd_ctl_elem_value *ucontrol) | ||
| 208 | { | ||
| 209 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
| 210 | unsigned long value = snd_soc_read(codec, CS42L51_PCM_MIXER)&3; | ||
| 211 | |||
| 212 | switch (value) { | ||
| 213 | default: | ||
| 214 | case 0: | ||
| 215 | ucontrol->value.integer.value[0] = 0; | ||
| 216 | break; | ||
| 217 | /* same value : (L+R)/2 and (R+L)/2 */ | ||
| 218 | case 1: | ||
| 219 | case 2: | ||
| 220 | ucontrol->value.integer.value[0] = 1; | ||
| 221 | break; | ||
| 222 | case 3: | ||
| 223 | ucontrol->value.integer.value[0] = 2; | ||
| 224 | break; | ||
| 225 | } | ||
| 226 | |||
| 227 | return 0; | ||
| 228 | } | ||
| 229 | |||
| 230 | #define CHAN_MIX_NORMAL 0x00 | ||
| 231 | #define CHAN_MIX_BOTH 0x55 | ||
| 232 | #define CHAN_MIX_SWAP 0xFF | ||
| 233 | |||
| 234 | static int cs42l51_set_chan_mix(struct snd_kcontrol *kcontrol, | ||
| 235 | struct snd_ctl_elem_value *ucontrol) | ||
| 236 | { | ||
| 237 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
| 238 | unsigned char val; | ||
| 239 | |||
| 240 | switch (ucontrol->value.integer.value[0]) { | ||
| 241 | default: | ||
| 242 | case 0: | ||
| 243 | val = CHAN_MIX_NORMAL; | ||
| 244 | break; | ||
| 245 | case 1: | ||
| 246 | val = CHAN_MIX_BOTH; | ||
| 247 | break; | ||
| 248 | case 2: | ||
| 249 | val = CHAN_MIX_SWAP; | ||
| 250 | break; | ||
| 251 | } | ||
| 252 | |||
| 253 | snd_soc_write(codec, CS42L51_PCM_MIXER, val); | ||
| 254 | |||
| 255 | return 1; | ||
| 256 | } | ||
| 257 | |||
| 258 | static const DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -5150, 50, 0); | ||
| 259 | static const DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0); | ||
| 260 | /* This is a lie. after -102 db, it stays at -102 */ | ||
| 261 | /* maybe a range would be better */ | ||
| 262 | static const DECLARE_TLV_DB_SCALE(aout_tlv, -11550, 50, 0); | ||
| 263 | |||
| 264 | static const DECLARE_TLV_DB_SCALE(boost_tlv, 1600, 1600, 0); | ||
| 265 | static const char *chan_mix[] = { | ||
| 266 | "L R", | ||
| 267 | "L+R", | ||
| 268 | "R L", | ||
| 269 | }; | ||
| 270 | |||
| 271 | static const struct soc_enum cs42l51_chan_mix = | ||
| 272 | SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(chan_mix), chan_mix); | ||
| 273 | |||
| 274 | static const struct snd_kcontrol_new cs42l51_snd_controls[] = { | ||
| 275 | SOC_DOUBLE_R_SX_TLV("PCM Playback Volume", | ||
| 276 | CS42L51_PCMA_VOL, CS42L51_PCMB_VOL, | ||
| 277 | 7, 0xffffff99, 0x18, adc_pcm_tlv), | ||
| 278 | SOC_DOUBLE_R("PCM Playback Switch", | ||
| 279 | CS42L51_PCMA_VOL, CS42L51_PCMB_VOL, 7, 1, 1), | ||
| 280 | SOC_DOUBLE_R_SX_TLV("Analog Playback Volume", | ||
| 281 | CS42L51_AOUTA_VOL, CS42L51_AOUTB_VOL, | ||
| 282 | 8, 0xffffff19, 0x18, aout_tlv), | ||
| 283 | SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume", | ||
| 284 | CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, | ||
| 285 | 7, 0xffffff99, 0x18, adc_pcm_tlv), | ||
| 286 | SOC_DOUBLE_R("ADC Mixer Switch", | ||
| 287 | CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, 7, 1, 1), | ||
| 288 | SOC_SINGLE("Playback Deemphasis Switch", CS42L51_DAC_CTL, 3, 1, 0), | ||
| 289 | SOC_SINGLE("Auto-Mute Switch", CS42L51_DAC_CTL, 2, 1, 0), | ||
| 290 | SOC_SINGLE("Soft Ramp Switch", CS42L51_DAC_CTL, 1, 1, 0), | ||
| 291 | SOC_SINGLE("Zero Cross Switch", CS42L51_DAC_CTL, 0, 0, 0), | ||
| 292 | SOC_DOUBLE_TLV("Mic Boost Volume", | ||
| 293 | CS42L51_MIC_CTL, 0, 1, 1, 0, boost_tlv), | ||
| 294 | SOC_SINGLE_TLV("Bass Volume", CS42L51_TONE_CTL, 0, 0xf, 1, tone_tlv), | ||
| 295 | SOC_SINGLE_TLV("Treble Volume", CS42L51_TONE_CTL, 4, 0xf, 1, tone_tlv), | ||
| 296 | SOC_ENUM_EXT("PCM channel mixer", | ||
| 297 | cs42l51_chan_mix, | ||
| 298 | cs42l51_get_chan_mix, cs42l51_set_chan_mix), | ||
| 299 | }; | ||
| 300 | |||
| 301 | /* | ||
| 302 | * to power down, one must: | ||
| 303 | * 1.) Enable the PDN bit | ||
| 304 | * 2.) enable power-down for the select channels | ||
| 305 | * 3.) disable the PDN bit. | ||
| 306 | */ | ||
| 307 | static int cs42l51_pdn_event(struct snd_soc_dapm_widget *w, | ||
| 308 | struct snd_kcontrol *kcontrol, int event) | ||
| 309 | { | ||
| 310 | unsigned long value; | ||
| 311 | |||
| 312 | value = snd_soc_read(w->codec, CS42L51_POWER_CTL1); | ||
| 313 | value &= ~CS42L51_POWER_CTL1_PDN; | ||
| 314 | |||
| 315 | switch (event) { | ||
| 316 | case SND_SOC_DAPM_PRE_PMD: | ||
| 317 | value |= CS42L51_POWER_CTL1_PDN; | ||
| 318 | break; | ||
| 319 | default: | ||
| 320 | case SND_SOC_DAPM_POST_PMD: | ||
| 321 | break; | ||
| 322 | } | ||
| 323 | snd_soc_update_bits(w->codec, CS42L51_POWER_CTL1, | ||
| 324 | CS42L51_POWER_CTL1_PDN, value); | ||
| 325 | |||
| 326 | return 0; | ||
| 327 | } | ||
| 328 | |||
| 329 | static const char *cs42l51_dac_names[] = {"Direct PCM", | ||
| 330 | "DSP PCM", "ADC"}; | ||
| 331 | static const struct soc_enum cs42l51_dac_mux_enum = | ||
| 332 | SOC_ENUM_SINGLE(CS42L51_DAC_CTL, 6, 3, cs42l51_dac_names); | ||
| 333 | static const struct snd_kcontrol_new cs42l51_dac_mux_controls = | ||
| 334 | SOC_DAPM_ENUM("Route", cs42l51_dac_mux_enum); | ||
| 335 | |||
| 336 | static const char *cs42l51_adcl_names[] = {"AIN1 Left", "AIN2 Left", | ||
| 337 | "MIC Left", "MIC+preamp Left"}; | ||
| 338 | static const struct soc_enum cs42l51_adcl_mux_enum = | ||
| 339 | SOC_ENUM_SINGLE(CS42L51_ADC_INPUT, 4, 4, cs42l51_adcl_names); | ||
| 340 | static const struct snd_kcontrol_new cs42l51_adcl_mux_controls = | ||
| 341 | SOC_DAPM_ENUM("Route", cs42l51_adcl_mux_enum); | ||
| 342 | |||
| 343 | static const char *cs42l51_adcr_names[] = {"AIN1 Right", "AIN2 Right", | ||
| 344 | "MIC Right", "MIC+preamp Right"}; | ||
| 345 | static const struct soc_enum cs42l51_adcr_mux_enum = | ||
| 346 | SOC_ENUM_SINGLE(CS42L51_ADC_INPUT, 6, 4, cs42l51_adcr_names); | ||
| 347 | static const struct snd_kcontrol_new cs42l51_adcr_mux_controls = | ||
| 348 | SOC_DAPM_ENUM("Route", cs42l51_adcr_mux_enum); | ||
| 349 | |||
| 350 | static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = { | ||
| 351 | SND_SOC_DAPM_MICBIAS("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1), | ||
| 352 | SND_SOC_DAPM_PGA_E("Left PGA", CS42L51_POWER_CTL1, 3, 1, NULL, 0, | ||
| 353 | cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), | ||
| 354 | SND_SOC_DAPM_PGA_E("Right PGA", CS42L51_POWER_CTL1, 4, 1, NULL, 0, | ||
| 355 | cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), | ||
| 356 | SND_SOC_DAPM_ADC_E("Left ADC", "Left HiFi Capture", | ||
| 357 | CS42L51_POWER_CTL1, 1, 1, | ||
| 358 | cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), | ||
| 359 | SND_SOC_DAPM_ADC_E("Right ADC", "Right HiFi Capture", | ||
| 360 | CS42L51_POWER_CTL1, 2, 1, | ||
| 361 | cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), | ||
| 362 | SND_SOC_DAPM_DAC_E("Left DAC", "Left HiFi Playback", | ||
| 363 | CS42L51_POWER_CTL1, 5, 1, | ||
| 364 | cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), | ||
| 365 | SND_SOC_DAPM_DAC_E("Right DAC", "Right HiFi Playback", | ||
| 366 | CS42L51_POWER_CTL1, 6, 1, | ||
| 367 | cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), | ||
| 368 | |||
| 369 | /* analog/mic */ | ||
| 370 | SND_SOC_DAPM_INPUT("AIN1L"), | ||
| 371 | SND_SOC_DAPM_INPUT("AIN1R"), | ||
| 372 | SND_SOC_DAPM_INPUT("AIN2L"), | ||
| 373 | SND_SOC_DAPM_INPUT("AIN2R"), | ||
| 374 | SND_SOC_DAPM_INPUT("MICL"), | ||
| 375 | SND_SOC_DAPM_INPUT("MICR"), | ||
| 376 | |||
| 377 | SND_SOC_DAPM_MIXER("Mic Preamp Left", | ||
| 378 | CS42L51_MIC_POWER_CTL, 2, 1, NULL, 0), | ||
| 379 | SND_SOC_DAPM_MIXER("Mic Preamp Right", | ||
| 380 | CS42L51_MIC_POWER_CTL, 3, 1, NULL, 0), | ||
| 381 | |||
| 382 | /* HP */ | ||
| 383 | SND_SOC_DAPM_OUTPUT("HPL"), | ||
| 384 | SND_SOC_DAPM_OUTPUT("HPR"), | ||
| 385 | |||
| 386 | /* mux */ | ||
| 387 | SND_SOC_DAPM_MUX("DAC Mux", SND_SOC_NOPM, 0, 0, | ||
| 388 | &cs42l51_dac_mux_controls), | ||
| 389 | SND_SOC_DAPM_MUX("PGA-ADC Mux Left", SND_SOC_NOPM, 0, 0, | ||
| 390 | &cs42l51_adcl_mux_controls), | ||
| 391 | SND_SOC_DAPM_MUX("PGA-ADC Mux Right", SND_SOC_NOPM, 0, 0, | ||
| 392 | &cs42l51_adcr_mux_controls), | ||
| 393 | }; | ||
| 394 | |||
| 395 | static const struct snd_soc_dapm_route cs42l51_routes[] = { | ||
| 396 | {"HPL", NULL, "Left DAC"}, | ||
| 397 | {"HPR", NULL, "Right DAC"}, | ||
| 398 | |||
| 399 | {"Left ADC", NULL, "Left PGA"}, | ||
| 400 | {"Right ADC", NULL, "Right PGA"}, | ||
| 401 | |||
| 402 | {"Mic Preamp Left", NULL, "MICL"}, | ||
| 403 | {"Mic Preamp Right", NULL, "MICR"}, | ||
| 404 | |||
| 405 | {"PGA-ADC Mux Left", "AIN1 Left", "AIN1L" }, | ||
| 406 | {"PGA-ADC Mux Left", "AIN2 Left", "AIN2L" }, | ||
| 407 | {"PGA-ADC Mux Left", "MIC Left", "MICL" }, | ||
| 408 | {"PGA-ADC Mux Left", "MIC+preamp Left", "Mic Preamp Left" }, | ||
| 409 | {"PGA-ADC Mux Right", "AIN1 Right", "AIN1R" }, | ||
| 410 | {"PGA-ADC Mux Right", "AIN2 Right", "AIN2R" }, | ||
| 411 | {"PGA-ADC Mux Right", "MIC Right", "MICR" }, | ||
| 412 | {"PGA-ADC Mux Right", "MIC+preamp Right", "Mic Preamp Right" }, | ||
| 413 | |||
| 414 | {"Left PGA", NULL, "PGA-ADC Mux Left"}, | ||
| 415 | {"Right PGA", NULL, "PGA-ADC Mux Right"}, | ||
| 416 | }; | ||
| 417 | |||
| 418 | static int cs42l51_set_dai_fmt(struct snd_soc_dai *codec_dai, | ||
| 419 | unsigned int format) | ||
| 420 | { | ||
| 421 | struct snd_soc_codec *codec = codec_dai->codec; | ||
| 422 | struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec); | ||
| 423 | int ret = 0; | ||
| 424 | |||
| 425 | switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
| 426 | case SND_SOC_DAIFMT_I2S: | ||
| 427 | case SND_SOC_DAIFMT_LEFT_J: | ||
| 428 | case SND_SOC_DAIFMT_RIGHT_J: | ||
| 429 | cs42l51->audio_mode = format & SND_SOC_DAIFMT_FORMAT_MASK; | ||
| 430 | break; | ||
| 431 | default: | ||
| 432 | dev_err(codec->dev, "invalid DAI format\n"); | ||
| 433 | ret = -EINVAL; | ||
| 434 | } | ||
| 435 | |||
| 436 | switch (format & SND_SOC_DAIFMT_MASTER_MASK) { | ||
| 437 | case SND_SOC_DAIFMT_CBM_CFM: | ||
| 438 | cs42l51->func = MODE_MASTER; | ||
| 439 | break; | ||
| 440 | case SND_SOC_DAIFMT_CBS_CFS: | ||
| 441 | cs42l51->func = MODE_SLAVE_AUTO; | ||
| 442 | break; | ||
| 443 | default: | ||
| 444 | ret = -EINVAL; | ||
| 445 | break; | ||
| 446 | } | ||
| 447 | |||
| 448 | return ret; | ||
| 449 | } | ||
| 450 | |||
| 451 | struct cs42l51_ratios { | ||
| 452 | unsigned int ratio; | ||
| 453 | unsigned char speed_mode; | ||
| 454 | unsigned char mclk; | ||
| 455 | }; | ||
| 456 | |||
| 457 | static struct cs42l51_ratios slave_ratios[] = { | ||
| 458 | { 512, CS42L51_QSM_MODE, 0 }, { 768, CS42L51_QSM_MODE, 0 }, | ||
| 459 | { 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 }, | ||
| 460 | { 2048, CS42L51_QSM_MODE, 0 }, { 3072, CS42L51_QSM_MODE, 0 }, | ||
| 461 | { 256, CS42L51_HSM_MODE, 0 }, { 384, CS42L51_HSM_MODE, 0 }, | ||
| 462 | { 512, CS42L51_HSM_MODE, 0 }, { 768, CS42L51_HSM_MODE, 0 }, | ||
| 463 | { 1024, CS42L51_HSM_MODE, 0 }, { 1536, CS42L51_HSM_MODE, 0 }, | ||
| 464 | { 128, CS42L51_SSM_MODE, 0 }, { 192, CS42L51_SSM_MODE, 0 }, | ||
| 465 | { 256, CS42L51_SSM_MODE, 0 }, { 384, CS42L51_SSM_MODE, 0 }, | ||
| 466 | { 512, CS42L51_SSM_MODE, 0 }, { 768, CS42L51_SSM_MODE, 0 }, | ||
| 467 | { 128, CS42L51_DSM_MODE, 0 }, { 192, CS42L51_DSM_MODE, 0 }, | ||
| 468 | { 256, CS42L51_DSM_MODE, 0 }, { 384, CS42L51_DSM_MODE, 0 }, | ||
| 469 | }; | ||
| 470 | |||
| 471 | static struct cs42l51_ratios slave_auto_ratios[] = { | ||
| 472 | { 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 }, | ||
| 473 | { 2048, CS42L51_QSM_MODE, 1 }, { 3072, CS42L51_QSM_MODE, 1 }, | ||
| 474 | { 512, CS42L51_HSM_MODE, 0 }, { 768, CS42L51_HSM_MODE, 0 }, | ||
| 475 | { 1024, CS42L51_HSM_MODE, 1 }, { 1536, CS42L51_HSM_MODE, 1 }, | ||
| 476 | { 256, CS42L51_SSM_MODE, 0 }, { 384, CS42L51_SSM_MODE, 0 }, | ||
| 477 | { 512, CS42L51_SSM_MODE, 1 }, { 768, CS42L51_SSM_MODE, 1 }, | ||
| 478 | { 128, CS42L51_DSM_MODE, 0 }, { 192, CS42L51_DSM_MODE, 0 }, | ||
| 479 | { 256, CS42L51_DSM_MODE, 1 }, { 384, CS42L51_DSM_MODE, 1 }, | ||
| 480 | }; | ||
| 481 | |||
| 482 | static int cs42l51_set_dai_sysclk(struct snd_soc_dai *codec_dai, | ||
| 483 | int clk_id, unsigned int freq, int dir) | ||
| 484 | { | ||
| 485 | struct snd_soc_codec *codec = codec_dai->codec; | ||
| 486 | struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec); | ||
| 487 | struct cs42l51_ratios *ratios = NULL; | ||
| 488 | int nr_ratios = 0; | ||
| 489 | unsigned int rates = 0; | ||
| 490 | unsigned int rate_min = -1; | ||
| 491 | unsigned int rate_max = 0; | ||
| 492 | int i; | ||
| 493 | |||
| 494 | cs42l51->mclk = freq; | ||
| 495 | |||
| 496 | switch (cs42l51->func) { | ||
| 497 | case MODE_MASTER: | ||
| 498 | return -EINVAL; | ||
| 499 | case MODE_SLAVE: | ||
| 500 | ratios = slave_ratios; | ||
| 501 | nr_ratios = ARRAY_SIZE(slave_ratios); | ||
| 502 | break; | ||
| 503 | case MODE_SLAVE_AUTO: | ||
| 504 | ratios = slave_auto_ratios; | ||
| 505 | nr_ratios = ARRAY_SIZE(slave_auto_ratios); | ||
| 506 | break; | ||
| 507 | } | ||
| 508 | |||
| 509 | for (i = 0; i < nr_ratios; i++) { | ||
| 510 | unsigned int rate = freq / ratios[i].ratio; | ||
| 511 | rates |= snd_pcm_rate_to_rate_bit(rate); | ||
| 512 | if (rate < rate_min) | ||
| 513 | rate_min = rate; | ||
| 514 | if (rate > rate_max) | ||
| 515 | rate_max = rate; | ||
| 516 | } | ||
| 517 | rates &= ~SNDRV_PCM_RATE_KNOT; | ||
| 518 | |||
| 519 | if (!rates) { | ||
| 520 | dev_err(codec->dev, "could not find a valid sample rate\n"); | ||
| 521 | return -EINVAL; | ||
| 522 | } | ||
| 523 | |||
| 524 | codec_dai->playback.rates = rates; | ||
| 525 | codec_dai->playback.rate_min = rate_min; | ||
| 526 | codec_dai->playback.rate_max = rate_max; | ||
| 527 | |||
| 528 | codec_dai->capture.rates = rates; | ||
| 529 | codec_dai->capture.rate_min = rate_min; | ||
| 530 | codec_dai->capture.rate_max = rate_max; | ||
| 531 | |||
| 532 | return 0; | ||
| 533 | } | ||
| 534 | |||
| 535 | static int cs42l51_hw_params(struct snd_pcm_substream *substream, | ||
| 536 | struct snd_pcm_hw_params *params, | ||
| 537 | struct snd_soc_dai *dai) | ||
| 538 | { | ||
| 539 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
| 540 | struct snd_soc_device *socdev = rtd->socdev; | ||
| 541 | struct snd_soc_codec *codec = socdev->card->codec; | ||
| 542 | struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec); | ||
| 543 | int ret; | ||
| 544 | unsigned int i; | ||
| 545 | unsigned int rate; | ||
| 546 | unsigned int ratio; | ||
| 547 | struct cs42l51_ratios *ratios = NULL; | ||
| 548 | int nr_ratios = 0; | ||
| 549 | int intf_ctl, power_ctl, fmt; | ||
| 550 | |||
| 551 | switch (cs42l51->func) { | ||
| 552 | case MODE_MASTER: | ||
| 553 | return -EINVAL; | ||
| 554 | case MODE_SLAVE: | ||
| 555 | ratios = slave_ratios; | ||
| 556 | nr_ratios = ARRAY_SIZE(slave_ratios); | ||
| 557 | break; | ||
| 558 | case MODE_SLAVE_AUTO: | ||
| 559 | ratios = slave_auto_ratios; | ||
| 560 | nr_ratios = ARRAY_SIZE(slave_auto_ratios); | ||
| 561 | break; | ||
| 562 | } | ||
| 563 | |||
| 564 | /* Figure out which MCLK/LRCK ratio to use */ | ||
| 565 | rate = params_rate(params); /* Sampling rate, in Hz */ | ||
| 566 | ratio = cs42l51->mclk / rate; /* MCLK/LRCK ratio */ | ||
| 567 | for (i = 0; i < nr_ratios; i++) { | ||
| 568 | if (ratios[i].ratio == ratio) | ||
| 569 | break; | ||
| 570 | } | ||
| 571 | |||
| 572 | if (i == nr_ratios) { | ||
| 573 | /* We did not find a matching ratio */ | ||
| 574 | dev_err(codec->dev, "could not find matching ratio\n"); | ||
| 575 | return -EINVAL; | ||
| 576 | } | ||
| 577 | |||
| 578 | intf_ctl = snd_soc_read(codec, CS42L51_INTF_CTL); | ||
| 579 | power_ctl = snd_soc_read(codec, CS42L51_MIC_POWER_CTL); | ||
| 580 | |||
| 581 | intf_ctl &= ~(CS42L51_INTF_CTL_MASTER | CS42L51_INTF_CTL_ADC_I2S | ||
| 582 | | CS42L51_INTF_CTL_DAC_FORMAT(7)); | ||
| 583 | power_ctl &= ~(CS42L51_MIC_POWER_CTL_SPEED(3) | ||
| 584 | | CS42L51_MIC_POWER_CTL_MCLK_DIV2); | ||
| 585 | |||
| 586 | switch (cs42l51->func) { | ||
| 587 | case MODE_MASTER: | ||
| 588 | intf_ctl |= CS42L51_INTF_CTL_MASTER; | ||
| 589 | power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode); | ||
| 590 | break; | ||
| 591 | case MODE_SLAVE: | ||
| 592 | power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode); | ||
| 593 | break; | ||
| 594 | case MODE_SLAVE_AUTO: | ||
| 595 | power_ctl |= CS42L51_MIC_POWER_CTL_AUTO; | ||
| 596 | break; | ||
| 597 | } | ||
| 598 | |||
| 599 | switch (cs42l51->audio_mode) { | ||
| 600 | case SND_SOC_DAIFMT_I2S: | ||
| 601 | intf_ctl |= CS42L51_INTF_CTL_ADC_I2S; | ||
| 602 | intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_I2S); | ||
| 603 | break; | ||
| 604 | case SND_SOC_DAIFMT_LEFT_J: | ||
| 605 | intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_LJ24); | ||
| 606 | break; | ||
| 607 | case SND_SOC_DAIFMT_RIGHT_J: | ||
| 608 | switch (params_format(params)) { | ||
| 609 | case SNDRV_PCM_FORMAT_S16_LE: | ||
| 610 | case SNDRV_PCM_FORMAT_S16_BE: | ||
| 611 | fmt = CS42L51_DAC_DIF_RJ16; | ||
| 612 | break; | ||
| 613 | case SNDRV_PCM_FORMAT_S18_3LE: | ||
| 614 | case SNDRV_PCM_FORMAT_S18_3BE: | ||
| 615 | fmt = CS42L51_DAC_DIF_RJ18; | ||
| 616 | break; | ||
| 617 | case SNDRV_PCM_FORMAT_S20_3LE: | ||
| 618 | case SNDRV_PCM_FORMAT_S20_3BE: | ||
| 619 | fmt = CS42L51_DAC_DIF_RJ20; | ||
| 620 | break; | ||
| 621 | case SNDRV_PCM_FORMAT_S24_LE: | ||
| 622 | case SNDRV_PCM_FORMAT_S24_BE: | ||
| 623 | fmt = CS42L51_DAC_DIF_RJ24; | ||
| 624 | break; | ||
| 625 | default: | ||
| 626 | dev_err(codec->dev, "unknown format\n"); | ||
| 627 | return -EINVAL; | ||
| 628 | } | ||
| 629 | intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(fmt); | ||
| 630 | break; | ||
| 631 | default: | ||
| 632 | dev_err(codec->dev, "unknown format\n"); | ||
| 633 | return -EINVAL; | ||
| 634 | } | ||
| 635 | |||
| 636 | if (ratios[i].mclk) | ||
| 637 | power_ctl |= CS42L51_MIC_POWER_CTL_MCLK_DIV2; | ||
| 638 | |||
| 639 | ret = snd_soc_write(codec, CS42L51_INTF_CTL, intf_ctl); | ||
| 640 | if (ret < 0) | ||
| 641 | return ret; | ||
| 642 | |||
| 643 | ret = snd_soc_write(codec, CS42L51_MIC_POWER_CTL, power_ctl); | ||
| 644 | if (ret < 0) | ||
| 645 | return ret; | ||
| 646 | |||
| 647 | return 0; | ||
| 648 | } | ||
| 649 | |||
| 650 | static int cs42l51_dai_mute(struct snd_soc_dai *dai, int mute) | ||
| 651 | { | ||
| 652 | struct snd_soc_codec *codec = dai->codec; | ||
| 653 | int reg; | ||
| 654 | int mask = CS42L51_DAC_OUT_CTL_DACA_MUTE|CS42L51_DAC_OUT_CTL_DACB_MUTE; | ||
| 655 | |||
| 656 | reg = snd_soc_read(codec, CS42L51_DAC_OUT_CTL); | ||
| 657 | |||
| 658 | if (mute) | ||
| 659 | reg |= mask; | ||
| 660 | else | ||
| 661 | reg &= ~mask; | ||
| 662 | |||
| 663 | return snd_soc_write(codec, CS42L51_DAC_OUT_CTL, reg); | ||
| 664 | } | ||
| 665 | |||
| 666 | static struct snd_soc_dai_ops cs42l51_dai_ops = { | ||
| 667 | .hw_params = cs42l51_hw_params, | ||
| 668 | .set_sysclk = cs42l51_set_dai_sysclk, | ||
| 669 | .set_fmt = cs42l51_set_dai_fmt, | ||
| 670 | .digital_mute = cs42l51_dai_mute, | ||
| 671 | }; | ||
| 672 | |||
| 673 | struct snd_soc_dai cs42l51_dai = { | ||
| 674 | .name = "CS42L51 HiFi", | ||
| 675 | .playback = { | ||
| 676 | .stream_name = "Playback", | ||
| 677 | .channels_min = 1, | ||
| 678 | .channels_max = 2, | ||
| 679 | .rates = SNDRV_PCM_RATE_8000_96000, | ||
| 680 | .formats = CS42L51_FORMATS, | ||
| 681 | }, | ||
| 682 | .capture = { | ||
| 683 | .stream_name = "Capture", | ||
| 684 | .channels_min = 1, | ||
| 685 | .channels_max = 2, | ||
| 686 | .rates = SNDRV_PCM_RATE_8000_96000, | ||
| 687 | .formats = CS42L51_FORMATS, | ||
| 688 | }, | ||
| 689 | .ops = &cs42l51_dai_ops, | ||
| 690 | }; | ||
| 691 | EXPORT_SYMBOL_GPL(cs42l51_dai); | ||
| 692 | |||
| 693 | |||
| 694 | static int cs42l51_probe(struct platform_device *pdev) | ||
| 695 | { | ||
| 696 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
| 697 | struct snd_soc_codec *codec; | ||
| 698 | int ret = 0; | ||
| 699 | |||
| 700 | if (!cs42l51_codec) { | ||
| 701 | dev_err(&pdev->dev, "CS42L51 codec not yet registered\n"); | ||
| 702 | return -EINVAL; | ||
| 703 | } | ||
| 704 | |||
| 705 | socdev->card->codec = cs42l51_codec; | ||
| 706 | codec = socdev->card->codec; | ||
| 707 | |||
| 708 | /* Register PCMs */ | ||
| 709 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | ||
| 710 | if (ret < 0) { | ||
| 711 | dev_err(&pdev->dev, "failed to create PCMs\n"); | ||
| 712 | return ret; | ||
| 713 | } | ||
| 714 | |||
| 715 | snd_soc_add_controls(codec, cs42l51_snd_controls, | ||
| 716 | ARRAY_SIZE(cs42l51_snd_controls)); | ||
| 717 | snd_soc_dapm_new_controls(codec, cs42l51_dapm_widgets, | ||
| 718 | ARRAY_SIZE(cs42l51_dapm_widgets)); | ||
| 719 | snd_soc_dapm_add_routes(codec, cs42l51_routes, | ||
| 720 | ARRAY_SIZE(cs42l51_routes)); | ||
| 721 | |||
| 722 | return 0; | ||
| 723 | } | ||
| 724 | |||
| 725 | |||
| 726 | static int cs42l51_remove(struct platform_device *pdev) | ||
| 727 | { | ||
| 728 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
| 729 | |||
| 730 | snd_soc_free_pcms(socdev); | ||
| 731 | snd_soc_dapm_free(socdev); | ||
| 732 | |||
| 733 | return 0; | ||
| 734 | } | ||
| 735 | |||
| 736 | struct snd_soc_codec_device soc_codec_device_cs42l51 = { | ||
| 737 | .probe = cs42l51_probe, | ||
| 738 | .remove = cs42l51_remove | ||
| 739 | }; | ||
| 740 | EXPORT_SYMBOL_GPL(soc_codec_device_cs42l51); | ||
| 741 | |||
| 742 | static int __init cs42l51_init(void) | ||
| 743 | { | ||
| 744 | int ret; | ||
| 745 | |||
| 746 | ret = i2c_add_driver(&cs42l51_i2c_driver); | ||
| 747 | if (ret != 0) { | ||
| 748 | printk(KERN_ERR "%s: can't add i2c driver\n", __func__); | ||
| 749 | return ret; | ||
| 750 | } | ||
| 751 | return 0; | ||
| 752 | } | ||
| 753 | module_init(cs42l51_init); | ||
| 754 | |||
| 755 | static void __exit cs42l51_exit(void) | ||
| 756 | { | ||
| 757 | i2c_del_driver(&cs42l51_i2c_driver); | ||
| 758 | } | ||
| 759 | module_exit(cs42l51_exit); | ||
| 760 | |||
| 761 | MODULE_AUTHOR("Arnaud Patard <apatard@mandriva.com>"); | ||
| 762 | MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver"); | ||
| 763 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/cs42l51.h b/sound/soc/codecs/cs42l51.h new file mode 100644 index 000000000000..8f0bd9786ad2 --- /dev/null +++ b/sound/soc/codecs/cs42l51.h | |||
| @@ -0,0 +1,163 @@ | |||
| 1 | /* | ||
| 2 | * cs42l51.h | ||
| 3 | * | ||
| 4 | * ASoC Driver for Cirrus Logic CS42L51 codecs | ||
| 5 | * | ||
| 6 | * Copyright (c) 2010 Arnaud Patard <apatard@mandriva.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License, or | ||
| 11 | * (at your option) any later version. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU General Public License for more details. | ||
| 17 | */ | ||
| 18 | #ifndef _CS42L51_H | ||
| 19 | #define _CS42L51_H | ||
| 20 | |||
| 21 | #define CS42L51_CHIP_ID 0x1B | ||
| 22 | #define CS42L51_CHIP_REV_A 0x00 | ||
| 23 | #define CS42L51_CHIP_REV_B 0x01 | ||
| 24 | |||
| 25 | #define CS42L51_CHIP_REV_ID 0x01 | ||
| 26 | #define CS42L51_MK_CHIP_REV(a, b) ((a)<<3|(b)) | ||
| 27 | |||
| 28 | #define CS42L51_POWER_CTL1 0x02 | ||
| 29 | #define CS42L51_POWER_CTL1_PDN_DACB (1<<6) | ||
| 30 | #define CS42L51_POWER_CTL1_PDN_DACA (1<<5) | ||
| 31 | #define CS42L51_POWER_CTL1_PDN_PGAB (1<<4) | ||
| 32 | #define CS42L51_POWER_CTL1_PDN_PGAA (1<<3) | ||
| 33 | #define CS42L51_POWER_CTL1_PDN_ADCB (1<<2) | ||
| 34 | #define CS42L51_POWER_CTL1_PDN_ADCA (1<<1) | ||
| 35 | #define CS42L51_POWER_CTL1_PDN (1<<0) | ||
| 36 | |||
| 37 | #define CS42L51_MIC_POWER_CTL 0x03 | ||
| 38 | #define CS42L51_MIC_POWER_CTL_AUTO (1<<7) | ||
| 39 | #define CS42L51_MIC_POWER_CTL_SPEED(x) (((x)&3)<<5) | ||
| 40 | #define CS42L51_QSM_MODE 3 | ||
| 41 | #define CS42L51_HSM_MODE 2 | ||
| 42 | #define CS42L51_SSM_MODE 1 | ||
| 43 | #define CS42L51_DSM_MODE 0 | ||
| 44 | #define CS42L51_MIC_POWER_CTL_3ST_SP (1<<4) | ||
| 45 | #define CS42L51_MIC_POWER_CTL_PDN_MICB (1<<3) | ||
| 46 | #define CS42L51_MIC_POWER_CTL_PDN_MICA (1<<2) | ||
| 47 | #define CS42L51_MIC_POWER_CTL_PDN_BIAS (1<<1) | ||
| 48 | #define CS42L51_MIC_POWER_CTL_MCLK_DIV2 (1<<0) | ||
| 49 | |||
| 50 | #define CS42L51_INTF_CTL 0x04 | ||
| 51 | #define CS42L51_INTF_CTL_LOOPBACK (1<<7) | ||
| 52 | #define CS42L51_INTF_CTL_MASTER (1<<6) | ||
| 53 | #define CS42L51_INTF_CTL_DAC_FORMAT(x) (((x)&7)<<3) | ||
| 54 | #define CS42L51_DAC_DIF_LJ24 0x00 | ||
| 55 | #define CS42L51_DAC_DIF_I2S 0x01 | ||
| 56 | #define CS42L51_DAC_DIF_RJ24 0x02 | ||
| 57 | #define CS42L51_DAC_DIF_RJ20 0x03 | ||
| 58 | #define CS42L51_DAC_DIF_RJ18 0x04 | ||
| 59 | #define CS42L51_DAC_DIF_RJ16 0x05 | ||
| 60 | #define CS42L51_INTF_CTL_ADC_I2S (1<<2) | ||
| 61 | #define CS42L51_INTF_CTL_DIGMIX (1<<1) | ||
| 62 | #define CS42L51_INTF_CTL_MICMIX (1<<0) | ||
| 63 | |||
| 64 | #define CS42L51_MIC_CTL 0x05 | ||
| 65 | #define CS42L51_MIC_CTL_ADC_SNGVOL (1<<7) | ||
| 66 | #define CS42L51_MIC_CTL_ADCD_DBOOST (1<<6) | ||
| 67 | #define CS42L51_MIC_CTL_ADCA_DBOOST (1<<5) | ||
| 68 | #define CS42L51_MIC_CTL_MICBIAS_SEL (1<<4) | ||
| 69 | #define CS42L51_MIC_CTL_MICBIAS_LVL(x) (((x)&3)<<2) | ||
| 70 | #define CS42L51_MIC_CTL_MICB_BOOST (1<<1) | ||
| 71 | #define CS42L51_MIC_CTL_MICA_BOOST (1<<0) | ||
| 72 | |||
| 73 | #define CS42L51_ADC_CTL 0x06 | ||
| 74 | #define CS42L51_ADC_CTL_ADCB_HPFEN (1<<7) | ||
| 75 | #define CS42L51_ADC_CTL_ADCB_HPFRZ (1<<6) | ||
| 76 | #define CS42L51_ADC_CTL_ADCA_HPFEN (1<<5) | ||
| 77 | #define CS42L51_ADC_CTL_ADCA_HPFRZ (1<<4) | ||
| 78 | #define CS42L51_ADC_CTL_SOFTB (1<<3) | ||
| 79 | #define CS42L51_ADC_CTL_ZCROSSB (1<<2) | ||
| 80 | #define CS42L51_ADC_CTL_SOFTA (1<<1) | ||
| 81 | #define CS42L51_ADC_CTL_ZCROSSA (1<<0) | ||
| 82 | |||
| 83 | #define CS42L51_ADC_INPUT 0x07 | ||
| 84 | #define CS42L51_ADC_INPUT_AINB_MUX(x) (((x)&3)<<6) | ||
| 85 | #define CS42L51_ADC_INPUT_AINA_MUX(x) (((x)&3)<<4) | ||
| 86 | #define CS42L51_ADC_INPUT_INV_ADCB (1<<3) | ||
| 87 | #define CS42L51_ADC_INPUT_INV_ADCA (1<<2) | ||
| 88 | #define CS42L51_ADC_INPUT_ADCB_MUTE (1<<1) | ||
| 89 | #define CS42L51_ADC_INPUT_ADCA_MUTE (1<<0) | ||
| 90 | |||
| 91 | #define CS42L51_DAC_OUT_CTL 0x08 | ||
| 92 | #define CS42L51_DAC_OUT_CTL_HP_GAIN(x) (((x)&7)<<5) | ||
| 93 | #define CS42L51_DAC_OUT_CTL_DAC_SNGVOL (1<<4) | ||
| 94 | #define CS42L51_DAC_OUT_CTL_INV_PCMB (1<<3) | ||
| 95 | #define CS42L51_DAC_OUT_CTL_INV_PCMA (1<<2) | ||
| 96 | #define CS42L51_DAC_OUT_CTL_DACB_MUTE (1<<1) | ||
| 97 | #define CS42L51_DAC_OUT_CTL_DACA_MUTE (1<<0) | ||
| 98 | |||
| 99 | #define CS42L51_DAC_CTL 0x09 | ||
| 100 | #define CS42L51_DAC_CTL_DATA_SEL(x) (((x)&3)<<6) | ||
| 101 | #define CS42L51_DAC_CTL_FREEZE (1<<5) | ||
| 102 | #define CS42L51_DAC_CTL_DEEMPH (1<<3) | ||
| 103 | #define CS42L51_DAC_CTL_AMUTE (1<<2) | ||
| 104 | #define CS42L51_DAC_CTL_DACSZ(x) (((x)&3)<<0) | ||
| 105 | |||
| 106 | #define CS42L51_ALC_PGA_CTL 0x0A | ||
| 107 | #define CS42L51_ALC_PGB_CTL 0x0B | ||
| 108 | #define CS42L51_ALC_PGX_ALCX_SRDIS (1<<7) | ||
| 109 | #define CS42L51_ALC_PGX_ALCX_ZCDIS (1<<6) | ||
| 110 | #define CS42L51_ALC_PGX_PGX_VOL(x) (((x)&0x1f)<<0) | ||
| 111 | |||
| 112 | #define CS42L51_ADCA_ATT 0x0C | ||
| 113 | #define CS42L51_ADCB_ATT 0x0D | ||
| 114 | |||
| 115 | #define CS42L51_ADCA_VOL 0x0E | ||
| 116 | #define CS42L51_ADCB_VOL 0x0F | ||
| 117 | #define CS42L51_PCMA_VOL 0x10 | ||
| 118 | #define CS42L51_PCMB_VOL 0x11 | ||
| 119 | #define CS42L51_MIX_MUTE_ADCMIX (1<<7) | ||
| 120 | #define CS42L51_MIX_VOLUME(x) (((x)&0x7f)<<0) | ||
| 121 | |||
| 122 | #define CS42L51_BEEP_FREQ 0x12 | ||
| 123 | #define CS42L51_BEEP_VOL 0x13 | ||
| 124 | #define CS42L51_BEEP_CONF 0x14 | ||
| 125 | |||
| 126 | #define CS42L51_TONE_CTL 0x15 | ||
| 127 | #define CS42L51_TONE_CTL_TREB(x) (((x)&0xf)<<4) | ||
| 128 | #define CS42L51_TONE_CTL_BASS(x) (((x)&0xf)<<0) | ||
| 129 | |||
| 130 | #define CS42L51_AOUTA_VOL 0x16 | ||
| 131 | #define CS42L51_AOUTB_VOL 0x17 | ||
| 132 | #define CS42L51_PCM_MIXER 0x18 | ||
| 133 | #define CS42L51_LIMIT_THRES_DIS 0x19 | ||
| 134 | #define CS42L51_LIMIT_REL 0x1A | ||
| 135 | #define CS42L51_LIMIT_ATT 0x1B | ||
| 136 | #define CS42L51_ALC_EN 0x1C | ||
| 137 | #define CS42L51_ALC_REL 0x1D | ||
| 138 | #define CS42L51_ALC_THRES 0x1E | ||
| 139 | #define CS42L51_NOISE_CONF 0x1F | ||
| 140 | |||
| 141 | #define CS42L51_STATUS 0x20 | ||
| 142 | #define CS42L51_STATUS_SP_CLKERR (1<<6) | ||
| 143 | #define CS42L51_STATUS_SPEA_OVFL (1<<5) | ||
| 144 | #define CS42L51_STATUS_SPEB_OVFL (1<<4) | ||
| 145 | #define CS42L51_STATUS_PCMA_OVFL (1<<3) | ||
| 146 | #define CS42L51_STATUS_PCMB_OVFL (1<<2) | ||
| 147 | #define CS42L51_STATUS_ADCA_OVFL (1<<1) | ||
| 148 | #define CS42L51_STATUS_ADCB_OVFL (1<<0) | ||
| 149 | |||
| 150 | #define CS42L51_CHARGE_FREQ 0x21 | ||
| 151 | |||
| 152 | #define CS42L51_FIRSTREG 0x01 | ||
| 153 | /* | ||
| 154 | * Hack: with register 0x21, it makes 33 registers. Looks like someone in the | ||
| 155 | * i2c layer doesn't like i2c smbus block read of 33 regs. Workaround by using | ||
| 156 | * 32 regs | ||
| 157 | */ | ||
| 158 | #define CS42L51_LASTREG 0x20 | ||
| 159 | #define CS42L51_NUMREGS (CS42L51_LASTREG - CS42L51_FIRSTREG + 1) | ||
| 160 | |||
| 161 | extern struct snd_soc_dai cs42l51_dai; | ||
| 162 | extern struct snd_soc_codec_device soc_codec_device_cs42l51; | ||
| 163 | #endif | ||
diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c index 75af2d6e0e78..3c51d6a57523 100644 --- a/sound/soc/codecs/da7210.c +++ b/sound/soc/codecs/da7210.c | |||
| @@ -15,23 +15,15 @@ | |||
| 15 | * option) any later version. | 15 | * option) any later version. |
| 16 | */ | 16 | */ |
| 17 | 17 | ||
| 18 | #include <linux/module.h> | ||
| 19 | #include <linux/moduleparam.h> | ||
| 20 | #include <linux/kernel.h> | ||
| 21 | #include <linux/init.h> | ||
| 22 | #include <linux/delay.h> | 18 | #include <linux/delay.h> |
| 23 | #include <linux/pm.h> | ||
| 24 | #include <linux/i2c.h> | 19 | #include <linux/i2c.h> |
| 25 | #include <linux/platform_device.h> | 20 | #include <linux/platform_device.h> |
| 26 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
| 27 | #include <sound/core.h> | ||
| 28 | #include <sound/pcm.h> | 22 | #include <sound/pcm.h> |
| 29 | #include <sound/pcm_params.h> | 23 | #include <sound/pcm_params.h> |
| 30 | #include <sound/soc.h> | ||
| 31 | #include <sound/soc-dapm.h> | 24 | #include <sound/soc-dapm.h> |
| 32 | #include <sound/tlv.h> | ||
| 33 | #include <sound/initval.h> | 25 | #include <sound/initval.h> |
| 34 | #include <asm/div64.h> | 26 | #include <sound/tlv.h> |
| 35 | 27 | ||
| 36 | #include "da7210.h" | 28 | #include "da7210.h" |
| 37 | 29 | ||
| @@ -145,6 +137,29 @@ | |||
| 145 | 137 | ||
| 146 | #define DA7210_VERSION "0.0.1" | 138 | #define DA7210_VERSION "0.0.1" |
| 147 | 139 | ||
| 140 | /* | ||
| 141 | * Playback Volume | ||
| 142 | * | ||
| 143 | * max : 0x3F (+15.0 dB) | ||
| 144 | * (1.5 dB step) | ||
| 145 | * min : 0x11 (-54.0 dB) | ||
| 146 | * mute : 0x10 | ||
| 147 | * reserved : 0x00 - 0x0F | ||
| 148 | * | ||
| 149 | * ** FIXME ** | ||
| 150 | * | ||
| 151 | * Reserved area are considered as "mute". | ||
| 152 | * -> min = -79.5 dB | ||
| 153 | */ | ||
| 154 | static const DECLARE_TLV_DB_SCALE(hp_out_tlv, -7950, 150, 1); | ||
| 155 | |||
| 156 | static const struct snd_kcontrol_new da7210_snd_controls[] = { | ||
| 157 | |||
| 158 | SOC_DOUBLE_R_TLV("HeadPhone Playback Volume", | ||
| 159 | DA7210_HP_L_VOL, DA7210_HP_R_VOL, | ||
| 160 | 0, 0x3F, 0, hp_out_tlv), | ||
| 161 | }; | ||
| 162 | |||
| 148 | /* Codec private data */ | 163 | /* Codec private data */ |
| 149 | struct da7210_priv { | 164 | struct da7210_priv { |
| 150 | struct snd_soc_codec codec; | 165 | struct snd_soc_codec codec; |
| @@ -227,10 +242,6 @@ static int da7210_startup(struct snd_pcm_substream *substream, | |||
| 227 | struct snd_soc_codec *codec = dai->codec; | 242 | struct snd_soc_codec *codec = dai->codec; |
| 228 | 243 | ||
| 229 | if (is_play) { | 244 | if (is_play) { |
| 230 | /* PlayBack Volume 40 */ | ||
| 231 | snd_soc_update_bits(codec, DA7210_HP_L_VOL, 0x3F, 40); | ||
| 232 | snd_soc_update_bits(codec, DA7210_HP_R_VOL, 0x3F, 40); | ||
| 233 | |||
| 234 | /* Enable Out */ | 245 | /* Enable Out */ |
| 235 | snd_soc_update_bits(codec, DA7210_OUTMIX_L, 0x1F, 0x10); | 246 | snd_soc_update_bits(codec, DA7210_OUTMIX_L, 0x1F, 0x10); |
| 236 | snd_soc_update_bits(codec, DA7210_OUTMIX_R, 0x1F, 0x10); | 247 | snd_soc_update_bits(codec, DA7210_OUTMIX_R, 0x1F, 0x10); |
| @@ -488,7 +499,7 @@ static int da7210_init(struct da7210_priv *da7210) | |||
| 488 | ret = snd_soc_register_dai(&da7210_dai); | 499 | ret = snd_soc_register_dai(&da7210_dai); |
| 489 | if (ret) { | 500 | if (ret) { |
| 490 | dev_err(codec->dev, "Failed to register DAI: %d\n", ret); | 501 | dev_err(codec->dev, "Failed to register DAI: %d\n", ret); |
| 491 | goto init_err; | 502 | goto codec_err; |
| 492 | } | 503 | } |
| 493 | 504 | ||
| 494 | /* FIXME | 505 | /* FIXME |
| @@ -574,6 +585,8 @@ static int da7210_init(struct da7210_priv *da7210) | |||
| 574 | 585 | ||
| 575 | return ret; | 586 | return ret; |
| 576 | 587 | ||
| 588 | codec_err: | ||
| 589 | snd_soc_unregister_codec(codec); | ||
| 577 | init_err: | 590 | init_err: |
| 578 | kfree(codec->reg_cache); | 591 | kfree(codec->reg_cache); |
| 579 | codec->reg_cache = NULL; | 592 | codec->reg_cache = NULL; |
| @@ -601,8 +614,10 @@ static int __devinit da7210_i2c_probe(struct i2c_client *i2c, | |||
| 601 | codec->control_data = i2c; | 614 | codec->control_data = i2c; |
| 602 | 615 | ||
| 603 | ret = da7210_init(da7210); | 616 | ret = da7210_init(da7210); |
| 604 | if (ret < 0) | 617 | if (ret < 0) { |
| 605 | pr_err("Failed to initialise da7210 audio codec\n"); | 618 | pr_err("Failed to initialise da7210 audio codec\n"); |
| 619 | kfree(da7210); | ||
| 620 | } | ||
| 606 | 621 | ||
| 607 | return ret; | 622 | return ret; |
| 608 | } | 623 | } |
| @@ -656,6 +671,9 @@ static int da7210_probe(struct platform_device *pdev) | |||
| 656 | if (ret < 0) | 671 | if (ret < 0) |
| 657 | goto pcm_err; | 672 | goto pcm_err; |
| 658 | 673 | ||
| 674 | snd_soc_add_controls(da7210_codec, da7210_snd_controls, | ||
| 675 | ARRAY_SIZE(da7210_snd_controls)); | ||
| 676 | |||
| 659 | dev_info(&pdev->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION); | 677 | dev_info(&pdev->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION); |
| 660 | 678 | ||
| 661 | pcm_err: | 679 | pcm_err: |
diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c new file mode 100644 index 000000000000..66557de1e4fe --- /dev/null +++ b/sound/soc/codecs/jz4740.c | |||
| @@ -0,0 +1,511 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * You should have received a copy of the GNU General Public License along | ||
| 9 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 10 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 11 | * | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/kernel.h> | ||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/platform_device.h> | ||
| 17 | #include <linux/slab.h> | ||
| 18 | |||
| 19 | #include <linux/delay.h> | ||
| 20 | |||
| 21 | #include <sound/core.h> | ||
| 22 | #include <sound/pcm.h> | ||
| 23 | #include <sound/pcm_params.h> | ||
| 24 | #include <sound/initval.h> | ||
| 25 | #include <sound/soc-dapm.h> | ||
| 26 | #include <sound/soc.h> | ||
| 27 | |||
| 28 | #define JZ4740_REG_CODEC_1 0x0 | ||
| 29 | #define JZ4740_REG_CODEC_2 0x1 | ||
| 30 | |||
| 31 | #define JZ4740_CODEC_1_LINE_ENABLE BIT(29) | ||
| 32 | #define JZ4740_CODEC_1_MIC_ENABLE BIT(28) | ||
| 33 | #define JZ4740_CODEC_1_SW1_ENABLE BIT(27) | ||
| 34 | #define JZ4740_CODEC_1_ADC_ENABLE BIT(26) | ||
| 35 | #define JZ4740_CODEC_1_SW2_ENABLE BIT(25) | ||
| 36 | #define JZ4740_CODEC_1_DAC_ENABLE BIT(24) | ||
| 37 | #define JZ4740_CODEC_1_VREF_DISABLE BIT(20) | ||
| 38 | #define JZ4740_CODEC_1_VREF_AMP_DISABLE BIT(19) | ||
| 39 | #define JZ4740_CODEC_1_VREF_PULLDOWN BIT(18) | ||
| 40 | #define JZ4740_CODEC_1_VREF_LOW_CURRENT BIT(17) | ||
| 41 | #define JZ4740_CODEC_1_VREF_HIGH_CURRENT BIT(16) | ||
| 42 | #define JZ4740_CODEC_1_HEADPHONE_DISABLE BIT(14) | ||
| 43 | #define JZ4740_CODEC_1_HEADPHONE_AMP_CHANGE_ANY BIT(13) | ||
| 44 | #define JZ4740_CODEC_1_HEADPHONE_CHARGE BIT(12) | ||
| 45 | #define JZ4740_CODEC_1_HEADPHONE_PULLDOWN (BIT(11) | BIT(10)) | ||
| 46 | #define JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M BIT(9) | ||
| 47 | #define JZ4740_CODEC_1_HEADPHONE_POWERDOWN BIT(8) | ||
| 48 | #define JZ4740_CODEC_1_SUSPEND BIT(1) | ||
| 49 | #define JZ4740_CODEC_1_RESET BIT(0) | ||
| 50 | |||
| 51 | #define JZ4740_CODEC_1_LINE_ENABLE_OFFSET 29 | ||
| 52 | #define JZ4740_CODEC_1_MIC_ENABLE_OFFSET 28 | ||
| 53 | #define JZ4740_CODEC_1_SW1_ENABLE_OFFSET 27 | ||
| 54 | #define JZ4740_CODEC_1_ADC_ENABLE_OFFSET 26 | ||
| 55 | #define JZ4740_CODEC_1_SW2_ENABLE_OFFSET 25 | ||
| 56 | #define JZ4740_CODEC_1_DAC_ENABLE_OFFSET 24 | ||
| 57 | #define JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET 14 | ||
| 58 | #define JZ4740_CODEC_1_HEADPHONE_POWERDOWN_OFFSET 8 | ||
| 59 | |||
| 60 | #define JZ4740_CODEC_2_INPUT_VOLUME_MASK 0x1f0000 | ||
| 61 | #define JZ4740_CODEC_2_SAMPLE_RATE_MASK 0x000f00 | ||
| 62 | #define JZ4740_CODEC_2_MIC_BOOST_GAIN_MASK 0x000030 | ||
| 63 | #define JZ4740_CODEC_2_HEADPHONE_VOLUME_MASK 0x000003 | ||
| 64 | |||
| 65 | #define JZ4740_CODEC_2_INPUT_VOLUME_OFFSET 16 | ||
| 66 | #define JZ4740_CODEC_2_SAMPLE_RATE_OFFSET 8 | ||
| 67 | #define JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET 4 | ||
| 68 | #define JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET 0 | ||
| 69 | |||
| 70 | static const uint32_t jz4740_codec_regs[] = { | ||
| 71 | 0x021b2302, 0x00170803, | ||
| 72 | }; | ||
| 73 | |||
| 74 | struct jz4740_codec { | ||
| 75 | void __iomem *base; | ||
| 76 | struct resource *mem; | ||
| 77 | |||
| 78 | uint32_t reg_cache[2]; | ||
| 79 | struct snd_soc_codec codec; | ||
| 80 | }; | ||
| 81 | |||
| 82 | static inline struct jz4740_codec *codec_to_jz4740(struct snd_soc_codec *codec) | ||
| 83 | { | ||
| 84 | return container_of(codec, struct jz4740_codec, codec); | ||
| 85 | } | ||
| 86 | |||
| 87 | static unsigned int jz4740_codec_read(struct snd_soc_codec *codec, | ||
| 88 | unsigned int reg) | ||
| 89 | { | ||
| 90 | struct jz4740_codec *jz4740_codec = codec_to_jz4740(codec); | ||
| 91 | return readl(jz4740_codec->base + (reg << 2)); | ||
| 92 | } | ||
| 93 | |||
| 94 | static int jz4740_codec_write(struct snd_soc_codec *codec, unsigned int reg, | ||
| 95 | unsigned int val) | ||
| 96 | { | ||
| 97 | struct jz4740_codec *jz4740_codec = codec_to_jz4740(codec); | ||
| 98 | |||
| 99 | jz4740_codec->reg_cache[reg] = val; | ||
| 100 | writel(val, jz4740_codec->base + (reg << 2)); | ||
| 101 | |||
| 102 | return 0; | ||
| 103 | } | ||
| 104 | |||
| 105 | static const struct snd_kcontrol_new jz4740_codec_controls[] = { | ||
| 106 | SOC_SINGLE("Master Playback Volume", JZ4740_REG_CODEC_2, | ||
| 107 | JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0), | ||
| 108 | SOC_SINGLE("Master Capture Volume", JZ4740_REG_CODEC_2, | ||
| 109 | JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0), | ||
| 110 | SOC_SINGLE("Master Playback Switch", JZ4740_REG_CODEC_1, | ||
| 111 | JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET, 1, 1), | ||
| 112 | SOC_SINGLE("Mic Capture Volume", JZ4740_REG_CODEC_2, | ||
| 113 | JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0), | ||
| 114 | }; | ||
| 115 | |||
| 116 | static const struct snd_kcontrol_new jz4740_codec_output_controls[] = { | ||
| 117 | SOC_DAPM_SINGLE("Bypass Switch", JZ4740_REG_CODEC_1, | ||
| 118 | JZ4740_CODEC_1_SW1_ENABLE_OFFSET, 1, 0), | ||
| 119 | SOC_DAPM_SINGLE("DAC Switch", JZ4740_REG_CODEC_1, | ||
| 120 | JZ4740_CODEC_1_SW2_ENABLE_OFFSET, 1, 0), | ||
| 121 | }; | ||
| 122 | |||
| 123 | static const struct snd_kcontrol_new jz4740_codec_input_controls[] = { | ||
| 124 | SOC_DAPM_SINGLE("Line Capture Switch", JZ4740_REG_CODEC_1, | ||
| 125 | JZ4740_CODEC_1_LINE_ENABLE_OFFSET, 1, 0), | ||
| 126 | SOC_DAPM_SINGLE("Mic Capture Switch", JZ4740_REG_CODEC_1, | ||
| 127 | JZ4740_CODEC_1_MIC_ENABLE_OFFSET, 1, 0), | ||
| 128 | }; | ||
| 129 | |||
| 130 | static const struct snd_soc_dapm_widget jz4740_codec_dapm_widgets[] = { | ||
| 131 | SND_SOC_DAPM_ADC("ADC", "Capture", JZ4740_REG_CODEC_1, | ||
| 132 | JZ4740_CODEC_1_ADC_ENABLE_OFFSET, 0), | ||
| 133 | SND_SOC_DAPM_DAC("DAC", "Playback", JZ4740_REG_CODEC_1, | ||
| 134 | JZ4740_CODEC_1_DAC_ENABLE_OFFSET, 0), | ||
| 135 | |||
| 136 | SND_SOC_DAPM_MIXER("Output Mixer", JZ4740_REG_CODEC_1, | ||
| 137 | JZ4740_CODEC_1_HEADPHONE_POWERDOWN_OFFSET, 1, | ||
| 138 | jz4740_codec_output_controls, | ||
| 139 | ARRAY_SIZE(jz4740_codec_output_controls)), | ||
| 140 | |||
| 141 | SND_SOC_DAPM_MIXER_NAMED_CTL("Input Mixer", SND_SOC_NOPM, 0, 0, | ||
| 142 | jz4740_codec_input_controls, | ||
| 143 | ARRAY_SIZE(jz4740_codec_input_controls)), | ||
| 144 | SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0), | ||
| 145 | |||
| 146 | SND_SOC_DAPM_OUTPUT("LOUT"), | ||
| 147 | SND_SOC_DAPM_OUTPUT("ROUT"), | ||
| 148 | |||
| 149 | SND_SOC_DAPM_INPUT("MIC"), | ||
| 150 | SND_SOC_DAPM_INPUT("LIN"), | ||
| 151 | SND_SOC_DAPM_INPUT("RIN"), | ||
| 152 | }; | ||
| 153 | |||
| 154 | static const struct snd_soc_dapm_route jz4740_codec_dapm_routes[] = { | ||
| 155 | {"Line Input", NULL, "LIN"}, | ||
| 156 | {"Line Input", NULL, "RIN"}, | ||
| 157 | |||
| 158 | {"Input Mixer", "Line Capture Switch", "Line Input"}, | ||
| 159 | {"Input Mixer", "Mic Capture Switch", "MIC"}, | ||
| 160 | |||
| 161 | {"ADC", NULL, "Input Mixer"}, | ||
| 162 | |||
| 163 | {"Output Mixer", "Bypass Switch", "Input Mixer"}, | ||
| 164 | {"Output Mixer", "DAC Switch", "DAC"}, | ||
| 165 | |||
| 166 | {"LOUT", NULL, "Output Mixer"}, | ||
| 167 | {"ROUT", NULL, "Output Mixer"}, | ||
| 168 | }; | ||
| 169 | |||
| 170 | static int jz4740_codec_hw_params(struct snd_pcm_substream *substream, | ||
| 171 | struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) | ||
| 172 | { | ||
| 173 | uint32_t val; | ||
| 174 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
| 175 | struct snd_soc_device *socdev = rtd->socdev; | ||
| 176 | struct snd_soc_codec *codec = socdev->card->codec; | ||
| 177 | |||
| 178 | switch (params_rate(params)) { | ||
| 179 | case 8000: | ||
| 180 | val = 0; | ||
| 181 | break; | ||
| 182 | case 11025: | ||
| 183 | val = 1; | ||
| 184 | break; | ||
| 185 | case 12000: | ||
| 186 | val = 2; | ||
| 187 | break; | ||
| 188 | case 16000: | ||
| 189 | val = 3; | ||
| 190 | break; | ||
| 191 | case 22050: | ||
| 192 | val = 4; | ||
| 193 | break; | ||
| 194 | case 24000: | ||
| 195 | val = 5; | ||
| 196 | break; | ||
| 197 | case 32000: | ||
| 198 | val = 6; | ||
| 199 | break; | ||
| 200 | case 44100: | ||
| 201 | val = 7; | ||
| 202 | break; | ||
| 203 | case 48000: | ||
| 204 | val = 8; | ||
| 205 | break; | ||
| 206 | default: | ||
| 207 | return -EINVAL; | ||
| 208 | } | ||
| 209 | |||
| 210 | val <<= JZ4740_CODEC_2_SAMPLE_RATE_OFFSET; | ||
| 211 | |||
| 212 | snd_soc_update_bits(codec, JZ4740_REG_CODEC_2, | ||
| 213 | JZ4740_CODEC_2_SAMPLE_RATE_MASK, val); | ||
| 214 | |||
| 215 | return 0; | ||
| 216 | } | ||
| 217 | |||
| 218 | static struct snd_soc_dai_ops jz4740_codec_dai_ops = { | ||
| 219 | .hw_params = jz4740_codec_hw_params, | ||
| 220 | }; | ||
| 221 | |||
| 222 | struct snd_soc_dai jz4740_codec_dai = { | ||
| 223 | .name = "jz4740", | ||
| 224 | .playback = { | ||
| 225 | .stream_name = "Playback", | ||
| 226 | .channels_min = 2, | ||
| 227 | .channels_max = 2, | ||
| 228 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
| 229 | .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8, | ||
| 230 | }, | ||
| 231 | .capture = { | ||
| 232 | .stream_name = "Capture", | ||
| 233 | .channels_min = 2, | ||
| 234 | .channels_max = 2, | ||
| 235 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
| 236 | .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8, | ||
| 237 | }, | ||
| 238 | .ops = &jz4740_codec_dai_ops, | ||
| 239 | .symmetric_rates = 1, | ||
| 240 | }; | ||
| 241 | EXPORT_SYMBOL_GPL(jz4740_codec_dai); | ||
| 242 | |||
| 243 | static void jz4740_codec_wakeup(struct snd_soc_codec *codec) | ||
| 244 | { | ||
| 245 | int i; | ||
| 246 | uint32_t *cache = codec->reg_cache; | ||
| 247 | |||
| 248 | snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, | ||
| 249 | JZ4740_CODEC_1_RESET, JZ4740_CODEC_1_RESET); | ||
| 250 | udelay(2); | ||
| 251 | |||
| 252 | snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, | ||
| 253 | JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET, 0); | ||
| 254 | |||
| 255 | for (i = 0; i < ARRAY_SIZE(jz4740_codec_regs); ++i) | ||
| 256 | jz4740_codec_write(codec, i, cache[i]); | ||
| 257 | } | ||
| 258 | |||
| 259 | static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec, | ||
| 260 | enum snd_soc_bias_level level) | ||
| 261 | { | ||
| 262 | unsigned int mask; | ||
| 263 | unsigned int value; | ||
| 264 | |||
| 265 | switch (level) { | ||
| 266 | case SND_SOC_BIAS_ON: | ||
| 267 | break; | ||
| 268 | case SND_SOC_BIAS_PREPARE: | ||
| 269 | mask = JZ4740_CODEC_1_VREF_DISABLE | | ||
| 270 | JZ4740_CODEC_1_VREF_AMP_DISABLE | | ||
| 271 | JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M; | ||
| 272 | value = 0; | ||
| 273 | |||
| 274 | snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value); | ||
| 275 | break; | ||
| 276 | case SND_SOC_BIAS_STANDBY: | ||
| 277 | /* The only way to clear the suspend flag is to reset the codec */ | ||
| 278 | if (codec->bias_level == SND_SOC_BIAS_OFF) | ||
| 279 | jz4740_codec_wakeup(codec); | ||
| 280 | |||
| 281 | mask = JZ4740_CODEC_1_VREF_DISABLE | | ||
| 282 | JZ4740_CODEC_1_VREF_AMP_DISABLE | | ||
| 283 | JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M; | ||
| 284 | value = JZ4740_CODEC_1_VREF_DISABLE | | ||
| 285 | JZ4740_CODEC_1_VREF_AMP_DISABLE | | ||
| 286 | JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M; | ||
| 287 | |||
| 288 | snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value); | ||
| 289 | break; | ||
| 290 | case SND_SOC_BIAS_OFF: | ||
| 291 | mask = JZ4740_CODEC_1_SUSPEND; | ||
| 292 | value = JZ4740_CODEC_1_SUSPEND; | ||
| 293 | |||
| 294 | snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value); | ||
| 295 | break; | ||
| 296 | default: | ||
| 297 | break; | ||
| 298 | } | ||
| 299 | |||
| 300 | codec->bias_level = level; | ||
| 301 | |||
| 302 | return 0; | ||
| 303 | } | ||
| 304 | |||
| 305 | static struct snd_soc_codec *jz4740_codec_codec; | ||
| 306 | |||
| 307 | static int jz4740_codec_dev_probe(struct platform_device *pdev) | ||
| 308 | { | ||
| 309 | int ret; | ||
| 310 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
| 311 | struct snd_soc_codec *codec = jz4740_codec_codec; | ||
| 312 | |||
| 313 | BUG_ON(!codec); | ||
| 314 | |||
| 315 | socdev->card->codec = codec; | ||
| 316 | |||
| 317 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | ||
| 318 | if (ret) { | ||
| 319 | dev_err(&pdev->dev, "Failed to create pcms: %d\n", ret); | ||
| 320 | return ret; | ||
| 321 | } | ||
| 322 | |||
| 323 | snd_soc_add_controls(codec, jz4740_codec_controls, | ||
| 324 | ARRAY_SIZE(jz4740_codec_controls)); | ||
| 325 | |||
| 326 | snd_soc_dapm_new_controls(codec, jz4740_codec_dapm_widgets, | ||
| 327 | ARRAY_SIZE(jz4740_codec_dapm_widgets)); | ||
| 328 | |||
| 329 | snd_soc_dapm_add_routes(codec, jz4740_codec_dapm_routes, | ||
| 330 | ARRAY_SIZE(jz4740_codec_dapm_routes)); | ||
| 331 | |||
| 332 | snd_soc_dapm_new_widgets(codec); | ||
| 333 | |||
| 334 | return 0; | ||
| 335 | } | ||
| 336 | |||
| 337 | static int jz4740_codec_dev_remove(struct platform_device *pdev) | ||
| 338 | { | ||
| 339 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
| 340 | |||
| 341 | snd_soc_free_pcms(socdev); | ||
| 342 | snd_soc_dapm_free(socdev); | ||
| 343 | |||
| 344 | return 0; | ||
| 345 | } | ||
| 346 | |||
| 347 | #ifdef CONFIG_PM_SLEEP | ||
| 348 | |||
| 349 | static int jz4740_codec_suspend(struct platform_device *pdev, pm_message_t state) | ||
| 350 | { | ||
| 351 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
| 352 | struct snd_soc_codec *codec = socdev->card->codec; | ||
| 353 | |||
| 354 | return jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
| 355 | } | ||
| 356 | |||
| 357 | static int jz4740_codec_resume(struct platform_device *pdev) | ||
| 358 | { | ||
| 359 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
| 360 | struct snd_soc_codec *codec = socdev->card->codec; | ||
| 361 | |||
| 362 | return jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
| 363 | } | ||
| 364 | |||
| 365 | #else | ||
| 366 | #define jz4740_codec_suspend NULL | ||
| 367 | #define jz4740_codec_resume NULL | ||
| 368 | #endif | ||
| 369 | |||
| 370 | struct snd_soc_codec_device soc_codec_dev_jz4740_codec = { | ||
| 371 | .probe = jz4740_codec_dev_probe, | ||
| 372 | .remove = jz4740_codec_dev_remove, | ||
| 373 | .suspend = jz4740_codec_suspend, | ||
| 374 | .resume = jz4740_codec_resume, | ||
| 375 | }; | ||
| 376 | EXPORT_SYMBOL_GPL(soc_codec_dev_jz4740_codec); | ||
| 377 | |||
| 378 | static int __devinit jz4740_codec_probe(struct platform_device *pdev) | ||
| 379 | { | ||
| 380 | int ret; | ||
| 381 | struct jz4740_codec *jz4740_codec; | ||
| 382 | struct snd_soc_codec *codec; | ||
| 383 | struct resource *mem; | ||
| 384 | |||
| 385 | jz4740_codec = kzalloc(sizeof(*jz4740_codec), GFP_KERNEL); | ||
| 386 | if (!jz4740_codec) | ||
| 387 | return -ENOMEM; | ||
| 388 | |||
| 389 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 390 | if (!mem) { | ||
| 391 | dev_err(&pdev->dev, "Failed to get mmio memory resource\n"); | ||
| 392 | ret = -ENOENT; | ||
| 393 | goto err_free_codec; | ||
| 394 | } | ||
| 395 | |||
| 396 | mem = request_mem_region(mem->start, resource_size(mem), pdev->name); | ||
| 397 | if (!mem) { | ||
| 398 | dev_err(&pdev->dev, "Failed to request mmio memory region\n"); | ||
| 399 | ret = -EBUSY; | ||
| 400 | goto err_free_codec; | ||
| 401 | } | ||
| 402 | |||
| 403 | jz4740_codec->base = ioremap(mem->start, resource_size(mem)); | ||
| 404 | if (!jz4740_codec->base) { | ||
| 405 | dev_err(&pdev->dev, "Failed to ioremap mmio memory\n"); | ||
| 406 | ret = -EBUSY; | ||
| 407 | goto err_release_mem_region; | ||
| 408 | } | ||
| 409 | jz4740_codec->mem = mem; | ||
| 410 | |||
| 411 | jz4740_codec_dai.dev = &pdev->dev; | ||
| 412 | |||
| 413 | codec = &jz4740_codec->codec; | ||
| 414 | |||
| 415 | codec->dev = &pdev->dev; | ||
| 416 | codec->name = "jz4740"; | ||
| 417 | codec->owner = THIS_MODULE; | ||
| 418 | |||
| 419 | codec->read = jz4740_codec_read; | ||
| 420 | codec->write = jz4740_codec_write; | ||
| 421 | codec->set_bias_level = jz4740_codec_set_bias_level; | ||
| 422 | codec->bias_level = SND_SOC_BIAS_OFF; | ||
| 423 | |||
| 424 | codec->dai = &jz4740_codec_dai; | ||
| 425 | codec->num_dai = 1; | ||
| 426 | |||
| 427 | codec->reg_cache = jz4740_codec->reg_cache; | ||
| 428 | codec->reg_cache_size = 2; | ||
| 429 | memcpy(codec->reg_cache, jz4740_codec_regs, sizeof(jz4740_codec_regs)); | ||
| 430 | |||
| 431 | mutex_init(&codec->mutex); | ||
| 432 | INIT_LIST_HEAD(&codec->dapm_widgets); | ||
| 433 | INIT_LIST_HEAD(&codec->dapm_paths); | ||
| 434 | |||
| 435 | jz4740_codec_codec = codec; | ||
| 436 | |||
| 437 | snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, | ||
| 438 | JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE); | ||
| 439 | |||
| 440 | platform_set_drvdata(pdev, jz4740_codec); | ||
| 441 | |||
| 442 | ret = snd_soc_register_codec(codec); | ||
| 443 | if (ret) { | ||
| 444 | dev_err(&pdev->dev, "Failed to register codec\n"); | ||
| 445 | goto err_iounmap; | ||
| 446 | } | ||
| 447 | |||
| 448 | ret = snd_soc_register_dai(&jz4740_codec_dai); | ||
| 449 | if (ret) { | ||
| 450 | dev_err(&pdev->dev, "Failed to register codec dai\n"); | ||
| 451 | goto err_unregister_codec; | ||
| 452 | } | ||
| 453 | |||
| 454 | jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
| 455 | |||
| 456 | return 0; | ||
| 457 | |||
| 458 | err_unregister_codec: | ||
| 459 | snd_soc_unregister_codec(codec); | ||
| 460 | err_iounmap: | ||
| 461 | iounmap(jz4740_codec->base); | ||
| 462 | err_release_mem_region: | ||
| 463 | release_mem_region(mem->start, resource_size(mem)); | ||
| 464 | err_free_codec: | ||
| 465 | kfree(jz4740_codec); | ||
| 466 | |||
| 467 | return ret; | ||
| 468 | } | ||
| 469 | |||
| 470 | static int __devexit jz4740_codec_remove(struct platform_device *pdev) | ||
| 471 | { | ||
| 472 | struct jz4740_codec *jz4740_codec = platform_get_drvdata(pdev); | ||
| 473 | struct resource *mem = jz4740_codec->mem; | ||
| 474 | |||
| 475 | snd_soc_unregister_dai(&jz4740_codec_dai); | ||
| 476 | snd_soc_unregister_codec(&jz4740_codec->codec); | ||
| 477 | |||
| 478 | iounmap(jz4740_codec->base); | ||
| 479 | release_mem_region(mem->start, resource_size(mem)); | ||
| 480 | |||
| 481 | platform_set_drvdata(pdev, NULL); | ||
| 482 | kfree(jz4740_codec); | ||
| 483 | |||
| 484 | return 0; | ||
| 485 | } | ||
| 486 | |||
| 487 | static struct platform_driver jz4740_codec_driver = { | ||
| 488 | .probe = jz4740_codec_probe, | ||
| 489 | .remove = __devexit_p(jz4740_codec_remove), | ||
| 490 | .driver = { | ||
| 491 | .name = "jz4740-codec", | ||
| 492 | .owner = THIS_MODULE, | ||
| 493 | }, | ||
| 494 | }; | ||
| 495 | |||
| 496 | static int __init jz4740_codec_init(void) | ||
| 497 | { | ||
| 498 | return platform_driver_register(&jz4740_codec_driver); | ||
| 499 | } | ||
| 500 | module_init(jz4740_codec_init); | ||
| 501 | |||
| 502 | static void __exit jz4740_codec_exit(void) | ||
| 503 | { | ||
| 504 | platform_driver_unregister(&jz4740_codec_driver); | ||
| 505 | } | ||
| 506 | module_exit(jz4740_codec_exit); | ||
| 507 | |||
| 508 | MODULE_DESCRIPTION("JZ4740 SoC internal codec driver"); | ||
| 509 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); | ||
| 510 | MODULE_LICENSE("GPL v2"); | ||
| 511 | MODULE_ALIAS("platform:jz4740-codec"); | ||
diff --git a/sound/soc/codecs/jz4740.h b/sound/soc/codecs/jz4740.h new file mode 100644 index 000000000000..b5a0691be763 --- /dev/null +++ b/sound/soc/codecs/jz4740.h | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2009, Lars-Peter Clausen <lars@metafoo.de> | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * You should have received a copy of the GNU General Public License along | ||
| 9 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 10 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 11 | * | ||
| 12 | */ | ||
| 13 | |||
| 14 | #ifndef __SND_SOC_CODECS_JZ4740_CODEC_H__ | ||
| 15 | #define __SND_SOC_CODECS_JZ4740_CODEC_H__ | ||
| 16 | |||
| 17 | extern struct snd_soc_dai jz4740_codec_dai; | ||
| 18 | extern struct snd_soc_codec_device soc_codec_dev_jz4740_codec; | ||
| 19 | |||
| 20 | #endif | ||
diff --git a/sound/soc/codecs/spdif_transciever.c b/sound/soc/codecs/spdif_transciever.c index a63191141052..9119836051a4 100644 --- a/sound/soc/codecs/spdif_transciever.c +++ b/sound/soc/codecs/spdif_transciever.c | |||
| @@ -16,8 +16,10 @@ | |||
| 16 | 16 | ||
| 17 | #include <linux/module.h> | 17 | #include <linux/module.h> |
| 18 | #include <linux/moduleparam.h> | 18 | #include <linux/moduleparam.h> |
| 19 | #include <linux/slab.h> | ||
| 19 | #include <sound/soc.h> | 20 | #include <sound/soc.h> |
| 20 | #include <sound/pcm.h> | 21 | #include <sound/pcm.h> |
| 22 | #include <sound/initval.h> | ||
| 21 | 23 | ||
| 22 | #include "spdif_transciever.h" | 24 | #include "spdif_transciever.h" |
| 23 | 25 | ||
| @@ -26,6 +28,48 @@ MODULE_LICENSE("GPL"); | |||
| 26 | #define STUB_RATES SNDRV_PCM_RATE_8000_96000 | 28 | #define STUB_RATES SNDRV_PCM_RATE_8000_96000 |
| 27 | #define STUB_FORMATS SNDRV_PCM_FMTBIT_S16_LE | 29 | #define STUB_FORMATS SNDRV_PCM_FMTBIT_S16_LE |
| 28 | 30 | ||
| 31 | static struct snd_soc_codec *spdif_dit_codec; | ||
| 32 | |||
| 33 | static int spdif_dit_codec_probe(struct platform_device *pdev) | ||
| 34 | { | ||
| 35 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
| 36 | struct snd_soc_codec *codec; | ||
| 37 | int ret; | ||
| 38 | |||
| 39 | if (spdif_dit_codec == NULL) { | ||
| 40 | dev_err(&pdev->dev, "Codec device not registered\n"); | ||
| 41 | return -ENODEV; | ||
| 42 | } | ||
| 43 | |||
| 44 | socdev->card->codec = spdif_dit_codec; | ||
| 45 | codec = spdif_dit_codec; | ||
| 46 | |||
| 47 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | ||
| 48 | if (ret < 0) { | ||
| 49 | dev_err(codec->dev, "failed to create pcms: %d\n", ret); | ||
| 50 | goto err_create_pcms; | ||
| 51 | } | ||
| 52 | |||
| 53 | return 0; | ||
| 54 | |||
| 55 | err_create_pcms: | ||
| 56 | return ret; | ||
| 57 | } | ||
| 58 | |||
| 59 | static int spdif_dit_codec_remove(struct platform_device *pdev) | ||
| 60 | { | ||
| 61 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
| 62 | |||
| 63 | snd_soc_free_pcms(socdev); | ||
| 64 | |||
| 65 | return 0; | ||
| 66 | } | ||
| 67 | |||
| 68 | struct snd_soc_codec_device soc_codec_dev_spdif_dit = { | ||
| 69 | .probe = spdif_dit_codec_probe, | ||
| 70 | .remove = spdif_dit_codec_remove, | ||
| 71 | }; EXPORT_SYMBOL_GPL(soc_codec_dev_spdif_dit); | ||
| 72 | |||
| 29 | struct snd_soc_dai dit_stub_dai = { | 73 | struct snd_soc_dai dit_stub_dai = { |
| 30 | .name = "DIT", | 74 | .name = "DIT", |
| 31 | .playback = { | 75 | .playback = { |
| @@ -40,13 +84,61 @@ EXPORT_SYMBOL_GPL(dit_stub_dai); | |||
| 40 | 84 | ||
| 41 | static int spdif_dit_probe(struct platform_device *pdev) | 85 | static int spdif_dit_probe(struct platform_device *pdev) |
| 42 | { | 86 | { |
| 87 | struct snd_soc_codec *codec; | ||
| 88 | int ret; | ||
| 89 | |||
| 90 | if (spdif_dit_codec) { | ||
| 91 | dev_err(&pdev->dev, "Another Codec is registered\n"); | ||
| 92 | ret = -EINVAL; | ||
| 93 | goto err_reg_codec; | ||
| 94 | } | ||
| 95 | |||
| 96 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); | ||
| 97 | if (codec == NULL) | ||
| 98 | return -ENOMEM; | ||
| 99 | |||
| 100 | codec->dev = &pdev->dev; | ||
| 101 | |||
| 102 | mutex_init(&codec->mutex); | ||
| 103 | |||
| 104 | INIT_LIST_HEAD(&codec->dapm_widgets); | ||
| 105 | INIT_LIST_HEAD(&codec->dapm_paths); | ||
| 106 | |||
| 107 | codec->name = "spdif-dit"; | ||
| 108 | codec->owner = THIS_MODULE; | ||
| 109 | codec->dai = &dit_stub_dai; | ||
| 110 | codec->num_dai = 1; | ||
| 111 | |||
| 112 | spdif_dit_codec = codec; | ||
| 113 | |||
| 114 | ret = snd_soc_register_codec(codec); | ||
| 115 | if (ret < 0) { | ||
| 116 | dev_err(codec->dev, "Failed to register codec: %d\n", ret); | ||
| 117 | goto err_reg_codec; | ||
| 118 | } | ||
| 119 | |||
| 43 | dit_stub_dai.dev = &pdev->dev; | 120 | dit_stub_dai.dev = &pdev->dev; |
| 44 | return snd_soc_register_dai(&dit_stub_dai); | 121 | ret = snd_soc_register_dai(&dit_stub_dai); |
| 122 | if (ret < 0) { | ||
| 123 | dev_err(codec->dev, "Failed to register dai: %d\n", ret); | ||
| 124 | goto err_reg_dai; | ||
| 125 | } | ||
| 126 | |||
| 127 | return 0; | ||
| 128 | |||
| 129 | err_reg_dai: | ||
| 130 | snd_soc_unregister_codec(codec); | ||
| 131 | err_reg_codec: | ||
| 132 | kfree(spdif_dit_codec); | ||
| 133 | return ret; | ||
| 45 | } | 134 | } |
| 46 | 135 | ||
| 47 | static int spdif_dit_remove(struct platform_device *pdev) | 136 | static int spdif_dit_remove(struct platform_device *pdev) |
| 48 | { | 137 | { |
| 49 | snd_soc_unregister_dai(&dit_stub_dai); | 138 | snd_soc_unregister_dai(&dit_stub_dai); |
| 139 | snd_soc_unregister_codec(spdif_dit_codec); | ||
| 140 | kfree(spdif_dit_codec); | ||
| 141 | spdif_dit_codec = NULL; | ||
| 50 | return 0; | 142 | return 0; |
| 51 | } | 143 | } |
| 52 | 144 | ||
diff --git a/sound/soc/codecs/spdif_transciever.h b/sound/soc/codecs/spdif_transciever.h index 296f2eb6c4ef..1e102124f546 100644 --- a/sound/soc/codecs/spdif_transciever.h +++ b/sound/soc/codecs/spdif_transciever.h | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #ifndef CODEC_STUBS_H | 12 | #ifndef CODEC_STUBS_H |
| 13 | #define CODEC_STUBS_H | 13 | #define CODEC_STUBS_H |
| 14 | 14 | ||
| 15 | extern struct snd_soc_codec_device soc_codec_dev_spdif_dit; | ||
| 15 | extern struct snd_soc_dai dit_stub_dai; | 16 | extern struct snd_soc_dai dit_stub_dai; |
| 16 | 17 | ||
| 17 | #endif /* CODEC_STUBS_H */ | 18 | #endif /* CODEC_STUBS_H */ |
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c index b0bae3508b29..0a4b0fef3355 100644 --- a/sound/soc/codecs/tlv320aic23.c +++ b/sound/soc/codecs/tlv320aic23.c | |||
| @@ -560,13 +560,16 @@ static int tlv320aic23_set_bias_level(struct snd_soc_codec *codec, | |||
| 560 | switch (level) { | 560 | switch (level) { |
| 561 | case SND_SOC_BIAS_ON: | 561 | case SND_SOC_BIAS_ON: |
| 562 | /* vref/mid, osc on, dac unmute */ | 562 | /* vref/mid, osc on, dac unmute */ |
| 563 | reg &= ~(TLV320AIC23_DEVICE_PWR_OFF | TLV320AIC23_OSC_OFF | \ | ||
| 564 | TLV320AIC23_DAC_OFF); | ||
| 563 | tlv320aic23_write(codec, TLV320AIC23_PWR, reg); | 565 | tlv320aic23_write(codec, TLV320AIC23_PWR, reg); |
| 564 | break; | 566 | break; |
| 565 | case SND_SOC_BIAS_PREPARE: | 567 | case SND_SOC_BIAS_PREPARE: |
| 566 | break; | 568 | break; |
| 567 | case SND_SOC_BIAS_STANDBY: | 569 | case SND_SOC_BIAS_STANDBY: |
| 568 | /* everything off except vref/vmid, */ | 570 | /* everything off except vref/vmid, */ |
| 569 | tlv320aic23_write(codec, TLV320AIC23_PWR, reg | 0x0040); | 571 | tlv320aic23_write(codec, TLV320AIC23_PWR, reg | \ |
| 572 | TLV320AIC23_CLK_OFF); | ||
| 570 | break; | 573 | break; |
| 571 | case SND_SOC_BIAS_OFF: | 574 | case SND_SOC_BIAS_OFF: |
| 572 | /* everything off, dac mute, inactive */ | 575 | /* everything off, dac mute, inactive */ |
| @@ -615,7 +618,6 @@ static int tlv320aic23_suspend(struct platform_device *pdev, | |||
| 615 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 618 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
| 616 | struct snd_soc_codec *codec = socdev->card->codec; | 619 | struct snd_soc_codec *codec = socdev->card->codec; |
| 617 | 620 | ||
| 618 | tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0); | ||
| 619 | tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF); | 621 | tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF); |
| 620 | 622 | ||
| 621 | return 0; | 623 | return 0; |
| @@ -632,7 +634,6 @@ static int tlv320aic23_resume(struct platform_device *pdev) | |||
| 632 | u16 val = tlv320aic23_read_reg_cache(codec, reg); | 634 | u16 val = tlv320aic23_read_reg_cache(codec, reg); |
| 633 | tlv320aic23_write(codec, reg, val); | 635 | tlv320aic23_write(codec, reg, val); |
| 634 | } | 636 | } |
| 635 | |||
| 636 | tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 637 | tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
| 637 | 638 | ||
| 638 | return 0; | 639 | return 0; |
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 65adc77eada1..8651b01ed223 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c | |||
| @@ -49,8 +49,6 @@ | |||
| 49 | 49 | ||
| 50 | #define NSAMPLE_MAX 5700 | 50 | #define NSAMPLE_MAX 5700 |
| 51 | 51 | ||
| 52 | #define LATENCY_TIME_MS 20 | ||
| 53 | |||
| 54 | #define MODE7_LTHR 10 | 52 | #define MODE7_LTHR 10 |
| 55 | #define MODE7_UTHR (DAC33_BUFFER_SIZE_SAMPLES - 10) | 53 | #define MODE7_UTHR (DAC33_BUFFER_SIZE_SAMPLES - 10) |
| 56 | 54 | ||
| @@ -62,6 +60,9 @@ | |||
| 62 | #define US_TO_SAMPLES(rate, us) \ | 60 | #define US_TO_SAMPLES(rate, us) \ |
| 63 | (rate / (1000000 / us)) | 61 | (rate / (1000000 / us)) |
| 64 | 62 | ||
| 63 | #define UTHR_FROM_PERIOD_SIZE(samples, playrate, burstrate) \ | ||
| 64 | ((samples * 5000) / ((burstrate * 5000) / (burstrate - playrate))) | ||
| 65 | |||
| 65 | static void dac33_calculate_times(struct snd_pcm_substream *substream); | 66 | static void dac33_calculate_times(struct snd_pcm_substream *substream); |
| 66 | static int dac33_prepare_chip(struct snd_pcm_substream *substream); | 67 | static int dac33_prepare_chip(struct snd_pcm_substream *substream); |
| 67 | 68 | ||
| @@ -107,6 +108,10 @@ struct tlv320dac33_priv { | |||
| 107 | * this */ | 108 | * this */ |
| 108 | enum dac33_fifo_modes fifo_mode;/* FIFO mode selection */ | 109 | enum dac33_fifo_modes fifo_mode;/* FIFO mode selection */ |
| 109 | unsigned int nsample; /* burst read amount from host */ | 110 | unsigned int nsample; /* burst read amount from host */ |
| 111 | int mode1_latency; /* latency caused by the i2c writes in | ||
| 112 | * us */ | ||
| 113 | int auto_fifo_config; /* Configure the FIFO based on the | ||
| 114 | * period size */ | ||
| 110 | u8 burst_bclkdiv; /* BCLK divider value in burst mode */ | 115 | u8 burst_bclkdiv; /* BCLK divider value in burst mode */ |
| 111 | unsigned int burst_rate; /* Interface speed in Burst modes */ | 116 | unsigned int burst_rate; /* Interface speed in Burst modes */ |
| 112 | 117 | ||
| @@ -120,6 +125,8 @@ struct tlv320dac33_priv { | |||
| 120 | * samples */ | 125 | * samples */ |
| 121 | unsigned int mode7_us_to_lthr; /* Time to reach lthr from uthr */ | 126 | unsigned int mode7_us_to_lthr; /* Time to reach lthr from uthr */ |
| 122 | 127 | ||
| 128 | unsigned int uthr; | ||
| 129 | |||
| 123 | enum dac33_state state; | 130 | enum dac33_state state; |
| 124 | }; | 131 | }; |
| 125 | 132 | ||
| @@ -442,6 +449,39 @@ static int dac33_set_nsample(struct snd_kcontrol *kcontrol, | |||
| 442 | return ret; | 449 | return ret; |
| 443 | } | 450 | } |
| 444 | 451 | ||
| 452 | static int dac33_get_uthr(struct snd_kcontrol *kcontrol, | ||
| 453 | struct snd_ctl_elem_value *ucontrol) | ||
| 454 | { | ||
| 455 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
| 456 | struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); | ||
| 457 | |||
| 458 | ucontrol->value.integer.value[0] = dac33->uthr; | ||
| 459 | |||
| 460 | return 0; | ||
| 461 | } | ||
| 462 | |||
| 463 | static int dac33_set_uthr(struct snd_kcontrol *kcontrol, | ||
| 464 | struct snd_ctl_elem_value *ucontrol) | ||
| 465 | { | ||
| 466 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
| 467 | struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); | ||
| 468 | int ret = 0; | ||
| 469 | |||
| 470 | if (dac33->substream) | ||
| 471 | return -EBUSY; | ||
| 472 | |||
| 473 | if (dac33->uthr == ucontrol->value.integer.value[0]) | ||
| 474 | return 0; | ||
| 475 | |||
| 476 | if (ucontrol->value.integer.value[0] < (MODE7_LTHR + 10) || | ||
| 477 | ucontrol->value.integer.value[0] > MODE7_UTHR) | ||
| 478 | ret = -EINVAL; | ||
| 479 | else | ||
| 480 | dac33->uthr = ucontrol->value.integer.value[0]; | ||
| 481 | |||
| 482 | return ret; | ||
| 483 | } | ||
| 484 | |||
| 445 | static int dac33_get_fifo_mode(struct snd_kcontrol *kcontrol, | 485 | static int dac33_get_fifo_mode(struct snd_kcontrol *kcontrol, |
| 446 | struct snd_ctl_elem_value *ucontrol) | 486 | struct snd_ctl_elem_value *ucontrol) |
| 447 | { | 487 | { |
| @@ -503,13 +543,18 @@ static const struct snd_kcontrol_new dac33_snd_controls[] = { | |||
| 503 | DAC33_LINEL_TO_LLO_VOL, DAC33_LINER_TO_RLO_VOL, 0, 127, 1), | 543 | DAC33_LINEL_TO_LLO_VOL, DAC33_LINER_TO_RLO_VOL, 0, 127, 1), |
| 504 | }; | 544 | }; |
| 505 | 545 | ||
| 506 | static const struct snd_kcontrol_new dac33_nsample_snd_controls[] = { | 546 | static const struct snd_kcontrol_new dac33_mode_snd_controls[] = { |
| 507 | SOC_SINGLE_EXT("nSample", 0, 0, 5900, 0, | ||
| 508 | dac33_get_nsample, dac33_set_nsample), | ||
| 509 | SOC_ENUM_EXT("FIFO Mode", dac33_fifo_mode_enum, | 547 | SOC_ENUM_EXT("FIFO Mode", dac33_fifo_mode_enum, |
| 510 | dac33_get_fifo_mode, dac33_set_fifo_mode), | 548 | dac33_get_fifo_mode, dac33_set_fifo_mode), |
| 511 | }; | 549 | }; |
| 512 | 550 | ||
| 551 | static const struct snd_kcontrol_new dac33_fifo_snd_controls[] = { | ||
| 552 | SOC_SINGLE_EXT("nSample", 0, 0, 5900, 0, | ||
| 553 | dac33_get_nsample, dac33_set_nsample), | ||
| 554 | SOC_SINGLE_EXT("UTHR", 0, 0, MODE7_UTHR, 0, | ||
| 555 | dac33_get_uthr, dac33_set_uthr), | ||
| 556 | }; | ||
| 557 | |||
| 513 | /* Analog bypass */ | 558 | /* Analog bypass */ |
| 514 | static const struct snd_kcontrol_new dac33_dapm_abypassl_control = | 559 | static const struct snd_kcontrol_new dac33_dapm_abypassl_control = |
| 515 | SOC_DAPM_SINGLE("Switch", DAC33_LINEL_TO_LLO_VOL, 7, 1, 1); | 560 | SOC_DAPM_SINGLE("Switch", DAC33_LINEL_TO_LLO_VOL, 7, 1, 1); |
| @@ -612,7 +657,7 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) | |||
| 612 | switch (dac33->fifo_mode) { | 657 | switch (dac33->fifo_mode) { |
| 613 | case DAC33_FIFO_MODE1: | 658 | case DAC33_FIFO_MODE1: |
| 614 | dac33_write16(codec, DAC33_NSAMPLE_MSB, | 659 | dac33_write16(codec, DAC33_NSAMPLE_MSB, |
| 615 | DAC33_THRREG(dac33->nsample + dac33->alarm_threshold)); | 660 | DAC33_THRREG(dac33->nsample)); |
| 616 | 661 | ||
| 617 | /* Take the timestamps */ | 662 | /* Take the timestamps */ |
| 618 | spin_lock_irq(&dac33->lock); | 663 | spin_lock_irq(&dac33->lock); |
| @@ -761,6 +806,10 @@ static void dac33_shutdown(struct snd_pcm_substream *substream, | |||
| 761 | struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); | 806 | struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); |
| 762 | 807 | ||
| 763 | dac33->substream = NULL; | 808 | dac33->substream = NULL; |
| 809 | |||
| 810 | /* Reset the nSample restrictions */ | ||
| 811 | dac33->nsample_min = 0; | ||
| 812 | dac33->nsample_max = NSAMPLE_MAX; | ||
| 764 | } | 813 | } |
| 765 | 814 | ||
| 766 | static int dac33_hw_params(struct snd_pcm_substream *substream, | 815 | static int dac33_hw_params(struct snd_pcm_substream *substream, |
| @@ -985,7 +1034,7 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream) | |||
| 985 | * Configure the threshold levels, and leave 10 sample space | 1034 | * Configure the threshold levels, and leave 10 sample space |
| 986 | * at the bottom, and also at the top of the FIFO | 1035 | * at the bottom, and also at the top of the FIFO |
| 987 | */ | 1036 | */ |
| 988 | dac33_write16(codec, DAC33_UTHR_MSB, DAC33_THRREG(MODE7_UTHR)); | 1037 | dac33_write16(codec, DAC33_UTHR_MSB, DAC33_THRREG(dac33->uthr)); |
| 989 | dac33_write16(codec, DAC33_LTHR_MSB, DAC33_THRREG(MODE7_LTHR)); | 1038 | dac33_write16(codec, DAC33_LTHR_MSB, DAC33_THRREG(MODE7_LTHR)); |
| 990 | break; | 1039 | break; |
| 991 | default: | 1040 | default: |
| @@ -1003,57 +1052,71 @@ static void dac33_calculate_times(struct snd_pcm_substream *substream) | |||
| 1003 | struct snd_soc_device *socdev = rtd->socdev; | 1052 | struct snd_soc_device *socdev = rtd->socdev; |
| 1004 | struct snd_soc_codec *codec = socdev->card->codec; | 1053 | struct snd_soc_codec *codec = socdev->card->codec; |
| 1005 | struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); | 1054 | struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); |
| 1055 | unsigned int period_size = substream->runtime->period_size; | ||
| 1056 | unsigned int rate = substream->runtime->rate; | ||
| 1006 | unsigned int nsample_limit; | 1057 | unsigned int nsample_limit; |
| 1007 | 1058 | ||
| 1008 | /* In bypass mode we don't need to calculate */ | 1059 | /* In bypass mode we don't need to calculate */ |
| 1009 | if (!dac33->fifo_mode) | 1060 | if (!dac33->fifo_mode) |
| 1010 | return; | 1061 | return; |
| 1011 | 1062 | ||
| 1012 | /* Number of samples (16bit, stereo) in one period */ | ||
| 1013 | dac33->nsample_min = snd_pcm_lib_period_bytes(substream) / 4; | ||
| 1014 | |||
| 1015 | /* Number of samples (16bit, stereo) in ALSA buffer */ | ||
| 1016 | dac33->nsample_max = snd_pcm_lib_buffer_bytes(substream) / 4; | ||
| 1017 | /* Subtract one period from the total */ | ||
| 1018 | dac33->nsample_max -= dac33->nsample_min; | ||
| 1019 | |||
| 1020 | /* Number of samples for LATENCY_TIME_MS / 2 */ | ||
| 1021 | dac33->alarm_threshold = substream->runtime->rate / | ||
| 1022 | (1000 / (LATENCY_TIME_MS / 2)); | ||
| 1023 | |||
| 1024 | /* Find and fix up the lowest nsmaple limit */ | ||
| 1025 | nsample_limit = substream->runtime->rate / (1000 / LATENCY_TIME_MS); | ||
| 1026 | |||
| 1027 | if (dac33->nsample_min < nsample_limit) | ||
| 1028 | dac33->nsample_min = nsample_limit; | ||
| 1029 | |||
| 1030 | if (dac33->nsample < dac33->nsample_min) | ||
| 1031 | dac33->nsample = dac33->nsample_min; | ||
| 1032 | |||
| 1033 | /* | ||
| 1034 | * Find and fix up the highest nsmaple limit | ||
| 1035 | * In order to not overflow the DAC33 buffer substract the | ||
| 1036 | * alarm_threshold value from the size of the DAC33 buffer | ||
| 1037 | */ | ||
| 1038 | nsample_limit = DAC33_BUFFER_SIZE_SAMPLES - dac33->alarm_threshold; | ||
| 1039 | |||
| 1040 | if (dac33->nsample_max > nsample_limit) | ||
| 1041 | dac33->nsample_max = nsample_limit; | ||
| 1042 | |||
| 1043 | if (dac33->nsample > dac33->nsample_max) | ||
| 1044 | dac33->nsample = dac33->nsample_max; | ||
| 1045 | |||
| 1046 | switch (dac33->fifo_mode) { | 1063 | switch (dac33->fifo_mode) { |
| 1047 | case DAC33_FIFO_MODE1: | 1064 | case DAC33_FIFO_MODE1: |
| 1065 | /* Number of samples under i2c latency */ | ||
| 1066 | dac33->alarm_threshold = US_TO_SAMPLES(rate, | ||
| 1067 | dac33->mode1_latency); | ||
| 1068 | if (dac33->auto_fifo_config) { | ||
| 1069 | if (period_size <= dac33->alarm_threshold) | ||
| 1070 | /* | ||
| 1071 | * Configure nSamaple to number of periods, | ||
| 1072 | * which covers the latency requironment. | ||
| 1073 | */ | ||
| 1074 | dac33->nsample = period_size * | ||
| 1075 | ((dac33->alarm_threshold / period_size) + | ||
| 1076 | (dac33->alarm_threshold % period_size ? | ||
| 1077 | 1 : 0)); | ||
| 1078 | else | ||
| 1079 | dac33->nsample = period_size; | ||
| 1080 | } else { | ||
| 1081 | /* nSample time shall not be shorter than i2c latency */ | ||
| 1082 | dac33->nsample_min = dac33->alarm_threshold; | ||
| 1083 | /* | ||
| 1084 | * nSample should not be bigger than alsa buffer minus | ||
| 1085 | * size of one period to avoid overruns | ||
| 1086 | */ | ||
| 1087 | dac33->nsample_max = substream->runtime->buffer_size - | ||
| 1088 | period_size; | ||
| 1089 | nsample_limit = DAC33_BUFFER_SIZE_SAMPLES - | ||
| 1090 | dac33->alarm_threshold; | ||
| 1091 | if (dac33->nsample_max > nsample_limit) | ||
| 1092 | dac33->nsample_max = nsample_limit; | ||
| 1093 | |||
| 1094 | /* Correct the nSample if it is outside of the ranges */ | ||
| 1095 | if (dac33->nsample < dac33->nsample_min) | ||
| 1096 | dac33->nsample = dac33->nsample_min; | ||
| 1097 | if (dac33->nsample > dac33->nsample_max) | ||
| 1098 | dac33->nsample = dac33->nsample_max; | ||
| 1099 | } | ||
| 1100 | |||
| 1048 | dac33->mode1_us_burst = SAMPLES_TO_US(dac33->burst_rate, | 1101 | dac33->mode1_us_burst = SAMPLES_TO_US(dac33->burst_rate, |
| 1049 | dac33->nsample); | 1102 | dac33->nsample); |
| 1050 | dac33->t_stamp1 = 0; | 1103 | dac33->t_stamp1 = 0; |
| 1051 | dac33->t_stamp2 = 0; | 1104 | dac33->t_stamp2 = 0; |
| 1052 | break; | 1105 | break; |
| 1053 | case DAC33_FIFO_MODE7: | 1106 | case DAC33_FIFO_MODE7: |
| 1107 | if (dac33->auto_fifo_config) { | ||
| 1108 | dac33->uthr = UTHR_FROM_PERIOD_SIZE( | ||
| 1109 | period_size, | ||
| 1110 | rate, | ||
| 1111 | dac33->burst_rate) + 9; | ||
| 1112 | if (dac33->uthr > MODE7_UTHR) | ||
| 1113 | dac33->uthr = MODE7_UTHR; | ||
| 1114 | if (dac33->uthr < (MODE7_LTHR + 10)) | ||
| 1115 | dac33->uthr = (MODE7_LTHR + 10); | ||
| 1116 | } | ||
| 1054 | dac33->mode7_us_to_lthr = | 1117 | dac33->mode7_us_to_lthr = |
| 1055 | SAMPLES_TO_US(substream->runtime->rate, | 1118 | SAMPLES_TO_US(substream->runtime->rate, |
| 1056 | MODE7_UTHR - MODE7_LTHR + 1); | 1119 | dac33->uthr - MODE7_LTHR + 1); |
| 1057 | dac33->t_stamp1 = 0; | 1120 | dac33->t_stamp1 = 0; |
| 1058 | break; | 1121 | break; |
| 1059 | default: | 1122 | default: |
| @@ -1104,7 +1167,7 @@ static snd_pcm_sframes_t dac33_dai_delay( | |||
| 1104 | struct snd_soc_codec *codec = socdev->card->codec; | 1167 | struct snd_soc_codec *codec = socdev->card->codec; |
| 1105 | struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); | 1168 | struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); |
| 1106 | unsigned long long t0, t1, t_now; | 1169 | unsigned long long t0, t1, t_now; |
| 1107 | unsigned int time_delta; | 1170 | unsigned int time_delta, uthr; |
| 1108 | int samples_out, samples_in, samples; | 1171 | int samples_out, samples_in, samples; |
| 1109 | snd_pcm_sframes_t delay = 0; | 1172 | snd_pcm_sframes_t delay = 0; |
| 1110 | 1173 | ||
| @@ -1182,6 +1245,7 @@ static snd_pcm_sframes_t dac33_dai_delay( | |||
| 1182 | case DAC33_FIFO_MODE7: | 1245 | case DAC33_FIFO_MODE7: |
| 1183 | spin_lock(&dac33->lock); | 1246 | spin_lock(&dac33->lock); |
| 1184 | t0 = dac33->t_stamp1; | 1247 | t0 = dac33->t_stamp1; |
| 1248 | uthr = dac33->uthr; | ||
| 1185 | spin_unlock(&dac33->lock); | 1249 | spin_unlock(&dac33->lock); |
| 1186 | t_now = ktime_to_us(ktime_get()); | 1250 | t_now = ktime_to_us(ktime_get()); |
| 1187 | 1251 | ||
| @@ -1194,7 +1258,7 @@ static snd_pcm_sframes_t dac33_dai_delay( | |||
| 1194 | * Either the timestamps are messed or equal. Report | 1258 | * Either the timestamps are messed or equal. Report |
| 1195 | * maximum delay | 1259 | * maximum delay |
| 1196 | */ | 1260 | */ |
| 1197 | delay = MODE7_UTHR; | 1261 | delay = uthr; |
| 1198 | goto out; | 1262 | goto out; |
| 1199 | } | 1263 | } |
| 1200 | 1264 | ||
| @@ -1208,8 +1272,8 @@ static snd_pcm_sframes_t dac33_dai_delay( | |||
| 1208 | substream->runtime->rate, | 1272 | substream->runtime->rate, |
| 1209 | time_delta); | 1273 | time_delta); |
| 1210 | 1274 | ||
| 1211 | if (likely(MODE7_UTHR > samples_out)) | 1275 | if (likely(uthr > samples_out)) |
| 1212 | delay = MODE7_UTHR - samples_out; | 1276 | delay = uthr - samples_out; |
| 1213 | else | 1277 | else |
| 1214 | delay = 0; | 1278 | delay = 0; |
| 1215 | } else { | 1279 | } else { |
| @@ -1227,8 +1291,8 @@ static snd_pcm_sframes_t dac33_dai_delay( | |||
| 1227 | time_delta); | 1291 | time_delta); |
| 1228 | delay = MODE7_LTHR + samples_in - samples_out; | 1292 | delay = MODE7_LTHR + samples_in - samples_out; |
| 1229 | 1293 | ||
| 1230 | if (unlikely(delay > MODE7_UTHR)) | 1294 | if (unlikely(delay > uthr)) |
| 1231 | delay = MODE7_UTHR; | 1295 | delay = uthr; |
| 1232 | } | 1296 | } |
| 1233 | break; | 1297 | break; |
| 1234 | default: | 1298 | default: |
| @@ -1347,10 +1411,15 @@ static int dac33_soc_probe(struct platform_device *pdev) | |||
| 1347 | 1411 | ||
| 1348 | snd_soc_add_controls(codec, dac33_snd_controls, | 1412 | snd_soc_add_controls(codec, dac33_snd_controls, |
| 1349 | ARRAY_SIZE(dac33_snd_controls)); | 1413 | ARRAY_SIZE(dac33_snd_controls)); |
| 1350 | /* Only add the nSample controls, if we have valid IRQ number */ | 1414 | /* Only add the FIFO controls, if we have valid IRQ number */ |
| 1351 | if (dac33->irq >= 0) | 1415 | if (dac33->irq >= 0) { |
| 1352 | snd_soc_add_controls(codec, dac33_nsample_snd_controls, | 1416 | snd_soc_add_controls(codec, dac33_mode_snd_controls, |
| 1353 | ARRAY_SIZE(dac33_nsample_snd_controls)); | 1417 | ARRAY_SIZE(dac33_mode_snd_controls)); |
| 1418 | /* FIFO usage controls only, if autoio config is not selected */ | ||
| 1419 | if (!dac33->auto_fifo_config) | ||
| 1420 | snd_soc_add_controls(codec, dac33_fifo_snd_controls, | ||
| 1421 | ARRAY_SIZE(dac33_fifo_snd_controls)); | ||
| 1422 | } | ||
| 1354 | 1423 | ||
| 1355 | dac33_add_widgets(codec); | 1424 | dac33_add_widgets(codec); |
| 1356 | 1425 | ||
| @@ -1481,9 +1550,14 @@ static int __devinit dac33_i2c_probe(struct i2c_client *client, | |||
| 1481 | /* Pre calculate the burst rate */ | 1550 | /* Pre calculate the burst rate */ |
| 1482 | dac33->burst_rate = BURST_BASEFREQ_HZ / dac33->burst_bclkdiv / 32; | 1551 | dac33->burst_rate = BURST_BASEFREQ_HZ / dac33->burst_bclkdiv / 32; |
| 1483 | dac33->keep_bclk = pdata->keep_bclk; | 1552 | dac33->keep_bclk = pdata->keep_bclk; |
| 1553 | dac33->auto_fifo_config = pdata->auto_fifo_config; | ||
| 1554 | dac33->mode1_latency = pdata->mode1_latency; | ||
| 1555 | if (!dac33->mode1_latency) | ||
| 1556 | dac33->mode1_latency = 10000; /* 10ms */ | ||
| 1484 | dac33->irq = client->irq; | 1557 | dac33->irq = client->irq; |
| 1485 | dac33->nsample = NSAMPLE_MAX; | 1558 | dac33->nsample = NSAMPLE_MAX; |
| 1486 | dac33->nsample_max = NSAMPLE_MAX; | 1559 | dac33->nsample_max = NSAMPLE_MAX; |
| 1560 | dac33->uthr = MODE7_UTHR; | ||
| 1487 | /* Disable FIFO use by default */ | 1561 | /* Disable FIFO use by default */ |
| 1488 | dac33->fifo_mode = DAC33_FIFO_BYPASS; | 1562 | dac33->fifo_mode = DAC33_FIFO_BYPASS; |
| 1489 | 1563 | ||
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index b4fcdb01fc49..7b618bbff884 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c | |||
| @@ -43,37 +43,37 @@ | |||
| 43 | */ | 43 | */ |
| 44 | static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { | 44 | static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { |
| 45 | 0x00, /* this register not used */ | 45 | 0x00, /* this register not used */ |
| 46 | 0x91, /* REG_CODEC_MODE (0x1) */ | 46 | 0x00, /* REG_CODEC_MODE (0x1) */ |
| 47 | 0xc3, /* REG_OPTION (0x2) */ | 47 | 0x00, /* REG_OPTION (0x2) */ |
| 48 | 0x00, /* REG_UNKNOWN (0x3) */ | 48 | 0x00, /* REG_UNKNOWN (0x3) */ |
| 49 | 0x00, /* REG_MICBIAS_CTL (0x4) */ | 49 | 0x00, /* REG_MICBIAS_CTL (0x4) */ |
| 50 | 0x20, /* REG_ANAMICL (0x5) */ | 50 | 0x00, /* REG_ANAMICL (0x5) */ |
| 51 | 0x00, /* REG_ANAMICR (0x6) */ | 51 | 0x00, /* REG_ANAMICR (0x6) */ |
| 52 | 0x00, /* REG_AVADC_CTL (0x7) */ | 52 | 0x00, /* REG_AVADC_CTL (0x7) */ |
| 53 | 0x00, /* REG_ADCMICSEL (0x8) */ | 53 | 0x00, /* REG_ADCMICSEL (0x8) */ |
| 54 | 0x00, /* REG_DIGMIXING (0x9) */ | 54 | 0x00, /* REG_DIGMIXING (0x9) */ |
| 55 | 0x0c, /* REG_ATXL1PGA (0xA) */ | 55 | 0x0f, /* REG_ATXL1PGA (0xA) */ |
| 56 | 0x0c, /* REG_ATXR1PGA (0xB) */ | 56 | 0x0f, /* REG_ATXR1PGA (0xB) */ |
| 57 | 0x00, /* REG_AVTXL2PGA (0xC) */ | 57 | 0x0f, /* REG_AVTXL2PGA (0xC) */ |
| 58 | 0x00, /* REG_AVTXR2PGA (0xD) */ | 58 | 0x0f, /* REG_AVTXR2PGA (0xD) */ |
| 59 | 0x00, /* REG_AUDIO_IF (0xE) */ | 59 | 0x00, /* REG_AUDIO_IF (0xE) */ |
| 60 | 0x00, /* REG_VOICE_IF (0xF) */ | 60 | 0x00, /* REG_VOICE_IF (0xF) */ |
| 61 | 0x00, /* REG_ARXR1PGA (0x10) */ | 61 | 0x3f, /* REG_ARXR1PGA (0x10) */ |
| 62 | 0x00, /* REG_ARXL1PGA (0x11) */ | 62 | 0x3f, /* REG_ARXL1PGA (0x11) */ |
| 63 | 0x6c, /* REG_ARXR2PGA (0x12) */ | 63 | 0x3f, /* REG_ARXR2PGA (0x12) */ |
| 64 | 0x6c, /* REG_ARXL2PGA (0x13) */ | 64 | 0x3f, /* REG_ARXL2PGA (0x13) */ |
| 65 | 0x00, /* REG_VRXPGA (0x14) */ | 65 | 0x25, /* REG_VRXPGA (0x14) */ |
| 66 | 0x00, /* REG_VSTPGA (0x15) */ | 66 | 0x00, /* REG_VSTPGA (0x15) */ |
| 67 | 0x00, /* REG_VRX2ARXPGA (0x16) */ | 67 | 0x00, /* REG_VRX2ARXPGA (0x16) */ |
| 68 | 0x00, /* REG_AVDAC_CTL (0x17) */ | 68 | 0x00, /* REG_AVDAC_CTL (0x17) */ |
| 69 | 0x00, /* REG_ARX2VTXPGA (0x18) */ | 69 | 0x00, /* REG_ARX2VTXPGA (0x18) */ |
| 70 | 0x00, /* REG_ARXL1_APGA_CTL (0x19) */ | 70 | 0x32, /* REG_ARXL1_APGA_CTL (0x19) */ |
| 71 | 0x00, /* REG_ARXR1_APGA_CTL (0x1A) */ | 71 | 0x32, /* REG_ARXR1_APGA_CTL (0x1A) */ |
| 72 | 0x4a, /* REG_ARXL2_APGA_CTL (0x1B) */ | 72 | 0x32, /* REG_ARXL2_APGA_CTL (0x1B) */ |
| 73 | 0x4a, /* REG_ARXR2_APGA_CTL (0x1C) */ | 73 | 0x32, /* REG_ARXR2_APGA_CTL (0x1C) */ |
| 74 | 0x00, /* REG_ATX2ARXPGA (0x1D) */ | 74 | 0x00, /* REG_ATX2ARXPGA (0x1D) */ |
| 75 | 0x00, /* REG_BT_IF (0x1E) */ | 75 | 0x00, /* REG_BT_IF (0x1E) */ |
| 76 | 0x00, /* REG_BTPGA (0x1F) */ | 76 | 0x55, /* REG_BTPGA (0x1F) */ |
| 77 | 0x00, /* REG_BTSTPGA (0x20) */ | 77 | 0x00, /* REG_BTSTPGA (0x20) */ |
| 78 | 0x00, /* REG_EAR_CTL (0x21) */ | 78 | 0x00, /* REG_EAR_CTL (0x21) */ |
| 79 | 0x00, /* REG_HS_SEL (0x22) */ | 79 | 0x00, /* REG_HS_SEL (0x22) */ |
| @@ -85,32 +85,32 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { | |||
| 85 | 0x00, /* REG_PRECKR_CTL (0x28) */ | 85 | 0x00, /* REG_PRECKR_CTL (0x28) */ |
| 86 | 0x00, /* REG_HFL_CTL (0x29) */ | 86 | 0x00, /* REG_HFL_CTL (0x29) */ |
| 87 | 0x00, /* REG_HFR_CTL (0x2A) */ | 87 | 0x00, /* REG_HFR_CTL (0x2A) */ |
| 88 | 0x00, /* REG_ALC_CTL (0x2B) */ | 88 | 0x05, /* REG_ALC_CTL (0x2B) */ |
| 89 | 0x00, /* REG_ALC_SET1 (0x2C) */ | 89 | 0x00, /* REG_ALC_SET1 (0x2C) */ |
| 90 | 0x00, /* REG_ALC_SET2 (0x2D) */ | 90 | 0x00, /* REG_ALC_SET2 (0x2D) */ |
| 91 | 0x00, /* REG_BOOST_CTL (0x2E) */ | 91 | 0x00, /* REG_BOOST_CTL (0x2E) */ |
| 92 | 0x00, /* REG_SOFTVOL_CTL (0x2F) */ | 92 | 0x00, /* REG_SOFTVOL_CTL (0x2F) */ |
| 93 | 0x00, /* REG_DTMF_FREQSEL (0x30) */ | 93 | 0x13, /* REG_DTMF_FREQSEL (0x30) */ |
| 94 | 0x00, /* REG_DTMF_TONEXT1H (0x31) */ | 94 | 0x00, /* REG_DTMF_TONEXT1H (0x31) */ |
| 95 | 0x00, /* REG_DTMF_TONEXT1L (0x32) */ | 95 | 0x00, /* REG_DTMF_TONEXT1L (0x32) */ |
| 96 | 0x00, /* REG_DTMF_TONEXT2H (0x33) */ | 96 | 0x00, /* REG_DTMF_TONEXT2H (0x33) */ |
| 97 | 0x00, /* REG_DTMF_TONEXT2L (0x34) */ | 97 | 0x00, /* REG_DTMF_TONEXT2L (0x34) */ |
| 98 | 0x00, /* REG_DTMF_TONOFF (0x35) */ | 98 | 0x79, /* REG_DTMF_TONOFF (0x35) */ |
| 99 | 0x00, /* REG_DTMF_WANONOFF (0x36) */ | 99 | 0x11, /* REG_DTMF_WANONOFF (0x36) */ |
| 100 | 0x00, /* REG_I2S_RX_SCRAMBLE_H (0x37) */ | 100 | 0x00, /* REG_I2S_RX_SCRAMBLE_H (0x37) */ |
| 101 | 0x00, /* REG_I2S_RX_SCRAMBLE_M (0x38) */ | 101 | 0x00, /* REG_I2S_RX_SCRAMBLE_M (0x38) */ |
| 102 | 0x00, /* REG_I2S_RX_SCRAMBLE_L (0x39) */ | 102 | 0x00, /* REG_I2S_RX_SCRAMBLE_L (0x39) */ |
| 103 | 0x06, /* REG_APLL_CTL (0x3A) */ | 103 | 0x06, /* REG_APLL_CTL (0x3A) */ |
| 104 | 0x00, /* REG_DTMF_CTL (0x3B) */ | 104 | 0x00, /* REG_DTMF_CTL (0x3B) */ |
| 105 | 0x00, /* REG_DTMF_PGA_CTL2 (0x3C) */ | 105 | 0x44, /* REG_DTMF_PGA_CTL2 (0x3C) */ |
| 106 | 0x00, /* REG_DTMF_PGA_CTL1 (0x3D) */ | 106 | 0x69, /* REG_DTMF_PGA_CTL1 (0x3D) */ |
| 107 | 0x00, /* REG_MISC_SET_1 (0x3E) */ | 107 | 0x00, /* REG_MISC_SET_1 (0x3E) */ |
| 108 | 0x00, /* REG_PCMBTMUX (0x3F) */ | 108 | 0x00, /* REG_PCMBTMUX (0x3F) */ |
| 109 | 0x00, /* not used (0x40) */ | 109 | 0x00, /* not used (0x40) */ |
| 110 | 0x00, /* not used (0x41) */ | 110 | 0x00, /* not used (0x41) */ |
| 111 | 0x00, /* not used (0x42) */ | 111 | 0x00, /* not used (0x42) */ |
| 112 | 0x00, /* REG_RX_PATH_SEL (0x43) */ | 112 | 0x00, /* REG_RX_PATH_SEL (0x43) */ |
| 113 | 0x00, /* REG_VDL_APGA_CTL (0x44) */ | 113 | 0x32, /* REG_VDL_APGA_CTL (0x44) */ |
| 114 | 0x00, /* REG_VIBRA_CTL (0x45) */ | 114 | 0x00, /* REG_VIBRA_CTL (0x45) */ |
| 115 | 0x00, /* REG_VIBRA_SET (0x46) */ | 115 | 0x00, /* REG_VIBRA_SET (0x46) */ |
| 116 | 0x00, /* REG_VIBRA_PWM_SET (0x47) */ | 116 | 0x00, /* REG_VIBRA_PWM_SET (0x47) */ |
| @@ -143,6 +143,9 @@ struct twl4030_priv { | |||
| 143 | u8 earpiece_enabled; | 143 | u8 earpiece_enabled; |
| 144 | u8 predrivel_enabled, predriver_enabled; | 144 | u8 predrivel_enabled, predriver_enabled; |
| 145 | u8 carkitl_enabled, carkitr_enabled; | 145 | u8 carkitl_enabled, carkitr_enabled; |
| 146 | |||
| 147 | /* Delay needed after enabling the digimic interface */ | ||
| 148 | unsigned int digimic_delay; | ||
| 146 | }; | 149 | }; |
| 147 | 150 | ||
| 148 | /* | 151 | /* |
| @@ -244,58 +247,95 @@ static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) | |||
| 244 | udelay(10); | 247 | udelay(10); |
| 245 | } | 248 | } |
| 246 | 249 | ||
| 247 | static void twl4030_init_chip(struct snd_soc_codec *codec) | 250 | static inline void twl4030_check_defaults(struct snd_soc_codec *codec) |
| 248 | { | 251 | { |
| 249 | u8 *cache = codec->reg_cache; | 252 | int i, difference = 0; |
| 250 | int i; | 253 | u8 val; |
| 254 | |||
| 255 | dev_dbg(codec->dev, "Checking TWL audio default configuration\n"); | ||
| 256 | for (i = 1; i <= TWL4030_REG_MISC_SET_2; i++) { | ||
| 257 | twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, i); | ||
| 258 | if (val != twl4030_reg[i]) { | ||
| 259 | difference++; | ||
| 260 | dev_dbg(codec->dev, | ||
| 261 | "Reg 0x%02x: chip: 0x%02x driver: 0x%02x\n", | ||
| 262 | i, val, twl4030_reg[i]); | ||
| 263 | } | ||
| 264 | } | ||
| 265 | dev_dbg(codec->dev, "Found %d non maching registers. %s\n", | ||
| 266 | difference, difference ? "Not OK" : "OK"); | ||
| 267 | } | ||
| 251 | 268 | ||
| 252 | /* clear CODECPDZ prior to setting register defaults */ | 269 | static inline void twl4030_reset_registers(struct snd_soc_codec *codec) |
| 253 | twl4030_codec_enable(codec, 0); | 270 | { |
| 271 | int i; | ||
| 254 | 272 | ||
| 255 | /* set all audio section registers to reasonable defaults */ | 273 | /* set all audio section registers to reasonable defaults */ |
| 256 | for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++) | 274 | for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++) |
| 257 | if (i != TWL4030_REG_APLL_CTL) | 275 | if (i != TWL4030_REG_APLL_CTL) |
| 258 | twl4030_write(codec, i, cache[i]); | 276 | twl4030_write(codec, i, twl4030_reg[i]); |
| 259 | 277 | ||
| 260 | } | 278 | } |
| 261 | 279 | ||
| 262 | static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable) | 280 | static void twl4030_init_chip(struct platform_device *pdev) |
| 263 | { | 281 | { |
| 282 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
| 283 | struct twl4030_setup_data *setup = socdev->codec_data; | ||
| 284 | struct snd_soc_codec *codec = socdev->card->codec; | ||
| 264 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 285 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); |
| 265 | int status = -1; | 286 | u8 reg, byte; |
| 287 | int i = 0; | ||
| 266 | 288 | ||
| 267 | if (enable) { | 289 | /* Check defaults, if instructed before anything else */ |
| 268 | twl4030->apll_enabled++; | 290 | if (setup && setup->check_defaults) |
| 269 | if (twl4030->apll_enabled == 1) | 291 | twl4030_check_defaults(codec); |
| 270 | status = twl4030_codec_enable_resource( | ||
| 271 | TWL4030_CODEC_RES_APLL); | ||
| 272 | } else { | ||
| 273 | twl4030->apll_enabled--; | ||
| 274 | if (!twl4030->apll_enabled) | ||
| 275 | status = twl4030_codec_disable_resource( | ||
| 276 | TWL4030_CODEC_RES_APLL); | ||
| 277 | } | ||
| 278 | 292 | ||
| 279 | if (status >= 0) | 293 | /* Reset registers, if no setup data or if instructed to do so */ |
| 280 | twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status); | 294 | if (!setup || (setup && setup->reset_registers)) |
| 281 | } | 295 | twl4030_reset_registers(codec); |
| 282 | 296 | ||
| 283 | static void twl4030_power_up(struct snd_soc_codec *codec) | 297 | /* Refresh APLL_CTL register from HW */ |
| 284 | { | 298 | twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, |
| 285 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 299 | TWL4030_REG_APLL_CTL); |
| 286 | u8 anamicl, regmisc1, byte; | 300 | twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, byte); |
| 287 | int i = 0; | 301 | |
| 302 | /* anti-pop when changing analog gain */ | ||
| 303 | reg = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1); | ||
| 304 | twl4030_write(codec, TWL4030_REG_MISC_SET_1, | ||
| 305 | reg | TWL4030_SMOOTH_ANAVOL_EN); | ||
| 288 | 306 | ||
| 289 | if (twl4030->codec_powered) | 307 | twl4030_write(codec, TWL4030_REG_OPTION, |
| 308 | TWL4030_ATXL1_EN | TWL4030_ATXR1_EN | | ||
| 309 | TWL4030_ARXL2_EN | TWL4030_ARXR2_EN); | ||
| 310 | |||
| 311 | /* REG_ARXR2_APGA_CTL reset according to the TRM: 0dB, DA_EN */ | ||
| 312 | twl4030_write(codec, TWL4030_REG_ARXR2_APGA_CTL, 0x32); | ||
| 313 | |||
| 314 | /* Machine dependent setup */ | ||
| 315 | if (!setup) | ||
| 290 | return; | 316 | return; |
| 291 | 317 | ||
| 292 | /* set CODECPDZ to turn on codec */ | 318 | twl4030->digimic_delay = setup->digimic_delay; |
| 293 | twl4030_codec_enable(codec, 1); | 319 | |
| 320 | /* Configuration for headset ramp delay from setup data */ | ||
| 321 | if (setup->sysclk != twl4030->sysclk) | ||
| 322 | dev_warn(codec->dev, | ||
| 323 | "Mismatch in APLL mclk: %u (configured: %u)\n", | ||
| 324 | setup->sysclk, twl4030->sysclk); | ||
| 325 | |||
| 326 | reg = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); | ||
| 327 | reg &= ~TWL4030_RAMP_DELAY; | ||
| 328 | reg |= (setup->ramp_delay_value << 2); | ||
| 329 | twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, reg); | ||
| 294 | 330 | ||
| 295 | /* initiate offset cancellation */ | 331 | /* initiate offset cancellation */ |
| 296 | anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL); | 332 | twl4030_codec_enable(codec, 1); |
| 333 | |||
| 334 | reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL); | ||
| 335 | reg &= ~TWL4030_OFFSET_CNCL_SEL; | ||
| 336 | reg |= setup->offset_cncl_path; | ||
| 297 | twl4030_write(codec, TWL4030_REG_ANAMICL, | 337 | twl4030_write(codec, TWL4030_REG_ANAMICL, |
| 298 | anamicl | TWL4030_CNCL_OFFSET_START); | 338 | reg | TWL4030_CNCL_OFFSET_START); |
| 299 | 339 | ||
| 300 | /* wait for offset cancellation to complete */ | 340 | /* wait for offset cancellation to complete */ |
| 301 | do { | 341 | do { |
| @@ -310,23 +350,28 @@ static void twl4030_power_up(struct snd_soc_codec *codec) | |||
| 310 | /* Make sure that the reg_cache has the same value as the HW */ | 350 | /* Make sure that the reg_cache has the same value as the HW */ |
| 311 | twl4030_write_reg_cache(codec, TWL4030_REG_ANAMICL, byte); | 351 | twl4030_write_reg_cache(codec, TWL4030_REG_ANAMICL, byte); |
| 312 | 352 | ||
| 313 | /* anti-pop when changing analog gain */ | ||
| 314 | regmisc1 = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1); | ||
| 315 | twl4030_write(codec, TWL4030_REG_MISC_SET_1, | ||
| 316 | regmisc1 | TWL4030_SMOOTH_ANAVOL_EN); | ||
| 317 | |||
| 318 | /* toggle CODECPDZ as per TRM */ | ||
| 319 | twl4030_codec_enable(codec, 0); | 353 | twl4030_codec_enable(codec, 0); |
| 320 | twl4030_codec_enable(codec, 1); | ||
| 321 | } | 354 | } |
| 322 | 355 | ||
| 323 | /* | 356 | static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable) |
| 324 | * Unconditional power down | ||
| 325 | */ | ||
| 326 | static void twl4030_power_down(struct snd_soc_codec *codec) | ||
| 327 | { | 357 | { |
| 328 | /* power down */ | 358 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); |
| 329 | twl4030_codec_enable(codec, 0); | 359 | int status = -1; |
| 360 | |||
| 361 | if (enable) { | ||
| 362 | twl4030->apll_enabled++; | ||
| 363 | if (twl4030->apll_enabled == 1) | ||
| 364 | status = twl4030_codec_enable_resource( | ||
| 365 | TWL4030_CODEC_RES_APLL); | ||
| 366 | } else { | ||
| 367 | twl4030->apll_enabled--; | ||
| 368 | if (!twl4030->apll_enabled) | ||
| 369 | status = twl4030_codec_disable_resource( | ||
| 370 | TWL4030_CODEC_RES_APLL); | ||
| 371 | } | ||
| 372 | |||
| 373 | if (status >= 0) | ||
| 374 | twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status); | ||
| 330 | } | 375 | } |
| 331 | 376 | ||
| 332 | /* Earpiece */ | 377 | /* Earpiece */ |
| @@ -500,10 +545,11 @@ static const struct snd_kcontrol_new twl4030_dapm_abypassl2_control = | |||
| 500 | static const struct snd_kcontrol_new twl4030_dapm_abypassv_control = | 545 | static const struct snd_kcontrol_new twl4030_dapm_abypassv_control = |
| 501 | SOC_DAPM_SINGLE("Switch", TWL4030_REG_VDL_APGA_CTL, 2, 1, 0); | 546 | SOC_DAPM_SINGLE("Switch", TWL4030_REG_VDL_APGA_CTL, 2, 1, 0); |
| 502 | 547 | ||
| 503 | /* Digital bypass gain, 0 mutes the bypass */ | 548 | /* Digital bypass gain, mute instead of -30dB */ |
| 504 | static const unsigned int twl4030_dapm_dbypass_tlv[] = { | 549 | static const unsigned int twl4030_dapm_dbypass_tlv[] = { |
| 505 | TLV_DB_RANGE_HEAD(2), | 550 | TLV_DB_RANGE_HEAD(3), |
| 506 | 0, 3, TLV_DB_SCALE_ITEM(-2400, 0, 1), | 551 | 0, 1, TLV_DB_SCALE_ITEM(-3000, 600, 1), |
| 552 | 2, 3, TLV_DB_SCALE_ITEM(-2400, 0, 0), | ||
| 507 | 4, 7, TLV_DB_SCALE_ITEM(-1800, 600, 0), | 553 | 4, 7, TLV_DB_SCALE_ITEM(-1800, 600, 0), |
| 508 | }; | 554 | }; |
| 509 | 555 | ||
| @@ -531,36 +577,6 @@ static const struct snd_kcontrol_new twl4030_dapm_dbypassv_control = | |||
| 531 | TWL4030_REG_VSTPGA, 0, 0x29, 0, | 577 | TWL4030_REG_VSTPGA, 0, 0x29, 0, |
| 532 | twl4030_dapm_dbypassv_tlv); | 578 | twl4030_dapm_dbypassv_tlv); |
| 533 | 579 | ||
| 534 | static int micpath_event(struct snd_soc_dapm_widget *w, | ||
| 535 | struct snd_kcontrol *kcontrol, int event) | ||
| 536 | { | ||
| 537 | struct soc_enum *e = (struct soc_enum *)w->kcontrols->private_value; | ||
| 538 | unsigned char adcmicsel, micbias_ctl; | ||
| 539 | |||
| 540 | adcmicsel = twl4030_read_reg_cache(w->codec, TWL4030_REG_ADCMICSEL); | ||
| 541 | micbias_ctl = twl4030_read_reg_cache(w->codec, TWL4030_REG_MICBIAS_CTL); | ||
| 542 | /* Prepare the bits for the given TX path: | ||
| 543 | * shift_l == 0: TX1 microphone path | ||
| 544 | * shift_l == 2: TX2 microphone path */ | ||
| 545 | if (e->shift_l) { | ||
| 546 | /* TX2 microphone path */ | ||
| 547 | if (adcmicsel & TWL4030_TX2IN_SEL) | ||
| 548 | micbias_ctl |= TWL4030_MICBIAS2_CTL; /* digimic */ | ||
| 549 | else | ||
| 550 | micbias_ctl &= ~TWL4030_MICBIAS2_CTL; | ||
| 551 | } else { | ||
| 552 | /* TX1 microphone path */ | ||
| 553 | if (adcmicsel & TWL4030_TX1IN_SEL) | ||
| 554 | micbias_ctl |= TWL4030_MICBIAS1_CTL; /* digimic */ | ||
| 555 | else | ||
| 556 | micbias_ctl &= ~TWL4030_MICBIAS1_CTL; | ||
| 557 | } | ||
| 558 | |||
| 559 | twl4030_write(w->codec, TWL4030_REG_MICBIAS_CTL, micbias_ctl); | ||
| 560 | |||
| 561 | return 0; | ||
| 562 | } | ||
| 563 | |||
| 564 | /* | 580 | /* |
| 565 | * Output PGA builder: | 581 | * Output PGA builder: |
| 566 | * Handle the muting and unmuting of the given output (turning off the | 582 | * Handle the muting and unmuting of the given output (turning off the |
| @@ -814,6 +830,16 @@ static int headsetrpga_event(struct snd_soc_dapm_widget *w, | |||
| 814 | return 0; | 830 | return 0; |
| 815 | } | 831 | } |
| 816 | 832 | ||
| 833 | static int digimic_event(struct snd_soc_dapm_widget *w, | ||
| 834 | struct snd_kcontrol *kcontrol, int event) | ||
| 835 | { | ||
| 836 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec); | ||
| 837 | |||
| 838 | if (twl4030->digimic_delay) | ||
| 839 | mdelay(twl4030->digimic_delay); | ||
| 840 | return 0; | ||
| 841 | } | ||
| 842 | |||
| 817 | /* | 843 | /* |
| 818 | * Some of the gain controls in TWL (mostly those which are associated with | 844 | * Some of the gain controls in TWL (mostly those which are associated with |
| 819 | * the outputs) are implemented in an interesting way: | 845 | * the outputs) are implemented in an interesting way: |
| @@ -1374,14 +1400,10 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | |||
| 1374 | /* Analog/Digital mic path selection. | 1400 | /* Analog/Digital mic path selection. |
| 1375 | TX1 Left/Right: either analog Left/Right or Digimic0 | 1401 | TX1 Left/Right: either analog Left/Right or Digimic0 |
| 1376 | TX2 Left/Right: either analog Left/Right or Digimic1 */ | 1402 | TX2 Left/Right: either analog Left/Right or Digimic1 */ |
| 1377 | SND_SOC_DAPM_MUX_E("TX1 Capture Route", SND_SOC_NOPM, 0, 0, | 1403 | SND_SOC_DAPM_MUX("TX1 Capture Route", SND_SOC_NOPM, 0, 0, |
| 1378 | &twl4030_dapm_micpathtx1_control, micpath_event, | 1404 | &twl4030_dapm_micpathtx1_control), |
| 1379 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD| | 1405 | SND_SOC_DAPM_MUX("TX2 Capture Route", SND_SOC_NOPM, 0, 0, |
| 1380 | SND_SOC_DAPM_POST_REG), | 1406 | &twl4030_dapm_micpathtx2_control), |
| 1381 | SND_SOC_DAPM_MUX_E("TX2 Capture Route", SND_SOC_NOPM, 0, 0, | ||
| 1382 | &twl4030_dapm_micpathtx2_control, micpath_event, | ||
| 1383 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD| | ||
| 1384 | SND_SOC_DAPM_POST_REG), | ||
| 1385 | 1407 | ||
| 1386 | /* Analog input mixers for the capture amplifiers */ | 1408 | /* Analog input mixers for the capture amplifiers */ |
| 1387 | SND_SOC_DAPM_MIXER("Analog Left", | 1409 | SND_SOC_DAPM_MIXER("Analog Left", |
| @@ -1398,10 +1420,17 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | |||
| 1398 | SND_SOC_DAPM_PGA("ADC Physical Right", | 1420 | SND_SOC_DAPM_PGA("ADC Physical Right", |
| 1399 | TWL4030_REG_AVADC_CTL, 1, 0, NULL, 0), | 1421 | TWL4030_REG_AVADC_CTL, 1, 0, NULL, 0), |
| 1400 | 1422 | ||
| 1401 | SND_SOC_DAPM_PGA("Digimic0 Enable", | 1423 | SND_SOC_DAPM_PGA_E("Digimic0 Enable", |
| 1402 | TWL4030_REG_ADCMICSEL, 1, 0, NULL, 0), | 1424 | TWL4030_REG_ADCMICSEL, 1, 0, NULL, 0, |
| 1403 | SND_SOC_DAPM_PGA("Digimic1 Enable", | 1425 | digimic_event, SND_SOC_DAPM_POST_PMU), |
| 1404 | TWL4030_REG_ADCMICSEL, 3, 0, NULL, 0), | 1426 | SND_SOC_DAPM_PGA_E("Digimic1 Enable", |
| 1427 | TWL4030_REG_ADCMICSEL, 3, 0, NULL, 0, | ||
| 1428 | digimic_event, SND_SOC_DAPM_POST_PMU), | ||
| 1429 | |||
| 1430 | SND_SOC_DAPM_SUPPLY("micbias1 select", TWL4030_REG_MICBIAS_CTL, 5, 0, | ||
| 1431 | NULL, 0), | ||
| 1432 | SND_SOC_DAPM_SUPPLY("micbias2 select", TWL4030_REG_MICBIAS_CTL, 6, 0, | ||
| 1433 | NULL, 0), | ||
| 1405 | 1434 | ||
| 1406 | SND_SOC_DAPM_MICBIAS("Mic Bias 1", TWL4030_REG_MICBIAS_CTL, 0, 0), | 1435 | SND_SOC_DAPM_MICBIAS("Mic Bias 1", TWL4030_REG_MICBIAS_CTL, 0, 0), |
| 1407 | SND_SOC_DAPM_MICBIAS("Mic Bias 2", TWL4030_REG_MICBIAS_CTL, 1, 0), | 1436 | SND_SOC_DAPM_MICBIAS("Mic Bias 2", TWL4030_REG_MICBIAS_CTL, 1, 0), |
| @@ -1419,8 +1448,11 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
| 1419 | /* Supply for the digital part (APLL) */ | 1448 | /* Supply for the digital part (APLL) */ |
| 1420 | {"Digital Voice Playback Mixer", NULL, "APLL Enable"}, | 1449 | {"Digital Voice Playback Mixer", NULL, "APLL Enable"}, |
| 1421 | 1450 | ||
| 1422 | {"Digital R1 Playback Mixer", NULL, "AIF Enable"}, | 1451 | {"DAC Left1", NULL, "AIF Enable"}, |
| 1423 | {"Digital L1 Playback Mixer", NULL, "AIF Enable"}, | 1452 | {"DAC Right1", NULL, "AIF Enable"}, |
| 1453 | {"DAC Left2", NULL, "AIF Enable"}, | ||
| 1454 | {"DAC Right1", NULL, "AIF Enable"}, | ||
| 1455 | |||
| 1424 | {"Digital R2 Playback Mixer", NULL, "AIF Enable"}, | 1456 | {"Digital R2 Playback Mixer", NULL, "AIF Enable"}, |
| 1425 | {"Digital L2 Playback Mixer", NULL, "AIF Enable"}, | 1457 | {"Digital L2 Playback Mixer", NULL, "AIF Enable"}, |
| 1426 | 1458 | ||
| @@ -1491,10 +1523,10 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
| 1491 | 1523 | ||
| 1492 | /* outputs */ | 1524 | /* outputs */ |
| 1493 | /* Must be always connected (for AIF and APLL) */ | 1525 | /* Must be always connected (for AIF and APLL) */ |
| 1494 | {"Virtual HiFi OUT", NULL, "Digital L1 Playback Mixer"}, | 1526 | {"Virtual HiFi OUT", NULL, "DAC Left1"}, |
| 1495 | {"Virtual HiFi OUT", NULL, "Digital R1 Playback Mixer"}, | 1527 | {"Virtual HiFi OUT", NULL, "DAC Right1"}, |
| 1496 | {"Virtual HiFi OUT", NULL, "Digital L2 Playback Mixer"}, | 1528 | {"Virtual HiFi OUT", NULL, "DAC Left2"}, |
| 1497 | {"Virtual HiFi OUT", NULL, "Digital R2 Playback Mixer"}, | 1529 | {"Virtual HiFi OUT", NULL, "DAC Right2"}, |
| 1498 | /* Must be always connected (for APLL) */ | 1530 | /* Must be always connected (for APLL) */ |
| 1499 | {"Virtual Voice OUT", NULL, "Digital Voice Playback Mixer"}, | 1531 | {"Virtual Voice OUT", NULL, "Digital Voice Playback Mixer"}, |
| 1500 | /* Physical outputs */ | 1532 | /* Physical outputs */ |
| @@ -1531,6 +1563,9 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
| 1531 | {"Digimic0 Enable", NULL, "DIGIMIC0"}, | 1563 | {"Digimic0 Enable", NULL, "DIGIMIC0"}, |
| 1532 | {"Digimic1 Enable", NULL, "DIGIMIC1"}, | 1564 | {"Digimic1 Enable", NULL, "DIGIMIC1"}, |
| 1533 | 1565 | ||
| 1566 | {"DIGIMIC0", NULL, "micbias1 select"}, | ||
| 1567 | {"DIGIMIC1", NULL, "micbias2 select"}, | ||
| 1568 | |||
| 1534 | /* TX1 Left capture path */ | 1569 | /* TX1 Left capture path */ |
| 1535 | {"TX1 Capture Route", "Analog", "ADC Physical Left"}, | 1570 | {"TX1 Capture Route", "Analog", "ADC Physical Left"}, |
| 1536 | {"TX1 Capture Route", "Digimic0", "Digimic0 Enable"}, | 1571 | {"TX1 Capture Route", "Digimic0", "Digimic0 Enable"}, |
| @@ -1605,10 +1640,10 @@ static int twl4030_set_bias_level(struct snd_soc_codec *codec, | |||
| 1605 | break; | 1640 | break; |
| 1606 | case SND_SOC_BIAS_STANDBY: | 1641 | case SND_SOC_BIAS_STANDBY: |
| 1607 | if (codec->bias_level == SND_SOC_BIAS_OFF) | 1642 | if (codec->bias_level == SND_SOC_BIAS_OFF) |
| 1608 | twl4030_power_up(codec); | 1643 | twl4030_codec_enable(codec, 1); |
| 1609 | break; | 1644 | break; |
| 1610 | case SND_SOC_BIAS_OFF: | 1645 | case SND_SOC_BIAS_OFF: |
| 1611 | twl4030_power_down(codec); | 1646 | twl4030_codec_enable(codec, 0); |
| 1612 | break; | 1647 | break; |
| 1613 | } | 1648 | } |
| 1614 | codec->bias_level = level; | 1649 | codec->bias_level = level; |
| @@ -1794,13 +1829,6 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, | |||
| 1794 | return -EINVAL; | 1829 | return -EINVAL; |
| 1795 | } | 1830 | } |
| 1796 | 1831 | ||
| 1797 | if (mode != old_mode) { | ||
| 1798 | /* change rate and set CODECPDZ */ | ||
| 1799 | twl4030_codec_enable(codec, 0); | ||
| 1800 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | ||
| 1801 | twl4030_codec_enable(codec, 1); | ||
| 1802 | } | ||
| 1803 | |||
| 1804 | /* sample size */ | 1832 | /* sample size */ |
| 1805 | old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF); | 1833 | old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF); |
| 1806 | format = old_format; | 1834 | format = old_format; |
| @@ -1818,16 +1846,20 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, | |||
| 1818 | return -EINVAL; | 1846 | return -EINVAL; |
| 1819 | } | 1847 | } |
| 1820 | 1848 | ||
| 1821 | if (format != old_format) { | 1849 | if (format != old_format || mode != old_mode) { |
| 1822 | 1850 | if (twl4030->codec_powered) { | |
| 1823 | /* clear CODECPDZ before changing format (codec requirement) */ | 1851 | /* |
| 1824 | twl4030_codec_enable(codec, 0); | 1852 | * If the codec is powered, than we need to toggle the |
| 1825 | 1853 | * codec power. | |
| 1826 | /* change format */ | 1854 | */ |
| 1827 | twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); | 1855 | twl4030_codec_enable(codec, 0); |
| 1828 | 1856 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | |
| 1829 | /* set CODECPDZ afterwards */ | 1857 | twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); |
| 1830 | twl4030_codec_enable(codec, 1); | 1858 | twl4030_codec_enable(codec, 1); |
| 1859 | } else { | ||
| 1860 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | ||
| 1861 | twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); | ||
| 1862 | } | ||
| 1831 | } | 1863 | } |
| 1832 | 1864 | ||
| 1833 | /* Store the important parameters for the DAI configuration and set | 1865 | /* Store the important parameters for the DAI configuration and set |
| @@ -1877,6 +1909,7 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, | |||
| 1877 | unsigned int fmt) | 1909 | unsigned int fmt) |
| 1878 | { | 1910 | { |
| 1879 | struct snd_soc_codec *codec = codec_dai->codec; | 1911 | struct snd_soc_codec *codec = codec_dai->codec; |
| 1912 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | ||
| 1880 | u8 old_format, format; | 1913 | u8 old_format, format; |
| 1881 | 1914 | ||
| 1882 | /* get format */ | 1915 | /* get format */ |
| @@ -1911,15 +1944,17 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, | |||
| 1911 | } | 1944 | } |
| 1912 | 1945 | ||
| 1913 | if (format != old_format) { | 1946 | if (format != old_format) { |
| 1914 | 1947 | if (twl4030->codec_powered) { | |
| 1915 | /* clear CODECPDZ before changing format (codec requirement) */ | 1948 | /* |
| 1916 | twl4030_codec_enable(codec, 0); | 1949 | * If the codec is powered, than we need to toggle the |
| 1917 | 1950 | * codec power. | |
| 1918 | /* change format */ | 1951 | */ |
| 1919 | twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); | 1952 | twl4030_codec_enable(codec, 0); |
| 1920 | 1953 | twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); | |
| 1921 | /* set CODECPDZ afterwards */ | 1954 | twl4030_codec_enable(codec, 1); |
| 1922 | twl4030_codec_enable(codec, 1); | 1955 | } else { |
| 1956 | twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); | ||
| 1957 | } | ||
| 1923 | } | 1958 | } |
| 1924 | 1959 | ||
| 1925 | return 0; | 1960 | return 0; |
| @@ -2011,6 +2046,7 @@ static int twl4030_voice_hw_params(struct snd_pcm_substream *substream, | |||
| 2011 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 2046 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
| 2012 | struct snd_soc_device *socdev = rtd->socdev; | 2047 | struct snd_soc_device *socdev = rtd->socdev; |
| 2013 | struct snd_soc_codec *codec = socdev->card->codec; | 2048 | struct snd_soc_codec *codec = socdev->card->codec; |
| 2049 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | ||
| 2014 | u8 old_mode, mode; | 2050 | u8 old_mode, mode; |
| 2015 | 2051 | ||
| 2016 | /* Enable voice digital filters */ | 2052 | /* Enable voice digital filters */ |
| @@ -2035,10 +2071,17 @@ static int twl4030_voice_hw_params(struct snd_pcm_substream *substream, | |||
| 2035 | } | 2071 | } |
| 2036 | 2072 | ||
| 2037 | if (mode != old_mode) { | 2073 | if (mode != old_mode) { |
| 2038 | /* change rate and set CODECPDZ */ | 2074 | if (twl4030->codec_powered) { |
| 2039 | twl4030_codec_enable(codec, 0); | 2075 | /* |
| 2040 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | 2076 | * If the codec is powered, than we need to toggle the |
| 2041 | twl4030_codec_enable(codec, 1); | 2077 | * codec power. |
| 2078 | */ | ||
| 2079 | twl4030_codec_enable(codec, 0); | ||
| 2080 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | ||
| 2081 | twl4030_codec_enable(codec, 1); | ||
| 2082 | } else { | ||
| 2083 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | ||
| 2084 | } | ||
| 2042 | } | 2085 | } |
| 2043 | 2086 | ||
| 2044 | return 0; | 2087 | return 0; |
| @@ -2068,6 +2111,7 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, | |||
| 2068 | unsigned int fmt) | 2111 | unsigned int fmt) |
| 2069 | { | 2112 | { |
| 2070 | struct snd_soc_codec *codec = codec_dai->codec; | 2113 | struct snd_soc_codec *codec = codec_dai->codec; |
| 2114 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | ||
| 2071 | u8 old_format, format; | 2115 | u8 old_format, format; |
| 2072 | 2116 | ||
| 2073 | /* get format */ | 2117 | /* get format */ |
| @@ -2099,10 +2143,17 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, | |||
| 2099 | } | 2143 | } |
| 2100 | 2144 | ||
| 2101 | if (format != old_format) { | 2145 | if (format != old_format) { |
| 2102 | /* change format and set CODECPDZ */ | 2146 | if (twl4030->codec_powered) { |
| 2103 | twl4030_codec_enable(codec, 0); | 2147 | /* |
| 2104 | twl4030_write(codec, TWL4030_REG_VOICE_IF, format); | 2148 | * If the codec is powered, than we need to toggle the |
| 2105 | twl4030_codec_enable(codec, 1); | 2149 | * codec power. |
| 2150 | */ | ||
| 2151 | twl4030_codec_enable(codec, 0); | ||
| 2152 | twl4030_write(codec, TWL4030_REG_VOICE_IF, format); | ||
| 2153 | twl4030_codec_enable(codec, 1); | ||
| 2154 | } else { | ||
| 2155 | twl4030_write(codec, TWL4030_REG_VOICE_IF, format); | ||
| 2156 | } | ||
| 2106 | } | 2157 | } |
| 2107 | 2158 | ||
| 2108 | return 0; | 2159 | return 0; |
| @@ -2202,31 +2253,15 @@ static struct snd_soc_codec *twl4030_codec; | |||
| 2202 | static int twl4030_soc_probe(struct platform_device *pdev) | 2253 | static int twl4030_soc_probe(struct platform_device *pdev) |
| 2203 | { | 2254 | { |
| 2204 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 2255 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
| 2205 | struct twl4030_setup_data *setup = socdev->codec_data; | ||
| 2206 | struct snd_soc_codec *codec; | 2256 | struct snd_soc_codec *codec; |
| 2207 | struct twl4030_priv *twl4030; | ||
| 2208 | int ret; | 2257 | int ret; |
| 2209 | 2258 | ||
| 2210 | BUG_ON(!twl4030_codec); | 2259 | BUG_ON(!twl4030_codec); |
| 2211 | 2260 | ||
| 2212 | codec = twl4030_codec; | 2261 | codec = twl4030_codec; |
| 2213 | twl4030 = snd_soc_codec_get_drvdata(codec); | ||
| 2214 | socdev->card->codec = codec; | 2262 | socdev->card->codec = codec; |
| 2215 | 2263 | ||
| 2216 | /* Configuration for headset ramp delay from setup data */ | 2264 | twl4030_init_chip(pdev); |
| 2217 | if (setup) { | ||
| 2218 | unsigned char hs_pop; | ||
| 2219 | |||
| 2220 | if (setup->sysclk != twl4030->sysclk) | ||
| 2221 | dev_warn(&pdev->dev, | ||
| 2222 | "Mismatch in APLL mclk: %u (configured: %u)\n", | ||
| 2223 | setup->sysclk, twl4030->sysclk); | ||
| 2224 | |||
| 2225 | hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); | ||
| 2226 | hs_pop &= ~TWL4030_RAMP_DELAY; | ||
| 2227 | hs_pop |= (setup->ramp_delay_value << 2); | ||
| 2228 | twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, hs_pop); | ||
| 2229 | } | ||
| 2230 | 2265 | ||
| 2231 | /* register pcms */ | 2266 | /* register pcms */ |
| 2232 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | 2267 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); |
| @@ -2247,6 +2282,8 @@ static int twl4030_soc_remove(struct platform_device *pdev) | |||
| 2247 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 2282 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
| 2248 | struct snd_soc_codec *codec = socdev->card->codec; | 2283 | struct snd_soc_codec *codec = socdev->card->codec; |
| 2249 | 2284 | ||
| 2285 | /* Reset registers to their chip default before leaving */ | ||
| 2286 | twl4030_reset_registers(codec); | ||
| 2250 | twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); | 2287 | twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); |
| 2251 | snd_soc_free_pcms(socdev); | 2288 | snd_soc_free_pcms(socdev); |
| 2252 | snd_soc_dapm_free(socdev); | 2289 | snd_soc_dapm_free(socdev); |
| @@ -2287,6 +2324,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) | |||
| 2287 | codec->read = twl4030_read_reg_cache; | 2324 | codec->read = twl4030_read_reg_cache; |
| 2288 | codec->write = twl4030_write; | 2325 | codec->write = twl4030_write; |
| 2289 | codec->set_bias_level = twl4030_set_bias_level; | 2326 | codec->set_bias_level = twl4030_set_bias_level; |
| 2327 | codec->idle_bias_off = 1; | ||
| 2290 | codec->dai = twl4030_dai; | 2328 | codec->dai = twl4030_dai; |
| 2291 | codec->num_dai = ARRAY_SIZE(twl4030_dai); | 2329 | codec->num_dai = ARRAY_SIZE(twl4030_dai); |
| 2292 | codec->reg_cache_size = sizeof(twl4030_reg); | 2330 | codec->reg_cache_size = sizeof(twl4030_reg); |
| @@ -2302,9 +2340,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) | |||
| 2302 | 2340 | ||
| 2303 | /* Set the defaults, and power up the codec */ | 2341 | /* Set the defaults, and power up the codec */ |
| 2304 | twl4030->sysclk = twl4030_codec_get_mclk() / 1000; | 2342 | twl4030->sysclk = twl4030_codec_get_mclk() / 1000; |
| 2305 | twl4030_init_chip(codec); | ||
| 2306 | codec->bias_level = SND_SOC_BIAS_OFF; | 2343 | codec->bias_level = SND_SOC_BIAS_OFF; |
| 2307 | twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
| 2308 | 2344 | ||
| 2309 | ret = snd_soc_register_codec(codec); | 2345 | ret = snd_soc_register_codec(codec); |
| 2310 | if (ret != 0) { | 2346 | if (ret != 0) { |
| @@ -2322,7 +2358,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) | |||
| 2322 | return 0; | 2358 | return 0; |
| 2323 | 2359 | ||
| 2324 | error_codec: | 2360 | error_codec: |
| 2325 | twl4030_power_down(codec); | 2361 | twl4030_codec_enable(codec, 0); |
| 2326 | kfree(codec->reg_cache); | 2362 | kfree(codec->reg_cache); |
| 2327 | error_cache: | 2363 | error_cache: |
| 2328 | kfree(twl4030); | 2364 | kfree(twl4030); |
diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h index f206d242ca31..6c57430f6e24 100644 --- a/sound/soc/codecs/twl4030.h +++ b/sound/soc/codecs/twl4030.h | |||
| @@ -41,7 +41,11 @@ extern struct snd_soc_codec_device soc_codec_dev_twl4030; | |||
| 41 | 41 | ||
| 42 | struct twl4030_setup_data { | 42 | struct twl4030_setup_data { |
| 43 | unsigned int ramp_delay_value; | 43 | unsigned int ramp_delay_value; |
| 44 | unsigned int digimic_delay; /* in ms */ | ||
| 44 | unsigned int sysclk; | 45 | unsigned int sysclk; |
| 46 | unsigned int offset_cncl_path; | ||
| 47 | unsigned int check_defaults:1; | ||
| 48 | unsigned int reset_registers:1; | ||
| 45 | unsigned int hs_extmute:1; | 49 | unsigned int hs_extmute:1; |
| 46 | void (*set_hs_extmute)(int mute); | 50 | void (*set_hs_extmute)(int mute); |
| 47 | }; | 51 | }; |
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index af36346ff336..64a807f1a8a1 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c | |||
| @@ -360,6 +360,13 @@ static int headset_power_mode(struct snd_soc_codec *codec, int high_perf) | |||
| 360 | return 0; | 360 | return 0; |
| 361 | } | 361 | } |
| 362 | 362 | ||
| 363 | static int twl6040_hs_dac_event(struct snd_soc_dapm_widget *w, | ||
| 364 | struct snd_kcontrol *kcontrol, int event) | ||
| 365 | { | ||
| 366 | msleep(1); | ||
| 367 | return 0; | ||
| 368 | } | ||
| 369 | |||
| 363 | static int twl6040_power_mode_event(struct snd_soc_dapm_widget *w, | 370 | static int twl6040_power_mode_event(struct snd_soc_dapm_widget *w, |
| 364 | struct snd_kcontrol *kcontrol, int event) | 371 | struct snd_kcontrol *kcontrol, int event) |
| 365 | { | 372 | { |
| @@ -371,6 +378,8 @@ static int twl6040_power_mode_event(struct snd_soc_dapm_widget *w, | |||
| 371 | else | 378 | else |
| 372 | priv->non_lp--; | 379 | priv->non_lp--; |
| 373 | 380 | ||
| 381 | msleep(1); | ||
| 382 | |||
| 374 | return 0; | 383 | return 0; |
| 375 | } | 384 | } |
| 376 | 385 | ||
| @@ -471,20 +480,6 @@ static const struct snd_kcontrol_new hfdacl_switch_controls = | |||
| 471 | static const struct snd_kcontrol_new hfdacr_switch_controls = | 480 | static const struct snd_kcontrol_new hfdacr_switch_controls = |
| 472 | SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFRCTL, 2, 1, 0); | 481 | SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFRCTL, 2, 1, 0); |
| 473 | 482 | ||
| 474 | /* Headset driver switches */ | ||
| 475 | static const struct snd_kcontrol_new hsl_driver_switch_controls = | ||
| 476 | SOC_DAPM_SINGLE("Switch", TWL6040_REG_HSLCTL, 2, 1, 0); | ||
| 477 | |||
| 478 | static const struct snd_kcontrol_new hsr_driver_switch_controls = | ||
| 479 | SOC_DAPM_SINGLE("Switch", TWL6040_REG_HSRCTL, 2, 1, 0); | ||
| 480 | |||
| 481 | /* Handsfree driver switches */ | ||
| 482 | static const struct snd_kcontrol_new hfl_driver_switch_controls = | ||
| 483 | SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFLCTL, 4, 1, 0); | ||
| 484 | |||
| 485 | static const struct snd_kcontrol_new hfr_driver_switch_controls = | ||
| 486 | SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFRCTL, 4, 1, 0); | ||
| 487 | |||
| 488 | static const struct snd_kcontrol_new ep_driver_switch_controls = | 483 | static const struct snd_kcontrol_new ep_driver_switch_controls = |
| 489 | SOC_DAPM_SINGLE("Switch", TWL6040_REG_EARCTL, 0, 1, 0); | 484 | SOC_DAPM_SINGLE("Switch", TWL6040_REG_EARCTL, 0, 1, 0); |
| 490 | 485 | ||
| @@ -548,10 +543,14 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { | |||
| 548 | TWL6040_REG_DMICBCTL, 4, 0), | 543 | TWL6040_REG_DMICBCTL, 4, 0), |
| 549 | 544 | ||
| 550 | /* DACs */ | 545 | /* DACs */ |
| 551 | SND_SOC_DAPM_DAC("HSDAC Left", "Headset Playback", | 546 | SND_SOC_DAPM_DAC_E("HSDAC Left", "Headset Playback", |
| 552 | TWL6040_REG_HSLCTL, 0, 0), | 547 | TWL6040_REG_HSLCTL, 0, 0, |
| 553 | SND_SOC_DAPM_DAC("HSDAC Right", "Headset Playback", | 548 | twl6040_hs_dac_event, |
| 554 | TWL6040_REG_HSRCTL, 0, 0), | 549 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), |
| 550 | SND_SOC_DAPM_DAC_E("HSDAC Right", "Headset Playback", | ||
| 551 | TWL6040_REG_HSRCTL, 0, 0, | ||
| 552 | twl6040_hs_dac_event, | ||
| 553 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), | ||
| 555 | SND_SOC_DAPM_DAC_E("HFDAC Left", "Handsfree Playback", | 554 | SND_SOC_DAPM_DAC_E("HFDAC Left", "Handsfree Playback", |
| 556 | TWL6040_REG_HFLCTL, 0, 0, | 555 | TWL6040_REG_HFLCTL, 0, 0, |
| 557 | twl6040_power_mode_event, | 556 | twl6040_power_mode_event, |
| @@ -571,18 +570,19 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { | |||
| 571 | SND_SOC_DAPM_SWITCH("HFDAC Right Playback", | 570 | SND_SOC_DAPM_SWITCH("HFDAC Right Playback", |
| 572 | SND_SOC_NOPM, 0, 0, &hfdacr_switch_controls), | 571 | SND_SOC_NOPM, 0, 0, &hfdacr_switch_controls), |
| 573 | 572 | ||
| 574 | SND_SOC_DAPM_SWITCH("Headset Left Driver", | 573 | /* Analog playback drivers */ |
| 575 | SND_SOC_NOPM, 0, 0, &hsl_driver_switch_controls), | 574 | SND_SOC_DAPM_PGA_E("Handsfree Left Driver", |
| 576 | SND_SOC_DAPM_SWITCH("Headset Right Driver", | 575 | TWL6040_REG_HFLCTL, 4, 0, NULL, 0, |
| 577 | SND_SOC_NOPM, 0, 0, &hsr_driver_switch_controls), | ||
| 578 | SND_SOC_DAPM_SWITCH_E("Handsfree Left Driver", | ||
| 579 | SND_SOC_NOPM, 0, 0, &hfl_driver_switch_controls, | ||
| 580 | twl6040_power_mode_event, | 576 | twl6040_power_mode_event, |
| 581 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), | 577 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), |
| 582 | SND_SOC_DAPM_SWITCH_E("Handsfree Right Driver", | 578 | SND_SOC_DAPM_PGA_E("Handsfree Right Driver", |
| 583 | SND_SOC_NOPM, 0, 0, &hfr_driver_switch_controls, | 579 | TWL6040_REG_HFRCTL, 4, 0, NULL, 0, |
| 584 | twl6040_power_mode_event, | 580 | twl6040_power_mode_event, |
| 585 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), | 581 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), |
| 582 | SND_SOC_DAPM_PGA("Headset Left Driver", | ||
| 583 | TWL6040_REG_HSLCTL, 2, 0, NULL, 0), | ||
| 584 | SND_SOC_DAPM_PGA("Headset Right Driver", | ||
| 585 | TWL6040_REG_HSRCTL, 2, 0, NULL, 0), | ||
| 586 | SND_SOC_DAPM_SWITCH_E("Earphone Driver", | 586 | SND_SOC_DAPM_SWITCH_E("Earphone Driver", |
| 587 | SND_SOC_NOPM, 0, 0, &ep_driver_switch_controls, | 587 | SND_SOC_NOPM, 0, 0, &ep_driver_switch_controls, |
| 588 | twl6040_power_mode_event, | 588 | twl6040_power_mode_event, |
| @@ -616,8 +616,8 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
| 616 | {"HSDAC Left Playback", "Switch", "HSDAC Left"}, | 616 | {"HSDAC Left Playback", "Switch", "HSDAC Left"}, |
| 617 | {"HSDAC Right Playback", "Switch", "HSDAC Right"}, | 617 | {"HSDAC Right Playback", "Switch", "HSDAC Right"}, |
| 618 | 618 | ||
| 619 | {"Headset Left Driver", "Switch", "HSDAC Left Playback"}, | 619 | {"Headset Left Driver", NULL, "HSDAC Left Playback"}, |
| 620 | {"Headset Right Driver", "Switch", "HSDAC Right Playback"}, | 620 | {"Headset Right Driver", NULL, "HSDAC Right Playback"}, |
| 621 | 621 | ||
| 622 | {"HSOL", NULL, "Headset Left Driver"}, | 622 | {"HSOL", NULL, "Headset Left Driver"}, |
| 623 | {"HSOR", NULL, "Headset Right Driver"}, | 623 | {"HSOR", NULL, "Headset Right Driver"}, |
| @@ -928,7 +928,7 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai, | |||
| 928 | case 19200000: | 928 | case 19200000: |
| 929 | /* mclk input, pll disabled */ | 929 | /* mclk input, pll disabled */ |
| 930 | hppllctl |= TWL6040_MCLK_19200KHZ | | 930 | hppllctl |= TWL6040_MCLK_19200KHZ | |
| 931 | TWL6040_HPLLSQRBP | | 931 | TWL6040_HPLLSQRENA | |
| 932 | TWL6040_HPLLBP; | 932 | TWL6040_HPLLBP; |
| 933 | break; | 933 | break; |
| 934 | case 26000000: | 934 | case 26000000: |
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c index 28aac53c97bb..f3b4c1d6a82d 100644 --- a/sound/soc/codecs/uda134x.c +++ b/sound/soc/codecs/uda134x.c | |||
| @@ -28,19 +28,6 @@ | |||
| 28 | #include "uda134x.h" | 28 | #include "uda134x.h" |
| 29 | 29 | ||
| 30 | 30 | ||
| 31 | #define POWER_OFF_ON_STANDBY 1 | ||
| 32 | /* | ||
| 33 | ALSA SOC usually puts the device in standby mode when it's not used | ||
| 34 | for sometime. If you define POWER_OFF_ON_STANDBY the driver will | ||
| 35 | turn off the ADC/DAC when this callback is invoked and turn it back | ||
| 36 | on when needed. Unfortunately this will result in a very light bump | ||
| 37 | (it can be audible only with good earphones). If this bothers you | ||
| 38 | just comment this line, you will have slightly higher power | ||
| 39 | consumption . Please note that sending the L3 command for ADC is | ||
| 40 | enough to make the bump, so it doesn't make difference if you | ||
| 41 | completely take off power from the codec. | ||
| 42 | */ | ||
| 43 | |||
| 44 | #define UDA134X_RATES SNDRV_PCM_RATE_8000_48000 | 31 | #define UDA134X_RATES SNDRV_PCM_RATE_8000_48000 |
| 45 | #define UDA134X_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ | 32 | #define UDA134X_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ |
| 46 | SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE) | 33 | SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE) |
| @@ -58,7 +45,7 @@ static const char uda134x_reg[UDA134X_REGS_NUM] = { | |||
| 58 | /* Extended address registers */ | 45 | /* Extended address registers */ |
| 59 | 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, | 46 | 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 60 | /* Status, data regs */ | 47 | /* Status, data regs */ |
| 61 | 0x00, 0x83, 0x00, 0x40, 0x80, 0x00, | 48 | 0x00, 0x83, 0x00, 0x40, 0x80, 0xC0, 0x00, |
| 62 | }; | 49 | }; |
| 63 | 50 | ||
| 64 | /* | 51 | /* |
| @@ -117,6 +104,7 @@ static int uda134x_write(struct snd_soc_codec *codec, unsigned int reg, | |||
| 117 | case UDA134X_DATA000: | 104 | case UDA134X_DATA000: |
| 118 | case UDA134X_DATA001: | 105 | case UDA134X_DATA001: |
| 119 | case UDA134X_DATA010: | 106 | case UDA134X_DATA010: |
| 107 | case UDA134X_DATA011: | ||
| 120 | addr = UDA134X_DATA0_ADDR; | 108 | addr = UDA134X_DATA0_ADDR; |
| 121 | break; | 109 | break; |
| 122 | case UDA134X_DATA1: | 110 | case UDA134X_DATA1: |
| @@ -353,8 +341,22 @@ static int uda134x_set_bias_level(struct snd_soc_codec *codec, | |||
| 353 | switch (level) { | 341 | switch (level) { |
| 354 | case SND_SOC_BIAS_ON: | 342 | case SND_SOC_BIAS_ON: |
| 355 | /* ADC, DAC on */ | 343 | /* ADC, DAC on */ |
| 356 | reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1); | 344 | switch (pd->model) { |
| 357 | uda134x_write(codec, UDA134X_STATUS1, reg | 0x03); | 345 | case UDA134X_UDA1340: |
| 346 | case UDA134X_UDA1344: | ||
| 347 | case UDA134X_UDA1345: | ||
| 348 | reg = uda134x_read_reg_cache(codec, UDA134X_DATA011); | ||
| 349 | uda134x_write(codec, UDA134X_DATA011, reg | 0x03); | ||
| 350 | break; | ||
| 351 | case UDA134X_UDA1341: | ||
| 352 | reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1); | ||
| 353 | uda134x_write(codec, UDA134X_STATUS1, reg | 0x03); | ||
| 354 | break; | ||
| 355 | default: | ||
| 356 | printk(KERN_ERR "UDA134X SoC codec: " | ||
| 357 | "unsupported model %d\n", pd->model); | ||
| 358 | return -EINVAL; | ||
| 359 | } | ||
| 358 | break; | 360 | break; |
| 359 | case SND_SOC_BIAS_PREPARE: | 361 | case SND_SOC_BIAS_PREPARE: |
| 360 | /* power on */ | 362 | /* power on */ |
| @@ -367,8 +369,22 @@ static int uda134x_set_bias_level(struct snd_soc_codec *codec, | |||
| 367 | break; | 369 | break; |
| 368 | case SND_SOC_BIAS_STANDBY: | 370 | case SND_SOC_BIAS_STANDBY: |
| 369 | /* ADC, DAC power off */ | 371 | /* ADC, DAC power off */ |
| 370 | reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1); | 372 | switch (pd->model) { |
| 371 | uda134x_write(codec, UDA134X_STATUS1, reg & ~(0x03)); | 373 | case UDA134X_UDA1340: |
| 374 | case UDA134X_UDA1344: | ||
| 375 | case UDA134X_UDA1345: | ||
| 376 | reg = uda134x_read_reg_cache(codec, UDA134X_DATA011); | ||
| 377 | uda134x_write(codec, UDA134X_DATA011, reg & ~(0x03)); | ||
| 378 | break; | ||
| 379 | case UDA134X_UDA1341: | ||
| 380 | reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1); | ||
| 381 | uda134x_write(codec, UDA134X_STATUS1, reg & ~(0x03)); | ||
| 382 | break; | ||
| 383 | default: | ||
| 384 | printk(KERN_ERR "UDA134X SoC codec: " | ||
| 385 | "unsupported model %d\n", pd->model); | ||
| 386 | return -EINVAL; | ||
| 387 | } | ||
| 372 | break; | 388 | break; |
| 373 | case SND_SOC_BIAS_OFF: | 389 | case SND_SOC_BIAS_OFF: |
| 374 | /* power off */ | 390 | /* power off */ |
| @@ -531,9 +547,7 @@ static int uda134x_soc_probe(struct platform_device *pdev) | |||
| 531 | codec->num_dai = 1; | 547 | codec->num_dai = 1; |
| 532 | codec->read = uda134x_read_reg_cache; | 548 | codec->read = uda134x_read_reg_cache; |
| 533 | codec->write = uda134x_write; | 549 | codec->write = uda134x_write; |
| 534 | #ifdef POWER_OFF_ON_STANDBY | 550 | |
| 535 | codec->set_bias_level = uda134x_set_bias_level; | ||
| 536 | #endif | ||
| 537 | INIT_LIST_HEAD(&codec->dapm_widgets); | 551 | INIT_LIST_HEAD(&codec->dapm_widgets); |
| 538 | INIT_LIST_HEAD(&codec->dapm_paths); | 552 | INIT_LIST_HEAD(&codec->dapm_paths); |
| 539 | 553 | ||
| @@ -544,6 +558,14 @@ static int uda134x_soc_probe(struct platform_device *pdev) | |||
| 544 | 558 | ||
| 545 | uda134x_reset(codec); | 559 | uda134x_reset(codec); |
| 546 | 560 | ||
| 561 | if (pd->is_powered_on_standby) { | ||
| 562 | codec->set_bias_level = NULL; | ||
| 563 | uda134x_set_bias_level(codec, SND_SOC_BIAS_ON); | ||
| 564 | } else { | ||
| 565 | codec->set_bias_level = uda134x_set_bias_level; | ||
| 566 | uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
| 567 | } | ||
| 568 | |||
| 547 | /* register pcms */ | 569 | /* register pcms */ |
| 548 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | 570 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); |
| 549 | if (ret < 0) { | 571 | if (ret < 0) { |
diff --git a/sound/soc/codecs/uda134x.h b/sound/soc/codecs/uda134x.h index 94f440490b31..205f03b3eaf8 100644 --- a/sound/soc/codecs/uda134x.h +++ b/sound/soc/codecs/uda134x.h | |||
| @@ -23,9 +23,10 @@ | |||
| 23 | #define UDA134X_DATA000 10 | 23 | #define UDA134X_DATA000 10 |
| 24 | #define UDA134X_DATA001 11 | 24 | #define UDA134X_DATA001 11 |
| 25 | #define UDA134X_DATA010 12 | 25 | #define UDA134X_DATA010 12 |
| 26 | #define UDA134X_DATA1 13 | 26 | #define UDA134X_DATA011 13 |
| 27 | #define UDA134X_DATA1 14 | ||
| 27 | 28 | ||
| 28 | #define UDA134X_REGS_NUM 14 | 29 | #define UDA134X_REGS_NUM 15 |
| 29 | 30 | ||
| 30 | #define STATUS0_DAIFMT_MASK (~(7<<1)) | 31 | #define STATUS0_DAIFMT_MASK (~(7<<1)) |
| 31 | #define STATUS0_SYSCLK_MASK (~(3<<4)) | 32 | #define STATUS0_SYSCLK_MASK (~(3<<4)) |
diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 002e289d1255..4bcd168794e1 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c | |||
| @@ -795,6 +795,8 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c, | |||
| 795 | 795 | ||
| 796 | dev_set_drvdata(&i2c->dev, wm2000); | 796 | dev_set_drvdata(&i2c->dev, wm2000); |
| 797 | wm2000->anc_eng_ena = 1; | 797 | wm2000->anc_eng_ena = 1; |
| 798 | wm2000->anc_active = 1; | ||
| 799 | wm2000->spk_ena = 1; | ||
| 798 | wm2000->i2c = i2c; | 800 | wm2000->i2c = i2c; |
| 799 | 801 | ||
| 800 | wm2000_reset(wm2000); | 802 | wm2000_reset(wm2000); |
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index 37242a7d3077..0ad039b4adf5 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c | |||
| @@ -482,7 +482,8 @@ static int wm8523_register(struct wm8523_priv *wm8523, | |||
| 482 | 482 | ||
| 483 | if (wm8523_codec) { | 483 | if (wm8523_codec) { |
| 484 | dev_err(codec->dev, "Another WM8523 is registered\n"); | 484 | dev_err(codec->dev, "Another WM8523 is registered\n"); |
| 485 | return -EINVAL; | 485 | ret = -EINVAL; |
| 486 | goto err; | ||
| 486 | } | 487 | } |
| 487 | 488 | ||
| 488 | mutex_init(&codec->mutex); | 489 | mutex_init(&codec->mutex); |
| @@ -570,18 +571,19 @@ static int wm8523_register(struct wm8523_priv *wm8523, | |||
| 570 | ret = snd_soc_register_codec(codec); | 571 | ret = snd_soc_register_codec(codec); |
| 571 | if (ret != 0) { | 572 | if (ret != 0) { |
| 572 | dev_err(codec->dev, "Failed to register codec: %d\n", ret); | 573 | dev_err(codec->dev, "Failed to register codec: %d\n", ret); |
| 573 | return ret; | 574 | goto err_enable; |
| 574 | } | 575 | } |
| 575 | 576 | ||
| 576 | ret = snd_soc_register_dai(&wm8523_dai); | 577 | ret = snd_soc_register_dai(&wm8523_dai); |
| 577 | if (ret != 0) { | 578 | if (ret != 0) { |
| 578 | dev_err(codec->dev, "Failed to register DAI: %d\n", ret); | 579 | dev_err(codec->dev, "Failed to register DAI: %d\n", ret); |
| 579 | snd_soc_unregister_codec(codec); | 580 | goto err_codec; |
| 580 | return ret; | ||
| 581 | } | 581 | } |
| 582 | 582 | ||
| 583 | return 0; | 583 | return 0; |
| 584 | 584 | ||
| 585 | err_codec: | ||
| 586 | snd_soc_unregister_codec(codec); | ||
| 585 | err_enable: | 587 | err_enable: |
| 586 | regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies); | 588 | regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies); |
| 587 | err_get: | 589 | err_get: |
diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index effb14eee7d4..e2dba07f0260 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c | |||
| @@ -439,7 +439,8 @@ static int wm8711_register(struct wm8711_priv *wm8711, | |||
| 439 | 439 | ||
| 440 | if (wm8711_codec) { | 440 | if (wm8711_codec) { |
| 441 | dev_err(codec->dev, "Another WM8711 is registered\n"); | 441 | dev_err(codec->dev, "Another WM8711 is registered\n"); |
| 442 | return -EINVAL; | 442 | ret = -EINVAL; |
| 443 | goto err; | ||
| 443 | } | 444 | } |
| 444 | 445 | ||
| 445 | mutex_init(&codec->mutex); | 446 | mutex_init(&codec->mutex); |
diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c new file mode 100644 index 000000000000..b9ea8904ad4b --- /dev/null +++ b/sound/soc/codecs/wm8741.c | |||
| @@ -0,0 +1,579 @@ | |||
| 1 | /* | ||
| 2 | * wm8741.c -- WM8741 ALSA SoC Audio driver | ||
| 3 | * | ||
| 4 | * Copyright 2010 Wolfson Microelectronics plc | ||
| 5 | * | ||
| 6 | * Author: Ian Lartey <ian@opensource.wolfsonmicro.com> | ||
| 7 | * | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License version 2 as | ||
| 11 | * published by the Free Software Foundation. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/moduleparam.h> | ||
| 16 | #include <linux/init.h> | ||
| 17 | #include <linux/delay.h> | ||
| 18 | #include <linux/pm.h> | ||
| 19 | #include <linux/i2c.h> | ||
| 20 | #include <linux/platform_device.h> | ||
| 21 | #include <linux/regulator/consumer.h> | ||
| 22 | #include <linux/slab.h> | ||
| 23 | #include <sound/core.h> | ||
| 24 | #include <sound/pcm.h> | ||
| 25 | #include <sound/pcm_params.h> | ||
| 26 | #include <sound/soc.h> | ||
| 27 | #include <sound/soc-dapm.h> | ||
| 28 | #include <sound/initval.h> | ||
| 29 | #include <sound/tlv.h> | ||
| 30 | |||
| 31 | #include "wm8741.h" | ||
| 32 | |||
| 33 | static struct snd_soc_codec *wm8741_codec; | ||
| 34 | struct snd_soc_codec_device soc_codec_dev_wm8741; | ||
| 35 | |||
| 36 | #define WM8741_NUM_SUPPLIES 2 | ||
| 37 | static const char *wm8741_supply_names[WM8741_NUM_SUPPLIES] = { | ||
| 38 | "AVDD", | ||
| 39 | "DVDD", | ||
| 40 | }; | ||
| 41 | |||
| 42 | #define WM8741_NUM_RATES 4 | ||
| 43 | |||
| 44 | /* codec private data */ | ||
| 45 | struct wm8741_priv { | ||
| 46 | struct snd_soc_codec codec; | ||
| 47 | u16 reg_cache[WM8741_REGISTER_COUNT]; | ||
| 48 | struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES]; | ||
| 49 | unsigned int sysclk; | ||
| 50 | unsigned int rate_constraint_list[WM8741_NUM_RATES]; | ||
| 51 | struct snd_pcm_hw_constraint_list rate_constraint; | ||
| 52 | }; | ||
| 53 | |||
| 54 | static const u16 wm8741_reg_defaults[WM8741_REGISTER_COUNT] = { | ||
| 55 | 0x0000, /* R0 - DACLLSB Attenuation */ | ||
| 56 | 0x0000, /* R1 - DACLMSB Attenuation */ | ||
| 57 | 0x0000, /* R2 - DACRLSB Attenuation */ | ||
| 58 | 0x0000, /* R3 - DACRMSB Attenuation */ | ||
| 59 | 0x0000, /* R4 - Volume Control */ | ||
| 60 | 0x000A, /* R5 - Format Control */ | ||
| 61 | 0x0000, /* R6 - Filter Control */ | ||
| 62 | 0x0000, /* R7 - Mode Control 1 */ | ||
| 63 | 0x0002, /* R8 - Mode Control 2 */ | ||
| 64 | 0x0000, /* R9 - Reset */ | ||
| 65 | 0x0002, /* R32 - ADDITONAL_CONTROL_1 */ | ||
| 66 | }; | ||
| 67 | |||
| 68 | |||
| 69 | static int wm8741_reset(struct snd_soc_codec *codec) | ||
| 70 | { | ||
| 71 | return snd_soc_write(codec, WM8741_RESET, 0); | ||
| 72 | } | ||
| 73 | |||
| 74 | static const DECLARE_TLV_DB_SCALE(dac_tlv_fine, -12700, 13, 0); | ||
| 75 | static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 400, 0); | ||
| 76 | |||
| 77 | static const struct snd_kcontrol_new wm8741_snd_controls[] = { | ||
| 78 | SOC_DOUBLE_R_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION, | ||
| 79 | WM8741_DACRLSB_ATTENUATION, 1, 255, 1, dac_tlv_fine), | ||
| 80 | SOC_DOUBLE_R_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION, | ||
| 81 | WM8741_DACRMSB_ATTENUATION, 0, 511, 1, dac_tlv), | ||
| 82 | }; | ||
| 83 | |||
| 84 | static const struct snd_soc_dapm_widget wm8741_dapm_widgets[] = { | ||
| 85 | SND_SOC_DAPM_DAC("DACL", "Playback", SND_SOC_NOPM, 0, 0), | ||
| 86 | SND_SOC_DAPM_DAC("DACR", "Playback", SND_SOC_NOPM, 0, 0), | ||
| 87 | SND_SOC_DAPM_OUTPUT("VOUTLP"), | ||
| 88 | SND_SOC_DAPM_OUTPUT("VOUTLN"), | ||
| 89 | SND_SOC_DAPM_OUTPUT("VOUTRP"), | ||
| 90 | SND_SOC_DAPM_OUTPUT("VOUTRN"), | ||
| 91 | }; | ||
| 92 | |||
| 93 | static const struct snd_soc_dapm_route intercon[] = { | ||
| 94 | { "VOUTLP", NULL, "DACL" }, | ||
| 95 | { "VOUTLN", NULL, "DACL" }, | ||
| 96 | { "VOUTRP", NULL, "DACR" }, | ||
| 97 | { "VOUTRN", NULL, "DACR" }, | ||
| 98 | }; | ||
| 99 | |||
| 100 | static int wm8741_add_widgets(struct snd_soc_codec *codec) | ||
| 101 | { | ||
| 102 | snd_soc_dapm_new_controls(codec, wm8741_dapm_widgets, | ||
| 103 | ARRAY_SIZE(wm8741_dapm_widgets)); | ||
| 104 | |||
| 105 | snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); | ||
| 106 | |||
| 107 | return 0; | ||
| 108 | } | ||
| 109 | |||
| 110 | static struct { | ||
| 111 | int value; | ||
| 112 | int ratio; | ||
| 113 | } lrclk_ratios[WM8741_NUM_RATES] = { | ||
| 114 | { 1, 256 }, | ||
| 115 | { 2, 384 }, | ||
| 116 | { 3, 512 }, | ||
| 117 | { 4, 768 }, | ||
| 118 | }; | ||
| 119 | |||
| 120 | |||
| 121 | static int wm8741_startup(struct snd_pcm_substream *substream, | ||
| 122 | struct snd_soc_dai *dai) | ||
| 123 | { | ||
| 124 | struct snd_soc_codec *codec = dai->codec; | ||
| 125 | struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); | ||
| 126 | |||
| 127 | /* The set of sample rates that can be supported depends on the | ||
| 128 | * MCLK supplied to the CODEC - enforce this. | ||
| 129 | */ | ||
| 130 | if (!wm8741->sysclk) { | ||
| 131 | dev_err(codec->dev, | ||
| 132 | "No MCLK configured, call set_sysclk() on init\n"); | ||
| 133 | return -EINVAL; | ||
| 134 | } | ||
| 135 | |||
| 136 | snd_pcm_hw_constraint_list(substream->runtime, 0, | ||
| 137 | SNDRV_PCM_HW_PARAM_RATE, | ||
| 138 | &wm8741->rate_constraint); | ||
| 139 | |||
| 140 | return 0; | ||
| 141 | } | ||
| 142 | |||
| 143 | static int wm8741_hw_params(struct snd_pcm_substream *substream, | ||
| 144 | struct snd_pcm_hw_params *params, | ||
| 145 | struct snd_soc_dai *dai) | ||
| 146 | { | ||
| 147 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
| 148 | struct snd_soc_device *socdev = rtd->socdev; | ||
| 149 | struct snd_soc_codec *codec = socdev->card->codec; | ||
| 150 | struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); | ||
| 151 | u16 iface = snd_soc_read(codec, WM8741_FORMAT_CONTROL) & 0x1FC; | ||
| 152 | int i; | ||
| 153 | |||
| 154 | /* Find a supported LRCLK ratio */ | ||
| 155 | for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) { | ||
| 156 | if (wm8741->sysclk / params_rate(params) == | ||
| 157 | lrclk_ratios[i].ratio) | ||
| 158 | break; | ||
| 159 | } | ||
| 160 | |||
| 161 | /* Should never happen, should be handled by constraints */ | ||
| 162 | if (i == ARRAY_SIZE(lrclk_ratios)) { | ||
| 163 | dev_err(codec->dev, "MCLK/fs ratio %d unsupported\n", | ||
| 164 | wm8741->sysclk / params_rate(params)); | ||
| 165 | return -EINVAL; | ||
| 166 | } | ||
| 167 | |||
| 168 | /* bit size */ | ||
| 169 | switch (params_format(params)) { | ||
| 170 | case SNDRV_PCM_FORMAT_S16_LE: | ||
| 171 | break; | ||
| 172 | case SNDRV_PCM_FORMAT_S20_3LE: | ||
| 173 | iface |= 0x0001; | ||
| 174 | break; | ||
| 175 | case SNDRV_PCM_FORMAT_S24_LE: | ||
| 176 | iface |= 0x0002; | ||
| 177 | break; | ||
| 178 | case SNDRV_PCM_FORMAT_S32_LE: | ||
| 179 | iface |= 0x0003; | ||
| 180 | break; | ||
| 181 | default: | ||
| 182 | dev_dbg(codec->dev, "wm8741_hw_params: Unsupported bit size param = %d", | ||
| 183 | params_format(params)); | ||
| 184 | return -EINVAL; | ||
| 185 | } | ||
| 186 | |||
| 187 | dev_dbg(codec->dev, "wm8741_hw_params: bit size param = %d", | ||
| 188 | params_format(params)); | ||
| 189 | |||
| 190 | snd_soc_write(codec, WM8741_FORMAT_CONTROL, iface); | ||
| 191 | return 0; | ||
| 192 | } | ||
| 193 | |||
| 194 | static int wm8741_set_dai_sysclk(struct snd_soc_dai *codec_dai, | ||
| 195 | int clk_id, unsigned int freq, int dir) | ||
| 196 | { | ||
| 197 | struct snd_soc_codec *codec = codec_dai->codec; | ||
| 198 | struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); | ||
| 199 | unsigned int val; | ||
| 200 | int i; | ||
| 201 | |||
| 202 | dev_dbg(codec->dev, "wm8741_set_dai_sysclk info: freq=%dHz\n", freq); | ||
| 203 | |||
| 204 | wm8741->sysclk = freq; | ||
| 205 | |||
| 206 | wm8741->rate_constraint.count = 0; | ||
| 207 | |||
| 208 | for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) { | ||
| 209 | dev_dbg(codec->dev, "index = %d, ratio = %d, freq = %d", | ||
| 210 | i, lrclk_ratios[i].ratio, freq); | ||
| 211 | |||
| 212 | val = freq / lrclk_ratios[i].ratio; | ||
| 213 | /* Check that it's a standard rate since core can't | ||
| 214 | * cope with others and having the odd rates confuses | ||
| 215 | * constraint matching. | ||
| 216 | */ | ||
| 217 | switch (val) { | ||
| 218 | case 32000: | ||
| 219 | case 44100: | ||
| 220 | case 48000: | ||
| 221 | case 64000: | ||
| 222 | case 88200: | ||
| 223 | case 96000: | ||
| 224 | dev_dbg(codec->dev, "Supported sample rate: %dHz\n", | ||
| 225 | val); | ||
| 226 | wm8741->rate_constraint_list[i] = val; | ||
| 227 | wm8741->rate_constraint.count++; | ||
| 228 | break; | ||
| 229 | default: | ||
| 230 | dev_dbg(codec->dev, "Skipping sample rate: %dHz\n", | ||
| 231 | val); | ||
| 232 | } | ||
| 233 | } | ||
| 234 | |||
| 235 | /* Need at least one supported rate... */ | ||
| 236 | if (wm8741->rate_constraint.count == 0) | ||
| 237 | return -EINVAL; | ||
| 238 | |||
| 239 | return 0; | ||
| 240 | } | ||
| 241 | |||
| 242 | static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai, | ||
| 243 | unsigned int fmt) | ||
| 244 | { | ||
| 245 | struct snd_soc_codec *codec = codec_dai->codec; | ||
| 246 | u16 iface = snd_soc_read(codec, WM8741_FORMAT_CONTROL) & 0x1C3; | ||
| 247 | |||
| 248 | /* check master/slave audio interface */ | ||
| 249 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
| 250 | case SND_SOC_DAIFMT_CBS_CFS: | ||
| 251 | break; | ||
| 252 | default: | ||
| 253 | return -EINVAL; | ||
| 254 | } | ||
| 255 | |||
| 256 | /* interface format */ | ||
| 257 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
| 258 | case SND_SOC_DAIFMT_I2S: | ||
| 259 | iface |= 0x0008; | ||
| 260 | break; | ||
| 261 | case SND_SOC_DAIFMT_RIGHT_J: | ||
| 262 | break; | ||
| 263 | case SND_SOC_DAIFMT_LEFT_J: | ||
| 264 | iface |= 0x0004; | ||
| 265 | break; | ||
| 266 | case SND_SOC_DAIFMT_DSP_A: | ||
| 267 | iface |= 0x0003; | ||
| 268 | break; | ||
| 269 | case SND_SOC_DAIFMT_DSP_B: | ||
| 270 | iface |= 0x0013; | ||
| 271 | break; | ||
| 272 | default: | ||
| 273 | return -EINVAL; | ||
| 274 | } | ||
| 275 | |||
| 276 | /* clock inversion */ | ||
| 277 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
| 278 | case SND_SOC_DAIFMT_NB_NF: | ||
| 279 | break; | ||
| 280 | case SND_SOC_DAIFMT_IB_IF: | ||
| 281 | iface |= 0x0010; | ||
| 282 | break; | ||
| 283 | case SND_SOC_DAIFMT_IB_NF: | ||
| 284 | iface |= 0x0020; | ||
| 285 | break; | ||
| 286 | case SND_SOC_DAIFMT_NB_IF: | ||
| 287 | iface |= 0x0030; | ||
| 288 | break; | ||
| 289 | default: | ||
| 290 | return -EINVAL; | ||
| 291 | } | ||
| 292 | |||
| 293 | |||
| 294 | dev_dbg(codec->dev, "wm8741_set_dai_fmt: Format=%x, Clock Inv=%x\n", | ||
| 295 | fmt & SND_SOC_DAIFMT_FORMAT_MASK, | ||
| 296 | ((fmt & SND_SOC_DAIFMT_INV_MASK))); | ||
| 297 | |||
| 298 | snd_soc_write(codec, WM8741_FORMAT_CONTROL, iface); | ||
| 299 | return 0; | ||
| 300 | } | ||
| 301 | |||
| 302 | #define WM8741_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ | ||
| 303 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \ | ||
| 304 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \ | ||
| 305 | SNDRV_PCM_RATE_192000) | ||
| 306 | |||
| 307 | #define WM8741_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ | ||
| 308 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) | ||
| 309 | |||
| 310 | static struct snd_soc_dai_ops wm8741_dai_ops = { | ||
| 311 | .startup = wm8741_startup, | ||
| 312 | .hw_params = wm8741_hw_params, | ||
| 313 | .set_sysclk = wm8741_set_dai_sysclk, | ||
| 314 | .set_fmt = wm8741_set_dai_fmt, | ||
| 315 | }; | ||
| 316 | |||
| 317 | struct snd_soc_dai wm8741_dai = { | ||
| 318 | .name = "WM8741", | ||
| 319 | .playback = { | ||
| 320 | .stream_name = "Playback", | ||
| 321 | .channels_min = 2, /* Mono modes not yet supported */ | ||
| 322 | .channels_max = 2, | ||
| 323 | .rates = WM8741_RATES, | ||
| 324 | .formats = WM8741_FORMATS, | ||
| 325 | }, | ||
| 326 | .ops = &wm8741_dai_ops, | ||
| 327 | }; | ||
| 328 | EXPORT_SYMBOL_GPL(wm8741_dai); | ||
| 329 | |||
| 330 | #ifdef CONFIG_PM | ||
| 331 | static int wm8741_resume(struct platform_device *pdev) | ||
| 332 | { | ||
| 333 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
| 334 | struct snd_soc_codec *codec = socdev->card->codec; | ||
| 335 | u16 *cache = codec->reg_cache; | ||
| 336 | int i; | ||
| 337 | |||
| 338 | /* RESTORE REG Cache */ | ||
| 339 | for (i = 0; i < WM8741_REGISTER_COUNT; i++) { | ||
| 340 | if (cache[i] == wm8741_reg_defaults[i] || WM8741_RESET == i) | ||
| 341 | continue; | ||
| 342 | snd_soc_write(codec, i, cache[i]); | ||
| 343 | } | ||
| 344 | return 0; | ||
| 345 | } | ||
| 346 | #else | ||
| 347 | #define wm8741_suspend NULL | ||
| 348 | #define wm8741_resume NULL | ||
| 349 | #endif | ||
| 350 | |||
| 351 | static int wm8741_probe(struct platform_device *pdev) | ||
| 352 | { | ||
| 353 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
| 354 | struct snd_soc_codec *codec; | ||
| 355 | int ret = 0; | ||
| 356 | |||
| 357 | if (wm8741_codec == NULL) { | ||
| 358 | dev_err(&pdev->dev, "Codec device not registered\n"); | ||
| 359 | return -ENODEV; | ||
| 360 | } | ||
| 361 | |||
| 362 | socdev->card->codec = wm8741_codec; | ||
| 363 | codec = wm8741_codec; | ||
| 364 | |||
| 365 | /* register pcms */ | ||
| 366 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | ||
| 367 | if (ret < 0) { | ||
| 368 | dev_err(codec->dev, "failed to create pcms: %d\n", ret); | ||
| 369 | goto pcm_err; | ||
| 370 | } | ||
| 371 | |||
| 372 | snd_soc_add_controls(codec, wm8741_snd_controls, | ||
| 373 | ARRAY_SIZE(wm8741_snd_controls)); | ||
| 374 | wm8741_add_widgets(codec); | ||
| 375 | |||
| 376 | return ret; | ||
| 377 | |||
| 378 | pcm_err: | ||
| 379 | return ret; | ||
| 380 | } | ||
| 381 | |||
| 382 | static int wm8741_remove(struct platform_device *pdev) | ||
| 383 | { | ||
| 384 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
| 385 | |||
| 386 | snd_soc_free_pcms(socdev); | ||
| 387 | snd_soc_dapm_free(socdev); | ||
| 388 | |||
| 389 | return 0; | ||
| 390 | } | ||
| 391 | |||
| 392 | struct snd_soc_codec_device soc_codec_dev_wm8741 = { | ||
| 393 | .probe = wm8741_probe, | ||
| 394 | .remove = wm8741_remove, | ||
| 395 | .resume = wm8741_resume, | ||
| 396 | }; | ||
| 397 | EXPORT_SYMBOL_GPL(soc_codec_dev_wm8741); | ||
| 398 | |||
| 399 | static int wm8741_register(struct wm8741_priv *wm8741, | ||
| 400 | enum snd_soc_control_type control) | ||
| 401 | { | ||
| 402 | int ret; | ||
| 403 | struct snd_soc_codec *codec = &wm8741->codec; | ||
| 404 | int i; | ||
| 405 | |||
| 406 | if (wm8741_codec) { | ||
| 407 | dev_err(codec->dev, "Another WM8741 is registered\n"); | ||
| 408 | return -EINVAL; | ||
| 409 | } | ||
| 410 | |||
| 411 | mutex_init(&codec->mutex); | ||
| 412 | INIT_LIST_HEAD(&codec->dapm_widgets); | ||
| 413 | INIT_LIST_HEAD(&codec->dapm_paths); | ||
| 414 | |||
| 415 | snd_soc_codec_set_drvdata(codec, wm8741); | ||
| 416 | codec->name = "WM8741"; | ||
| 417 | codec->owner = THIS_MODULE; | ||
| 418 | codec->bias_level = SND_SOC_BIAS_OFF; | ||
| 419 | codec->set_bias_level = NULL; | ||
| 420 | codec->dai = &wm8741_dai; | ||
| 421 | codec->num_dai = 1; | ||
| 422 | codec->reg_cache_size = WM8741_REGISTER_COUNT; | ||
| 423 | codec->reg_cache = &wm8741->reg_cache; | ||
| 424 | |||
| 425 | wm8741->rate_constraint.list = &wm8741->rate_constraint_list[0]; | ||
| 426 | wm8741->rate_constraint.count = | ||
| 427 | ARRAY_SIZE(wm8741->rate_constraint_list); | ||
| 428 | |||
| 429 | memcpy(codec->reg_cache, wm8741_reg_defaults, | ||
| 430 | sizeof(wm8741->reg_cache)); | ||
| 431 | |||
| 432 | ret = snd_soc_codec_set_cache_io(codec, 7, 9, control); | ||
| 433 | if (ret != 0) { | ||
| 434 | dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); | ||
| 435 | goto err; | ||
| 436 | } | ||
| 437 | |||
| 438 | for (i = 0; i < ARRAY_SIZE(wm8741->supplies); i++) | ||
| 439 | wm8741->supplies[i].supply = wm8741_supply_names[i]; | ||
| 440 | |||
| 441 | ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8741->supplies), | ||
| 442 | wm8741->supplies); | ||
| 443 | if (ret != 0) { | ||
| 444 | dev_err(codec->dev, "Failed to request supplies: %d\n", ret); | ||
| 445 | goto err; | ||
| 446 | } | ||
| 447 | |||
| 448 | ret = regulator_bulk_enable(ARRAY_SIZE(wm8741->supplies), | ||
| 449 | wm8741->supplies); | ||
| 450 | if (ret != 0) { | ||
| 451 | dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); | ||
| 452 | goto err_get; | ||
| 453 | } | ||
| 454 | |||
| 455 | ret = wm8741_reset(codec); | ||
| 456 | if (ret < 0) { | ||
| 457 | dev_err(codec->dev, "Failed to issue reset\n"); | ||
| 458 | goto err_enable; | ||
| 459 | } | ||
| 460 | |||
| 461 | wm8741_dai.dev = codec->dev; | ||
| 462 | |||
| 463 | /* Change some default settings - latch VU */ | ||
| 464 | wm8741->reg_cache[WM8741_DACLLSB_ATTENUATION] |= WM8741_UPDATELL; | ||
| 465 | wm8741->reg_cache[WM8741_DACLMSB_ATTENUATION] |= WM8741_UPDATELM; | ||
| 466 | wm8741->reg_cache[WM8741_DACRLSB_ATTENUATION] |= WM8741_UPDATERL; | ||
| 467 | wm8741->reg_cache[WM8741_DACRLSB_ATTENUATION] |= WM8741_UPDATERM; | ||
| 468 | |||
| 469 | wm8741_codec = codec; | ||
| 470 | |||
| 471 | ret = snd_soc_register_codec(codec); | ||
| 472 | if (ret != 0) { | ||
| 473 | dev_err(codec->dev, "Failed to register codec: %d\n", ret); | ||
| 474 | return ret; | ||
| 475 | } | ||
| 476 | |||
| 477 | ret = snd_soc_register_dai(&wm8741_dai); | ||
| 478 | if (ret != 0) { | ||
| 479 | dev_err(codec->dev, "Failed to register DAI: %d\n", ret); | ||
| 480 | snd_soc_unregister_codec(codec); | ||
| 481 | return ret; | ||
| 482 | } | ||
| 483 | |||
| 484 | dev_dbg(codec->dev, "Successful registration\n"); | ||
| 485 | return 0; | ||
| 486 | |||
| 487 | err_enable: | ||
| 488 | regulator_bulk_disable(ARRAY_SIZE(wm8741->supplies), wm8741->supplies); | ||
| 489 | |||
| 490 | err_get: | ||
| 491 | regulator_bulk_free(ARRAY_SIZE(wm8741->supplies), wm8741->supplies); | ||
| 492 | |||
| 493 | err: | ||
| 494 | kfree(wm8741); | ||
| 495 | return ret; | ||
| 496 | } | ||
| 497 | |||
| 498 | static void wm8741_unregister(struct wm8741_priv *wm8741) | ||
| 499 | { | ||
| 500 | regulator_bulk_free(ARRAY_SIZE(wm8741->supplies), wm8741->supplies); | ||
| 501 | |||
| 502 | snd_soc_unregister_dai(&wm8741_dai); | ||
| 503 | snd_soc_unregister_codec(&wm8741->codec); | ||
| 504 | kfree(wm8741); | ||
| 505 | wm8741_codec = NULL; | ||
| 506 | } | ||
| 507 | |||
| 508 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
| 509 | static __devinit int wm8741_i2c_probe(struct i2c_client *i2c, | ||
| 510 | const struct i2c_device_id *id) | ||
| 511 | { | ||
| 512 | struct wm8741_priv *wm8741; | ||
| 513 | struct snd_soc_codec *codec; | ||
| 514 | |||
| 515 | wm8741 = kzalloc(sizeof(struct wm8741_priv), GFP_KERNEL); | ||
| 516 | if (wm8741 == NULL) | ||
| 517 | return -ENOMEM; | ||
| 518 | |||
| 519 | codec = &wm8741->codec; | ||
| 520 | codec->hw_write = (hw_write_t)i2c_master_send; | ||
| 521 | |||
| 522 | i2c_set_clientdata(i2c, wm8741); | ||
| 523 | codec->control_data = i2c; | ||
| 524 | |||
| 525 | codec->dev = &i2c->dev; | ||
| 526 | |||
| 527 | return wm8741_register(wm8741, SND_SOC_I2C); | ||
| 528 | } | ||
| 529 | |||
| 530 | static __devexit int wm8741_i2c_remove(struct i2c_client *client) | ||
| 531 | { | ||
| 532 | struct wm8741_priv *wm8741 = i2c_get_clientdata(client); | ||
| 533 | wm8741_unregister(wm8741); | ||
| 534 | return 0; | ||
| 535 | } | ||
| 536 | |||
| 537 | static const struct i2c_device_id wm8741_i2c_id[] = { | ||
| 538 | { "wm8741", 0 }, | ||
| 539 | { } | ||
| 540 | }; | ||
| 541 | MODULE_DEVICE_TABLE(i2c, wm8741_i2c_id); | ||
| 542 | |||
| 543 | |||
| 544 | static struct i2c_driver wm8741_i2c_driver = { | ||
| 545 | .driver = { | ||
| 546 | .name = "WM8741", | ||
| 547 | .owner = THIS_MODULE, | ||
| 548 | }, | ||
| 549 | .probe = wm8741_i2c_probe, | ||
| 550 | .remove = __devexit_p(wm8741_i2c_remove), | ||
| 551 | .id_table = wm8741_i2c_id, | ||
| 552 | }; | ||
| 553 | #endif | ||
| 554 | |||
| 555 | static int __init wm8741_modinit(void) | ||
| 556 | { | ||
| 557 | int ret; | ||
| 558 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
| 559 | ret = i2c_add_driver(&wm8741_i2c_driver); | ||
| 560 | if (ret != 0) { | ||
| 561 | printk(KERN_ERR "Failed to register WM8741 I2C driver: %d\n", | ||
| 562 | ret); | ||
| 563 | } | ||
| 564 | #endif | ||
| 565 | return 0; | ||
| 566 | } | ||
| 567 | module_init(wm8741_modinit); | ||
| 568 | |||
| 569 | static void __exit wm8741_exit(void) | ||
| 570 | { | ||
| 571 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
| 572 | i2c_del_driver(&wm8741_i2c_driver); | ||
| 573 | #endif | ||
| 574 | } | ||
| 575 | module_exit(wm8741_exit); | ||
| 576 | |||
| 577 | MODULE_DESCRIPTION("ASoC WM8741 driver"); | ||
| 578 | MODULE_AUTHOR("Ian Lartey <ian@opensource.wolfsonmicro.com>"); | ||
| 579 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/wm8741.h b/sound/soc/codecs/wm8741.h new file mode 100644 index 000000000000..fdef6ecd1f6f --- /dev/null +++ b/sound/soc/codecs/wm8741.h | |||
| @@ -0,0 +1,214 @@ | |||
| 1 | /* | ||
| 2 | * wm8741.h -- WM8423 ASoC driver | ||
| 3 | * | ||
| 4 | * Copyright 2010 Wolfson Microelectronics, plc | ||
| 5 | * | ||
| 6 | * Author: Ian Lartey <ian@opensource.wolfsonmicro.com> | ||
| 7 | * | ||
| 8 | * Based on wm8753.h | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or modify | ||
| 11 | * it under the terms of the GNU General Public License version 2 as | ||
| 12 | * published by the Free Software Foundation. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #ifndef _WM8741_H | ||
| 16 | #define _WM8741_H | ||
| 17 | |||
| 18 | /* | ||
| 19 | * Register values. | ||
| 20 | */ | ||
| 21 | #define WM8741_DACLLSB_ATTENUATION 0x00 | ||
| 22 | #define WM8741_DACLMSB_ATTENUATION 0x01 | ||
| 23 | #define WM8741_DACRLSB_ATTENUATION 0x02 | ||
| 24 | #define WM8741_DACRMSB_ATTENUATION 0x03 | ||
| 25 | #define WM8741_VOLUME_CONTROL 0x04 | ||
| 26 | #define WM8741_FORMAT_CONTROL 0x05 | ||
| 27 | #define WM8741_FILTER_CONTROL 0x06 | ||
| 28 | #define WM8741_MODE_CONTROL_1 0x07 | ||
| 29 | #define WM8741_MODE_CONTROL_2 0x08 | ||
| 30 | #define WM8741_RESET 0x09 | ||
| 31 | #define WM8741_ADDITIONAL_CONTROL_1 0x20 | ||
| 32 | |||
| 33 | #define WM8741_REGISTER_COUNT 11 | ||
| 34 | #define WM8741_MAX_REGISTER 0x20 | ||
| 35 | |||
| 36 | /* | ||
| 37 | * Field Definitions. | ||
| 38 | */ | ||
| 39 | |||
| 40 | /* | ||
| 41 | * R0 (0x00) - DACLLSB_ATTENUATION | ||
| 42 | */ | ||
| 43 | #define WM8741_UPDATELL 0x0020 /* UPDATELL */ | ||
| 44 | #define WM8741_UPDATELL_MASK 0x0020 /* UPDATELL */ | ||
| 45 | #define WM8741_UPDATELL_SHIFT 5 /* UPDATELL */ | ||
| 46 | #define WM8741_UPDATELL_WIDTH 1 /* UPDATELL */ | ||
| 47 | #define WM8741_LAT_4_0_MASK 0x001F /* LAT[4:0] - [4:0] */ | ||
| 48 | #define WM8741_LAT_4_0_SHIFT 0 /* LAT[4:0] - [4:0] */ | ||
| 49 | #define WM8741_LAT_4_0_WIDTH 5 /* LAT[4:0] - [4:0] */ | ||
| 50 | |||
| 51 | /* | ||
| 52 | * R1 (0x01) - DACLMSB_ATTENUATION | ||
| 53 | */ | ||
| 54 | #define WM8741_UPDATELM 0x0020 /* UPDATELM */ | ||
| 55 | #define WM8741_UPDATELM_MASK 0x0020 /* UPDATELM */ | ||
| 56 | #define WM8741_UPDATELM_SHIFT 5 /* UPDATELM */ | ||
| 57 | #define WM8741_UPDATELM_WIDTH 1 /* UPDATELM */ | ||
| 58 | #define WM8741_LAT_9_5_0_MASK 0x001F /* LAT[9:5] - [4:0] */ | ||
| 59 | #define WM8741_LAT_9_5_0_SHIFT 0 /* LAT[9:5] - [4:0] */ | ||
| 60 | #define WM8741_LAT_9_5_0_WIDTH 5 /* LAT[9:5] - [4:0] */ | ||
| 61 | |||
| 62 | /* | ||
| 63 | * R2 (0x02) - DACRLSB_ATTENUATION | ||
| 64 | */ | ||
| 65 | #define WM8741_UPDATERL 0x0020 /* UPDATERL */ | ||
| 66 | #define WM8741_UPDATERL_MASK 0x0020 /* UPDATERL */ | ||
| 67 | #define WM8741_UPDATERL_SHIFT 5 /* UPDATERL */ | ||
| 68 | #define WM8741_UPDATERL_WIDTH 1 /* UPDATERL */ | ||
| 69 | #define WM8741_RAT_4_0_MASK 0x001F /* RAT[4:0] - [4:0] */ | ||
| 70 | #define WM8741_RAT_4_0_SHIFT 0 /* RAT[4:0] - [4:0] */ | ||
| 71 | #define WM8741_RAT_4_0_WIDTH 5 /* RAT[4:0] - [4:0] */ | ||
| 72 | |||
| 73 | /* | ||
| 74 | * R3 (0x03) - DACRMSB_ATTENUATION | ||
| 75 | */ | ||
| 76 | #define WM8741_UPDATERM 0x0020 /* UPDATERM */ | ||
| 77 | #define WM8741_UPDATERM_MASK 0x0020 /* UPDATERM */ | ||
| 78 | #define WM8741_UPDATERM_SHIFT 5 /* UPDATERM */ | ||
| 79 | #define WM8741_UPDATERM_WIDTH 1 /* UPDATERM */ | ||
| 80 | #define WM8741_RAT_9_5_0_MASK 0x001F /* RAT[9:5] - [4:0] */ | ||
| 81 | #define WM8741_RAT_9_5_0_SHIFT 0 /* RAT[9:5] - [4:0] */ | ||
| 82 | #define WM8741_RAT_9_5_0_WIDTH 5 /* RAT[9:5] - [4:0] */ | ||
| 83 | |||
| 84 | /* | ||
| 85 | * R4 (0x04) - VOLUME_CONTROL | ||
| 86 | */ | ||
| 87 | #define WM8741_AMUTE 0x0080 /* AMUTE */ | ||
| 88 | #define WM8741_AMUTE_MASK 0x0080 /* AMUTE */ | ||
| 89 | #define WM8741_AMUTE_SHIFT 7 /* AMUTE */ | ||
| 90 | #define WM8741_AMUTE_WIDTH 1 /* AMUTE */ | ||
| 91 | #define WM8741_ZFLAG_MASK 0x0060 /* ZFLAG - [6:5] */ | ||
| 92 | #define WM8741_ZFLAG_SHIFT 5 /* ZFLAG - [6:5] */ | ||
| 93 | #define WM8741_ZFLAG_WIDTH 2 /* ZFLAG - [6:5] */ | ||
| 94 | #define WM8741_IZD 0x0010 /* IZD */ | ||
| 95 | #define WM8741_IZD_MASK 0x0010 /* IZD */ | ||
| 96 | #define WM8741_IZD_SHIFT 4 /* IZD */ | ||
| 97 | #define WM8741_IZD_WIDTH 1 /* IZD */ | ||
| 98 | #define WM8741_SOFT 0x0008 /* SOFT MUTE */ | ||
| 99 | #define WM8741_SOFT_MASK 0x0008 /* SOFT MUTE */ | ||
| 100 | #define WM8741_SOFT_SHIFT 3 /* SOFT MUTE */ | ||
| 101 | #define WM8741_SOFT_WIDTH 1 /* SOFT MUTE */ | ||
| 102 | #define WM8741_ATC 0x0004 /* ATC */ | ||
| 103 | #define WM8741_ATC_MASK 0x0004 /* ATC */ | ||
| 104 | #define WM8741_ATC_SHIFT 2 /* ATC */ | ||
| 105 | #define WM8741_ATC_WIDTH 1 /* ATC */ | ||
| 106 | #define WM8741_ATT2DB 0x0002 /* ATT2DB */ | ||
| 107 | #define WM8741_ATT2DB_MASK 0x0002 /* ATT2DB */ | ||
| 108 | #define WM8741_ATT2DB_SHIFT 1 /* ATT2DB */ | ||
| 109 | #define WM8741_ATT2DB_WIDTH 1 /* ATT2DB */ | ||
| 110 | #define WM8741_VOL_RAMP 0x0001 /* VOL_RAMP */ | ||
| 111 | #define WM8741_VOL_RAMP_MASK 0x0001 /* VOL_RAMP */ | ||
| 112 | #define WM8741_VOL_RAMP_SHIFT 0 /* VOL_RAMP */ | ||
| 113 | #define WM8741_VOL_RAMP_WIDTH 1 /* VOL_RAMP */ | ||
| 114 | |||
| 115 | /* | ||
| 116 | * R5 (0x05) - FORMAT_CONTROL | ||
| 117 | */ | ||
| 118 | #define WM8741_PWDN 0x0080 /* PWDN */ | ||
| 119 | #define WM8741_PWDN_MASK 0x0080 /* PWDN */ | ||
| 120 | #define WM8741_PWDN_SHIFT 7 /* PWDN */ | ||
| 121 | #define WM8741_PWDN_WIDTH 1 /* PWDN */ | ||
| 122 | #define WM8741_REV 0x0040 /* REV */ | ||
| 123 | #define WM8741_REV_MASK 0x0040 /* REV */ | ||
| 124 | #define WM8741_REV_SHIFT 6 /* REV */ | ||
| 125 | #define WM8741_REV_WIDTH 1 /* REV */ | ||
| 126 | #define WM8741_BCP 0x0020 /* BCP */ | ||
| 127 | #define WM8741_BCP_MASK 0x0020 /* BCP */ | ||
| 128 | #define WM8741_BCP_SHIFT 5 /* BCP */ | ||
| 129 | #define WM8741_BCP_WIDTH 1 /* BCP */ | ||
| 130 | #define WM8741_LRP 0x0010 /* LRP */ | ||
| 131 | #define WM8741_LRP_MASK 0x0010 /* LRP */ | ||
| 132 | #define WM8741_LRP_SHIFT 4 /* LRP */ | ||
| 133 | #define WM8741_LRP_WIDTH 1 /* LRP */ | ||
| 134 | #define WM8741_FMT_MASK 0x000C /* FMT - [3:2] */ | ||
| 135 | #define WM8741_FMT_SHIFT 2 /* FMT - [3:2] */ | ||
| 136 | #define WM8741_FMT_WIDTH 2 /* FMT - [3:2] */ | ||
| 137 | #define WM8741_IWL_MASK 0x0003 /* IWL - [1:0] */ | ||
| 138 | #define WM8741_IWL_SHIFT 0 /* IWL - [1:0] */ | ||
| 139 | #define WM8741_IWL_WIDTH 2 /* IWL - [1:0] */ | ||
| 140 | |||
| 141 | /* | ||
| 142 | * R6 (0x06) - FILTER_CONTROL | ||
| 143 | */ | ||
| 144 | #define WM8741_ZFLAG_HI 0x0080 /* ZFLAG_HI */ | ||
| 145 | #define WM8741_ZFLAG_HI_MASK 0x0080 /* ZFLAG_HI */ | ||
| 146 | #define WM8741_ZFLAG_HI_SHIFT 7 /* ZFLAG_HI */ | ||
| 147 | #define WM8741_ZFLAG_HI_WIDTH 1 /* ZFLAG_HI */ | ||
| 148 | #define WM8741_DEEMPH_MASK 0x0060 /* DEEMPH - [6:5] */ | ||
| 149 | #define WM8741_DEEMPH_SHIFT 5 /* DEEMPH - [6:5] */ | ||
| 150 | #define WM8741_DEEMPH_WIDTH 2 /* DEEMPH - [6:5] */ | ||
| 151 | #define WM8741_DSDFILT_MASK 0x0018 /* DSDFILT - [4:3] */ | ||
| 152 | #define WM8741_DSDFILT_SHIFT 3 /* DSDFILT - [4:3] */ | ||
| 153 | #define WM8741_DSDFILT_WIDTH 2 /* DSDFILT - [4:3] */ | ||
| 154 | #define WM8741_FIRSEL_MASK 0x0007 /* FIRSEL - [2:0] */ | ||
| 155 | #define WM8741_FIRSEL_SHIFT 0 /* FIRSEL - [2:0] */ | ||
| 156 | #define WM8741_FIRSEL_WIDTH 3 /* FIRSEL - [2:0] */ | ||
| 157 | |||
| 158 | /* | ||
| 159 | * R7 (0x07) - MODE_CONTROL_1 | ||
| 160 | */ | ||
| 161 | #define WM8741_MODE8X 0x0080 /* MODE8X */ | ||
| 162 | #define WM8741_MODE8X_MASK 0x0080 /* MODE8X */ | ||
| 163 | #define WM8741_MODE8X_SHIFT 7 /* MODE8X */ | ||
| 164 | #define WM8741_MODE8X_WIDTH 1 /* MODE8X */ | ||
| 165 | #define WM8741_OSR_MASK 0x0060 /* OSR - [6:5] */ | ||
| 166 | #define WM8741_OSR_SHIFT 5 /* OSR - [6:5] */ | ||
| 167 | #define WM8741_OSR_WIDTH 2 /* OSR - [6:5] */ | ||
| 168 | #define WM8741_SR_MASK 0x001C /* SR - [4:2] */ | ||
| 169 | #define WM8741_SR_SHIFT 2 /* SR - [4:2] */ | ||
| 170 | #define WM8741_SR_WIDTH 3 /* SR - [4:2] */ | ||
| 171 | #define WM8741_MODESEL_MASK 0x0003 /* MODESEL - [1:0] */ | ||
| 172 | #define WM8741_MODESEL_SHIFT 0 /* MODESEL - [1:0] */ | ||
| 173 | #define WM8741_MODESEL_WIDTH 2 /* MODESEL - [1:0] */ | ||
| 174 | |||
| 175 | /* | ||
| 176 | * R8 (0x08) - MODE_CONTROL_2 | ||
| 177 | */ | ||
| 178 | #define WM8741_DSD_GAIN 0x0040 /* DSD_GAIN */ | ||
| 179 | #define WM8741_DSD_GAIN_MASK 0x0040 /* DSD_GAIN */ | ||
| 180 | #define WM8741_DSD_GAIN_SHIFT 6 /* DSD_GAIN */ | ||
| 181 | #define WM8741_DSD_GAIN_WIDTH 1 /* DSD_GAIN */ | ||
| 182 | #define WM8741_SDOUT 0x0020 /* SDOUT */ | ||
| 183 | #define WM8741_SDOUT_MASK 0x0020 /* SDOUT */ | ||
| 184 | #define WM8741_SDOUT_SHIFT 5 /* SDOUT */ | ||
| 185 | #define WM8741_SDOUT_WIDTH 1 /* SDOUT */ | ||
| 186 | #define WM8741_DOUT 0x0010 /* DOUT */ | ||
| 187 | #define WM8741_DOUT_MASK 0x0010 /* DOUT */ | ||
| 188 | #define WM8741_DOUT_SHIFT 4 /* DOUT */ | ||
| 189 | #define WM8741_DOUT_WIDTH 1 /* DOUT */ | ||
| 190 | #define WM8741_DIFF_MASK 0x000C /* DIFF - [3:2] */ | ||
| 191 | #define WM8741_DIFF_SHIFT 2 /* DIFF - [3:2] */ | ||
| 192 | #define WM8741_DIFF_WIDTH 2 /* DIFF - [3:2] */ | ||
| 193 | #define WM8741_DITHER_MASK 0x0003 /* DITHER - [1:0] */ | ||
| 194 | #define WM8741_DITHER_SHIFT 0 /* DITHER - [1:0] */ | ||
| 195 | #define WM8741_DITHER_WIDTH 2 /* DITHER - [1:0] */ | ||
| 196 | |||
| 197 | /* | ||
| 198 | * R32 (0x20) - ADDITONAL_CONTROL_1 | ||
| 199 | */ | ||
| 200 | #define WM8741_DSD_LEVEL 0x0002 /* DSD_LEVEL */ | ||
| 201 | #define WM8741_DSD_LEVEL_MASK 0x0002 /* DSD_LEVEL */ | ||
| 202 | #define WM8741_DSD_LEVEL_SHIFT 1 /* DSD_LEVEL */ | ||
| 203 | #define WM8741_DSD_LEVEL_WIDTH 1 /* DSD_LEVEL */ | ||
| 204 | #define WM8741_DSD_NO_NOTCH 0x0001 /* DSD_NO_NOTCH */ | ||
| 205 | #define WM8741_DSD_NO_NOTCH_MASK 0x0001 /* DSD_NO_NOTCH */ | ||
| 206 | #define WM8741_DSD_NO_NOTCH_SHIFT 0 /* DSD_NO_NOTCH */ | ||
| 207 | #define WM8741_DSD_NO_NOTCH_WIDTH 1 /* DSD_NO_NOTCH */ | ||
| 208 | |||
| 209 | #define WM8741_SYSCLK 0 | ||
| 210 | |||
| 211 | extern struct snd_soc_dai wm8741_dai; | ||
| 212 | extern struct snd_soc_codec_device soc_codec_dev_wm8741; | ||
| 213 | |||
| 214 | #endif | ||
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 9407e193fcc3..e2c05e3e323a 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c | |||
| @@ -884,6 +884,7 @@ static int wm8750_i2c_remove(struct i2c_client *client) | |||
| 884 | 884 | ||
| 885 | static const struct i2c_device_id wm8750_i2c_id[] = { | 885 | static const struct i2c_device_id wm8750_i2c_id[] = { |
| 886 | { "wm8750", 0 }, | 886 | { "wm8750", 0 }, |
| 887 | { "wm8987", 0 }, /* WM8987 is register compatible with WM8750 */ | ||
| 887 | { } | 888 | { } |
| 888 | }; | 889 | }; |
| 889 | MODULE_DEVICE_TABLE(i2c, wm8750_i2c_id); | 890 | MODULE_DEVICE_TABLE(i2c, wm8750_i2c_id); |
| @@ -925,14 +926,22 @@ static int __devexit wm8750_spi_remove(struct spi_device *spi) | |||
| 925 | return 0; | 926 | return 0; |
| 926 | } | 927 | } |
| 927 | 928 | ||
| 929 | static const struct spi_device_id wm8750_spi_id[] = { | ||
| 930 | { "wm8750", 0 }, | ||
| 931 | { "wm8987", 0 }, | ||
| 932 | { } | ||
| 933 | }; | ||
| 934 | MODULE_DEVICE_TABLE(spi, wm8750_spi_id); | ||
| 935 | |||
| 928 | static struct spi_driver wm8750_spi_driver = { | 936 | static struct spi_driver wm8750_spi_driver = { |
| 929 | .driver = { | 937 | .driver = { |
| 930 | .name = "wm8750", | 938 | .name = "WM8750 SPI Codec", |
| 931 | .bus = &spi_bus_type, | 939 | .bus = &spi_bus_type, |
| 932 | .owner = THIS_MODULE, | 940 | .owner = THIS_MODULE, |
| 933 | }, | 941 | }, |
| 934 | .probe = wm8750_spi_probe, | 942 | .probe = wm8750_spi_probe, |
| 935 | .remove = __devexit_p(wm8750_spi_remove), | 943 | .remove = __devexit_p(wm8750_spi_remove), |
| 944 | .id_table = wm8750_spi_id, | ||
| 936 | }; | 945 | }; |
| 937 | #endif | 946 | #endif |
| 938 | 947 | ||
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 87f14f8675fa..f7dcabf6283c 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c | |||
| @@ -2433,7 +2433,8 @@ static int wm8904_register(struct wm8904_priv *wm8904, | |||
| 2433 | 2433 | ||
| 2434 | if (wm8904_codec) { | 2434 | if (wm8904_codec) { |
| 2435 | dev_err(codec->dev, "Another WM8904 is registered\n"); | 2435 | dev_err(codec->dev, "Another WM8904 is registered\n"); |
| 2436 | return -EINVAL; | 2436 | ret = -EINVAL; |
| 2437 | goto err; | ||
| 2437 | } | 2438 | } |
| 2438 | 2439 | ||
| 2439 | mutex_init(&codec->mutex); | 2440 | mutex_init(&codec->mutex); |
| @@ -2462,7 +2463,8 @@ static int wm8904_register(struct wm8904_priv *wm8904, | |||
| 2462 | default: | 2463 | default: |
| 2463 | dev_err(codec->dev, "Unknown device type %d\n", | 2464 | dev_err(codec->dev, "Unknown device type %d\n", |
| 2464 | wm8904->devtype); | 2465 | wm8904->devtype); |
| 2465 | return -EINVAL; | 2466 | ret = -EINVAL; |
| 2467 | goto err; | ||
| 2466 | } | 2468 | } |
| 2467 | 2469 | ||
| 2468 | memcpy(codec->reg_cache, wm8904_reg, sizeof(wm8904_reg)); | 2470 | memcpy(codec->reg_cache, wm8904_reg, sizeof(wm8904_reg)); |
| @@ -2566,18 +2568,19 @@ static int wm8904_register(struct wm8904_priv *wm8904, | |||
| 2566 | ret = snd_soc_register_codec(codec); | 2568 | ret = snd_soc_register_codec(codec); |
| 2567 | if (ret != 0) { | 2569 | if (ret != 0) { |
| 2568 | dev_err(codec->dev, "Failed to register codec: %d\n", ret); | 2570 | dev_err(codec->dev, "Failed to register codec: %d\n", ret); |
| 2569 | return ret; | 2571 | goto err_enable; |
| 2570 | } | 2572 | } |
| 2571 | 2573 | ||
| 2572 | ret = snd_soc_register_dai(&wm8904_dai); | 2574 | ret = snd_soc_register_dai(&wm8904_dai); |
| 2573 | if (ret != 0) { | 2575 | if (ret != 0) { |
| 2574 | dev_err(codec->dev, "Failed to register DAI: %d\n", ret); | 2576 | dev_err(codec->dev, "Failed to register DAI: %d\n", ret); |
| 2575 | snd_soc_unregister_codec(codec); | 2577 | goto err_codec; |
| 2576 | return ret; | ||
| 2577 | } | 2578 | } |
| 2578 | 2579 | ||
| 2579 | return 0; | 2580 | return 0; |
| 2580 | 2581 | ||
| 2582 | err_codec: | ||
| 2583 | snd_soc_unregister_codec(codec); | ||
| 2581 | err_enable: | 2584 | err_enable: |
| 2582 | regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies); | 2585 | regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies); |
| 2583 | err_get: | 2586 | err_get: |
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index e3c4bbfaae27..f0c11138e610 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c | |||
| @@ -845,6 +845,7 @@ static void wm8940_unregister(struct wm8940_priv *wm8940) | |||
| 845 | static int wm8940_i2c_probe(struct i2c_client *i2c, | 845 | static int wm8940_i2c_probe(struct i2c_client *i2c, |
| 846 | const struct i2c_device_id *id) | 846 | const struct i2c_device_id *id) |
| 847 | { | 847 | { |
| 848 | int ret; | ||
| 848 | struct wm8940_priv *wm8940; | 849 | struct wm8940_priv *wm8940; |
| 849 | struct snd_soc_codec *codec; | 850 | struct snd_soc_codec *codec; |
| 850 | 851 | ||
| @@ -858,7 +859,11 @@ static int wm8940_i2c_probe(struct i2c_client *i2c, | |||
| 858 | codec->control_data = i2c; | 859 | codec->control_data = i2c; |
| 859 | codec->dev = &i2c->dev; | 860 | codec->dev = &i2c->dev; |
| 860 | 861 | ||
| 861 | return wm8940_register(wm8940, SND_SOC_I2C); | 862 | ret = wm8940_register(wm8940, SND_SOC_I2C); |
| 863 | if (ret < 0) | ||
| 864 | kfree(wm8940); | ||
| 865 | |||
| 866 | return ret; | ||
| 862 | } | 867 | } |
| 863 | 868 | ||
| 864 | static int __devexit wm8940_i2c_remove(struct i2c_client *client) | 869 | static int __devexit wm8940_i2c_remove(struct i2c_client *client) |
diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c index fedb76452f1b..5f025593d84d 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c | |||
| @@ -964,7 +964,8 @@ static int wm8955_register(struct wm8955_priv *wm8955, | |||
| 964 | 964 | ||
| 965 | if (wm8955_codec) { | 965 | if (wm8955_codec) { |
| 966 | dev_err(codec->dev, "Another WM8955 is registered\n"); | 966 | dev_err(codec->dev, "Another WM8955 is registered\n"); |
| 967 | return -EINVAL; | 967 | ret = -EINVAL; |
| 968 | goto err; | ||
| 968 | } | 969 | } |
| 969 | 970 | ||
| 970 | mutex_init(&codec->mutex); | 971 | mutex_init(&codec->mutex); |
| @@ -1047,18 +1048,19 @@ static int wm8955_register(struct wm8955_priv *wm8955, | |||
| 1047 | ret = snd_soc_register_codec(codec); | 1048 | ret = snd_soc_register_codec(codec); |
| 1048 | if (ret != 0) { | 1049 | if (ret != 0) { |
| 1049 | dev_err(codec->dev, "Failed to register codec: %d\n", ret); | 1050 | dev_err(codec->dev, "Failed to register codec: %d\n", ret); |
| 1050 | return ret; | 1051 | goto err_enable; |
| 1051 | } | 1052 | } |
| 1052 | 1053 | ||
| 1053 | ret = snd_soc_register_dai(&wm8955_dai); | 1054 | ret = snd_soc_register_dai(&wm8955_dai); |
| 1054 | if (ret != 0) { | 1055 | if (ret != 0) { |
| 1055 | dev_err(codec->dev, "Failed to register DAI: %d\n", ret); | 1056 | dev_err(codec->dev, "Failed to register DAI: %d\n", ret); |
| 1056 | snd_soc_unregister_codec(codec); | 1057 | goto err_codec; |
| 1057 | return ret; | ||
| 1058 | } | 1058 | } |
| 1059 | 1059 | ||
| 1060 | return 0; | 1060 | return 0; |
| 1061 | 1061 | ||
| 1062 | err_codec: | ||
| 1063 | snd_soc_unregister_codec(codec); | ||
| 1062 | err_enable: | 1064 | err_enable: |
| 1063 | regulator_bulk_disable(ARRAY_SIZE(wm8955->supplies), wm8955->supplies); | 1065 | regulator_bulk_disable(ARRAY_SIZE(wm8955->supplies), wm8955->supplies); |
| 1064 | err_get: | 1066 | err_get: |
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 7233cc68435a..3c6ee61f6c95 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c | |||
| @@ -79,12 +79,13 @@ struct wm8960_priv { | |||
| 79 | struct snd_soc_dapm_widget *lout1; | 79 | struct snd_soc_dapm_widget *lout1; |
| 80 | struct snd_soc_dapm_widget *rout1; | 80 | struct snd_soc_dapm_widget *rout1; |
| 81 | struct snd_soc_dapm_widget *out3; | 81 | struct snd_soc_dapm_widget *out3; |
| 82 | bool deemph; | ||
| 83 | int playback_fs; | ||
| 82 | }; | 84 | }; |
| 83 | 85 | ||
| 84 | #define wm8960_reset(c) snd_soc_write(c, WM8960_RESET, 0) | 86 | #define wm8960_reset(c) snd_soc_write(c, WM8960_RESET, 0) |
| 85 | 87 | ||
| 86 | /* enumerated controls */ | 88 | /* enumerated controls */ |
| 87 | static const char *wm8960_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"}; | ||
| 88 | static const char *wm8960_polarity[] = {"No Inversion", "Left Inverted", | 89 | static const char *wm8960_polarity[] = {"No Inversion", "Left Inverted", |
| 89 | "Right Inverted", "Stereo Inversion"}; | 90 | "Right Inverted", "Stereo Inversion"}; |
| 90 | static const char *wm8960_3d_upper_cutoff[] = {"High", "Low"}; | 91 | static const char *wm8960_3d_upper_cutoff[] = {"High", "Low"}; |
| @@ -93,7 +94,6 @@ static const char *wm8960_alcfunc[] = {"Off", "Right", "Left", "Stereo"}; | |||
| 93 | static const char *wm8960_alcmode[] = {"ALC", "Limiter"}; | 94 | static const char *wm8960_alcmode[] = {"ALC", "Limiter"}; |
| 94 | 95 | ||
| 95 | static const struct soc_enum wm8960_enum[] = { | 96 | static const struct soc_enum wm8960_enum[] = { |
| 96 | SOC_ENUM_SINGLE(WM8960_DACCTL1, 1, 4, wm8960_deemph), | ||
| 97 | SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity), | 97 | SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity), |
| 98 | SOC_ENUM_SINGLE(WM8960_DACCTL2, 5, 4, wm8960_polarity), | 98 | SOC_ENUM_SINGLE(WM8960_DACCTL2, 5, 4, wm8960_polarity), |
| 99 | SOC_ENUM_SINGLE(WM8960_3D, 6, 2, wm8960_3d_upper_cutoff), | 99 | SOC_ENUM_SINGLE(WM8960_3D, 6, 2, wm8960_3d_upper_cutoff), |
| @@ -102,6 +102,59 @@ static const struct soc_enum wm8960_enum[] = { | |||
| 102 | SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode), | 102 | SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode), |
| 103 | }; | 103 | }; |
| 104 | 104 | ||
| 105 | static const int deemph_settings[] = { 0, 32000, 44100, 48000 }; | ||
| 106 | |||
| 107 | static int wm8960_set_deemph(struct snd_soc_codec *codec) | ||
| 108 | { | ||
| 109 | struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); | ||
| 110 | int val, i, best; | ||
| 111 | |||
| 112 | /* If we're using deemphasis select the nearest available sample | ||
| 113 | * rate. | ||
| 114 | */ | ||
| 115 | if (wm8960->deemph) { | ||
| 116 | best = 1; | ||
| 117 | for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) { | ||
| 118 | if (abs(deemph_settings[i] - wm8960->playback_fs) < | ||
| 119 | abs(deemph_settings[best] - wm8960->playback_fs)) | ||
| 120 | best = i; | ||
| 121 | } | ||
| 122 | |||
| 123 | val = best << 1; | ||
| 124 | } else { | ||
| 125 | val = 0; | ||
| 126 | } | ||
| 127 | |||
| 128 | dev_dbg(codec->dev, "Set deemphasis %d\n", val); | ||
| 129 | |||
| 130 | return snd_soc_update_bits(codec, WM8960_DACCTL1, | ||
| 131 | 0x6, val); | ||
| 132 | } | ||
| 133 | |||
| 134 | static int wm8960_get_deemph(struct snd_kcontrol *kcontrol, | ||
| 135 | struct snd_ctl_elem_value *ucontrol) | ||
| 136 | { | ||
| 137 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
| 138 | struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); | ||
| 139 | |||
| 140 | return wm8960->deemph; | ||
| 141 | } | ||
| 142 | |||
| 143 | static int wm8960_put_deemph(struct snd_kcontrol *kcontrol, | ||
| 144 | struct snd_ctl_elem_value *ucontrol) | ||
| 145 | { | ||
| 146 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
| 147 | struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); | ||
| 148 | int deemph = ucontrol->value.enumerated.item[0]; | ||
| 149 | |||
| 150 | if (deemph > 1) | ||
| 151 | return -EINVAL; | ||
| 152 | |||
| 153 | wm8960->deemph = deemph; | ||
| 154 | |||
| 155 | return wm8960_set_deemph(codec); | ||
| 156 | } | ||
| 157 | |||
| 105 | static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0); | 158 | static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0); |
| 106 | static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1); | 159 | static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1); |
| 107 | static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0); | 160 | static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0); |
| @@ -131,23 +184,24 @@ SOC_SINGLE("Speaker DC Volume", WM8960_CLASSD3, 3, 5, 0), | |||
| 131 | SOC_SINGLE("Speaker AC Volume", WM8960_CLASSD3, 0, 5, 0), | 184 | SOC_SINGLE("Speaker AC Volume", WM8960_CLASSD3, 0, 5, 0), |
| 132 | 185 | ||
| 133 | SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0), | 186 | SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0), |
| 134 | SOC_ENUM("ADC Polarity", wm8960_enum[1]), | 187 | SOC_ENUM("ADC Polarity", wm8960_enum[0]), |
| 135 | SOC_ENUM("Playback De-emphasis", wm8960_enum[0]), | ||
| 136 | SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0), | 188 | SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0), |
| 137 | 189 | ||
| 138 | SOC_ENUM("DAC Polarity", wm8960_enum[2]), | 190 | SOC_ENUM("DAC Polarity", wm8960_enum[2]), |
| 191 | SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0, | ||
| 192 | wm8960_get_deemph, wm8960_put_deemph), | ||
| 139 | 193 | ||
| 140 | SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[3]), | 194 | SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[2]), |
| 141 | SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[4]), | 195 | SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[3]), |
| 142 | SOC_SINGLE("3D Volume", WM8960_3D, 1, 15, 0), | 196 | SOC_SINGLE("3D Volume", WM8960_3D, 1, 15, 0), |
| 143 | SOC_SINGLE("3D Switch", WM8960_3D, 0, 1, 0), | 197 | SOC_SINGLE("3D Switch", WM8960_3D, 0, 1, 0), |
| 144 | 198 | ||
| 145 | SOC_ENUM("ALC Function", wm8960_enum[5]), | 199 | SOC_ENUM("ALC Function", wm8960_enum[4]), |
| 146 | SOC_SINGLE("ALC Max Gain", WM8960_ALC1, 4, 7, 0), | 200 | SOC_SINGLE("ALC Max Gain", WM8960_ALC1, 4, 7, 0), |
| 147 | SOC_SINGLE("ALC Target", WM8960_ALC1, 0, 15, 1), | 201 | SOC_SINGLE("ALC Target", WM8960_ALC1, 0, 15, 1), |
| 148 | SOC_SINGLE("ALC Min Gain", WM8960_ALC2, 4, 7, 0), | 202 | SOC_SINGLE("ALC Min Gain", WM8960_ALC2, 4, 7, 0), |
| 149 | SOC_SINGLE("ALC Hold Time", WM8960_ALC2, 0, 15, 0), | 203 | SOC_SINGLE("ALC Hold Time", WM8960_ALC2, 0, 15, 0), |
| 150 | SOC_ENUM("ALC Mode", wm8960_enum[6]), | 204 | SOC_ENUM("ALC Mode", wm8960_enum[5]), |
| 151 | SOC_SINGLE("ALC Decay", WM8960_ALC3, 4, 15, 0), | 205 | SOC_SINGLE("ALC Decay", WM8960_ALC3, 4, 15, 0), |
| 152 | SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0), | 206 | SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0), |
| 153 | 207 | ||
| @@ -433,6 +487,21 @@ static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai, | |||
| 433 | return 0; | 487 | return 0; |
| 434 | } | 488 | } |
| 435 | 489 | ||
| 490 | static struct { | ||
| 491 | int rate; | ||
| 492 | unsigned int val; | ||
| 493 | } alc_rates[] = { | ||
| 494 | { 48000, 0 }, | ||
| 495 | { 44100, 0 }, | ||
| 496 | { 32000, 1 }, | ||
| 497 | { 22050, 2 }, | ||
| 498 | { 24000, 2 }, | ||
| 499 | { 16000, 3 }, | ||
| 500 | { 11250, 4 }, | ||
| 501 | { 12000, 4 }, | ||
| 502 | { 8000, 5 }, | ||
| 503 | }; | ||
| 504 | |||
| 436 | static int wm8960_hw_params(struct snd_pcm_substream *substream, | 505 | static int wm8960_hw_params(struct snd_pcm_substream *substream, |
| 437 | struct snd_pcm_hw_params *params, | 506 | struct snd_pcm_hw_params *params, |
| 438 | struct snd_soc_dai *dai) | 507 | struct snd_soc_dai *dai) |
| @@ -440,7 +509,9 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, | |||
| 440 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 509 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
| 441 | struct snd_soc_device *socdev = rtd->socdev; | 510 | struct snd_soc_device *socdev = rtd->socdev; |
| 442 | struct snd_soc_codec *codec = socdev->card->codec; | 511 | struct snd_soc_codec *codec = socdev->card->codec; |
| 512 | struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); | ||
| 443 | u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3; | 513 | u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3; |
| 514 | int i; | ||
| 444 | 515 | ||
| 445 | /* bit size */ | 516 | /* bit size */ |
| 446 | switch (params_format(params)) { | 517 | switch (params_format(params)) { |
| @@ -454,6 +525,18 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, | |||
| 454 | break; | 525 | break; |
| 455 | } | 526 | } |
| 456 | 527 | ||
| 528 | /* Update filters for the new rate */ | ||
| 529 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
| 530 | wm8960->playback_fs = params_rate(params); | ||
| 531 | wm8960_set_deemph(codec); | ||
| 532 | } else { | ||
| 533 | for (i = 0; i < ARRAY_SIZE(alc_rates); i++) | ||
| 534 | if (alc_rates[i].rate == params_rate(params)) | ||
| 535 | snd_soc_update_bits(codec, | ||
| 536 | WM8960_ADDCTL3, 0x7, | ||
| 537 | alc_rates[i].val); | ||
| 538 | } | ||
| 539 | |||
| 457 | /* set iface */ | 540 | /* set iface */ |
| 458 | snd_soc_write(codec, WM8960_IFACE1, iface); | 541 | snd_soc_write(codec, WM8960_IFACE1, iface); |
| 459 | return 0; | 542 | return 0; |
diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index 5b9a756242f1..2549d3a297ab 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c | |||
| @@ -1102,7 +1102,7 @@ static int wm8961_register(struct wm8961_priv *wm8961) | |||
| 1102 | ret = wm8961_reset(codec); | 1102 | ret = wm8961_reset(codec); |
| 1103 | if (ret < 0) { | 1103 | if (ret < 0) { |
| 1104 | dev_err(codec->dev, "Failed to issue reset\n"); | 1104 | dev_err(codec->dev, "Failed to issue reset\n"); |
| 1105 | return ret; | 1105 | goto err; |
| 1106 | } | 1106 | } |
| 1107 | 1107 | ||
| 1108 | /* Enable class W */ | 1108 | /* Enable class W */ |
| @@ -1147,18 +1147,19 @@ static int wm8961_register(struct wm8961_priv *wm8961) | |||
| 1147 | ret = snd_soc_register_codec(codec); | 1147 | ret = snd_soc_register_codec(codec); |
| 1148 | if (ret != 0) { | 1148 | if (ret != 0) { |
| 1149 | dev_err(codec->dev, "Failed to register codec: %d\n", ret); | 1149 | dev_err(codec->dev, "Failed to register codec: %d\n", ret); |
| 1150 | return ret; | 1150 | goto err; |
| 1151 | } | 1151 | } |
| 1152 | 1152 | ||
| 1153 | ret = snd_soc_register_dai(&wm8961_dai); | 1153 | ret = snd_soc_register_dai(&wm8961_dai); |
| 1154 | if (ret != 0) { | 1154 | if (ret != 0) { |
| 1155 | dev_err(codec->dev, "Failed to register DAI: %d\n", ret); | 1155 | dev_err(codec->dev, "Failed to register DAI: %d\n", ret); |
| 1156 | snd_soc_unregister_codec(codec); | 1156 | goto err_codec; |
| 1157 | return ret; | ||
| 1158 | } | 1157 | } |
| 1159 | 1158 | ||
| 1160 | return 0; | 1159 | return 0; |
| 1161 | 1160 | ||
| 1161 | err_codec: | ||
| 1162 | snd_soc_unregister_codec(codec); | ||
| 1162 | err: | 1163 | err: |
| 1163 | kfree(wm8961); | 1164 | kfree(wm8961); |
| 1164 | return ret; | 1165 | return ret; |
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index a2c4b2f37cca..1468fe10cbbe 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c | |||
| @@ -670,7 +670,8 @@ static __devinit int wm8974_register(struct wm8974_priv *wm8974) | |||
| 670 | 670 | ||
| 671 | if (wm8974_codec) { | 671 | if (wm8974_codec) { |
| 672 | dev_err(codec->dev, "Another WM8974 is registered\n"); | 672 | dev_err(codec->dev, "Another WM8974 is registered\n"); |
| 673 | return -EINVAL; | 673 | ret = -EINVAL; |
| 674 | goto err; | ||
| 674 | } | 675 | } |
| 675 | 676 | ||
| 676 | mutex_init(&codec->mutex); | 677 | mutex_init(&codec->mutex); |
diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c index 51d5f433215c..8a1ad778e7e3 100644 --- a/sound/soc/codecs/wm8978.c +++ b/sound/soc/codecs/wm8978.c | |||
| @@ -1076,7 +1076,6 @@ static __devinit int wm8978_register(struct wm8978_priv *wm8978) | |||
| 1076 | err_codec: | 1076 | err_codec: |
| 1077 | snd_soc_unregister_codec(codec); | 1077 | snd_soc_unregister_codec(codec); |
| 1078 | err: | 1078 | err: |
| 1079 | kfree(wm8978); | ||
| 1080 | return ret; | 1079 | return ret; |
| 1081 | } | 1080 | } |
| 1082 | 1081 | ||
| @@ -1085,13 +1084,13 @@ static __devexit void wm8978_unregister(struct wm8978_priv *wm8978) | |||
| 1085 | wm8978_set_bias_level(&wm8978->codec, SND_SOC_BIAS_OFF); | 1084 | wm8978_set_bias_level(&wm8978->codec, SND_SOC_BIAS_OFF); |
| 1086 | snd_soc_unregister_dai(&wm8978_dai); | 1085 | snd_soc_unregister_dai(&wm8978_dai); |
| 1087 | snd_soc_unregister_codec(&wm8978->codec); | 1086 | snd_soc_unregister_codec(&wm8978->codec); |
| 1088 | kfree(wm8978); | ||
| 1089 | wm8978_codec = NULL; | 1087 | wm8978_codec = NULL; |
| 1090 | } | 1088 | } |
| 1091 | 1089 | ||
| 1092 | static __devinit int wm8978_i2c_probe(struct i2c_client *i2c, | 1090 | static __devinit int wm8978_i2c_probe(struct i2c_client *i2c, |
| 1093 | const struct i2c_device_id *id) | 1091 | const struct i2c_device_id *id) |
| 1094 | { | 1092 | { |
| 1093 | int ret; | ||
| 1095 | struct wm8978_priv *wm8978; | 1094 | struct wm8978_priv *wm8978; |
| 1096 | struct snd_soc_codec *codec; | 1095 | struct snd_soc_codec *codec; |
| 1097 | 1096 | ||
| @@ -1107,13 +1106,18 @@ static __devinit int wm8978_i2c_probe(struct i2c_client *i2c, | |||
| 1107 | 1106 | ||
| 1108 | codec->dev = &i2c->dev; | 1107 | codec->dev = &i2c->dev; |
| 1109 | 1108 | ||
| 1110 | return wm8978_register(wm8978); | 1109 | ret = wm8978_register(wm8978); |
| 1110 | if (ret < 0) | ||
| 1111 | kfree(wm8978); | ||
| 1112 | |||
| 1113 | return ret; | ||
| 1111 | } | 1114 | } |
| 1112 | 1115 | ||
| 1113 | static __devexit int wm8978_i2c_remove(struct i2c_client *client) | 1116 | static __devexit int wm8978_i2c_remove(struct i2c_client *client) |
| 1114 | { | 1117 | { |
| 1115 | struct wm8978_priv *wm8978 = i2c_get_clientdata(client); | 1118 | struct wm8978_priv *wm8978 = i2c_get_clientdata(client); |
| 1116 | wm8978_unregister(wm8978); | 1119 | wm8978_unregister(wm8978); |
| 1120 | kfree(wm8978); | ||
| 1117 | return 0; | 1121 | return 0; |
| 1118 | } | 1122 | } |
| 1119 | 1123 | ||
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index c018772cc430..dd8d909788c1 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c | |||
| @@ -30,8 +30,6 @@ | |||
| 30 | 30 | ||
| 31 | #include "wm8990.h" | 31 | #include "wm8990.h" |
| 32 | 32 | ||
| 33 | #define WM8990_VERSION "0.2" | ||
| 34 | |||
| 35 | /* codec private data */ | 33 | /* codec private data */ |
| 36 | struct wm8990_priv { | 34 | struct wm8990_priv { |
| 37 | unsigned int sysclk; | 35 | unsigned int sysclk; |
| @@ -1511,8 +1509,6 @@ static int wm8990_probe(struct platform_device *pdev) | |||
| 1511 | struct wm8990_priv *wm8990; | 1509 | struct wm8990_priv *wm8990; |
| 1512 | int ret; | 1510 | int ret; |
| 1513 | 1511 | ||
| 1514 | pr_info("WM8990 Audio Codec %s\n", WM8990_VERSION); | ||
| 1515 | |||
| 1516 | setup = socdev->codec_data; | 1512 | setup = socdev->codec_data; |
| 1517 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); | 1513 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); |
| 1518 | if (codec == NULL) | 1514 | if (codec == NULL) |
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index e84a1177f350..a87046a96f2a 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c | |||
| @@ -1677,6 +1677,26 @@ static struct { | |||
| 1677 | 1677 | ||
| 1678 | static int wm8994_readable(unsigned int reg) | 1678 | static int wm8994_readable(unsigned int reg) |
| 1679 | { | 1679 | { |
| 1680 | switch (reg) { | ||
| 1681 | case WM8994_GPIO_1: | ||
| 1682 | case WM8994_GPIO_2: | ||
| 1683 | case WM8994_GPIO_3: | ||
| 1684 | case WM8994_GPIO_4: | ||
| 1685 | case WM8994_GPIO_5: | ||
| 1686 | case WM8994_GPIO_6: | ||
| 1687 | case WM8994_GPIO_7: | ||
| 1688 | case WM8994_GPIO_8: | ||
| 1689 | case WM8994_GPIO_9: | ||
| 1690 | case WM8994_GPIO_10: | ||
| 1691 | case WM8994_GPIO_11: | ||
| 1692 | case WM8994_INTERRUPT_STATUS_1: | ||
| 1693 | case WM8994_INTERRUPT_STATUS_2: | ||
| 1694 | case WM8994_INTERRUPT_RAW_STATUS_2: | ||
| 1695 | return 1; | ||
| 1696 | default: | ||
| 1697 | break; | ||
| 1698 | } | ||
| 1699 | |||
| 1680 | if (reg >= ARRAY_SIZE(access_masks)) | 1700 | if (reg >= ARRAY_SIZE(access_masks)) |
| 1681 | return 0; | 1701 | return 0; |
| 1682 | return access_masks[reg].readable != 0; | 1702 | return access_masks[reg].readable != 0; |
| @@ -2341,6 +2361,20 @@ SOC_DAPM_SINGLE("AIF2 Switch", WM8994_AIF1_ADC1_RIGHT_MIXER_ROUTING, | |||
| 2341 | 0, 1, 0), | 2361 | 0, 1, 0), |
| 2342 | }; | 2362 | }; |
| 2343 | 2363 | ||
| 2364 | static const struct snd_kcontrol_new aif1adc2l_mix[] = { | ||
| 2365 | SOC_DAPM_SINGLE("DMIC Switch", WM8994_AIF1_ADC2_LEFT_MIXER_ROUTING, | ||
| 2366 | 1, 1, 0), | ||
| 2367 | SOC_DAPM_SINGLE("AIF2 Switch", WM8994_AIF1_ADC2_LEFT_MIXER_ROUTING, | ||
| 2368 | 0, 1, 0), | ||
| 2369 | }; | ||
| 2370 | |||
| 2371 | static const struct snd_kcontrol_new aif1adc2r_mix[] = { | ||
| 2372 | SOC_DAPM_SINGLE("DMIC Switch", WM8994_AIF1_ADC2_RIGHT_MIXER_ROUTING, | ||
| 2373 | 1, 1, 0), | ||
| 2374 | SOC_DAPM_SINGLE("AIF2 Switch", WM8994_AIF1_ADC2_RIGHT_MIXER_ROUTING, | ||
| 2375 | 0, 1, 0), | ||
| 2376 | }; | ||
| 2377 | |||
| 2344 | static const struct snd_kcontrol_new aif2dac2l_mix[] = { | 2378 | static const struct snd_kcontrol_new aif2dac2l_mix[] = { |
| 2345 | SOC_DAPM_SINGLE("Right Sidetone Switch", WM8994_DAC2_LEFT_MIXER_ROUTING, | 2379 | SOC_DAPM_SINGLE("Right Sidetone Switch", WM8994_DAC2_LEFT_MIXER_ROUTING, |
| 2346 | 5, 1, 0), | 2380 | 5, 1, 0), |
| @@ -2472,6 +2506,7 @@ static const struct snd_kcontrol_new aif3adc_mux = | |||
| 2472 | static const struct snd_soc_dapm_widget wm8994_dapm_widgets[] = { | 2506 | static const struct snd_soc_dapm_widget wm8994_dapm_widgets[] = { |
| 2473 | SND_SOC_DAPM_INPUT("DMIC1DAT"), | 2507 | SND_SOC_DAPM_INPUT("DMIC1DAT"), |
| 2474 | SND_SOC_DAPM_INPUT("DMIC2DAT"), | 2508 | SND_SOC_DAPM_INPUT("DMIC2DAT"), |
| 2509 | SND_SOC_DAPM_INPUT("Clock"), | ||
| 2475 | 2510 | ||
| 2476 | SND_SOC_DAPM_SUPPLY("CLK_SYS", SND_SOC_NOPM, 0, 0, clk_sys_event, | 2511 | SND_SOC_DAPM_SUPPLY("CLK_SYS", SND_SOC_NOPM, 0, 0, clk_sys_event, |
| 2477 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), | 2512 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), |
| @@ -2506,6 +2541,11 @@ SND_SOC_DAPM_MIXER("AIF1ADC1L Mixer", SND_SOC_NOPM, 0, 0, | |||
| 2506 | SND_SOC_DAPM_MIXER("AIF1ADC1R Mixer", SND_SOC_NOPM, 0, 0, | 2541 | SND_SOC_DAPM_MIXER("AIF1ADC1R Mixer", SND_SOC_NOPM, 0, 0, |
| 2507 | aif1adc1r_mix, ARRAY_SIZE(aif1adc1r_mix)), | 2542 | aif1adc1r_mix, ARRAY_SIZE(aif1adc1r_mix)), |
| 2508 | 2543 | ||
| 2544 | SND_SOC_DAPM_MIXER("AIF1ADC2L Mixer", SND_SOC_NOPM, 0, 0, | ||
| 2545 | aif1adc2l_mix, ARRAY_SIZE(aif1adc2l_mix)), | ||
| 2546 | SND_SOC_DAPM_MIXER("AIF1ADC2R Mixer", SND_SOC_NOPM, 0, 0, | ||
| 2547 | aif1adc2r_mix, ARRAY_SIZE(aif1adc2r_mix)), | ||
| 2548 | |||
| 2509 | SND_SOC_DAPM_MIXER("AIF2DAC2L Mixer", SND_SOC_NOPM, 0, 0, | 2549 | SND_SOC_DAPM_MIXER("AIF2DAC2L Mixer", SND_SOC_NOPM, 0, 0, |
| 2510 | aif2dac2l_mix, ARRAY_SIZE(aif2dac2l_mix)), | 2550 | aif2dac2l_mix, ARRAY_SIZE(aif2dac2l_mix)), |
| 2511 | SND_SOC_DAPM_MIXER("AIF2DAC2R Mixer", SND_SOC_NOPM, 0, 0, | 2551 | SND_SOC_DAPM_MIXER("AIF2DAC2R Mixer", SND_SOC_NOPM, 0, 0, |
| @@ -2668,6 +2708,14 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
| 2668 | { "AIF1ADC1R Mixer", "ADC/DMIC Switch", "ADCR Mux" }, | 2708 | { "AIF1ADC1R Mixer", "ADC/DMIC Switch", "ADCR Mux" }, |
| 2669 | { "AIF1ADC1R Mixer", "AIF2 Switch", "AIF2DACR" }, | 2709 | { "AIF1ADC1R Mixer", "AIF2 Switch", "AIF2DACR" }, |
| 2670 | 2710 | ||
| 2711 | { "AIF1ADC2L", NULL, "AIF1ADC2L Mixer" }, | ||
| 2712 | { "AIF1ADC2L Mixer", "DMIC Switch", "DMIC2L" }, | ||
| 2713 | { "AIF1ADC2L Mixer", "AIF2 Switch", "AIF2DACL" }, | ||
| 2714 | |||
| 2715 | { "AIF1ADC2R", NULL, "AIF1ADC2R Mixer" }, | ||
| 2716 | { "AIF1ADC2R Mixer", "DMIC Switch", "DMIC2R" }, | ||
| 2717 | { "AIF1ADC2R Mixer", "AIF2 Switch", "AIF2DACR" }, | ||
| 2718 | |||
| 2671 | /* Pin level routing for AIF3 */ | 2719 | /* Pin level routing for AIF3 */ |
| 2672 | { "AIF1DAC1L", NULL, "AIF1DAC Mux" }, | 2720 | { "AIF1DAC1L", NULL, "AIF1DAC Mux" }, |
| 2673 | { "AIF1DAC1R", NULL, "AIF1DAC Mux" }, | 2721 | { "AIF1DAC1R", NULL, "AIF1DAC Mux" }, |
| @@ -2946,11 +2994,14 @@ static int wm8994_set_fll(struct snd_soc_dai *dai, int id, int src, | |||
| 2946 | return 0; | 2994 | return 0; |
| 2947 | } | 2995 | } |
| 2948 | 2996 | ||
| 2997 | static int opclk_divs[] = { 10, 20, 30, 40, 55, 60, 80, 120, 160 }; | ||
| 2998 | |||
| 2949 | static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai, | 2999 | static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai, |
| 2950 | int clk_id, unsigned int freq, int dir) | 3000 | int clk_id, unsigned int freq, int dir) |
| 2951 | { | 3001 | { |
| 2952 | struct snd_soc_codec *codec = dai->codec; | 3002 | struct snd_soc_codec *codec = dai->codec; |
| 2953 | struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); | 3003 | struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); |
| 3004 | int i; | ||
| 2954 | 3005 | ||
| 2955 | switch (dai->id) { | 3006 | switch (dai->id) { |
| 2956 | case 1: | 3007 | case 1: |
| @@ -2988,6 +3039,25 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai, | |||
| 2988 | dev_dbg(dai->dev, "AIF%d using FLL2\n", dai->id); | 3039 | dev_dbg(dai->dev, "AIF%d using FLL2\n", dai->id); |
| 2989 | break; | 3040 | break; |
| 2990 | 3041 | ||
| 3042 | case WM8994_SYSCLK_OPCLK: | ||
| 3043 | /* Special case - a division (times 10) is given and | ||
| 3044 | * no effect on main clocking. | ||
| 3045 | */ | ||
| 3046 | if (freq) { | ||
| 3047 | for (i = 0; i < ARRAY_SIZE(opclk_divs); i++) | ||
| 3048 | if (opclk_divs[i] == freq) | ||
| 3049 | break; | ||
| 3050 | if (i == ARRAY_SIZE(opclk_divs)) | ||
| 3051 | return -EINVAL; | ||
| 3052 | snd_soc_update_bits(codec, WM8994_CLOCKING_2, | ||
| 3053 | WM8994_OPCLK_DIV_MASK, i); | ||
| 3054 | snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_2, | ||
| 3055 | WM8994_OPCLK_ENA, WM8994_OPCLK_ENA); | ||
| 3056 | } else { | ||
| 3057 | snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_2, | ||
| 3058 | WM8994_OPCLK_ENA, 0); | ||
| 3059 | } | ||
| 3060 | |||
| 2991 | default: | 3061 | default: |
| 2992 | return -EINVAL; | 3062 | return -EINVAL; |
| 2993 | } | 3063 | } |
| @@ -4004,6 +4074,11 @@ static int wm8994_codec_probe(struct platform_device *pdev) | |||
| 4004 | 1 << WM8994_AIF2DAC_3D_GAIN_SHIFT, | 4074 | 1 << WM8994_AIF2DAC_3D_GAIN_SHIFT, |
| 4005 | 1 << WM8994_AIF2DAC_3D_GAIN_SHIFT); | 4075 | 1 << WM8994_AIF2DAC_3D_GAIN_SHIFT); |
| 4006 | 4076 | ||
| 4077 | /* Unconditionally enable AIF1 ADC TDM mode; it only affects | ||
| 4078 | * behaviour on idle TDM clock cycles. */ | ||
| 4079 | snd_soc_update_bits(codec, WM8994_AIF1_CONTROL_1, | ||
| 4080 | WM8994_AIF1ADC_TDM, WM8994_AIF1ADC_TDM); | ||
| 4081 | |||
| 4007 | wm8994_update_class_w(codec); | 4082 | wm8994_update_class_w(codec); |
| 4008 | 4083 | ||
| 4009 | ret = snd_soc_register_codec(codec); | 4084 | ret = snd_soc_register_codec(codec); |
diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h index 7072dc539354..2e0ca67a8df7 100644 --- a/sound/soc/codecs/wm8994.h +++ b/sound/soc/codecs/wm8994.h | |||
| @@ -20,6 +20,9 @@ extern struct snd_soc_dai wm8994_dai[]; | |||
| 20 | #define WM8994_SYSCLK_FLL1 3 | 20 | #define WM8994_SYSCLK_FLL1 3 |
| 21 | #define WM8994_SYSCLK_FLL2 4 | 21 | #define WM8994_SYSCLK_FLL2 4 |
| 22 | 22 | ||
| 23 | /* OPCLK is also configured with set_dai_sysclk, specify division*10 as rate. */ | ||
| 24 | #define WM8994_SYSCLK_OPCLK 5 | ||
| 25 | |||
| 23 | #define WM8994_FLL1 1 | 26 | #define WM8994_FLL1 1 |
| 24 | #define WM8994_FLL2 2 | 27 | #define WM8994_FLL2 2 |
| 25 | 28 | ||
diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 13186fb4dcb4..76b37ff6c264 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c | |||
| @@ -1356,7 +1356,7 @@ static int wm9081_register(struct wm9081_priv *wm9081, | |||
| 1356 | ret = snd_soc_codec_set_cache_io(codec, 8, 16, control); | 1356 | ret = snd_soc_codec_set_cache_io(codec, 8, 16, control); |
| 1357 | if (ret != 0) { | 1357 | if (ret != 0) { |
| 1358 | dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); | 1358 | dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); |
| 1359 | return ret; | 1359 | goto err; |
| 1360 | } | 1360 | } |
| 1361 | 1361 | ||
| 1362 | reg = snd_soc_read(codec, WM9081_SOFTWARE_RESET); | 1362 | reg = snd_soc_read(codec, WM9081_SOFTWARE_RESET); |
| @@ -1369,7 +1369,7 @@ static int wm9081_register(struct wm9081_priv *wm9081, | |||
| 1369 | ret = wm9081_reset(codec); | 1369 | ret = wm9081_reset(codec); |
| 1370 | if (ret < 0) { | 1370 | if (ret < 0) { |
| 1371 | dev_err(codec->dev, "Failed to issue reset\n"); | 1371 | dev_err(codec->dev, "Failed to issue reset\n"); |
| 1372 | return ret; | 1372 | goto err; |
| 1373 | } | 1373 | } |
| 1374 | 1374 | ||
| 1375 | wm9081_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 1375 | wm9081_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
| @@ -1388,18 +1388,19 @@ static int wm9081_register(struct wm9081_priv *wm9081, | |||
| 1388 | ret = snd_soc_register_codec(codec); | 1388 | ret = snd_soc_register_codec(codec); |
| 1389 | if (ret != 0) { | 1389 | if (ret != 0) { |
| 1390 | dev_err(codec->dev, "Failed to register codec: %d\n", ret); | 1390 | dev_err(codec->dev, "Failed to register codec: %d\n", ret); |
| 1391 | return ret; | 1391 | goto err; |
| 1392 | } | 1392 | } |
| 1393 | 1393 | ||
| 1394 | ret = snd_soc_register_dai(&wm9081_dai); | 1394 | ret = snd_soc_register_dai(&wm9081_dai); |
| 1395 | if (ret != 0) { | 1395 | if (ret != 0) { |
| 1396 | dev_err(codec->dev, "Failed to register DAI: %d\n", ret); | 1396 | dev_err(codec->dev, "Failed to register DAI: %d\n", ret); |
| 1397 | snd_soc_unregister_codec(codec); | 1397 | goto err_codec; |
| 1398 | return ret; | ||
| 1399 | } | 1398 | } |
| 1400 | 1399 | ||
| 1401 | return 0; | 1400 | return 0; |
| 1402 | 1401 | ||
| 1402 | err_codec: | ||
| 1403 | snd_soc_unregister_codec(codec); | ||
| 1403 | err: | 1404 | err: |
| 1404 | kfree(wm9081); | 1405 | kfree(wm9081); |
| 1405 | return ret; | 1406 | return ret; |
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 16f1a57da08a..2cb81538cd91 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c | |||
| @@ -410,6 +410,8 @@ static int hp_event(struct snd_soc_dapm_widget *w, | |||
| 410 | WM8993_HPOUT1L_DLY | | 410 | WM8993_HPOUT1L_DLY | |
| 411 | WM8993_HPOUT1R_DLY, 0); | 411 | WM8993_HPOUT1R_DLY, 0); |
| 412 | 412 | ||
| 413 | snd_soc_write(codec, WM8993_DC_SERVO_0, 0); | ||
| 414 | |||
| 413 | snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1, | 415 | snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1, |
| 414 | WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA, | 416 | WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA, |
| 415 | 0); | 417 | 0); |
diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index adadcd3aa1b1..9e8932abf158 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c | |||
| @@ -26,6 +26,7 @@ | |||
| 26 | #include <mach/asp.h> | 26 | #include <mach/asp.h> |
| 27 | 27 | ||
| 28 | #include "davinci-pcm.h" | 28 | #include "davinci-pcm.h" |
| 29 | #include "davinci-i2s.h" | ||
| 29 | 30 | ||
| 30 | 31 | ||
| 31 | /* | 32 | /* |
| @@ -68,16 +69,21 @@ | |||
| 68 | #define DAVINCI_MCBSP_RCR_RDATDLY(v) ((v) << 16) | 69 | #define DAVINCI_MCBSP_RCR_RDATDLY(v) ((v) << 16) |
| 69 | #define DAVINCI_MCBSP_RCR_RFIG (1 << 18) | 70 | #define DAVINCI_MCBSP_RCR_RFIG (1 << 18) |
| 70 | #define DAVINCI_MCBSP_RCR_RWDLEN2(v) ((v) << 21) | 71 | #define DAVINCI_MCBSP_RCR_RWDLEN2(v) ((v) << 21) |
| 72 | #define DAVINCI_MCBSP_RCR_RFRLEN2(v) ((v) << 24) | ||
| 73 | #define DAVINCI_MCBSP_RCR_RPHASE BIT(31) | ||
| 71 | 74 | ||
| 72 | #define DAVINCI_MCBSP_XCR_XWDLEN1(v) ((v) << 5) | 75 | #define DAVINCI_MCBSP_XCR_XWDLEN1(v) ((v) << 5) |
| 73 | #define DAVINCI_MCBSP_XCR_XFRLEN1(v) ((v) << 8) | 76 | #define DAVINCI_MCBSP_XCR_XFRLEN1(v) ((v) << 8) |
| 74 | #define DAVINCI_MCBSP_XCR_XDATDLY(v) ((v) << 16) | 77 | #define DAVINCI_MCBSP_XCR_XDATDLY(v) ((v) << 16) |
| 75 | #define DAVINCI_MCBSP_XCR_XFIG (1 << 18) | 78 | #define DAVINCI_MCBSP_XCR_XFIG (1 << 18) |
| 76 | #define DAVINCI_MCBSP_XCR_XWDLEN2(v) ((v) << 21) | 79 | #define DAVINCI_MCBSP_XCR_XWDLEN2(v) ((v) << 21) |
| 80 | #define DAVINCI_MCBSP_XCR_XFRLEN2(v) ((v) << 24) | ||
| 81 | #define DAVINCI_MCBSP_XCR_XPHASE BIT(31) | ||
| 77 | 82 | ||
| 78 | #define DAVINCI_MCBSP_SRGR_FWID(v) ((v) << 8) | 83 | #define DAVINCI_MCBSP_SRGR_FWID(v) ((v) << 8) |
| 79 | #define DAVINCI_MCBSP_SRGR_FPER(v) ((v) << 16) | 84 | #define DAVINCI_MCBSP_SRGR_FPER(v) ((v) << 16) |
| 80 | #define DAVINCI_MCBSP_SRGR_FSGM (1 << 28) | 85 | #define DAVINCI_MCBSP_SRGR_FSGM (1 << 28) |
| 86 | #define DAVINCI_MCBSP_SRGR_CLKSM BIT(29) | ||
| 81 | 87 | ||
| 82 | #define DAVINCI_MCBSP_PCR_CLKRP (1 << 0) | 88 | #define DAVINCI_MCBSP_PCR_CLKRP (1 << 0) |
| 83 | #define DAVINCI_MCBSP_PCR_CLKXP (1 << 1) | 89 | #define DAVINCI_MCBSP_PCR_CLKXP (1 << 1) |
| @@ -116,6 +122,7 @@ static const unsigned char double_fmt[SNDRV_PCM_FORMAT_S32_LE + 1] = { | |||
| 116 | }; | 122 | }; |
| 117 | 123 | ||
| 118 | struct davinci_mcbsp_dev { | 124 | struct davinci_mcbsp_dev { |
| 125 | struct device *dev; | ||
| 119 | struct davinci_pcm_dma_params dma_params[2]; | 126 | struct davinci_pcm_dma_params dma_params[2]; |
| 120 | void __iomem *base; | 127 | void __iomem *base; |
| 121 | #define MOD_DSP_A 0 | 128 | #define MOD_DSP_A 0 |
| @@ -144,6 +151,11 @@ struct davinci_mcbsp_dev { | |||
| 144 | * won't end up being swapped because of the underrun. | 151 | * won't end up being swapped because of the underrun. |
| 145 | */ | 152 | */ |
| 146 | unsigned enable_channel_combine:1; | 153 | unsigned enable_channel_combine:1; |
| 154 | |||
| 155 | unsigned int fmt; | ||
| 156 | int clk_div; | ||
| 157 | int clk_input_pin; | ||
| 158 | bool i2s_accurate_sck; | ||
| 147 | }; | 159 | }; |
| 148 | 160 | ||
| 149 | static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev, | 161 | static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev, |
| @@ -254,10 +266,12 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, | |||
| 254 | struct davinci_mcbsp_dev *dev = cpu_dai->private_data; | 266 | struct davinci_mcbsp_dev *dev = cpu_dai->private_data; |
| 255 | unsigned int pcr; | 267 | unsigned int pcr; |
| 256 | unsigned int srgr; | 268 | unsigned int srgr; |
| 269 | /* Attention srgr is updated by hw_params! */ | ||
| 257 | srgr = DAVINCI_MCBSP_SRGR_FSGM | | 270 | srgr = DAVINCI_MCBSP_SRGR_FSGM | |
| 258 | DAVINCI_MCBSP_SRGR_FPER(DEFAULT_BITPERSAMPLE * 2 - 1) | | 271 | DAVINCI_MCBSP_SRGR_FPER(DEFAULT_BITPERSAMPLE * 2 - 1) | |
| 259 | DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1); | 272 | DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1); |
| 260 | 273 | ||
| 274 | dev->fmt = fmt; | ||
| 261 | /* set master/slave audio interface */ | 275 | /* set master/slave audio interface */ |
| 262 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | 276 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { |
| 263 | case SND_SOC_DAIFMT_CBS_CFS: | 277 | case SND_SOC_DAIFMT_CBS_CFS: |
| @@ -268,11 +282,26 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, | |||
| 268 | DAVINCI_MCBSP_PCR_CLKRM; | 282 | DAVINCI_MCBSP_PCR_CLKRM; |
| 269 | break; | 283 | break; |
| 270 | case SND_SOC_DAIFMT_CBM_CFS: | 284 | case SND_SOC_DAIFMT_CBM_CFS: |
| 271 | /* McBSP CLKR pin is the input for the Sample Rate Generator. | 285 | pcr = DAVINCI_MCBSP_PCR_FSRM | DAVINCI_MCBSP_PCR_FSXM; |
| 272 | * McBSP FSR and FSX are driven by the Sample Rate Generator. */ | 286 | /* |
| 273 | pcr = DAVINCI_MCBSP_PCR_SCLKME | | 287 | * Selection of the clock input pin that is the |
| 274 | DAVINCI_MCBSP_PCR_FSXM | | 288 | * input for the Sample Rate Generator. |
| 275 | DAVINCI_MCBSP_PCR_FSRM; | 289 | * McBSP FSR and FSX are driven by the Sample Rate |
| 290 | * Generator. | ||
| 291 | */ | ||
| 292 | switch (dev->clk_input_pin) { | ||
| 293 | case MCBSP_CLKS: | ||
| 294 | pcr |= DAVINCI_MCBSP_PCR_CLKXM | | ||
| 295 | DAVINCI_MCBSP_PCR_CLKRM; | ||
| 296 | break; | ||
| 297 | case MCBSP_CLKR: | ||
| 298 | pcr |= DAVINCI_MCBSP_PCR_SCLKME; | ||
| 299 | break; | ||
| 300 | default: | ||
| 301 | dev_err(dev->dev, "bad clk_input_pin\n"); | ||
| 302 | return -EINVAL; | ||
| 303 | } | ||
| 304 | |||
| 276 | break; | 305 | break; |
| 277 | case SND_SOC_DAIFMT_CBM_CFM: | 306 | case SND_SOC_DAIFMT_CBM_CFM: |
| 278 | /* codec is master */ | 307 | /* codec is master */ |
| @@ -372,6 +401,18 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, | |||
| 372 | return 0; | 401 | return 0; |
| 373 | } | 402 | } |
| 374 | 403 | ||
| 404 | static int davinci_i2s_dai_set_clkdiv(struct snd_soc_dai *cpu_dai, | ||
| 405 | int div_id, int div) | ||
| 406 | { | ||
| 407 | struct davinci_mcbsp_dev *dev = cpu_dai->private_data; | ||
| 408 | |||
| 409 | if (div_id != DAVINCI_MCBSP_CLKGDV) | ||
| 410 | return -ENODEV; | ||
| 411 | |||
| 412 | dev->clk_div = div; | ||
| 413 | return 0; | ||
| 414 | } | ||
| 415 | |||
| 375 | static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, | 416 | static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, |
| 376 | struct snd_pcm_hw_params *params, | 417 | struct snd_pcm_hw_params *params, |
| 377 | struct snd_soc_dai *dai) | 418 | struct snd_soc_dai *dai) |
| @@ -380,8 +421,8 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, | |||
| 380 | struct davinci_pcm_dma_params *dma_params = | 421 | struct davinci_pcm_dma_params *dma_params = |
| 381 | &dev->dma_params[substream->stream]; | 422 | &dev->dma_params[substream->stream]; |
| 382 | struct snd_interval *i = NULL; | 423 | struct snd_interval *i = NULL; |
| 383 | int mcbsp_word_length; | 424 | int mcbsp_word_length, master; |
| 384 | unsigned int rcr, xcr, srgr; | 425 | unsigned int rcr, xcr, srgr, clk_div, freq, framesize; |
| 385 | u32 spcr; | 426 | u32 spcr; |
| 386 | snd_pcm_format_t fmt; | 427 | snd_pcm_format_t fmt; |
| 387 | unsigned element_cnt = 1; | 428 | unsigned element_cnt = 1; |
| @@ -396,12 +437,59 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, | |||
| 396 | davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); | 437 | davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); |
| 397 | } | 438 | } |
| 398 | 439 | ||
| 399 | i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); | 440 | master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK; |
| 400 | srgr = DAVINCI_MCBSP_SRGR_FSGM; | 441 | fmt = params_format(params); |
| 401 | srgr |= DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1); | 442 | mcbsp_word_length = asp_word_length[fmt]; |
| 402 | 443 | ||
| 403 | i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS); | 444 | switch (master) { |
| 404 | srgr |= DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1); | 445 | case SND_SOC_DAIFMT_CBS_CFS: |
| 446 | freq = clk_get_rate(dev->clk); | ||
| 447 | srgr = DAVINCI_MCBSP_SRGR_FSGM | | ||
| 448 | DAVINCI_MCBSP_SRGR_CLKSM; | ||
| 449 | srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length * | ||
| 450 | 8 - 1); | ||
| 451 | if (dev->i2s_accurate_sck) { | ||
| 452 | clk_div = 256; | ||
| 453 | do { | ||
| 454 | framesize = (freq / (--clk_div)) / | ||
| 455 | params->rate_num * | ||
| 456 | params->rate_den; | ||
| 457 | } while (((framesize < 33) || (framesize > 4095)) && | ||
| 458 | (clk_div)); | ||
| 459 | clk_div--; | ||
| 460 | srgr |= DAVINCI_MCBSP_SRGR_FPER(framesize - 1); | ||
| 461 | } else { | ||
| 462 | /* symmetric waveforms */ | ||
| 463 | clk_div = freq / (mcbsp_word_length * 16) / | ||
| 464 | params->rate_num * params->rate_den; | ||
| 465 | srgr |= DAVINCI_MCBSP_SRGR_FPER(mcbsp_word_length * | ||
| 466 | 16 - 1); | ||
| 467 | } | ||
| 468 | clk_div &= 0xFF; | ||
| 469 | srgr |= clk_div; | ||
| 470 | break; | ||
| 471 | case SND_SOC_DAIFMT_CBM_CFS: | ||
| 472 | srgr = DAVINCI_MCBSP_SRGR_FSGM; | ||
| 473 | clk_div = dev->clk_div - 1; | ||
| 474 | srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length * 8 - 1); | ||
| 475 | srgr |= DAVINCI_MCBSP_SRGR_FPER(mcbsp_word_length * 16 - 1); | ||
| 476 | clk_div &= 0xFF; | ||
| 477 | srgr |= clk_div; | ||
| 478 | break; | ||
| 479 | case SND_SOC_DAIFMT_CBM_CFM: | ||
| 480 | /* Clock and frame sync given from external sources */ | ||
| 481 | i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); | ||
| 482 | srgr = DAVINCI_MCBSP_SRGR_FSGM; | ||
| 483 | srgr |= DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1); | ||
| 484 | pr_debug("%s - %d FWID set: re-read srgr = %X\n", | ||
| 485 | __func__, __LINE__, snd_interval_value(i) - 1); | ||
| 486 | |||
| 487 | i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS); | ||
| 488 | srgr |= DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1); | ||
| 489 | break; | ||
| 490 | default: | ||
| 491 | return -EINVAL; | ||
| 492 | } | ||
| 405 | davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr); | 493 | davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr); |
| 406 | 494 | ||
| 407 | rcr = DAVINCI_MCBSP_RCR_RFIG; | 495 | rcr = DAVINCI_MCBSP_RCR_RFIG; |
| @@ -426,12 +514,41 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, | |||
| 426 | element_cnt = 1; | 514 | element_cnt = 1; |
| 427 | fmt = double_fmt[fmt]; | 515 | fmt = double_fmt[fmt]; |
| 428 | } | 516 | } |
| 517 | switch (master) { | ||
| 518 | case SND_SOC_DAIFMT_CBS_CFS: | ||
| 519 | case SND_SOC_DAIFMT_CBS_CFM: | ||
| 520 | rcr |= DAVINCI_MCBSP_RCR_RFRLEN2(0); | ||
| 521 | xcr |= DAVINCI_MCBSP_XCR_XFRLEN2(0); | ||
| 522 | rcr |= DAVINCI_MCBSP_RCR_RPHASE; | ||
| 523 | xcr |= DAVINCI_MCBSP_XCR_XPHASE; | ||
| 524 | break; | ||
| 525 | case SND_SOC_DAIFMT_CBM_CFM: | ||
| 526 | case SND_SOC_DAIFMT_CBM_CFS: | ||
| 527 | rcr |= DAVINCI_MCBSP_RCR_RFRLEN2(element_cnt - 1); | ||
| 528 | xcr |= DAVINCI_MCBSP_XCR_XFRLEN2(element_cnt - 1); | ||
| 529 | break; | ||
| 530 | default: | ||
| 531 | return -EINVAL; | ||
| 532 | } | ||
| 429 | } | 533 | } |
| 430 | dma_params->acnt = dma_params->data_type = data_type[fmt]; | 534 | dma_params->acnt = dma_params->data_type = data_type[fmt]; |
| 431 | dma_params->fifo_level = 0; | 535 | dma_params->fifo_level = 0; |
| 432 | mcbsp_word_length = asp_word_length[fmt]; | 536 | mcbsp_word_length = asp_word_length[fmt]; |
| 433 | rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1); | 537 | |
| 434 | xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1); | 538 | switch (master) { |
| 539 | case SND_SOC_DAIFMT_CBS_CFS: | ||
| 540 | case SND_SOC_DAIFMT_CBS_CFM: | ||
| 541 | rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(0); | ||
| 542 | xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(0); | ||
| 543 | break; | ||
| 544 | case SND_SOC_DAIFMT_CBM_CFM: | ||
| 545 | case SND_SOC_DAIFMT_CBM_CFS: | ||
| 546 | rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1); | ||
| 547 | xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1); | ||
| 548 | break; | ||
| 549 | default: | ||
| 550 | return -EINVAL; | ||
| 551 | } | ||
| 435 | 552 | ||
| 436 | rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) | | 553 | rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) | |
| 437 | DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length); | 554 | DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length); |
| @@ -442,6 +559,10 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, | |||
| 442 | davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr); | 559 | davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr); |
| 443 | else | 560 | else |
| 444 | davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr); | 561 | davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr); |
| 562 | |||
| 563 | pr_debug("%s - %d srgr=%X\n", __func__, __LINE__, srgr); | ||
| 564 | pr_debug("%s - %d xcr=%X\n", __func__, __LINE__, xcr); | ||
| 565 | pr_debug("%s - %d rcr=%X\n", __func__, __LINE__, rcr); | ||
| 445 | return 0; | 566 | return 0; |
| 446 | } | 567 | } |
| 447 | 568 | ||
| @@ -500,6 +621,7 @@ static struct snd_soc_dai_ops davinci_i2s_dai_ops = { | |||
| 500 | .trigger = davinci_i2s_trigger, | 621 | .trigger = davinci_i2s_trigger, |
| 501 | .hw_params = davinci_i2s_hw_params, | 622 | .hw_params = davinci_i2s_hw_params, |
| 502 | .set_fmt = davinci_i2s_set_dai_fmt, | 623 | .set_fmt = davinci_i2s_set_dai_fmt, |
| 624 | .set_clkdiv = davinci_i2s_dai_set_clkdiv, | ||
| 503 | 625 | ||
| 504 | }; | 626 | }; |
| 505 | 627 | ||
| @@ -526,6 +648,8 @@ static int davinci_i2s_probe(struct platform_device *pdev) | |||
| 526 | struct snd_platform_data *pdata = pdev->dev.platform_data; | 648 | struct snd_platform_data *pdata = pdev->dev.platform_data; |
| 527 | struct davinci_mcbsp_dev *dev; | 649 | struct davinci_mcbsp_dev *dev; |
| 528 | struct resource *mem, *ioarea, *res; | 650 | struct resource *mem, *ioarea, *res; |
| 651 | enum dma_event_q asp_chan_q = EVENTQ_0; | ||
| 652 | enum dma_event_q ram_chan_q = EVENTQ_1; | ||
| 529 | int ret; | 653 | int ret; |
| 530 | 654 | ||
| 531 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 655 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| @@ -552,7 +676,17 @@ static int davinci_i2s_probe(struct platform_device *pdev) | |||
| 552 | pdata->sram_size_playback; | 676 | pdata->sram_size_playback; |
| 553 | dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].sram_size = | 677 | dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].sram_size = |
| 554 | pdata->sram_size_capture; | 678 | pdata->sram_size_capture; |
| 679 | dev->clk_input_pin = pdata->clk_input_pin; | ||
| 680 | dev->i2s_accurate_sck = pdata->i2s_accurate_sck; | ||
| 681 | asp_chan_q = pdata->asp_chan_q; | ||
| 682 | ram_chan_q = pdata->ram_chan_q; | ||
| 555 | } | 683 | } |
| 684 | |||
| 685 | dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].asp_chan_q = asp_chan_q; | ||
| 686 | dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].ram_chan_q = ram_chan_q; | ||
| 687 | dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].asp_chan_q = asp_chan_q; | ||
| 688 | dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].ram_chan_q = ram_chan_q; | ||
| 689 | |||
| 556 | dev->clk = clk_get(&pdev->dev, NULL); | 690 | dev->clk = clk_get(&pdev->dev, NULL); |
| 557 | if (IS_ERR(dev->clk)) { | 691 | if (IS_ERR(dev->clk)) { |
| 558 | ret = -ENODEV; | 692 | ret = -ENODEV; |
| @@ -584,6 +718,7 @@ static int davinci_i2s_probe(struct platform_device *pdev) | |||
| 584 | goto err_free_mem; | 718 | goto err_free_mem; |
| 585 | } | 719 | } |
| 586 | dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start; | 720 | dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start; |
| 721 | dev->dev = &pdev->dev; | ||
| 587 | 722 | ||
| 588 | davinci_i2s_dai.private_data = dev; | 723 | davinci_i2s_dai.private_data = dev; |
| 589 | davinci_i2s_dai.capture.dma_data = dev->dma_params; | 724 | davinci_i2s_dai.capture.dma_data = dev->dma_params; |
diff --git a/sound/soc/davinci/davinci-i2s.h b/sound/soc/davinci/davinci-i2s.h index 241648ce8873..0b1e77b8c279 100644 --- a/sound/soc/davinci/davinci-i2s.h +++ b/sound/soc/davinci/davinci-i2s.h | |||
| @@ -12,6 +12,11 @@ | |||
| 12 | #ifndef _DAVINCI_I2S_H | 12 | #ifndef _DAVINCI_I2S_H |
| 13 | #define _DAVINCI_I2S_H | 13 | #define _DAVINCI_I2S_H |
| 14 | 14 | ||
| 15 | /* McBSP dividers */ | ||
| 16 | enum davinci_mcbsp_div { | ||
| 17 | DAVINCI_MCBSP_CLKGDV, /* Sample rate generator divider */ | ||
| 18 | }; | ||
| 19 | |||
| 15 | extern struct snd_soc_dai davinci_i2s_dai; | 20 | extern struct snd_soc_dai davinci_i2s_dai; |
| 16 | 21 | ||
| 17 | #endif | 22 | #endif |
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index d3955096d872..b24720894af6 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c | |||
| @@ -890,7 +890,8 @@ static int davinci_mcasp_probe(struct platform_device *pdev) | |||
| 890 | dev->rxnumevt = pdata->rxnumevt; | 890 | dev->rxnumevt = pdata->rxnumevt; |
| 891 | 891 | ||
| 892 | dma_data = &dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]; | 892 | dma_data = &dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]; |
| 893 | dma_data->eventq_no = pdata->eventq_no; | 893 | dma_data->asp_chan_q = pdata->asp_chan_q; |
| 894 | dma_data->ram_chan_q = pdata->ram_chan_q; | ||
| 894 | dma_data->dma_addr = (dma_addr_t) (pdata->tx_dma_offset + | 895 | dma_data->dma_addr = (dma_addr_t) (pdata->tx_dma_offset + |
| 895 | io_v2p(dev->base)); | 896 | io_v2p(dev->base)); |
| 896 | 897 | ||
| @@ -904,7 +905,8 @@ static int davinci_mcasp_probe(struct platform_device *pdev) | |||
| 904 | dma_data->channel = res->start; | 905 | dma_data->channel = res->start; |
| 905 | 906 | ||
| 906 | dma_data = &dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]; | 907 | dma_data = &dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]; |
| 907 | dma_data->eventq_no = pdata->eventq_no; | 908 | dma_data->asp_chan_q = pdata->asp_chan_q; |
| 909 | dma_data->ram_chan_q = pdata->ram_chan_q; | ||
| 908 | dma_data->dma_addr = (dma_addr_t)(pdata->rx_dma_offset + | 910 | dma_data->dma_addr = (dma_addr_t)(pdata->rx_dma_offset + |
| 909 | io_v2p(dev->base)); | 911 | io_v2p(dev->base)); |
| 910 | 912 | ||
diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index 2dc406f42fe7..a7124116d2e0 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c | |||
| @@ -381,7 +381,7 @@ static int request_ping_pong(struct snd_pcm_substream *substream, | |||
| 381 | /* Request ram master channel */ | 381 | /* Request ram master channel */ |
| 382 | link = prtd->ram_channel = edma_alloc_channel(EDMA_CHANNEL_ANY, | 382 | link = prtd->ram_channel = edma_alloc_channel(EDMA_CHANNEL_ANY, |
| 383 | davinci_pcm_dma_irq, substream, | 383 | davinci_pcm_dma_irq, substream, |
| 384 | EVENTQ_1); | 384 | prtd->params->ram_chan_q); |
| 385 | if (link < 0) | 385 | if (link < 0) |
| 386 | goto exit1; | 386 | goto exit1; |
| 387 | 387 | ||
| @@ -477,7 +477,8 @@ static int davinci_pcm_dma_request(struct snd_pcm_substream *substream) | |||
| 477 | 477 | ||
| 478 | /* Request asp master DMA channel */ | 478 | /* Request asp master DMA channel */ |
| 479 | link = prtd->asp_channel = edma_alloc_channel(params->channel, | 479 | link = prtd->asp_channel = edma_alloc_channel(params->channel, |
| 480 | davinci_pcm_dma_irq, substream, EVENTQ_0); | 480 | davinci_pcm_dma_irq, substream, |
| 481 | prtd->params->asp_chan_q); | ||
| 481 | if (link < 0) | 482 | if (link < 0) |
| 482 | goto exit1; | 483 | goto exit1; |
| 483 | 484 | ||
| @@ -800,7 +801,7 @@ static void davinci_pcm_free(struct snd_pcm *pcm) | |||
| 800 | dma_free_writecombine(pcm->card->dev, buf->bytes, | 801 | dma_free_writecombine(pcm->card->dev, buf->bytes, |
| 801 | buf->area, buf->addr); | 802 | buf->area, buf->addr); |
| 802 | buf->area = NULL; | 803 | buf->area = NULL; |
| 803 | iram_dma = (struct snd_dma_buffer *)buf->private_data; | 804 | iram_dma = buf->private_data; |
| 804 | if (iram_dma) { | 805 | if (iram_dma) { |
| 805 | sram_free(iram_dma->area, iram_dma->bytes); | 806 | sram_free(iram_dma->area, iram_dma->bytes); |
| 806 | kfree(iram_dma); | 807 | kfree(iram_dma); |
diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h index 0764944cf10f..b799a02333d8 100644 --- a/sound/soc/davinci/davinci-pcm.h +++ b/sound/soc/davinci/davinci-pcm.h | |||
| @@ -21,7 +21,8 @@ struct davinci_pcm_dma_params { | |||
| 21 | unsigned short acnt; | 21 | unsigned short acnt; |
| 22 | dma_addr_t dma_addr; /* device physical address for DMA */ | 22 | dma_addr_t dma_addr; /* device physical address for DMA */ |
| 23 | unsigned sram_size; | 23 | unsigned sram_size; |
| 24 | enum dma_event_q eventq_no; /* event queue number */ | 24 | enum dma_event_q asp_chan_q; /* event queue number for ASP channel */ |
| 25 | enum dma_event_q ram_chan_q; /* event queue number for RAM channel */ | ||
| 25 | unsigned char data_type; /* xfer data type */ | 26 | unsigned char data_type; /* xfer data type */ |
| 26 | unsigned char convert_mono_stereo; | 27 | unsigned char convert_mono_stereo; |
| 27 | unsigned int fifo_level; | 28 | unsigned int fifo_level; |
diff --git a/sound/soc/davinci/davinci-vcif.c b/sound/soc/davinci/davinci-vcif.c index 9aa980d38231..48678533da7a 100644 --- a/sound/soc/davinci/davinci-vcif.c +++ b/sound/soc/davinci/davinci-vcif.c | |||
| @@ -203,7 +203,7 @@ static int davinci_vcif_probe(struct platform_device *pdev) | |||
| 203 | int ret; | 203 | int ret; |
| 204 | 204 | ||
| 205 | davinci_vcif_dev = kzalloc(sizeof(struct davinci_vcif_dev), GFP_KERNEL); | 205 | davinci_vcif_dev = kzalloc(sizeof(struct davinci_vcif_dev), GFP_KERNEL); |
| 206 | if (!davinci_vc) { | 206 | if (!davinci_vcif_dev) { |
| 207 | dev_dbg(&pdev->dev, | 207 | dev_dbg(&pdev->dev, |
| 208 | "could not allocate memory for private data\n"); | 208 | "could not allocate memory for private data\n"); |
| 209 | return -ENOMEM; | 209 | return -ENOMEM; |
diff --git a/sound/soc/ep93xx/Kconfig b/sound/soc/ep93xx/Kconfig new file mode 100644 index 000000000000..f617f560f46b --- /dev/null +++ b/sound/soc/ep93xx/Kconfig | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | config SND_EP93XX_SOC | ||
| 2 | tristate "SoC Audio support for the Cirrus Logic EP93xx series" | ||
| 3 | depends on ARCH_EP93XX && SND_SOC | ||
| 4 | help | ||
| 5 | Say Y or M if you want to add support for codecs attached to | ||
| 6 | the EP93xx I2S interface. | ||
| 7 | |||
| 8 | config SND_EP93XX_SOC_I2S | ||
| 9 | tristate | ||
| 10 | |||
| 11 | config SND_EP93XX_SOC_SNAPPERCL15 | ||
| 12 | tristate "SoC Audio support for Bluewater Systems Snapper CL15 module" | ||
| 13 | depends on SND_EP93XX_SOC && MACH_SNAPPER_CL15 | ||
| 14 | select SND_EP93XX_SOC_I2S | ||
| 15 | select SND_SOC_TLV320AIC23 | ||
| 16 | help | ||
| 17 | Say Y or M here if you want to add support for I2S audio on the | ||
| 18 | Bluewater Systems Snapper CL15 module. | ||
diff --git a/sound/soc/ep93xx/Makefile b/sound/soc/ep93xx/Makefile new file mode 100644 index 000000000000..272e60f57b9a --- /dev/null +++ b/sound/soc/ep93xx/Makefile | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # EP93xx Platform Support | ||
| 2 | snd-soc-ep93xx-objs := ep93xx-pcm.o | ||
| 3 | snd-soc-ep93xx-i2s-objs := ep93xx-i2s.o | ||
| 4 | |||
| 5 | obj-$(CONFIG_SND_EP93XX_SOC) += snd-soc-ep93xx.o | ||
| 6 | obj-$(CONFIG_SND_EP93XX_SOC_I2S) += snd-soc-ep93xx-i2s.o | ||
| 7 | |||
| 8 | # EP93XX Machine Support | ||
| 9 | snd-soc-snappercl15-objs := snappercl15.o | ||
| 10 | |||
| 11 | obj-$(CONFIG_SND_EP93XX_SOC_SNAPPERCL15) += snd-soc-snappercl15.o | ||
diff --git a/sound/soc/ep93xx/ep93xx-i2s.c b/sound/soc/ep93xx/ep93xx-i2s.c new file mode 100644 index 000000000000..00b946632184 --- /dev/null +++ b/sound/soc/ep93xx/ep93xx-i2s.c | |||
| @@ -0,0 +1,487 @@ | |||
| 1 | /* | ||
| 2 | * linux/sound/soc/ep93xx-i2s.c | ||
| 3 | * EP93xx I2S driver | ||
| 4 | * | ||
| 5 | * Copyright (C) 2010 Ryan Mallon <ryan@bluewatersys.com> | ||
| 6 | * | ||
| 7 | * Based on the original driver by: | ||
| 8 | * Copyright (C) 2007 Chase Douglas <chasedouglas@gmail> | ||
| 9 | * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org> | ||
| 10 | * | ||
| 11 | * This program is free software; you can redistribute it and/or modify | ||
| 12 | * it under the terms of the GNU General Public License version 2 as | ||
| 13 | * published by the Free Software Foundation. | ||
| 14 | * | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/init.h> | ||
| 19 | #include <linux/slab.h> | ||
| 20 | #include <linux/clk.h> | ||
| 21 | #include <linux/io.h> | ||
| 22 | |||
| 23 | #include <sound/core.h> | ||
| 24 | #include <sound/pcm.h> | ||
| 25 | #include <sound/pcm_params.h> | ||
| 26 | #include <sound/initval.h> | ||
| 27 | #include <sound/soc.h> | ||
| 28 | |||
| 29 | #include <mach/hardware.h> | ||
| 30 | #include <mach/ep93xx-regs.h> | ||
| 31 | #include <mach/dma.h> | ||
| 32 | |||
| 33 | #include "ep93xx-pcm.h" | ||
| 34 | #include "ep93xx-i2s.h" | ||
| 35 | |||
| 36 | #define EP93XX_I2S_TXCLKCFG 0x00 | ||
| 37 | #define EP93XX_I2S_RXCLKCFG 0x04 | ||
| 38 | #define EP93XX_I2S_GLCTRL 0x0C | ||
| 39 | |||
| 40 | #define EP93XX_I2S_TXLINCTRLDATA 0x28 | ||
| 41 | #define EP93XX_I2S_TXCTRL 0x2C | ||
| 42 | #define EP93XX_I2S_TXWRDLEN 0x30 | ||
| 43 | #define EP93XX_I2S_TX0EN 0x34 | ||
| 44 | |||
| 45 | #define EP93XX_I2S_RXLINCTRLDATA 0x58 | ||
| 46 | #define EP93XX_I2S_RXCTRL 0x5C | ||
| 47 | #define EP93XX_I2S_RXWRDLEN 0x60 | ||
| 48 | #define EP93XX_I2S_RX0EN 0x64 | ||
| 49 | |||
| 50 | #define EP93XX_I2S_WRDLEN_16 (0 << 0) | ||
| 51 | #define EP93XX_I2S_WRDLEN_24 (1 << 0) | ||
| 52 | #define EP93XX_I2S_WRDLEN_32 (2 << 0) | ||
| 53 | |||
| 54 | #define EP93XX_I2S_LINCTRLDATA_R_JUST (1 << 2) /* Right justify */ | ||
| 55 | |||
| 56 | #define EP93XX_I2S_CLKCFG_LRS (1 << 0) /* lrclk polarity */ | ||
| 57 | #define EP93XX_I2S_CLKCFG_CKP (1 << 1) /* Bit clock polarity */ | ||
| 58 | #define EP93XX_I2S_CLKCFG_REL (1 << 2) /* First bit transition */ | ||
| 59 | #define EP93XX_I2S_CLKCFG_MASTER (1 << 3) /* Master mode */ | ||
| 60 | #define EP93XX_I2S_CLKCFG_NBCG (1 << 4) /* Not bit clock gating */ | ||
| 61 | |||
| 62 | struct ep93xx_i2s_info { | ||
| 63 | struct clk *mclk; | ||
| 64 | struct clk *sclk; | ||
| 65 | struct clk *lrclk; | ||
| 66 | struct ep93xx_pcm_dma_params *dma_params; | ||
| 67 | struct resource *mem; | ||
| 68 | void __iomem *regs; | ||
| 69 | }; | ||
| 70 | |||
| 71 | struct ep93xx_pcm_dma_params ep93xx_i2s_dma_params[] = { | ||
| 72 | [SNDRV_PCM_STREAM_PLAYBACK] = { | ||
| 73 | .name = "i2s-pcm-out", | ||
| 74 | .dma_port = EP93XX_DMA_M2P_PORT_I2S1, | ||
| 75 | }, | ||
| 76 | [SNDRV_PCM_STREAM_CAPTURE] = { | ||
| 77 | .name = "i2s-pcm-in", | ||
| 78 | .dma_port = EP93XX_DMA_M2P_PORT_I2S1, | ||
| 79 | }, | ||
| 80 | }; | ||
| 81 | |||
| 82 | static inline void ep93xx_i2s_write_reg(struct ep93xx_i2s_info *info, | ||
| 83 | unsigned reg, unsigned val) | ||
| 84 | { | ||
| 85 | __raw_writel(val, info->regs + reg); | ||
| 86 | } | ||
| 87 | |||
| 88 | static inline unsigned ep93xx_i2s_read_reg(struct ep93xx_i2s_info *info, | ||
| 89 | unsigned reg) | ||
| 90 | { | ||
| 91 | return __raw_readl(info->regs + reg); | ||
| 92 | } | ||
| 93 | |||
| 94 | static void ep93xx_i2s_enable(struct ep93xx_i2s_info *info, int stream) | ||
| 95 | { | ||
| 96 | unsigned base_reg; | ||
| 97 | int i; | ||
| 98 | |||
| 99 | if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 && | ||
| 100 | (ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) { | ||
| 101 | /* Enable clocks */ | ||
| 102 | clk_enable(info->mclk); | ||
| 103 | clk_enable(info->sclk); | ||
| 104 | clk_enable(info->lrclk); | ||
| 105 | |||
| 106 | /* Enable i2s */ | ||
| 107 | ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 1); | ||
| 108 | } | ||
| 109 | |||
| 110 | /* Enable fifos */ | ||
| 111 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
| 112 | base_reg = EP93XX_I2S_TX0EN; | ||
| 113 | else | ||
| 114 | base_reg = EP93XX_I2S_RX0EN; | ||
| 115 | for (i = 0; i < 3; i++) | ||
| 116 | ep93xx_i2s_write_reg(info, base_reg + (i * 4), 1); | ||
| 117 | } | ||
| 118 | |||
| 119 | static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream) | ||
| 120 | { | ||
| 121 | unsigned base_reg; | ||
| 122 | int i; | ||
| 123 | |||
| 124 | /* Disable fifos */ | ||
| 125 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
| 126 | base_reg = EP93XX_I2S_TX0EN; | ||
| 127 | else | ||
| 128 | base_reg = EP93XX_I2S_RX0EN; | ||
| 129 | for (i = 0; i < 3; i++) | ||
| 130 | ep93xx_i2s_write_reg(info, base_reg + (i * 4), 0); | ||
| 131 | |||
| 132 | if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 && | ||
| 133 | (ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) { | ||
| 134 | /* Disable i2s */ | ||
| 135 | ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 0); | ||
| 136 | |||
| 137 | /* Disable clocks */ | ||
| 138 | clk_disable(info->lrclk); | ||
| 139 | clk_disable(info->sclk); | ||
| 140 | clk_disable(info->mclk); | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | static int ep93xx_i2s_startup(struct snd_pcm_substream *substream, | ||
| 145 | struct snd_soc_dai *dai) | ||
| 146 | { | ||
| 147 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
| 148 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
| 149 | struct ep93xx_i2s_info *info = rtd->dai->cpu_dai->private_data; | ||
| 150 | |||
| 151 | snd_soc_dai_set_dma_data(cpu_dai, substream, | ||
| 152 | &info->dma_params[substream->stream]); | ||
| 153 | return 0; | ||
| 154 | } | ||
| 155 | |||
| 156 | static void ep93xx_i2s_shutdown(struct snd_pcm_substream *substream, | ||
| 157 | struct snd_soc_dai *dai) | ||
| 158 | { | ||
| 159 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
| 160 | struct ep93xx_i2s_info *info = rtd->dai->cpu_dai->private_data; | ||
| 161 | |||
| 162 | ep93xx_i2s_disable(info, substream->stream); | ||
| 163 | } | ||
| 164 | |||
| 165 | static int ep93xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, | ||
| 166 | unsigned int fmt) | ||
| 167 | { | ||
| 168 | struct ep93xx_i2s_info *info = cpu_dai->private_data; | ||
| 169 | unsigned int clk_cfg, lin_ctrl; | ||
| 170 | |||
| 171 | clk_cfg = ep93xx_i2s_read_reg(info, EP93XX_I2S_RXCLKCFG); | ||
| 172 | lin_ctrl = ep93xx_i2s_read_reg(info, EP93XX_I2S_RXLINCTRLDATA); | ||
| 173 | |||
| 174 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
| 175 | case SND_SOC_DAIFMT_I2S: | ||
| 176 | clk_cfg |= EP93XX_I2S_CLKCFG_REL; | ||
| 177 | lin_ctrl &= ~EP93XX_I2S_LINCTRLDATA_R_JUST; | ||
| 178 | break; | ||
| 179 | |||
| 180 | case SND_SOC_DAIFMT_LEFT_J: | ||
| 181 | clk_cfg &= ~EP93XX_I2S_CLKCFG_REL; | ||
| 182 | lin_ctrl &= ~EP93XX_I2S_LINCTRLDATA_R_JUST; | ||
| 183 | break; | ||
| 184 | |||
| 185 | case SND_SOC_DAIFMT_RIGHT_J: | ||
| 186 | clk_cfg &= ~EP93XX_I2S_CLKCFG_REL; | ||
| 187 | lin_ctrl |= EP93XX_I2S_LINCTRLDATA_R_JUST; | ||
| 188 | break; | ||
| 189 | |||
| 190 | default: | ||
| 191 | return -EINVAL; | ||
| 192 | } | ||
| 193 | |||
| 194 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
| 195 | case SND_SOC_DAIFMT_CBS_CFS: | ||
| 196 | /* CPU is master */ | ||
| 197 | clk_cfg |= EP93XX_I2S_CLKCFG_MASTER; | ||
| 198 | break; | ||
| 199 | |||
| 200 | case SND_SOC_DAIFMT_CBM_CFM: | ||
| 201 | /* Codec is master */ | ||
| 202 | clk_cfg &= ~EP93XX_I2S_CLKCFG_MASTER; | ||
| 203 | break; | ||
| 204 | |||
| 205 | default: | ||
| 206 | return -EINVAL; | ||
| 207 | } | ||
| 208 | |||
| 209 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
| 210 | case SND_SOC_DAIFMT_NB_NF: | ||
| 211 | /* Negative bit clock, lrclk low on left word */ | ||
| 212 | clk_cfg &= ~(EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_REL); | ||
| 213 | break; | ||
| 214 | |||
| 215 | case SND_SOC_DAIFMT_NB_IF: | ||
| 216 | /* Negative bit clock, lrclk low on right word */ | ||
| 217 | clk_cfg &= ~EP93XX_I2S_CLKCFG_CKP; | ||
| 218 | clk_cfg |= EP93XX_I2S_CLKCFG_REL; | ||
| 219 | break; | ||
| 220 | |||
| 221 | case SND_SOC_DAIFMT_IB_NF: | ||
| 222 | /* Positive bit clock, lrclk low on left word */ | ||
| 223 | clk_cfg |= EP93XX_I2S_CLKCFG_CKP; | ||
| 224 | clk_cfg &= ~EP93XX_I2S_CLKCFG_REL; | ||
| 225 | break; | ||
| 226 | |||
| 227 | case SND_SOC_DAIFMT_IB_IF: | ||
| 228 | /* Positive bit clock, lrclk low on right word */ | ||
| 229 | clk_cfg |= EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_REL; | ||
| 230 | break; | ||
| 231 | } | ||
| 232 | |||
| 233 | /* Write new register values */ | ||
| 234 | ep93xx_i2s_write_reg(info, EP93XX_I2S_RXCLKCFG, clk_cfg); | ||
| 235 | ep93xx_i2s_write_reg(info, EP93XX_I2S_TXCLKCFG, clk_cfg); | ||
| 236 | ep93xx_i2s_write_reg(info, EP93XX_I2S_RXLINCTRLDATA, lin_ctrl); | ||
| 237 | ep93xx_i2s_write_reg(info, EP93XX_I2S_TXLINCTRLDATA, lin_ctrl); | ||
| 238 | return 0; | ||
| 239 | } | ||
| 240 | |||
| 241 | static int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream, | ||
| 242 | struct snd_pcm_hw_params *params, | ||
| 243 | struct snd_soc_dai *dai) | ||
| 244 | { | ||
| 245 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
| 246 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
| 247 | struct ep93xx_i2s_info *info = cpu_dai->private_data; | ||
| 248 | unsigned word_len, div, sdiv, lrdiv; | ||
| 249 | int found = 0, err; | ||
| 250 | |||
| 251 | switch (params_format(params)) { | ||
| 252 | case SNDRV_PCM_FORMAT_S16_LE: | ||
| 253 | word_len = EP93XX_I2S_WRDLEN_16; | ||
| 254 | break; | ||
| 255 | |||
| 256 | case SNDRV_PCM_FORMAT_S24_LE: | ||
| 257 | word_len = EP93XX_I2S_WRDLEN_24; | ||
| 258 | break; | ||
| 259 | |||
| 260 | case SNDRV_PCM_FORMAT_S32_LE: | ||
| 261 | word_len = EP93XX_I2S_WRDLEN_32; | ||
| 262 | break; | ||
| 263 | |||
| 264 | default: | ||
| 265 | return -EINVAL; | ||
| 266 | } | ||
| 267 | |||
| 268 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
| 269 | ep93xx_i2s_write_reg(info, EP93XX_I2S_TXWRDLEN, word_len); | ||
| 270 | else | ||
| 271 | ep93xx_i2s_write_reg(info, EP93XX_I2S_RXWRDLEN, word_len); | ||
| 272 | |||
| 273 | /* | ||
| 274 | * Calculate the sdiv (bit clock) and lrdiv (left/right clock) values. | ||
| 275 | * If the lrclk is pulse length is larger than the word size, then the | ||
| 276 | * bit clock will be gated for the unused bits. | ||
| 277 | */ | ||
| 278 | div = (clk_get_rate(info->mclk) / params_rate(params)) * | ||
| 279 | params_channels(params); | ||
| 280 | for (sdiv = 2; sdiv <= 4; sdiv += 2) | ||
| 281 | for (lrdiv = 32; lrdiv <= 128; lrdiv <<= 1) | ||
| 282 | if (sdiv * lrdiv == div) { | ||
| 283 | found = 1; | ||
| 284 | goto out; | ||
| 285 | } | ||
| 286 | out: | ||
| 287 | if (!found) | ||
| 288 | return -EINVAL; | ||
| 289 | |||
| 290 | err = clk_set_rate(info->sclk, clk_get_rate(info->mclk) / sdiv); | ||
| 291 | if (err) | ||
| 292 | return err; | ||
| 293 | |||
| 294 | err = clk_set_rate(info->lrclk, clk_get_rate(info->sclk) / lrdiv); | ||
| 295 | if (err) | ||
| 296 | return err; | ||
| 297 | |||
| 298 | ep93xx_i2s_enable(info, substream->stream); | ||
| 299 | return 0; | ||
| 300 | } | ||
| 301 | |||
| 302 | static int ep93xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, | ||
| 303 | unsigned int freq, int dir) | ||
| 304 | { | ||
| 305 | struct ep93xx_i2s_info *info = cpu_dai->private_data; | ||
| 306 | |||
| 307 | if (dir == SND_SOC_CLOCK_IN || clk_id != 0) | ||
| 308 | return -EINVAL; | ||
| 309 | |||
| 310 | return clk_set_rate(info->mclk, freq); | ||
| 311 | } | ||
| 312 | |||
| 313 | #ifdef CONFIG_PM | ||
| 314 | static int ep93xx_i2s_suspend(struct snd_soc_dai *dai) | ||
| 315 | { | ||
| 316 | struct ep93xx_i2s_info *info = dai->private_data; | ||
| 317 | |||
| 318 | if (!dai->active) | ||
| 319 | return; | ||
| 320 | |||
| 321 | ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_PLAYBACK); | ||
| 322 | ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_CAPTURE); | ||
| 323 | } | ||
| 324 | |||
| 325 | static int ep93xx_i2s_resume(struct snd_soc_dai *dai) | ||
| 326 | { | ||
| 327 | struct ep93xx_i2s_info *info = dai->private_data; | ||
| 328 | |||
| 329 | if (!dai->active) | ||
| 330 | return; | ||
| 331 | |||
| 332 | ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_PLAYBACK); | ||
| 333 | ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_CAPTURE); | ||
| 334 | } | ||
| 335 | #else | ||
| 336 | #define ep93xx_i2s_suspend NULL | ||
| 337 | #define ep93xx_i2s_resume NULL | ||
| 338 | #endif | ||
| 339 | |||
| 340 | static struct snd_soc_dai_ops ep93xx_i2s_dai_ops = { | ||
| 341 | .startup = ep93xx_i2s_startup, | ||
| 342 | .shutdown = ep93xx_i2s_shutdown, | ||
| 343 | .hw_params = ep93xx_i2s_hw_params, | ||
| 344 | .set_sysclk = ep93xx_i2s_set_sysclk, | ||
| 345 | .set_fmt = ep93xx_i2s_set_dai_fmt, | ||
| 346 | }; | ||
| 347 | |||
| 348 | #define EP93XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ | ||
| 349 | SNDRV_PCM_FMTBIT_S24_LE | \ | ||
| 350 | SNDRV_PCM_FMTBIT_S32_LE) | ||
| 351 | |||
| 352 | struct snd_soc_dai ep93xx_i2s_dai = { | ||
| 353 | .name = "ep93xx-i2s", | ||
| 354 | .id = 0, | ||
| 355 | .symmetric_rates= 1, | ||
| 356 | .suspend = ep93xx_i2s_suspend, | ||
| 357 | .resume = ep93xx_i2s_resume, | ||
| 358 | .playback = { | ||
| 359 | .channels_min = 2, | ||
| 360 | .channels_max = 2, | ||
| 361 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
| 362 | .formats = EP93XX_I2S_FORMATS, | ||
| 363 | }, | ||
| 364 | .capture = { | ||
| 365 | .channels_min = 2, | ||
| 366 | .channels_max = 2, | ||
| 367 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
| 368 | .formats = EP93XX_I2S_FORMATS, | ||
| 369 | }, | ||
| 370 | .ops = &ep93xx_i2s_dai_ops, | ||
| 371 | }; | ||
| 372 | EXPORT_SYMBOL_GPL(ep93xx_i2s_dai); | ||
| 373 | |||
| 374 | static int ep93xx_i2s_probe(struct platform_device *pdev) | ||
| 375 | { | ||
| 376 | struct ep93xx_i2s_info *info; | ||
| 377 | struct resource *res; | ||
| 378 | int err; | ||
| 379 | |||
| 380 | info = kzalloc(sizeof(struct ep93xx_i2s_info), GFP_KERNEL); | ||
| 381 | if (!info) { | ||
| 382 | err = -ENOMEM; | ||
| 383 | goto fail; | ||
| 384 | } | ||
| 385 | |||
| 386 | ep93xx_i2s_dai.dev = &pdev->dev; | ||
| 387 | ep93xx_i2s_dai.private_data = info; | ||
| 388 | info->dma_params = ep93xx_i2s_dma_params; | ||
| 389 | |||
| 390 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 391 | if (!res) { | ||
| 392 | err = -ENODEV; | ||
| 393 | goto fail; | ||
| 394 | } | ||
| 395 | |||
| 396 | info->mem = request_mem_region(res->start, resource_size(res), | ||
| 397 | pdev->name); | ||
| 398 | if (!info->mem) { | ||
| 399 | err = -EBUSY; | ||
| 400 | goto fail; | ||
| 401 | } | ||
| 402 | |||
| 403 | info->regs = ioremap(info->mem->start, resource_size(info->mem)); | ||
| 404 | if (!info->regs) { | ||
| 405 | err = -ENXIO; | ||
| 406 | goto fail_release_mem; | ||
| 407 | } | ||
| 408 | |||
| 409 | info->mclk = clk_get(&pdev->dev, "mclk"); | ||
| 410 | if (IS_ERR(info->mclk)) { | ||
| 411 | err = PTR_ERR(info->mclk); | ||
| 412 | goto fail_unmap_mem; | ||
| 413 | } | ||
| 414 | |||
| 415 | info->sclk = clk_get(&pdev->dev, "sclk"); | ||
| 416 | if (IS_ERR(info->sclk)) { | ||
| 417 | err = PTR_ERR(info->sclk); | ||
| 418 | goto fail_put_mclk; | ||
| 419 | } | ||
| 420 | |||
| 421 | info->lrclk = clk_get(&pdev->dev, "lrclk"); | ||
| 422 | if (IS_ERR(info->lrclk)) { | ||
| 423 | err = PTR_ERR(info->lrclk); | ||
| 424 | goto fail_put_sclk; | ||
| 425 | } | ||
| 426 | |||
| 427 | err = snd_soc_register_dai(&ep93xx_i2s_dai); | ||
| 428 | if (err) | ||
| 429 | goto fail_put_lrclk; | ||
| 430 | |||
| 431 | return 0; | ||
| 432 | |||
| 433 | fail_put_lrclk: | ||
| 434 | clk_put(info->lrclk); | ||
| 435 | fail_put_sclk: | ||
| 436 | clk_put(info->sclk); | ||
| 437 | fail_put_mclk: | ||
| 438 | clk_put(info->mclk); | ||
| 439 | fail_unmap_mem: | ||
| 440 | iounmap(info->regs); | ||
| 441 | fail_release_mem: | ||
| 442 | release_mem_region(info->mem->start, resource_size(info->mem)); | ||
| 443 | kfree(info); | ||
| 444 | fail: | ||
| 445 | return err; | ||
| 446 | } | ||
| 447 | |||
| 448 | static int __devexit ep93xx_i2s_remove(struct platform_device *pdev) | ||
| 449 | { | ||
| 450 | struct ep93xx_i2s_info *info = ep93xx_i2s_dai.private_data; | ||
| 451 | |||
| 452 | snd_soc_unregister_dai(&ep93xx_i2s_dai); | ||
| 453 | clk_put(info->lrclk); | ||
| 454 | clk_put(info->sclk); | ||
| 455 | clk_put(info->mclk); | ||
| 456 | iounmap(info->regs); | ||
| 457 | release_mem_region(info->mem->start, resource_size(info->mem)); | ||
| 458 | kfree(info); | ||
| 459 | return 0; | ||
| 460 | } | ||
| 461 | |||
| 462 | static struct platform_driver ep93xx_i2s_driver = { | ||
| 463 | .probe = ep93xx_i2s_probe, | ||
| 464 | .remove = __devexit_p(ep93xx_i2s_remove), | ||
| 465 | .driver = { | ||
| 466 | .name = "ep93xx-i2s", | ||
| 467 | .owner = THIS_MODULE, | ||
| 468 | }, | ||
| 469 | }; | ||
| 470 | |||
| 471 | static int __init ep93xx_i2s_init(void) | ||
| 472 | { | ||
| 473 | return platform_driver_register(&ep93xx_i2s_driver); | ||
| 474 | } | ||
| 475 | |||
| 476 | static void __exit ep93xx_i2s_exit(void) | ||
| 477 | { | ||
| 478 | platform_driver_unregister(&ep93xx_i2s_driver); | ||
| 479 | } | ||
| 480 | |||
| 481 | module_init(ep93xx_i2s_init); | ||
| 482 | module_exit(ep93xx_i2s_exit); | ||
| 483 | |||
| 484 | MODULE_ALIAS("platform:ep93xx-i2s"); | ||
| 485 | MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>"); | ||
| 486 | MODULE_DESCRIPTION("EP93XX I2S driver"); | ||
| 487 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/ep93xx/ep93xx-i2s.h b/sound/soc/ep93xx/ep93xx-i2s.h new file mode 100644 index 000000000000..3bd4ebfaa1de --- /dev/null +++ b/sound/soc/ep93xx/ep93xx-i2s.h | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | /* | ||
| 2 | * linux/sound/soc/ep93xx-i2s.h | ||
| 3 | * EP93xx I2S driver | ||
| 4 | * | ||
| 5 | * Copyright (C) 2010 Ryan Mallon <ryan@bluewatersys.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | * | ||
| 11 | */ | ||
| 12 | |||
| 13 | #ifndef _EP93XX_SND_SOC_I2S_H | ||
| 14 | #define _EP93XX_SND_SOC_I2S_H | ||
| 15 | |||
| 16 | extern struct snd_soc_dai ep93xx_i2s_dai; | ||
| 17 | |||
| 18 | #endif /* _EP93XX_SND_SOC_I2S_H */ | ||
diff --git a/sound/soc/ep93xx/ep93xx-pcm.c b/sound/soc/ep93xx/ep93xx-pcm.c new file mode 100644 index 000000000000..4ba938400791 --- /dev/null +++ b/sound/soc/ep93xx/ep93xx-pcm.c | |||
| @@ -0,0 +1,319 @@ | |||
| 1 | /* | ||
| 2 | * linux/sound/arm/ep93xx-pcm.c - EP93xx ALSA PCM interface | ||
| 3 | * | ||
| 4 | * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org> | ||
| 5 | * Copyright (C) 2006 Applied Data Systems | ||
| 6 | * | ||
| 7 | * Rewritten for the SoC audio subsystem (Based on PXA2xx code): | ||
| 8 | * Copyright (c) 2008 Ryan Mallon <ryan@bluewatersys.com> | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or modify | ||
| 11 | * it under the terms of the GNU General Public License version 2 as | ||
| 12 | * published by the Free Software Foundation. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/init.h> | ||
| 17 | #include <linux/device.h> | ||
| 18 | #include <linux/slab.h> | ||
| 19 | #include <linux/dma-mapping.h> | ||
| 20 | |||
| 21 | #include <sound/core.h> | ||
| 22 | #include <sound/pcm.h> | ||
| 23 | #include <sound/pcm_params.h> | ||
| 24 | #include <sound/soc.h> | ||
| 25 | |||
| 26 | #include <mach/dma.h> | ||
| 27 | #include <mach/hardware.h> | ||
| 28 | #include <mach/ep93xx-regs.h> | ||
| 29 | |||
| 30 | #include "ep93xx-pcm.h" | ||
| 31 | |||
| 32 | static const struct snd_pcm_hardware ep93xx_pcm_hardware = { | ||
| 33 | .info = (SNDRV_PCM_INFO_MMAP | | ||
| 34 | SNDRV_PCM_INFO_MMAP_VALID | | ||
| 35 | SNDRV_PCM_INFO_INTERLEAVED | | ||
| 36 | SNDRV_PCM_INFO_BLOCK_TRANSFER), | ||
| 37 | |||
| 38 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
| 39 | .rate_min = SNDRV_PCM_RATE_8000, | ||
| 40 | .rate_max = SNDRV_PCM_RATE_48000, | ||
| 41 | |||
| 42 | .formats = (SNDRV_PCM_FMTBIT_S16_LE | | ||
| 43 | SNDRV_PCM_FMTBIT_S24_LE | | ||
| 44 | SNDRV_PCM_FMTBIT_S32_LE), | ||
| 45 | |||
| 46 | .buffer_bytes_max = 131072, | ||
| 47 | .period_bytes_min = 32, | ||
| 48 | .period_bytes_max = 32768, | ||
| 49 | .periods_min = 1, | ||
| 50 | .periods_max = 32, | ||
| 51 | .fifo_size = 32, | ||
| 52 | }; | ||
| 53 | |||
| 54 | struct ep93xx_runtime_data | ||
| 55 | { | ||
| 56 | struct ep93xx_dma_m2p_client cl; | ||
| 57 | struct ep93xx_pcm_dma_params *params; | ||
| 58 | int pointer_bytes; | ||
| 59 | struct tasklet_struct period_tasklet; | ||
| 60 | int periods; | ||
| 61 | struct ep93xx_dma_buffer buf[32]; | ||
| 62 | }; | ||
| 63 | |||
| 64 | static void ep93xx_pcm_period_elapsed(unsigned long data) | ||
| 65 | { | ||
| 66 | struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data; | ||
| 67 | snd_pcm_period_elapsed(substream); | ||
| 68 | } | ||
| 69 | |||
| 70 | static void ep93xx_pcm_buffer_started(void *cookie, | ||
| 71 | struct ep93xx_dma_buffer *buf) | ||
| 72 | { | ||
| 73 | } | ||
| 74 | |||
| 75 | static void ep93xx_pcm_buffer_finished(void *cookie, | ||
| 76 | struct ep93xx_dma_buffer *buf, | ||
| 77 | int bytes, int error) | ||
| 78 | { | ||
| 79 | struct snd_pcm_substream *substream = cookie; | ||
| 80 | struct ep93xx_runtime_data *rtd = substream->runtime->private_data; | ||
| 81 | |||
| 82 | if (buf == rtd->buf + rtd->periods - 1) | ||
| 83 | rtd->pointer_bytes = 0; | ||
| 84 | else | ||
| 85 | rtd->pointer_bytes += buf->size; | ||
| 86 | |||
| 87 | if (!error) { | ||
| 88 | ep93xx_dma_m2p_submit_recursive(&rtd->cl, buf); | ||
| 89 | tasklet_schedule(&rtd->period_tasklet); | ||
| 90 | } else { | ||
| 91 | snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | static int ep93xx_pcm_open(struct snd_pcm_substream *substream) | ||
| 96 | { | ||
| 97 | struct snd_soc_pcm_runtime *soc_rtd = substream->private_data; | ||
| 98 | struct snd_soc_dai *cpu_dai = soc_rtd->dai->cpu_dai; | ||
| 99 | struct ep93xx_pcm_dma_params *dma_params; | ||
| 100 | struct ep93xx_runtime_data *rtd; | ||
| 101 | int ret; | ||
| 102 | |||
| 103 | dma_params = snd_soc_dai_get_dma_data(cpu_dai, substream); | ||
| 104 | snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware); | ||
| 105 | |||
| 106 | rtd = kmalloc(sizeof(*rtd), GFP_KERNEL); | ||
| 107 | if (!rtd) | ||
| 108 | return -ENOMEM; | ||
| 109 | |||
| 110 | memset(&rtd->period_tasklet, 0, sizeof(rtd->period_tasklet)); | ||
| 111 | rtd->period_tasklet.func = ep93xx_pcm_period_elapsed; | ||
| 112 | rtd->period_tasklet.data = (unsigned long)substream; | ||
| 113 | |||
| 114 | rtd->cl.name = dma_params->name; | ||
| 115 | rtd->cl.flags = dma_params->dma_port | EP93XX_DMA_M2P_IGNORE_ERROR | | ||
| 116 | ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? | ||
| 117 | EP93XX_DMA_M2P_TX : EP93XX_DMA_M2P_RX); | ||
| 118 | rtd->cl.cookie = substream; | ||
| 119 | rtd->cl.buffer_started = ep93xx_pcm_buffer_started; | ||
| 120 | rtd->cl.buffer_finished = ep93xx_pcm_buffer_finished; | ||
| 121 | ret = ep93xx_dma_m2p_client_register(&rtd->cl); | ||
| 122 | if (ret < 0) { | ||
| 123 | kfree(rtd); | ||
| 124 | return ret; | ||
| 125 | } | ||
| 126 | |||
| 127 | substream->runtime->private_data = rtd; | ||
| 128 | return 0; | ||
| 129 | } | ||
| 130 | |||
| 131 | static int ep93xx_pcm_close(struct snd_pcm_substream *substream) | ||
| 132 | { | ||
| 133 | struct ep93xx_runtime_data *rtd = substream->runtime->private_data; | ||
| 134 | |||
| 135 | ep93xx_dma_m2p_client_unregister(&rtd->cl); | ||
| 136 | kfree(rtd); | ||
| 137 | return 0; | ||
| 138 | } | ||
| 139 | |||
| 140 | static int ep93xx_pcm_hw_params(struct snd_pcm_substream *substream, | ||
| 141 | struct snd_pcm_hw_params *params) | ||
| 142 | { | ||
| 143 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 144 | struct ep93xx_runtime_data *rtd = runtime->private_data; | ||
| 145 | size_t totsize = params_buffer_bytes(params); | ||
| 146 | size_t period = params_period_bytes(params); | ||
| 147 | int i; | ||
| 148 | |||
| 149 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | ||
| 150 | runtime->dma_bytes = totsize; | ||
| 151 | |||
| 152 | rtd->periods = (totsize + period - 1) / period; | ||
| 153 | for (i = 0; i < rtd->periods; i++) { | ||
| 154 | rtd->buf[i].bus_addr = runtime->dma_addr + (i * period); | ||
| 155 | rtd->buf[i].size = period; | ||
| 156 | if ((i + 1) * period > totsize) | ||
| 157 | rtd->buf[i].size = totsize - (i * period); | ||
| 158 | } | ||
| 159 | |||
| 160 | return 0; | ||
| 161 | } | ||
| 162 | |||
| 163 | static int ep93xx_pcm_hw_free(struct snd_pcm_substream *substream) | ||
| 164 | { | ||
| 165 | snd_pcm_set_runtime_buffer(substream, NULL); | ||
| 166 | return 0; | ||
| 167 | } | ||
| 168 | |||
| 169 | static int ep93xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | ||
| 170 | { | ||
| 171 | struct ep93xx_runtime_data *rtd = substream->runtime->private_data; | ||
| 172 | int ret; | ||
| 173 | int i; | ||
| 174 | |||
| 175 | ret = 0; | ||
| 176 | switch (cmd) { | ||
| 177 | case SNDRV_PCM_TRIGGER_START: | ||
| 178 | case SNDRV_PCM_TRIGGER_RESUME: | ||
| 179 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
| 180 | rtd->pointer_bytes = 0; | ||
| 181 | for (i = 0; i < rtd->periods; i++) | ||
| 182 | ep93xx_dma_m2p_submit(&rtd->cl, rtd->buf + i); | ||
| 183 | break; | ||
| 184 | |||
| 185 | case SNDRV_PCM_TRIGGER_STOP: | ||
| 186 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
| 187 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
| 188 | ep93xx_dma_m2p_flush(&rtd->cl); | ||
| 189 | break; | ||
| 190 | |||
| 191 | default: | ||
| 192 | ret = -EINVAL; | ||
| 193 | break; | ||
| 194 | } | ||
| 195 | |||
| 196 | return ret; | ||
| 197 | } | ||
| 198 | |||
| 199 | static snd_pcm_uframes_t ep93xx_pcm_pointer(struct snd_pcm_substream *substream) | ||
| 200 | { | ||
| 201 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 202 | struct ep93xx_runtime_data *rtd = substream->runtime->private_data; | ||
| 203 | |||
| 204 | /* FIXME: implement this with sub-period granularity */ | ||
| 205 | return bytes_to_frames(runtime, rtd->pointer_bytes); | ||
| 206 | } | ||
| 207 | |||
| 208 | static int ep93xx_pcm_mmap(struct snd_pcm_substream *substream, | ||
| 209 | struct vm_area_struct *vma) | ||
| 210 | { | ||
| 211 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 212 | |||
| 213 | return dma_mmap_writecombine(substream->pcm->card->dev, vma, | ||
| 214 | runtime->dma_area, | ||
| 215 | runtime->dma_addr, | ||
| 216 | runtime->dma_bytes); | ||
| 217 | } | ||
| 218 | |||
| 219 | static struct snd_pcm_ops ep93xx_pcm_ops = { | ||
| 220 | .open = ep93xx_pcm_open, | ||
| 221 | .close = ep93xx_pcm_close, | ||
| 222 | .ioctl = snd_pcm_lib_ioctl, | ||
| 223 | .hw_params = ep93xx_pcm_hw_params, | ||
| 224 | .hw_free = ep93xx_pcm_hw_free, | ||
| 225 | .trigger = ep93xx_pcm_trigger, | ||
| 226 | .pointer = ep93xx_pcm_pointer, | ||
| 227 | .mmap = ep93xx_pcm_mmap, | ||
| 228 | }; | ||
| 229 | |||
| 230 | static int ep93xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) | ||
| 231 | { | ||
| 232 | struct snd_pcm_substream *substream = pcm->streams[stream].substream; | ||
| 233 | struct snd_dma_buffer *buf = &substream->dma_buffer; | ||
| 234 | size_t size = ep93xx_pcm_hardware.buffer_bytes_max; | ||
| 235 | |||
| 236 | buf->dev.type = SNDRV_DMA_TYPE_DEV; | ||
| 237 | buf->dev.dev = pcm->card->dev; | ||
| 238 | buf->private_data = NULL; | ||
| 239 | buf->area = dma_alloc_writecombine(pcm->card->dev, size, | ||
| 240 | &buf->addr, GFP_KERNEL); | ||
| 241 | buf->bytes = size; | ||
| 242 | |||
| 243 | return (buf->area == NULL) ? -ENOMEM : 0; | ||
| 244 | } | ||
| 245 | |||
| 246 | static void ep93xx_pcm_free_dma_buffers(struct snd_pcm *pcm) | ||
| 247 | { | ||
| 248 | struct snd_pcm_substream *substream; | ||
| 249 | struct snd_dma_buffer *buf; | ||
| 250 | int stream; | ||
| 251 | |||
| 252 | for (stream = 0; stream < 2; stream++) { | ||
| 253 | substream = pcm->streams[stream].substream; | ||
| 254 | if (!substream) | ||
| 255 | continue; | ||
| 256 | |||
| 257 | buf = &substream->dma_buffer; | ||
| 258 | if (!buf->area) | ||
| 259 | continue; | ||
| 260 | |||
| 261 | dma_free_writecombine(pcm->card->dev, buf->bytes, buf->area, | ||
| 262 | buf->addr); | ||
| 263 | buf->area = NULL; | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 267 | static u64 ep93xx_pcm_dmamask = 0xffffffff; | ||
| 268 | |||
| 269 | static int ep93xx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, | ||
| 270 | struct snd_pcm *pcm) | ||
| 271 | { | ||
| 272 | int ret = 0; | ||
| 273 | |||
| 274 | if (!card->dev->dma_mask) | ||
| 275 | card->dev->dma_mask = &ep93xx_pcm_dmamask; | ||
| 276 | if (!card->dev->coherent_dma_mask) | ||
| 277 | card->dev->coherent_dma_mask = 0xffffffff; | ||
| 278 | |||
| 279 | if (dai->playback.channels_min) { | ||
| 280 | ret = ep93xx_pcm_preallocate_dma_buffer(pcm, | ||
| 281 | SNDRV_PCM_STREAM_PLAYBACK); | ||
| 282 | if (ret) | ||
| 283 | return ret; | ||
| 284 | } | ||
| 285 | |||
| 286 | if (dai->capture.channels_min) { | ||
| 287 | ret = ep93xx_pcm_preallocate_dma_buffer(pcm, | ||
| 288 | SNDRV_PCM_STREAM_CAPTURE); | ||
| 289 | if (ret) | ||
| 290 | return ret; | ||
| 291 | } | ||
| 292 | |||
| 293 | return 0; | ||
| 294 | } | ||
| 295 | |||
| 296 | struct snd_soc_platform ep93xx_soc_platform = { | ||
| 297 | .name = "ep93xx-audio", | ||
| 298 | .pcm_ops = &ep93xx_pcm_ops, | ||
| 299 | .pcm_new = &ep93xx_pcm_new, | ||
| 300 | .pcm_free = &ep93xx_pcm_free_dma_buffers, | ||
| 301 | }; | ||
| 302 | EXPORT_SYMBOL_GPL(ep93xx_soc_platform); | ||
| 303 | |||
| 304 | static int __init ep93xx_soc_platform_init(void) | ||
| 305 | { | ||
| 306 | return snd_soc_register_platform(&ep93xx_soc_platform); | ||
| 307 | } | ||
| 308 | |||
| 309 | static void __exit ep93xx_soc_platform_exit(void) | ||
| 310 | { | ||
| 311 | snd_soc_unregister_platform(&ep93xx_soc_platform); | ||
| 312 | } | ||
| 313 | |||
| 314 | module_init(ep93xx_soc_platform_init); | ||
| 315 | module_exit(ep93xx_soc_platform_exit); | ||
| 316 | |||
| 317 | MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>"); | ||
| 318 | MODULE_DESCRIPTION("EP93xx ALSA PCM interface"); | ||
| 319 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/ep93xx/ep93xx-pcm.h b/sound/soc/ep93xx/ep93xx-pcm.h new file mode 100644 index 000000000000..4ffdd3f62fe9 --- /dev/null +++ b/sound/soc/ep93xx/ep93xx-pcm.h | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | /* | ||
| 2 | * sound/soc/ep93xx/ep93xx-pcm.h - EP93xx ALSA PCM interface | ||
| 3 | * | ||
| 4 | * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org> | ||
| 5 | * Copyright (C) 2006 Applied Data Systems | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #ifndef _EP93XX_SND_SOC_PCM_H | ||
| 13 | #define _EP93XX_SND_SOC_PCM_H | ||
| 14 | |||
| 15 | struct ep93xx_pcm_dma_params { | ||
| 16 | char *name; | ||
| 17 | int dma_port; | ||
| 18 | }; | ||
| 19 | |||
| 20 | extern struct snd_soc_platform ep93xx_soc_platform; | ||
| 21 | |||
| 22 | #endif /* _EP93XX_SND_SOC_PCM_H */ | ||
diff --git a/sound/soc/ep93xx/snappercl15.c b/sound/soc/ep93xx/snappercl15.c new file mode 100644 index 000000000000..64955340ff75 --- /dev/null +++ b/sound/soc/ep93xx/snappercl15.c | |||
| @@ -0,0 +1,150 @@ | |||
| 1 | /* | ||
| 2 | * snappercl15.c -- SoC audio for Bluewater Systems Snapper CL15 module | ||
| 3 | * | ||
| 4 | * Copyright (C) 2008 Bluewater Systems Ltd | ||
| 5 | * Author: Ryan Mallon <ryan@bluewatersys.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify it | ||
| 8 | * under the terms of the GNU General Public License as published by the | ||
| 9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 10 | * option) any later version. | ||
| 11 | * | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/platform_device.h> | ||
| 15 | #include <sound/core.h> | ||
| 16 | #include <sound/pcm.h> | ||
| 17 | #include <sound/soc.h> | ||
| 18 | #include <sound/soc-dapm.h> | ||
| 19 | |||
| 20 | #include <asm/mach-types.h> | ||
| 21 | #include <mach/hardware.h> | ||
| 22 | |||
| 23 | #include "../codecs/tlv320aic23.h" | ||
| 24 | #include "ep93xx-pcm.h" | ||
| 25 | #include "ep93xx-i2s.h" | ||
| 26 | |||
| 27 | #define CODEC_CLOCK 5644800 | ||
| 28 | |||
| 29 | static int snappercl15_hw_params(struct snd_pcm_substream *substream, | ||
| 30 | struct snd_pcm_hw_params *params) | ||
| 31 | { | ||
| 32 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
| 33 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
| 34 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
| 35 | int err; | ||
| 36 | |||
| 37 | err = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | | ||
| 38 | SND_SOC_DAIFMT_NB_IF | | ||
| 39 | SND_SOC_DAIFMT_CBS_CFS); | ||
| 40 | |||
| 41 | err = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | | ||
| 42 | SND_SOC_DAIFMT_NB_IF | | ||
| 43 | SND_SOC_DAIFMT_CBS_CFS); | ||
| 44 | if (err) | ||
| 45 | return err; | ||
| 46 | |||
| 47 | err = snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, | ||
| 48 | SND_SOC_CLOCK_IN); | ||
| 49 | if (err) | ||
| 50 | return err; | ||
| 51 | |||
| 52 | err = snd_soc_dai_set_sysclk(cpu_dai, 0, CODEC_CLOCK, | ||
| 53 | SND_SOC_CLOCK_OUT); | ||
| 54 | if (err) | ||
| 55 | return err; | ||
| 56 | |||
| 57 | return 0; | ||
| 58 | } | ||
| 59 | |||
| 60 | static struct snd_soc_ops snappercl15_ops = { | ||
| 61 | .hw_params = snappercl15_hw_params, | ||
| 62 | }; | ||
| 63 | |||
| 64 | static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = { | ||
| 65 | SND_SOC_DAPM_HP("Headphone Jack", NULL), | ||
| 66 | SND_SOC_DAPM_LINE("Line In", NULL), | ||
| 67 | SND_SOC_DAPM_MIC("Mic Jack", NULL), | ||
| 68 | }; | ||
| 69 | |||
| 70 | static const struct snd_soc_dapm_route audio_map[] = { | ||
| 71 | {"Headphone Jack", NULL, "LHPOUT"}, | ||
| 72 | {"Headphone Jack", NULL, "RHPOUT"}, | ||
| 73 | |||
| 74 | {"LLINEIN", NULL, "Line In"}, | ||
| 75 | {"RLINEIN", NULL, "Line In"}, | ||
| 76 | |||
| 77 | {"MICIN", NULL, "Mic Jack"}, | ||
| 78 | }; | ||
| 79 | |||
| 80 | static int snappercl15_tlv320aic23_init(struct snd_soc_codec *codec) | ||
| 81 | { | ||
| 82 | snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets, | ||
| 83 | ARRAY_SIZE(tlv320aic23_dapm_widgets)); | ||
| 84 | |||
| 85 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | ||
| 86 | return 0; | ||
| 87 | } | ||
| 88 | |||
| 89 | static struct snd_soc_dai_link snappercl15_dai = { | ||
| 90 | .name = "tlv320aic23", | ||
| 91 | .stream_name = "AIC23", | ||
| 92 | .cpu_dai = &ep93xx_i2s_dai, | ||
| 93 | .codec_dai = &tlv320aic23_dai, | ||
| 94 | .init = snappercl15_tlv320aic23_init, | ||
| 95 | .ops = &snappercl15_ops, | ||
| 96 | }; | ||
| 97 | |||
| 98 | static struct snd_soc_card snd_soc_snappercl15 = { | ||
| 99 | .name = "Snapper CL15", | ||
| 100 | .platform = &ep93xx_soc_platform, | ||
| 101 | .dai_link = &snappercl15_dai, | ||
| 102 | .num_links = 1, | ||
| 103 | }; | ||
| 104 | |||
| 105 | static struct snd_soc_device snappercl15_snd_devdata = { | ||
| 106 | .card = &snd_soc_snappercl15, | ||
| 107 | .codec_dev = &soc_codec_dev_tlv320aic23, | ||
| 108 | }; | ||
| 109 | |||
| 110 | static struct platform_device *snappercl15_snd_device; | ||
| 111 | |||
| 112 | static int __init snappercl15_init(void) | ||
| 113 | { | ||
| 114 | int ret; | ||
| 115 | |||
| 116 | if (!machine_is_snapper_cl15()) | ||
| 117 | return -ENODEV; | ||
| 118 | |||
| 119 | ret = ep93xx_i2s_acquire(EP93XX_SYSCON_DEVCFG_I2SONAC97, | ||
| 120 | EP93XX_SYSCON_I2SCLKDIV_ORIDE | | ||
| 121 | EP93XX_SYSCON_I2SCLKDIV_SPOL); | ||
| 122 | if (ret) | ||
| 123 | return ret; | ||
| 124 | |||
| 125 | snappercl15_snd_device = platform_device_alloc("soc-audio", -1); | ||
| 126 | if (!snappercl15_snd_device) | ||
| 127 | return -ENOMEM; | ||
| 128 | |||
| 129 | platform_set_drvdata(snappercl15_snd_device, &snappercl15_snd_devdata); | ||
| 130 | snappercl15_snd_devdata.dev = &snappercl15_snd_device->dev; | ||
| 131 | ret = platform_device_add(snappercl15_snd_device); | ||
| 132 | if (ret) | ||
| 133 | platform_device_put(snappercl15_snd_device); | ||
| 134 | |||
| 135 | return ret; | ||
| 136 | } | ||
| 137 | |||
| 138 | static void __exit snappercl15_exit(void) | ||
| 139 | { | ||
| 140 | platform_device_unregister(snappercl15_snd_device); | ||
| 141 | ep93xx_i2s_release(); | ||
| 142 | } | ||
| 143 | |||
| 144 | module_init(snappercl15_init); | ||
| 145 | module_exit(snappercl15_exit); | ||
| 146 | |||
| 147 | MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>"); | ||
| 148 | MODULE_DESCRIPTION("ALSA SoC Snapper CL15"); | ||
| 149 | MODULE_LICENSE("GPL"); | ||
| 150 | |||
diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index 4f455bd6851f..676841cbae98 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c | |||
| @@ -16,7 +16,6 @@ | |||
| 16 | 16 | ||
| 17 | #include <asm/mpc52xx_psc.h> | 17 | #include <asm/mpc52xx_psc.h> |
| 18 | 18 | ||
| 19 | #include "mpc5200_psc_i2s.h" | ||
| 20 | #include "mpc5200_dma.h" | 19 | #include "mpc5200_dma.h" |
| 21 | 20 | ||
| 22 | /** | 21 | /** |
diff --git a/sound/soc/fsl/mpc5200_psc_i2s.h b/sound/soc/fsl/mpc5200_psc_i2s.h deleted file mode 100644 index ce55e070fdf3..000000000000 --- a/sound/soc/fsl/mpc5200_psc_i2s.h +++ /dev/null | |||
| @@ -1,12 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * Freescale MPC5200 PSC in I2S mode | ||
| 3 | * ALSA SoC Digital Audio Interface (DAI) driver | ||
| 4 | * | ||
| 5 | */ | ||
| 6 | |||
| 7 | #ifndef __SOUND_SOC_FSL_MPC52xx_PSC_I2S_H__ | ||
| 8 | #define __SOUND_SOC_FSL_MPC52xx_PSC_I2S_H__ | ||
| 9 | |||
| 10 | extern struct snd_soc_dai psc_i2s_dai[]; | ||
| 11 | |||
| 12 | #endif /* __SOUND_SOC_FSL_MPC52xx_PSC_I2S_H__ */ | ||
diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig index 252defea93b5..52dac5e3874c 100644 --- a/sound/soc/imx/Kconfig +++ b/sound/soc/imx/Kconfig | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | config SND_IMX_SOC | 1 | menuconfig SND_IMX_SOC |
| 2 | tristate "SoC Audio for Freescale i.MX CPUs" | 2 | tristate "SoC Audio for Freescale i.MX CPUs" |
| 3 | depends on ARCH_MXC | 3 | depends on ARCH_MXC |
| 4 | select SND_PCM | 4 | select SND_PCM |
| @@ -8,14 +8,12 @@ config SND_IMX_SOC | |||
| 8 | Say Y or M if you want to add support for codecs attached to | 8 | Say Y or M if you want to add support for codecs attached to |
| 9 | the i.MX SSI interface. | 9 | the i.MX SSI interface. |
| 10 | 10 | ||
| 11 | config SND_MXC_SOC_SSI | 11 | if SND_IMX_SOC |
| 12 | tristate | ||
| 13 | 12 | ||
| 14 | config SND_MXC_SOC_WM1133_EV1 | 13 | config SND_MXC_SOC_WM1133_EV1 |
| 15 | tristate "Audio on the the i.MX31ADS with WM1133-EV1 fitted" | 14 | tristate "Audio on the the i.MX31ADS with WM1133-EV1 fitted" |
| 16 | depends on SND_IMX_SOC && MACH_MX31ADS_WM1133_EV1 && EXPERIMENTAL | 15 | depends on MACH_MX31ADS_WM1133_EV1 && EXPERIMENTAL |
| 17 | select SND_SOC_WM8350 | 16 | select SND_SOC_WM8350 |
| 18 | select SND_MXC_SOC_SSI | ||
| 19 | help | 17 | help |
| 20 | Enable support for audio on the i.MX31ADS with the WM1133-EV1 | 18 | Enable support for audio on the i.MX31ADS with the WM1133-EV1 |
| 21 | PMIC board with WM8835x fitted. | 19 | PMIC board with WM8835x fitted. |
| @@ -23,8 +21,17 @@ config SND_MXC_SOC_WM1133_EV1 | |||
| 23 | config SND_SOC_PHYCORE_AC97 | 21 | config SND_SOC_PHYCORE_AC97 |
| 24 | tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards" | 22 | tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards" |
| 25 | depends on MACH_PCM043 || MACH_PCA100 | 23 | depends on MACH_PCM043 || MACH_PCA100 |
| 26 | select SND_MXC_SOC_SSI | ||
| 27 | select SND_SOC_WM9712 | 24 | select SND_SOC_WM9712 |
| 28 | help | 25 | help |
| 29 | Say Y if you want to add support for SoC audio on Phytec phyCORE | 26 | Say Y if you want to add support for SoC audio on Phytec phyCORE |
| 30 | and phyCARD boards in AC97 mode | 27 | and phyCARD boards in AC97 mode |
| 28 | |||
| 29 | config SND_SOC_EUKREA_TLV320 | ||
| 30 | tristate "Eukrea TLV320" | ||
| 31 | depends on MACH_EUKREA_MBIMX27_BASEBOARD || MACH_EUKREA_MBIMXSD_BASEBOARD | ||
| 32 | select SND_SOC_TLV320AIC23 | ||
| 33 | help | ||
| 34 | Enable I2S based access to the TLV320AIC23B codec attached | ||
| 35 | to the SSI interface | ||
| 36 | |||
| 37 | endif # SND_IMX_SOC | ||
diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile index 2d203635ac11..7bc57baf2b0e 100644 --- a/sound/soc/imx/Makefile +++ b/sound/soc/imx/Makefile | |||
| @@ -8,8 +8,10 @@ endif | |||
| 8 | obj-$(CONFIG_SND_IMX_SOC) += snd-soc-imx.o | 8 | obj-$(CONFIG_SND_IMX_SOC) += snd-soc-imx.o |
| 9 | 9 | ||
| 10 | # i.MX Machine Support | 10 | # i.MX Machine Support |
| 11 | snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o | ||
| 11 | snd-soc-phycore-ac97-objs := phycore-ac97.o | 12 | snd-soc-phycore-ac97-objs := phycore-ac97.o |
| 12 | snd-soc-wm1133-ev1-objs := wm1133-ev1.o | 13 | snd-soc-wm1133-ev1-objs := wm1133-ev1.o |
| 13 | 14 | ||
| 15 | obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o | ||
| 14 | obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o | 16 | obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o |
| 15 | obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o | 17 | obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o |
diff --git a/sound/soc/imx/eukrea-tlv320.c b/sound/soc/imx/eukrea-tlv320.c new file mode 100644 index 000000000000..f15dfbdc47ee --- /dev/null +++ b/sound/soc/imx/eukrea-tlv320.c | |||
| @@ -0,0 +1,137 @@ | |||
| 1 | /* | ||
| 2 | * eukrea-tlv320.c -- SoC audio for eukrea_cpuimxXX in I2S mode | ||
| 3 | * | ||
| 4 | * Copyright 2010 Eric Bénard, Eukréa Electromatique <eric@eukrea.com> | ||
| 5 | * | ||
| 6 | * based on sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c | ||
| 7 | * which is Copyright 2009 Simtec Electronics | ||
| 8 | * and on sound/soc/imx/phycore-ac97.c which is | ||
| 9 | * Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> | ||
| 10 | * | ||
| 11 | * This program is free software; you can redistribute it and/or modify it | ||
| 12 | * under the terms of the GNU General Public License as published by the | ||
| 13 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 14 | * option) any later version. | ||
| 15 | * | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <linux/module.h> | ||
| 19 | #include <linux/moduleparam.h> | ||
| 20 | #include <linux/device.h> | ||
| 21 | #include <linux/i2c.h> | ||
| 22 | #include <sound/core.h> | ||
| 23 | #include <sound/pcm.h> | ||
| 24 | #include <sound/soc.h> | ||
| 25 | #include <sound/soc-dapm.h> | ||
| 26 | #include <asm/mach-types.h> | ||
| 27 | |||
| 28 | #include "../codecs/tlv320aic23.h" | ||
| 29 | #include "imx-ssi.h" | ||
| 30 | |||
| 31 | #define CODEC_CLOCK 12000000 | ||
| 32 | |||
| 33 | static int eukrea_tlv320_hw_params(struct snd_pcm_substream *substream, | ||
| 34 | struct snd_pcm_hw_params *params) | ||
| 35 | { | ||
| 36 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
| 37 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
| 38 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
| 39 | int ret; | ||
| 40 | |||
| 41 | ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | | ||
| 42 | SND_SOC_DAIFMT_NB_NF | | ||
| 43 | SND_SOC_DAIFMT_CBM_CFM); | ||
| 44 | if (ret) { | ||
| 45 | pr_err("%s: failed set cpu dai format\n", __func__); | ||
| 46 | return ret; | ||
| 47 | } | ||
| 48 | |||
| 49 | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | | ||
| 50 | SND_SOC_DAIFMT_NB_NF | | ||
| 51 | SND_SOC_DAIFMT_CBM_CFM); | ||
| 52 | if (ret) { | ||
| 53 | pr_err("%s: failed set codec dai format\n", __func__); | ||
| 54 | return ret; | ||
| 55 | } | ||
| 56 | |||
| 57 | ret = snd_soc_dai_set_sysclk(codec_dai, 0, | ||
| 58 | CODEC_CLOCK, SND_SOC_CLOCK_OUT); | ||
| 59 | if (ret) { | ||
| 60 | pr_err("%s: failed setting codec sysclk\n", __func__); | ||
| 61 | return ret; | ||
| 62 | } | ||
| 63 | snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffc, 0xffffffc, 2, 0); | ||
| 64 | |||
| 65 | ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, | ||
| 66 | SND_SOC_CLOCK_IN); | ||
| 67 | if (ret) { | ||
| 68 | pr_err("can't set CPU system clock IMX_SSP_SYS_CLK\n"); | ||
| 69 | return ret; | ||
| 70 | } | ||
| 71 | |||
| 72 | return 0; | ||
| 73 | } | ||
| 74 | |||
| 75 | static struct snd_soc_ops eukrea_tlv320_snd_ops = { | ||
| 76 | .hw_params = eukrea_tlv320_hw_params, | ||
| 77 | }; | ||
| 78 | |||
| 79 | static struct snd_soc_dai_link eukrea_tlv320_dai = { | ||
| 80 | .name = "tlv320aic23", | ||
| 81 | .stream_name = "TLV320AIC23", | ||
| 82 | .codec_dai = &tlv320aic23_dai, | ||
| 83 | .ops = &eukrea_tlv320_snd_ops, | ||
| 84 | }; | ||
| 85 | |||
| 86 | static struct snd_soc_card eukrea_tlv320 = { | ||
| 87 | .name = "cpuimx-audio", | ||
| 88 | .platform = &imx_soc_platform, | ||
| 89 | .dai_link = &eukrea_tlv320_dai, | ||
| 90 | .num_links = 1, | ||
| 91 | }; | ||
| 92 | |||
| 93 | static struct snd_soc_device eukrea_tlv320_snd_devdata = { | ||
| 94 | .card = &eukrea_tlv320, | ||
| 95 | .codec_dev = &soc_codec_dev_tlv320aic23, | ||
| 96 | }; | ||
| 97 | |||
| 98 | static struct platform_device *eukrea_tlv320_snd_device; | ||
| 99 | |||
| 100 | static int __init eukrea_tlv320_init(void) | ||
| 101 | { | ||
| 102 | int ret; | ||
| 103 | |||
| 104 | if (!machine_is_eukrea_cpuimx27() && !machine_is_eukrea_cpuimx25sd() | ||
| 105 | && !machine_is_eukrea_cpuimx35sd()) | ||
| 106 | /* return happy. We might run on a totally different machine */ | ||
| 107 | return 0; | ||
| 108 | |||
| 109 | eukrea_tlv320_snd_device = platform_device_alloc("soc-audio", -1); | ||
| 110 | if (!eukrea_tlv320_snd_device) | ||
| 111 | return -ENOMEM; | ||
| 112 | |||
| 113 | eukrea_tlv320_dai.cpu_dai = &imx_ssi_pcm_dai[0]; | ||
| 114 | |||
| 115 | platform_set_drvdata(eukrea_tlv320_snd_device, &eukrea_tlv320_snd_devdata); | ||
| 116 | eukrea_tlv320_snd_devdata.dev = &eukrea_tlv320_snd_device->dev; | ||
| 117 | ret = platform_device_add(eukrea_tlv320_snd_device); | ||
| 118 | |||
| 119 | if (ret) { | ||
| 120 | printk(KERN_ERR "ASoC: Platform device allocation failed\n"); | ||
| 121 | platform_device_put(eukrea_tlv320_snd_device); | ||
| 122 | } | ||
| 123 | |||
| 124 | return ret; | ||
| 125 | } | ||
| 126 | |||
| 127 | static void __exit eukrea_tlv320_exit(void) | ||
| 128 | { | ||
| 129 | platform_device_unregister(eukrea_tlv320_snd_device); | ||
| 130 | } | ||
| 131 | |||
| 132 | module_init(eukrea_tlv320_init); | ||
| 133 | module_exit(eukrea_tlv320_exit); | ||
| 134 | |||
| 135 | MODULE_AUTHOR("Eric Bénard <eric@eukrea.com>"); | ||
| 136 | MODULE_DESCRIPTION("CPUIMX ALSA SoC driver"); | ||
| 137 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/imx/imx-pcm-dma-mx2.c index 05f19c9284f4..0a595da4811d 100644 --- a/sound/soc/imx/imx-pcm-dma-mx2.c +++ b/sound/soc/imx/imx-pcm-dma-mx2.c | |||
| @@ -292,12 +292,16 @@ static int snd_imx_open(struct snd_pcm_substream *substream) | |||
| 292 | int ret; | 292 | int ret; |
| 293 | 293 | ||
| 294 | iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL); | 294 | iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL); |
| 295 | if (iprtd == NULL) | ||
| 296 | return -ENOMEM; | ||
| 295 | runtime->private_data = iprtd; | 297 | runtime->private_data = iprtd; |
| 296 | 298 | ||
| 297 | ret = snd_pcm_hw_constraint_integer(substream->runtime, | 299 | ret = snd_pcm_hw_constraint_integer(substream->runtime, |
| 298 | SNDRV_PCM_HW_PARAM_PERIODS); | 300 | SNDRV_PCM_HW_PARAM_PERIODS); |
| 299 | if (ret < 0) | 301 | if (ret < 0) { |
| 302 | kfree(iprtd); | ||
| 300 | return ret; | 303 | return ret; |
| 304 | } | ||
| 301 | 305 | ||
| 302 | snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware); | 306 | snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware); |
| 303 | return 0; | 307 | return 0; |
diff --git a/sound/soc/imx/imx-pcm-fiq.c b/sound/soc/imx/imx-pcm-fiq.c index 6b518e07eea9..b2bf27282cd2 100644 --- a/sound/soc/imx/imx-pcm-fiq.c +++ b/sound/soc/imx/imx-pcm-fiq.c | |||
| @@ -192,6 +192,8 @@ static int snd_imx_open(struct snd_pcm_substream *substream) | |||
| 192 | int ret; | 192 | int ret; |
| 193 | 193 | ||
| 194 | iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL); | 194 | iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL); |
| 195 | if (iprtd == NULL) | ||
| 196 | return -ENOMEM; | ||
| 195 | runtime->private_data = iprtd; | 197 | runtime->private_data = iprtd; |
| 196 | 198 | ||
| 197 | iprtd->substream = substream; | 199 | iprtd->substream = substream; |
| @@ -202,8 +204,10 @@ static int snd_imx_open(struct snd_pcm_substream *substream) | |||
| 202 | 204 | ||
| 203 | ret = snd_pcm_hw_constraint_integer(substream->runtime, | 205 | ret = snd_pcm_hw_constraint_integer(substream->runtime, |
| 204 | SNDRV_PCM_HW_PARAM_PERIODS); | 206 | SNDRV_PCM_HW_PARAM_PERIODS); |
| 205 | if (ret < 0) | 207 | if (ret < 0) { |
| 208 | kfree(iprtd); | ||
| 206 | return ret; | 209 | return ret; |
| 210 | } | ||
| 207 | 211 | ||
| 208 | snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware); | 212 | snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware); |
| 209 | return 0; | 213 | return 0; |
diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/imx/imx-ssi.c index 80b4fee2442b..50f51624c535 100644 --- a/sound/soc/imx/imx-ssi.c +++ b/sound/soc/imx/imx-ssi.c | |||
| @@ -83,8 +83,6 @@ static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, | |||
| 83 | /* | 83 | /* |
| 84 | * SSI DAI format configuration. | 84 | * SSI DAI format configuration. |
| 85 | * Should only be called when port is inactive (i.e. SSIEN = 0). | 85 | * Should only be called when port is inactive (i.e. SSIEN = 0). |
| 86 | * Note: We don't use the I2S modes but instead manually configure the | ||
| 87 | * SSI for I2S because the I2S mode is only a register preset. | ||
| 88 | */ | 86 | */ |
| 89 | static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) | 87 | static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) |
| 90 | { | 88 | { |
| @@ -99,6 +97,10 @@ static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) | |||
| 99 | /* data on rising edge of bclk, frame low 1clk before data */ | 97 | /* data on rising edge of bclk, frame low 1clk before data */ |
| 100 | strcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0; | 98 | strcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0; |
| 101 | scr |= SSI_SCR_NET; | 99 | scr |= SSI_SCR_NET; |
| 100 | if (ssi->flags & IMX_SSI_USE_I2S_SLAVE) { | ||
| 101 | scr &= ~SSI_I2S_MODE_MASK; | ||
| 102 | scr |= SSI_SCR_I2S_MODE_SLAVE; | ||
| 103 | } | ||
| 102 | break; | 104 | break; |
| 103 | case SND_SOC_DAIFMT_LEFT_J: | 105 | case SND_SOC_DAIFMT_LEFT_J: |
| 104 | /* data on rising edge of bclk, frame high with data */ | 106 | /* data on rising edge of bclk, frame high with data */ |
| @@ -143,6 +145,11 @@ static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) | |||
| 143 | 145 | ||
| 144 | strcr |= SSI_STCR_TFEN0; | 146 | strcr |= SSI_STCR_TFEN0; |
| 145 | 147 | ||
| 148 | if (ssi->flags & IMX_SSI_NET) | ||
| 149 | scr |= SSI_SCR_NET; | ||
| 150 | if (ssi->flags & IMX_SSI_SYN) | ||
| 151 | scr |= SSI_SCR_SYN; | ||
| 152 | |||
| 146 | writel(strcr, ssi->base + SSI_STCR); | 153 | writel(strcr, ssi->base + SSI_STCR); |
| 147 | writel(strcr, ssi->base + SSI_SRCR); | 154 | writel(strcr, ssi->base + SSI_SRCR); |
| 148 | writel(scr, ssi->base + SSI_SCR); | 155 | writel(scr, ssi->base + SSI_SCR); |
diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig new file mode 100644 index 000000000000..5351cba66c9e --- /dev/null +++ b/sound/soc/jz4740/Kconfig | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | config SND_JZ4740_SOC | ||
| 2 | tristate "SoC Audio for Ingenic JZ4740 SoC" | ||
| 3 | depends on MACH_JZ4740 && SND_SOC | ||
| 4 | help | ||
| 5 | Say Y or M if you want to add support for codecs attached to | ||
| 6 | the JZ4740 I2S interface. You will also need to select the audio | ||
| 7 | interfaces to support below. | ||
| 8 | |||
| 9 | config SND_JZ4740_SOC_I2S | ||
| 10 | depends on SND_JZ4740_SOC | ||
| 11 | tristate "SoC Audio (I2S protocol) for Ingenic JZ4740 SoC" | ||
| 12 | help | ||
| 13 | Say Y if you want to use I2S protocol and I2S codec on Ingenic JZ4740 | ||
| 14 | based boards. | ||
| 15 | |||
| 16 | config SND_JZ4740_SOC_QI_LB60 | ||
| 17 | tristate "SoC Audio support for Qi LB60" | ||
| 18 | depends on SND_JZ4740_SOC && JZ4740_QI_LB60 | ||
| 19 | select SND_JZ4740_SOC_I2S | ||
| 20 | select SND_SOC_JZ4740_CODEC | ||
| 21 | help | ||
| 22 | Say Y if you want to add support for ASoC audio on the Qi LB60 board | ||
| 23 | a.k.a Qi Ben NanoNote. | ||
diff --git a/sound/soc/jz4740/Makefile b/sound/soc/jz4740/Makefile new file mode 100644 index 000000000000..be873c1b0c20 --- /dev/null +++ b/sound/soc/jz4740/Makefile | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # | ||
| 2 | # Jz4740 Platform Support | ||
| 3 | # | ||
| 4 | snd-soc-jz4740-objs := jz4740-pcm.o | ||
| 5 | snd-soc-jz4740-i2s-objs := jz4740-i2s.o | ||
| 6 | |||
| 7 | obj-$(CONFIG_SND_JZ4740_SOC) += snd-soc-jz4740.o | ||
| 8 | obj-$(CONFIG_SND_JZ4740_SOC_I2S) += snd-soc-jz4740-i2s.o | ||
| 9 | |||
| 10 | # Jz4740 Machine Support | ||
| 11 | snd-soc-qi-lb60-objs := qi_lb60.o | ||
| 12 | |||
| 13 | obj-$(CONFIG_SND_JZ4740_SOC_QI_LB60) += snd-soc-qi-lb60.o | ||
diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c new file mode 100644 index 000000000000..eb518f0c5e01 --- /dev/null +++ b/sound/soc/jz4740/jz4740-i2s.c | |||
| @@ -0,0 +1,540 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms of the GNU General Public License as published by the | ||
| 6 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 7 | * option) any later version. | ||
| 8 | * | ||
| 9 | * You should have received a copy of the GNU General Public License along | ||
| 10 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 11 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 12 | * | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/init.h> | ||
| 16 | #include <linux/io.h> | ||
| 17 | #include <linux/kernel.h> | ||
| 18 | #include <linux/module.h> | ||
| 19 | #include <linux/platform_device.h> | ||
| 20 | #include <linux/slab.h> | ||
| 21 | |||
| 22 | #include <linux/clk.h> | ||
| 23 | #include <linux/delay.h> | ||
| 24 | |||
| 25 | #include <linux/dma-mapping.h> | ||
| 26 | |||
| 27 | #include <sound/core.h> | ||
| 28 | #include <sound/pcm.h> | ||
| 29 | #include <sound/pcm_params.h> | ||
| 30 | #include <sound/soc.h> | ||
| 31 | #include <sound/soc-dapm.h> | ||
| 32 | #include <sound/initval.h> | ||
| 33 | |||
| 34 | #include "jz4740-i2s.h" | ||
| 35 | #include "jz4740-pcm.h" | ||
| 36 | |||
| 37 | #define JZ_REG_AIC_CONF 0x00 | ||
| 38 | #define JZ_REG_AIC_CTRL 0x04 | ||
| 39 | #define JZ_REG_AIC_I2S_FMT 0x10 | ||
| 40 | #define JZ_REG_AIC_FIFO_STATUS 0x14 | ||
| 41 | #define JZ_REG_AIC_I2S_STATUS 0x1c | ||
| 42 | #define JZ_REG_AIC_CLK_DIV 0x30 | ||
| 43 | #define JZ_REG_AIC_FIFO 0x34 | ||
| 44 | |||
| 45 | #define JZ_AIC_CONF_FIFO_RX_THRESHOLD_MASK (0xf << 12) | ||
| 46 | #define JZ_AIC_CONF_FIFO_TX_THRESHOLD_MASK (0xf << 8) | ||
| 47 | #define JZ_AIC_CONF_OVERFLOW_PLAY_LAST BIT(6) | ||
| 48 | #define JZ_AIC_CONF_INTERNAL_CODEC BIT(5) | ||
| 49 | #define JZ_AIC_CONF_I2S BIT(4) | ||
| 50 | #define JZ_AIC_CONF_RESET BIT(3) | ||
| 51 | #define JZ_AIC_CONF_BIT_CLK_MASTER BIT(2) | ||
| 52 | #define JZ_AIC_CONF_SYNC_CLK_MASTER BIT(1) | ||
| 53 | #define JZ_AIC_CONF_ENABLE BIT(0) | ||
| 54 | |||
| 55 | #define JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 12 | ||
| 56 | #define JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 8 | ||
| 57 | |||
| 58 | #define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK (0x7 << 19) | ||
| 59 | #define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK (0x7 << 16) | ||
| 60 | #define JZ_AIC_CTRL_ENABLE_RX_DMA BIT(15) | ||
| 61 | #define JZ_AIC_CTRL_ENABLE_TX_DMA BIT(14) | ||
| 62 | #define JZ_AIC_CTRL_MONO_TO_STEREO BIT(11) | ||
| 63 | #define JZ_AIC_CTRL_SWITCH_ENDIANNESS BIT(10) | ||
| 64 | #define JZ_AIC_CTRL_SIGNED_TO_UNSIGNED BIT(9) | ||
| 65 | #define JZ_AIC_CTRL_FLUSH BIT(8) | ||
| 66 | #define JZ_AIC_CTRL_ENABLE_ROR_INT BIT(6) | ||
| 67 | #define JZ_AIC_CTRL_ENABLE_TUR_INT BIT(5) | ||
| 68 | #define JZ_AIC_CTRL_ENABLE_RFS_INT BIT(4) | ||
| 69 | #define JZ_AIC_CTRL_ENABLE_TFS_INT BIT(3) | ||
| 70 | #define JZ_AIC_CTRL_ENABLE_LOOPBACK BIT(2) | ||
| 71 | #define JZ_AIC_CTRL_ENABLE_PLAYBACK BIT(1) | ||
| 72 | #define JZ_AIC_CTRL_ENABLE_CAPTURE BIT(0) | ||
| 73 | |||
| 74 | #define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET 19 | ||
| 75 | #define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET 16 | ||
| 76 | |||
| 77 | #define JZ_AIC_I2S_FMT_DISABLE_BIT_CLK BIT(12) | ||
| 78 | #define JZ_AIC_I2S_FMT_ENABLE_SYS_CLK BIT(4) | ||
| 79 | #define JZ_AIC_I2S_FMT_MSB BIT(0) | ||
| 80 | |||
| 81 | #define JZ_AIC_I2S_STATUS_BUSY BIT(2) | ||
| 82 | |||
| 83 | #define JZ_AIC_CLK_DIV_MASK 0xf | ||
| 84 | |||
| 85 | struct jz4740_i2s { | ||
| 86 | struct resource *mem; | ||
| 87 | void __iomem *base; | ||
| 88 | dma_addr_t phys_base; | ||
| 89 | |||
| 90 | struct clk *clk_aic; | ||
| 91 | struct clk *clk_i2s; | ||
| 92 | |||
| 93 | struct jz4740_pcm_config pcm_config_playback; | ||
| 94 | struct jz4740_pcm_config pcm_config_capture; | ||
| 95 | }; | ||
| 96 | |||
| 97 | static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s, | ||
| 98 | unsigned int reg) | ||
| 99 | { | ||
| 100 | return readl(i2s->base + reg); | ||
| 101 | } | ||
| 102 | |||
| 103 | static inline void jz4740_i2s_write(const struct jz4740_i2s *i2s, | ||
| 104 | unsigned int reg, uint32_t value) | ||
| 105 | { | ||
| 106 | writel(value, i2s->base + reg); | ||
| 107 | } | ||
| 108 | |||
| 109 | static inline struct jz4740_i2s *jz4740_dai_to_i2s(struct snd_soc_dai *dai) | ||
| 110 | { | ||
| 111 | return dai->private_data; | ||
| 112 | } | ||
| 113 | |||
| 114 | static int jz4740_i2s_startup(struct snd_pcm_substream *substream, | ||
| 115 | struct snd_soc_dai *dai) | ||
| 116 | { | ||
| 117 | struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai); | ||
| 118 | uint32_t conf, ctrl; | ||
| 119 | |||
| 120 | if (dai->active) | ||
| 121 | return 0; | ||
| 122 | |||
| 123 | ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL); | ||
| 124 | ctrl |= JZ_AIC_CTRL_FLUSH; | ||
| 125 | jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl); | ||
| 126 | |||
| 127 | clk_enable(i2s->clk_i2s); | ||
| 128 | |||
| 129 | conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF); | ||
| 130 | conf |= JZ_AIC_CONF_ENABLE; | ||
| 131 | jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf); | ||
| 132 | |||
| 133 | return 0; | ||
| 134 | } | ||
| 135 | |||
| 136 | static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream, | ||
| 137 | struct snd_soc_dai *dai) | ||
| 138 | { | ||
| 139 | struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai); | ||
| 140 | uint32_t conf; | ||
| 141 | |||
| 142 | if (!dai->active) | ||
| 143 | return; | ||
| 144 | |||
| 145 | conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF); | ||
| 146 | conf &= ~JZ_AIC_CONF_ENABLE; | ||
| 147 | jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf); | ||
| 148 | |||
| 149 | clk_disable(i2s->clk_i2s); | ||
| 150 | } | ||
| 151 | |||
| 152 | static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd, | ||
| 153 | struct snd_soc_dai *dai) | ||
| 154 | { | ||
| 155 | struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai); | ||
| 156 | |||
| 157 | uint32_t ctrl; | ||
| 158 | uint32_t mask; | ||
| 159 | |||
| 160 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
| 161 | mask = JZ_AIC_CTRL_ENABLE_PLAYBACK | JZ_AIC_CTRL_ENABLE_TX_DMA; | ||
| 162 | else | ||
| 163 | mask = JZ_AIC_CTRL_ENABLE_CAPTURE | JZ_AIC_CTRL_ENABLE_RX_DMA; | ||
| 164 | |||
| 165 | ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL); | ||
| 166 | |||
| 167 | switch (cmd) { | ||
| 168 | case SNDRV_PCM_TRIGGER_START: | ||
| 169 | case SNDRV_PCM_TRIGGER_RESUME: | ||
| 170 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
| 171 | ctrl |= mask; | ||
| 172 | break; | ||
| 173 | case SNDRV_PCM_TRIGGER_STOP: | ||
| 174 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
| 175 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
| 176 | ctrl &= ~mask; | ||
| 177 | break; | ||
| 178 | default: | ||
| 179 | return -EINVAL; | ||
| 180 | } | ||
| 181 | |||
| 182 | jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl); | ||
| 183 | |||
| 184 | return 0; | ||
| 185 | } | ||
| 186 | |||
| 187 | static int jz4740_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) | ||
| 188 | { | ||
| 189 | struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai); | ||
| 190 | |||
| 191 | uint32_t format = 0; | ||
| 192 | uint32_t conf; | ||
| 193 | |||
| 194 | conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF); | ||
| 195 | |||
| 196 | conf &= ~(JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER); | ||
| 197 | |||
| 198 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
| 199 | case SND_SOC_DAIFMT_CBS_CFS: | ||
| 200 | conf |= JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER; | ||
| 201 | format |= JZ_AIC_I2S_FMT_ENABLE_SYS_CLK; | ||
| 202 | break; | ||
| 203 | case SND_SOC_DAIFMT_CBM_CFS: | ||
| 204 | conf |= JZ_AIC_CONF_SYNC_CLK_MASTER; | ||
| 205 | break; | ||
| 206 | case SND_SOC_DAIFMT_CBS_CFM: | ||
| 207 | conf |= JZ_AIC_CONF_BIT_CLK_MASTER; | ||
| 208 | break; | ||
| 209 | case SND_SOC_DAIFMT_CBM_CFM: | ||
| 210 | break; | ||
| 211 | default: | ||
| 212 | return -EINVAL; | ||
| 213 | } | ||
| 214 | |||
| 215 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
| 216 | case SND_SOC_DAIFMT_MSB: | ||
| 217 | format |= JZ_AIC_I2S_FMT_MSB; | ||
| 218 | break; | ||
| 219 | case SND_SOC_DAIFMT_I2S: | ||
| 220 | break; | ||
| 221 | default: | ||
| 222 | return -EINVAL; | ||
| 223 | } | ||
| 224 | |||
| 225 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
| 226 | case SND_SOC_DAIFMT_NB_NF: | ||
| 227 | break; | ||
| 228 | default: | ||
| 229 | return -EINVAL; | ||
| 230 | } | ||
| 231 | |||
| 232 | jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf); | ||
| 233 | jz4740_i2s_write(i2s, JZ_REG_AIC_I2S_FMT, format); | ||
| 234 | |||
| 235 | return 0; | ||
| 236 | } | ||
| 237 | |||
| 238 | static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream, | ||
| 239 | struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) | ||
| 240 | { | ||
| 241 | struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai); | ||
| 242 | enum jz4740_dma_width dma_width; | ||
| 243 | struct jz4740_pcm_config *pcm_config; | ||
| 244 | unsigned int sample_size; | ||
| 245 | uint32_t ctrl; | ||
| 246 | |||
| 247 | ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL); | ||
| 248 | |||
| 249 | switch (params_format(params)) { | ||
| 250 | case SNDRV_PCM_FORMAT_S8: | ||
| 251 | sample_size = 0; | ||
| 252 | dma_width = JZ4740_DMA_WIDTH_8BIT; | ||
| 253 | break; | ||
| 254 | case SNDRV_PCM_FORMAT_S16: | ||
| 255 | sample_size = 1; | ||
| 256 | dma_width = JZ4740_DMA_WIDTH_16BIT; | ||
| 257 | break; | ||
| 258 | default: | ||
| 259 | return -EINVAL; | ||
| 260 | } | ||
| 261 | |||
| 262 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
| 263 | ctrl &= ~JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK; | ||
| 264 | ctrl |= sample_size << JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET; | ||
| 265 | if (params_channels(params) == 1) | ||
| 266 | ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO; | ||
| 267 | else | ||
| 268 | ctrl &= ~JZ_AIC_CTRL_MONO_TO_STEREO; | ||
| 269 | |||
| 270 | pcm_config = &i2s->pcm_config_playback; | ||
| 271 | pcm_config->dma_config.dst_width = dma_width; | ||
| 272 | |||
| 273 | } else { | ||
| 274 | ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK; | ||
| 275 | ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET; | ||
| 276 | |||
| 277 | pcm_config = &i2s->pcm_config_capture; | ||
| 278 | pcm_config->dma_config.src_width = dma_width; | ||
| 279 | } | ||
| 280 | |||
| 281 | jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl); | ||
| 282 | |||
| 283 | snd_soc_dai_set_dma_data(dai, substream, pcm_config); | ||
| 284 | |||
| 285 | return 0; | ||
| 286 | } | ||
| 287 | |||
| 288 | static int jz4740_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, | ||
| 289 | unsigned int freq, int dir) | ||
| 290 | { | ||
| 291 | struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai); | ||
| 292 | struct clk *parent; | ||
| 293 | int ret = 0; | ||
| 294 | |||
| 295 | switch (clk_id) { | ||
| 296 | case JZ4740_I2S_CLKSRC_EXT: | ||
| 297 | parent = clk_get(NULL, "ext"); | ||
| 298 | clk_set_parent(i2s->clk_i2s, parent); | ||
| 299 | break; | ||
| 300 | case JZ4740_I2S_CLKSRC_PLL: | ||
| 301 | parent = clk_get(NULL, "pll half"); | ||
| 302 | clk_set_parent(i2s->clk_i2s, parent); | ||
| 303 | ret = clk_set_rate(i2s->clk_i2s, freq); | ||
| 304 | break; | ||
| 305 | default: | ||
| 306 | return -EINVAL; | ||
| 307 | } | ||
| 308 | clk_put(parent); | ||
| 309 | |||
| 310 | return ret; | ||
| 311 | } | ||
| 312 | |||
| 313 | static int jz4740_i2s_suspend(struct snd_soc_dai *dai) | ||
| 314 | { | ||
| 315 | struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai); | ||
| 316 | uint32_t conf; | ||
| 317 | |||
| 318 | if (dai->active) { | ||
| 319 | conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF); | ||
| 320 | conf &= ~JZ_AIC_CONF_ENABLE; | ||
| 321 | jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf); | ||
| 322 | |||
| 323 | clk_disable(i2s->clk_i2s); | ||
| 324 | } | ||
| 325 | |||
| 326 | clk_disable(i2s->clk_aic); | ||
| 327 | |||
| 328 | return 0; | ||
| 329 | } | ||
| 330 | |||
| 331 | static int jz4740_i2s_resume(struct snd_soc_dai *dai) | ||
| 332 | { | ||
| 333 | struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai); | ||
| 334 | uint32_t conf; | ||
| 335 | |||
| 336 | clk_enable(i2s->clk_aic); | ||
| 337 | |||
| 338 | if (dai->active) { | ||
| 339 | clk_enable(i2s->clk_i2s); | ||
| 340 | |||
| 341 | conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF); | ||
| 342 | conf |= JZ_AIC_CONF_ENABLE; | ||
| 343 | jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf); | ||
| 344 | } | ||
| 345 | |||
| 346 | return 0; | ||
| 347 | } | ||
| 348 | |||
| 349 | static int jz4740_i2s_probe(struct platform_device *pdev, struct snd_soc_dai *dai) | ||
| 350 | { | ||
| 351 | struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai); | ||
| 352 | uint32_t conf; | ||
| 353 | |||
| 354 | conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) | | ||
| 355 | (8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) | | ||
| 356 | JZ_AIC_CONF_OVERFLOW_PLAY_LAST | | ||
| 357 | JZ_AIC_CONF_I2S | | ||
| 358 | JZ_AIC_CONF_INTERNAL_CODEC; | ||
| 359 | |||
| 360 | jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, JZ_AIC_CONF_RESET); | ||
| 361 | jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf); | ||
| 362 | |||
| 363 | return 0; | ||
| 364 | } | ||
| 365 | |||
| 366 | static struct snd_soc_dai_ops jz4740_i2s_dai_ops = { | ||
| 367 | .startup = jz4740_i2s_startup, | ||
| 368 | .shutdown = jz4740_i2s_shutdown, | ||
| 369 | .trigger = jz4740_i2s_trigger, | ||
| 370 | .hw_params = jz4740_i2s_hw_params, | ||
| 371 | .set_fmt = jz4740_i2s_set_fmt, | ||
| 372 | .set_sysclk = jz4740_i2s_set_sysclk, | ||
| 373 | }; | ||
| 374 | |||
| 375 | #define JZ4740_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \ | ||
| 376 | SNDRV_PCM_FMTBIT_S16_LE) | ||
| 377 | |||
| 378 | struct snd_soc_dai jz4740_i2s_dai = { | ||
| 379 | .name = "jz4740-i2s", | ||
| 380 | .probe = jz4740_i2s_probe, | ||
| 381 | .playback = { | ||
| 382 | .channels_min = 1, | ||
| 383 | .channels_max = 2, | ||
| 384 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
| 385 | .formats = JZ4740_I2S_FMTS, | ||
| 386 | }, | ||
| 387 | .capture = { | ||
| 388 | .channels_min = 2, | ||
| 389 | .channels_max = 2, | ||
| 390 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
| 391 | .formats = JZ4740_I2S_FMTS, | ||
| 392 | }, | ||
| 393 | .symmetric_rates = 1, | ||
| 394 | .ops = &jz4740_i2s_dai_ops, | ||
| 395 | .suspend = jz4740_i2s_suspend, | ||
| 396 | .resume = jz4740_i2s_resume, | ||
| 397 | }; | ||
| 398 | EXPORT_SYMBOL_GPL(jz4740_i2s_dai); | ||
| 399 | |||
| 400 | static void __devinit jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s) | ||
| 401 | { | ||
| 402 | struct jz4740_dma_config *dma_config; | ||
| 403 | |||
| 404 | /* Playback */ | ||
| 405 | dma_config = &i2s->pcm_config_playback.dma_config; | ||
| 406 | dma_config->src_width = JZ4740_DMA_WIDTH_32BIT, | ||
| 407 | dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE; | ||
| 408 | dma_config->request_type = JZ4740_DMA_TYPE_AIC_TRANSMIT; | ||
| 409 | dma_config->flags = JZ4740_DMA_SRC_AUTOINC; | ||
| 410 | dma_config->mode = JZ4740_DMA_MODE_SINGLE; | ||
| 411 | i2s->pcm_config_playback.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO; | ||
| 412 | |||
| 413 | /* Capture */ | ||
| 414 | dma_config = &i2s->pcm_config_capture.dma_config; | ||
| 415 | dma_config->dst_width = JZ4740_DMA_WIDTH_32BIT, | ||
| 416 | dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE; | ||
| 417 | dma_config->request_type = JZ4740_DMA_TYPE_AIC_RECEIVE; | ||
| 418 | dma_config->flags = JZ4740_DMA_DST_AUTOINC; | ||
| 419 | dma_config->mode = JZ4740_DMA_MODE_SINGLE; | ||
| 420 | i2s->pcm_config_capture.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO; | ||
| 421 | } | ||
| 422 | |||
| 423 | static int __devinit jz4740_i2s_dev_probe(struct platform_device *pdev) | ||
| 424 | { | ||
| 425 | struct jz4740_i2s *i2s; | ||
| 426 | int ret; | ||
| 427 | |||
| 428 | i2s = kzalloc(sizeof(*i2s), GFP_KERNEL); | ||
| 429 | |||
| 430 | if (!i2s) | ||
| 431 | return -ENOMEM; | ||
| 432 | |||
| 433 | i2s->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 434 | if (!i2s->mem) { | ||
| 435 | ret = -ENOENT; | ||
| 436 | goto err_free; | ||
| 437 | } | ||
| 438 | |||
| 439 | i2s->mem = request_mem_region(i2s->mem->start, resource_size(i2s->mem), | ||
| 440 | pdev->name); | ||
| 441 | if (!i2s->mem) { | ||
| 442 | ret = -EBUSY; | ||
| 443 | goto err_free; | ||
| 444 | } | ||
| 445 | |||
| 446 | i2s->base = ioremap_nocache(i2s->mem->start, resource_size(i2s->mem)); | ||
| 447 | if (!i2s->base) { | ||
| 448 | ret = -EBUSY; | ||
| 449 | goto err_release_mem_region; | ||
| 450 | } | ||
| 451 | |||
| 452 | i2s->phys_base = i2s->mem->start; | ||
| 453 | |||
| 454 | i2s->clk_aic = clk_get(&pdev->dev, "aic"); | ||
| 455 | if (IS_ERR(i2s->clk_aic)) { | ||
| 456 | ret = PTR_ERR(i2s->clk_aic); | ||
| 457 | goto err_iounmap; | ||
| 458 | } | ||
| 459 | |||
| 460 | i2s->clk_i2s = clk_get(&pdev->dev, "i2s"); | ||
| 461 | if (IS_ERR(i2s->clk_i2s)) { | ||
| 462 | ret = PTR_ERR(i2s->clk_i2s); | ||
| 463 | goto err_clk_put_aic; | ||
| 464 | } | ||
| 465 | |||
| 466 | clk_enable(i2s->clk_aic); | ||
| 467 | |||
| 468 | jz4740_i2c_init_pcm_config(i2s); | ||
| 469 | |||
| 470 | jz4740_i2s_dai.private_data = i2s; | ||
| 471 | ret = snd_soc_register_dai(&jz4740_i2s_dai); | ||
| 472 | |||
| 473 | if (ret) { | ||
| 474 | dev_err(&pdev->dev, "Failed to register DAI\n"); | ||
| 475 | goto err_clk_put_i2s; | ||
| 476 | } | ||
| 477 | |||
| 478 | platform_set_drvdata(pdev, i2s); | ||
| 479 | |||
| 480 | return 0; | ||
| 481 | |||
| 482 | err_clk_put_i2s: | ||
| 483 | clk_disable(i2s->clk_aic); | ||
| 484 | clk_put(i2s->clk_i2s); | ||
| 485 | err_clk_put_aic: | ||
| 486 | clk_put(i2s->clk_aic); | ||
| 487 | err_iounmap: | ||
| 488 | iounmap(i2s->base); | ||
| 489 | err_release_mem_region: | ||
| 490 | release_mem_region(i2s->mem->start, resource_size(i2s->mem)); | ||
| 491 | err_free: | ||
| 492 | kfree(i2s); | ||
| 493 | |||
| 494 | return ret; | ||
| 495 | } | ||
| 496 | |||
| 497 | static int __devexit jz4740_i2s_dev_remove(struct platform_device *pdev) | ||
| 498 | { | ||
| 499 | struct jz4740_i2s *i2s = platform_get_drvdata(pdev); | ||
| 500 | |||
| 501 | snd_soc_unregister_dai(&jz4740_i2s_dai); | ||
| 502 | |||
| 503 | clk_disable(i2s->clk_aic); | ||
| 504 | clk_put(i2s->clk_i2s); | ||
| 505 | clk_put(i2s->clk_aic); | ||
| 506 | |||
| 507 | iounmap(i2s->base); | ||
| 508 | release_mem_region(i2s->mem->start, resource_size(i2s->mem)); | ||
| 509 | |||
| 510 | platform_set_drvdata(pdev, NULL); | ||
| 511 | kfree(i2s); | ||
| 512 | |||
| 513 | return 0; | ||
| 514 | } | ||
| 515 | |||
| 516 | static struct platform_driver jz4740_i2s_driver = { | ||
| 517 | .probe = jz4740_i2s_dev_probe, | ||
| 518 | .remove = __devexit_p(jz4740_i2s_dev_remove), | ||
| 519 | .driver = { | ||
| 520 | .name = "jz4740-i2s", | ||
| 521 | .owner = THIS_MODULE, | ||
| 522 | }, | ||
| 523 | }; | ||
| 524 | |||
| 525 | static int __init jz4740_i2s_init(void) | ||
| 526 | { | ||
| 527 | return platform_driver_register(&jz4740_i2s_driver); | ||
| 528 | } | ||
| 529 | module_init(jz4740_i2s_init); | ||
| 530 | |||
| 531 | static void __exit jz4740_i2s_exit(void) | ||
| 532 | { | ||
| 533 | platform_driver_unregister(&jz4740_i2s_driver); | ||
| 534 | } | ||
| 535 | module_exit(jz4740_i2s_exit); | ||
| 536 | |||
| 537 | MODULE_AUTHOR("Lars-Peter Clausen, <lars@metafoo.de>"); | ||
| 538 | MODULE_DESCRIPTION("Ingenic JZ4740 SoC I2S driver"); | ||
| 539 | MODULE_LICENSE("GPL"); | ||
| 540 | MODULE_ALIAS("platform:jz4740-i2s"); | ||
diff --git a/sound/soc/jz4740/jz4740-i2s.h b/sound/soc/jz4740/jz4740-i2s.h new file mode 100644 index 000000000000..da22ed88a589 --- /dev/null +++ b/sound/soc/jz4740/jz4740-i2s.h | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | /* | ||
| 2 | * This program is free software; you can redistribute it and/or modify | ||
| 3 | * it under the terms of the GNU General Public License version 2 as | ||
| 4 | * published by the Free Software Foundation. | ||
| 5 | */ | ||
| 6 | |||
| 7 | #ifndef _JZ4740_I2S_H | ||
| 8 | #define _JZ4740_I2S_H | ||
| 9 | |||
| 10 | /* I2S clock source */ | ||
| 11 | #define JZ4740_I2S_CLKSRC_EXT 0 | ||
| 12 | #define JZ4740_I2S_CLKSRC_PLL 1 | ||
| 13 | |||
| 14 | #define JZ4740_I2S_BIT_CLK 0 | ||
| 15 | |||
| 16 | extern struct snd_soc_dai jz4740_i2s_dai; | ||
| 17 | |||
| 18 | #endif | ||
diff --git a/sound/soc/jz4740/jz4740-pcm.c b/sound/soc/jz4740/jz4740-pcm.c new file mode 100644 index 000000000000..ee68d850c8dd --- /dev/null +++ b/sound/soc/jz4740/jz4740-pcm.c | |||
| @@ -0,0 +1,373 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms of the GNU General Public License as published by the | ||
| 6 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 7 | * option) any later version. | ||
| 8 | * | ||
| 9 | * You should have received a copy of the GNU General Public License along | ||
| 10 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 11 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 12 | * | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/init.h> | ||
| 16 | #include <linux/interrupt.h> | ||
| 17 | #include <linux/kernel.h> | ||
| 18 | #include <linux/module.h> | ||
| 19 | #include <linux/platform_device.h> | ||
| 20 | #include <linux/slab.h> | ||
| 21 | |||
| 22 | #include <linux/dma-mapping.h> | ||
| 23 | |||
| 24 | #include <sound/core.h> | ||
| 25 | #include <sound/pcm.h> | ||
| 26 | #include <sound/pcm_params.h> | ||
| 27 | #include <sound/soc.h> | ||
| 28 | |||
| 29 | #include <asm/mach-jz4740/dma.h> | ||
| 30 | #include "jz4740-pcm.h" | ||
| 31 | |||
| 32 | struct jz4740_runtime_data { | ||
| 33 | unsigned long dma_period; | ||
| 34 | dma_addr_t dma_start; | ||
| 35 | dma_addr_t dma_pos; | ||
| 36 | dma_addr_t dma_end; | ||
| 37 | |||
| 38 | struct jz4740_dma_chan *dma; | ||
| 39 | |||
| 40 | dma_addr_t fifo_addr; | ||
| 41 | }; | ||
| 42 | |||
| 43 | /* identify hardware playback capabilities */ | ||
| 44 | static const struct snd_pcm_hardware jz4740_pcm_hardware = { | ||
| 45 | .info = SNDRV_PCM_INFO_MMAP | | ||
| 46 | SNDRV_PCM_INFO_MMAP_VALID | | ||
| 47 | SNDRV_PCM_INFO_INTERLEAVED | | ||
| 48 | SNDRV_PCM_INFO_BLOCK_TRANSFER, | ||
| 49 | .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8, | ||
| 50 | |||
| 51 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
| 52 | .channels_min = 1, | ||
| 53 | .channels_max = 2, | ||
| 54 | .period_bytes_min = 16, | ||
| 55 | .period_bytes_max = 2 * PAGE_SIZE, | ||
| 56 | .periods_min = 2, | ||
| 57 | .periods_max = 128, | ||
| 58 | .buffer_bytes_max = 128 * 2 * PAGE_SIZE, | ||
| 59 | .fifo_size = 32, | ||
| 60 | }; | ||
| 61 | |||
| 62 | static void jz4740_pcm_start_transfer(struct jz4740_runtime_data *prtd, | ||
| 63 | struct snd_pcm_substream *substream) | ||
| 64 | { | ||
| 65 | unsigned long count; | ||
| 66 | |||
| 67 | if (prtd->dma_pos == prtd->dma_end) | ||
| 68 | prtd->dma_pos = prtd->dma_start; | ||
| 69 | |||
| 70 | if (prtd->dma_pos + prtd->dma_period > prtd->dma_end) | ||
| 71 | count = prtd->dma_end - prtd->dma_pos; | ||
| 72 | else | ||
| 73 | count = prtd->dma_period; | ||
| 74 | |||
| 75 | jz4740_dma_disable(prtd->dma); | ||
| 76 | |||
| 77 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
| 78 | jz4740_dma_set_src_addr(prtd->dma, prtd->dma_pos); | ||
| 79 | jz4740_dma_set_dst_addr(prtd->dma, prtd->fifo_addr); | ||
| 80 | } else { | ||
| 81 | jz4740_dma_set_src_addr(prtd->dma, prtd->fifo_addr); | ||
| 82 | jz4740_dma_set_dst_addr(prtd->dma, prtd->dma_pos); | ||
| 83 | } | ||
| 84 | |||
| 85 | jz4740_dma_set_transfer_count(prtd->dma, count); | ||
| 86 | |||
| 87 | prtd->dma_pos += count; | ||
| 88 | |||
| 89 | jz4740_dma_enable(prtd->dma); | ||
| 90 | } | ||
| 91 | |||
| 92 | static void jz4740_pcm_dma_transfer_done(struct jz4740_dma_chan *dma, int err, | ||
| 93 | void *dev_id) | ||
| 94 | { | ||
| 95 | struct snd_pcm_substream *substream = dev_id; | ||
| 96 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 97 | struct jz4740_runtime_data *prtd = runtime->private_data; | ||
| 98 | |||
| 99 | snd_pcm_period_elapsed(substream); | ||
| 100 | |||
| 101 | jz4740_pcm_start_transfer(prtd, substream); | ||
| 102 | } | ||
| 103 | |||
| 104 | static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream, | ||
| 105 | struct snd_pcm_hw_params *params) | ||
| 106 | { | ||
| 107 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 108 | struct jz4740_runtime_data *prtd = runtime->private_data; | ||
| 109 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
| 110 | struct jz4740_pcm_config *config; | ||
| 111 | |||
| 112 | config = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream); | ||
| 113 | |||
| 114 | if (!config) | ||
| 115 | return 0; | ||
| 116 | |||
| 117 | if (!prtd->dma) { | ||
| 118 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
| 119 | prtd->dma = jz4740_dma_request(substream, "PCM Capture"); | ||
| 120 | else | ||
| 121 | prtd->dma = jz4740_dma_request(substream, "PCM Playback"); | ||
| 122 | } | ||
| 123 | |||
| 124 | if (!prtd->dma) | ||
| 125 | return -EBUSY; | ||
| 126 | |||
| 127 | jz4740_dma_configure(prtd->dma, &config->dma_config); | ||
| 128 | prtd->fifo_addr = config->fifo_addr; | ||
| 129 | |||
| 130 | jz4740_dma_set_complete_cb(prtd->dma, jz4740_pcm_dma_transfer_done); | ||
| 131 | |||
| 132 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | ||
| 133 | runtime->dma_bytes = params_buffer_bytes(params); | ||
| 134 | |||
| 135 | prtd->dma_period = params_period_bytes(params); | ||
| 136 | prtd->dma_start = runtime->dma_addr; | ||
| 137 | prtd->dma_pos = prtd->dma_start; | ||
| 138 | prtd->dma_end = prtd->dma_start + runtime->dma_bytes; | ||
| 139 | |||
| 140 | return 0; | ||
| 141 | } | ||
| 142 | |||
| 143 | static int jz4740_pcm_hw_free(struct snd_pcm_substream *substream) | ||
| 144 | { | ||
| 145 | struct jz4740_runtime_data *prtd = substream->runtime->private_data; | ||
| 146 | |||
| 147 | snd_pcm_set_runtime_buffer(substream, NULL); | ||
| 148 | if (prtd->dma) { | ||
| 149 | jz4740_dma_free(prtd->dma); | ||
| 150 | prtd->dma = NULL; | ||
| 151 | } | ||
| 152 | |||
| 153 | return 0; | ||
| 154 | } | ||
| 155 | |||
| 156 | static int jz4740_pcm_prepare(struct snd_pcm_substream *substream) | ||
| 157 | { | ||
| 158 | struct jz4740_runtime_data *prtd = substream->runtime->private_data; | ||
| 159 | |||
| 160 | if (!prtd->dma) | ||
| 161 | return -EBUSY; | ||
| 162 | |||
| 163 | prtd->dma_pos = prtd->dma_start; | ||
| 164 | |||
| 165 | return 0; | ||
| 166 | } | ||
| 167 | |||
| 168 | static int jz4740_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | ||
| 169 | { | ||
| 170 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 171 | struct jz4740_runtime_data *prtd = runtime->private_data; | ||
| 172 | |||
| 173 | switch (cmd) { | ||
| 174 | case SNDRV_PCM_TRIGGER_START: | ||
| 175 | case SNDRV_PCM_TRIGGER_RESUME: | ||
| 176 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
| 177 | jz4740_pcm_start_transfer(prtd, substream); | ||
| 178 | break; | ||
| 179 | case SNDRV_PCM_TRIGGER_STOP: | ||
| 180 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
| 181 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
| 182 | jz4740_dma_disable(prtd->dma); | ||
| 183 | break; | ||
| 184 | default: | ||
| 185 | break; | ||
| 186 | } | ||
| 187 | |||
| 188 | return 0; | ||
| 189 | } | ||
| 190 | |||
| 191 | static snd_pcm_uframes_t jz4740_pcm_pointer(struct snd_pcm_substream *substream) | ||
| 192 | { | ||
| 193 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 194 | struct jz4740_runtime_data *prtd = runtime->private_data; | ||
| 195 | unsigned long byte_offset; | ||
| 196 | snd_pcm_uframes_t offset; | ||
| 197 | struct jz4740_dma_chan *dma = prtd->dma; | ||
| 198 | |||
| 199 | /* prtd->dma_pos points to the end of the current transfer. So by | ||
| 200 | * subtracting prdt->dma_start we get the offset to the end of the | ||
| 201 | * current period in bytes. By subtracting the residue of the transfer | ||
| 202 | * we get the current offset in bytes. */ | ||
| 203 | byte_offset = prtd->dma_pos - prtd->dma_start; | ||
| 204 | byte_offset -= jz4740_dma_get_residue(dma); | ||
| 205 | |||
| 206 | offset = bytes_to_frames(runtime, byte_offset); | ||
| 207 | if (offset >= runtime->buffer_size) | ||
| 208 | offset = 0; | ||
| 209 | |||
| 210 | return offset; | ||
| 211 | } | ||
| 212 | |||
| 213 | static int jz4740_pcm_open(struct snd_pcm_substream *substream) | ||
| 214 | { | ||
| 215 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 216 | struct jz4740_runtime_data *prtd; | ||
| 217 | |||
| 218 | prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); | ||
| 219 | if (prtd == NULL) | ||
| 220 | return -ENOMEM; | ||
| 221 | |||
| 222 | snd_soc_set_runtime_hwparams(substream, &jz4740_pcm_hardware); | ||
| 223 | |||
| 224 | runtime->private_data = prtd; | ||
| 225 | |||
| 226 | return 0; | ||
| 227 | } | ||
| 228 | |||
| 229 | static int jz4740_pcm_close(struct snd_pcm_substream *substream) | ||
| 230 | { | ||
| 231 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 232 | struct jz4740_runtime_data *prtd = runtime->private_data; | ||
| 233 | |||
| 234 | kfree(prtd); | ||
| 235 | |||
| 236 | return 0; | ||
| 237 | } | ||
| 238 | |||
| 239 | static int jz4740_pcm_mmap(struct snd_pcm_substream *substream, | ||
| 240 | struct vm_area_struct *vma) | ||
| 241 | { | ||
| 242 | return remap_pfn_range(vma, vma->vm_start, | ||
| 243 | substream->dma_buffer.addr >> PAGE_SHIFT, | ||
| 244 | vma->vm_end - vma->vm_start, vma->vm_page_prot); | ||
| 245 | } | ||
| 246 | |||
| 247 | static struct snd_pcm_ops jz4740_pcm_ops = { | ||
| 248 | .open = jz4740_pcm_open, | ||
| 249 | .close = jz4740_pcm_close, | ||
| 250 | .ioctl = snd_pcm_lib_ioctl, | ||
| 251 | .hw_params = jz4740_pcm_hw_params, | ||
| 252 | .hw_free = jz4740_pcm_hw_free, | ||
| 253 | .prepare = jz4740_pcm_prepare, | ||
| 254 | .trigger = jz4740_pcm_trigger, | ||
| 255 | .pointer = jz4740_pcm_pointer, | ||
| 256 | .mmap = jz4740_pcm_mmap, | ||
| 257 | }; | ||
| 258 | |||
| 259 | static int jz4740_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) | ||
| 260 | { | ||
| 261 | struct snd_pcm_substream *substream = pcm->streams[stream].substream; | ||
| 262 | struct snd_dma_buffer *buf = &substream->dma_buffer; | ||
| 263 | size_t size = jz4740_pcm_hardware.buffer_bytes_max; | ||
| 264 | |||
| 265 | buf->dev.type = SNDRV_DMA_TYPE_DEV; | ||
| 266 | buf->dev.dev = pcm->card->dev; | ||
| 267 | buf->private_data = NULL; | ||
| 268 | |||
| 269 | buf->area = dma_alloc_noncoherent(pcm->card->dev, size, | ||
| 270 | &buf->addr, GFP_KERNEL); | ||
| 271 | if (!buf->area) | ||
| 272 | return -ENOMEM; | ||
| 273 | |||
| 274 | buf->bytes = size; | ||
| 275 | |||
| 276 | return 0; | ||
| 277 | } | ||
| 278 | |||
| 279 | static void jz4740_pcm_free(struct snd_pcm *pcm) | ||
| 280 | { | ||
| 281 | struct snd_pcm_substream *substream; | ||
| 282 | struct snd_dma_buffer *buf; | ||
| 283 | int stream; | ||
| 284 | |||
| 285 | for (stream = 0; stream < SNDRV_PCM_STREAM_LAST; ++stream) { | ||
| 286 | substream = pcm->streams[stream].substream; | ||
| 287 | if (!substream) | ||
| 288 | continue; | ||
| 289 | |||
| 290 | buf = &substream->dma_buffer; | ||
| 291 | if (!buf->area) | ||
| 292 | continue; | ||
| 293 | |||
| 294 | dma_free_noncoherent(pcm->card->dev, buf->bytes, buf->area, | ||
| 295 | buf->addr); | ||
| 296 | buf->area = NULL; | ||
| 297 | } | ||
| 298 | } | ||
| 299 | |||
| 300 | static u64 jz4740_pcm_dmamask = DMA_BIT_MASK(32); | ||
| 301 | |||
| 302 | int jz4740_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, | ||
| 303 | struct snd_pcm *pcm) | ||
| 304 | { | ||
| 305 | int ret = 0; | ||
| 306 | |||
| 307 | if (!card->dev->dma_mask) | ||
| 308 | card->dev->dma_mask = &jz4740_pcm_dmamask; | ||
| 309 | |||
| 310 | if (!card->dev->coherent_dma_mask) | ||
| 311 | card->dev->coherent_dma_mask = DMA_BIT_MASK(32); | ||
| 312 | |||
| 313 | if (dai->playback.channels_min) { | ||
| 314 | ret = jz4740_pcm_preallocate_dma_buffer(pcm, | ||
| 315 | SNDRV_PCM_STREAM_PLAYBACK); | ||
| 316 | if (ret) | ||
| 317 | goto err; | ||
| 318 | } | ||
| 319 | |||
| 320 | if (dai->capture.channels_min) { | ||
| 321 | ret = jz4740_pcm_preallocate_dma_buffer(pcm, | ||
| 322 | SNDRV_PCM_STREAM_CAPTURE); | ||
| 323 | if (ret) | ||
| 324 | goto err; | ||
| 325 | } | ||
| 326 | |||
| 327 | err: | ||
| 328 | return ret; | ||
| 329 | } | ||
| 330 | |||
| 331 | struct snd_soc_platform jz4740_soc_platform = { | ||
| 332 | .name = "jz4740-pcm", | ||
| 333 | .pcm_ops = &jz4740_pcm_ops, | ||
| 334 | .pcm_new = jz4740_pcm_new, | ||
| 335 | .pcm_free = jz4740_pcm_free, | ||
| 336 | }; | ||
| 337 | EXPORT_SYMBOL_GPL(jz4740_soc_platform); | ||
| 338 | |||
| 339 | static int __devinit jz4740_pcm_probe(struct platform_device *pdev) | ||
| 340 | { | ||
| 341 | return snd_soc_register_platform(&jz4740_soc_platform); | ||
| 342 | } | ||
| 343 | |||
| 344 | static int __devexit jz4740_pcm_remove(struct platform_device *pdev) | ||
| 345 | { | ||
| 346 | snd_soc_unregister_platform(&jz4740_soc_platform); | ||
| 347 | return 0; | ||
| 348 | } | ||
| 349 | |||
| 350 | static struct platform_driver jz4740_pcm_driver = { | ||
| 351 | .probe = jz4740_pcm_probe, | ||
| 352 | .remove = __devexit_p(jz4740_pcm_remove), | ||
| 353 | .driver = { | ||
| 354 | .name = "jz4740-pcm", | ||
| 355 | .owner = THIS_MODULE, | ||
| 356 | }, | ||
| 357 | }; | ||
| 358 | |||
| 359 | static int __init jz4740_soc_platform_init(void) | ||
| 360 | { | ||
| 361 | return platform_driver_register(&jz4740_pcm_driver); | ||
| 362 | } | ||
| 363 | module_init(jz4740_soc_platform_init); | ||
| 364 | |||
| 365 | static void __exit jz4740_soc_platform_exit(void) | ||
| 366 | { | ||
| 367 | return platform_driver_unregister(&jz4740_pcm_driver); | ||
| 368 | } | ||
| 369 | module_exit(jz4740_soc_platform_exit); | ||
| 370 | |||
| 371 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); | ||
| 372 | MODULE_DESCRIPTION("Ingenic SoC JZ4740 PCM driver"); | ||
| 373 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/jz4740/jz4740-pcm.h b/sound/soc/jz4740/jz4740-pcm.h new file mode 100644 index 000000000000..e3f221e2779c --- /dev/null +++ b/sound/soc/jz4740/jz4740-pcm.h | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * This program is free software; you can redistribute it and/or modify | ||
| 4 | * it under the terms of the GNU General Public License version 2 as | ||
| 5 | * published by the Free Software Foundation. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef _JZ4740_PCM_H | ||
| 9 | #define _JZ4740_PCM_H | ||
| 10 | |||
| 11 | #include <linux/dma-mapping.h> | ||
| 12 | #include <asm/mach-jz4740/dma.h> | ||
| 13 | |||
| 14 | /* platform data */ | ||
| 15 | extern struct snd_soc_platform jz4740_soc_platform; | ||
| 16 | |||
| 17 | struct jz4740_pcm_config { | ||
| 18 | struct jz4740_dma_config dma_config; | ||
| 19 | phys_addr_t fifo_addr; | ||
| 20 | }; | ||
| 21 | |||
| 22 | #endif | ||
diff --git a/sound/soc/jz4740/qi_lb60.c b/sound/soc/jz4740/qi_lb60.c new file mode 100644 index 000000000000..f15f4918f15f --- /dev/null +++ b/sound/soc/jz4740/qi_lb60.c | |||
| @@ -0,0 +1,166 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2009, Lars-Peter Clausen <lars@metafoo.de> | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * You should have received a copy of the GNU General Public License along | ||
| 9 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 10 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 11 | * | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/moduleparam.h> | ||
| 16 | #include <linux/timer.h> | ||
| 17 | #include <linux/interrupt.h> | ||
| 18 | #include <linux/platform_device.h> | ||
| 19 | #include <sound/core.h> | ||
| 20 | #include <sound/pcm.h> | ||
| 21 | #include <sound/soc.h> | ||
| 22 | #include <sound/soc-dapm.h> | ||
| 23 | #include <linux/gpio.h> | ||
| 24 | |||
| 25 | #include "../codecs/jz4740.h" | ||
| 26 | #include "jz4740-pcm.h" | ||
| 27 | #include "jz4740-i2s.h" | ||
| 28 | |||
| 29 | |||
| 30 | #define QI_LB60_SND_GPIO JZ_GPIO_PORTB(29) | ||
| 31 | #define QI_LB60_AMP_GPIO JZ_GPIO_PORTD(4) | ||
| 32 | |||
| 33 | static int qi_lb60_spk_event(struct snd_soc_dapm_widget *widget, | ||
| 34 | struct snd_kcontrol *ctrl, int event) | ||
| 35 | { | ||
| 36 | int on = 0; | ||
| 37 | if (event & SND_SOC_DAPM_POST_PMU) | ||
| 38 | on = 1; | ||
| 39 | else if (event & SND_SOC_DAPM_PRE_PMD) | ||
| 40 | on = 0; | ||
| 41 | |||
| 42 | gpio_set_value(QI_LB60_SND_GPIO, on); | ||
| 43 | gpio_set_value(QI_LB60_AMP_GPIO, on); | ||
| 44 | |||
| 45 | return 0; | ||
| 46 | } | ||
| 47 | |||
| 48 | static const struct snd_soc_dapm_widget qi_lb60_widgets[] = { | ||
| 49 | SND_SOC_DAPM_SPK("Speaker", qi_lb60_spk_event), | ||
| 50 | SND_SOC_DAPM_MIC("Mic", NULL), | ||
| 51 | }; | ||
| 52 | |||
| 53 | static const struct snd_soc_dapm_route qi_lb60_routes[] = { | ||
| 54 | {"Mic", NULL, "MIC"}, | ||
| 55 | {"Speaker", NULL, "LOUT"}, | ||
| 56 | {"Speaker", NULL, "ROUT"}, | ||
| 57 | }; | ||
| 58 | |||
| 59 | #define QI_LB60_DAIFMT (SND_SOC_DAIFMT_I2S | \ | ||
| 60 | SND_SOC_DAIFMT_NB_NF | \ | ||
| 61 | SND_SOC_DAIFMT_CBM_CFM) | ||
| 62 | |||
| 63 | static int qi_lb60_codec_init(struct snd_soc_codec *codec) | ||
| 64 | { | ||
| 65 | int ret; | ||
| 66 | struct snd_soc_dai *cpu_dai = codec->socdev->card->dai_link->cpu_dai; | ||
| 67 | |||
| 68 | snd_soc_dapm_nc_pin(codec, "LIN"); | ||
| 69 | snd_soc_dapm_nc_pin(codec, "RIN"); | ||
| 70 | |||
| 71 | ret = snd_soc_dai_set_fmt(cpu_dai, QI_LB60_DAIFMT); | ||
| 72 | if (ret < 0) { | ||
| 73 | dev_err(codec->dev, "Failed to set cpu dai format: %d\n", ret); | ||
| 74 | return ret; | ||
| 75 | } | ||
| 76 | |||
| 77 | snd_soc_dapm_new_controls(codec, qi_lb60_widgets, ARRAY_SIZE(qi_lb60_widgets)); | ||
| 78 | snd_soc_dapm_add_routes(codec, qi_lb60_routes, ARRAY_SIZE(qi_lb60_routes)); | ||
| 79 | snd_soc_dapm_sync(codec); | ||
| 80 | |||
| 81 | return 0; | ||
| 82 | } | ||
| 83 | |||
| 84 | static struct snd_soc_dai_link qi_lb60_dai = { | ||
| 85 | .name = "jz4740", | ||
| 86 | .stream_name = "jz4740", | ||
| 87 | .cpu_dai = &jz4740_i2s_dai, | ||
| 88 | .codec_dai = &jz4740_codec_dai, | ||
| 89 | .init = qi_lb60_codec_init, | ||
| 90 | }; | ||
| 91 | |||
| 92 | static struct snd_soc_card qi_lb60 = { | ||
| 93 | .name = "QI LB60", | ||
| 94 | .dai_link = &qi_lb60_dai, | ||
| 95 | .num_links = 1, | ||
| 96 | .platform = &jz4740_soc_platform, | ||
| 97 | }; | ||
| 98 | |||
| 99 | static struct snd_soc_device qi_lb60_snd_devdata = { | ||
| 100 | .card = &qi_lb60, | ||
| 101 | .codec_dev = &soc_codec_dev_jz4740_codec, | ||
| 102 | }; | ||
| 103 | |||
| 104 | static struct platform_device *qi_lb60_snd_device; | ||
| 105 | |||
| 106 | static int __init qi_lb60_init(void) | ||
| 107 | { | ||
| 108 | int ret; | ||
| 109 | |||
| 110 | qi_lb60_snd_device = platform_device_alloc("soc-audio", -1); | ||
| 111 | |||
| 112 | if (!qi_lb60_snd_device) | ||
| 113 | return -ENOMEM; | ||
| 114 | |||
| 115 | ret = gpio_request(QI_LB60_SND_GPIO, "SND"); | ||
| 116 | if (ret) { | ||
| 117 | pr_err("qi_lb60 snd: Failed to request SND GPIO(%d): %d\n", | ||
| 118 | QI_LB60_SND_GPIO, ret); | ||
| 119 | goto err_device_put; | ||
| 120 | } | ||
| 121 | |||
| 122 | ret = gpio_request(QI_LB60_AMP_GPIO, "AMP"); | ||
| 123 | if (ret) { | ||
| 124 | pr_err("qi_lb60 snd: Failed to request AMP GPIO(%d): %d\n", | ||
| 125 | QI_LB60_AMP_GPIO, ret); | ||
| 126 | goto err_gpio_free_snd; | ||
| 127 | } | ||
| 128 | |||
| 129 | gpio_direction_output(QI_LB60_SND_GPIO, 0); | ||
| 130 | gpio_direction_output(QI_LB60_AMP_GPIO, 0); | ||
| 131 | |||
| 132 | platform_set_drvdata(qi_lb60_snd_device, &qi_lb60_snd_devdata); | ||
| 133 | qi_lb60_snd_devdata.dev = &qi_lb60_snd_device->dev; | ||
| 134 | |||
| 135 | ret = platform_device_add(qi_lb60_snd_device); | ||
| 136 | if (ret) { | ||
| 137 | pr_err("qi_lb60 snd: Failed to add snd soc device: %d\n", ret); | ||
| 138 | goto err_unset_pdata; | ||
| 139 | } | ||
| 140 | |||
| 141 | return 0; | ||
| 142 | |||
| 143 | err_unset_pdata: | ||
| 144 | platform_set_drvdata(qi_lb60_snd_device, NULL); | ||
| 145 | /*err_gpio_free_amp:*/ | ||
| 146 | gpio_free(QI_LB60_AMP_GPIO); | ||
| 147 | err_gpio_free_snd: | ||
| 148 | gpio_free(QI_LB60_SND_GPIO); | ||
| 149 | err_device_put: | ||
| 150 | platform_device_put(qi_lb60_snd_device); | ||
| 151 | |||
| 152 | return ret; | ||
| 153 | } | ||
| 154 | module_init(qi_lb60_init); | ||
| 155 | |||
| 156 | static void __exit qi_lb60_exit(void) | ||
| 157 | { | ||
| 158 | gpio_free(QI_LB60_AMP_GPIO); | ||
| 159 | gpio_free(QI_LB60_SND_GPIO); | ||
| 160 | platform_device_unregister(qi_lb60_snd_device); | ||
| 161 | } | ||
| 162 | module_exit(qi_lb60_exit); | ||
| 163 | |||
| 164 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); | ||
| 165 | MODULE_DESCRIPTION("ALSA SoC QI LB60 Audio support"); | ||
| 166 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/kirkwood/Kconfig b/sound/soc/kirkwood/Kconfig new file mode 100644 index 000000000000..16ec2a2dba4d --- /dev/null +++ b/sound/soc/kirkwood/Kconfig | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | config SND_KIRKWOOD_SOC | ||
| 2 | tristate "SoC Audio for the Marvell Kirkwood chip" | ||
| 3 | depends on ARCH_KIRKWOOD | ||
| 4 | help | ||
| 5 | Say Y or M if you want to add support for codecs attached to | ||
| 6 | the Kirkwood I2S interface. You will also need to select the | ||
| 7 | audio interfaces to support below. | ||
| 8 | |||
| 9 | config SND_KIRKWOOD_SOC_I2S | ||
| 10 | tristate | ||
| 11 | |||
| 12 | config SND_KIRKWOOD_SOC_OPENRD | ||
| 13 | tristate "SoC Audio support for Kirkwood Openrd Client" | ||
| 14 | depends on SND_KIRKWOOD_SOC && MACH_OPENRD_CLIENT | ||
| 15 | select SND_KIRKWOOD_SOC_I2S | ||
| 16 | select SND_SOC_CS42L51 | ||
| 17 | help | ||
| 18 | Say Y if you want to add support for SoC audio on | ||
| 19 | Openrd Client. | ||
| 20 | |||
diff --git a/sound/soc/kirkwood/Makefile b/sound/soc/kirkwood/Makefile new file mode 100644 index 000000000000..33a16dcab5b5 --- /dev/null +++ b/sound/soc/kirkwood/Makefile | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | snd-soc-kirkwood-objs := kirkwood-dma.o | ||
| 2 | snd-soc-kirkwood-i2s-objs := kirkwood-i2s.o | ||
| 3 | |||
| 4 | obj-$(CONFIG_SND_KIRKWOOD_SOC) += snd-soc-kirkwood.o | ||
| 5 | obj-$(CONFIG_SND_KIRKWOOD_SOC_I2S) += snd-soc-kirkwood-i2s.o | ||
| 6 | |||
| 7 | snd-soc-openrd-objs := kirkwood-openrd.o | ||
| 8 | |||
| 9 | obj-$(CONFIG_SND_KIRKWOOD_SOC_OPENRD) += snd-soc-openrd.o | ||
diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c new file mode 100644 index 000000000000..a30205be3e2b --- /dev/null +++ b/sound/soc/kirkwood/kirkwood-dma.c | |||
| @@ -0,0 +1,383 @@ | |||
| 1 | /* | ||
| 2 | * kirkwood-dma.c | ||
| 3 | * | ||
| 4 | * (c) 2010 Arnaud Patard <apatard@mandriva.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms of the GNU General Public License as published by the | ||
| 8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 9 | * option) any later version. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/init.h> | ||
| 13 | #include <linux/module.h> | ||
| 14 | #include <linux/device.h> | ||
| 15 | #include <linux/io.h> | ||
| 16 | #include <linux/slab.h> | ||
| 17 | #include <linux/interrupt.h> | ||
| 18 | #include <linux/dma-mapping.h> | ||
| 19 | #include <linux/mbus.h> | ||
| 20 | #include <sound/soc.h> | ||
| 21 | #include "kirkwood-dma.h" | ||
| 22 | #include "kirkwood.h" | ||
| 23 | |||
| 24 | #define KIRKWOOD_RATES \ | ||
| 25 | (SNDRV_PCM_RATE_44100 | \ | ||
| 26 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) | ||
| 27 | #define KIRKWOOD_FORMATS \ | ||
| 28 | (SNDRV_PCM_FMTBIT_S16_LE | \ | ||
| 29 | SNDRV_PCM_FMTBIT_S24_LE | \ | ||
| 30 | SNDRV_PCM_FMTBIT_S32_LE) | ||
| 31 | |||
| 32 | struct kirkwood_dma_priv { | ||
| 33 | struct snd_pcm_substream *play_stream; | ||
| 34 | struct snd_pcm_substream *rec_stream; | ||
| 35 | struct kirkwood_dma_data *data; | ||
| 36 | }; | ||
| 37 | |||
| 38 | static struct snd_pcm_hardware kirkwood_dma_snd_hw = { | ||
| 39 | .info = (SNDRV_PCM_INFO_INTERLEAVED | | ||
| 40 | SNDRV_PCM_INFO_MMAP | | ||
| 41 | SNDRV_PCM_INFO_MMAP_VALID | | ||
| 42 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
| 43 | SNDRV_PCM_INFO_PAUSE), | ||
| 44 | .formats = KIRKWOOD_FORMATS, | ||
| 45 | .rates = KIRKWOOD_RATES, | ||
| 46 | .rate_min = 44100, | ||
| 47 | .rate_max = 96000, | ||
| 48 | .channels_min = 1, | ||
| 49 | .channels_max = 2, | ||
| 50 | .buffer_bytes_max = KIRKWOOD_SND_MAX_PERIOD_BYTES * KIRKWOOD_SND_MAX_PERIODS, | ||
| 51 | .period_bytes_min = KIRKWOOD_SND_MIN_PERIOD_BYTES, | ||
| 52 | .period_bytes_max = KIRKWOOD_SND_MAX_PERIOD_BYTES, | ||
| 53 | .periods_min = KIRKWOOD_SND_MIN_PERIODS, | ||
| 54 | .periods_max = KIRKWOOD_SND_MAX_PERIODS, | ||
| 55 | .fifo_size = 0, | ||
| 56 | }; | ||
| 57 | |||
| 58 | static u64 kirkwood_dma_dmamask = 0xFFFFFFFFUL; | ||
| 59 | |||
| 60 | static irqreturn_t kirkwood_dma_irq(int irq, void *dev_id) | ||
| 61 | { | ||
| 62 | struct kirkwood_dma_priv *prdata = dev_id; | ||
| 63 | struct kirkwood_dma_data *priv = prdata->data; | ||
| 64 | unsigned long mask, status, cause; | ||
| 65 | |||
| 66 | mask = readl(priv->io + KIRKWOOD_INT_MASK); | ||
| 67 | status = readl(priv->io + KIRKWOOD_INT_CAUSE) & mask; | ||
| 68 | |||
| 69 | cause = readl(priv->io + KIRKWOOD_ERR_CAUSE); | ||
| 70 | if (unlikely(cause)) { | ||
| 71 | printk(KERN_WARNING "%s: got err interrupt 0x%lx\n", | ||
| 72 | __func__, cause); | ||
| 73 | writel(cause, priv->io + KIRKWOOD_ERR_CAUSE); | ||
| 74 | return IRQ_HANDLED; | ||
| 75 | } | ||
| 76 | |||
| 77 | /* we've enabled only bytes interrupts ... */ | ||
| 78 | if (status & ~(KIRKWOOD_INT_CAUSE_PLAY_BYTES | \ | ||
| 79 | KIRKWOOD_INT_CAUSE_REC_BYTES)) { | ||
| 80 | printk(KERN_WARNING "%s: unexpected interrupt %lx\n", | ||
| 81 | __func__, status); | ||
| 82 | return IRQ_NONE; | ||
| 83 | } | ||
| 84 | |||
| 85 | /* ack int */ | ||
| 86 | writel(status, priv->io + KIRKWOOD_INT_CAUSE); | ||
| 87 | |||
| 88 | if (status & KIRKWOOD_INT_CAUSE_PLAY_BYTES) | ||
| 89 | snd_pcm_period_elapsed(prdata->play_stream); | ||
| 90 | |||
| 91 | if (status & KIRKWOOD_INT_CAUSE_REC_BYTES) | ||
| 92 | snd_pcm_period_elapsed(prdata->rec_stream); | ||
| 93 | |||
| 94 | return IRQ_HANDLED; | ||
| 95 | } | ||
| 96 | |||
| 97 | static void kirkwood_dma_conf_mbus_windows(void __iomem *base, int win, | ||
| 98 | unsigned long dma, | ||
| 99 | struct mbus_dram_target_info *dram) | ||
| 100 | { | ||
| 101 | int i; | ||
| 102 | |||
| 103 | /* First disable and clear windows */ | ||
| 104 | writel(0, base + KIRKWOOD_AUDIO_WIN_CTRL_REG(win)); | ||
| 105 | writel(0, base + KIRKWOOD_AUDIO_WIN_BASE_REG(win)); | ||
| 106 | |||
| 107 | /* try to find matching cs for current dma address */ | ||
| 108 | for (i = 0; i < dram->num_cs; i++) { | ||
| 109 | struct mbus_dram_window *cs = dram->cs + i; | ||
| 110 | if ((cs->base & 0xffff0000) < (dma & 0xffff0000)) { | ||
| 111 | writel(cs->base & 0xffff0000, | ||
| 112 | base + KIRKWOOD_AUDIO_WIN_BASE_REG(win)); | ||
| 113 | writel(((cs->size - 1) & 0xffff0000) | | ||
| 114 | (cs->mbus_attr << 8) | | ||
| 115 | (dram->mbus_dram_target_id << 4) | 1, | ||
| 116 | base + KIRKWOOD_AUDIO_WIN_CTRL_REG(win)); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | static int kirkwood_dma_open(struct snd_pcm_substream *substream) | ||
| 122 | { | ||
| 123 | int err; | ||
| 124 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 125 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
| 126 | struct snd_soc_dai *cpu_dai = soc_runtime->dai->cpu_dai; | ||
| 127 | struct kirkwood_dma_data *priv; | ||
| 128 | struct kirkwood_dma_priv *prdata = cpu_dai->private_data; | ||
| 129 | unsigned long addr; | ||
| 130 | |||
| 131 | priv = snd_soc_dai_get_dma_data(cpu_dai, substream); | ||
| 132 | snd_soc_set_runtime_hwparams(substream, &kirkwood_dma_snd_hw); | ||
| 133 | |||
| 134 | /* Ensure that all constraints linked to dma burst are fullfilled */ | ||
| 135 | err = snd_pcm_hw_constraint_minmax(runtime, | ||
| 136 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, | ||
| 137 | priv->burst * 2, | ||
| 138 | KIRKWOOD_AUDIO_BUF_MAX-1); | ||
| 139 | if (err < 0) | ||
| 140 | return err; | ||
| 141 | |||
| 142 | err = snd_pcm_hw_constraint_step(runtime, 0, | ||
| 143 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, | ||
| 144 | priv->burst); | ||
| 145 | if (err < 0) | ||
| 146 | return err; | ||
| 147 | |||
| 148 | err = snd_pcm_hw_constraint_step(substream->runtime, 0, | ||
| 149 | SNDRV_PCM_HW_PARAM_PERIOD_BYTES, | ||
| 150 | priv->burst); | ||
| 151 | if (err < 0) | ||
| 152 | return err; | ||
| 153 | |||
| 154 | if (soc_runtime->dai->cpu_dai->private_data == NULL) { | ||
| 155 | prdata = kzalloc(sizeof(struct kirkwood_dma_priv), GFP_KERNEL); | ||
| 156 | if (prdata == NULL) | ||
| 157 | return -ENOMEM; | ||
| 158 | |||
| 159 | prdata->data = priv; | ||
| 160 | |||
| 161 | err = request_irq(priv->irq, kirkwood_dma_irq, IRQF_SHARED, | ||
| 162 | "kirkwood-i2s", prdata); | ||
| 163 | if (err) { | ||
| 164 | kfree(prdata); | ||
| 165 | return -EBUSY; | ||
| 166 | } | ||
| 167 | |||
| 168 | soc_runtime->dai->cpu_dai->private_data = prdata; | ||
| 169 | |||
| 170 | /* | ||
| 171 | * Enable Error interrupts. We're only ack'ing them but | ||
| 172 | * it's usefull for diagnostics | ||
| 173 | */ | ||
| 174 | writel((unsigned long)-1, priv->io + KIRKWOOD_ERR_MASK); | ||
| 175 | } | ||
| 176 | |||
| 177 | addr = virt_to_phys(substream->dma_buffer.area); | ||
| 178 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
| 179 | prdata->play_stream = substream; | ||
| 180 | kirkwood_dma_conf_mbus_windows(priv->io, | ||
| 181 | KIRKWOOD_PLAYBACK_WIN, addr, priv->dram); | ||
| 182 | } else { | ||
| 183 | prdata->rec_stream = substream; | ||
| 184 | kirkwood_dma_conf_mbus_windows(priv->io, | ||
| 185 | KIRKWOOD_RECORD_WIN, addr, priv->dram); | ||
| 186 | } | ||
| 187 | |||
| 188 | return 0; | ||
| 189 | } | ||
| 190 | |||
| 191 | static int kirkwood_dma_close(struct snd_pcm_substream *substream) | ||
| 192 | { | ||
| 193 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
| 194 | struct snd_soc_dai *cpu_dai = soc_runtime->dai->cpu_dai; | ||
| 195 | struct kirkwood_dma_priv *prdata = cpu_dai->private_data; | ||
| 196 | struct kirkwood_dma_data *priv; | ||
| 197 | |||
| 198 | priv = snd_soc_dai_get_dma_data(cpu_dai, substream); | ||
| 199 | |||
| 200 | if (!prdata || !priv) | ||
| 201 | return 0; | ||
| 202 | |||
| 203 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
| 204 | prdata->play_stream = NULL; | ||
| 205 | else | ||
| 206 | prdata->rec_stream = NULL; | ||
| 207 | |||
| 208 | if (!prdata->play_stream && !prdata->rec_stream) { | ||
| 209 | writel(0, priv->io + KIRKWOOD_ERR_MASK); | ||
| 210 | free_irq(priv->irq, prdata); | ||
| 211 | kfree(prdata); | ||
| 212 | soc_runtime->dai->cpu_dai->private_data = NULL; | ||
| 213 | } | ||
| 214 | |||
| 215 | return 0; | ||
| 216 | } | ||
| 217 | |||
| 218 | static int kirkwood_dma_hw_params(struct snd_pcm_substream *substream, | ||
| 219 | struct snd_pcm_hw_params *params) | ||
| 220 | { | ||
| 221 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 222 | |||
| 223 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | ||
| 224 | runtime->dma_bytes = params_buffer_bytes(params); | ||
| 225 | |||
| 226 | return 0; | ||
| 227 | } | ||
| 228 | |||
| 229 | static int kirkwood_dma_hw_free(struct snd_pcm_substream *substream) | ||
| 230 | { | ||
| 231 | snd_pcm_set_runtime_buffer(substream, NULL); | ||
| 232 | return 0; | ||
| 233 | } | ||
| 234 | |||
| 235 | static int kirkwood_dma_prepare(struct snd_pcm_substream *substream) | ||
| 236 | { | ||
| 237 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 238 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
| 239 | struct snd_soc_dai *cpu_dai = soc_runtime->dai->cpu_dai; | ||
| 240 | struct kirkwood_dma_data *priv; | ||
| 241 | unsigned long size, count; | ||
| 242 | |||
| 243 | priv = snd_soc_dai_get_dma_data(cpu_dai, substream); | ||
| 244 | |||
| 245 | /* compute buffer size in term of "words" as requested in specs */ | ||
| 246 | size = frames_to_bytes(runtime, runtime->buffer_size); | ||
| 247 | size = (size>>2)-1; | ||
| 248 | count = snd_pcm_lib_period_bytes(substream); | ||
| 249 | |||
| 250 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
| 251 | writel(count, priv->io + KIRKWOOD_PLAY_BYTE_INT_COUNT); | ||
| 252 | writel(runtime->dma_addr, priv->io + KIRKWOOD_PLAY_BUF_ADDR); | ||
| 253 | writel(size, priv->io + KIRKWOOD_PLAY_BUF_SIZE); | ||
| 254 | } else { | ||
| 255 | writel(count, priv->io + KIRKWOOD_REC_BYTE_INT_COUNT); | ||
| 256 | writel(runtime->dma_addr, priv->io + KIRKWOOD_REC_BUF_ADDR); | ||
| 257 | writel(size, priv->io + KIRKWOOD_REC_BUF_SIZE); | ||
| 258 | } | ||
| 259 | |||
| 260 | |||
| 261 | return 0; | ||
| 262 | } | ||
| 263 | |||
| 264 | static snd_pcm_uframes_t kirkwood_dma_pointer(struct snd_pcm_substream | ||
| 265 | *substream) | ||
| 266 | { | ||
| 267 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
| 268 | struct snd_soc_dai *cpu_dai = soc_runtime->dai->cpu_dai; | ||
| 269 | struct kirkwood_dma_data *priv; | ||
| 270 | snd_pcm_uframes_t count; | ||
| 271 | |||
| 272 | priv = snd_soc_dai_get_dma_data(cpu_dai, substream); | ||
| 273 | |||
| 274 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
| 275 | count = bytes_to_frames(substream->runtime, | ||
| 276 | readl(priv->io + KIRKWOOD_PLAY_BYTE_COUNT)); | ||
| 277 | else | ||
| 278 | count = bytes_to_frames(substream->runtime, | ||
| 279 | readl(priv->io + KIRKWOOD_REC_BYTE_COUNT)); | ||
| 280 | |||
| 281 | return count; | ||
| 282 | } | ||
| 283 | |||
| 284 | struct snd_pcm_ops kirkwood_dma_ops = { | ||
| 285 | .open = kirkwood_dma_open, | ||
| 286 | .close = kirkwood_dma_close, | ||
| 287 | .ioctl = snd_pcm_lib_ioctl, | ||
| 288 | .hw_params = kirkwood_dma_hw_params, | ||
| 289 | .hw_free = kirkwood_dma_hw_free, | ||
| 290 | .prepare = kirkwood_dma_prepare, | ||
| 291 | .pointer = kirkwood_dma_pointer, | ||
| 292 | }; | ||
| 293 | |||
| 294 | static int kirkwood_dma_preallocate_dma_buffer(struct snd_pcm *pcm, | ||
| 295 | int stream) | ||
| 296 | { | ||
| 297 | struct snd_pcm_substream *substream = pcm->streams[stream].substream; | ||
| 298 | struct snd_dma_buffer *buf = &substream->dma_buffer; | ||
| 299 | size_t size = kirkwood_dma_snd_hw.buffer_bytes_max; | ||
| 300 | |||
| 301 | buf->dev.type = SNDRV_DMA_TYPE_DEV; | ||
| 302 | buf->dev.dev = pcm->card->dev; | ||
| 303 | buf->area = dma_alloc_coherent(pcm->card->dev, size, | ||
| 304 | &buf->addr, GFP_KERNEL); | ||
| 305 | if (!buf->area) | ||
| 306 | return -ENOMEM; | ||
| 307 | buf->bytes = size; | ||
| 308 | buf->private_data = NULL; | ||
| 309 | |||
| 310 | return 0; | ||
| 311 | } | ||
| 312 | |||
| 313 | static int kirkwood_dma_new(struct snd_card *card, | ||
| 314 | struct snd_soc_dai *dai, struct snd_pcm *pcm) | ||
| 315 | { | ||
| 316 | int ret; | ||
| 317 | |||
| 318 | if (!card->dev->dma_mask) | ||
| 319 | card->dev->dma_mask = &kirkwood_dma_dmamask; | ||
| 320 | if (!card->dev->coherent_dma_mask) | ||
| 321 | card->dev->coherent_dma_mask = 0xffffffff; | ||
| 322 | |||
| 323 | if (dai->playback.channels_min) { | ||
| 324 | ret = kirkwood_dma_preallocate_dma_buffer(pcm, | ||
| 325 | SNDRV_PCM_STREAM_PLAYBACK); | ||
| 326 | if (ret) | ||
| 327 | return ret; | ||
| 328 | } | ||
| 329 | |||
| 330 | if (dai->capture.channels_min) { | ||
| 331 | ret = kirkwood_dma_preallocate_dma_buffer(pcm, | ||
| 332 | SNDRV_PCM_STREAM_CAPTURE); | ||
| 333 | if (ret) | ||
| 334 | return ret; | ||
| 335 | } | ||
| 336 | |||
| 337 | return 0; | ||
| 338 | } | ||
| 339 | |||
| 340 | static void kirkwood_dma_free_dma_buffers(struct snd_pcm *pcm) | ||
| 341 | { | ||
| 342 | struct snd_pcm_substream *substream; | ||
| 343 | struct snd_dma_buffer *buf; | ||
| 344 | int stream; | ||
| 345 | |||
| 346 | for (stream = 0; stream < 2; stream++) { | ||
| 347 | substream = pcm->streams[stream].substream; | ||
| 348 | if (!substream) | ||
| 349 | continue; | ||
| 350 | buf = &substream->dma_buffer; | ||
| 351 | if (!buf->area) | ||
| 352 | continue; | ||
| 353 | |||
| 354 | dma_free_coherent(pcm->card->dev, buf->bytes, | ||
| 355 | buf->area, buf->addr); | ||
| 356 | buf->area = NULL; | ||
| 357 | } | ||
| 358 | } | ||
| 359 | |||
| 360 | struct snd_soc_platform kirkwood_soc_platform = { | ||
| 361 | .name = "kirkwood-dma", | ||
| 362 | .pcm_ops = &kirkwood_dma_ops, | ||
| 363 | .pcm_new = kirkwood_dma_new, | ||
| 364 | .pcm_free = kirkwood_dma_free_dma_buffers, | ||
| 365 | }; | ||
| 366 | EXPORT_SYMBOL_GPL(kirkwood_soc_platform); | ||
| 367 | |||
| 368 | static int __init kirkwood_soc_platform_init(void) | ||
| 369 | { | ||
| 370 | return snd_soc_register_platform(&kirkwood_soc_platform); | ||
| 371 | } | ||
| 372 | module_init(kirkwood_soc_platform_init); | ||
| 373 | |||
| 374 | static void __exit kirkwood_soc_platform_exit(void) | ||
| 375 | { | ||
| 376 | snd_soc_unregister_platform(&kirkwood_soc_platform); | ||
| 377 | } | ||
| 378 | module_exit(kirkwood_soc_platform_exit); | ||
| 379 | |||
| 380 | MODULE_AUTHOR("Arnaud Patard <apatard@mandriva.com>"); | ||
| 381 | MODULE_DESCRIPTION("Marvell Kirkwood Audio DMA module"); | ||
| 382 | MODULE_LICENSE("GPL"); | ||
| 383 | |||
diff --git a/sound/soc/kirkwood/kirkwood-dma.h b/sound/soc/kirkwood/kirkwood-dma.h new file mode 100644 index 000000000000..ba4454cd34f1 --- /dev/null +++ b/sound/soc/kirkwood/kirkwood-dma.h | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | /* | ||
| 2 | * kirkwood-dma.h | ||
| 3 | * | ||
| 4 | * (c) 2010 Arnaud Patard <apatard@mandriva.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms of the GNU General Public License as published by the | ||
| 8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 9 | * option) any later version. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #ifndef _KIRKWOOD_DMA_H | ||
| 13 | #define _KIRKWOOD_DMA_H | ||
| 14 | |||
| 15 | extern struct snd_soc_platform kirkwood_soc_platform; | ||
| 16 | |||
| 17 | #endif | ||
diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c new file mode 100644 index 000000000000..981ffc2a13c8 --- /dev/null +++ b/sound/soc/kirkwood/kirkwood-i2s.c | |||
| @@ -0,0 +1,495 @@ | |||
| 1 | /* | ||
| 2 | * kirkwood-i2s.c | ||
| 3 | * | ||
| 4 | * (c) 2010 Arnaud Patard <apatard@mandriva.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms of the GNU General Public License as published by the | ||
| 8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 9 | * option) any later version. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/init.h> | ||
| 13 | #include <linux/module.h> | ||
| 14 | #include <linux/platform_device.h> | ||
| 15 | #include <linux/io.h> | ||
| 16 | #include <linux/slab.h> | ||
| 17 | #include <linux/mbus.h> | ||
| 18 | #include <linux/delay.h> | ||
| 19 | #include <sound/pcm.h> | ||
| 20 | #include <sound/pcm_params.h> | ||
| 21 | #include <sound/soc.h> | ||
| 22 | #include <plat/audio.h> | ||
| 23 | #include "kirkwood-i2s.h" | ||
| 24 | #include "kirkwood.h" | ||
| 25 | |||
| 26 | #define DRV_NAME "kirkwood-i2s" | ||
| 27 | |||
| 28 | #define KIRKWOOD_I2S_RATES \ | ||
| 29 | (SNDRV_PCM_RATE_44100 | \ | ||
| 30 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) | ||
| 31 | #define KIRKWOOD_I2S_FORMATS \ | ||
| 32 | (SNDRV_PCM_FMTBIT_S16_LE | \ | ||
| 33 | SNDRV_PCM_FMTBIT_S24_LE | \ | ||
| 34 | SNDRV_PCM_FMTBIT_S32_LE) | ||
| 35 | |||
| 36 | |||
| 37 | struct snd_soc_dai kirkwood_i2s_dai; | ||
| 38 | static struct kirkwood_dma_data *priv; | ||
| 39 | |||
| 40 | static int kirkwood_i2s_set_fmt(struct snd_soc_dai *cpu_dai, | ||
| 41 | unsigned int fmt) | ||
| 42 | { | ||
| 43 | unsigned long mask; | ||
| 44 | unsigned long value; | ||
| 45 | |||
| 46 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
| 47 | case SND_SOC_DAIFMT_RIGHT_J: | ||
| 48 | mask = KIRKWOOD_I2S_CTL_RJ; | ||
| 49 | break; | ||
| 50 | case SND_SOC_DAIFMT_LEFT_J: | ||
| 51 | mask = KIRKWOOD_I2S_CTL_LJ; | ||
| 52 | break; | ||
| 53 | case SND_SOC_DAIFMT_I2S: | ||
| 54 | mask = KIRKWOOD_I2S_CTL_I2S; | ||
| 55 | break; | ||
| 56 | default: | ||
| 57 | return -EINVAL; | ||
| 58 | } | ||
| 59 | |||
| 60 | /* | ||
| 61 | * Set same format for playback and record | ||
| 62 | * This avoids some troubles. | ||
| 63 | */ | ||
| 64 | value = readl(priv->io+KIRKWOOD_I2S_PLAYCTL); | ||
| 65 | value &= ~KIRKWOOD_I2S_CTL_JUST_MASK; | ||
| 66 | value |= mask; | ||
| 67 | writel(value, priv->io+KIRKWOOD_I2S_PLAYCTL); | ||
| 68 | |||
| 69 | value = readl(priv->io+KIRKWOOD_I2S_RECCTL); | ||
| 70 | value &= ~KIRKWOOD_I2S_CTL_JUST_MASK; | ||
| 71 | value |= mask; | ||
| 72 | writel(value, priv->io+KIRKWOOD_I2S_RECCTL); | ||
| 73 | |||
| 74 | return 0; | ||
| 75 | } | ||
| 76 | |||
| 77 | static inline void kirkwood_set_dco(void __iomem *io, unsigned long rate) | ||
| 78 | { | ||
| 79 | unsigned long value; | ||
| 80 | |||
| 81 | value = KIRKWOOD_DCO_CTL_OFFSET_0; | ||
| 82 | switch (rate) { | ||
| 83 | default: | ||
| 84 | case 44100: | ||
| 85 | value |= KIRKWOOD_DCO_CTL_FREQ_11; | ||
| 86 | break; | ||
| 87 | case 48000: | ||
| 88 | value |= KIRKWOOD_DCO_CTL_FREQ_12; | ||
| 89 | break; | ||
| 90 | case 96000: | ||
| 91 | value |= KIRKWOOD_DCO_CTL_FREQ_24; | ||
| 92 | break; | ||
| 93 | } | ||
| 94 | writel(value, io + KIRKWOOD_DCO_CTL); | ||
| 95 | |||
| 96 | /* wait for dco locked */ | ||
| 97 | do { | ||
| 98 | cpu_relax(); | ||
| 99 | value = readl(io + KIRKWOOD_DCO_SPCR_STATUS); | ||
| 100 | value &= KIRKWOOD_DCO_SPCR_STATUS; | ||
| 101 | } while (value == 0); | ||
| 102 | } | ||
| 103 | |||
| 104 | static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream, | ||
| 105 | struct snd_pcm_hw_params *params, | ||
| 106 | struct snd_soc_dai *dai) | ||
| 107 | { | ||
| 108 | unsigned int i2s_reg, reg; | ||
| 109 | unsigned long i2s_value, value; | ||
| 110 | |||
| 111 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
| 112 | i2s_reg = KIRKWOOD_I2S_PLAYCTL; | ||
| 113 | reg = KIRKWOOD_PLAYCTL; | ||
| 114 | } else { | ||
| 115 | i2s_reg = KIRKWOOD_I2S_RECCTL; | ||
| 116 | reg = KIRKWOOD_RECCTL; | ||
| 117 | } | ||
| 118 | |||
| 119 | /* set dco conf */ | ||
| 120 | kirkwood_set_dco(priv->io, params_rate(params)); | ||
| 121 | |||
| 122 | i2s_value = readl(priv->io+i2s_reg); | ||
| 123 | i2s_value &= ~KIRKWOOD_I2S_CTL_SIZE_MASK; | ||
| 124 | |||
| 125 | value = readl(priv->io+reg); | ||
| 126 | value &= ~KIRKWOOD_PLAYCTL_SIZE_MASK; | ||
| 127 | |||
| 128 | /* | ||
| 129 | * Size settings in play/rec i2s control regs and play/rec control | ||
| 130 | * regs must be the same. | ||
| 131 | */ | ||
| 132 | switch (params_format(params)) { | ||
| 133 | case SNDRV_PCM_FORMAT_S16_LE: | ||
| 134 | i2s_value |= KIRKWOOD_I2S_CTL_SIZE_16; | ||
| 135 | value |= KIRKWOOD_PLAYCTL_SIZE_16_C; | ||
| 136 | break; | ||
| 137 | /* | ||
| 138 | * doesn't work... S20_3LE != kirkwood 20bit format ? | ||
| 139 | * | ||
| 140 | case SNDRV_PCM_FORMAT_S20_3LE: | ||
| 141 | i2s_value |= KIRKWOOD_I2S_CTL_SIZE_20; | ||
| 142 | value |= KIRKWOOD_PLAYCTL_SIZE_20; | ||
| 143 | break; | ||
| 144 | */ | ||
| 145 | case SNDRV_PCM_FORMAT_S24_LE: | ||
| 146 | i2s_value |= KIRKWOOD_I2S_CTL_SIZE_24; | ||
| 147 | value |= KIRKWOOD_PLAYCTL_SIZE_24; | ||
| 148 | break; | ||
| 149 | case SNDRV_PCM_FORMAT_S32_LE: | ||
| 150 | i2s_value |= KIRKWOOD_I2S_CTL_SIZE_32; | ||
| 151 | value |= KIRKWOOD_PLAYCTL_SIZE_32; | ||
| 152 | break; | ||
| 153 | default: | ||
| 154 | return -EINVAL; | ||
| 155 | } | ||
| 156 | |||
| 157 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
| 158 | value &= ~KIRKWOOD_PLAYCTL_MONO_MASK; | ||
| 159 | if (params_channels(params) == 1) | ||
| 160 | value |= KIRKWOOD_PLAYCTL_MONO_BOTH; | ||
| 161 | else | ||
| 162 | value |= KIRKWOOD_PLAYCTL_MONO_OFF; | ||
| 163 | } | ||
| 164 | |||
| 165 | writel(i2s_value, priv->io+i2s_reg); | ||
| 166 | writel(value, priv->io+reg); | ||
| 167 | |||
| 168 | return 0; | ||
| 169 | } | ||
| 170 | |||
| 171 | static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream, | ||
| 172 | int cmd, struct snd_soc_dai *dai) | ||
| 173 | { | ||
| 174 | unsigned long value; | ||
| 175 | |||
| 176 | /* | ||
| 177 | * specs says KIRKWOOD_PLAYCTL must be read 2 times before | ||
| 178 | * changing it. So read 1 time here and 1 later. | ||
| 179 | */ | ||
| 180 | value = readl(priv->io + KIRKWOOD_PLAYCTL); | ||
| 181 | |||
| 182 | switch (cmd) { | ||
| 183 | case SNDRV_PCM_TRIGGER_START: | ||
| 184 | /* stop audio, enable interrupts */ | ||
| 185 | value = readl(priv->io + KIRKWOOD_PLAYCTL); | ||
| 186 | value |= KIRKWOOD_PLAYCTL_PAUSE; | ||
| 187 | writel(value, priv->io + KIRKWOOD_PLAYCTL); | ||
| 188 | |||
| 189 | value = readl(priv->io + KIRKWOOD_INT_MASK); | ||
| 190 | value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES; | ||
| 191 | writel(value, priv->io + KIRKWOOD_INT_MASK); | ||
| 192 | |||
| 193 | /* configure audio & enable i2s playback */ | ||
| 194 | value = readl(priv->io + KIRKWOOD_PLAYCTL); | ||
| 195 | value &= ~KIRKWOOD_PLAYCTL_BURST_MASK; | ||
| 196 | value &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE | ||
| 197 | | KIRKWOOD_PLAYCTL_SPDIF_EN); | ||
| 198 | |||
| 199 | if (priv->burst == 32) | ||
| 200 | value |= KIRKWOOD_PLAYCTL_BURST_32; | ||
| 201 | else | ||
| 202 | value |= KIRKWOOD_PLAYCTL_BURST_128; | ||
| 203 | value |= KIRKWOOD_PLAYCTL_I2S_EN; | ||
| 204 | writel(value, priv->io + KIRKWOOD_PLAYCTL); | ||
| 205 | break; | ||
| 206 | |||
| 207 | case SNDRV_PCM_TRIGGER_STOP: | ||
| 208 | /* stop audio, disable interrupts */ | ||
| 209 | value = readl(priv->io + KIRKWOOD_PLAYCTL); | ||
| 210 | value |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE; | ||
| 211 | writel(value, priv->io + KIRKWOOD_PLAYCTL); | ||
| 212 | |||
| 213 | value = readl(priv->io + KIRKWOOD_INT_MASK); | ||
| 214 | value &= ~KIRKWOOD_INT_CAUSE_PLAY_BYTES; | ||
| 215 | writel(value, priv->io + KIRKWOOD_INT_MASK); | ||
| 216 | |||
| 217 | /* disable all playbacks */ | ||
| 218 | value = readl(priv->io + KIRKWOOD_PLAYCTL); | ||
| 219 | value &= ~(KIRKWOOD_PLAYCTL_I2S_EN | KIRKWOOD_PLAYCTL_SPDIF_EN); | ||
| 220 | writel(value, priv->io + KIRKWOOD_PLAYCTL); | ||
| 221 | break; | ||
| 222 | |||
| 223 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
| 224 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
| 225 | value = readl(priv->io + KIRKWOOD_PLAYCTL); | ||
| 226 | value |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE; | ||
| 227 | writel(value, priv->io + KIRKWOOD_PLAYCTL); | ||
| 228 | break; | ||
| 229 | |||
| 230 | case SNDRV_PCM_TRIGGER_RESUME: | ||
| 231 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
| 232 | value = readl(priv->io + KIRKWOOD_PLAYCTL); | ||
| 233 | value &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE); | ||
| 234 | writel(value, priv->io + KIRKWOOD_PLAYCTL); | ||
| 235 | break; | ||
| 236 | |||
| 237 | default: | ||
| 238 | return -EINVAL; | ||
| 239 | } | ||
| 240 | |||
| 241 | return 0; | ||
| 242 | } | ||
| 243 | |||
| 244 | static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream, | ||
| 245 | int cmd, struct snd_soc_dai *dai) | ||
| 246 | { | ||
| 247 | unsigned long value; | ||
| 248 | |||
| 249 | value = readl(priv->io + KIRKWOOD_RECCTL); | ||
| 250 | |||
| 251 | switch (cmd) { | ||
| 252 | case SNDRV_PCM_TRIGGER_START: | ||
| 253 | /* stop audio, enable interrupts */ | ||
| 254 | value = readl(priv->io + KIRKWOOD_RECCTL); | ||
| 255 | value |= KIRKWOOD_RECCTL_PAUSE; | ||
| 256 | writel(value, priv->io + KIRKWOOD_RECCTL); | ||
| 257 | |||
| 258 | value = readl(priv->io + KIRKWOOD_INT_MASK); | ||
| 259 | value |= KIRKWOOD_INT_CAUSE_REC_BYTES; | ||
| 260 | writel(value, priv->io + KIRKWOOD_INT_MASK); | ||
| 261 | |||
| 262 | /* configure audio & enable i2s record */ | ||
| 263 | value = readl(priv->io + KIRKWOOD_RECCTL); | ||
| 264 | value &= ~KIRKWOOD_RECCTL_BURST_MASK; | ||
| 265 | value &= ~KIRKWOOD_RECCTL_MONO; | ||
| 266 | value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE | ||
| 267 | | KIRKWOOD_RECCTL_SPDIF_EN); | ||
| 268 | |||
| 269 | if (priv->burst == 32) | ||
| 270 | value |= KIRKWOOD_RECCTL_BURST_32; | ||
| 271 | else | ||
| 272 | value |= KIRKWOOD_RECCTL_BURST_128; | ||
| 273 | value |= KIRKWOOD_RECCTL_I2S_EN; | ||
| 274 | |||
| 275 | writel(value, priv->io + KIRKWOOD_RECCTL); | ||
| 276 | break; | ||
| 277 | |||
| 278 | case SNDRV_PCM_TRIGGER_STOP: | ||
| 279 | /* stop audio, disable interrupts */ | ||
| 280 | value = readl(priv->io + KIRKWOOD_RECCTL); | ||
| 281 | value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE; | ||
| 282 | writel(value, priv->io + KIRKWOOD_RECCTL); | ||
| 283 | |||
| 284 | value = readl(priv->io + KIRKWOOD_INT_MASK); | ||
| 285 | value &= ~KIRKWOOD_INT_CAUSE_REC_BYTES; | ||
| 286 | writel(value, priv->io + KIRKWOOD_INT_MASK); | ||
| 287 | |||
| 288 | /* disable all records */ | ||
| 289 | value = readl(priv->io + KIRKWOOD_RECCTL); | ||
| 290 | value &= ~(KIRKWOOD_RECCTL_I2S_EN | KIRKWOOD_RECCTL_SPDIF_EN); | ||
| 291 | writel(value, priv->io + KIRKWOOD_RECCTL); | ||
| 292 | break; | ||
| 293 | |||
| 294 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
| 295 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
| 296 | value = readl(priv->io + KIRKWOOD_RECCTL); | ||
| 297 | value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE; | ||
| 298 | writel(value, priv->io + KIRKWOOD_RECCTL); | ||
| 299 | break; | ||
| 300 | |||
| 301 | case SNDRV_PCM_TRIGGER_RESUME: | ||
| 302 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
| 303 | value = readl(priv->io + KIRKWOOD_RECCTL); | ||
| 304 | value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE); | ||
| 305 | writel(value, priv->io + KIRKWOOD_RECCTL); | ||
| 306 | break; | ||
| 307 | |||
| 308 | default: | ||
| 309 | return -EINVAL; | ||
| 310 | } | ||
| 311 | |||
| 312 | return 0; | ||
| 313 | } | ||
| 314 | |||
| 315 | static int kirkwood_i2s_trigger(struct snd_pcm_substream *substream, int cmd, | ||
| 316 | struct snd_soc_dai *dai) | ||
| 317 | { | ||
| 318 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
| 319 | return kirkwood_i2s_play_trigger(substream, cmd, dai); | ||
| 320 | else | ||
| 321 | return kirkwood_i2s_rec_trigger(substream, cmd, dai); | ||
| 322 | |||
| 323 | return 0; | ||
| 324 | } | ||
| 325 | |||
| 326 | static int kirkwood_i2s_probe(struct platform_device *pdev, | ||
| 327 | struct snd_soc_dai *dai) | ||
| 328 | { | ||
| 329 | unsigned long value; | ||
| 330 | unsigned int reg_data; | ||
| 331 | |||
| 332 | /* put system in a "safe" state : */ | ||
| 333 | /* disable audio interrupts */ | ||
| 334 | writel(0xffffffff, priv->io + KIRKWOOD_INT_CAUSE); | ||
| 335 | writel(0, priv->io + KIRKWOOD_INT_MASK); | ||
| 336 | |||
| 337 | reg_data = readl(priv->io + 0x1200); | ||
| 338 | reg_data &= (~(0x333FF8)); | ||
| 339 | reg_data |= 0x111D18; | ||
| 340 | writel(reg_data, priv->io + 0x1200); | ||
| 341 | |||
| 342 | msleep(500); | ||
| 343 | |||
| 344 | reg_data = readl(priv->io + 0x1200); | ||
| 345 | reg_data &= (~(0x333FF8)); | ||
| 346 | reg_data |= 0x111D18; | ||
| 347 | writel(reg_data, priv->io + 0x1200); | ||
| 348 | |||
| 349 | /* disable playback/record */ | ||
| 350 | value = readl(priv->io + KIRKWOOD_PLAYCTL); | ||
| 351 | value &= ~(KIRKWOOD_PLAYCTL_I2S_EN|KIRKWOOD_PLAYCTL_SPDIF_EN); | ||
| 352 | writel(value, priv->io + KIRKWOOD_PLAYCTL); | ||
| 353 | |||
| 354 | value = readl(priv->io + KIRKWOOD_RECCTL); | ||
| 355 | value &= ~(KIRKWOOD_RECCTL_I2S_EN | KIRKWOOD_RECCTL_SPDIF_EN); | ||
| 356 | writel(value, priv->io + KIRKWOOD_RECCTL); | ||
| 357 | |||
| 358 | return 0; | ||
| 359 | |||
| 360 | } | ||
| 361 | |||
| 362 | static void kirkwood_i2s_remove(struct platform_device *pdev, | ||
| 363 | struct snd_soc_dai *dai) | ||
| 364 | { | ||
| 365 | } | ||
| 366 | |||
| 367 | static struct snd_soc_dai_ops kirkwood_i2s_dai_ops = { | ||
| 368 | .trigger = kirkwood_i2s_trigger, | ||
| 369 | .hw_params = kirkwood_i2s_hw_params, | ||
| 370 | .set_fmt = kirkwood_i2s_set_fmt, | ||
| 371 | }; | ||
| 372 | |||
| 373 | |||
| 374 | struct snd_soc_dai kirkwood_i2s_dai = { | ||
| 375 | .name = DRV_NAME, | ||
| 376 | .id = 0, | ||
| 377 | .probe = kirkwood_i2s_probe, | ||
| 378 | .remove = kirkwood_i2s_remove, | ||
| 379 | .playback = { | ||
| 380 | .channels_min = 1, | ||
| 381 | .channels_max = 2, | ||
| 382 | .rates = KIRKWOOD_I2S_RATES, | ||
| 383 | .formats = KIRKWOOD_I2S_FORMATS,}, | ||
| 384 | .capture = { | ||
| 385 | .channels_min = 1, | ||
| 386 | .channels_max = 2, | ||
| 387 | .rates = KIRKWOOD_I2S_RATES, | ||
| 388 | .formats = KIRKWOOD_I2S_FORMATS,}, | ||
| 389 | .ops = &kirkwood_i2s_dai_ops, | ||
| 390 | }; | ||
| 391 | EXPORT_SYMBOL_GPL(kirkwood_i2s_dai); | ||
| 392 | |||
| 393 | static __devinit int kirkwood_i2s_dev_probe(struct platform_device *pdev) | ||
| 394 | { | ||
| 395 | struct resource *mem; | ||
| 396 | struct kirkwood_asoc_platform_data *data = | ||
| 397 | pdev->dev.platform_data; | ||
| 398 | int err; | ||
| 399 | |||
| 400 | priv = kzalloc(sizeof(struct kirkwood_dma_data), GFP_KERNEL); | ||
| 401 | if (!priv) { | ||
| 402 | dev_err(&pdev->dev, "allocation failed\n"); | ||
| 403 | err = -ENOMEM; | ||
| 404 | goto error; | ||
| 405 | } | ||
| 406 | |||
| 407 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 408 | if (!mem) { | ||
| 409 | dev_err(&pdev->dev, "platform_get_resource failed\n"); | ||
| 410 | err = -ENXIO; | ||
| 411 | goto err_alloc; | ||
| 412 | } | ||
| 413 | |||
| 414 | priv->mem = request_mem_region(mem->start, SZ_16K, DRV_NAME); | ||
| 415 | if (!priv->mem) { | ||
| 416 | dev_err(&pdev->dev, "request_mem_region failed\n"); | ||
| 417 | err = -EBUSY; | ||
| 418 | goto error; | ||
| 419 | } | ||
| 420 | |||
| 421 | priv->io = ioremap(priv->mem->start, SZ_16K); | ||
| 422 | if (!priv->io) { | ||
| 423 | dev_err(&pdev->dev, "ioremap failed\n"); | ||
| 424 | err = -ENOMEM; | ||
| 425 | goto err_iomem; | ||
| 426 | } | ||
| 427 | |||
| 428 | priv->irq = platform_get_irq(pdev, 0); | ||
| 429 | if (priv->irq <= 0) { | ||
| 430 | dev_err(&pdev->dev, "platform_get_irq failed\n"); | ||
| 431 | err = -ENXIO; | ||
| 432 | goto err_ioremap; | ||
| 433 | } | ||
| 434 | |||
| 435 | if (!data || !data->dram) { | ||
| 436 | dev_err(&pdev->dev, "no platform data ?!\n"); | ||
| 437 | err = -EINVAL; | ||
| 438 | goto err_ioremap; | ||
| 439 | } | ||
| 440 | |||
| 441 | priv->dram = data->dram; | ||
| 442 | priv->burst = data->burst; | ||
| 443 | |||
| 444 | kirkwood_i2s_dai.capture.dma_data = priv; | ||
| 445 | kirkwood_i2s_dai.playback.dma_data = priv; | ||
| 446 | |||
| 447 | return snd_soc_register_dai(&kirkwood_i2s_dai); | ||
| 448 | |||
| 449 | err_ioremap: | ||
| 450 | iounmap(priv->io); | ||
| 451 | err_iomem: | ||
| 452 | release_mem_region(priv->mem->start, SZ_16K); | ||
| 453 | err_alloc: | ||
| 454 | kfree(priv); | ||
| 455 | error: | ||
| 456 | return err; | ||
| 457 | } | ||
| 458 | |||
| 459 | static __devexit int kirkwood_i2s_dev_remove(struct platform_device *pdev) | ||
| 460 | { | ||
| 461 | if (priv) { | ||
| 462 | iounmap(priv->io); | ||
| 463 | release_mem_region(priv->mem->start, SZ_16K); | ||
| 464 | kfree(priv); | ||
| 465 | } | ||
| 466 | snd_soc_unregister_dai(&kirkwood_i2s_dai); | ||
| 467 | return 0; | ||
| 468 | } | ||
| 469 | |||
| 470 | static struct platform_driver kirkwood_i2s_driver = { | ||
| 471 | .probe = kirkwood_i2s_dev_probe, | ||
| 472 | .remove = kirkwood_i2s_dev_remove, | ||
| 473 | .driver = { | ||
| 474 | .name = DRV_NAME, | ||
| 475 | .owner = THIS_MODULE, | ||
| 476 | }, | ||
| 477 | }; | ||
| 478 | |||
| 479 | static int __init kirkwood_i2s_init(void) | ||
| 480 | { | ||
| 481 | return platform_driver_register(&kirkwood_i2s_driver); | ||
| 482 | } | ||
| 483 | module_init(kirkwood_i2s_init); | ||
| 484 | |||
| 485 | static void __exit kirkwood_i2s_exit(void) | ||
| 486 | { | ||
| 487 | platform_driver_unregister(&kirkwood_i2s_driver); | ||
| 488 | } | ||
| 489 | module_exit(kirkwood_i2s_exit); | ||
| 490 | |||
| 491 | /* Module information */ | ||
| 492 | MODULE_AUTHOR("Arnaud Patard, <apatard@mandriva.com>"); | ||
| 493 | MODULE_DESCRIPTION("Kirkwood I2S SoC Interface"); | ||
| 494 | MODULE_LICENSE("GPL"); | ||
| 495 | MODULE_ALIAS("platform:kirkwood-i2s"); | ||
diff --git a/sound/soc/kirkwood/kirkwood-i2s.h b/sound/soc/kirkwood/kirkwood-i2s.h new file mode 100644 index 000000000000..c5595c616d7a --- /dev/null +++ b/sound/soc/kirkwood/kirkwood-i2s.h | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | /* | ||
| 2 | * kirkwood-i2s.h | ||
| 3 | * | ||
| 4 | * (c) 2010 Arnaud Patard <apatard@mandriva.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms of the GNU General Public License as published by the | ||
| 8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 9 | * option) any later version. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #ifndef _KIRKWOOD_I2S_H | ||
| 13 | #define _KIRKWOOD_I2S_H | ||
| 14 | |||
| 15 | extern struct snd_soc_dai kirkwood_i2s_dai; | ||
| 16 | |||
| 17 | #endif | ||
diff --git a/sound/soc/kirkwood/kirkwood-openrd.c b/sound/soc/kirkwood/kirkwood-openrd.c new file mode 100644 index 000000000000..0353d06bc41a --- /dev/null +++ b/sound/soc/kirkwood/kirkwood-openrd.c | |||
| @@ -0,0 +1,126 @@ | |||
| 1 | /* | ||
| 2 | * kirkwood-openrd.c | ||
| 3 | * | ||
| 4 | * (c) 2010 Arnaud Patard <apatard@mandriva.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms of the GNU General Public License as published by the | ||
| 8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 9 | * option) any later version. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/moduleparam.h> | ||
| 14 | #include <linux/interrupt.h> | ||
| 15 | #include <linux/platform_device.h> | ||
| 16 | #include <linux/slab.h> | ||
| 17 | #include <sound/soc.h> | ||
| 18 | #include <mach/kirkwood.h> | ||
| 19 | #include <plat/audio.h> | ||
| 20 | #include <asm/mach-types.h> | ||
| 21 | #include "kirkwood-i2s.h" | ||
| 22 | #include "kirkwood-dma.h" | ||
| 23 | #include "../codecs/cs42l51.h" | ||
| 24 | |||
| 25 | static int openrd_client_hw_params(struct snd_pcm_substream *substream, | ||
| 26 | struct snd_pcm_hw_params *params) | ||
| 27 | { | ||
| 28 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
| 29 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
| 30 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
| 31 | int ret; | ||
| 32 | unsigned int freq, fmt; | ||
| 33 | |||
| 34 | fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS; | ||
| 35 | ret = snd_soc_dai_set_fmt(cpu_dai, fmt); | ||
| 36 | if (ret < 0) | ||
| 37 | return ret; | ||
| 38 | |||
| 39 | ret = snd_soc_dai_set_fmt(codec_dai, fmt); | ||
| 40 | if (ret < 0) | ||
| 41 | return ret; | ||
| 42 | |||
| 43 | switch (params_rate(params)) { | ||
| 44 | default: | ||
| 45 | case 44100: | ||
| 46 | freq = 11289600; | ||
| 47 | break; | ||
| 48 | case 48000: | ||
| 49 | freq = 12288000; | ||
| 50 | break; | ||
| 51 | case 96000: | ||
| 52 | freq = 24576000; | ||
| 53 | break; | ||
| 54 | } | ||
| 55 | |||
| 56 | return snd_soc_dai_set_sysclk(codec_dai, 0, freq, SND_SOC_CLOCK_IN); | ||
| 57 | |||
| 58 | } | ||
| 59 | |||
| 60 | static struct snd_soc_ops openrd_client_ops = { | ||
| 61 | .hw_params = openrd_client_hw_params, | ||
| 62 | }; | ||
| 63 | |||
| 64 | |||
| 65 | static struct snd_soc_dai_link openrd_client_dai[] = { | ||
| 66 | { | ||
| 67 | .name = "CS42L51", | ||
| 68 | .stream_name = "CS42L51 HiFi", | ||
| 69 | .cpu_dai = &kirkwood_i2s_dai, | ||
| 70 | .codec_dai = &cs42l51_dai, | ||
| 71 | .ops = &openrd_client_ops, | ||
| 72 | }, | ||
| 73 | }; | ||
| 74 | |||
| 75 | |||
| 76 | static struct snd_soc_card openrd_client = { | ||
| 77 | .name = "OpenRD Client", | ||
| 78 | .platform = &kirkwood_soc_platform, | ||
| 79 | .dai_link = openrd_client_dai, | ||
| 80 | .num_links = ARRAY_SIZE(openrd_client_dai), | ||
| 81 | }; | ||
| 82 | |||
| 83 | static struct snd_soc_device openrd_client_snd_devdata = { | ||
| 84 | .card = &openrd_client, | ||
| 85 | .codec_dev = &soc_codec_device_cs42l51, | ||
| 86 | }; | ||
| 87 | |||
| 88 | static struct platform_device *openrd_client_snd_device; | ||
| 89 | |||
| 90 | static int __init openrd_client_init(void) | ||
| 91 | { | ||
| 92 | int ret; | ||
| 93 | |||
| 94 | if (!machine_is_openrd_client()) | ||
| 95 | return 0; | ||
| 96 | |||
| 97 | openrd_client_snd_device = platform_device_alloc("soc-audio", -1); | ||
| 98 | if (!openrd_client_snd_device) | ||
| 99 | return -ENOMEM; | ||
| 100 | |||
| 101 | platform_set_drvdata(openrd_client_snd_device, | ||
| 102 | &openrd_client_snd_devdata); | ||
| 103 | openrd_client_snd_devdata.dev = &openrd_client_snd_device->dev; | ||
| 104 | |||
| 105 | ret = platform_device_add(openrd_client_snd_device); | ||
| 106 | if (ret) { | ||
| 107 | printk(KERN_ERR "%s: platform_device_add failed\n", __func__); | ||
| 108 | platform_device_put(openrd_client_snd_device); | ||
| 109 | } | ||
| 110 | |||
| 111 | return ret; | ||
| 112 | } | ||
| 113 | |||
| 114 | static void __exit openrd_client_exit(void) | ||
| 115 | { | ||
| 116 | platform_device_unregister(openrd_client_snd_device); | ||
| 117 | } | ||
| 118 | |||
| 119 | module_init(openrd_client_init); | ||
| 120 | module_exit(openrd_client_exit); | ||
| 121 | |||
| 122 | /* Module information */ | ||
| 123 | MODULE_AUTHOR("Arnaud Patard <apatard@mandriva.com>"); | ||
| 124 | MODULE_DESCRIPTION("ALSA SoC OpenRD Client"); | ||
| 125 | MODULE_LICENSE("GPL"); | ||
| 126 | MODULE_ALIAS("platform:soc-audio"); | ||
diff --git a/sound/soc/kirkwood/kirkwood.h b/sound/soc/kirkwood/kirkwood.h new file mode 100644 index 000000000000..bb6e6a5648c9 --- /dev/null +++ b/sound/soc/kirkwood/kirkwood.h | |||
| @@ -0,0 +1,129 @@ | |||
| 1 | /* | ||
| 2 | * kirkwood.h | ||
| 3 | * | ||
| 4 | * (c) 2010 Arnaud Patard <apatard@mandriva.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms of the GNU General Public License as published by the | ||
| 8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 9 | * option) any later version. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #ifndef _KIRKWOOD_AUDIO_H | ||
| 13 | #define _KIRKWOOD_AUDIO_H | ||
| 14 | |||
| 15 | #define KIRKWOOD_RECORD_WIN 0 | ||
| 16 | #define KIRKWOOD_PLAYBACK_WIN 1 | ||
| 17 | #define KIRKWOOD_MAX_AUDIO_WIN 2 | ||
| 18 | |||
| 19 | #define KIRKWOOD_AUDIO_WIN_BASE_REG(win) (0xA00 + ((win)<<3)) | ||
| 20 | #define KIRKWOOD_AUDIO_WIN_CTRL_REG(win) (0xA04 + ((win)<<3)) | ||
| 21 | |||
| 22 | |||
| 23 | #define KIRKWOOD_RECCTL 0x1000 | ||
| 24 | #define KIRKWOOD_RECCTL_SPDIF_EN (1<<11) | ||
| 25 | #define KIRKWOOD_RECCTL_I2S_EN (1<<10) | ||
| 26 | #define KIRKWOOD_RECCTL_PAUSE (1<<9) | ||
| 27 | #define KIRKWOOD_RECCTL_MUTE (1<<8) | ||
| 28 | #define KIRKWOOD_RECCTL_BURST_MASK (3<<5) | ||
| 29 | #define KIRKWOOD_RECCTL_BURST_128 (2<<5) | ||
| 30 | #define KIRKWOOD_RECCTL_BURST_32 (1<<5) | ||
| 31 | #define KIRKWOOD_RECCTL_MONO (1<<4) | ||
| 32 | #define KIRKWOOD_RECCTL_MONO_CHAN_RIGHT (1<<3) | ||
| 33 | #define KIRKWOOD_RECCTL_MONO_CHAN_LEFT (0<<3) | ||
| 34 | #define KIRKWOOD_RECCTL_SIZE_MASK (7<<0) | ||
| 35 | #define KIRKWOOD_RECCTL_SIZE_16 (7<<0) | ||
| 36 | #define KIRKWOOD_RECCTL_SIZE_16_C (3<<0) | ||
| 37 | #define KIRKWOOD_RECCTL_SIZE_20 (2<<0) | ||
| 38 | #define KIRKWOOD_RECCTL_SIZE_24 (1<<0) | ||
| 39 | #define KIRKWOOD_RECCTL_SIZE_32 (0<<0) | ||
| 40 | |||
| 41 | #define KIRKWOOD_REC_BUF_ADDR 0x1004 | ||
| 42 | #define KIRKWOOD_REC_BUF_SIZE 0x1008 | ||
| 43 | #define KIRKWOOD_REC_BYTE_COUNT 0x100C | ||
| 44 | |||
| 45 | #define KIRKWOOD_PLAYCTL 0x1100 | ||
| 46 | #define KIRKWOOD_PLAYCTL_PLAY_BUSY (1<<16) | ||
| 47 | #define KIRKWOOD_PLAYCTL_BURST_MASK (3<<11) | ||
| 48 | #define KIRKWOOD_PLAYCTL_BURST_128 (2<<11) | ||
| 49 | #define KIRKWOOD_PLAYCTL_BURST_32 (1<<11) | ||
| 50 | #define KIRKWOOD_PLAYCTL_PAUSE (1<<9) | ||
| 51 | #define KIRKWOOD_PLAYCTL_SPDIF_MUTE (1<<8) | ||
| 52 | #define KIRKWOOD_PLAYCTL_MONO_MASK (3<<5) | ||
| 53 | #define KIRKWOOD_PLAYCTL_MONO_BOTH (3<<5) | ||
| 54 | #define KIRKWOOD_PLAYCTL_MONO_OFF (0<<5) | ||
| 55 | #define KIRKWOOD_PLAYCTL_I2S_MUTE (1<<7) | ||
| 56 | #define KIRKWOOD_PLAYCTL_SPDIF_EN (1<<4) | ||
| 57 | #define KIRKWOOD_PLAYCTL_I2S_EN (1<<3) | ||
| 58 | #define KIRKWOOD_PLAYCTL_SIZE_MASK (7<<0) | ||
| 59 | #define KIRKWOOD_PLAYCTL_SIZE_16 (7<<0) | ||
| 60 | #define KIRKWOOD_PLAYCTL_SIZE_16_C (3<<0) | ||
| 61 | #define KIRKWOOD_PLAYCTL_SIZE_20 (2<<0) | ||
| 62 | #define KIRKWOOD_PLAYCTL_SIZE_24 (1<<0) | ||
| 63 | #define KIRKWOOD_PLAYCTL_SIZE_32 (0<<0) | ||
| 64 | |||
| 65 | #define KIRKWOOD_PLAY_BUF_ADDR 0x1104 | ||
| 66 | #define KIRKWOOD_PLAY_BUF_SIZE 0x1108 | ||
| 67 | #define KIRKWOOD_PLAY_BYTE_COUNT 0x110C | ||
| 68 | |||
| 69 | #define KIRKWOOD_DCO_CTL 0x1204 | ||
| 70 | #define KIRKWOOD_DCO_CTL_OFFSET_MASK (0xFFF<<2) | ||
| 71 | #define KIRKWOOD_DCO_CTL_OFFSET_0 (0x800<<2) | ||
| 72 | #define KIRKWOOD_DCO_CTL_FREQ_MASK (3<<0) | ||
| 73 | #define KIRKWOOD_DCO_CTL_FREQ_11 (0<<0) | ||
| 74 | #define KIRKWOOD_DCO_CTL_FREQ_12 (1<<0) | ||
| 75 | #define KIRKWOOD_DCO_CTL_FREQ_24 (2<<0) | ||
| 76 | |||
| 77 | #define KIRKWOOD_DCO_SPCR_STATUS 0x120c | ||
| 78 | #define KIRKWOOD_DCO_SPCR_STATUS_DCO_LOCK (1<<16) | ||
| 79 | |||
| 80 | #define KIRKWOOD_ERR_CAUSE 0x1300 | ||
| 81 | #define KIRKWOOD_ERR_MASK 0x1304 | ||
| 82 | |||
| 83 | #define KIRKWOOD_INT_CAUSE 0x1308 | ||
| 84 | #define KIRKWOOD_INT_MASK 0x130C | ||
| 85 | #define KIRKWOOD_INT_CAUSE_PLAY_BYTES (1<<14) | ||
| 86 | #define KIRKWOOD_INT_CAUSE_REC_BYTES (1<<13) | ||
| 87 | #define KIRKWOOD_INT_CAUSE_DMA_PLAY_END (1<<7) | ||
| 88 | #define KIRKWOOD_INT_CAUSE_DMA_PLAY_3Q (1<<6) | ||
| 89 | #define KIRKWOOD_INT_CAUSE_DMA_PLAY_HALF (1<<5) | ||
| 90 | #define KIRKWOOD_INT_CAUSE_DMA_PLAY_1Q (1<<4) | ||
| 91 | #define KIRKWOOD_INT_CAUSE_DMA_REC_END (1<<3) | ||
| 92 | #define KIRKWOOD_INT_CAUSE_DMA_REC_3Q (1<<2) | ||
| 93 | #define KIRKWOOD_INT_CAUSE_DMA_REC_HALF (1<<1) | ||
| 94 | #define KIRKWOOD_INT_CAUSE_DMA_REC_1Q (1<<0) | ||
| 95 | |||
| 96 | #define KIRKWOOD_REC_BYTE_INT_COUNT 0x1310 | ||
| 97 | #define KIRKWOOD_PLAY_BYTE_INT_COUNT 0x1314 | ||
| 98 | #define KIRKWOOD_BYTE_INT_COUNT_MASK 0xffffff | ||
| 99 | |||
| 100 | #define KIRKWOOD_I2S_PLAYCTL 0x2508 | ||
| 101 | #define KIRKWOOD_I2S_RECCTL 0x2408 | ||
| 102 | #define KIRKWOOD_I2S_CTL_JUST_MASK (0xf<<26) | ||
| 103 | #define KIRKWOOD_I2S_CTL_LJ (0<<26) | ||
| 104 | #define KIRKWOOD_I2S_CTL_I2S (5<<26) | ||
| 105 | #define KIRKWOOD_I2S_CTL_RJ (8<<26) | ||
| 106 | #define KIRKWOOD_I2S_CTL_SIZE_MASK (3<<30) | ||
| 107 | #define KIRKWOOD_I2S_CTL_SIZE_16 (3<<30) | ||
| 108 | #define KIRKWOOD_I2S_CTL_SIZE_20 (2<<30) | ||
| 109 | #define KIRKWOOD_I2S_CTL_SIZE_24 (1<<30) | ||
| 110 | #define KIRKWOOD_I2S_CTL_SIZE_32 (0<<30) | ||
| 111 | |||
| 112 | #define KIRKWOOD_AUDIO_BUF_MAX (16*1024*1024) | ||
| 113 | |||
| 114 | /* Theses values come from the marvell alsa driver */ | ||
| 115 | /* need to find where they come from */ | ||
| 116 | #define KIRKWOOD_SND_MIN_PERIODS 8 | ||
| 117 | #define KIRKWOOD_SND_MAX_PERIODS 16 | ||
| 118 | #define KIRKWOOD_SND_MIN_PERIOD_BYTES 0x4000 | ||
| 119 | #define KIRKWOOD_SND_MAX_PERIOD_BYTES 0x4000 | ||
| 120 | |||
| 121 | struct kirkwood_dma_data { | ||
| 122 | struct resource *mem; | ||
| 123 | void __iomem *io; | ||
| 124 | int irq; | ||
| 125 | int burst; | ||
| 126 | struct mbus_dram_target_info *dram; | ||
| 127 | }; | ||
| 128 | |||
| 129 | #endif | ||
diff --git a/sound/soc/nuc900/Kconfig b/sound/soc/nuc900/Kconfig new file mode 100644 index 000000000000..a0ed1c618f60 --- /dev/null +++ b/sound/soc/nuc900/Kconfig | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | ## | ||
| 2 | ## NUC900 series AC97 API | ||
| 3 | ## | ||
| 4 | config SND_SOC_NUC900 | ||
| 5 | tristate "SoC Audio for NUC900 series" | ||
| 6 | depends on ARCH_W90X900 | ||
| 7 | help | ||
| 8 | This option enables support for AC97 mode on the NUC900 SoC. | ||
| 9 | |||
| 10 | config SND_SOC_NUC900_AC97 | ||
| 11 | tristate | ||
| 12 | select AC97_BUS | ||
| 13 | select SND_AC97_CODEC | ||
| 14 | select SND_SOC_AC97_BUS | ||
| 15 | |||
| 16 | |||
| 17 | ## | ||
| 18 | ## Boards | ||
| 19 | ## | ||
| 20 | config SND_SOC_NUC900EVB | ||
| 21 | tristate "NUC900 AC97 support for demo board" | ||
| 22 | depends on SND_SOC_NUC900 | ||
| 23 | select SND_SOC_NUC900_AC97 | ||
| 24 | select SND_SOC_AC97_CODEC | ||
| 25 | help | ||
| 26 | Select this option to enable audio (AC97) on the | ||
| 27 | NUC900 demoboard. | ||
diff --git a/sound/soc/nuc900/Makefile b/sound/soc/nuc900/Makefile new file mode 100644 index 000000000000..7e46c7150316 --- /dev/null +++ b/sound/soc/nuc900/Makefile | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # NUC900 series audio | ||
| 2 | snd-soc-nuc900-pcm-objs := nuc900-pcm.o | ||
| 3 | snd-soc-nuc900-ac97-objs := nuc900-ac97.o | ||
| 4 | |||
| 5 | obj-$(CONFIG_SND_SOC_NUC900) += snd-soc-nuc900-pcm.o | ||
| 6 | obj-$(CONFIG_SND_SOC_NUC900_AC97) += snd-soc-nuc900-ac97.o | ||
| 7 | |||
| 8 | # Boards | ||
| 9 | snd-soc-nuc900-audio-objs := nuc900-audio.o | ||
| 10 | |||
| 11 | obj-$(CONFIG_SND_SOC_NUC900EVB) += snd-soc-nuc900-audio.o | ||
diff --git a/sound/soc/nuc900/nuc900-ac97.c b/sound/soc/nuc900/nuc900-ac97.c new file mode 100644 index 000000000000..caa7c901bc2e --- /dev/null +++ b/sound/soc/nuc900/nuc900-ac97.c | |||
| @@ -0,0 +1,430 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2009-2010 Nuvoton technology corporation. | ||
| 3 | * | ||
| 4 | * Wan ZongShun <mcuos.com@gmail.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation;version 2 of the License. | ||
| 9 | * | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/init.h> | ||
| 13 | #include <linux/module.h> | ||
| 14 | #include <linux/slab.h> | ||
| 15 | #include <linux/device.h> | ||
| 16 | #include <linux/delay.h> | ||
| 17 | #include <linux/mutex.h> | ||
| 18 | #include <linux/suspend.h> | ||
| 19 | #include <sound/core.h> | ||
| 20 | #include <sound/pcm.h> | ||
| 21 | #include <sound/initval.h> | ||
| 22 | #include <sound/soc.h> | ||
| 23 | #include <linux/device.h> | ||
| 24 | #include <linux/clk.h> | ||
| 25 | |||
| 26 | #include <mach/mfp.h> | ||
| 27 | |||
| 28 | #include "nuc900-audio.h" | ||
| 29 | |||
| 30 | static DEFINE_MUTEX(ac97_mutex); | ||
| 31 | struct nuc900_audio *nuc900_ac97_data; | ||
| 32 | |||
| 33 | static int nuc900_checkready(void) | ||
| 34 | { | ||
| 35 | struct nuc900_audio *nuc900_audio = nuc900_ac97_data; | ||
| 36 | |||
| 37 | if (!(AUDIO_READ(nuc900_audio->mmio + ACTL_ACIS0) & CODEC_READY)) | ||
| 38 | return -EPERM; | ||
| 39 | |||
| 40 | return 0; | ||
| 41 | } | ||
| 42 | |||
| 43 | /* AC97 controller reads codec register */ | ||
| 44 | static unsigned short nuc900_ac97_read(struct snd_ac97 *ac97, | ||
| 45 | unsigned short reg) | ||
| 46 | { | ||
| 47 | struct nuc900_audio *nuc900_audio = nuc900_ac97_data; | ||
| 48 | unsigned long timeout = 0x10000, val; | ||
| 49 | |||
| 50 | mutex_lock(&ac97_mutex); | ||
| 51 | |||
| 52 | val = nuc900_checkready(); | ||
| 53 | if (!!val) { | ||
| 54 | dev_err(nuc900_audio->dev, "AC97 codec is not ready\n"); | ||
| 55 | goto out; | ||
| 56 | } | ||
| 57 | |||
| 58 | /* set the R_WB bit and write register index */ | ||
| 59 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS1, R_WB | reg); | ||
| 60 | |||
| 61 | /* set the valid frame bit and valid slots */ | ||
| 62 | val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0); | ||
| 63 | val |= (VALID_FRAME | SLOT1_VALID); | ||
| 64 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, val); | ||
| 65 | |||
| 66 | udelay(100); | ||
| 67 | |||
| 68 | /* polling the AC_R_FINISH */ | ||
| 69 | while (!(AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON) & AC_R_FINISH) | ||
| 70 | && timeout--) | ||
| 71 | mdelay(1); | ||
| 72 | |||
| 73 | if (!timeout) { | ||
| 74 | dev_err(nuc900_audio->dev, "AC97 read register time out !\n"); | ||
| 75 | val = -EPERM; | ||
| 76 | goto out; | ||
| 77 | } | ||
| 78 | |||
| 79 | val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0) ; | ||
| 80 | val &= ~SLOT1_VALID; | ||
| 81 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, val); | ||
| 82 | |||
| 83 | if (AUDIO_READ(nuc900_audio->mmio + ACTL_ACIS1) >> 2 != reg) { | ||
| 84 | dev_err(nuc900_audio->dev, | ||
| 85 | "R_INDEX of REG_ACTL_ACIS1 not match!\n"); | ||
| 86 | } | ||
| 87 | |||
| 88 | udelay(100); | ||
| 89 | val = (AUDIO_READ(nuc900_audio->mmio + ACTL_ACIS2) & 0xFFFF); | ||
| 90 | |||
| 91 | out: | ||
| 92 | mutex_unlock(&ac97_mutex); | ||
| 93 | return val; | ||
| 94 | } | ||
| 95 | |||
| 96 | /* AC97 controller writes to codec register */ | ||
| 97 | static void nuc900_ac97_write(struct snd_ac97 *ac97, unsigned short reg, | ||
| 98 | unsigned short val) | ||
| 99 | { | ||
| 100 | struct nuc900_audio *nuc900_audio = nuc900_ac97_data; | ||
| 101 | unsigned long tmp, timeout = 0x10000; | ||
| 102 | |||
| 103 | mutex_lock(&ac97_mutex); | ||
| 104 | |||
| 105 | tmp = nuc900_checkready(); | ||
| 106 | if (!!tmp) | ||
| 107 | dev_err(nuc900_audio->dev, "AC97 codec is not ready\n"); | ||
| 108 | |||
| 109 | /* clear the R_WB bit and write register index */ | ||
| 110 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS1, reg); | ||
| 111 | |||
| 112 | /* write register value */ | ||
| 113 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS2, val); | ||
| 114 | |||
| 115 | /* set the valid frame bit and valid slots */ | ||
| 116 | tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0); | ||
| 117 | tmp |= SLOT1_VALID | SLOT2_VALID | VALID_FRAME; | ||
| 118 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, tmp); | ||
| 119 | |||
| 120 | udelay(100); | ||
| 121 | |||
| 122 | /* polling the AC_W_FINISH */ | ||
| 123 | while ((AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON) & AC_W_FINISH) | ||
| 124 | && timeout--) | ||
| 125 | mdelay(1); | ||
| 126 | |||
| 127 | if (!timeout) | ||
| 128 | dev_err(nuc900_audio->dev, "AC97 write register time out !\n"); | ||
| 129 | |||
| 130 | tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0); | ||
| 131 | tmp &= ~(SLOT1_VALID | SLOT2_VALID); | ||
| 132 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, tmp); | ||
| 133 | |||
| 134 | mutex_unlock(&ac97_mutex); | ||
| 135 | |||
| 136 | } | ||
| 137 | |||
| 138 | static void nuc900_ac97_warm_reset(struct snd_ac97 *ac97) | ||
| 139 | { | ||
| 140 | struct nuc900_audio *nuc900_audio = nuc900_ac97_data; | ||
| 141 | unsigned long val; | ||
| 142 | |||
| 143 | mutex_lock(&ac97_mutex); | ||
| 144 | |||
| 145 | /* warm reset AC 97 */ | ||
| 146 | val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON); | ||
| 147 | val |= AC_W_RES; | ||
| 148 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACCON, val); | ||
| 149 | |||
| 150 | udelay(100); | ||
| 151 | |||
| 152 | val = nuc900_checkready(); | ||
| 153 | if (!!val) | ||
| 154 | dev_err(nuc900_audio->dev, "AC97 codec is not ready\n"); | ||
| 155 | |||
| 156 | mutex_unlock(&ac97_mutex); | ||
| 157 | } | ||
| 158 | |||
| 159 | static void nuc900_ac97_cold_reset(struct snd_ac97 *ac97) | ||
| 160 | { | ||
| 161 | struct nuc900_audio *nuc900_audio = nuc900_ac97_data; | ||
| 162 | unsigned long val; | ||
| 163 | |||
| 164 | mutex_lock(&ac97_mutex); | ||
| 165 | |||
| 166 | /* reset Audio Controller */ | ||
| 167 | val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET); | ||
| 168 | val |= ACTL_RESET_BIT; | ||
| 169 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val); | ||
| 170 | |||
| 171 | val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET); | ||
| 172 | val &= (~ACTL_RESET_BIT); | ||
| 173 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val); | ||
| 174 | |||
| 175 | /* reset AC-link interface */ | ||
| 176 | |||
| 177 | val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET); | ||
| 178 | val |= AC_RESET; | ||
| 179 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val); | ||
| 180 | |||
| 181 | val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET); | ||
| 182 | val &= ~AC_RESET; | ||
| 183 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val); | ||
| 184 | |||
| 185 | /* cold reset AC 97 */ | ||
| 186 | val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON); | ||
| 187 | val |= AC_C_RES; | ||
| 188 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACCON, val); | ||
| 189 | |||
| 190 | val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON); | ||
| 191 | val &= (~AC_C_RES); | ||
| 192 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACCON, val); | ||
| 193 | |||
| 194 | udelay(100); | ||
| 195 | |||
| 196 | mutex_unlock(&ac97_mutex); | ||
| 197 | |||
| 198 | } | ||
| 199 | |||
| 200 | /* AC97 controller operations */ | ||
| 201 | struct snd_ac97_bus_ops soc_ac97_ops = { | ||
| 202 | .read = nuc900_ac97_read, | ||
| 203 | .write = nuc900_ac97_write, | ||
| 204 | .reset = nuc900_ac97_cold_reset, | ||
| 205 | .warm_reset = nuc900_ac97_warm_reset, | ||
| 206 | } | ||
| 207 | EXPORT_SYMBOL_GPL(soc_ac97_ops); | ||
| 208 | |||
| 209 | static int nuc900_ac97_trigger(struct snd_pcm_substream *substream, | ||
| 210 | int cmd, struct snd_soc_dai *dai) | ||
| 211 | { | ||
| 212 | struct nuc900_audio *nuc900_audio = nuc900_ac97_data; | ||
| 213 | int ret; | ||
| 214 | unsigned long val, tmp; | ||
| 215 | |||
| 216 | ret = 0; | ||
| 217 | |||
| 218 | switch (cmd) { | ||
| 219 | case SNDRV_PCM_TRIGGER_START: | ||
| 220 | case SNDRV_PCM_TRIGGER_RESUME: | ||
| 221 | val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET); | ||
| 222 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
| 223 | tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0); | ||
| 224 | tmp |= (SLOT3_VALID | SLOT4_VALID | VALID_FRAME); | ||
| 225 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, tmp); | ||
| 226 | |||
| 227 | tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_PSR); | ||
| 228 | tmp |= (P_DMA_END_IRQ | P_DMA_MIDDLE_IRQ); | ||
| 229 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, tmp); | ||
| 230 | val |= AC_PLAY; | ||
| 231 | } else { | ||
| 232 | tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_RSR); | ||
| 233 | tmp |= (R_DMA_END_IRQ | R_DMA_MIDDLE_IRQ); | ||
| 234 | |||
| 235 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, tmp); | ||
| 236 | val |= AC_RECORD; | ||
| 237 | } | ||
| 238 | |||
| 239 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val); | ||
| 240 | |||
| 241 | break; | ||
| 242 | case SNDRV_PCM_TRIGGER_STOP: | ||
| 243 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
| 244 | val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET); | ||
| 245 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
| 246 | tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0); | ||
| 247 | tmp &= ~(SLOT3_VALID | SLOT4_VALID); | ||
| 248 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, tmp); | ||
| 249 | |||
| 250 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, RESET_PRSR); | ||
| 251 | val &= ~AC_PLAY; | ||
| 252 | } else { | ||
| 253 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, RESET_PRSR); | ||
| 254 | val &= ~AC_RECORD; | ||
| 255 | } | ||
| 256 | |||
| 257 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val); | ||
| 258 | |||
| 259 | break; | ||
| 260 | default: | ||
| 261 | ret = -EINVAL; | ||
| 262 | } | ||
| 263 | |||
| 264 | return ret; | ||
| 265 | } | ||
| 266 | |||
| 267 | static int nuc900_ac97_probe(struct platform_device *pdev, | ||
| 268 | struct snd_soc_dai *dai) | ||
| 269 | { | ||
| 270 | struct nuc900_audio *nuc900_audio = nuc900_ac97_data; | ||
| 271 | unsigned long val; | ||
| 272 | |||
| 273 | mutex_lock(&ac97_mutex); | ||
| 274 | |||
| 275 | /* enable unit clock */ | ||
| 276 | clk_enable(nuc900_audio->clk); | ||
| 277 | |||
| 278 | /* enable audio controller and AC-link interface */ | ||
| 279 | val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON); | ||
| 280 | val |= (IIS_AC_PIN_SEL | ACLINK_EN); | ||
| 281 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val); | ||
| 282 | |||
| 283 | mutex_unlock(&ac97_mutex); | ||
| 284 | |||
| 285 | return 0; | ||
| 286 | } | ||
| 287 | |||
| 288 | static void nuc900_ac97_remove(struct platform_device *pdev, | ||
| 289 | struct snd_soc_dai *dai) | ||
| 290 | { | ||
| 291 | struct nuc900_audio *nuc900_audio = nuc900_ac97_data; | ||
| 292 | |||
| 293 | clk_disable(nuc900_audio->clk); | ||
| 294 | } | ||
| 295 | |||
| 296 | static struct snd_soc_dai_ops nuc900_ac97_dai_ops = { | ||
| 297 | .trigger = nuc900_ac97_trigger, | ||
| 298 | }; | ||
| 299 | |||
| 300 | struct snd_soc_dai nuc900_ac97_dai = { | ||
| 301 | .name = "nuc900-ac97", | ||
| 302 | .probe = nuc900_ac97_probe, | ||
| 303 | .remove = nuc900_ac97_remove, | ||
| 304 | .ac97_control = 1, | ||
| 305 | .playback = { | ||
| 306 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
| 307 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
| 308 | .channels_min = 1, | ||
| 309 | .channels_max = 2, | ||
| 310 | }, | ||
| 311 | .capture = { | ||
| 312 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
| 313 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
| 314 | .channels_min = 1, | ||
| 315 | .channels_max = 2, | ||
| 316 | }, | ||
| 317 | .ops = &nuc900_ac97_dai_ops, | ||
| 318 | } | ||
| 319 | EXPORT_SYMBOL_GPL(nuc900_ac97_dai); | ||
| 320 | |||
| 321 | static int __devinit nuc900_ac97_drvprobe(struct platform_device *pdev) | ||
| 322 | { | ||
| 323 | struct nuc900_audio *nuc900_audio; | ||
| 324 | int ret; | ||
| 325 | |||
| 326 | if (nuc900_ac97_data) | ||
| 327 | return -EBUSY; | ||
| 328 | |||
| 329 | nuc900_audio = kzalloc(sizeof(struct nuc900_audio), GFP_KERNEL); | ||
| 330 | if (!nuc900_audio) | ||
| 331 | return -ENOMEM; | ||
| 332 | |||
| 333 | spin_lock_init(&nuc900_audio->lock); | ||
| 334 | |||
| 335 | nuc900_audio->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 336 | if (!nuc900_audio->res) { | ||
| 337 | ret = -ENODEV; | ||
| 338 | goto out0; | ||
| 339 | } | ||
| 340 | |||
| 341 | if (!request_mem_region(nuc900_audio->res->start, | ||
| 342 | resource_size(nuc900_audio->res), pdev->name)) { | ||
| 343 | ret = -EBUSY; | ||
| 344 | goto out0; | ||
| 345 | } | ||
| 346 | |||
| 347 | nuc900_audio->mmio = ioremap(nuc900_audio->res->start, | ||
| 348 | resource_size(nuc900_audio->res)); | ||
| 349 | if (!nuc900_audio->mmio) { | ||
| 350 | ret = -ENOMEM; | ||
| 351 | goto out1; | ||
| 352 | } | ||
| 353 | |||
| 354 | nuc900_audio->clk = clk_get(&pdev->dev, NULL); | ||
| 355 | if (IS_ERR(nuc900_audio->clk)) { | ||
| 356 | ret = PTR_ERR(nuc900_audio->clk); | ||
| 357 | goto out2; | ||
| 358 | } | ||
| 359 | |||
| 360 | nuc900_audio->irq_num = platform_get_irq(pdev, 0); | ||
| 361 | if (!nuc900_audio->irq_num) { | ||
| 362 | ret = -EBUSY; | ||
| 363 | goto out2; | ||
| 364 | } | ||
| 365 | |||
| 366 | nuc900_ac97_data = nuc900_audio; | ||
| 367 | |||
| 368 | nuc900_audio->dev = nuc900_ac97_dai.dev = &pdev->dev; | ||
| 369 | |||
| 370 | ret = snd_soc_register_dai(&nuc900_ac97_dai); | ||
| 371 | if (ret) | ||
| 372 | goto out3; | ||
| 373 | |||
| 374 | mfp_set_groupg(nuc900_audio->dev); /* enbale ac97 multifunction pin*/ | ||
| 375 | |||
| 376 | return 0; | ||
| 377 | |||
| 378 | out3: | ||
| 379 | clk_put(nuc900_audio->clk); | ||
| 380 | out2: | ||
| 381 | iounmap(nuc900_audio->mmio); | ||
| 382 | out1: | ||
| 383 | release_mem_region(nuc900_audio->res->start, | ||
| 384 | resource_size(nuc900_audio->res)); | ||
| 385 | out0: | ||
| 386 | kfree(nuc900_audio); | ||
| 387 | return ret; | ||
| 388 | } | ||
| 389 | |||
| 390 | static int __devexit nuc900_ac97_drvremove(struct platform_device *pdev) | ||
| 391 | { | ||
| 392 | |||
| 393 | snd_soc_unregister_dai(&nuc900_ac97_dai); | ||
| 394 | |||
| 395 | clk_put(nuc900_ac97_data->clk); | ||
| 396 | iounmap(nuc900_ac97_data->mmio); | ||
| 397 | release_mem_region(nuc900_ac97_data->res->start, | ||
| 398 | resource_size(nuc900_ac97_data->res)); | ||
| 399 | |||
| 400 | nuc900_ac97_data = NULL; | ||
| 401 | |||
| 402 | return 0; | ||
| 403 | } | ||
| 404 | |||
| 405 | static struct platform_driver nuc900_ac97_driver = { | ||
| 406 | .driver = { | ||
| 407 | .name = "nuc900-audio", | ||
| 408 | .owner = THIS_MODULE, | ||
| 409 | }, | ||
| 410 | .probe = nuc900_ac97_drvprobe, | ||
| 411 | .remove = __devexit_p(nuc900_ac97_drvremove), | ||
| 412 | }; | ||
| 413 | |||
| 414 | static int __init nuc900_ac97_init(void) | ||
| 415 | { | ||
| 416 | return platform_driver_register(&nuc900_ac97_driver); | ||
| 417 | } | ||
| 418 | |||
| 419 | static void __exit nuc900_ac97_exit(void) | ||
| 420 | { | ||
| 421 | platform_driver_unregister(&nuc900_ac97_driver); | ||
| 422 | } | ||
| 423 | |||
| 424 | module_init(nuc900_ac97_init); | ||
| 425 | module_exit(nuc900_ac97_exit); | ||
| 426 | |||
| 427 | MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); | ||
| 428 | MODULE_DESCRIPTION("NUC900 AC97 SoC driver!"); | ||
| 429 | MODULE_LICENSE("GPL"); | ||
| 430 | MODULE_ALIAS("platform:nuc900-ac97"); | ||
diff --git a/sound/soc/nuc900/nuc900-audio.c b/sound/soc/nuc900/nuc900-audio.c new file mode 100644 index 000000000000..72e6f518f7b2 --- /dev/null +++ b/sound/soc/nuc900/nuc900-audio.c | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2010 Nuvoton technology corporation. | ||
| 3 | * | ||
| 4 | * Wan ZongShun <mcuos.com@gmail.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation;version 2 of the License. | ||
| 9 | * | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/moduleparam.h> | ||
| 14 | #include <linux/timer.h> | ||
| 15 | #include <linux/interrupt.h> | ||
| 16 | #include <linux/platform_device.h> | ||
| 17 | |||
| 18 | #include <sound/core.h> | ||
| 19 | #include <sound/pcm.h> | ||
| 20 | #include <sound/soc.h> | ||
| 21 | #include <sound/soc-dapm.h> | ||
| 22 | |||
| 23 | #include "../codecs/ac97.h" | ||
| 24 | #include "nuc900-audio.h" | ||
| 25 | |||
| 26 | static struct snd_soc_dai_link nuc900evb_ac97_dai = { | ||
| 27 | .name = "AC97", | ||
| 28 | .stream_name = "AC97 HiFi", | ||
| 29 | .cpu_dai = &nuc900_ac97_dai, | ||
| 30 | .codec_dai = &ac97_dai, | ||
| 31 | }; | ||
| 32 | |||
| 33 | static struct snd_soc_card nuc900evb_audio_machine = { | ||
| 34 | .name = "NUC900EVB_AC97", | ||
| 35 | .dai_link = &nuc900evb_ac97_dai, | ||
| 36 | .num_links = 1, | ||
| 37 | .platform = &nuc900_soc_platform, | ||
| 38 | }; | ||
| 39 | |||
| 40 | static struct snd_soc_device nuc900evb_ac97_devdata = { | ||
| 41 | .card = &nuc900evb_audio_machine, | ||
| 42 | .codec_dev = &soc_codec_dev_ac97, | ||
| 43 | }; | ||
| 44 | |||
| 45 | static struct platform_device *nuc900evb_asoc_dev; | ||
| 46 | |||
| 47 | static int __init nuc900evb_audio_init(void) | ||
| 48 | { | ||
| 49 | int ret; | ||
| 50 | |||
| 51 | ret = -ENOMEM; | ||
| 52 | nuc900evb_asoc_dev = platform_device_alloc("soc-audio", -1); | ||
| 53 | if (!nuc900evb_asoc_dev) | ||
| 54 | goto out; | ||
| 55 | |||
| 56 | /* nuc900 board audio device */ | ||
| 57 | platform_set_drvdata(nuc900evb_asoc_dev, &nuc900evb_ac97_devdata); | ||
| 58 | |||
| 59 | nuc900evb_ac97_devdata.dev = &nuc900evb_asoc_dev->dev; | ||
| 60 | ret = platform_device_add(nuc900evb_asoc_dev); | ||
| 61 | |||
| 62 | if (ret) { | ||
| 63 | platform_device_put(nuc900evb_asoc_dev); | ||
| 64 | nuc900evb_asoc_dev = NULL; | ||
| 65 | } | ||
| 66 | |||
| 67 | out: | ||
| 68 | return ret; | ||
| 69 | } | ||
| 70 | |||
| 71 | static void __exit nuc900evb_audio_exit(void) | ||
| 72 | { | ||
| 73 | platform_device_unregister(nuc900evb_asoc_dev); | ||
| 74 | } | ||
| 75 | |||
| 76 | module_init(nuc900evb_audio_init); | ||
| 77 | module_exit(nuc900evb_audio_exit); | ||
| 78 | |||
| 79 | MODULE_LICENSE("GPL"); | ||
| 80 | MODULE_DESCRIPTION("NUC900 Series ASoC audio support"); | ||
| 81 | MODULE_AUTHOR("Wan ZongShun"); | ||
diff --git a/sound/soc/nuc900/nuc900-audio.h b/sound/soc/nuc900/nuc900-audio.h new file mode 100644 index 000000000000..3038f519729f --- /dev/null +++ b/sound/soc/nuc900/nuc900-audio.h | |||
| @@ -0,0 +1,117 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2010 Nuvoton technology corporation. | ||
| 3 | * | ||
| 4 | * Wan ZongShun <mcuos.com@gmail.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation;version 2 of the License. | ||
| 9 | * | ||
| 10 | */ | ||
| 11 | |||
| 12 | #ifndef _NUC900_AUDIO_H | ||
| 13 | #define _NUC900_AUDIO_H | ||
| 14 | |||
| 15 | #include <linux/io.h> | ||
| 16 | |||
| 17 | /* Audio Control Registers */ | ||
| 18 | #define ACTL_CON 0x00 | ||
| 19 | #define ACTL_RESET 0x04 | ||
| 20 | #define ACTL_RDSTB 0x08 | ||
| 21 | #define ACTL_RDST_LENGTH 0x0C | ||
| 22 | #define ACTL_RDSTC 0x10 | ||
| 23 | #define ACTL_RSR 0x14 | ||
| 24 | #define ACTL_PDSTB 0x18 | ||
| 25 | #define ACTL_PDST_LENGTH 0x1C | ||
| 26 | #define ACTL_PDSTC 0x20 | ||
| 27 | #define ACTL_PSR 0x24 | ||
| 28 | #define ACTL_IISCON 0x28 | ||
| 29 | #define ACTL_ACCON 0x2C | ||
| 30 | #define ACTL_ACOS0 0x30 | ||
| 31 | #define ACTL_ACOS1 0x34 | ||
| 32 | #define ACTL_ACOS2 0x38 | ||
| 33 | #define ACTL_ACIS0 0x3C | ||
| 34 | #define ACTL_ACIS1 0x40 | ||
| 35 | #define ACTL_ACIS2 0x44 | ||
| 36 | #define ACTL_COUNTER 0x48 | ||
| 37 | |||
| 38 | /* bit definition of REG_ACTL_CON register */ | ||
| 39 | #define R_DMA_IRQ 0x1000 | ||
| 40 | #define T_DMA_IRQ 0x0800 | ||
| 41 | #define IIS_AC_PIN_SEL 0x0100 | ||
| 42 | #define FIFO_TH 0x0080 | ||
| 43 | #define ADC_EN 0x0010 | ||
| 44 | #define M80_EN 0x0008 | ||
| 45 | #define ACLINK_EN 0x0004 | ||
| 46 | #define IIS_EN 0x0002 | ||
| 47 | |||
| 48 | /* bit definition of REG_ACTL_RESET register */ | ||
| 49 | #define W5691_PLAY 0x20000 | ||
| 50 | #define ACTL_RESET_BIT 0x10000 | ||
| 51 | #define RECORD_RIGHT_CHNNEL 0x08000 | ||
| 52 | #define RECORD_LEFT_CHNNEL 0x04000 | ||
| 53 | #define PLAY_RIGHT_CHNNEL 0x02000 | ||
| 54 | #define PLAY_LEFT_CHNNEL 0x01000 | ||
| 55 | #define DAC_PLAY 0x00800 | ||
| 56 | #define ADC_RECORD 0x00400 | ||
| 57 | #define M80_PLAY 0x00200 | ||
| 58 | #define AC_RECORD 0x00100 | ||
| 59 | #define AC_PLAY 0x00080 | ||
| 60 | #define IIS_RECORD 0x00040 | ||
| 61 | #define IIS_PLAY 0x00020 | ||
| 62 | #define DAC_RESET 0x00010 | ||
| 63 | #define ADC_RESET 0x00008 | ||
| 64 | #define M80_RESET 0x00004 | ||
| 65 | #define AC_RESET 0x00002 | ||
| 66 | #define IIS_RESET 0x00001 | ||
| 67 | |||
| 68 | /* bit definition of REG_ACTL_ACCON register */ | ||
| 69 | #define AC_BCLK_PU_EN 0x20 | ||
| 70 | #define AC_R_FINISH 0x10 | ||
| 71 | #define AC_W_FINISH 0x08 | ||
| 72 | #define AC_W_RES 0x04 | ||
| 73 | #define AC_C_RES 0x02 | ||
| 74 | |||
| 75 | /* bit definition of ACTL_RSR register */ | ||
| 76 | #define R_FIFO_EMPTY 0x04 | ||
| 77 | #define R_DMA_END_IRQ 0x02 | ||
| 78 | #define R_DMA_MIDDLE_IRQ 0x01 | ||
| 79 | |||
| 80 | /* bit definition of ACTL_PSR register */ | ||
| 81 | #define P_FIFO_EMPTY 0x04 | ||
| 82 | #define P_DMA_END_IRQ 0x02 | ||
| 83 | #define P_DMA_MIDDLE_IRQ 0x01 | ||
| 84 | |||
| 85 | /* bit definition of ACTL_ACOS0 register */ | ||
| 86 | #define SLOT1_VALID 0x01 | ||
| 87 | #define SLOT2_VALID 0x02 | ||
| 88 | #define SLOT3_VALID 0x04 | ||
| 89 | #define SLOT4_VALID 0x08 | ||
| 90 | #define VALID_FRAME 0x10 | ||
| 91 | |||
| 92 | /* bit definition of ACTL_ACOS1 register */ | ||
| 93 | #define R_WB 0x80 | ||
| 94 | |||
| 95 | #define CODEC_READY 0x10 | ||
| 96 | #define RESET_PRSR 0x00 | ||
| 97 | #define AUDIO_WRITE(addr, val) __raw_writel(val, addr) | ||
| 98 | #define AUDIO_READ(addr) __raw_readl(addr) | ||
| 99 | |||
| 100 | struct nuc900_audio { | ||
| 101 | void __iomem *mmio; | ||
| 102 | spinlock_t lock; | ||
| 103 | dma_addr_t dma_addr[2]; | ||
| 104 | unsigned long buffersize[2]; | ||
| 105 | unsigned long irq_num; | ||
| 106 | struct snd_pcm_substream *substream; | ||
| 107 | struct resource *res; | ||
| 108 | struct clk *clk; | ||
| 109 | struct device *dev; | ||
| 110 | |||
| 111 | }; | ||
| 112 | |||
| 113 | extern struct nuc900_audio *nuc900_ac97_data; | ||
| 114 | extern struct snd_soc_dai nuc900_ac97_dai; | ||
| 115 | extern struct snd_soc_platform nuc900_soc_platform; | ||
| 116 | |||
| 117 | #endif /*end _NUC900_AUDIO_H */ | ||
diff --git a/sound/soc/nuc900/nuc900-pcm.c b/sound/soc/nuc900/nuc900-pcm.c new file mode 100644 index 000000000000..e81e803b3a63 --- /dev/null +++ b/sound/soc/nuc900/nuc900-pcm.c | |||
| @@ -0,0 +1,354 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2010 Nuvoton technology corporation. | ||
| 3 | * | ||
| 4 | * Wan ZongShun <mcuos.com@gmail.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation;version 2 of the License. | ||
| 9 | * | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/init.h> | ||
| 14 | #include <linux/io.h> | ||
| 15 | #include <linux/platform_device.h> | ||
| 16 | #include <linux/slab.h> | ||
| 17 | #include <linux/dma-mapping.h> | ||
| 18 | |||
| 19 | #include <sound/core.h> | ||
| 20 | #include <sound/pcm.h> | ||
| 21 | #include <sound/pcm_params.h> | ||
| 22 | #include <sound/soc.h> | ||
| 23 | |||
| 24 | #include <mach/hardware.h> | ||
| 25 | |||
| 26 | #include "nuc900-audio.h" | ||
| 27 | |||
| 28 | static const struct snd_pcm_hardware nuc900_pcm_hardware = { | ||
| 29 | .info = SNDRV_PCM_INFO_INTERLEAVED | | ||
| 30 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
| 31 | SNDRV_PCM_INFO_MMAP | | ||
| 32 | SNDRV_PCM_INFO_MMAP_VALID | | ||
| 33 | SNDRV_PCM_INFO_PAUSE | | ||
| 34 | SNDRV_PCM_INFO_RESUME, | ||
| 35 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
| 36 | .channels_min = 1, | ||
| 37 | .channels_max = 2, | ||
| 38 | .buffer_bytes_max = 4*1024, | ||
| 39 | .period_bytes_min = 1*1024, | ||
| 40 | .period_bytes_max = 4*1024, | ||
| 41 | .periods_min = 1, | ||
| 42 | .periods_max = 1024, | ||
| 43 | }; | ||
| 44 | |||
| 45 | static int nuc900_dma_hw_params(struct snd_pcm_substream *substream, | ||
| 46 | struct snd_pcm_hw_params *params) | ||
| 47 | { | ||
| 48 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 49 | struct nuc900_audio *nuc900_audio = runtime->private_data; | ||
| 50 | unsigned long flags; | ||
| 51 | int ret = 0; | ||
| 52 | |||
| 53 | spin_lock_irqsave(&nuc900_audio->lock, flags); | ||
| 54 | |||
| 55 | ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); | ||
| 56 | if (ret < 0) | ||
| 57 | return ret; | ||
| 58 | |||
| 59 | nuc900_audio->substream = substream; | ||
| 60 | nuc900_audio->dma_addr[substream->stream] = runtime->dma_addr; | ||
| 61 | nuc900_audio->buffersize[substream->stream] = | ||
| 62 | params_buffer_bytes(params); | ||
| 63 | |||
| 64 | spin_unlock_irqrestore(&nuc900_audio->lock, flags); | ||
| 65 | |||
| 66 | return ret; | ||
| 67 | } | ||
| 68 | |||
| 69 | static void nuc900_update_dma_register(struct snd_pcm_substream *substream, | ||
| 70 | dma_addr_t dma_addr, size_t count) | ||
| 71 | { | ||
| 72 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 73 | struct nuc900_audio *nuc900_audio = runtime->private_data; | ||
| 74 | void __iomem *mmio_addr, *mmio_len; | ||
| 75 | |||
| 76 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
| 77 | mmio_addr = nuc900_audio->mmio + ACTL_PDSTB; | ||
| 78 | mmio_len = nuc900_audio->mmio + ACTL_PDST_LENGTH; | ||
| 79 | } else { | ||
| 80 | mmio_addr = nuc900_audio->mmio + ACTL_RDSTB; | ||
| 81 | mmio_len = nuc900_audio->mmio + ACTL_RDST_LENGTH; | ||
| 82 | } | ||
| 83 | |||
| 84 | AUDIO_WRITE(mmio_addr, dma_addr); | ||
| 85 | AUDIO_WRITE(mmio_len, count); | ||
| 86 | } | ||
| 87 | |||
| 88 | static void nuc900_dma_start(struct snd_pcm_substream *substream) | ||
| 89 | { | ||
| 90 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 91 | struct nuc900_audio *nuc900_audio = runtime->private_data; | ||
| 92 | unsigned long val; | ||
| 93 | |||
| 94 | val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON); | ||
| 95 | val |= (T_DMA_IRQ | R_DMA_IRQ); | ||
| 96 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val); | ||
| 97 | } | ||
| 98 | |||
| 99 | static void nuc900_dma_stop(struct snd_pcm_substream *substream) | ||
| 100 | { | ||
| 101 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 102 | struct nuc900_audio *nuc900_audio = runtime->private_data; | ||
| 103 | unsigned long val; | ||
| 104 | |||
| 105 | val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON); | ||
| 106 | val &= ~(T_DMA_IRQ | R_DMA_IRQ); | ||
| 107 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val); | ||
| 108 | } | ||
| 109 | |||
| 110 | static irqreturn_t nuc900_dma_interrupt(int irq, void *dev_id) | ||
| 111 | { | ||
| 112 | struct snd_pcm_substream *substream = dev_id; | ||
| 113 | struct nuc900_audio *nuc900_audio = substream->runtime->private_data; | ||
| 114 | unsigned long val; | ||
| 115 | |||
| 116 | spin_lock(&nuc900_audio->lock); | ||
| 117 | |||
| 118 | val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON); | ||
| 119 | |||
| 120 | if (val & R_DMA_IRQ) { | ||
| 121 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val | R_DMA_IRQ); | ||
| 122 | |||
| 123 | val = AUDIO_READ(nuc900_audio->mmio + ACTL_RSR); | ||
| 124 | |||
| 125 | if (val & R_DMA_MIDDLE_IRQ) { | ||
| 126 | val |= R_DMA_MIDDLE_IRQ; | ||
| 127 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, val); | ||
| 128 | } | ||
| 129 | |||
| 130 | if (val & R_DMA_END_IRQ) { | ||
| 131 | val |= R_DMA_END_IRQ; | ||
| 132 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, val); | ||
| 133 | } | ||
| 134 | } else if (val & T_DMA_IRQ) { | ||
| 135 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val | T_DMA_IRQ); | ||
| 136 | |||
| 137 | val = AUDIO_READ(nuc900_audio->mmio + ACTL_PSR); | ||
| 138 | |||
| 139 | if (val & P_DMA_MIDDLE_IRQ) { | ||
| 140 | val |= P_DMA_MIDDLE_IRQ; | ||
| 141 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, val); | ||
| 142 | } | ||
| 143 | |||
| 144 | if (val & P_DMA_END_IRQ) { | ||
| 145 | val |= P_DMA_END_IRQ; | ||
| 146 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, val); | ||
| 147 | } | ||
| 148 | } else { | ||
| 149 | dev_err(nuc900_audio->dev, "Wrong DMA interrupt status!\n"); | ||
| 150 | spin_unlock(&nuc900_audio->lock); | ||
| 151 | return IRQ_HANDLED; | ||
| 152 | } | ||
| 153 | |||
| 154 | spin_unlock(&nuc900_audio->lock); | ||
| 155 | |||
| 156 | snd_pcm_period_elapsed(substream); | ||
| 157 | |||
| 158 | return IRQ_HANDLED; | ||
| 159 | } | ||
| 160 | |||
| 161 | static int nuc900_dma_hw_free(struct snd_pcm_substream *substream) | ||
| 162 | { | ||
| 163 | snd_pcm_lib_free_pages(substream); | ||
| 164 | return 0; | ||
| 165 | } | ||
| 166 | |||
| 167 | static int nuc900_dma_prepare(struct snd_pcm_substream *substream) | ||
| 168 | { | ||
| 169 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 170 | struct nuc900_audio *nuc900_audio = runtime->private_data; | ||
| 171 | unsigned long flags, val; | ||
| 172 | |||
| 173 | spin_lock_irqsave(&nuc900_audio->lock, flags); | ||
| 174 | |||
| 175 | nuc900_update_dma_register(substream, | ||
| 176 | nuc900_audio->dma_addr[substream->stream], | ||
| 177 | nuc900_audio->buffersize[substream->stream]); | ||
| 178 | |||
| 179 | val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET); | ||
| 180 | |||
| 181 | switch (runtime->channels) { | ||
| 182 | case 1: | ||
| 183 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
| 184 | val &= ~(PLAY_LEFT_CHNNEL | PLAY_RIGHT_CHNNEL); | ||
| 185 | val |= PLAY_RIGHT_CHNNEL; | ||
| 186 | } else { | ||
| 187 | val &= ~(RECORD_LEFT_CHNNEL | RECORD_RIGHT_CHNNEL); | ||
| 188 | val |= RECORD_RIGHT_CHNNEL; | ||
| 189 | } | ||
| 190 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val); | ||
| 191 | break; | ||
| 192 | case 2: | ||
| 193 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
| 194 | val |= (PLAY_LEFT_CHNNEL | PLAY_RIGHT_CHNNEL); | ||
| 195 | else | ||
| 196 | val |= (RECORD_LEFT_CHNNEL | RECORD_RIGHT_CHNNEL); | ||
| 197 | AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val); | ||
| 198 | break; | ||
| 199 | default: | ||
| 200 | return -EINVAL; | ||
| 201 | } | ||
| 202 | spin_unlock_irqrestore(&nuc900_audio->lock, flags); | ||
| 203 | return 0; | ||
| 204 | } | ||
| 205 | |||
| 206 | static int nuc900_dma_trigger(struct snd_pcm_substream *substream, int cmd) | ||
| 207 | { | ||
| 208 | int ret = 0; | ||
| 209 | |||
| 210 | switch (cmd) { | ||
| 211 | case SNDRV_PCM_TRIGGER_START: | ||
| 212 | case SNDRV_PCM_TRIGGER_RESUME: | ||
| 213 | nuc900_dma_start(substream); | ||
| 214 | break; | ||
| 215 | |||
| 216 | case SNDRV_PCM_TRIGGER_STOP: | ||
| 217 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
| 218 | nuc900_dma_stop(substream); | ||
| 219 | break; | ||
| 220 | |||
| 221 | default: | ||
| 222 | ret = -EINVAL; | ||
| 223 | break; | ||
| 224 | } | ||
| 225 | |||
| 226 | return ret; | ||
| 227 | } | ||
| 228 | |||
| 229 | int nuc900_dma_getposition(struct snd_pcm_substream *substream, | ||
| 230 | dma_addr_t *src, dma_addr_t *dst) | ||
| 231 | { | ||
| 232 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 233 | struct nuc900_audio *nuc900_audio = runtime->private_data; | ||
| 234 | |||
| 235 | if (src != NULL) | ||
| 236 | *src = AUDIO_READ(nuc900_audio->mmio + ACTL_PDSTC); | ||
| 237 | |||
| 238 | if (dst != NULL) | ||
| 239 | *dst = AUDIO_READ(nuc900_audio->mmio + ACTL_RDSTC); | ||
| 240 | |||
| 241 | return 0; | ||
| 242 | } | ||
| 243 | |||
| 244 | static snd_pcm_uframes_t nuc900_dma_pointer(struct snd_pcm_substream *substream) | ||
| 245 | { | ||
| 246 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 247 | dma_addr_t src, dst; | ||
| 248 | unsigned long res; | ||
| 249 | |||
| 250 | nuc900_dma_getposition(substream, &src, &dst); | ||
| 251 | |||
| 252 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
| 253 | res = dst - runtime->dma_addr; | ||
| 254 | else | ||
| 255 | res = src - runtime->dma_addr; | ||
| 256 | |||
| 257 | return bytes_to_frames(substream->runtime, res); | ||
| 258 | } | ||
| 259 | |||
| 260 | static int nuc900_dma_open(struct snd_pcm_substream *substream) | ||
| 261 | { | ||
| 262 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 263 | struct nuc900_audio *nuc900_audio; | ||
| 264 | |||
| 265 | snd_soc_set_runtime_hwparams(substream, &nuc900_pcm_hardware); | ||
| 266 | |||
| 267 | nuc900_audio = nuc900_ac97_data; | ||
| 268 | |||
| 269 | if (request_irq(nuc900_audio->irq_num, nuc900_dma_interrupt, | ||
| 270 | IRQF_DISABLED, "nuc900-dma", substream)) | ||
| 271 | return -EBUSY; | ||
| 272 | |||
| 273 | runtime->private_data = nuc900_audio; | ||
| 274 | |||
| 275 | return 0; | ||
| 276 | } | ||
| 277 | |||
| 278 | static int nuc900_dma_close(struct snd_pcm_substream *substream) | ||
| 279 | { | ||
| 280 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 281 | struct nuc900_audio *nuc900_audio = runtime->private_data; | ||
| 282 | |||
| 283 | free_irq(nuc900_audio->irq_num, substream); | ||
| 284 | |||
| 285 | return 0; | ||
| 286 | } | ||
| 287 | |||
| 288 | static int nuc900_dma_mmap(struct snd_pcm_substream *substream, | ||
| 289 | struct vm_area_struct *vma) | ||
| 290 | { | ||
| 291 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 292 | |||
| 293 | return dma_mmap_writecombine(substream->pcm->card->dev, vma, | ||
| 294 | runtime->dma_area, | ||
| 295 | runtime->dma_addr, | ||
| 296 | runtime->dma_bytes); | ||
| 297 | } | ||
| 298 | |||
| 299 | static struct snd_pcm_ops nuc900_dma_ops = { | ||
| 300 | .open = nuc900_dma_open, | ||
| 301 | .close = nuc900_dma_close, | ||
| 302 | .ioctl = snd_pcm_lib_ioctl, | ||
| 303 | .hw_params = nuc900_dma_hw_params, | ||
| 304 | .hw_free = nuc900_dma_hw_free, | ||
| 305 | .prepare = nuc900_dma_prepare, | ||
| 306 | .trigger = nuc900_dma_trigger, | ||
| 307 | .pointer = nuc900_dma_pointer, | ||
| 308 | .mmap = nuc900_dma_mmap, | ||
| 309 | }; | ||
| 310 | |||
| 311 | static void nuc900_dma_free_dma_buffers(struct snd_pcm *pcm) | ||
| 312 | { | ||
| 313 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
| 314 | } | ||
| 315 | |||
| 316 | static u64 nuc900_pcm_dmamask = DMA_BIT_MASK(32); | ||
| 317 | static int nuc900_dma_new(struct snd_card *card, | ||
| 318 | struct snd_soc_dai *dai, struct snd_pcm *pcm) | ||
| 319 | { | ||
| 320 | if (!card->dev->dma_mask) | ||
| 321 | card->dev->dma_mask = &nuc900_pcm_dmamask; | ||
| 322 | if (!card->dev->coherent_dma_mask) | ||
| 323 | card->dev->coherent_dma_mask = DMA_BIT_MASK(32); | ||
| 324 | |||
| 325 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | ||
| 326 | card->dev, 4 * 1024, (4 * 1024) - 1); | ||
| 327 | |||
| 328 | return 0; | ||
| 329 | } | ||
| 330 | |||
| 331 | struct snd_soc_platform nuc900_soc_platform = { | ||
| 332 | .name = "nuc900-dma", | ||
| 333 | .pcm_ops = &nuc900_dma_ops, | ||
| 334 | .pcm_new = nuc900_dma_new, | ||
| 335 | .pcm_free = nuc900_dma_free_dma_buffers, | ||
| 336 | } | ||
| 337 | EXPORT_SYMBOL_GPL(nuc900_soc_platform); | ||
| 338 | |||
| 339 | static int __init nuc900_soc_platform_init(void) | ||
| 340 | { | ||
| 341 | return snd_soc_register_platform(&nuc900_soc_platform); | ||
| 342 | } | ||
| 343 | |||
| 344 | static void __exit nuc900_soc_platform_exit(void) | ||
| 345 | { | ||
| 346 | snd_soc_unregister_platform(&nuc900_soc_platform); | ||
| 347 | } | ||
| 348 | |||
| 349 | module_init(nuc900_soc_platform_init); | ||
| 350 | module_exit(nuc900_soc_platform_exit); | ||
| 351 | |||
| 352 | MODULE_AUTHOR("Wan ZongShun, <mcuos.com@gmail.com>"); | ||
| 353 | MODULE_DESCRIPTION("nuc900 Audio DMA module"); | ||
| 354 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 6f44cb4d30b8..86f213905e2c 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c | |||
| @@ -59,6 +59,7 @@ struct omap_mcbsp_data { | |||
| 59 | int configured; | 59 | int configured; |
| 60 | unsigned int in_freq; | 60 | unsigned int in_freq; |
| 61 | int clk_div; | 61 | int clk_div; |
| 62 | int wlen; | ||
| 62 | }; | 63 | }; |
| 63 | 64 | ||
| 64 | #define to_mcbsp(priv) container_of((priv), struct omap_mcbsp_data, bus_id) | 65 | #define to_mcbsp(priv) container_of((priv), struct omap_mcbsp_data, bus_id) |
| @@ -154,20 +155,51 @@ static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream) | |||
| 154 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 155 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
| 155 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | 156 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; |
| 156 | struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data); | 157 | struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data); |
| 158 | struct omap_pcm_dma_data *dma_data; | ||
| 157 | int dma_op_mode = omap_mcbsp_get_dma_op_mode(mcbsp_data->bus_id); | 159 | int dma_op_mode = omap_mcbsp_get_dma_op_mode(mcbsp_data->bus_id); |
| 158 | int samples; | 160 | int words; |
| 161 | |||
| 162 | dma_data = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream); | ||
| 159 | 163 | ||
| 160 | /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ | 164 | /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ |
| 161 | if (dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) | 165 | if (dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) |
| 162 | samples = snd_pcm_lib_period_bytes(substream) >> 1; | 166 | /* |
| 167 | * Configure McBSP threshold based on either: | ||
| 168 | * packet_size, when the sDMA is in packet mode, or | ||
| 169 | * based on the period size. | ||
| 170 | */ | ||
| 171 | if (dma_data->packet_size) | ||
| 172 | words = dma_data->packet_size; | ||
| 173 | else | ||
| 174 | words = snd_pcm_lib_period_bytes(substream) / | ||
| 175 | (mcbsp_data->wlen / 8); | ||
| 163 | else | 176 | else |
| 164 | samples = 1; | 177 | words = 1; |
| 165 | 178 | ||
| 166 | /* Configure McBSP internal buffer usage */ | 179 | /* Configure McBSP internal buffer usage */ |
| 167 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 180 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
| 168 | omap_mcbsp_set_tx_threshold(mcbsp_data->bus_id, samples - 1); | 181 | omap_mcbsp_set_tx_threshold(mcbsp_data->bus_id, words); |
| 169 | else | 182 | else |
| 170 | omap_mcbsp_set_rx_threshold(mcbsp_data->bus_id, samples - 1); | 183 | omap_mcbsp_set_rx_threshold(mcbsp_data->bus_id, words); |
| 184 | } | ||
| 185 | |||
| 186 | static int omap_mcbsp_hwrule_min_buffersize(struct snd_pcm_hw_params *params, | ||
| 187 | struct snd_pcm_hw_rule *rule) | ||
| 188 | { | ||
| 189 | struct snd_interval *buffer_size = hw_param_interval(params, | ||
| 190 | SNDRV_PCM_HW_PARAM_BUFFER_SIZE); | ||
| 191 | struct snd_interval *channels = hw_param_interval(params, | ||
| 192 | SNDRV_PCM_HW_PARAM_CHANNELS); | ||
| 193 | struct omap_mcbsp_data *mcbsp_data = rule->private; | ||
| 194 | struct snd_interval frames; | ||
| 195 | int size; | ||
| 196 | |||
| 197 | snd_interval_any(&frames); | ||
| 198 | size = omap_mcbsp_get_fifo_size(mcbsp_data->bus_id); | ||
| 199 | |||
| 200 | frames.min = size / channels->min; | ||
| 201 | frames.integer = 1; | ||
| 202 | return snd_interval_refine(buffer_size, &frames); | ||
| 171 | } | 203 | } |
| 172 | 204 | ||
| 173 | static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, | 205 | static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, |
| @@ -182,33 +214,35 @@ static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, | |||
| 182 | if (!cpu_dai->active) | 214 | if (!cpu_dai->active) |
| 183 | err = omap_mcbsp_request(bus_id); | 215 | err = omap_mcbsp_request(bus_id); |
| 184 | 216 | ||
| 217 | /* | ||
| 218 | * OMAP3 McBSP FIFO is word structured. | ||
| 219 | * McBSP2 has 1024 + 256 = 1280 word long buffer, | ||
| 220 | * McBSP1,3,4,5 has 128 word long buffer | ||
| 221 | * This means that the size of the FIFO depends on the sample format. | ||
| 222 | * For example on McBSP3: | ||
| 223 | * 16bit samples: size is 128 * 2 = 256 bytes | ||
| 224 | * 32bit samples: size is 128 * 4 = 512 bytes | ||
| 225 | * It is simpler to place constraint for buffer and period based on | ||
| 226 | * channels. | ||
| 227 | * McBSP3 as example again (16 or 32 bit samples): | ||
| 228 | * 1 channel (mono): size is 128 frames (128 words) | ||
| 229 | * 2 channels (stereo): size is 128 / 2 = 64 frames (2 * 64 words) | ||
| 230 | * 4 channels: size is 128 / 4 = 32 frames (4 * 32 words) | ||
| 231 | */ | ||
| 185 | if (cpu_is_omap343x()) { | 232 | if (cpu_is_omap343x()) { |
| 186 | int dma_op_mode = omap_mcbsp_get_dma_op_mode(bus_id); | ||
| 187 | int max_period; | ||
| 188 | |||
| 189 | /* | 233 | /* |
| 190 | * McBSP2 in OMAP3 has 1024 * 32-bit internal audio buffer. | 234 | * Rule for the buffer size. We should not allow |
| 191 | * Set constraint for minimum buffer size to the same than FIFO | 235 | * smaller buffer than the FIFO size to avoid underruns |
| 192 | * size in order to avoid underruns in playback startup because | 236 | */ |
| 193 | * HW is keeping the DMA request active until FIFO is filled. | 237 | snd_pcm_hw_rule_add(substream->runtime, 0, |
| 194 | */ | 238 | SNDRV_PCM_HW_PARAM_CHANNELS, |
| 195 | if (bus_id == 1) | 239 | omap_mcbsp_hwrule_min_buffersize, |
| 196 | snd_pcm_hw_constraint_minmax(substream->runtime, | 240 | mcbsp_data, |
| 197 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, | 241 | SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1); |
| 198 | 4096, UINT_MAX); | 242 | |
| 199 | 243 | /* Make sure, that the period size is always even */ | |
| 200 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 244 | snd_pcm_hw_constraint_step(substream->runtime, 0, |
| 201 | max_period = omap_mcbsp_get_max_tx_threshold(bus_id); | 245 | SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2); |
| 202 | else | ||
| 203 | max_period = omap_mcbsp_get_max_rx_threshold(bus_id); | ||
| 204 | |||
| 205 | max_period++; | ||
| 206 | max_period <<= 1; | ||
| 207 | |||
| 208 | if (dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) | ||
| 209 | snd_pcm_hw_constraint_minmax(substream->runtime, | ||
| 210 | SNDRV_PCM_HW_PARAM_PERIOD_BYTES, | ||
| 211 | 32, max_period); | ||
| 212 | } | 246 | } |
| 213 | 247 | ||
| 214 | return err; | 248 | return err; |
| @@ -289,11 +323,14 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, | |||
| 289 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | 323 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; |
| 290 | struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data); | 324 | struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data); |
| 291 | struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs; | 325 | struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs; |
| 292 | int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id; | 326 | struct omap_pcm_dma_data *dma_data; |
| 327 | int dma, bus_id = mcbsp_data->bus_id; | ||
| 293 | int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT; | 328 | int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT; |
| 329 | int pkt_size = 0; | ||
| 294 | unsigned long port; | 330 | unsigned long port; |
| 295 | unsigned int format, div, framesize, master; | 331 | unsigned int format, div, framesize, master; |
| 296 | 332 | ||
| 333 | dma_data = &omap_mcbsp_dai_dma_params[cpu_dai->id][substream->stream]; | ||
| 297 | if (cpu_class_is_omap1()) { | 334 | if (cpu_class_is_omap1()) { |
| 298 | dma = omap1_dma_reqs[bus_id][substream->stream]; | 335 | dma = omap1_dma_reqs[bus_id][substream->stream]; |
| 299 | port = omap1_mcbsp_port[bus_id][substream->stream]; | 336 | port = omap1_mcbsp_port[bus_id][substream->stream]; |
| @@ -306,35 +343,74 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, | |||
| 306 | } else if (cpu_is_omap343x()) { | 343 | } else if (cpu_is_omap343x()) { |
| 307 | dma = omap24xx_dma_reqs[bus_id][substream->stream]; | 344 | dma = omap24xx_dma_reqs[bus_id][substream->stream]; |
| 308 | port = omap34xx_mcbsp_port[bus_id][substream->stream]; | 345 | port = omap34xx_mcbsp_port[bus_id][substream->stream]; |
| 309 | omap_mcbsp_dai_dma_params[id][substream->stream].set_threshold = | ||
| 310 | omap_mcbsp_set_threshold; | ||
| 311 | /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ | ||
| 312 | if (omap_mcbsp_get_dma_op_mode(bus_id) == | ||
| 313 | MCBSP_DMA_MODE_THRESHOLD) | ||
| 314 | sync_mode = OMAP_DMA_SYNC_FRAME; | ||
| 315 | } else { | 346 | } else { |
| 316 | return -ENODEV; | 347 | return -ENODEV; |
| 317 | } | 348 | } |
| 318 | omap_mcbsp_dai_dma_params[id][substream->stream].name = | ||
| 319 | substream->stream ? "Audio Capture" : "Audio Playback"; | ||
| 320 | omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma; | ||
| 321 | omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port; | ||
| 322 | omap_mcbsp_dai_dma_params[id][substream->stream].sync_mode = sync_mode; | ||
| 323 | switch (params_format(params)) { | 349 | switch (params_format(params)) { |
| 324 | case SNDRV_PCM_FORMAT_S16_LE: | 350 | case SNDRV_PCM_FORMAT_S16_LE: |
| 325 | omap_mcbsp_dai_dma_params[id][substream->stream].data_type = | 351 | dma_data->data_type = OMAP_DMA_DATA_TYPE_S16; |
| 326 | OMAP_DMA_DATA_TYPE_S16; | 352 | wlen = 16; |
| 327 | break; | 353 | break; |
| 328 | case SNDRV_PCM_FORMAT_S32_LE: | 354 | case SNDRV_PCM_FORMAT_S32_LE: |
| 329 | omap_mcbsp_dai_dma_params[id][substream->stream].data_type = | 355 | dma_data->data_type = OMAP_DMA_DATA_TYPE_S32; |
| 330 | OMAP_DMA_DATA_TYPE_S32; | 356 | wlen = 32; |
| 331 | break; | 357 | break; |
| 332 | default: | 358 | default: |
| 333 | return -EINVAL; | 359 | return -EINVAL; |
| 334 | } | 360 | } |
| 361 | if (cpu_is_omap343x()) { | ||
| 362 | dma_data->set_threshold = omap_mcbsp_set_threshold; | ||
| 363 | /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ | ||
| 364 | if (omap_mcbsp_get_dma_op_mode(bus_id) == | ||
| 365 | MCBSP_DMA_MODE_THRESHOLD) { | ||
| 366 | int period_words, max_thrsh; | ||
| 367 | |||
| 368 | period_words = params_period_bytes(params) / (wlen / 8); | ||
| 369 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
| 370 | max_thrsh = omap_mcbsp_get_max_tx_threshold( | ||
| 371 | mcbsp_data->bus_id); | ||
| 372 | else | ||
| 373 | max_thrsh = omap_mcbsp_get_max_rx_threshold( | ||
| 374 | mcbsp_data->bus_id); | ||
| 375 | /* | ||
| 376 | * If the period contains less or equal number of words, | ||
| 377 | * we are using the original threshold mode setup: | ||
| 378 | * McBSP threshold = sDMA frame size = period_size | ||
| 379 | * Otherwise we switch to sDMA packet mode: | ||
| 380 | * McBSP threshold = sDMA packet size | ||
| 381 | * sDMA frame size = period size | ||
| 382 | */ | ||
| 383 | if (period_words > max_thrsh) { | ||
| 384 | int divider = 0; | ||
| 385 | |||
| 386 | /* | ||
| 387 | * Look for the biggest threshold value, which | ||
| 388 | * divides the period size evenly. | ||
| 389 | */ | ||
| 390 | divider = period_words / max_thrsh; | ||
| 391 | if (period_words % max_thrsh) | ||
| 392 | divider++; | ||
| 393 | while (period_words % divider && | ||
| 394 | divider < period_words) | ||
| 395 | divider++; | ||
| 396 | if (divider == period_words) | ||
| 397 | return -EINVAL; | ||
| 398 | |||
| 399 | pkt_size = period_words / divider; | ||
| 400 | sync_mode = OMAP_DMA_SYNC_PACKET; | ||
| 401 | } else { | ||
| 402 | sync_mode = OMAP_DMA_SYNC_FRAME; | ||
| 403 | } | ||
| 404 | } | ||
| 405 | } | ||
| 335 | 406 | ||
| 336 | snd_soc_dai_set_dma_data(cpu_dai, substream, | 407 | dma_data->name = substream->stream ? "Audio Capture" : "Audio Playback"; |
| 337 | &omap_mcbsp_dai_dma_params[id][substream->stream]); | 408 | dma_data->dma_req = dma; |
| 409 | dma_data->port_addr = port; | ||
| 410 | dma_data->sync_mode = sync_mode; | ||
| 411 | dma_data->packet_size = pkt_size; | ||
| 412 | |||
| 413 | snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); | ||
| 338 | 414 | ||
| 339 | if (mcbsp_data->configured) { | 415 | if (mcbsp_data->configured) { |
| 340 | /* McBSP already configured by another stream */ | 416 | /* McBSP already configured by another stream */ |
| @@ -360,7 +436,6 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, | |||
| 360 | switch (params_format(params)) { | 436 | switch (params_format(params)) { |
| 361 | case SNDRV_PCM_FORMAT_S16_LE: | 437 | case SNDRV_PCM_FORMAT_S16_LE: |
| 362 | /* Set word lengths */ | 438 | /* Set word lengths */ |
| 363 | wlen = 16; | ||
| 364 | regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_16); | 439 | regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_16); |
| 365 | regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16); | 440 | regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16); |
| 366 | regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_16); | 441 | regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_16); |
| @@ -368,7 +443,6 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, | |||
| 368 | break; | 443 | break; |
| 369 | case SNDRV_PCM_FORMAT_S32_LE: | 444 | case SNDRV_PCM_FORMAT_S32_LE: |
| 370 | /* Set word lengths */ | 445 | /* Set word lengths */ |
| 371 | wlen = 32; | ||
| 372 | regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_32); | 446 | regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_32); |
| 373 | regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_32); | 447 | regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_32); |
| 374 | regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_32); | 448 | regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_32); |
| @@ -409,6 +483,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, | |||
| 409 | } | 483 | } |
| 410 | 484 | ||
| 411 | omap_mcbsp_config(bus_id, &mcbsp_data->regs); | 485 | omap_mcbsp_config(bus_id, &mcbsp_data->regs); |
| 486 | mcbsp_data->wlen = wlen; | ||
| 412 | mcbsp_data->configured = 1; | 487 | mcbsp_data->configured = 1; |
| 413 | 488 | ||
| 414 | return 0; | 489 | return 0; |
diff --git a/sound/soc/omap/omap3pandora.c b/sound/soc/omap/omap3pandora.c index 87ce842fa2e8..9eecac135bbb 100644 --- a/sound/soc/omap/omap3pandora.c +++ b/sound/soc/omap/omap3pandora.c | |||
| @@ -43,12 +43,14 @@ | |||
| 43 | 43 | ||
| 44 | static struct regulator *omap3pandora_dac_reg; | 44 | static struct regulator *omap3pandora_dac_reg; |
| 45 | 45 | ||
| 46 | static int omap3pandora_cmn_hw_params(struct snd_pcm_substream *substream, | 46 | static int omap3pandora_hw_params(struct snd_pcm_substream *substream, |
| 47 | struct snd_pcm_hw_params *params, unsigned int fmt) | 47 | struct snd_pcm_hw_params *params) |
| 48 | { | 48 | { |
| 49 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 49 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
| 50 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | 50 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; |
| 51 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | 51 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; |
| 52 | int fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | | ||
| 53 | SND_SOC_DAIFMT_CBS_CFS; | ||
| 52 | int ret; | 54 | int ret; |
| 53 | 55 | ||
| 54 | /* Set codec DAI configuration */ | 56 | /* Set codec DAI configuration */ |
| @@ -91,24 +93,6 @@ static int omap3pandora_cmn_hw_params(struct snd_pcm_substream *substream, | |||
| 91 | return 0; | 93 | return 0; |
| 92 | } | 94 | } |
| 93 | 95 | ||
| 94 | static int omap3pandora_out_hw_params(struct snd_pcm_substream *substream, | ||
| 95 | struct snd_pcm_hw_params *params) | ||
| 96 | { | ||
| 97 | return omap3pandora_cmn_hw_params(substream, params, | ||
| 98 | SND_SOC_DAIFMT_I2S | | ||
| 99 | SND_SOC_DAIFMT_IB_NF | | ||
| 100 | SND_SOC_DAIFMT_CBS_CFS); | ||
| 101 | } | ||
| 102 | |||
| 103 | static int omap3pandora_in_hw_params(struct snd_pcm_substream *substream, | ||
| 104 | struct snd_pcm_hw_params *params) | ||
| 105 | { | ||
| 106 | return omap3pandora_cmn_hw_params(substream, params, | ||
| 107 | SND_SOC_DAIFMT_I2S | | ||
| 108 | SND_SOC_DAIFMT_NB_NF | | ||
| 109 | SND_SOC_DAIFMT_CBS_CFS); | ||
| 110 | } | ||
| 111 | |||
| 112 | static int omap3pandora_dac_event(struct snd_soc_dapm_widget *w, | 96 | static int omap3pandora_dac_event(struct snd_soc_dapm_widget *w, |
| 113 | struct snd_kcontrol *k, int event) | 97 | struct snd_kcontrol *k, int event) |
| 114 | { | 98 | { |
| @@ -231,12 +215,8 @@ static int omap3pandora_in_init(struct snd_soc_codec *codec) | |||
| 231 | return snd_soc_dapm_sync(codec); | 215 | return snd_soc_dapm_sync(codec); |
| 232 | } | 216 | } |
| 233 | 217 | ||
| 234 | static struct snd_soc_ops omap3pandora_out_ops = { | 218 | static struct snd_soc_ops omap3pandora_ops = { |
| 235 | .hw_params = omap3pandora_out_hw_params, | 219 | .hw_params = omap3pandora_hw_params, |
| 236 | }; | ||
| 237 | |||
| 238 | static struct snd_soc_ops omap3pandora_in_ops = { | ||
| 239 | .hw_params = omap3pandora_in_hw_params, | ||
| 240 | }; | 220 | }; |
| 241 | 221 | ||
| 242 | /* Digital audio interface glue - connects codec <--> CPU */ | 222 | /* Digital audio interface glue - connects codec <--> CPU */ |
| @@ -246,14 +226,14 @@ static struct snd_soc_dai_link omap3pandora_dai[] = { | |||
| 246 | .stream_name = "HiFi Out", | 226 | .stream_name = "HiFi Out", |
| 247 | .cpu_dai = &omap_mcbsp_dai[0], | 227 | .cpu_dai = &omap_mcbsp_dai[0], |
| 248 | .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI], | 228 | .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI], |
| 249 | .ops = &omap3pandora_out_ops, | 229 | .ops = &omap3pandora_ops, |
| 250 | .init = omap3pandora_out_init, | 230 | .init = omap3pandora_out_init, |
| 251 | }, { | 231 | }, { |
| 252 | .name = "TWL4030", | 232 | .name = "TWL4030", |
| 253 | .stream_name = "Line/Mic In", | 233 | .stream_name = "Line/Mic In", |
| 254 | .cpu_dai = &omap_mcbsp_dai[1], | 234 | .cpu_dai = &omap_mcbsp_dai[1], |
| 255 | .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI], | 235 | .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI], |
| 256 | .ops = &omap3pandora_in_ops, | 236 | .ops = &omap3pandora_ops, |
| 257 | .init = omap3pandora_in_init, | 237 | .init = omap3pandora_in_init, |
| 258 | } | 238 | } |
| 259 | }; | 239 | }; |
diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c index 47d831ef2dbb..88052d29617f 100644 --- a/sound/soc/omap/rx51.c +++ b/sound/soc/omap/rx51.c | |||
| @@ -27,6 +27,7 @@ | |||
| 27 | #include <linux/gpio.h> | 27 | #include <linux/gpio.h> |
| 28 | #include <linux/platform_device.h> | 28 | #include <linux/platform_device.h> |
| 29 | #include <sound/core.h> | 29 | #include <sound/core.h> |
| 30 | #include <sound/jack.h> | ||
| 30 | #include <sound/pcm.h> | 31 | #include <sound/pcm.h> |
| 31 | #include <sound/soc.h> | 32 | #include <sound/soc.h> |
| 32 | #include <sound/soc-dapm.h> | 33 | #include <sound/soc-dapm.h> |
| @@ -37,14 +38,22 @@ | |||
| 37 | #include "omap-pcm.h" | 38 | #include "omap-pcm.h" |
| 38 | #include "../codecs/tlv320aic3x.h" | 39 | #include "../codecs/tlv320aic3x.h" |
| 39 | 40 | ||
| 41 | #define RX51_TVOUT_SEL_GPIO 40 | ||
| 42 | #define RX51_JACK_DETECT_GPIO 177 | ||
| 40 | /* | 43 | /* |
| 41 | * REVISIT: TWL4030 GPIO base in RX-51. Now statically defined to 192. This | 44 | * REVISIT: TWL4030 GPIO base in RX-51. Now statically defined to 192. This |
| 42 | * gpio is reserved in arch/arm/mach-omap2/board-rx51-peripherals.c | 45 | * gpio is reserved in arch/arm/mach-omap2/board-rx51-peripherals.c |
| 43 | */ | 46 | */ |
| 44 | #define RX51_SPEAKER_AMP_TWL_GPIO (192 + 7) | 47 | #define RX51_SPEAKER_AMP_TWL_GPIO (192 + 7) |
| 45 | 48 | ||
| 49 | enum { | ||
| 50 | RX51_JACK_DISABLED, | ||
| 51 | RX51_JACK_TVOUT, /* tv-out */ | ||
| 52 | }; | ||
| 53 | |||
| 46 | static int rx51_spk_func; | 54 | static int rx51_spk_func; |
| 47 | static int rx51_dmic_func; | 55 | static int rx51_dmic_func; |
| 56 | static int rx51_jack_func; | ||
| 48 | 57 | ||
| 49 | static void rx51_ext_control(struct snd_soc_codec *codec) | 58 | static void rx51_ext_control(struct snd_soc_codec *codec) |
| 50 | { | 59 | { |
| @@ -57,6 +66,9 @@ static void rx51_ext_control(struct snd_soc_codec *codec) | |||
| 57 | else | 66 | else |
| 58 | snd_soc_dapm_disable_pin(codec, "DMic"); | 67 | snd_soc_dapm_disable_pin(codec, "DMic"); |
| 59 | 68 | ||
| 69 | gpio_set_value(RX51_TVOUT_SEL_GPIO, | ||
| 70 | rx51_jack_func == RX51_JACK_TVOUT); | ||
| 71 | |||
| 60 | snd_soc_dapm_sync(codec); | 72 | snd_soc_dapm_sync(codec); |
| 61 | } | 73 | } |
| 62 | 74 | ||
| @@ -162,6 +174,40 @@ static int rx51_set_input(struct snd_kcontrol *kcontrol, | |||
| 162 | return 1; | 174 | return 1; |
| 163 | } | 175 | } |
| 164 | 176 | ||
| 177 | static int rx51_get_jack(struct snd_kcontrol *kcontrol, | ||
| 178 | struct snd_ctl_elem_value *ucontrol) | ||
| 179 | { | ||
| 180 | ucontrol->value.integer.value[0] = rx51_jack_func; | ||
| 181 | |||
| 182 | return 0; | ||
| 183 | } | ||
| 184 | |||
| 185 | static int rx51_set_jack(struct snd_kcontrol *kcontrol, | ||
| 186 | struct snd_ctl_elem_value *ucontrol) | ||
| 187 | { | ||
| 188 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
| 189 | |||
| 190 | if (rx51_jack_func == ucontrol->value.integer.value[0]) | ||
| 191 | return 0; | ||
| 192 | |||
| 193 | rx51_jack_func = ucontrol->value.integer.value[0]; | ||
| 194 | rx51_ext_control(codec); | ||
| 195 | |||
| 196 | return 1; | ||
| 197 | } | ||
| 198 | |||
| 199 | static struct snd_soc_jack rx51_av_jack; | ||
| 200 | |||
| 201 | static struct snd_soc_jack_gpio rx51_av_jack_gpios[] = { | ||
| 202 | { | ||
| 203 | .gpio = RX51_JACK_DETECT_GPIO, | ||
| 204 | .name = "avdet-gpio", | ||
| 205 | .report = SND_JACK_VIDEOOUT, | ||
| 206 | .invert = 1, | ||
| 207 | .debounce_time = 200, | ||
| 208 | }, | ||
| 209 | }; | ||
| 210 | |||
| 165 | static const struct snd_soc_dapm_widget aic34_dapm_widgets[] = { | 211 | static const struct snd_soc_dapm_widget aic34_dapm_widgets[] = { |
| 166 | SND_SOC_DAPM_SPK("Ext Spk", rx51_spk_event), | 212 | SND_SOC_DAPM_SPK("Ext Spk", rx51_spk_event), |
| 167 | SND_SOC_DAPM_MIC("DMic", NULL), | 213 | SND_SOC_DAPM_MIC("DMic", NULL), |
| @@ -177,10 +223,12 @@ static const struct snd_soc_dapm_route audio_map[] = { | |||
| 177 | 223 | ||
| 178 | static const char *spk_function[] = {"Off", "On"}; | 224 | static const char *spk_function[] = {"Off", "On"}; |
| 179 | static const char *input_function[] = {"ADC", "Digital Mic"}; | 225 | static const char *input_function[] = {"ADC", "Digital Mic"}; |
| 226 | static const char *jack_function[] = {"Off", "TV-OUT"}; | ||
| 180 | 227 | ||
| 181 | static const struct soc_enum rx51_enum[] = { | 228 | static const struct soc_enum rx51_enum[] = { |
| 182 | SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function), | 229 | SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function), |
| 183 | SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function), | 230 | SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function), |
| 231 | SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(jack_function), jack_function), | ||
| 184 | }; | 232 | }; |
| 185 | 233 | ||
| 186 | static const struct snd_kcontrol_new aic34_rx51_controls[] = { | 234 | static const struct snd_kcontrol_new aic34_rx51_controls[] = { |
| @@ -188,10 +236,13 @@ static const struct snd_kcontrol_new aic34_rx51_controls[] = { | |||
| 188 | rx51_get_spk, rx51_set_spk), | 236 | rx51_get_spk, rx51_set_spk), |
| 189 | SOC_ENUM_EXT("Input Select", rx51_enum[1], | 237 | SOC_ENUM_EXT("Input Select", rx51_enum[1], |
| 190 | rx51_get_input, rx51_set_input), | 238 | rx51_get_input, rx51_set_input), |
| 239 | SOC_ENUM_EXT("Jack Function", rx51_enum[2], | ||
| 240 | rx51_get_jack, rx51_set_jack), | ||
| 191 | }; | 241 | }; |
| 192 | 242 | ||
| 193 | static int rx51_aic34_init(struct snd_soc_codec *codec) | 243 | static int rx51_aic34_init(struct snd_soc_codec *codec) |
| 194 | { | 244 | { |
| 245 | struct snd_soc_card *card = codec->socdev->card; | ||
| 195 | int err; | 246 | int err; |
| 196 | 247 | ||
| 197 | /* Set up NC codec pins */ | 248 | /* Set up NC codec pins */ |
| @@ -214,7 +265,16 @@ static int rx51_aic34_init(struct snd_soc_codec *codec) | |||
| 214 | 265 | ||
| 215 | snd_soc_dapm_sync(codec); | 266 | snd_soc_dapm_sync(codec); |
| 216 | 267 | ||
| 217 | return 0; | 268 | /* AV jack detection */ |
| 269 | err = snd_soc_jack_new(card, "AV Jack", | ||
| 270 | SND_JACK_VIDEOOUT, &rx51_av_jack); | ||
| 271 | if (err) | ||
| 272 | return err; | ||
| 273 | err = snd_soc_jack_add_gpios(&rx51_av_jack, | ||
| 274 | ARRAY_SIZE(rx51_av_jack_gpios), | ||
| 275 | rx51_av_jack_gpios); | ||
| 276 | |||
| 277 | return err; | ||
| 218 | } | 278 | } |
| 219 | 279 | ||
| 220 | /* Digital audio interface glue - connects codec <--> CPU */ | 280 | /* Digital audio interface glue - connects codec <--> CPU */ |
| @@ -259,6 +319,11 @@ static int __init rx51_soc_init(void) | |||
| 259 | if (!machine_is_nokia_rx51()) | 319 | if (!machine_is_nokia_rx51()) |
| 260 | return -ENODEV; | 320 | return -ENODEV; |
| 261 | 321 | ||
| 322 | err = gpio_request(RX51_TVOUT_SEL_GPIO, "tvout_sel"); | ||
| 323 | if (err) | ||
| 324 | goto err_gpio_tvout_sel; | ||
| 325 | gpio_direction_output(RX51_TVOUT_SEL_GPIO, 0); | ||
| 326 | |||
| 262 | rx51_snd_device = platform_device_alloc("soc-audio", -1); | 327 | rx51_snd_device = platform_device_alloc("soc-audio", -1); |
| 263 | if (!rx51_snd_device) { | 328 | if (!rx51_snd_device) { |
| 264 | err = -ENOMEM; | 329 | err = -ENOMEM; |
| @@ -277,13 +342,19 @@ static int __init rx51_soc_init(void) | |||
| 277 | err2: | 342 | err2: |
| 278 | platform_device_put(rx51_snd_device); | 343 | platform_device_put(rx51_snd_device); |
| 279 | err1: | 344 | err1: |
| 345 | gpio_free(RX51_TVOUT_SEL_GPIO); | ||
| 346 | err_gpio_tvout_sel: | ||
| 280 | 347 | ||
| 281 | return err; | 348 | return err; |
| 282 | } | 349 | } |
| 283 | 350 | ||
| 284 | static void __exit rx51_soc_exit(void) | 351 | static void __exit rx51_soc_exit(void) |
| 285 | { | 352 | { |
| 353 | snd_soc_jack_free_gpios(&rx51_av_jack, ARRAY_SIZE(rx51_av_jack_gpios), | ||
| 354 | rx51_av_jack_gpios); | ||
| 355 | |||
| 286 | platform_device_unregister(rx51_snd_device); | 356 | platform_device_unregister(rx51_snd_device); |
| 357 | gpio_free(RX51_TVOUT_SEL_GPIO); | ||
| 287 | } | 358 | } |
| 288 | 359 | ||
| 289 | module_init(rx51_soc_init); | 360 | module_init(rx51_soc_init); |
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig index 2a7cc222d098..213963ac3c28 100644 --- a/sound/soc/s3c24xx/Kconfig +++ b/sound/soc/s3c24xx/Kconfig | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | config SND_S3C24XX_SOC | 1 | config SND_S3C24XX_SOC |
| 2 | tristate "SoC Audio for the Samsung S3CXXXX chips" | 2 | tristate "SoC Audio for the Samsung S3CXXXX chips" |
| 3 | depends on ARCH_S3C2410 || ARCH_S3C64XX | 3 | depends on ARCH_S3C2410 || ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210 |
| 4 | select S3C64XX_DMA if ARCH_S3C64XX | 4 | select S3C64XX_DMA if ARCH_S3C64XX |
| 5 | help | 5 | help |
| 6 | Say Y or M if you want to add support for codecs attached to | 6 | Say Y or M if you want to add support for codecs attached to |
| @@ -120,8 +120,14 @@ config SND_S3C24XX_SOC_SIMTEC_HERMES | |||
| 120 | 120 | ||
| 121 | config SND_SOC_SMDK_WM9713 | 121 | config SND_SOC_SMDK_WM9713 |
| 122 | tristate "SoC AC97 Audio support for SMDK with WM9713" | 122 | tristate "SoC AC97 Audio support for SMDK with WM9713" |
| 123 | depends on SND_S3C24XX_SOC && MACH_SMDK6410 | 123 | depends on SND_S3C24XX_SOC && (MACH_SMDK6410 || MACH_SMDKC100 || MACH_SMDKV210 || MACH_SMDKC110) |
| 124 | select SND_SOC_WM9713 | 124 | select SND_SOC_WM9713 |
| 125 | select SND_S3C_SOC_AC97 | 125 | select SND_S3C_SOC_AC97 |
| 126 | help | 126 | help |
| 127 | Sat Y if you want to add support for SoC audio on the SMDK. | 127 | Sat Y if you want to add support for SoC audio on the SMDK. |
| 128 | |||
| 129 | config SND_S3C64XX_SOC_SMARTQ | ||
| 130 | tristate "SoC I2S Audio support for SmartQ board" | ||
| 131 | depends on SND_S3C24XX_SOC && MACH_SMARTQ | ||
| 132 | select SND_S3C64XX_SOC_I2S | ||
| 133 | select SND_SOC_WM8750 | ||
diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile index 81d8dc503f87..50172c385d90 100644 --- a/sound/soc/s3c24xx/Makefile +++ b/sound/soc/s3c24xx/Makefile | |||
| @@ -29,6 +29,7 @@ snd-soc-s3c24xx-simtec-hermes-objs := s3c24xx_simtec_hermes.o | |||
| 29 | snd-soc-s3c24xx-simtec-tlv320aic23-objs := s3c24xx_simtec_tlv320aic23.o | 29 | snd-soc-s3c24xx-simtec-tlv320aic23-objs := s3c24xx_simtec_tlv320aic23.o |
| 30 | snd-soc-smdk64xx-wm8580-objs := smdk64xx_wm8580.o | 30 | snd-soc-smdk64xx-wm8580-objs := smdk64xx_wm8580.o |
| 31 | snd-soc-smdk-wm9713-objs := smdk_wm9713.o | 31 | snd-soc-smdk-wm9713-objs := smdk_wm9713.o |
| 32 | snd-soc-s3c64xx-smartq-wm8987-objs := smartq_wm8987.o | ||
| 32 | 33 | ||
| 33 | obj-$(CONFIG_SND_S3C24XX_SOC_JIVE_WM8750) += snd-soc-jive-wm8750.o | 34 | obj-$(CONFIG_SND_S3C24XX_SOC_JIVE_WM8750) += snd-soc-jive-wm8750.o |
| 34 | obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o | 35 | obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o |
| @@ -41,3 +42,4 @@ obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o | |||
| 41 | obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o | 42 | obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o |
| 42 | obj-$(CONFIG_SND_S3C64XX_SOC_WM8580) += snd-soc-smdk64xx-wm8580.o | 43 | obj-$(CONFIG_SND_S3C64XX_SOC_WM8580) += snd-soc-smdk64xx-wm8580.o |
| 43 | obj-$(CONFIG_SND_SOC_SMDK_WM9713) += snd-soc-smdk-wm9713.o | 44 | obj-$(CONFIG_SND_SOC_SMDK_WM9713) += snd-soc-smdk-wm9713.o |
| 45 | obj-$(CONFIG_SND_S3C64XX_SOC_SMARTQ) += snd-soc-s3c64xx-smartq-wm8987.o | ||
diff --git a/sound/soc/s3c24xx/s3c-ac97.c b/sound/soc/s3c24xx/s3c-ac97.c index ecf4fd04ae96..31f6d45b6384 100644 --- a/sound/soc/s3c24xx/s3c-ac97.c +++ b/sound/soc/s3c24xx/s3c-ac97.c | |||
| @@ -31,7 +31,6 @@ | |||
| 31 | #define AC_CMD_DATA(x) (x & 0xffff) | 31 | #define AC_CMD_DATA(x) (x & 0xffff) |
| 32 | 32 | ||
| 33 | struct s3c_ac97_info { | 33 | struct s3c_ac97_info { |
| 34 | unsigned state; | ||
| 35 | struct clk *ac97_clk; | 34 | struct clk *ac97_clk; |
| 36 | void __iomem *regs; | 35 | void __iomem *regs; |
| 37 | struct mutex lock; | 36 | struct mutex lock; |
diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.c b/sound/soc/s3c24xx/s3c-i2s-v2.c index 13311c8cf965..64376b2aac73 100644 --- a/sound/soc/s3c24xx/s3c-i2s-v2.c +++ b/sound/soc/s3c24xx/s3c-i2s-v2.c | |||
| @@ -32,7 +32,8 @@ | |||
| 32 | 32 | ||
| 33 | #undef S3C_IIS_V2_SUPPORTED | 33 | #undef S3C_IIS_V2_SUPPORTED |
| 34 | 34 | ||
| 35 | #if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413) | 35 | #if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413) \ |
| 36 | || defined(CONFIG_CPU_S5PV210) | ||
| 36 | #define S3C_IIS_V2_SUPPORTED | 37 | #define S3C_IIS_V2_SUPPORTED |
| 37 | #endif | 38 | #endif |
| 38 | 39 | ||
diff --git a/sound/soc/s3c24xx/smartq_wm8987.c b/sound/soc/s3c24xx/smartq_wm8987.c new file mode 100644 index 000000000000..b480348140b0 --- /dev/null +++ b/sound/soc/s3c24xx/smartq_wm8987.c | |||
| @@ -0,0 +1,295 @@ | |||
| 1 | /* sound/soc/s3c24xx/smartq_wm8987.c | ||
| 2 | * | ||
| 3 | * Copyright 2010 Maurus Cuelenaere <mcuelenaere@gmail.com> | ||
| 4 | * | ||
| 5 | * Based on smdk6410_wm8987.c | ||
| 6 | * Copyright 2007 Wolfson Microelectronics PLC. - linux@wolfsonmicro.com | ||
| 7 | * Graeme Gregory - graeme.gregory@wolfsonmicro.com | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify it | ||
| 10 | * under the terms of the GNU General Public License as published by the | ||
| 11 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 12 | * option) any later version. | ||
| 13 | * | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/module.h> | ||
| 17 | #include <linux/platform_device.h> | ||
| 18 | #include <linux/gpio.h> | ||
| 19 | |||
| 20 | #include <sound/pcm.h> | ||
| 21 | #include <sound/pcm_params.h> | ||
| 22 | #include <sound/soc-dapm.h> | ||
| 23 | #include <sound/jack.h> | ||
| 24 | |||
| 25 | #include <asm/mach-types.h> | ||
| 26 | |||
| 27 | #include "s3c-dma.h" | ||
| 28 | #include "s3c64xx-i2s.h" | ||
| 29 | |||
| 30 | #include "../codecs/wm8750.h" | ||
| 31 | |||
| 32 | /* | ||
| 33 | * WM8987 is register compatible with WM8750, so using that as base driver. | ||
| 34 | */ | ||
| 35 | |||
| 36 | static struct snd_soc_card snd_soc_smartq; | ||
| 37 | |||
| 38 | static int smartq_hifi_hw_params(struct snd_pcm_substream *substream, | ||
| 39 | struct snd_pcm_hw_params *params) | ||
| 40 | { | ||
| 41 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
| 42 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
| 43 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
| 44 | struct s3c_i2sv2_rate_calc div; | ||
| 45 | unsigned int clk = 0; | ||
| 46 | int ret; | ||
| 47 | |||
| 48 | s3c_i2sv2_iis_calc_rate(&div, NULL, params_rate(params), | ||
| 49 | s3c_i2sv2_get_clock(cpu_dai)); | ||
| 50 | |||
| 51 | switch (params_rate(params)) { | ||
| 52 | case 8000: | ||
| 53 | case 16000: | ||
| 54 | case 32000: | ||
| 55 | case 48000: | ||
| 56 | case 96000: | ||
| 57 | clk = 12288000; | ||
| 58 | break; | ||
| 59 | case 11025: | ||
| 60 | case 22050: | ||
| 61 | case 44100: | ||
| 62 | case 88200: | ||
| 63 | clk = 11289600; | ||
| 64 | break; | ||
| 65 | } | ||
| 66 | |||
| 67 | /* set codec DAI configuration */ | ||
| 68 | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | | ||
| 69 | SND_SOC_DAIFMT_NB_NF | | ||
| 70 | SND_SOC_DAIFMT_CBS_CFS); | ||
| 71 | if (ret < 0) | ||
| 72 | return ret; | ||
| 73 | |||
| 74 | /* set cpu DAI configuration */ | ||
| 75 | ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | | ||
| 76 | SND_SOC_DAIFMT_NB_NF | | ||
| 77 | SND_SOC_DAIFMT_CBS_CFS); | ||
| 78 | if (ret < 0) | ||
| 79 | return ret; | ||
| 80 | |||
| 81 | /* set the codec system clock for DAC and ADC */ | ||
| 82 | ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk, | ||
| 83 | SND_SOC_CLOCK_IN); | ||
| 84 | if (ret < 0) | ||
| 85 | return ret; | ||
| 86 | |||
| 87 | /* set MCLK division for sample rate */ | ||
| 88 | ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_I2SV2_DIV_RCLK, div.fs_div); | ||
| 89 | if (ret < 0) | ||
| 90 | return ret; | ||
| 91 | |||
| 92 | /* set prescaler division for sample rate */ | ||
| 93 | ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_I2SV2_DIV_PRESCALER, | ||
| 94 | div.clk_div - 1); | ||
| 95 | if (ret < 0) | ||
| 96 | return ret; | ||
| 97 | |||
| 98 | return 0; | ||
| 99 | } | ||
| 100 | |||
| 101 | /* | ||
| 102 | * SmartQ WM8987 HiFi DAI operations. | ||
| 103 | */ | ||
| 104 | static struct snd_soc_ops smartq_hifi_ops = { | ||
| 105 | .hw_params = smartq_hifi_hw_params, | ||
| 106 | }; | ||
| 107 | |||
| 108 | static struct snd_soc_jack smartq_jack; | ||
| 109 | |||
| 110 | static struct snd_soc_jack_pin smartq_jack_pins[] = { | ||
| 111 | /* Disable speaker when headphone is plugged in */ | ||
| 112 | { | ||
| 113 | .pin = "Internal Speaker", | ||
| 114 | .mask = SND_JACK_HEADPHONE, | ||
| 115 | }, | ||
| 116 | }; | ||
| 117 | |||
| 118 | static struct snd_soc_jack_gpio smartq_jack_gpios[] = { | ||
| 119 | { | ||
| 120 | .gpio = S3C64XX_GPL(12), | ||
| 121 | .name = "headphone detect", | ||
| 122 | .report = SND_JACK_HEADPHONE, | ||
| 123 | .debounce_time = 200, | ||
| 124 | }, | ||
| 125 | }; | ||
| 126 | |||
| 127 | static const struct snd_kcontrol_new wm8987_smartq_controls[] = { | ||
| 128 | SOC_DAPM_PIN_SWITCH("Internal Speaker"), | ||
| 129 | SOC_DAPM_PIN_SWITCH("Headphone Jack"), | ||
| 130 | SOC_DAPM_PIN_SWITCH("Internal Mic"), | ||
| 131 | }; | ||
| 132 | |||
| 133 | static int smartq_speaker_event(struct snd_soc_dapm_widget *w, | ||
| 134 | struct snd_kcontrol *k, | ||
| 135 | int event) | ||
| 136 | { | ||
| 137 | gpio_set_value(S3C64XX_GPK(12), SND_SOC_DAPM_EVENT_OFF(event)); | ||
| 138 | |||
| 139 | return 0; | ||
| 140 | } | ||
| 141 | |||
| 142 | static const struct snd_soc_dapm_widget wm8987_dapm_widgets[] = { | ||
| 143 | SND_SOC_DAPM_SPK("Internal Speaker", smartq_speaker_event), | ||
| 144 | SND_SOC_DAPM_HP("Headphone Jack", NULL), | ||
| 145 | SND_SOC_DAPM_MIC("Internal Mic", NULL), | ||
| 146 | }; | ||
| 147 | |||
| 148 | static const struct snd_soc_dapm_route audio_map[] = { | ||
| 149 | {"Headphone Jack", NULL, "LOUT2"}, | ||
| 150 | {"Headphone Jack", NULL, "ROUT2"}, | ||
| 151 | |||
| 152 | {"Internal Speaker", NULL, "LOUT2"}, | ||
| 153 | {"Internal Speaker", NULL, "ROUT2"}, | ||
| 154 | |||
| 155 | {"Mic Bias", NULL, "Internal Mic"}, | ||
| 156 | {"LINPUT2", NULL, "Mic Bias"}, | ||
| 157 | }; | ||
| 158 | |||
| 159 | static int smartq_wm8987_init(struct snd_soc_codec *codec) | ||
| 160 | { | ||
| 161 | int err = 0; | ||
| 162 | |||
| 163 | /* Add SmartQ specific widgets */ | ||
| 164 | snd_soc_dapm_new_controls(codec, wm8987_dapm_widgets, | ||
| 165 | ARRAY_SIZE(wm8987_dapm_widgets)); | ||
| 166 | |||
| 167 | /* add SmartQ specific controls */ | ||
| 168 | err = snd_soc_add_controls(codec, wm8987_smartq_controls, | ||
| 169 | ARRAY_SIZE(wm8987_smartq_controls)); | ||
| 170 | |||
| 171 | if (err < 0) | ||
| 172 | return err; | ||
| 173 | |||
| 174 | /* setup SmartQ specific audio path */ | ||
| 175 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | ||
| 176 | |||
| 177 | /* set endpoints to not connected */ | ||
| 178 | snd_soc_dapm_nc_pin(codec, "LINPUT1"); | ||
| 179 | snd_soc_dapm_nc_pin(codec, "RINPUT1"); | ||
| 180 | snd_soc_dapm_nc_pin(codec, "OUT3"); | ||
| 181 | snd_soc_dapm_nc_pin(codec, "ROUT1"); | ||
| 182 | |||
| 183 | /* set endpoints to default off mode */ | ||
| 184 | snd_soc_dapm_enable_pin(codec, "Internal Speaker"); | ||
| 185 | snd_soc_dapm_enable_pin(codec, "Internal Mic"); | ||
| 186 | snd_soc_dapm_disable_pin(codec, "Headphone Jack"); | ||
| 187 | |||
| 188 | err = snd_soc_dapm_sync(codec); | ||
| 189 | if (err) | ||
| 190 | return err; | ||
| 191 | |||
| 192 | /* Headphone jack detection */ | ||
| 193 | err = snd_soc_jack_new(&snd_soc_smartq, "Headphone Jack", | ||
| 194 | SND_JACK_HEADPHONE, &smartq_jack); | ||
| 195 | if (err) | ||
| 196 | return err; | ||
| 197 | |||
| 198 | err = snd_soc_jack_add_pins(&smartq_jack, ARRAY_SIZE(smartq_jack_pins), | ||
| 199 | smartq_jack_pins); | ||
| 200 | if (err) | ||
| 201 | return err; | ||
| 202 | |||
| 203 | err = snd_soc_jack_add_gpios(&smartq_jack, | ||
| 204 | ARRAY_SIZE(smartq_jack_gpios), | ||
| 205 | smartq_jack_gpios); | ||
| 206 | |||
| 207 | return err; | ||
| 208 | } | ||
| 209 | |||
| 210 | static struct snd_soc_dai_link smartq_dai[] = { | ||
| 211 | { | ||
| 212 | .name = "wm8987", | ||
| 213 | .stream_name = "SmartQ Hi-Fi", | ||
| 214 | .cpu_dai = &s3c64xx_i2s_dai[0], | ||
| 215 | .codec_dai = &wm8750_dai, | ||
| 216 | .init = smartq_wm8987_init, | ||
| 217 | .ops = &smartq_hifi_ops, | ||
| 218 | }, | ||
| 219 | }; | ||
| 220 | |||
| 221 | static struct snd_soc_card snd_soc_smartq = { | ||
| 222 | .name = "SmartQ", | ||
| 223 | .platform = &s3c24xx_soc_platform, | ||
| 224 | .dai_link = smartq_dai, | ||
| 225 | .num_links = ARRAY_SIZE(smartq_dai), | ||
| 226 | }; | ||
| 227 | |||
| 228 | static struct snd_soc_device smartq_snd_devdata = { | ||
| 229 | .card = &snd_soc_smartq, | ||
| 230 | .codec_dev = &soc_codec_dev_wm8750, | ||
| 231 | }; | ||
| 232 | |||
| 233 | static struct platform_device *smartq_snd_device; | ||
| 234 | |||
| 235 | static int __init smartq_init(void) | ||
| 236 | { | ||
| 237 | int ret; | ||
| 238 | |||
| 239 | if (!machine_is_smartq7() && !machine_is_smartq5()) { | ||
| 240 | pr_info("Only SmartQ is supported by this ASoC driver\n"); | ||
| 241 | return -ENODEV; | ||
| 242 | } | ||
| 243 | |||
| 244 | smartq_snd_device = platform_device_alloc("soc-audio", -1); | ||
| 245 | if (!smartq_snd_device) | ||
| 246 | return -ENOMEM; | ||
| 247 | |||
| 248 | platform_set_drvdata(smartq_snd_device, &smartq_snd_devdata); | ||
| 249 | smartq_snd_devdata.dev = &smartq_snd_device->dev; | ||
| 250 | |||
| 251 | ret = platform_device_add(smartq_snd_device); | ||
| 252 | if (ret) { | ||
| 253 | platform_device_put(smartq_snd_device); | ||
| 254 | return ret; | ||
| 255 | } | ||
| 256 | |||
| 257 | /* Initialise GPIOs used by amplifiers */ | ||
| 258 | ret = gpio_request(S3C64XX_GPK(12), "amplifiers shutdown"); | ||
| 259 | if (ret) { | ||
| 260 | dev_err(&smartq_snd_device->dev, "Failed to register GPK12\n"); | ||
| 261 | goto err_unregister_device; | ||
| 262 | } | ||
| 263 | |||
| 264 | /* Disable amplifiers */ | ||
| 265 | ret = gpio_direction_output(S3C64XX_GPK(12), 1); | ||
| 266 | if (ret) { | ||
| 267 | dev_err(&smartq_snd_device->dev, "Failed to configure GPK12\n"); | ||
| 268 | goto err_free_gpio_amp_shut; | ||
| 269 | } | ||
| 270 | |||
| 271 | return 0; | ||
| 272 | |||
| 273 | err_free_gpio_amp_shut: | ||
| 274 | gpio_free(S3C64XX_GPK(12)); | ||
| 275 | err_unregister_device: | ||
| 276 | platform_device_unregister(smartq_snd_device); | ||
| 277 | |||
| 278 | return ret; | ||
| 279 | } | ||
| 280 | |||
| 281 | static void __exit smartq_exit(void) | ||
| 282 | { | ||
| 283 | snd_soc_jack_free_gpios(&smartq_jack, ARRAY_SIZE(smartq_jack_gpios), | ||
| 284 | smartq_jack_gpios); | ||
| 285 | |||
| 286 | platform_device_unregister(smartq_snd_device); | ||
| 287 | } | ||
| 288 | |||
| 289 | module_init(smartq_init); | ||
| 290 | module_exit(smartq_exit); | ||
| 291 | |||
| 292 | /* Module information */ | ||
| 293 | MODULE_AUTHOR("Maurus Cuelenaere <mcuelenaere@gmail.com>"); | ||
| 294 | MODULE_DESCRIPTION("ALSA SoC SmartQ WM8987"); | ||
| 295 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/s3c24xx/smdk_wm9713.c b/sound/soc/s3c24xx/smdk_wm9713.c index 24fd39f38ccb..5527b9e88c98 100644 --- a/sound/soc/s3c24xx/smdk_wm9713.c +++ b/sound/soc/s3c24xx/smdk_wm9713.c | |||
| @@ -25,6 +25,9 @@ static struct snd_soc_card smdk; | |||
| 25 | * Default CFG switch settings to use this driver: | 25 | * Default CFG switch settings to use this driver: |
| 26 | * | 26 | * |
| 27 | * SMDK6410: Set CFG1 1-3 On, CFG2 1-4 Off | 27 | * SMDK6410: Set CFG1 1-3 On, CFG2 1-4 Off |
| 28 | * SMDKC100: Set CFG6 1-3 On, CFG7 1 On | ||
| 29 | * SMDKC110: Set CFGB10 1-2 Off, CFGB12 1-3 On | ||
| 30 | * SMDKV210: Set CFGB10 1-2 Off, CFGB12 1-3 On | ||
| 28 | */ | 31 | */ |
| 29 | 32 | ||
| 30 | /* | 33 | /* |
diff --git a/sound/soc/s6000/s6000-i2s.c b/sound/soc/s6000/s6000-i2s.c index 5b9ac1759bd2..59e3fa7bcb05 100644 --- a/sound/soc/s6000/s6000-i2s.c +++ b/sound/soc/s6000/s6000-i2s.c | |||
| @@ -451,16 +451,15 @@ static int __devinit s6000_i2s_probe(struct platform_device *pdev) | |||
| 451 | goto err_release_none; | 451 | goto err_release_none; |
| 452 | } | 452 | } |
| 453 | 453 | ||
| 454 | region = request_mem_region(scbmem->start, | 454 | region = request_mem_region(scbmem->start, resource_size(scbmem), |
| 455 | scbmem->end - scbmem->start + 1, | 455 | pdev->name); |
| 456 | pdev->name); | ||
| 457 | if (!region) { | 456 | if (!region) { |
| 458 | dev_err(&pdev->dev, "I2S SCB region already claimed\n"); | 457 | dev_err(&pdev->dev, "I2S SCB region already claimed\n"); |
| 459 | ret = -EBUSY; | 458 | ret = -EBUSY; |
| 460 | goto err_release_none; | 459 | goto err_release_none; |
| 461 | } | 460 | } |
| 462 | 461 | ||
| 463 | mmio = ioremap(scbmem->start, scbmem->end - scbmem->start + 1); | 462 | mmio = ioremap(scbmem->start, resource_size(scbmem)); |
| 464 | if (!mmio) { | 463 | if (!mmio) { |
| 465 | dev_err(&pdev->dev, "can't ioremap SCB region\n"); | 464 | dev_err(&pdev->dev, "can't ioremap SCB region\n"); |
| 466 | ret = -ENOMEM; | 465 | ret = -ENOMEM; |
| @@ -474,9 +473,8 @@ static int __devinit s6000_i2s_probe(struct platform_device *pdev) | |||
| 474 | goto err_release_map; | 473 | goto err_release_map; |
| 475 | } | 474 | } |
| 476 | 475 | ||
| 477 | region = request_mem_region(sifmem->start, | 476 | region = request_mem_region(sifmem->start, resource_size(sifmem), |
| 478 | sifmem->end - sifmem->start + 1, | 477 | pdev->name); |
| 479 | pdev->name); | ||
| 480 | if (!region) { | 478 | if (!region) { |
| 481 | dev_err(&pdev->dev, "I2S SIF region already claimed\n"); | 479 | dev_err(&pdev->dev, "I2S SIF region already claimed\n"); |
| 482 | ret = -EBUSY; | 480 | ret = -EBUSY; |
| @@ -490,8 +488,8 @@ static int __devinit s6000_i2s_probe(struct platform_device *pdev) | |||
| 490 | goto err_release_sif; | 488 | goto err_release_sif; |
| 491 | } | 489 | } |
| 492 | 490 | ||
| 493 | region = request_mem_region(dma1->start, dma1->end - dma1->start + 1, | 491 | region = request_mem_region(dma1->start, resource_size(dma1), |
| 494 | pdev->name); | 492 | pdev->name); |
| 495 | if (!region) { | 493 | if (!region) { |
| 496 | dev_err(&pdev->dev, "I2S DMA region already claimed\n"); | 494 | dev_err(&pdev->dev, "I2S DMA region already claimed\n"); |
| 497 | ret = -EBUSY; | 495 | ret = -EBUSY; |
| @@ -500,9 +498,8 @@ static int __devinit s6000_i2s_probe(struct platform_device *pdev) | |||
| 500 | 498 | ||
| 501 | dma2 = platform_get_resource(pdev, IORESOURCE_DMA, 1); | 499 | dma2 = platform_get_resource(pdev, IORESOURCE_DMA, 1); |
| 502 | if (dma2) { | 500 | if (dma2) { |
| 503 | region = request_mem_region(dma2->start, | 501 | region = request_mem_region(dma2->start, resource_size(dma2), |
| 504 | dma2->end - dma2->start + 1, | 502 | pdev->name); |
| 505 | pdev->name); | ||
| 506 | if (!region) { | 503 | if (!region) { |
| 507 | dev_err(&pdev->dev, | 504 | dev_err(&pdev->dev, |
| 508 | "I2S DMA region already claimed\n"); | 505 | "I2S DMA region already claimed\n"); |
| @@ -561,15 +558,15 @@ err_release_dev: | |||
| 561 | kfree(dev); | 558 | kfree(dev); |
| 562 | err_release_dma2: | 559 | err_release_dma2: |
| 563 | if (dma2) | 560 | if (dma2) |
| 564 | release_mem_region(dma2->start, dma2->end - dma2->start + 1); | 561 | release_mem_region(dma2->start, resource_size(dma2)); |
| 565 | err_release_dma1: | 562 | err_release_dma1: |
| 566 | release_mem_region(dma1->start, dma1->end - dma1->start + 1); | 563 | release_mem_region(dma1->start, resource_size(dma1)); |
| 567 | err_release_sif: | 564 | err_release_sif: |
| 568 | release_mem_region(sifmem->start, (sifmem->end - sifmem->start) + 1); | 565 | release_mem_region(sifmem->start, resource_size(sifmem)); |
| 569 | err_release_map: | 566 | err_release_map: |
| 570 | iounmap(mmio); | 567 | iounmap(mmio); |
| 571 | err_release_scb: | 568 | err_release_scb: |
| 572 | release_mem_region(scbmem->start, (scbmem->end - scbmem->start) + 1); | 569 | release_mem_region(scbmem->start, resource_size(scbmem)); |
| 573 | err_release_none: | 570 | err_release_none: |
| 574 | return ret; | 571 | return ret; |
| 575 | } | 572 | } |
| @@ -590,19 +587,18 @@ static void __devexit s6000_i2s_remove(struct platform_device *pdev) | |||
| 590 | kfree(dev); | 587 | kfree(dev); |
| 591 | 588 | ||
| 592 | region = platform_get_resource(pdev, IORESOURCE_DMA, 0); | 589 | region = platform_get_resource(pdev, IORESOURCE_DMA, 0); |
| 593 | release_mem_region(region->start, region->end - region->start + 1); | 590 | release_mem_region(region->start, resource_size(region)); |
| 594 | 591 | ||
| 595 | region = platform_get_resource(pdev, IORESOURCE_DMA, 1); | 592 | region = platform_get_resource(pdev, IORESOURCE_DMA, 1); |
| 596 | if (region) | 593 | if (region) |
| 597 | release_mem_region(region->start, | 594 | release_mem_region(region->start, resource_size(region)); |
| 598 | region->end - region->start + 1); | ||
| 599 | 595 | ||
| 600 | region = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 596 | region = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 601 | release_mem_region(region->start, (region->end - region->start) + 1); | 597 | release_mem_region(region->start, resource_size(region)); |
| 602 | 598 | ||
| 603 | iounmap(mmio); | 599 | iounmap(mmio); |
| 604 | region = platform_get_resource(pdev, IORESOURCE_IO, 0); | 600 | region = platform_get_resource(pdev, IORESOURCE_IO, 0); |
| 605 | release_mem_region(region->start, (region->end - region->start) + 1); | 601 | release_mem_region(region->start, resource_size(region)); |
| 606 | } | 602 | } |
| 607 | 603 | ||
| 608 | static struct platform_driver s6000_i2s_driver = { | 604 | static struct platform_driver s6000_i2s_driver = { |
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index a1d14bc3c76f..52d7e8ed9c1f 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig | |||
| @@ -48,7 +48,7 @@ config SND_SH7760_AC97 | |||
| 48 | 48 | ||
| 49 | config SND_FSI_AK4642 | 49 | config SND_FSI_AK4642 |
| 50 | bool "FSI-AK4642 sound support" | 50 | bool "FSI-AK4642 sound support" |
| 51 | depends on SND_SOC_SH4_FSI | 51 | depends on SND_SOC_SH4_FSI && I2C_SH_MOBILE |
| 52 | select SND_SOC_AK4642 | 52 | select SND_SOC_AK4642 |
| 53 | help | 53 | help |
| 54 | This option enables generic sound support for the | 54 | This option enables generic sound support for the |
| @@ -56,7 +56,7 @@ config SND_FSI_AK4642 | |||
| 56 | 56 | ||
| 57 | config SND_FSI_DA7210 | 57 | config SND_FSI_DA7210 |
| 58 | bool "FSI-DA7210 sound support" | 58 | bool "FSI-DA7210 sound support" |
| 59 | depends on SND_SOC_SH4_FSI | 59 | depends on SND_SOC_SH4_FSI && I2C_SH_MOBILE |
| 60 | select SND_SOC_DA7210 | 60 | select SND_SOC_DA7210 |
| 61 | help | 61 | help |
| 62 | This option enables generic sound support for the | 62 | This option enables generic sound support for the |
diff --git a/sound/soc/sh/fsi-ak4642.c b/sound/soc/sh/fsi-ak4642.c index be018542314e..dad575a22622 100644 --- a/sound/soc/sh/fsi-ak4642.c +++ b/sound/soc/sh/fsi-ak4642.c | |||
| @@ -9,16 +9,7 @@ | |||
| 9 | * for more details. | 9 | * for more details. |
| 10 | */ | 10 | */ |
| 11 | 11 | ||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/moduleparam.h> | ||
| 14 | #include <linux/platform_device.h> | 12 | #include <linux/platform_device.h> |
| 15 | #include <linux/i2c.h> | ||
| 16 | #include <linux/io.h> | ||
| 17 | #include <sound/core.h> | ||
| 18 | #include <sound/pcm.h> | ||
| 19 | #include <sound/soc.h> | ||
| 20 | #include <sound/soc-dapm.h> | ||
| 21 | |||
| 22 | #include <sound/sh_fsi.h> | 13 | #include <sound/sh_fsi.h> |
| 23 | #include <../sound/soc/codecs/ak4642.h> | 14 | #include <../sound/soc/codecs/ak4642.h> |
| 24 | 15 | ||
| @@ -38,7 +29,7 @@ static int fsi_ak4642_dai_init(struct snd_soc_codec *codec) | |||
| 38 | static struct snd_soc_dai_link fsi_dai_link = { | 29 | static struct snd_soc_dai_link fsi_dai_link = { |
| 39 | .name = "AK4642", | 30 | .name = "AK4642", |
| 40 | .stream_name = "AK4642", | 31 | .stream_name = "AK4642", |
| 41 | .cpu_dai = &fsi_soc_dai[0], /* fsi */ | 32 | .cpu_dai = &fsi_soc_dai[FSI_PORT_A], |
| 42 | .codec_dai = &ak4642_dai, | 33 | .codec_dai = &ak4642_dai, |
| 43 | .init = fsi_ak4642_dai_init, | 34 | .init = fsi_ak4642_dai_init, |
| 44 | .ops = NULL, | 35 | .ops = NULL, |
| @@ -62,7 +53,7 @@ static int __init fsi_ak4642_init(void) | |||
| 62 | { | 53 | { |
| 63 | int ret = -ENOMEM; | 54 | int ret = -ENOMEM; |
| 64 | 55 | ||
| 65 | fsi_snd_device = platform_device_alloc("soc-audio", -1); | 56 | fsi_snd_device = platform_device_alloc("soc-audio", FSI_PORT_A); |
| 66 | if (!fsi_snd_device) | 57 | if (!fsi_snd_device) |
| 67 | goto out; | 58 | goto out; |
| 68 | 59 | ||
diff --git a/sound/soc/sh/fsi-da7210.c b/sound/soc/sh/fsi-da7210.c index 33b4d177f466..121bbb07bb03 100644 --- a/sound/soc/sh/fsi-da7210.c +++ b/sound/soc/sh/fsi-da7210.c | |||
| @@ -10,16 +10,7 @@ | |||
| 10 | * option) any later version. | 10 | * option) any later version. |
| 11 | */ | 11 | */ |
| 12 | 12 | ||
| 13 | #include <linux/interrupt.h> | ||
| 14 | #include <linux/platform_device.h> | 13 | #include <linux/platform_device.h> |
| 15 | #include <linux/io.h> | ||
| 16 | #include <linux/i2c.h> | ||
| 17 | #include <sound/core.h> | ||
| 18 | #include <sound/pcm.h> | ||
| 19 | #include <sound/pcm_params.h> | ||
| 20 | #include <sound/soc.h> | ||
| 21 | #include <sound/soc-dapm.h> | ||
| 22 | |||
| 23 | #include <sound/sh_fsi.h> | 14 | #include <sound/sh_fsi.h> |
| 24 | #include "../codecs/da7210.h" | 15 | #include "../codecs/da7210.h" |
| 25 | 16 | ||
| @@ -33,7 +24,7 @@ static int fsi_da7210_init(struct snd_soc_codec *codec) | |||
| 33 | static struct snd_soc_dai_link fsi_da7210_dai = { | 24 | static struct snd_soc_dai_link fsi_da7210_dai = { |
| 34 | .name = "DA7210", | 25 | .name = "DA7210", |
| 35 | .stream_name = "DA7210", | 26 | .stream_name = "DA7210", |
| 36 | .cpu_dai = &fsi_soc_dai[1], /* FSI B */ | 27 | .cpu_dai = &fsi_soc_dai[FSI_PORT_B], |
| 37 | .codec_dai = &da7210_dai, | 28 | .codec_dai = &da7210_dai, |
| 38 | .init = fsi_da7210_init, | 29 | .init = fsi_da7210_init, |
| 39 | }; | 30 | }; |
| @@ -56,7 +47,7 @@ static int __init fsi_da7210_sound_init(void) | |||
| 56 | { | 47 | { |
| 57 | int ret; | 48 | int ret; |
| 58 | 49 | ||
| 59 | fsi_da7210_snd_device = platform_device_alloc("soc-audio", -1); | 50 | fsi_da7210_snd_device = platform_device_alloc("soc-audio", FSI_PORT_B); |
| 60 | if (!fsi_da7210_snd_device) | 51 | if (!fsi_da7210_snd_device) |
| 61 | return -ENOMEM; | 52 | return -ENOMEM; |
| 62 | 53 | ||
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index ec4acac49ebd..58c6bec642de 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c | |||
| @@ -12,21 +12,12 @@ | |||
| 12 | * published by the Free Software Foundation. | 12 | * published by the Free Software Foundation. |
| 13 | */ | 13 | */ |
| 14 | 14 | ||
| 15 | #include <linux/init.h> | ||
| 16 | #include <linux/module.h> | ||
| 17 | #include <linux/platform_device.h> | ||
| 18 | #include <linux/delay.h> | 15 | #include <linux/delay.h> |
| 19 | #include <linux/list.h> | ||
| 20 | #include <linux/pm_runtime.h> | 16 | #include <linux/pm_runtime.h> |
| 21 | #include <linux/io.h> | 17 | #include <linux/io.h> |
| 22 | #include <linux/slab.h> | 18 | #include <linux/slab.h> |
| 23 | #include <sound/core.h> | ||
| 24 | #include <sound/pcm.h> | ||
| 25 | #include <sound/initval.h> | ||
| 26 | #include <sound/soc.h> | 19 | #include <sound/soc.h> |
| 27 | #include <sound/pcm_params.h> | ||
| 28 | #include <sound/sh_fsi.h> | 20 | #include <sound/sh_fsi.h> |
| 29 | #include <asm/atomic.h> | ||
| 30 | 21 | ||
| 31 | #define DO_FMT 0x0000 | 22 | #define DO_FMT 0x0000 |
| 32 | #define DOFF_CTL 0x0004 | 23 | #define DOFF_CTL 0x0004 |
| @@ -39,9 +30,11 @@ | |||
| 39 | #define DIDT 0x0020 | 30 | #define DIDT 0x0020 |
| 40 | #define DODT 0x0024 | 31 | #define DODT 0x0024 |
| 41 | #define MUTE_ST 0x0028 | 32 | #define MUTE_ST 0x0028 |
| 42 | #define REG_END MUTE_ST | 33 | #define OUT_SEL 0x0030 |
| 43 | 34 | #define REG_END OUT_SEL | |
| 44 | 35 | ||
| 36 | #define A_MST_CTLR 0x0180 | ||
| 37 | #define B_MST_CTLR 0x01A0 | ||
| 45 | #define CPU_INT_ST 0x01F4 | 38 | #define CPU_INT_ST 0x01F4 |
| 46 | #define CPU_IEMSK 0x01F8 | 39 | #define CPU_IEMSK 0x01F8 |
| 47 | #define CPU_IMSK 0x01FC | 40 | #define CPU_IMSK 0x01FC |
| @@ -52,18 +45,18 @@ | |||
| 52 | #define CLK_RST 0x0210 | 45 | #define CLK_RST 0x0210 |
| 53 | #define SOFT_RST 0x0214 | 46 | #define SOFT_RST 0x0214 |
| 54 | #define FIFO_SZ 0x0218 | 47 | #define FIFO_SZ 0x0218 |
| 55 | #define MREG_START CPU_INT_ST | 48 | #define MREG_START A_MST_CTLR |
| 56 | #define MREG_END FIFO_SZ | 49 | #define MREG_END FIFO_SZ |
| 57 | 50 | ||
| 58 | /* DO_FMT */ | 51 | /* DO_FMT */ |
| 59 | /* DI_FMT */ | 52 | /* DI_FMT */ |
| 60 | #define CR_FMT(param) ((param) << 4) | 53 | #define CR_MONO (0x0 << 4) |
| 61 | # define CR_MONO 0x0 | 54 | #define CR_MONO_D (0x1 << 4) |
| 62 | # define CR_MONO_D 0x1 | 55 | #define CR_PCM (0x2 << 4) |
| 63 | # define CR_PCM 0x2 | 56 | #define CR_I2S (0x3 << 4) |
| 64 | # define CR_I2S 0x3 | 57 | #define CR_TDM (0x4 << 4) |
| 65 | # define CR_TDM 0x4 | 58 | #define CR_TDM_D (0x5 << 4) |
| 66 | # define CR_TDM_D 0x5 | 59 | #define CR_SPDIF 0x00100120 |
| 67 | 60 | ||
| 68 | /* DOFF_CTL */ | 61 | /* DOFF_CTL */ |
| 69 | /* DIFF_CTL */ | 62 | /* DIFF_CTL */ |
| @@ -75,6 +68,14 @@ | |||
| 75 | #define ERR_UNDER 0x00000001 | 68 | #define ERR_UNDER 0x00000001 |
| 76 | #define ST_ERR (ERR_OVER | ERR_UNDER) | 69 | #define ST_ERR (ERR_OVER | ERR_UNDER) |
| 77 | 70 | ||
| 71 | /* CKG1 */ | ||
| 72 | #define ACKMD_MASK 0x00007000 | ||
| 73 | #define BPFMD_MASK 0x00000700 | ||
| 74 | |||
| 75 | /* A/B MST_CTLR */ | ||
| 76 | #define BP (1 << 4) /* Fix the signal of Biphase output */ | ||
| 77 | #define SE (1 << 0) /* Fix the master clock */ | ||
| 78 | |||
| 78 | /* CLK_RST */ | 79 | /* CLK_RST */ |
| 79 | #define B_CLK 0x00000010 | 80 | #define B_CLK 0x00000010 |
| 80 | #define A_CLK 0x00000001 | 81 | #define A_CLK 0x00000001 |
| @@ -119,9 +120,13 @@ struct fsi_priv { | |||
| 119 | int period_len; | 120 | int period_len; |
| 120 | int buffer_len; | 121 | int buffer_len; |
| 121 | int periods; | 122 | int periods; |
| 123 | |||
| 124 | u32 mst_ctrl; | ||
| 122 | }; | 125 | }; |
| 123 | 126 | ||
| 124 | struct fsi_regs { | 127 | struct fsi_core { |
| 128 | int ver; | ||
| 129 | |||
| 125 | u32 int_st; | 130 | u32 int_st; |
| 126 | u32 iemsk; | 131 | u32 iemsk; |
| 127 | u32 imsk; | 132 | u32 imsk; |
| @@ -132,7 +137,7 @@ struct fsi_master { | |||
| 132 | int irq; | 137 | int irq; |
| 133 | struct fsi_priv fsia; | 138 | struct fsi_priv fsia; |
| 134 | struct fsi_priv fsib; | 139 | struct fsi_priv fsib; |
| 135 | struct fsi_regs *regs; | 140 | struct fsi_core *core; |
| 136 | struct sh_fsi_platform_info *info; | 141 | struct sh_fsi_platform_info *info; |
| 137 | spinlock_t lock; | 142 | spinlock_t lock; |
| 138 | }; | 143 | }; |
| @@ -169,24 +174,30 @@ static void __fsi_reg_mask_set(u32 reg, u32 mask, u32 data) | |||
| 169 | 174 | ||
| 170 | static void fsi_reg_write(struct fsi_priv *fsi, u32 reg, u32 data) | 175 | static void fsi_reg_write(struct fsi_priv *fsi, u32 reg, u32 data) |
| 171 | { | 176 | { |
| 172 | if (reg > REG_END) | 177 | if (reg > REG_END) { |
| 178 | pr_err("fsi: register access err (%s)\n", __func__); | ||
| 173 | return; | 179 | return; |
| 180 | } | ||
| 174 | 181 | ||
| 175 | __fsi_reg_write((u32)(fsi->base + reg), data); | 182 | __fsi_reg_write((u32)(fsi->base + reg), data); |
| 176 | } | 183 | } |
| 177 | 184 | ||
| 178 | static u32 fsi_reg_read(struct fsi_priv *fsi, u32 reg) | 185 | static u32 fsi_reg_read(struct fsi_priv *fsi, u32 reg) |
| 179 | { | 186 | { |
| 180 | if (reg > REG_END) | 187 | if (reg > REG_END) { |
| 188 | pr_err("fsi: register access err (%s)\n", __func__); | ||
| 181 | return 0; | 189 | return 0; |
| 190 | } | ||
| 182 | 191 | ||
| 183 | return __fsi_reg_read((u32)(fsi->base + reg)); | 192 | return __fsi_reg_read((u32)(fsi->base + reg)); |
| 184 | } | 193 | } |
| 185 | 194 | ||
| 186 | static void fsi_reg_mask_set(struct fsi_priv *fsi, u32 reg, u32 mask, u32 data) | 195 | static void fsi_reg_mask_set(struct fsi_priv *fsi, u32 reg, u32 mask, u32 data) |
| 187 | { | 196 | { |
| 188 | if (reg > REG_END) | 197 | if (reg > REG_END) { |
| 198 | pr_err("fsi: register access err (%s)\n", __func__); | ||
| 189 | return; | 199 | return; |
| 200 | } | ||
| 190 | 201 | ||
| 191 | __fsi_reg_mask_set((u32)(fsi->base + reg), mask, data); | 202 | __fsi_reg_mask_set((u32)(fsi->base + reg), mask, data); |
| 192 | } | 203 | } |
| @@ -196,8 +207,10 @@ static void fsi_master_write(struct fsi_master *master, u32 reg, u32 data) | |||
| 196 | unsigned long flags; | 207 | unsigned long flags; |
| 197 | 208 | ||
| 198 | if ((reg < MREG_START) || | 209 | if ((reg < MREG_START) || |
| 199 | (reg > MREG_END)) | 210 | (reg > MREG_END)) { |
| 211 | pr_err("fsi: register access err (%s)\n", __func__); | ||
| 200 | return; | 212 | return; |
| 213 | } | ||
| 201 | 214 | ||
| 202 | spin_lock_irqsave(&master->lock, flags); | 215 | spin_lock_irqsave(&master->lock, flags); |
| 203 | __fsi_reg_write((u32)(master->base + reg), data); | 216 | __fsi_reg_write((u32)(master->base + reg), data); |
| @@ -210,8 +223,10 @@ static u32 fsi_master_read(struct fsi_master *master, u32 reg) | |||
| 210 | unsigned long flags; | 223 | unsigned long flags; |
| 211 | 224 | ||
| 212 | if ((reg < MREG_START) || | 225 | if ((reg < MREG_START) || |
| 213 | (reg > MREG_END)) | 226 | (reg > MREG_END)) { |
| 227 | pr_err("fsi: register access err (%s)\n", __func__); | ||
| 214 | return 0; | 228 | return 0; |
| 229 | } | ||
| 215 | 230 | ||
| 216 | spin_lock_irqsave(&master->lock, flags); | 231 | spin_lock_irqsave(&master->lock, flags); |
| 217 | ret = __fsi_reg_read((u32)(master->base + reg)); | 232 | ret = __fsi_reg_read((u32)(master->base + reg)); |
| @@ -226,8 +241,10 @@ static void fsi_master_mask_set(struct fsi_master *master, | |||
| 226 | unsigned long flags; | 241 | unsigned long flags; |
| 227 | 242 | ||
| 228 | if ((reg < MREG_START) || | 243 | if ((reg < MREG_START) || |
| 229 | (reg > MREG_END)) | 244 | (reg > MREG_END)) { |
| 245 | pr_err("fsi: register access err (%s)\n", __func__); | ||
| 230 | return; | 246 | return; |
| 247 | } | ||
| 231 | 248 | ||
| 232 | spin_lock_irqsave(&master->lock, flags); | 249 | spin_lock_irqsave(&master->lock, flags); |
| 233 | __fsi_reg_mask_set((u32)(master->base + reg), mask, data); | 250 | __fsi_reg_mask_set((u32)(master->base + reg), mask, data); |
| @@ -349,8 +366,8 @@ static void fsi_irq_enable(struct fsi_priv *fsi, int is_play) | |||
| 349 | u32 data = fsi_port_ab_io_bit(fsi, is_play); | 366 | u32 data = fsi_port_ab_io_bit(fsi, is_play); |
| 350 | struct fsi_master *master = fsi_get_master(fsi); | 367 | struct fsi_master *master = fsi_get_master(fsi); |
| 351 | 368 | ||
| 352 | fsi_master_mask_set(master, master->regs->imsk, data, data); | 369 | fsi_master_mask_set(master, master->core->imsk, data, data); |
| 353 | fsi_master_mask_set(master, master->regs->iemsk, data, data); | 370 | fsi_master_mask_set(master, master->core->iemsk, data, data); |
| 354 | } | 371 | } |
| 355 | 372 | ||
| 356 | static void fsi_irq_disable(struct fsi_priv *fsi, int is_play) | 373 | static void fsi_irq_disable(struct fsi_priv *fsi, int is_play) |
| @@ -358,18 +375,18 @@ static void fsi_irq_disable(struct fsi_priv *fsi, int is_play) | |||
| 358 | u32 data = fsi_port_ab_io_bit(fsi, is_play); | 375 | u32 data = fsi_port_ab_io_bit(fsi, is_play); |
| 359 | struct fsi_master *master = fsi_get_master(fsi); | 376 | struct fsi_master *master = fsi_get_master(fsi); |
| 360 | 377 | ||
| 361 | fsi_master_mask_set(master, master->regs->imsk, data, 0); | 378 | fsi_master_mask_set(master, master->core->imsk, data, 0); |
| 362 | fsi_master_mask_set(master, master->regs->iemsk, data, 0); | 379 | fsi_master_mask_set(master, master->core->iemsk, data, 0); |
| 363 | } | 380 | } |
| 364 | 381 | ||
| 365 | static u32 fsi_irq_get_status(struct fsi_master *master) | 382 | static u32 fsi_irq_get_status(struct fsi_master *master) |
| 366 | { | 383 | { |
| 367 | return fsi_master_read(master, master->regs->int_st); | 384 | return fsi_master_read(master, master->core->int_st); |
| 368 | } | 385 | } |
| 369 | 386 | ||
| 370 | static void fsi_irq_clear_all_status(struct fsi_master *master) | 387 | static void fsi_irq_clear_all_status(struct fsi_master *master) |
| 371 | { | 388 | { |
| 372 | fsi_master_write(master, master->regs->int_st, 0x0000000); | 389 | fsi_master_write(master, master->core->int_st, 0); |
| 373 | } | 390 | } |
| 374 | 391 | ||
| 375 | static void fsi_irq_clear_status(struct fsi_priv *fsi) | 392 | static void fsi_irq_clear_status(struct fsi_priv *fsi) |
| @@ -381,7 +398,30 @@ static void fsi_irq_clear_status(struct fsi_priv *fsi) | |||
| 381 | data |= fsi_port_ab_io_bit(fsi, 1); | 398 | data |= fsi_port_ab_io_bit(fsi, 1); |
| 382 | 399 | ||
| 383 | /* clear interrupt factor */ | 400 | /* clear interrupt factor */ |
| 384 | fsi_master_mask_set(master, master->regs->int_st, data, 0); | 401 | fsi_master_mask_set(master, master->core->int_st, data, 0); |
| 402 | } | ||
| 403 | |||
| 404 | /************************************************************************ | ||
| 405 | |||
| 406 | |||
| 407 | SPDIF master clock function | ||
| 408 | |||
| 409 | These functions are used later FSI2 | ||
| 410 | ************************************************************************/ | ||
| 411 | static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable) | ||
| 412 | { | ||
| 413 | struct fsi_master *master = fsi_get_master(fsi); | ||
| 414 | u32 val = BP | SE; | ||
| 415 | |||
| 416 | if (master->core->ver < 2) { | ||
| 417 | pr_err("fsi: register access err (%s)\n", __func__); | ||
| 418 | return; | ||
| 419 | } | ||
| 420 | |||
| 421 | if (enable) | ||
| 422 | fsi_master_mask_set(master, fsi->mst_ctrl, val, val); | ||
| 423 | else | ||
| 424 | fsi_master_mask_set(master, fsi->mst_ctrl, val, 0); | ||
| 385 | } | 425 | } |
| 386 | 426 | ||
| 387 | /************************************************************************ | 427 | /************************************************************************ |
| @@ -662,8 +702,8 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, | |||
| 662 | struct snd_soc_dai *dai) | 702 | struct snd_soc_dai *dai) |
| 663 | { | 703 | { |
| 664 | struct fsi_priv *fsi = fsi_get_priv(substream); | 704 | struct fsi_priv *fsi = fsi_get_priv(substream); |
| 665 | const char *msg; | ||
| 666 | u32 flags = fsi_get_info_flags(fsi); | 705 | u32 flags = fsi_get_info_flags(fsi); |
| 706 | struct fsi_master *master = fsi_get_master(fsi); | ||
| 667 | u32 fmt; | 707 | u32 fmt; |
| 668 | u32 reg; | 708 | u32 reg; |
| 669 | u32 data; | 709 | u32 data; |
| @@ -700,36 +740,40 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, | |||
| 700 | fmt = is_play ? SH_FSI_GET_OFMT(flags) : SH_FSI_GET_IFMT(flags); | 740 | fmt = is_play ? SH_FSI_GET_OFMT(flags) : SH_FSI_GET_IFMT(flags); |
| 701 | switch (fmt) { | 741 | switch (fmt) { |
| 702 | case SH_FSI_FMT_MONO: | 742 | case SH_FSI_FMT_MONO: |
| 703 | msg = "MONO"; | 743 | data = CR_MONO; |
| 704 | data = CR_FMT(CR_MONO); | ||
| 705 | fsi->chan = 1; | 744 | fsi->chan = 1; |
| 706 | break; | 745 | break; |
| 707 | case SH_FSI_FMT_MONO_DELAY: | 746 | case SH_FSI_FMT_MONO_DELAY: |
| 708 | msg = "MONO Delay"; | 747 | data = CR_MONO_D; |
| 709 | data = CR_FMT(CR_MONO_D); | ||
| 710 | fsi->chan = 1; | 748 | fsi->chan = 1; |
| 711 | break; | 749 | break; |
| 712 | case SH_FSI_FMT_PCM: | 750 | case SH_FSI_FMT_PCM: |
| 713 | msg = "PCM"; | 751 | data = CR_PCM; |
| 714 | data = CR_FMT(CR_PCM); | ||
| 715 | fsi->chan = 2; | 752 | fsi->chan = 2; |
| 716 | break; | 753 | break; |
| 717 | case SH_FSI_FMT_I2S: | 754 | case SH_FSI_FMT_I2S: |
| 718 | msg = "I2S"; | 755 | data = CR_I2S; |
| 719 | data = CR_FMT(CR_I2S); | ||
| 720 | fsi->chan = 2; | 756 | fsi->chan = 2; |
| 721 | break; | 757 | break; |
| 722 | case SH_FSI_FMT_TDM: | 758 | case SH_FSI_FMT_TDM: |
| 723 | msg = "TDM"; | ||
| 724 | fsi->chan = is_play ? | 759 | fsi->chan = is_play ? |
| 725 | SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags); | 760 | SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags); |
| 726 | data = CR_FMT(CR_TDM) | (fsi->chan - 1); | 761 | data = CR_TDM | (fsi->chan - 1); |
| 727 | break; | 762 | break; |
| 728 | case SH_FSI_FMT_TDM_DELAY: | 763 | case SH_FSI_FMT_TDM_DELAY: |
| 729 | msg = "TDM Delay"; | ||
| 730 | fsi->chan = is_play ? | 764 | fsi->chan = is_play ? |
| 731 | SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags); | 765 | SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags); |
| 732 | data = CR_FMT(CR_TDM_D) | (fsi->chan - 1); | 766 | data = CR_TDM_D | (fsi->chan - 1); |
| 767 | break; | ||
| 768 | case SH_FSI_FMT_SPDIF: | ||
| 769 | if (master->core->ver < 2) { | ||
| 770 | dev_err(dai->dev, "This FSI can not use SPDIF\n"); | ||
| 771 | return -EINVAL; | ||
| 772 | } | ||
| 773 | data = CR_SPDIF; | ||
| 774 | fsi->chan = 2; | ||
| 775 | fsi_spdif_clk_ctrl(fsi, 1); | ||
| 776 | fsi_reg_mask_set(fsi, OUT_SEL, 0x0010, 0x0010); | ||
| 733 | break; | 777 | break; |
| 734 | default: | 778 | default: |
| 735 | dev_err(dai->dev, "unknown format.\n"); | 779 | dev_err(dai->dev, "unknown format.\n"); |
| @@ -737,12 +781,6 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, | |||
| 737 | } | 781 | } |
| 738 | fsi_reg_write(fsi, reg, data); | 782 | fsi_reg_write(fsi, reg, data); |
| 739 | 783 | ||
| 740 | /* | ||
| 741 | * clear clk reset if master mode | ||
| 742 | */ | ||
| 743 | if (is_master) | ||
| 744 | fsi_clk_ctrl(fsi, 1); | ||
| 745 | |||
| 746 | /* irq clear */ | 784 | /* irq clear */ |
| 747 | fsi_irq_disable(fsi, is_play); | 785 | fsi_irq_disable(fsi, is_play); |
| 748 | fsi_irq_clear_status(fsi); | 786 | fsi_irq_clear_status(fsi); |
| @@ -789,10 +827,93 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, | |||
| 789 | return ret; | 827 | return ret; |
| 790 | } | 828 | } |
| 791 | 829 | ||
| 830 | static int fsi_dai_hw_params(struct snd_pcm_substream *substream, | ||
| 831 | struct snd_pcm_hw_params *params, | ||
| 832 | struct snd_soc_dai *dai) | ||
| 833 | { | ||
| 834 | struct fsi_priv *fsi = fsi_get_priv(substream); | ||
| 835 | struct fsi_master *master = fsi_get_master(fsi); | ||
| 836 | int (*set_rate)(int is_porta, int rate) = master->info->set_rate; | ||
| 837 | int fsi_ver = master->core->ver; | ||
| 838 | int is_play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); | ||
| 839 | int ret; | ||
| 840 | |||
| 841 | /* if slave mode, set_rate is not needed */ | ||
| 842 | if (!fsi_is_master_mode(fsi, is_play)) | ||
| 843 | return 0; | ||
| 844 | |||
| 845 | /* it is error if no set_rate */ | ||
| 846 | if (!set_rate) | ||
| 847 | return -EIO; | ||
| 848 | |||
| 849 | ret = set_rate(fsi_is_port_a(fsi), params_rate(params)); | ||
| 850 | if (ret > 0) { | ||
| 851 | u32 data = 0; | ||
| 852 | |||
| 853 | switch (ret & SH_FSI_ACKMD_MASK) { | ||
| 854 | default: | ||
| 855 | /* FALL THROUGH */ | ||
| 856 | case SH_FSI_ACKMD_512: | ||
| 857 | data |= (0x0 << 12); | ||
| 858 | break; | ||
| 859 | case SH_FSI_ACKMD_256: | ||
| 860 | data |= (0x1 << 12); | ||
| 861 | break; | ||
| 862 | case SH_FSI_ACKMD_128: | ||
| 863 | data |= (0x2 << 12); | ||
| 864 | break; | ||
| 865 | case SH_FSI_ACKMD_64: | ||
| 866 | data |= (0x3 << 12); | ||
| 867 | break; | ||
| 868 | case SH_FSI_ACKMD_32: | ||
| 869 | if (fsi_ver < 2) | ||
| 870 | dev_err(dai->dev, "unsupported ACKMD\n"); | ||
| 871 | else | ||
| 872 | data |= (0x4 << 12); | ||
| 873 | break; | ||
| 874 | } | ||
| 875 | |||
| 876 | switch (ret & SH_FSI_BPFMD_MASK) { | ||
| 877 | default: | ||
| 878 | /* FALL THROUGH */ | ||
| 879 | case SH_FSI_BPFMD_32: | ||
| 880 | data |= (0x0 << 8); | ||
| 881 | break; | ||
| 882 | case SH_FSI_BPFMD_64: | ||
| 883 | data |= (0x1 << 8); | ||
| 884 | break; | ||
| 885 | case SH_FSI_BPFMD_128: | ||
| 886 | data |= (0x2 << 8); | ||
| 887 | break; | ||
| 888 | case SH_FSI_BPFMD_256: | ||
| 889 | data |= (0x3 << 8); | ||
| 890 | break; | ||
| 891 | case SH_FSI_BPFMD_512: | ||
| 892 | data |= (0x4 << 8); | ||
| 893 | break; | ||
| 894 | case SH_FSI_BPFMD_16: | ||
| 895 | if (fsi_ver < 2) | ||
| 896 | dev_err(dai->dev, "unsupported ACKMD\n"); | ||
| 897 | else | ||
| 898 | data |= (0x7 << 8); | ||
| 899 | break; | ||
| 900 | } | ||
| 901 | |||
| 902 | fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data); | ||
| 903 | udelay(10); | ||
| 904 | fsi_clk_ctrl(fsi, 1); | ||
| 905 | ret = 0; | ||
| 906 | } | ||
| 907 | |||
| 908 | return ret; | ||
| 909 | |||
| 910 | } | ||
| 911 | |||
| 792 | static struct snd_soc_dai_ops fsi_dai_ops = { | 912 | static struct snd_soc_dai_ops fsi_dai_ops = { |
| 793 | .startup = fsi_dai_startup, | 913 | .startup = fsi_dai_startup, |
| 794 | .shutdown = fsi_dai_shutdown, | 914 | .shutdown = fsi_dai_shutdown, |
| 795 | .trigger = fsi_dai_trigger, | 915 | .trigger = fsi_dai_trigger, |
| 916 | .hw_params = fsi_dai_hw_params, | ||
| 796 | }; | 917 | }; |
| 797 | 918 | ||
| 798 | /************************************************************************ | 919 | /************************************************************************ |
| @@ -965,11 +1086,6 @@ static int fsi_probe(struct platform_device *pdev) | |||
| 965 | unsigned int irq; | 1086 | unsigned int irq; |
| 966 | int ret; | 1087 | int ret; |
| 967 | 1088 | ||
| 968 | if (0 != pdev->id) { | ||
| 969 | dev_err(&pdev->dev, "current fsi support id 0 only now\n"); | ||
| 970 | return -ENODEV; | ||
| 971 | } | ||
| 972 | |||
| 973 | id_entry = pdev->id_entry; | 1089 | id_entry = pdev->id_entry; |
| 974 | if (!id_entry) { | 1090 | if (!id_entry) { |
| 975 | dev_err(&pdev->dev, "unknown fsi device\n"); | 1091 | dev_err(&pdev->dev, "unknown fsi device\n"); |
| @@ -998,14 +1114,21 @@ static int fsi_probe(struct platform_device *pdev) | |||
| 998 | goto exit_kfree; | 1114 | goto exit_kfree; |
| 999 | } | 1115 | } |
| 1000 | 1116 | ||
| 1117 | /* master setting */ | ||
| 1001 | master->irq = irq; | 1118 | master->irq = irq; |
| 1002 | master->info = pdev->dev.platform_data; | 1119 | master->info = pdev->dev.platform_data; |
| 1120 | master->core = (struct fsi_core *)id_entry->driver_data; | ||
| 1121 | spin_lock_init(&master->lock); | ||
| 1122 | |||
| 1123 | /* FSI A setting */ | ||
| 1003 | master->fsia.base = master->base; | 1124 | master->fsia.base = master->base; |
| 1004 | master->fsia.master = master; | 1125 | master->fsia.master = master; |
| 1126 | master->fsia.mst_ctrl = A_MST_CTLR; | ||
| 1127 | |||
| 1128 | /* FSI B setting */ | ||
| 1005 | master->fsib.base = master->base + 0x40; | 1129 | master->fsib.base = master->base + 0x40; |
| 1006 | master->fsib.master = master; | 1130 | master->fsib.master = master; |
| 1007 | master->regs = (struct fsi_regs *)id_entry->driver_data; | 1131 | master->fsib.mst_ctrl = B_MST_CTLR; |
| 1008 | spin_lock_init(&master->lock); | ||
| 1009 | 1132 | ||
| 1010 | pm_runtime_enable(&pdev->dev); | 1133 | pm_runtime_enable(&pdev->dev); |
| 1011 | pm_runtime_resume(&pdev->dev); | 1134 | pm_runtime_resume(&pdev->dev); |
| @@ -1085,21 +1208,27 @@ static struct dev_pm_ops fsi_pm_ops = { | |||
| 1085 | .runtime_resume = fsi_runtime_nop, | 1208 | .runtime_resume = fsi_runtime_nop, |
| 1086 | }; | 1209 | }; |
| 1087 | 1210 | ||
| 1088 | static struct fsi_regs fsi_regs = { | 1211 | static struct fsi_core fsi1_core = { |
| 1212 | .ver = 1, | ||
| 1213 | |||
| 1214 | /* Interrupt */ | ||
| 1089 | .int_st = INT_ST, | 1215 | .int_st = INT_ST, |
| 1090 | .iemsk = IEMSK, | 1216 | .iemsk = IEMSK, |
| 1091 | .imsk = IMSK, | 1217 | .imsk = IMSK, |
| 1092 | }; | 1218 | }; |
| 1093 | 1219 | ||
| 1094 | static struct fsi_regs fsi2_regs = { | 1220 | static struct fsi_core fsi2_core = { |
| 1221 | .ver = 2, | ||
| 1222 | |||
| 1223 | /* Interrupt */ | ||
| 1095 | .int_st = CPU_INT_ST, | 1224 | .int_st = CPU_INT_ST, |
| 1096 | .iemsk = CPU_IEMSK, | 1225 | .iemsk = CPU_IEMSK, |
| 1097 | .imsk = CPU_IMSK, | 1226 | .imsk = CPU_IMSK, |
| 1098 | }; | 1227 | }; |
| 1099 | 1228 | ||
| 1100 | static struct platform_device_id fsi_id_table[] = { | 1229 | static struct platform_device_id fsi_id_table[] = { |
| 1101 | { "sh_fsi", (kernel_ulong_t)&fsi_regs }, | 1230 | { "sh_fsi", (kernel_ulong_t)&fsi1_core }, |
| 1102 | { "sh_fsi2", (kernel_ulong_t)&fsi2_regs }, | 1231 | { "sh_fsi2", (kernel_ulong_t)&fsi2_core }, |
| 1103 | }; | 1232 | }; |
| 1104 | 1233 | ||
| 1105 | static struct platform_driver fsi_driver = { | 1234 | static struct platform_driver fsi_driver = { |
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 998569d60330..5299932db0b6 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c | |||
| @@ -84,7 +84,7 @@ static int run_delayed_work(struct delayed_work *dwork) | |||
| 84 | /* codec register dump */ | 84 | /* codec register dump */ |
| 85 | static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) | 85 | static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) |
| 86 | { | 86 | { |
| 87 | int i, step = 1, count = 0; | 87 | int ret, i, step = 1, count = 0; |
| 88 | 88 | ||
| 89 | if (!codec->reg_cache_size) | 89 | if (!codec->reg_cache_size) |
| 90 | return 0; | 90 | return 0; |
| @@ -101,12 +101,24 @@ static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) | |||
| 101 | if (count >= PAGE_SIZE - 1) | 101 | if (count >= PAGE_SIZE - 1) |
| 102 | break; | 102 | break; |
| 103 | 103 | ||
| 104 | if (codec->display_register) | 104 | if (codec->display_register) { |
| 105 | count += codec->display_register(codec, buf + count, | 105 | count += codec->display_register(codec, buf + count, |
| 106 | PAGE_SIZE - count, i); | 106 | PAGE_SIZE - count, i); |
| 107 | else | 107 | } else { |
| 108 | count += snprintf(buf + count, PAGE_SIZE - count, | 108 | /* If the read fails it's almost certainly due to |
| 109 | "%4x", codec->read(codec, i)); | 109 | * the register being volatile and the device being |
| 110 | * powered off. | ||
| 111 | */ | ||
| 112 | ret = codec->read(codec, i); | ||
| 113 | if (ret >= 0) | ||
| 114 | count += snprintf(buf + count, | ||
| 115 | PAGE_SIZE - count, | ||
| 116 | "%4x", ret); | ||
| 117 | else | ||
| 118 | count += snprintf(buf + count, | ||
| 119 | PAGE_SIZE - count, | ||
| 120 | "<no data: %d>", ret); | ||
| 121 | } | ||
| 110 | 122 | ||
| 111 | if (count >= PAGE_SIZE - 1) | 123 | if (count >= PAGE_SIZE - 1) |
| 112 | break; | 124 | break; |
| @@ -2353,6 +2365,99 @@ int snd_soc_limit_volume(struct snd_soc_codec *codec, | |||
| 2353 | EXPORT_SYMBOL_GPL(snd_soc_limit_volume); | 2365 | EXPORT_SYMBOL_GPL(snd_soc_limit_volume); |
| 2354 | 2366 | ||
| 2355 | /** | 2367 | /** |
| 2368 | * snd_soc_info_volsw_2r_sx - double with tlv and variable data size | ||
| 2369 | * mixer info callback | ||
| 2370 | * @kcontrol: mixer control | ||
| 2371 | * @uinfo: control element information | ||
| 2372 | * | ||
| 2373 | * Returns 0 for success. | ||
| 2374 | */ | ||
| 2375 | int snd_soc_info_volsw_2r_sx(struct snd_kcontrol *kcontrol, | ||
| 2376 | struct snd_ctl_elem_info *uinfo) | ||
| 2377 | { | ||
| 2378 | struct soc_mixer_control *mc = | ||
| 2379 | (struct soc_mixer_control *)kcontrol->private_value; | ||
| 2380 | int max = mc->max; | ||
| 2381 | int min = mc->min; | ||
| 2382 | |||
| 2383 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
| 2384 | uinfo->count = 2; | ||
| 2385 | uinfo->value.integer.min = 0; | ||
| 2386 | uinfo->value.integer.max = max-min; | ||
| 2387 | |||
| 2388 | return 0; | ||
| 2389 | } | ||
| 2390 | EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r_sx); | ||
| 2391 | |||
| 2392 | /** | ||
| 2393 | * snd_soc_get_volsw_2r_sx - double with tlv and variable data size | ||
| 2394 | * mixer get callback | ||
| 2395 | * @kcontrol: mixer control | ||
| 2396 | * @uinfo: control element information | ||
| 2397 | * | ||
| 2398 | * Returns 0 for success. | ||
| 2399 | */ | ||
| 2400 | int snd_soc_get_volsw_2r_sx(struct snd_kcontrol *kcontrol, | ||
| 2401 | struct snd_ctl_elem_value *ucontrol) | ||
| 2402 | { | ||
| 2403 | struct soc_mixer_control *mc = | ||
| 2404 | (struct soc_mixer_control *)kcontrol->private_value; | ||
| 2405 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
| 2406 | unsigned int mask = (1<<mc->shift)-1; | ||
| 2407 | int min = mc->min; | ||
| 2408 | int val = snd_soc_read(codec, mc->reg) & mask; | ||
| 2409 | int valr = snd_soc_read(codec, mc->rreg) & mask; | ||
| 2410 | |||
| 2411 | ucontrol->value.integer.value[0] = ((val & 0xff)-min) & mask; | ||
| 2412 | ucontrol->value.integer.value[1] = ((valr & 0xff)-min) & mask; | ||
| 2413 | return 0; | ||
| 2414 | } | ||
| 2415 | EXPORT_SYMBOL_GPL(snd_soc_get_volsw_2r_sx); | ||
| 2416 | |||
| 2417 | /** | ||
| 2418 | * snd_soc_put_volsw_2r_sx - double with tlv and variable data size | ||
| 2419 | * mixer put callback | ||
| 2420 | * @kcontrol: mixer control | ||
| 2421 | * @uinfo: control element information | ||
| 2422 | * | ||
| 2423 | * Returns 0 for success. | ||
| 2424 | */ | ||
| 2425 | int snd_soc_put_volsw_2r_sx(struct snd_kcontrol *kcontrol, | ||
| 2426 | struct snd_ctl_elem_value *ucontrol) | ||
| 2427 | { | ||
| 2428 | struct soc_mixer_control *mc = | ||
| 2429 | (struct soc_mixer_control *)kcontrol->private_value; | ||
| 2430 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
| 2431 | unsigned int mask = (1<<mc->shift)-1; | ||
| 2432 | int min = mc->min; | ||
| 2433 | int ret; | ||
| 2434 | unsigned int val, valr, oval, ovalr; | ||
| 2435 | |||
| 2436 | val = ((ucontrol->value.integer.value[0]+min) & 0xff); | ||
| 2437 | val &= mask; | ||
| 2438 | valr = ((ucontrol->value.integer.value[1]+min) & 0xff); | ||
| 2439 | valr &= mask; | ||
| 2440 | |||
| 2441 | oval = snd_soc_read(codec, mc->reg) & mask; | ||
| 2442 | ovalr = snd_soc_read(codec, mc->rreg) & mask; | ||
| 2443 | |||
| 2444 | ret = 0; | ||
| 2445 | if (oval != val) { | ||
| 2446 | ret = snd_soc_write(codec, mc->reg, val); | ||
| 2447 | if (ret < 0) | ||
| 2448 | return ret; | ||
| 2449 | } | ||
| 2450 | if (ovalr != valr) { | ||
| 2451 | ret = snd_soc_write(codec, mc->rreg, valr); | ||
| 2452 | if (ret < 0) | ||
| 2453 | return ret; | ||
| 2454 | } | ||
| 2455 | |||
| 2456 | return 0; | ||
| 2457 | } | ||
| 2458 | EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r_sx); | ||
| 2459 | |||
| 2460 | /** | ||
| 2356 | * snd_soc_dai_set_sysclk - configure DAI system or master clock. | 2461 | * snd_soc_dai_set_sysclk - configure DAI system or master clock. |
| 2357 | * @dai: DAI | 2462 | * @dai: DAI |
| 2358 | * @clk_id: DAI specific clock ID | 2463 | * @clk_id: DAI specific clock ID |
