Re: sx1 mixer support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi!

> Pavel Machek wrote:
> > 
> > (I think this should go in through omap tree, because sx1 support in
> > mainline is nonexistent for now, but...)
> 
> Yes, it's fine with me.
> 
> > --- /dev/null
> > +++ b/sound/arm/omap/omap-alsa-sx1-mixer.c
> (snip)
> > +static int current_volume 		= 0;	/* current volume, we cant read it */
> > +static int current_fm_volume 		= 0;	/* current FM radio volume, we cant read it */
> 
> Zero-initializations can be stripped.  (And lines seem still too long
> :)

Fixed.

> > +int set_mixer_volume(int mixer_vol)
> 
> Missing static?  Also there are many global functions around here.
> 
> > +{
> > +	/* FIXME: Alsa has mixer_vol in 0-100 range, while SX1 needs 0-9 range */
> 
> An incorrect information.  ALSA native mixers can use any value ranges
> while OSS mixers require 0-100 range.

Aha, I had this fixed but not commited. Sorry.

> > +/* ---------------------------------------------------------------------------------------- */
> > +static int __pcm_playback_target_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
> 
> What is the reason to use __ prefix?

I guess Vladimir liked it, fixed.

> Don't use snd_xxx_t typedefs any more.  They are obsolete (and will be
> removed in 2.6.20 tree).

Fixed.

> Also, the line is too long (ditto for other callbacks).

Yep, fixed.

