summaryrefslogblamecommitdiffstats
path: root/drivers/staging/tidspbridge/rmgr/drv.c
blob: 3815622284c0c902b3d2ba794b226ab36875f1e4 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
















                                                                       
                        




                                                          



















































                                                                             
                                                      


                                                 

                                                                 

                                               

                                                                              





























                                                                            
                                                                        

                                              

                                                                              





















                                                                    
                                                     
 
                                                                              

























                                                                                
                                                       
 
                                                                              

























                                                                                
                                                                 

                                              

                                                    



                                              
                                                                      

                                              

                                                    





                                              
                                                        
 
                                                                              















                                                  

                                                              
 


                                                                              

























                                                                    

                                                                               

                                            
                                                
                                                                              











                                                                         
                                                   
















                                                                    
                                                                        
 
                                                                               
                                                                              
























                                                                         
                                                        
 
                                                                              





































                                                                                
                                                                    
                                                       

                                           
                                                      
                                                                              







                                                        
                                                                           














                                            
                                                                


                                           
                                                      











                                                                
                                               



                                              
                                     



























                                                                             
                                       





























                                                      
                                              

                       
                                                                         























                                                                           
                                                             

                       
                               





                                                                    
                                        









                                                                                
                                                           
                
                                   


















































































                                                                                
                                                 



                                    
                                        





                                                                               
                                                                              





























                                                                 
                                                        


                                                             
                                                                         



















                                                                          
                                                        


                                                             
                                                                         


































                                                                           
                                                             





                                       
                                           















                                                                            
                                                                      



                                                                      
                                           



                                                                                
                                   

         
                                                                     
                                                                   
                                                                





































































































































































                                                                               
                                                              



                                                                         
                                                                  




                                                                              

                                                            
                                                            
                                                                  

















                                                                                
                                                                              









                                                                              
                                 










                                                                                
                                                   







                                                                         
                                       









                                                       

                                                          





                                                
                                                                              




                                                                             
                                              
                    
                                                   








                                                             
                                                                   

                                     
                                             

                                       
                                                                   
                                                    
 
/*
 * drv.c
 *
 * DSP-BIOS Bridge driver support functions for TI OMAP processors.
 *
 * DSP/BIOS Bridge resource allocation module.
 *
 * Copyright (C) 2005-2006 Texas Instruments, Inc.
 *
 * This package is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */
#include <linux/types.h>

/*  ----------------------------------- Host OS */
#include <dspbridge/host_os.h>

/*  ----------------------------------- DSP/BIOS Bridge */
#include <dspbridge/dbdefs.h>

/*  ----------------------------------- Trace & Debug */
#include <dspbridge/dbc.h>

/*  ----------------------------------- OS Adaptation Layer */
#include <dspbridge/cfg.h>
#include <dspbridge/list.h>

/*  ----------------------------------- This */
#include <dspbridge/drv.h>
#include <dspbridge/dev.h>

#include <dspbridge/node.h>
#include <dspbridge/proc.h>
#include <dspbridge/strm.h>
#include <dspbridge/nodepriv.h>
#include <dspbridge/dspchnl.h>
#include <dspbridge/resourcecleanup.h>

/*  ----------------------------------- Defines, Data Structures, Typedefs */
struct drv_object {
	struct lst_list *dev_list;
	struct lst_list *dev_node_string;
};

/*
 *  This is the Device Extension. Named with the Prefix
 *  DRV_ since it is living in this module
 */
struct drv_ext {
	struct list_head link;
	char sz_string[MAXREGPATHLENGTH];
};

/*  ----------------------------------- Globals */
static s32 refs;
static bool ext_phys_mem_pool_enabled;
struct ext_phys_mem_pool {
	u32 phys_mem_base;
	u32 phys_mem_size;
	u32 virt_mem_base;
	u32 next_phys_alloc_ptr;
};
static struct ext_phys_mem_pool ext_mem_pool;

/*  ----------------------------------- Function Prototypes */
static int request_bridge_resources(struct cfg_hostres *res);


/* GPP PROCESS CLEANUP CODE */

static int drv_proc_free_node_res(void *process_ctxt);

/* Allocate and add a node resource element
* This function is called from .Node_Allocate. */
int drv_insert_node_res_element(void *hnode, void *node_resource,
				       void *process_ctxt)
{
	struct node_res_object **node_res_obj =
	    (struct node_res_object **)node_resource;
	struct process_context *ctxt = (struct process_context *)process_ctxt;
	int status = 0;
	struct node_res_object *temp_node_res = NULL;

	*node_res_obj = kzalloc(sizeof(struct node_res_object), GFP_KERNEL);
	if (*node_res_obj == NULL)
		status = -EFAULT;

	if (DSP_SUCCEEDED(status)) {
		if (mutex_lock_interruptible(&ctxt->node_mutex)) {
			kfree(*node_res_obj);
			return -EPERM;
		}
		(*node_res_obj)->hnode = hnode;
		if (ctxt->node_list != NULL) {
			temp_node_res = ctxt->node_list;
			while (temp_node_res->next != NULL)
				temp_node_res = temp_node_res->next;

			temp_node_res->next = *node_res_obj;
		} else {
			ctxt->node_list = *node_res_obj;
		}
		mutex_unlock(&ctxt->node_mutex);
	}

	return status;
}

/* Release all Node resources and its context
* This is called from .Node_Delete. */
int drv_remove_node_res_element(void *node_resource, void *process_ctxt)
{
	struct node_res_object *node_res_obj =
	    (struct node_res_object *)node_resource;
	struct process_context *ctxt = (struct process_context *)process_ctxt;
	struct node_res_object *temp_node;
	int status = 0;

	if (mutex_lock_interruptible(&ctxt->node_mutex))
		return -EPERM;
	temp_node = ctxt->node_list;
	if (temp_node == node_res_obj) {
		ctxt->node_list = node_res_obj->next;
	} else {
		while (temp_node && temp_node->next != node_res_obj)
			temp_node = temp_node->next;
		if (!temp_node)
			status = -ENOENT;
		else
			temp_node->next = node_res_obj->next;
	}
	mutex_unlock(&ctxt->node_mutex);
	kfree(node_res_obj);
	return status;
}

/* Actual Node De-Allocation */
static int drv_proc_free_node_res(void *process_ctxt)
{
	struct process_context *ctxt = (struct process_context *)process_ctxt;
	int status = 0;
	struct node_res_object *node_list = NULL;
	struct node_res_object *node_res_obj = NULL;
	u32 node_state;

	node_list = ctxt->node_list;
	while (node_list != NULL) {
		node_res_obj = node_list;
		node_list = node_list->next;
		if (node_res_obj->node_allocated) {
			node_state = node_get_state(node_res_obj->hnode);
			if (node_state <= NODE_DELETING) {
				if ((node_state == NODE_RUNNING) ||
				    (node_state == NODE_PAUSED) ||
				    (node_state == NODE_TERMINATING))
					status = node_terminate
					    (node_res_obj->hnode, &status);

				status = node_delete(node_res_obj->hnode, ctxt);
			}
		}
	}
	return status;
}

/* Release all Mapped and Reserved DMM resources */
int drv_remove_all_dmm_res_elements(void *process_ctxt)
{
	struct process_context *ctxt = (struct process_context *)process_ctxt;
	int status = 0;
	struct dmm_map_object *temp_map, *map_obj;
	struct dmm_rsv_object *temp_rsv, *rsv_obj;

	/* Free DMM mapped memory resources */
	list_for_each_entry_safe(map_obj, temp_map, &ctxt->dmm_map_list, link) {
		status = proc_un_map(ctxt->hprocessor,
				     (void *)map_obj->dsp_addr, ctxt);
		if (DSP_FAILED(status))
			pr_err("%s: proc_un_map failed!"
			       " status = 0x%xn", __func__, status);
	}

	/* Free DMM reserved memory resources */
	list_for_each_entry_safe(rsv_obj, temp_rsv, &ctxt->dmm_rsv_list, link) {
		status = proc_un_reserve_memory(ctxt->hprocessor, (void *)
						rsv_obj->dsp_reserved_addr,
						ctxt);
		if (DSP_FAILED(status))
			pr_err("%s: proc_un_reserve_memory failed!"
			       " status = 0x%xn", __func__, status);
	}
	return status;
}

/* Update Node allocation status */
void drv_proc_node_update_status(void *node_resource, s32 status)
{
	struct node_res_object *node_res_obj =
	    (struct node_res_object *)node_resource;
	DBC_ASSERT(node_resource != NULL);
	node_res_obj->node_allocated = status;
}

/* Update Node Heap status */
void drv_proc_node_update_heap_status(void *node_resource, s32 status)
{
	struct node_res_object *node_res_obj =
	    (struct node_res_object *)node_resource;
	DBC_ASSERT(node_resource != NULL);
	node_res_obj->heap_allocated = status;
}

/* Release all Node resources and its context
* This is called from .bridge_release.
 */
int drv_remove_all_node_res_elements(void *process_ctxt)
{
	struct process_context *ctxt = (struct process_context *)process_ctxt;
	int status = 0;
	struct node_res_object *temp_node2 = NULL;
	struct node_res_object *temp_node = NULL;

	drv_proc_free_node_res(ctxt);
	temp_node = ctxt->node_list;
	while (temp_node != NULL) {
		temp_node2 = temp_node;
		temp_node = temp_node->next;
		kfree(temp_node2);
	}
	ctxt->node_list = NULL;
	return status;
}

/* Getting the node resource element */
int drv_get_node_res_element(void *hnode, void *node_resource,
				    void *process_ctxt)
{
	struct node_res_object **node_res =
				     (struct node_res_object **)node_resource;
	struct process_context *ctxt = (struct process_context *)process_ctxt;
	int status = 0;
	struct node_res_object *temp_node2 = NULL;
	struct node_res_object *temp_node = NULL;

	if (mutex_lock_interruptible(&ctxt->node_mutex))
		return -EPERM;

	temp_node = ctxt->node_list;
	while ((temp_node != NULL) && (temp_node->hnode != hnode)) {
		temp_node2 = temp_node;
		temp_node = temp_node->next;
	}

	mutex_unlock(&ctxt->node_mutex);

	if (temp_node != NULL)
		*node_res = temp_node;
	else
		status = -ENOENT;

	return status;
}

/* Allocate the STRM resource element
* This is called after the actual resource is allocated
 */
int drv_proc_insert_strm_res_element(void *stream_obj,
					    void *strm_res, void *process_ctxt)
{
	struct strm_res_object **pstrm_res =
	    (struct strm_res_object **)strm_res;
	struct process_context *ctxt = (struct process_context *)process_ctxt;
	int status = 0;
	struct strm_res_object *temp_strm_res = NULL;

	*pstrm_res = kzalloc(sizeof(struct strm_res_object), GFP_KERNEL);
	if (*pstrm_res == NULL)
		status = -EFAULT;

	if (DSP_SUCCEEDED(status)) {
		if (mutex_lock_interruptible(&ctxt->strm_mutex)) {
			kfree(*pstrm_res);
			return -EPERM;
		}
		(*pstrm_res)->hstream = stream_obj;
		if (ctxt->pstrm_list != NULL) {
			temp_strm_res = ctxt->pstrm_list;
			while (temp_strm_res->next != NULL)
				temp_strm_res = temp_strm_res->next;

			temp_strm_res->next = *pstrm_res;
		} else {
			ctxt->pstrm_list = *pstrm_res;
		}
		mutex_unlock(&ctxt->strm_mutex);
	}
	return status;
}

/* Release Stream resource element context
* This function called after the actual resource is freed
 */
int drv_proc_remove_strm_res_element(void *strm_res, void *process_ctxt)
{
	struct strm_res_object *pstrm_res = (struct strm_res_object *)strm_res;
	struct process_context *ctxt = (struct process_context *)process_ctxt;
	struct strm_res_object *temp_strm_res;
	int status = 0;

	if (mutex_lock_interruptible(&ctxt->strm_mutex))
		return -EPERM;
	temp_strm_res = ctxt->pstrm_list;

	if (ctxt->pstrm_list == pstrm_res) {
		ctxt->pstrm_list = pstrm_res->next;
	} else {
		while (temp_strm_res && temp_strm_res->next != pstrm_res)
			temp_strm_res = temp_strm_res->next;
		if (temp_strm_res == NULL)
			status = -ENOENT;
		else
			temp_strm_res->next = pstrm_res->next;
	}
	mutex_unlock(&ctxt->strm_mutex);
	kfree(pstrm_res);
	return status;
}

/* Release all Stream resources and its context
* This is called from .bridge_release.
 */
int drv_remove_all_strm_res_elements(void *process_ctxt)
{
	struct process_context *ctxt = (struct process_context *)process_ctxt;
	int status = 0;
	struct strm_res_object *strm_res = NULL;
	struct strm_res_object *strm_tmp = NULL;
	struct stream_info strm_info;
	struct dsp_streaminfo user;
	u8 **ap_buffer = NULL;
	u8 *buf_ptr;
	u32 ul_bytes;
	u32 dw_arg;
	s32 ul_buf_size;

	strm_tmp = ctxt->pstrm_list;
	while (strm_tmp) {
		strm_res = strm_tmp;
		strm_tmp = strm_tmp->next;
		if (strm_res->num_bufs) {
			ap_buffer = kmalloc((strm_res->num_bufs *
					sizeof(u8 *)), GFP_KERNEL);
			if (ap_buffer) {
				status = strm_free_buffer(strm_res->hstream,
							  ap_buffer,
							  strm_res->num_bufs,
							  ctxt);
				kfree(ap_buffer);
			}
		}
		strm_info.user_strm = &user;
		user.number_bufs_in_stream = 0;
		strm_get_info(strm_res->hstream, &strm_info, sizeof(strm_info));
		while (user.number_bufs_in_stream--)
			strm_reclaim(strm_res->hstream, &buf_ptr, &ul_bytes,
				     (u32 *) &ul_buf_size, &dw_arg);
		status = strm_close(strm_res->hstream, ctxt);
	}
	return status;
}

