/*
 * dma_sg_list_buffer.c  -- MATRIX VISION Frame Grabber Driver for mvHyperion series.
 *
 *   Copyright (c) 2006 MATRIX VISION GmbH (info@matrix-vision.de)
 *
 $Id: dma_sg_list_buffer.c,v 1.10 2010-06-23 14:52:23 ug Exp $
 *
 */
/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "drivermain.h"
#include "clf_func.h"
#include "linux/gfp.h"


//----------------------------------------------------------------------------------------------
int alloc_contiguous_buffer( struct pci_dev *pdev, int size, void **buffer, dma_addr_t *phys_addr, int direction )
//----------------------------------------------------------------------------------------------
{
	*buffer = dma_alloc_coherent( &pdev->dev, size, phys_addr, GFP_KERNEL );
	if( *buffer == NULL )
	{
		printk(KERN_ERR "hyperion2 %s couldn't allocate sg_buffer_pool size %d\n", __FUNCTION__, size );
		return -ENOMEM;
	}
	memset( *buffer, 0, size );
	return size;
}

//----------------------------------------------------------------------------------------------
void free_contiguous_buffer( struct pci_dev *pdev, int size, void *buffer, dma_addr_t phys_addr, int direction )
//----------------------------------------------------------------------------------------------
{
	dma_free_coherent( &pdev->dev, size, buffer, phys_addr );
/*
	pci_unmap_page( device->pdev, dev_obj->dma_sg_list_pool[i].phy,  PAGE_SIZE*(1<<dev_obj->dma_sg_list_pool[i].order), PCI_DMA_FROMDEVICE );
		free_pages( (unsigned long)dev_obj->dma_sg_list_pool[i].buf, dev_obj->dma_sg_list_pool[i].order );
*/
}

//----------------------------------------------------------------------------------------------
int init_sg_buffer_list( struct hyperion_object *device )
//----------------------------------------------------------------------------------------------
{
	struct hyperion_device_object *dev_obj = (struct hyperion_device_object *)device->hyperion_device_object;	
	int i, result = -ENOMEM;

	INIT_LIST_HEAD( &dev_obj->dma_sg_list.head );
	spin_lock_init( &dev_obj->dma_sg_list.s_lock );

	for( i = 0; i < MAX_COUNT_REQUEST_OBJECT; i++)
	{
		dev_obj->dma_sg_list_pool[i].index = i;
		dev_obj->dma_sg_list_pool[i].buf_size = (512*1024);
		list_add_tail( &dev_obj->dma_sg_list_pool[i].list_entry, &dev_obj->dma_sg_list.head );
//alloc buffer for sg_dma_address
		result = alloc_contiguous_buffer( device->pdev, dev_obj->dma_sg_list_pool[i].buf_size, &dev_obj->dma_sg_list_pool[i].buf, &dev_obj->dma_sg_list_pool[i].phy, PCI_DMA_TODEVICE );
		if( result < 0 )
			return result;
		else
			dev_obj->dma_sg_list_pool[i].size = result;
//alloc buffer for communication host - hyperion processor
		result = alloc_contiguous_buffer( device->pdev, PAGE_SIZE, &dev_obj->dma_sg_list_pool[i].nios_msg, &dev_obj->dma_sg_list_pool[i].nios_msg_phy, PCI_DMA_TODEVICE );
		if( result < 0 )
			return result;
		else
			dev_obj->dma_sg_list_pool[i].nios_msg_size = result;
			
//alloc buffer for communication hyperion processor - host
		result = alloc_contiguous_buffer(  device->pdev, PAGE_SIZE, &dev_obj->dma_sg_list_pool[i].host_msg, &dev_obj->dma_sg_list_pool[i].host_msg_phy, PCI_DMA_FROMDEVICE );
		if( result < 0 )
			return result;
		else
			dev_obj->dma_sg_list_pool[i].host_msg_size = result;
//alloc buffer for request_extension
		result = alloc_contiguous_buffer( device->pdev, PAGE_SIZE, &dev_obj->dma_sg_list_pool[i].request_extension, &dev_obj->dma_sg_list_pool[i].req_ext_phy, PCI_DMA_BIDIRECTIONAL );
		if( result < 0 )
			return result;

		if( dma_supported(&device->pdev->dev, DMA_BIT_MASK(64)) )
		{
			const u64 highPart = DMA_BIT_MASK(32) << 32;
			dev_obj->dma_sg_list_pool[i].phy |=  (dev_obj->dma_sg_list_pool[i].phy & highPart) ? dev_obj->address_space_encoding : 0 ;
			dev_obj->dma_sg_list_pool[i].nios_msg_phy |= (dev_obj->dma_sg_list_pool[i].nios_msg_phy & highPart) ? dev_obj->address_space_encoding : 0;
			dev_obj->dma_sg_list_pool[i].host_msg_phy |= (dev_obj->dma_sg_list_pool[i].host_msg_phy & highPart) ? dev_obj->address_space_encoding : 0;
			dev_obj->dma_sg_list_pool[i].req_ext_phy |= (dev_obj->dma_sg_list_pool[i].req_ext_phy & highPart) ? dev_obj->address_space_encoding : 0;
		}

#if (BITS_PER_LONG == 32)
		printk( ": - %s - " "[%d] nios_msg %p phy %x host_msg %p phy %x sg_list_dma_addr %p phy %x\n", __FUNCTION__, i, dev_obj->dma_sg_list_pool[i].nios_msg, dev_obj->dma_sg_list_pool[i].nios_msg_phy, dev_obj->dma_sg_list_pool[i].host_msg, dev_obj->dma_sg_list_pool[i].host_msg_phy, dev_obj->dma_sg_list_pool[i].buf, dev_obj->dma_sg_list_pool[i].phy );
#else
		printk( ": - %s - " "[%d] nios_msg %p phy %llx host_msg %p phy %llx sg_list_dma_addr %p phy %llx\n", __FUNCTION__, i, dev_obj->dma_sg_list_pool[i].nios_msg,  (long long unsigned int)dev_obj->dma_sg_list_pool[i].nios_msg_phy, dev_obj->dma_sg_list_pool[i].host_msg,  (long long unsigned int)dev_obj->dma_sg_list_pool[i].host_msg_phy, dev_obj->dma_sg_list_pool[i].buf,  (long long unsigned int)dev_obj->dma_sg_list_pool[i].phy );
#endif
	}
	return 0;
}

//----------------------------------------------------------------------------------------------
void release_sg_buffer_list( struct hyperion_object *device )
//----------------------------------------------------------------------------------------------
{
	struct hyperion_device_object *dev_obj = (struct hyperion_device_object *)device->hyperion_device_object;
	int i;
	for( i = 0; i < MAX_COUNT_REQUEST_OBJECT; i++)
	{
		list_del( &dev_obj->dma_sg_list_pool[i].list_entry );
		free_contiguous_buffer( device->pdev, dev_obj->dma_sg_list_pool[i].buf_size, dev_obj->dma_sg_list_pool[i].buf, dev_obj->dma_sg_list_pool[i].phy, PCI_DMA_FROMDEVICE );

		free_contiguous_buffer( device->pdev, PAGE_SIZE, dev_obj->dma_sg_list_pool[i].nios_msg, dev_obj->dma_sg_list_pool[i].nios_msg_phy, PCI_DMA_FROMDEVICE );

		free_contiguous_buffer( device->pdev, PAGE_SIZE, dev_obj->dma_sg_list_pool[i].host_msg, dev_obj->dma_sg_list_pool[i].host_msg_phy, PCI_DMA_FROMDEVICE );

		free_contiguous_buffer( device->pdev, PAGE_SIZE, dev_obj->dma_sg_list_pool[i].request_extension, dev_obj->dma_sg_list_pool[i].req_ext_phy, PCI_DMA_FROMDEVICE );
	}
}

//----------------------------------------------------------------------------------------------
struct dma_sg_list_entry* get_sg_list_buffer( struct hyperion_device_object *dev_obj )
//----------------------------------------------------------------------------------------------
{
	unsigned long irqflags;
	struct list_head *next;
	struct dma_sg_list_entry *dma_sg_list = NULL;
	//unsigned long irq_flags;
	spin_lock_irqsave( &dev_obj->dma_sg_list.s_lock, irqflags );
	if( !list_empty( &dev_obj->dma_sg_list.head ) )
	{
		next = remove_head_list( &dev_obj->dma_sg_list.head );
		dma_sg_list = container_of( next, struct dma_sg_list_entry, list_entry);
	}
	else
		dma_sg_list = NULL;
	spin_unlock_irqrestore( &dev_obj->dma_sg_list.s_lock, irqflags );
	return dma_sg_list;
}

//----------------------------------------------------------------------------------------------
void free_sg_list_buffer( struct hyperion_device_object *dev_obj, struct dma_sg_list_entry *dma_sg_list )
//----------------------------------------------------------------------------------------------
{	
	unsigned long irqflags;
	//unsigned long irq_flags;
	if( dma_sg_list != NULL )
	{
		dma_sg_list->size = 0;
		dma_sg_list->ioobj = 0;
		spin_lock_irqsave( &dev_obj->dma_sg_list.s_lock, irqflags );
		list_add_tail( &dma_sg_list->list_entry, &dev_obj->dma_sg_list.head );
		spin_unlock_irqrestore( &dev_obj->dma_sg_list.s_lock, irqflags );
	}
	//ExInterlockedInsertTailList( &(pdx->dma_sglist_free_list), &(buf->list), &pdx->sglist_lock);
}
