]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
SPI: TSC2102 OMAP audio driver
authorAndrzej Zaborowski <balrog@zabor.org>
Thu, 19 Oct 2006 13:46:39 +0000 (16:46 +0300)
committerTony Lindgren <tony@atomide.com>
Thu, 19 Oct 2006 13:46:39 +0000 (16:46 +0300)
ALSA driver for OMAP boards with a TSC2102 chip. It is based on the
TSC2101 and AIC23 drivers. The chip has only one audio playback output
and no inputs. It can play stereo sound with 16-bit samples (only) and
supports different sampling rates between 8 and 48 kHz. The patch also
includes an ALSA mixer. The mixer controlls two volume levels, for
left and right channels, each with 128 steps. It can also toggle the
analog de-emphasis filter and bass boost filter which are incorporated
in the chip.

In addition to that the chip has an equaliser filter, but the mixer
doesn't use it because the coefficients of the filter are difficult to
map to other values used in normal equalisers. Maybe they should have
constant values per board.

Signed-off-by: Andrzej Zaborowski <balrog@zabor.org>
sound/arm/Kconfig
sound/arm/omap/Makefile
sound/arm/omap/omap-alsa-tsc2102-mixer.c [new file with mode: 0644]
sound/arm/omap/omap-alsa-tsc2102.c [new file with mode: 0644]
sound/arm/omap/omap-alsa-tsc2102.h [new file with mode: 0644]

index 6d7b7e7518241c4803bf497a65245b9b72b088f4..8f3467378e77134bbe615e4b56253ecad5a8954a 100644 (file)
@@ -63,4 +63,16 @@ config SND_OMAP_TSC2101
          To compile this driver as a module, choose M here: the module
          will be called snd-omap-tsc2101.
 
+config SND_OMAP_TSC2102
+       tristate "OMAP TSC2102 alsa driver"
+       depends on ARCH_OMAP && SND
+       select SND_PCM
+       select TSC2102
+       help
+         Say Y here if you have an OMAP platform board
+         and want to use its TSC2102 audio chip.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-omap-tsc2102.
+
 endmenu
index 354c7b4aabf769f59a4fd877a33045f2511b15f0..64e341f6243449280879e51f95ba69573ac59a36 100644 (file)
@@ -7,3 +7,6 @@ snd-omap-alsa-aic23-objs := omap-alsa.o omap-alsa-dma.o omap-alsa-aic23.o omap-a
 
 obj-$(CONFIG_SND_OMAP_TSC2101) += snd-omap-alsa-tsc2101.o
 snd-omap-alsa-tsc2101-objs := omap-alsa.o omap-alsa-dma.o omap-alsa-tsc2101.o omap-alsa-tsc2101-mixer.o