/* Getting the stream resource element */
int drv_get_strm_res_element(void *stream_obj, void *strm_resources,
				    void *process_ctxt)
{
	struct strm_res_object **strm_res =
	    (struct strm_res_object **)strm_resources;
	struct process_context *ctxt = (struct process_context *)process_ctxt;
	int status = 0;
	struct strm_res_object *temp_strm2 = NULL;
	struct strm_res_object *temp_strm;

	if (mutex_lock_interruptible(&ctxt->strm_mutex))
		return -EPERM;

	temp_strm = ctxt->pstrm_list;
	while ((temp_strm != NULL) && (temp_strm->hstream != stream_obj)) {
		temp_strm2 = temp_strm;
		temp_strm = temp_strm->next;
	}

	mutex_unlock(&ctxt->strm_mutex);

	if (temp_strm != NULL)
		*strm_res = temp_strm;
	else
		status = -ENOENT;

	return status;
}

/* Updating the stream resource element */
int drv_proc_update_strm_res(u32 num_bufs, void *strm_resources)
{
	int status = 0;
	struct strm_res_object **strm_res =
	    (struct strm_res_object **)strm_resources;

	(*strm_res)->num_bufs = num_bufs;
	return status;
}

/* GPP PROCESS CLEANUP CODE END */

/*
 *  ======== = drv_create ======== =
 *  Purpose:
 *      DRV Object gets created only once during Driver Loading.
 */
int drv_create(OUT struct drv_object **drv_obj)
{
	int status = 0;
	struct drv_object *pdrv_object = NULL;

	DBC_REQUIRE(drv_obj != NULL);
	DBC_REQUIRE(refs > 0);

	pdrv_object = kzalloc(sizeof(struct drv_object), GFP_KERNEL);
	if (pdrv_object) {
		/* Create and Initialize List of device objects */
		pdrv_object->dev_list = kzalloc(sizeof(struct lst_list),
							GFP_KERNEL);
		if (pdrv_object->dev_list) {
			/* Create and Initialize List of device Extension */
			pdrv_object->dev_node_string =
				kzalloc(sizeof(struct lst_list), GFP_KERNEL);
			if (!(pdrv_object->dev_node_string)) {
				status = -EPERM;
			} else {
				INIT_LIST_HEAD(&pdrv_object->
					       dev_node_string->head);
				INIT_LIST_HEAD(&pdrv_object->dev_list->head);
			}
		} else {
			status = -ENOMEM;
		}
	} else {
		status = -ENOMEM;
	}
	/* Store the DRV Object in the Registry */
	if (DSP_SUCCEEDED(status))
		status = cfg_set_object((u32) pdrv_object, REG_DRV_OBJECT);
	if (DSP_SUCCEEDED(status)) {
		*drv_obj = pdrv_object;
	} else {
		kfree(pdrv_object->dev_list);
		kfree(pdrv_object->dev_node_string);
		/* Free the DRV Object */
		kfree(pdrv_object);
	}

	DBC_ENSURE(DSP_FAILED(status) || pdrv_object);
	return status;
}

/*
 *  ======== drv_exit ========
 *  Purpose:
 *      Discontinue usage of the DRV module.
 */
void drv_exit(void)
{
	DBC_REQUIRE(refs > 0);

	refs--;

	DBC_ENSURE(refs >= 0);
}

/*
 *  ======== = drv_destroy ======== =
 *  purpose:
 *      Invoked during bridge de-initialization
 */
int drv_destroy(struct drv_object *driver_obj)
{
	int status = 0;
	struct drv_object *pdrv_object = (struct drv_object *)driver_obj;

	DBC_REQUIRE(refs > 0);
	DBC_REQUIRE(pdrv_object);

	/*
	 *  Delete the List if it exists.Should not come here
	 *  as the drv_remove_dev_object and the Last drv_request_resources
	 *  removes the list if the lists are empty.
	 */
	kfree(pdrv_object->dev_list);
	kfree(pdrv_object->dev_node_string);
	kfree(pdrv_object);
	/* Update the DRV Object in Registry to be 0 */
	(void)cfg_set_object(0, REG_DRV_OBJECT);

	return status;
}

/*
 *  ======== drv_get_dev_object ========
 *  Purpose:
 *      Given a index, returns a handle to DevObject from the list.
 */
int drv_get_dev_object(u32 index, struct drv_object *hdrv_obj,
			      struct dev_object **device_obj)
{
	int status = 0;
#ifdef CONFIG_TIDSPBRIDGE_DEBUG
	/* used only for Assertions and debug messages */
	struct drv_object *pdrv_obj = (struct drv_object *)hdrv_obj;
#endif
	struct dev_object *dev_obj;
	u32 i;
	DBC_REQUIRE(pdrv_obj);
	DBC_REQUIRE(device_obj != NULL);
	DBC_REQUIRE(index >= 0);
	DBC_REQUIRE(refs > 0);
	DBC_ASSERT(!(LST_IS_EMPTY(pdrv_obj->dev_list)));

	dev_obj = (struct dev_object *)drv_get_first_dev_object();
	for (i = 0; i < index; i++) {
		dev_obj =
		    (struct dev_object *)drv_get_next_dev_object((u32) dev_obj);
	}
	if (dev_obj) {
		*device_obj = (struct dev_object *)dev_obj;
	} else {
		*device_obj = NULL;
		status = -EPERM;
	}

	return status;
}

/*
 *  ======== drv_get_first_dev_object ========
 *  Purpose:
 *      Retrieve the first Device Object handle from an internal linked list of
 *      of DEV_OBJECTs maintained by DRV.
 */
u32 drv_get_first_dev_object(void)
{
	u32 dw_dev_object = 0;
	struct drv_object *pdrv_obj;

	if (DSP_SUCCEEDED(cfg_get_object((u32 *) &pdrv_obj, REG_DRV_OBJECT))) {
		if ((pdrv_obj->dev_list != NULL) &&
		    !LST_IS_EMPTY(pdrv_obj->dev_list))
			dw_dev_object = (u32) lst_first(pdrv_obj->dev_list);
	}

	return dw_dev_object;
}

/*
 *  ======== DRV_GetFirstDevNodeString ========
 *  Purpose:
 *      Retrieve the first Device Extension from an internal linked list of
 *      of Pointer to dev_node Strings maintained by DRV.
 */
u32 drv_get_first_dev_extension(void)
{
	u32 dw_dev_extension = 0;
	struct drv_object *pdrv_obj;

	if (DSP_SUCCEEDED(cfg_get_object((u32 *) &pdrv_obj, REG_DRV_OBJECT))) {

		if ((pdrv_obj->dev_node_string != NULL) &&
		    !LST_IS_EMPTY(pdrv_obj->dev_node_string)) {
			dw_dev_extension =
			    (u32) lst_first(pdrv_obj->dev_node_string);
		}
	}

	return dw_dev_extension;
}

/*
 *  ======== drv_get_next_dev_object ========
 *  Purpose:
 *      Retrieve the next Device Object handle from an internal linked list of
 *      of DEV_OBJECTs maintained by DRV, after having previously called
 *      drv_get_first_dev_object() and zero or more DRV_GetNext.
 */
u32 drv_get_next_dev_object(u32 hdev_obj)
{
	u32 dw_next_dev_object = 0;
	struct drv_object *pdrv_obj;

	DBC_REQUIRE(hdev_obj != 0);

	if (DSP_SUCCEEDED(cfg_get_object((u32 *) &pdrv_obj, REG_DRV_OBJECT))) {

		if ((pdrv_obj->dev_list != NULL) &&
		    !LST_IS_EMPTY(pdrv_obj->dev_list)) {
			dw_next_dev_object = (u32) lst_next(pdrv_obj->dev_list,
							    (struct list_head *)
							    hdev_obj);
		}
	}
	return dw_next_dev_object;
}

/*
 *  ======== drv_get_next_dev_extension ========
 *  Purpose:
 *      Retrieve the next Device Extension from an internal linked list of
 *      of pointer to DevNodeString maintained by DRV, after having previously
 *      called drv_get_first_dev_extension() and zero or more
 *      drv_get_next_dev_extension().
 */
u32 drv_get_next_dev_extension(u32 dev_extension)
{
	u32 dw_dev_extension = 0;
	struct drv_object *pdrv_obj;

	DBC_REQUIRE(dev_extension != 0);

	if (DSP_SUCCEEDED(cfg_get_object((u32 *) &pdrv_obj, REG_DRV_OBJECT))) {
		if ((pdrv_obj->dev_node_string != NULL) &&
		    !LST_IS_EMPTY(pdrv_obj->dev_node_string)) {
			dw_dev_extension =
			    (u32) lst_next(pdrv_obj->dev_node_string,
					   (struct list_head *)dev_extension);
		}
	}

	return dw_dev_extension;
}

/*
 *  ======== drv_init ========
 *  Purpose:
 *      Initialize DRV module private state.
 */
int drv_init(void)
{
	s32 ret = 1;		/* function return value */

	DBC_REQUIRE(refs >= 0);

	if (ret)
		refs++;

	DBC_ENSURE((ret && (refs > 0)) || (!ret && (refs >= 0)));

	return ret;
}

/*
 *  ======== drv_insert_dev_object ========
 *  Purpose:
 *      Insert a DevObject into the list of Manager object.
 */
int drv_insert_dev_object(struct drv_object *driver_obj,
				 struct dev_object *hdev_obj)
{
	int status = 0;
	struct drv_object *pdrv_object = (struct drv_object *)driver_obj;