> > +static int __pcm_playback_target_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
> > +{
> > +	int ret_val = 0;
> > +	int cur_val = ucontrol->value.integer.value[0];
> > +
> > +	if ((cur_val >= 0) &&
> > +	    (cur_val < PLAYBACK_TARGET_COUNT) &&
> > +	    (cur_val != current_playback_target)) {
> > +		if (cur_val == PLAYBACK_TARGET_LOUDSPEAKER) {
> > +			set_record_source(REC_SRC_SINGLE_ENDED_MICIN_HED);
> > +			set_loudspeaker_to_playback_target();
> > + 		}
> > +		else if (cur_val == PLAYBACK_TARGET_HEADPHONE) {
> 
> '}' ane else should be in the same line.

Ok.

> > +/*
> > + * Alsa mixer interface function for getting the volume read from the SX1 in a
> > + * 0-100 alsa mixer format.
> > + */
> 
> I don't think this comment is correct...

Hmm, plus it is completely useless. Removed.

> > +static int __pcm_playback_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
> > +{
> > +	ucontrol->value.integer.value[0] = current_volume;
> > +	return 0;
> > +}
> > +
> > +static int __pcm_playback_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
> > +{
> > +	return set_mixer_volume(ucontrol->value.integer.value[0]);
> 
> The put callback is supposed to return the following values:
> 
> 0 = the value isn't changed
> 1 = the value is changed
> netagive value = error
> 
> The return of 1 is not strictly needed in practice, but it's better to
> behave in a correct manner.

Hmm, that leads to some quite ugly code, but ok.

> > --- /dev/null
> > +++ b/sound/arm/omap/omap-alsa-sx1.c
> (snip)
> > +/* Send IPC message to sound server */
> > +extern int cn_sx1snd_send(unsigned int cmd, unsigned int arg1, unsigned int arg2)
> > +{
> > +	struct cn_msg *m;
> > +	unsigned short data[3];
> > +	int err;
> > +
> > +	m = kzalloc(sizeof(*m) + sizeof(data), GFP_ATOMIC);
> 
> Better to use gfp_any() here, too?
> (Or is it really too big for stack?)

Let's go with gfp_any() for now.

> > +/*
> > + * Defines codec specific functions pointers that can be used from the
> > + * common omap-alsa base driver for all omap codecs.
> > + */
> > +void egold_configure(void);
> > +void egold_set_samplerate(long rate);
> > +void egold_clock_setup(void);
> > +int egold_clock_on(void);
> > +int egold_clock_off(void);
> > +int egold_get_default_samplerate(void);
> 
> Do they need to be exported?  I see them only in a single file as
> callbacks...

Apparently they do not. Fixed.

Thanks for all the comments!

---

From: Vladimir Ananiev <[email protected]>

Add mixer support for Siemens SX1. As SX1 is an cellphone, and this
allows voice calls, it is quite important.

Signed-off-by: Pavel Machek <[email protected]>

diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig
index 8f34673..8c035b0 100644
--- a/sound/arm/Kconfig
+++ b/sound/arm/Kconfig
@@ -63,6 +63,17 @@ config SND_OMAP_TSC2101
  	  To compile this driver as a module, choose M here: the module
  	  will be called snd-omap-tsc2101.
 
+config SND_SX1
+ 	tristate "Siemens SX1 Egold alsa driver"
+ 	depends on ARCH_OMAP && SND
+ 	select SND_PCM
+	select OMAP_MCBSP
+ 	help
+ 	  Say Y here if you have a OMAP310 based Siemens SX1.
+ 
+ 	  To compile this driver as a module, choose M here: the module
+ 	  will be called snd-omap-sx1.
+
 config SND_OMAP_TSC2102
  	tristate "OMAP TSC2102 alsa driver"
  	depends on ARCH_OMAP && SND
diff --git a/sound/arm/omap/Makefile b/sound/arm/omap/Makefile
index 64e341f..c6bebac 100644
--- a/sound/arm/omap/Makefile
+++ b/sound/arm/omap/Makefile
@@ -10,3 +10,6 @@ snd-omap-alsa-tsc2101-objs := omap-alsa.
 
 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
+
+obj-$(CONFIG_SND_SX1) += snd-omap-alsa-sx1.o
+snd-omap-alsa-sx1-objs := omap-alsa.o omap-alsa-dma.o omap-alsa-sx1.o omap-alsa-sx1-mixer.o
diff --git a/sound/arm/omap/omap-alsa-sx1-mixer.c b/sound/arm/omap/omap-alsa-sx1-mixer.c
new file mode 100644
index 0000000..d826f26
--- /dev/null
+++ b/sound/arm/omap/omap-alsa-sx1-mixer.c
@@ -0,0 +1,470 @@
+/*
+ * sound/arm/omap/omap-alsa-sx1-mixer.c
+ *
+ * Alsa codec Driver for Siemens SX1 board.
+ * based on omap-alsa-tsc2101-mixer.c
+ *
+ *  Copyright (C) 2006 Vladimir Ananiev (vovan888 at gmail com)
+ *
+ * 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.
+ *
+ * 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 "omap-alsa-sx1.h"
+#include "omap-alsa-sx1-mixer.h"
+
+#include <linux/types.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+
+static int current_playback_target	= PLAYBACK_TARGET_LOUDSPEAKER;
+static int current_rec_src 		= REC_SRC_SINGLE_ENDED_MICIN_HED;
+static int current_volume;	/* current volume, we cant read it */
+static int current_fm_volume;	/* current FM radio volume, we cant read it */
+
+/* 
+ * Select SX1 recording source.
+ */
+static void set_record_source(int val)
+{
+	/* TODO Recording is done on McBSP2 and Mic only */
+	current_rec_src	= val;
+}
+
+static int set_mixer_volume(int mixer_vol)
+{
+	int ret, i;
+	if ((mixer_vol < 0) || (mixer_vol > 9)) {
+		printk(KERN_ERR "Trying a bad mixer volume (%d)!\n", mixer_vol);
+		return -EPERM;
+	}
+	ret = (current_volume != mixer_vol);
+	current_volume = mixer_vol;	/* set current volume, we cant read it */
+
+	i = cn_sx1snd_send(DAC_VOLUME_UPDATE, mixer_vol, 0);
+	if (i)
+		return i;
+	return ret;
+}
+
+static void set_loudspeaker_to_playback_target(void)
+{
+	/* TODO */
+	cn_sx1snd_send(DAC_SETAUDIODEVICE, SX1_DEVICE_SPEAKER, 0);
+
+	current_playback_target	= PLAYBACK_TARGET_LOUDSPEAKER;
+}
+
+static void set_headphone_to_playback_target(void)
+{
+	/* TODO */
+	cn_sx1snd_send(DAC_SETAUDIODEVICE, SX1_DEVICE_HEADPHONE, 0);
+
+	current_playback_target	= PLAYBACK_TARGET_HEADPHONE;
+}
+
+static void set_telephone_to_playback_target(void)
+{
+	/* TODO */
+	cn_sx1snd_send(DAC_SETAUDIODEVICE, SX1_DEVICE_PHONE, 0);
+
+	current_playback_target	= PLAYBACK_TARGET_CELLPHONE;
+}
+
+static void set_telephone_to_record_source(void)
+{
+	cn_sx1snd_send(DAC_SETAUDIODEVICE, SX1_DEVICE_PHONE, 0);
+}
+
+static void init_playback_targets(void)
+{
+	set_loudspeaker_to_playback_target();
+	set_mixer_volume(DEFAULT_OUTPUT_VOLUME);
+}
+
+/*
+ * Initializes SX1 record source (to mic) and playback target (to loudspeaker)
+ */
+void snd_omap_init_mixer(void)
+{
+	/* Select headset to record source */
+	set_record_source(REC_SRC_SINGLE_ENDED_MICIN_HED);
+	/* Init loudspeaker as a default playback target*/
+	init_playback_targets();
+}
+
+/* -------------------------------------------------------------------------- */
+static int pcm_playback_target_info(struct snd_kcontrol *kcontrol, 
+				    struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[PLAYBACK_TARGET_COUNT] = {
+        	"Loudspeaker", "Headphone", "Cellphone"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = PLAYBACK_TARGET_COUNT;
+	if (uinfo->value.enumerated.item > PLAYBACK_TARGET_COUNT - 1) {
+        	uinfo->value.enumerated.item = PLAYBACK_TARGET_COUNT - 1;
+	}
+	strcpy(uinfo->value.enumerated.name, 
+	       texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int pcm_playback_target_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = current_playback_target;
+	return 0;
+}
+
+static int pcm_playback_target_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int ret_val = 0;
+	int cur_val = ucontrol->value.integer.value[0];
+
+	if ((cur_val >= 0) &&
+	    (cur_val < PLAYBACK_TARGET_COUNT) &&
+	    (cur_val != current_playback_target)) {
+		if (cur_val == PLAYBACK_TARGET_LOUDSPEAKER) {
+			set_record_source(REC_SRC_SINGLE_ENDED_MICIN_HED);
+			set_loudspeaker_to_playback_target();
+ 		} else if (cur_val == PLAYBACK_TARGET_HEADPHONE) {
+			set_record_source(REC_SRC_SINGLE_ENDED_MICIN_HND);
+ 			set_headphone_to_playback_target();
+ 		} else if (cur_val == PLAYBACK_TARGET_CELLPHONE) {
+			set_telephone_to_record_source();
+			set_telephone_to_playback_target();
+		}
+		ret_val	= 1;
+	}
+	return ret_val;
+}
+
+/*-----------------------------------------------------------*/
+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			= 1;
+	uinfo->value.integer.min	= 0;
+	uinfo->value.integer.max	= 9;
+	return 0;
+}
+
+static int pcm_playback_volume_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = current_volume;
+	return 0;
+}
+
+static int pcm_playback_volume_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	return set_mixer_volume(ucontrol->value.integer.value[0]);
+}
+
+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			= 1;
+	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] = 1;
+	return 0;
+}
+
+static int pcm_playback_switch_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	return 0;
+}
+
+/* ----------------------------------------------------------- */
+
+static int headset_playback_volume_info(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	= 9;
+	return 0;
+}
+
+static int headset_playback_volume_get(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0]	= current_volume;
+	return 0;
+}
+
+static int headset_playback_volume_put(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	return set_mixer_volume(ucontrol->value.integer.value[0]);
+}
+
+static int headset_playback_switch_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 headset_playback_switch_get(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = 1;
+	return 0;
+}
+
+static int headset_playback_switch_put(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	/* mute/unmute headset */
+#if 0
+	return adc_pga_unmute_control(ucontrol->value.integer.value[0],
+				TSC2101_HEADSET_GAIN_CTRL,
+				15);
+#endif
+	return 0;
+}
+/* ----------------------------------------------------------- */
+static int fmradio_playback_volume_info(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	= 9;
+	return 0;
+}
+
+static int fmradio_playback_volume_get(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = current_fm_volume;
+	return 0;
+}
+
+static int fmradio_playback_volume_put(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	int ret = current_fm_volume != ucontrol->value.integer.value[0];
+	int i;
+	current_fm_volume = ucontrol->value.integer.value[0];
+	i = cn_sx1snd_send(DAC_FMRADIO_OPEN, current_fm_volume, 0);
+	if (i)
+		return i;
+	return ret;
+}
+
+static int fmradio_playback_switch_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 fmradio_playback_switch_get(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = 1;
+	return 0;
+}
+
+static int fmradio_playback_switch_put(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	/* mute/unmute FM radio */
+	if (ucontrol->value.integer.value[0])
+		cn_sx1snd_send(DAC_FMRADIO_OPEN, current_fm_volume, 0);
+	else
+		cn_sx1snd_send(DAC_FMRADIO_CLOSE, 0, 0);
+
+	return 0;
+}
+/* ----------------------------------------------------------- */
+static int cellphone_input_switch_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 cellphone_input_switch_get(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = 1;
+	return 0;
+}
+
+static int cellphone_input_switch_put(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+#if 0
+	return adc_pga_unmute_control(ucontrol->value.integer.value[0],
+				TSC2101_BUZZER_GAIN_CTRL, 15);
+#endif
+	return 0;
+}
+/* ----------------------------------------------------------- */
+
+static int buzzer_input_switch_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 buzzer_input_switch_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = 1;
+	return 0;
+}
+
+static int buzzer_input_switch_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+#if 0
+	return adc_pga_unmute_control(ucontrol->value.integer.value[0],
+				TSC2101_BUZZER_GAIN_CTRL, 6);
+#endif
+	return 0;
+}
+/*-----------------------------------------------------------*/
+
+static struct snd_kcontrol_new egold_control[] __devinitdata = {
+	{
+		.name  = "Playback Playback Route",
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.index = 0,
+		.access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info  = pcm_playback_target_info,
+		.get   = pcm_playback_target_get,
+		.put   = pcm_playback_target_put,
+	}, {
+		.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  = "Headset Playback Volume",
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.index = 1,
+		.access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info  = headset_playback_volume_info,
+		.get   = headset_playback_volume_get,
+		.put   = headset_playback_volume_put,
+	}, {
+		.name  = "Headset Playback Switch",
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.index = 1,
+		.access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info  = headset_playback_switch_info,
+		.get   = headset_playback_switch_get,
+		.put   = headset_playback_switch_put,
+	}, {
+		.name  = "FM Playback Volume",
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.index = 2,
+		.access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info  = fmradio_playback_volume_info,
+		.get   = fmradio_playback_volume_get,
+		.put   = fmradio_playback_volume_put,
+	}, {
+		.name  = "FM Playback Switch",
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.index = 2,
+		.access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info  = fmradio_playback_switch_info,
+		.get   = fmradio_playback_switch_get,
+		.put   = fmradio_playback_switch_put,
+	}, {
+		.name  = "Cellphone Input Switch",
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.index = 0,
+		.access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info  = cellphone_input_switch_info,
+		.get   = cellphone_input_switch_get,
+		.put   = cellphone_input_switch_put,
+	}, {
+		.name  = "Buzzer Input Switch",
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.index = 0,
+		.access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info  = buzzer_input_switch_info,
+		.get   = buzzer_input_switch_get,
+		.put   = buzzer_input_switch_put,
+	}
+};
+
+#ifdef CONFIG_PM
+void snd_omap_suspend_mixer(void)
+{
+}
+
+void snd_omap_resume_mixer(void)
+{
+	snd_omap_init_mixer();
+}
+#endif
+
+int snd_omap_mixer(struct snd_card_omap_codec *egold)
+{
+	int i = 0;
+	int err = 0;
+
+	if (!egold)
+		return -EINVAL;
+
+	for (i=0; i < ARRAY_SIZE(egold_control); i++) {
+		err = snd_ctl_add(egold->card, 
+				  snd_ctl_new1(&egold_control[i], egold->card));
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
diff --git a/sound/arm/omap/omap-alsa-sx1-mixer.h b/sound/arm/omap/omap-alsa-sx1-mixer.h
new file mode 100644
index 0000000..02b8b6a
--- /dev/null
+++ b/sound/arm/omap/omap-alsa-sx1-mixer.h
@@ -0,0 +1,45 @@
+/*
+ * sound/arm/omap/omap-alsa-sx1-mixer.h
+ *
+ * Alsa codec Driver for Siemens SX1 board.
+ * based on omap-alsa-tsc2101-mixer.c
+ *
+ *  Copyright (C) 2006 Vladimir Ananiev (vovan888 at gmail com)
+ *
+ *
+ * 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.
+ *
+ * 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 OMAPALSATSC2101MIXER_H_
+#define OMAPALSATSC2101MIXER_H_
+
+#include "omap-alsa-dma.h"
+
+#define PLAYBACK_TARGET_COUNT		0x03
+#define PLAYBACK_TARGET_LOUDSPEAKER	0x00
+#define PLAYBACK_TARGET_HEADPHONE	0x01
+#define PLAYBACK_TARGET_CELLPHONE	0x02
+
+/* following are used for register 03h Mixer PGA control bits D7-D5 for selecting record source */
+#define REC_SRC_TARGET_COUNT		0x08
+#define REC_SRC_SINGLE_ENDED_MICIN_HED	0x00	/* oss code referred to MIXER_LINE */
+#define REC_SRC_SINGLE_ENDED_MICIN_HND	0x01	/* oss code referred to MIXER_MIC */
+#define REC_SRC_SINGLE_ENDED_AUX1	0x02
+#define REC_SRC_SINGLE_ENDED_AUX2	0x03
+#define REC_SRC_MICIN_HED_AND_AUX1	0x04
+#define REC_SRC_MICIN_HED_AND_AUX2	0x05
+#define REC_SRC_MICIN_HND_AND_AUX1	0x06
+#define REC_SRC_MICIN_HND_AND_AUX2	0x07
+
+#define DEFAULT_OUTPUT_VOLUME		5	/* default output volume to dac dgc */
+#define DEFAULT_INPUT_VOLUME		2	/* default record volume */
+
+#endif
diff --git a/sound/arm/omap/omap-alsa-sx1.c b/sound/arm/omap/omap-alsa-sx1.c
new file mode 100644
index 0000000..c05a65e
--- /dev/null
+++ b/sound/arm/omap/omap-alsa-sx1.c
@@ -0,0 +1,281 @@
+/*
+ * arch/arm/mach-omap1/omap-alsa-sx1.c
+ *
+ * Alsa codec Driver for Siemens SX1 board.
+ * based on omap-alsa-tsc2101.c	and cn_test.c example by Evgeniy Polyakov
+ *
+ * Copyright (C) 2006 Vladimir Ananiev (vovan888 at gmail com)
+ *
+ * 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 <asm/io.h>
+#include <asm/arch/mcbsp.h>
+
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <asm/mach-types.h>
+#include <asm/arch/dma.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/gpio.h>
+
+#include <asm/arch/omap-alsa.h>
+#include "omap-alsa-sx1.h"
+
+#include <linux/connector.h>
+
+/* Connector implementation */
+static struct cb_id cn_sx1snd_id = { CN_IDX_SX1SND, CN_VAL_SX1SND };
+static char cn_sx1snd_name[] = "cn_sx1snd";
+
+void cn_sx1snd_callback(void *data)
+{
+	struct cn_msg *msg = (struct cn_msg *)data;
+
+	printk("%s: %lu: idx=%x, val=%x, seq=%u, ack=%u, len=%d: %s.\n",
+	       __func__, jiffies, msg->id.idx, msg->id.val,
+	       msg->seq, msg->ack, msg->len, (char *)msg->data);
+}
+
+/* Send IPC message to sound server */
+extern int cn_sx1snd_send(unsigned int cmd, unsigned int arg1, unsigned int arg2)
+{
+	struct cn_msg *m;
+	unsigned short data[3];
+	int err;
+
+	m = kzalloc(sizeof(*m) + sizeof(data), gfp_any());
+	if (!m)
+		return -1; 
+
+	memcpy(&m->id, &cn_sx1snd_id, sizeof(m->id));
+	m->seq = 1;
+	m->len = sizeof(data);
+
+	data[0] = (unsigned short)cmd;
+	data[1] = (unsigned short)arg1;
+	data[2] = (unsigned short)arg2;
+
+	memcpy(m + 1, data, m->len);
+
+	err = cn_netlink_send(m, CN_IDX_SX1SND, gfp_any());
+	snd_printd("sent= %02X %02X %02X, err=%d\n", cmd,arg1,arg2,err);
+	kfree(m);
+
+	if (err == -ESRCH)
+		return -1;	/* there are no listeners on socket */
+	return 0;
+}
+
+/* Hardware capabilities
+ *
+ * DAC USB-mode sampling rates (MCLK = 12 MHz)
+ * The rates and rate_reg_into MUST be in the same order
+ */
+static unsigned int rates[] = {
+	 8000, 11025, 12000,
+	 16000, 22050, 24000,
+	 32000, 44100, 48000,
+};
+
+static snd_pcm_hw_constraint_list_t egold_hw_constraints_rates = {
+	.count = ARRAY_SIZE(rates),
+	.list = rates,
+	.mask = 0,
+};
+
+static snd_pcm_hardware_t egold_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 = 8000,
+	.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,
+};
+
+static snd_pcm_hardware_t egold_snd_omap_alsa_capture = {
+	.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
+	.formats = (SNDRV_PCM_FMTBIT_S16_LE),
+	.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_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 = 8000,
+	.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,
+};
+
+static long current_rate = -1; /* current rate in egold format 0..8 */
+/*
+ * ALSA operations according to board file
+ */
+
+/*
+ * Sample rate changing
+ */
+void egold_set_samplerate(long sample_rate)
+{
+	int egold_rate = 0;
+	int clkgdv = 0;
+	u16 srgr1, srgr2;
+
+	/* Set the sample rate */
+#if 0
+	/* fw15: 5005E490 - divs are different !!! */
+	clkgdv	= CODEC_CLOCK / (sample_rate * (DEFAULT_BITPERSAMPLE * 2 - 1));
+#endif
+	switch (sample_rate) {
+		case 8000:	clkgdv = 71; egold_rate = FRQ_8000; break;
+		case 11025:	clkgdv = 51; egold_rate = FRQ_11025; break;
+		case 12000:	clkgdv = 47; egold_rate = FRQ_12000; break;
+		case 16000:	clkgdv = 35; egold_rate = FRQ_16000; break;
+		case 22050:	clkgdv = 25; egold_rate = FRQ_22050; break;
+		case 24000:	clkgdv = 23; egold_rate = FRQ_24000; break;
+		case 32000:	clkgdv = 17; egold_rate = FRQ_32000; break;
+		case 44100:	clkgdv = 12; egold_rate = FRQ_44100; break;
+		case 48000:	clkgdv = 11; egold_rate = FRQ_48000; break;
+	}
+
+	srgr1 = (FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv));
+	srgr2 = ((FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1)));
+
+	OMAP_MCBSP_WRITE(OMAP1510_MCBSP1_BASE, SRGR2, srgr2);
+	OMAP_MCBSP_WRITE(OMAP1510_MCBSP1_BASE, SRGR1, srgr1);
+	current_rate = egold_rate;
+	snd_printd("set samplerate=%ld\n", sample_rate);
+
+}
+
+void egold_configure(void)
+{
+}
+
+/*
+ *  Omap MCBSP clock and Power Management configuration
+ *
+ *  Here we have some functions that allows clock to be enabled and
+ *   disabled only when needed. Besides doing clock configuration
+ *   it allows turn on/turn off audio when necessary.
+ */
+
+/*
+ * Do clock framework mclk search
+ */
+void egold_clock_setup(void)
+{
+	omap_request_gpio(OSC_EN);
+	omap_set_gpio_direction(OSC_EN, 0); /* output */
+	snd_printd("\n");
+}
+
+/*
+ * Do some sanity check, set clock rate, starts it and turn codec audio on
+ */
+int egold_clock_on(void)
+{
+	omap_set_gpio_dataout(OSC_EN, 1);
+	egold_set_samplerate(44100); /* TODO */
+	cn_sx1snd_send(DAC_SETAUDIODEVICE, SX1_DEVICE_SPEAKER, 0);
+	cn_sx1snd_send(DAC_OPEN_DEFAULT, current_rate , 4);
+	snd_printd("\n");
+	return 0;
+}
+
+/*
+ * Do some sanity check, turn clock off and then turn codec audio off
+ */
+int egold_clock_off(void)
+{
+	cn_sx1snd_send(DAC_CLOSE, 0 , 0);
+	cn_sx1snd_send(DAC_SETAUDIODEVICE, SX1_DEVICE_PHONE, 0);
+	omap_set_gpio_dataout(OSC_EN, 0);
+	snd_printd("\n");
+	return 0;
+}
+
+int egold_get_default_samplerate(void)
+{
+	snd_printd("\n");
+	return DEFAULT_SAMPLE_RATE;
+}
+
+static int __init snd_omap_alsa_egold_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct omap_alsa_codec_config *codec_cfg;
+
+	codec_cfg = pdev->dev.platform_data;
+	if (!codec_cfg)
+		return -ENODEV;
+
+	codec_cfg->hw_constraints_rates	= &egold_hw_constraints_rates;
+	codec_cfg->snd_omap_alsa_playback  = &egold_snd_omap_alsa_playback;
+	codec_cfg->snd_omap_alsa_capture  = &egold_snd_omap_alsa_capture;
+	codec_cfg->codec_configure_dev	= egold_configure;
+	codec_cfg->codec_set_samplerate	= egold_set_samplerate;
+	codec_cfg->codec_clock_setup	= egold_clock_setup;
+	codec_cfg->codec_clock_on	= egold_clock_on;
+	codec_cfg->codec_clock_off	= egold_clock_off;
+	codec_cfg->get_default_samplerate = egold_get_default_samplerate;
+	ret = snd_omap_alsa_post_probe(pdev, codec_cfg);
+
+	snd_printd("\n");
+	return ret;
+}
+
+static struct platform_driver omap_alsa_driver = {
+	.probe		= snd_omap_alsa_egold_probe,
+	.remove 	= snd_omap_alsa_remove,
+	.suspend	= snd_omap_alsa_suspend,
+	.resume		= snd_omap_alsa_resume,
+	.driver	= {
+		.name =	"omap_alsa_mcbsp",
+	},
+};
+
+static int __init omap_alsa_egold_init(void)
+{
+	int	retval;
+
+	retval = cn_add_callback(&cn_sx1snd_id, cn_sx1snd_name, cn_sx1snd_callback);
+	if(retval)
+		printk(KERN_WARNING "cn_sx1snd failed to register\n");
+	return platform_driver_register(&omap_alsa_driver);
+}
+
+static void __exit omap_alsa_egold_exit(void)
+{
+	cn_del_callback(&cn_sx1snd_id);
+	platform_driver_unregister(&omap_alsa_driver);
+}
+
+module_init(omap_alsa_egold_init);
+module_exit(omap_alsa_egold_exit);
diff --git a/sound/arm/omap/omap-alsa-sx1.h b/sound/arm/omap/omap-alsa-sx1.h
new file mode 100644
index 0000000..e7bec00
--- /dev/null
+++ b/sound/arm/omap/omap-alsa-sx1.h
@@ -0,0 +1,70 @@
+/*
+ * arch/arc/mach-omap1/omap-alsa-sx1.h
+ *
+ * based on omap-alsa-tsc2101.h
+ *
+ * Alsa Driver for Siemens SX1.
+ * Copyright (C) 2006 Vladimir Ananiev (vovan888 at gmail com)
+ *
+ * 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_SX1_H_
+#define OMAP_ALSA_SX1_H_
+
+#include <linux/types.h>
+
+#define NUMBER_SAMPLE_RATES_SUPPORTED	9
+
+/*
+ * AUDIO related MACROS
+ */
+#ifndef DEFAULT_BITPERSAMPLE
+#define DEFAULT_BITPERSAMPLE		16
+#endif
+
+#define DEFAULT_SAMPLE_RATE		44100
+/* fw15: 18356000 */
+#define CODEC_CLOCK 			18359000
+/* McBSP for playing music */
+#define AUDIO_MCBSP        		OMAP_MCBSP1
+/* McBSP for record/play audio from phone and mic */
+#define AUDIO_MCBSP_PCM    		OMAP_MCBSP2
+/* gpio pin for enable/disable clock */
+#define OSC_EN				2
+
+/* Send IPC message to sound server */
+extern int cn_sx1snd_send(unsigned int cmd, unsigned int arg1, unsigned int arg2);
+/* cmd for IPC_GROUP_DAC */
+#define DAC_VOLUME_UPDATE		0
+#define DAC_SETAUDIODEVICE		1
+#define DAC_OPEN_RING			2
+#define DAC_OPEN_DEFAULT		3
+#define DAC_CLOSE			4
+#define DAC_FMRADIO_OPEN		5
+#define DAC_FMRADIO_CLOSE		6
+#define DAC_PLAYTONE			7
+/* cmd for IPC_GROUP_PCM */
+#define PCM_PLAY			(0+8)
+#define PCM_RECORD			(1+8)
+#define PCM_CLOSE			(2+8)
+
+/* for DAC_SETAUDIODEVICE */
+#define SX1_DEVICE_SPEAKER		0
+#define SX1_DEVICE_HEADPHONE		4
+#define SX1_DEVICE_PHONE		3
+/* frequencies for MdaDacOpenDefaultL,  MdaDacOpenRingL */
+#define FRQ_8000        0
+#define FRQ_11025       1
+#define FRQ_12000       2
+#define FRQ_16000       3
+#define FRQ_22050       4
+#define FRQ_24000       5
+#define FRQ_32000       6
+#define FRQ_44100       7
+#define FRQ_48000       8
+
+#endif


-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux