[Armadeus-commitlog] SF.net SVN: armadeus:[837] trunk/buildroot/target/device/armadeus/linux/ kerne
Brought to you by:
sszy
|
From: <ar...@us...> - 2008-07-18 19:52:40
|
Revision: 837
http://armadeus.svn.sourceforge.net/armadeus/?rev=837&view=rev
Author: artemys
Date: 2008-07-18 19:52:49 +0000 (Fri, 18 Jul 2008)
Log Message:
-----------
[LINUX] Add first version of ALSA drivers for TSC2102 / i.MXL SSI (should produce sound :-) )
Modified Paths:
--------------
trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.23.1/021-linux-2.6.23.1-apf9328.diff
trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.23.1/073-linux-2.6.23.1-tsc2102.diff
Added Paths:
-----------
trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.23.1/090-alsa-create-imx-alsa_h.diff
trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.23.1/091-alsa-add-imx-ssi-tsc2102-driver.diff
trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.23.1/092-alsa-imx-ssi-tsc2102-integrate-driver-to-linux-build-system.diff
trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.23.1/093-imxregs-add-ssi-registers-definition.diff
Modified: trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.23.1/021-linux-2.6.23.1-apf9328.diff
===================================================================
--- trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.23.1/021-linux-2.6.23.1-apf9328.diff 2008-07-18 16:35:49 UTC (rev 836)
+++ trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.23.1/021-linux-2.6.23.1-apf9328.diff 2008-07-18 19:52:49 UTC (rev 837)
@@ -1,6 +1,6 @@
--- linux-2.6.23.1.1/arch/arm/mach-imx/apf9328.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.23.1.1.mod/arch/arm/mach-imx/apf9328.c 2008-04-01 16:15:37.000000000 +0200
-@@ -0,0 +1,543 @@
+@@ -0,0 +1,570 @@
+/*
+ * linux/arch/arm/mach-imx/apf9328.c
+ *
@@ -43,6 +43,7 @@
+#include <asm/arch/mmc.h>
+#include <asm/arch/spi_imx.h>
+#include <asm/arch/imx-regs.h> // imx_gpio_mode()
++#include <asm/arch/imx-alsa.h>
+
+#ifdef CONFIG_DM9000
+#include <linux/dm9000.h>
@@ -121,6 +122,29 @@
+ .resource = pwm_resources,
+};
+
++#if defined(CONFIG_SND_IMX_TSC2102) || defined(CONFIG_SND_IMX_TSC2102_MODULE)
++static struct imx_alsa_codec_config tsc2102_alsa_pdata;
++
++static struct resource ssi_resources[] = {
++ [0] = {
++ .start = (IMX_SSI_BASE),
++ .end = (IMX_SSI_BASE + 0x28),
++ .flags = IORESOURCE_MEM,
++ },
++};
++
++static struct platform_device tsc2102_alsa_device = {
++ .name = "tsc2102-alsa", // Should be the same as the driver name
++ .id = 0,
++ .num_resources = ARRAY_SIZE(ssi_resources),
++ .resource = ssi_resources,
++ .dev = {
++ .platform_data = &tsc2102_alsa_pdata,
++ }
++};
++#endif // defined(CONFIG_SND_IMX_TSC2102) || defined(CONFIG_SND_IMX_TSC2102_MODULE)
++
++
+// --- SERIAL RESSOURCE ---
+static struct imxuart_platform_data uart_pdata = {
+ .flags = IMXUART_HAVE_RTSCTS,
@@ -448,6 +472,9 @@
+#ifdef CONFIG_IMX_BACKLIGHT
+ &imxbl_device,
+#endif // CONFIG_IMX_BACKLIGHT
++#if defined(CONFIG_SND_IMX_TSC2102) || defined(CONFIG_SND_IMX_TSC2102_MODULE)
++ &tsc2102_alsa_device,
++#endif // CONFIG_SND_IMX_TSC2102 || CONFIG_SND_IMX_TSC2102_MODULE
+};
+
+static void __init apf9328_init(void)
@@ -798,7 +825,7 @@
endmenu
--- ref/linux-2.6.23.1/arch/arm/mach-imx/Makefile 2006-10-12 22:37:14.000000000 +0200
+++ linux-2.6.23.1/arch/arm/mach-imx/Makefile 2006-10-07 20:09:07.000000000 +0200
-@@ -13,6 +13,7 @@ obj-y += irq.o time.o dma.o generic.o
+@@ -15,6 +15,7 @@
obj-$(CONFIG_ARCH_MX1ADS) += mx1ads.o
obj-$(CONFIG_MACH_MX1FS2) += mx1fs2.o
obj-$(CONFIG_MACH_SCB9328) += scb9328.o
@@ -879,7 +906,7 @@
+#endif // __ASM_ARCH_APF9328_H
--- ref/linux-2.6.23.1/include/asm-arm/arch-imx/hardware.h 2006-10-12 22:37:14.000000000 +0200
+++ linux-2.6.23.1/include/asm-arm/arch-imx/hardware.h 2006-10-07 20:09:07.000000000 +0200
-@@ -107,4 +107,8 @@ extern unsigned int imx_get_usb_clk(void
+@@ -104,4 +104,8 @@ extern unsigned int imx_get_usb_clk(void
#include "scb9328.h"
#endif
Modified: trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.23.1/073-linux-2.6.23.1-tsc2102.diff
===================================================================
--- trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.23.1/073-linux-2.6.23.1-tsc2102.diff 2008-07-18 16:35:49 UTC (rev 836)
+++ trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.23.1/073-linux-2.6.23.1-tsc2102.diff 2008-07-18 19:52:49 UTC (rev 837)
@@ -179,7 +179,7 @@
+#endif /* __LINUX_SPI_TSC2102_H */
--- linux-2.6.23.original/drivers/spi/tsc2102.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.23.mod/drivers/spi/tsc2102.c 2007-09-22 01:42:55.000000000 +0200
-@@ -0,0 +1,1103 @@
+@@ -0,0 +1,1148 @@
+/*
+ * drivers/spi/tsc2102.c
+ *
@@ -229,6 +229,13 @@
+#include <asm/apm.h>
+#endif
+
++#ifdef CONFIG_MACH_APF9328 // TODO: Put MCLK choice in Kconfig ??
++#define MCLK_16M 1 // DevFull
++#endif // CONFIG_MACH_APF9328
++
++/* #define DEBUG */
++#define DEBUG_AUDIO
++
+/* Bit field definitions for chip registers */
+#define TSC2102_ADC_TS_CONTROL 0x8bf4
+#define TSC2102_ADC_SCAN_CONTROL 0x2ff4
@@ -252,10 +259,17 @@
+#define TSC2102_DAC_OFF 0xafa0
+#define TSC2102_FS44K (1 << 13)
+#define TSC2102_PLL1_OFF 0x0000
++#ifdef MCLK_16M /* 16Mhz master clock */
++#define TSC2102_PLL1_44K (PLL1_PLLEN | PLL1_Q_VAL(0) | PLL1_P_VAL(1) | PLL1_J_VAL(5))
++#define TSC2102_PLL1_48K (PLL1_PLLEN | PLL1_Q_VAL(0) | PLL1_P_VAL(1) | PLL1_J_VAL(6))
++#define TSC2102_PLL2_44K (PLL2_D_VAL(6448))
++#define TSC2102_PLL2_48K (PLL2_D_VAL(1440))
++#else
+#define TSC2102_PLL1_44K 0x811c
+#define TSC2102_PLL1_48K 0x8120
+#define TSC2102_PLL2_44K (5462 << 2)
+#define TSC2102_PLL2_48K (1920 << 2)
++#endif // MCLK_16M
+#define TSC2102_SLVMS (1 << 11)
+#define TSC2102_DEEMPF (1 << 0)
+#define TSC2102_BASSBC (1 << 1)
@@ -297,6 +311,7 @@
+ struct tsc2102_spi_req req_pressure;
+ struct tsc2102_spi_req req_stopadc;
+ struct tsc2102_spi_req req_mode;
++ struct tsc2102_spi_req req_click;
+
+ int bat[2], aux[1], temp[2];
+ struct class_device *hwmondev;
@@ -683,7 +698,8 @@
+ }
+}
+
-+#ifdef CONFIG_SOUND
++#if defined(CONFIG_SOUND) || defined(CONFIG_SOUND_MODULE)
++
+/*
+ * Volume level values should be in the range [0, 127].
+ * Higher values mean lower volume.
@@ -705,6 +721,7 @@
+ tsc2102_write_sync(TSC2102_DAC_GAIN_CTRL, val);
+ mutex_unlock(&tsc.lock_sync);
+}
++EXPORT_SYMBOL(tsc2102_set_volume);
+
+void tsc2102_set_mute(int left_ch, int right_ch)
+{
@@ -720,6 +737,7 @@
+
+ mutex_unlock(&tsc.lock_sync);
+}
++EXPORT_SYMBOL(tsc2102_set_mute);
+
+void tsc2102_get_mute(int *left_ch, int *right_ch)
+{
@@ -733,6 +751,7 @@
+ *left_ch = !!(val & (1 << 15));
+ *right_ch = !!(val & (1 << 7));
+}
++EXPORT_SYMBOL(tsc2102_get_mute);
+
+void tsc2102_set_deemphasis(int enable)
+{
@@ -748,6 +767,7 @@
+ tsc2102_write_sync(TSC2102_DAC_POWER_CTRL, val);
+ mutex_unlock(&tsc.lock_sync);
+}
++EXPORT_SYMBOL(tsc2102_set_deemphasis);
+
+void tsc2102_set_bassboost(int enable)
+{
@@ -763,8 +783,9 @@
+ tsc2102_write_sync(TSC2102_DAC_POWER_CTRL, val);
+ mutex_unlock(&tsc.lock_sync);
+}
++EXPORT_SYMBOL(tsc2102_set_bassboost);
+
-+/* {rate, dsor, fsref} */
++/* {rate, divisor, fsref?=44,1k} */
+static const struct tsc2102_rate_info_s tsc2102_rates[] = {
+ /* Fsref / 6.0 */
+ {7350, 63, 1},
@@ -791,6 +812,7 @@
+ {44100, 0, 1},
+ {48000, 0, 0},
+
++ /* end of struct */
+ {0, 0, 0},
+};
+
@@ -809,7 +831,8 @@
+
+ mutex_lock(&tsc.lock_sync);
+
-+ tsc2102_write_sync(TSC2102_AUDIO1_CTRL, tsc2102_rates[i].divisor);
++ val = tsc2102_read_sync(TSC2102_AUDIO1_CTRL) & ~AC1_DACFS_MASK;
++ tsc2102_write_sync(TSC2102_AUDIO1_CTRL, val | AC1_DACFS(tsc2102_rates[i].divisor) );
+
+ val = tsc2102_read_sync(TSC2102_AUDIO3_CTRL);
+
@@ -828,6 +851,7 @@
+ mutex_unlock(&tsc.lock_sync);
+ return 0;
+}
++EXPORT_SYMBOL(tsc2102_set_rate);
+
+/*
+ * Perform basic set-up with default values and power the DAC on.
@@ -837,8 +861,8 @@
+ mutex_lock(&tsc.lock_sync);
+
+ if (state) {
-+ /* 16-bit words, DSP mode, sample at Fsref */
-+ tsc2102_write_sync(TSC2102_AUDIO1_CTRL, 0x0100);
++ /* 16-bit words, I2S mode, sample at Fsref */
++ tsc2102_write_sync(TSC2102_AUDIO1_CTRL, AC1_WLEN(0) | AC1_DATFM(0) | AC1_DACFS(0) );
+ /* Keyclicks off, soft-stepping at normal rate */
+ tsc2102_write_sync(TSC2102_AUDIO2_CTRL, TSC2102_KEYCLICK_OFF);
+ /* 44.1 kHz Fsref, continuous transfer mode, master DAC */
@@ -860,6 +884,7 @@
+
+ mutex_unlock(&tsc.lock_sync);
+}
++EXPORT_SYMBOL(tsc2102_dac_power);
+
+void tsc2102_set_i2s_master(int state)
+{
@@ -875,7 +900,8 @@
+
+ mutex_unlock(&tsc.lock_sync);
+}
-+#endif /* CONFIG_SOUND */
++EXPORT_SYMBOL(tsc2102_set_i2s_master);
++#endif /* CONFIG_SOUND || CONFIG_SOUND_MODULE */
+
+static int tsc2102_configure(struct tsc2102_dev *dev)
+{
@@ -894,6 +920,24 @@
+ /* PINT/DAV acts as DAV */
+ tsc2102_write_sync(TSC2102_TS_STATUS_CTRL, TSC2102_ADC_DAV);
+
++#if defined(CONFIG_SOUND) || defined(CONFIG_SOUND_MODULE)
++ /* 16-bit words, I2S mode, sample at Fsref */
++ tsc2102_write_sync(TSC2102_AUDIO1_CTRL, AC1_WLEN(0) | AC1_DATFM(0) | AC1_DACFS(0) );
++ /* Keyclicks off, soft-stepping at normal rate */
++ tsc2102_write_sync(TSC2102_AUDIO2_CTRL, TSC2102_KEYCLICK_OFF);
++ /* 48 kHz Fsref, continuous transfer mode, master DAC */
++ tsc2102_write_sync(TSC2102_AUDIO3_CTRL, 0x0800);
++ /* Soft-stepping enabled */
++ tsc2102_write_sync(TSC2102_AUDIO4_CTRL, 0x0000);
++ /* FSRef = 44,1k */
++ tsc2102_write_sync(TSC2102_PLL1_CTRL, TSC2102_PLL1_44K);
++ tsc2102_write_sync(TSC2102_PLL2_CTRL, TSC2102_PLL2_44K);
++
++ tsc2102_write_sync(TSC2102_DAC_GAIN_CTRL, 0xA0A0);
++ tsc2102_write_sync(TSC2102_DAC_POWER_CTRL, 0x3BA0);
++ tsc2102_write_sync(TSC2102_DAC_GAIN_CTRL, 0x7F7F);
++#endif /* CONFIG_SOUND || CONFIG_SOUND_MODULE */
++
+ /* Init mode state machine */
+ dev->state = 0;
+ dev->touch_in_progress = 0;
@@ -1104,6 +1148,7 @@
+ tsc2102_request_alloc(&tsc, &tsc.req_pressure, 0, 1, &spi_buffer);
+ tsc2102_request_alloc(&tsc, &tsc.req_stopadc, 1, 1, &spi_buffer);
+ tsc2102_request_alloc(&tsc, &tsc.req_mode, 1, 1, &spi_buffer);
++ tsc2102_request_alloc(&tsc, &tsc.req_click, 1, 1, &spi_buffer);
+
+ spin_lock_init(&tsc.lock);
+ mutex_init(&tsc.lock_sync);
Added: trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.23.1/090-alsa-create-imx-alsa_h.diff
===================================================================
--- trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.23.1/090-alsa-create-imx-alsa_h.diff (rev 0)
+++ trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.23.1/090-alsa-create-imx-alsa_h.diff 2008-07-18 19:52:49 UTC (rev 837)
@@ -0,0 +1,137 @@
+PATCH AUTOMATICALLY GENERATED
+DON'T EDIT IT OR YOUR MODIFICATIONS WILL BE LOST
+(Take a look at armadeus/target/linux/ directory)
+--- linux-2.6.23.1.original/include/asm-arm/arch-imx/imx-alsa.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.23.1.mod/include/asm-arm/arch-imx/imx-alsa.h 2008-07-18 11:51:12.000000000 +0200
+@@ -0,0 +1,131 @@
++/*
++ * linux/include/asm-arm/arch-imx/imx-alsa.h
++ *
++ * Alsa Driver for AIC23 and TSC2101 codecs on iMX platform boards.
++ *
++ * Copyright (C) 2008 Armadeus <nic...@ar...>
++ * Based on the omap alsa 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.
++ *
++ * 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.
++ *
++ */
++
++#ifndef __IMX_ALSA_H
++#define __IMX_ALSA_H
++
++#include <sound/driver.h>
++#include <asm/arch/dma.h>
++#include <asm/arch/imx-dma.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++/*#include <asm/arch/ssi.h>*/
++#include <linux/platform_device.h>
++/*
++ * 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 DMA_BUF_SIZE (1024 * 8)
++
++/*
++ * Buffer management for alsa and dma
++ */
++struct audio_stream {
++ char *id; /* identification string */
++ int stream_id; /* numeric identification */
++// int dma_dev; /* dma number of that device */
++ imx_dmach_t dma_dev; /* device identifier for DMA */
++ int dma_in_progress;
++ int offset; /* store start position of the last period in the alsa buffer */
++ int active:1; /* we are using this stream for transfer now */
++ int period; /* current transfer period */
++ int periods; /* current count of periods registered in the DMA engine */
++ spinlock_t dma_lock; /* for locking in DMA operations */
++ struct snd_pcm_substream *stream; /* the pcm stream */
++ int (*hw_start)(void); /* interface to start HW interface, e.g. McBSP */
++ int (*hw_stop)(void); /* interface to stop HW interface, e.g. McBSP */
++};
++
++
++/*
++ * Alsa card structure
++ */
++struct snd_card_imx_codec {
++ struct snd_card *card;
++ struct snd_pcm *pcm;
++ long samplerate;
++ struct audio_stream s[2]; /* playback & capture */
++};
++
++/* Codec specific information and function pointers.
++ * Codec imx-alsa-tsc2101.c
++ * is responsible for defining the function pointers.
++ */
++struct imx_alsa_codec_config {
++ char *name;
++ struct imx_ssi_reg_cfg *mcbsp_regs_alsa;
++ struct snd_pcm_hw_constraint_list *hw_constraints_rates;
++ struct snd_pcm_hardware *snd_imx_alsa_playback;
++ struct snd_pcm_hardware *snd_imx_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_imx_mixer(struct snd_card_imx_codec *);
++void snd_imx_init_mixer(void);
++
++#ifdef CONFIG_PM
++void snd_imx_suspend_mixer(void);
++void snd_imx_resume_mixer(void);
++#endif
++
++int snd_imx_alsa_post_probe(struct platform_device *pdev, struct imx_alsa_codec_config *config);
++int snd_imx_alsa_remove(struct platform_device *pdev);
++#ifdef CONFIG_PM
++int snd_imx_alsa_suspend(struct platform_device *pdev, pm_message_t state);
++int snd_imx_alsa_resume(struct platform_device *pdev);
++#else
++#define snd_imx_alsa_suspend NULL
++#define snd_imx_alsa_resume NULL
++#endif
++void snd_imx_alsa_dma_interrupt(int, void *);
++
++#endif
Added: trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.23.1/091-alsa-add-imx-ssi-tsc2102-driver.diff
===================================================================
--- trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.23.1/091-alsa-add-imx-ssi-tsc2102-driver.diff (rev 0)
+++ trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.23.1/091-alsa-add-imx-ssi-tsc2102-driver.diff 2008-07-18 19:52:49 UTC (rev 837)
@@ -0,0 +1,1442 @@
+PATCH AUTOMATICALLY GENERATED
+DON'T EDIT IT OR YOUR MODIFICATIONS WILL BE LOST
+(Take a look at armadeus/target/linux/ directory)
+--- linux-2.6.23.1.original/sound/arm/imx-alsa.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.23.1.mod/sound/arm/imx-alsa.c 2008-07-18 15:10:45.000000000 +0200
+@@ -0,0 +1,683 @@
++/*
++ * sound/arm/imx-alsa.c
++ *
++ * Alsa Driver for i.MXL / SSI
++ *
++ * Copyright (C) 2008 Armadeus Systems <nic...@ar...>
++ * <jul...@ar...>
++ * Based on omap-alsa.c
++ *
++ * 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.
++ *
++ */
++
++#include <linux/platform_device.h>
++#include <linux/spi/tsc2102.h>
++#ifdef CONFIG_PM
++#include <linux/pm.h>
++#endif
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++
++#include <asm/arch/imx-alsa.h>
++#include <linux/dma-mapping.h>
++#include <asm/arch/imx-dma.h>
++
++#define TSC_MASTER
++
++#define TRANSMIT_WM_LEVEL 8 // (in free places)
++#define SSI_DMA_BLR_TRANSMIT 16 // (3*2) // DMA Burst Length (in bytes)
++#define SSI_DMA_BLR 8
++
++MODULE_AUTHOR("Nicolas Colombain / Julien Boibessot - Armadeus Systems");
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("i.MX1/L driver for ALSA");
++MODULE_ALIAS("iMX_alsa_ssi");
++
++static struct snd_card_imx_codec *alsa_codec = NULL;
++static struct imx_alsa_codec_config *alsa_codec_config = NULL;
++
++
++/*
++ * HW interface start and stop helper functions
++ */
++static int audio_ifc_start(void)
++{
++ printk("Starting HW\n");
++ SSI_SCSR |= SSI_TE; /* TE enable */
++ return 0;
++}
++
++static int audio_ifc_stop(void)
++{
++ printk("Stopping HW\n");
++ SSI_SCSR &= ~SSI_TE; /* TE disable */
++ return 0;
++}
++
++#ifdef DEBUG
++static void dump_ssi_registers(void)
++{
++ printk("SSI_STX: 0x%x\n", SSI_STX);
++ printk("SSI_SRX: 0x%x\n", SSI_SRX);
++ printk("SSI_SCSR: 0x%x\n", SSI_SCSR);
++ printk("SSI_STCR: 0x%x\n", SSI_STCR);
++ printk("SSI_SRCR: 0x%x\n", SSI_SRCR);
++ printk("SSI_STCCR: 0x%x\n", SSI_STCCR);
++ printk("SSI_SRCCR: 0x%x\n", SSI_SRCCR);
++ printk("SSI_STSR: 0x%x\n", SSI_STSR);
++ printk("SSI_SFCSR: 0x%x\n", SSI_SFCSR);
++ printk("SSI_STR: 0x%x\n", SSI_STR);
++ printk("SSI_SOR: 0x%x\n", SSI_SOR);
++}
++#endif
++
++static void imx_alsa_audio_init(struct snd_card_imx_codec *imx_alsa)
++{
++ /* Setup DMA stuff */
++ imx_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].id = "Alsa imx out";
++ imx_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].stream_id =
++ SNDRV_PCM_STREAM_PLAYBACK;
++ imx_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].dma_dev =
++ IMX_DMA_CHANNELS-1;
++ imx_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].hw_start =
++ audio_ifc_start;
++ imx_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].hw_stop =
++ audio_ifc_stop;
++
++ imx_alsa->s[SNDRV_PCM_STREAM_CAPTURE].id = "Alsa imx in";
++ imx_alsa->s[SNDRV_PCM_STREAM_CAPTURE].stream_id =
++ SNDRV_PCM_STREAM_CAPTURE;
++ imx_alsa->s[SNDRV_PCM_STREAM_CAPTURE].dma_dev =
++ IMX_DMA_CHANNELS-2;
++ imx_alsa->s[SNDRV_PCM_STREAM_CAPTURE].hw_start =
++ audio_ifc_start;
++ imx_alsa->s[SNDRV_PCM_STREAM_CAPTURE].hw_stop =
++ audio_ifc_stop;
++}
++
++static void dma_err_handler(int channel, void *data, int errcode)
++{
++ printk("dma_err_handler %d %d\n",channel, errcode);
++
++ imx_dma_disable(channel);
++}
++
++/*
++ * DMA functions
++ * Depends on imx-alsa-dma.c functions and (imx) dma.c
++ *
++ */
++static int audio_dma_request(struct audio_stream *s,
++ void (*callback) (int, void *))
++{
++ int err=0;
++ ADEBUG();
++
++ err = imx_dma_request_by_prio(&(s->dma_dev), s->id, DMA_PRIO_HIGH);
++ if (err < 0) {
++ printk(KERN_ERR "Unable to grab dma channel: %d\n", s->dma_dev);
++ } else {
++ err = imx_dma_setup_handlers(s->dma_dev,
++ snd_imx_alsa_dma_interrupt,
++ dma_err_handler,
++ s);
++ if (err < 0)
++ printk(KERN_ERR "Unable to setup dma handler for channel %d\n", s->dma_dev);
++ }
++ printk("audio_dma_request done (%d)\n", s->dma_dev);
++ imx_dma_disable(s->dma_dev);
++ return err;
++}
++
++static int audio_dma_free(struct audio_stream *s)
++{
++ int err = 0;
++ ADEBUG();
++
++ imx_dma_free(s->dma_dev); // doesn't return an error if failed
++
++ 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)
++{
++ struct snd_pcm_substream *substream = s->stream;
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ unsigned int offset;
++ unsigned long flags;
++ 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 */
++ spin_unlock_irqrestore(&s->dma_lock, flags);
++
++ /* Now, the position related to the end of that period */
++// offset = bytes_to_frames(runtime, s->offset) + (runtime->period_size >> 1); //bytes_to_frames(runtime, count);
++ if( s->dma_in_progress ) {
++ offset = (runtime->period_size * (s->periods)) + (runtime->period_size >> 1);
++ if (offset >= runtime->buffer_size)
++ offset = runtime->period_size >> 1;
++ } else {
++ offset = (runtime->period_size * (s->periods));
++ if (offset >= runtime->buffer_size)
++ offset = 0;
++ }
++// printk("audio_get_dma_pos: %d (%d)\n", offset, bytes_to_frames(runtime, s->offset));
++ 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 */
++ imx_dma_disable(s->dma_dev);
++
++ 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)
++{
++ //#define IMX_SSI_BASE (0x00218000) /* a retrouver depuis le plateform data !!! */
++ struct snd_pcm_substream *substream = s->stream;
++ struct snd_pcm_runtime *runtime;
++ unsigned int dma_size;
++ unsigned int offset;
++ int ret;
++ dma_addr_t source;
++
++ runtime = substream->runtime;
++ imx_dma_disable(s->dma_dev);
++
++ if (s->active) {
++ dma_size = frames_to_bytes(runtime, runtime->period_size);
++ offset = dma_size * s->period;
++// printk("dma_size %d, off %d psize %d from 0x%x\n", dma_size, offset, runtime->period_size, (dma_addr_t)runtime->dma_area);
++ snd_assert(dma_size <= DMA_BUF_SIZE,);
++
++ source = dma_map_single(NULL,
++ runtime->dma_area + offset,
++ dma_size,
++ DMA_TO_DEVICE);
++ if (dma_mapping_error(source)) {
++ printk("Unable to map DMA buffer\n");
++ return;
++ }
++
++ ret = imx_dma_setup_single(s->dma_dev,
++ source, dma_size,
++ 0x00218000, DMA_MODE_WRITE);
++ if (ret) {
++ printk(KERN_ERR
++ "audio_process_dma: cannot queue DMA buffer (%i)\n", ret);
++ return;
++ }
++ imx_dma_enable(s->dma_dev);
++
++ s->dma_in_progress = 1;
++ s->period++;
++ s->period %= runtime->periods;
++// s->periods++;
++ s->offset = offset;
++
++ } else {
++ printk("stream not active\n");
++ }
++}
++
++/*
++ * This is called when dma IRQ occurs at the end of each transmited block
++ */
++void snd_imx_alsa_dma_interrupt(int channel, void *data)
++{
++ struct audio_stream *s = data;
++ struct snd_pcm_substream *substream = s->stream;
++ struct snd_pcm_runtime *runtime;
++// unsigned int dma_size;
++// unsigned int offset;
++// unsigned long temp;
++// int ret, i;
++// dma_addr_t source;
++
++ runtime = substream->runtime;
++
++ //printk("[%x] <dma %d>\n",IMX_TCN(IMX_TIM2_BASE), channel);
++/* // Reset DMA channel
++ CCR(channel) &= ~CCR_CEN; TO REMOVE
++ CCR(channel) |= CCR_CEN;*/
++ /*
++ * 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);
++ s->dma_in_progress = 0;
++ s->periods++;
++ s->periods %= runtime->periods;
++
++// if (s->periods > 0)
++// s->periods--;
++
++ /* Trig next DMA transfer */
++ audio_process_dma(s);
++ spin_unlock(&s->dma_lock);
++}
++
++/*
++ * Alsa section
++ * PCM settings and callbacks
++ */
++static int snd_imx_alsa_trigger(struct snd_pcm_substream * substream, int cmd)
++{
++ struct snd_card_imx_codec *chip =
++ snd_pcm_substream_chip(substream);
++ int stream_id = substream->pstr->stream;
++ struct audio_stream *s = &chip->s[stream_id];
++ int err = 0;
++ u32 temp;
++
++ /* 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 */
++ // clear SSI underrun
++ temp = SSI_SCSR;
++ SSI_STX = 0x0000;
++
++ s->active = 1;
++ s->dma_in_progress = 0;
++ audio_process_dma(s);
++ SSI_SCSR |= SSI_TE;
++ break;
++
++ case SNDRV_PCM_TRIGGER_STOP:
++ /* requested stream shutdown */
++ audio_stop_dma(s);
++ SSI_SCSR &= ~SSI_TE;
++ break;
++
++ default:
++ printk("Not supported (yet?) ALSA trigger: 0x%x\n", cmd);
++ err = -EINVAL;
++ break;
++ }
++ spin_unlock(&s->dma_lock);
++
++ return err;
++}
++
++static int snd_imx_alsa_prepare(struct snd_pcm_substream * substream)
++{
++ struct snd_card_imx_codec *chip = snd_pcm_substream_chip(substream);
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct audio_stream *s = &chip->s[substream->pstr->stream];
++
++ /* 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_imx_alsa_pointer(struct snd_pcm_substream *substream)
++{
++ struct snd_card_imx_codec *chip = snd_pcm_substream_chip(substream);
++
++ ADEBUG();
++ return audio_get_dma_pos(&chip->s[substream->pstr->stream]);
++}
++
++static int snd_card_imx_alsa_open(struct snd_pcm_substream * substream)
++{
++ struct snd_card_imx_codec *chip =
++ snd_pcm_substream_chip(substream);
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ int stream_id = substream->pstr->stream;
++ int err;
++
++ printk ("snd_card_imx_alsa_open substream 0x%x\n", substream);
++ 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_imx_alsa_playback);
++ }
++ else{
++ runtime->hw = *(alsa_codec_config->snd_imx_alsa_capture);
++ printk ("capture not supported\n");
++ }
++ if ((err = snd_pcm_hw_constraint_integer(runtime,
++ SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
++ return err;
++
++ printk ("playback supported %d\n",runtime->hw.rate_min);
++ if ((err = snd_pcm_hw_constraint_list(runtime,
++ 0,
++ SNDRV_PCM_HW_PARAM_RATE,
++ alsa_codec_config->hw_constraints_rates)) < 0)
++ return err;
++ printk("success\n");
++ return 0;
++}
++
++static int snd_card_imx_alsa_close(struct snd_pcm_substream * substream)
++{
++ struct snd_card_imx_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_imx_alsa_hw_params(struct snd_pcm_substream * substream,
++ struct snd_pcm_hw_params * hw_params)
++{
++ return snd_pcm_lib_malloc_pages(substream,
++ params_buffer_bytes(hw_params));
++}
++
++static int snd_imx_alsa_hw_free(struct snd_pcm_substream * substream)
++{
++ return snd_pcm_lib_free_pages(substream);
++}
++
++/* pcm operations */
++static struct snd_pcm_ops snd_card_imx_alsa_playback_ops = {
++ .open = snd_card_imx_alsa_open,
++ .close = snd_card_imx_alsa_close,
++ .ioctl = snd_pcm_lib_ioctl,
++ .hw_params = snd_imx_alsa_hw_params,
++ .hw_free = snd_imx_alsa_hw_free,
++ .prepare = snd_imx_alsa_prepare,
++ .trigger = snd_imx_alsa_trigger,
++ .pointer = snd_imx_alsa_pointer,
++};
++
++static struct snd_pcm_ops snd_card_imx_alsa_capture_ops = {
++ .open = snd_card_imx_alsa_open,
++ .close = snd_card_imx_alsa_close,
++ .ioctl = snd_pcm_lib_ioctl,
++ .hw_params = snd_imx_alsa_hw_params,
++ .hw_free = snd_imx_alsa_hw_free,
++ .prepare = snd_imx_alsa_prepare,
++ .trigger = snd_imx_alsa_trigger,
++ .pointer = snd_imx_alsa_pointer,
++};
++
++/*
++ * Alsa init and exit section
++ *
++ * Inits pcm alsa structures, allocate the alsa buffer, suspend, resume
++ */
++static int __init snd_card_imx_alsa_pcm(struct snd_card_imx_codec *imx_alsa,
++ int device)
++{
++ struct snd_pcm *pcm;
++ int err;
++
++ ADEBUG();
++ if ((err = snd_pcm_new(imx_alsa->card, "IMX 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),
++ 8 * 1024, 8 * 1024);
++
++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
++ &snd_card_imx_alsa_playback_ops);
++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
++ &snd_card_imx_alsa_capture_ops);
++ pcm->private_data = imx_alsa;
++ pcm->info_flags = 0;
++ strcpy(pcm->name, "imx alsa pcm");
++
++ imx_alsa_audio_init(imx_alsa);
++
++ /* request DMA channels */
++ audio_dma_request(&imx_alsa->s[SNDRV_PCM_STREAM_PLAYBACK],
++ snd_imx_alsa_dma_interrupt);
++ audio_dma_request(&imx_alsa->s[SNDRV_PCM_STREAM_CAPTURE],
++ snd_imx_alsa_dma_interrupt);
++
++ /* configure i.MXL DMA control register for given channels */
++ CCR(imx_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].dma_dev) =
++ CCR_DMOD_FIFO |
++ CCR_SMOD_LINEAR |
++ CCR_SSIZ_32 | CCR_DSIZ_16 |
++ CCR_REN;
++ CCR(imx_alsa->s[SNDRV_PCM_STREAM_CAPTURE].dma_dev) =
++ CCR_DMOD_LINEAR |
++ CCR_SMOD_FIFO |
++ CCR_SSIZ_16 | CCR_DSIZ_32 |
++ CCR_REN;
++ /* configure i.MXL DMA request channel */
++ RSSR(imx_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].dma_dev) = DMA_REQ_SSI_T;
++ RSSR(imx_alsa->s[SNDRV_PCM_STREAM_CAPTURE].dma_dev) = DMA_REQ_SSI_R;
++ /* configure DMA burst length per channel */
++ BLR(imx_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].dma_dev) = SSI_DMA_BLR_TRANSMIT;
++ BLR(imx_alsa->s[SNDRV_PCM_STREAM_CAPTURE].dma_dev) = SSI_DMA_BLR;
++
++ /* Reset SSI before configuring it */
++ SSI_SCSR &= ~SSI_EN;
++ SSI_SCSR |= SSI_EN;
++ /* SSI FIFO watermark */
++ SSI_SFCSR = SSI_RFWM(8) | SSI_TFWM(TRANSMIT_WM_LEVEL);
++#ifdef TSC_MASTER
++ /* Clocks */
++ SSI_STCCR = SSI_WL_16 | SSI_DC(0);
++ SSI_SRCCR = SSI_WL_16 | SSI_DC(0);
++ /* Control */
++ SSI_SCSR = SSI_I2S_SLAVE_MODE | SSI_SYN | SSI_EN;
++ /* Transmit config */
++ SSI_STCR = SSI_MAE | SSI_FEN | SSI_SCKP | SSI_FSI | SSI_EFS;
++#else
++ /* Clocks */
++ SSI_STCCR = 0x00006103;
++ SSI_SRCCR = 0x00006103;
++ /* Transmit config */
++ SSI_STCR = SSI_MAE | SSI_FEN | SSI_FDIR | SSI_DIR | SSI_SCKP | SSI_FSI | SSI_EFS;
++ /* Control */
++ SSI_SCSR = /*SSI_SYS_CLK_EN |*/ SSI_I2S_MASTER_MODE | SSI_SYN | /*SSI_TE |*/ SSI_EN;
++#endif // TSC_MASTER
++
++ imx_alsa->pcm = pcm;
++
++ return 0;
++}
++
++
++#ifdef CONFIG_PM
++/*
++ * Driver suspend/resume - calls alsa functions. Some hints from aaci.c
++ */
++int snd_imx_alsa_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ struct snd_card_imx_codec *chip;
++ struct snd_card *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_imx_suspend_mixer();
++ }
++ }
++ return 0;
++}
++
++int snd_imx_alsa_resume(struct platform_device *pdev)
++{
++ struct snd_card_imx_codec *chip;
++ struct snd_card *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_imx_resume_mixer();
++ }
++ }
++ return 0;
++}
++
++#endif /* CONFIG_PM */
++
++void snd_imx_alsa_free(struct snd_card * card)
++{
++ struct snd_card_imx_codec *chip = card->private_data;
++
++ /*
++ * 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 soundcard structure.
++ * Called by the probe method in codec after function pointers has been set.
++ */
++int snd_imx_alsa_post_probe(struct platform_device *pdev, struct imx_alsa_codec_config *config)
++{
++ int err = 0;
++ int def_rate;
++ struct snd_card *card;
++
++ alsa_codec_config = config;
++
++ alsa_codec_config->codec_clock_setup();
++ alsa_codec_config->codec_clock_on();
++
++ if (alsa_codec_config && alsa_codec_config->codec_configure_dev)
++ alsa_codec_config->codec_configure_dev();
++
++ alsa_codec_config->codec_clock_off();
++
++ /* create the soundcard */
++ card = snd_card_new(-1, "IMX-ALSA", 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_imx_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_imx_mixer(alsa_codec)) < 0)
++ goto nodev3;
++
++ /* PCM */
++ if ((err = snd_card_imx_alsa_pcm(alsa_codec, 0)) < 0)
++ goto nodev3;
++ strcpy(card->driver, "IMXALSA");
++ strcpy(card->shortname, "ARMADEUS IMX_ALSA");
++ sprintf(card->longname, "ARMADEUS IMX_ALSA");
++
++ snd_imx_init_mixer();
++ snd_card_set_dev(card, &pdev->dev);
++
++ /* Register the created soundcard */
++ 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:
++
++ return err;
++}
++
++int snd_imx_alsa_remove(struct platform_device *pdev)
++{
++ struct snd_card *card = platform_get_drvdata(pdev);
++ struct snd_card_imx_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;
++}
++
+PATCH AUTOMATICALLY GENERATED
+DON'T EDIT IT OR YOUR MODIFICATIONS WILL BE LOST
+(Take a look at armadeus/target/linux/ directory)
+--- linux-2.6.23.1.original/sound/arm/imx-alsa-dma.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.23.1.mod/sound/arm/imx-alsa-dma.h 2008-07-18 15:11:05.000000000 +0200
+@@ -0,0 +1,50 @@
++/*
++ * linux/sound/arm/imx/imx-alsa-dma.h
++ *
++ * Common audio DMA handling for the iMX processors
++ *
++ * Copyright (C) 2008 Nicolas Colombain <nic...@ar...>
++ *
++ * Copyright (C) 2006 Mika Laitio <la...@cc...>
++ *
++ * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
++ *
++ * Copyright (C) 2004 Texas Instruments, Inc.
++ *
++ * Copyright (C) 2000, 2001 Nicolas Pitre <ni...@ca...>
++ *
++ * This package is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
++ *
++ */
++
++#ifndef __IMX_AUDIO_ALSA_DMA_H
++#define __IMX_AUDIO_ALSA_DMA_H
++
++/************************** INCLUDES *************************************/
++
++#include <asm/arch/imx-alsa.h>
++
++/************************** GLOBAL DATA STRUCTURES *********************************/
++
++typedef void (*dma_callback_t) (int lch, u16 ch_status, void *data);
++
++/**************** ARCH SPECIFIC FUNCIONS *******************************************/
++
++void imx_clear_alsa_sound_dma(struct audio_stream * s);
++
++/*int imx_request_alsa_sound_dma(int device_id, const char *device_name,
++ void *data, int **channels);*/
++//int imx_free_alsa_sound_dma(void *data, int **channels);
++
++int imx_start_alsa_sound_dma(struct audio_stream *s, dma_addr_t dma_ptr, u_int dma_size);
++
++void imx_stop_alsa_sound_dma(struct audio_stream *s);
++
++#endif
++
+PATCH AUTOMATICALLY GENERATED
+DON'T EDIT IT OR YOUR MODIFICATIONS WILL BE LOST
+(Take a look at armadeus/target/linux/ directory)
+--- linux-2.6.23.1.original/sound/arm/imx-alsa-tsc2102.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.23.1.mod/sound/arm/imx-alsa-tsc2102.c 2008-07-18 15:11:34.000000000 +0200
+@@ -0,0 +1,345 @@
++/*
++ * sound/arm/imx-alsa-tsc2102.c
++ *
++ * Alsa codec driver for TSC2102 chip for iMXL platforms.
++ *
++ * Copyright (c) 2008 Jorasse <jo...@ar...>
++ * Code based on the TSC2101 ALSA driver for omap platforms.
++ * Copyright (c) 2006 Andrzej Zaborowski <ba...@za...>
++ * Code based on the TSC2101 ALSA 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 <linux/delay.h>
++#include <linux/soundcard.h>
++#include <linux/platform_device.h>
++#include <linux/clk.h>
++#include <linux/slab.h>
++#ifdef CONFIG_PM
++#include <linux/pm.h>
++#endif
++#include <linux/spi/tsc2102.h>
++
++#include <asm/arch/hardware.h>
++#include <asm/arch/imx-regs.h>
++#include <asm/arch/imx-alsa.h>
++
++#include "imx-alsa-tsc2102.h"
++
++static struct clk *tsc2102_bclk = 0;
++
++/*
++ * Hardware capabilities
++ */
++
++/* DAC sampling rates (BCLK = 12 MHz) */
++static unsigned int rates[] = {
++ 7350, 8000, 8820, 9600, 11025, 12000, 14700,
++ 16000, 22050, 24000, 29400, 32000, 44100, 48000,
++};
++
++static struct snd_pcm_hw_constraint_list tsc2102_hw_constraints_rates = {
++ .count = ARRAY_SIZE(rates),
++ .list = rates,
++ .mask = 0,
++};
++
++#define IMX_SSI_RATES \
++ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
++ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
++ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
++ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_KNOT)
++
++static struct snd_pcm_hardware tsc2102_snd_imx_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 = IMX_SSI_RATES,
++ .rate_min = 8000,
++ .rate_max = 48000,
++ .channels_min = 2,
++ .channels_max = 2,
++ .buffer_bytes_max = 32 * 1024,
++ .period_bytes_min = 64,
++ .period_bytes_max = 8 * 1024, //test
++ .periods_min = 2,
++ .periods_max = 255,
++ .fifo_size = 0,
++};
++
++#define DUMP_TSC2102_AUDIO_REGISTERS 1
++#ifdef DUMP_TSC2102_AUDIO_REGISTERS
++static void dump_tsc2102_audio_regs(void) {
++ printk("TSC2102_AUDIO1_CTRL = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_AUDIO1_CTRL));
++ printk("TSC2102_DAC_GAIN_CTRL = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_DAC_GAIN_CTRL));
++ printk("TSC2102_AUDIO2_CTRL = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_AUDIO2_CTRL));
++ printk("TSC2102_DAC_POWER_CTRL = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_DAC_POWER_CTRL));
++ printk("TSC2102_AUDIO3_CTRL = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_AUDIO3_CTRL));
++ printk("TSC2102_LCH_BASS_BOOST_N0 = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_LCH_BASS_BOOST_N0));
++ printk("TSC2102_LCH_BASS_BOOST_N1 = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_LCH_BASS_BOOST_N1));
++ printk("TSC2102_LCH_BASS_BOOST_N2 = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_LCH_BASS_BOOST_N2));
++ printk("TSC2102_LCH_BASS_BOOST_N3 = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_LCH_BASS_BOOST_N3));
++ printk("TSC2102_LCH_BASS_BOOST_N4 = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_LCH_BASS_BOOST_N4));
++ printk("TSC2102_LCH_BASS_BOOST_N5 = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_LCH_BASS_BOOST_N5));
++ printk("TSC2102_LCH_BASS_BOOST_D1 = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_LCH_BASS_BOOST_D1));
++ printk("TSC2102_LCH_BASS_BOOST_D2 = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_LCH_BASS_BOOST_D2));
++ printk("TSC2102_LCH_BASS_BOOST_D4 = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_LCH_BASS_BOOST_D4));
++ printk("TSC2102_LCH_BASS_BOOST_D5 = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_LCH_BASS_BOOST_D5));
++ printk("TSC2102_RCH_BASS_BOOST_N0 = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_RCH_BASS_BOOST_N0));
++ printk("TSC2102_RCH_BASS_BOOST_N1 = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_RCH_BASS_BOOST_N1));
++ printk("TSC2102_RCH_BASS_BOOST_N2 = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_RCH_BASS_BOOST_N2));
++ printk("TSC2102_RCH_BASS_BOOST_N3 = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_RCH_BASS_BOOST_N3));
++ printk("TSC2102_RCH_BASS_BOOST_N4 = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_RCH_BASS_BOOST_N4));
++ printk("TSC2102_RCH_BASS_BOOST_N5 = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_RCH_BASS_BOOST_N5));
++ printk("TSC2102_RCH_BASS_BOOST_D1 = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_RCH_BASS_BOOST_D1));
++ printk("TSC2102_RCH_BASS_BOOST_D2 = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_RCH_BASS_BOOST_D2));
++ printk("TSC2102_RCH_BASS_BOOST_D4 = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_RCH_BASS_BOOST_D4));
++ printk("TSC2102_RCH_BASS_BOOST_D5 = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_RCH_BASS_BOOST_D5));
++ printk("TSC2102_PLL1_CTRL = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_PLL1_CTRL));
++ printk("TSC2102_PLL2_CTRL = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_PLL2_CTRL));
++ printk("TSC2102_AUDIO4_CTRL = 0x%04x\n",
++ tsc2102_read_sync(TSC2102_AUDIO4_CTRL));
++}
++#endif
++
++/*
++ * ALSA operations according to board file
++ */
++
++static long current_rate = 0;
++
++/*
++ * Sample rate changing
++ */
++static void tsc2102_set_samplerate(long sample_rate)
++{
++ int clkgdv = 0;
++ u16 srgr1, srgr2;
++
++ if (sample_rate == current_rate)
++ return;
++ current_rate = 0;
++
++#if 0
++ /* Wait for any frames to complete */
++ udelay(125);
++#endif
++ /* Set the sample rate on the TSC */
++ if (tsc2102_set_rate(sample_rate)) {
++ printk("error setting TSC at %d Hz sample rate\n", sample_rate);
++ return;
++ }
++
++#ifdef TSC_MASTER
++
++#else
++ /* Set the sample rate on the i.MXL SSI */
++ printk("erreur 1");
++ imx_get_perclk3();
++ clkgdv = CODEC_CLOCK / (sample_rate * (DEFAULT_BITPERSAMPLE * 2 - 1));
++ if (clkgdv)
++ srgr1 = (FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv));
++ else
++ return;
++ xxxxx
++ /* Stereo Mode */
++ srgr2 = (CLKSM | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1));
++#endif
++
++ current_rate = sample_rate;
++ dump_tsc2102_audio_regs();
++}
++
++static void tsc2102_configure(void)
++{
++ tsc2102_dac_power(1);
++
++#ifdef TSC_MASTER
++ tsc2102_set_i2s_master(1);
++#else
++#error "to be implemented"
++ tsc2102_set_i2s_master(0);
++#endif
++}
++
++/*
++ * Do clock framework bclk search
++ */
++static void tsc2102_clock_setup(void)
++{
++ /*tsc2102_bclk = clk_get(0, "bclk"); no clock management*/
++}
++
++/*
++ * Do some sanity checks, set clock rate, start it.
++ */
++static int tsc2102_clock_on(void)
++{
++/* int err;
++no clock management*/
++
++#ifdef TSC_MASTER
++
++#endif
++
++/* if (clk_get_usecount(tsc2102_bclk) > 0 &&
++ clk_get_rate(tsc2102_bclk) != CODEC_CLOCK) {
++ printk(KERN_WARNING
++ "BCLK already in use at %d Hz. We change it to %d Hz\n",
++ (uint) clk_get_rate(tsc2102_bclk), CODEC_CLOCK);
++
++ err = clk_set_rate(tsc2102_bclk, CODEC_CLOCK);
++ if (err) {
++ printk(KERN_WARNING "Cannot set BCLK clock rate "
++ "for TSC2102 codec, error code = %d\n", err);
++ }
++ }
++
++ clk_enable(tsc2102_bclk);
++no clock management*/
++
++ /* Clock disabled when SSI disabled */
++ //?? SSI_SOR |= 0x40;
++
++ return 0;
++}
++
++/*
++ * Turn off the audio codec and then stop the clock.
++ */
++static int tsc2102_clock_off(void)
++{
++ DPRINTK("clock use count = %d\n", clk_get_usecount(tsc2102_bclk));
++
++/* clk_disable(tsc2102_bclk);
++no clock management */
++
++ /* Clock enabled when SSI disabled */
++ //?? SSI_SOR &= ~0x40;
++
++ return 0;
++}
++
++static int tsc2102_get_default_samplerate(void)
++{
++ return DEFAULT_SAMPLE_RATE;
++}
++
++#ifdef CONFIG_PM
++static int snd_imx_alsa_tsc2102_suspend(
++ struct platform_device *pdev, pm_message_t state)
++{
++ tsc2102_dac_power(0);
++ current_rate = 0;
++
++ return snd_imx_alsa_suspend(pdev, state);
++}
++
++static int snd_imx_alsa_tsc2102_resume(struct platform_device *pdev)
++{
++ tsc2102_dac_power(1);
++
++#ifdef TSC_MASTER
++ tsc2102_set_i2s_master(1);
++#else
++ tsc2102_set_i2s_master(0);
++#endif
++
++ return snd_imx_alsa_resume(pdev);
++}
++#endif // CONFIG_PM
++
++static int __init snd_imx_alsa_tsc2102_probe(struct platform_device *pdev)
++{
++ int ret;
++ struct imx_alsa_codec_config *codec_cfg = pdev->dev.platform_data;
++
++ if (codec_cfg) {
++ codec_cfg->hw_constraints_rates =
++ &tsc2102_hw_constraints_rates;
++ codec_cfg->snd_imx_alsa_playback =
++ &tsc2102_snd_imx_alsa_playback;
++ codec_cfg->codec_configure_dev = tsc2102_configure;
++ codec_cfg->codec_set_samplerate = tsc2102_set_samplerate;
++ codec_cfg->codec_clock_setup = tsc2102_clock_setup;
++ codec_cfg->codec_clock_on = tsc2102_clock_on;
++ codec_cfg->codec_clock_off = tsc2102_clock_off;
++ codec_cfg->get_default_samplerate =
++ tsc2102_get_default_samplerate;
++ ret = snd_imx_alsa_post_probe(pdev, codec_cfg);
++ } else
++ ret = -ENODEV;
++
++ return ret;
++}
++
++static int snd_imx_alsa_tsc2102_remove(struct platform_device *pdev)
++{
++ tsc2102_dac_power(0);
++
++ return snd_imx_alsa_remove(pdev);
++}
++
++static struct platform_driver imx_alsa_driver = {
++ .probe = snd_imx_alsa_tsc2102_probe,
++ .remove = snd_imx_alsa_tsc2102_remove,
++#ifdef CONFIG_PM
++ .suspend = snd_imx_alsa_tsc2102_suspend,
++ .resume = snd_imx_alsa_tsc2102_resume,
++#endif // CONFIG_PM
++ .driver = {
++ .name = "tsc2102-alsa",
++ },
++};
++
++static int __init imx_alsa_tsc2102_init(void)
++{
++ int err;
++
++ ADEBUG();
++ err = platform_driver_register(&imx_alsa_driver);
++
++ return err;
++}
++
++static void __exit imx_alsa_tsc2102_exit(void)
++{
++ ADEBUG();
++ platform_driver_unregister(&imx_alsa_driver);
++}
++
++module_init(imx_alsa_tsc2102_init);
++module_exit(imx_alsa_tsc2102_exit);
++
+PATCH AUTOMATICALLY GENERATED
+DON'T EDIT IT OR YOUR MODIFICATIONS WILL BE LOST
+(Take a look at armadeus/target/linux/ directory)
+--- linux-2.6.23.1.original/sound/arm/imx-alsa-tsc2102.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.23.1.mod/sound/arm/imx-alsa-tsc2102.h 2008-07-18 15:11:51.000000000 +0200
+@@ -0,0 +1,46 @@
++/*
++ * sound/arm/imx-alsa-tsc2102.h
++ *
++ * Alsa codec driver for TSC2102 chip for IMX1 platforms.
++ *
++ * Copyright (c) 2008 Jorasse <jo...@ar...>
++ * Code based on the TSC2101 ALSA driver for omap platforms.
++ * Copyright (c) 2006 Andrzej Zaborowski <ba...@za...>
++ * Code based on the TSC2101 ALSA 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.
++ */
++
++#ifndef IMX_ALSA_TSC2102_H_
++#define IMX_ALSA_TSC2102_H_
++
++#include <linux/types.h>
++
++/* Define to set the TSC as the master, otherwise slave */
++#define TSC_MASTER
++
++/*
++ * Audio related macros
++ */
++#ifndef DEFAULT_BITPERSAMPLE
++#define DEFAULT_BITPERSAMPLE 16
++#endif
++
++#define DEFAULT_SAMPLE_RATE 48000
++#define CODEC_CLOCK 16000000
++#define AUDIO_MCBSP OMAP_MCBSP1 /* to remove */
++
++/*
++ * ALSA mixer related macros
++ */
++#define OUTPUT_VOLUME_MIN 0x7f /* 1111111 = -63.5 dB */
++#define OUTPUT_VOLUME_MAX 0x00 /* 0000000 */
++#define OUTPUT_VOLUME_RANGE (OUTPUT_VOLUME_MIN - OUTPUT_VOLUME_MAX)
++
++#define DEFAULT_OUTPUT_VOLUME 90 /* Default output volume */
++
++#endif /* IMX_ALSA_TSC2102_H_ */
++
+PATCH AUTOMATICALLY GENERATED
+DON'T EDIT IT OR YOUR MODIFICATIONS WILL BE LOST
+(Take a look at armadeus/target/linux/ directory)
+--- linux-2.6.23.1.original/sound/arm/imx-alsa-tsc2102-mixer.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.23.1.mod/sound/arm/imx-alsa-tsc2102-mixer.c 2008-07-18 15:12:06.000000000 +0200
+@@ -0,0 +1,288 @@
++/*
++ * sound/arm/imx-alsa-tsc2102-mixer.c
++ *
++ * Alsa codec driver for TSC2102 chip for iMXL platforms.
++ *
++ * Copyright (c) 2008 Jorasse <jo...@ar...>
++ * Code based on the TSC2101 ALSA driver for omap platforms.
++ * Copyright (c) 2006 Andrzej Zaborowski <ba...@za...>
++ * Code based on the TSC2101 ALSA 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.
++ *
++ * 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.
++ */
++
++#include <linux/types.h>
++#include <linux/spi/tsc2102.h>
++
++#include <asm/arch/imx-alsa.h>
++
++#include <sound/driver.h>
++#include <sound/initval.h>
++#include <sound/control.h>
++
++#include "imx-alsa-tsc2102.h"
++
++static int vol[2], mute[2], filter[2];
++
++/*
++ * Converts the Alsa mixer volume (0 - 100) to actual Digital
++ * Gain Control (DGC) value that can be written or read from the
++ * TSC2102 registers.
++ *
++ * Note that the number "OUTPUT_VOLUME_MAX" is smaller than
++ * OUTPUT_VOLUME_MIN because DGC works as a volume decreaser. (The
++ * higher the value sent to DAC, the more the volume of controlled
++ * channel is decreased)
++ */
++static void set_dac_gain_stereo(int left_ch, int right_ch)
++{
++ int lch, rch;
++
++ if (left_ch > 100)
++ vol[0] = 100;
++ else if (left_ch < 0)
++ vol[0] = 0;
++ else
++ vol[0] = left_ch;
++ lch = OUTPUT_VOLUME_MIN - vol[0] *
++ (OUTPUT_VOLUME_MIN - OUTPUT_VOLUME_MAX) / 100;
++
++ if (right_ch > 100)
++ vol[1] = 100;
++ else if (right_ch < 0)
++ vol[1] = 0;
++ else
++ vol[1] = right_ch;
++ rch = OUTPUT_VOLUME_MIN - vol[1] *
++ (OUTPUT_VOLUME_MIN - OUTPUT_VOLUME_MAX) / 100;
++
++ tsc2102_set_volume(lch, rch);
++}
++
++void init_playback_targets(void)
++{
++ set_dac_gain_stereo(DEFAULT_OUTPUT_VOLUME, DEFAULT_OUTPUT_VOLUME);
++
++ /* Unmute */
++ tsc2102_set_mute(0, 0);
++
++ mute[0] = mute[1] = 0;
++ filter[0] = filter[1] = 0;
++}
++
++/*
++ * Initializes TSC 2102 and playback target.
++ */
++void snd_imx_init_mixer(void)
++{
++ FN_IN;
++
++ init_playback_targets();
++
++ FN_OUT(0);
++}
++
++static int __pcm_playback_volume_info(
++ struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
++{
++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
++ uinfo->count = 2;
++ uinfo->value.integer.min = 0;
++ uinfo->value.integer.max = 100;
++ return 0;
++}
++
++static int __pcm_playback_volume_get(
++ struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
++{
++ ucontrol->value.integer.value[0] = vol[0]; /* L */
++ ucontrol->value.integer.value[1] = vol[1]; /* R */
++
++ return 0;
++}
++
++static int __pcm_playback_volume_put(
++ struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
++{
++ set_dac_gain_stereo(
++ ucontrol->value.integer.value[0], /* L */
++ ucontrol->value.integer.value[1]); /* R */
++ return 1;
++}
++
++static int __pcm_playback_switch_info(
++ struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
++{
++ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
++ uinfo->count = 2;
++ uinfo->value.integer.min = 0;
++ uinfo->value.integer.max = 1;
++ return 0;
++}
++
++static int __pcm_playback_switch_get(
++ struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
++{
++ ucontrol->value.integer.value[0] = !mute[0]; /* L */
++ ucontrol->value.integer.value[1] = !mute[1]; /* R */
++
++ return 0;
++}
++
++static int __pcm_playback_switch_put(
++ struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
++{
++ mute[0] = (ucontrol->value.integer.value[0] == 0); /* L */
++ mute[1] = (ucontrol->value.integer.value[1] == 0); /* R */
++
++ tsc2102_set_mute(mute[0], mute[1]);
++ return 1;
++}
++
++static int __pcm_playback_deemphasis_info(
++ struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
++{
++ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
++ uinfo->count = 1;
++ uinfo->value.integer.min = 0;
++ uinfo->value.integer.max = 1;
++ return 0;
++}
++
++static int __pcm_playback_deemphasis_get(
++ struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
++{
++ ucontrol->value.integer.value[0] = filter[0];
++ return 0;
++}
++
++static int __pcm_playback_deemphasis_put(
++ struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
++{
++ filter[0] = (ucontrol->value.integer.value[0] > 0);
++
++ tsc2102_set_deemphasis(filter[0]);
++ return 1;
++}
++
++static int __pcm_playback_bassboost_info(
++ struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
++{
++ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
++ uinfo->count = 1;
++ uinfo->value.integer.min = 0;
++ uinfo->value.integer.max = 1;
++ return 0;
++}
++
++static int __pcm_playback_bassboost_get(
++ struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
++{
++ ucontrol->value.integer.value[0] = filter[1];
++ return 0;
++}
++
++static int __pcm_playback_bassboost_put(
++ struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
++{
++ filter[1] = (ucontrol->value.integer.value[0] > 0);
++
++ tsc2102_set_bassboost(filter[1]);
++ return 1;
++}
++
++static struct snd_kcontrol_new tsc2102_control[] __devinitdata = {
++ {
++ .name = "Master Playback Volume",
++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++ .index = 0,
++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
++ .info = __pcm_playback_volume_info,
++ .get = __pcm_playback_volume_get,
++ .put = __pcm_playback_volume_put,
++ },
++ {
++ .name = "Master Playback Switch",
++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++ .index = 0,
++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
++ .info = __pcm_playback_switch_info,
++ .get = __pcm_playback_switch_get,
++ .put = __pcm_playback_switch_put,
++ },
++ {
++ .name = "De-emphasis Filter Switch",
++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++ .index = 0,
++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
++ .info = __pcm_playback_deemphasis_info,
++ .get = __pcm_playback_deemphasis_get,
++ .put = __pcm_playback_deemphasis_put,
++ },
++ {
++ .name = "Bass-boost Filter Switch",
++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++ .index = 0,
++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
++ .info = __pcm_playback_bassboost_info,
++ .get = __pcm_playback_bassboost_get,
++ .put = __pcm_playback_bassboost_put,
++ },
++};
++
++#ifdef CONFIG_PM
++void snd_imx_suspend_mixer(void)
++{
++ /* Nothing to do */
++}
++
++void snd_imx_resume_mixer(void)
++{
++ /* The chip was reset, restore the last used values */
++ set_dac_gain_stereo(vol[0], vol[1]);
++
++ tsc2102_set_mute(mute[0], mute[1]);
++ tsc2102_set_deemphasis(filter[0]);
++ tsc2102_set_bassboost(filter[1]);
++}
++#endif
++
++int snd_imx_mixer(struct snd_card_imx_codec *tsc2102)
++{
++ int i, err;
++
++ if (!tsc2102)
++ return -EINVAL;
++
++ for (i = 0; i < ARRAY_SIZE(tsc2102_control); i ++) {
++ err = snd_ctl_add(tsc2102->card,
++ snd_ctl_new1(&tsc2102_control[i],
++ tsc2102->card));
++
++ if (err < 0)
++ return err;
++ }
++ return 0;
++}
++
++MODULE_DESCRIPTION("Interface driver for TI TSC2102 chips.");
++MODULE_LICENSE("GPL");
++
Added: trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.23.1/092-alsa-imx-ssi-tsc2102-integrate-driver-to-linux-build-system.diff
===================================================================
--- trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.23.1/092-alsa-imx-ssi-tsc2102-integrate-driver-to-linux-build-system.diff (rev 0)
+++ trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.23.1/092-alsa-imx-ssi-tsc2102-integrate-driver-to-linux-build-system.diff 2008-07-18 19:52:49 UTC (rev 837)
@@ -0,0 +1,29 @@
+--- linux-2.6.23.1/sound/arm/Kconfig 2007-10-12 18:43:44.000000000 +0200
++++ linux-2.6.23.1.alsa/sound/arm/Kconfig 2008-07-17 17:39:17.000000000 +0200
+@@ -33,4 +33,16 @@
+ Say Y or M if you want to support any AC97 codec attached to
+ the PXA2xx AC97 interface.
+
++config SND_IMX_TSC2102
++ tristate "i.MX1/L TSC2102 alsa driver"
++ depends on ARCH_IMX && SND
++ select SND_PCM
++ select SPI_TSC2102
++ help
++ Say Y here if you have an i.MX1/L platform board
++ and a TSC2102 audio chip.
++
++ To compile this driver as a module, choose M here: the module
++ will be called snd-imx-tsc2102.
++
+ endmenu
+--- linux-2.6.23.1/sound/arm/Makefile 2007-10-12 18:43:44.000000000 +0200
++++ linux-2.6.23.1.alsa/sound/arm/Makefile 2008-07-17 15:53:07.000000000 +0200
+@@ -13,3 +13,7 @@
+
+ obj-$(CONFIG_SND_PXA2XX_AC97) += snd-pxa2xx-ac97.o
+ snd-pxa2xx-ac97-objs := pxa2xx-ac97.o
++
++obj-$(CONFIG_SND_IMX_TSC2102) += snd-imx-alsa-tsc2102.o
++snd-imx-alsa-tsc2102-objs := imx-alsa.o imx-alsa-tsc2102.o imx-alsa-tsc2102-mixer.o
++
Added: trunk/buildroot/target/device/armadeus/linux...
[truncated message content] |