|
@@ -0,0 +1,3231 @@
|
|
|
+/*
|
|
|
+ * 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.
|
|
|
+ */
|
|
|
+
|
|
|
+/* ----------------------------------- Host OS */
|
|
|
+#include <dspbridge/host_os.h>
|
|
|
+
|
|
|
+/* ----------------------------------- DSP/BIOS Bridge */
|
|
|
+#include <dspbridge/std.h>
|
|
|
+#include <dspbridge/dbdefs.h>
|
|
|
+
|
|
|
+/* ----------------------------------- Trace & Debug */
|
|
|
+#include <dspbridge/dbc.h>
|
|
|
+
|
|
|
+/* ----------------------------------- OS Adaptation Layer */
|
|
|
+#include <dspbridge/cfg.h>
|
|
|
+#include <dspbridge/list.h>
|
|
|
+#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/gb.h>
|
|
|
+#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/drvdefs.h>
|
|
|
+#include <dspbridge/resourcecleanup.h>
|
|
|
+#include <_tiomap.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 *hdev_obj; /* Device object */
|
|
|
+ /* Function interface to Bridge driver */
|
|
|
+ struct bridge_drv_interface *intf_fxns;
|
|
|
+ struct dcd_manager *hdcd_mgr; /* Proc/Node data manager */
|
|
|
+ struct disp_object *disp_obj; /* Node dispatcher */
|
|
|
+ struct lst_list *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 */
|
|
|
+ struct gb_t_map *pipe_map; /* Pipe connection bit map */
|
|
|
+ struct gb_t_map *pipe_done_map; /* Pipes that are half free */
|
|
|
+ struct gb_t_map *chnl_map; /* Channel allocation bit map */
|
|
|
+ struct gb_t_map *dma_chnl_map; /* DMA Channel allocation bit map */
|
|
|
+ struct gb_t_map *zc_chnl_map; /* Zero-Copy Channel alloc bit map */
|
|
|
+ struct ntfy_object *ntfy_obj; /* Manages registered notifications */
|
|
|
+ struct mutex node_mgr_lock; /* For critical sections */
|
|
|
+ u32 ul_fxn_addrs[NUMRMSFXNS]; /* RMS function addresses */
|
|
|
+ struct msg_mgr *msg_mgr_obj;
|
|
|
+
|
|
|
+ /* Processor properties needed by Node Dispatcher */
|
|
|
+ u32 ul_num_chnls; /* Total number of channels */
|
|
|
+ u32 ul_chnl_offset; /* Offset of chnl ids rsvd for RMS */
|
|
|
+ u32 ul_chnl_buf_size; /* Buffer size for data to RMS */
|
|
|
+ int proc_family; /* eg, 5000 */
|
|
|
+ int proc_type; /* eg, 5510 */
|
|
|
+ u32 udsp_word_size; /* Size of DSP word on host bytes */
|
|
|
+ u32 udsp_data_mau_size; /* Size of DSP data MAU */
|
|
|
+ u32 udsp_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 */
|
|
|
+ bool loader_init; /* Loader Init function succeeded? */
|
|
|
+};
|
|
|
+
|
|
|
+/*
|
|
|
+ * ======== 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 *hnode_mgr; /* The manager of this node */
|
|
|
+ struct proc_object *hprocessor; /* Back pointer to processor */
|
|
|
+ struct dsp_uuid node_uuid; /* Node's ID */
|
|
|
+ s32 prio; /* Node's current priority */
|
|
|
+ u32 utimeout; /* Timeout for blocking NODE calls */
|
|
|
+ u32 heap_size; /* Heap Size */
|
|
|
+ u32 udsp_heap_virt_addr; /* Heap Size */
|
|
|
+ u32 ugpp_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 *pargs; /* Optional args to pass to node */
|
|
|
+ struct ntfy_object *ntfy_obj; /* Manages registered notifications */
|
|
|
+ char *pstr_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 = {
|
|
|
+ 0, /* cb_struct */
|
|
|
+ 1, /* segment_id */
|
|
|
+ 0, /* buf_alignment */
|
|
|
+};
|
|
|
+
|
|
|
+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 *hNode1,
|
|
|
+ struct node_object *hNode2, u32 uStream1,
|
|
|
+ u32 uStream2);
|
|
|
+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 * pulFxnAddr,
|
|
|
+ u32 uPhase);
|
|
|
+static int get_node_props(struct dcd_manager *hdcd_mgr,
|
|
|
+ struct node_object *hnode,
|
|
|
+ CONST struct dsp_uuid *pNodeId,
|
|
|
+ struct dcd_genericobj *pdcdProps);
|
|
|
+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 ulDspRunAddr, u32 ulDspLoadAddr,
|
|
|
+ u32 ul_num_bytes, u32 nMemSpace);
|
|
|
+static u32 mem_write(void *priv_ref, u32 ulDspAddr, void *pbuf,
|
|
|
+ u32 ul_num_bytes, u32 nMemSpace);
|
|
|
+
|
|
|
+static u32 refs; /* module reference count */
|
|
|
+
|
|
|
+/* Dynamic loader functions. */
|
|
|
+static struct node_ldr_fxns nldr_fxns = {
|
|
|
+ nldr_allocate,
|
|
|
+ nldr_create,
|
|
|
+ nldr_delete,
|
|
|
+ nldr_exit,
|
|
|
+ nldr_get_fxn_addr,
|
|
|
+ nldr_init,
|
|
|
+ nldr_load,
|
|
|
+ nldr_unload,
|
|
|
+};
|
|
|
+
|
|
|
+enum node_state node_get_state(void *hnode)
|
|
|
+{
|
|
|
+ struct node_object *pnode = (struct node_object *)hnode;
|
|
|
+ if (!pnode)
|
|
|
+ return -1;
|
|
|
+ else
|
|
|
+ return pnode->node_state;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * ======== node_allocate ========
|
|
|
+ * Purpose:
|
|
|
+ * Allocate GPP resources to manage a node on the DSP.
|
|
|
+ */
|
|
|
+int node_allocate(struct proc_object *hprocessor,
|
|
|
+ IN CONST struct dsp_uuid *pNodeId,
|
|
|
+ OPTIONAL IN CONST struct dsp_cbdata *pargs,
|
|
|
+ OPTIONAL IN CONST struct dsp_nodeattrin *attr_in,
|
|
|
+ OUT struct node_object **ph_node,
|
|
|
+ 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_addr, ul_stack_seg_val;
|
|
|
+ u32 ul_gpp_mem_base;
|
|
|
+ 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;
|
|
|
+
|
|
|
+ DBC_REQUIRE(refs > 0);
|
|
|
+ DBC_REQUIRE(hprocessor != NULL);
|
|
|
+ DBC_REQUIRE(ph_node != NULL);
|
|
|
+ DBC_REQUIRE(pNodeId != NULL);
|
|
|
+
|
|
|
+ *ph_node = 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 (DSP_SUCCEEDED(status)) {
|
|
|
+ status = dev_get_node_manager(hdev_obj, &hnode_mgr);
|
|
|
+ if (hnode_mgr == NULL)
|
|
|
+ status = -EPERM;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if (DSP_FAILED(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 (DSP_FAILED(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->ul_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 (DSP_FAILED(status))
|
|
|
+ goto func_end;
|
|
|
+
|
|
|
+ pnode = kzalloc(sizeof(struct node_object), GFP_KERNEL);
|
|
|
+ if (pnode == NULL) {
|
|
|
+ status = -ENOMEM;
|
|
|
+ goto func_end;
|
|
|
+ }
|
|
|
+ pnode->hnode_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->hdcd_mgr, pnode, pNodeId,
|
|
|
+ &(pnode->dcd_props));
|
|
|
+ if (DSP_FAILED(status))
|
|
|
+ goto func_cont;
|
|
|
+
|
|
|
+ pnode->node_uuid = *pNodeId;
|
|
|
+ pnode->hprocessor = hprocessor;
|
|
|
+ pnode->ntype = pnode->dcd_props.obj_data.node_obj.ndb_props.ntype;
|
|
|
+ pnode->utimeout = pnode->dcd_props.obj_data.node_obj.ndb_props.utimeout;
|
|
|
+ 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.udsp_heap_addr = 0;
|
|
|
+ pnode->create_args.asa.task_arg_obj.udsp_heap_res_addr = 0;
|
|
|
+ pnode->create_args.asa.task_arg_obj.ugpp_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.ugpp_heap_addr =
|
|
|
+ (u32) attr_in->pgpp_virt_addr;
|
|
|
+ }
|
|
|
+ if (DSP_FAILED(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.udsp_heap_res_addr),
|
|
|
+ pr_ctxt);
|
|
|
+ if (DSP_FAILED(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.
|
|
|
+ udsp_heap_res_addr, (void **)&mapped_addr, map_attrs,
|
|
|
+ pr_ctxt);
|
|
|
+ if (DSP_FAILED(status))
|
|
|
+ pr_err("%s: Failed to map memory for Heap: 0x%x\n",
|
|
|
+ __func__, status);
|
|
|
+ else
|
|
|
+ pnode->create_args.asa.task_arg_obj.udsp_heap_addr =
|
|
|
+ (u32) mapped_addr;
|
|
|
+
|
|
|
+func_cont:
|
|
|
+ mutex_unlock(&hnode_mgr->node_mgr_lock);
|
|
|
+ if (attr_in != NULL) {
|
|
|
+ /* Overrides of NBD properties */
|
|
|
+ pnode->utimeout = attr_in->utimeout;
|
|
|
+ pnode->prio = attr_in->prio;
|
|
|
+ }
|
|
|
+ /* Create object to manage notifications */
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ pnode->ntfy_obj = kmalloc(sizeof(struct ntfy_object),
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (pnode->ntfy_obj)
|
|
|
+ ntfy_init(pnode->ntfy_obj);
|
|
|
+ else
|
|
|
+ status = -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (DSP_SUCCEEDED(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 (DSP_SUCCEEDED(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 (DSP_SUCCEEDED(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 (DSP_SUCCEEDED(status)) {
|
|
|
+ /*Get the shared mem mgr for this nodes dev object */
|
|
|
+ status = cmm_get_handle(hprocessor, &hcmm_mgr);
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ /* Allocate a SM addr translator for this node
|
|
|
+ * w/ deflt attr */
|
|
|
+ status = cmm_xlator_create(&pnode->xlator,
|
|
|
+ hcmm_mgr, NULL);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (DSP_SUCCEEDED(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 (DSP_SUCCEEDED(status) && node_type != NODE_DEVICE) {
|
|
|
+ /* Create a message queue for this node */
|
|
|
+ intf_fxns = hnode_mgr->intf_fxns;
|
|
|
+ status =
|
|
|
+ (*intf_fxns->pfn_msg_create_queue) (hnode_mgr->msg_mgr_obj,
|
|
|
+ &pnode->msg_queue_obj,
|
|
|
+ 0,
|
|
|
+ pnode->create_args.asa.
|
|
|
+ node_msg_args.max_msgs,
|
|
|
+ pnode);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ /* Create object for dynamic loading */
|
|
|
+
|
|
|
+ status = hnode_mgr->nldr_fxns.pfn_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 (DSP_SUCCEEDED(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) {
|
|
|
+ status =
|
|
|
+ hnode_mgr->nldr_fxns.
|
|
|
+ pfn_get_fxn_addr(pnode->nldr_node_obj, "DYNEXT_BEG",
|
|
|
+ &dynext_base);
|
|
|
+ if (DSP_FAILED(status))
|
|
|
+ pr_err("%s: Failed to get addr for DYNEXT_BEG"
|
|
|
+ " status = 0x%x\n", __func__, status);
|
|
|
+
|
|
|
+ status =
|
|
|
+ hnode_mgr->nldr_fxns.
|
|
|
+ pfn_get_fxn_addr(pnode->nldr_node_obj,
|
|
|
+ "L1DSRAM_HEAP", &pul_value);
|
|
|
+
|
|
|
+ if (DSP_FAILED(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 (DSP_FAILED(status)) {
|
|
|
+ pr_err("%s: Failed to get host resource, status"
|
|
|
+ " = 0x%x\n", __func__, status);
|
|
|
+ goto func_end;
|
|
|
+ }
|
|
|
+
|
|
|
+ ul_gpp_mem_base = (u32) host_res->dw_mem_base[1];
|
|
|
+ off_set = pul_value - dynext_base;
|
|
|
+ ul_stack_seg_addr = ul_gpp_mem_base + off_set;
|
|
|
+ ul_stack_seg_val = (u32) *((reg_uword32 *)
|
|
|
+ ((u32)
|
|
|
+ (ul_stack_seg_addr)));
|
|
|
+
|
|
|
+ dev_dbg(bridge, "%s: StackSegVal = 0x%x, StackSegAddr ="
|
|
|
+ " 0x%x\n", __func__, ul_stack_seg_val,
|
|
|
+ ul_stack_seg_addr);
|
|
|
+
|
|
|
+ pnode->create_args.asa.task_arg_obj.stack_seg =
|
|
|
+ ul_stack_seg_val;
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ /* Add the node to the node manager's list of allocated
|
|
|
+ * nodes. */
|
|
|
+ lst_init_elem((struct list_head *)pnode);
|
|
|
+ NODE_SET_STATE(pnode, NODE_ALLOCATED);
|
|
|
+
|
|
|
+ mutex_lock(&hnode_mgr->node_mgr_lock);
|
|
|
+
|
|
|
+ lst_put_tail(hnode_mgr->node_list, (struct list_head *) pnode);
|
|
|
+ ++(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;
|
|
|
+
|
|
|
+ if (DSP_SUCCEEDED(status))
|
|
|
+ *ph_node = pnode;
|
|
|
+
|
|
|
+ /* Notify all clients registered for DSP_NODESTATECHANGE. */
|
|
|
+ proc_notify_all_clients(hprocessor, DSP_NODESTATECHANGE);
|
|
|
+ } else {
|
|
|
+ /* Cleanup */
|
|
|
+ if (pnode)
|
|
|
+ delete_node(pnode, pr_ctxt);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ drv_insert_node_res_element(*ph_node, &node_res, pr_ctxt);
|
|
|
+ drv_proc_node_update_heap_status(node_res, true);
|
|
|
+ drv_proc_node_update_status(node_res, true);
|
|
|
+ }
|
|
|
+ DBC_ENSURE((DSP_FAILED(status) && (*ph_node == NULL)) ||
|
|
|
+ (DSP_SUCCEEDED(status) && *ph_node));
|
|
|
+func_end:
|
|
|
+ dev_dbg(bridge, "%s: hprocessor: %p pNodeId: %p pargs: %p attr_in: %p "
|
|
|
+ "ph_node: %p status: 0x%x\n", __func__, hprocessor,
|
|
|
+ pNodeId, pargs, attr_in, ph_node, 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,
|
|
|
+ OPTIONAL IN OUT struct dsp_bufferattr *pattr,
|
|
|
+ OUT u8 **pbuffer)
|
|
|
+{
|
|
|
+ struct node_object *pnode = (struct node_object *)hnode;
|
|
|
+ int status = 0;
|
|
|
+ bool va_flag = false;
|
|
|
+ bool set_info;
|
|
|
+ u32 proc_id;
|
|
|
+
|
|
|
+ DBC_REQUIRE(refs > 0);
|
|
|
+ DBC_REQUIRE(pbuffer != NULL);
|
|
|
+
|
|
|
+ DBC_REQUIRE(usize > 0);
|
|
|
+
|
|
|
+ if (!pnode)
|
|
|
+ status = -EFAULT;
|
|
|
+ else if (node_get_type(pnode) == NODE_DEVICE)
|
|
|
+ status = -EPERM;
|
|
|
+
|
|
|
+ if (DSP_FAILED(status))
|
|
|
+ goto func_end;
|
|
|
+
|
|
|
+ if (pattr == NULL)
|
|
|
+ pattr = &node_dfltbufattrs; /* set defaults */
|
|
|
+
|
|
|
+ status = proc_get_processor_id(pnode->hprocessor, &proc_id);
|
|
|
+ if (proc_id != DSP_UNIT) {
|
|
|
+ DBC_ASSERT(NULL);
|
|
|
+ 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 (DSP_SUCCEEDED(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 suportted */
|
|
|
+ status = -EPERM;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (DSP_SUCCEEDED(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;
|
|
|
+
|
|
|
+ DBC_REQUIRE(refs > 0);
|
|
|
+
|
|
|
+ if (!hnode || !hnode->hnode_mgr) {
|
|
|
+ status = -EFAULT;
|
|
|
+ } else {
|
|
|
+ hnode_mgr = hnode->hnode_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 (DSP_FAILED(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->hprocessor, &proc_id);
|
|
|
+ if (proc_id == DSP_UNIT) {
|
|
|
+ status =
|
|
|
+ disp_node_change_priority(hnode_mgr->disp_obj,
|
|
|
+ hnode,
|
|
|
+ hnode_mgr->ul_fxn_addrs
|
|
|
+ [RMSCHANGENODEPRIORITY],
|
|
|
+ hnode->node_env, prio);
|
|
|
+ }
|
|
|
+ if (DSP_SUCCEEDED(status))
|
|
|
+ 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 *hNode1, u32 uStream1,
|
|
|
+ struct node_object *hNode2,
|
|
|
+ u32 uStream2, OPTIONAL IN struct dsp_strmattr *pattrs,
|
|
|
+ OPTIONAL IN 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;
|
|
|
+ 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 = GB_NOBITS;
|
|
|
+ u32 chnl_id = GB_NOBITS;
|
|
|
+ s8 chnl_mode;
|
|
|
+ u32 dw_length;
|
|
|
+ int status = 0;
|
|
|
+ DBC_REQUIRE(refs > 0);
|
|
|
+
|
|
|
+ if ((hNode1 != (struct node_object *)DSP_HGPPNODE && !hNode1) ||
|
|
|
+ (hNode2 != (struct node_object *)DSP_HGPPNODE && !hNode2))
|
|
|
+ status = -EFAULT;
|
|
|
+
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ /* The two nodes must be on the same processor */
|
|
|
+ if (hNode1 != (struct node_object *)DSP_HGPPNODE &&
|
|
|
+ hNode2 != (struct node_object *)DSP_HGPPNODE &&
|
|
|
+ hNode1->hnode_mgr != hNode2->hnode_mgr)
|
|
|
+ status = -EPERM;
|
|
|
+ /* Cannot connect a node to itself */
|
|
|
+ if (hNode1 == hNode2)
|
|
|
+ status = -EPERM;
|
|
|
+
|
|
|
+ }
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ /* node_get_type() will return NODE_GPP if hnode =
|
|
|
+ * DSP_HGPPNODE. */
|
|
|
+ node1_type = node_get_type(hNode1);
|
|
|
+ node2_type = node_get_type(hNode2);
|
|
|
+ /* Check stream indices ranges */
|
|
|
+ if ((node1_type != NODE_GPP && node1_type != NODE_DEVICE &&
|
|
|
+ uStream1 >= MAX_OUTPUTS(hNode1)) || (node2_type != NODE_GPP
|
|
|
+ && node2_type !=
|
|
|
+ NODE_DEVICE
|
|
|
+ && uStream2 >=
|
|
|
+ MAX_INPUTS(hNode2)))
|
|
|
+ status = -EINVAL;
|
|
|
+ }
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ /*
|
|
|
+ * 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))
|
|
|
+ status = -EPERM;
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ * Check stream mode. Default is STRMMODE_PROCCOPY.
|
|
|
+ */
|
|
|
+ if (DSP_SUCCEEDED(status) && pattrs) {
|
|
|
+ if (pattrs->strm_mode != STRMMODE_PROCCOPY)
|
|
|
+ status = -EPERM; /* illegal stream mode */
|
|
|
+
|
|
|
+ }
|
|
|
+ if (DSP_FAILED(status))
|
|
|
+ goto func_end;
|
|
|
+
|
|
|
+ if (node1_type != NODE_GPP) {
|
|
|
+ hnode_mgr = hNode1->hnode_mgr;
|
|
|
+ } else {
|
|
|
+ DBC_ASSERT(hNode2 != (struct node_object *)DSP_HGPPNODE);
|
|
|
+ hnode_mgr = hNode2->hnode_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(hNode1) != NODE_ALLOCATED)
|
|
|
+ status = -EBADR;
|
|
|
+
|
|
|
+ if (node2_type != NODE_GPP && node_get_state(hNode2) != NODE_ALLOCATED)
|
|
|
+ status = -EBADR;
|
|
|
+
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ /* 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 =
|
|
|
+ &(hNode1->create_args.asa.
|
|
|
+ task_arg_obj.strm_out_def[uStream1]);
|
|
|
+ if (output->sz_device != NULL)
|
|
|
+ status = -EISCONN;
|
|
|
+
|
|
|
+ }
|
|
|
+ if (node2_type == NODE_TASK || node2_type == NODE_DAISSOCKET) {
|
|
|
+ input =
|
|
|
+ &(hNode2->create_args.asa.
|
|
|
+ task_arg_obj.strm_in_def[uStream2]);
|
|
|
+ if (input->sz_device != NULL)
|
|
|
+ status = -EISCONN;
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /* Connecting two task nodes? */
|
|
|
+ if (DSP_SUCCEEDED(status) && ((node1_type == NODE_TASK ||
|
|
|
+ node1_type == NODE_DAISSOCKET)
|
|
|
+ && (node2_type == NODE_TASK
|
|
|
+ || node2_type == NODE_DAISSOCKET))) {
|
|
|
+ /* Find available pipe */
|
|
|
+ pipe_id = gb_findandset(hnode_mgr->pipe_map);
|
|
|
+ if (pipe_id == GB_NOBITS) {
|
|
|
+ status = -ECONNREFUSED;
|
|
|
+ } else {
|
|
|
+ hNode1->outputs[uStream1].type = NODECONNECT;
|
|
|
+ hNode2->inputs[uStream2].type = NODECONNECT;
|
|
|
+ hNode1->outputs[uStream1].dev_id = pipe_id;
|
|
|
+ hNode2->inputs[uStream2].dev_id = pipe_id;
|
|
|
+ output->sz_device = kzalloc(PIPENAMELEN + 1,
|
|
|
+ GFP_KERNEL);
|
|
|
+ input->sz_device = kzalloc(PIPENAMELEN + 1, GFP_KERNEL);
|
|
|
+ if (output->sz_device == NULL ||
|
|
|
+ input->sz_device == NULL) {
|
|
|
+ /* Undo the connection */
|
|
|
+ kfree(output->sz_device);
|
|
|
+
|
|
|
+ kfree(input->sz_device);
|
|
|
+
|
|
|
+ output->sz_device = NULL;
|
|
|
+ input->sz_device = NULL;
|
|
|
+ gb_clear(hnode_mgr->pipe_map, pipe_id);
|
|
|
+ status = -ENOMEM;
|
|
|
+ } else {
|
|
|
+ /* 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 (DSP_SUCCEEDED(status) && (node1_type == NODE_GPP ||
|
|
|
+ node2_type == NODE_GPP)) {
|
|
|
+ if (node1_type == NODE_GPP) {
|
|
|
+ chnl_mode = CHNL_MODETODSP;
|
|
|
+ } else {
|
|
|
+ DBC_ASSERT(node2_type == NODE_GPP);
|
|
|
+ chnl_mode = 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. */
|
|
|
+ if (pattrs) {
|
|
|
+ if (pattrs->strm_mode == STRMMODE_RDMA) {
|
|
|
+ chnl_id =
|
|
|
+ gb_findandset(hnode_mgr->dma_chnl_map);
|
|
|
+ /* dma chans are 2nd transport chnl set
|
|
|
+ * ids(e.g. 16-31) */
|
|
|
+ (chnl_id != GB_NOBITS) ?
|
|
|
+ (chnl_id =
|
|
|
+ chnl_id +
|
|
|
+ hnode_mgr->ul_num_chnls) : chnl_id;
|
|
|
+ } else if (pattrs->strm_mode == STRMMODE_ZEROCOPY) {
|
|
|
+ chnl_id = gb_findandset(hnode_mgr->zc_chnl_map);
|
|
|
+ /* zero-copy chans are 3nd transport set
|
|
|
+ * (e.g. 32-47) */
|
|
|
+ (chnl_id != GB_NOBITS) ? (chnl_id = chnl_id +
|
|
|
+ (2 *
|
|
|
+ hnode_mgr->
|
|
|
+ ul_num_chnls))
|
|
|
+ : chnl_id;
|
|
|
+ } else { /* must be PROCCOPY */
|
|
|
+ DBC_ASSERT(pattrs->strm_mode ==
|
|
|
+ STRMMODE_PROCCOPY);
|
|
|
+ chnl_id = gb_findandset(hnode_mgr->chnl_map);
|
|
|
+ /* e.g. 0-15 */
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ /* default to PROCCOPY */
|
|
|
+ chnl_id = gb_findandset(hnode_mgr->chnl_map);
|
|
|
+ }
|
|
|
+ if (chnl_id == GB_NOBITS) {
|
|
|
+ status = -ECONNREFUSED;
|
|
|
+ goto func_cont2;
|
|
|
+ }
|
|
|
+ pstr_dev_name = kzalloc(HOSTNAMELEN + 1, GFP_KERNEL);
|
|
|
+ if (pstr_dev_name != NULL)
|
|
|
+ goto func_cont2;
|
|
|
+
|
|
|
+ if (pattrs) {
|
|
|
+ if (pattrs->strm_mode == STRMMODE_RDMA) {
|
|
|
+ gb_clear(hnode_mgr->dma_chnl_map, chnl_id -
|
|
|
+ hnode_mgr->ul_num_chnls);
|
|
|
+ } else if (pattrs->strm_mode == STRMMODE_ZEROCOPY) {
|
|
|
+ gb_clear(hnode_mgr->zc_chnl_map, chnl_id -
|
|
|
+ (2 * hnode_mgr->ul_num_chnls));
|
|
|
+ } else {
|
|
|
+ DBC_ASSERT(pattrs->strm_mode ==
|
|
|
+ STRMMODE_PROCCOPY);
|
|
|
+ gb_clear(hnode_mgr->chnl_map, chnl_id);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ gb_clear(hnode_mgr->chnl_map, chnl_id);
|
|
|
+ }
|
|
|
+ status = -ENOMEM;
|
|
|
+func_cont2:
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ if (hNode1 == (struct node_object *)DSP_HGPPNODE) {
|
|
|
+ hNode2->inputs[uStream2].type = HOSTCONNECT;
|
|
|
+ hNode2->inputs[uStream2].dev_id = chnl_id;
|
|
|
+ input->sz_device = pstr_dev_name;
|
|
|
+ } else {
|
|
|
+ hNode1->outputs[uStream1].type = HOSTCONNECT;
|
|
|
+ hNode1->outputs[uStream1].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 (DSP_SUCCEEDED(status) && ((node1_type == NODE_DEVICE) ||
|
|
|
+ (node2_type == NODE_DEVICE))) {
|
|
|
+ if (node2_type == NODE_DEVICE) {
|
|
|
+ /* node1 == > device */
|
|
|
+ dev_node_obj = hNode2;
|
|
|
+ hnode = hNode1;
|
|
|
+ pstream = &(hNode1->outputs[uStream1]);
|
|
|
+ pstrm_def = output;
|
|
|
+ } else {
|
|
|
+ /* device == > node2 */
|
|
|
+ dev_node_obj = hNode1;
|
|
|
+ hnode = hNode2;
|
|
|
+ pstream = &(hNode2->inputs[uStream2]);
|
|
|
+ pstrm_def = input;
|
|
|
+ }
|
|
|
+ /* Set up create args */
|
|
|
+ pstream->type = DEVICECONNECT;
|
|
|
+ dw_length = strlen(dev_node_obj->pstr_dev_name);
|
|
|
+ if (conn_param != NULL) {
|
|
|
+ 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 == NULL) {
|
|
|
+ status = -ENOMEM;
|
|
|
+ } else {
|
|
|
+ /* Copy device name */
|
|
|
+ strncpy(pstrm_def->sz_device,
|
|
|
+ dev_node_obj->pstr_dev_name, dw_length);
|
|
|
+ if (conn_param != NULL) {
|
|
|
+ strncat(pstrm_def->sz_device,
|
|
|
+ (char *)conn_param->node_data,
|
|
|
+ (u32) conn_param->cb_data);
|
|
|
+ }
|
|
|
+ dev_node_obj->device_owner = hnode;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ /* Fill in create args */
|
|
|
+ if (node1_type == NODE_TASK || node1_type == NODE_DAISSOCKET) {
|
|
|
+ hNode1->create_args.asa.task_arg_obj.num_outputs++;
|
|
|
+ fill_stream_def(hNode1, output, pattrs);
|
|
|
+ }
|
|
|
+ if (node2_type == NODE_TASK || node2_type == NODE_DAISSOCKET) {
|
|
|
+ hNode2->create_args.asa.task_arg_obj.num_inputs++;
|
|
|
+ fill_stream_def(hNode2, input, pattrs);
|
|
|
+ }
|
|
|
+ /* Update hNode1 and hNode2 stream_connect */
|
|
|
+ if (node1_type != NODE_GPP && node1_type != NODE_DEVICE) {
|
|
|
+ hNode1->num_outputs++;
|
|
|
+ if (uStream1 > hNode1->max_output_index)
|
|
|
+ hNode1->max_output_index = uStream1;
|
|
|
+
|
|
|
+ }
|
|
|
+ if (node2_type != NODE_GPP && node2_type != NODE_DEVICE) {
|
|
|
+ hNode2->num_inputs++;
|
|
|
+ if (uStream2 > hNode2->max_input_index)
|
|
|
+ hNode2->max_input_index = uStream2;
|
|
|
+
|
|
|
+ }
|
|
|
+ fill_stream_connect(hNode1, hNode2, uStream1, uStream2);
|
|
|
+ }
|
|
|
+ /* end of sync_enter_cs */
|
|
|
+ /* Exit critical section */
|
|
|
+ mutex_unlock(&hnode_mgr->node_mgr_lock);
|
|
|
+func_end:
|
|
|
+ dev_dbg(bridge, "%s: hNode1: %p uStream1: %d hNode2: %p uStream2: %d"
|
|
|
+ "pattrs: %p status: 0x%x\n", __func__, hNode1,
|
|
|
+ uStream1, hNode2, uStream2, 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_BRIDGE_DVFS) && !defined(CONFIG_CPU_FREQ)
|
|
|
+ struct dspbridge_platform_data *pdata =
|
|
|
+ omap_dspbridge_dev->dev.platform_data;
|
|
|
+#endif
|
|
|
+
|
|
|
+ DBC_REQUIRE(refs > 0);
|
|
|
+ if (!pnode) {
|
|
|
+ status = -EFAULT;
|
|
|
+ goto func_end;
|
|
|
+ }
|
|
|
+ hprocessor = hnode->hprocessor;
|
|
|
+ status = proc_get_state(hprocessor, &proc_state,
|
|
|
+ sizeof(struct dsp_processorstate));
|
|
|
+ if (DSP_FAILED(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->hnode_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 (DSP_SUCCEEDED(status))
|
|
|
+ status = proc_get_processor_id(pnode->hprocessor, &proc_id);
|
|
|
+
|
|
|
+ if (DSP_FAILED(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 (DSP_SUCCEEDED(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_BRIDGE_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.pfn_load(hnode->nldr_node_obj,
|
|
|
+ NLDR_CREATE);
|
|
|
+ /* Get address of node's create function */
|
|
|
+ if (DSP_SUCCEEDED(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_BRIDGE_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 (DSP_SUCCEEDED(status)) {
|
|
|
+ if (node_type == NODE_DAISSOCKET) {
|
|
|
+ status = hnode_mgr->nldr_fxns.pfn_get_fxn_addr
|
|
|
+ (hnode->nldr_node_obj,
|
|
|
+ hnode->dcd_props.obj_data.node_obj.
|
|
|
+ pstr_i_alg_name,
|
|
|
+ &hnode->create_args.asa.
|
|
|
+ task_arg_obj.ul_dais_arg);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ if (node_type != NODE_DEVICE) {
|
|
|
+ status = disp_node_create(hnode_mgr->disp_obj, hnode,
|
|
|
+ hnode_mgr->ul_fxn_addrs
|
|
|
+ [RMSCREATENODE],
|
|
|
+ ul_create_fxn,
|
|
|
+ &(hnode->create_args),
|
|
|
+ &(hnode->node_env));
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ /* Set the message queue id to the node env
|
|
|
+ * pointer */
|
|
|
+ intf_fxns = hnode_mgr->intf_fxns;
|
|
|
+ (*intf_fxns->pfn_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.pfn_unload(hnode->nldr_node_obj,
|
|
|
+ NLDR_CREATE);
|
|
|
+ hnode->loaded = false;
|
|
|
+ }
|
|
|
+ if (DSP_FAILED(status1))
|
|
|
+ pr_err("%s: Failed to unload create code: 0x%x\n",
|
|
|
+ __func__, status1);
|
|
|
+func_cont2:
|
|
|
+ /* Update node state and node manager state */
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ 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 (DSP_SUCCEEDED(status)) {
|
|
|
+ proc_notify_clients(hnode->hprocessor, 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(OUT struct node_mgr **phNodeMgr,
|
|
|
+ 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;
|
|
|
+ DBC_REQUIRE(refs > 0);
|
|
|
+ DBC_REQUIRE(phNodeMgr != NULL);
|
|
|
+ DBC_REQUIRE(hdev_obj != NULL);
|
|
|
+
|
|
|
+ *phNodeMgr = NULL;
|
|
|
+ /* Allocate Node manager object */
|
|
|
+ node_mgr_obj = kzalloc(sizeof(struct node_mgr), GFP_KERNEL);
|
|
|
+ if (node_mgr_obj) {
|
|
|
+ node_mgr_obj->hdev_obj = hdev_obj;
|
|
|
+ node_mgr_obj->node_list = kzalloc(sizeof(struct lst_list),
|
|
|
+ GFP_KERNEL);
|
|
|
+ node_mgr_obj->pipe_map = gb_create(MAXPIPES);
|
|
|
+ node_mgr_obj->pipe_done_map = gb_create(MAXPIPES);
|
|
|
+ if (node_mgr_obj->node_list == NULL
|
|
|
+ || node_mgr_obj->pipe_map == NULL
|
|
|
+ || node_mgr_obj->pipe_done_map == NULL) {
|
|
|
+ status = -ENOMEM;
|
|
|
+ } else {
|
|
|
+ INIT_LIST_HEAD(&node_mgr_obj->node_list->head);
|
|
|
+ node_mgr_obj->ntfy_obj = kmalloc(
|
|
|
+ sizeof(struct ntfy_object), GFP_KERNEL);
|
|
|
+ if (node_mgr_obj->ntfy_obj)
|
|
|
+ ntfy_init(node_mgr_obj->ntfy_obj);
|
|
|
+ else
|
|
|
+ status = -ENOMEM;
|
|
|
+ }
|
|
|
+ node_mgr_obj->num_created = 0;
|
|
|
+ } else {
|
|
|
+ status = -ENOMEM;
|
|
|
+ }
|
|
|
+ /* get devNodeType */
|
|
|
+ if (DSP_SUCCEEDED(status))
|
|
|
+ status = dev_get_dev_type(hdev_obj, &dev_type);
|
|
|
+
|
|
|
+ /* Create the DCD Manager */
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ status =
|
|
|
+ dcd_create_manager(sz_zl_file, &node_mgr_obj->hdcd_mgr);
|
|
|
+ if (DSP_SUCCEEDED(status))
|
|
|
+ status = get_proc_props(node_mgr_obj, hdev_obj);
|
|
|
+
|
|
|
+ }
|
|
|
+ /* Create NODE Dispatcher */
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ disp_attr_obj.ul_chnl_offset = node_mgr_obj->ul_chnl_offset;
|
|
|
+ disp_attr_obj.ul_chnl_buf_size = node_mgr_obj->ul_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);
|
|
|
+ }
|
|
|
+ /* Create a STRM Manager */
|
|
|
+ if (DSP_SUCCEEDED(status))
|
|
|
+ status = strm_create(&node_mgr_obj->strm_mgr_obj, hdev_obj);
|
|
|
+
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ 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);
|
|
|
+ node_mgr_obj->chnl_map = gb_create(node_mgr_obj->ul_num_chnls);
|
|
|
+ /* dma chnl map. ul_num_chnls is # per transport */
|
|
|
+ node_mgr_obj->dma_chnl_map =
|
|
|
+ gb_create(node_mgr_obj->ul_num_chnls);
|
|
|
+ node_mgr_obj->zc_chnl_map =
|
|
|
+ gb_create(node_mgr_obj->ul_num_chnls);
|
|
|
+ if ((node_mgr_obj->chnl_map == NULL)
|
|
|
+ || (node_mgr_obj->dma_chnl_map == NULL)
|
|
|
+ || (node_mgr_obj->zc_chnl_map == NULL)) {
|
|
|
+ status = -ENOMEM;
|
|
|
+ } else {
|
|
|
+ /* Block out reserved channels */
|
|
|
+ for (i = 0; i < node_mgr_obj->ul_chnl_offset; i++)
|
|
|
+ gb_set(node_mgr_obj->chnl_map, i);
|
|
|
+
|
|
|
+ /* Block out channels reserved for RMS */
|
|
|
+ gb_set(node_mgr_obj->chnl_map,
|
|
|
+ node_mgr_obj->ul_chnl_offset);
|
|
|
+ gb_set(node_mgr_obj->chnl_map,
|
|
|
+ node_mgr_obj->ul_chnl_offset + 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ /* 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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Get loader functions and create loader */
|
|
|
+ if (DSP_SUCCEEDED(status))
|
|
|
+ node_mgr_obj->nldr_fxns = nldr_fxns; /* Dyn loader funcs */
|
|
|
+
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ nldr_attrs_obj.pfn_ovly = ovly;
|
|
|
+ nldr_attrs_obj.pfn_write = mem_write;
|
|
|
+ nldr_attrs_obj.us_dsp_word_size = node_mgr_obj->udsp_word_size;
|
|
|
+ nldr_attrs_obj.us_dsp_mau_size = node_mgr_obj->udsp_mau_size;
|
|
|
+ node_mgr_obj->loader_init = node_mgr_obj->nldr_fxns.pfn_init();
|
|
|
+ status =
|
|
|
+ node_mgr_obj->nldr_fxns.pfn_create(&node_mgr_obj->nldr_obj,
|
|
|
+ hdev_obj,
|
|
|
+ &nldr_attrs_obj);
|
|
|
+ }
|
|
|
+ if (DSP_SUCCEEDED(status))
|
|
|
+ *phNodeMgr = node_mgr_obj;
|
|
|
+ else
|
|
|
+ delete_node_mgr(node_mgr_obj);
|
|
|
+
|
|
|
+ DBC_ENSURE((DSP_FAILED(status) && (*phNodeMgr == NULL)) ||
|
|
|
+ (DSP_SUCCEEDED(status) && *phNodeMgr));
|
|
|
+
|
|
|
+ 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_object *hnode,
|
|
|
+ struct process_context *pr_ctxt)
|
|
|
+{
|
|
|
+ struct node_object *pnode = (struct node_object *)hnode;
|
|
|
+ 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;
|
|
|
+
|
|
|
+ struct dsp_processorstate proc_state;
|
|
|
+ DBC_REQUIRE(refs > 0);
|
|
|
+
|
|
|
+ if (!hnode) {
|
|
|
+ status = -EFAULT;
|
|
|
+ goto func_end;
|
|
|
+ }
|
|
|
+ /* create struct dsp_cbdata struct for PWR call */
|
|
|
+ cb_data.cb_data = PWR_TIMEOUT;
|
|
|
+ hnode_mgr = hnode->hnode_mgr;
|
|
|
+ hprocessor = hnode->hprocessor;
|
|
|
+ disp_obj = hnode_mgr->disp_obj;
|
|
|
+ node_type = node_get_type(hnode);
|
|
|
+ intf_fxns = hnode_mgr->intf_fxns;
|
|
|
+ /* Enter critical section */
|
|
|
+ mutex_lock(&hnode_mgr->node_mgr_lock);
|
|
|
+
|
|
|
+ state = node_get_state(hnode);
|
|
|
+ /* 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 && hnode->node_env == (u32) NULL) &&
|
|
|
+ node_type != NODE_DEVICE) {
|
|
|
+ status = proc_get_processor_id(pnode->hprocessor, &proc_id);
|
|
|
+ if (DSP_FAILED(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 && hnode->loaded &&
|
|
|
+ hnode->phase_split) {
|
|
|
+ /* Ok to unload execute code as long as node
|
|
|
+ * is not * running */
|
|
|
+ status1 =
|
|
|
+ hnode_mgr->nldr_fxns.
|
|
|
+ pfn_unload(hnode->nldr_node_obj,
|
|
|
+ NLDR_EXECUTE);
|
|
|
+ hnode->loaded = false;
|
|
|
+ NODE_SET_STATE(hnode, NODE_DONE);
|
|
|
+ }
|
|
|
+ /* Load delete phase code if not loaded or if haven't
|
|
|
+ * * unloaded EXECUTE phase */
|
|
|
+ if ((!(hnode->loaded) || (state == NODE_RUNNING)) &&
|
|
|
+ hnode->phase_split) {
|
|
|
+ status =
|
|
|
+ hnode_mgr->nldr_fxns.
|
|
|
+ pfn_load(hnode->nldr_node_obj, NLDR_DELETE);
|
|
|
+ if (DSP_SUCCEEDED(status))
|
|
|
+ hnode->loaded = true;
|
|
|
+ else
|
|
|
+ pr_err("%s: fail - load delete code:"
|
|
|
+ " 0x%x\n", __func__, status);
|
|
|
+ }
|
|
|
+ }
|
|
|
+func_cont1:
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ /* Unblock a thread trying to terminate the node */
|
|
|
+ (void)sync_set_event(hnode->sync_done);
|
|
|
+ if (proc_id == DSP_UNIT) {
|
|
|
+ /* ul_delete_fxn = address of node's delete
|
|
|
+ * function */
|
|
|
+ status = get_fxn_address(hnode, &ul_delete_fxn,
|
|
|
+ DELETEPHASE);
|
|
|
+ } else if (proc_id == IVA_UNIT)
|
|
|
+ ul_delete_fxn = (u32) hnode->node_env;
|
|
|
+ if (DSP_SUCCEEDED(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, hnode,
|
|
|
+ hnode_mgr->
|
|
|
+ ul_fxn_addrs
|
|
|
+ [RMSDELETENODE],
|
|
|
+ ul_delete_fxn,
|
|
|
+ hnode->node_env);
|
|
|
+ } else
|
|
|
+ NODE_SET_STATE(hnode, NODE_DONE);
|
|
|
+
|
|
|
+ /* Unload execute, if not unloaded, and delete
|
|
|
+ * function */
|
|
|
+ if (state == NODE_RUNNING &&
|
|
|
+ hnode->phase_split) {
|
|
|
+ status1 =
|
|
|
+ hnode_mgr->nldr_fxns.
|
|
|
+ pfn_unload(hnode->nldr_node_obj,
|
|
|
+ NLDR_EXECUTE);
|
|
|
+ }
|
|
|
+ if (DSP_FAILED(status1))
|
|
|
+ pr_err("%s: fail - unload execute code:"
|
|
|
+ " 0x%x\n", __func__, status1);
|
|
|
+
|
|
|
+ status1 =
|
|
|
+ hnode_mgr->nldr_fxns.pfn_unload(hnode->
|
|
|
+ nldr_node_obj,
|
|
|
+ NLDR_DELETE);
|
|
|
+ hnode->loaded = false;
|
|
|
+ if (DSP_FAILED(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 */
|
|
|
+ lst_remove_elem(hnode_mgr->node_list, (struct list_head *)hnode);
|
|
|
+ hnode_mgr->num_nodes--;
|
|
|
+ /* Decrement count of nodes created on DSP */
|
|
|
+ if ((state != NODE_ALLOCATED) || ((state == NODE_ALLOCATED) &&
|
|
|
+ (hnode->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! */
|
|
|
+ if (drv_get_node_res_element(hnode, &node_res, pr_ctxt) !=
|
|
|
+ -ENOENT)
|
|
|
+ drv_proc_node_update_status(node_res, false);
|
|
|
+ delete_node(hnode, pr_ctxt);
|
|
|
+
|
|
|
+ drv_remove_node_res_element(node_res, pr_ctxt);
|
|
|
+ /* Exit critical section */
|
|
|
+ mutex_unlock(&hnode_mgr->node_mgr_lock);
|
|
|
+ proc_notify_clients(hprocessor, DSP_NODESTATECHANGE);
|
|
|
+func_end:
|
|
|
+ dev_dbg(bridge, "%s: hnode: %p status 0x%x\n", __func__, hnode, status);
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * ======== node_delete_mgr ========
|
|
|
+ * Purpose:
|
|
|
+ * Delete the NODE Manager.
|
|
|
+ */
|
|
|
+int node_delete_mgr(struct node_mgr *hnode_mgr)
|
|
|
+{
|
|
|
+ int status = 0;
|
|
|
+
|
|
|
+ DBC_REQUIRE(refs > 0);
|
|
|
+
|
|
|
+ if (hnode_mgr)
|
|
|
+ delete_node_mgr(hnode_mgr);
|
|
|
+ else
|
|
|
+ status = -EFAULT;
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * ======== node_enum_nodes ========
|
|
|
+ * Purpose:
|
|
|
+ * Enumerate currently allocated nodes.
|
|
|
+ */
|
|
|
+int node_enum_nodes(struct node_mgr *hnode_mgr, void **node_tab,
|
|
|
+ u32 node_tab_size, OUT u32 *pu_num_nodes,
|
|
|
+ OUT u32 *pu_allocated)
|
|
|
+{
|
|
|
+ struct node_object *hnode;
|
|
|
+ u32 i;
|
|
|
+ int status = 0;
|
|
|
+ DBC_REQUIRE(refs > 0);
|
|
|
+ DBC_REQUIRE(node_tab != NULL || node_tab_size == 0);
|
|
|
+ DBC_REQUIRE(pu_num_nodes != NULL);
|
|
|
+ DBC_REQUIRE(pu_allocated != NULL);
|
|
|
+
|
|
|
+ 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 {
|
|
|
+ hnode = (struct node_object *)lst_first(hnode_mgr->
|
|
|
+ node_list);
|
|
|
+ for (i = 0; i < hnode_mgr->num_nodes; i++) {
|
|
|
+ DBC_ASSERT(hnode);
|
|
|
+ node_tab[i] = hnode;
|
|
|
+ hnode = (struct node_object *)lst_next
|
|
|
+ (hnode_mgr->node_list,
|
|
|
+ (struct list_head *)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_exit ========
|
|
|
+ * Purpose:
|
|
|
+ * Discontinue usage of NODE module.
|
|
|
+ */
|
|
|
+void node_exit(void)
|
|
|
+{
|
|
|
+ DBC_REQUIRE(refs > 0);
|
|
|
+
|
|
|
+ refs--;
|
|
|
+
|
|
|
+ DBC_ENSURE(refs >= 0);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * ======== node_free_msg_buf ========
|
|
|
+ * Purpose:
|
|
|
+ * Frees the message buffer.
|
|
|
+ */
|
|
|
+int node_free_msg_buf(struct node_object *hnode, IN u8 * pbuffer,
|
|
|
+ OPTIONAL struct dsp_bufferattr *pattr)
|
|
|
+{
|
|
|
+ struct node_object *pnode = (struct node_object *)hnode;
|
|
|
+ int status = 0;
|
|
|
+ u32 proc_id;
|
|
|
+ DBC_REQUIRE(refs > 0);
|
|
|
+ DBC_REQUIRE(pbuffer != NULL);
|
|
|
+ DBC_REQUIRE(pnode != NULL);
|
|
|
+ DBC_REQUIRE(pnode->xlator != NULL);
|
|
|
+
|
|
|
+ if (!hnode) {
|
|
|
+ status = -EFAULT;
|
|
|
+ goto func_end;
|
|
|
+ }
|
|
|
+ status = proc_get_processor_id(pnode->hprocessor, &proc_id);
|
|
|
+ if (proc_id == DSP_UNIT) {
|
|
|
+ if (DSP_SUCCEEDED(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 {
|
|
|
+ DBC_ASSERT(NULL); /* BUG */
|
|
|
+ }
|
|
|
+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,
|
|
|
+ OUT struct dsp_nodeattr *pattr, u32 attr_size)
|
|
|
+{
|
|
|
+ struct node_mgr *hnode_mgr;
|
|
|
+ int status = 0;
|
|
|
+ DBC_REQUIRE(refs > 0);
|
|
|
+ DBC_REQUIRE(pattr != NULL);
|
|
|
+ DBC_REQUIRE(attr_size >= sizeof(struct dsp_nodeattr));
|
|
|
+
|
|
|
+ if (!hnode) {
|
|
|
+ status = -EFAULT;
|
|
|
+ } else {
|
|
|
+ hnode_mgr = hnode->hnode_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.utimeout = hnode->utimeout;
|
|
|
+ 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.ugpp_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 status;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * ======== 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,
|
|
|
+ OUT u32 *pulId)
|
|
|
+{
|
|
|
+ enum node_type node_type;
|
|
|
+ int status = -EINVAL;
|
|
|
+ DBC_REQUIRE(refs > 0);
|
|
|
+ DBC_REQUIRE(dir == DSP_TONODE || dir == DSP_FROMNODE);
|
|
|
+ DBC_REQUIRE(pulId != NULL);
|
|
|
+
|
|
|
+ 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) {
|
|
|
+ *pulId = hnode->inputs[index].dev_id;
|
|
|
+ status = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ DBC_ASSERT(dir == DSP_FROMNODE);
|
|
|
+ if (index < MAX_OUTPUTS(hnode)) {
|
|
|
+ if (hnode->outputs[index].type == HOSTCONNECT) {
|
|
|
+ *pulId = 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,
|
|
|
+ OUT struct dsp_msg *pmsg, 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;
|
|
|
+
|
|
|
+ DBC_REQUIRE(refs > 0);
|
|
|
+ DBC_REQUIRE(pmsg != NULL);
|
|
|
+
|
|
|
+ if (!hnode) {
|
|
|
+ status = -EFAULT;
|
|
|
+ goto func_end;
|
|
|
+ }
|
|
|
+ hprocessor = hnode->hprocessor;
|
|
|
+ status = proc_get_state(hprocessor, &proc_state,
|
|
|
+ sizeof(struct dsp_processorstate));
|
|
|
+ if (DSP_FAILED(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->hnode_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->pfn_msg_get) (hnode->msg_queue_obj, pmsg, utimeout);
|
|
|
+ /* Check if message contains SM descriptor */
|
|
|
+ if (DSP_FAILED(status) || !(pmsg->dw_cmd & DSP_RMSBUFDESC))
|
|
|
+ goto func_end;
|
|
|
+
|
|
|
+ /* Translate DSP byte addr to GPP Va. */
|
|
|
+ tmp_buf = cmm_xlator_translate(hnode->xlator,
|
|
|
+ (void *)(pmsg->dw_arg1 *
|
|
|
+ hnode->hnode_mgr->
|
|
|
+ udsp_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 */
|
|
|
+ pmsg->dw_arg1 = (u32) tmp_buf;
|
|
|
+ pmsg->dw_arg2 *= hnode->hnode_mgr->udsp_word_size;
|
|
|
+ } else {
|
|
|
+ status = -ESRCH;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ status = -ESRCH;
|
|
|
+ }
|
|
|
+func_end:
|
|
|
+ dev_dbg(bridge, "%s: hnode: %p pmsg: %p utimeout: 0x%x\n", __func__,
|
|
|
+ hnode, pmsg, utimeout);
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * ======== node_get_nldr_obj ========
|
|
|
+ */
|
|
|
+int node_get_nldr_obj(struct node_mgr *hnode_mgr,
|
|
|
+ struct nldr_object **phNldrObj)
|
|
|
+{
|
|
|
+ int status = 0;
|
|
|
+ struct node_mgr *node_mgr_obj = hnode_mgr;
|
|
|
+ DBC_REQUIRE(phNldrObj != NULL);
|
|
|
+
|
|
|
+ if (!hnode_mgr)
|
|
|
+ status = -EFAULT;
|
|
|
+ else
|
|
|
+ *phNldrObj = node_mgr_obj->nldr_obj;
|
|
|
+
|
|
|
+ DBC_ENSURE(DSP_SUCCEEDED(status) || ((phNldrObj != NULL) &&
|
|
|
+ (*phNldrObj == NULL)));
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * ======== node_get_strm_mgr ========
|
|
|
+ * Purpose:
|
|
|
+ * Returns the Stream manager.
|
|
|
+ */
|
|
|
+int node_get_strm_mgr(struct node_object *hnode,
|
|
|
+ struct strm_mgr **phStrmMgr)
|
|
|
+{
|
|
|
+ int status = 0;
|
|
|
+
|
|
|
+ DBC_REQUIRE(refs > 0);
|
|
|
+
|
|
|
+ if (!hnode)
|
|
|
+ status = -EFAULT;
|
|
|
+ else
|
|
|
+ *phStrmMgr = hnode->hnode_mgr->strm_mgr_obj;
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * ======== node_get_load_type ========
|
|
|
+ */
|
|
|
+enum nldr_loadtype node_get_load_type(struct node_object *hnode)
|
|
|
+{
|
|
|
+ DBC_REQUIRE(refs > 0);
|
|
|
+ DBC_REQUIRE(hnode);
|
|
|
+ if (!hnode) {
|
|
|
+ dev_dbg(bridge, "%s: Failed. hnode: %p\n", __func__, hnode);
|
|
|
+ return -1;
|
|
|
+ } else {
|
|
|
+ return hnode->dcd_props.obj_data.node_obj.us_load_type;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * ======== node_get_timeout ========
|
|
|
+ * Purpose:
|
|
|
+ * Returns the timeout value for this node.
|
|
|
+ */
|
|
|
+u32 node_get_timeout(struct node_object *hnode)
|
|
|
+{
|
|
|
+ DBC_REQUIRE(refs > 0);
|
|
|
+ DBC_REQUIRE(hnode);
|
|
|
+ if (!hnode) {
|
|
|
+ dev_dbg(bridge, "%s: failed. hnode: %p\n", __func__, hnode);
|
|
|
+ return 0;
|
|
|
+ } else {
|
|
|
+ return hnode->utimeout;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * ======== 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_init ========
|
|
|
+ * Purpose:
|
|
|
+ * Initialize the NODE module.
|
|
|
+ */
|
|
|
+bool node_init(void)
|
|
|
+{
|
|
|
+ DBC_REQUIRE(refs >= 0);
|
|
|
+
|
|
|
+ refs++;
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * ======== node_on_exit ========
|
|
|
+ * Purpose:
|
|
|
+ * Gets called when RMS_EXIT is received for a node.
|
|
|
+ */
|
|
|
+void node_on_exit(struct node_object *hnode, s32 nStatus)
|
|
|
+{
|
|
|
+ if (!hnode)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* Set node state to done */
|
|
|
+ NODE_SET_STATE(hnode, NODE_DONE);
|
|
|
+ hnode->exit_status = nStatus;
|
|
|
+ if (hnode->loaded && hnode->phase_split) {
|
|
|
+ (void)hnode->hnode_mgr->nldr_fxns.pfn_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->hprocessor, 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;
|
|
|
+
|
|
|
+ DBC_REQUIRE(refs > 0);
|
|
|
+
|
|
|
+ if (!hnode) {
|
|
|
+ status = -EFAULT;
|
|
|
+ } else {
|
|
|
+ node_type = node_get_type(hnode);
|
|
|
+ if (node_type != NODE_TASK && node_type != NODE_DAISSOCKET)
|
|
|
+ status = -EPERM;
|
|
|
+ }
|
|
|
+ if (DSP_FAILED(status))
|
|
|
+ goto func_end;
|
|
|
+
|
|
|
+ status = proc_get_processor_id(pnode->hprocessor, &proc_id);
|
|
|
+
|
|
|
+ if (proc_id == IVA_UNIT)
|
|
|
+ status = -ENOSYS;
|
|
|
+
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ hnode_mgr = hnode->hnode_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 (DSP_FAILED(status))
|
|
|
+ goto func_cont;
|
|
|
+ hprocessor = hnode->hprocessor;
|
|
|
+ status = proc_get_state(hprocessor, &proc_state,
|
|
|
+ sizeof(struct dsp_processorstate));
|
|
|
+ if (DSP_FAILED(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->ul_fxn_addrs[RMSCHANGENODEPRIORITY],
|
|
|
+ hnode->node_env, NODE_SUSPENDEDPRI);
|
|
|
+
|
|
|
+ /* Update state */
|
|
|
+ if (DSP_SUCCEEDED(status))
|
|
|
+ NODE_SET_STATE(hnode, NODE_PAUSED);
|
|
|
+
|
|
|
+func_cont:
|
|
|
+ /* End of sync_enter_cs */
|
|
|
+ /* Leave critical section */
|
|
|
+ mutex_unlock(&hnode_mgr->node_mgr_lock);
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ proc_notify_clients(hnode->hprocessor,
|
|
|
+ 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,
|
|
|
+ IN 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;
|
|
|
+
|
|
|
+ DBC_REQUIRE(refs > 0);
|
|
|
+ DBC_REQUIRE(pmsg != NULL);
|
|
|
+
|
|
|
+ if (!hnode) {
|
|
|
+ status = -EFAULT;
|
|
|
+ goto func_end;
|
|
|
+ }
|
|
|
+ hprocessor = hnode->hprocessor;
|
|
|
+ status = proc_get_state(hprocessor, &proc_state,
|
|
|
+ sizeof(struct dsp_processorstate));
|
|
|
+ if (DSP_FAILED(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->hnode_mgr;
|
|
|
+ node_type = node_get_type(hnode);
|
|
|
+ if (node_type != NODE_MESSAGE && node_type != NODE_TASK &&
|
|
|
+ node_type != NODE_DAISSOCKET)
|
|
|
+ status = -EPERM;
|
|
|
+
|
|
|
+ if (DSP_SUCCEEDED(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 (DSP_FAILED(status))
|
|
|
+ goto func_end;
|
|
|
+
|
|
|
+ /* assign pmsg values to new msg */
|
|
|
+ new_msg = *pmsg;
|
|
|
+ /* Now, check if message contains a SM buffer descriptor */
|
|
|
+ if (pmsg->dw_cmd & DSP_RMSBUFDESC) {
|
|
|
+ /* Translate GPP Va to DSP physical buf Ptr. */
|
|
|
+ tmp_buf = cmm_xlator_translate(hnode->xlator,
|
|
|
+ (void *)new_msg.dw_arg1,
|
|
|
+ CMM_VA2DSPPA);
|
|
|
+ if (tmp_buf != NULL) {
|
|
|
+ /* got translation, convert to MAUs in msg */
|
|
|
+ if (hnode->hnode_mgr->udsp_word_size != 0) {
|
|
|
+ new_msg.dw_arg1 =
|
|
|
+ (u32) tmp_buf /
|
|
|
+ hnode->hnode_mgr->udsp_word_size;
|
|
|
+ /* MAUs */
|
|
|
+ new_msg.dw_arg2 /= hnode->hnode_mgr->
|
|
|
+ udsp_word_size;
|
|
|
+ } else {
|
|
|
+ pr_err("%s: udsp_word_size is zero!\n",
|
|
|
+ __func__);
|
|
|
+ status = -EPERM; /* bad DSPWordSize */
|
|
|
+ }
|
|
|
+ } else { /* failed to translate buffer address */
|
|
|
+ status = -ESRCH;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ intf_fxns = hnode_mgr->intf_fxns;
|
|
|
+ status = (*intf_fxns->pfn_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;
|
|
|
+
|
|
|
+ DBC_REQUIRE(refs > 0);
|
|
|
+ DBC_REQUIRE(hnotification != NULL);
|
|
|
+
|
|
|
+ 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 (DSP_SUCCEEDED(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->hnode_mgr->intf_fxns;
|
|
|
+ status = (*intf_fxns->pfn_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;
|
|
|
+
|
|
|
+ DBC_REQUIRE(refs > 0);
|
|
|
+
|
|
|
+ if (!hnode) {
|
|
|
+ status = -EFAULT;
|
|
|
+ goto func_end;
|
|
|
+ }
|
|
|
+ hprocessor = hnode->hprocessor;
|
|
|
+ status = proc_get_state(hprocessor, &proc_state,
|
|
|
+ sizeof(struct dsp_processorstate));
|
|
|
+ if (DSP_FAILED(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 (DSP_FAILED(status))
|
|
|
+ goto func_end;
|
|
|
+
|
|
|
+ hnode_mgr = hnode->hnode_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 (DSP_SUCCEEDED(status))
|
|
|
+ status = proc_get_processor_id(pnode->hprocessor, &proc_id);
|
|
|
+
|
|
|
+ if (DSP_FAILED(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.pfn_load(hnode->nldr_node_obj,
|
|
|
+ NLDR_EXECUTE);
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ hnode->loaded = true;
|
|
|
+ } else {
|
|
|
+ pr_err("%s: fail - load execute code: 0x%x\n",
|
|
|
+ __func__, status);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (DSP_SUCCEEDED(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 (DSP_SUCCEEDED(status)) {
|
|
|
+ ul_fxn_addr = hnode_mgr->ul_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->ul_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 */
|
|
|
+ DBC_ASSERT(false);
|
|
|
+ }
|
|
|
+func_cont1:
|
|
|
+ /* Update node state. */
|
|
|
+ if (DSP_SUCCEEDED(status))
|
|
|
+ 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 (DSP_SUCCEEDED(status)) {
|
|
|
+ proc_notify_clients(hnode->hprocessor, 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, OUT 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;
|
|
|
+
|
|
|
+ DBC_REQUIRE(refs > 0);
|
|
|
+ DBC_REQUIRE(pstatus != NULL);
|
|
|
+
|
|
|
+ if (!hnode || !hnode->hnode_mgr) {
|
|
|
+ status = -EFAULT;
|
|
|
+ goto func_end;
|
|
|
+ }
|
|
|
+ if (pnode->hprocessor == NULL) {
|
|
|
+ status = -EFAULT;
|
|
|
+ goto func_end;
|
|
|
+ }
|
|
|
+ status = proc_get_processor_id(pnode->hprocessor, &proc_id);
|
|
|
+
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ hnode_mgr = hnode->hnode_mgr;
|
|
|
+ node_type = node_get_type(hnode);
|
|
|
+ if (node_type != NODE_TASK && node_type != NODE_DAISSOCKET)
|
|
|
+ status = -EPERM;
|
|
|
+ }
|
|
|
+ if (DSP_SUCCEEDED(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 (DSP_SUCCEEDED(status)) {
|
|
|
+ /*
|
|
|
+ * Send exit message. Do not change state to NODE_DONE
|
|
|
+ * here. That will be done in callback.
|
|
|
+ */
|
|
|
+ status = proc_get_state(pnode->hprocessor, &proc_state,
|
|
|
+ sizeof(struct dsp_processorstate));
|
|
|
+ if (DSP_FAILED(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.dw_cmd = RMS_EXIT;
|
|
|
+ msg.dw_arg1 = hnode->node_env;
|
|
|
+ killmsg.dw_cmd = RMS_KILLTASK;
|
|
|
+ killmsg.dw_arg1 = hnode->node_env;
|
|
|
+ intf_fxns = hnode_mgr->intf_fxns;
|
|
|
+
|
|
|
+ if (hnode->utimeout > MAXTIMEOUT)
|
|
|
+ kill_time_out = MAXTIMEOUT;
|
|
|
+ else
|
|
|
+ kill_time_out = (hnode->utimeout) * 2;
|
|
|
+
|
|
|
+ status = (*intf_fxns->pfn_msg_put) (hnode->msg_queue_obj, &msg,
|
|
|
+ hnode->utimeout);
|
|
|
+ if (DSP_FAILED(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->pfn_msg_put)(hnode->msg_queue_obj,
|
|
|
+ &killmsg, hnode->utimeout);
|
|
|
+ if (DSP_FAILED(status))
|
|
|
+ goto func_cont;
|
|
|
+ status = sync_wait_on_event(hnode->sync_done,
|
|
|
+ kill_time_out / 2);
|
|
|
+ if (DSP_FAILED(status)) {
|
|
|
+ /*
|
|
|
+ * Here it goes the part of the simulation of
|
|
|
+ * the DSP exception.
|
|
|
+ */
|
|
|
+ dev_get_deh_mgr(hnode_mgr->hdev_obj, &hdeh_mgr);
|
|
|
+ if (!hdeh_mgr)
|
|
|
+ goto func_cont;
|
|
|
+
|
|
|
+ (*intf_fxns->pfn_deh_notify)(hdeh_mgr, DSP_SYSERROR,
|
|
|
+ DSP_EXCEPTIONABORT);
|
|
|
+ }
|
|
|
+ }
|
|
|
+func_cont:
|
|
|
+ if (DSP_SUCCEEDED(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 cmm_xlatorobject *xlator;
|
|
|
+ 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->hprocessor;
|
|
|
+#endif
|
|
|
+ int status;
|
|
|
+ if (!hnode)
|
|
|
+ goto func_end;
|
|
|
+ hnode_mgr = hnode->hnode_mgr;
|
|
|
+ if (!hnode_mgr)
|
|
|
+ goto func_end;
|
|
|
+ xlator = hnode->xlator;
|
|
|
+ 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->pfn_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.udsp_heap_res_addr) {
|
|
|
+ status = proc_un_map(hnode->hprocessor, (void *)
|
|
|
+ task_arg_obj.udsp_heap_addr,
|
|
|
+ pr_ctxt);
|
|
|
+
|
|
|
+ status = proc_un_reserve_memory(hnode->hprocessor,
|
|
|
+ (void *)
|
|
|
+ task_arg_obj.
|
|
|
+ udsp_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->pstr_dev_name);
|
|
|
+ hnode->pstr_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.pstr_create_phase_fxn);
|
|
|
+ hnode->dcd_props.obj_data.node_obj.pstr_create_phase_fxn = NULL;
|
|
|
+
|
|
|
+ kfree(hnode->dcd_props.obj_data.node_obj.pstr_execute_phase_fxn);
|
|
|
+ hnode->dcd_props.obj_data.node_obj.pstr_execute_phase_fxn = NULL;
|
|
|
+
|
|
|
+ kfree(hnode->dcd_props.obj_data.node_obj.pstr_delete_phase_fxn);
|
|
|
+ hnode->dcd_props.obj_data.node_obj.pstr_delete_phase_fxn = NULL;
|
|
|
+
|
|
|
+ kfree(hnode->dcd_props.obj_data.node_obj.pstr_i_alg_name);
|
|
|
+ hnode->dcd_props.obj_data.node_obj.pstr_i_alg_name = NULL;
|
|
|
+
|
|
|
+ /* Free all SM address translator resources */
|
|
|
+ if (xlator) {
|
|
|
+ (void)cmm_xlator_delete(xlator, TRUE); /* force free */
|
|
|
+ xlator = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ kfree(hnode->nldr_node_obj);
|
|
|
+ hnode->nldr_node_obj = NULL;
|
|
|
+ hnode->hnode_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;
|
|
|
+
|
|
|
+ if (hnode_mgr) {
|
|
|
+ /* Free resources */
|
|
|
+ if (hnode_mgr->hdcd_mgr)
|
|
|
+ dcd_destroy_manager(hnode_mgr->hdcd_mgr);
|
|
|
+
|
|
|
+ /* Remove any elements remaining in lists */
|
|
|
+ if (hnode_mgr->node_list) {
|
|
|
+ while ((hnode = (struct node_object *)
|
|
|
+ lst_get_head(hnode_mgr->node_list)))
|
|
|
+ delete_node(hnode, NULL);
|
|
|
+
|
|
|
+ DBC_ASSERT(LST_IS_EMPTY(hnode_mgr->node_list));
|
|
|
+ kfree(hnode_mgr->node_list);
|
|
|
+ }
|
|
|
+ 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->pipe_map)
|
|
|
+ gb_delete(hnode_mgr->pipe_map);
|
|
|
+
|
|
|
+ if (hnode_mgr->pipe_done_map)
|
|
|
+ gb_delete(hnode_mgr->pipe_done_map);
|
|
|
+
|
|
|
+ if (hnode_mgr->chnl_map)
|
|
|
+ gb_delete(hnode_mgr->chnl_map);
|
|
|
+
|
|
|
+ if (hnode_mgr->dma_chnl_map)
|
|
|
+ gb_delete(hnode_mgr->dma_chnl_map);
|
|
|
+
|
|
|
+ if (hnode_mgr->zc_chnl_map)
|
|
|
+ gb_delete(hnode_mgr->zc_chnl_map);
|
|
|
+
|
|
|
+ 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.pfn_delete(hnode_mgr->nldr_obj);
|
|
|
+
|
|
|
+ if (hnode_mgr->loader_init)
|
|
|
+ hnode_mgr->nldr_fxns.pfn_exit();
|
|
|
+
|
|
|
+ kfree(hnode_mgr);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * ======== fill_stream_connect ========
|
|
|
+ * Purpose:
|
|
|
+ * Fills stream information.
|
|
|
+ */
|
|
|
+static void fill_stream_connect(struct node_object *hNode1,
|
|
|
+ struct node_object *hNode2,
|
|
|
+ u32 uStream1, u32 uStream2)
|
|
|
+{
|
|
|
+ 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(hNode1);
|
|
|
+ node2_type = node_get_type(hNode2);
|
|
|
+ if (hNode1 != (struct node_object *)DSP_HGPPNODE) {
|
|
|
+
|
|
|
+ if (node1_type != NODE_DEVICE) {
|
|
|
+ strm_index = hNode1->num_inputs +
|
|
|
+ hNode1->num_outputs - 1;
|
|
|
+ strm1 = &(hNode1->stream_connect[strm_index]);
|
|
|
+ strm1->cb_struct = sizeof(struct dsp_streamconnect);
|
|
|
+ strm1->this_node_stream_index = uStream1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (hNode2 != (struct node_object *)DSP_HGPPNODE) {
|
|
|
+ /* NODE == > NODE */
|
|
|
+ if (node1_type != NODE_DEVICE) {
|
|
|
+ strm1->connected_node = hNode2;
|
|
|
+ strm1->ui_connected_node_id = hNode2->node_uuid;
|
|
|
+ strm1->connected_node_stream_index = uStream2;
|
|
|
+ strm1->connect_type = CONNECTTYPE_NODEOUTPUT;
|
|
|
+ }
|
|
|
+ if (node2_type != NODE_DEVICE) {
|
|
|
+ strm_index = hNode2->num_inputs +
|
|
|
+ hNode2->num_outputs - 1;
|
|
|
+ strm2 = &(hNode2->stream_connect[strm_index]);
|
|
|
+ strm2->cb_struct =
|
|
|
+ sizeof(struct dsp_streamconnect);
|
|
|
+ strm2->this_node_stream_index = uStream2;
|
|
|
+ strm2->connected_node = hNode1;
|
|
|
+ strm2->ui_connected_node_id = hNode1->node_uuid;
|
|
|
+ strm2->connected_node_stream_index = uStream1;
|
|
|
+ strm2->connect_type = CONNECTTYPE_NODEINPUT;
|
|
|
+ }
|
|
|
+ } else if (node1_type != NODE_DEVICE)
|
|
|
+ strm1->connect_type = CONNECTTYPE_GPPOUTPUT;
|
|
|
+ } else {
|
|
|
+ /* GPP == > NODE */
|
|
|
+ DBC_ASSERT(hNode2 != (struct node_object *)DSP_HGPPNODE);
|
|
|
+ strm_index = hNode2->num_inputs + hNode2->num_outputs - 1;
|
|
|
+ strm2 = &(hNode2->stream_connect[strm_index]);
|
|
|
+ strm2->cb_struct = sizeof(struct dsp_streamconnect);
|
|
|
+ strm2->this_node_stream_index = uStream2;
|
|
|
+ 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->hnode_mgr;
|
|
|
+
|
|
|
+ if (pattrs != NULL) {
|
|
|
+ pstrm_def->num_bufs = pattrs->num_bufs;
|
|
|
+ pstrm_def->buf_size =
|
|
|
+ pattrs->buf_size / hnode_mgr->udsp_data_mau_size;
|
|
|
+ pstrm_def->seg_id = pattrs->seg_id;
|
|
|
+ pstrm_def->buf_alignment = pattrs->buf_alignment;
|
|
|
+ pstrm_def->utimeout = pattrs->utimeout;
|
|
|
+ } else {
|
|
|
+ pstrm_def->num_bufs = DEFAULTNBUFS;
|
|
|
+ pstrm_def->buf_size =
|
|
|
+ DEFAULTBUFSIZE / hnode_mgr->udsp_data_mau_size;
|
|
|
+ pstrm_def->seg_id = DEFAULTSEGID;
|
|
|
+ pstrm_def->buf_alignment = DEFAULTALIGNMENT;
|
|
|
+ pstrm_def->utimeout = 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 (gb_test(hnode_mgr->pipe_done_map, stream.dev_id)) {
|
|
|
+ /* The other node has already been deleted */
|
|
|
+ gb_clear(hnode_mgr->pipe_done_map, stream.dev_id);
|
|
|
+ gb_clear(hnode_mgr->pipe_map, stream.dev_id);
|
|
|
+ } else {
|
|
|
+ /* The other node has not been deleted yet */
|
|
|
+ gb_set(hnode_mgr->pipe_done_map, stream.dev_id);
|
|
|
+ }
|
|
|
+ } else if (stream.type == HOSTCONNECT) {
|
|
|
+ if (stream.dev_id < hnode_mgr->ul_num_chnls) {
|
|
|
+ gb_clear(hnode_mgr->chnl_map, stream.dev_id);
|
|
|
+ } else if (stream.dev_id < (2 * hnode_mgr->ul_num_chnls)) {
|
|
|
+ /* dsp-dma */
|
|
|
+ gb_clear(hnode_mgr->dma_chnl_map, stream.dev_id -
|
|
|
+ (1 * hnode_mgr->ul_num_chnls));
|
|
|
+ } else if (stream.dev_id < (3 * hnode_mgr->ul_num_chnls)) {
|
|
|
+ /* zero-copy */
|
|
|
+ gb_clear(hnode_mgr->zc_chnl_map, stream.dev_id -
|
|
|
+ (2 * hnode_mgr->ul_num_chnls));
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * ======== 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 * pulFxnAddr,
|
|
|
+ u32 uPhase)
|
|
|
+{
|
|
|
+ char *pstr_fxn_name = NULL;
|
|
|
+ struct node_mgr *hnode_mgr = hnode->hnode_mgr;
|
|
|
+ int status = 0;
|
|
|
+ DBC_REQUIRE(node_get_type(hnode) == NODE_TASK ||
|
|
|
+ node_get_type(hnode) == NODE_DAISSOCKET ||
|
|
|
+ node_get_type(hnode) == NODE_MESSAGE);
|
|
|
+
|
|
|
+ switch (uPhase) {
|
|
|
+ case CREATEPHASE:
|
|
|
+ pstr_fxn_name =
|
|
|
+ hnode->dcd_props.obj_data.node_obj.pstr_create_phase_fxn;
|
|
|
+ break;
|
|
|
+ case EXECUTEPHASE:
|
|
|
+ pstr_fxn_name =
|
|
|
+ hnode->dcd_props.obj_data.node_obj.pstr_execute_phase_fxn;
|
|
|
+ break;
|
|
|
+ case DELETEPHASE:
|
|
|
+ pstr_fxn_name =
|
|
|
+ hnode->dcd_props.obj_data.node_obj.pstr_delete_phase_fxn;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ /* Should never get here */
|
|
|
+ DBC_ASSERT(false);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ status =
|
|
|
+ hnode_mgr->nldr_fxns.pfn_get_fxn_addr(hnode->nldr_node_obj,
|
|
|
+ pstr_fxn_name, pulFxnAddr);
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * ======== get_node_info ========
|
|
|
+ * Purpose:
|
|
|
+ * Retrieves the node information.
|
|
|
+ */
|
|
|
+void get_node_info(struct node_object *hnode, struct dsp_nodeinfo *pNodeInfo)
|
|
|
+{
|
|
|
+ u32 i;
|
|
|
+
|
|
|
+ DBC_REQUIRE(hnode);
|
|
|
+ DBC_REQUIRE(pNodeInfo != NULL);
|
|
|
+
|
|
|
+ pNodeInfo->cb_struct = sizeof(struct dsp_nodeinfo);
|
|
|
+ pNodeInfo->nb_node_database_props =
|
|
|
+ hnode->dcd_props.obj_data.node_obj.ndb_props;
|
|
|
+ pNodeInfo->execution_priority = hnode->prio;
|
|
|
+ pNodeInfo->device_owner = hnode->device_owner;
|
|
|
+ pNodeInfo->number_streams = hnode->num_inputs + hnode->num_outputs;
|
|
|
+ pNodeInfo->node_env = hnode->node_env;
|
|
|
+
|
|
|
+ pNodeInfo->ns_execution_state = node_get_state(hnode);
|
|
|
+
|
|
|
+ /* Copy stream connect data */
|
|
|
+ for (i = 0; i < hnode->num_inputs + hnode->num_outputs; i++)
|
|
|
+ pNodeInfo->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 *pNodeId,
|
|
|
+ struct dcd_genericobj *pdcdProps)
|
|
|
+{
|
|
|
+ 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 =
|
|
|
+ &(pdcdProps->obj_data.node_obj.ndb_props);
|
|
|
+ int status = 0;
|
|
|
+ char sz_uuid[MAXUUIDLEN];
|
|
|
+
|
|
|
+ status = dcd_get_object_def(hdcd_mgr, (struct dsp_uuid *)pNodeId,
|
|
|
+ DSP_DCDNODETYPE, pdcdProps);
|
|
|
+
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ hnode->ntype = node_type = pndb_props->ntype;
|
|
|
+
|
|
|
+ /* Create UUID value to set in registry. */
|
|
|
+ uuid_uuid_to_string((struct dsp_uuid *)pNodeId, sz_uuid,
|
|
|
+ MAXUUIDLEN);
|
|
|
+ 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 =
|
|
|
+ pdcdProps->obj_data.node_obj.msg_segid;
|
|
|
+ pmsg_args->notify_type =
|
|
|
+ pdcdProps->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 */
|
|
|
+ DBC_REQUIRE(pndb_props->ac_name);
|
|
|
+ len = strlen(pndb_props->ac_name);
|
|
|
+ DBC_ASSERT(len < MAXDEVNAMELEN);
|
|
|
+ hnode->pstr_dev_name = kzalloc(len + 1, GFP_KERNEL);
|
|
|
+ if (hnode->pstr_dev_name == NULL) {
|
|
|
+ status = -ENOMEM;
|
|
|
+ } else {
|
|
|
+ strncpy(hnode->pstr_dev_name,
|
|
|
+ pndb_props->ac_name, len);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (DSP_SUCCEEDED(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 (DSP_SUCCEEDED(status)) {
|
|
|
+ host_res = pbridge_context->resources;
|
|
|
+ if (!host_res)
|
|
|
+ return -EPERM;
|
|
|
+ hnode_mgr->ul_chnl_offset = host_res->dw_chnl_offset;
|
|
|
+ hnode_mgr->ul_chnl_buf_size = host_res->dw_chnl_buf_size;
|
|
|
+ hnode_mgr->ul_num_chnls = host_res->dw_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->udsp_word_size = DSPWORDSIZE;
|
|
|
+ hnode_mgr->udsp_data_mau_size = DSPWORDSIZE;
|
|
|
+ hnode_mgr->udsp_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,
|
|
|
+ IN CONST struct dsp_uuid *pNodeId,
|
|
|
+ OUT 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;
|
|
|
+
|
|
|
+ DBC_REQUIRE(refs > 0);
|
|
|
+ DBC_REQUIRE(hprocessor != NULL);
|
|
|
+ DBC_REQUIRE(pNodeId != NULL);
|
|
|
+
|
|
|
+ if (hprocessor == NULL || pNodeId == NULL) {
|
|
|
+ status = -EFAULT;
|
|
|
+ goto func_end;
|
|
|
+ }
|
|
|
+ status = proc_get_state(hprocessor, &proc_state,
|
|
|
+ sizeof(struct dsp_processorstate));
|
|
|
+ if (DSP_FAILED(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.pstr_create_phase_fxn = NULL;
|
|
|
+ dcd_node_props.pstr_execute_phase_fxn = NULL;
|
|
|
+ dcd_node_props.pstr_delete_phase_fxn = NULL;
|
|
|
+ dcd_node_props.pstr_i_alg_name = NULL;
|
|
|
+
|
|
|
+ status = dcd_get_object_def(hnode_mgr->hdcd_mgr,
|
|
|
+ (struct dsp_uuid *)pNodeId, DSP_DCDNODETYPE,
|
|
|
+ (struct dcd_genericobj *)&dcd_node_props);
|
|
|
+
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ *node_props = dcd_node_props.ndb_props;
|
|
|
+ kfree(dcd_node_props.pstr_create_phase_fxn);
|
|
|
+
|
|
|
+ kfree(dcd_node_props.pstr_execute_phase_fxn);
|
|
|
+
|
|
|
+ kfree(dcd_node_props.pstr_delete_phase_fxn);
|
|
|
+
|
|
|
+ kfree(dcd_node_props.pstr_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->hdev_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->ul_fxn_addrs[i]));
|
|
|
+ if (DSP_FAILED(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 ulDspRunAddr, u32 ulDspLoadAddr,
|
|
|
+ u32 ul_num_bytes, u32 nMemSpace)
|
|
|
+{
|
|
|
+ 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;
|
|
|
+
|
|
|
+ DBC_REQUIRE(hnode);
|
|
|
+
|
|
|
+ hnode_mgr = hnode->hnode_mgr;
|
|
|
+
|
|
|
+ ul_size = ul_num_bytes / hnode_mgr->udsp_word_size;
|
|
|
+ ul_timeout = hnode->utimeout;
|
|
|
+
|
|
|
+ /* Call new MemCopy function */
|
|
|
+ intf_fxns = hnode_mgr->intf_fxns;
|
|
|
+ status = dev_get_bridge_context(hnode_mgr->hdev_obj, &hbridge_context);
|
|
|
+ if (DSP_SUCCEEDED(status)) {
|
|
|
+ status =
|
|
|
+ (*intf_fxns->pfn_brd_mem_copy) (hbridge_context,
|
|
|
+ ulDspRunAddr, ulDspLoadAddr,
|
|
|
+ ul_num_bytes, (u32) nMemSpace);
|
|
|
+ if (DSP_SUCCEEDED(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 ulDspAddr, void *pbuf,
|
|
|
+ u32 ul_num_bytes, u32 nMemSpace)
|
|
|
+{
|
|
|
+ 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;
|
|
|
+
|
|
|
+ DBC_REQUIRE(hnode);
|
|
|
+ DBC_REQUIRE(nMemSpace & DBLL_CODE || nMemSpace & DBLL_DATA);
|
|
|
+
|
|
|
+ hnode_mgr = hnode->hnode_mgr;
|
|
|
+
|
|
|
+ ul_timeout = hnode->utimeout;
|
|
|
+ mem_sect_type = (nMemSpace & DBLL_CODE) ? RMS_CODE : RMS_DATA;
|
|
|
+
|
|
|
+ /* Call new MemWrite function */
|
|
|
+ intf_fxns = hnode_mgr->intf_fxns;
|
|
|
+ status = dev_get_bridge_context(hnode_mgr->hdev_obj, &hbridge_context);
|
|
|
+ status = (*intf_fxns->pfn_brd_mem_write) (hbridge_context, pbuf,
|
|
|
+ ulDspAddr, ul_num_bytes, mem_sect_type);
|
|
|
+
|
|
|
+ return ul_num_bytes;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * ======== 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;
|
|
|
+ u32 n;
|
|
|
+
|
|
|
+ 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);
|
|
|
+
|
|
|
+ node_obj = (struct node_object *)(node_mgr->node_list->head.next);
|
|
|
+
|
|
|
+ for (n = 0; n < node_mgr->num_nodes; n++) {
|
|
|
+ status = nldr_find_addr(node_obj->nldr_node_obj, sym_addr,
|
|
|
+ offset_range, sym_addr_output, sym_name);
|
|
|
+
|
|
|
+ if (DSP_SUCCEEDED(status))
|
|
|
+ break;
|
|
|
+
|
|
|
+ node_obj = (struct node_object *) (node_obj->list_elem.next);
|
|
|
+ }
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|