|  |  | @@ -0,0 +1,214 @@ | 
		
	
		
			
			|  |  |  | /* | 
		
	
		
			
			|  |  |  | Copyright (C) 2001 Paul Davis | 
		
	
		
			
			|  |  |  | Copyright (C) 2002 Dave LaRose | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 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 program is distributed in the hope that it will be useful, | 
		
	
		
			
			|  |  |  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
		
	
		
			
			|  |  |  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
		
	
		
			
			|  |  |  | GNU General Public License for more details. | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 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. | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | $Id$ | 
		
	
		
			
			|  |  |  | */ | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | #include <jack/hardware.h> | 
		
	
		
			
			|  |  |  | #include <jack/alsa_driver.h> | 
		
	
		
			
			|  |  |  | #include <jack/hdsp.h> | 
		
	
		
			
			|  |  |  | #include <jack/error.h> | 
		
	
		
			
			|  |  |  | #include <jack/internal.h> | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | /* Constants to make working with the hdsp matrix mixer easier */ | 
		
	
		
			
			|  |  |  | static const int HDSP_MINUS_INFINITY_GAIN = 0; | 
		
	
		
			
			|  |  |  | static const int HDSP_UNITY_GAIN = 32768; | 
		
	
		
			
			|  |  |  | static const int HDSP_MAX_GAIN = 65535; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | /* | 
		
	
		
			
			|  |  |  | * Use these two arrays to choose the value of the input_channel | 
		
	
		
			
			|  |  |  | * argument to hsdp_set_mixer_gain().  hdsp_physical_input_index[n] | 
		
	
		
			
			|  |  |  | * selects the nth optical/analog input.  audio_stream_index[n] | 
		
	
		
			
			|  |  |  | * selects the nth channel being received from the host via pci/pccard. | 
		
	
		
			
			|  |  |  | */ | 
		
	
		
			
			|  |  |  | static const int hdsp_num_input_channels = 52; | 
		
	
		
			
			|  |  |  | static const int hdsp_physical_input_index[] = { | 
		
	
		
			
			|  |  |  | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, | 
		
	
		
			
			|  |  |  | 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25}; | 
		
	
		
			
			|  |  |  | static const int hdsp_audio_stream_index[] = { | 
		
	
		
			
			|  |  |  | 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, | 
		
	
		
			
			|  |  |  | 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51}; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | /* | 
		
	
		
			
			|  |  |  | * Use this array to choose the value of the output_channel | 
		
	
		
			
			|  |  |  | * argument to hsdp_set_mixer_gain().  hdsp_physical_output_index[26] | 
		
	
		
			
			|  |  |  | * and hdsp_physical_output_index[27] refer to the two "line out" | 
		
	
		
			
			|  |  |  | * channels (1/4" phone jack on the front of digiface/multiface). | 
		
	
		
			
			|  |  |  | */ | 
		
	
		
			
			|  |  |  | static const int hdsp_num_output_channels = 28; | 
		
	
		
			
			|  |  |  | static const int hdsp_physical_output_index[] = { | 
		
	
		
			
			|  |  |  | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, | 
		
	
		
			
			|  |  |  | 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | /* Function for checking argument values */ | 
		
	
		
			
			|  |  |  | static int clamp_int(int value, int lower_bound, int upper_bound) | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | if(value < lower_bound) { | 
		
	
		
			
			|  |  |  | return lower_bound; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | if(value > upper_bound) { | 
		
	
		
			
			|  |  |  | return upper_bound; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | return value; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | /* Note(XXX): Maybe should share this code with hammerfall.c? */ | 
		
	
		
			
			|  |  |  | static void | 
		
	
		
			
			|  |  |  | set_control_id (snd_ctl_elem_id_t *ctl, const char *name) | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | snd_ctl_elem_id_set_name (ctl, name); | 
		
	
		
			
			|  |  |  | snd_ctl_elem_id_set_numid (ctl, 0); | 
		
	
		
			
			|  |  |  | snd_ctl_elem_id_set_interface (ctl, SND_CTL_ELEM_IFACE_PCM); | 
		
	
		
			
			|  |  |  | snd_ctl_elem_id_set_device (ctl, 0); | 
		
	
		
			
			|  |  |  | snd_ctl_elem_id_set_subdevice (ctl, 0); | 
		
	
		
			
			|  |  |  | snd_ctl_elem_id_set_index (ctl, 0); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | /* The hdsp matrix mixer lets you connect pretty much any input to */ | 
		
	
		
			
			|  |  |  | /* any output with gain from -inf to about +2dB. Pretty slick. */ | 
		
	
		
			
			|  |  |  | /* This routine makes a convenient way to set the gain from */ | 
		
	
		
			
			|  |  |  | /* input_channel to output_channel (see hdsp_physical_input_index */ | 
		
	
		
			
			|  |  |  | /* etc. above. */ | 
		
	
		
			
			|  |  |  | /* gain is an int from 0 to 65535, with 0 being -inf gain, and */ | 
		
	
		
			
			|  |  |  | /* 65535 being about 2dB. */ | 
		
	
		
			
			|  |  |  | static int hdsp_set_mixer_gain(jack_hardware_t *hw, int input_channel, | 
		
	
		
			
			|  |  |  | int output_channel, int gain) | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | hdsp_t *h = (hdsp_t *) hw->private; | 
		
	
		
			
			|  |  |  | snd_ctl_elem_value_t *ctl; | 
		
	
		
			
			|  |  |  | snd_ctl_elem_id_t *ctl_id; | 
		
	
		
			
			|  |  |  | int err; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | /* Check args */ | 
		
	
		
			
			|  |  |  | input_channel = clamp_int(input_channel, 0, hdsp_num_input_channels); | 
		
	
		
			
			|  |  |  | output_channel = clamp_int(output_channel, 0, hdsp_num_output_channels); | 
		
	
		
			
			|  |  |  | gain = clamp_int(gain, HDSP_MINUS_INFINITY_GAIN, HDSP_MAX_GAIN); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | /* Allocate control element and select "Mixer" control */ | 
		
	
		
			
			|  |  |  | snd_ctl_elem_value_alloca (&ctl); | 
		
	
		
			
			|  |  |  | snd_ctl_elem_id_alloca (&ctl_id); | 
		
	
		
			
			|  |  |  | set_control_id (ctl_id, "Mixer"); | 
		
	
		
			
			|  |  |  | snd_ctl_elem_value_set_id (ctl, ctl_id); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | /* Apparently non-standard and unstable interface for the */ | 
		
	
		
			
			|  |  |  | /* mixer control. */ | 
		
	
		
			
			|  |  |  | snd_ctl_elem_value_set_integer (ctl, 0, input_channel); | 
		
	
		
			
			|  |  |  | snd_ctl_elem_value_set_integer (ctl, 1, output_channel); | 
		
	
		
			
			|  |  |  | snd_ctl_elem_value_set_integer (ctl, 2, gain); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | /* Commit the mixer value and check for errors */ | 
		
	
		
			
			|  |  |  | if ((err = snd_ctl_elem_write (h->driver->ctl_handle, ctl)) != 0) { | 
		
	
		
			
			|  |  |  | jack_error ("ALSA/HDSP: cannot set mixer gain (%s)", snd_strerror (err)); | 
		
	
		
			
			|  |  |  | return -1; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | /* Note (XXX): Perhaps we should maintain a cache of the current */ | 
		
	
		
			
			|  |  |  | /* mixer values, since it's not clear how to query them from the */ | 
		
	
		
			
			|  |  |  | /* hdsp hardware.  We'll leave this out until a little later. */ | 
		
	
		
			
			|  |  |  | return 0; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | static int hdsp_set_input_monitor_mask (jack_hardware_t *hw, unsigned long mask) | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | int i; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | /* For each input channel */ | 
		
	
		
			
			|  |  |  | for (i = 0; i < 26; i++) { | 
		
	
		
			
			|  |  |  | /* Monitoring requested for this channel? */ | 
		
	
		
			
			|  |  |  | if(mask & (1<<i)) { | 
		
	
		
			
			|  |  |  | /* Yes.  Connect physical input to output */ | 
		
	
		
			
			|  |  |  | if(hdsp_set_mixer_gain (hw, hdsp_physical_input_index[i], | 
		
	
		
			
			|  |  |  | hdsp_physical_output_index[i], | 
		
	
		
			
			|  |  |  | HDSP_UNITY_GAIN) != 0) { | 
		
	
		
			
			|  |  |  | return -1; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | /* ...and disconnect the corresponding software */ | 
		
	
		
			
			|  |  |  | /* channel */ | 
		
	
		
			
			|  |  |  | if(hdsp_set_mixer_gain (hw, hdsp_audio_stream_index[i], | 
		
	
		
			
			|  |  |  | hdsp_physical_output_index[i], | 
		
	
		
			
			|  |  |  | HDSP_MINUS_INFINITY_GAIN) != 0) { | 
		
	
		
			
			|  |  |  | return -1; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } else { | 
		
	
		
			
			|  |  |  | /* No.  Disconnect physical input from output */ | 
		
	
		
			
			|  |  |  | if(hdsp_set_mixer_gain (hw, hdsp_physical_input_index[i], | 
		
	
		
			
			|  |  |  | hdsp_physical_output_index[i], | 
		
	
		
			
			|  |  |  | HDSP_MINUS_INFINITY_GAIN) != 0) { | 
		
	
		
			
			|  |  |  | return -1; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | /* ...and connect the corresponding software */ | 
		
	
		
			
			|  |  |  | /* channel */ | 
		
	
		
			
			|  |  |  | if(hdsp_set_mixer_gain (hw, hdsp_audio_stream_index[i], | 
		
	
		
			
			|  |  |  | hdsp_physical_output_index[i], | 
		
	
		
			
			|  |  |  | HDSP_UNITY_GAIN) != 0) { | 
		
	
		
			
			|  |  |  | return -1; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | /* Cache the monitor mask */ | 
		
	
		
			
			|  |  |  | hw->input_monitor_mask = mask; | 
		
	
		
			
			|  |  |  | return 0; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | static int hdsp_change_sample_clock (jack_hardware_t *hw, SampleClockMode mode) | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | // Empty for now, until Dave understands more about clock sync so | 
		
	
		
			
			|  |  |  | // he can test. | 
		
	
		
			
			|  |  |  | return -1; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | void | 
		
	
		
			
			|  |  |  | jack_alsa_hdsp_release (jack_hardware_t *hw) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | hdsp_t *h = (hdsp_t *) hw->private; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if (h != 0) { | 
		
	
		
			
			|  |  |  | free (h); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | /* Mostly copied directly from hammerfall.c */ | 
		
	
		
			
			|  |  |  | jack_hardware_t * | 
		
	
		
			
			|  |  |  | jack_alsa_hdsp_hw_new (alsa_driver_t *driver) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | jack_hardware_t *hw; | 
		
	
		
			
			|  |  |  | hdsp_t *h; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | hw = (jack_hardware_t *) malloc (sizeof (jack_hardware_t)); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | /* Not using clock lock-sync-whatever in home hardware setup */ | 
		
	
		
			
			|  |  |  | /* yet.  Will write this code when can test it. */ | 
		
	
		
			
			|  |  |  | /* hw->capabilities = Cap_HardwareMonitoring|Cap_AutoSync|Cap_WordClock|Cap_ClockMaster|Cap_ClockLockReporting; */ | 
		
	
		
			
			|  |  |  | hw->capabilities = Cap_HardwareMonitoring; | 
		
	
		
			
			|  |  |  | hw->input_monitor_mask = 0; | 
		
	
		
			
			|  |  |  | hw->private = 0; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | hw->set_input_monitor_mask = hdsp_set_input_monitor_mask; | 
		
	
		
			
			|  |  |  | hw->change_sample_clock = hdsp_change_sample_clock; | 
		
	
		
			
			|  |  |  | hw->release = jack_alsa_hdsp_release; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | h = (hdsp_t *) malloc (sizeof (hdsp_t)); | 
		
	
		
			
			|  |  |  | h->driver = driver; | 
		
	
		
			
			|  |  |  | hw->private = h; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | return hw; | 
		
	
		
			
			|  |  |  | } |