	DBC_REQUIRE(refs > 0);
	DBC_REQUIRE(hdev_obj != NULL);
	DBC_REQUIRE(pdrv_object);
	DBC_ASSERT(pdrv_object->dev_list);

	lst_put_tail(pdrv_object->dev_list, (struct list_head *)hdev_obj);

	DBC_ENSURE(DSP_SUCCEEDED(status)
		   && !LST_IS_EMPTY(pdrv_object->dev_list));

	return status;
}

/*
 *  ======== drv_remove_dev_object ========
 *  Purpose:
 *      Search for and remove a DeviceObject from the given list of DRV
 *      objects.
 */
int drv_remove_dev_object(struct drv_object *driver_obj,
				 struct dev_object *hdev_obj)
{
	int status = -EPERM;
	struct drv_object *pdrv_object = (struct drv_object *)driver_obj;
	struct list_head *cur_elem;

	DBC_REQUIRE(refs > 0);
	DBC_REQUIRE(pdrv_object);
	DBC_REQUIRE(hdev_obj != NULL);

	DBC_REQUIRE(pdrv_object->dev_list != NULL);
	DBC_REQUIRE(!LST_IS_EMPTY(pdrv_object->dev_list));

	/* Search list for p_proc_object: */
	for (cur_elem = lst_first(pdrv_object->dev_list); cur_elem != NULL;
	     cur_elem = lst_next(pdrv_object->dev_list, cur_elem)) {
		/* If found, remove it. */
		if ((struct dev_object *)cur_elem == hdev_obj) {
			lst_remove_elem(pdrv_object->dev_list, cur_elem);
			status = 0;
			break;
		}
	}
	/* Remove list if empty. */
	if (LST_IS_EMPTY(pdrv_object->dev_list)) {
		kfree(pdrv_object->dev_list);
		pdrv_object->dev_list = NULL;
	}
	DBC_ENSURE((pdrv_object->dev_list == NULL) ||
		   !LST_IS_EMPTY(pdrv_object->dev_list));

	return status;
}

/*
 *  ======== drv_request_resources ========
 *  Purpose:
 *      Requests  resources from the OS.
 */
int drv_request_resources(u32 dw_context, u32 *dev_node_strg)
{
	int status = 0;
	struct drv_object *pdrv_object;
	struct drv_ext *pszdev_node;

	DBC_REQUIRE(dw_context != 0);
	DBC_REQUIRE(dev_node_strg != NULL);

	/*
	 *  Allocate memory to hold the string. This will live untill
	 *  it is freed in the Release resources. Update the driver object
	 *  list.
	 */

	status = cfg_get_object((u32 *) &pdrv_object, REG_DRV_OBJECT);
	if (DSP_SUCCEEDED(status)) {
		pszdev_node = kzalloc(sizeof(struct drv_ext), GFP_KERNEL);
		if (pszdev_node) {
			lst_init_elem(&pszdev_node->link);
			strncpy(pszdev_node->sz_string,
				(char *)dw_context, MAXREGPATHLENGTH - 1);
			pszdev_node->sz_string[MAXREGPATHLENGTH - 1] = '\0';
			/* Update the Driver Object List */
			*dev_node_strg = (u32) pszdev_node->sz_string;
			lst_put_tail(pdrv_object->dev_node_string,
				     (struct list_head *)pszdev_node);
		} else {
			status = -ENOMEM;
			*dev_node_strg = 0;
		}
	} else {
		dev_dbg(bridge, "%s: Failed to get Driver Object from Registry",
			__func__);
		*dev_node_strg = 0;
	}

	DBC_ENSURE((DSP_SUCCEEDED(status) && dev_node_strg != NULL &&
		    !LST_IS_EMPTY(pdrv_object->dev_node_string)) ||
		   (DSP_FAILED(status) && *dev_node_strg == 0));

	return status;
}

/*
 *  ======== drv_release_resources ========
 *  Purpose:
 *      Releases  resources from the OS.
 */
int drv_release_resources(u32 dw_context, struct drv_object *hdrv_obj)
{
	int status = 0;
	struct drv_object *pdrv_object = (struct drv_object *)hdrv_obj;
	struct drv_ext *pszdev_node;

	/*
	 *  Irrespective of the status go ahead and clean it
	 *  The following will over write the status.
	 */
	for (pszdev_node = (struct drv_ext *)drv_get_first_dev_extension();
	     pszdev_node != NULL; pszdev_node = (struct drv_ext *)
	     drv_get_next_dev_extension((u32) pszdev_node)) {
		if (!pdrv_object->dev_node_string) {
			/* When this could happen? */
			continue;
		}
		if ((u32) pszdev_node == dw_context) {
			/* Found it */
			/* Delete from the Driver object list */
			lst_remove_elem(pdrv_object->dev_node_string,
					(struct list_head *)pszdev_node);
			kfree((void *)pszdev_node);
			break;
		}
		/* Delete the List if it is empty */
		if (LST_IS_EMPTY(pdrv_object->dev_node_string)) {
			kfree(pdrv_object->dev_node_string);
			pdrv_object->dev_node_string = NULL;
		}
	}
	return status;
}

/*
 *  ======== request_bridge_resources ========
 *  Purpose:
 *      Reserves shared memory for bridge.
 */
static int request_bridge_resources(struct cfg_hostres *res)
{
	int status = 0;
	struct cfg_hostres *host_res = res;

	/* num_mem_windows must not be more than CFG_MAXMEMREGISTERS */
	host_res->num_mem_windows = 2;

	/* First window is for DSP internal memory */
	host_res->dw_sys_ctrl_base = ioremap(OMAP_SYSC_BASE, OMAP_SYSC_SIZE);
	dev_dbg(bridge, "dw_mem_base[0] 0x%x\n", host_res->dw_mem_base[0]);
	dev_dbg(bridge, "dw_mem_base[3] 0x%x\n", host_res->dw_mem_base[3]);
	dev_dbg(bridge, "dw_dmmu_base %p\n", host_res->dw_dmmu_base);

	/* for 24xx base port is not mapping the mamory for DSP
	 * internal memory TODO Do a ioremap here */
	/* Second window is for DSP external memory shared with MPU */

	/* These are hard-coded values */
	host_res->birq_registers = 0;
	host_res->birq_attrib = 0;
	host_res->dw_offset_for_monitor = 0;
	host_res->dw_chnl_offset = 0;
	/* CHNL_MAXCHANNELS */
	host_res->dw_num_chnls = CHNL_MAXCHANNELS;
	host_res->dw_chnl_buf_size = 0x400;

	return status;
}

/*
 *  ======== drv_request_bridge_res_dsp ========
 *  Purpose:
 *      Reserves shared memory for bridge.
 */
int drv_request_bridge_res_dsp(void **phost_resources)
{
	int status = 0;
	struct cfg_hostres *host_res;
	u32 dw_buff_size;
	u32 dma_addr;
	u32 shm_size;
	struct drv_data *drv_datap = dev_get_drvdata(bridge);

	dw_buff_size = sizeof(struct cfg_hostres);

	host_res = kzalloc(dw_buff_size, GFP_KERNEL);

	if (host_res != NULL) {
		request_bridge_resources(host_res);
		/* num_mem_windows must not be more than CFG_MAXMEMREGISTERS */
		host_res->num_mem_windows = 4;

		host_res->dw_mem_base[0] = 0;
		host_res->dw_mem_base[2] = (u32) ioremap(OMAP_DSP_MEM1_BASE,
							 OMAP_DSP_MEM1_SIZE);
		host_res->dw_mem_base[3] = (u32) ioremap(OMAP_DSP_MEM2_BASE,
							 OMAP_DSP_MEM2_SIZE);
		host_res->dw_mem_base[4] = (u32) ioremap(OMAP_DSP_MEM3_BASE,
							 OMAP_DSP_MEM3_SIZE);
		host_res->dw_per_base = ioremap(OMAP_PER_CM_BASE,
						OMAP_PER_CM_SIZE);
		host_res->dw_per_pm_base = (u32) ioremap(OMAP_PER_PRM_BASE,
							 OMAP_PER_PRM_SIZE);
		host_res->dw_core_pm_base = (u32) ioremap(OMAP_CORE_PRM_BASE,
							  OMAP_CORE_PRM_SIZE);
		host_res->dw_dmmu_base = ioremap(OMAP_DMMU_BASE,
						 OMAP_DMMU_SIZE);

		dev_dbg(bridge, "dw_mem_base[0] 0x%x\n",
			host_res->dw_mem_base[0]);
		dev_dbg(bridge, "dw_mem_base[1] 0x%x\n",
			host_res->dw_mem_base[1]);
		dev_dbg(bridge, "dw_mem_base[2] 0x%x\n",
			host_res->dw_mem_base[2]);
		dev_dbg(bridge, "dw_mem_base[3] 0x%x\n",
			host_res->dw_mem_base[3]);
		dev_dbg(bridge, "dw_mem_base[4] 0x%x\n",
			host_res->dw_mem_base[4]);
		dev_dbg(bridge, "dw_dmmu_base %p\n", host_res->dw_dmmu_base);

		shm_size = drv_datap->shm_size;
		if (shm_size >= 0x10000) {
			/* Allocate Physically contiguous,
			 * non-cacheable  memory */
			host_res->dw_mem_base[1] =
			    (u32) mem_alloc_phys_mem(shm_size, 0x100000,
						     &dma_addr);
			if (host_res->dw_mem_base[1] == 0) {
				status = -ENOMEM;
				pr_err("shm reservation Failed\n");
			} else {
				host_res->dw_mem_length[1] = shm_size;
				host_res->dw_mem_phys[1] = dma_addr;

				dev_dbg(bridge, "%s: Bridge shm address 0x%x "
					"dma_addr %x size %x\n", __func__,
					host_res->dw_mem_base[1],
					dma_addr, shm_size);
			}
		}
		if (DSP_SUCCEEDED(status)) {
			/* These are hard-coded values */
			host_res->birq_registers = 0;
			host_res->birq_attrib = 0;
			host_res->dw_offset_for_monitor = 0;
			host_res->dw_chnl_offset = 0;
			/* CHNL_MAXCHANNELS */
			host_res->dw_num_chnls = CHNL_MAXCHANNELS;
			host_res->dw_chnl_buf_size = 0x400;
			dw_buff_size = sizeof(struct cfg_hostres);
		}
		*phost_resources = host_res;
	}
	/* End Mem alloc */
	return status;
}

void mem_ext_phys_pool_init(u32 pool_phys_base, u32 pool_size)
{
	u32 pool_virt_base;

	/* get the virtual address for the physical memory pool passed */
	pool_virt_base = (u32) ioremap(pool_phys_base, pool_size);

	if ((void **)pool_virt_base == NULL) {
		pr_err("%s: external physical memory map failed\n", __func__);
		ext_phys_mem_pool_enabled = false;
	} else {
		ext_mem_pool.phys_mem_base = pool_phys_base;
		ext_mem_pool.phys_mem_size = pool_size;
		ext_mem_pool.virt_mem_base = pool_virt_base;
		ext_mem_pool.next_phys_alloc_ptr = pool_phys_base;
		ext_phys_mem_pool_enabled = true;
	}
}

void mem_ext_phys_pool_release(void)
{
	if (ext_phys_mem_pool_enabled) {
		iounmap((void *)(ext_mem_pool.virt_mem_base));
		ext_phys_mem_pool_enabled = false;
	}
}

/*
 *  ======== mem_ext_phys_mem_alloc ========
 *  Purpose:
 *     Allocate physically contiguous, uncached memory from external memory pool
 */

static void *mem_ext_phys_mem_alloc(u32 bytes, u32 align, OUT u32 * phys_addr)
{
	u32 new_alloc_ptr;
	u32 offset;
	u32 virt_addr;

	if (align == 0)
		align = 1;

	if (bytes > ((ext_mem_pool.phys_mem_base + ext_mem_pool.phys_mem_size)
		     - ext_mem_pool.next_phys_alloc_ptr)) {
		phys_addr = NULL;
		return NULL;
	} else {
		offset = (ext_mem_pool.next_phys_alloc_ptr & (align - 1));
		if (offset == 0)
			new_alloc_ptr = ext_mem_pool.next_phys_alloc_ptr;
		else
			new_alloc_ptr = (ext_mem_pool.next_phys_alloc_ptr) +
			    (align - offset);
		if ((new_alloc_ptr + bytes) <=
		    (ext_mem_pool.phys_mem_base + ext_mem_pool.phys_mem_size)) {
			/* we can allocate */
			*phys_addr = new_alloc_ptr;
			ext_mem_pool.next_phys_alloc_ptr =
			    new_alloc_ptr + bytes;
			virt_addr =
			    ext_mem_pool.virt_mem_base + (new_alloc_ptr -
							  ext_mem_pool.
							  phys_mem_base);
			return (void *)virt_addr;
		} else {
			*phys_addr = 0;
			return NULL;
		}
	}
}

/*
 *  ======== mem_alloc_phys_mem ========
 *  Purpose:
 *      Allocate physically contiguous, uncached memory
 */
void *mem_alloc_phys_mem(u32 byte_size, u32 align_mask,
				OUT u32 *physical_address)
{
	void *va_mem = NULL;
	dma_addr_t pa_mem;

	if (byte_size > 0) {
		if (ext_phys_mem_pool_enabled) {
			va_mem = mem_ext_phys_mem_alloc(byte_size, align_mask,
							(u32 *) &pa_mem);
		} else
			va_mem = dma_alloc_coherent(NULL, byte_size, &pa_mem,
								GFP_KERNEL);
		if (va_mem == NULL)
			*physical_address = 0;
		else
			*physical_address = pa_mem;
	}
	return va_mem;
}

/*
 *  ======== mem_free_phys_mem ========
 *  Purpose:
 *      Free the given block of physically contiguous memory.
 */
void mem_free_phys_mem(void *virtual_address, u32 physical_address,
		       u32 byte_size)
{
	DBC_REQUIRE(virtual_address != NULL);

	if (!ext_phys_mem_pool_enabled)
		dma_free_coherent(NULL, byte_size, virtual_address,
				  physical_address);
}