| /* Copyright (c) 2010, Code Aurora Forum. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 and |
| * only version 2 as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| * 02110-1301, USA. |
| * |
| */ |
| |
| #include "video_core_type.h" |
| #include "vcd.h" |
| |
| static const struct vcd_dev_state_table *vcd_dev_state_table[]; |
| static const struct vcd_dev_state_table vcd_dev_table_null; |
| |
| struct vcd_drv_ctxt *vcd_get_drv_context(void) |
| { |
| static struct vcd_drv_ctxt drv_context = { |
| {&vcd_dev_table_null, VCD_DEVICE_STATE_NULL}, |
| {0}, |
| 0 |
| }; |
| |
| return &drv_context; |
| |
| } |
| |
| void vcd_do_device_state_transition(struct vcd_drv_ctxt *drv_ctxt, |
| enum vcd_dev_state_enum to_state, u32 ev_code) |
| { |
| struct vcd_dev_state_ctxt *state_ctxt; |
| |
| if (!drv_ctxt || to_state >= VCD_DEVICE_STATE_MAX) { |
| VCD_MSG_ERROR("Bad parameters. drv_ctxt=%p, to_state=%d", |
| drv_ctxt, to_state); |
| } |
| |
| state_ctxt = &drv_ctxt->dev_state; |
| |
| if (state_ctxt->state == to_state) { |
| VCD_MSG_HIGH("Device already in requested to_state=%d", |
| to_state); |
| |
| return; |
| } |
| |
| VCD_MSG_MED("vcd_do_device_state_transition: D%d -> D%d, for api %d", |
| (int)state_ctxt->state, (int)to_state, ev_code); |
| |
| if (state_ctxt->state_table->pf_exit) |
| state_ctxt->state_table->pf_exit(drv_ctxt, ev_code); |
| |
| |
| state_ctxt->state = to_state; |
| state_ctxt->state_table = vcd_dev_state_table[to_state]; |
| |
| if (state_ctxt->state_table->pf_entry) |
| state_ctxt->state_table->pf_entry(drv_ctxt, ev_code); |
| } |
| |
| void vcd_hw_timeout_handler(void *user_data) |
| { |
| struct vcd_drv_ctxt *drv_ctxt; |
| |
| VCD_MSG_HIGH("vcd_hw_timeout_handler:"); |
| user_data = NULL; |
| drv_ctxt = vcd_get_drv_context(); |
| mutex_lock(drv_ctxt->dev_mutex); |
| if (drv_ctxt->dev_state.state_table->ev_hdlr.pf_timeout) |
| drv_ctxt->dev_state.state_table->ev_hdlr.pf_timeout(drv_ctxt, |
| user_data); |
| else |
| VCD_MSG_ERROR("hw_timeout unsupported in device state %d", |
| drv_ctxt->dev_state.state); |
| mutex_unlock(drv_ctxt->dev_mutex); |
| } |
| |
| void vcd_ddl_callback(u32 event, u32 status, void *payload, |
| u32 size, u32 *ddl_handle, void *const client_data) |
| { |
| struct vcd_drv_ctxt *drv_ctxt; |
| struct vcd_dev_ctxt *dev_ctxt; |
| struct vcd_dev_state_ctxt *dev_state; |
| struct vcd_clnt_ctxt *cctxt; |
| struct vcd_transc *transc; |
| |
| VCD_MSG_LOW("vcd_ddl_callback:"); |
| |
| VCD_MSG_LOW("event=0x%x status=0x%x", event, status); |
| |
| drv_ctxt = vcd_get_drv_context(); |
| dev_ctxt = &drv_ctxt->dev_ctxt; |
| dev_state = &drv_ctxt->dev_state; |
| |
| dev_ctxt->cont = true; |
| vcd_device_timer_stop(dev_ctxt); |
| |
| switch (dev_state->state) { |
| case VCD_DEVICE_STATE_NULL: |
| VCD_MSG_HIGH("Callback unexpected in NULL state"); |
| break; |
| case VCD_DEVICE_STATE_NOT_INIT: |
| VCD_MSG_HIGH("Callback unexpected in NOT_INIT state"); |
| break; |
| case VCD_DEVICE_STATE_INITING: |
| if (dev_state->state_table->ev_hdlr.pf_dev_cb) { |
| dev_state->state_table->ev_hdlr.pf_dev_cb(drv_ctxt, |
| event, status, payload, size, ddl_handle, |
| client_data); |
| } else { |
| VCD_MSG_HIGH("No device handler in %d state", |
| dev_state->state); |
| } |
| break; |
| case VCD_DEVICE_STATE_READY: |
| transc = (struct vcd_transc *)client_data; |
| |
| if (!transc || !transc->in_use || !transc->cctxt) { |
| VCD_MSG_ERROR("Invalid clientdata received from DDL "); |
| } else { |
| cctxt = transc->cctxt; |
| |
| if (cctxt->clnt_state.state_table->ev_hdlr.pf_clnt_cb) { |
| cctxt->clnt_state.state_table->ev_hdlr. |
| pf_clnt_cb(cctxt, event, status, |
| payload, size, ddl_handle, client_data); |
| } else { |
| VCD_MSG_HIGH("No client handler in" |
| " (dsm:READY, csm:%d) state", |
| (int)cctxt->clnt_state.state); |
| |
| if (VCD_FAILED(status)) { |
| VCD_MSG_FATAL("DDL callback" |
| " returned failure 0x%x", |
| status); |
| } |
| } |
| } |
| break; |
| default: |
| VCD_MSG_ERROR("Unknown state"); |
| break; |
| } |
| |
| } |
| |
| u32 vcd_init_device_context(struct vcd_drv_ctxt *drv_ctxt, u32 ev_code) |
| { |
| struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt; |
| struct sched_init_param sched_init; |
| u32 rc; |
| struct ddl_init_config ddl_init; |
| |
| VCD_MSG_LOW("vcd_init_device_context:"); |
| |
| dev_ctxt->pending_cmd = VCD_CMD_NONE; |
| |
| rc = vcd_power_event(dev_ctxt, NULL, VCD_EVT_PWR_DEV_INIT_BEGIN); |
| VCD_FAILED_RETURN(rc, "VCD_EVT_PWR_DEV_INIT_BEGIN failed"); |
| |
| VCD_MSG_HIGH("Device powered ON and clocked"); |
| |
| sched_init.perf_lvl = dev_ctxt->max_perf_lvl; |
| rc = vcd_map_sched_status(sched_create(&sched_init, |
| &dev_ctxt->sched_hdl)); |
| |
| if (VCD_FAILED(rc)) { |
| VCD_MSG_ERROR("rc = 0x%x. Failed: sched_create", rc); |
| |
| vcd_power_event(dev_ctxt, NULL, VCD_EVT_PWR_DEV_INIT_FAIL); |
| |
| return rc; |
| } |
| |
| VCD_MSG_HIGH("Created scheduler instance."); |
| |
| ddl_init.core_virtual_base_addr = dev_ctxt->device_base_addr; |
| ddl_init.pf_interrupt_clr = dev_ctxt->config.pf_interrupt_clr; |
| ddl_init.ddl_callback = vcd_ddl_callback; |
| |
| rc = ddl_device_init(&ddl_init, NULL); |
| |
| if (VCD_FAILED(rc)) { |
| VCD_MSG_ERROR("rc = 0x%x. Failed: ddl_device_init", rc); |
| |
| sched_destroy(dev_ctxt->sched_hdl); |
| dev_ctxt->sched_hdl = NULL; |
| |
| vcd_power_event(dev_ctxt, NULL, VCD_EVT_PWR_DEV_INIT_FAIL); |
| } else { |
| vcd_device_timer_start(dev_ctxt); |
| vcd_do_device_state_transition(drv_ctxt, |
| VCD_DEVICE_STATE_INITING, ev_code); |
| } |
| |
| return rc; |
| } |
| |
| void vcd_handle_device_init_failed(struct vcd_drv_ctxt *drv_ctxt, u32 status) |
| { |
| struct vcd_clnt_ctxt *client; |
| struct vcd_clnt_ctxt *tmp_client; |
| |
| VCD_MSG_ERROR("Device init failed. status = %d", status); |
| |
| client = drv_ctxt->dev_ctxt.cctxt_list_head; |
| while (client) { |
| client->callback(VCD_EVT_RESP_OPEN, status, NULL, 0, 0, |
| client->client_data); |
| |
| tmp_client = client; |
| client = client->next; |
| |
| vcd_destroy_client_context(tmp_client); |
| } |
| if (ddl_device_release(NULL)) |
| VCD_MSG_ERROR("Failed: ddl_device_release"); |
| |
| (void)sched_destroy(drv_ctxt->dev_ctxt.sched_hdl); |
| drv_ctxt->dev_ctxt.sched_hdl = NULL; |
| |
| if (vcd_power_event(&drv_ctxt->dev_ctxt, NULL, |
| VCD_EVT_PWR_DEV_INIT_FAIL)) |
| VCD_MSG_ERROR("VCD_EVT_PWR_DEV_INIT_FAIL failed"); |
| |
| vcd_do_device_state_transition(drv_ctxt, VCD_DEVICE_STATE_NOT_INIT, |
| DEVICE_STATE_EVENT_NUMBER(pf_dev_cb)); |
| } |
| |
| u32 vcd_deinit_device_context(struct vcd_drv_ctxt *drv_ctxt, u32 ev_code) |
| { |
| struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt; |
| u32 rc = VCD_S_SUCCESS; |
| |
| VCD_MSG_LOW("vcd_deinit_device_context:"); |
| |
| rc = vcd_power_event(&drv_ctxt->dev_ctxt, NULL, |
| VCD_EVT_PWR_DEV_TERM_BEGIN); |
| |
| VCD_FAILED_RETURN(rc, "VCD_EVT_PWR_DEV_TERM_BEGIN failed"); |
| |
| rc = ddl_device_release(NULL); |
| |
| if (VCD_FAILED(rc)) { |
| VCD_MSG_ERROR("rc = 0x%x. Failed: ddl_device_release", rc); |
| |
| vcd_power_event(dev_ctxt, NULL, VCD_EVT_PWR_DEV_TERM_FAIL); |
| } else { |
| sched_destroy(dev_ctxt->sched_hdl); |
| dev_ctxt->sched_hdl = NULL; |
| |
| vcd_power_event(dev_ctxt, NULL, VCD_EVT_PWR_DEV_TERM_END); |
| |
| vcd_do_device_state_transition(drv_ctxt, |
| VCD_DEVICE_STATE_NOT_INIT, ev_code); |
| } |
| return rc; |
| } |
| |
| void vcd_term_driver_context(struct vcd_drv_ctxt *drv_ctxt) |
| { |
| struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt; |
| |
| VCD_MSG_HIGH("All driver instances terminated"); |
| |
| if (dev_ctxt->config.pf_deregister_isr) |
| dev_ctxt->config.pf_deregister_isr(); |
| |
| if (dev_ctxt->config.pf_un_map_dev_base_addr) |
| dev_ctxt->config.pf_un_map_dev_base_addr(); |
| |
| if (dev_ctxt->config.pf_timer_release) |
| dev_ctxt->config.pf_timer_release(dev_ctxt->hw_timer_handle); |
| |
| kfree(dev_ctxt->trans_tbl); |
| |
| memset(dev_ctxt, 0, sizeof(struct vcd_dev_ctxt)); |
| |
| vcd_do_device_state_transition(drv_ctxt, VCD_DEVICE_STATE_NULL, |
| DEVICE_STATE_EVENT_NUMBER(pf_term)); |
| |
| } |
| |
| u32 vcd_reset_device_context(struct vcd_drv_ctxt *drv_ctxt, u32 ev_code) |
| { |
| struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt; |
| u32 rc = VCD_S_SUCCESS; |
| |
| VCD_MSG_LOW("vcd_reset_device_context:"); |
| vcd_reset_device_channels(dev_ctxt); |
| rc = vcd_power_event(&drv_ctxt->dev_ctxt, NULL, |
| VCD_EVT_PWR_DEV_TERM_BEGIN); |
| VCD_FAILED_RETURN(rc, "VCD_EVT_PWR_DEV_TERM_BEGIN failed"); |
| if (ddl_reset_hw(0)) |
| VCD_MSG_HIGH("HW Reset done"); |
| else |
| VCD_MSG_FATAL("HW Reset failed"); |
| |
| vcd_power_event(dev_ctxt, NULL, VCD_EVT_PWR_DEV_TERM_END); |
| |
| return VCD_S_SUCCESS; |
| } |
| |
| void vcd_handle_device_err_fatal(struct vcd_dev_ctxt *dev_ctxt, |
| struct vcd_clnt_ctxt *trig_clnt) |
| { |
| struct vcd_clnt_ctxt *cctxt = dev_ctxt->cctxt_list_head; |
| VCD_MSG_LOW("vcd_handle_device_err_fatal:"); |
| while (cctxt) { |
| if (cctxt != trig_clnt) { |
| vcd_clnt_handle_device_err_fatal(cctxt, |
| VCD_EVT_IND_HWERRFATAL); |
| } |
| cctxt = cctxt->next; |
| } |
| dev_ctxt->pending_cmd = VCD_CMD_DEVICE_RESET; |
| vcd_do_device_state_transition(vcd_get_drv_context(), |
| VCD_DEVICE_STATE_INVALID, DEVICE_STATE_EVENT_NUMBER(pf_dev_cb)); |
| } |
| |
| void vcd_handle_for_last_clnt_close( |
| struct vcd_dev_ctxt *dev_ctxt, u32 send_deinit) |
| { |
| if (!dev_ctxt->cctxt_list_head) { |
| VCD_MSG_HIGH("All clients are closed"); |
| if (send_deinit) |
| vcd_deinit_device_context(vcd_get_drv_context(), |
| DEVICE_STATE_EVENT_NUMBER(pf_close)); |
| else |
| dev_ctxt->pending_cmd = VCD_CMD_DEVICE_TERM; |
| } |
| } |
| void vcd_continue(void) |
| { |
| struct vcd_drv_ctxt *drv_ctxt; |
| struct vcd_dev_ctxt *dev_ctxt; |
| u32 cont; |
| struct vcd_transc *transc; |
| u32 rc; |
| VCD_MSG_LOW("vcd_continue:"); |
| |
| drv_ctxt = vcd_get_drv_context(); |
| dev_ctxt = &drv_ctxt->dev_ctxt; |
| |
| dev_ctxt->cont = false; |
| |
| if (dev_ctxt->pending_cmd == VCD_CMD_DEVICE_INIT) { |
| VCD_MSG_HIGH("VCD_CMD_DEVICE_INIT is pending"); |
| |
| dev_ctxt->pending_cmd = VCD_CMD_NONE; |
| |
| vcd_init_device_context(drv_ctxt, |
| DEVICE_STATE_EVENT_NUMBER(pf_open)); |
| } else if (dev_ctxt->pending_cmd == VCD_CMD_DEVICE_TERM) { |
| VCD_MSG_HIGH("VCD_CMD_DEVICE_TERM is pending"); |
| |
| dev_ctxt->pending_cmd = VCD_CMD_NONE; |
| |
| vcd_deinit_device_context(drv_ctxt, |
| DEVICE_STATE_EVENT_NUMBER(pf_close)); |
| } else if (dev_ctxt->pending_cmd == VCD_CMD_DEVICE_RESET) { |
| VCD_MSG_HIGH("VCD_CMD_DEVICE_RESET is pending"); |
| dev_ctxt->pending_cmd = VCD_CMD_NONE; |
| vcd_reset_device_context(drv_ctxt, |
| DEVICE_STATE_EVENT_NUMBER(pf_dev_cb)); |
| } else { |
| if (dev_ctxt->set_perf_lvl_pending) { |
| rc = vcd_power_event(dev_ctxt, NULL, |
| VCD_EVT_PWR_DEV_SET_PERFLVL); |
| |
| if (VCD_FAILED(rc)) { |
| VCD_MSG_ERROR |
| ("VCD_EVT_PWR_CLNT_SET_PERFLVL failed"); |
| VCD_MSG_HIGH("Not running at desired perf " |
| "level.curr=%d, reqd=%d", |
| dev_ctxt->curr_perf_lvl, |
| dev_ctxt->reqd_perf_lvl); |
| } else { |
| dev_ctxt->set_perf_lvl_pending = false; |
| } |
| } |
| |
| do { |
| cont = false; |
| |
| if (vcd_get_command_channel_in_loop(dev_ctxt, |
| &transc)) { |
| if (vcd_submit_command_in_continue(dev_ctxt, |
| transc)) |
| cont = true; |
| else { |
| VCD_MSG_MED |
| ("No more commands to submit"); |
| |
| vcd_release_command_channel(dev_ctxt, |
| transc); |
| |
| vcd_release_interim_command_channels( |
| dev_ctxt); |
| } |
| } |
| } while (cont); |
| |
| do { |
| cont = false; |
| |
| if (vcd_get_frame_channel_in_loop(dev_ctxt, &transc)) { |
| if (vcd_try_submit_frame_in_continue(dev_ctxt, |
| transc)) { |
| cont = true; |
| } else { |
| VCD_MSG_MED("No more frames to submit"); |
| |
| vcd_release_frame_channel(dev_ctxt, |
| transc); |
| |
| vcd_release_interim_frame_channels( |
| dev_ctxt); |
| } |
| } |
| |
| } while (cont); |
| |
| if (!vcd_core_is_busy(dev_ctxt)) { |
| rc = vcd_power_event(dev_ctxt, NULL, |
| VCD_EVT_PWR_CLNT_CMD_END); |
| |
| if (VCD_FAILED(rc)) |
| VCD_MSG_ERROR("Failed:" |
| "VCD_EVT_PWR_CLNT_CMD_END"); |
| } |
| } |
| } |
| |
| static void vcd_pause_all_sessions(struct vcd_dev_ctxt *dev_ctxt) |
| { |
| struct vcd_clnt_ctxt *cctxt = dev_ctxt->cctxt_list_head; |
| u32 rc; |
| |
| while (cctxt) { |
| if (cctxt->clnt_state.state_table->ev_hdlr.pf_pause) { |
| rc = cctxt->clnt_state.state_table->ev_hdlr. |
| pf_pause(cctxt); |
| |
| if (VCD_FAILED(rc)) |
| VCD_MSG_ERROR("Client pause failed"); |
| |
| } |
| |
| cctxt = cctxt->next; |
| } |
| } |
| |
| static void vcd_resume_all_sessions(struct vcd_dev_ctxt *dev_ctxt) |
| { |
| struct vcd_clnt_ctxt *cctxt = dev_ctxt->cctxt_list_head; |
| u32 rc; |
| |
| while (cctxt) { |
| if (cctxt->clnt_state.state_table->ev_hdlr.pf_resume) { |
| rc = cctxt->clnt_state.state_table->ev_hdlr. |
| pf_resume(cctxt); |
| |
| if (VCD_FAILED(rc)) |
| VCD_MSG_ERROR("Client resume failed"); |
| |
| } |
| |
| cctxt = cctxt->next; |
| } |
| } |
| |
| static u32 vcd_init_cmn(struct vcd_drv_ctxt *drv_ctxt, |
| struct vcd_init_config *config, s32 *driver_handle) |
| { |
| struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt; |
| s32 driver_id; |
| |
| if (dev_ctxt->config.pf_interrupt_clr != config->pf_interrupt_clr || |
| dev_ctxt->config.pf_register_isr != |
| config->pf_register_isr || |
| dev_ctxt->config.pf_deregister_isr != |
| config->pf_deregister_isr || |
| dev_ctxt->config.pf_map_dev_base_addr != |
| config->pf_map_dev_base_addr || |
| dev_ctxt->config.pf_un_map_dev_base_addr != |
| config->pf_un_map_dev_base_addr) { |
| VCD_MSG_ERROR("Device config mismatch"); |
| VCD_MSG_HIGH("VCD will be using config from 1st vcd_init"); |
| } |
| |
| *driver_handle = 0; |
| |
| driver_id = 0; |
| while (driver_id < VCD_DRIVER_INSTANCE_MAX && |
| dev_ctxt->driver_ids[driver_id]) { |
| ++driver_id; |
| } |
| |
| if (driver_id == VCD_DRIVER_INSTANCE_MAX) { |
| VCD_MSG_ERROR("Max driver instances reached"); |
| |
| return VCD_ERR_FAIL; |
| } |
| |
| ++dev_ctxt->refs; |
| dev_ctxt->driver_ids[driver_id] = true; |
| *driver_handle = driver_id + 1; |
| |
| VCD_MSG_HIGH("Driver_id = %d. No of driver instances = %d", |
| driver_id, dev_ctxt->refs); |
| |
| return VCD_S_SUCCESS; |
| |
| } |
| |
| static u32 vcd_init_in_null(struct vcd_drv_ctxt *drv_ctxt, |
| struct vcd_init_config *config, s32 *driver_handle) { |
| u32 rc = VCD_S_SUCCESS; |
| struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt; |
| u32 done_create_timer = false; |
| VCD_MSG_LOW("vcd_init_in_dev_null:"); |
| |
| dev_ctxt->config = *config; |
| |
| dev_ctxt->device_base_addr = (u8 *)config->pf_map_dev_base_addr( |
| dev_ctxt->config.device_name); |
| |
| if (!dev_ctxt->device_base_addr) { |
| VCD_MSG_ERROR("NULL Device_base_addr"); |
| |
| return VCD_ERR_FAIL; |
| } |
| |
| if (config->pf_register_isr) |
| config->pf_register_isr(dev_ctxt->config.device_name); |
| |
| if (config->pf_timer_create) { |
| if (config->pf_timer_create(vcd_hw_timeout_handler, NULL, |
| &dev_ctxt->hw_timer_handle)) |
| done_create_timer = true; |
| else { |
| VCD_MSG_ERROR("timercreate failed"); |
| return VCD_ERR_FAIL; |
| } |
| } |
| |
| |
| rc = vcd_init_cmn(drv_ctxt, config, driver_handle); |
| |
| if (!VCD_FAILED(rc)) { |
| vcd_do_device_state_transition(drv_ctxt, |
| VCD_DEVICE_STATE_NOT_INIT, |
| DEVICE_STATE_EVENT_NUMBER(pf_init)); |
| } else { |
| if (dev_ctxt->config.pf_un_map_dev_base_addr) |
| dev_ctxt->config.pf_un_map_dev_base_addr(); |
| |
| if (dev_ctxt->config.pf_deregister_isr) |
| dev_ctxt->config.pf_deregister_isr(); |
| |
| if (done_create_timer && dev_ctxt->config.pf_timer_release) |
| dev_ctxt->config.pf_timer_release( |
| dev_ctxt->hw_timer_handle); |
| } |
| |
| return rc; |
| |
| } |
| |
| u32 npelly_init(void); |
| |
| static u32 vcd_init_in_not_init(struct vcd_drv_ctxt *drv_ctxt, |
| struct vcd_init_config *config, s32 *driver_handle) |
| { |
| u32 rc; |
| VCD_MSG_LOW("vcd_init_in_dev_not_init:"); |
| |
| rc = npelly_init(); |
| |
| if (rc) |
| return rc; |
| return vcd_init_cmn(drv_ctxt, config, driver_handle); |
| |
| } |
| |
| static u32 vcd_init_in_initing(struct vcd_drv_ctxt *drv_ctxt, |
| struct vcd_init_config *config, s32 *driver_handle) |
| { |
| VCD_MSG_LOW("vcd_init_in_dev_initing:"); |
| |
| return vcd_init_cmn(drv_ctxt, config, driver_handle); |
| |
| } |
| |
| static u32 vcd_init_in_ready(struct vcd_drv_ctxt *drv_ctxt, |
| struct vcd_init_config *config, s32 *driver_handle) |
| { |
| VCD_MSG_LOW("vcd_init_in_dev_ready:"); |
| |
| return vcd_init_cmn(drv_ctxt, config, driver_handle); |
| } |
| |
| static u32 vcd_term_cmn(struct vcd_drv_ctxt *drv_ctxt, s32 driver_handle) |
| { |
| struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt; |
| |
| if (!vcd_validate_driver_handle(dev_ctxt, driver_handle)) { |
| VCD_MSG_ERROR("Invalid driver handle = %d", driver_handle); |
| |
| return VCD_ERR_BAD_HANDLE; |
| } |
| |
| if (vcd_check_for_client_context(dev_ctxt, driver_handle - 1)) { |
| VCD_MSG_ERROR("Driver has active client"); |
| |
| return VCD_ERR_BAD_STATE; |
| } |
| |
| --dev_ctxt->refs; |
| dev_ctxt->driver_ids[driver_handle - 1] = false; |
| |
| VCD_MSG_HIGH("Driver_id %d terminated. No of driver instances = %d", |
| driver_handle - 1, dev_ctxt->refs); |
| |
| return VCD_S_SUCCESS; |
| } |
| |
| static u32 vcd_term_in_not_init(struct vcd_drv_ctxt *drv_ctxt, |
| s32 driver_handle) |
| { |
| struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt; |
| u32 rc; |
| |
| VCD_MSG_LOW("vcd_term_in_dev_not_init:"); |
| |
| rc = vcd_term_cmn(drv_ctxt, driver_handle); |
| |
| if (!VCD_FAILED(rc) && !dev_ctxt->refs) |
| vcd_term_driver_context(drv_ctxt); |
| |
| return rc; |
| } |
| |
| static u32 vcd_term_in_initing(struct vcd_drv_ctxt *drv_ctxt, s32 driver_handle) |
| { |
| VCD_MSG_LOW("vcd_term_in_dev_initing:"); |
| |
| return vcd_term_cmn(drv_ctxt, driver_handle); |
| } |
| |
| static u32 vcd_term_in_ready(struct vcd_drv_ctxt *drv_ctxt, s32 driver_handle) |
| { |
| VCD_MSG_LOW("vcd_term_in_dev_ready:"); |
| |
| return vcd_term_cmn(drv_ctxt, driver_handle); |
| } |
| |
| static u32 vcd_term_in_invalid(struct vcd_drv_ctxt *drv_ctxt, |
| s32 driver_handle) |
| { |
| u32 rc; |
| VCD_MSG_LOW("vcd_term_in_invalid:"); |
| rc = vcd_term_cmn(drv_ctxt, driver_handle); |
| if (!VCD_FAILED(rc) && !drv_ctxt->dev_ctxt.refs) |
| vcd_term_driver_context(drv_ctxt); |
| |
| return rc; |
| } |
| |
| static u32 vcd_open_cmn(struct vcd_drv_ctxt *drv_ctxt, s32 driver_handle, |
| u32 decoding, void (*callback) (u32 event, u32 status, void *info, |
| u32 size, void *handle, void *const client_data), void *client_data, |
| struct vcd_clnt_ctxt **pp_cctxt) |
| { |
| struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt; |
| struct vcd_clnt_ctxt *cctxt; |
| struct vcd_clnt_ctxt *client; |
| |
| if (!vcd_validate_driver_handle(dev_ctxt, driver_handle)) { |
| VCD_MSG_ERROR("Invalid driver handle = %d", driver_handle); |
| |
| return VCD_ERR_BAD_HANDLE; |
| } |
| |
| cctxt = kzalloc(sizeof(struct vcd_clnt_ctxt), GFP_KERNEL); |
| if (!cctxt) { |
| VCD_MSG_ERROR("No memory for client ctxt"); |
| return VCD_ERR_ALLOC_FAIL; |
| } |
| |
| cctxt->dev_ctxt = dev_ctxt; |
| cctxt->driver_id = driver_handle - 1; |
| cctxt->decoding = decoding; |
| cctxt->callback = callback; |
| cctxt->client_data = client_data; |
| |
| client = dev_ctxt->cctxt_list_head; |
| dev_ctxt->cctxt_list_head = cctxt; |
| cctxt->next = client; |
| |
| *pp_cctxt = cctxt; |
| |
| return VCD_S_SUCCESS; |
| |
| } |
| |
| static u32 vcd_open_in_not_init(struct vcd_drv_ctxt *drv_ctxt, |
| s32 driver_handle, u32 decoding, void (*callback) (u32 event, |
| u32 status, void *info, u32 size, void *handle, |
| void *const client_data), void *client_data) |
| { |
| struct vcd_clnt_ctxt *cctxt; |
| u32 rc; |
| |
| VCD_MSG_LOW("vcd_open_in_dev_not_init:"); |
| |
| rc = vcd_open_cmn(drv_ctxt, driver_handle, decoding, callback, |
| client_data, &cctxt); |
| |
| VCD_FAILED_RETURN(rc, "Failed: vcd_open_cmn"); |
| |
| rc = vcd_init_device_context(drv_ctxt, |
| DEVICE_STATE_EVENT_NUMBER(pf_open)); |
| |
| if (VCD_FAILED(rc)) |
| vcd_destroy_client_context(cctxt); |
| |
| return rc; |
| } |
| |
| static u32 vcd_open_in_initing(struct vcd_drv_ctxt *drv_ctxt, s32 driver_handle, |
| u32 decoding, void (*callback) (u32 event, u32 status, void *info, |
| u32 size, void *handle, void *const client_data), void *client_data) |
| { |
| struct vcd_clnt_ctxt *cctxt; |
| |
| VCD_MSG_LOW("vcd_open_in_dev_initing:"); |
| |
| return vcd_open_cmn(drv_ctxt, driver_handle, decoding, callback, |
| client_data, &cctxt); |
| } |
| |
| static u32 vcd_open_in_ready(struct vcd_drv_ctxt *drv_ctxt, s32 driver_handle, |
| u32 decoding, void (*callback) (u32 event, u32 status, void *info, |
| u32 size, void *handle, void *const client_data), void *client_data) |
| { |
| struct vcd_clnt_ctxt *cctxt; |
| struct vcd_handle_container container; |
| u32 rc; |
| |
| VCD_MSG_LOW("vcd_open_in_dev_ready:"); |
| |
| rc = vcd_open_cmn(drv_ctxt, driver_handle, decoding, callback, |
| client_data, &cctxt); |
| |
| VCD_FAILED_RETURN(rc, "Failed: vcd_open_cmn"); |
| |
| rc = vcd_init_client_context(cctxt); |
| |
| if (!VCD_FAILED(rc)) { |
| container.handle = (void *)cctxt; |
| |
| callback(VCD_EVT_RESP_OPEN, VCD_S_SUCCESS, &container, |
| sizeof(container), container.handle, client_data); |
| } else { |
| VCD_MSG_ERROR("rc = 0x%x. Failed: vcd_init_client_context", rc); |
| |
| vcd_destroy_client_context(cctxt); |
| } |
| |
| return rc; |
| } |
| |
| static u32 vcd_close_in_ready(struct vcd_drv_ctxt *drv_ctxt, |
| struct vcd_clnt_ctxt *cctxt) { |
| u32 rc; |
| |
| VCD_MSG_LOW("vcd_close_in_dev_ready:"); |
| |
| if (cctxt->clnt_state.state_table->ev_hdlr.pf_close) { |
| rc = cctxt->clnt_state.state_table->ev_hdlr. |
| pf_close(cctxt); |
| } else { |
| VCD_MSG_ERROR("Unsupported API in client state %d", |
| cctxt->clnt_state.state); |
| |
| rc = VCD_ERR_BAD_STATE; |
| } |
| |
| if (!VCD_FAILED(rc)) |
| vcd_handle_for_last_clnt_close(&drv_ctxt->dev_ctxt, true); |
| |
| return rc; |
| } |
| |
| static u32 vcd_close_in_dev_invalid(struct vcd_drv_ctxt *drv_ctxt, |
| struct vcd_clnt_ctxt *cctxt) |
| { |
| u32 rc; |
| VCD_MSG_LOW("vcd_close_in_dev_invalid:"); |
| if (cctxt->clnt_state.state_table->ev_hdlr.pf_close) { |
| rc = cctxt->clnt_state.state_table->ev_hdlr.pf_close(cctxt); |
| } else { |
| VCD_MSG_ERROR("Unsupported API in client state %d", |
| cctxt->clnt_state.state); |
| rc = VCD_ERR_BAD_STATE; |
| } |
| if (!VCD_FAILED(rc) && !drv_ctxt->dev_ctxt.cctxt_list_head) { |
| VCD_MSG_HIGH("All INVALID clients are closed"); |
| vcd_do_device_state_transition(drv_ctxt, |
| VCD_DEVICE_STATE_NOT_INIT, |
| DEVICE_STATE_EVENT_NUMBER(pf_close)); |
| } |
| return rc; |
| } |
| |
| static u32 vcd_resume_in_ready(struct vcd_drv_ctxt *drv_ctxt, |
| struct vcd_clnt_ctxt *cctxt) |
| { |
| u32 rc = VCD_S_SUCCESS; |
| |
| VCD_MSG_LOW("vcd_resume_in_ready:"); |
| |
| if (cctxt->clnt_state.state_table->ev_hdlr.pf_resume) { |
| rc = cctxt->clnt_state.state_table->ev_hdlr.pf_resume(cctxt); |
| } else { |
| VCD_MSG_ERROR("Unsupported API in client state %d", |
| cctxt->clnt_state.state); |
| |
| rc = VCD_ERR_BAD_STATE; |
| } |
| |
| return rc; |
| } |
| |
| static u32 vcd_set_dev_pwr_in_ready(struct vcd_drv_ctxt *drv_ctxt, |
| enum vcd_power_state pwr_state) |
| { |
| u32 rc = VCD_S_SUCCESS; |
| struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt; |
| |
| VCD_MSG_LOW("vcd_set_dev_pwr_in_ready:"); |
| |
| switch (pwr_state) { |
| case VCD_PWR_STATE_SLEEP: |
| vcd_pause_all_sessions(dev_ctxt); |
| |
| dev_ctxt->pwr_state = VCD_PWR_STATE_SLEEP; |
| |
| break; |
| case VCD_PWR_STATE_ON: |
| if (dev_ctxt->pwr_state == VCD_PWR_STATE_SLEEP) |
| vcd_resume_all_sessions(dev_ctxt); |
| |
| dev_ctxt->pwr_state = VCD_PWR_STATE_ON; |
| break; |
| default: |
| VCD_MSG_ERROR("Invalid power state requested %d", |
| pwr_state); |
| break; |
| } |
| return rc; |
| } |
| |
| static void vcd_dev_cb_in_initing(struct vcd_drv_ctxt *drv_ctxt, u32 event, |
| u32 status, void *payload, u32 size, u32 *ddl_handle, |
| void *const client_data) |
| { |
| struct vcd_dev_ctxt *dev_ctxt; |
| struct vcd_clnt_ctxt *client; |
| struct vcd_clnt_ctxt *tmp_client; |
| struct vcd_handle_container container; |
| u32 rc = VCD_S_SUCCESS; |
| u32 client_inited = false; |
| u32 fail_all_open = false; |
| |
| VCD_MSG_LOW("vcd_dev_cb_in_initing:"); |
| |
| if (event != VCD_EVT_RESP_DEVICE_INIT) { |
| VCD_MSG_ERROR("vcd_dev_cb_in_initing: Unexpected event %d", |
| (int)event); |
| return; |
| } |
| |
| dev_ctxt = &drv_ctxt->dev_ctxt; |
| |
| dev_ctxt->cont = false; |
| |
| if (VCD_FAILED(status)) { |
| vcd_handle_device_init_failed(drv_ctxt, status); |
| |
| return; |
| } |
| |
| vcd_do_device_state_transition(drv_ctxt, VCD_DEVICE_STATE_READY, |
| DEVICE_STATE_EVENT_NUMBER(pf_open)); |
| |
| if (!dev_ctxt->cctxt_list_head) { |
| VCD_MSG_HIGH("All clients are closed"); |
| |
| dev_ctxt->pending_cmd = VCD_CMD_DEVICE_TERM; |
| |
| return; |
| } |
| |
| if (!dev_ctxt->ddl_cmd_ch_depth || !dev_ctxt->trans_tbl) |
| rc = vcd_setup_with_ddl_capabilities(dev_ctxt); |
| |
| |
| if (VCD_FAILED(rc)) { |
| VCD_MSG_ERROR("rc = 0x%x: Failed " |
| "vcd_setup_with_ddl_capabilities", rc); |
| |
| fail_all_open = true; |
| } |
| |
| client = dev_ctxt->cctxt_list_head; |
| while (client) { |
| if (!fail_all_open) |
| rc = vcd_init_client_context(client); |
| |
| |
| if (!VCD_FAILED(rc)) { |
| container.handle = (void *)client; |
| client->callback(VCD_EVT_RESP_OPEN, VCD_S_SUCCESS, |
| &container, sizeof(container), container.handle, |
| client->client_data); |
| |
| client = client->next; |
| |
| client_inited = true; |
| } else { |
| VCD_MSG_ERROR("rc = 0x%x, Failed: " |
| "vcd_init_client_context", rc); |
| |
| client->callback(VCD_EVT_RESP_OPEN, rc, NULL, 0, 0, |
| client->client_data); |
| |
| tmp_client = client; |
| client = client->next; |
| |
| vcd_destroy_client_context(tmp_client); |
| } |
| } |
| |
| if (!client_inited || fail_all_open) { |
| VCD_MSG_ERROR("All client open requests failed"); |
| |
| dev_ctxt->pending_cmd = VCD_CMD_DEVICE_TERM; |
| } else if (vcd_power_event(dev_ctxt, NULL, VCD_EVT_PWR_DEV_INIT_END)) { |
| VCD_MSG_ERROR("VCD_EVT_PWR_DEV_INIT_END failed"); |
| } |
| } |
| |
| static void vcd_hw_timeout_cmn(struct vcd_drv_ctxt *drv_ctxt, void *user_data) |
| { |
| struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt; |
| VCD_MSG_LOW("vcd_hw_timeout_cmn:"); |
| vcd_device_timer_stop(dev_ctxt); |
| |
| vcd_handle_device_err_fatal(dev_ctxt, NULL); |
| |
| /* Reset HW. */ |
| vcd_reset_device_context(drv_ctxt, DEVICE_STATE_EVENT_NUMBER( |
| pf_timeout)); |
| } |
| |
| static void vcd_dev_enter_null(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code) |
| { |
| VCD_MSG_MED("Entering DEVICE_STATE_NULL on api %d", ev_code); |
| } |
| |
| static void vcd_dev_enter_not_init(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code) |
| { |
| VCD_MSG_MED("Entering DEVICE_STATE_NOT_INIT on api %d", ev_code); |
| } |
| |
| static void vcd_dev_enter_initing(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code) |
| { |
| VCD_MSG_MED("Entering DEVICE_STATE_INITING on api %d", ev_code); |
| } |
| |
| static void vcd_dev_enter_ready(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code) |
| { |
| VCD_MSG_MED("Entering DEVICE_STATE_READY on api %d", ev_code); |
| } |
| |
| static void vcd_dev_enter_invalid(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code) |
| { |
| VCD_MSG_MED("Entering DEVICE_STATE_INVALID on api %d", ev_code); |
| } |
| |
| static void vcd_dev_exit_null(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code) |
| { |
| VCD_MSG_MED("Exiting DEVICE_STATE_NULL on api %d", ev_code); |
| } |
| |
| static void vcd_dev_exit_not_init(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code) |
| { |
| VCD_MSG_MED("Exiting DEVICE_STATE_NOT_INIT on api %d", ev_code); |
| } |
| |
| static void vcd_dev_exit_initing(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code) |
| { |
| VCD_MSG_MED("Exiting DEVICE_STATE_INITING on api %d", ev_code); |
| } |
| |
| static void vcd_dev_exit_ready(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code) |
| { |
| VCD_MSG_MED("Exiting DEVICE_STATE_READY on api %d", ev_code); |
| } |
| |
| static void vcd_dev_exit_invalid(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code) |
| { |
| VCD_MSG_MED("Exiting DEVICE_STATE_INVALID on api %d", ev_code); |
| } |
| |
| static const struct vcd_dev_state_table vcd_dev_table_null = { |
| { |
| vcd_init_in_null, |
| NULL, |
| NULL, |
| NULL, |
| NULL, |
| NULL, |
| NULL, |
| NULL, |
| }, |
| vcd_dev_enter_null, |
| vcd_dev_exit_null |
| }; |
| |
| static const struct vcd_dev_state_table vcd_dev_table_not_init = { |
| { |
| vcd_init_in_not_init, |
| vcd_term_in_not_init, |
| vcd_open_in_not_init, |
| NULL, |
| NULL, |
| NULL, |
| NULL, |
| NULL, |
| }, |
| vcd_dev_enter_not_init, |
| vcd_dev_exit_not_init |
| }; |
| |
| static const struct vcd_dev_state_table vcd_dev_table_initing = { |
| { |
| vcd_init_in_initing, |
| vcd_term_in_initing, |
| vcd_open_in_initing, |
| NULL, |
| NULL, |
| NULL, |
| vcd_dev_cb_in_initing, |
| vcd_hw_timeout_cmn, |
| }, |
| vcd_dev_enter_initing, |
| vcd_dev_exit_initing |
| }; |
| |
| static const struct vcd_dev_state_table vcd_dev_table_ready = { |
| { |
| vcd_init_in_ready, |
| vcd_term_in_ready, |
| vcd_open_in_ready, |
| vcd_close_in_ready, |
| vcd_resume_in_ready, |
| vcd_set_dev_pwr_in_ready, |
| NULL, |
| vcd_hw_timeout_cmn, |
| }, |
| vcd_dev_enter_ready, |
| vcd_dev_exit_ready |
| }; |
| |
| static const struct vcd_dev_state_table vcd_dev_table_in_invalid = { |
| { |
| NULL, |
| vcd_term_in_invalid, |
| NULL, |
| vcd_close_in_dev_invalid, |
| NULL, |
| NULL, |
| NULL, |
| NULL, |
| }, |
| vcd_dev_enter_invalid, |
| vcd_dev_exit_invalid |
| }; |
| |
| static const struct vcd_dev_state_table *vcd_dev_state_table[] = { |
| &vcd_dev_table_null, |
| &vcd_dev_table_not_init, |
| &vcd_dev_table_initing, |
| &vcd_dev_table_ready, |
| &vcd_dev_table_in_invalid |
| }; |