diff options
Diffstat (limited to 'target/linux/coldfire/patches/009-Add-ALSA-driver-for-MCF5445x.patch')
-rw-r--r-- | target/linux/coldfire/patches/009-Add-ALSA-driver-for-MCF5445x.patch | 1869 |
1 files changed, 1869 insertions, 0 deletions
diff --git a/target/linux/coldfire/patches/009-Add-ALSA-driver-for-MCF5445x.patch b/target/linux/coldfire/patches/009-Add-ALSA-driver-for-MCF5445x.patch new file mode 100644 index 0000000000..52c1f56a8c --- /dev/null +++ b/target/linux/coldfire/patches/009-Add-ALSA-driver-for-MCF5445x.patch @@ -0,0 +1,1869 @@ +From d37d2d880efdc0ce515df4155ddafef3835d1b7f Mon Sep 17 00:00:00 2001 +From: Alison Wang <b18965@freescale.com> +Date: Thu, 4 Aug 2011 09:59:41 +0800 +Subject: [PATCH 09/52] Add ALSA driver for MCF5445x + +Add ALSA driver for MCF54451 and MCF54455. + +Signed-off-by: Alison Wang <b18965@freescale.com> +--- + sound/Kconfig | 6 +- + sound/Makefile | 1 + + sound/coldfire/Kconfig | 14 + + sound/coldfire/Makefile | 6 + + sound/coldfire/coldfire-codec-spi.c | 93 ++ + sound/coldfire/snd-coldfire.c | 1664 +++++++++++++++++++++++++++++++++++ + sound/coldfire/snd-coldfire.h | 15 + + 7 files changed, 1795 insertions(+), 4 deletions(-) + create mode 100644 sound/coldfire/Kconfig + create mode 100644 sound/coldfire/Makefile + create mode 100644 sound/coldfire/coldfire-codec-spi.c + create mode 100644 sound/coldfire/snd-coldfire.c + create mode 100644 sound/coldfire/snd-coldfire.h + +--- a/sound/Kconfig ++++ b/sound/Kconfig +@@ -59,8 +59,6 @@ config SOUND_OSS_CORE_PRECLAIM + + source "sound/oss/dmasound/Kconfig" + +-if !M68K +- + menuconfig SND + tristate "Advanced Linux Sound Architecture" + help +@@ -85,6 +83,8 @@ source "sound/aoa/Kconfig" + + source "sound/arm/Kconfig" + ++source "sound/coldfire/Kconfig" ++ + source "sound/atmel/Kconfig" + + source "sound/spi/Kconfig" +@@ -121,8 +121,6 @@ source "sound/oss/Kconfig" + + endif # SOUND_PRIME + +-endif # !M68K +- + endif # SOUND + + # AC97_BUS is used from both sound and ucb1400 +--- a/sound/Makefile ++++ b/sound/Makefile +@@ -8,6 +8,7 @@ obj-$(CONFIG_DMASOUND) += oss/ + obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \ + sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ + obj-$(CONFIG_SND_AOA) += aoa/ ++obj-$(CONFIG_SND_COLDFIRE) += coldfire/ + + # This one must be compilable even if sound is configured out + obj-$(CONFIG_AC97_BUS) += ac97_bus.o +--- /dev/null ++++ b/sound/coldfire/Kconfig +@@ -0,0 +1,14 @@ ++ ++menu "ALSA for Coldfire" ++ ++config SND_COLDFIRE ++ bool "Coldfire sound devices" ++ depends on SND ++ select SND_PCM ++ select SSIAUDIO_USE_EDMA ++ default y ++ help ++ Support for sound devices specific to Coldfire architectures. ++ ++endmenu ++ +--- /dev/null ++++ b/sound/coldfire/Makefile +@@ -0,0 +1,6 @@ ++# ++# Makefile for Coldfire ALSA ++# ++ ++obj-y += snd-coldfire.o coldfire-codec-spi.o ++ +--- /dev/null ++++ b/sound/coldfire/coldfire-codec-spi.c +@@ -0,0 +1,93 @@ ++/* ++ * linux/sound/coldfire/coldfire-codec-spi.c ++ * ++ * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. All Rights Reserved. ++ * Author: Kurt Mahan <kmahan@freescale.com> ++ * ++ * Simple SPI interface for the CODEC. ++ * ++ * This is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include <linux/device.h> ++#include <linux/init.h> ++#include <linux/spi/spi.h> ++#include <linux/kernel.h> ++#include <asm/mcfsim.h> ++#include <asm/coldfire.h> ++#include <linux/delay.h> ++ ++#define MCF_CODEC_SPI_DRIVER_NAME "mcf_codec_spi" ++ ++static struct spi_device *mcf_codec_spi; ++ ++/* ++ * Write CODEC register via SPI ++ */ ++int mcf_codec_spi_write(u8 addr, u16 data) ++{ ++ u16 spi_word; ++ ++ if (mcf_codec_spi == NULL) ++ return -ENODEV; ++ ++ spi_word = ((addr & 0x7F)<<9)|(data & 0x1FF); ++ return spi_write(mcf_codec_spi, (const u8 *)&spi_word, ++ sizeof(spi_word)); ++} ++EXPORT_SYMBOL(mcf_codec_spi_write); ++ ++static int __devinit mcf_codec_spi_probe(struct spi_device *spi) ++{ ++ spi->dev.power.power_state = PMSG_ON; ++ mcf_codec_spi = spi; ++ ++ return 0; ++} ++ ++static int __devexit mcf_codec_spi_remove(struct spi_device *spi) ++{ ++ return 0; ++} ++ ++static int mcf_codec_spi_suspend(struct spi_device *spi, pm_message_t message) ++{ ++ return 0; ++} ++ ++static int mcf_codec_spi_resume(struct spi_device *spi) ++{ ++ return 0; ++} ++ ++static struct spi_driver mcf_codec_spi_driver = { ++ .driver = { ++ .name = MCF_CODEC_SPI_DRIVER_NAME, ++ .bus = &spi_bus_type, ++ .owner = THIS_MODULE, ++ }, ++ .probe = mcf_codec_spi_probe, ++ .remove = __devexit_p(mcf_codec_spi_remove), ++ .suspend = mcf_codec_spi_suspend, ++ .resume = mcf_codec_spi_resume, ++}; ++ ++static int __init mcf_codec_spi_init(void) ++{ ++ return spi_register_driver(&mcf_codec_spi_driver); ++} ++module_init(mcf_codec_spi_init); ++ ++static void __exit mcf_codec_spi_exit(void) ++{ ++ spi_unregister_driver(&mcf_codec_spi_driver); ++} ++module_exit(mcf_codec_spi_exit); ++ ++ ++MODULE_DESCRIPTION("Coldfire Codec SPI driver"); ++MODULE_AUTHOR("Kurt Mahan, Freescale Semiconductor, Inc."); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/sound/coldfire/snd-coldfire.c +@@ -0,0 +1,1664 @@ ++/* ++ * linux/sound/coldfire/snd-coldfire.c ++ * ++ * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. All Rights Reserved. ++ * Author: York Sun <r58495@freescale.com> ++ * Alison Wang <b18965@freescale.com> ++ * ++ * Coldfire ALSA driver based on SSI and TLV320A ++ * ++ * This 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. ++ * ++ *************************************************************************** ++ * ++ * NOTE: This driver was tested on MCF5445x, MCF5301x, MCF5227x, MCF532x and ++ * MCF537x platforms. ++ * */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/platform_device.h> ++#include <linux/init.h> ++#include <linux/device.h> ++#include <linux/slab.h> ++#include <linux/dma-mapping.h> ++#include <linux/ioctl.h> ++#include <linux/soundcard.h> ++#include <linux/spi/spi.h> ++ ++#include <sound/core.h> ++#include <sound/pcm.h> ++#include <sound/pcm_params.h> ++#include <sound/control.h> ++#include <linux/fs.h> ++#include <linux/delay.h> ++ ++#include <asm/mcfsim.h> ++#include <asm/coldfire.h> ++#include <asm/mcf_edma.h> ++#include "snd-coldfire.h" ++ ++#if defined(CONFIG_M5445X) ++#include <asm/mcf5445x_ssi.h> ++#endif ++ ++#define CF_ALSA_DEBUG 0 ++#if CF_ALSA_DEBUG ++#define DBG(fmt, args...) printk(KERN_INFO "[%s] " fmt , \ ++ __func__, ## args) ++#else ++#define DBG(fmt, args...) do {} while (0) ++#endif ++ ++#define SOUND_CARD_NAME "Coldfire ALSA" ++#define MAX_BUFFER_SIZE (32*1024) ++ ++/* eDMA channel for SSI channel 0,1 TX,RX */ ++#define DMA_TX_TCD0 MCF_EDMA_CHAN_TIMER2 ++#define DMA_TX_TCD1 MCF_EDMA_CHAN_TIMER3 ++#define DMA_RX_TCD0 MCF_EDMA_CHAN_TIMER0 ++#define DMA_RX_TCD1 MCF_EDMA_CHAN_TIMER1 ++ ++#define CODEC_LEFT_IN_REG (0x00) ++#define CODEC_RIGHT_IN_REG (0x01) ++#define CODEC_LEFT_HP_VOL_REG (0x02) ++#define CODEC_RIGHT_HP_VOL_REG (0x03) ++#define CODEC_ANALOG_APATH_REG (0x04) ++#define CODEC_DIGITAL_APATH_REG (0x05) ++#define CODEC_POWER_DOWN_REG (0x06) ++#define CODEC_DIGITAL_IF_FMT_REG (0x07) ++#define CODEC_SAMPLE_RATE_REG (0x08) ++#define CODEC_DIGITAL_IF_ACT_REG (0x09) ++#define CODEC_RESET_REG (0x0f) ++ ++#define TLV320AIC23_CODEC_SAMPLE_RATE_REG (0x08) ++#define TLV320AIC23_CODEC_SAMPLERATE_REG_8KHZ (0x0C) ++#define TLV320AIC23_CODEC_SAMPLERATE_REG_11KHZ (0x0C) ++#define TLV320AIC23_CODEC_SAMPLERATE_REG_16KHZ (0x58) ++#define TLV320AIC23_CODEC_SAMPLERATE_REG_22KHZ (0x62) ++#define TLV320AIC23_CODEC_SAMPLERATE_REG_44KHZ (0x22) ++#define TLV320AIC23_CODEC_SAMPLERATE_REG_48KHZ (0x00) ++ ++#define MCF_SSI_AUDIO_IRQ_LEVEL (5) ++#define TLV320A_VOL_MAX 0x07F ++#define TLV320A_VOL_MIN 0x030 ++#define TLV320A_VOL_INIT 0x065 ++#define TLV320A_LINEIN_MAX 0x1F ++#define TLV320A_LINEIN_INIT 0x17 ++#define TLV320A_ANALOGPATH_INIT 0x11 ++ ++/* Codec settings */ ++#define MCF_SSI_AUDIO_MCLK_1 (12288000U) /*Hz*/ ++#define MCF_SSI_AUDIO_MCLK_2 (16934400U) /*Hz*/ ++#define MCF_SSI_AUDIO_MCLK_3 (14112000U) /*Hz*/ ++#define MCF_SSI_AUDIO_MCLK_4 (18432000U) /*Hz*/ ++ ++#define MCF_SSI_AUDIO_SSDIV_VALUE_1 \ ++ ((((u32)MCF_CLK*2)/MCF_SSI_AUDIO_MCLK_1)+ \ ++ (((((u32)MCF_CLK*2*10)/MCF_SSI_AUDIO_MCLK_1)%10) > 5)) ++ ++#define MCF_SSI_AUDIO_SSDIV_VALUE_2 \ ++ ((((u32)MCF_CLK*2)/MCF_SSI_AUDIO_MCLK_2)+ \ ++ (((((u32)MCF_CLK*2*10)/MCF_SSI_AUDIO_MCLK_2)%10) > 5)) ++ ++#define MCF_SSI_AUDIO_SSDIV_VALUE_3 \ ++ ((((u32)MCF_CLK*2)/MCF_SSI_AUDIO_MCLK_3)+ \ ++ (((((u32)MCF_CLK*2*10)/MCF_SSI_AUDIO_MCLK_3)%10) > 5)) ++ ++#define MCF_SSI_AUDIO_SSDIV_VALUE_4 \ ++ ((((u32)MCF_CLK*2)/MCF_SSI_AUDIO_MCLK_4)+ \ ++ (((((u32)MCF_CLK*2*10)/MCF_SSI_AUDIO_MCLK_4)%10) > 5)) ++ ++#define SNDRV_COLDFIRE_PCM_PLAYBACK_FORMATS SNDRV_PCM_FMTBIT_S16_BE ++#define SNDRV_COLDFIRE_PCM_CAPTURE_FORMATS SNDRV_PCM_FMTBIT_S16_BE ++ ++#define RXFWM 2 ++#define TXFWM 2 ++#define HW_PERIODS_BYTES_MIN 4096 ++#define HW_PERIODS_BYTES_STEP 4096 ++ ++#define INPUT_MICROPHONE 0 ++#define INPUT_LINEIN 1 ++#define NUM_TCDS 4 ++ ++static char *id; ++static struct platform_device *device; ++static int g_tx_dmaing; ++static int g_rx_dmaing; ++static unsigned char g_mastervol, g_lineinvol, g_analogpath; ++ ++/** Use 4 TCDs for scatter/gather address ++ * to setup dma chain, one TCD per period ++ * so that we don't need change them on the fly ++ */ ++ ++/** ++ * Link Descriptor ++ * ++ * must be aligned on a 32-byte boundary. ++ */ ++struct dma_tcd { ++ __be32 saddr; /* source address */ ++ __be16 attr; /* transfer attribute */ ++ __be16 soffset; /* source offset */ ++ __be32 nbytes; /* minor byte count */ ++ __be32 slast; /* last source address adjust */ ++ __be32 daddr; /* dest address */ ++ __be16 citer; /* current minor looplink, major count */ ++ __be16 doffset; /* dest offset */ ++ __be32 dlast_sga; /* last dest addr adjust, scatter/gather addr*/ ++ __be16 biter; /* begging minor looklink, major count */ ++ __be16 csr; /* control and status */ ++} __packed; ++ ++/** dma_private: p-substream DMA data ++ * ++ * The tcd[] array is first because it needs to be aligned on a 32-byte ++ * boundary, so putting it first will ensure alignment without padding the ++ * structure. ++ * ++ * @tcd[]: array of TCDs ++ */ ++struct dma_private { ++ struct dma_tcd tcd0[NUM_TCDS]; ++ struct dma_tcd tcd1[NUM_TCDS]; ++ dma_addr_t tcd_buf_phys; /* physical address of dma_private */ ++ dma_addr_t dma_buf_phys; ++ dma_addr_t dma_buf_next; ++ dma_addr_t dma_buf_end; ++ size_t period_size; ++ unsigned int num_periods; ++}; ++ ++struct tlv320a_audio_device { ++ struct spi_device *spi; ++ u32 speed; ++ u32 stereo; ++ u32 bits; ++ u32 format; ++ u8 isopen; ++ u8 dmaing; ++ u8 ssi_enabled; ++ u8 channel; ++ spinlock_t lock; ++ u8 *audio_buf; ++}; ++ ++/* chip specific define */ ++struct chip_spec { ++ struct snd_card *card; ++ struct snd_pcm *pcm; ++ struct tlv320a_audio_device *audio_device; ++ u32 offset; ++ void *mixer_data; ++}; ++ ++/* hardware definition */ ++static struct snd_pcm_hardware snd_coldfire_playback_hw = { ++ .info = (SNDRV_PCM_INFO_INTERLEAVED | ++#if defined(CONFIG_MMU) ++ SNDRV_PCM_INFO_MMAP | ++ SNDRV_PCM_INFO_MMAP_VALID| ++#endif ++ SNDRV_PCM_INFO_BLOCK_TRANSFER), ++ .formats = SNDRV_COLDFIRE_PCM_PLAYBACK_FORMATS, ++ .rates = SNDRV_PCM_RATE_8000_48000, ++ .rate_min = 8000, ++ .rate_max = 48000, ++ .channels_min = 1, ++ .channels_max = 2, ++ .buffer_bytes_max = MAX_BUFFER_SIZE, ++ .period_bytes_min = HW_PERIODS_BYTES_MIN, ++ .period_bytes_max = MAX_BUFFER_SIZE/NUM_TCDS, ++ .periods_min = NUM_TCDS, ++ .periods_max = NUM_TCDS, ++ .fifo_size = 0, ++}; ++ ++/* hardware definition */ ++static struct snd_pcm_hardware snd_coldfire_capture_hw = { ++ .info = (SNDRV_PCM_INFO_INTERLEAVED | ++#if defined(CONFIG_MMU) ++ SNDRV_PCM_INFO_MMAP | ++ SNDRV_PCM_INFO_MMAP_VALID| ++#endif ++ SNDRV_PCM_INFO_BLOCK_TRANSFER), ++ .formats = SNDRV_COLDFIRE_PCM_CAPTURE_FORMATS, ++ .rates = SNDRV_PCM_RATE_8000_48000, ++ .rate_min = 8000, ++ .rate_max = 48000, ++ .channels_min = 1, ++ .channels_max = 2, ++ .buffer_bytes_max = MAX_BUFFER_SIZE, ++ .period_bytes_min = HW_PERIODS_BYTES_MIN, ++ .period_bytes_max = MAX_BUFFER_SIZE/NUM_TCDS, ++ .periods_min = NUM_TCDS, ++ .periods_max = NUM_TCDS, ++ .fifo_size = 0, ++}; ++ ++static unsigned int rates[] = {8000, 11025, 16000, 22000, ++ 22050, 44000, 44100, 48000}; ++ ++/* hw constraints */ ++static struct snd_pcm_hw_constraint_list constraints_rates = { ++ .count = ARRAY_SIZE(rates), ++ .list = rates, ++ .mask = 0, ++}; ++ ++static inline void ssi_audio_dma_playback_start(void) ++{ ++ g_tx_dmaing = 1; ++ mcf_edma_start_transfer(DMA_TX_TCD0); ++ mcf_edma_start_transfer(DMA_TX_TCD1); ++} ++ ++static inline void ssi_audio_dma_capture_start(void) ++{ ++ g_rx_dmaing = 1; ++ mcf_edma_start_transfer(DMA_RX_TCD0); ++ mcf_edma_start_transfer(DMA_RX_TCD1); ++} ++ ++static inline void ssi_audio_dma_playback_stop(void) ++{ ++ g_tx_dmaing = 0; ++ mcf_edma_stop_transfer(DMA_TX_TCD0); ++ mcf_edma_stop_transfer(DMA_TX_TCD1); ++} ++ ++inline void ssi_audio_dma_capture_stop(void) ++{ ++ g_rx_dmaing = 0; ++ mcf_edma_stop_transfer(DMA_RX_TCD0); ++ mcf_edma_stop_transfer(DMA_RX_TCD1); ++} ++ ++/** ++ * fill_tcd_params - Fill transfer control descriptor (TCD) ++ * @base: base address of TCD ++ * @source: source address ++ * @dest: destination address ++ * @attr: attributes ++ * @soff: source offset ++ * @nbytes: number of bytes to be transfered in minor loop ++ * @slast: last source address adjustment ++ * @citer: major loop count ++ * @biter: beginning minor loop count ++ * @doff: destination offset ++ * @dlast_sga: last destination address adjustment ++ * @major_int: generate interrupt after each major loop ++ * @disable_req: disable DMA request after major loop ++ * @enable_sg: enable scatter/gather address ++ */ ++void fill_tcd_params(u32 base, u32 source, u32 dest, ++ u32 attr, u32 soff, u32 nbytes, u32 slast, ++ u32 citer, u32 biter, u32 doff, u32 dlast_sga, ++ int major_int, int disable_req, int enable_sg) ++{ ++ struct dma_tcd *tcd = (struct dma_tcd *) base; ++ ++ tcd->saddr = source; ++ tcd->attr = attr; ++ tcd->soffset = soff; ++ tcd->nbytes = nbytes; ++ tcd->slast = slast; ++ tcd->daddr = dest; ++ tcd->citer = citer & 0x7fff; ++ tcd->doffset = doff; ++ tcd->dlast_sga = dlast_sga; ++ tcd->biter = biter & 0x7fff; ++ tcd->csr = ((major_int) ? 0x2 : 0) | ++ ((disable_req) ? 0x8 : 0) | ++ ((enable_sg) ? 0x10 : 0); ++} ++ ++static int ++ssi_audio_dma_playback_config(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct dma_private *dma_private = runtime->private_data; ++ u32 size = frames_to_bytes(runtime, runtime->period_size); ++ u32 offset, soffset, daddr0, daddr1, attr, sga0, sga1; ++ u32 i, nbyte, major_loops; ++ ++ if ((runtime->channels < 1) || (runtime->channels > 2)) { ++ printk(KERN_ERR "Error on channels = %d\n", runtime->channels); ++ return -EINVAL; ++ } ++ ++ dma_private->dma_buf_phys = runtime->dma_addr; ++ dma_private->dma_buf_next = dma_private->dma_buf_phys; ++ dma_private->dma_buf_end = dma_private->dma_buf_phys + ++ runtime->periods * size; ++ ++ if (runtime->format == SNDRV_PCM_FORMAT_S16_BE) { ++ nbyte = 2 * TXFWM; ++ soffset = 2 * runtime->channels; ++ daddr0 = (u32)&MCF_SSI_TX0 + 2; ++ daddr1 = (u32)&MCF_SSI_TX1 + 2; ++ attr = MCF_EDMA_TCD_ATTR_SSIZE_16BIT | ++ MCF_EDMA_TCD_ATTR_DSIZE_16BIT; ++ } else { ++ printk(KERN_ERR "Not supported PCM format %x\n", ++ runtime->format); ++ return -EINVAL; ++ } ++ ++ major_loops = size/nbyte/runtime->channels; ++ sga0 = (u32)dma_private->tcd_buf_phys; ++ sga1 = (u32)dma_private->tcd_buf_phys + ++ 4 * sizeof(struct dma_tcd); ++ ++#if defined(CONFIG_M5301x) || defined(CONFIG_M5445X) ++ MCF_EDMA_TCD10_CSR = 0; ++ MCF_EDMA_TCD11_CSR = 0; ++#else ++ MCF_EDMA_TCD11_CSR = 0; ++ MCF_EDMA_TCD12_CSR = 0; ++#endif ++ offset = (runtime->channels - 1) * 2; ++ mcf_edma_set_tcd_params(DMA_TX_TCD0, ++ (u32)dma_private->dma_buf_next, ++ daddr0, ++ attr, ++ soffset, ++ nbyte, ++ 0, /* slast */ ++ major_loops, /* citer */ ++ major_loops, /* biter */ ++ 0, /* dest offset */ ++ sga0, ++ 1, /* major_int */ ++ 0); /* enable dma request after */ ++ ++ mcf_edma_set_tcd_params(DMA_TX_TCD1, ++ (u32)dma_private->dma_buf_next + offset, ++ daddr1, ++ attr, ++ soffset, ++ nbyte, ++ 0, /* slast */ ++ major_loops, /* citer */ ++ major_loops, /* biter */ ++ 0, /* dest offset */ ++ sga1, ++ 0, /* major_int */ ++ 0); /* enable dma request after */ ++ ++ while (!(MCF_EDMA_TCD_CSR(DMA_TX_TCD0) & MCF_EDMA_TCD_CSR_E_SG)) ++ MCF_EDMA_TCD_CSR(DMA_TX_TCD0) |= MCF_EDMA_TCD_CSR_E_SG; ++ while (!(MCF_EDMA_TCD_CSR(DMA_TX_TCD1) & MCF_EDMA_TCD_CSR_E_SG)) ++ MCF_EDMA_TCD_CSR(DMA_TX_TCD1) |= MCF_EDMA_TCD_CSR_E_SG; ++ ++ for (i = 0; i < NUM_TCDS; i++) { ++ dma_private->dma_buf_next += size; ++ if (dma_private->dma_buf_next >= dma_private->dma_buf_end) ++ dma_private->dma_buf_next = dma_private->dma_buf_phys; ++ sga0 = (u32)dma_private->tcd_buf_phys + ++ ((i+1)%NUM_TCDS) * sizeof(struct dma_tcd); ++ sga1 = (u32)dma_private->tcd_buf_phys + ++ ((i+1)%NUM_TCDS + 4) * sizeof(struct dma_tcd); ++ DBG("sga0 = 0x%x, sga1 = 0x%x.\n", sga0, sga1); ++ fill_tcd_params((u32)&dma_private->tcd0[i], ++ (u32)dma_private->dma_buf_next, ++ daddr0, ++ attr, ++ soffset, ++ nbyte, ++ 0, /* slast */ ++ major_loops, /* citer */ ++ major_loops, /* biter */ ++ 0, /* dest offset */ ++ sga0, ++ 1, /* major_int */ ++ 0, /* enable dma request after */ ++ 1); /* enable scatter/gather */ ++ ++ fill_tcd_params((u32)&dma_private->tcd1[i], ++ (u32)dma_private->dma_buf_next + offset, ++ daddr1, ++ attr, ++ soffset, ++ nbyte, ++ 0, /* slast */ ++ major_loops, /* citer */ ++ major_loops, /* biter */ ++ 0, /* dest offset */ ++ sga1, ++ 0, /* major_int */ ++ 0, /* enable dma request after */ ++ 1); /* enable scatter/gather */ ++ } ++ ++ return 0; ++} ++ ++static int ++ssi_audio_dma_capture_config(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct dma_private *dma_private = runtime->private_data; ++ u32 size = frames_to_bytes(runtime, runtime->period_size); ++ u32 offset, saddr0, saddr1, doffset, attr, sga0, sga1; ++ int i, nbyte, major_loops; ++ ++ if ((runtime->channels < 1) || (runtime->channels > 2)) { ++ printk(KERN_ERR "Error on channels = %d\n", runtime->channels); ++ return -EINVAL; ++ } ++ ++ dma_private->dma_buf_phys = runtime->dma_addr; ++ dma_private->dma_buf_next = dma_private->dma_buf_phys; ++ dma_private->dma_buf_end = dma_private->dma_buf_phys + ++ runtime->periods * size; ++ ++ switch (runtime->format) { ++ case SNDRV_PCM_FORMAT_S16_BE: ++ saddr0 = (u32)&MCF_SSI_RX0 + 2; ++ saddr1 = (u32)&MCF_SSI_RX1 + 2; ++ nbyte = 2 * RXFWM; ++ doffset = 2 * runtime->channels; ++ attr = MCF_EDMA_TCD_ATTR_SSIZE_16BIT | ++ MCF_EDMA_TCD_ATTR_DSIZE_16BIT; ++ break; ++ default: ++ printk(KERN_ERR "Not supported PCM format %x\n", ++ runtime->format); ++ return -EINVAL; ++ } ++ ++ major_loops = size/nbyte/runtime->channels; ++ sga0 = (u32)dma_private->tcd_buf_phys; ++ sga1 = (u32)dma_private->tcd_buf_phys + ++ 4 * sizeof(struct dma_tcd); ++ ++#if defined(CONFIG_M5301x) || defined(CONFIG_M5445X) ++ MCF_EDMA_TCD8_CSR = 0; ++ MCF_EDMA_TCD9_CSR = 0; ++#else ++ MCF_EDMA_TCD9_CSR = 0; ++ MCF_EDMA_TCD10_CSR = 0; ++#endif ++ offset = (runtime->channels - 1) * 2; ++ mcf_edma_set_tcd_params(DMA_RX_TCD0, ++ saddr0, ++ (u32)dma_private->dma_buf_next, ++ attr, ++ 0, /* source offset */ ++ nbyte, ++ 0, /* slast */ ++ major_loops, /* citer */ ++ major_loops, /* biter */ ++ doffset, ++ sga0, ++ 1, /* major_int */ ++ 0); /* enable dma request after */ ++ ++ mcf_edma_set_tcd_params(DMA_RX_TCD1, ++ saddr1, ++ (u32)dma_private->dma_buf_next + offset, ++ attr, ++ 0, /* source offset */ ++ nbyte, ++ 0, /* slast */ ++ major_loops, /* citer */ ++ major_loops, /* biter */ ++ doffset, ++ sga1, ++ 0, /* major_int */ ++ 0); /* enable dma request after */ ++ ++ while (!(MCF_EDMA_TCD_CSR(DMA_RX_TCD0) & MCF_EDMA_TCD_CSR_E_SG)) ++ MCF_EDMA_TCD_CSR(DMA_RX_TCD0) |= MCF_EDMA_TCD_CSR_E_SG; ++ while (!(MCF_EDMA_TCD_CSR(DMA_RX_TCD1) & MCF_EDMA_TCD_CSR_E_SG)) ++ MCF_EDMA_TCD_CSR(DMA_RX_TCD1) |= MCF_EDMA_TCD_CSR_E_SG; ++ ++ for (i = 0; i < NUM_TCDS; i++) { ++ dma_private->dma_buf_next += size; ++ if (dma_private->dma_buf_next >= dma_private->dma_buf_end) ++ dma_private->dma_buf_next = dma_private->dma_buf_phys; ++ sga0 = (u32)dma_private->tcd_buf_phys + ++ ((i+1)%NUM_TCDS) * sizeof(struct dma_tcd); ++ sga1 = (u32)dma_private->tcd_buf_phys + ++ ((i+1)%NUM_TCDS + 4) * sizeof(struct dma_tcd); ++ fill_tcd_params((u32)&dma_private->tcd0[i], ++ saddr0, ++ (u32)dma_private->dma_buf_next, ++ attr, ++ 0, /* source offset */ ++ nbyte, ++ 0, /* slast */ ++ major_loops, /* citer */ ++ major_loops, /* biter */ ++ doffset, ++ sga0, ++ 1, /* major_int */ ++ 0, /* enable dma request after */ ++ 1); /* enable scatter/gather */ ++ fill_tcd_params((u32)&dma_private->tcd1[i], ++ saddr1, ++ (u32)dma_private->dma_buf_next + offset, ++ attr, ++ 0, /* source offset */ ++ nbyte, ++ 0, /* slast */ ++ major_loops, /* citer */ ++ major_loops, /* biter */ ++ doffset, ++ sga1, ++ 0, /* major_int */ ++ 0, /* enable dma request after */ ++ 1); /* enable scatter/gather */ ++ } ++ return 0; ++} ++ ++static inline void ssi_audio_enable_ssi_playback(void) ++{ ++ MCF_SSI_CR |= MCF_SSI_CR_SSI_EN | MCF_SSI_CR_TE; ++} ++ ++static inline void ssi_audio_enable_ssi_capture(void) ++{ ++ MCF_SSI_CR |= MCF_SSI_CR_SSI_EN | MCF_SSI_CR_RE; ++} ++ ++static inline void ssi_audio_disable_ssi(void) ++{ ++ MCF_SSI_CR &= ~(MCF_SSI_CR_TE | MCF_SSI_CR_RE | MCF_SSI_CR_SSI_EN); ++} ++ ++static inline void ssi_audio_disable_ssi_playback(void) ++{ ++ MCF_SSI_CR &= ~MCF_SSI_CR_TE; ++} ++ ++static inline void ssi_audio_disable_ssi_capture(void) ++{ ++ MCF_SSI_CR &= ~MCF_SSI_CR_RE; ++} ++ ++static irqreturn_t ssi_audio_dma_playback_handler(int channel, void *dev_id) ++{ ++ struct snd_pcm_substream *substream; ++ struct snd_pcm_runtime *runtime; ++ ++ substream = (struct snd_pcm_substream *)dev_id; ++ runtime = substream->runtime; ++ ++ /* inform ALSA middle layer about transfer status */ ++ snd_pcm_period_elapsed(substream); ++ mcf_edma_confirm_interrupt_handled(DMA_TX_TCD0); ++ mcf_edma_confirm_interrupt_handled(DMA_TX_TCD1); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t ssi_audio_dma_capture_handler(int channel, void *dev_id) ++{ ++ struct snd_pcm_substream *substream; ++ struct snd_pcm_runtime *runtime; ++ ++ substream = (struct snd_pcm_substream *)dev_id; ++ runtime = substream->runtime; ++ ++ /* inform ALSA middle layer about transfer status */ ++ snd_pcm_period_elapsed(substream); ++ mcf_edma_confirm_interrupt_handled(DMA_RX_TCD0); ++ mcf_edma_confirm_interrupt_handled(DMA_RX_TCD1); ++ ++ return IRQ_HANDLED; ++} ++ ++int ssi_audio_dma_request_playback_channel(struct snd_pcm_substream *substream) ++{ ++ int err; ++ struct chip_spec *chip = snd_pcm_substream_chip(substream); ++ ++ /* request eDMA channel */ ++ err = mcf_edma_request_channel(DMA_TX_TCD0, ++ ssi_audio_dma_playback_handler, ++ NULL, ++ MCF_SSI_AUDIO_IRQ_LEVEL, ++ substream, ++ &(chip->audio_device->lock), ++ id); ++ if (err < 0) ++ return err; ++ err = mcf_edma_request_channel(DMA_TX_TCD1, ++ ssi_audio_dma_playback_handler, ++ NULL, ++ MCF_SSI_AUDIO_IRQ_LEVEL, ++ substream, ++ &(chip->audio_device->lock), ++ id); ++ return err; ++} ++ ++int ssi_audio_dma_request_capture_channel(struct snd_pcm_substream *substream) ++{ ++ int err; ++ struct chip_spec *chip = snd_pcm_substream_chip(substream); ++ ++ /* request 2 eDMA channels for two fifo */ ++ err = mcf_edma_request_channel(DMA_RX_TCD0, ++ ssi_audio_dma_capture_handler, ++ NULL, ++ MCF_SSI_AUDIO_IRQ_LEVEL, ++ substream, ++ &(chip->audio_device->lock), ++ id); ++ if (err < 0) ++ return err; ++ err = mcf_edma_request_channel(DMA_RX_TCD1, ++ ssi_audio_dma_capture_handler, ++ NULL, ++ MCF_SSI_AUDIO_IRQ_LEVEL, ++ substream, ++ &(chip->audio_device->lock), ++ id); ++ return err; ++} ++ ++static inline void ssi_audio_init_dma(void) ++{ ++ /* SSI DMA Signals mapped to DMA request */ ++ MCF_CCM_MISCCR &= ~MCF_CCM_MISCCR_TIM_DMA; ++} ++ ++static void ssi_audio_adjust_codec_speed(struct snd_pcm_substream *substream) ++{ ++ ssi_audio_disable_ssi(); ++ ++ if (substream->runtime->format == SNDRV_PCM_FORMAT_S16_BE) { ++ MCF_SSI_CCR = MCF_SSI_CCR_WL(7) | /* 16 bit word length */ ++ MCF_SSI_CCR_DC(1); /* Frame rate divider */ ++ } ++ ++ switch (substream->runtime->rate) { ++ case 8000: ++#if defined(CONFIG_M532x) || defined(CONFIG_M537x) ++ MCF_CCM_CDR = (MCF_CCM_CDR | MCF_CCM_CDR_SSIDIV(0x20)) ++ | MCF_CCM_CDR_SSIDIV(MCF_SSI_AUDIO_SSDIV_VALUE_1); ++#else ++ MCF_CCM_CDR = (MCF_CCM_CDR & ~MCF_CCM_CDR_SSIDIV(0xFF)) | ++ MCF_CCM_CDR_SSIDIV(MCF_SSI_AUDIO_SSDIV_VALUE_1); ++#endif ++ MCF_SSI_CCR |= MCF_SSI_CCR_PM(11); ++ mcf_codec_spi_write(TLV320AIC23_CODEC_SAMPLE_RATE_REG, ++ TLV320AIC23_CODEC_SAMPLERATE_REG_8KHZ); ++ break; ++ case 11000: ++ case 11025: ++#if defined(CONFIG_M532x) || defined(CONFIG_M537x) ++ MCF_CCM_CDR = (MCF_CCM_CDR & ~MCF_CCM_CDR_SSIDIV(0x3F)) | ++ MCF_CCM_CDR_SSIDIV(0x2B); ++ MCF_SSI_CCR |= MCF_SSI_CCR_PM(11); ++#else ++ MCF_CCM_CDR = (MCF_CCM_CDR & ~MCF_CCM_CDR_SSIDIV(0xFF)) | ++ MCF_CCM_CDR_SSIDIV(MCF_SSI_AUDIO_SSDIV_VALUE_3); ++ MCF_SSI_CCR |= MCF_SSI_CCR_PM(9); ++#endif ++ mcf_codec_spi_write(TLV320AIC23_CODEC_SAMPLE_RATE_REG, ++ TLV320AIC23_CODEC_SAMPLERATE_REG_11KHZ); ++ break; ++ case 16000: ++#if defined(CONFIG_M532x) || defined(CONFIG_M537x) ++ MCF_CCM_CDR = (MCF_CCM_CDR | MCF_CCM_CDR_SSIDIV(0x20)) ++ | MCF_CCM_CDR_SSIDIV(MCF_SSI_AUDIO_SSDIV_VALUE_1); ++#else ++ MCF_CCM_CDR = (MCF_CCM_CDR & ~MCF_CCM_CDR_SSIDIV(0xFF)) | ++ MCF_CCM_CDR_SSIDIV(MCF_SSI_AUDIO_SSDIV_VALUE_1); ++#endif ++ MCF_SSI_CCR |= MCF_SSI_CCR_PM(5); ++ mcf_codec_spi_write(TLV320AIC23_CODEC_SAMPLE_RATE_REG, ++ TLV320AIC23_CODEC_SAMPLERATE_REG_16KHZ); ++ break; ++ case 22000: ++ case 22050: ++#if defined(CONFIG_M532x) || defined(CONFIG_M537x) ++ MCF_CCM_CDR = (MCF_CCM_CDR & ~MCF_CCM_CDR_SSIDIV(0x3F)) | ++ MCF_CCM_CDR_SSIDIV(0x2B); ++ MCF_SSI_CCR |= MCF_SSI_CCR_PM(5); ++#else ++ MCF_CCM_CDR = (MCF_CCM_CDR & ~MCF_CCM_CDR_SSIDIV(0xFF)) | ++ MCF_CCM_CDR_SSIDIV(MCF_SSI_AUDIO_SSDIV_VALUE_3); ++ MCF_SSI_CCR |= MCF_SSI_CCR_PM(4); ++#endif ++ mcf_codec_spi_write(TLV320AIC23_CODEC_SAMPLE_RATE_REG, ++ TLV320AIC23_CODEC_SAMPLERATE_REG_22KHZ); ++ break; ++ case 48000: ++#if defined(CONFIG_M532x) || defined(CONFIG_M537x) ++ MCF_CCM_CDR = (MCF_CCM_CDR & ~MCF_CCM_CDR_SSIDIV(0x3F)) | ++ MCF_CCM_CDR_SSIDIV(MCF_SSI_AUDIO_SSDIV_VALUE_4); ++ MCF_SSI_CCR |= MCF_SSI_CCR_PM(3); ++#else ++ MCF_CCM_CDR = (MCF_CCM_CDR & ~MCF_CCM_CDR_SSIDIV(0xFF)) | ++ MCF_CCM_CDR_SSIDIV(MCF_SSI_AUDIO_SSDIV_VALUE_1); ++ MCF_SSI_CCR |= MCF_SSI_CCR_PM(1); ++#endif ++ mcf_codec_spi_write(TLV320AIC23_CODEC_SAMPLE_RATE_REG, ++ TLV320AIC23_CODEC_SAMPLERATE_REG_48KHZ); ++ break; ++ case 44000: ++ case 44100: ++ default: ++#if defined(CONFIG_M532x) || defined(CONFIG_M537x) ++ MCF_CCM_CDR = (MCF_CCM_CDR & ~MCF_CCM_CDR_SSIDIV(0x3F)) | ++ MCF_CCM_CDR_SSIDIV(0x2B); ++#else ++ MCF_CCM_CDR = (MCF_CCM_CDR & ~MCF_CCM_CDR_SSIDIV(0xFF)) | ++ MCF_CCM_CDR_SSIDIV(MCF_SSI_AUDIO_SSDIV_VALUE_2); ++#endif ++ MCF_SSI_CCR |= MCF_SSI_CCR_PM(2); ++ mcf_codec_spi_write(TLV320AIC23_CODEC_SAMPLE_RATE_REG, ++ TLV320AIC23_CODEC_SAMPLERATE_REG_44KHZ); ++ break; ++ } ++ DBG("MCF_CCM_CDR = 0x%x, MCF_SSI_CCR = 0x%x.\n", ++ MCF_CCM_CDR, MCF_SSI_CCR); ++} ++ ++static void ssi_audio_codec_reset(void) ++{ ++ mcf_codec_spi_write(CODEC_RESET_REG, 0); /* reset the audio chip */ ++ udelay(2500); /* wait for reset */ ++} ++ ++static void ++ssi_audio_init_codec_for_playback(struct snd_pcm_substream *substream) ++{ ++ mcf_codec_spi_write(CODEC_LEFT_IN_REG, g_lineinvol); ++ mcf_codec_spi_write(CODEC_RIGHT_IN_REG, g_lineinvol); ++ mcf_codec_spi_write(CODEC_POWER_DOWN_REG, 0x060); ++ mcf_codec_spi_write(CODEC_DIGITAL_IF_FMT_REG, 0x02); ++ mcf_codec_spi_write(CODEC_DIGITAL_APATH_REG, 0x006); ++ mcf_codec_spi_write(CODEC_DIGITAL_IF_ACT_REG, 0x001); ++ mcf_codec_spi_write(CODEC_ANALOG_APATH_REG, g_analogpath); ++ mcf_codec_spi_write(CODEC_LEFT_HP_VOL_REG, g_mastervol); ++ mcf_codec_spi_write(CODEC_RIGHT_HP_VOL_REG, g_mastervol); ++} ++ ++static void ++ssi_audio_init_codec_for_capture(struct snd_pcm_substream *substream) ++{ ++ mcf_codec_spi_write(CODEC_LEFT_IN_REG, g_lineinvol); ++ mcf_codec_spi_write(CODEC_RIGHT_IN_REG, g_lineinvol); ++ mcf_codec_spi_write(CODEC_POWER_DOWN_REG, 0x060); ++ mcf_codec_spi_write(CODEC_DIGITAL_IF_FMT_REG, 0x02); ++ mcf_codec_spi_write(CODEC_DIGITAL_APATH_REG, 0x006); ++ mcf_codec_spi_write(CODEC_DIGITAL_IF_ACT_REG, 0x001); ++ mcf_codec_spi_write(CODEC_ANALOG_APATH_REG, g_analogpath); ++ mcf_codec_spi_write(CODEC_LEFT_HP_VOL_REG, g_mastervol); ++ mcf_codec_spi_write(CODEC_RIGHT_HP_VOL_REG, g_mastervol); ++} ++ ++static void ssi_audio_chip_init(void) ++{ ++ int chip_initialized = 0; ++ if (chip_initialized == 1) ++ return; ++ ++ ssi_audio_init_dma(); ++ /* Enable the SSI pins */ ++#if defined(CONFIG_M5227x) ++ MCF_GPIO_PAR_UART = (MCF_GPIO_PAR_UART ++ &~MCF_GPIO_PAR_UART_PAR_U1TXD(0xF) ++ &~MCF_GPIO_PAR_UART_PAR_U1RXD(0xF) ++ &~MCF_GPIO_PAR_UART_PAR_U1RTS(0xF) ++ &~MCF_GPIO_PAR_UART_PAR_U1CTS(0xF)) ++ | MCF_GPIO_PAR_UART_PAR_U1CTS_SSI_BCLK ++ | MCF_GPIO_PAR_UART_PAR_U1RTS_SSI_FS ++ | MCF_GPIO_PAR_UART_PAR_U1RXD_SSI_RXD ++ | MCF_GPIO_PAR_UART_PAR_U1TXD_SSI_TXD; ++ ++ MCF_GPIO_PAR_TIMER = (MCF_GPIO_PAR_TIMER ++ &~MCF_GPIO_PAR_TIMER_PAR_T3IN(0xF)) ++ | MCF_GPIO_PAR_TIMER_PAR_T3IN_SSI_MCLK; ++#endif ++#if defined(CONFIG_M532x) ++ MCF_GPIO_PAR_SSI = (0 ++ | MCF_GPIO_PAR_SSI_PAR_MCLK ++ | MCF_GPIO_PAR_SSI_PAR_TXD(3) ++ | MCF_GPIO_PAR_SSI_PAR_RXD(3) ++ | MCF_GPIO_PAR_SSI_PAR_FS(3) ++ | MCF_GPIO_PAR_SSI_PAR_BCLK(3)); ++#endif ++#if defined(CONFIG_M537x) ++ MCF_GPIO_PAR_UART = (MCF_GPIO_PAR_UART ++ &~MCF_GPIO_PAR_UART_PAR_UTXD1(0xF) ++ &~MCF_GPIO_PAR_UART_PAR_URXD1(0xF) ++ &~MCF_GPIO_PAR_UART_PAR_URTS1(0xF) ++ &~MCF_GPIO_PAR_UART_PAR_UCTS1(0xF)) ++ | MCF_GPIO_PAR_UART_PAR_UCTS1_SSI_BCLK ++ | MCF_GPIO_PAR_UART_PAR_URTS1_SSI_FS ++ | MCF_GPIO_PAR_UART_PAR_URXD1_SSI_RXD ++ | MCF_GPIO_PAR_UART_PAR_UTXD1_SSI_TXD; ++ ++ MCF_GPIO_PAR_IRQ = MCF_GPIO_PAR_IRQ_PAR_IRQ4(1); ++#endif ++#if defined(CONFIG_M5301x) ++ MCF_GPIO_PAR_SSIH = (MCF_GPIO_PAR_SSIH_PAR_RXD_SSI_RXD | ++ MCF_GPIO_PAR_SSIH_PAR_TXD_SSI_TXD | ++ MCF_GPIO_PAR_SSIH_PAR_FS_SSI_FS | ++ MCF_GPIO_PAR_SSIH_PAR_MCLK_SSI_MCLK); ++ MCF_GPIO_PAR_SSIL = MCF_GPIO_PAR_SSIL_PAR_BCLK_SSI_BCLK; ++#endif ++#if defined(CONFIG_M5445X) ++ MCF_GPIO_PAR_SSI = (MCF_GPIO_PAR_SSI_MCLK | ++ MCF_GPIO_PAR_SSI_STXD_STXD | ++ MCF_GPIO_PAR_SSI_SRXD_SRXD | ++ MCF_GPIO_PAR_SSI_FS_FS | ++ MCF_GPIO_PAR_SSI_BCLK_BCLK); ++#endif ++ chip_initialized = 1; ++} ++ ++static void ssi_audio_init_ssi_playback(void) ++{ ++ /* Issue a SSI reset */ ++ MCF_SSI_CR &= ~MCF_SSI_CR_SSI_EN; ++ ++ /* SSI module uses internal CPU clock */ ++ MCF_CCM_MISCCR |= MCF_CCM_MISCCR_SSI_SRC; ++#if defined(CONFIG_M5445X) || defined(CONFIG_M532x) || defined(CONFIG_M537x) \ ++ || defined(CONFIG_M5227x) ++ MCF_CCM_MISCCR |= MCF_CCM_MISCCR_SSI_PUE | MCF_CCM_MISCCR_SSI_PUS; ++#endif ++#if defined(CONFIG_M5301x) ++ MCF_GPIO_PCRH |= MCF_GPIO_PCRH_SSI_PUS | MCF_GPIO_PCRH_SSI_PUE; ++#endif ++ MCF_SSI_CR = MCF_SSI_CR_CIS | ++ MCF_SSI_CR_TCH | /* Enable two channel mode */ ++ MCF_SSI_CR_MCE | /* clock out on SSI_MCLK pin */ ++ MCF_SSI_CR_I2S_MASTER | /* I2S master mode */ ++ MCF_SSI_CR_SYN | /* Enable synchronous mode */ ++ MCF_SSI_CR_NET; /* Auto set by I2S Master */ ++ ++ MCF_SSI_TCR = 0 | ++ /* internally generated bit clock */ ++ MCF_SSI_TCR_TXDIR | ++ /* internally generated frame sync */ ++ MCF_SSI_TCR_TFDIR | ++ /* Clock data on falling edge of bit clock */ ++ MCF_SSI_TCR_TSCKP | ++ /* Frame sync active low */ ++ MCF_SSI_TCR_TFSI | ++ /* TX frame sync 1 bit before data */ ++ MCF_SSI_TCR_TEFS | ++ /* TX FIFO 0 enabled */ ++ MCF_SSI_TCR_TFEN0 | ++ /* TX FIFO 1 enabled */ ++ MCF_SSI_TCR_TFEN1 | ++ MCF_SSI_TCR_TXBIT0; ++ ++ MCF_SSI_FCSR = MCF_SSI_FCSR_TFWM0(TXFWM) | MCF_SSI_FCSR_TFWM1(TXFWM); ++ ++ MCF_SSI_IER = MCF_SSI_IER_TDMAE | /* DMA request enabled */ ++ MCF_SSI_IER_TFE0 | ++ MCF_SSI_IER_TFE1; /* set by reset actually*/ ++} ++ ++static void ssi_audio_init_ssi_capture(void) ++{ ++ /* Issue a SSI reset */ ++ MCF_SSI_CR &= ~MCF_SSI_CR_SSI_EN; ++ ++ /* SSI module uses internal CPU clock */ ++ MCF_CCM_MISCCR |= MCF_CCM_MISCCR_SSI_SRC; ++#if defined(CONFIG_M5445X) || defined(CONFIG_M532x) || defined(CONFIG_M537x) \ ++ || defined(CONFIG_M5227x) ++ MCF_CCM_MISCCR |= MCF_CCM_MISCCR_SSI_PUE | MCF_CCM_MISCCR_SSI_PUS; ++#endif ++#if defined(CONFIG_M5301x) ++ MCF_GPIO_PCRH |= MCF_GPIO_PCRH_SSI_PUS | MCF_GPIO_PCRH_SSI_PUE; ++#endif ++ MCF_SSI_CR = MCF_SSI_CR_CIS | ++ MCF_SSI_CR_TCH | /* Enable two channel mode */ ++ MCF_SSI_CR_MCE | /* clock out on SSI_MCLK pin */ ++ MCF_SSI_CR_I2S_MASTER | /* I2S master mode */ ++ MCF_SSI_CR_SYN | /* Enable synchronous mode */ ++ MCF_SSI_CR_NET; /* Auto set by I2S Master */ ++ ++ MCF_SSI_TCR = 0 | ++ /* internally generated bit clock */ ++ MCF_SSI_TCR_TXDIR | ++ /* internally generated frame sync */ ++ MCF_SSI_TCR_TFDIR | ++ /* Clock data on falling edge of bit clock */ ++ MCF_SSI_TCR_TSCKP | ++ /* Frame sync active low */ ++ MCF_SSI_TCR_TFSI | ++ /* TX frame sync 1 bit before data */ ++ MCF_SSI_TCR_TEFS | ++ /* TX FIFO 0 enabled */ ++ MCF_SSI_TCR_TFEN0 | ++ /* TX FIFO 1 enabled */ ++ MCF_SSI_TCR_TFEN1 | ++ MCF_SSI_TCR_TXBIT0; ++ ++ MCF_SSI_RCR = 0 | ++ /* Clock data on rising edge of bit clock */ ++ MCF_SSI_RCR_RSCKP | ++ /* Frame sync active low */ ++ MCF_SSI_RCR_RFSI | ++ /* RX frame sync 1 bit before data */ ++ MCF_SSI_RCR_REFS | ++ /* RX FIFO 0 enabled */ ++ MCF_SSI_RCR_RFEN0 | ++ /* RX FIFO 1 enabled */ ++ MCF_SSI_RCR_RFEN1 | ++ MCF_SSI_RCR_RXBIT0; /* Auto set by I2S Master */ ++ ++ MCF_SSI_FCSR = MCF_SSI_FCSR_RFWM0(RXFWM) | MCF_SSI_FCSR_RFWM1(RXFWM); ++ ++ /* interrupts */ ++ MCF_SSI_IER = MCF_SSI_IER_RDMAE | /* DMA request enabled */ ++ MCF_SSI_IER_RFF0 | /* rx FIFO 0 full */ ++ MCF_SSI_IER_RFF1; /* rx FIFO 1 full */ ++} ++ ++static int snd_coldfire_playback_open(struct snd_pcm_substream *substream) ++{ ++ int err; ++ struct chip_spec *chip = snd_pcm_substream_chip(substream); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct dma_private *dma_private; ++ dma_addr_t tcd_buf_phys; ++ ++ runtime->hw = snd_coldfire_playback_hw; ++ err = snd_pcm_hw_constraint_integer(runtime, ++ SNDRV_PCM_HW_PARAM_PERIODS); ++ if (err < 0) { ++ printk(KERN_ERR "invalid buffer size\n"); ++ return err; ++ } ++ /* to make sure period_bytes is the multiple of size of minor loops */ ++ err = snd_pcm_hw_constraint_step(runtime, 0, ++ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, ++ HW_PERIODS_BYTES_STEP); ++ if (err < 0) { ++ printk(KERN_ERR "Error setting period_bytes step, " ++ "err=%d\n", err); ++ return err; ++ } ++ err = snd_pcm_hw_constraint_list(substream->runtime, 0, ++ SNDRV_PCM_HW_PARAM_RATE, ++ &constraints_rates); ++ if (err < 0) { ++ printk(KERN_ERR "Error setting rate constraints, " ++ "err=%d\n", err); ++ return err; ++ } ++ ssi_audio_chip_init(); ++ ssi_audio_init_ssi_playback(); ++ ssi_audio_init_codec_for_playback(substream); ++ err = ssi_audio_dma_request_playback_channel(substream); ++ if (err < 0) { ++ printk(KERN_ERR "Error requesting dma channel, err=%d\n", err); ++ return err; ++ } ++ ++ dma_private = dma_alloc_coherent(substream->pcm->dev, ++ sizeof(struct dma_private), &tcd_buf_phys, GFP_KERNEL); ++ ++ if (!dma_private) { ++ dev_err(substream->pcm->card->dev, ++ "can't allocate DMA private data\n"); ++ return -ENOMEM; ++ } ++ ++ dma_private->tcd_buf_phys = tcd_buf_phys; ++ runtime->private_data = dma_private; ++ ++ chip->offset = 0; ++ g_tx_dmaing = 0; ++ return 0; ++} ++ ++static int snd_coldfire_capture_open(struct snd_pcm_substream *substream) ++{ ++ int err; ++ struct chip_spec *chip = snd_pcm_substream_chip(substream); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct dma_private *dma_private; ++ dma_addr_t tcd_buf_phys; ++ ++ runtime->hw = snd_coldfire_capture_hw; ++ ++ err = snd_pcm_hw_constraint_integer(runtime, ++ SNDRV_PCM_HW_PARAM_PERIODS); ++ if (err < 0) { ++ printk(KERN_ERR "invalid buffer size\n"); ++ return err; ++ } ++ /* to make sure period_bytes is the multiple of size of minor loops */ ++ err = snd_pcm_hw_constraint_step(runtime, 0, ++ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, ++ HW_PERIODS_BYTES_STEP); ++ if (err < 0) { ++ printk(KERN_ERR "Error setting period_bytes step, " ++ "err=%d\n", err); ++ return err; ++ } ++ err = snd_pcm_hw_constraint_list(substream->runtime, 0, ++ SNDRV_PCM_HW_PARAM_RATE, ++ &constraints_rates); ++ if (err < 0) { ++ printk(KERN_ERR "Error setting pcm_hw_constraint_list, " ++ "err=%d\n", err); ++ return err; ++ } ++ ++ ssi_audio_chip_init(); ++ ssi_audio_init_ssi_capture(); ++ ssi_audio_init_codec_for_capture(substream); ++ err = ssi_audio_dma_request_capture_channel(substream); ++ if (err < 0) { ++ printk(KERN_ERR "Error requesting dma channel, err=%d\n", err); ++ return err; ++ } ++ ++ dma_private = dma_alloc_coherent(substream->pcm->dev, ++ sizeof(struct dma_private), &tcd_buf_phys, GFP_KERNEL); ++ ++ if (!dma_private) { ++ dev_err(substream->pcm->card->dev, ++ "can't allocate DMA private data\n"); ++ return -ENOMEM; ++ } ++ dma_private->tcd_buf_phys = tcd_buf_phys; ++ dma_private->dma_buf_phys = substream->dma_buffer.addr; ++ ++ runtime->private_data = dma_private; ++ ++ chip->offset = 0; ++ g_rx_dmaing = 0; ++ return 0; ++} ++ ++static int snd_coldfire_playback_close(struct snd_pcm_substream *substream) ++{ ++ struct dma_private *dma_private = substream->runtime->private_data; ++ ++ ssi_audio_dma_playback_stop(); ++ mcf_edma_free_channel(DMA_TX_TCD0, substream); ++ mcf_edma_free_channel(DMA_TX_TCD1, substream); ++ if (dma_private) { ++ dma_free_coherent(substream->pcm->dev, ++ sizeof(struct dma_private), ++ dma_private, dma_private->tcd_buf_phys); ++ substream->runtime->private_data = NULL; ++ } ++ ssi_audio_disable_ssi(); ++ return 0; ++} ++ ++static int snd_coldfire_capture_close(struct snd_pcm_substream *substream) ++{ ++ struct dma_private *dma_private = substream->runtime->private_data; ++ ++ ssi_audio_dma_capture_stop(); ++ mcf_edma_free_channel(DMA_RX_TCD0, substream); ++ mcf_edma_free_channel(DMA_RX_TCD1, substream); ++ /* Deallocate the fsl_dma_private structure */ ++ if (dma_private) { ++ dma_free_coherent(substream->pcm->dev, ++ sizeof(struct dma_private), ++ dma_private, dma_private->tcd_buf_phys); ++ substream->runtime->private_data = NULL; ++ } ++ ssi_audio_disable_ssi(); ++ return 0; ++} ++ ++static int snd_coldfire_pcm_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *hw_params) ++{ ++ int err; ++ ++ /* set runtime buffer */ ++ err = snd_pcm_lib_malloc_pages( ++ substream, params_buffer_bytes(hw_params)); ++ if (err < 0) ++ printk(KERN_ERR "Error allocating pages, err=%d\n", err); ++ return err; ++} ++ ++static int snd_coldfire_pcm_hw_free(struct snd_pcm_substream *substream) ++{ ++ /* free the memory if was newly allocated */ ++ return snd_pcm_lib_free_pages(substream); ++} ++ ++static int ++snd_coldfire_pcm_playback_prepare(struct snd_pcm_substream *substream) ++{ ++ int err; ++ ++ if (g_tx_dmaing == 1) ++ return 0; ++ ++ ssi_audio_adjust_codec_speed(substream); ++ err = ssi_audio_dma_playback_config(substream); ++ if (err < 0) { ++ printk(KERN_ERR "Error configuring playback, " ++ "err=%d\n", err); ++ return err; ++ } ++ ++ ssi_audio_dma_playback_start(); ++ return 0; ++} ++ ++static int snd_coldfire_pcm_capture_prepare(struct snd_pcm_substream *substream) ++{ ++ int err; ++ ++ if (g_rx_dmaing == 1) ++ return 0; ++ ++ ssi_audio_adjust_codec_speed(substream); ++ err = ssi_audio_dma_capture_config(substream); ++ if (err < 0) { ++ printk(KERN_ERR "Error configuring capture, " ++ "err=%d\n", err); ++ return err; ++ } ++ ssi_audio_dma_capture_start(); ++ ++ return 0; ++} ++ ++static int ++snd_coldfire_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) ++{ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ ssi_audio_enable_ssi_playback(); ++ break; ++ case SNDRV_PCM_TRIGGER_STOP: ++ ssi_audio_disable_ssi_playback(); ++ break; ++ default: ++ printk(KERN_ERR "Unsupported trigger command, cmd=%d\n", cmd); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int ++snd_coldfire_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) ++{ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ ssi_audio_enable_ssi_capture(); ++ break; ++ case SNDRV_PCM_TRIGGER_STOP: ++ ssi_audio_disable_ssi_capture(); ++ break; ++ default: ++ printk(KERN_ERR "Unsupported trigger command, cmd=%d\n", cmd); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static snd_pcm_uframes_t ++snd_coldfire_pcm_playback_pointer(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct dma_private *dma_private = runtime->private_data; ++ snd_pcm_uframes_t pointer; ++ u32 offset; ++ ++ offset = (u32)(MCF_EDMA_TCD_SADDR(DMA_TX_TCD0) - ++ dma_private->dma_buf_phys); ++ if (runtime->format == SNDRV_PCM_FORMAT_S16_BE) ++ pointer = offset / (runtime->channels == 1 ? 2 : 4); ++ else ++ pointer = 0; ++ ++ return pointer; ++} ++ ++static snd_pcm_uframes_t ++snd_coldfire_pcm_capture_pointer(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct dma_private *dma_private = runtime->private_data; ++ snd_pcm_uframes_t pointer; ++ u32 offset; ++ ++ offset = (u32)(MCF_EDMA_TCD_DADDR(DMA_RX_TCD0) - ++ dma_private->dma_buf_phys); ++ if (runtime->format == SNDRV_PCM_FORMAT_S16_BE) ++ pointer = offset / (runtime->channels == 1 ? 2 : 4); ++ else ++ pointer = 0; ++ ++ return pointer; ++} ++ ++static struct snd_pcm_ops snd_coldfire_playback_ops = { ++ .open = snd_coldfire_playback_open, ++ .close = snd_coldfire_playback_close, ++ .ioctl = snd_pcm_lib_ioctl, ++ .hw_params = snd_coldfire_pcm_hw_params, ++ .hw_free = snd_coldfire_pcm_hw_free, ++ .prepare = snd_coldfire_pcm_playback_prepare, ++ .trigger = snd_coldfire_pcm_playback_trigger, ++ .pointer = snd_coldfire_pcm_playback_pointer, ++}; ++ ++static struct snd_pcm_ops snd_coldfire_capture_ops = { ++ .open = snd_coldfire_capture_open, ++ .close = snd_coldfire_capture_close, ++ .ioctl = snd_pcm_lib_ioctl, ++ .hw_params = snd_coldfire_pcm_hw_params, ++ .hw_free = snd_coldfire_pcm_hw_free, ++ .prepare = snd_coldfire_pcm_capture_prepare, ++ .trigger = snd_coldfire_pcm_capture_trigger, ++ .pointer = snd_coldfire_pcm_capture_pointer, ++}; ++ ++static int snd_coldfire_new_pcm(struct chip_spec *chip) ++{ ++ struct snd_pcm *pcm; ++ int err; ++ ++ err = snd_pcm_new(chip->card, "coldfire", 0, 1, 1, ++ &pcm); ++ if (err < 0) { ++ printk(KERN_ERR "Error creating new pcm, err=%d\n", err); ++ return err; ++ } ++ pcm->private_data = chip; ++ strncpy(pcm->name, SOUND_CARD_NAME, sizeof(pcm->name)); ++ chip->pcm = pcm; ++ pcm->info_flags = 0; ++ ++ /* set operators */ ++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, ++ &snd_coldfire_playback_ops); ++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, ++ &snd_coldfire_capture_ops); ++ /* pre-allocation of buffers */ ++ err = snd_pcm_lib_preallocate_pages_for_all( ++ pcm, ++ SNDRV_DMA_TYPE_CONTINUOUS, ++ snd_dma_continuous_data(GFP_KERNEL), ++ MAX_BUFFER_SIZE, ++ MAX_BUFFER_SIZE); ++ ++ if (!pcm->streams[0].substream->dma_buffer.addr) ++ pcm->streams[0].substream->dma_buffer.addr = ++ virt_to_phys(pcm->streams[0].substream->dma_buffer.area); ++ if (!pcm->streams[1].substream->dma_buffer.addr) ++ pcm->streams[1].substream->dma_buffer.addr = ++ virt_to_phys(pcm->streams[1].substream->dma_buffer.area); ++ ++ if (err) { ++ printk(KERN_ERR ++ "Can't pre-allocate DMA buffer (size=%u)\n", ++ MAX_BUFFER_SIZE); ++ return -ENOMEM; ++ } ++ ++ chip->audio_device = ++ kmalloc(sizeof(struct tlv320a_audio_device), GFP_DMA); ++ ++ if (!chip->audio_device) { ++ snd_pcm_lib_preallocate_free_for_all(pcm); ++ printk(KERN_ERR ++ "Can't allocate buffer for audio device\n"); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static int tlv320a_set_out_volume(unsigned char value) ++{ ++ unsigned char data; ++ ++ if (value > TLV320A_VOL_MAX) ++ data = TLV320A_VOL_MAX; ++ else ++ data = value; ++ ++ if (mcf_codec_spi_write(CODEC_LEFT_HP_VOL_REG, data) < 0) ++ return -EINVAL; ++ ++ if (mcf_codec_spi_write(CODEC_RIGHT_HP_VOL_REG, data) < 0) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int tlv320a_info_out_volume(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = 1; ++ uinfo->value.integer.min = TLV320A_VOL_MIN; ++ uinfo->value.integer.max = TLV320A_VOL_MAX; ++ return 0; ++} ++ ++static int tlv320a_get_out_volume(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ ucontrol->value.integer.value[0] = g_mastervol; ++ return 0; ++} ++ ++static int tlv320a_put_out_volume(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ unsigned char vol; ++ int change; ++ ++ vol = ucontrol->value.integer.value[0]; ++ ++ if (vol > TLV320A_VOL_MAX) ++ return -EINVAL; ++ ++ change = (g_mastervol != vol); ++ if (change) { ++ g_mastervol = vol; ++ tlv320a_set_out_volume(vol); ++ } ++ return change; ++} ++ ++static int tlv320a_set_linein_volume(unsigned char value) ++{ ++ unsigned char data; ++ ++ if (value > TLV320A_LINEIN_MAX) ++ data = TLV320A_LINEIN_MAX; ++ else ++ data = value; ++ ++ if (mcf_codec_spi_write(CODEC_LEFT_IN_REG, data) < 0) ++ return -EINVAL; ++ ++ if (mcf_codec_spi_write(CODEC_RIGHT_IN_REG, data) < 0) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int tlv320a_info_linein_volume(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = 1; ++ uinfo->value.integer.min = 0; ++ uinfo->value.integer.max = TLV320A_LINEIN_MAX; ++ return 0; ++} ++ ++static int tlv320a_get_linein_volume(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ ucontrol->value.integer.value[0] = g_lineinvol; ++ return 0; ++} ++ ++static int tlv320a_put_linein_volume(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ unsigned char vol; ++ int change; ++ ++ vol = ucontrol->value.integer.value[0]; ++ ++ if (vol > TLV320A_LINEIN_MAX) ++ return -EINVAL; ++ ++ change = (g_lineinvol != vol); ++ if (change) { ++ g_lineinvol = vol; ++ tlv320a_set_linein_volume(vol); ++ } ++ return change; ++} ++ ++#define tlv320a_info_mic_boost snd_ctl_boolean_mono_info ++static int tlv320a_get_mic_boost(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ ucontrol->value.integer.value[0] = ((g_analogpath & 0x1) == 1); ++ return 0; ++} ++ ++static int tlv320a_put_mic_boost(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ int oldboost, newboost; ++ u8 data; ++ if (ucontrol->value.integer.value[0] == 1) ++ newboost = 1; ++ else ++ newboost = 0; ++ oldboost = g_analogpath & 0x1; ++ ++ if (oldboost == newboost) ++ return 0; ++ data = (g_analogpath & 0xfe) | (newboost & 0x1); ++ if (mcf_codec_spi_write(CODEC_ANALOG_APATH_REG, data) < 0) ++ return -EINVAL; ++ g_analogpath = data; ++ return 1; ++} ++ ++static int tlv320a_info_capture_source(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ static char *texts[] = { "Line-In", "Microphone" }; ++ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; ++ uinfo->count = 1; ++ uinfo->value.enumerated.items = 2; ++ if (uinfo->value.enumerated.item > 1) ++ uinfo->value.enumerated.item = 1; ++ strcpy(uinfo->value.enumerated.name, ++ texts[uinfo->value.enumerated.item]); ++ return 0; ++} ++ ++static int tlv320a_get_capture_source(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ ++ ucontrol->value.enumerated.item[0] = ((g_analogpath & 0x4) == 0x4); ++ return 0; ++} ++ ++static int tlv320a_put_capture_source(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ int oldinput, newinput; ++ u8 data; ++ ++ if (ucontrol->value.enumerated.item[0] > 1) ++ return -EINVAL; ++ ++ oldinput = (g_analogpath & 0x4) ? INPUT_MICROPHONE : INPUT_LINEIN; ++ ++ if (ucontrol->value.enumerated.item[0]) ++ newinput = INPUT_MICROPHONE; ++ else ++ newinput = INPUT_LINEIN; ++ if (oldinput == newinput) ++ return 0; ++ data = (g_analogpath & 0xfb) | ++ (newinput == INPUT_MICROPHONE ? 0x4 : 0); ++ if (mcf_codec_spi_write(CODEC_ANALOG_APATH_REG, data) < 0) ++ return -EINVAL; ++ g_analogpath = data; ++ return 1; ++} ++ ++static struct snd_kcontrol_new tlv320_mixer_out __devinitdata = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "play volume", ++ .index = 0, ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, ++ .info = tlv320a_info_out_volume, ++ .get = tlv320a_get_out_volume, ++ .put = tlv320a_put_out_volume, ++}; ++ ++static struct snd_kcontrol_new tlv320_mixer_linein __devinitdata = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "record volume", ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, ++ .info = tlv320a_info_linein_volume, ++ .get = tlv320a_get_linein_volume, ++ .put = tlv320a_put_linein_volume, ++}; ++ ++static struct snd_kcontrol_new tlv320_mixer_capture_source __devinitdata = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "record source", ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, ++ .info = tlv320a_info_capture_source, ++ .get = tlv320a_get_capture_source, ++ .put = tlv320a_put_capture_source, ++}; ++ ++static struct snd_kcontrol_new tlv320_mixer_mic_boost __devinitdata = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "mic Boost", ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, ++ .info = tlv320a_info_mic_boost, ++ .get = tlv320a_get_mic_boost, ++ .put = tlv320a_put_mic_boost, ++}; ++ ++static int __devinit coldfire_alsa_audio_probe(struct platform_device *dev) ++{ ++ struct snd_card *card; ++ struct chip_spec *chip; ++ int err; ++ ++ err = snd_card_create(-1, id, THIS_MODULE, ++ sizeof(struct chip_spec), &card); ++ if (err < 0) ++ return -ENOMEM; ++ ++ chip = card->private_data; ++ ++ chip->card = card; ++ card->dev = &dev->dev; ++ ++ err = snd_coldfire_new_pcm(chip); ++ if (err < 0) ++ return -ENOMEM; ++ ++ strcpy(card->driver, "coldfire"); ++ strcpy(card->shortname, "Coldfire-TLV320A"); ++ sprintf(card->longname, "Freescale Coldfire with TLV320A"); ++ ++ err = snd_card_register(card); ++ if (err == 0) { ++ pr_debug(KERN_INFO "Coldfire audio support initialized\n"); ++ platform_set_drvdata(dev, card); ++ } ++ ++ strcpy(chip->card->mixername, "TLV320A Volume"); ++ err = snd_ctl_add(chip->card, snd_ctl_new1(&tlv320_mixer_out, chip)); ++ if (err) ++ goto error; ++ err = snd_ctl_add(chip->card, snd_ctl_new1(&tlv320_mixer_linein, chip)); ++ if (err) ++ goto error; ++ err = snd_ctl_add(chip->card, ++ snd_ctl_new1(&tlv320_mixer_capture_source, ++ chip)); ++ if (err) ++ goto error; ++ err = snd_ctl_add(chip->card, ++ snd_ctl_new1(&tlv320_mixer_mic_boost, ++ chip)); ++ if (err) ++ goto error; ++ g_mastervol = TLV320A_VOL_INIT; ++ g_lineinvol = TLV320A_LINEIN_INIT; ++ g_analogpath = TLV320A_ANALOGPATH_INIT; ++ ssi_audio_codec_reset(); ++ return 0; ++error: ++ kfree(card->private_data); ++ snd_card_free(card); ++ platform_set_drvdata(dev, NULL); ++ return err; ++} ++ ++static int coldfire_alsa_audio_remove(struct platform_device *dev) ++{ ++ struct snd_card *card; ++ ++ card = platform_get_drvdata(dev); ++ kfree(card->private_data); ++ snd_card_free(card); ++ platform_set_drvdata(dev, NULL); ++ ++ return 0; ++} ++ ++static struct platform_driver coldfire_alsa_audio_driver = { ++ .probe = coldfire_alsa_audio_probe, ++ .remove = coldfire_alsa_audio_remove, ++ .driver = { ++ .name = SOUND_CARD_NAME, ++ }, ++}; ++ ++static int __init coldfire_alsa_audio_init(void) ++{ ++ int err; ++ err = platform_driver_register(&coldfire_alsa_audio_driver); ++ if (err < 0) ++ return err; ++ ++ device = platform_device_register_simple(SOUND_CARD_NAME, -1, NULL, 0); ++ if (!IS_ERR(device)) { ++ if (platform_get_drvdata(device)) ++ return 0; ++ platform_device_unregister(device); ++ platform_driver_unregister(&coldfire_alsa_audio_driver); ++ err = -ENODEV; ++ } else ++ err = PTR_ERR(device); ++ ++ platform_driver_unregister(&coldfire_alsa_audio_driver); ++ return err; ++} ++ ++static void __exit coldfire_alsa_audio_exit(void) ++{ ++ platform_device_unregister(device); ++ platform_driver_unregister(&coldfire_alsa_audio_driver); ++} ++ ++module_init(coldfire_alsa_audio_init); ++module_exit(coldfire_alsa_audio_exit); ++ ++MODULE_DESCRIPTION("Coldfire driver for ALSA"); ++MODULE_LICENSE("GPL"); ++MODULE_SUPPORTED_DEVICE("{{TLV320A}}"); ++ ++module_param(id, charp, 0444); ++MODULE_PARM_DESC(id, "ID string for Coldfire + TLV320A soundcard."); +--- /dev/null ++++ b/sound/coldfire/snd-coldfire.h +@@ -0,0 +1,15 @@ ++/* ++ * linux/sound/coldfire/snd-coldfire.h ++ * ++ * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ++ * ALSA driver for Coldfire SSI ++ * ++ * This 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. ++ */ ++ ++extern int mcf_codec_spi_write(u8 addr, u16 data); ++ |