/* * Samsung Exynos SoC series VPU driver * * Copyright (c) 2015 Samsung Electronics Co., Ltd * * This program 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. */ #include #include #include #include #include #include "vpu-framemgr.h" #include "vpu-device.h" #include "vpu-graphmgr.h" #define TIMER_CHECK_POINT(v) graphmgr->tick_pos = v #define GRAPH_CHECK_POINT(v) graphmgr->sched_pos = v static void __vpu_gframe_s_free(struct vpu_graphmgr *graphmgr, struct vpu_gframe *gframe) { BUG_ON(!graphmgr); BUG_ON(!gframe); gframe->state = VPU_GFRAME_STATE_FREE; list_add_tail(&gframe->list, &graphmgr->gfre_list); graphmgr->gfre_cnt++; } static void __vpu_gframe_free_head(struct vpu_graphmgr *graphmgr, struct vpu_gframe **gframe) { BUG_ON(!graphmgr); BUG_ON(!gframe); if (graphmgr->gfre_cnt) *gframe = container_of(graphmgr->gfre_list.next, struct vpu_gframe, list); else *gframe = NULL; } static void __vpu_gframe_s_ready(struct vpu_graphmgr *graphmgr, struct vpu_gframe *gframe) { BUG_ON(!graphmgr); BUG_ON(!gframe); gframe->state = VPU_GFRAME_STATE_FREE; list_add_tail(&gframe->list, &graphmgr->grdy_list); graphmgr->grdy_cnt++; } static void __vpu_gframe_g_ready(struct vpu_graphmgr *graphmgr, struct vpu_gframe **gframe) { if (graphmgr->grdy_cnt && (*gframe = container_of(graphmgr->grdy_list.next, struct vpu_gframe, list))) { list_del(&(*gframe)->list); graphmgr->grdy_cnt--; (*gframe)->state = VPU_FRAME_STATE_INVALID; } else { *gframe = NULL; } } static void __vpu_gframe_s_request(struct vpu_graphmgr *graphmgr, struct vpu_gframe *prev, struct vpu_gframe *gframe, struct vpu_gframe *next) { gframe->state = VPU_GFRAME_STATE_REQUEST; if (prev && next) { next->list.prev = &gframe->list; gframe->list.next = &next->list; gframe->list.prev = &prev->list; prev->list.next = &gframe->list; } else if (prev) { list_add_tail(&gframe->list, &graphmgr->greq_list); } else { list_add(&gframe->list, &graphmgr->greq_list); } graphmgr->greq_cnt++; } static void __vpu_gframe_s_alloc(struct vpu_graphmgr *graphmgr, struct vpu_gframe *gframe) { gframe->state = VPU_GFRAME_STATE_ALLOC; list_add_tail(&gframe->list, &graphmgr->galc_list); graphmgr->galc_cnt++; } void __vpu_gframe_s_process(struct vpu_graphmgr *graphmgr, struct vpu_gframe *gframe) { BUG_ON(!graphmgr); BUG_ON(!gframe); gframe->state = VPU_GFRAME_STATE_PROCESS; list_add_tail(&gframe->list, &graphmgr->gpro_list); graphmgr->gpro_cnt++; } static void __vpu_gframe_s_complete(struct vpu_graphmgr *graphmgr, struct vpu_gframe *gframe) { BUG_ON(!graphmgr); BUG_ON(!gframe); gframe->state = VPU_GFRAME_STATE_COMPLETE; list_add_tail(&gframe->list, &graphmgr->gcom_list); graphmgr->gcom_cnt++; } void __vpu_gframe_trans_fre_to_rdy(struct vpu_graphmgr *graphmgr, struct vpu_gframe *gframe) { BUG_ON(!graphmgr); BUG_ON(!gframe); BUG_ON(!graphmgr->gfre_cnt); BUG_ON(gframe->state != VPU_GFRAME_STATE_FREE); list_del(&gframe->list); graphmgr->gfre_cnt--; __vpu_gframe_s_ready(graphmgr, gframe); } void __vpu_gframe_trans_rdy_to_fre(struct vpu_graphmgr *graphmgr, struct vpu_gframe *gframe) { BUG_ON(!graphmgr); BUG_ON(!gframe); BUG_ON(!graphmgr->grdy_cnt); BUG_ON(gframe->state != VPU_GFRAME_STATE_READY); list_del(&gframe->list); graphmgr->grdy_cnt--; __vpu_gframe_s_free(graphmgr, gframe); } void __vpu_gframe_trans_req_to_alc(struct vpu_graphmgr *graphmgr, struct vpu_gframe *gframe) { BUG_ON(!graphmgr); BUG_ON(!gframe); BUG_ON(!graphmgr->greq_cnt); BUG_ON(gframe->state != VPU_GFRAME_STATE_REQUEST); list_del(&gframe->list); graphmgr->greq_cnt--; __vpu_gframe_s_alloc(graphmgr, gframe); } void __vpu_gframe_trans_req_to_pro(struct vpu_graphmgr *graphmgr, struct vpu_gframe *gframe) { BUG_ON(!graphmgr); BUG_ON(!gframe); BUG_ON(!graphmgr->greq_cnt); BUG_ON(gframe->state != VPU_GFRAME_STATE_REQUEST); list_del(&gframe->list); graphmgr->greq_cnt--; __vpu_gframe_s_process(graphmgr, gframe); } void __vpu_gframe_trans_req_to_fre(struct vpu_graphmgr *graphmgr, struct vpu_gframe *gframe) { BUG_ON(!graphmgr); BUG_ON(!gframe); BUG_ON(!graphmgr->greq_cnt); BUG_ON(gframe->state != VPU_GFRAME_STATE_REQUEST); list_del(&gframe->list); graphmgr->greq_cnt--; __vpu_gframe_s_free(graphmgr, gframe); } void __vpu_gframe_trans_alc_to_pro(struct vpu_graphmgr *graphmgr, struct vpu_gframe *gframe) { BUG_ON(!graphmgr); BUG_ON(!gframe); BUG_ON(!graphmgr->galc_cnt); BUG_ON(gframe->state != VPU_GFRAME_STATE_ALLOC); list_del(&gframe->list); graphmgr->galc_cnt--; __vpu_gframe_s_process(graphmgr, gframe); } void __vpu_gframe_trans_alc_to_fre(struct vpu_graphmgr *graphmgr, struct vpu_gframe *gframe) { BUG_ON(!graphmgr); BUG_ON(!gframe); BUG_ON(!graphmgr->galc_cnt); BUG_ON(gframe->state != VPU_GFRAME_STATE_ALLOC); list_del(&gframe->list); graphmgr->galc_cnt--; __vpu_gframe_s_free(graphmgr, gframe); } static void __vpu_gframe_trans_pro_to_com(struct vpu_graphmgr *graphmgr, struct vpu_gframe *gframe) { BUG_ON(!graphmgr); BUG_ON(!gframe); BUG_ON(!graphmgr->gpro_cnt); BUG_ON(gframe->state != VPU_GFRAME_STATE_PROCESS); list_del(&gframe->list); graphmgr->gpro_cnt--; __vpu_gframe_s_complete(graphmgr, gframe); } void __vpu_gframe_trans_pro_to_fre(struct vpu_graphmgr *graphmgr, struct vpu_gframe *gframe) { BUG_ON(!graphmgr); BUG_ON(!gframe); BUG_ON(!graphmgr->gpro_cnt); BUG_ON(gframe->state != VPU_GFRAME_STATE_PROCESS); list_del(&gframe->list); graphmgr->gpro_cnt--; __vpu_gframe_s_free(graphmgr, gframe); } static void __vpu_gframe_trans_com_to_fre(struct vpu_graphmgr *graphmgr, struct vpu_gframe *gframe) { BUG_ON(!graphmgr); BUG_ON(!gframe); BUG_ON(!graphmgr->gcom_cnt); BUG_ON(gframe->state != VPU_GFRAME_STATE_COMPLETE); list_del(&gframe->list); graphmgr->gcom_cnt--; __vpu_gframe_s_free(graphmgr, gframe); } void vpu_gframe_print(struct vpu_graphmgr *graphmgr) { DLOG_INIT(); struct vpu_gframe *gframe, *gtemp; BUG_ON(!graphmgr); DLOG("READY LIST(%d) :", graphmgr->grdy_cnt); list_for_each_entry_safe(gframe, gtemp, &graphmgr->grdy_list, list) { BUG_ON(!gframe->graph); BUG_ON(!gframe->frame); DLOG(" %2d(%d, %d)", gframe->graph->id, gframe->index, gframe->frame->id); } vpu_info("%s\n", DLOG_OUT()); DLOG("REQUEST LIST(%d) :", graphmgr->greq_cnt); list_for_each_entry_safe(gframe, gtemp, &graphmgr->greq_list, list) { BUG_ON(!gframe->graph); BUG_ON(!gframe->frame); DLOG(" %2d(%d, %d)", gframe->graph->id, gframe->index, gframe->frame->id); } vpu_info("%s\n", DLOG_OUT()); DLOG("ALLOC LIST(%d) :", graphmgr->galc_cnt); list_for_each_entry_safe(gframe, gtemp, &graphmgr->gpro_list, list) { BUG_ON(!gframe->graph); BUG_ON(!gframe->frame); DLOG(" %2d(%d, %d)", gframe->graph->id, gframe->index, gframe->frame->id); } vpu_info("%s\n", DLOG_OUT()); DLOG("PROCESS LIST(%d) :", graphmgr->gpro_cnt); list_for_each_entry_safe(gframe, gtemp, &graphmgr->gpro_list, list) { BUG_ON(!gframe->graph); BUG_ON(!gframe->frame); DLOG(" %2d(%d, %d)", gframe->graph->id, gframe->index, gframe->frame->id); } vpu_info("%s\n", DLOG_OUT()); DLOG("COMPLETE LIST(%d) :", graphmgr->gcom_cnt); list_for_each_entry_safe(gframe, gtemp, &graphmgr->gcom_list, list) { BUG_ON(!gframe->graph); BUG_ON(!gframe->frame); DLOG(" %2d(%d, %d)", gframe->graph->id, gframe->index, gframe->frame->id); } vpu_info("%s\n", DLOG_OUT()); } static int __vpu_itf_enum(struct vpu_interface *interface, struct vpu_graph *graph) { int ret = 0; unsigned long flags; struct vpu_framemgr *iframemgr; struct vpu_frame *iframe; BUG_ON(!interface); BUG_ON(!graph); iframemgr = &interface->framemgr; ret = vpu_hw_enum(interface); if (ret) { vpu_err("vpu_hw_enum is fail(%d)\n", ret); goto p_err; } framemgr_e_barrier_irqs(iframemgr, 0, flags); vpu_frame_pick_fre_to_req(iframemgr, &iframe); framemgr_x_barrier_irqr(iframemgr, 0, flags); if (!iframe) { vpu_err("iframe is NULL\n"); ret = -ENOMEM; goto p_err; } iframe->id = 0; iframe->lock = &graph->local_lock; iframe->findex = VPU_MAX_FRAME; iframe->gindex = VPU_MAX_GFRAME; iframe->message = VPU_FRAME_INIT; iframe->param0 = 0; iframe->param1 = graph->id; iframe->param2 = 0; iframe->param3 = 0; ret = vpu_hw_init(interface, iframe); if (ret) { vpu_err("vpu_hw_init is fail(%d)\n", ret); goto p_err; } p_err: return ret; } static int __vpu_itf_create(struct vpu_interface *interface, struct vpu_graph *graph) { int ret = 0; unsigned long flags; struct vpu_framemgr *iframemgr; struct vpu_frame *iframe; struct vpuo_pu *pu, *temp; struct vpu_mark_for_invoke_ext_mem_vec_ds mem_mark; u32 i; BUG_ON(!interface); BUG_ON(!graph); iframemgr = &interface->framemgr; mem_mark.num_of_buffers = 0; framemgr_e_barrier_irqs(iframemgr, 0, flags); vpu_frame_pick_fre_to_req(iframemgr, &iframe); framemgr_x_barrier_irqr(iframemgr, 0, flags); if (!iframe) { vpu_err("iframe is NULL\n"); ret = -ENOMEM; goto p_err; } #ifdef DBG_TIMEMEASURE vpu_get_timestamp(&iframe->time[VPU_TMP_REQUEST]); #endif if (test_bit(VS4L_GRAPH_FLAG_PRIMITIVE, &graph->flags)) { for (i = 0; i < graph->iobuffer_cnt; ++i) { mem_mark.external_mem_index[i] = graph->iobuffer_idx[i]; mem_mark.num_of_buffers++; } goto p_skip_primitive; } list_for_each_entry_safe(pu, temp, &graph->inleaf_list, gleaf_entry) { for (i = 0; i < pu->buffer_cnt; ++i) { if (!pu->buffer_ptr[i]) continue; if (mem_mark.num_of_buffers >= VPUL_MAX_MAPS_DESC) { vpu_err("mem_mark.num_of_buffers is over1(%d)\n", mem_mark.num_of_buffers); break; } if (!pu->buffer_shm[i]) { mem_mark.external_mem_index[mem_mark.num_of_buffers] = pu->buffer_idx[i]; mem_mark.num_of_buffers++; } } } list_for_each_entry_safe(pu, temp, &graph->otleaf_list, gleaf_entry) { for (i = 0; i < pu->buffer_cnt; ++i) { if (!pu->buffer_ptr[i]) continue; if (mem_mark.num_of_buffers >= VPUL_MAX_MAPS_DESC) { vpu_err("mem_mark.num_of_buffers is over2(%d)\n", mem_mark.num_of_buffers); break; } if (!pu->buffer_shm[i]) { mem_mark.external_mem_index[mem_mark.num_of_buffers] = pu->buffer_idx[i]; mem_mark.num_of_buffers++; } } } p_skip_primitive: iframe->id = 0; iframe->lock = &graph->local_lock; iframe->findex = VPU_MAX_FRAME; iframe->gindex = VPU_MAX_GFRAME; iframe->message = VPU_FRAME_CREATE; iframe->param0 = (ulong)graph->desc_mtask; iframe->param1 = graph->id; iframe->param2 = (ulong)&mem_mark; iframe->param3 = 0; ret = vpu_hw_create(interface, iframe); if (ret) { vpu_err("vpu_hw_create is fail(%d)\n", ret); goto p_err; } #ifdef DBG_TIMEMEASURE vpu_iinfo("[TM] C : %ldus, %ldus\n", graph, VPU_TIME_IN_US(iframe->time[VPU_TMP_PROCESS]) - VPU_TIME_IN_US(iframe->time[VPU_TMP_REQUEST]), VPU_TIME_IN_US(iframe->time[VPU_TMP_DONE]) - VPU_TIME_IN_US(iframe->time[VPU_TMP_PROCESS])); #endif p_err: return ret; } static int __vpu_itf_destroy(struct vpu_interface *interface, struct vpu_graph *graph) { int ret = 0; unsigned long flags; struct vpu_framemgr *iframemgr; struct vpu_frame *iframe; BUG_ON(!interface); BUG_ON(!graph); iframemgr = &interface->framemgr; framemgr_e_barrier_irqs(iframemgr, 0, flags); vpu_frame_pick_fre_to_req(iframemgr, &iframe); framemgr_x_barrier_irqr(iframemgr, 0, flags); if (!iframe) { vpu_err("iframe is NULL\n"); ret = -ENOMEM; goto p_err; } #ifdef DBG_TIMEMEASURE vpu_get_timestamp(&iframe->time[VPU_TMP_REQUEST]); #endif iframe->id = 0; iframe->lock = &graph->local_lock; iframe->findex = VPU_MAX_FRAME; iframe->gindex = VPU_MAX_GFRAME; iframe->message = VPU_FRAME_DESTROY; iframe->param0 = (ulong)graph->desc_mtask; iframe->param1 = graph->id; iframe->param2 = 0; iframe->param3 = 0; ret = vpu_hw_destroy(interface, iframe); if (ret) { vpu_err("vpu_hw_destory is fail(%d)\n", ret); goto p_err; } #ifdef DBG_TIMEMEASURE vpu_iinfo("[TM] D : %ldus, %ldus\n", graph, VPU_TIME_IN_US(iframe->time[VPU_TMP_PROCESS]) - VPU_TIME_IN_US(iframe->time[VPU_TMP_REQUEST]), VPU_TIME_IN_US(iframe->time[VPU_TMP_DONE]) - VPU_TIME_IN_US(iframe->time[VPU_TMP_PROCESS])); #endif p_err: return ret; } static int __vpu_itf_allocate(struct vpu_interface *interface, struct vpu_graph *graph) { int ret = 0; unsigned long flags; struct vpu_framemgr *iframemgr; struct vpu_frame *iframe; BUG_ON(!interface); BUG_ON(!graph); iframemgr = &interface->framemgr; framemgr_e_barrier_irqs(iframemgr, 0, flags); vpu_frame_pick_fre_to_req(iframemgr, &iframe); framemgr_x_barrier_irqr(iframemgr, 0, flags); if (!iframe) { vpu_err("iframe is NULL\n"); ret = -ENOMEM; goto p_err; } #ifdef DBG_TIMEMEASURE vpu_get_timestamp(&iframe->time[VPU_TMP_REQUEST]); #endif iframe->id = 0; iframe->lock = &graph->local_lock; iframe->findex = VPU_MAX_FRAME; iframe->gindex = VPU_MAX_GFRAME; iframe->message = VPU_FRAME_ALLOCATE; iframe->param0 = (ulong)graph->desc_mtask; iframe->param1 = graph->id; iframe->param2 = 0; iframe->param3 = 0; ret = vpu_hw_allocate(interface, iframe); if (ret) { vpu_err("vpu_hw_allocate is fail(%d)\n", ret); goto p_err; } #ifdef DBG_TIMEMEASURE vpu_iinfo("[TM] A : %ldus, %ldus\n", graph, VPU_TIME_IN_US(iframe->time[VPU_TMP_PROCESS]) - VPU_TIME_IN_US(iframe->time[VPU_TMP_REQUEST]), VPU_TIME_IN_US(iframe->time[VPU_TMP_DONE]) - VPU_TIME_IN_US(iframe->time[VPU_TMP_PROCESS])); #endif p_err: return ret; } static int __vpu_itf_process(struct vpu_interface *interface, struct vpu_graph *graph, struct vpu_frame *frame) { int ret = 0; unsigned long flags; struct vpu_framemgr *iframemgr; struct vpu_frame *iframe; struct vpuo_pu *pu, *temp; struct vpu_invoke_external_mem_vector_ds mvectors; union vpul_pu_parameters *update_array; u32 i; BUG_ON(!interface); BUG_ON(!graph); BUG_ON(!frame); iframemgr = &interface->framemgr; mvectors.num_of_buffers = 0; update_array = NULL; framemgr_e_barrier_irqs(iframemgr, 0, flags); vpu_frame_pick_fre_to_req(iframemgr, &iframe); framemgr_x_barrier_irqr(iframemgr, 0, flags); if (!iframe) { vpu_err("iframe is NULL\n"); ret = -ENOMEM; goto p_err; } if (test_bit(VS4L_GRAPH_FLAG_PRIMITIVE, &graph->flags)) { for (i = 0; i < graph->iobuffer_cnt; ++i) { mvectors.addresses_vector[i] = graph->iobuffer_dat[i]; mvectors.num_of_buffers++; } goto p_skip_primitive; } list_for_each_entry_safe(pu, temp, &graph->inleaf_list, gleaf_entry) { for (i = 0; i < pu->buffer_cnt; ++i) { if (!pu->buffer_ptr[i]) continue; if (mvectors.num_of_buffers >= VPUL_MAX_MAPS_DESC) { vpu_err("mvectors.num_of_buffers is over1(%d)\n", mvectors.num_of_buffers); break; } if (!pu->buffer_shm[i]) { mvectors.addresses_vector[mvectors.num_of_buffers] = *pu->buffer_ptr[i]; mvectors.num_of_buffers++; } } } list_for_each_entry_safe(pu, temp, &graph->otleaf_list, gleaf_entry) { for (i = 0; i < pu->buffer_cnt; ++i) { if (!pu->buffer_ptr[i]) continue; if (mvectors.num_of_buffers >= VPUL_MAX_MAPS_DESC) { vpu_err("mvectors.num_of_buffers is over2(%d)\n", mvectors.num_of_buffers); break; } if (!pu->buffer_shm[i]){ mvectors.addresses_vector[mvectors.num_of_buffers] = *pu->buffer_ptr[i]; mvectors.num_of_buffers++; } } } p_skip_primitive: if (test_bit(VPU_GRAPH_FLAG_UPDATE_PARAM, &graph->flags)) { ret = CALL_GOPS(graph, update_param, frame); if (ret) { vpu_err("GOPS(update_param) is fail(%d)\n", ret); goto p_err; } update_array = graph->update_array; clear_bit(VPU_GRAPH_FLAG_UPDATE_PARAM, &graph->flags); } iframe->id = frame->id; iframe->lock = &graph->local_lock; iframe->findex = frame->index; iframe->gindex = frame->gindex; iframe->message = VPU_FRAME_PROCESS; iframe->param0 = (ulong)graph->desc_mtask; iframe->param1 = graph->id; iframe->param2 = (ulong)&mvectors; /* return : DONE or NDONE */ iframe->param3 = (ulong)update_array; /* return : error code if param2 is NDONE */ iframe->flags = frame->flags; iframe->incl = frame->incl; iframe->otcl = frame->otcl; ret = CALL_GOPS(graph, process, frame); if (ret) { vpu_err("GOPS(process) is fail(%d)\n", ret); goto p_err; } ret = vpu_hw_process(interface, iframe); if (ret) { vpu_err("vpu_hw_process is fail(%d)\n", ret); goto p_err; } p_err: return ret; } static void __vpu_graphmgr_sched(struct vpu_graphmgr *graphmgr) { int ret = 0; struct vpu_graph *graph; struct vpu_frame *frame; struct vpu_gframe *ready, *request, *process, *prev, *next, *temp; struct vpu_interface *interface; struct vpu_resource *resource; interface = graphmgr->interface; resource = graphmgr->resource; ready = NULL; request = NULL; prev = NULL; next = NULL; GRAPH_CHECK_POINT(20); mutex_lock(&graphmgr->glock); /* 1. priority order */ while (1) { __vpu_gframe_g_ready(graphmgr, &ready); if (!ready) break; list_for_each_entry_safe(next, temp, &graphmgr->greq_list, list) { if (ready->priority > next->priority) break; prev = next; next = NULL; } __vpu_gframe_s_request(graphmgr, prev, ready, next); } GRAPH_CHECK_POINT(21); /* 2. resource allocation */ list_for_each_entry_safe(request, temp, &graphmgr->greq_list, list) { graph = request->graph; frame = request->frame; #ifdef VPU_DYNAMIC_RESOURCE ret = CALL_GOPS(graph, get_resource, frame); if (ret) continue; #endif __vpu_gframe_trans_req_to_pro(graphmgr, request); } mutex_unlock(&graphmgr->glock); GRAPH_CHECK_POINT(22); /* 3. process graph */ list_for_each_entry_safe(process, temp, &graphmgr->gpro_list, list) { graph = process->graph; frame = process->frame; ret = __vpu_itf_process(interface, graph, frame); if (ret) { vpu_err("__vpu_itf_process is fail(%d)\n", ret); ret = CALL_GOPS(graph, cancel, frame); if (ret) { vpu_err("CALL_GOPS(cancel) is fail(%d)\n", ret); BUG(); } ret = CALL_GOPS(graph, put_resource, frame); if (ret) { vpu_err("CALL_GOPS(put_resource) is fail(%d)\n", ret); BUG(); } process->graph = NULL; process->frame = NULL; __vpu_gframe_trans_pro_to_fre(graphmgr, process); continue; } __vpu_gframe_trans_pro_to_com(graphmgr, process); } GRAPH_CHECK_POINT(23); graphmgr->sched_cnt++; } static int vpu_time_thread(void *data) { struct vpu_graphmgr *graphmgr = (struct vpu_graphmgr *)data; struct vpu_gframe *request, *temp1, *temp2; struct vpu_gframe *next, *prev; struct vpu_graph *graph; BUG_ON(!graphmgr); while (!kthread_should_stop()) { TIMER_CHECK_POINT(1); msleep(VPU_TIME_TICK); if (!test_bit(VPU_GRAPHMGR_OPEN, &graphmgr->state)) return 0; if (!atomic_read(&graphmgr->periodics)) goto p_noperiodic; mutex_lock(&graphmgr->glock); TIMER_CHECK_POINT(2); list_for_each_entry_safe(request, temp1, &graphmgr->greq_list, list) { graph = request->graph; if (!test_bit(VPU_GRAPH_STATE_START, &graph->state)) { vpu_err("graph %d is NOT start\n", graph->id); BUG(); } if (test_bit(VS4L_GRAPH_FLAG_PERIODIC, &graph->flags)) { if (--request->ticks > 0) continue; TIMER_CHECK_POINT(3); vpu_iinfo("priority changed(%d -> %d)\n", graph, request->priority, VPU_GRAPH_MAX_PRIORITY + 1); request->priority = VPU_GRAPH_MAX_PRIORITY + 1; request->ticks = graph->period_ticks; list_del(&request->list); graphmgr->greq_cnt--; next = NULL; prev = NULL; list_for_each_entry_safe(next, temp2, &graphmgr->greq_list, list) { if (request->priority > next->priority) break; prev = next; next = NULL; } TIMER_CHECK_POINT(4); __vpu_gframe_s_request(graphmgr, prev, request, next); } } mutex_unlock(&graphmgr->glock); p_noperiodic: TIMER_CHECK_POINT(5); graphmgr->tick_cnt++; } return 0; } static void vpu_graph_thread(struct kthread_work *work) { int ret = 0; struct vpu_graphmgr *graphmgr; struct vpu_graph *graph; struct vpu_frame *frame; struct vpu_gframe *gframe, * temp; BUG_ON(!work); frame = container_of(work, struct vpu_frame, work); graph = frame->owner; graphmgr = graph->cookie; GRAPH_CHECK_POINT(1); switch (frame->message) { case VPU_FRAME_REQUEST: GRAPH_CHECK_POINT(2); ret = CALL_GOPS(graph, request, frame); if (ret) { vpu_err("CALL_GOPS(request) is fail(%d)\n", ret); BUG(); } __vpu_gframe_free_head(graphmgr, &gframe); if (!gframe) { vpu_err("gframe is NULL\n"); BUG(); } frame->gindex = gframe->index; gframe->graph = graph; gframe->frame = frame; gframe->priority = graph->priority; gframe->ticks = graph->period_ticks; __vpu_gframe_trans_fre_to_rdy(graphmgr, gframe); GRAPH_CHECK_POINT(3); break; case VPU_CTRL_STOP: GRAPH_CHECK_POINT(4); list_for_each_entry_safe(gframe, temp, &graphmgr->grdy_list, list) { if (gframe->graph->id != graph->id) continue; ret = CALL_GOPS(graph, cancel, gframe->frame); if (ret) { vpu_err("CALL_GOPS(cancel) is fail(%d)\n", ret); BUG(); } gframe->graph = NULL; gframe->frame = NULL; __vpu_gframe_trans_rdy_to_fre(graphmgr, gframe); } GRAPH_CHECK_POINT(5); mutex_lock(&graphmgr->glock); list_for_each_entry_safe(gframe, temp, &graphmgr->greq_list, list) { if (gframe->graph->id != graph->id) continue; ret = CALL_GOPS(graph, cancel, gframe->frame); if (ret) { vpu_err("CALL_GOPS(cancel) is fail(%d)\n", ret); BUG(); } ret = CALL_GOPS(graph, put_resource, gframe->frame); if (ret) { vpu_err("CALL_GOPS(put_resource) is fail(%d)\n", ret); BUG(); } gframe->graph = NULL; gframe->frame = NULL; __vpu_gframe_trans_req_to_fre(graphmgr, gframe); } mutex_unlock(&graphmgr->glock); GRAPH_CHECK_POINT(6); ret = CALL_GOPS(graph, control, frame); if (ret) { vpu_err("CALL_GOPS(control) is fail(%d)\n", ret); BUG(); } return; default: BUG(); break; } GRAPH_CHECK_POINT(7); __vpu_graphmgr_sched(graphmgr); } static void vpu_interface_thread(struct kthread_work *work) { int ret = 0; u32 frame_index; u32 gframe_index; unsigned long flag; struct vpu_graphmgr *graphmgr; struct vpu_graph *graph; struct vpu_framemgr *iframemgr; struct vpu_frame *frame, *iframe; struct vpu_gframe *gframe; struct vpu_interface *interface; BUG_ON(!work); iframe = container_of(work, struct vpu_frame, work); interface = iframe->owner; iframemgr = &interface->framemgr; graphmgr = interface->cookie; GRAPH_CHECK_POINT(10); switch (iframe->message) { case VPU_FRAME_ALLOCATE: GRAPH_CHECK_POINT(11); frame_index = iframe->findex; gframe_index = iframe->gindex; if (gframe_index >= VPU_MAX_GFRAME) { vpu_err("gframe index(%d) is invalid\n", gframe_index); BUG(); } if (frame_index >= VPU_MAX_FRAME) { vpu_err("frame index(%d) is invalid\n", frame_index); BUG(); } gframe = &graphmgr->gframe[gframe_index]; if (gframe->state != VPU_GFRAME_STATE_ALLOC) { vpu_err("gframe state is invalid(%d)\n", gframe->state); vpu_gframe_print(graphmgr); BUG(); } graph = gframe->graph; if (!graph) { vpu_err("graph is NULL(%d)\n", gframe_index); BUG(); } frame = gframe->frame; if (!frame) { vpu_err("frame is NULL(%d)\n", gframe_index); BUG(); } frame->message = iframe->message; frame->param0 = iframe->param2; frame->param1 = iframe->param3; /* return status check */ if (frame->param0) { vpu_err("allocation is fail(%ld, %ld)\n", frame->param0, frame->param1); ret = CALL_GOPS(graph, cancel, frame); if (ret) { vpu_err("CALL_GOPS(cancel) is fail(%d)\n", ret); BUG(); } ret = CALL_GOPS(graph, put_resource, frame); if (ret) { vpu_err("CALL_GOPS(put_resource) is fail(%d)\n", ret); BUG(); } /* gframe cleanup */ mutex_lock(&graphmgr->glock); gframe->graph = NULL; gframe->frame = NULL; __vpu_gframe_trans_alc_to_fre(graphmgr, gframe); mutex_unlock(&graphmgr->glock); } else { /* gframe transition */ mutex_lock(&graphmgr->glock); __vpu_gframe_trans_alc_to_pro(graphmgr, gframe); mutex_unlock(&graphmgr->glock); } /* iframe cleanup */ framemgr_e_barrier_irqs(iframemgr, 0, flag); vpu_frame_trans_com_to_fre(iframemgr, iframe); framemgr_x_barrier_irqr(iframemgr, 0, flag); break; case VPU_FRAME_PROCESS: GRAPH_CHECK_POINT(12); frame_index = iframe->findex; gframe_index = iframe->gindex; if (gframe_index >= VPU_MAX_GFRAME) { vpu_err("gframe index(%d) is invalid\n", gframe_index); BUG(); } if (frame_index >= VPU_MAX_FRAME) { vpu_err("frame index(%d) is invalid\n", frame_index); BUG(); } gframe = &graphmgr->gframe[gframe_index]; if (gframe->state != VPU_GFRAME_STATE_COMPLETE) { vpu_err("gframe state is invalid(%d)\n", gframe->state); vpu_gframe_print(graphmgr); BUG(); } graph = gframe->graph; if (!graph) { vpu_err("graph is NULL(%d)\n", gframe_index); BUG(); } frame = gframe->frame; if (!frame) { vpu_err("frame is NULL(%d)\n", gframe_index); BUG(); } frame->message = iframe->message; frame->gindex = VPU_MAX_GFRAME; frame->param0 = iframe->param2; frame->param1 = iframe->param3; #ifdef VPU_DYNAMIC_RESOURCE ret = CALL_GOPS(graph, put_resource, frame); if (ret) { vpu_err("CALL_GOPS(put_resource) is fail(%d)\n", ret); BUG(); } #endif ret = CALL_GOPS(graph, done, frame); if (ret) { vpu_err("CALL_GOPS(done) is fail(%d)\n", ret); BUG(); } /* gframe cleanup */ gframe->graph = NULL; gframe->frame = NULL; gframe->ticks = 0; __vpu_gframe_trans_com_to_fre(graphmgr, gframe); /* iframe cleanup */ framemgr_e_barrier_irqs(iframemgr, 0, flag); vpu_frame_trans_com_to_fre(iframemgr, iframe); framemgr_x_barrier_irqr(iframemgr, 0, flag); break; default: BUG(); break; } GRAPH_CHECK_POINT(13); __vpu_graphmgr_sched(graphmgr); } int vpu_graphmgr_grp_register(struct vpu_graphmgr *graphmgr, struct vpu_graph *graph) { int ret = 0; u32 index; BUG_ON(!graphmgr); BUG_ON(!graph); mutex_lock(&graphmgr->mlock); for (index = 0; index < VPU_MAX_GRAPH; index++) { if (!graphmgr->graph[index]) { graphmgr->graph[index] = graph; graph->id = index; break; } } mutex_unlock(&graphmgr->mlock); if (index >= VPU_MAX_GRAPH) { vpu_err("graph slot is lack\n"); ret = -EINVAL; goto p_err; } init_kthread_work(&graph->control.work, vpu_graph_thread); for (index = 0; index < VPU_MAX_FRAME; ++index) init_kthread_work(&graph->framemgr.frame[index].work, vpu_graph_thread); graph->global_lock = &graphmgr->mlock; p_err: return ret; } int vpu_graphmgr_grp_unregister(struct vpu_graphmgr *graphmgr, struct vpu_graph *graph) { int ret = 0; BUG_ON(!graphmgr); BUG_ON(!graph); mutex_lock(&graphmgr->mlock); graphmgr->graph[graph->id] = NULL; mutex_unlock(&graphmgr->mlock); return ret; } int vpu_graphmgr_grp_start(struct vpu_graphmgr *graphmgr, struct vpu_graph *graph) { int ret = 0; if (test_bit(VS4L_GRAPH_FLAG_PERIODIC, &graph->flags)) atomic_inc(&graphmgr->periodics); mutex_lock(&graphmgr->mlock); if (test_bit(VPU_GRAPHMGR_ENUM, &graphmgr->state)) { mutex_unlock(&graphmgr->mlock); goto p_skip_enum; } ret = __vpu_itf_enum(graphmgr->interface, graph); if (ret) { mutex_unlock(&graphmgr->mlock); vpu_err("__vpu_itf_enum is fail(%d)\n", ret); goto p_err; } set_bit(VPU_GRAPHMGR_ENUM, &graphmgr->state); mutex_unlock(&graphmgr->mlock); p_skip_enum: ret = __vpu_itf_create(graphmgr->interface, graph); if (ret) { vpu_err("__vpu_itf_create is fail(%d)\n", ret); goto p_err; } ret = __vpu_itf_allocate(graphmgr->interface, graph); if (ret) { vpu_err("__vpu_itf_allocate is fail(%d)\n", ret); goto p_err; } p_err: return ret; } int vpu_graphmgr_grp_stop(struct vpu_graphmgr *graphmgr, struct vpu_graph *graph) { int ret = 0; if (test_bit(VS4L_GRAPH_FLAG_PERIODIC, &graph->flags)) atomic_dec(&graphmgr->periodics); ret = __vpu_itf_destroy(graphmgr->interface, graph); if (ret) { vpu_err("__vpu_itf_destroy is fail(%d)\n", ret); goto p_err; } p_err: return ret; } int vpu_graphmgr_itf_register(struct vpu_graphmgr *graphmgr, struct vpu_interface *interface) { int ret = 0; u32 index; BUG_ON(!graphmgr); BUG_ON(!interface); graphmgr->interface = interface; for (index = 0; index < VPU_MAX_FRAME; ++index) init_kthread_work(&interface->framemgr.frame[index].work, vpu_interface_thread); return ret; } int vpu_graphmgr_itf_unregister(struct vpu_graphmgr *graphmgr, struct vpu_interface *interface) { int ret = 0; BUG_ON(!graphmgr); BUG_ON(!interface); graphmgr->interface = NULL; return ret; } void vpu_graphmgr_queue(struct vpu_graphmgr *graphmgr, struct vpu_frame *frame) { BUG_ON(!graphmgr); BUG_ON(!frame); queue_kthread_work(&graphmgr->worker, &frame->work); } int vpu_graphmgr_probe(struct vpu_graphmgr *graphmgr, struct vpu_resource *resource) { int ret = 0; u32 index; struct vpu_device *device = container_of(graphmgr, struct vpu_device, graphmgr); BUG_ON(!graphmgr); BUG_ON(!device); graphmgr->tick_cnt = 0; graphmgr->tick_pos = 0; graphmgr->sched_cnt = 0; graphmgr->sched_pos = 0; graphmgr->task_graph = NULL; graphmgr->task_timer = NULL; graphmgr->resource = resource; atomic_set(&graphmgr->periodics, 0); mutex_init(&graphmgr->mlock); mutex_init(&graphmgr->glock); clear_bit(VPU_GRAPHMGR_OPEN, &graphmgr->state); clear_bit(VPU_GRAPHMGR_ENUM, &graphmgr->state); for (index = 0; index < VPU_MAX_GRAPH; ++index) graphmgr->graph[index] = NULL; INIT_LIST_HEAD(&graphmgr->gfre_list); INIT_LIST_HEAD(&graphmgr->grdy_list); INIT_LIST_HEAD(&graphmgr->greq_list); INIT_LIST_HEAD(&graphmgr->galc_list); INIT_LIST_HEAD(&graphmgr->gpro_list); INIT_LIST_HEAD(&graphmgr->gcom_list); graphmgr->gfre_cnt = 0; graphmgr->grdy_cnt = 0; graphmgr->greq_cnt = 0; graphmgr->galc_cnt = 0; graphmgr->gpro_cnt = 0; graphmgr->gcom_cnt = 0; for (index = 0; index < VPU_MAX_GFRAME; ++index) { graphmgr->gframe[index].index = index; graphmgr->gframe[index].graph = NULL; graphmgr->gframe[index].frame = NULL; graphmgr->gframe[index].state = VPU_GFRAME_STATE_INVALID; graphmgr->gframe[index].ticks = 0; __vpu_gframe_s_free(graphmgr, &graphmgr->gframe[index]); } return ret; } int vpu_graphmgr_open(struct vpu_graphmgr *graphmgr) { int ret = 0; char name[30]; struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 }; BUG_ON(!graphmgr); init_kthread_worker(&graphmgr->worker); snprintf(name, sizeof(name), "vpu_graph"); graphmgr->task_graph = kthread_run(kthread_worker_fn, &graphmgr->worker, name); if (IS_ERR_OR_NULL(graphmgr->task_graph)) { vpu_err("kthread_run is fail\n"); ret = -EINVAL; goto p_err; } ret = sched_setscheduler_nocheck(graphmgr->task_graph, SCHED_FIFO, ¶m); if (ret) { vpu_err("sched_setscheduler_nocheck is fail(%d)\n", ret); goto p_err; } snprintf(name, sizeof(name), "vpu_timer"); graphmgr->task_timer = kthread_run(vpu_time_thread, graphmgr, name); if (IS_ERR_OR_NULL(graphmgr->task_timer)) { vpu_err("kthread_run is fail\n"); ret = -EINVAL; goto p_err; } ret = sched_setscheduler_nocheck(graphmgr->task_timer, SCHED_FIFO, ¶m); if (ret) { vpu_err("sched_setscheduler_nocheck is fail(%d)\n", ret); goto p_err; } set_bit(VPU_GRAPHMGR_OPEN, &graphmgr->state); p_err: return ret; } int vpu_graphmgr_close(struct vpu_graphmgr *graphmgr) { int ret = 0; struct vpu_gframe *gframe, *temp; BUG_ON(!graphmgr); if (graphmgr->grdy_cnt > 0) { ret++; vpu_err("ready gframe is NOT empty(%d)\n", graphmgr->grdy_cnt); list_for_each_entry_safe(gframe, temp, &graphmgr->grdy_list, list) { list_del(&gframe->list); graphmgr->grdy_cnt--; __vpu_gframe_s_free(graphmgr, gframe); } } if (graphmgr->greq_cnt > 0) { ret++; vpu_err("request gframe is NOT empty(%d)\n", graphmgr->greq_cnt); list_for_each_entry_safe(gframe, temp, &graphmgr->greq_list, list) { list_del(&gframe->list); graphmgr->greq_cnt--; __vpu_gframe_s_free(graphmgr, gframe); } } if (graphmgr->gpro_cnt > 0) { ret++; vpu_err("process gframe is NOT empty(%d)\n", graphmgr->gpro_cnt); list_for_each_entry_safe(gframe, temp, &graphmgr->gpro_list, list) { list_del(&gframe->list); graphmgr->gpro_cnt--; __vpu_gframe_s_free(graphmgr, gframe); } } kthread_stop(graphmgr->task_timer); kthread_stop(graphmgr->task_graph); clear_bit(VPU_GRAPHMGR_OPEN, &graphmgr->state); clear_bit(VPU_GRAPHMGR_ENUM, &graphmgr->state); return ret; }