diff options
Diffstat (limited to 'drivers/staging/tidspbridge/rmgr/disp.c')
-rw-r--r-- | drivers/staging/tidspbridge/rmgr/disp.c | 752 |
1 files changed, 752 insertions, 0 deletions
diff --git a/drivers/staging/tidspbridge/rmgr/disp.c b/drivers/staging/tidspbridge/rmgr/disp.c new file mode 100644 index 000000000000..b7ce4353e06b --- /dev/null +++ b/drivers/staging/tidspbridge/rmgr/disp.c @@ -0,0 +1,752 @@ +/* + * disp.c + * + * DSP-BIOS Bridge driver support functions for TI OMAP processors. + * + * Node Dispatcher interface. Communicates with Resource Manager Server + * (RMS) on DSP. Access to RMS is synchronized in NODE. + * + * Copyright (C) 2005-2006 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +#include <linux/types.h> + +/* ----------------------------------- Host OS */ +#include <dspbridge/host_os.h> + +/* ----------------------------------- DSP/BIOS Bridge */ +#include <dspbridge/dbdefs.h> + +/* ----------------------------------- Trace & Debug */ +#include <dspbridge/dbc.h> + +/* ----------------------------------- OS Adaptation Layer */ +#include <dspbridge/sync.h> + +/* ----------------------------------- Link Driver */ +#include <dspbridge/dspdefs.h> + +/* ----------------------------------- Platform Manager */ +#include <dspbridge/dev.h> +#include <dspbridge/chnldefs.h> + +/* ----------------------------------- Resource Manager */ +#include <dspbridge/nodedefs.h> +#include <dspbridge/nodepriv.h> +#include <dspbridge/rms_sh.h> + +/* ----------------------------------- This */ +#include <dspbridge/disp.h> + +/* Size of a reply from RMS */ +#define REPLYSIZE (3 * sizeof(rms_word)) + +/* Reserved channel offsets for communication with RMS */ +#define CHNLTORMSOFFSET 0 +#define CHNLFROMRMSOFFSET 1 + +#define CHNLIOREQS 1 + +/* + * ======== disp_object ======== + */ +struct disp_object { + struct dev_object *hdev_obj; /* Device for this processor */ + /* Function interface to Bridge driver */ + struct bridge_drv_interface *intf_fxns; + struct chnl_mgr *hchnl_mgr; /* Channel manager */ + struct chnl_object *chnl_to_dsp; /* Chnl for commands to RMS */ + struct chnl_object *chnl_from_dsp; /* Chnl for replies from RMS */ + u8 *pbuf; /* Buffer for commands, replies */ + u32 ul_bufsize; /* pbuf size in bytes */ + u32 ul_bufsize_rms; /* pbuf size in RMS words */ + u32 char_size; /* Size of DSP character */ + u32 word_size; /* Size of DSP word */ + u32 data_mau_size; /* Size of DSP Data MAU */ +}; + +static u32 refs; + +static void delete_disp(struct disp_object *disp_obj); +static int fill_stream_def(rms_word *pdw_buf, u32 *ptotal, u32 offset, + struct node_strmdef strm_def, u32 max, + u32 chars_in_rms_word); +static int send_message(struct disp_object *disp_obj, u32 timeout, + u32 ul_bytes, u32 *pdw_arg); + +/* + * ======== disp_create ======== + * Create a NODE Dispatcher object. + */ +int disp_create(struct disp_object **dispatch_obj, + struct dev_object *hdev_obj, + const struct disp_attr *disp_attrs) +{ + struct disp_object *disp_obj; + struct bridge_drv_interface *intf_fxns; + u32 ul_chnl_id; + struct chnl_attr chnl_attr_obj; + int status = 0; + u8 dev_type; + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(dispatch_obj != NULL); + DBC_REQUIRE(disp_attrs != NULL); + DBC_REQUIRE(hdev_obj != NULL); + + *dispatch_obj = NULL; + + /* Allocate Node Dispatcher object */ + disp_obj = kzalloc(sizeof(struct disp_object), GFP_KERNEL); + if (disp_obj == NULL) + status = -ENOMEM; + else + disp_obj->hdev_obj = hdev_obj; + + /* Get Channel manager and Bridge function interface */ + if (!status) { + status = dev_get_chnl_mgr(hdev_obj, &(disp_obj->hchnl_mgr)); + if (!status) { + (void)dev_get_intf_fxns(hdev_obj, &intf_fxns); + disp_obj->intf_fxns = intf_fxns; + } + } + + /* check device type and decide if streams or messag'ing is used for + * RMS/EDS */ + if (status) + goto func_cont; + + status = dev_get_dev_type(hdev_obj, &dev_type); + + if (status) + goto func_cont; + + if (dev_type != DSP_UNIT) { + status = -EPERM; + goto func_cont; + } + + disp_obj->char_size = DSPWORDSIZE; + disp_obj->word_size = DSPWORDSIZE; + disp_obj->data_mau_size = DSPWORDSIZE; + /* Open channels for communicating with the RMS */ + chnl_attr_obj.uio_reqs = CHNLIOREQS; + chnl_attr_obj.event_obj = NULL; + ul_chnl_id = disp_attrs->ul_chnl_offset + CHNLTORMSOFFSET; + status = (*intf_fxns->pfn_chnl_open) (&(disp_obj->chnl_to_dsp), + disp_obj->hchnl_mgr, + CHNL_MODETODSP, ul_chnl_id, + &chnl_attr_obj); + + if (!status) { + ul_chnl_id = disp_attrs->ul_chnl_offset + CHNLFROMRMSOFFSET; + status = + (*intf_fxns->pfn_chnl_open) (&(disp_obj->chnl_from_dsp), + disp_obj->hchnl_mgr, + CHNL_MODEFROMDSP, ul_chnl_id, + &chnl_attr_obj); + } + if (!status) { + /* Allocate buffer for commands, replies */ + disp_obj->ul_bufsize = disp_attrs->ul_chnl_buf_size; + disp_obj->ul_bufsize_rms = RMS_COMMANDBUFSIZE; + disp_obj->pbuf = kzalloc(disp_obj->ul_bufsize, GFP_KERNEL); + if (disp_obj->pbuf == NULL) + status = -ENOMEM; + } +func_cont: + if (!status) + *dispatch_obj = disp_obj; + else + delete_disp(disp_obj); + + DBC_ENSURE((status && *dispatch_obj == NULL) || + (!status && *dispatch_obj)); + return status; +} + +/* + * ======== disp_delete ======== + * Delete the NODE Dispatcher. + */ +void disp_delete(struct disp_object *disp_obj) +{ + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(disp_obj); + + delete_disp(disp_obj); +} + +/* + * ======== disp_exit ======== + * Discontinue usage of DISP module. + */ +void disp_exit(void) +{ + DBC_REQUIRE(refs > 0); + + refs--; + + DBC_ENSURE(refs >= 0); +} + +/* + * ======== disp_init ======== + * Initialize the DISP module. + */ +bool disp_init(void) +{ + bool ret = true; + + DBC_REQUIRE(refs >= 0); + + if (ret) + refs++; + + DBC_ENSURE((ret && (refs > 0)) || (!ret && (refs >= 0))); + return ret; +} + +/* + * ======== disp_node_change_priority ======== + * Change the priority of a node currently running on the target. + */ +int disp_node_change_priority(struct disp_object *disp_obj, + struct node_object *hnode, + u32 rms_fxn, nodeenv node_env, s32 prio) +{ + u32 dw_arg; + struct rms_command *rms_cmd; + int status = 0; + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(disp_obj); + DBC_REQUIRE(hnode != NULL); + + /* Send message to RMS to change priority */ + rms_cmd = (struct rms_command *)(disp_obj->pbuf); + rms_cmd->fxn = (rms_word) (rms_fxn); + rms_cmd->arg1 = (rms_word) node_env; + rms_cmd->arg2 = prio; + status = send_message(disp_obj, node_get_timeout(hnode), + sizeof(struct rms_command), &dw_arg); + + return status; +} + +/* + * ======== disp_node_create ======== + * Create a node on the DSP by remotely calling the node's create function. + */ +int disp_node_create(struct disp_object *disp_obj, + struct node_object *hnode, u32 rms_fxn, + u32 ul_create_fxn, + const struct node_createargs *pargs, + nodeenv *node_env) +{ + struct node_msgargs node_msg_args; + struct node_taskargs task_arg_obj; + struct rms_command *rms_cmd; + struct rms_msg_args *pmsg_args; + struct rms_more_task_args *more_task_args; + enum node_type node_type; + u32 dw_length; + rms_word *pdw_buf = NULL; + u32 ul_bytes; + u32 i; + u32 total; + u32 chars_in_rms_word; + s32 task_args_offset; + s32 sio_in_def_offset; + s32 sio_out_def_offset; + s32 sio_defs_offset; + s32 args_offset = -1; + s32 offset; + struct node_strmdef strm_def; + u32 max; + int status = 0; + struct dsp_nodeinfo node_info; + u8 dev_type; + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(disp_obj); + DBC_REQUIRE(hnode != NULL); + DBC_REQUIRE(node_get_type(hnode) != NODE_DEVICE); + DBC_REQUIRE(node_env != NULL); + + status = dev_get_dev_type(disp_obj->hdev_obj, &dev_type); + + if (status) + goto func_end; + + if (dev_type != DSP_UNIT) { + dev_dbg(bridge, "%s: unknown device type = 0x%x\n", + __func__, dev_type); + goto func_end; + } + DBC_REQUIRE(pargs != NULL); + node_type = node_get_type(hnode); + node_msg_args = pargs->asa.node_msg_args; + max = disp_obj->ul_bufsize_rms; /*Max # of RMS words that can be sent */ + DBC_ASSERT(max == RMS_COMMANDBUFSIZE); + chars_in_rms_word = sizeof(rms_word) / disp_obj->char_size; + /* Number of RMS words needed to hold arg data */ + dw_length = + (node_msg_args.arg_length + chars_in_rms_word - + 1) / chars_in_rms_word; + /* Make sure msg args and command fit in buffer */ + total = sizeof(struct rms_command) / sizeof(rms_word) + + sizeof(struct rms_msg_args) + / sizeof(rms_word) - 1 + dw_length; + if (total >= max) { + status = -EPERM; + dev_dbg(bridge, "%s: Message args too large for buffer! size " + "= %d, max = %d\n", __func__, total, max); + } + /* + * Fill in buffer to send to RMS. + * The buffer will have the following format: + * + * RMS command: + * Address of RMS_CreateNode() + * Address of node's create function + * dummy argument + * node type + * + * Message Args: + * max number of messages + * segid for message buffer allocation + * notification type to use when message is received + * length of message arg data + * message args data + * + * Task Args (if task or socket node): + * priority + * stack size + * system stack size + * stack segment + * misc + * number of input streams + * pSTRMInDef[] - offsets of STRM definitions for input streams + * number of output streams + * pSTRMOutDef[] - offsets of STRM definitions for output + * streams + * STRMInDef[] - array of STRM definitions for input streams + * STRMOutDef[] - array of STRM definitions for output streams + * + * Socket Args (if DAIS socket node): + * + */ + if (!status) { + total = 0; /* Total number of words in buffer so far */ + pdw_buf = (rms_word *) disp_obj->pbuf; + rms_cmd = (struct rms_command *)pdw_buf; + rms_cmd->fxn = (rms_word) (rms_fxn); + rms_cmd->arg1 = (rms_word) (ul_create_fxn); + if (node_get_load_type(hnode) == NLDR_DYNAMICLOAD) { + /* Flush ICACHE on Load */ + rms_cmd->arg2 = 1; /* dummy argument */ + } else { + /* Do not flush ICACHE */ + rms_cmd->arg2 = 0; /* dummy argument */ + } + rms_cmd->data = node_get_type(hnode); + /* + * args_offset is the offset of the data field in struct + * rms_command structure. We need this to calculate stream + * definition offsets. + */ + args_offset = 3; + total += sizeof(struct rms_command) / sizeof(rms_word); + /* Message args */ + pmsg_args = (struct rms_msg_args *)(pdw_buf + total); + pmsg_args->max_msgs = node_msg_args.max_msgs; + pmsg_args->segid = node_msg_args.seg_id; + pmsg_args->notify_type = node_msg_args.notify_type; + pmsg_args->arg_length = node_msg_args.arg_length; + total += sizeof(struct rms_msg_args) / sizeof(rms_word) - 1; + memcpy(pdw_buf + total, node_msg_args.pdata, + node_msg_args.arg_length); + total += dw_length; + } + if (status) + goto func_end; + + /* If node is a task node, copy task create arguments into buffer */ + if (node_type == NODE_TASK || node_type == NODE_DAISSOCKET) { + task_arg_obj = pargs->asa.task_arg_obj; + task_args_offset = total; + total += sizeof(struct rms_more_task_args) / sizeof(rms_word) + + 1 + task_arg_obj.num_inputs + task_arg_obj.num_outputs; + /* Copy task arguments */ + if (total < max) { + total = task_args_offset; + more_task_args = (struct rms_more_task_args *)(pdw_buf + + total); + /* + * Get some important info about the node. Note that we + * don't just reach into the hnode struct because + * that would break the node object's abstraction. + */ + get_node_info(hnode, &node_info); + more_task_args->priority = node_info.execution_priority; + more_task_args->stack_size = task_arg_obj.stack_size; + more_task_args->sysstack_size = + task_arg_obj.sys_stack_size; + more_task_args->stack_seg = task_arg_obj.stack_seg; + more_task_args->heap_addr = task_arg_obj.udsp_heap_addr; + more_task_args->heap_size = task_arg_obj.heap_size; + more_task_args->misc = task_arg_obj.ul_dais_arg; + more_task_args->num_input_streams = + task_arg_obj.num_inputs; + total += + sizeof(struct rms_more_task_args) / + sizeof(rms_word); + dev_dbg(bridge, "%s: udsp_heap_addr %x, heap_size %x\n", + __func__, task_arg_obj.udsp_heap_addr, + task_arg_obj.heap_size); + /* Keep track of pSIOInDef[] and pSIOOutDef[] + * positions in the buffer, since this needs to be + * filled in later. */ + sio_in_def_offset = total; + total += task_arg_obj.num_inputs; + pdw_buf[total++] = task_arg_obj.num_outputs; + sio_out_def_offset = total; + total += task_arg_obj.num_outputs; + sio_defs_offset = total; + /* Fill SIO defs and offsets */ + offset = sio_defs_offset; + for (i = 0; i < task_arg_obj.num_inputs; i++) { + if (status) + break; + + pdw_buf[sio_in_def_offset + i] = + (offset - args_offset) + * (sizeof(rms_word) / DSPWORDSIZE); + strm_def = task_arg_obj.strm_in_def[i]; + status = + fill_stream_def(pdw_buf, &total, offset, + strm_def, max, + chars_in_rms_word); + offset = total; + } + for (i = 0; (i < task_arg_obj.num_outputs) && + (!status); i++) { + pdw_buf[sio_out_def_offset + i] = + (offset - args_offset) + * (sizeof(rms_word) / DSPWORDSIZE); + strm_def = task_arg_obj.strm_out_def[i]; + status = + fill_stream_def(pdw_buf, &total, offset, + strm_def, max, + chars_in_rms_word); + offset = total; + } + } else { + /* Args won't fit */ + status = -EPERM; + } + } + if (!status) { + ul_bytes = total * sizeof(rms_word); + DBC_ASSERT(ul_bytes < (RMS_COMMANDBUFSIZE * sizeof(rms_word))); + status = send_message(disp_obj, node_get_timeout(hnode), + ul_bytes, node_env); + if (status >= 0) { + /* + * Message successfully received from RMS. + * Return the status of the Node's create function + * on the DSP-side + */ + status = (((rms_word *) (disp_obj->pbuf))[0]); + if (status < 0) + dev_dbg(bridge, "%s: DSP-side failed: 0x%x\n", + __func__, status); + } + } +func_end: + return status; +} + +/* + * ======== disp_node_delete ======== + * purpose: + * Delete a node on the DSP by remotely calling the node's delete function. + * + */ +int disp_node_delete(struct disp_object *disp_obj, + struct node_object *hnode, u32 rms_fxn, + u32 ul_delete_fxn, nodeenv node_env) +{ + u32 dw_arg; + struct rms_command *rms_cmd; + int status = 0; + u8 dev_type; + + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(disp_obj); + DBC_REQUIRE(hnode != NULL); + + status = dev_get_dev_type(disp_obj->hdev_obj, &dev_type); + + if (!status) { + + if (dev_type == DSP_UNIT) { + + /* + * Fill in buffer to send to RMS + */ + rms_cmd = (struct rms_command *)disp_obj->pbuf; + rms_cmd->fxn = (rms_word) (rms_fxn); + rms_cmd->arg1 = (rms_word) node_env; + rms_cmd->arg2 = (rms_word) (ul_delete_fxn); + rms_cmd->data = node_get_type(hnode); + + status = send_message(disp_obj, node_get_timeout(hnode), + sizeof(struct rms_command), + &dw_arg); + if (status >= 0) { + /* + * Message successfully received from RMS. + * Return the status of the Node's delete + * function on the DSP-side + */ + status = (((rms_word *) (disp_obj->pbuf))[0]); + if (status < 0) + dev_dbg(bridge, "%s: DSP-side failed: " + "0x%x\n", __func__, status); + } + + } + } + return status; +} + +/* + * ======== disp_node_run ======== + * purpose: + * Start execution of a node's execute phase, or resume execution of a node + * that has been suspended (via DISP_NodePause()) on the DSP. + */ +int disp_node_run(struct disp_object *disp_obj, + struct node_object *hnode, u32 rms_fxn, + u32 ul_execute_fxn, nodeenv node_env) +{ + u32 dw_arg; + struct rms_command *rms_cmd; + int status = 0; + u8 dev_type; + DBC_REQUIRE(refs > 0); + DBC_REQUIRE(disp_obj); + DBC_REQUIRE(hnode != NULL); + + status = dev_get_dev_type(disp_obj->hdev_obj, &dev_type); + + if (!status) { + + if (dev_type == DSP_UNIT) { + + /* + * Fill in buffer to send to RMS. + */ + rms_cmd = (struct rms_command *)disp_obj->pbuf; + rms_cmd->fxn = (rms_word) (rms_fxn); + rms_cmd->arg1 = (rms_word) node_env; + rms_cmd->arg2 = (rms_word) (ul_execute_fxn); + rms_cmd->data = node_get_type(hnode); + + status = send_message(disp_obj, node_get_timeout(hnode), + sizeof(struct rms_command), + &dw_arg); + if (status >= 0) { + /* + * Message successfully received from RMS. + * Return the status of the Node's execute + * function on the DSP-side + */ + status = (((rms_word *) (disp_obj->pbuf))[0]); + if (status < 0) + dev_dbg(bridge, "%s: DSP-side failed: " + "0x%x\n", __func__, status); + } + + } + } + + return status; +} + +/* + * ======== delete_disp ======== + * purpose: + * Frees the resources allocated for the dispatcher. + */ +static void delete_disp(struct disp_object *disp_obj) +{ + int status = 0; + struct bridge_drv_interface *intf_fxns; + + if (disp_obj) { + intf_fxns = disp_obj->intf_fxns; + + /* Free Node Dispatcher resources */ + if (disp_obj->chnl_from_dsp) { + /* Channel close can fail only if the channel handle + * is invalid. */ + status = (*intf_fxns->pfn_chnl_close) + (disp_obj->chnl_from_dsp); + if (status) { + dev_dbg(bridge, "%s: Failed to close channel " + "from RMS: 0x%x\n", __func__, status); + } + } + if (disp_obj->chnl_to_dsp) { + status = + (*intf_fxns->pfn_chnl_close) (disp_obj-> + chnl_to_dsp); + if (status) { + dev_dbg(bridge, "%s: Failed to close channel to" + " RMS: 0x%x\n", __func__, status); + } + } + kfree(disp_obj->pbuf); + + kfree(disp_obj); + } +} + +/* + * ======== fill_stream_def ======== + * purpose: + * Fills stream definitions. + */ +static int fill_stream_def(rms_word *pdw_buf, u32 *ptotal, u32 offset, + struct node_strmdef strm_def, u32 max, + u32 chars_in_rms_word) +{ + struct rms_strm_def *strm_def_obj; + u32 total = *ptotal; + u32 name_len; + u32 dw_length; + int status = 0; + + if (total + sizeof(struct rms_strm_def) / sizeof(rms_word) >= max) { + status = -EPERM; + } else { + strm_def_obj = (struct rms_strm_def *)(pdw_buf + total); + strm_def_obj->bufsize = strm_def.buf_size; + strm_def_obj->nbufs = strm_def.num_bufs; + strm_def_obj->segid = strm_def.seg_id; + strm_def_obj->align = strm_def.buf_alignment; + strm_def_obj->timeout = strm_def.utimeout; + } + + if (!status) { + /* + * Since we haven't added the device name yet, subtract + * 1 from total. + */ + total += sizeof(struct rms_strm_def) / sizeof(rms_word) - 1; + DBC_REQUIRE(strm_def.sz_device); + dw_length = strlen(strm_def.sz_device) + 1; + + /* Number of RMS_WORDS needed to hold device name */ + name_len = + (dw_length + chars_in_rms_word - 1) / chars_in_rms_word; + + if (total + name_len >= max) { + status = -EPERM; + } else { + /* + * Zero out last word, since the device name may not + * extend to completely fill this word. + */ + pdw_buf[total + name_len - 1] = 0; + /** TODO USE SERVICES * */ + memcpy(pdw_buf + total, strm_def.sz_device, dw_length); + total += name_len; + *ptotal = total; + } + } + + return status; +} + +/* + * ======== send_message ====== + * Send command message to RMS, get reply from RMS. + */ +static int send_message(struct disp_object *disp_obj, u32 timeout, + u32 ul_bytes, u32 *pdw_arg) +{ + struct bridge_drv_interface *intf_fxns; + struct chnl_object *chnl_obj; + u32 dw_arg = 0; + u8 *pbuf; + struct chnl_ioc chnl_ioc_obj; + int status = 0; + + DBC_REQUIRE(pdw_arg != NULL); + + *pdw_arg = (u32) NULL; + intf_fxns = disp_obj->intf_fxns; + chnl_obj = disp_obj->chnl_to_dsp; + pbuf = disp_obj->pbuf; + + /* Send the command */ + status = (*intf_fxns->pfn_chnl_add_io_req) (chnl_obj, pbuf, ul_bytes, 0, + 0L, dw_arg); + if (status) + goto func_end; + + status = + (*intf_fxns->pfn_chnl_get_ioc) (chnl_obj, timeout, &chnl_ioc_obj); + if (!status) { + if (!CHNL_IS_IO_COMPLETE(chnl_ioc_obj)) { + if (CHNL_IS_TIMED_OUT(chnl_ioc_obj)) + status = -ETIME; + else + status = -EPERM; + } + } + /* Get the reply */ + if (status) + goto func_end; + + chnl_obj = disp_obj->chnl_from_dsp; + ul_bytes = REPLYSIZE; + status = (*intf_fxns->pfn_chnl_add_io_req) (chnl_obj, pbuf, ul_bytes, + 0, 0L, dw_arg); + if (status) + goto func_end; + + status = + (*intf_fxns->pfn_chnl_get_ioc) (chnl_obj, timeout, &chnl_ioc_obj); + if (!status) { + if (CHNL_IS_TIMED_OUT(chnl_ioc_obj)) { + status = -ETIME; + } else if (chnl_ioc_obj.byte_size < ul_bytes) { + /* Did not get all of the reply from the RMS */ + status = -EPERM; + } else { + if (CHNL_IS_IO_COMPLETE(chnl_ioc_obj)) { + DBC_ASSERT(chnl_ioc_obj.pbuf == pbuf); + status = (*((rms_word *) chnl_ioc_obj.pbuf)); + *pdw_arg = + (((rms_word *) (chnl_ioc_obj.pbuf))[1]); + } else { + status = -EPERM; + } + } + } +func_end: + return status; +} |