/*******************************************************************************
 *
 *  codec_enum.c - Implementation of codec enumeration functions.
 *
 *  Copyright(c) 2004 Intel Corporation. All rights reserved.
 *
 *  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., 59
 *  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 *  The full GNU General Public License is included in this distribution in the
 *  file called LICENSE.
 *
 *  CONTACTS:
 *
 *  Matt Jared		matt.jared@intel.com
 *  Andy Kopp		andy.kopp@intel.com
 *  Dan Kogan		dan.d.kogan@intel.com
 *
 *  codec_enum.c,v 1.10 2004/09/28 21:19:23 tiwai Exp
 *
 *  codec_enum.c,v
 *  tiwai
 *  2004/09/28 21:19:23
 *
 ******************************************************************************/

#define __NO_VERSION__
#include <sound/driver.h>
#include <linux/module.h>
#include <asm/uaccess.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/control.h>
#include <sound/info.h>

#include "azx_controller.h"
#include "azx_ringbuffer.h"
#include "azx.h"
#include "codec_enum.h"


// ***********************************************************************
// ** ADTs
// ***********************************************************************
// audio widget capabilites mask - 32bits
#define AUD_WID_CAP_STEREO_MASK		0x00000001
#define AUD_WID_CAP_IN_AMP_MASK		0x00000002
#define AUD_WID_CAP_OUT_AMP_MASK	0x00000004
#define AUD_WID_CAP_AMP_P_OVRD_MASK	0x00000008
#define AUD_WID_CAP_FORMAT_OVRD_MASK	0x00000010
#define AUD_WID_CAP_STRIPE_MASK		0x00000020
#define AUD_WID_CAP_PROC_WID_MASK	0x00000040
#define AUD_WID_CAP_UNSOL_CAP_MASK	0x00000080
#define AUD_WID_CAP_CONN_LIST_MASK	0x00000100
#define AUD_WID_CAP_DIGITAL_MASK	0x00000200
#define AUD_WID_CAP_PWR_CTL_MASK	0x00000401
#define AUD_WID_CAP_L_R_SWAP_MASK	0x00000800
#define AUD_WID_CAP_RSVD_A_MASK		0x0000F000
#define AUD_WID_CAP_DELAY_MASK		0x000F0000
#define AUD_WID_CAP_TYPE_MASK		0x00F00000
#define AUD_WID_CAP_RSVD_B_MASK		0xFF000000

// audio widget bit offsets
#define AUD_WID_CAP_STEREO_BIT_OFF 	0
#define AUD_WID_CAP_IN_AMP_BIT_OFF 	1
#define AUD_WID_CAP_OUT_AMP_BIT_OFF 	2
#define AUD_WID_CAP_AMP_P_OVRD_BIT_OFF 	3
#define AUD_WID_CAP_FORMAT_OVRD_BIT_OFF	4
#define AUD_WID_CAP_STRIPE_BIT_OFF 	5
#define AUD_WID_CAP_PROC_WID_BIT_OFF 	6
#define AUD_WID_CAP_UNSOL_CAP_BIT_OFF 	7
#define AUD_WID_CAP_CONN_LIST_BIT_OFF 	8
#define AUD_WID_CAP_DIGITAL_BIT_OFF 	9
#define AUD_WID_CAP_PWR_CTL_BIT_OFF 	10
#define AUD_WID_CAP_L_R_SWAP_BIT_OFF 	11
#define AUD_WID_CAP_RSVD_A_BIT_OFF 	12
#define AUD_WID_CAP_DELAY_BIT_OFF 	16
#define AUD_WID_CAP_TYPE_BIT_OFF 	20
#define AUD_WID_CAP_RSVD_B_BIT_OFF 	24

// pin widget control mask - 8bits
#define PIN_WID_CTL_VREFEN_MASK		0x07
#define PIN_WID_CTL_RSVD_MASK		0x18
#define PIN_WID_CTL_IN_EN_MASK		0x20
#define PIN_WID_CTL_OUT_EN_MASK		0x40
#define PIN_WID_CTL_H_PHN_EN_MASK	0x80

// pin widget control bit offsets
#define PIN_WID_CTL_VREFEN_BIT_OFF	0
#define PIN_WID_CTL_RSVD_BIT_OFF	3
#define PIN_WID_CTL_IN_EN_BIT_OFF	5
#define PIN_WID_CTL_OUT_EN_BIT_OFF	6
#define PIN_WID_CTL_H_PHN_EN_BIT_OFF	7

// pin widget capabilities mask - 32bits
#define PIN_WID_CAP_IMP_SENSE_MASK		0x00000001
#define PIN_WID_CAP_TRIG_REQ_MASK		0x00000002
#define PIN_WID_CAP_PRESENCE_DETECT_MASK	0x00000004
#define PIN_WID_CAP_H_PHN_DRV_CAP_MASK		0x00000008
#define PIN_WID_CAP_OUT_CAP_MASK		0x00000010
#define PIN_WID_CAP_IN_CAP_MASK			0x00000020
#define PIN_WID_CAP_BAL_IO_PIN_MASK		0x00000040
#define PIN_WID_CAP_RSVD_A_MASK			0x00000080
#define PIN_WID_CAP_VREF_CTL_MASK		0x0000FF00
#define PIN_WID_CAP_EAPD_CAP_MASK		0x00010000
#define PIN_WID_CAP_RSVD_B_MASK			0xFFFE0000

// pin widget capabilities bit offsets
#define PIN_WID_CAP_IMP_SENSE_BIT_OFF		0
#define PIN_WID_CAP_TRIG_REQ_BIT_OFF		1
#define PIN_WID_CAP_PRESENCE_DETECT_BIT_OFF	2
#define PIN_WID_CAP_H_PHN_DRV_CAP_BIT_OFF	3
#define PIN_WID_CAP_OUT_CAP_BIT_OFF		4
#define PIN_WID_CAP_IN_CAP_BIT_OFF		5
#define PIN_WID_CAP_BAL_IO_PIN_BIT_OFF		6
#define PIN_WID_CAP_RSVD_A_BIT_OFF		7
#define PIN_WID_CAP_VREF_CTL_BIT_OFF		8
#define PIN_WID_CAP_EAPD_CAP_BIT_OFF		16
#define PIN_WID_CAP_RSVD_B_BIT_OFF		17

// pin widget default configuration mask - 32bits
#define PIN_WiD_DEF_CONF_SEQUENCE_MASK 		0x0000000F
#define PIN_WiD_DEF_CONF_DEF_ASSOC_MASK 	0x000000F0
#define PIN_WiD_DEF_CONF_MISC_MASK 		0x00000F00
#define PIN_WiD_DEF_CONF_COLOR_MASK 		0x0000F000
#define PIN_WiD_DEF_CONF_CONN_TYPE_MASK 	0x000F0000
#define PIN_WID_DEF_CONF_DEF_DEVICE_MASK	0x00F00000
#define PIN_WiD_DEF_CONF_LOCATION_MASK 		0x3F000000
#define PIN_WiD_DEF_PORT_CONN_MASK 		0xC0000000

// pin widget default configuration bit offset
#define PIN_WiD_DEF_CONF_SEQUENCE_BIT_OFF	0
#define PIN_WiD_DEF_CONF_DEF_ASSOC_BIT_OFF 	4
#define PIN_WiD_DEF_CONF_MISC_BIT_OFF 		8
#define PIN_WiD_DEF_CONF_COLOR_BIT_OFF 		12
#define PIN_WiD_DEF_CONF_CONN_TYPE_BIT_OFF 	16
#define PIN_WID_DEF_CONF_DEF_DEVICE_BIT_OFF	20
#define PIN_WiD_DEF_CONF_LOCATION_BIT_OFF	24
#define PIN_WiD_DEF_PORT_CONN_BIT_OFF 		30

// amplifier widget capabilities mask - 32bits
#define AMP_WID_CAP_OFFSET_MASK		0x0000007F
#define AMP_WID_CAP_RSVD_A_MASK		0x00000080
#define AMP_WID_CAP_NUM_STEPS_MASK	0x00007F00
#define AMP_WID_CAP_RSVD_B_MASK		0x00008000
#define AMP_WID_CAP_STEP_SIZE_MASK	0x007F0000
#define AMP_WID_CAP_RSVD_C_MASK		0x7F800000
#define AMP_WID_CAP_MUTE_CAP_MASK	0x80000000

// amplifier widget capabilities bit offset
#define AMP_WID_CAP_OFFSET_BIT_OFF	0
#define AMP_WID_CAP_RSVD_A_BIT_OFF	7
#define AMP_WID_CAP_NUM_STEPS_BIT_OFF	8
#define AMP_WID_CAP_RSVD_B_BIT_OFF	15
#define AMP_WID_CAP_STEP_SIZE_BIT_OFF	16
#define AMP_WID_CAP_RSVD_C_BIT_OFF	23
#define AMP_WID_CAP_MUTE_CAP_BIT_OFF	31

// generic widget type for recursion
typedef struct {
	u8 NID;
	u8 type;
	unsigned long cl_present;
} s_WID;

// stores critical NIDs found during path search
typedef struct {
	unsigned char DAC_NID;
	unsigned char ADC_NID;
} path_nid_t;

// function group types
#define AUD_FUNC_GROUP           0x1


// widget types
#define AUD_OUT_WID              0x0
#define AUD_IN_WID               0x1
#define AUD_MIX_WID              0x2
#define AUD_SEL_WID              0x3
#define AUD_PIN_WID              0x4
#define AUD_POW_WID              0x5
#define AUD_VOL_WID              0x6
#define AUD_BEEP_WID             0x7
#define AUD_VEND_WID             0xF

// jack types
#define JACK_LINE_OUT            0x0
#define JACK_SPEAKER             0x1
#define JACK_HP_OUT              0x2
#define JACK_CD                  0x3
#define JACK_SPDIF_OUT           0x4
#define JACK_DIG_OTHER_OUT       0x5
#define JACK_MODEM_LINE_SIDE     0x6
#define JACK_MODEM_HAND_SIDE     0x7
#define JACK_LINE_IN             0x8
#define JACK_AUX                 0x9
#define JACK_MIC_IN              0xA
#define JACK_TELEPHONY           0xB
#define JACK_SPDIF_IN            0xC
#define JACK_DIG_OTHER_IN        0xD
#define JACK_OTHER               0xF

// jack colors
#define JACK_COLOR_UNKNOWN       0x0
#define JACK_COLOR_BLACK         0x1
#define JACK_COLOR_GREY          0x2
#define JACK_COLOR_BLUE          0x3
#define JACK_COLOR_GREENE        0x4
#define JACK_COLOR_RED           0x5
#define JACK_COLOR_ORANGE        0x6
#define JACK_COLOR_YELLOW        0x7
#define JACK_COLOR_PURPLE        0x8
#define JACK_COLOR_PINK          0x9
#define JACK_COLOR_WHITE         0xE
#define JACK_COLOR_OTHER         0xF


// ***********************************************************************
// ** Function Prototypes
// ***********************************************************************


// Controller command wrapper functions
static unsigned char get_wid_type               (azx_t* chip, u8 widget_nid);
static unsigned char get_wid_amp_out            (azx_t* chip, u8 wid_nid);
static unsigned char get_wid_amp_in             (azx_t* chip, u8 wid_nid);
static u32  get_wid_caps               (azx_t* chip, u8 w_nid, u32* w_caps,  u32* resp);
static u32  get_amp_caps               (azx_t* chip, u8 direction, u8 w_nid, u32* amp_caps,  u32* resp);
static u32  get_pin_caps               (azx_t* chip, u8 pin_nid, u32* pin_caps,  u32* resp);
static u32  get_pin_ctl_caps           (azx_t* chip, u8 pin_nid, u32* pin_ctl_caps,  u32* resp);
static u32  get_conn_list_len          (azx_t* chip, u8 w_nid, u32* is_long_form);
static u32  get_conn_list_entry        (azx_t* chip, s_WID* wid_list, u32 wid_size, u8 host_nid, u8 index);
static u32  get_sub_node_count         (azx_t* chip, u8 nid, u8* start_nid, u8* end_nid, u32* total_nodes);
static u32  get_func_grp_type          (azx_t* chip, u8 nid);
static u32  get_default_device_value   (azx_t* chip, u8 pin_wid, u32* default_dev, u32* color);
static u32  set_stream_channel         (azx_t* chip, u8 wid_nid, u16 stream_id, u16 channels);
// static u32  set_stream_format          (azx_t* chip, u8 wid_nid, u32 stream_format);
static u32  unmute_widget_output       (azx_t* chip, u8 wid_nid);
static u32  unmute_widget_input        (azx_t* chip, u8 wid_nid);
static u32  unmute_widget              (azx_t* chip, u8 wid_nid, u32 index);
static u32  set_connection_select      (azx_t* chip, u8 wid_nid, u8 list_index);
static u32  set_pin_wid_in_enable      (azx_t* chip, u8 wid_nid);
static u32  set_pin_wid_mic_in_enable  (azx_t* chip, u8 wid_nid);

// Codec enumeration
static u32  set_output_wid_connection  (azx_t* chip, s_WID widget, u32 stream_id, u32 channels, azx_mix_elem_t* mix_elements);
static u32  set_input_wid_connection   (azx_t* chip, s_WID widget, u32 stream_id, u32 channels, cap_source_t* cap_source_info, azx_mix_elem_t* mix_elements);
static u32  set_cd_analog_connection   (azx_t* chip, s_WID widget, azx_mix_elem_t* mix_elements);
static u32  set_mic_rp_wid_connection  (azx_t* chip, s_WID widget, cap_source_t* cap_source_info);

// Util functions
static u32  get_bit_value              (u32 pos, u32 target);

// ***********************************************************************
// ** GLOBALS
// ***********************************************************************
static unsigned char g_ADC_NID;
static unsigned int g_audio_codec_addr;

// Andy added
static s_WID g_lo_pw;
static unsigned char g_lo_pw_nid;
static unsigned char g_cd_pw_nid;
static unsigned char g_cd_path_found;
static unsigned char g_mic_rp_pw_nid;
static unsigned char g_li_pw_nid;
static unsigned char g_cap_source_set;
	
static path_nid_t g_path_nids;


// ***********************************************************************
// ** API Function Implementations
// ***********************************************************************

u32 set_output_path(azx_t* chip, u32 stream_id, u32 channels, azx_mix_elem_t* mix_elements)
{

//	snd_printdd(" ################### ***** set_output_path() *****\n");

	// zero value for channels is acceptible

  	unsigned char node_type;
  	unsigned char start_node_id;
  	unsigned char end_node_id;
  	unsigned int total_nodes;
	u32 nid = 0;
	int i;
	u32 pin_caps;
	s_WID list_wid;// = NULL;
	unsigned int default_device;
	unsigned int jack_color;
	u8 wid_type;

	if( chip == NULL ){
      		printk(KERN_ERR "CODEC-ERROR: chip parameter passed to set_output_path() is invalid!\n");
      		return E_AZX_INVALID_PARAM;
    	}

  	// **** test to make sure root node is AFG ****

  	// ** get AFG node NID **

  	// use total number of nodes area of response to determine function group
  	// type
  	get_sub_node_count(chip, nid, &start_node_id, &end_node_id, &total_nodes);
  	nid = start_node_id;

  	// ** Test for AFG type **
  	node_type = get_func_grp_type(chip, nid);

  	if(AUD_FUNC_GROUP != node_type){
      		printk(KERN_ERR " ################### Node type is not AFG\n");
      		return E_AZX_INVALID_FUNC_GROUP;
    	}

  	// **** get node count for AFG node ****
  	get_sub_node_count(chip, node_type, &start_node_id, &end_node_id, &total_nodes);

  	// Iterate through all the widgets, looking for target
  	// path.

	default_device = 0;
	jack_color     = 0;

	for(i = start_node_id; i <= end_node_id; i++){
      		nid = i;

      		wid_type = get_wid_type(chip, nid);
//      		snd_printdd(" ################### DBG: wid_type is: 0x%x\n", wid_type);

      		if(wid_type == AUD_PIN_WID){
//	  		snd_printdd(" ################### ** PW FOUND: NID is: 0x%x **\n", nid);

	  		get_default_device_value(chip, nid, &default_device, &jack_color);

	  		// log color of jack and jack type for future reporting
//	  		snd_printdd(" ################### DBG: type of jack is: 0x%x\n", default_device);
//	  		snd_printdd(" ################### DBG: color of line out jack is: 0x%x\n", jack_color);


	  		// get pin caps
	  		get_pin_caps(chip, nid, &pin_caps, NULL);
	  
	  		if(pin_caps & PIN_WID_CAP_OUT_CAP_MASK){
//	      			snd_printdd(" ################### DBG: Pin Complex is set for output\n");
	      			list_wid.NID = nid;
	      			list_wid.type = wid_type;
				
				// for use in cd loopback
				g_lo_pw.NID = nid;
				g_lo_pw.type = wid_type;
				
				g_lo_pw_nid = nid;
							
	      			if(set_output_wid_connection(chip, list_wid, stream_id, channels, mix_elements)){
//		  			snd_printdd(" ################### DBG: Successfully set widget connection\n");
		  			break;
				}
	    		}
		}

//    		snd_printdd(" ################### DBG: current nid is: 0x%x\n", nid);

    	}



  	// test mast vol incr

//	snd_printdd(" ################### LEAVING set_output_path()  ################### \n");
  	return E_AZX_CODEC_OK;
}


u32 set_input_path(azx_t* chip, u32 stream_id, u32 channels, cap_source_t* cap_source_info, azx_mix_elem_t* mix_elements)
{
  	unsigned char node_type;
  	unsigned char start_node_id;
  	unsigned char end_node_id;
  	unsigned int total_nodes;
  	u32 nid = 0;
	unsigned int i;
	s_WID list_wid;// = NULL;
	u8 wid_type;
	u32 pin_caps;
	unsigned int default_device;
	unsigned int jack_color;
	unsigned int x;
	
	g_cap_source_set = 0;

	// zero value for channels is acceptible

  	if( chip == NULL ){
      		printk(KERN_ERR "CODEC-ERROR: one or more of the parameters passed to set_input_path() are invalid!\n");
      		return E_AZX_INVALID_PARAM;
    	}

  	// **** test to make sure root node is AFG ****

  	// ** get AFG node NID **

  	// use total number of nodes area of response to determine function group
  	// type
  	get_sub_node_count(chip, nid, &start_node_id, &end_node_id, &total_nodes);
  	nid = start_node_id;
  	// TODO: Add logic to check for modem function group

  	// ** Test for AFG type **
  	node_type = get_func_grp_type(chip, nid);

  	if(AUD_FUNC_GROUP != node_type){
      		snd_printdd(" ################### Node type is not AFG\n");
      		return E_AZX_INVALID_FUNC_GROUP;
    	}

  	// **** get node count for AFG node ****
  	get_sub_node_count(chip, node_type, &start_node_id, &end_node_id, &total_nodes);

  	// Iterate through all the widgets, looking for target
  	// path.
  	for(i = start_node_id; i <= end_node_id; i++){
      		wid_type = get_wid_type(chip, nid);
      		nid = i;

      		snd_printdd(" ################### DBG: wid_type is: 0x%x\n", wid_type);

      		if(wid_type == AUD_IN_WID){// ADC
	
	  		snd_printdd(" ################### ** ADC FOUND: NID is: 0x%x **\n", nid);

	  		g_path_nids.ADC_NID = nid;

	  		set_stream_channel(chip, nid, stream_id, channels);
			unmute_widget_input(chip, nid);

	  		// used for setting connection list later on
	  		g_ADC_NID = nid;

	  		list_wid.NID = nid;
	  		list_wid.type = wid_type;

			// Iterate through all the widgets, looking for target Line In pin widget
			for(x = start_node_id; x <= end_node_id; x++){
				nid = x;

				wid_type = get_wid_type(chip, nid);
		//      		snd_printdd(" ################### DBG: wid_type is: 0x%x\n", wid_type);

				if(wid_type == AUD_PIN_WID){// pin widget

		//			snd_printdd(" ################### ** PIN WIDGET FOUND: NID is: 0x%x **\n", nid);

					// get pin caps
					get_pin_caps(chip, nid, &pin_caps, NULL);

					if(pin_caps & PIN_WID_CAP_IN_CAP_MASK){
		//	      			snd_printdd(" ################### DBG: Pin Complex is set for input\n");

						get_default_device_value(chip, nid, &default_device, &jack_color);

						if ( JACK_LINE_IN == default_device ) {
							g_li_pw_nid = nid;
							snd_printdd(" ################### DBG: Line In Pin Widget found, nid[%i]\n", g_li_pw_nid);
						}
					}

				}	

			}
			
	  		if(set_input_wid_connection(chip, list_wid, stream_id, channels, cap_source_info, mix_elements)){
	      			snd_printdd(" ################### DBG: Successfully set widget connection\n");
	      			break;
	    		}

		}	

      		snd_printdd(" ################### DBG: current nid is: 0x%x\n", nid);

    	}

  	return E_AZX_CODEC_OK;
}

u32  set_cd_analog_loopback_path(azx_t* chip, azx_mix_elem_t* mix_elements)
{
  	unsigned char node_type;
  	unsigned char start_node_id;
  	unsigned char end_node_id;
  	unsigned int total_nodes;
  	u32 nid = 0;
	unsigned int i;
	u32 pin_caps;
	unsigned int default_device;
	unsigned int jack_color;
	u8 wid_type;

//	snd_printdd(" ################### ***** set_cd_analog_loopback_path() *****\n");

  	// zero value for channels is acceptible

  	if( chip == NULL ){
      		printk(KERN_ERR "CODEC-ERROR: chip parameter passed to set_cd_analog_loopback_path() is invalid!\n");
      		return E_AZX_INVALID_PARAM;
    	}

  	// **** test to make sure root node is AFG ****

  	// ** get AFG node NID **

  	// use total number of nodes area of response to determine function group
  	// type
  	get_sub_node_count(chip, nid, &start_node_id, &end_node_id, &total_nodes);
  	nid = start_node_id;
  	// TODO: Add logic to check for modem function group

  	// ** Test for AFG type **
  	node_type = get_func_grp_type(chip, nid);

  	if(AUD_FUNC_GROUP != node_type){
      		printk(KERN_ERR " ################### Node type is not AFG\n");
      		return E_AZX_INVALID_FUNC_GROUP;
    	}

	// **** get node count for AFG node ****
	get_sub_node_count(chip, node_type, &start_node_id, &end_node_id, &total_nodes);

	// Iterate through all the widgets, looking for target CD pin widget
  	for(i = start_node_id; i <= end_node_id; i++){
      		nid = i;

		wid_type = get_wid_type(chip, nid);
//      		snd_printdd(" ################### DBG: wid_type is: 0x%x\n", wid_type);

      		if(wid_type == AUD_PIN_WID){// pin widget
	
//			snd_printdd(" ################### ** PIN WIDGET FOUND: NID is: 0x%x **\n", nid);

			// get pin caps
			get_pin_caps(chip, nid, &pin_caps, NULL);
	  
	  		if(pin_caps & PIN_WID_CAP_IN_CAP_MASK){
//	      			snd_printdd(" ################### DBG: Pin Complex is set for input\n");
		  		
				get_default_device_value(chip, nid, &default_device, &jack_color);
	    			
				if ( JACK_CD == default_device ) {
					g_cd_pw_nid = nid;
					snd_printdd(" ################### DBG: CD Pin Widget found, nid[%i]\n", g_cd_pw_nid);
				}
			}

		}	

    	}
	
	if ( 0 == g_cd_pw_nid ) {
		printk(KERN_WARNING "WARNING: This platform does not support CD analog loopback, please enable CDDA for CD playback.\n");
		return E_AZX_CODEC_OK;
	}

	set_cd_analog_connection(chip, g_lo_pw, mix_elements);
	
	return E_AZX_CODEC_OK;
}


u32 set_mic_rp_path(azx_t* chip, cap_source_t* cap_source_info)
{
  	unsigned char node_type;
  	unsigned char start_node_id;
  	unsigned char end_node_id;
  	u32 total_nodes;
	u32 nid = 0;
	unsigned int i;
	u32 pin_caps;
	unsigned int default_device;
	unsigned int jack_color;
	s_WID list_wid;// = NULL;
	u8 wid_type;

	snd_printdd(" ################### ***** set_mic_rp_path() *****\n");

  	// zero value for channels is acceptible

  	if( chip == NULL ){
      		printk(KERN_ERR "CODEC-ERROR: chip parameter passed to set_mic_rp_path() is invalid!\n");
      		return E_AZX_INVALID_PARAM;
    	}

  	// **** test to make sure root node is AFG ****

  	// ** get AFG node NID **

  	// use total number of nodes area of response to determine function group
  	// type
  	get_sub_node_count(chip, nid, &start_node_id, &end_node_id, &total_nodes);
  	nid = start_node_id;
  	// TODO: Add logic to check for modem function group

  	// ** Test for AFG type **
  	node_type = get_func_grp_type(chip, nid);

	if(AUD_FUNC_GROUP != node_type){
		printk(KERN_ERR " ################### Node type is not AFG\n");
		return E_AZX_INVALID_FUNC_GROUP;
	}

	// **** get node count for AFG node ****
	get_sub_node_count(chip, node_type, &start_node_id, &end_node_id, &total_nodes);
	
  	// Iterate through all the widgets, looking for target CD pin widget
  	for(i = start_node_id; i <= end_node_id; i++){
//		snd_printdd("   MIC: i = %i\n", i);
		
      		nid = i;

		wid_type = get_wid_type(chip, nid);
//      		snd_printdd(" ################### DBG: wid_type is: 0x%x\n", wid_type);

      		if(wid_type == AUD_PIN_WID){// pin widget
	
//	  		snd_printdd(" ################### ** PIN WIDGET FOUND: NID is: 0x%x **\n", nid);

	  		// get pin caps
	  		get_pin_caps(chip, nid, &pin_caps, NULL);
	  
	  		if(pin_caps & PIN_WID_CAP_IN_CAP_MASK){
//	      			snd_printdd(" ################### DBG: Pin Complex is set for input\n");
		  		
				get_default_device_value(chip, nid, &default_device, &jack_color);
	    			
				if ( JACK_MIC_IN == default_device ) {
					g_mic_rp_pw_nid = nid;
					break;
				}
			}

		}	

    	}
	
	
	if ( !(g_mic_rp_pw_nid) ) {
		printk(KERN_ERR "Error: No MIC pin widget found.  Mic input not supported\n");
		return 0;
	}

	// just for test
//	g_mic_rp_pw_nid = 0x18;
		
	nid = g_ADC_NID;
	
	list_wid.NID = nid;
	list_wid.type = AUD_IN_WID;
	
	g_cap_source_set = 0;
	
	set_mic_rp_wid_connection(chip, list_wid, cap_source_info);
  	
	snd_printdd("  MIC: chip->cap_source_info.num_items = %i\n", cap_source_info->num_items);
	
	return E_AZX_CODEC_OK;
}


// ***********************************************************************
// ** Internal Codec Discovery Function Implementations
// ***********************************************************************
/**
 * Recursive function used to traverse widget
 * hierarchy for CD analog loopback path discovery.
*/
u32 set_cd_analog_connection(azx_t* chip, s_WID widget, azx_mix_elem_t* mix_elements)
{
  	unsigned int is_long_form;
  	unsigned int idx;
 	unsigned int conn_len = get_conn_list_len(chip, widget.NID, &is_long_form);
	unsigned int i,j;
	unsigned int n;
	s_WID orig_wid;
	unsigned int total_count;
	u32 amp_caps;
	u32 pin_caps;

  	snd_printdd(" !*!*!**!*!*!*!*!*!* set_cd_analog_connection() *****\n");

  	snd_printdd(" ################### DBG: widget.NID is: 0x%x\n", widget.NID);
  	snd_printdd(" ################### DBG: widget.type is: 0x%x\n", widget.type);

  	if(is_long_form){
//		snd_printdd(" ################### DBG: has long form\n");
      		idx = 2;
    	}else{
//		snd_printdd(" ################### DBG: has short form\n");
      		idx = 4;
   	}

	widget.cl_present = 0;
  	
	if(conn_len > 1) { // > 1 == more than one (hardwired) input
  //      	snd_printdd(" ################### DBG: has connection length > 1\n");
      		widget.cl_present = 1;
    	}
	
	snd_printdd("connection list length for nid [%x] = %i\n", widget.NID, conn_len);
	
	if( widget.NID == g_cd_pw_nid  ){ // DACsnd_printdd(" ################### DBG: DAC FOUND!\n");
		snd_printdd(" ################### DBG: CD Pin Widget found in set connection!!\n");
		
		// unmute amps
		unmute_widget(chip, widget.NID, 0x0);
		
		// setup pin widget for input
		set_pin_wid_in_enable(chip, widget.NID);

		// flag global variable for mixer control creation
		g_cd_path_found = 1;
		
      		return 1; // DONE
		
    	}else if(widget.cl_present){
//		snd_printdd(" ################### DBG: widget has a connection list \n");
      
     		// get next entry on list

      		s_WID wids[idx];
		n = 0;
		total_count = 0;

      		for(i = 0; i < conn_len; i+=idx){
			int counter;
	  		
			snd_printdd("n = %i\n", n);
			
		        get_conn_list_entry(chip, wids, idx, widget.NID, n);
			orig_wid.NID = widget.NID;
			orig_wid.type = widget.type;  

			for (counter = 0; counter < idx; counter++) {
				snd_printdd("    *** wids[%i].nid = %x\n", counter, wids[counter].NID);
			}
			
	  		for (j = 0; j < idx; j++){
		
	      			if(total_count >= conn_len)
					break;
		      
		      		widget.NID = wids[j].NID;
	      			widget.type = wids[j].type;

	      			if(set_cd_analog_connection(chip, widget, mix_elements)){
		  			// check to make sure widget previously called on has a connection list.
		  			// set_wid_connection could have returned true because a hardwired connection
		  			// was found.
					
					// restore original widget info
					widget.NID = orig_wid.NID;		  			
					widget.type = orig_wid.type;
					
					if(widget.cl_present) {
						set_connection_select(chip, widget.NID, total_count);
					}
										
					// unmute amps
      					unmute_widget(chip, widget.NID, total_count);
					
					// Get amp attributes for mixer control
					// find the amp with the largest range and use as mixer control
					
					// check output amp
					get_amp_caps(chip, 1, widget.NID, &amp_caps, NULL);
					
//					if (((amp_caps & AMP_WID_CAP_NUM_STEPS_MASK) >> AMP_WID_CAP_NUM_STEPS_BIT_OFF) > g_tmp_me.amp_max_volume) {
					if (((amp_caps & AMP_WID_CAP_NUM_STEPS_MASK) >> AMP_WID_CAP_NUM_STEPS_BIT_OFF) > mix_elements[AZX_MIX_CD].max_vol) {
						snd_printdd("Setting NID[%x] output amp to temp ME\n", widget.NID);
						strcpy(mix_elements[AZX_MIX_CD].label, "CD Playback Volume");
						mix_elements[AZX_MIX_CD].nid = widget.NID;
						mix_elements[AZX_MIX_CD].max_vol = (amp_caps & AMP_WID_CAP_NUM_STEPS_MASK) >> AMP_WID_CAP_NUM_STEPS_BIT_OFF;
						mix_elements[AZX_MIX_CD].index = total_count;
						mix_elements[AZX_MIX_CD].direction = 1; // output
						mix_elements[AZX_MIX_CD].type = 0; // volume
						mix_elements[AZX_MIX_CD].path = AZX_MIX_CD;
						mix_elements[AZX_MIX_CD].vol[0] = mix_elements[AZX_MIX_CD].max_vol;
						mix_elements[AZX_MIX_CD].vol[1] = mix_elements[AZX_MIX_CD].max_vol;
						if (amp_caps & AMP_WID_CAP_MUTE_CAP_MASK) {
							strcpy(mix_elements[AZX_MIX_CD_MUTE].label, "CD Playback Switch");
							mix_elements[AZX_MIX_CD_MUTE].nid = widget.NID;
							mix_elements[AZX_MIX_CD_MUTE].max_vol = 1;
							mix_elements[AZX_MIX_CD_MUTE].index = total_count;
							mix_elements[AZX_MIX_CD_MUTE].direction = 1; // output
							mix_elements[AZX_MIX_CD_MUTE].type = 1; // mute
							mix_elements[AZX_MIX_CD_MUTE].path = AZX_MIX_CD_MUTE;
							mix_elements[AZX_MIX_CD_MUTE].sw[0] = 1;
							mix_elements[AZX_MIX_CD_MUTE].sw[1] = 1;
						}
					}
					
					// check input amp
					get_amp_caps(chip, 0, widget.NID, &amp_caps, NULL);
					
					if (((amp_caps & AMP_WID_CAP_NUM_STEPS_MASK) >> AMP_WID_CAP_NUM_STEPS_BIT_OFF) > mix_elements[AZX_MIX_CD].max_vol) {
						snd_printdd("Setting NID[%x] input amp to temp ME\n", widget.NID);
						strcpy(mix_elements[AZX_MIX_CD].label, "CD Playback Volume");
						mix_elements[AZX_MIX_CD].nid = widget.NID;
						mix_elements[AZX_MIX_CD].max_vol = (amp_caps & AMP_WID_CAP_NUM_STEPS_MASK) >> AMP_WID_CAP_NUM_STEPS_BIT_OFF;
						mix_elements[AZX_MIX_CD].index = total_count;
						mix_elements[AZX_MIX_CD].direction = 0; // output
						mix_elements[AZX_MIX_CD].type = 0; // volume
						mix_elements[AZX_MIX_CD].path = AZX_MIX_CD;
						mix_elements[AZX_MIX_CD].vol[0] = mix_elements[AZX_MIX_CD].max_vol;
						mix_elements[AZX_MIX_CD].vol[1] = mix_elements[AZX_MIX_CD].max_vol;
						if (amp_caps & AMP_WID_CAP_MUTE_CAP_MASK) {
							strcpy(mix_elements[AZX_MIX_CD_MUTE].label, "CD Playback Switch");
							mix_elements[AZX_MIX_CD_MUTE].nid = widget.NID;
							mix_elements[AZX_MIX_CD_MUTE].max_vol = 1;
							mix_elements[AZX_MIX_CD_MUTE].index = total_count;
							mix_elements[AZX_MIX_CD_MUTE].direction = 0; // output
							mix_elements[AZX_MIX_CD_MUTE].type = 1; // mute
							mix_elements[AZX_MIX_CD_MUTE].path = AZX_MIX_CD_MUTE;
							mix_elements[AZX_MIX_CD_MUTE].sw[0] = 1;
							mix_elements[AZX_MIX_CD_MUTE].sw[1] = 1;
						}
					}
					
					return 1;
				}

	      			total_count++;
				
				snd_printdd("   *** Done with j loop, i = %i\n", i);
	    		}
			
			// restore original widget info
			widget.NID = orig_wid.NID;
			widget.type = orig_wid.type;
			
	  		// iterate offset
	  		n += idx;

		}

    	}else if(conn_len != 0) {
		
		if ( (widget.type == AUD_PIN_WID) && (widget.NID != g_lo_pw_nid) ) {
			get_pin_caps(chip, widget.NID, &pin_caps, NULL);
			
			snd_printdd("pin_caps for nid[%x] = %x\n", widget.NID, pin_caps);
		
			if (pin_caps & PIN_WID_CAP_OUT_CAP_MASK) {
				snd_printdd("input pin but not CD pw!!!\n");
				return 0;
			}
		}
		
//		snd_printdd(" ################### DBG: widget has hard wired connection\n");

      		// get connection entry --> hard wired input
      		get_conn_list_entry(chip, &widget, 0, widget.NID, 0);
		

      		if(set_cd_analog_connection(chip, widget, mix_elements))
			return 1;
    	}

  	return 0;
}

/**
 * Recursive function used to traverse widget
 * hierarchy for potential connections out.
*/
u32 set_output_wid_connection(azx_t* chip, s_WID widget, u32 stream_id, u32 channels, azx_mix_elem_t* mix_elements)
{
//	snd_printdd(" ################### ***** set_output_wid_connection() *****\n");

//	snd_printdd(" ################### DBG: widget.NID is: 0x%x\n", widget.NID);
//	snd_printdd(" ################### DBG: widget.type is: 0x%x\n", widget.type);

  	unsigned int is_long_form;
  	unsigned int idx;
  	unsigned int conn_len = get_conn_list_len(chip, widget.NID, &is_long_form);
	u32 amp_caps;
	unsigned int i,j;
	unsigned int n;
	unsigned int total_count;
	int array_idx = -1;

  	if(is_long_form){
//		snd_printdd(" ################### DBG: has long form\n");
		idx = 2;
    	}else{
//		snd_printdd(" ################### DBG: has short form\n");
		idx = 4;
   	}

  	widget.cl_present = 0;
	if(conn_len > 1) { // > 1 == more than one (hardwired) input
//		snd_printdd(" ################### DBG: has connection length > 1\n");
      		widget.cl_present = 1;
	}

	// look at each widget too see if it has amp out
	// capability.  If it does, un-mute it.
	if(get_wid_amp_out(chip, widget.NID)){
//      		snd_printdd(" ################################################### ADDING TO AMP_OUT LIST NID: 0x%x ##\n", widget.NID);

      		get_amp_caps(chip, 1, widget.NID, &amp_caps, NULL);

     		if(amp_caps & AMP_WID_CAP_NUM_STEPS_MASK){
			array_idx = AZX_MIX_MASTER;
			strcpy(mix_elements[array_idx].label, "Master Playback Volume");
			mix_elements[array_idx].max_vol = (amp_caps & AMP_WID_CAP_NUM_STEPS_MASK) >> AMP_WID_CAP_NUM_STEPS_BIT_OFF;
			mix_elements[array_idx].type = 0; // volume
			mix_elements[array_idx].vol[0] = mix_elements[array_idx].max_vol; // initial volume
			mix_elements[array_idx].vol[1] = mix_elements[array_idx].max_vol; // initial volume
		} else if (amp_caps & AMP_WID_CAP_MUTE_CAP_MASK){
			array_idx = AZX_MIX_MASTER_MUTE;
			strcpy(mix_elements[array_idx].label, "Master Playback Switch");
			mix_elements[array_idx].max_vol = 1;
			mix_elements[array_idx].type = 1; // mute
			mix_elements[array_idx].sw[0] = 1; // iniital value
			mix_elements[array_idx].sw[1] = 1; // iniital value
		}

		if (array_idx >= 0) {
			mix_elements[array_idx].nid = widget.NID;
			mix_elements[array_idx].index = 0;
			mix_elements[array_idx].direction = 1; // output
			mix_elements[array_idx].path = array_idx;
		}
				
      		unmute_widget_output(chip, widget.NID);
    	}



	if( (widget.type == AUD_OUT_WID) && (widget.NID != 0x0) ){ // DACsnd_printdd(" ################### DBG: DAC FOUND!\n");
		set_stream_channel(chip, widget.NID, stream_id, channels);
		g_path_nids.DAC_NID = widget.NID;
		return 1; // DONE
	}else if(widget.cl_present){
//		snd_printdd(" ################### DBG: widget has a connection list \n");
      
		// get next entry on list

		s_WID wids[idx];
		n = 0;
		total_count = 0;

		for(i = 0; i < conn_len; i+=idx){
			
		        get_conn_list_entry(chip, wids, idx, widget.NID, n);
			  

			for (j = 0; j < idx; j++){
		
				if(total_count >= conn_len)
					break;
		      
				widget.NID = wids[j].NID;
				widget.type = wids[j].type;

				if(set_output_wid_connection(chip, widget, stream_id, channels, mix_elements)){
					// check to make sure widget previously called on has a connection list.
					// set_wid_connection could have returned true because a hardwired connection
					// was found.
					if(widget.cl_present)
						set_connection_select(chip, widget.NID, 0);

					return 1;
				}

				total_count++;

			}

			// iterate offset
			n += idx;

		}

	}else if(conn_len != 0) {
//		snd_printdd(" ################### DBG: widget has hard wired connection\n");

		// get connection entry --> hard wired input
		get_conn_list_entry(chip, &widget, 0, widget.NID, 0);


		if(set_output_wid_connection(chip, widget, stream_id, channels, mix_elements)) {
			return 1;
		}
	}

	return 0;
}

/**
 * Recursive function used to traverse widget
 * hierarchy for potential connections in.
*/
u32 set_input_wid_connection(azx_t* chip, s_WID widget, u32 stream_id, u32 channels, cap_source_t* cap_source_info, azx_mix_elem_t* mix_elements)
{
	unsigned int is_long_form;
	unsigned int idx;

  	unsigned int default_device;
 	unsigned int jack_color;

  	// 0x0 == line out
  	// 0x8 == line in
  	// 0x3 == blue
  	// 0x4 == green

	unsigned int conn_len = get_conn_list_len(chip, widget.NID, &is_long_form);
	unsigned int i,j;
	unsigned int n;
	s_WID orig_wid;
	unsigned int total_count;
	u32 pin_caps;
	u32 amp_caps;
	unsigned char cap_source_index;
	
	snd_printdd(" ################### ***** set_input_wid_connection() *****\n");

  	snd_printdd(" ################### DBG: widget.NID is: 0x%x\n", widget.NID);
  	snd_printdd(" ################### DBG: widget.type is: 0x%x\n", widget.type);

  	snd_printdd(" ################### DBG: conn_len is %d\n", conn_len);


  	if(is_long_form){
      		snd_printdd(" ################### DBG: has long form\n");
      		idx = 2;
    	}else{
      		snd_printdd(" ################### DBG: has short form\n");
      		idx = 4;
    	}

	widget.cl_present = 0;
	
	if(conn_len > 1){ // > 1 == more than one (hardwired) input
        	// snd_printdd(" ################### DBG: has connection length > 1\n");
      		widget.cl_present = 1;
    	}

  	// look at each widget too see if it has amp in
  	// capability.  If it does, un-mute it.
  	if(get_wid_amp_in(chip, widget.NID)){
	
      		get_amp_caps(chip, 0, widget.NID, &amp_caps, NULL);

		strcpy(mix_elements[AZX_MIX_CAP].label, "Capture Volume");
		mix_elements[AZX_MIX_CAP].nid = widget.NID;
		mix_elements[AZX_MIX_CAP].max_vol = (amp_caps & AMP_WID_CAP_NUM_STEPS_MASK) >> AMP_WID_CAP_NUM_STEPS_BIT_OFF;
		mix_elements[AZX_MIX_CAP].index = 0;
		mix_elements[AZX_MIX_CAP].direction = 0;
		mix_elements[AZX_MIX_CAP].type = 0; // volume
		mix_elements[AZX_MIX_CAP].path = AZX_MIX_CAP; // volume
		mix_elements[AZX_MIX_CAP].vol[0] = mix_elements[AZX_MIX_CAP].max_vol; // initial volume
		mix_elements[AZX_MIX_CAP].vol[1] = mix_elements[AZX_MIX_CAP].max_vol; // initial volume
		
      		unmute_widget_input(chip, widget.NID);
    	}

	if( (widget.type == AUD_PIN_WID) && (widget.NID == g_li_pw_nid) ){ // end point
    		
		snd_printdd(" ################### DBG: PIN WIDGET FOUND!\n");
      		get_default_device_value(chip, widget.NID, &default_device, &jack_color);


		// log color of jack and jack type for future reporting
  		snd_printdd(" ################### DBG: type of jack is: 0x%x\n", default_device);
  		snd_printdd(" ################### DBG: color of line in jack is: 0x%x\n", jack_color);

		set_pin_wid_in_enable(chip, widget.NID);
		set_stream_channel(chip, widget.NID, stream_id, channels);

		unmute_widget(chip, widget.NID, 0x0);
		
		return 1; // DONE
		
	}else if(widget.cl_present){
      		
      		// get next entry on list

      		s_WID wids[idx];
		n = 0;
		total_count = 0;

      		for(i = 0; i < conn_len; i+=idx){

	  		get_conn_list_entry(chip, wids, idx, widget.NID, n);

			orig_wid.NID = widget.NID;
			orig_wid.type = widget.type;  

	  		for (j = 0; j < idx; j++){

	      			if(total_count >= conn_len)
					break;

	      			widget.NID = wids[j].NID;
	      			widget.type = wids[j].type;

				snd_printdd("   Line-In: looking at NID - %x\n", widget.NID);
	      			if(set_input_wid_connection(chip, widget, stream_id, channels, cap_source_info, mix_elements)){
		  			// check to make sure widget previously called on has a
		  			// connection list.
		  			// set_wid_connection could have returned true because a
		  			// hardwired connection was found.
		  			if(widget.cl_present){
		      				set_connection_select(chip, g_ADC_NID, n+j);
		    			}
		
					if( !(g_cap_source_set) ) {
						// add capture source info for mixer control creation
						cap_source_info->num_items++;
						cap_source_index = cap_source_info->num_items;
						cap_source_info->cap_labels[cap_source_index - 1] = "Line";
						cap_source_info->cap_index[cap_source_index - 1] = total_count;
						snd_printdd("Line In cap index = %i\n", total_count);
						cap_source_info->nids[cap_source_index - 1] = orig_wid.NID;
						snd_printdd("Line In NID = %x\n", orig_wid.NID);
						cap_source_info->init_val = cap_source_index - 1;
						g_cap_source_set = 1;
					}
		  			
		  			return 1;
				}

	      			total_count++;
				snd_printdd("   Line-In: done with j loop\n");
	    		}


	  	// iterate offset
	  	n += idx;

		}

    	}else if(conn_len != 0) {
		
		if ( (widget.type == AUD_PIN_WID) && (widget.NID != g_lo_pw_nid) ) {
			get_pin_caps(chip, widget.NID, &pin_caps, NULL);
			
			snd_printdd("pin_caps for nid[%x] = %x\n", widget.NID, pin_caps);
		
			if (pin_caps & PIN_WID_CAP_OUT_CAP_MASK) {
				snd_printdd("input pin but not what I am looking for!!!\n");
				return 0;
			}
		}
		
		snd_printdd(" ################### DBG: widget has hard wired connection\n");

      		// get connection entry --> hard wired input
      		get_conn_list_entry(chip, &widget, 0, widget.NID, 0);
      		if(set_input_wid_connection(chip, widget, stream_id, channels, cap_source_info, mix_elements)) {
      			return 1;
		}
	}

  	return 0;
}

/**
 * Recursive function used to traverse widget
 * hierarchy for potential connection for Mic (rear panel).
*/
u32 set_mic_rp_wid_connection(azx_t* chip, s_WID widget, cap_source_t* cap_source_info)
{
//	snd_printdd(" ################### ***** set_mic_rp_wid_connection() *****\n");

//	snd_printdd(" ################### DBG: widget.NID is: 0x%x\n", widget.NID);
//	snd_printdd(" ################### DBG: widget.type is: 0x%x\n", widget.type);

	unsigned int is_long_form;
	unsigned int idx;

	// 0x0 == line out
	// 0x8 == line in
	// 0x3 == blue
	// 0x4 == green

	unsigned int conn_len = get_conn_list_len(chip, widget.NID, &is_long_form);

	unsigned int i,j;
	unsigned int n;
	s_WID orig_wid;
	unsigned int total_count;
	unsigned char cap_source_index;

	if(is_long_form){
		idx = 2;
	}else{
		idx = 4;
	}

	widget.cl_present = 0;
	
	if(conn_len > 1){ // > 1 == more than one (hardwired) input
		widget.cl_present = 1;
	}

	if( (widget.type == AUD_PIN_WID) && (widget.NID == g_mic_rp_pw_nid) ){ // end point
		
		snd_printdd(" ################### DBG: Mic PIN WIDGET FOUND - NID[%x]!\n", widget.NID);
		
		// unmute amps
		unmute_widget(chip, widget.NID, 0x0);
		
		// setup pin widget for input
		set_pin_wid_mic_in_enable(chip, widget.NID);

		return 1; // DONE
		
	}else if(widget.cl_present){
		
		s_WID wids[idx];
		n = 0;
		total_count = 0;

      		for(i = 0; i < conn_len; i+=idx){

	  		get_conn_list_entry(chip, wids, idx, widget.NID, n);

			orig_wid.NID = widget.NID;
			orig_wid.type = widget.type;
			
	  		for (j = 0; j < idx; j++){

	      			if(total_count >= conn_len)
					break;

	      			widget.NID = wids[j].NID;
	      			widget.type = wids[j].type;
				
				snd_printdd("   Mic-In: looking at NID - %x\n", widget.NID);

	      			if(set_mic_rp_wid_connection(chip, widget, cap_source_info)){
		  			// check to make sure widget previously called on has a
		  			// connection list.
		  			// set_wid_connection could have returned true because a
		  			// hardwired connection was found.
		  			if(widget.cl_present){
		      				set_connection_select(chip, g_ADC_NID, n+j);
		    			}
		
					if( !(g_cap_source_set) ) {
						// add capture source info for mixer control creation
						snd_printdd("   Setting capture source for Mic\n");
						cap_source_info->num_items++;
						cap_source_index = cap_source_info->num_items;
						cap_source_info->cap_labels[cap_source_index - 1] = "Mic";
						cap_source_info->cap_index[cap_source_index - 1] = total_count;
						snd_printdd("Mic In cap index = %i\n", total_count);
						cap_source_info->nids[cap_source_index - 1] = orig_wid.NID;
						snd_printdd("Mic In NID = %x\n", orig_wid.NID);
						cap_source_info->init_val = cap_source_index - 1;
						g_cap_source_set = 1;
					}
		  
		  			return 1;
				}

	      			total_count++;
	    		}


	  	// iterate offset
	  	n += idx;

		}

    	}else if(conn_len != 0) { 
//		snd_printdd(" ################### DBG: widget has hard wired connection\n");
      		// get connection entry --> hard wired input
      		get_conn_list_entry(chip, &widget, 0, widget.NID, 0);
      		if(set_mic_rp_wid_connection(chip, widget, cap_source_info))
      			return 1;
	}

  	return 0;
}

// ***********************************************************************
// ** Utility Function Implementations
// ***********************************************************************
static u32 get_wid_caps(azx_t* chip, u8 w_nid, u32* w_caps,  u32* resp)
{
	u32 cad = g_audio_codec_addr;
  	u32 direct = 0x0;
  	u32 resp0;
  	u32 resp0_ex;
  	u32 parameter = AUD_WIDGET_CAP;

	snd_azx_send_corb_cmd(chip, cad, w_nid, direct, GET_PARAMETER, parameter, &resp0, &resp0_ex);
	
  	if(resp != NULL) {
    		*resp = resp0;
	}
	
  	*w_caps = resp0;
	
  	return 0;
}

static u32 get_amp_caps(azx_t* chip, u8 direction, u8 w_nid, u32* amp_caps, u32* resp)
{
  	u32 cad = g_audio_codec_addr;
  	u32 direct = 0x0;
  	u32 resp0;
  	u32 resp0_ex;
  	u32 parameter = 0;

  	if(direction) {
    		parameter = OUT_AMP_CAP; // out
	} else {
    		parameter = IN_AMP_CAP; // in
	}
	
	snd_azx_send_corb_cmd(chip, cad, w_nid, direct, GET_PARAMETER, parameter, &resp0, &resp0_ex);

  	if(resp != NULL) {
    		*resp = resp0;
	}

  	*amp_caps = resp0;

  	return 0;
}

static u32 get_pin_caps(azx_t* chip, u8 pin_nid, u32* pin_caps, u32* resp )
{
  	u32 cad = g_audio_codec_addr;
  	u32 direct = 0x0;
  	u32 resp0;
  	u32 resp0_ex;
  	u32 parameter = PIN_CAP;

	snd_azx_send_corb_cmd(chip, cad, pin_nid, direct, GET_PARAMETER, parameter, &resp0, &resp0_ex);

  	if(resp != NULL) {
    		*resp = resp0;
	}
	
  	*pin_caps = resp0;
  	return 0;
}

static u32 get_pin_ctl_caps(azx_t* chip, u8 pin_nid, u32* pin_ctl_caps, u32* resp)
{
  	u32 cad = g_audio_codec_addr;
  	u32 direct = 0x0;
  	u32 resp0;
  	u32 resp0_ex;
  	u32 parameter = 0x0;

	snd_azx_send_corb_cmd(chip, cad, pin_nid, direct, GET_PIN_WIDGET_CONTROL, parameter, &resp0, &resp0_ex);

 	if(resp != NULL) {
    		*resp = resp0;
	}
	
  	*pin_ctl_caps = resp0;
  	return 0;
}

static u8 get_wid_type(azx_t* chip, u8 widget_nid)
{
  	u8 type;
  	u32 w_caps;
  	get_wid_caps(chip, widget_nid, &w_caps, NULL);
	
  	type = (w_caps & AUD_WID_CAP_TYPE_MASK) >> AUD_WID_CAP_TYPE_BIT_OFF;
  	return type;
}

static u8 get_wid_amp_out(azx_t* chip, u8 wid_nid)
{
  	u8 out_amp;
  	u32 w_caps;
  	snd_printdd(" ################### ***** get_wid_amp_out() *****\n");
  	get_wid_caps(chip, wid_nid, &w_caps, NULL);
  	out_amp = (w_caps & AUD_WID_CAP_OUT_AMP_MASK) >> AUD_WID_CAP_OUT_AMP_BIT_OFF;
  	snd_printdd(" ################### out_amp is: 0x%x\n", out_amp);
  	return out_amp;
}

static u8 get_wid_amp_in(azx_t* chip, u8 wid_nid)
{
  	u8 in_amp;
  	u32 w_caps;
  	snd_printdd(" ################### ***** get_wid_amp_in() *****\n");
  	get_wid_caps(chip, wid_nid, &w_caps, NULL);
  	in_amp = (w_caps & AUD_WID_CAP_IN_AMP_MASK) >> AUD_WID_CAP_IN_AMP_BIT_OFF;
  	snd_printdd(" ################### in_amp is: 0x%x\n", in_amp);
  	return in_amp;
}

static u32 get_conn_list_len(azx_t* chip, u8 w_nid, u32* is_long_form)
{
  	u32 cad = g_audio_codec_addr;
  	u32 direct = 0x0;
  	u32 resp0;
  	u32 resp0_ex;
  	u32 parameter = CONNECTION_LIST_LENGTH;

	snd_azx_send_corb_cmd(chip, cad, w_nid, direct, GET_PARAMETER, parameter, &resp0, &resp0_ex);

  	// get long form bit
  	*is_long_form = get_bit_value(7, resp0);

  	return resp0;
}

static unsigned int get_conn_list_entry(azx_t* chip, s_WID* wid, u32 wid_size, u8 host_nid, u8 index_nid)
{
  	u32 cad = g_audio_codec_addr;
  	u32 direct = 0x0;
  	u32 resp0;
  	u32 resp0_ex;
  	unsigned char dep_range_val = 0;
  	u32 parameter = index_nid;
	unsigned char nid_start;
	unsigned char nid_end;
	unsigned int size;
	unsigned int i;

  	snd_printdd(" ################### ***** get_conn_list_entry() *****\n");
  
  	snd_printdd(" ################### DBG: wid_size is: %d\n", wid_size);

	snd_azx_send_corb_cmd(chip, cad, host_nid, direct, GET_CONNECTION_LIST_ENTRY, parameter, &resp0, &resp0_ex);

  	snd_printdd(" ################### DBG: resp0 is: 0x%x\n", resp0);

  	// check for independant or range NID
  
  	if(wid_size == 2)
    		dep_range_val = get_bit_value(15, resp0);
  	else if(wid_size == 4)
    		dep_range_val = get_bit_value(7, resp0);

  	snd_printdd(" ################### DBG: dep_range_val is: 0x%x\n", dep_range_val);

  	if(wid_size == 0){ // zero connection list
        	snd_printdd(" ################### DBG: inside \"if(wid_size == 0)\"\n");
      		wid->NID = resp0;
      		wid->type = get_wid_type(chip, wid->NID);

      		snd_printdd(" ################### DBG: wid->NID is: 0x%x\n", wid->NID);
      		snd_printdd(" ################### DBG: wid->type is: 0x%x\n", wid->type);
    	}else if(dep_range_val){ // nids in a range 
      		// because a range was identified, need to create an array
      		// and ignore passed in array;
		nid_start = resp0;
		nid_end = resp0 >> 8;
		size = nid_start + nid_end;
		{
			s_WID wid_ar[size];

			snd_printdd(" ################### DBG: RANGE BIT IS SET, CALCULATING ALL NIDs IN RANGE!!!\n");

			for(i = nid_start; i > nid_end; i++){
				wid_ar[i].NID = i;
				wid_ar[i].type = get_wid_type(chip, i);
			}

			wid = wid_ar; // FIXME: this doesn't accomplish anything
		}
    	}else if(wid_size == 2){ // long form connection list
    		wid[0].NID = resp0;
      		wid[0].type = get_wid_type(chip, wid->NID);
  	
      		wid[1].NID = resp0 >> 8;
      		wid[1].type = get_wid_type(chip,  wid->NID);

    	}else if(wid_size == 4){ // short form connection list
        	wid[0].NID = resp0;
      		wid[0].type = get_wid_type(chip, wid->NID);

      		wid[1].NID = resp0 >> 8;
      		wid[1].type = get_wid_type(chip, wid->NID);

      		wid[2].NID = resp0 >> 16;
      		wid[2].type = get_wid_type(chip, wid->NID);

      		wid[3].NID = resp0 >> 24;
      		wid[3].type = get_wid_type(chip, wid->NID);

    	}

  	snd_printdd(" ################### ***** LEAVING get_conn_list_entry() *****\n");

  	return 0;
}

static u32 get_sub_node_count(azx_t* chip, u8 nid, u8* start_nid, u8* end_nid, u32* total_nodes)
{
  	u32 cad = g_audio_codec_addr;
  	u32 direct = 0x0;
  	u32 resp0;
  	u32 resp0_ex;
  	u32 parameter = SUB_NODE_COUNT;

	snd_azx_send_corb_cmd(chip, cad, nid, direct, GET_PARAMETER, parameter, &resp0, &resp0_ex);

 	*start_nid     = (resp0 & 0x00FF0000) >>16;
  	*total_nodes   = resp0 & 0x000000FF;
  	*end_nid       = *start_nid+*total_nodes;
  
  	return 0;
}

static u32 get_func_grp_type(azx_t* chip, u8 nid)
{
  	u32 cad = g_audio_codec_addr;
  	u32 direct = 0x0;
  	u32 resp0;
  	u32 resp0_ex;
  	u32 parameter = FUNC_GRP_TYPE;
  	
	snd_azx_send_corb_cmd(chip, cad, nid, direct, GET_PARAMETER, parameter, &resp0, &resp0_ex);

	return resp0;
}

static u32 get_default_device_value(azx_t* chip, u8 pin_wid, u32* default_dev, u32* color)
{
  	u32 cad = g_audio_codec_addr;
  	u32 direct = 0x0;
  	u32 resp0;
  	u32 resp0_ex;
  	u32 parameter = 0x0;

	snd_azx_send_corb_cmd(chip, cad, pin_wid, direct, GET_CONFIG_DEFAULT_BYTES, parameter, &resp0, &resp0_ex);

  	*default_dev = (resp0 & PIN_WID_DEF_CONF_DEF_DEVICE_MASK) >> PIN_WID_DEF_CONF_DEF_DEVICE_BIT_OFF;
  	*color = (resp0 & PIN_WiD_DEF_CONF_COLOR_MASK) >> PIN_WiD_DEF_CONF_COLOR_BIT_OFF;
  	
	return 0;
}

static u32 set_stream_channel(azx_t* chip, u8 wid_nid, u16 stream_id, u16 channels)
{
  	u32 cad = g_audio_codec_addr;
  	u32 direct = 0x0;
  	u32 resp0;
  	u32 resp0_ex;
  	u16 parameter = (stream_id << 4) | channels;

  	snd_printdd(" ################### ***** set_stream_channel() *****\n");
  	snd_printdd(" ################### NID is: 0x%x\n", wid_nid);

	snd_azx_send_corb_cmd(chip, cad, wid_nid, direct, SET_CHANNEL_STREAMID, parameter, &resp0, &resp0_ex);

  	return 0;
}

/*
static u32 set_stream_format(azx_t* chip, u8 wid_nid, u32 stream_format)
{
  	u32 cad = g_audio_codec_addr;
  	u32 direct = 0x0;
  	u32 resp0;
  	u32 resp0_ex;
  	u32 parameter = stream_format;

  	snd_printdd(" ################### ***** set_stream_output_format() *****\n");
  	snd_printdd(" ################### NID is: 0x%x\n", wid_nid);
  	snd_printdd(" ################### stream_format is: 0x%x\n", stream_format);

	snd_azx_send_corb_cmd(chip, cad, wid_nid, direct, SET_STREAM_FORMAT, parameter, &resp0, &resp0_ex);

  	return 0;
}
*/

static u32 unmute_widget_output(azx_t* chip, u8 wid_nid)
{
  	// determine if mix or pin widget, to derive mute mask
  	u8 wid_type = get_wid_type(chip, wid_nid);

	u32 cad = g_audio_codec_addr;
  	u32 direct = 0x0;
  	u32 resp0;
  	u32 resp0_ex;
  	u32 parameter = 0;
	
  	snd_printdd(" ################### ***** unmute_widget() *****\n");
  	snd_printdd(" ################### NID is: 0x%x\n", wid_nid);

  	// unmute left
 	snd_printdd(" ################### unmute left\n");

  	parameter = 0xB000;

  	if(wid_type == AUD_MIX_WID){ // mixer
        	snd_printdd(" ################### mix wid found, parameter = 0xA040 \n");
      		parameter = 0xB040;
    	}


	snd_azx_send_corb_cmd(chip, cad, wid_nid, direct, SET_AMPLIFIER_GAIN_MUTE, parameter, &resp0, &resp0_ex);

 	return 0;
}


static u32 unmute_widget_input(azx_t* chip, u8 wid_nid)
{
  	// determine if mix or pin widget, to derive mute mask
  	u32 cad = g_audio_codec_addr;
  	u32 direct = 0x0;
  	u32 resp0;
  	u32 resp0_ex;
  	u32 parameter = 0;

  	snd_printdd(" ################### ***** unmute_widget_input() *****\n");
  	snd_printdd(" ################### NID is: 0x%x\n", wid_nid);

  	// *** unmute left ***
  	parameter = 0xF043;

	snd_azx_send_corb_cmd(chip, cad, wid_nid, direct, SET_AMPLIFIER_GAIN_MUTE, parameter, &resp0, &resp0_ex);

  	return 0;
}

static u32 unmute_widget(azx_t* chip, u8 wid_nid, u32 index)
{
  	// determine if mix or pin widget, to derive mute mask
  	u32 cad = g_audio_codec_addr;
  	u32 direct = 0x0;
  	u32 resp0;
  	u32 resp0_ex;
  	u32 parameter = 0;

	unsigned int max_volume;
	
	u32 amp_caps;
		
	snd_printdd(" ################### ***** unmute_widget_input() *****\n");
	snd_printdd(" ################### NID is: 0x%x\n", wid_nid);

	get_amp_caps(chip, 1, wid_nid, &amp_caps, NULL);
	max_volume = (amp_caps & AMP_WID_CAP_NUM_STEPS_MASK) >> AMP_WID_CAP_NUM_STEPS_BIT_OFF;
	
	get_amp_caps(chip, 0, wid_nid, &amp_caps, NULL);
	
	if (max_volume < ((amp_caps & AMP_WID_CAP_NUM_STEPS_MASK) >> AMP_WID_CAP_NUM_STEPS_BIT_OFF)) {
		max_volume = (amp_caps & AMP_WID_CAP_NUM_STEPS_MASK) >> AMP_WID_CAP_NUM_STEPS_BIT_OFF;
	}
	
	snd_printdd("max volume (num_steps) = %i [0x%x]\n", max_volume, max_volume);
	
	parameter = 0xF000 | (index << 8) | max_volume;

	snd_printdd("paramter = %x\n", parameter);

	snd_azx_send_corb_cmd(chip, cad, wid_nid, direct, SET_AMPLIFIER_GAIN_MUTE, parameter, &resp0, &resp0_ex);

  	return 0;
}


static u32 set_connection_select(azx_t* chip, u8 wid_nid, u8 list_index)
{
  	u32 cad = g_audio_codec_addr;
  	u32 direct = 0x0;
  	u32 resp0;
  	u32 resp0_ex;
  	u32 parameter = list_index;

  	snd_printdd(" ################### ***** set_connection_select() *****\n");
  	snd_printdd(" ################### NID is: 0x%x\n", wid_nid);
  	snd_printdd(" ################### list_index is: 0x%x\n", list_index);

	snd_azx_send_corb_cmd(chip, cad, wid_nid, direct, SET_CONNECTION_SELECT, parameter, &resp0, &resp0_ex);

  	return 0;
}


static u32 get_bit_value(u32 pos, u32 target)
{
  	unsigned int mask = 1 << pos;
  	unsigned int value = target & mask;
  	return value;
}


static u32  set_pin_wid_in_enable(azx_t* chip, u8 wid_nid)
{
  	u32 cad = g_audio_codec_addr;
  	u32 direct = 0x0;
  	u32 resp0;
  	u32 resp0_ex;
  	u32 parameter = 0x20; // set 'in enable' bit
  	
  	snd_printdd(" ################### ***** set_pin_wid_in_enabled() *****\n");
	snd_printdd(" ################### NID is: 0x%x\n", wid_nid);

	snd_azx_send_corb_cmd(chip, cad, wid_nid, direct, SET_PIN_WIDGET_CONTROL, parameter, &resp0, &resp0_ex);

	return 0;
}

static u32  set_pin_wid_mic_in_enable(azx_t* chip, u8 wid_nid)
{
//	snd_printdd(" ################### ***** set_pin_wid_mic_in_enable() *****\n");
//	snd_printdd(" ################### NID is: 0x%x\n", wid_nid);
  	u32 cad = g_audio_codec_addr;
  	u32 direct = 0x0;
  	u32 resp0;
  	u32 resp0_ex;
  	u32 parameter = 0x24; // set 'in enable' bit
  	
	snd_azx_send_corb_cmd(chip, cad, wid_nid, direct, SET_PIN_WIDGET_CONTROL, parameter, &resp0, &resp0_ex);

	return 0;
}

u32 set_audio_codec_address(azx_t* chip)
{
  	unsigned int i;
  	unsigned char val;

  	u32 codec_mask = snd_azx_get_codec_mask(chip);

  	for(i = 0; i < codec_mask; i++){
      		val = get_bit_value(i, codec_mask);

      		// NOTE: currently only allowing one codec to be used

      		if(val){ // codec found
			g_audio_codec_addr = i;
	  		break;	  
		}
    	}
	return E_AZX_CODEC_OK;
}


u8 get_dac_nid(void)
{
	return g_path_nids.DAC_NID;
}

u8 get_adc_nid(void)
{
	return g_path_nids.ADC_NID;
}

u32 get_audio_codec_address(void)
{
  	return g_audio_codec_addr;
}

#ifdef CONFIG_PROC_FS
static const char *get_wid_type_string(u8 wid_value)
{
	static char *names[16] = {
		[AUD_OUT_WID] = "Audio Output",
		[AUD_IN_WID] = "Audio Input",
		[AUD_MIX_WID] = "Audio Mixer",
		[AUD_SEL_WID] = "Audio Selector",
		[AUD_PIN_WID] = "Pin Complex",
		[AUD_POW_WID] = "Power Widget",
		[AUD_VOL_WID] = "Volume Knob Widget",
		[AUD_BEEP_WID] = "Beep Generator Widget",
		[AUD_VEND_WID] = "Vendor Defined Widget",
	};
	wid_value &= 0xf;
	if (names[wid_value])
		return names[wid_value];
	else
		return "UNKOWN Widget";
}

void snd_azx_print_codec_info(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
{
	azx_t* chip = entry->private_data;
  	u32 cad           = g_audio_codec_addr;
  	u32 nid           = 0;
  	u32 direct        = 0;
  	u32 parameter     = 0;
  	u32 resp0;
  	u32 resp0_ex;
  	unsigned int total_nodes;
  	unsigned int conn_len;
  	unsigned int idx;
  	unsigned char node_type;
  	unsigned char start_node_id;
  	unsigned char end_node_id;
  	unsigned int vend_id;
  	unsigned int rev_id;
	unsigned int i,j,k;
	unsigned int n;
	unsigned char search_nid;
	unsigned int total_count;

  	u32 wid_caps;
	u32 amp_caps;
  	u32 pin_caps;
  	u32 pin_ctl_caps;
  	u32 is_long_form = 0;

	u32 wid_caps_resp;
	u32 amp_caps_resp;
	u32 pin_caps_resp;
	u32 pin_ctl_caps_resp;
	
  	snd_printdd ("***** collect_codec_info() *****\n");

  	if(g_audio_codec_addr == 0)
     		snd_printdd("######### ERROR -- CODEC ADDRESS NOT SET ##########\n");

  	// output initial XML
	snd_iprintf(buffer, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
 
  	// get codec vendor ID
	snd_azx_send_corb_cmd(chip, cad, nid, direct, GET_PARAMETER, parameter, &resp0, &resp0_ex);

  	vend_id = resp0 & 0x0000FFFF;
	

  	// get codec revision number
  	parameter = 0x02;
	snd_azx_send_corb_cmd(chip, cad, nid, direct, GET_PARAMETER, parameter, &resp0, &resp0_ex);

  	rev_id = resp0 & 0x0000FFFF;

	snd_iprintf(buffer,
		    "<codec address=\"0x%x\" vendor=\"0x%X\" revision=\"0x%X\">\n",
		    g_audio_codec_addr, vend_id, rev_id);
	snd_iprintf(buffer, "<function_group>\n");
  
  	// **** test to make sure root node is AFG ****

  	// ** get AFG node NID **

  	// use total number of nodes area of response to determine function group 
  	// type
  	get_sub_node_count(chip, nid, &start_node_id, &end_node_id, &total_nodes);
  	nid = start_node_id;    
 
  	// ** Test for AFG type **
  	node_type = get_func_grp_type(chip, nid);

  	if(0x1 != node_type){
      		snd_printdd("Node type is not AFG\n");
      		return;
    	}
  
    	// **** get node count for AFG node ****
  	get_sub_node_count(chip, node_type, &start_node_id, &end_node_id, &total_nodes);
  
  	// widget IDs will most likely never start at zero
  	if(start_node_id == 0)
    		start_node_id++;

  	snd_iprintf(buffer,
		    "<audio_function_group start_node_id=\"0x%X\" end_node_id=\"0x%X\" total_nodes=\"%d\">\n",
		    start_node_id, end_node_id, total_nodes);

  	// Iterate through all the widgets, looking for output
  	// path.
  	for(i = start_node_id+1; i <= end_node_id; i++){
		wid_caps_resp = 0;
		amp_caps_resp = 0;
		pin_caps_resp = 0;
		pin_ctl_caps_resp = 0;

      		nid = i;

      		// get wid caps
      		get_wid_caps(chip, nid, &wid_caps, &wid_caps_resp);

      		// get amp caps
      		get_amp_caps(chip, 1, nid, &amp_caps, &amp_caps_resp);

      		// get pin caps
      		get_pin_caps(chip, nid, &pin_caps, &pin_caps_resp);

      		// get pin ctl caps
      		get_pin_ctl_caps(chip, nid, &pin_ctl_caps, &pin_ctl_caps_resp);

      		snd_iprintf(buffer,
			    "<widget nid=\"0x%X\" type=\"%s\" wid_caps=\"0x%X\" amp_caps=\"0x%X\" pin_caps=\"0x%X\" pin_ctl_caps=\"0x%X\">\n",
			    nid, get_wid_type_string(get_wid_type(chip, nid)),
			    wid_caps_resp, amp_caps_resp, pin_caps_resp, pin_ctl_caps_resp);
     
      		// get conn list len
		if ((wid_caps & AUD_WID_CAP_CONN_LIST_MASK) >> AUD_WID_CAP_CONN_LIST_BIT_OFF) {
	      		conn_len = get_conn_list_len(chip, nid, &is_long_form);
		} else {
			conn_len = 0;
      		}
		
		if(is_long_form) {
			idx = 2;
     		} else {
			idx = 4;
		}
		
      		snd_iprintf(buffer,
			    "<connection_list length=\"%d\" long_form=\"%d\" index=\"%d\">\n",
			    conn_len, is_long_form, idx);
      
      		if(conn_len){
	  		s_WID wids[idx];

	  		n = 0;
	  		search_nid = nid;
	  		total_count = 0;
	  
	  		// get conn list entry nids
	  		for(j = 0; j < conn_len; j+=idx){

	      			// NOTE: search_nid does not need to be updated.
	      			//       The index (n) controls what elements in
	      			//       the connection list to grab

	      			get_conn_list_entry(chip, wids, idx, search_nid, n);
	  
	     			for (k = 0; k < idx; k++){
		  
		  			if(total_count >= conn_len)
		    				break;

					snd_iprintf(buffer,
						    "<entry nid=\"0x%X\"/>\n", wids[k].NID);
		  			total_count++;
				}

				// iterate offset
				n+=idx;
	    		}
		}
      
      		snd_iprintf(buffer, "</connection_list>\n");
      		snd_iprintf(buffer, "</widget>\n");
	}

  	snd_iprintf(buffer, "</audio_function_group>\n");
  	snd_iprintf(buffer, "</function_group>\n");
  	snd_iprintf(buffer, "</codec>\n");
}
#endif
