/* * node.c * * DSP-BIOS Bridge driver support functions for TI OMAP processors. * * DSP/BIOS Bridge Node Manager. * * 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> #include <linux/bitmap.h> #include <linux/list.h> /* ----------------------------------- Host OS */ #include <dspbridge/host_os.h> /* ----------------------------------- DSP/BIOS Bridge */ #include <dspbridge/dbdefs.h> /* ----------------------------------- OS Adaptation Layer */ #include <dspbridge/memdefs.h> #include <dspbridge/proc.h> #include <dspbridge/strm.h> #include <dspbridge/sync.h> #include <dspbridge/ntfy.h> /* ----------------------------------- Platform Manager */ #include <dspbridge/cmm.h> #include <dspbridge/cod.h> #include <dspbridge/dev.h> #include <dspbridge/msg.h> /* ----------------------------------- Resource Manager */ #include <dspbridge/dbdcd.h> #include <dspbridge/disp.h> #include <dspbridge/rms_sh.h> /* ----------------------------------- Link Driver */ #include <dspbridge/dspdefs.h> #include <dspbridge/dspioctl.h> /* ----------------------------------- Others */ #include <dspbridge/uuidutil.h> /* ----------------------------------- This */ #include <dspbridge/nodepriv.h> #include <dspbridge/node.h> #include <dspbridge/dmm.h> /* Static/Dynamic Loader includes */ #include <dspbridge/dbll.h> #include <dspbridge/nldr.h> #include <dspbridge/drv.h> #include <dspbridge/resourcecleanup.h> #include <_tiomap.h> #include <dspbridge/dspdeh.h> #define HOSTPREFIX "/host" #define PIPEPREFIX "/dbpipe" #define MAX_INPUTS(h) \ ((h)->dcd_props.obj_data.node_obj.ndb_props.num_input_streams) #define MAX_OUTPUTS(h) \ ((h)->dcd_props.obj_data.node_obj.ndb_props.num_output_streams) #define NODE_GET_PRIORITY(h) ((h)->prio) #define NODE_SET_PRIORITY(hnode, prio) ((hnode)->prio = prio) #define NODE_SET_STATE(hnode, state) ((hnode)->node_state = state) #define MAXPIPES 100 /* Max # of /pipe connections (CSL limit) */ #define MAXDEVSUFFIXLEN 2 /* Max(Log base 10 of MAXPIPES, MAXSTREAMS) */ #define PIPENAMELEN (sizeof(PIPEPREFIX) + MAXDEVSUFFIXLEN) #define HOSTNAMELEN (sizeof(HOSTPREFIX) + MAXDEVSUFFIXLEN) #define MAXDEVNAMELEN 32 /* dsp_ndbprops.ac_name size */ #define CREATEPHASE 1 #define EXECUTEPHASE 2 #define DELETEPHASE 3 /* Define default STRM parameters */ /* * TBD: Put in header file, make global DSP_STRMATTRS with defaults, * or make defaults configurable. */ #define DEFAULTBUFSIZE 32 #define DEFAULTNBUFS 2 #define DEFAULTSEGID 0 #define DEFAULTALIGNMENT 0 #define DEFAULTTIMEOUT 10000 #define RMSQUERYSERVER 0 #define RMSCONFIGURESERVER 1 #define RMSCREATENODE 2 #define RMSEXECUTENODE 3 #define RMSDELETENODE 4 #define RMSCHANGENODEPRIORITY 5 #define RMSREADMEMORY 6 #define RMSWRITEMEMORY 7 #define RMSCOPY 8 #define MAXTIMEOUT 2000 #define NUMRMSFXNS 9 #define PWR_TIMEOUT 500 /* default PWR timeout in msec */ #define STACKSEGLABEL "L1DSRAM_HEAP" /* Label for DSP Stack Segment Addr */ /* * ======== node_mgr ======== */ struct node_mgr { struct dev_object *dev_obj; /* Device object */ /* Function interface to Bridge driver */ struct bridge_drv_interface *intf_fxns; struct dcd_manager *dcd_mgr; /* Proc/Node data manager */ struct disp_object *disp_obj; /* Node dispatcher */ struct list_head node_list; /* List of all allocated nodes */ u32 num_nodes; /* Number of nodes in node_list */ u32 num_created; /* Number of nodes *created* on DSP */ DECLARE_BITMAP(pipe_map, MAXPIPES); /* Pipe connection bitmap */ DECLARE_BITMAP(pipe_done_map, MAXPIPES); /* Pipes that are half free */ /* Channel allocation bitmap */ DECLARE_BITMAP(chnl_map, CHNL_MAXCHANNELS); /* DMA Channel allocation bitmap */ DECLARE_BITMAP(dma_chnl_map, CHNL_MAXCHANNELS); /* Zero-Copy Channel alloc bitmap */ DECLARE_BITMAP(zc_chnl_map, CHNL_MAXCHANNELS); struct ntfy_object *ntfy_obj; /* Manages registered notifications */ struct mutex node_mgr_lock; /* For critical sections */ u32 fxn_addrs[NUMRMSFXNS]; /* RMS function addresses */ struct msg_mgr *msg_mgr_obj; /* Processor properties needed by Node Dispatcher */ u32 num_chnls; /* Total number of channels */ u32 chnl_offset; /* Offset of chnl ids rsvd for RMS */ u32 chnl_buf_size; /* Buffer size for data to RMS */ int proc_family; /* eg, 5000 */ int proc_type; /* eg, 5510 */ u32 dsp_word_size; /* Size of DSP word on host bytes */ u32 dsp_data_mau_size; /* Size of DSP data MAU */ u32 dsp_mau_size; /* Size of MAU */ s32 min_pri; /* Minimum runtime priority for node */ s32 max_pri; /* Maximum runtime priority for node */ struct strm_mgr *strm_mgr_obj; /* STRM manager */ /* Loader properties */ struct nldr_object *nldr_obj; /* Handle to loader */ struct node_ldr_fxns nldr_fxns; /* Handle to loader functions */ }; /* * ======== connecttype ======== */ enum connecttype { NOTCONNECTED = 0, NODECONNECT, HOSTCONNECT, DEVICECONNECT, }; /* * ======== stream_chnl ======== */ struct stream_chnl { enum connecttype type; /* Type of stream connection */ u32 dev_id; /* pipe or channel id */ }; /* * ======== node_object ======== */ struct node_object { struct list_head list_elem; struct node_mgr *node_mgr; /* The manager of this node */ struct proc_object *processor; /* Back pointer to processor */ struct dsp_uuid node_uuid; /* Node's ID */ s32 prio; /* Node's current priority */ u32 timeout; /* Timeout for blocking NODE calls */ u32 heap_size; /* Heap Size */ u32 dsp_heap_virt_addr; /* Heap Size */ u32 gpp_heap_virt_addr; /* Heap Size */ enum node_type ntype; /* Type of node: message, task, etc */ enum node_state node_state; /* NODE_ALLOCATED, NODE_CREATED, ... */ u32 num_inputs; /* Current number of inputs */ u32 num_outputs; /* Current number of outputs */ u32 max_input_index; /* Current max input stream index */ u32 max_output_index; /* Current max output stream index */ struct stream_chnl *inputs; /* Node's input streams */ struct stream_chnl *outputs; /* Node's output streams */ struct node_createargs create_args; /* Args for node create func */ nodeenv node_env; /* Environment returned by RMS */ struct dcd_genericobj dcd_props; /* Node properties from DCD */ struct dsp_cbdata *args; /* Optional args to pass to node */ struct ntfy_object *ntfy_obj; /* Manages registered notifications */ char *str_dev_name; /* device name, if device node */ struct sync_object *sync_done; /* Synchronize node_terminate */ s32 exit_status; /* execute function return status */ /* Information needed for node_get_attr() */ void *device_owner; /* If dev node, task that owns it */ u32 num_gpp_inputs; /* Current # of from GPP streams */ u32 num_gpp_outputs; /* Current # of to GPP streams */ /* Current stream connections */ struct dsp_streamconnect *stream_connect; /* Message queue */ struct msg_queue *msg_queue_obj; /* These fields used for SM messaging */ struct cmm_xlatorobject *xlator; /* Node's SM addr translator */ /* Handle to pass to dynamic loader */ struct nldr_nodeobject *nldr_node_obj; bool loaded; /* Code is (dynamically) loaded */ bool phase_split; /* Phases split in many libs or ovly */ }; /* Default buffer attributes */ static struct dsp_bufferattr node_dfltbufattrs = { .cb_struct = 0, .segment_id = 1, .buf_alignment = 0, }; static void delete_node(struct node_object *hnode, struct process_context *pr_ctxt); static void delete_node_mgr(struct node_mgr *hnode_mgr); static void fill_stream_connect(struct node_object *node1, struct node_object *node2, u32 stream1, u32 stream2); static void fill_stream_def(struct node_object *hnode, struct node_strmdef *pstrm_def, struct dsp_strmattr *pattrs); static void free_stream(struct node_mgr *hnode_mgr, struct stream_chnl stream); static int get_fxn_address(struct node_object *hnode, u32 * fxn_addr, u32 phase); static int get_node_props(struct dcd_manager *hdcd_mgr, struct node_object *hnode, const struct dsp_uuid *node_uuid, struct dcd_genericobj *dcd_prop); static int get_proc_props(struct node_mgr *hnode_mgr, struct dev_object *hdev_obj); static int get_rms_fxns(struct node_mgr *hnode_mgr); static u32 ovly(void *priv_ref, u32 dsp_run_addr, u32 dsp_load_addr, u32 ul_num_bytes, u32 mem_space); static u32 mem_write(void *priv_ref, u32 dsp_add, void *pbuf, u32 ul_num_bytes, u32 mem_space); /* Dynamic loader functions. */ static struct node_ldr_fxns nldr_fxns = { nldr_allocate, nldr_create, nldr_delete, nldr_get_fxn_addr, nldr_load, nldr_unload, }; enum node_state node_get_state(void *hnode) { struct node_object *pnode = (struct node_object *)hnode; if (!pnode) return -1; return pnode->node_state; } /* * ======== node_allocate ======== * Purpose: * Allocate GPP resources to manage a node on the DSP. */ int node_allocate(struct proc_object *hprocessor, const struct dsp_uuid *node_uuid, const struct dsp_cbdata *pargs, const struct dsp_nodeattrin *attr_in, struct node_res_object **noderes, struct process_context *pr_ctxt) { struct node_mgr *hnode_mgr; struct dev_object *hdev_obj; struct node_object *pnode = NULL; enum node_type node_type = NODE_TASK; struct node_msgargs *pmsg_args; struct node_taskargs *ptask_args; u32 num_streams; struct bridge_drv_interface *intf_fxns; int status = 0; struct cmm_object *hcmm_mgr = NULL; /* Shared memory manager hndl */ u32 proc_id; u32 pul_value; u32 dynext_base; u32 off_set = 0; u32 ul_stack_seg_val; struct cfg_hostres *host_res; struct bridge_dev_context *pbridge_context; u32 mapped_addr = 0; u32 map_attrs = 0x0; struct dsp_processorstate proc_state; #ifdef DSP_DMM_DEBUG struct dmm_object *dmm_mgr; struct proc_object *p_proc_object = (struct proc_object *)hprocessor; #endif void *node_res; *noderes = NULL; status = proc_get_processor_id(hprocessor, &proc_id); if (proc_id != DSP_UNIT) goto func_end; status = proc_get_dev_object(hprocessor, &hdev_obj); if (!status) { status = dev_get_node_manager(hdev_obj, &hnode_mgr); if (hnode_mgr == NULL) status = -EPERM; } if (status) goto func_end; status = dev_get_bridge_context(hdev_obj, &pbridge_context); if (!pbridge_context) { status = -EFAULT; goto func_end; } status = proc_get_state(hprocessor, &proc_state, sizeof(struct dsp_processorstate)); if (status) goto func_end; /* If processor is in error state then don't attempt to send the message */ if (proc_state.proc_state == PROC_ERROR) { status = -EPERM; goto func_end; } /* Assuming that 0 is not a valid function address */ if (hnode_mgr->fxn_addrs[0] == 0) { /* No RMS on target - we currently can't handle this */ pr_err("%s: Failed, no RMS in base image\n", __func__); status = -EPERM; } else { /* Validate attr_in fields, if non-NULL */ if (attr_in) { /* Check if attr_in->prio is within range */ if (attr_in->prio < hnode_mgr->min_pri || attr_in->prio > hnode_mgr->max_pri) status = -EDOM; } } /* Allocate node object and fill in */ if (status) goto func_end; pnode = kzalloc(sizeof(struct node_object), GFP_KERNEL); if (pnode == NULL) { status = -ENOMEM; goto func_end; } pnode->node_mgr = hnode_mgr; /* This critical section protects get_node_props */ mutex_lock(&hnode_mgr->node_mgr_lock); /* Get dsp_ndbprops from node database */ status = get_node_props(hnode_mgr->dcd_mgr, pnode, node_uuid, &(pnode->dcd_props)); if (status) goto func_cont; pnode->node_uuid = *node_uuid; pnode->processor = hprocessor; pnode->ntype = pnode->dcd_props.obj_data.node_obj.ndb_props.ntype; pnode->timeout = pnode->dcd_props.obj_data.node_obj.ndb_props.timeout; pnode->prio = pnode->dcd_props.obj_data.node_obj.ndb_props.prio; /* Currently only C64 DSP builds support Node Dynamic * heaps */ /* Allocate memory for node heap */ pnode->create_args.asa.task_arg_obj.heap_size = 0; pnode->create_args.asa.task_arg_obj.dsp_heap_addr = 0; pnode->create_args.asa.task_arg_obj.dsp_heap_res_addr = 0; pnode->create_args.asa.task_arg_obj.gpp_heap_addr = 0; if (!attr_in) goto func_cont; /* Check if we have a user allocated node heap */ if (!(attr_in->pgpp_virt_addr)) goto func_cont; /* check for page aligned Heap size */ if (((attr_in->heap_size) & (PG_SIZE4K - 1))) { pr_err("%s: node heap size not aligned to 4K, size = 0x%x \n", __func__, attr_in->heap_size); status = -EINVAL; } else { pnode->create_args.asa.task_arg_obj.heap_size = attr_in->heap_size; pnode->create_args.asa.task_arg_obj.gpp_heap_addr = (u32) attr_in->pgpp_virt_addr; } if (status) goto func_cont; status = proc_reserve_memory(hprocessor, pnode->create_args.asa.task_arg_obj. heap_size + PAGE_SIZE, (void **)&(pnode->create_args.asa. task_arg_obj.dsp_heap_res_addr), pr_ctxt); if (status) { pr_err("%s: Failed to reserve memory for heap: 0x%x\n", __func__, status); goto func_cont; } #ifdef DSP_DMM_DEBUG status = dmm_get_handle(p_proc_object, &dmm_mgr); if (!dmm_mgr) { status = DSP_EHANDLE; goto func_cont; } dmm_mem_map_dump(dmm_mgr); #endif map_attrs |= DSP_MAPLITTLEENDIAN; map_attrs |= DSP_MAPELEMSIZE32; map_attrs |= DSP_MAPVIRTUALADDR; status = proc_map(hprocessor, (void *)attr_in->pgpp_virt_addr, pnode->create_args.asa.task_arg_obj.heap_size, (void *)pnode->create_args.asa.task_arg_obj. dsp_heap_res_addr, (void **)&mapped_addr, map_attrs, pr_ctxt); if (status) pr_err("%s: Failed to map memory for Heap: 0x%x\n", __func__, status); else pnode->create_args.asa.task_arg_obj.dsp_heap_addr = (u32) mapped_addr; func_cont: mutex_unlock(&hnode_mgr->node_mgr_lock); if (attr_in != NULL) { /* Overrides of NBD properties */ pnode->timeout = attr_in->timeout; pnode->prio = attr_in->prio; } /* Create object to manage notifications */ if (!status) { pnode->ntfy_obj = kmalloc(sizeof(struct ntfy_object), GFP_KERNEL); if (pnode->ntfy_obj) ntfy_init(pnode->ntfy_obj); else status = -ENOMEM; } if (!status) { node_type = node_get_type(pnode); /* Allocate dsp_streamconnect array for device, task, and * dais socket nodes. */ if (node_type != NODE_MESSAGE) { num_streams = MAX_INPUTS(pnode) + MAX_OUTPUTS(pnode); pnode->stream_connect = kzalloc(num_streams * sizeof(struct dsp_streamconnect), GFP_KERNEL); if (num_streams > 0 && pnode->stream_connect == NULL) status = -ENOMEM; } if (!status && (node_type == NODE_TASK || node_type == NODE_DAISSOCKET)) { /* Allocate arrays for maintainig stream connections */ pnode->inputs = kzalloc(MAX_INPUTS(pnode) * sizeof(struct stream_chnl), GFP_KERNEL); pnode->outputs = kzalloc(MAX_OUTPUTS(pnode) * sizeof(struct stream_chnl), GFP_KERNEL); ptask_args = &(pnode->create_args.asa.task_arg_obj); ptask_args->strm_in_def = kzalloc(MAX_INPUTS(pnode) * sizeof(struct node_strmdef), GFP_KERNEL); ptask_args->strm_out_def = kzalloc(MAX_OUTPUTS(pnode) * sizeof(struct node_strmdef), GFP_KERNEL); if ((MAX_INPUTS(pnode) > 0 && (pnode->inputs == NULL || ptask_args->strm_in_def == NULL)) || (MAX_OUTPUTS(pnode) > 0 && (pnode->outputs == NULL || ptask_args->strm_out_def == NULL))) status = -ENOMEM; } } if (!status && (node_type != NODE_DEVICE)) { /* Create an event that will be posted when RMS_EXIT is * received. */ pnode->sync_done = kzalloc(sizeof(struct sync_object), GFP_KERNEL); if (pnode->sync_done) sync_init_event(pnode->sync_done); else status = -ENOMEM; if (!status) { /*Get the shared mem mgr for this nodes dev object */ status = cmm_get_handle(hprocessor, &hcmm_mgr); if (!status) { /* Allocate a SM addr translator for this node * w/ deflt attr */ status = cmm_xlator_create(&pnode->xlator, hcmm_mgr, NULL); } } if (!status) { /* Fill in message args */ if ((pargs != NULL) && (pargs->cb_data > 0)) { pmsg_args = &(pnode->create_args.asa.node_msg_args); pmsg_args->pdata = kzalloc(pargs->cb_data, GFP_KERNEL); if (pmsg_args->pdata == NULL) { status = -ENOMEM; } else { pmsg_args->arg_length = pargs->cb_data; memcpy(pmsg_args->pdata, pargs->node_data, pargs->cb_data); } } } } if (!status && node_type != NODE_DEVICE) { /* Create a message queue for this node */ intf_fxns = hnode_mgr->intf_fxns; status = (*intf_fxns->msg_create_queue) (hnode_mgr->msg_mgr_obj, &pnode->msg_queue_obj, 0, pnode->create_args.asa. node_msg_args.max_msgs, pnode); } if (!status) { /* Create object for dynamic loading */ status = hnode_mgr->nldr_fxns.allocate(hnode_mgr->nldr_obj, (void *)pnode, &pnode->dcd_props. obj_data.node_obj, &pnode-> nldr_node_obj, &pnode->phase_split); } /* Compare value read from Node Properties and check if it is same as * STACKSEGLABEL, if yes read the Address of STACKSEGLABEL, calculate * GPP Address, Read the value in that address and override the * stack_seg value in task args */ if (!status && (char *)pnode->dcd_props.obj_data.node_obj.ndb_props. stack_seg_name != NULL) { if (strcmp((char *) pnode->dcd_props.obj_data.node_obj.ndb_props. stack_seg_name, STACKSEGLABEL) == 0) { void __iomem *stack_seg; u32 stack_seg_pa; status = hnode_mgr->nldr_fxns. get_fxn_addr(pnode->nldr_node_obj, "DYNEXT_BEG", &dynext_base); if (status) pr_err("%s: Failed to get addr for DYNEXT_BEG" " status = 0x%x\n", __func__, status); status = hnode_mgr->nldr_fxns. get_fxn_addr(pnode->nldr_node_obj, "L1DSRAM_HEAP", &pul_value); if (status) pr_err("%s: Failed to get addr for L1DSRAM_HEAP" " status = 0x%x\n", __func__, status); host_res = pbridge_context->resources; if (!host_res) status = -EPERM; if (status) { pr_err("%s: Failed to get host resource, status" " = 0x%x\n", __func__, status); goto func_end; } off_set = pul_value - dynext_base; stack_seg_pa = host_res->mem_phys[1] + off_set; stack_seg = ioremap(stack_seg_pa, SZ_32); if (!stack_seg) { status = -ENOMEM; goto func_end; } ul_stack_seg_val = readl(stack_seg); iounmap(stack_seg); dev_dbg(bridge, "%s: StackSegVal = 0x%x, StackSegAddr =" " 0x%x\n", __func__, ul_stack_seg_val, host_res->mem_base[1] + off_set); pnode->create_args.asa.task_arg_obj.stack_seg = ul_stack_seg_val; } } if (!status) { /* Add the node to the node manager's list of allocated * nodes. */ NODE_SET_STATE(pnode, NODE_ALLOCATED); mutex_lock(&hnode_mgr->node_mgr_lock); list_add_tail(&pnode->list_elem, &hnode_mgr->node_list); ++(hnode_mgr->num_nodes); /* Exit critical section */ mutex_unlock(&hnode_mgr->node_mgr_lock); /* Preset this to assume phases are split * (for overlay and dll) */ pnode->phase_split = true; /* Notify all clients registered for DSP_NODESTATECHANGE. */ proc_notify_all_clients(hprocessor, DSP_NODESTATECHANGE); } else { /* Cleanup */ if (pnode) delete_node(pnode, pr_ctxt); } if (!status) { status = drv_insert_node_res_element(pnode, &node_res, pr_ctxt); if (status) { delete_node(pnode, pr_ctxt); goto func_end; } *noderes = (struct node_res_object *)node_res; drv_proc_node_update_heap_status(node_res, true); drv_proc_node_update_status(node_res, true); } func_end: dev_dbg(bridge, "%s: hprocessor: %p pNodeId: %p pargs: %p attr_in: %p " "node_res: %p status: 0x%x\n", __func__, hprocessor, node_uuid, pargs, attr_in, noderes, status); return status; } /* * ======== node_alloc_msg_buf ======== * Purpose: * Allocates buffer for zero copy messaging. */ DBAPI node_alloc_msg_buf(struct node_object *hnode, u32 usize, struct dsp_bufferattr *pattr, u8 **pbuffer) { struct node_object *pnode = (struct node_object *)hnode; int status = 0; bool va_flag = false; bool set_info; u32 proc_id; if (!pnode) status = -EFAULT; else if (node_get_type(pnode) == NODE_DEVICE) status = -EPERM; if (status) goto func_end; if (pattr == NULL) pattr = &node_dfltbufattrs; /* set defaults */ status = proc_get_processor_id(pnode->processor, &proc_id); if (proc_id != DSP_UNIT) { goto func_end; } /* If segment ID includes MEM_SETVIRTUALSEGID then pbuffer is a * virt address, so set this info in this node's translator * object for future ref. If MEM_GETVIRTUALSEGID then retrieve * virtual address from node's translator. */ if ((pattr->segment_id & MEM_SETVIRTUALSEGID) || (pattr->segment_id & MEM_GETVIRTUALSEGID)) { va_flag = true; set_info = (pattr->segment_id & MEM_SETVIRTUALSEGID) ? true : false; /* Clear mask bits */ pattr->segment_id &= ~MEM_MASKVIRTUALSEGID; /* Set/get this node's translators virtual address base/size */ status = cmm_xlator_info(pnode->xlator, pbuffer, usize, pattr->segment_id, set_info); } if (!status && (!va_flag)) { if (pattr->segment_id != 1) { /* Node supports single SM segment only. */ status = -EBADR; } /* Arbitrary SM buffer alignment not supported for host side * allocs, but guaranteed for the following alignment * values. */ switch (pattr->buf_alignment) { case 0: case 1: case 2: case 4: break; default: /* alignment value not supportted */ status = -EPERM; break; } if (!status) { /* allocate physical buffer from seg_id in node's * translator */ (void)cmm_xlator_alloc_buf(pnode->xlator, pbuffer, usize); if (*pbuffer == NULL) { pr_err("%s: error - Out of shared memory\n", __func__); status = -ENOMEM; } } } func_end: return status; } /* * ======== node_change_priority ======== * Purpose: * Change the priority of a node in the allocated state, or that is * currently running or paused on the target. */ int node_change_priority(struct node_object *hnode, s32 prio) { struct node_object *pnode = (struct node_object *)hnode; struct node_mgr *hnode_mgr = NULL; enum node_type node_type; enum node_state state; int status = 0; u32 proc_id; if (!hnode || !hnode->node_mgr) { status = -EFAULT; } else { hnode_mgr = hnode->node_mgr; node_type = node_get_type(hnode); if (node_type != NODE_TASK && node_type != NODE_DAISSOCKET) status = -EPERM; else if (prio < hnode_mgr->min_pri || prio > hnode_mgr->max_pri) status = -EDOM; } if (status) goto func_end; /* Enter critical section */ mutex_lock(&hnode_mgr->node_mgr_lock); state = node_get_state(hnode); if (state == NODE_ALLOCATED || state == NODE_PAUSED) { NODE_SET_PRIORITY(hnode, prio); } else { if (state != NODE_RUNNING) { status = -EBADR; goto func_cont; } status = proc_get_processor_id(pnode->processor, &proc_id); if (proc_id == DSP_UNIT) { status = disp_node_change_priority(hnode_mgr->disp_obj, hnode, hnode_mgr->fxn_addrs [RMSCHANGENODEPRIORITY], hnode->node_env, prio); } if (status >= 0) NODE_SET_PRIORITY(hnode, prio); } func_cont: /* Leave critical section */ mutex_unlock(&hnode_mgr->node_mgr_lock); func_end: return status; } /* * ======== node_connect ======== * Purpose: * Connect two nodes on the DSP, or a node on the DSP to the GPP. */ int node_connect(struct node_object *node1, u32 stream1, struct node_object *node2, u32 stream2, struct dsp_strmattr *pattrs, struct dsp_cbdata *conn_param) { struct node_mgr *hnode_mgr; char *pstr_dev_name = NULL; enum node_type node1_type = NODE_TASK; enum node_type node2_type = NODE_TASK; enum dsp_strmmode strm_mode; struct node_strmdef *pstrm_def; struct node_strmdef *input = NULL; struct node_strmdef *output = NULL; struct node_object *dev_node_obj; struct node_object *hnode; struct stream_chnl *pstream; u32 pipe_id; u32 chnl_id; s8 chnl_mode; u32 dw_length; int status = 0; if (!node1 || !node2) return -EFAULT; /* The two nodes must be on the same processor */ if (node1 != (struct node_object *)DSP_HGPPNODE && node2 != (struct node_object *)DSP_HGPPNODE && node1->node_mgr != node2->node_mgr) return -EPERM; /* Cannot connect a node to itself */ if (node1 == node2) return -EPERM; /* node_get_type() will return NODE_GPP if hnode = DSP_HGPPNODE. */ node1_type = node_get_type(node1); node2_type = node_get_type(node2); /* Check stream indices ranges */ if ((node1_type != NODE_GPP && node1_type != NODE_DEVICE && stream1 >= MAX_OUTPUTS(node1)) || (node2_type != NODE_GPP && node2_type != NODE_DEVICE && stream2 >= MAX_INPUTS(node2))) return -EINVAL; /* * Only the following types of connections are allowed: * task/dais socket < == > task/dais socket * task/dais socket < == > device * task/dais socket < == > GPP * * ie, no message nodes, and at least one task or dais * socket node. */ if (node1_type == NODE_MESSAGE || node2_type == NODE_MESSAGE || (node1_type != NODE_TASK && node1_type != NODE_DAISSOCKET && node2_type != NODE_TASK && node2_type != NODE_DAISSOCKET)) return -EPERM; /* * Check stream mode. Default is STRMMODE_PROCCOPY. */ if (pattrs && pattrs->strm_mode != STRMMODE_PROCCOPY) return -EPERM; /* illegal stream mode */ if (node1_type != NODE_GPP) { hnode_mgr = node1->node_mgr; } else { hnode_mgr = node2->node_mgr; } /* Enter critical section */ mutex_lock(&hnode_mgr->node_mgr_lock); /* Nodes must be in the allocated state */ if (node1_type != NODE_GPP && node_get_state(node1) != NODE_ALLOCATED) { status = -EBADR; goto out_unlock; } if (node2_type != NODE_GPP && node_get_state(node2) != NODE_ALLOCATED) { status = -EBADR; goto out_unlock; } /* * Check that stream indices for task and dais socket nodes * are not already be used. (Device nodes checked later) */ if (node1_type == NODE_TASK || node1_type == NODE_DAISSOCKET) { output = &(node1->create_args.asa. task_arg_obj.strm_out_def[stream1]); if (output->sz_device) { status = -EISCONN; goto out_unlock; } } if (node2_type == NODE_TASK || node2_type == NODE_DAISSOCKET) { input = &(node2->create_args.asa. task_arg_obj.strm_in_def[stream2]); if (input->sz_device) { status = -EISCONN; goto out_unlock; } } /* Connecting two task nodes? */ if ((node1_type == NODE_TASK || node1_type == NODE_DAISSOCKET) && (node2_type == NODE_TASK || node2_type == NODE_DAISSOCKET)) { /* Find available pipe */ pipe_id = find_first_zero_bit(hnode_mgr->pipe_map, MAXPIPES); if (pipe_id == MAXPIPES) { status = -ECONNREFUSED; goto out_unlock; } set_bit(pipe_id, hnode_mgr->pipe_map); node1->outputs[stream1].type = NODECONNECT; node2->inputs[stream2].type = NODECONNECT; node1->outputs[stream1].dev_id = pipe_id; node2->inputs[stream2].dev_id = pipe_id; output->sz_device = kzalloc(PIPENAMELEN + 1, GFP_KERNEL); input->sz_device = kzalloc(PIPENAMELEN + 1, GFP_KERNEL); if (!output->sz_device || !input->sz_device) { /* Undo the connection */ kfree(output->sz_device); kfree(input->sz_device); clear_bit(pipe_id, hnode_mgr->pipe_map); status = -ENOMEM; goto out_unlock; } /* Copy "/dbpipe<pipId>" name to device names */ sprintf(output->sz_device, "%s%d", PIPEPREFIX, pipe_id); strcpy(input->sz_device, output->sz_device); } /* Connecting task node to host? */ if (node1_type == NODE_GPP || node2_type == NODE_GPP) { pstr_dev_name = kzalloc(HOSTNAMELEN + 1, GFP_KERNEL); if (!pstr_dev_name) { status = -ENOMEM; goto out_unlock; } chnl_mode = (node1_type == NODE_GPP) ? CHNL_MODETODSP : CHNL_MODEFROMDSP; /* * Reserve a channel id. We need to put the name "/host<id>" * in the node's create_args, but the host * side channel will not be opened until DSPStream_Open is * called for this node. */ strm_mode = pattrs ? pattrs->strm_mode : STRMMODE_PROCCOPY; switch (strm_mode) { case STRMMODE_RDMA: chnl_id = find_first_zero_bit(hnode_mgr->dma_chnl_map, CHNL_MAXCHANNELS); if (chnl_id < CHNL_MAXCHANNELS) { set_bit(chnl_id, hnode_mgr->dma_chnl_map); /* dma chans are 2nd transport chnl set * ids(e.g. 16-31) */ chnl_id = chnl_id + hnode_mgr->num_chnls; } break; case STRMMODE_ZEROCOPY: chnl_id = find_first_zero_bit(hnode_mgr->zc_chnl_map, CHNL_MAXCHANNELS); if (chnl_id < CHNL_MAXCHANNELS) { set_bit(chnl_id, hnode_mgr->zc_chnl_map); /* zero-copy chans are 3nd transport set * (e.g. 32-47) */ chnl_id = chnl_id + (2 * hnode_mgr->num_chnls); } break; case STRMMODE_PROCCOPY: chnl_id = find_first_zero_bit(hnode_mgr->chnl_map, CHNL_MAXCHANNELS); if (chnl_id < CHNL_MAXCHANNELS) set_bit(chnl_id, hnode_mgr->chnl_map); break; default: status = -EINVAL; goto out_unlock; } if (chnl_id == CHNL_MAXCHANNELS) { status = -ECONNREFUSED; goto out_unlock; } if (node1 == (struct node_object *)DSP_HGPPNODE) { node2->inputs[stream2].type = HOSTCONNECT; node2->inputs[stream2].dev_id = chnl_id; input->sz_device = pstr_dev_name; } else { node1->outputs[stream1].type = HOSTCONNECT; node1->outputs[stream1].dev_id = chnl_id; output->sz_device = pstr_dev_name; } sprintf(pstr_dev_name, "%s%d", HOSTPREFIX, chnl_id); } /* Connecting task node to device node? */ if ((node1_type == NODE_DEVICE) || (node2_type == NODE_DEVICE)) { if (node2_type == NODE_DEVICE) { /* node1 == > device */ dev_node_obj = node2; hnode = node1; pstream = &(node1->outputs[stream1]); pstrm_def = output; } else { /* device == > node2 */ dev_node_obj = node1; hnode = node2; pstream = &(node2->inputs[stream2]); pstrm_def = input; } /* Set up create args */ pstream->type = DEVICECONNECT; dw_length = strlen(dev_node_obj->str_dev_name); if (conn_param) pstrm_def->sz_device = kzalloc(dw_length + 1 + conn_param->cb_data, GFP_KERNEL); else pstrm_def->sz_device = kzalloc(dw_length + 1, GFP_KERNEL); if (!pstrm_def->sz_device) { status = -ENOMEM; goto out_unlock; } /* Copy device name */ strncpy(pstrm_def->sz_device, dev_node_obj->str_dev_name, dw_length); if (conn_param) strncat(pstrm_def->sz_device, (char *)conn_param->node_data, (u32) conn_param->cb_data); dev_node_obj->device_owner = hnode; } /* Fill in create args */ if (node1_type == NODE_TASK || node1_type == NODE_DAISSOCKET) { node1->create_args.asa.task_arg_obj.num_outputs++; fill_stream_def(node1, output, pattrs); } if (node2_type == NODE_TASK || node2_type == NODE_DAISSOCKET) { node2->create_args.asa.task_arg_obj.num_inputs++; fill_stream_def(node2, input, pattrs); } /* Update node1 and node2 stream_connect */ if (node1_type != NODE_GPP && node1_type != NODE_DEVICE) { node1->num_outputs++; if (stream1 > node1->max_output_index) node1->max_output_index = stream1; } if (node2_type != NODE_GPP && node2_type != NODE_DEVICE) { node2->num_inputs++; if (stream2 > node2->max_input_index) node2->max_input_index = stream2; } fill_stream_connect(node1, node2, stream1, stream2); /* end of sync_enter_cs */ /* Exit critical section */ out_unlock: if (status && pstr_dev_name) kfree(pstr_dev_name); mutex_unlock(&hnode_mgr->node_mgr_lock); dev_dbg(bridge, "%s: node1: %p stream1: %d node2: %p stream2: %d" "pattrs: %p status: 0x%x\n", __func__, node1, stream1, node2, stream2, pattrs, status); return status; } /* * ======== node_create ======== * Purpose: * Create a node on the DSP by remotely calling the node's create function. */ int node_create(struct node_object *hnode) { struct node_object *pnode = (struct node_object *)hnode; struct node_mgr *hnode_mgr; struct bridge_drv_interface *intf_fxns; u32 ul_create_fxn; enum node_type node_type; int status = 0; int status1 = 0; struct dsp_cbdata cb_data; u32 proc_id = 255; struct dsp_processorstate proc_state; struct proc_object *hprocessor; #if defined(CONFIG_TIDSPBRIDGE_DVFS) && !defined(CONFIG_CPU_FREQ) struct dspbridge_platform_data *pdata = omap_dspbridge_dev->dev.platform_data; #endif if (!pnode) { status = -EFAULT; goto func_end; } hprocessor = hnode->processor; status = proc_get_state(hprocessor, &proc_state, sizeof(struct dsp_processorstate)); if (status) goto func_end; /* If processor is in error state then don't attempt to create new node */ if (proc_state.proc_state == PROC_ERROR) { status = -EPERM; goto func_end; } /* create struct dsp_cbdata struct for PWR calls */ cb_data.cb_data = PWR_TIMEOUT; node_type = node_get_type(hnode); hnode_mgr = hnode->node_mgr; intf_fxns = hnode_mgr->intf_fxns; /* Get access to node dispatcher */ mutex_lock(&hnode_mgr->node_mgr_lock); /* Check node state */ if (node_get_state(hnode) != NODE_ALLOCATED) status = -EBADR; if (!status) status = proc_get_processor_id(pnode->processor, &proc_id); if (status) goto func_cont2; if (proc_id != DSP_UNIT) goto func_cont2; /* Make sure streams are properly connected */ if ((hnode->num_inputs && hnode->max_input_index > hnode->num_inputs - 1) || (hnode->num_outputs && hnode->max_output_index > hnode->num_outputs - 1)) status = -ENOTCONN; if (!status) { /* If node's create function is not loaded, load it */ /* Boost the OPP level to max level that DSP can be requested */ #if defined(CONFIG_TIDSPBRIDGE_DVFS) && !defined(CONFIG_CPU_FREQ) if (pdata->cpu_set_freq) (*pdata->cpu_set_freq) (pdata->mpu_speed[VDD1_OPP3]); #endif status = hnode_mgr->nldr_fxns.load(hnode->nldr_node_obj, NLDR_CREATE); /* Get address of node's create function */ if (!status) { hnode->loaded = true; if (node_type != NODE_DEVICE) { status = get_fxn_address(hnode, &ul_create_fxn, CREATEPHASE); } } else { pr_err("%s: failed to load create code: 0x%x\n", __func__, status); } /* Request the lowest OPP level */ #if defined(CONFIG_TIDSPBRIDGE_DVFS) && !defined(CONFIG_CPU_FREQ) if (pdata->cpu_set_freq) (*pdata->cpu_set_freq) (pdata->mpu_speed[VDD1_OPP1]); #endif /* Get address of iAlg functions, if socket node */ if (!status) { if (node_type == NODE_DAISSOCKET) { status = hnode_mgr->nldr_fxns.get_fxn_addr (hnode->nldr_node_obj, hnode->dcd_props.obj_data.node_obj. str_i_alg_name, &hnode->create_args.asa. task_arg_obj.dais_arg); } } } if (!status) { if (node_type != NODE_DEVICE) { status = disp_node_create(hnode_mgr->disp_obj, hnode, hnode_mgr->fxn_addrs [RMSCREATENODE], ul_create_fxn, &(hnode->create_args), &(hnode->node_env)); if (status >= 0) { /* Set the message queue id to the node env * pointer */ intf_fxns = hnode_mgr->intf_fxns; (*intf_fxns->msg_set_queue_id) (hnode-> msg_queue_obj, hnode->node_env); } } } /* Phase II/Overlays: Create, execute, delete phases possibly in * different files/sections. */ if (hnode->loaded && hnode->phase_split) { /* If create code was dynamically loaded, we can now unload * it. */ status1 = hnode_mgr->nldr_fxns.unload(hnode->nldr_node_obj, NLDR_CREATE); hnode->loaded = false; } if (status1) pr_err("%s: Failed to unload create code: 0x%x\n", __func__, status1); func_cont2: /* Update node state and node manager state */ if (status >= 0) { NODE_SET_STATE(hnode, NODE_CREATED); hnode_mgr->num_created++; goto func_cont; } if (status != -EBADR) { /* Put back in NODE_ALLOCATED state if error occurred */ NODE_SET_STATE(hnode, NODE_ALLOCATED); } func_cont: /* Free access to node dispatcher */ mutex_unlock(&hnode_mgr->node_mgr_lock); func_end: if (status >= 0) { proc_notify_clients(hnode->processor, DSP_NODESTATECHANGE); ntfy_notify(hnode->ntfy_obj, DSP_NODESTATECHANGE); } dev_dbg(bridge, "%s: hnode: %p status: 0x%x\n", __func__, hnode, status); return status; } /* * ======== node_create_mgr ======== * Purpose: * Create a NODE Manager object. */ int node_create_mgr(struct node_mgr **node_man, struct dev_object *hdev_obj) { u32 i; struct node_mgr *node_mgr_obj = NULL; struct disp_attr disp_attr_obj; char *sz_zl_file = ""; struct nldr_attrs nldr_attrs_obj; int status = 0; u8 dev_type; *node_man = NULL; /* Allocate Node manager object */ node_mgr_obj = kzalloc(sizeof(struct node_mgr), GFP_KERNEL); if (!node_mgr_obj) return -ENOMEM; node_mgr_obj->dev_obj = hdev_obj; node_mgr_obj->ntfy_obj = kmalloc(sizeof(struct ntfy_object), GFP_KERNEL); if (!node_mgr_obj->ntfy_obj) { status = -ENOMEM; goto out_err; } ntfy_init(node_mgr_obj->ntfy_obj); INIT_LIST_HEAD(&node_mgr_obj->node_list); dev_get_dev_type(hdev_obj, &dev_type); status = dcd_create_manager(sz_zl_file, &node_mgr_obj->dcd_mgr); if (status) goto out_err; status = get_proc_props(node_mgr_obj, hdev_obj); if (status) goto out_err; /* Create NODE Dispatcher */ disp_attr_obj.chnl_offset = node_mgr_obj->chnl_offset; disp_attr_obj.chnl_buf_size = node_mgr_obj->chnl_buf_size; disp_attr_obj.proc_family = node_mgr_obj->proc_family; disp_attr_obj.proc_type = node_mgr_obj->proc_type; status = disp_create(&node_mgr_obj->disp_obj, hdev_obj, &disp_attr_obj); if (status) goto out_err; /* Create a STRM Manager */ status = strm_create(&node_mgr_obj->strm_mgr_obj, hdev_obj); if (status) goto out_err; dev_get_intf_fxns(hdev_obj, &node_mgr_obj->intf_fxns); /* Get msg_ctrl queue manager */ dev_get_msg_mgr(hdev_obj, &node_mgr_obj->msg_mgr_obj); mutex_init(&node_mgr_obj->node_mgr_lock); /* Block out reserved channels */ for (i = 0; i < node_mgr_obj->chnl_offset; i++) set_bit(i, node_mgr_obj->chnl_map); /* Block out channels reserved for RMS */ set_bit(node_mgr_obj->chnl_offset, node_mgr_obj->chnl_map); set_bit(node_mgr_obj->chnl_offset + 1, node_mgr_obj->chnl_map); /* NO RM Server on the IVA */ if (dev_type != IVA_UNIT) { /* Get addresses of any RMS functions loaded */ status = get_rms_fxns(node_mgr_obj); if (status) goto out_err; } /* Get loader functions and create loader */ node_mgr_obj->nldr_fxns = nldr_fxns; /* Dyn loader funcs */ nldr_attrs_obj.ovly = ovly; nldr_attrs_obj.write = mem_write; nldr_attrs_obj.dsp_word_size = node_mgr_obj->dsp_word_size; nldr_attrs_obj.dsp_mau_size = node_mgr_obj->dsp_mau_size; status = node_mgr_obj->nldr_fxns.create(&node_mgr_obj->nldr_obj, hdev_obj, &nldr_attrs_obj); if (status) goto out_err; *node_man = node_mgr_obj; return status; out_err: delete_node_mgr(node_mgr_obj); return status; } /* * ======== node_delete ======== * Purpose: * Delete a node on the DSP by remotely calling the node's delete function. * Loads the node's delete function if necessary. Free GPP side resources * after node's delete function returns. */ int node_delete(struct node_res_object *noderes, struct process_context *pr_ctxt) { struct node_object *pnode = noderes->node; struct node_mgr *hnode_mgr; struct proc_object *hprocessor; struct disp_object *disp_obj; u32 ul_delete_fxn; enum node_type node_type; enum node_state state; int status = 0; int status1 = 0; struct dsp_cbdata cb_data; u32 proc_id; struct bridge_drv_interface *intf_fxns; void *node_res = noderes; struct dsp_processorstate proc_state; if (!pnode) { status = -EFAULT; goto func_end; } /* create struct dsp_cbdata struct for PWR call */ cb_data.cb_data = PWR_TIMEOUT; hnode_mgr = pnode->node_mgr; hprocessor = pnode->processor; disp_obj = hnode_mgr->disp_obj; node_type = node_get_type(pnode); intf_fxns = hnode_mgr->intf_fxns; /* Enter critical section */ mutex_lock(&hnode_mgr->node_mgr_lock); state = node_get_state(pnode); /* Execute delete phase code for non-device node in all cases * except when the node was only allocated. Delete phase must be * executed even if create phase was executed, but failed. * If the node environment pointer is non-NULL, the delete phase * code must be executed. */ if (!(state == NODE_ALLOCATED && pnode->node_env == (u32) NULL) && node_type != NODE_DEVICE) { status = proc_get_processor_id(pnode->processor, &proc_id); if (status) goto func_cont1; if (proc_id == DSP_UNIT || proc_id == IVA_UNIT) { /* If node has terminated, execute phase code will * have already been unloaded in node_on_exit(). If the * node is PAUSED, the execute phase is loaded, and it * is now ok to unload it. If the node is running, we * will unload the execute phase only after deleting * the node. */ if (state == NODE_PAUSED && pnode->loaded && pnode->phase_split) { /* Ok to unload execute code as long as node * is not * running */ status1 = hnode_mgr->nldr_fxns. unload(pnode->nldr_node_obj, NLDR_EXECUTE); pnode->loaded = false; NODE_SET_STATE(pnode, NODE_DONE); } /* Load delete phase code if not loaded or if haven't * * unloaded EXECUTE phase */ if ((!(pnode->loaded) || (state == NODE_RUNNING)) && pnode->phase_split) { status = hnode_mgr->nldr_fxns. load(pnode->nldr_node_obj, NLDR_DELETE); if (!status) pnode->loaded = true; else pr_err("%s: fail - load delete code:" " 0x%x\n", __func__, status); } } func_cont1: if (!status) { /* Unblock a thread trying to terminate the node */ (void)sync_set_event(pnode->sync_done); if (proc_id == DSP_UNIT) { /* ul_delete_fxn = address of node's delete * function */ status = get_fxn_address(pnode, &ul_delete_fxn, DELETEPHASE); } else if (proc_id == IVA_UNIT) ul_delete_fxn = (u32) pnode->node_env; if (!status) { status = proc_get_state(hprocessor, &proc_state, sizeof(struct dsp_processorstate)); if (proc_state.proc_state != PROC_ERROR) { status = disp_node_delete(disp_obj, pnode, hnode_mgr-> fxn_addrs [RMSDELETENODE], ul_delete_fxn, pnode->node_env); } else NODE_SET_STATE(pnode, NODE_DONE); /* Unload execute, if not unloaded, and delete * function */ if (state == NODE_RUNNING && pnode->phase_split) { status1 = hnode_mgr->nldr_fxns. unload(pnode->nldr_node_obj, NLDR_EXECUTE); } if (status1) pr_err("%s: fail - unload execute code:" " 0x%x\n", __func__, status1); status1 = hnode_mgr->nldr_fxns.unload(pnode-> nldr_node_obj, NLDR_DELETE); pnode->loaded = false; if (status1) pr_err("%s: fail - unload delete code: " "0x%x\n", __func__, status1); } } } /* Free host side resources even if a failure occurred */ /* Remove node from hnode_mgr->node_list */ list_del(&pnode->list_elem); hnode_mgr->num_nodes--; /* Decrement count of nodes created on DSP */ if ((state != NODE_ALLOCATED) || ((state == NODE_ALLOCATED) && (pnode->node_env != (u32) NULL))) hnode_mgr->num_created--; /* Free host-side resources allocated by node_create() * delete_node() fails if SM buffers not freed by client! */ drv_proc_node_update_status(node_res, false); delete_node(pnode, pr_ctxt); /* * Release all Node resources and its context */ idr_remove(pr_ctxt->node_id, ((struct node_res_object *)node_res)->id); kfree(node_res); /* Exit critical section */ mutex_unlock(&hnode_mgr->node_mgr_lock); proc_notify_clients(hprocessor, DSP_NODESTATECHANGE); func_end: dev_dbg(bridge, "%s: pnode: %p status 0x%x\n", __func__, pnode, status); return status; } /* * ======== node_delete_mgr ======== * Purpose: * Delete the NODE Manager. */ int node_delete_mgr(struct node_mgr *hnode_mgr) { if (!hnode_mgr) return -EFAULT; delete_node_mgr(hnode_mgr); return 0; } /* * ======== node_enum_nodes ======== * Purpose: * Enumerate currently allocated nodes. */ int node_enum_nodes(struct node_mgr *hnode_mgr, void **node_tab, u32 node_tab_size, u32 *pu_num_nodes, u32 *pu_allocated) { struct node_object *hnode; u32 i = 0; int status = 0; if (!hnode_mgr) { status = -EFAULT; goto func_end; } /* Enter critical section */ mutex_lock(&hnode_mgr->node_mgr_lock); if (hnode_mgr->num_nodes > node_tab_size) { *pu_allocated = hnode_mgr->num_nodes; *pu_num_nodes = 0; status = -EINVAL; } else { list_for_each_entry(hnode, &hnode_mgr->node_list, list_elem) node_tab[i++] = hnode; *pu_allocated = *pu_num_nodes = hnode_mgr->num_nodes; } /* end of sync_enter_cs */ /* Exit critical section */ mutex_unlock(&hnode_mgr->node_mgr_lock); func_end: return status; } /* * ======== node_free_msg_buf ======== * Purpose: * Frees the message buffer. */ int node_free_msg_buf(struct node_object *hnode, u8 * pbuffer, struct dsp_bufferattr *pattr) { struct node_object *pnode = (struct node_object *)hnode; int status = 0; u32 proc_id; if (!hnode) { status = -EFAULT; goto func_end; } status = proc_get_processor_id(pnode->processor, &proc_id); if (proc_id == DSP_UNIT) { if (!status) { if (pattr == NULL) { /* set defaults */ pattr = &node_dfltbufattrs; } /* Node supports single SM segment only */ if (pattr->segment_id != 1) status = -EBADR; /* pbuffer is clients Va. */ status = cmm_xlator_free_buf(pnode->xlator, pbuffer); } } else { } func_end: return status; } /* * ======== node_get_attr ======== * Purpose: * Copy the current attributes of the specified node into a dsp_nodeattr * structure. */ int node_get_attr(struct node_object *hnode, struct dsp_nodeattr *pattr, u32 attr_size) { struct node_mgr *hnode_mgr; if (!hnode) return -EFAULT; hnode_mgr = hnode->node_mgr; /* Enter hnode_mgr critical section since we're accessing * data that could be changed by node_change_priority() and * node_connect(). */ mutex_lock(&hnode_mgr->node_mgr_lock); pattr->cb_struct = sizeof(struct dsp_nodeattr); /* dsp_nodeattrin */ pattr->in_node_attr_in.cb_struct = sizeof(struct dsp_nodeattrin); pattr->in_node_attr_in.prio = hnode->prio; pattr->in_node_attr_in.timeout = hnode->timeout; pattr->in_node_attr_in.heap_size = hnode->create_args.asa.task_arg_obj.heap_size; pattr->in_node_attr_in.pgpp_virt_addr = (void *) hnode->create_args.asa.task_arg_obj.gpp_heap_addr; pattr->node_attr_inputs = hnode->num_gpp_inputs; pattr->node_attr_outputs = hnode->num_gpp_outputs; /* dsp_nodeinfo */ get_node_info(hnode, &(pattr->node_info)); /* end of sync_enter_cs */ /* Exit critical section */ mutex_unlock(&hnode_mgr->node_mgr_lock); return 0; } /* * ======== node_get_channel_id ======== * Purpose: * Get the channel index reserved for a stream connection between the * host and a node. */ int node_get_channel_id(struct node_object *hnode, u32 dir, u32 index, u32 *chan_id) { enum node_type node_type; int status = -EINVAL; if (!hnode) { status = -EFAULT; return status; } node_type = node_get_type(hnode); if (node_type != NODE_TASK && node_type != NODE_DAISSOCKET) { status = -EPERM; return status; } if (dir == DSP_TONODE) { if (index < MAX_INPUTS(hnode)) { if (hnode->inputs[index].type == HOSTCONNECT) { *chan_id = hnode->inputs[index].dev_id; status = 0; } } } else { if (index < MAX_OUTPUTS(hnode)) { if (hnode->outputs[index].type == HOSTCONNECT) { *chan_id = hnode->outputs[index].dev_id; status = 0; } } } return status; } /* * ======== node_get_message ======== * Purpose: * Retrieve a message from a node on the DSP. */ int node_get_message(struct node_object *hnode, struct dsp_msg *message, u32 utimeout) { struct node_mgr *hnode_mgr; enum node_type node_type; struct bridge_drv_interface *intf_fxns; int status = 0; void *tmp_buf; struct dsp_processorstate proc_state; struct proc_object *hprocessor; if (!hnode) { status = -EFAULT; goto func_end; } hprocessor = hnode->processor; status = proc_get_state(hprocessor, &proc_state, sizeof(struct dsp_processorstate)); if (status) goto func_end; /* If processor is in error state then don't attempt to get the message */ if (proc_state.proc_state == PROC_ERROR) { status = -EPERM; goto func_end; } hnode_mgr = hnode->node_mgr; node_type = node_get_type(hnode); if (node_type != NODE_MESSAGE && node_type != NODE_TASK && node_type != NODE_DAISSOCKET) { status = -EPERM; goto func_end; } /* This function will block unless a message is available. Since * DSPNode_RegisterNotify() allows notification when a message * is available, the system can be designed so that * DSPNode_GetMessage() is only called when a message is * available. */ intf_fxns = hnode_mgr->intf_fxns; status = (*intf_fxns->msg_get) (hnode->msg_queue_obj, message, utimeout); /* Check if message contains SM descriptor */ if (status || !(message->cmd & DSP_RMSBUFDESC)) goto func_end; /* Translate DSP byte addr to GPP Va. */ tmp_buf = cmm_xlator_translate(hnode->xlator, (void *)(message->arg1 * hnode->node_mgr-> dsp_word_size), CMM_DSPPA2PA); if (tmp_buf != NULL) { /* now convert this GPP Pa to Va */ tmp_buf = cmm_xlator_translate(hnode->xlator, tmp_buf, CMM_PA2VA); if (tmp_buf != NULL) { /* Adjust SM size in msg */ message->arg1 = (u32) tmp_buf; message->arg2 *= hnode->node_mgr->dsp_word_size; } else { status = -ESRCH; } } else { status = -ESRCH; } func_end: dev_dbg(bridge, "%s: hnode: %p message: %p utimeout: 0x%x\n", __func__, hnode, message, utimeout); return status; } /* * ======== node_get_nldr_obj ======== */ int node_get_nldr_obj(struct node_mgr *hnode_mgr, struct nldr_object **nldr_ovlyobj) { int status = 0; struct node_mgr *node_mgr_obj = hnode_mgr; if (!hnode_mgr) status = -EFAULT; else *nldr_ovlyobj = node_mgr_obj->nldr_obj; return status; } /* * ======== node_get_strm_mgr ======== * Purpose: * Returns the Stream manager. */ int node_get_strm_mgr(struct node_object *hnode, struct strm_mgr **strm_man) { int status = 0; if (!hnode) status = -EFAULT; else *strm_man = hnode->node_mgr->strm_mgr_obj; return status; } /* * ======== node_get_load_type ======== */ enum nldr_loadtype node_get_load_type(struct node_object *hnode) { if (!hnode) { dev_dbg(bridge, "%s: Failed. hnode: %p\n", __func__, hnode); return -1; } else { return hnode->dcd_props.obj_data.node_obj.load_type; } } /* * ======== node_get_timeout ======== * Purpose: * Returns the timeout value for this node. */ u32 node_get_timeout(struct node_object *hnode) { if (!hnode) { dev_dbg(bridge, "%s: failed. hnode: %p\n", __func__, hnode); return 0; } else { return hnode->timeout; } } /* * ======== node_get_type ======== * Purpose: * Returns the node type. */ enum node_type node_get_type(struct node_object *hnode) { enum node_type node_type; if (hnode == (struct node_object *)DSP_HGPPNODE) node_type = NODE_GPP; else { if (!hnode) node_type = -1; else node_type = hnode->ntype; } return node_type; } /* * ======== node_on_exit ======== * Purpose: * Gets called when RMS_EXIT is received for a node. */ void node_on_exit(struct node_object *hnode, s32 node_status) { if (!hnode) return; /* Set node state to done */ NODE_SET_STATE(hnode, NODE_DONE); hnode->exit_status = node_status; if (hnode->loaded && hnode->phase_split) { (void)hnode->node_mgr->nldr_fxns.unload(hnode-> nldr_node_obj, NLDR_EXECUTE); hnode->loaded = false; } /* Unblock call to node_terminate */ (void)sync_set_event(hnode->sync_done); /* Notify clients */ proc_notify_clients(hnode->processor, DSP_NODESTATECHANGE); ntfy_notify(hnode->ntfy_obj, DSP_NODESTATECHANGE); } /* * ======== node_pause ======== * Purpose: * Suspend execution of a node currently running on the DSP. */ int node_pause(struct node_object *hnode) { struct node_object *pnode = (struct node_object *)hnode; enum node_type node_type; enum node_state state; struct node_mgr *hnode_mgr; int status = 0; u32 proc_id; struct dsp_processorstate proc_state; struct proc_object *hprocessor; if (!hnode) { status = -EFAULT; } else { node_type = node_get_type(hnode); if (node_type != NODE_TASK && node_type != NODE_DAISSOCKET) status = -EPERM; } if (status) goto func_end; status = proc_get_processor_id(pnode->processor, &proc_id); if (proc_id == IVA_UNIT) status = -ENOSYS; if (!status) { hnode_mgr = hnode->node_mgr; /* Enter critical section */ mutex_lock(&hnode_mgr->node_mgr_lock); state = node_get_state(hnode); /* Check node state */ if (state != NODE_RUNNING) status = -EBADR; if (status) goto func_cont; hprocessor = hnode->processor; status = proc_get_state(hprocessor, &proc_state, sizeof(struct dsp_processorstate)); if (status) goto func_cont; /* If processor is in error state then don't attempt to send the message */ if (proc_state.proc_state == PROC_ERROR) { status = -EPERM; goto func_cont; } status = disp_node_change_priority(hnode_mgr->disp_obj, hnode, hnode_mgr->fxn_addrs[RMSCHANGENODEPRIORITY], hnode->node_env, NODE_SUSPENDEDPRI); /* Update state */ if (status >= 0) NODE_SET_STATE(hnode, NODE_PAUSED); func_cont: /* End of sync_enter_cs */ /* Leave critical section */ mutex_unlock(&hnode_mgr->node_mgr_lock); if (status >= 0) { proc_notify_clients(hnode->processor, DSP_NODESTATECHANGE); ntfy_notify(hnode->ntfy_obj, DSP_NODESTATECHANGE); } } func_end: dev_dbg(bridge, "%s: hnode: %p status 0x%x\n", __func__, hnode, status); return status; } /* * ======== node_put_message ======== * Purpose: * Send a message to a message node, task node, or XDAIS socket node. This * function will block until the message stream can accommodate the * message, or a timeout occurs. */ int node_put_message(struct node_object *hnode, const struct dsp_msg *pmsg, u32 utimeout) { struct node_mgr *hnode_mgr = NULL; enum node_type node_type; struct bridge_drv_interface *intf_fxns; enum node_state state; int status = 0; void *tmp_buf; struct dsp_msg new_msg; struct dsp_processorstate proc_state; struct proc_object *hprocessor; if (!hnode) { status = -EFAULT; goto func_end; } hprocessor = hnode->processor; status = proc_get_state(hprocessor, &proc_state, sizeof(struct dsp_processorstate)); if (status) goto func_end; /* If processor is in bad state then don't attempt sending the message */ if (proc_state.proc_state == PROC_ERROR) { status = -EPERM; goto func_end; } hnode_mgr = hnode->node_mgr; node_type = node_get_type(hnode); if (node_type != NODE_MESSAGE && node_type != NODE_TASK && node_type != NODE_DAISSOCKET) status = -EPERM; if (!status) { /* Check node state. Can't send messages to a node after * we've sent the RMS_EXIT command. There is still the * possibility that node_terminate can be called after we've * checked the state. Could add another SYNC object to * prevent this (can't use node_mgr_lock, since we don't * want to block other NODE functions). However, the node may * still exit on its own, before this message is sent. */ mutex_lock(&hnode_mgr->node_mgr_lock); state = node_get_state(hnode); if (state == NODE_TERMINATING || state == NODE_DONE) status = -EBADR; /* end of sync_enter_cs */ mutex_unlock(&hnode_mgr->node_mgr_lock); } if (status) goto func_end; /* assign pmsg values to new msg */ new_msg = *pmsg; /* Now, check if message contains a SM buffer descriptor */ if (pmsg->cmd & DSP_RMSBUFDESC) { /* Translate GPP Va to DSP physical buf Ptr. */ tmp_buf = cmm_xlator_translate(hnode->xlator, (void *)new_msg.arg1, CMM_VA2DSPPA); if (tmp_buf != NULL) { /* got translation, convert to MAUs in msg */ if (hnode->node_mgr->dsp_word_size != 0) { new_msg.arg1 = (u32) tmp_buf / hnode->node_mgr->dsp_word_size; /* MAUs */ new_msg.arg2 /= hnode->node_mgr-> dsp_word_size; } else { pr_err("%s: dsp_word_size is zero!\n", __func__); status = -EPERM; /* bad DSPWordSize */ } } else { /* failed to translate buffer address */ status = -ESRCH; } } if (!status) { intf_fxns = hnode_mgr->intf_fxns; status = (*intf_fxns->msg_put) (hnode->msg_queue_obj, &new_msg, utimeout); } func_end: dev_dbg(bridge, "%s: hnode: %p pmsg: %p utimeout: 0x%x, " "status 0x%x\n", __func__, hnode, pmsg, utimeout, status); return status; } /* * ======== node_register_notify ======== * Purpose: * Register to be notified on specific events for this node. */ int node_register_notify(struct node_object *hnode, u32 event_mask, u32 notify_type, struct dsp_notification *hnotification) { struct bridge_drv_interface *intf_fxns; int status = 0; if (!hnode) { status = -EFAULT; } else { /* Check if event mask is a valid node related event */ if (event_mask & ~(DSP_NODESTATECHANGE | DSP_NODEMESSAGEREADY)) status = -EINVAL; /* Check if notify type is valid */ if (notify_type != DSP_SIGNALEVENT) status = -EINVAL; /* Only one Notification can be registered at a * time - Limitation */ if (event_mask == (DSP_NODESTATECHANGE | DSP_NODEMESSAGEREADY)) status = -EINVAL; } if (!status) { if (event_mask == DSP_NODESTATECHANGE) { status = ntfy_register(hnode->ntfy_obj, hnotification, event_mask & DSP_NODESTATECHANGE, notify_type); } else { /* Send Message part of event mask to msg_ctrl */ intf_fxns = hnode->node_mgr->intf_fxns; status = (*intf_fxns->msg_register_notify) (hnode->msg_queue_obj, event_mask & DSP_NODEMESSAGEREADY, notify_type, hnotification); } } dev_dbg(bridge, "%s: hnode: %p event_mask: 0x%x notify_type: 0x%x " "hnotification: %p status 0x%x\n", __func__, hnode, event_mask, notify_type, hnotification, status); return status; } /* * ======== node_run ======== * Purpose: * Start execution of a node's execute phase, or resume execution of a node * that has been suspended (via NODE_NodePause()) on the DSP. Load the * node's execute function if necessary. */ int node_run(struct node_object *hnode) { struct node_object *pnode = (struct node_object *)hnode; struct node_mgr *hnode_mgr; enum node_type node_type; enum node_state state; u32 ul_execute_fxn; u32 ul_fxn_addr; int status = 0; u32 proc_id; struct bridge_drv_interface *intf_fxns; struct dsp_processorstate proc_state; struct proc_object *hprocessor; if (!hnode) { status = -EFAULT; goto func_end; } hprocessor = hnode->processor; status = proc_get_state(hprocessor, &proc_state, sizeof(struct dsp_processorstate)); if (status) goto func_end; /* If processor is in error state then don't attempt to run the node */ if (proc_state.proc_state == PROC_ERROR) { status = -EPERM; goto func_end; } node_type = node_get_type(hnode); if (node_type == NODE_DEVICE) status = -EPERM; if (status) goto func_end; hnode_mgr = hnode->node_mgr; if (!hnode_mgr) { status = -EFAULT; goto func_end; } intf_fxns = hnode_mgr->intf_fxns; /* Enter critical section */ mutex_lock(&hnode_mgr->node_mgr_lock); state = node_get_state(hnode); if (state != NODE_CREATED && state != NODE_PAUSED) status = -EBADR; if (!status) status = proc_get_processor_id(pnode->processor, &proc_id); if (status) goto func_cont1; if ((proc_id != DSP_UNIT) && (proc_id != IVA_UNIT)) goto func_cont1; if (state == NODE_CREATED) { /* If node's execute function is not loaded, load it */ if (!(hnode->loaded) && hnode->phase_split) { status = hnode_mgr->nldr_fxns.load(hnode->nldr_node_obj, NLDR_EXECUTE); if (!status) { hnode->loaded = true; } else { pr_err("%s: fail - load execute code: 0x%x\n", __func__, status); } } if (!status) { /* Get address of node's execute function */ if (proc_id == IVA_UNIT) ul_execute_fxn = (u32) hnode->node_env; else { status = get_fxn_address(hnode, &ul_execute_fxn, EXECUTEPHASE); } } if (!status) { ul_fxn_addr = hnode_mgr->fxn_addrs[RMSEXECUTENODE]; status = disp_node_run(hnode_mgr->disp_obj, hnode, ul_fxn_addr, ul_execute_fxn, hnode->node_env); } } else if (state == NODE_PAUSED) { ul_fxn_addr = hnode_mgr->fxn_addrs[RMSCHANGENODEPRIORITY]; status = disp_node_change_priority(hnode_mgr->disp_obj, hnode, ul_fxn_addr, hnode->node_env, NODE_GET_PRIORITY(hnode)); } else { /* We should never get here */ } func_cont1: /* Update node state. */ if (status >= 0) NODE_SET_STATE(hnode, NODE_RUNNING); else /* Set state back to previous value */ NODE_SET_STATE(hnode, state); /*End of sync_enter_cs */ /* Exit critical section */ mutex_unlock(&hnode_mgr->node_mgr_lock); if (status >= 0) { proc_notify_clients(hnode->processor, DSP_NODESTATECHANGE); ntfy_notify(hnode->ntfy_obj, DSP_NODESTATECHANGE); } func_end: dev_dbg(bridge, "%s: hnode: %p status 0x%x\n", __func__, hnode, status); return status; } /* * ======== node_terminate ======== * Purpose: * Signal a node running on the DSP that it should exit its execute phase * function. */ int node_terminate(struct node_object *hnode, int *pstatus) { struct node_object *pnode = (struct node_object *)hnode; struct node_mgr *hnode_mgr = NULL; enum node_type node_type; struct bridge_drv_interface *intf_fxns; enum node_state state; struct dsp_msg msg, killmsg; int status = 0; u32 proc_id, kill_time_out; struct deh_mgr *hdeh_mgr; struct dsp_processorstate proc_state; if (!hnode || !hnode->node_mgr) { status = -EFAULT; goto func_end; } if (pnode->processor == NULL) { status = -EFAULT; goto func_end; } status = proc_get_processor_id(pnode->processor, &proc_id); if (!status) { hnode_mgr = hnode->node_mgr; node_type = node_get_type(hnode); if (node_type != NODE_TASK && node_type != NODE_DAISSOCKET) status = -EPERM; } if (!status) { /* Check node state */ mutex_lock(&hnode_mgr->node_mgr_lock); state = node_get_state(hnode); if (state != NODE_RUNNING) { status = -EBADR; /* Set the exit status if node terminated on * its own. */ if (state == NODE_DONE) *pstatus = hnode->exit_status; } else { NODE_SET_STATE(hnode, NODE_TERMINATING); } /* end of sync_enter_cs */ mutex_unlock(&hnode_mgr->node_mgr_lock); } if (!status) { /* * Send exit message. Do not change state to NODE_DONE * here. That will be done in callback. */ status = proc_get_state(pnode->processor, &proc_state, sizeof(struct dsp_processorstate)); if (status) goto func_cont; /* If processor is in error state then don't attempt to send * A kill task command */ if (proc_state.proc_state == PROC_ERROR) { status = -EPERM; goto func_cont; } msg.cmd = RMS_EXIT; msg.arg1 = hnode->node_env; killmsg.cmd = RMS_KILLTASK; killmsg.arg1 = hnode->node_env; intf_fxns = hnode_mgr->intf_fxns; if (hnode->timeout > MAXTIMEOUT) kill_time_out = MAXTIMEOUT; else kill_time_out = (hnode->timeout) * 2; status = (*intf_fxns->msg_put) (hnode->msg_queue_obj, &msg, hnode->timeout); if (status) goto func_cont; /* * Wait on synchronization object that will be * posted in the callback on receiving RMS_EXIT * message, or by node_delete. Check for valid hnode, * in case posted by node_delete(). */ status = sync_wait_on_event(hnode->sync_done, kill_time_out / 2); if (status != ETIME) goto func_cont; status = (*intf_fxns->msg_put)(hnode->msg_queue_obj, &killmsg, hnode->timeout); if (status) goto func_cont; status = sync_wait_on_event(hnode->sync_done, kill_time_out / 2); if (status) { /* * Here it goes the part of the simulation of * the DSP exception. */ dev_get_deh_mgr(hnode_mgr->dev_obj, &hdeh_mgr); if (!hdeh_mgr) goto func_cont; bridge_deh_notify(hdeh_mgr, DSP_SYSERROR, DSP_EXCEPTIONABORT); } } func_cont: if (!status) { /* Enter CS before getting exit status, in case node was * deleted. */ mutex_lock(&hnode_mgr->node_mgr_lock); /* Make sure node wasn't deleted while we blocked */ if (!hnode) { status = -EPERM; } else { *pstatus = hnode->exit_status; dev_dbg(bridge, "%s: hnode: %p env 0x%x status 0x%x\n", __func__, hnode, hnode->node_env, status); } mutex_unlock(&hnode_mgr->node_mgr_lock); } /*End of sync_enter_cs */ func_end: return status; } /* * ======== delete_node ======== * Purpose: * Free GPP resources allocated in node_allocate() or node_connect(). */ static void delete_node(struct node_object *hnode, struct process_context *pr_ctxt) { struct node_mgr *hnode_mgr; struct bridge_drv_interface *intf_fxns; u32 i; enum node_type node_type; struct stream_chnl stream; struct node_msgargs node_msg_args; struct node_taskargs task_arg_obj; #ifdef DSP_DMM_DEBUG struct dmm_object *dmm_mgr; struct proc_object *p_proc_object = (struct proc_object *)hnode->processor; #endif int status; if (!hnode) goto func_end; hnode_mgr = hnode->node_mgr; if (!hnode_mgr) goto func_end; node_type = node_get_type(hnode); if (node_type != NODE_DEVICE) { node_msg_args = hnode->create_args.asa.node_msg_args; kfree(node_msg_args.pdata); /* Free msg_ctrl queue */ if (hnode->msg_queue_obj) { intf_fxns = hnode_mgr->intf_fxns; (*intf_fxns->msg_delete_queue) (hnode-> msg_queue_obj); hnode->msg_queue_obj = NULL; } kfree(hnode->sync_done); /* Free all stream info */ if (hnode->inputs) { for (i = 0; i < MAX_INPUTS(hnode); i++) { stream = hnode->inputs[i]; free_stream(hnode_mgr, stream); } kfree(hnode->inputs); hnode->inputs = NULL; } if (hnode->outputs) { for (i = 0; i < MAX_OUTPUTS(hnode); i++) { stream = hnode->outputs[i]; free_stream(hnode_mgr, stream); } kfree(hnode->outputs); hnode->outputs = NULL; } task_arg_obj = hnode->create_args.asa.task_arg_obj; if (task_arg_obj.strm_in_def) { for (i = 0; i < MAX_INPUTS(hnode); i++) { kfree(task_arg_obj.strm_in_def[i].sz_device); task_arg_obj.strm_in_def[i].sz_device = NULL; } kfree(task_arg_obj.strm_in_def); task_arg_obj.strm_in_def = NULL; } if (task_arg_obj.strm_out_def) { for (i = 0; i < MAX_OUTPUTS(hnode); i++) { kfree(task_arg_obj.strm_out_def[i].sz_device); task_arg_obj.strm_out_def[i].sz_device = NULL; } kfree(task_arg_obj.strm_out_def); task_arg_obj.strm_out_def = NULL; } if (task_arg_obj.dsp_heap_res_addr) { status = proc_un_map(hnode->processor, (void *) task_arg_obj.dsp_heap_addr, pr_ctxt); status = proc_un_reserve_memory(hnode->processor, (void *) task_arg_obj. dsp_heap_res_addr, pr_ctxt); #ifdef DSP_DMM_DEBUG status = dmm_get_handle(p_proc_object, &dmm_mgr); if (dmm_mgr) dmm_mem_map_dump(dmm_mgr); else status = DSP_EHANDLE; #endif } } if (node_type != NODE_MESSAGE) { kfree(hnode->stream_connect); hnode->stream_connect = NULL; } kfree(hnode->str_dev_name); hnode->str_dev_name = NULL; if (hnode->ntfy_obj) { ntfy_delete(hnode->ntfy_obj); kfree(hnode->ntfy_obj); hnode->ntfy_obj = NULL; } /* These were allocated in dcd_get_object_def (via node_allocate) */ kfree(hnode->dcd_props.obj_data.node_obj.str_create_phase_fxn); hnode->dcd_props.obj_data.node_obj.str_create_phase_fxn = NULL; kfree(hnode->dcd_props.obj_data.node_obj.str_execute_phase_fxn); hnode->dcd_props.obj_data.node_obj.str_execute_phase_fxn = NULL; kfree(hnode->dcd_props.obj_data.node_obj.str_delete_phase_fxn); hnode->dcd_props.obj_data.node_obj.str_delete_phase_fxn = NULL; kfree(hnode->dcd_props.obj_data.node_obj.str_i_alg_name); hnode->dcd_props.obj_data.node_obj.str_i_alg_name = NULL; /* Free all SM address translator resources */ kfree(hnode->xlator); kfree(hnode->nldr_node_obj); hnode->nldr_node_obj = NULL; hnode->node_mgr = NULL; kfree(hnode); hnode = NULL; func_end: return; } /* * ======== delete_node_mgr ======== * Purpose: * Frees the node manager. */ static void delete_node_mgr(struct node_mgr *hnode_mgr) { struct node_object *hnode, *tmp; if (hnode_mgr) { /* Free resources */ if (hnode_mgr->dcd_mgr) dcd_destroy_manager(hnode_mgr->dcd_mgr); /* Remove any elements remaining in lists */ list_for_each_entry_safe(hnode, tmp, &hnode_mgr->node_list, list_elem) { list_del(&hnode->list_elem); delete_node(hnode, NULL); } mutex_destroy(&hnode_mgr->node_mgr_lock); if (hnode_mgr->ntfy_obj) { ntfy_delete(hnode_mgr->ntfy_obj); kfree(hnode_mgr->ntfy_obj); } if (hnode_mgr->disp_obj) disp_delete(hnode_mgr->disp_obj); if (hnode_mgr->strm_mgr_obj) strm_delete(hnode_mgr->strm_mgr_obj); /* Delete the loader */ if (hnode_mgr->nldr_obj) hnode_mgr->nldr_fxns.delete(hnode_mgr->nldr_obj); kfree(hnode_mgr); } } /* * ======== fill_stream_connect ======== * Purpose: * Fills stream information. */ static void fill_stream_connect(struct node_object *node1, struct node_object *node2, u32 stream1, u32 stream2) { u32 strm_index; struct dsp_streamconnect *strm1 = NULL; struct dsp_streamconnect *strm2 = NULL; enum node_type node1_type = NODE_TASK; enum node_type node2_type = NODE_TASK; node1_type = node_get_type(node1); node2_type = node_get_type(node2); if (node1 != (struct node_object *)DSP_HGPPNODE) { if (node1_type != NODE_DEVICE) { strm_index = node1->num_inputs + node1->num_outputs - 1; strm1 = &(node1->stream_connect[strm_index]); strm1->cb_struct = sizeof(struct dsp_streamconnect); strm1->this_node_stream_index = stream1; } if (node2 != (struct node_object *)DSP_HGPPNODE) { /* NODE == > NODE */ if (node1_type != NODE_DEVICE) { strm1->connected_node = node2; strm1->ui_connected_node_id = node2->node_uuid; strm1->connected_node_stream_index = stream2; strm1->connect_type = CONNECTTYPE_NODEOUTPUT; } if (node2_type != NODE_DEVICE) { strm_index = node2->num_inputs + node2->num_outputs - 1; strm2 = &(node2->stream_connect[strm_index]); strm2->cb_struct = sizeof(struct dsp_streamconnect); strm2->this_node_stream_index = stream2; strm2->connected_node = node1; strm2->ui_connected_node_id = node1->node_uuid; strm2->connected_node_stream_index = stream1; strm2->connect_type = CONNECTTYPE_NODEINPUT; } } else if (node1_type != NODE_DEVICE) strm1->connect_type = CONNECTTYPE_GPPOUTPUT; } else { /* GPP == > NODE */ strm_index = node2->num_inputs + node2->num_outputs - 1; strm2 = &(node2->stream_connect[strm_index]); strm2->cb_struct = sizeof(struct dsp_streamconnect); strm2->this_node_stream_index = stream2; strm2->connect_type = CONNECTTYPE_GPPINPUT; } } /* * ======== fill_stream_def ======== * Purpose: * Fills Stream attributes. */ static void fill_stream_def(struct node_object *hnode, struct node_strmdef *pstrm_def, struct dsp_strmattr *pattrs) { struct node_mgr *hnode_mgr = hnode->node_mgr; if (pattrs != NULL) { pstrm_def->num_bufs = pattrs->num_bufs; pstrm_def->buf_size = pattrs->buf_size / hnode_mgr->dsp_data_mau_size; pstrm_def->seg_id = pattrs->seg_id; pstrm_def->buf_alignment = pattrs->buf_alignment; pstrm_def->timeout = pattrs->timeout; } else { pstrm_def->num_bufs = DEFAULTNBUFS; pstrm_def->buf_size = DEFAULTBUFSIZE / hnode_mgr->dsp_data_mau_size; pstrm_def->seg_id = DEFAULTSEGID; pstrm_def->buf_alignment = DEFAULTALIGNMENT; pstrm_def->timeout = DEFAULTTIMEOUT; } } /* * ======== free_stream ======== * Purpose: * Updates the channel mask and frees the pipe id. */ static void free_stream(struct node_mgr *hnode_mgr, struct stream_chnl stream) { /* Free up the pipe id unless other node has not yet been deleted. */ if (stream.type == NODECONNECT) { if (test_bit(stream.dev_id, hnode_mgr->pipe_done_map)) { /* The other node has already been deleted */ clear_bit(stream.dev_id, hnode_mgr->pipe_done_map); clear_bit(stream.dev_id, hnode_mgr->pipe_map); } else { /* The other node has not been deleted yet */ set_bit(stream.dev_id, hnode_mgr->pipe_done_map); } } else if (stream.type == HOSTCONNECT) { if (stream.dev_id < hnode_mgr->num_chnls) { clear_bit(stream.dev_id, hnode_mgr->chnl_map); } else if (stream.dev_id < (2 * hnode_mgr->num_chnls)) { /* dsp-dma */ clear_bit(stream.dev_id - (1 * hnode_mgr->num_chnls), hnode_mgr->dma_chnl_map); } else if (stream.dev_id < (3 * hnode_mgr->num_chnls)) { /* zero-copy */ clear_bit(stream.dev_id - (2 * hnode_mgr->num_chnls), hnode_mgr->zc_chnl_map); } } } /* * ======== get_fxn_address ======== * Purpose: * Retrieves the address for create, execute or delete phase for a node. */ static int get_fxn_address(struct node_object *hnode, u32 * fxn_addr, u32 phase) { char *pstr_fxn_name = NULL; struct node_mgr *hnode_mgr = hnode->node_mgr; int status = 0; switch (phase) { case CREATEPHASE: pstr_fxn_name = hnode->dcd_props.obj_data.node_obj.str_create_phase_fxn; break; case EXECUTEPHASE: pstr_fxn_name = hnode->dcd_props.obj_data.node_obj.str_execute_phase_fxn; break; case DELETEPHASE: pstr_fxn_name = hnode->dcd_props.obj_data.node_obj.str_delete_phase_fxn; break; default: /* Should never get here */ break; } status = hnode_mgr->nldr_fxns.get_fxn_addr(hnode->nldr_node_obj, pstr_fxn_name, fxn_addr); return status; } /* * ======== get_node_info ======== * Purpose: * Retrieves the node information. */ void get_node_info(struct node_object *hnode, struct dsp_nodeinfo *node_info) { u32 i; node_info->cb_struct = sizeof(struct dsp_nodeinfo); node_info->nb_node_database_props = hnode->dcd_props.obj_data.node_obj.ndb_props; node_info->execution_priority = hnode->prio; node_info->device_owner = hnode->device_owner; node_info->number_streams = hnode->num_inputs + hnode->num_outputs; node_info->node_env = hnode->node_env; node_info->ns_execution_state = node_get_state(hnode); /* Copy stream connect data */ for (i = 0; i < hnode->num_inputs + hnode->num_outputs; i++) node_info->sc_stream_connection[i] = hnode->stream_connect[i]; } /* * ======== get_node_props ======== * Purpose: * Retrieve node properties. */ static int get_node_props(struct dcd_manager *hdcd_mgr, struct node_object *hnode, const struct dsp_uuid *node_uuid, struct dcd_genericobj *dcd_prop) { u32 len; struct node_msgargs *pmsg_args; struct node_taskargs *task_arg_obj; enum node_type node_type = NODE_TASK; struct dsp_ndbprops *pndb_props = &(dcd_prop->obj_data.node_obj.ndb_props); int status = 0; char sz_uuid[MAXUUIDLEN]; status = dcd_get_object_def(hdcd_mgr, (struct dsp_uuid *)node_uuid, DSP_DCDNODETYPE, dcd_prop); if (!status) { hnode->ntype = node_type = pndb_props->ntype; /* Create UUID value to set in registry. */ snprintf(sz_uuid, MAXUUIDLEN, "%pUL", node_uuid); dev_dbg(bridge, "(node) UUID: %s\n", sz_uuid); /* Fill in message args that come from NDB */ if (node_type != NODE_DEVICE) { pmsg_args = &(hnode->create_args.asa.node_msg_args); pmsg_args->seg_id = dcd_prop->obj_data.node_obj.msg_segid; pmsg_args->notify_type = dcd_prop->obj_data.node_obj.msg_notify_type; pmsg_args->max_msgs = pndb_props->message_depth; dev_dbg(bridge, "(node) Max Number of Messages: 0x%x\n", pmsg_args->max_msgs); } else { /* Copy device name */ len = strlen(pndb_props->ac_name); hnode->str_dev_name = kzalloc(len + 1, GFP_KERNEL); if (hnode->str_dev_name == NULL) { status = -ENOMEM; } else { strncpy(hnode->str_dev_name, pndb_props->ac_name, len); } } } if (!status) { /* Fill in create args that come from NDB */ if (node_type == NODE_TASK || node_type == NODE_DAISSOCKET) { task_arg_obj = &(hnode->create_args.asa.task_arg_obj); task_arg_obj->prio = pndb_props->prio; task_arg_obj->stack_size = pndb_props->stack_size; task_arg_obj->sys_stack_size = pndb_props->sys_stack_size; task_arg_obj->stack_seg = pndb_props->stack_seg; dev_dbg(bridge, "(node) Priority: 0x%x Stack Size: " "0x%x words System Stack Size: 0x%x words " "Stack Segment: 0x%x profile count : 0x%x\n", task_arg_obj->prio, task_arg_obj->stack_size, task_arg_obj->sys_stack_size, task_arg_obj->stack_seg, pndb_props->count_profiles); } } return status; } /* * ======== get_proc_props ======== * Purpose: * Retrieve the processor properties. */ static int get_proc_props(struct node_mgr *hnode_mgr, struct dev_object *hdev_obj) { struct cfg_hostres *host_res; struct bridge_dev_context *pbridge_context; int status = 0; status = dev_get_bridge_context(hdev_obj, &pbridge_context); if (!pbridge_context) status = -EFAULT; if (!status) { host_res = pbridge_context->resources; if (!host_res) return -EPERM; hnode_mgr->chnl_offset = host_res->chnl_offset; hnode_mgr->chnl_buf_size = host_res->chnl_buf_size; hnode_mgr->num_chnls = host_res->num_chnls; /* * PROC will add an API to get dsp_processorinfo. * Fill in default values for now. */ /* TODO -- Instead of hard coding, take from registry */ hnode_mgr->proc_family = 6000; hnode_mgr->proc_type = 6410; hnode_mgr->min_pri = DSP_NODE_MIN_PRIORITY; hnode_mgr->max_pri = DSP_NODE_MAX_PRIORITY; hnode_mgr->dsp_word_size = DSPWORDSIZE; hnode_mgr->dsp_data_mau_size = DSPWORDSIZE; hnode_mgr->dsp_mau_size = 1; } return status; } /* * ======== node_get_uuid_props ======== * Purpose: * Fetch Node UUID properties from DCD/DOF file. */ int node_get_uuid_props(void *hprocessor, const struct dsp_uuid *node_uuid, struct dsp_ndbprops *node_props) { struct node_mgr *hnode_mgr = NULL; struct dev_object *hdev_obj; int status = 0; struct dcd_nodeprops dcd_node_props; struct dsp_processorstate proc_state; if (hprocessor == NULL || node_uuid == NULL) { status = -EFAULT; goto func_end; } status = proc_get_state(hprocessor, &proc_state, sizeof(struct dsp_processorstate)); if (status) goto func_end; /* If processor is in error state then don't attempt to send the message */ if (proc_state.proc_state == PROC_ERROR) { status = -EPERM; goto func_end; } status = proc_get_dev_object(hprocessor, &hdev_obj); if (hdev_obj) { status = dev_get_node_manager(hdev_obj, &hnode_mgr); if (hnode_mgr == NULL) { status = -EFAULT; goto func_end; } } /* * Enter the critical section. This is needed because * dcd_get_object_def will ultimately end up calling dbll_open/close, * which needs to be protected in order to not corrupt the zlib manager * (COD). */ mutex_lock(&hnode_mgr->node_mgr_lock); dcd_node_props.str_create_phase_fxn = NULL; dcd_node_props.str_execute_phase_fxn = NULL; dcd_node_props.str_delete_phase_fxn = NULL; dcd_node_props.str_i_alg_name = NULL; status = dcd_get_object_def(hnode_mgr->dcd_mgr, (struct dsp_uuid *)node_uuid, DSP_DCDNODETYPE, (struct dcd_genericobj *)&dcd_node_props); if (!status) { *node_props = dcd_node_props.ndb_props; kfree(dcd_node_props.str_create_phase_fxn); kfree(dcd_node_props.str_execute_phase_fxn); kfree(dcd_node_props.str_delete_phase_fxn); kfree(dcd_node_props.str_i_alg_name); } /* Leave the critical section, we're done. */ mutex_unlock(&hnode_mgr->node_mgr_lock); func_end: return status; } /* * ======== get_rms_fxns ======== * Purpose: * Retrieve the RMS functions. */ static int get_rms_fxns(struct node_mgr *hnode_mgr) { s32 i; struct dev_object *dev_obj = hnode_mgr->dev_obj; int status = 0; static char *psz_fxns[NUMRMSFXNS] = { "RMS_queryServer", /* RMSQUERYSERVER */ "RMS_configureServer", /* RMSCONFIGURESERVER */ "RMS_createNode", /* RMSCREATENODE */ "RMS_executeNode", /* RMSEXECUTENODE */ "RMS_deleteNode", /* RMSDELETENODE */ "RMS_changeNodePriority", /* RMSCHANGENODEPRIORITY */ "RMS_readMemory", /* RMSREADMEMORY */ "RMS_writeMemory", /* RMSWRITEMEMORY */ "RMS_copy", /* RMSCOPY */ }; for (i = 0; i < NUMRMSFXNS; i++) { status = dev_get_symbol(dev_obj, psz_fxns[i], &(hnode_mgr->fxn_addrs[i])); if (status) { if (status == -ESPIPE) { /* * May be loaded dynamically (in the future), * but return an error for now. */ dev_dbg(bridge, "%s: RMS function: %s currently" " not loaded\n", __func__, psz_fxns[i]); } else { dev_dbg(bridge, "%s: Symbol not found: %s " "status = 0x%x\n", __func__, psz_fxns[i], status); break; } } } return status; } /* * ======== ovly ======== * Purpose: * Called during overlay.Sends command to RMS to copy a block of data. */ static u32 ovly(void *priv_ref, u32 dsp_run_addr, u32 dsp_load_addr, u32 ul_num_bytes, u32 mem_space) { struct node_object *hnode = (struct node_object *)priv_ref; struct node_mgr *hnode_mgr; u32 ul_bytes = 0; u32 ul_size; u32 ul_timeout; int status = 0; struct bridge_dev_context *hbridge_context; /* Function interface to Bridge driver*/ struct bridge_drv_interface *intf_fxns; hnode_mgr = hnode->node_mgr; ul_size = ul_num_bytes / hnode_mgr->dsp_word_size; ul_timeout = hnode->timeout; /* Call new MemCopy function */ intf_fxns = hnode_mgr->intf_fxns; status = dev_get_bridge_context(hnode_mgr->dev_obj, &hbridge_context); if (!status) { status = (*intf_fxns->brd_mem_copy) (hbridge_context, dsp_run_addr, dsp_load_addr, ul_num_bytes, (u32) mem_space); if (!status) ul_bytes = ul_num_bytes; else pr_debug("%s: failed to copy brd memory, status 0x%x\n", __func__, status); } else { pr_debug("%s: failed to get Bridge context, status 0x%x\n", __func__, status); } return ul_bytes; } /* * ======== mem_write ======== */ static u32 mem_write(void *priv_ref, u32 dsp_add, void *pbuf, u32 ul_num_bytes, u32 mem_space) { struct node_object *hnode = (struct node_object *)priv_ref; struct node_mgr *hnode_mgr; u16 mem_sect_type; u32 ul_timeout; int status = 0; struct bridge_dev_context *hbridge_context; /* Function interface to Bridge driver */ struct bridge_drv_interface *intf_fxns; hnode_mgr = hnode->node_mgr; ul_timeout = hnode->timeout; mem_sect_type = (mem_space & DBLL_CODE) ? RMS_CODE : RMS_DATA; /* Call new MemWrite function */ intf_fxns = hnode_mgr->intf_fxns; status = dev_get_bridge_context(hnode_mgr->dev_obj, &hbridge_context); status = (*intf_fxns->brd_mem_write) (hbridge_context, pbuf, dsp_add, ul_num_bytes, mem_sect_type); return ul_num_bytes; } #ifdef CONFIG_TIDSPBRIDGE_BACKTRACE /* * ======== node_find_addr ======== */ int node_find_addr(struct node_mgr *node_mgr, u32 sym_addr, u32 offset_range, void *sym_addr_output, char *sym_name) { struct node_object *node_obj; int status = -ENOENT; list_for_each_entry(node_obj, &node_mgr->node_list, list_elem) { status = nldr_find_addr(node_obj->nldr_node_obj, sym_addr, offset_range, sym_addr_output, sym_name); if (!status) { pr_debug("%s(0x%x, 0x%x, 0x%x, 0x%x, %s)\n", __func__, (unsigned int) node_mgr, sym_addr, offset_range, (unsigned int) sym_addr_output, sym_name); break; } } return status; } #endif