public inbox for linux-omap@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] Alsa modularisations and support for tsc2101 7/7
@ 2006-02-20 18:28 lamikr
  0 siblings, 0 replies; only message in thread
From: lamikr @ 2006-02-20 18:28 UTC (permalink / raw)
  To: OMAP-Linux

[-- Attachment #1: Type: text/plain, Size: 198 bytes --]

ALSA Omap Patch

This patch creates the alsa mixer for tsc2101 codec.

signed-off by Mika Laitio <lamikr@cc.jyu.fi>
signed-off by Daniel Petrini <d.pensator@gmail.com>

----------------
Mika Laitio

[-- Attachment #2: alsa7-20060214.patch --]
[-- Type: text/x-patch, Size: 33327 bytes --]

ALSA Omap Patch

This patch only created the alsa mixer for tsc2101 codec.

signed-off by Mika Laitio <lamikr@cc.jyu.fi>
signed-off by Daniel Petrini <d.pensator@gmail.com>

Index: linux-omap-2.6.git-q/sound/arm/omap/omap-alsa-tsc2101-mixer.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-omap-2.6.git-q/sound/arm/omap/omap-alsa-tsc2101-mixer.c	2006-02-16 09:07:02.000000000 -0400
@@ -0,0 +1,894 @@
+/*
+ * sound/arm/omap/omap-alsa-tsc2101-mixer.c
+ * 
+ * Alsa Driver for TSC2101 codec for OMAP platform boards.
+ *
+ * Copyright (C) 2005 Mika Laitio <lamikr@cc.jyu.fi> and 
+ * 		     Everett Coleman II <gcc80x86@fuzzyneural.net>
+ *
+ * Board initialization code is based on the code in TSC2101 OSS driver.
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ * 	Written by Nishanth Menon and Sriram Kannan
+ * 
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the  GNU General Public License along
+ * with this program; if not, write  to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * History:
+ *
+ * 2006-02-10   Mika Laitio - Mixer for the tsc2101 driver used in omap boards.
+ * 		Can switch between headset and loudspeaker playback, 
+ * 		mute and unmute dgc, set dgc volume. Record source switch,
+ * 		keyclick, buzzer and headset volume and handset volume control 
+ * 		are still missing.
+ * 		
+ */
+ 
+#include <../arch/arm/mach-omap1/omap-alsa-tsc2101.h>
+#include "omap-alsa-tsc2101-mixer.h"
+
+#include <linux/types.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+
+//#define M_DPRINTK(ARGS...)  printk(KERN_INFO "<%s>: ",__FUNCTION__);printk(ARGS)
+#define M_DPRINTK(ARGS...)  		/* nop */
+
+#define DGC_DALVL_EXTRACT(ARG) ((ARG & 0x7f00) >> 8)
+#define DGC_DARVL_EXTRACT(ARG) ((ARG & 0x007f))
+#define GET_DGC_DALMU_BIT_VALUE(ARG)  (((ARG) & TSC2101_BIT(15)) >> 15)
+#define GET_DGC_DARMU_BIT_VALUE(ARG)  (((ARG) & TSC2101_BIT(7)) >> 7)
+#define IS_DGC_DALMU_UNMUTED(ARG)  (((GET_DGC_DALMU_BIT_VALUE(ARG)) == 0))
+#define IS_DGC_DARMU_UNMUTED(ARG) (((GET_DGC_DARMU_BIT_VALUE(ARG)) == 0))
+
+#define HGC_ADPGA_HED_EXTRACT(ARG) ((ARG & 0x7f00) >> 8)
+#define GET_DGC_HGCMU_BIT_VALUE(ARG) (((ARG) & TSC2101_BIT(15)) >> 15)
+#define IS_DGC_HGCMU_UNMUTED(ARG) (((GET_DGC_HGCMU_BIT_VALUE(ARG)) == 0))
+
+#define HNGC_ADPGA_HND_EXTRACT(ARG) ((ARG & 0x7f00) >> 8)
+#define GET_DGC_HNGCMU_BIT_VALUE(ARG) (((ARG) & TSC2101_BIT(15)) >> 15)
+#define IS_DGC_HNGCMU_UNMUTED(ARG) (((GET_DGC_HNGCMU_BIT_VALUE(ARG)) == 0))
+
+static int current_playback_target	= PLAYBACK_TARGET_LOUDSPEAKER;
+static int current_rec_src 		= REC_SRC_SINGLE_ENDED_MICIN_HED;
+
+/*
+ * Used for switching between TSC2101 recourd sources.
+ * Logic is adjusted from the TSC2101 OSS code.
+ */
+static int set_record_source(int val)
+{
+	u16	data;
+	int	maskedVal;
+	
+	FN_IN;
+	maskedVal	= 0xe0 & val;	
+
+/*	
+	// If more than one recording device selected, disable the device that is currently in use.
+	// NOTE: This does not take account differential input selections!
+	if (hweight32(maskedVal) > 1) {
+		maskedVal	&= ~current_rec_src;
+	}
+*/	
+	data	= omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+				TSC2101_MIXER_PGA_CTRL);
+	data	&= ~MPC_MICSEL(7); /* clear all MICSEL bits */
+	data	|= maskedVal;
+	omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
+				TSC2101_MIXER_PGA_CTRL,
+				data);
+	current_rec_src	= val;
+
+	FN_OUT(0);
+	return 0;
+}
+
+/*
+ * Converts the Alsa mixer volume (0 - 100) to real 
+ * Digital Gain Control (DGC) value that can be written
+ * or read from the TSC2101 registry.
+ * 
+ * Note that the number "OUTPUT_VOLUME_MAX" is smaller than OUTPUT_VOLUME_MIN
+ * because DGC works as a volume decreaser. (The more bigger value is put
+ * to DGC, the more the volume of controlled channel is decreased)
+ * 
+ * In addition the TCS2101 chip would allow the maximum volume reduction be 63.5 DB
+ * but according to some tests user can not hear anything with this chip
+ * when the volume is set to be less than 25 db.
+ * Therefore this function will return a value that means 38.5 db (63.5 db - 25 db) 
+ * reduction in the channel volume, when mixer is set to 0.
+ * For mixer value 100, this will return a value that means 0 db volume reduction.
+ * ([mute_left_bit]0000000[mute_right_bit]0000000)
+*/
+int get_mixer_volume_as_dac_gain_control_volume(int vol)
+{
+	u16 retVal;
+
+	/* Convert 0 -> 100 volume to 0x7F(min) -> y(max) volume range */
+	retVal	= ((vol * OUTPUT_VOLUME_RANGE) / 100) + OUTPUT_VOLUME_MAX;
+	/* invert the value for getting the proper range 0 min and 100 max */
+	retVal	= OUTPUT_VOLUME_MIN - retVal;
+	
+	return retVal;
+}
+
+/*
+ * Converts the Alsa mixer volume (0 - 100) to TSC2101 
+ * Digital Gain Control (DGC) volume. Alsa mixer volume 0
+ * is converted to value meaning the volume reduction of -38.5 db
+ * and Alsa mixer volume 100 is converted to value meaning the
+ * reduction of 0 db.
+ */
+int set_mixer_volume_as_dac_gain_control_volume(int mixerVolL, int mixerVolR) 
+{
+	u16 val;
+	int retVal;
+	int volL;
+	int volR;
+	
+	if ((mixerVolL < 0) || 
+	    (mixerVolL > 100) ||
+	    (mixerVolR < 0) ||
+	    (mixerVolR > 100)) {
+		printk(KERN_ERR "Trying a bad mixer volume as dac gain control volume value, left (%d), right (%d)!\n", mixerVolL, mixerVolR);
+		return -EPERM;
+	}
+	M_DPRINTK("mixer volume left = %d, right = %d\n", mixerVolL, mixerVolR);	
+	volL	= get_mixer_volume_as_dac_gain_control_volume(mixerVolL);
+	volR	= get_mixer_volume_as_dac_gain_control_volume(mixerVolR);
+	
+	val	= omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_DAC_GAIN_CTRL);
+	// keep the old mute bit settings
+	val	&= ~(DGC_DALVL(OUTPUT_VOLUME_MIN) | DGC_DARVL(OUTPUT_VOLUME_MIN));
+	val	|= DGC_DALVL(volL) | DGC_DARVL(volR);
+	retVal	= 2;
+	if (retVal) {
+		omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+				TSC2101_DAC_GAIN_CTRL, 
+				val);
+	}
+	M_DPRINTK("to registry: left = %d, right = %d, total = %d\n", DGC_DALVL_EXTRACT(val), DGC_DARVL_EXTRACT(val), val);
+	return retVal;
+}
+
+int dac_gain_control_unmute_control(int muteLeft, int muteRight)
+{
+	u16 val;
+	int count;
+
+	count	= 0;
+	val	= omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_DAC_GAIN_CTRL);
+	// in alsa mixer 1 --> on, 0 == off. In tsc2101 registry 1 --> off, 0 --> on
+	// so if values are same, it's time to change the registry value.
+	if (muteLeft == GET_DGC_DALMU_BIT_VALUE(val)) {
+		if (muteLeft == 0) {
+			val	= val | DGC_DALMU;	// mute --> turn bit on
+		}
+		else {
+			val	= val & ~DGC_DALMU;	// unmute --> turn bit off
+		}
+		count++;
+	} /* L */
+	if (muteRight == GET_DGC_DARMU_BIT_VALUE(val)) {
+		if (muteRight == 0) {
+			val	= val | DGC_DARMU;	// mute --> turn bit on
+		}
+		else {
+			val	= val & ~DGC_DARMU;	// unmute --> turn bit off
+		}		
+		count++;
+	} /* R */
+	if (count) {
+		omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_DAC_GAIN_CTRL, val);
+		M_DPRINTK("changed value, is_unmuted left = %d, right = %d\n", 
+			IS_DGC_DALMU_UNMUTED(val),
+			IS_DGC_DARMU_UNMUTED(val));
+	}
+	return count;	
+}
+
+/*
+ * Converts the DGC registry value read from the TSC2101 registry to 
+ * Alsa mixer volume format (0 - 100).
+ */
+int get_dac_gain_control_volume_as_mixer_volume(u16 vol) 
+{
+	u16 retVal;	
+
+	retVal	= OUTPUT_VOLUME_MIN - vol;
+	retVal	= ((retVal - OUTPUT_VOLUME_MAX) * 100) / OUTPUT_VOLUME_RANGE;
+	/* fix scaling error */
+	if ((retVal > 0) && (retVal < 100)) {
+		retVal++;
+	}
+	return retVal;
+}
+
+/*
+ * Converts the headset gain control volume (0 - 63.5 db)
+ * to Alsa mixer volume (0 - 100)
+ */
+int get_headset_gain_control_volume_as_mixer_volume(u16 registerVal) 
+{
+	u16 retVal;
+	
+	retVal	= ((registerVal * 100) / INPUT_VOLUME_RANGE);
+	return retVal;
+}
+
+/*
+ * Converts the handset gain control volume (0 - 63.5 db)
+ * to Alsa mixer volume (0 - 100)
+ */
+int get_handset_gain_control_volume_as_mixer_volume(u16 registerVal) 
+{
+	return get_headset_gain_control_volume_as_mixer_volume(registerVal);
+}
+
+/*
+ * Converts the Alsa mixer volume (0 - 100) to 
+ * headset gain control volume (0 - 63.5 db)
+ */
+int get_mixer_volume_as_headset_gain_control_volume(u16 mixerVal) 
+{
+	u16 retVal;
+	
+	retVal	= ((mixerVal * INPUT_VOLUME_RANGE) / 100) + INPUT_VOLUME_MIN;	
+	return retVal;
+}
+
+/*
+ * Writes Alsa mixer volume (0 - 100) to TSC2101 headset volume registry in
+ * a TSC2101 format. (0 - 63.5 db)
+ * In TSC2101 OSS driver this functionality was controlled with "SET_LINE" parameter.
+ */
+int set_mixer_volume_as_headset_gain_control_volume(int mixerVol) 
+{
+	int volume;
+	int retVal;
+	u16 val;
+
+	if (mixerVol < 0 || mixerVol > 100) {
+		M_DPRINTK("Trying a bad headset mixer volume value(%d)!\n", mixerVol);
+		return -EPERM;
+	}
+	M_DPRINTK("mixer volume = %d\n", mixerVol);
+	/* Convert 0 -> 100 volume to 0x0(min) -> 0x7D(max) volume range */
+	/* NOTE: 0 is minimum volume and not mute */
+	volume	= get_mixer_volume_as_headset_gain_control_volume(mixerVol);	
+	val	= omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+				TSC2101_HEADSET_GAIN_CTRL);
+	// preserve the old mute settings
+	val	&= ~(HGC_ADPGA_HED(INPUT_VOLUME_MAX));
+	val	|= HGC_ADPGA_HED(volume);
+	omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+			TSC2101_HEADSET_GAIN_CTRL,
+			val);	
+	retVal	= 1;
+	
+	M_DPRINTK("to registry = %d\n", val);	
+	return retVal;
+}
+
+/*
+ * Writes Alsa mixer volume (0 - 100) to TSC2101 handset volume registry in
+ * a TSC2101 format. (0 - 63.5 db)
+ * In TSC2101 OSS driver this functionality was controlled with "SET_MIC" parameter.
+ */
+int set_mixer_volume_as_handset_gain_control_volume(int mixerVol) 
+{
+	int volume;
+	int retVal;
+	u16 val;	
+
+	if (mixerVol < 0 || mixerVol > 100) {
+		M_DPRINTK("Trying a bad mic mixer volume value(%d)!\n", mixerVol);
+		return -EPERM;
+	}
+	M_DPRINTK("mixer volume = %d\n", mixerVol);
+	/* Convert 0 -> 100 volume to 0x0(min) -> 0x7D(max) volume range */
+	/* NOTE: 0 is minimum volume and not mute */
+	volume	= get_mixer_volume_as_headset_gain_control_volume(mixerVol);
+	val	= omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_HANDSET_GAIN_CTRL);
+	// preserve the old mute settigns
+	val	&= ~(HNGC_ADPGA_HND(INPUT_VOLUME_MAX));
+	val	|= HNGC_ADPGA_HND(volume);
+	omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+			TSC2101_HANDSET_GAIN_CTRL,
+			val);
+	retVal	= 1;
+	
+	M_DPRINTK("to registry = %d\n", val);	
+	return retVal;
+}
+
+void init_record_sources(void)
+{
+	// Mute Analog Sidetone
+	// analog sidetone gain db?
+	// Cell Phone In not connected to ADC
+	// Input selected by MICSEL connected to ADC
+	omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+			  TSC2101_MIXER_PGA_CTRL,
+			  MPC_ASTMU | MPC_ASTG(0x40) | ~MPC_CPADC | MPC_MICADC);
+	// Set record source, Select MIC_INHED input for headset
+	set_record_source(REC_SRC_SINGLE_ENDED_MICIN_HED);	
+}
+
+void set_loudspeaker_to_playback_target(void)
+{
+	u16	val;
+
+	// power down sp1, sp2 and loudspeaker
+	omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
+			TSC2101_CODEC_POWER_CTRL,
+			CPC_SP1PWDN | CPC_SP2PWDN | CPC_LDAPWDF);	
+	// ADC, DAC, Analog Sidetone, cellphone, buzzer softstepping enabled
+	// 1dB AGC hysteresis
+	// MICes bias 2V
+	omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+			TSC2101_AUDIO_CTRL_4, 
+			AC4_MB_HED(0));
+
+	// Set codec output volume
+/*
+	omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
+			TSC2101_DAC_GAIN_CTRL, 
+			0x0000);
+*/
+	// DAC left and right routed to SPK1/SPK2
+	// SPK1/SPK2 unmuted
+	// keyclicks routed to SPK1/SPK2
+	val	= AC5_DIFFIN |
+			AC5_DAC2SPK1(3) | AC5_AST2SPK1 | AC5_KCL2SPK1 |
+			AC5_DAC2SPK2(3) | AC5_AST2SPK2 | AC5_KCL2SPK2 |
+			AC5_HDSCPTC;
+	val	= val & ~AC5_HDSCPTC;
+	omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
+			TSC2101_AUDIO_CTRL_5,
+			val);
+	
+	// powerdown spk1/out32n and spk2
+	val	= omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
+				TSC2101_POWERDOWN_STS);
+	val	= val & ~(~PS_SPK1FL | ~PS_HNDFL | PS_LSPKFL);
+	omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
+			TSC2101_POWERDOWN_STS,
+			val);
+
+	// routing selected to SPK1 goes to OUT8P/OUT84 alsa. (loudspeaker)
+	// analog sidetone routed to loudspeaker
+	// buzzer pga routed to loudspeaker
+	// keyclick routing to loudspeaker
+	// cellphone input routed to loudspeaker
+	// mic selection (control register 04h/page2) routed to cell phone output (CP_OUT)
+	// routing selected for SPK1 goes also to cellphone output (CP_OUT)
+	// OUT8P/OUT8N (loudspeakers) unmuted (0 = unmuted)
+	// Cellphone output is not muted (0 = unmuted)
+	// Enable loudspeaker short protection control (0 = enable protection)
+	// VGND short protection control (0 = enable protection)
+	omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
+			TSC2101_AUDIO_CTRL_6,
+			AC6_SPL2LSK | AC6_AST2LSK | AC6_BUZ2LSK | AC6_KCL2LSK |
+			AC6_CPI2LSK | AC6_MIC2CPO | AC6_SPL2CPO |
+			~AC6_MUTLSPK | ~AC6_MUTSPK2 | ~AC6_LDSCPTC | ~AC6_VGNDSCPTC);
+	current_playback_target	= PLAYBACK_TARGET_LOUDSPEAKER;
+}
+
+void set_headphone_to_playback_target(void)
+{
+	//power down sp1, sp2 and loudspeaker
+	omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
+			TSC2101_CODEC_POWER_CTRL,
+			CPC_SP1PWDN | CPC_SP2PWDN | CPC_LDAPWDF);
+	/* ADC, DAC, Analog Sidetone, cellphone, buzzer softstepping enabled */
+	/* 1dB AGC hysteresis */
+	/* MICes bias 2V */
+	omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
+				TSC2101_AUDIO_CTRL_4, 
+				AC4_MB_HED(0));
+
+	/* Set codec output volume */
+/*
+	omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
+			TSC2101_DAC_GAIN_CTRL, 
+			0x0000);
+*/
+	/* DAC left and right routed to SPK2 */
+	/* SPK1/2 unmuted */
+	omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
+			TSC2101_AUDIO_CTRL_5,
+			    AC5_DAC2SPK1(3) | AC5_AST2SPK1 | AC5_KCL2SPK1 |
+			    AC5_DAC2SPK2(3) | AC5_AST2SPK2 | AC5_KCL2SPK2 |
+			    AC5_HDSCPTC);
+
+	/* OUT8P/N muted, CPOUT muted */
+	omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
+			TSC2101_AUDIO_CTRL_6,
+			AC6_MUTLSPK | AC6_MUTSPK2 | AC6_LDSCPTC |
+			AC6_VGNDSCPTC);
+	current_playback_target	= PLAYBACK_TARGET_HEADPHONE;
+}
+
+/*
+ * Checks whether the headset is detected.
+ * If headset is detected, the type is returned. Type can be
+ * 	0x01	= stereo headset detected
+ * 	0x02	= cellurar headset detected
+ * 	0x03	= stereo + cellurar headset detected
+ * If headset is not detected 0 is returned.
+ */
+u16 get_headset_detected(void)
+{
+	u16	curDetected;
+	u16	curType;
+	u16	curVal;
+	
+	curType	= 0;	// not detected;
+	curVal	= omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+					TSC2101_AUDIO_CTRL_7);
+	curDetected	= curVal & AC7_HDDETFL;
+	if (curDetected) {
+		printk("headset detected, checking type from %d \n", curVal);
+		curType	= ((curVal & 0x6000) >> 13);
+		printk("headset type detected = %d \n", curType);
+	}
+	else {
+		printk("headset not detected\n");
+	}
+	return curType;
+}
+
+void init_playback_targets(void)
+{
+	u16	val;
+
+	set_loudspeaker_to_playback_target();
+	/* Left line input volume control */
+	// = SET_LINE in the OSS driver	
+	set_mixer_volume_as_headset_gain_control_volume(DEFAULT_INPUT_VOLUME);
+
+	// Set headset to be controllable by handset mixer
+	// AGC enable for handset input
+	// Handset input not muted
+	val	= omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+				TSC2101_HANDSET_GAIN_CTRL);
+	val	= val | HNGC_AGCEN_HND;	
+	val	= val & ~HNGC_ADMUT_HND;
+	omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+			TSC2101_HANDSET_GAIN_CTRL,
+			val);	
+			
+	/* mic input volume control
+	   SET_MIC in the OSS driver 
+	*/
+	set_mixer_volume_as_handset_gain_control_volume(DEFAULT_INPUT_VOLUME);
+
+	/* Left/Right headphone channel volume control */
+	/* Zero-cross detect on */
+	set_mixer_volume_as_dac_gain_control_volume(DEFAULT_OUTPUT_VOLUME, DEFAULT_OUTPUT_VOLUME);	
+	dac_gain_control_unmute_control(1, 1); // unmute
+}
+
+/*
+ * Initializes tsc2101 recourd source (to line) and playback target (to loudspeaker)
+ */
+void snd_omap_init_mixer(void)
+{	
+	FN_IN;
+	
+	/* Headset/Hook switch detect enabled */
+	omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
+			TSC2101_AUDIO_CTRL_7,
+			AC7_DETECT);
+
+	init_record_sources();
+	init_playback_targets();
+
+	FN_OUT(0);
+}
+
+static int __pcm_playback_target_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) 
+{
+	static char *texts[PLAYBACK_TARGET_COUNT] = {
+        	"Loudspeaker", "Headphone"
+	};
+
+	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(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+	ucontrol->value.integer.value[0] = current_playback_target;
+	return 0;
+}
+
+static int __pcm_playback_target_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+	int	retVal;
+	int	curVal;
+	
+	retVal	= 0;
+	curVal	= ucontrol->value.integer.value[0];
+	if ((curVal >= 0) &&
+	    (curVal < PLAYBACK_TARGET_COUNT) &&
+	    (curVal != current_playback_target)) {		
+		if (curVal == 0) {
+			set_loudspeaker_to_playback_target();		
+		}
+		else {
+			set_headphone_to_playback_target();
+		}
+		retVal	= 1;
+	}
+	return retVal;
+}	
+
+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;
+}
+
+/*
+ * Alsa mixer interface function for getting the volume read from the DGC in a 
+ * 0 -100 alsa mixer format.
+ */
+static int __pcm_playback_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+	u16 volL;
+	u16 volR;	
+	u16 val;
+	
+	val	= omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_DAC_GAIN_CTRL);
+	M_DPRINTK("registry value = %d!\n", val);
+	volL	= DGC_DALVL_EXTRACT(val);
+	volR	= DGC_DARVL_EXTRACT(val);
+	volL	= volL & ~DGC_DALMU;	// make sure that other bits are not on
+	volR	= volR & ~DGC_DARMU;
+
+	volL	= get_dac_gain_control_volume_as_mixer_volume(volL);
+	volR	= get_dac_gain_control_volume_as_mixer_volume(volR);
+	
+	ucontrol->value.integer.value[0]	= volL; // L
+	ucontrol->value.integer.value[1]	= volR; // R	
+	
+	M_DPRINTK("mixer volume left = %ld, right = %ld\n", ucontrol->value.integer.value[0], ucontrol->value.integer.value[1]);
+	return 0;
+}
+
+static int __pcm_playback_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+	return set_mixer_volume_as_dac_gain_control_volume(ucontrol->value.integer.value[0], 
+							ucontrol->value.integer.value[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;
+}
+
+/* 
+ * When DGC_DALMU (bit 15) is 1, the left channel is muted.
+ * When DGC_DALMU is 0, left channel is not muted.
+ * Same logic apply also for the right channel.
+ */
+static int __pcm_playback_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+	u16 val	= omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_DAC_GAIN_CTRL);
+	
+	ucontrol->value.integer.value[0]	= IS_DGC_DALMU_UNMUTED(val);
+	ucontrol->value.integer.value[1]	= IS_DGC_DARMU_UNMUTED(val);
+	return 0;
+}
+
+static int __pcm_playback_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+	return dac_gain_control_unmute_control(ucontrol->value.integer.value[0], 
+					ucontrol->value.integer.value[1]);
+/*	
+	u16 val	= omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_DAC_GAIN_CTRL);
+	int count = 0;
+
+	// in alsa mixer 1 --> on, 0 == off. In tsc2101 registry 1 --> off, 0 --> on
+	// so if values are same, it's time to change the registry value.
+	if (ucontrol->value.integer.value[0] == GET_DGC_DALMU_BIT_VALUE(val)) {
+		if (ucontrol->value.integer.value[0] == 0) {
+			val	= val | DGC_DALMU;	// mute --> turn bit on
+		}
+		else {
+			val	= val & ~DGC_DALMU;	// unmute --> turn bit off
+		}
+		count++;
+	} // L 
+	if (ucontrol->value.integer.value[1] == GET_DGC_DARMU_BIT_VALUE(val)) {
+		if (ucontrol->value.integer.value[1] == 0) {
+			val	= val | DGC_DARMU;	// mute --> turn bit on
+		}
+		else {
+			val	= val & ~DGC_DARMU;	// unmute --> turn bit off
+		}		
+		count++;
+	} // R
+	if (count) {
+		omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_DAC_GAIN_CTRL, val);
+		M_DPRINTK("changed value, is_unmuted left = %d, right = %d\n", 
+			IS_DGC_DALMU_UNMUTED(val),
+			IS_DGC_DARMU_UNMUTED(val));
+	}
+	return count;
+*/	
+}
+
+static int __headset_playback_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) 
+{
+	uinfo->type			= SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count			= 1;
+	uinfo->value.integer.min	= 0;
+	uinfo->value.integer.max	= 100;
+	return 0;
+}
+
+static int __headset_playback_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+	u16 val;
+	u16 vol;
+	
+	val	= omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+				TSC2101_HEADSET_GAIN_CTRL);
+	M_DPRINTK("registry value = %d\n", val);
+	vol	= HGC_ADPGA_HED_EXTRACT(val);
+	vol	= vol & ~HGC_ADMUT_HED;
+
+	vol	= get_headset_gain_control_volume_as_mixer_volume(vol);
+	ucontrol->value.integer.value[0]	= vol;
+	
+	M_DPRINTK("mixer volume returned = %ld\n", ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int __headset_playback_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+	return set_mixer_volume_as_headset_gain_control_volume(ucontrol->value.integer.value[0]);	
+}
+
+static int __headset_playback_switch_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;
+}
+
+/* When HGC_ADMUT_HED (bit 15) is 1, the headset is muted.
+ * When HGC_ADMUT_HED is 0, headset is not muted.
+ */
+static int __headset_playback_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+	u16 val = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+				TSC2101_HEADSET_GAIN_CTRL);
+	ucontrol->value.integer.value[0]	= IS_DGC_HGCMU_UNMUTED(val);
+	return 0;
+}
+
+static int __headset_playback_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+	int count = 0;
+	u16 val = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+				TSC2101_HEADSET_GAIN_CTRL);
+	// in alsa mixer 1 --> on, 0 == off. In tsc2101 registry 1 --> off, 0 --> on
+	// so if values are same, it's time to change the registry value...
+	if (ucontrol->value.integer.value[0] == GET_DGC_HGCMU_BIT_VALUE(val)) {
+		if (ucontrol->value.integer.value[0] == 0) {
+			val	= val | HGC_ADMUT_HED;	// mute --> turn bit on
+		}
+		else {
+			val	= val & ~HGC_ADMUT_HED;	// unmute --> turn bit off
+		}
+		count++;
+		M_DPRINTK("changed value, is_unmuted = %d\n", IS_DGC_HGCMU_UNMUTED(val));
+	}
+	if (count) {
+		omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+				TSC2101_HEADSET_GAIN_CTRL, 
+				val);
+	}
+	return count;
+}
+
+static int __handset_playback_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) 
+{
+	uinfo->type			= SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count			= 1;
+	uinfo->value.integer.min	= 0;
+	uinfo->value.integer.max	= 100;
+	return 0;
+}
+
+static int __handset_playback_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+	u16 val;
+	u16 vol;
+	
+	val	= omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_HANDSET_GAIN_CTRL);
+	M_DPRINTK("registry value = %d\n", val);
+	vol	= HNGC_ADPGA_HND_EXTRACT(val);
+	vol	= vol & ~HNGC_ADMUT_HND;
+	vol	= get_handset_gain_control_volume_as_mixer_volume(vol);
+	ucontrol->value.integer.value[0]	= vol;
+	
+	M_DPRINTK("mixer volume returned = %ld\n", ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int __handset_playback_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+	return set_mixer_volume_as_handset_gain_control_volume(ucontrol->value.integer.value[0]);	
+}
+
+static int __handset_playback_switch_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;
+}
+
+/* When HNGC_ADMUT_HND (bit 15) is 1, the handset is muted.
+ * When HNGC_ADMUT_HND is 0, handset is not muted.
+ */
+static int __handset_playback_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+	u16 val = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_HANDSET_GAIN_CTRL);
+	ucontrol->value.integer.value[0]	= IS_DGC_HNGCMU_UNMUTED(val);
+	return 0;
+}
+
+static int __handset_playback_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) 
+{
+	int count = 0;
+	u16 val = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_HANDSET_GAIN_CTRL);
+	
+	// in alsa mixer 1 --> on, 0 == off. In tsc2101 registry 1 --> off, 0 --> on
+	// so if values are same, it's time to change the registry value...	
+	if (ucontrol->value.integer.value[0] == GET_DGC_HNGCMU_BIT_VALUE(val)) {
+		if (ucontrol->value.integer.value[0] == 0) {
+			val	= val | HNGC_ADMUT_HND;		// mute --> turn bit on
+		}
+		else {
+			val	= val & ~HNGC_ADMUT_HND;	// unmute --> turn bit off			
+		}
+		M_DPRINTK("changed value, is_unmuted = %d\n", IS_DGC_HNGCMU_UNMUTED(val));
+		count++;
+	}
+	if (count) {
+		omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, 
+				TSC2101_HANDSET_GAIN_CTRL, 
+				val);
+	}
+	return count;
+}
+
+static snd_kcontrol_new_t tsc2101_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  = "Handset Playback Volume",
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.index = 2,
+		.access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info  = __handset_playback_volume_info,
+		.get   = __handset_playback_volume_get,
+		.put   = __handset_playback_volume_put,
+	}, {
+		.name  = "Handset Playback Switch",
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.index = 2,
+		.access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info  = __handset_playback_switch_info,
+		.get   = __handset_playback_switch_get,
+		.put   = __handset_playback_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 *tsc2101) 
+{
+	int i=0;
+	int err=0;
+
+	if (!tsc2101) {
+		return -EINVAL;
+	}
+	for (i=0; i < ARRAY_SIZE(tsc2101_control); i++) {
+		if ((err = snd_ctl_add(tsc2101->card, 
+				snd_ctl_new1(&tsc2101_control[i], 
+				tsc2101->card))) < 0) {
+			return err;
+		}
+	}
+	return 0;
+}
Index: linux-omap-2.6.git-q/sound/arm/omap/omap-alsa-tsc2101-mixer.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-omap-2.6.git-q/sound/arm/omap/omap-alsa-tsc2101-mixer.h	2006-02-16 09:07:06.000000000 -0400
@@ -0,0 +1,79 @@
+/*
+ * sound/arm/omap/omap-alsa-tsc2101-mixer.c
+ * 
+ * Alsa Driver for TSC2101 codec for OMAP platform boards.
+ *
+ * Copyright (C) 2005 Mika Laitio <lamikr@cc.jyu.fi> and 
+ * 		     Everett Coleman II <gcc80x86@fuzzyneural.net>
+ *
+ * Based on the ideas in omap-aic23.c and sa11xx-uda1341.c
+ * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
+ * Copyright (C) 2002 Tomas Kasparek <tomas.kasparek@seznam.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the  GNU General Public License along
+ * with this program; if not, write  to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * History:
+ *
+ * 2006-02-10   Mika Laitio - Mixer for the tsc2101 driver used in omap boards.
+ * 		Can switch between headset and loudspeaker playback, 
+ * 		mute and unmute dgc, set dgc volume. Record source switch,
+ * 		keyclick, buzzer and headset volume and handset volume control 
+ * 		are still missing.
+ */
+
+#ifndef OMAPALSATSC2101MIXER_H_
+#define OMAPALSATSC2101MIXER_H_
+
+#include <asm/hardware/tsc2101.h>
+#include <../drivers/ssi/omap-tsc2101.h>
+#include "omap-alsa-dma.h"
+
+/* tsc2101 DAC gain control volume specific  */
+#define OUTPUT_VOLUME_MIN		0x7F	// 1111111 = -63.5 DB
+#define OUTPUT_VOLUME_MAX		0x32	// 110010
+#define OUTPUT_VOLUME_RANGE		(OUTPUT_VOLUME_MIN - OUTPUT_VOLUME_MAX)
+
+/* use input vol of 75 for 0dB gain */
+#define INPUT_VOLUME_MIN 		0x0
+#define INPUT_VOLUME_MAX		0x7D
+#define INPUT_VOLUME_RANGE		(INPUT_VOLUME_MAX - INPUT_VOLUME_MIN)
+
+#define PLAYBACK_TARGET_COUNT		0x02
+#define PLAYBACK_TARGET_LOUDSPEAKER	0x00
+#define PLAYBACK_TARGET_HEADPHONE	0x01
+
+/* 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	MPC_MICSEL(0)	// oss code referred to MIXER_LINE
+#define REC_SRC_SINGLE_ENDED_MICIN_HND	MPC_MICSEL(1)	// oss code referred to MIXER_MIC
+#define REC_SRC_SINGLE_ENDED_AUX1	MPC_MICSEL(2)
+#define REC_SRC_SINGLE_ENDED_AUX2	MPC_MICSEL(3)
+#define REC_SRC_MICIN_HED_AND_AUX1	MPC_MICSEL(4)
+#define REC_SRC_MICIN_HED_AND_AUX2	MPC_MICSEL(5)
+#define REC_SRC_MICIN_HND_AND_AUX1	MPC_MICSEL(6)
+#define REC_SRC_MICIN_HND_AND_AUX2	MPC_MICSEL(7)
+
+#define DEFAULT_OUTPUT_VOLUME		90	// default output volume to dac dgc
+#define DEFAULT_INPUT_VOLUME		20	// default record volume
+
+#define TSC2101_AUDIO_CODEC_REGISTERS_PAGE2     (2)
+
+#endif /*OMAPALSATSC2101MIXER_H_*/


[-- Attachment #3: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2006-02-20 18:28 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-02-20 18:28 [PATCH] Alsa modularisations and support for tsc2101 7/7 lamikr

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox