/** @file analog_out-kapi.c

Kernel functions for the analog output channels.

@par CREATION
@author Krauth Julien
@date   05.03.07

@par VERSION
@verbatim
$LastChangedRevision:$
$LastChangedDate:$
@endverbatim

@par LICENCE
@verbatim
Copyright (C) 2009  ADDI-DATA GmbH for the source code of this module.

ADDI-DATA GmbH
Airpark Business Center
Airport Boulevard B210
77836 Rheinmuenster
Germany
Tel: +49(0)7229/1847-0
Fax: +49(0)7229/1847-200
http://www.addi-data-com
info@addi-data.com

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later version.

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

You shoud also find the complete GPL in the COPYING file
accompanying this source code.
@endverbatim
*/

#include "apci3120-private.h"

EXPORT_SYMBOL(i_APCI3120_InitAnalogOutput);
EXPORT_SYMBOL(i_APCI3120_Write1AnalogOutput);
EXPORT_SYMBOL(i_APCI3120_WriteMoreAnalogOutput);
EXPORT_SYMBOL(i_APCI3120_ReadAnalogOutputBit);

/**@def EXPORT_NO_SYMBOLS
* Function in this file are not exported.
*/
EXPORT_NO_SYMBOLS;

//------------------------------------------------------------------------------

/** Initialize the analog output value.
*
* @param [in] 	pdev			: The device to initialize.
* @param [in]	b_ChannelNumber	: The analog output channel.
* @param [in]	b_Polarity		: Polarity of the analog input: <br>
*									- APCI3120_UNIPOLAR:  0V - +10V <br>
*									- APCI3120_BIPOLAR: -10V - +10V
*
* @retval 0: No error.
* @retval 1: The handle parameter of the board is wrong.
* @retval 2: Number of the analog output is wrong.
* @retval 3: The selected output voltage range is wrong.
*/
int i_APCI3120_InitAnalogOutput (struct pci_dev *pdev,
		uint8_t b_ChannelNumber,
		uint8_t b_Polarity)
{
	if (!pdev) return 1;

	/* Check the channel number */
	if (b_ChannelNumber >= APCI3120_PRIVDATA(pdev)->nrAnaOut) return 2;

	/* Test the polarity */
	if (b_Polarity != APCI3120_UNIPOLAR && b_Polarity != APCI3120_BIPOLAR) return 3;

	APCI3120_PRIVDATA(pdev)->outputPolarity[b_ChannelNumber] = b_Polarity;

	return 0;
}

//------------------------------------------------------------------------------

/** Write a value on a output channel.
*
* @param [in] 	pdev			: The device to initialize.
*
* @retval 0: No error.
* @retval 1: The handle parameter of the board is wrong.
* @retval 2: Wrong output value for the output channel.
*/
int i_APCI3120_Write1AnalogOutput (struct pci_dev *pdev,
		uint8_t b_ChannelNumber,
		uint32_t ui_ValueToWrite)
{
	uint16_t ui_DummyWrite  = 0;

	if (!pdev) return 1;

	ui_DummyWrite = (((uint16_t) (b_ChannelNumber & 0x03) << 14) & 0xC000);

	if ( ((APCI3120_PRIVDATA(pdev)->outputPolarity[b_ChannelNumber] == APCI3120_UNIPOLAR) && (ui_ValueToWrite > 8192)) ||
			((APCI3120_PRIVDATA(pdev)->outputPolarity[b_ChannelNumber] == APCI3120_BIPOLAR)  && (ui_ValueToWrite > 16383)) )
		return 2;

	if (APCI3120_PRIVDATA(pdev)->outputPolarity[b_ChannelNumber] == APCI3120_UNIPOLAR)
	{
		if (ui_ValueToWrite != 0)
		{
			ui_ValueToWrite = ui_DummyWrite | (1 << 13) | (ui_ValueToWrite + 8191);
		}
		else
		{
			ui_ValueToWrite = ui_DummyWrite | (1 << 13) | 8192;
		}
	}
	else
	{
		ui_ValueToWrite = ui_DummyWrite | (0 << 13) | ui_ValueToWrite;
	}

	/* Write the value */
	outw (ui_ValueToWrite, GET_BASE_IO(pdev) + ((b_ChannelNumber / 4) * 2) + 0x08);

	return 0;
}

//------------------------------------------------------------------------------

/** Read the analog output conversion bit.
*
* Test the end of conversion.
*
* @param [in] 	pdev			: The device to initialize.
*
* @retval 0: No error.
* @retval 1: The handle parameter of the board is wrong.
* @retval 2: The ready bit flag is not set.
*/
int i_APCI3120_ReadAnalogOutputBit (struct pci_dev *pdev)
{
	uint32_t ui_StatusValue = 0;

	if (!pdev) return 1;

	/* Reads the ready bit flag */
	ui_StatusValue = XPCI3120_INW (pdev, GET_BASE_IO(pdev) + APCI3120_RD_STATUS);

	/* Return 2 if the DAC is not ready */
	if ((ui_StatusValue & 0x1) != 0x1)
		return 2;

	return 0;
}

//------------------------------------------------------------------------------

/* returns true if conversion finished */
static int check_condition_WriteMoreAnalogOutput (struct pci_dev * pdev)
{
	return ( ( XPCI3120_INW(pdev, GET_BASE_IO(pdev) + APCI3120_RD_STATUS) & 0x1) == 0x1 );
}

//------------------------------------------------------------------------------

/** Write more analog outputs.
*
* @param [in] 	pdev					: The device to initialize.
* @param [in] 	pui_ValueToWriteArray	: Value to write on each output.
*
* @retval 0: No error.
* @retval 1: The handle parameter of the board is wrong.
* @retval 2: Wrong output value for the output channel.
*/
int i_APCI3120_WriteMoreAnalogOutput (struct pci_dev *pdev, uint32_t *pui_ValueToWriteArray)
{
	uint8_t  b_ChannelNbr         = 0;
	uint32_t ui_TmpValueToWrite[8];
	uint32_t ui_SaveValueToWriteArray[8];

	if (!pdev) return 1;

	/* Set the value to write on each channels */
	for (b_ChannelNbr = 0; b_ChannelNbr < APCI3120_PRIVDATA(pdev)->nrAnaOut; b_ChannelNbr ++)
	{
		if ( ((APCI3120_PRIVDATA(pdev)->outputPolarity[b_ChannelNbr] == APCI3120_UNIPOLAR) && (pui_ValueToWriteArray[b_ChannelNbr] > 8192)) ||
				((APCI3120_PRIVDATA(pdev)->outputPolarity[b_ChannelNbr] == APCI3120_BIPOLAR)  && (pui_ValueToWriteArray[b_ChannelNbr] > 16383)) )
			return 2;

		ui_TmpValueToWrite[b_ChannelNbr] = (((uint32_t) (b_ChannelNbr & 0x03) << 14) & 0xC000);

		if (APCI3120_PRIVDATA(pdev)->outputPolarity[b_ChannelNbr] == APCI3120_UNIPOLAR)
		{
			if (pui_ValueToWriteArray[b_ChannelNbr] != 0)
			{
				ui_SaveValueToWriteArray[b_ChannelNbr] = ui_TmpValueToWrite[b_ChannelNbr] | (1 << 13) | (pui_ValueToWriteArray[b_ChannelNbr] + 8191);
			}
			else
				ui_SaveValueToWriteArray[b_ChannelNbr] = ui_TmpValueToWrite[b_ChannelNbr] | (1 << 13) | 8192;
		}
		else
		{
			ui_SaveValueToWriteArray[b_ChannelNbr] = ui_TmpValueToWrite[b_ChannelNbr] | (0 << 13) | pui_ValueToWriteArray[b_ChannelNbr];
		}
	}

	/* Wait the end of conversion for each channels and write the value */
	for (b_ChannelNbr = 0; b_ChannelNbr < APCI3120_PRIVDATA(pdev)->nrAnaOut; b_ChannelNbr ++)
	{
		/* timeout of 50 microseconds plus 100 for security */
		if ( apci3120_busywait_with_timeout(pdev,check_condition_WriteMoreAnalogOutput, 30) )
			return 3;

		outw (ui_SaveValueToWriteArray[b_ChannelNbr], GET_BASE_IO(pdev) + ((b_ChannelNbr / 4) * 2) + 0x08);
	}

	return  0;
}