+
+obj-$(CONFIG_SND_OMAP_TSC2102) += snd-omap-alsa-tsc2102.o
+snd-omap-alsa-tsc2102-objs := omap-alsa.o omap-alsa-dma.o omap-alsa-tsc2102.o omap-alsa-tsc2102-mixer.o
diff --git a/sound/arm/omap/omap-alsa-tsc2102-mixer.c b/sound/arm/omap/omap-alsa-tsc2102-mixer.c
new file mode 100644 (file)
index 0000000..2e06d70
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * sound/arm/omap/omap-alsa-tsc2102-mixer.c
+ *
+ * Alsa mixer driver for TSC2102 chip for OMAP platforms.
+ *
+ * Copyright (c) 2006 Andrzej Zaborowski  <balrog@zabor.org>
+ * 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/omap-alsa.h>
+
+#include <sound/driver.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+
+#include "omap-alsa-tsc2102.h"
+#include "omap-alsa-dma.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_omap_init_mixer(void)
+{
+       FN_IN;
+
+       init_playback_targets();
+
+       FN_OUT(0);
+}
+
+static int __pcm_playback_volume_info(
+               snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *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(
+               snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *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(
+               snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *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(
+               snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *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(
+               snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *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(
+               snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *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(
+               snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *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(
+               snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ucontrol->value.integer.value[0] = filter[0];
+       return 0;
+}
+
+static int __pcm_playback_deemphasis_put(
+               snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+       filter[0] = (ucontrol->value.integer.value[0] > 0);
+
+       tsc2102_set_deemphasis(filter[0]);
+       return 1;
+}
+
+static int __pcm_playback_bassboost_info(
+               snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *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(
+               snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ucontrol->value.integer.value[0] = filter[1];
+       return 0;
+}
+
+static int __pcm_playback_bassboost_put(
+               snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+       filter[1] = (ucontrol->value.integer.value[0] > 0);
+
+       tsc2102_set_bassboost(filter[1]);
+       return 1;
+}
+
+static snd_kcontrol_new_t 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_omap_suspend_mixer(void)
+{
+       /* Nothing to do */
+}
+
+void snd_omap_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_omap_mixer(struct snd_card_omap_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;
+}
diff --git a/sound/arm/omap/omap-alsa-tsc2102.c b/sound/arm/omap/omap-alsa-tsc2102.c
new file mode 100644 (file)
index 0000000..41e2d07
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ * sound/arm/omap/omap-alsa-tsc2102.c
+ * 
+ * Alsa codec driver for TSC2102 chip for OMAP platforms.
+ *
+ * Copyright (c) 2006 Andrzej Zaborowski  <balrog@zabor.org>
+ * 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/module.h>
+#include <linux/spi/tsc2102.h>
+
+#include <asm/arch/mcbsp.h>
+#include <asm/arch/dma.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/omap-alsa.h>
+
+#include "omap-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 snd_pcm_hw_constraint_list_t tsc2102_hw_constraints_rates = {
+       .count = ARRAY_SIZE(rates),
+       .list = rates,
+       .mask = 0,
+};
+
+static snd_pcm_hardware_t tsc2102_snd_omap_alsa_playback = {
+       .info                   = SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_MMAP_VALID,
+       .formats                = SNDRV_PCM_FMTBIT_S16_LE,
+       .rates                  = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_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,
+       .rate_min               = 7350,
+       .rate_max               = 48000,
+       .channels_min           = 2,
+       .channels_max           = 2,
+       .buffer_bytes_max       = 128 * 1024,
+       .period_bytes_min       = 32,
+       .period_bytes_max       = 8 * 1024,
+       .periods_min            = 16,
+       .periods_max            = 255,
+       .fifo_size              = 0,
+};
+
+#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_AUDIO_CTRL_3));
+       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 (tsc2102_set_rate(sample_rate))
+               return;
+
+       /* Set the sample rate */
+#ifndef TSC_MASTER
+       clkgdv = CODEC_CLOCK / (sample_rate * (DEFAULT_BITPERSAMPLE * 2 - 1));
+       if (clkgdv)
+               srgr1 = (FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv));
+       else
+               return;
+
+       /* Stereo Mode */
+       srgr2 = CLKSM | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1);
+#else
+       srgr1 = FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv);
+       srgr2 = GSYNC | CLKSP | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1);
+#endif
+       OMAP_MCBSP_WRITE(OMAP1510_MCBSP1_BASE, SRGR2, srgr2);
+       OMAP_MCBSP_WRITE(OMAP1510_MCBSP1_BASE, SRGR1, srgr1);
+       current_rate = sample_rate;
+}
+
+static void tsc2102_configure(void)
+{
+       tsc2102_dac_power(1);
+
+#ifdef TSC_MASTER
+       tsc2102_set_i2s_master(1);
+#else
+       tsc2102_set_i2s_master(0);
+#endif
+}
+
+/*
+ * Omap McBSP clock and Power Management configuration
+ *  
+ * Here we have some functions that allow clock to be enabled and
+ * disabled only when needed.  Besides doing clock configuration
+ * they allow turn audio on and off when necessary.
+ */
+
+/*
+ * Do clock framework bclk search
+ */
+static void tsc2102_clock_setup(void)
+{
+       tsc2102_bclk = clk_get(0, "bclk");
+}
+
+/*
+ * Do some sanity checks, set clock rate, start it.
+ */
+static int tsc2102_clock_on(void)
+{
+       int err;
+
+       if (clk_get_usecount(tsc2102_bclk) > 0 &&
+                       clk_get_rate(tsc2102_bclk) != CODEC_CLOCK) {
+               /* BCLK is already in use */
+               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);
+       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);
+       return 0;
+}
+
+static int tsc2102_get_default_samplerate(void)
+{
+       return DEFAULT_SAMPLE_RATE;
+}
+
+static int snd_omap_alsa_tsc2102_suspend(
+               struct platform_device *pdev, pm_message_t state)
+{
+       tsc2102_dac_power(0);
+       current_rate = 0;
+
+       return snd_omap_alsa_suspend(pdev, state);
+}
+
+static int snd_omap_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_omap_alsa_resume(pdev);
+}
+
+static int __init snd_omap_alsa_tsc2102_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct omap_alsa_codec_config *codec_cfg = pdev->dev.platform_data;
+
+       if (codec_cfg) {
+               codec_cfg->hw_constraints_rates =
+                       &tsc2102_hw_constraints_rates;
+               codec_cfg->snd_omap_alsa_playback =
+                       &tsc2102_snd_omap_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_omap_alsa_post_probe(pdev, codec_cfg);
+       } else
+               ret = -ENODEV;
+
+       return ret;
+}
+
+static int snd_omap_alsa_tsc2102_remove(struct platform_device *pdev)
+{
+       tsc2102_dac_power(0);
+
+       return snd_omap_alsa_remove(pdev);
+}
+
+static struct platform_driver omap_alsa_driver = {
+       .probe          = snd_omap_alsa_tsc2102_probe,
+       .remove         = snd_omap_alsa_tsc2102_remove,
+       .suspend        = snd_omap_alsa_tsc2102_suspend,
+       .resume         = snd_omap_alsa_tsc2102_resume,
+       .driver         = {
+               .name   = "tsc2102-alsa",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init omap_alsa_tsc2102_init(void)
+{
+       int err;
+
+       ADEBUG();
+       err = platform_driver_register(&omap_alsa_driver);
+
+       return err;
+}
+
+static void __exit omap_alsa_tsc2102_exit(void)
+{
+       ADEBUG();
+       platform_driver_unregister(&omap_alsa_driver);
+}
+
+module_init(omap_alsa_tsc2102_init);
+module_exit(omap_alsa_tsc2102_exit);
diff --git a/sound/arm/omap/omap-alsa-tsc2102.h b/sound/arm/omap/omap-alsa-tsc2102.h
new file mode 100644 (file)
index 0000000..c4b34f8
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * sound/arm/omap/omap-alsa-tsc2102.h
+ * 
+ * Alsa codec driver for TSC2102 chip for OMAP platforms.
+ *
+ * Copyright (c) 2006 Andrzej Zaborowski  <balrog@zabor.org>
+ * 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 OMAP_ALSA_TSC2102_H_
+#define OMAP_ALSA_TSC2102_H_
+
+/* Define to set the tsc as the master w.r.t McBSP */
+#define TSC_MASTER
+
+/*
+ * Audio related macros
+ */
+#ifndef DEFAULT_BITPERSAMPLE
+#define DEFAULT_BITPERSAMPLE           16
+#endif
+
+#define DEFAULT_SAMPLE_RATE            44100
+#define CODEC_CLOCK                    12000000
+#define AUDIO_MCBSP                    OMAP_MCBSP1
+
+/*
+ * 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 /* OMAP_ALSA_TSC2102_H_ */