From c8af20c5833143346ad186cb886d0ec4dc1300d3 Mon Sep 17 00:00:00 2001 From: lamikr Date: Wed, 22 Mar 2006 07:58:41 -0800 Subject: [PATCH] [PATCH] ARM: OMAP: Alsa modularisations and support for tsc2101 2/3 (round 2) This 2/3 patch moves omap alsa aic23 driver files to sound/arm/omap directory. In addition it adds cleaner separation of codec specific functionality from the other functionality so that the same code can also be used by the tsc2101 driver. Structures required by the platform driver are now in include/asm-arm/arch-omap/omap-alsa.h. Unlike in the previous review, all other files required by this driver are now located in the sound/arm/omap directory. MCBSP settings required by the alsa driver are now initialized in the omap OSK board-code. Codec specific function pointers are now initialized in the codec-file. (omap-alsa-aic23.c) Signed-off-by: Mika Laitio Signed-off-by: Daniel Petrini Signed-off-by: Tony Lindgren --- arch/arm/mach-omap1/board-osk.c | 37 +- .../asm-arm/arch-omap/omap-alsa.h | 99 +- sound/arm/Makefile | 3 +- sound/arm/omap-aic23.c | 904 ------------------ sound/arm/omap/Makefile | 6 + .../omap-alsa-aic23-mixer.c} | 69 +- sound/arm/omap/omap-alsa-aic23.c | 321 +++++++ sound/arm/omap/omap-alsa-aic23.h | 83 ++ sound/arm/{ => omap}/omap-alsa-dma.c | 31 +- sound/arm/{ => omap}/omap-alsa-dma.h | 4 +- sound/arm/omap/omap-alsa.c | 579 +++++++++++ 11 files changed, 1123 insertions(+), 1013 deletions(-) rename sound/arm/omap-aic23.h => include/asm-arm/arch-omap/omap-alsa.h (63%) delete mode 100644 sound/arm/omap-aic23.c create mode 100644 sound/arm/omap/Makefile rename sound/arm/{omap-alsa-mixer.c => omap/omap-alsa-aic23-mixer.c} (97%) create mode 100644 sound/arm/omap/omap-alsa-aic23.c create mode 100644 sound/arm/omap/omap-alsa-aic23.h rename sound/arm/{ => omap}/omap-alsa-dma.c (96%) rename sound/arm/{ => omap}/omap-alsa-dma.h (95%) create mode 100644 sound/arm/omap/omap-alsa.c diff --git a/arch/arm/mach-omap1/board-osk.c b/arch/arm/mach-omap1/board-osk.c index 56c8a4b12bb..76248bbc512 100644 --- a/arch/arm/mach-omap1/board-osk.c +++ b/arch/arm/mach-omap1/board-osk.c @@ -47,6 +47,8 @@ #include #include #include +#include +#include static int osk_keymap[] = { KEY(0, 0, KEY_F1), @@ -149,9 +151,40 @@ static struct platform_device osk5912_cf_device = { .resource = osk5912_cf_resources, }; +#define DEFAULT_BITPERSAMPLE 16 + +static struct omap_mcbsp_reg_cfg mcbsp_regs = { + .spcr2 = FREE | FRST | GRST | XRST | XINTM(3), + .spcr1 = RINTM(3) | RRST, + .rcr2 = RPHASE | RFRLEN2(OMAP_MCBSP_WORD_8) | + RWDLEN2(OMAP_MCBSP_WORD_16) | RDATDLY(0), + .rcr1 = RFRLEN1(OMAP_MCBSP_WORD_8) | RWDLEN1(OMAP_MCBSP_WORD_16), + .xcr2 = XPHASE | XFRLEN2(OMAP_MCBSP_WORD_8) | + XWDLEN2(OMAP_MCBSP_WORD_16) | XDATDLY(0) | XFIG, + .xcr1 = XFRLEN1(OMAP_MCBSP_WORD_8) | XWDLEN1(OMAP_MCBSP_WORD_16), + .srgr1 = FWID(DEFAULT_BITPERSAMPLE - 1), + .srgr2 = GSYNC | CLKSP | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1), + /*.pcr0 = FSXM | FSRM | CLKXM | CLKRM | CLKXP | CLKRP,*/ /* mcbsp: master */ + .pcr0 = CLKXP | CLKRP, /* mcbsp: slave */ +}; + +static struct omap_alsa_codec_config alsa_config = { + .name = "OSK AIC23", + .mcbsp_regs_alsa = &mcbsp_regs, + .codec_configure_dev = NULL, // aic23_configure, + .codec_set_samplerate = NULL, // aic23_set_samplerate, + .codec_clock_setup = NULL, // aic23_clock_setup, + .codec_clock_on = NULL, // aic23_clock_on, + .codec_clock_off = NULL, // aic23_clock_off, + .get_default_samplerate = NULL, // aic23_get_default_samplerate, +}; + static struct platform_device osk5912_mcbsp1_device = { - .name = "omap_mcbsp", - .id = 1, + .name = "omap_alsa_mcbsp", + .id = 1, + .dev = { + .platform_data = &alsa_config, + }, }; static struct resource osk5912_kp_resources[] = { diff --git a/sound/arm/omap-aic23.h b/include/asm-arm/arch-omap/omap-alsa.h similarity index 63% rename from sound/arm/omap-aic23.h rename to include/asm-arm/arch-omap/omap-alsa.h index c471d6cd6bb..b4ec45a84e6 100644 --- a/sound/arm/omap-aic23.h +++ b/include/asm-arm/arch-omap/omap-alsa.h @@ -1,7 +1,9 @@ /* - * sound/arm/omap-aic23.h + * linux/include/asm-arm/arch-omap/omap-alsa.h * - * Alsa Driver for AIC23 codec on OSK5912 platform board + * Alsa Driver for AIC23 and TSC2101 codecs on OMAP platform boards. + * + * Copyright (C) 2006 Mika Laitio * * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil * Written by Daniel Petrini, David Cohen, Anderson Briglia @@ -33,39 +35,38 @@ * 2005/07/25 INdT-10LE Kernel Team - Alsa driver for omap osk, * original version based in sa1100 driver * and omap oss driver. - * - * 2005-12-18 Dirk Behme - Added L/R Channel Interchange fix as proposed by Ajaya Babu */ -#ifndef __OMAP_AIC23_H -#define __OMAP_AIC23_H +#ifndef __OMAP_ALSA_H +#define __OMAP_ALSA_H #include #include #include #include +#include +#include +/* + * Debug functions + */ +#undef DEBUG +//#define DEBUG + +#define ERR(ARGS...) printk(KERN_ERR "{%s}-ERROR: ", __FUNCTION__);printk(ARGS); + +#ifdef DEBUG +#define DPRINTK(ARGS...) printk(KERN_INFO "<%s>: ",__FUNCTION__);printk(ARGS) +#define ADEBUG() printk("XXX Alsa debug f:%s, l:%d\n", __FUNCTION__, __LINE__) +#define FN_IN printk(KERN_INFO "[%s]: start\n", __FUNCTION__) +#define FN_OUT(n) printk(KERN_INFO "[%s]: end(%u)\n",__FUNCTION__, n) +#else +#define DPRINTK(ARGS...) /* nop */ +#define ADEBUG() /* nop */ +#define FN_IN /* nop */ +#define FN_OUT(n) /* nop */ +#endif -#define DEFAULT_OUTPUT_VOLUME 0x60 -#define DEFAULT_INPUT_VOLUME 0x00 /* 0 ==> mute line in */ - -#define OUTPUT_VOLUME_MIN LHV_MIN -#define OUTPUT_VOLUME_MAX LHV_MAX -#define OUTPUT_VOLUME_RANGE (OUTPUT_VOLUME_MAX - OUTPUT_VOLUME_MIN) -#define OUTPUT_VOLUME_MASK OUTPUT_VOLUME_MAX - -#define INPUT_VOLUME_MIN LIV_MIN -#define INPUT_VOLUME_MAX LIV_MAX -#define INPUT_VOLUME_RANGE (INPUT_VOLUME_MAX - INPUT_VOLUME_MIN) -#define INPUT_VOLUME_MASK INPUT_VOLUME_MAX - -#define SIDETONE_MASK 0x1c0 -#define SIDETONE_0 0x100 -#define SIDETONE_6 0x000 -#define SIDETONE_9 0x040 -#define SIDETONE_12 0x080 -#define SIDETONE_18 0x0c0 - -#define DEFAULT_ANALOG_AUDIO_CONTROL DAC_SELECTED | STE_ENABLED | BYPASS_ON | INSEL_MIC | MICB_20DB +#define DMA_BUF_SIZE (1024 * 8) /* * Buffer management for alsa and dma @@ -100,32 +101,44 @@ struct snd_card_omap_codec { struct audio_stream s[2]; /* playback & capture */ }; +/* Codec specific information and function pointers. + * Codec (omap-alsa-aic23.c and omap-alsa-tsc2101.c) + * are responsible for defining the function pointers. + */ +struct omap_alsa_codec_config { + char *name; + struct omap_mcbsp_reg_cfg *mcbsp_regs_alsa; + snd_pcm_hw_constraint_list_t *hw_constraints_rates; + snd_pcm_hardware_t *snd_omap_alsa_playback; + snd_pcm_hardware_t *snd_omap_alsa_capture; + void (*codec_configure_dev)(void); + void (*codec_set_samplerate)(long); + void (*codec_clock_setup)(void); + int (*codec_clock_on)(void); + int (*codec_clock_off)(void); + int (*get_default_samplerate)(void); +}; + /*********** Mixer function prototypes *************************/ int snd_omap_mixer(struct snd_card_omap_codec *); void snd_omap_init_mixer(void); -/* Clock functions */ -int omap_aic23_clock_on(void); -int omap_aic23_clock_off(void); #ifdef CONFIG_PM void snd_omap_suspend_mixer(void); void snd_omap_resume_mixer(void); #endif +int snd_omap_alsa_post_probe(struct platform_device *pdev, struct omap_alsa_codec_config *config); +int snd_omap_alsa_remove(struct platform_device *pdev); +#ifdef CONFIG_PM +int snd_omap_alsa_suspend(struct platform_device *pdev, pm_message_t state); +int snd_omap_alsa_resume(struct platform_device *pdev); +#else +#define snd_omap_alsa_suspend NULL +#define snd_omap_alsa_resume NULL +#endif + /*********** function prototype to function called from the dma interrupt handler ******/ void callback_omap_alsa_sound_dma(void *); -/* Codec AIC23 */ -#if defined(CONFIG_SENSORS_TLV320AIC23) || defined (CONFIG_SENSORS_TLV320AIC23_MODULE) - -extern int tlv320aic23_write_value(u8 reg, u16 value); - -/* TLV320AIC23 is a write only device */ -static __inline__ void audio_aic23_write(u8 address, u16 data) -{ - tlv320aic23_write_value(address, data); -} - -#endif /* CONFIG_SENSORS_TLV320AIC23 */ - #endif diff --git a/sound/arm/Makefile b/sound/arm/Makefile index 019a9f03c2c..bd12f53404a 100644 --- a/sound/arm/Makefile +++ b/sound/arm/Makefile @@ -14,5 +14,4 @@ snd-pxa2xx-pcm-objs := pxa2xx-pcm.o obj-$(CONFIG_SND_PXA2XX_AC97) += snd-pxa2xx-ac97.o snd-pxa2xx-ac97-objs := pxa2xx-ac97.o -obj-$(CONFIG_SND_OMAP_AIC23) += snd-omap-aic23.o -snd-omap-aic23-objs := omap-aic23.o omap-alsa-dma.o omap-alsa-mixer.o +obj-$(CONFIG_SND) += omap/ diff --git a/sound/arm/omap-aic23.c b/sound/arm/omap-aic23.c deleted file mode 100644 index 69cc64a63a3..00000000000 --- a/sound/arm/omap-aic23.c +++ /dev/null @@ -1,904 +0,0 @@ -/* - * sound/arm/omap-aic23.c - * - * Alsa Driver for AIC23 codec on OSK5912 platform board - * - * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil - * Written by Daniel Petrini, David Cohen, Anderson Briglia - * {daniel.petrini, david.cohen, anderson.briglia}@indt.org.br - * - * Based on sa11xx-uda1341.c, - * Copyright (C) 2002 Tomas Kasparek - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - * History: - * - * 2005-07-29 INdT Kernel Team - Alsa driver for omap osk. Creation of new - * file omap-aic23.c - * - * 2005-12-18 Dirk Behme - Added L/R Channel Interchange fix as proposed by Ajaya Babu - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_PM -#include -#endif - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "omap-alsa-dma.h" -#include "omap-aic23.h" - -#undef DEBUG - -#ifdef DEBUG -#define ADEBUG() printk("XXX Alsa debug f:%s, l:%d\n", __FUNCTION__, __LINE__) -#else -#define ADEBUG() /* nop */ -#endif - -/* Define to set the AIC23 as the master w.r.t McBSP */ -#define AIC23_MASTER - -/* - * AUDIO related MACROS - */ -#define DEFAULT_BITPERSAMPLE 16 -#define AUDIO_RATE_DEFAULT 44100 -#define AUDIO_MCBSP OMAP_MCBSP1 -#define NUMBER_SAMPLE_RATES_SUPPORTED 10 - - -MODULE_AUTHOR("Daniel Petrini, David Cohen, Anderson Briglia - INdT"); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("OMAP AIC23 driver for ALSA"); -MODULE_SUPPORTED_DEVICE("{{AIC23,OMAP AIC23}}"); -MODULE_ALIAS("omap_mcbsp.1"); - -static char *id = NULL; -MODULE_PARM_DESC(id, "OMAP OSK ALSA Driver for AIC23 chip."); - -static struct snd_card_omap_codec *omap_aic23 = NULL; - -static struct clk *aic23_mclk = 0; - -struct sample_rate_rate_reg_info { - u8 control; /* SR3, SR2, SR1, SR0 and BOSR */ - u8 divider; /* if 0 CLKIN = MCLK, if 1 CLKIN = MCLK/2 */ -}; - -/* - * DAC USB-mode sampling rates (MCLK = 12 MHz) - * The rates and rate_reg_into MUST be in the same order - */ -static unsigned int rates[] = { - 4000, 8000, 16000, 22050, - 24000, 32000, 44100, - 48000, 88200, 96000, -}; -static const struct sample_rate_rate_reg_info - rate_reg_info[NUMBER_SAMPLE_RATES_SUPPORTED] = { - {0x06, 1}, /* 4000 */ - {0x06, 0}, /* 8000 */ - {0x0C, 1}, /* 16000 */ - {0x11, 1}, /* 22050 */ - {0x00, 1}, /* 24000 */ - {0x0C, 0}, /* 32000 */ - {0x11, 0}, /* 44100 */ - {0x00, 0}, /* 48000 */ - {0x1F, 0}, /* 88200 */ - {0x0E, 0}, /* 96000 */ -}; - -/* - * mcbsp configuration structure - */ -static struct omap_mcbsp_reg_cfg initial_config_mcbsp = { - .spcr2 = FREE | FRST | GRST | XRST | XINTM(3), - .spcr1 = RINTM(3) | RRST, - .rcr2 = RPHASE | RFRLEN2(OMAP_MCBSP_WORD_8) | - RWDLEN2(OMAP_MCBSP_WORD_16) | RDATDLY(0), - .rcr1 = RFRLEN1(OMAP_MCBSP_WORD_8) | RWDLEN1(OMAP_MCBSP_WORD_16), - .xcr2 = XPHASE | XFRLEN2(OMAP_MCBSP_WORD_8) | - XWDLEN2(OMAP_MCBSP_WORD_16) | XDATDLY(0) | XFIG, - .xcr1 = XFRLEN1(OMAP_MCBSP_WORD_8) | XWDLEN1(OMAP_MCBSP_WORD_16), - .srgr1 = FWID(DEFAULT_BITPERSAMPLE - 1), - .srgr2 = GSYNC | CLKSP | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1), -#ifndef AIC23_MASTER - /* configure McBSP to be the I2S master */ - .pcr0 = FSXM | FSRM | CLKXM | CLKRM | CLKXP | CLKRP, -#else - /* configure McBSP to be the I2S slave */ - .pcr0 = CLKXP | CLKRP, -#endif /* AIC23_MASTER */ -}; - -static snd_pcm_hw_constraint_list_t hw_constraints_rates = { - .count = ARRAY_SIZE(rates), - .list = rates, - .mask = 0, -}; - -/* - * HW interface start and stop helper functions - */ -static int audio_ifc_start(void) -{ - omap_mcbsp_start(AUDIO_MCBSP); - return 0; -} - -static int audio_ifc_stop(void) -{ - omap_mcbsp_stop(AUDIO_MCBSP); - return 0; -} - -/* - * Codec/mcbsp init and configuration section - * codec dependent code. - */ - -/* - * Sample rate changing - */ -static void omap_aic23_set_samplerate(struct snd_card_omap_codec - *omap_aic23, long rate) -{ - u8 count = 0; - u16 data = 0; - - /* Fix the rate if it has a wrong value */ - if (rate >= 96000) - rate = 96000; - else if (rate >= 88200) - rate = 88200; - else if (rate >= 48000) - rate = 48000; - else if (rate >= 44100) - rate = 44100; - else if (rate >= 32000) - rate = 32000; - else if (rate >= 24000) - rate = 24000; - else if (rate >= 22050) - rate = 22050; - else if (rate >= 16000) - rate = 16000; - else if (rate >= 8000) - rate = 8000; - else - rate = 4000; - - /* Search for the right sample rate */ - /* Verify what happens if the rate is not supported - * now it goes to 96Khz */ - while ((rates[count] != rate) && - (count < (NUMBER_SAMPLE_RATES_SUPPORTED - 1))) { - count++; - } - - data = (rate_reg_info[count].divider << CLKIN_SHIFT) | - (rate_reg_info[count].control << BOSR_SHIFT) | USB_CLK_ON; - - audio_aic23_write(SAMPLE_RATE_CONTROL_ADDR, data); - - omap_aic23->samplerate = rate; -} - -static inline void aic23_configure(void) -{ - /* Reset codec */ - audio_aic23_write(RESET_CONTROL_ADDR, 0); - - /* Initialize the AIC23 internal state */ - - /* Analog audio path control, DAC selected, delete INSEL_MIC for line in */ - audio_aic23_write(ANALOG_AUDIO_CONTROL_ADDR, DEFAULT_ANALOG_AUDIO_CONTROL); - - /* Digital audio path control, de-emphasis control 44.1kHz */ - audio_aic23_write(DIGITAL_AUDIO_CONTROL_ADDR, DEEMP_44K); - - /* Digital audio interface, master/slave mode, I2S, 16 bit */ -#ifdef AIC23_MASTER - audio_aic23_write(DIGITAL_AUDIO_FORMAT_ADDR, - MS_MASTER | IWL_16 | FOR_DSP); -#else - audio_aic23_write(DIGITAL_AUDIO_FORMAT_ADDR, IWL_16 | FOR_DSP); -#endif - - /* Enable digital interface */ - audio_aic23_write(DIGITAL_INTERFACE_ACT_ADDR, ACT_ON); - -} - -static void omap_aic23_audio_init(struct snd_card_omap_codec *omap_aic23) -{ - /* Setup DMA stuff */ - omap_aic23->s[SNDRV_PCM_STREAM_PLAYBACK].id = "Alsa AIC23 out"; - omap_aic23->s[SNDRV_PCM_STREAM_PLAYBACK].stream_id = - SNDRV_PCM_STREAM_PLAYBACK; - omap_aic23->s[SNDRV_PCM_STREAM_PLAYBACK].dma_dev = - OMAP_DMA_MCBSP1_TX; - omap_aic23->s[SNDRV_PCM_STREAM_PLAYBACK].hw_start = - audio_ifc_start; - omap_aic23->s[SNDRV_PCM_STREAM_PLAYBACK].hw_stop = - audio_ifc_stop; - - omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE].id = "Alsa AIC23 in"; - omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE].stream_id = - SNDRV_PCM_STREAM_CAPTURE; - omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE].dma_dev = - OMAP_DMA_MCBSP1_RX; - omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE].hw_start = - audio_ifc_start; - omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE].hw_stop = - audio_ifc_stop; - - /* configuring the McBSP */ - omap_mcbsp_request(AUDIO_MCBSP); - - /* if configured, then stop mcbsp */ - omap_mcbsp_stop(AUDIO_MCBSP); - - omap_mcbsp_config(AUDIO_MCBSP, &initial_config_mcbsp); - omap_mcbsp_start(AUDIO_MCBSP); - aic23_configure(); -} - -/* - * DMA functions - * Depends on omap-aic23-dma.c functions and (omap) dma.c - * - */ -#define DMA_BUF_SIZE 1024 * 8 - -static int audio_dma_request(struct audio_stream *s, - void (*callback) (void *)) -{ - int err; - - err = omap_request_alsa_sound_dma(s->dma_dev, s->id, s, &s->lch); - if (err < 0) - printk(KERN_ERR "unable to grab audio dma 0x%x\n", - s->dma_dev); - return err; -} - -static int audio_dma_free(struct audio_stream *s) -{ - int err = 0; - - err = omap_free_alsa_sound_dma(s, &s->lch); - if (err < 0) - printk(KERN_ERR "Unable to free audio dma channels!\n"); - return err; -} - -/* - * This function should calculate the current position of the dma in the - * buffer. It will help alsa middle layer to continue update the buffer. - * Its correctness is crucial for good functioning. - */ -static u_int audio_get_dma_pos(struct audio_stream *s) -{ - snd_pcm_substream_t *substream = s->stream; - snd_pcm_runtime_t *runtime = substream->runtime; - unsigned int offset; - unsigned long flags; - dma_addr_t count; - ADEBUG(); - - /* this must be called w/ interrupts locked as requested in dma.c */ - spin_lock_irqsave(&s->dma_lock, flags); - - /* For the current period let's see where we are */ - count = omap_get_dma_src_addr_counter(s->lch[s->dma_q_head]); - - spin_unlock_irqrestore(&s->dma_lock, flags); - - /* Now, the position related to the end of that period */ - offset = bytes_to_frames(runtime, s->offset) - bytes_to_frames(runtime, count); - - if (offset >= runtime->buffer_size || offset < 0) - offset = 0; - - return offset; -} - -/* - * this stops the dma and clears the dma ptrs - */ -static void audio_stop_dma(struct audio_stream *s) -{ - unsigned long flags; - ADEBUG(); - - spin_lock_irqsave(&s->dma_lock, flags); - s->active = 0; - s->period = 0; - s->periods = 0; - - /* this stops the dma channel and clears the buffer ptrs */ - /* this stops the dma channel and clears the buffer ptrs */ - omap_stop_alsa_sound_dma(s); - - omap_clear_alsa_sound_dma(s); - - spin_unlock_irqrestore(&s->dma_lock, flags); -} - -/* - * Main dma routine, requests dma according where you are in main alsa buffer - */ -static void audio_process_dma(struct audio_stream *s) -{ - snd_pcm_substream_t *substream = s->stream; - snd_pcm_runtime_t *runtime; - unsigned int dma_size; - unsigned int offset; - int ret; - - runtime = substream->runtime; - if (s->active) { - dma_size = frames_to_bytes(runtime, runtime->period_size); - offset = dma_size * s->period; - snd_assert(dma_size <= DMA_BUF_SIZE,); - ret = omap_start_alsa_sound_dma(s, - (dma_addr_t) runtime->dma_area + - offset, dma_size); - if (ret) { - printk(KERN_ERR - "audio_process_dma: cannot queue DMA buffer (%i)\n", - ret); - return; - } - - s->period++; - s->period %= runtime->periods; - s->periods++; - s->offset = offset; - } -} - -/* - * This is called when dma IRQ occurs at the end of each transmited block - */ -void callback_omap_alsa_sound_dma(void *data) -{ - struct audio_stream *s = data; - - /* - * If we are getting a callback for an active stream then we inform - * the PCM middle layer we've finished a period - */ - if (s->active) - snd_pcm_period_elapsed(s->stream); - - spin_lock(&s->dma_lock); - if (s->periods > 0) { - s->periods--; - } - audio_process_dma(s); - spin_unlock(&s->dma_lock); -} - - -/* - * Alsa section - * PCM settings and callbacks - */ - -static int snd_omap_alsa_trigger(snd_pcm_substream_t * substream, int cmd) -{ - struct snd_card_omap_codec *chip = - snd_pcm_substream_chip(substream); - int stream_id = substream->pstr->stream; - struct audio_stream *s = &chip->s[stream_id]; - int err = 0; - ADEBUG(); - - /* note local interrupts are already disabled in the midlevel code */ - spin_lock(&s->dma_lock); - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - /* requested stream startup */ - s->active = 1; - audio_process_dma(s); - break; - case SNDRV_PCM_TRIGGER_STOP: - /* requested stream shutdown */ - audio_stop_dma(s); - break; - default: - err = -EINVAL; - break; - } - spin_unlock(&s->dma_lock); - - return err; -} - -static int snd_omap_alsa_prepare(snd_pcm_substream_t * substream) -{ - struct snd_card_omap_codec *chip = - snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; - struct audio_stream *s = &chip->s[substream->pstr->stream]; - - /* set requested samplerate */ - omap_aic23_set_samplerate(chip, runtime->rate); - - s->period = 0; - s->periods = 0; - - return 0; -} - -static snd_pcm_uframes_t snd_omap_alsa_pointer(snd_pcm_substream_t * - substream) -{ - struct snd_card_omap_codec *chip = - snd_pcm_substream_chip(substream); - - return audio_get_dma_pos(&chip->s[substream->pstr->stream]); -} - -/* Hardware capabilities */ - -static snd_pcm_hardware_t snd_omap_alsa_capture = { - .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), - .formats = (SNDRV_PCM_FMTBIT_S16_LE), - .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | - SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_KNOT), - .rate_min = 8000, - .rate_max = 96000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 128 * 1024, - .period_bytes_min = 32, - .period_bytes_max = 8 * 1024, - .periods_min = 16, - .periods_max = 255, - .fifo_size = 0, -}; - -static snd_pcm_hardware_t snd_omap_alsa_playback = { - .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), - .formats = (SNDRV_PCM_FMTBIT_S16_LE), - .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | - SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_KNOT), - .rate_min = 8000, - .rate_max = 96000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 128 * 1024, - .period_bytes_min = 32, - .period_bytes_max = 8 * 1024, - .periods_min = 16, - .periods_max = 255, - .fifo_size = 0, -}; - -static int snd_card_omap_alsa_open(snd_pcm_substream_t * substream) -{ - struct snd_card_omap_codec *chip = - snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; - int stream_id = substream->pstr->stream; - int err; - ADEBUG(); - - chip->s[stream_id].stream = substream; - - omap_aic23_clock_on(); - - if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) - runtime->hw = snd_omap_alsa_playback; - else - runtime->hw = snd_omap_alsa_capture; - if ((err = - snd_pcm_hw_constraint_integer(runtime, - SNDRV_PCM_HW_PARAM_PERIODS)) < - 0) - return err; - if ((err = - snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &hw_constraints_rates)) < 0) - return err; - - return 0; -} - -static int snd_card_omap_alsa_close(snd_pcm_substream_t * substream) -{ - struct snd_card_omap_codec *chip = - snd_pcm_substream_chip(substream); - ADEBUG(); - - omap_aic23_clock_off(); - chip->s[substream->pstr->stream].stream = NULL; - - return 0; -} - -/* HW params & free */ - -static int snd_omap_alsa_hw_params(snd_pcm_substream_t * substream, - snd_pcm_hw_params_t * hw_params) -{ - return snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); -} - -static int snd_omap_alsa_hw_free(snd_pcm_substream_t * substream) -{ - return snd_pcm_lib_free_pages(substream); -} - -/* pcm operations */ -static snd_pcm_ops_t snd_card_omap_alsa_playback_ops = { - .open = snd_card_omap_alsa_open, - .close = snd_card_omap_alsa_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_omap_alsa_hw_params, - .hw_free = snd_omap_alsa_hw_free, - .prepare = snd_omap_alsa_prepare, - .trigger = snd_omap_alsa_trigger, - .pointer = snd_omap_alsa_pointer, -}; - -static snd_pcm_ops_t snd_card_omap_alsa_capture_ops = { - .open = snd_card_omap_alsa_open, - .close = snd_card_omap_alsa_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_omap_alsa_hw_params, - .hw_free = snd_omap_alsa_hw_free, - .prepare = snd_omap_alsa_prepare, - .trigger = snd_omap_alsa_trigger, - .pointer = snd_omap_alsa_pointer, -}; - -/* - * Alsa init and exit section - * - * Inits pcm alsa structures, allocate the alsa buffer, suspend, resume - */ -static int __init snd_card_omap_alsa_pcm(struct snd_card_omap_codec *omap_alsa, - int device) -{ - snd_pcm_t *pcm; - int err; - ADEBUG(); - - if ((err = - snd_pcm_new(omap_aic23->card, "AIC23 PCM", device, 1, 1, - &pcm)) < 0) - return err; - - /* sets up initial buffer with continuous allocation */ - snd_pcm_lib_preallocate_pages_for_all(pcm, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data - (GFP_KERNEL), - 128 * 1024, 128 * 1024); - - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, - &snd_card_omap_alsa_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, - &snd_card_omap_alsa_capture_ops); - pcm->private_data = omap_aic23; - pcm->info_flags = 0; - strcpy(pcm->name, "omap aic23 pcm"); - - omap_aic23_audio_init(omap_aic23); - - /* setup DMA controller */ - audio_dma_request(&omap_aic23->s[SNDRV_PCM_STREAM_PLAYBACK], - callback_omap_alsa_sound_dma); - audio_dma_request(&omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE], - callback_omap_alsa_sound_dma); - - omap_aic23->pcm = pcm; - - return 0; -} - -#ifdef CONFIG_PM -/* - * Driver suspend/resume - calls alsa functions. Some hints from aaci.c - */ -int snd_omap_alsa_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct snd_card_omap_codec *chip; - snd_card_t *card = platform_get_drvdata(pdev); - - if (card->power_state != SNDRV_CTL_POWER_D3hot) { - chip = card->private_data; - if (chip->card->power_state != SNDRV_CTL_POWER_D3hot) { - snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(chip->pcm); - /* Mutes and turn clock off */ - omap_aic23_clock_off(); - snd_omap_suspend_mixer(); - } - } - return 0; -} - -int snd_omap_alsa_resume(struct platform_device *pdev) -{ - struct snd_card_omap_codec *chip; - snd_card_t *card = platform_get_drvdata(pdev); - - if (card->power_state != SNDRV_CTL_POWER_D0) { - chip = card->private_data; - if (chip->card->power_state != SNDRV_CTL_POWER_D0) { - snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0); - omap_aic23_clock_on(); - snd_omap_resume_mixer(); - } - } - return 0; -} - -#else -#define snd_omap_alsa_suspend NULL -#define snd_omap_alsa_resume NULL -#endif /* CONFIG_PM */ - -/* - */ -void snd_omap_alsa_free(snd_card_t * card) -{ - struct snd_card_omap_codec *chip = card->private_data; - ADEBUG(); - - /* - * Turn off codec after it is done. - * Can't do it immediately, since it may still have - * buffered data. - */ - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(2); - - omap_mcbsp_stop(AUDIO_MCBSP); - omap_mcbsp_free(AUDIO_MCBSP); - - audio_aic23_write(RESET_CONTROL_ADDR, 0); - audio_aic23_write(POWER_DOWN_CONTROL_ADDR, 0xff); - - audio_dma_free(&chip->s[SNDRV_PCM_STREAM_PLAYBACK]); - audio_dma_free(&chip->s[SNDRV_PCM_STREAM_CAPTURE]); -} - -/* - * Omap MCBSP clock configuration - * - * Here we have some functions that allows clock to be enabled and - * disabled only when needed. Besides doing clock configuration - * it allows turn on/turn off audio when necessary. - */ -#define CODEC_CLOCK 12000000 -#define AUDIO_RATE_DEFAULT 44100 - -/* - * Do clock framework mclk search - */ -static __init void omap_aic23_clock_setup(void) -{ - aic23_mclk = clk_get(0, "mclk"); -} - -/* - * Do some sanity check, set clock rate, starts it and - * turn codec audio on - */ -int omap_aic23_clock_on(void) -{ - if (clk_get_usecount(aic23_mclk) > 0) { - /* MCLK is already in use */ - printk(KERN_WARNING - "MCLK in use at %d Hz. We change it to %d Hz\n", - (uint) clk_get_rate(aic23_mclk), - CODEC_CLOCK); - } - - if (clk_set_rate(aic23_mclk, CODEC_CLOCK)) { - printk(KERN_ERR - "Cannot set MCLK for AIC23 CODEC\n"); - return -ECANCELED; - } - - clk_enable(aic23_mclk); - - printk(KERN_DEBUG - "MCLK = %d [%d], usecount = %d\n", - (uint) clk_get_rate(aic23_mclk), CODEC_CLOCK, - clk_get_usecount(aic23_mclk)); - - /* Now turn the audio on */ - audio_aic23_write(POWER_DOWN_CONTROL_ADDR, - ~DEVICE_POWER_OFF & ~OUT_OFF & ~DAC_OFF & - ~ADC_OFF & ~MIC_OFF & ~LINE_OFF); - - return 0; -} -/* - * Do some sanity check, turn clock off and then turn - * codec audio off - */ -int omap_aic23_clock_off(void) -{ - if (clk_get_usecount(aic23_mclk) > 0) { - if (clk_get_rate(aic23_mclk) != CODEC_CLOCK) { - printk(KERN_WARNING - "MCLK for audio should be %d Hz. But is %d Hz\n", - (uint) clk_get_rate(aic23_mclk), - CODEC_CLOCK); - } - - clk_disable(aic23_mclk); - } - - audio_aic23_write(POWER_DOWN_CONTROL_ADDR, - DEVICE_POWER_OFF | OUT_OFF | DAC_OFF | - ADC_OFF | MIC_OFF | LINE_OFF); - return 0; -} - -/* module init & exit */ - -/* - * Inits alsa soudcard structure - */ -static int __init snd_omap_alsa_aic23_probe(struct platform_device *pdev) -{ - int err = 0; - snd_card_t *card; - ADEBUG(); - - /* gets clock from clock framework */ - omap_aic23_clock_setup(); - - /* register the soundcard */ - card = snd_card_new(-1, id, THIS_MODULE, sizeof(omap_aic23)); - if (card == NULL) - return -ENOMEM; - - omap_aic23 = kcalloc(1, sizeof(*omap_aic23), GFP_KERNEL); - if (omap_aic23 == NULL) - return -ENOMEM; - - card->private_data = (void *) omap_aic23; - card->private_free = snd_omap_alsa_free; - - omap_aic23->card = card; - omap_aic23->samplerate = AUDIO_RATE_DEFAULT; - - spin_lock_init(&omap_aic23->s[0].dma_lock); - spin_lock_init(&omap_aic23->s[1].dma_lock); - - /* mixer */ - if ((err = snd_omap_mixer(omap_aic23)) < 0) - goto nodev; - - /* PCM */ - if ((err = snd_card_omap_alsa_pcm(omap_aic23, 0)) < 0) - goto nodev; - - strcpy(card->driver, "AIC23"); - strcpy(card->shortname, "OSK AIC23"); - sprintf(card->longname, "OMAP OSK with AIC23"); - - snd_omap_init_mixer(); - - snd_card_set_dev(card, &pdev->dev); - - if ((err = snd_card_register(card)) == 0) { - printk(KERN_INFO "OSK audio support initialized\n"); - platform_set_drvdata(pdev, card); - return 0; - } - -nodev: - snd_card_free(card); - - return err; -} - -static int snd_omap_alsa_remove(struct platform_device *pdev) -{ - snd_card_t *card = platform_get_drvdata(pdev); - struct snd_card_omap_codec *chip = card->private_data; - - snd_card_free(card); - - omap_aic23 = NULL; - card->private_data = NULL; - kfree(chip); - - platform_set_drvdata(pdev, NULL); - - return 0; - -} - -static struct platform_driver omap_alsa_driver = { - .probe = snd_omap_alsa_aic23_probe, - .remove = snd_omap_alsa_remove, - .suspend = snd_omap_alsa_suspend, - .resume = snd_omap_alsa_resume, - .driver = { - .name = "omap_mcbsp", - }, -}; - -static int __init omap_alsa_aic23_init(void) -{ - int err; - ADEBUG(); - - err = platform_driver_register(&omap_alsa_driver); - - return err; -} - -static void __exit omap_alsa_aic23_exit(void) -{ - ADEBUG(); - - platform_driver_unregister(&omap_alsa_driver); -} - -module_init(omap_alsa_aic23_init); -module_exit(omap_alsa_aic23_exit); diff --git a/sound/arm/omap/Makefile b/sound/arm/omap/Makefile new file mode 100644 index 00000000000..74160da67d2 --- /dev/null +++ b/sound/arm/omap/Makefile @@ -0,0 +1,6 @@ +# +## Makefile for ALSA OMAP +# +# +obj-$(CONFIG_SND_OMAP_AIC23) += snd-omap-alsa-aic23.o +snd-omap-alsa-aic23-objs := omap-alsa.o omap-alsa-dma.o omap-alsa-aic23.o omap-alsa-aic23-mixer.o diff --git a/sound/arm/omap-alsa-mixer.c b/sound/arm/omap/omap-alsa-aic23-mixer.c similarity index 97% rename from sound/arm/omap-alsa-mixer.c rename to sound/arm/omap/omap-alsa-aic23-mixer.c index 476f1060921..1fb0160d1b2 100644 --- a/sound/arm/omap-alsa-mixer.c +++ b/sound/arm/omap/omap-alsa-aic23-mixer.c @@ -1,5 +1,5 @@ /* - * sound/arm/omap-alsa-mixer.c + * sound/arm/omap/omap-alsa-aic23-mixer.c * * Alsa Driver Mixer for generic codecs for omap boards * @@ -39,20 +39,10 @@ #include #include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include #include -#include "omap-aic23.h" +#include +#include "omap-alsa-aic23.h" #include #include @@ -67,7 +57,7 @@ MODULE_DESCRIPTION("OMAP Alsa mixer driver for ALSA"); /* Codec AIC23 */ #if defined(CONFIG_SENSORS_TLV320AIC23) || defined (CONFIG_SENSORS_TLV320AIC23_MODULE) -extern __inline__ void audio_aic23_write(u8, u16); +extern void audio_aic23_write(u8, u16); #define MIXER_NAME "Mixer AIC23" #define SND_OMAP_WRITE(reg, val) audio_aic23_write(reg, val) @@ -411,31 +401,6 @@ static snd_kcontrol_new_t snd_omap_controls[] = { OMAP_MUX("Capture Source", ANALOG_AUDIO_CONTROL_ADDR, AAC_INDEX, INSEL_MIC), }; -void snd_omap_init_mixer(void) -{ - u16 vol_reg; - - /* Line's default values */ - omap_regs[LINE_INDEX].l_reg = DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK; - omap_regs[LINE_INDEX].r_reg = DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK; - omap_regs[LINE_INDEX].sw = 0; - SND_OMAP_WRITE(LEFT_LINE_VOLUME_ADDR, DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK); - SND_OMAP_WRITE(RIGHT_LINE_VOLUME_ADDR, DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK); - - /* Analog Audio Control's default values */ - omap_regs[AAC_INDEX].l_reg = DEFAULT_ANALOG_AUDIO_CONTROL; - - /* Headphone's default values */ - vol_reg = LZC_ON; - vol_reg &= ~OUTPUT_VOLUME_MASK; - vol_reg |= DEFAULT_OUTPUT_VOLUME; - omap_regs[PCM_INDEX].l_reg = DEFAULT_OUTPUT_VOLUME; - omap_regs[PCM_INDEX].r_reg = DEFAULT_OUTPUT_VOLUME; - omap_regs[PCM_INDEX].sw = 1; - SND_OMAP_WRITE(LEFT_CHANNEL_VOLUME_ADDR, vol_reg); - SND_OMAP_WRITE(RIGHT_CHANNEL_VOLUME_ADDR, vol_reg); -} - #ifdef CONFIG_PM void snd_omap_suspend_mixer(void) @@ -474,6 +439,31 @@ void snd_omap_resume_mixer(void) } #endif +void snd_omap_init_mixer(void) +{ + u16 vol_reg; + + /* Line's default values */ + omap_regs[LINE_INDEX].l_reg = DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK; + omap_regs[LINE_INDEX].r_reg = DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK; + omap_regs[LINE_INDEX].sw = 0; + SND_OMAP_WRITE(LEFT_LINE_VOLUME_ADDR, DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK); + SND_OMAP_WRITE(RIGHT_LINE_VOLUME_ADDR, DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK); + + /* Analog Audio Control's default values */ + omap_regs[AAC_INDEX].l_reg = DEFAULT_ANALOG_AUDIO_CONTROL; + + /* Headphone's default values */ + vol_reg = LZC_ON; + vol_reg &= ~OUTPUT_VOLUME_MASK; + vol_reg |= DEFAULT_OUTPUT_VOLUME; + omap_regs[PCM_INDEX].l_reg = DEFAULT_OUTPUT_VOLUME; + omap_regs[PCM_INDEX].r_reg = DEFAULT_OUTPUT_VOLUME; + omap_regs[PCM_INDEX].sw = 1; + SND_OMAP_WRITE(LEFT_CHANNEL_VOLUME_ADDR, vol_reg); + SND_OMAP_WRITE(RIGHT_CHANNEL_VOLUME_ADDR, vol_reg); +} + int snd_omap_mixer(struct snd_card_omap_codec *chip) { snd_card_t *card; @@ -493,4 +483,3 @@ int snd_omap_mixer(struct snd_card_omap_codec *chip) return 0; } - diff --git a/sound/arm/omap/omap-alsa-aic23.c b/sound/arm/omap/omap-alsa-aic23.c new file mode 100644 index 00000000000..f4906161819 --- /dev/null +++ b/sound/arm/omap/omap-alsa-aic23.c @@ -0,0 +1,321 @@ +/* + * arch/arm/mach-omap1/omap-alsa-aic23.c + * + * Alsa codec Driver for AIC23 chip on OSK5912 platform board + * + * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil + * Written by Daniel Petrini, David Cohen, Anderson Briglia + * {daniel.petrini, david.cohen, anderson.briglia}@indt.org.br + * + * Copyright (C) 2006 Mika Laitio + * + * Based in former alsa driver for osk and oss driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "omap-alsa-aic23.h" + +static struct clk *aic23_mclk = 0; + +/* aic23 related */ +static const struct aic23_samplerate_reg_info + rate_reg_info[NUMBER_SAMPLE_RATES_SUPPORTED] = { + {4000, 0x06, 1}, /* 4000 */ + {8000, 0x06, 0}, /* 8000 */ + {16000, 0x0C, 1}, /* 16000 */ + {22050, 0x11, 1}, /* 22050 */ + {24000, 0x00, 1}, /* 24000 */ + {32000, 0x0C, 0}, /* 32000 */ + {44100, 0x11, 0}, /* 44100 */ + {48000, 0x00, 0}, /* 48000 */ + {88200, 0x1F, 0}, /* 88200 */ + {96000, 0x0E, 0}, /* 96000 */ +}; + +/* + * Hardware capabilities + */ + + /* + * DAC USB-mode sampling rates (MCLK = 12 MHz) + * The rates and rate_reg_into MUST be in the same order + */ +static unsigned int rates[] = { + 4000, 8000, 16000, 22050, + 24000, 32000, 44100, + 48000, 88200, 96000, +}; + +static snd_pcm_hw_constraint_list_t aic23_hw_constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static snd_pcm_hardware_t aic23_snd_omap_alsa_playback = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), + .formats = (SNDRV_PCM_FMTBIT_S16_LE), + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_KNOT), + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 8 * 1024, + .periods_min = 16, + .periods_max = 255, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t aic23_snd_omap_alsa_capture = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), + .formats = (SNDRV_PCM_FMTBIT_S16_LE), + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_KNOT), + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 8 * 1024, + .periods_min = 16, + .periods_max = 255, + .fifo_size = 0, +}; + +/* + * Codec/mcbsp init and configuration section + * codec dependent code. + */ + +extern int tlv320aic23_write_value(u8 reg, u16 value); + +/* TLV320AIC23 is a write only device */ +void audio_aic23_write(u8 address, u16 data) +{ + tlv320aic23_write_value(address, data); +} +EXPORT_SYMBOL_GPL(audio_aic23_write); + +/* + * Sample rate changing + */ +void aic23_set_samplerate(long rate) +{ + u8 count = 0; + u16 data = 0; + + /* Fix the rate if it has a wrong value */ + if (rate >= 96000) + rate = 96000; + else if (rate >= 88200) + rate = 88200; + else if (rate >= 48000) + rate = 48000; + else if (rate >= 44100) + rate = 44100; + else if (rate >= 32000) + rate = 32000; + else if (rate >= 24000) + rate = 24000; + else if (rate >= 22050) + rate = 22050; + else if (rate >= 16000) + rate = 16000; + else if (rate >= 8000) + rate = 8000; + else + rate = 4000; + + /* Search for the right sample rate */ + /* Verify what happens if the rate is not supported + * now it goes to 96Khz */ + while ((rate_reg_info[count].sample_rate != rate) && + (count < (NUMBER_SAMPLE_RATES_SUPPORTED - 1))) { + count++; + } + + data = (rate_reg_info[count].divider << CLKIN_SHIFT) | + (rate_reg_info[count].control << BOSR_SHIFT) | USB_CLK_ON; + + audio_aic23_write(SAMPLE_RATE_CONTROL_ADDR, data); +} + +inline void aic23_configure(void) +{ + /* Reset codec */ + audio_aic23_write(RESET_CONTROL_ADDR, 0); + + /* Initialize the AIC23 internal state */ + + /* Analog audio path control, DAC selected, delete INSEL_MIC for line in */ + audio_aic23_write(ANALOG_AUDIO_CONTROL_ADDR, DEFAULT_ANALOG_AUDIO_CONTROL); + + /* Digital audio path control, de-emphasis control 44.1kHz */ + audio_aic23_write(DIGITAL_AUDIO_CONTROL_ADDR, DEEMP_44K); + + /* Digital audio interface, master/slave mode, I2S, 16 bit */ +#ifdef AIC23_MASTER + audio_aic23_write(DIGITAL_AUDIO_FORMAT_ADDR, + MS_MASTER | IWL_16 | FOR_DSP); +#else + audio_aic23_write(DIGITAL_AUDIO_FORMAT_ADDR, IWL_16 | FOR_DSP); +#endif + + /* Enable digital interface */ + audio_aic23_write(DIGITAL_INTERFACE_ACT_ADDR, ACT_ON); +} + +/* + * Omap MCBSP clock configuration and Power Management + * + * Here we have some functions that allows clock to be enabled and + * disabled only when needed. Besides doing clock configuration + * it allows turn on/turn off audio when necessary. + */ +/* + * Do clock framework mclk search + */ +void aic23_clock_setup(void) +{ + aic23_mclk = clk_get(0, "mclk"); +} + +/* + * Do some sanity check, set clock rate, starts it and + * turn codec audio on + */ +int aic23_clock_on(void) +{ + if (clk_get_usecount(aic23_mclk) > 0) { + /* MCLK is already in use */ + printk(KERN_WARNING + "MCLK in use at %d Hz. We change it to %d Hz\n", + (uint) clk_get_rate(aic23_mclk), + CODEC_CLOCK); + } + + if (clk_set_rate(aic23_mclk, CODEC_CLOCK)) { + printk(KERN_ERR + "Cannot set MCLK for AIC23 CODEC\n"); + return -ECANCELED; + } + + clk_enable(aic23_mclk); + + printk(KERN_DEBUG + "MCLK = %d [%d], usecount = %d\n", + (uint) clk_get_rate(aic23_mclk), CODEC_CLOCK, + clk_get_usecount(aic23_mclk)); + + /* Now turn the audio on */ + audio_aic23_write(POWER_DOWN_CONTROL_ADDR, + ~DEVICE_POWER_OFF & ~OUT_OFF & ~DAC_OFF & + ~ADC_OFF & ~MIC_OFF & ~LINE_OFF); + return 0; +} +/* + * Do some sanity check, turn clock off and then turn + * codec audio off + */ +int aic23_clock_off(void) +{ + if (clk_get_usecount(aic23_mclk) > 0) { + if (clk_get_rate(aic23_mclk) != CODEC_CLOCK) { + printk(KERN_WARNING + "MCLK for audio should be %d Hz. But is %d Hz\n", + (uint) clk_get_rate(aic23_mclk), + CODEC_CLOCK); + } + + clk_disable(aic23_mclk); + } + + audio_aic23_write(POWER_DOWN_CONTROL_ADDR, + DEVICE_POWER_OFF | OUT_OFF | DAC_OFF | + ADC_OFF | MIC_OFF | LINE_OFF); + return 0; +} + +int aic23_get_default_samplerate(void) +{ + return DEFAULT_SAMPLE_RATE; +} + +static int __init snd_omap_alsa_aic23_probe(struct platform_device *pdev) +{ + int ret; + struct omap_alsa_codec_config *codec_cfg; + + codec_cfg = pdev->dev.platform_data; + if (codec_cfg != NULL) { + codec_cfg->hw_constraints_rates = &aic23_hw_constraints_rates; + codec_cfg->snd_omap_alsa_playback = &aic23_snd_omap_alsa_playback; + codec_cfg->snd_omap_alsa_capture = &aic23_snd_omap_alsa_capture; + codec_cfg->codec_configure_dev = aic23_configure; + codec_cfg->codec_set_samplerate = aic23_set_samplerate; + codec_cfg->codec_clock_setup = aic23_clock_setup; + codec_cfg->codec_clock_on = aic23_clock_on; + codec_cfg->codec_clock_off = aic23_clock_off; + codec_cfg->get_default_samplerate = aic23_get_default_samplerate; + ret = snd_omap_alsa_post_probe(pdev, codec_cfg); + } + else + ret = -ENODEV; + return ret; +} + +static struct platform_driver omap_alsa_driver = { + .probe = snd_omap_alsa_aic23_probe, + .remove = snd_omap_alsa_remove, + .suspend = snd_omap_alsa_suspend, + .resume = snd_omap_alsa_resume, + .driver = { + .name = "omap_alsa_mcbsp", + }, +}; + +static int __init omap_alsa_aic23_init(void) +{ + int err; + + ADEBUG(); + err = platform_driver_register(&omap_alsa_driver); + + return err; +} + +static void __exit omap_alsa_aic23_exit(void) +{ + ADEBUG(); + + platform_driver_unregister(&omap_alsa_driver); +} + +module_init(omap_alsa_aic23_init); +module_exit(omap_alsa_aic23_exit); diff --git a/sound/arm/omap/omap-alsa-aic23.h b/sound/arm/omap/omap-alsa-aic23.h new file mode 100644 index 00000000000..63907c41c41 --- /dev/null +++ b/sound/arm/omap/omap-alsa-aic23.h @@ -0,0 +1,83 @@ +/* + * sound/arm/omap-alsa-aic23.h + * + * Alsa Driver for AIC23 codec on OSK5912 platform board + * + * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil + * Written by Daniel Petrini, David Cohen, Anderson Briglia + * {daniel.petrini, david.cohen, anderson.briglia}@indt.org.br + * + * Copyright (C) 2006 Mika Laitio + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __OMAP_ALSA_AIC23_H +#define __OMAP_ALSA_AIC23_H + +#include +#include +#include +#include +#include + +/* Define to set the AIC23 as the master w.r.t McBSP */ +#define AIC23_MASTER + +#define NUMBER_SAMPLE_RATES_SUPPORTED 10 + +/* + * AUDIO related MACROS + */ +#ifndef DEFAULT_BITPERSAMPLE +#define DEFAULT_BITPERSAMPLE 16 +#endif + +#define DEFAULT_SAMPLE_RATE 44100 +#define CODEC_CLOCK 12000000 +#define AUDIO_MCBSP OMAP_MCBSP1 + +#define DEFAULT_OUTPUT_VOLUME 0x60 +#define DEFAULT_INPUT_VOLUME 0x00 /* 0 ==> mute line in */ + +#define OUTPUT_VOLUME_MIN LHV_MIN +#define OUTPUT_VOLUME_MAX LHV_MAX +#define OUTPUT_VOLUME_RANGE (OUTPUT_VOLUME_MAX - OUTPUT_VOLUME_MIN) +#define OUTPUT_VOLUME_MASK OUTPUT_VOLUME_MAX + +#define INPUT_VOLUME_MIN LIV_MIN +#define INPUT_VOLUME_MAX LIV_MAX +#define INPUT_VOLUME_RANGE (INPUT_VOLUME_MAX - INPUT_VOLUME_MIN) +#define INPUT_VOLUME_MASK INPUT_VOLUME_MAX + +#define SIDETONE_MASK 0x1c0 +#define SIDETONE_0 0x100 +#define SIDETONE_6 0x000 +#define SIDETONE_9 0x040 +#define SIDETONE_12 0x080 +#define SIDETONE_18 0x0c0 + +#define DEFAULT_ANALOG_AUDIO_CONTROL DAC_SELECTED | STE_ENABLED | BYPASS_ON | INSEL_MIC | MICB_20DB + +struct aic23_samplerate_reg_info { + u32 sample_rate; + u8 control; /* SR3, SR2, SR1, SR0 and BOSR */ + u8 divider; /* if 0 CLKIN = MCLK, if 1 CLKIN = MCLK/2 */ +}; + +/* + * Defines codec specific functions pointers that can be used from the + * common omap-alse base driver for all omap codecs. (tsc2101 and aic23) + */ +void define_codec_functions(struct omap_alsa_codec_config *codec_config); +inline void aic23_configure(void); +void aic23_set_samplerate(long rate); +void aic23_clock_setup(void); +int aic23_clock_on(void); +int aic23_clock_off(void); +int aic23_get_default_samplerate(void); + +#endif diff --git a/sound/arm/omap-alsa-dma.c b/sound/arm/omap/omap-alsa-dma.c similarity index 96% rename from sound/arm/omap-alsa-dma.c rename to sound/arm/omap/omap-alsa-dma.c index 6bcf0f7c764..7537d8e00bb 100644 --- a/sound/arm/omap-alsa-dma.c +++ b/sound/arm/omap/omap-alsa-dma.c @@ -1,5 +1,5 @@ /* - * sound/arm/omap-alsa-dma.c + * sound/arm/omap/omap-alsa-dma.c * * Common audio DMA handling for the OMAP processors * @@ -68,18 +68,9 @@ #include +#include + #undef DEBUG -#define DEBUG -#ifdef DEBUG -#define DPRINTK(ARGS...) printk(KERN_INFO "<%s>: ",__FUNCTION__);printk(ARGS) -#define FN_IN printk(KERN_INFO "[%s]: start\n", __FUNCTION__) -#define FN_OUT(n) printk(KERN_INFO "[%s]: end(%u)\n",__FUNCTION__, n) -#else - -#define DPRINTK( x... ) -#define FN_IN -#define FN_OUT(x) -#endif #define ERR(ARGS...) printk(KERN_ERR "{%s}-ERROR: ", __FUNCTION__);printk(ARGS); @@ -186,9 +177,9 @@ int omap_request_alsa_sound_dma(int device_id, const char *device_name, } spin_lock(&dma_list_lock); for (i = 0; i < nr_linked_channels; i++) { - err = omap_request_dma(device_id, + err = omap_request_dma(device_id, device_name, - sound_dma_irq_handler, + sound_dma_irq_handler, data, &chan[i]); @@ -248,7 +239,7 @@ int omap_free_alsa_sound_dma(void *data, int **channels) { int i; int *chan = NULL; - + FN_IN; if (unlikely(NULL == channels)) { BUG(); @@ -282,7 +273,7 @@ void omap_stop_alsa_sound_dma(struct audio_stream *s) { int *chan = s->lch; int i; - + FN_IN; if (unlikely(NULL == chan)) { BUG(); @@ -320,7 +311,7 @@ static int audio_set_dma_params_play(int channel, dma_addr_t dma_ptr, int dt = 0x1; /* data type 16 */ int cen = 32; /* Stereo */ int cfn = dma_size / (2 * cen); - + FN_IN; omap_set_dma_dest_params(channel, 0x05, 0x00, (OMAP1510_MCBSP1_BASE + 0x06), @@ -338,7 +329,7 @@ static int audio_set_dma_params_capture(int channel, dma_addr_t dma_ptr, int dt = 0x1; /* data type 16 */ int cen = 32; /* stereo */ int cfn = dma_size / (2 * cen); - + FN_IN; omap_set_dma_src_params(channel, 0x05, 0x00, (OMAP1510_MCBSP1_BASE + 0x02), @@ -368,8 +359,8 @@ static int audio_start_dma_chain(struct audio_stream *s) * Do the initial set of work to initialize all the channels as required. * We shall then initate a transfer */ -int omap_start_alsa_sound_dma(struct audio_stream *s, - dma_addr_t dma_ptr, +int omap_start_alsa_sound_dma(struct audio_stream *s, + dma_addr_t dma_ptr, u_int dma_size) { int ret = -EPERM; diff --git a/sound/arm/omap-alsa-dma.h b/sound/arm/omap/omap-alsa-dma.h similarity index 95% rename from sound/arm/omap-alsa-dma.h rename to sound/arm/omap/omap-alsa-dma.h index 187e09c1dc4..1cecc8aa55c 100644 --- a/sound/arm/omap-alsa-dma.h +++ b/sound/arm/omap/omap-alsa-dma.h @@ -1,5 +1,5 @@ /* - * sound/arm/omap-alsa-dma.h + * linux/sound/arm/omap/omap-alsa-dma.h * * Common audio DMA handling for the OMAP processors * @@ -32,7 +32,7 @@ /************************** INCLUDES *************************************/ -#include "omap-aic23.h" +#include /************************** GLOBAL DATA STRUCTURES *********************************/ diff --git a/sound/arm/omap/omap-alsa.c b/sound/arm/omap/omap-alsa.c new file mode 100644 index 00000000000..328003be2bc --- /dev/null +++ b/sound/arm/omap/omap-alsa.c @@ -0,0 +1,579 @@ +/* + * sound/arm/omap-alsa.c + * + * Alsa Driver for OMAP + * + * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil + * Written by Daniel Petrini, David Cohen, Anderson Briglia + * {daniel.petrini, david.cohen, anderson.briglia}@indt.org.br + * + * Copyright (C) 2006 Mika Laitio + * + * Based on sa11xx-uda1341.c, + * Copyright (C) 2002 Tomas Kasparek + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * History: + * + * 2005-07-29 INdT Kernel Team - Alsa driver for omap osk. Creation of new + * file omap-aic23.c + * + * 2005-12-18 Dirk Behme - Added L/R Channel Interchange fix as proposed + * by Ajaya Babu + * + */ + +#include +#ifdef CONFIG_PM +#include +#endif +#include +#include + +#include +#include "omap-alsa-dma.h" + +MODULE_AUTHOR("Mika Laitio, Daniel Petrini, David Cohen, Anderson Briglia - INdT"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("OMAP driver for ALSA"); +MODULE_ALIAS("omap_alsa_mcbsp.1"); + +static char *id = NULL; +static struct snd_card_omap_codec *alsa_codec = NULL; +static struct omap_alsa_codec_config *alsa_codec_config = NULL; + +/* + * HW interface start and stop helper functions + */ +static int audio_ifc_start(void) +{ + omap_mcbsp_start(AUDIO_MCBSP); + return 0; +} + +static int audio_ifc_stop(void) +{ + omap_mcbsp_stop(AUDIO_MCBSP); + return 0; +} + +static void omap_alsa_audio_init(struct snd_card_omap_codec *omap_alsa) +{ + /* Setup DMA stuff */ + omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].id = "Alsa omap out"; + omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].stream_id = + SNDRV_PCM_STREAM_PLAYBACK; + omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].dma_dev = + OMAP_DMA_MCBSP1_TX; + omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].hw_start = + audio_ifc_start; + omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].hw_stop = + audio_ifc_stop; + + omap_alsa->s[SNDRV_PCM_STREAM_CAPTURE].id = "Alsa omap in"; + omap_alsa->s[SNDRV_PCM_STREAM_CAPTURE].stream_id = + SNDRV_PCM_STREAM_CAPTURE; + omap_alsa->s[SNDRV_PCM_STREAM_CAPTURE].dma_dev = + OMAP_DMA_MCBSP1_RX; + omap_alsa->s[SNDRV_PCM_STREAM_CAPTURE].hw_start = + audio_ifc_start; + omap_alsa->s[SNDRV_PCM_STREAM_CAPTURE].hw_stop = + audio_ifc_stop; +} + +/* + * DMA functions + * Depends on omap-alsa-dma.c functions and (omap) dma.c + * + */ +static int audio_dma_request(struct audio_stream *s, + void (*callback) (void *)) +{ + int err; + ADEBUG(); + + err = omap_request_alsa_sound_dma(s->dma_dev, s->id, s, &s->lch); + if (err < 0) + printk(KERN_ERR "Unable to grab audio dma 0x%x\n", s->dma_dev); + return err; +} + +static int audio_dma_free(struct audio_stream *s) +{ + int err = 0; + ADEBUG(); + + err = omap_free_alsa_sound_dma(s, &s->lch); + if (err < 0) + printk(KERN_ERR "Unable to free audio dma channels!\n"); + return err; +} + +/* + * This function should calculate the current position of the dma in the + * buffer. It will help alsa middle layer to continue update the buffer. + * Its correctness is crucial for good functioning. + */ +static u_int audio_get_dma_pos(struct audio_stream *s) +{ + snd_pcm_substream_t *substream = s->stream; + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int offset; + unsigned long flags; + dma_addr_t count; + ADEBUG(); + + /* this must be called w/ interrupts locked as requested in dma.c */ + spin_lock_irqsave(&s->dma_lock, flags); + + /* For the current period let's see where we are */ + count = omap_get_dma_src_addr_counter(s->lch[s->dma_q_head]); + + spin_unlock_irqrestore(&s->dma_lock, flags); + + /* Now, the position related to the end of that period */ + offset = bytes_to_frames(runtime, s->offset) - bytes_to_frames(runtime, count); + + if (offset >= runtime->buffer_size) + offset = 0; + + return offset; +} + +/* + * this stops the dma and clears the dma ptrs + */ +static void audio_stop_dma(struct audio_stream *s) +{ + unsigned long flags; + ADEBUG(); + + spin_lock_irqsave(&s->dma_lock, flags); + s->active = 0; + s->period = 0; + s->periods = 0; + + /* this stops the dma channel and clears the buffer ptrs */ + omap_stop_alsa_sound_dma(s); + + omap_clear_alsa_sound_dma(s); + + spin_unlock_irqrestore(&s->dma_lock, flags); +} + +/* + * Main dma routine, requests dma according where you are in main alsa buffer + */ +static void audio_process_dma(struct audio_stream *s) +{ + snd_pcm_substream_t *substream = s->stream; + snd_pcm_runtime_t *runtime; + unsigned int dma_size; + unsigned int offset; + int ret; +#ifdef CONFIG_MACH_OMAP_H6300 + unsigned long flags; +#endif + + ADEBUG(); + runtime = substream->runtime; + if (s->active) { + dma_size = frames_to_bytes(runtime, runtime->period_size); + offset = dma_size * s->period; + snd_assert(dma_size <= DMA_BUF_SIZE,); +#ifdef CONFIG_MACH_OMAP_H6300 + spin_lock_irqsave(&s->dma_lock, flags); + omap_stop_alsa_sound_dma(s); + spin_unlock_irqrestore(&s->dma_lock, flags); +#endif + ret = omap_start_alsa_sound_dma(s, + (dma_addr_t) runtime->dma_area + + offset, dma_size); + if (ret) { + printk(KERN_ERR + "audio_process_dma: cannot queue DMA buffer (%i)\n", + ret); + return; + } + + s->period++; + s->period %= runtime->periods; + s->periods++; + s->offset = offset; + } +} + +/* + * This is called when dma IRQ occurs at the end of each transmited block + */ +void callback_omap_alsa_sound_dma(void *data) +{ + struct audio_stream *s = data; + + ADEBUG(); + /* + * If we are getting a callback for an active stream then we inform + * the PCM middle layer we've finished a period + */ + if (s->active) + snd_pcm_period_elapsed(s->stream); + + spin_lock(&s->dma_lock); + if (s->periods > 0) + s->periods--; + + audio_process_dma(s); + spin_unlock(&s->dma_lock); +} + +/* + * Alsa section + * PCM settings and callbacks + */ +static int snd_omap_alsa_trigger(snd_pcm_substream_t * substream, int cmd) +{ + struct snd_card_omap_codec *chip = + snd_pcm_substream_chip(substream); + int stream_id = substream->pstr->stream; + struct audio_stream *s = &chip->s[stream_id]; + int err = 0; + + ADEBUG(); + /* note local interrupts are already disabled in the midlevel code */ + spin_lock(&s->dma_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* requested stream startup */ + s->active = 1; + audio_process_dma(s); + break; + case SNDRV_PCM_TRIGGER_STOP: + /* requested stream shutdown */ + audio_stop_dma(s); + break; + default: + err = -EINVAL; + break; + } + spin_unlock(&s->dma_lock); + + return err; +} + +static int snd_omap_alsa_prepare(snd_pcm_substream_t * substream) +{ + struct snd_card_omap_codec *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + struct audio_stream *s = &chip->s[substream->pstr->stream]; + + ADEBUG(); + /* set requested samplerate */ + alsa_codec_config->codec_set_samplerate(runtime->rate); + chip->samplerate = runtime->rate; + + s->period = 0; + s->periods = 0; + + return 0; +} + +static snd_pcm_uframes_t snd_omap_alsa_pointer(snd_pcm_substream_t *substream) +{ + struct snd_card_omap_codec *chip = snd_pcm_substream_chip(substream); + + ADEBUG(); + return audio_get_dma_pos(&chip->s[substream->pstr->stream]); +} + +static int snd_card_omap_alsa_open(snd_pcm_substream_t * substream) +{ + struct snd_card_omap_codec *chip = + snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int stream_id = substream->pstr->stream; + int err; + + ADEBUG(); + chip->s[stream_id].stream = substream; + alsa_codec_config->codec_clock_on(); + if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) + runtime->hw = *(alsa_codec_config->snd_omap_alsa_playback); + else + runtime->hw = *(alsa_codec_config->snd_omap_alsa_capture); + + if ((err = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + + if ((err = snd_pcm_hw_constraint_list(runtime, + 0, + SNDRV_PCM_HW_PARAM_RATE, + alsa_codec_config->hw_constraints_rates)) < 0) + return err; + + return 0; +} + +static int snd_card_omap_alsa_close(snd_pcm_substream_t * substream) +{ + struct snd_card_omap_codec *chip = snd_pcm_substream_chip(substream); + + ADEBUG(); + alsa_codec_config->codec_clock_off(); + chip->s[substream->pstr->stream].stream = NULL; + + return 0; +} + +/* HW params & free */ +static int snd_omap_alsa_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); +} + +static int snd_omap_alsa_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +/* pcm operations */ +static snd_pcm_ops_t snd_card_omap_alsa_playback_ops = { + .open = snd_card_omap_alsa_open, + .close = snd_card_omap_alsa_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_omap_alsa_hw_params, + .hw_free = snd_omap_alsa_hw_free, + .prepare = snd_omap_alsa_prepare, + .trigger = snd_omap_alsa_trigger, + .pointer = snd_omap_alsa_pointer, +}; + +static snd_pcm_ops_t snd_card_omap_alsa_capture_ops = { + .open = snd_card_omap_alsa_open, + .close = snd_card_omap_alsa_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_omap_alsa_hw_params, + .hw_free = snd_omap_alsa_hw_free, + .prepare = snd_omap_alsa_prepare, + .trigger = snd_omap_alsa_trigger, + .pointer = snd_omap_alsa_pointer, +}; + +/* + * Alsa init and exit section + * + * Inits pcm alsa structures, allocate the alsa buffer, suspend, resume + */ +static int __init snd_card_omap_alsa_pcm(struct snd_card_omap_codec *omap_alsa, + int device) +{ + snd_pcm_t *pcm; + int err; + + ADEBUG(); + if ((err = snd_pcm_new(omap_alsa->card, "OMAP PCM", device, 1, 1, &pcm)) < 0) + return err; + + /* sets up initial buffer with continuous allocation */ + snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data + (GFP_KERNEL), + 128 * 1024, 128 * 1024); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_card_omap_alsa_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_card_omap_alsa_capture_ops); + pcm->private_data = omap_alsa; + pcm->info_flags = 0; + strcpy(pcm->name, "omap alsa pcm"); + + omap_alsa_audio_init(omap_alsa); + + /* setup DMA controller */ + audio_dma_request(&omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK], + callback_omap_alsa_sound_dma); + audio_dma_request(&omap_alsa->s[SNDRV_PCM_STREAM_CAPTURE], + callback_omap_alsa_sound_dma); + + omap_alsa->pcm = pcm; + + return 0; +} + + +#ifdef CONFIG_PM +/* + * Driver suspend/resume - calls alsa functions. Some hints from aaci.c + */ +int snd_omap_alsa_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct snd_card_omap_codec *chip; + snd_card_t *card = platform_get_drvdata(pdev); + + if (card->power_state != SNDRV_CTL_POWER_D3hot) { + chip = card->private_data; + if (chip->card->power_state != SNDRV_CTL_POWER_D3hot) { + snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot); + snd_pcm_suspend_all(chip->pcm); + /* Mutes and turn clock off */ + alsa_codec_config->codec_clock_off(); + snd_omap_suspend_mixer(); + } + } + return 0; +} + +int snd_omap_alsa_resume(struct platform_device *pdev) +{ + struct snd_card_omap_codec *chip; + snd_card_t *card = platform_get_drvdata(pdev); + + if (card->power_state != SNDRV_CTL_POWER_D0) { + chip = card->private_data; + if (chip->card->power_state != SNDRV_CTL_POWER_D0) { + snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0); + alsa_codec_config->codec_clock_on(); + snd_omap_resume_mixer(); + } + } + return 0; +} + +#endif /* CONFIG_PM */ + +void snd_omap_alsa_free(snd_card_t * card) +{ + struct snd_card_omap_codec *chip = card->private_data; + ADEBUG(); + + /* + * Turn off codec after it is done. + * Can't do it immediately, since it may still have + * buffered data. + */ + schedule_timeout_interruptible(2); + + omap_mcbsp_stop(AUDIO_MCBSP); + omap_mcbsp_free(AUDIO_MCBSP); + + audio_dma_free(&chip->s[SNDRV_PCM_STREAM_PLAYBACK]); + audio_dma_free(&chip->s[SNDRV_PCM_STREAM_CAPTURE]); +} + +/* module init & exit */ + +/* + * Inits alsa soudcard structure. + * Called by the probe method in codec after function pointers has been set. + */ +int snd_omap_alsa_post_probe(struct platform_device *pdev, struct omap_alsa_codec_config *config) +{ + int err = 0; + int def_rate; + snd_card_t *card; + + ADEBUG(); + alsa_codec_config = config; + + alsa_codec_config->codec_clock_setup(); + alsa_codec_config->codec_clock_on(); + + omap_mcbsp_request(AUDIO_MCBSP); + omap_mcbsp_stop(AUDIO_MCBSP); + omap_mcbsp_config(AUDIO_MCBSP, alsa_codec_config->mcbsp_regs_alsa); + omap_mcbsp_start(AUDIO_MCBSP); + + if (alsa_codec_config && alsa_codec_config->codec_configure_dev) + alsa_codec_config->codec_configure_dev(); + + alsa_codec_config->codec_clock_off(); + + /* register the soundcard */ + card = snd_card_new(-1, id, THIS_MODULE, sizeof(alsa_codec)); + if (card == NULL) + goto nodev1; + + alsa_codec = kcalloc(1, sizeof(*alsa_codec), GFP_KERNEL); + if (alsa_codec == NULL) + goto nodev2; + + card->private_data = (void *)alsa_codec; + card->private_free = snd_omap_alsa_free; + + alsa_codec->card = card; + def_rate = alsa_codec_config->get_default_samplerate(); + alsa_codec->samplerate = def_rate; + + spin_lock_init(&alsa_codec->s[0].dma_lock); + spin_lock_init(&alsa_codec->s[1].dma_lock); + + /* mixer */ + if ((err = snd_omap_mixer(alsa_codec)) < 0) + goto nodev3; + + /* PCM */ + if ((err = snd_card_omap_alsa_pcm(alsa_codec, 0)) < 0) + goto nodev3; + + strcpy(card->driver, "OMAP_ALSA"); + strcpy(card->shortname, alsa_codec_config->name); + sprintf(card->longname, alsa_codec_config->name); + + snd_omap_init_mixer(); + snd_card_set_dev(card, &pdev->dev); + + if ((err = snd_card_register(card)) == 0) { + printk(KERN_INFO "audio support initialized\n"); + platform_set_drvdata(pdev, card); + return 0; + } + +nodev3: + kfree(alsa_codec); +nodev2: + snd_card_free(card); +nodev1: + omap_mcbsp_stop(AUDIO_MCBSP); + omap_mcbsp_free(AUDIO_MCBSP); + + return err; +} + +int snd_omap_alsa_remove(struct platform_device *pdev) +{ + snd_card_t *card = platform_get_drvdata(pdev); + struct snd_card_omap_codec *chip = card->private_data; + + snd_card_free(card); + + alsa_codec = NULL; + card->private_data = NULL; + kfree(chip); + + platform_set_drvdata(pdev, NULL); + + return 0; +} -- 2.41.1