| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * Copyright (c) 2019 Amlogic, Inc. All rights reserved. |
| */ |
| |
| #define pr_fmt(fmt) "vout: " fmt |
| |
| /* Linux Headers */ |
| #include <linux/module.h> |
| #include <linux/delay.h> |
| #include <linux/sched.h> |
| #include <linux/err.h> |
| |
| /* Amlogic Headers */ |
| #include <linux/amlogic/media/vpu/vpu.h> |
| #include <linux/amlogic/media/vout/vout_notify.h> |
| |
| /* Local Headers */ |
| #include "vout_func.h" |
| |
| static DEFINE_MUTEX(vout_mutex); |
| |
| static struct vout_module_s vout_module = { |
| .vout_server_list = { |
| &vout_module.vout_server_list, |
| &vout_module.vout_server_list |
| }, |
| .curr_vout_server = NULL, |
| .next_vout_server = NULL, |
| .init_flag = 0, |
| /* vout_fr_policy: |
| * 0: disable |
| * 1: nearby (only for 60->59.94 and 30->29.97) |
| * 2: force (60/50/30/24/59.94/23.97) |
| */ |
| .fr_policy = 0, |
| }; |
| |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| static struct vout_module_s vout2_module = { |
| .vout_server_list = { |
| &vout2_module.vout_server_list, |
| &vout2_module.vout_server_list |
| }, |
| .curr_vout_server = NULL, |
| .next_vout_server = NULL, |
| .init_flag = 0, |
| /* vout_fr_policy: |
| * 0: disable |
| * 1: nearby (only for 60->59.94 and 30->29.97) |
| * 2: force (60/50/30/24/59.94/23.97) |
| */ |
| .fr_policy = 0, |
| }; |
| #endif |
| |
| #ifdef CONFIG_AMLOGIC_VOUT3_SERVE |
| static struct vout_module_s vout3_module = { |
| .vout_server_list = { |
| &vout3_module.vout_server_list, |
| &vout3_module.vout_server_list |
| }, |
| .curr_vout_server = NULL, |
| .next_vout_server = NULL, |
| .init_flag = 0, |
| /* vout_fr_policy: |
| * 0: disable |
| * 1: nearby (only for 60->59.94 and 30->29.97) |
| * 2: force (60/50/30/24/59.94/23.97) |
| */ |
| .fr_policy = 0, |
| }; |
| #endif |
| |
| static struct vinfo_s invalid_vinfo = { |
| .name = "invalid", |
| .mode = VMODE_INVALID, |
| .width = 1920, |
| .height = 1080, |
| .field_height = 1080, |
| .aspect_ratio_num = 16, |
| .aspect_ratio_den = 9, |
| .sync_duration_num = 60, |
| .sync_duration_den = 1, |
| .video_clk = 148500000, |
| .htotal = 2200, |
| .vtotal = 1125, |
| .viu_color_fmt = COLOR_FMT_RGB444, |
| .viu_mux = ((3 << 4) | VIU_MUX_MAX), |
| .vout_device = NULL, |
| }; |
| |
| struct vinfo_s *get_invalid_vinfo(int index, unsigned int flag) |
| { |
| if (flag) { |
| VOUTERR("invalid vinfo%d. current vmode is not supported\n", |
| index); |
| } |
| return &invalid_vinfo; |
| } |
| EXPORT_SYMBOL(get_invalid_vinfo); |
| |
| void vout_trim_string(char *str) |
| { |
| char *start, *end; |
| int len; |
| |
| if (!str) |
| return; |
| |
| len = strlen(str); |
| if ((str[len - 1] == '\n') || (str[len - 1] == '\r')) { |
| len--; |
| str[len] = '\0'; |
| } |
| |
| start = str; |
| end = str + len - 1; |
| while (*start && (*start == ' ')) |
| start++; |
| while (*end && (*end == ' ')) |
| *end-- = '\0'; |
| strcpy(str, start); |
| } |
| EXPORT_SYMBOL(vout_trim_string); |
| |
| struct vout_module_s *vout_func_get_vout_module(void) |
| { |
| return &vout_module; |
| } |
| EXPORT_SYMBOL(vout_func_get_vout_module); |
| |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| struct vout_module_s *vout_func_get_vout2_module(void) |
| { |
| return &vout2_module; |
| } |
| EXPORT_SYMBOL(vout_func_get_vout2_module); |
| #endif |
| |
| #ifdef CONFIG_AMLOGIC_VOUT3_SERVE |
| struct vout_module_s *vout_func_get_vout3_module(void) |
| { |
| return &vout3_module; |
| } |
| EXPORT_SYMBOL(vout_func_get_vout3_module); |
| #endif |
| |
| static inline int vout_func_check_state(int index, unsigned int state, |
| struct vout_server_s *p_server) |
| { |
| if (state & ~(1 << index)) { |
| VOUTERR("vout%d: server %s is activated by another vout\n", |
| index, p_server->name); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| void vout_func_set_state(int index, enum vmode_e mode) |
| { |
| struct vout_server_s *p_server; |
| struct vout_module_s *p_module = NULL; |
| void *data; |
| int state; |
| |
| mutex_lock(&vout_mutex); |
| |
| if (index == 1) |
| p_module = &vout_module; |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| else if (index == 2) |
| p_module = &vout2_module; |
| #endif |
| #ifdef CONFIG_AMLOGIC_VOUT3_SERVE |
| else if (index == 3) |
| p_module = &vout3_module; |
| #endif |
| |
| if (!p_module) { |
| VOUTERR("vout%d: %s: vout_module is NULL\n", index, __func__); |
| mutex_unlock(&vout_mutex); |
| return; |
| } |
| /*if (p_module->next_vout_server) { |
| * pr_info("%s: next: index=%d, server_name=%s, data=%px\n", |
| * __func__, index, p_module->next_vout_server->name, |
| * p_module->next_vout_server->data); |
| *} else { |
| * pr_info("%s: index=%d, next_server is null\n", |
| * __func__, index); |
| *} |
| */ |
| list_for_each_entry(p_server, &p_module->vout_server_list, list) { |
| data = p_server->data; |
| if (p_module->next_vout_server && p_server->name && |
| p_module->next_vout_server->name && |
| (strcmp(p_server->name, p_module->next_vout_server->name) == 0)) { |
| /*pr_info("%s: valid: index=%d, server_name=%s, data=%px\n", |
| * __func__, index, p_server->name, data); |
| */ |
| if (p_server->op.vmode_is_supported(mode, data)) { |
| p_module->curr_vout_server = p_server; |
| p_module->next_vout_server = NULL; |
| if (p_server->op.set_state) |
| p_server->op.set_state(index, data); |
| } |
| } else { |
| if (p_server->op.get_state) { |
| state = p_server->op.get_state(data); |
| if (state & (1 << index)) { |
| if (p_server->op.disable) |
| p_server->op.disable(mode, data); |
| } |
| } |
| if (p_server->op.clr_state) |
| p_server->op.clr_state(index, data); |
| } |
| } |
| |
| mutex_unlock(&vout_mutex); |
| } |
| EXPORT_SYMBOL(vout_func_set_state); |
| |
| void vout_func_update_viu(int index) |
| { |
| struct vinfo_s *vinfo = NULL; |
| struct vout_server_s *p_server; |
| struct vout_module_s *p_module = NULL; |
| unsigned int mux_sel; |
| unsigned int flag = 0; |
| |
| mutex_lock(&vout_mutex); |
| |
| if (index == 1) |
| p_module = &vout_module; |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| else if (index == 2) |
| p_module = &vout2_module; |
| #endif |
| #ifdef CONFIG_AMLOGIC_VOUT3_SERVE |
| else if (index == 3) |
| p_module = &vout3_module; |
| #endif |
| |
| if (!p_module) { |
| VOUTERR("vout%d: %s: vout_module is NULL\n", index, __func__); |
| mutex_unlock(&vout_mutex); |
| return; |
| } |
| p_server = p_module->curr_vout_server; |
| flag = p_module->init_flag; |
| |
| /* |
| *VOUTPR("%s: before: 0x%04x=0x%08x, 0x%04x=0x%08x\n", |
| * __func__, VPU_VIU_VENC_MUX_CTRL, |
| * vout_vcbus_read(VPU_VIU_VENC_MUX_CTRL), |
| * VPU_VENCX_CLK_CTRL, |
| * vout_vcbus_read(VPU_VENCX_CLK_CTRL)); |
| */ |
| |
| if (p_server && p_server->op.get_vinfo) |
| vinfo = p_server->op.get_vinfo(p_server->data); |
| if (!vinfo) |
| vinfo = get_invalid_vinfo(index, flag); |
| |
| mux_sel = vinfo->viu_mux; |
| |
| vout_viu_mux_update(index, mux_sel); |
| |
| mutex_unlock(&vout_mutex); |
| } |
| EXPORT_SYMBOL(vout_func_update_viu); |
| |
| int vout_func_set_vmode(int index, enum vmode_e mode) |
| { |
| int ret = -1; |
| struct vout_module_s *p_module = NULL; |
| void *data; |
| |
| mutex_lock(&vout_mutex); |
| |
| if (index == 1) |
| p_module = &vout_module; |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| else if (index == 2) |
| p_module = &vout2_module; |
| #endif |
| #ifdef CONFIG_AMLOGIC_VOUT3_SERVE |
| else if (index == 3) |
| p_module = &vout3_module; |
| #endif |
| |
| if (!p_module) { |
| VOUTERR("vout%d: %s: vout_module is NULL\n", index, __func__); |
| mutex_unlock(&vout_mutex); |
| return -1; |
| } |
| if (!p_module->curr_vout_server) { |
| VOUTERR("vout%d: %s: current_server is NULL\n", index, __func__); |
| mutex_unlock(&vout_mutex); |
| return -1; |
| } |
| data = p_module->curr_vout_server->data; |
| if (p_module->curr_vout_server->op.set_vmode) |
| ret = p_module->curr_vout_server->op.set_vmode(mode, data); |
| p_module->init_flag = 1; |
| |
| mutex_unlock(&vout_mutex); |
| |
| return ret; |
| } |
| EXPORT_SYMBOL(vout_func_set_vmode); |
| |
| /* |
| * interface export to client who want to set current vmode. |
| */ |
| int vout_func_set_current_vmode(int index, enum vmode_e mode) |
| { |
| vout_func_set_state(index, mode); |
| vout_func_update_viu(index); |
| |
| return vout_func_set_vmode(index, mode); |
| } |
| EXPORT_SYMBOL(vout_func_set_current_vmode); |
| |
| int vout_func_check_same_vmodeattr(int index, char *name) |
| { |
| struct vout_server_s *p_server = NULL; |
| void *data; |
| int ret = 1; |
| |
| mutex_lock(&vout_mutex); |
| |
| if (index == 1) |
| p_server = vout_module.curr_vout_server; |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| else if (index == 2) |
| p_server = vout2_module.curr_vout_server; |
| #endif |
| #ifdef CONFIG_AMLOGIC_VOUT3_SERVE |
| else if (index == 3) |
| p_server = vout3_module.curr_vout_server; |
| #endif |
| |
| if (!IS_ERR_OR_NULL(p_server)) { |
| data = p_server->data; |
| if (p_server->op.check_same_vmodeattr) |
| ret = p_server->op.check_same_vmodeattr(name, data); |
| } |
| |
| mutex_unlock(&vout_mutex); |
| return ret; |
| } |
| |
| /* |
| *interface export to client who want to set current vmode. |
| */ |
| enum vmode_e vout_func_validate_vmode(int index, char *name, unsigned int frac) |
| { |
| enum vmode_e ret = VMODE_MAX; |
| struct vout_server_s *p_server; |
| struct vout_module_s *p_module = NULL; |
| void *data; |
| int state; |
| |
| mutex_lock(&vout_mutex); |
| |
| if (index == 1) |
| p_module = &vout_module; |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| else if (index == 2) |
| p_module = &vout2_module; |
| #endif |
| #ifdef CONFIG_AMLOGIC_VOUT3_SERVE |
| else if (index == 3) |
| p_module = &vout3_module; |
| #endif |
| |
| if (!p_module) { |
| VOUTERR("vout%d: %s: vout_module is NULL\n", index, __func__); |
| mutex_unlock(&vout_mutex); |
| return VMODE_MAX; |
| } |
| p_module->next_vout_server = NULL; |
| list_for_each_entry(p_server, &p_module->vout_server_list, list) { |
| /*pr_info("%s: start: index=%d, server_name=%s, data=%px\n", |
| * __func__, index, p_server->name, |
| * p_server->data); |
| */ |
| data = p_server->data; |
| /* check state for another vout */ |
| if (p_server->op.get_state) { |
| state = p_server->op.get_state(data); |
| if (vout_func_check_state(index, state, p_server)) { |
| /* pr_info("check state: %d\n", state);*/ |
| continue; |
| } |
| } |
| if (p_server->op.validate_vmode) { |
| ret = p_server->op.validate_vmode(name, frac, data); |
| if (ret != VMODE_MAX) { /* valid vmode find. */ |
| p_module->next_vout_server = p_server; |
| /* pr_info("%s: next: index=%d, server_name=%s, data=%px\n", |
| * __func__, index, |
| * p_module->next_vout_server->name, |
| * p_module->next_vout_server->data); |
| */ |
| break; |
| } |
| } |
| } |
| mutex_unlock(&vout_mutex); |
| |
| return ret; |
| } |
| EXPORT_SYMBOL(vout_func_validate_vmode); |
| |
| int vout_func_get_disp_cap(int index, char *buf) |
| { |
| struct vout_server_s *p_server; |
| struct vout_module_s *p_module = NULL; |
| void *data; |
| int state, len; |
| |
| mutex_lock(&vout_mutex); |
| |
| if (index == 1) |
| p_module = &vout_module; |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| else if (index == 2) |
| p_module = &vout2_module; |
| #endif |
| #ifdef CONFIG_AMLOGIC_VOUT3_SERVE |
| else if (index == 3) |
| p_module = &vout3_module; |
| #endif |
| |
| if (!p_module) { |
| len = sprintf(buf, "vout%d: %s: vout_module is NULL\n", |
| index, __func__); |
| mutex_unlock(&vout_mutex); |
| return len; |
| } |
| |
| len = 0; |
| list_for_each_entry(p_server, &p_module->vout_server_list, list) { |
| data = p_server->data; |
| if (p_server->op.get_state) { |
| state = p_server->op.get_state(data); |
| if (vout_func_check_state(index, state, p_server)) |
| continue; |
| } |
| if (!p_server->op.get_disp_cap) |
| continue; |
| len += p_server->op.get_disp_cap(buf + len, data); |
| } |
| |
| mutex_unlock(&vout_mutex); |
| return len; |
| } |
| |
| int vout_func_set_vframe_rate_hint(int index, int duration) |
| { |
| int ret = -1; |
| struct vout_server_s *p_server = NULL; |
| void *data; |
| |
| mutex_lock(&vout_mutex); |
| |
| if (index == 1) |
| p_server = vout_module.curr_vout_server; |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| else if (index == 2) |
| p_server = vout2_module.curr_vout_server; |
| #endif |
| #ifdef CONFIG_AMLOGIC_VOUT3_SERVE |
| else if (index == 3) |
| p_server = vout3_module.curr_vout_server; |
| #endif |
| |
| if (!IS_ERR_OR_NULL(p_server)) { |
| data = p_server->data; |
| if (p_server->op.set_vframe_rate_hint) |
| ret = p_server->op.set_vframe_rate_hint(duration, data); |
| } |
| |
| mutex_unlock(&vout_mutex); |
| return ret; |
| } |
| EXPORT_SYMBOL(vout_func_set_vframe_rate_hint); |
| |
| /* |
| *interface export to client who want to notify about source frame rate end. |
| */ |
| int vout_func_get_vframe_rate_hint(int index) |
| { |
| int ret = 0; |
| struct vout_server_s *p_server = NULL; |
| void *data; |
| |
| mutex_lock(&vout_mutex); |
| |
| if (index == 1) |
| p_server = vout_module.curr_vout_server; |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| else if (index == 2) |
| p_server = vout2_module.curr_vout_server; |
| #endif |
| #ifdef CONFIG_AMLOGIC_VOUT3_SERVE |
| else if (index == 3) |
| p_server = vout3_module.curr_vout_server; |
| #endif |
| |
| if (!IS_ERR_OR_NULL(p_server)) { |
| data = p_server->data; |
| if (p_server->op.get_vframe_rate_hint) |
| ret = p_server->op.get_vframe_rate_hint(data); |
| } |
| |
| mutex_unlock(&vout_mutex); |
| return ret; |
| } |
| EXPORT_SYMBOL(vout_func_get_vframe_rate_hint); |
| |
| /* |
| * interface export to client who want to set test bist. |
| */ |
| void vout_func_set_test_bist(int index, unsigned int bist) |
| { |
| struct vout_server_s *p_server = NULL; |
| |
| mutex_lock(&vout_mutex); |
| |
| if (index == 1) |
| p_server = vout_module.curr_vout_server; |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| else if (index == 2) |
| p_server = vout2_module.curr_vout_server; |
| #endif |
| #ifdef CONFIG_AMLOGIC_VOUT3_SERVE |
| else if (index == 3) |
| p_server = vout3_module.curr_vout_server; |
| #endif |
| |
| if (!IS_ERR_OR_NULL(p_server)) { |
| if (p_server->op.set_bist) |
| p_server->op.set_bist(bist, p_server->data); |
| } |
| |
| mutex_unlock(&vout_mutex); |
| } |
| EXPORT_SYMBOL(vout_func_set_test_bist); |
| |
| int vout_func_vout_suspend(int index) |
| { |
| int ret = 0; |
| struct vout_server_s *p_server = NULL; |
| |
| mutex_lock(&vout_mutex); |
| |
| if (index == 1) |
| p_server = vout_module.curr_vout_server; |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| else if (index == 2) |
| p_server = vout2_module.curr_vout_server; |
| #endif |
| |
| if (!IS_ERR_OR_NULL(p_server)) { |
| if (p_server->op.vout_suspend) |
| ret = p_server->op.vout_suspend(p_server->data); |
| } |
| |
| mutex_unlock(&vout_mutex); |
| return ret; |
| } |
| EXPORT_SYMBOL(vout_func_vout_suspend); |
| |
| int vout_func_vout_resume(int index) |
| { |
| struct vout_server_s *p_server = NULL; |
| |
| mutex_lock(&vout_mutex); |
| |
| if (index == 1) |
| p_server = vout_module.curr_vout_server; |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| else if (index == 2) |
| p_server = vout2_module.curr_vout_server; |
| #endif |
| #ifdef CONFIG_AMLOGIC_VOUT3_SERVE |
| else if (index == 3) |
| p_server = vout3_module.curr_vout_server; |
| #endif |
| |
| if (!IS_ERR_OR_NULL(p_server)) { |
| if (p_server->op.vout_resume) { |
| /* ignore error when resume. */ |
| p_server->op.vout_resume(p_server->data); |
| } |
| } |
| |
| mutex_unlock(&vout_mutex); |
| return 0; |
| } |
| EXPORT_SYMBOL(vout_func_vout_resume); |
| |
| /* |
| *interface export to client who want to shutdown. |
| */ |
| int vout_func_vout_shutdown(int index) |
| { |
| int ret = -1; |
| struct vout_server_s *p_server; |
| struct vout_module_s *p_module = NULL; |
| |
| if (index == 1) |
| p_module = &vout_module; |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| else if (index == 2) |
| p_module = &vout2_module; |
| #endif |
| #ifdef CONFIG_AMLOGIC_VOUT3_SERVE |
| else if (index == 3) |
| p_module = &vout3_module; |
| #endif |
| |
| if (!p_module) { |
| VOUTERR("vout%d: %s: vout_module is NULL\n", index, __func__); |
| return -1; |
| } |
| list_for_each_entry(p_server, &p_module->vout_server_list, list) { |
| if (p_server->op.vout_shutdown) |
| ret = p_server->op.vout_shutdown(p_server->data); |
| } |
| |
| return ret; |
| } |
| EXPORT_SYMBOL(vout_func_vout_shutdown); |
| |
| /* |
| *here we offer two functions to get and register vout module server |
| *vout module server will set and store tvmode attributes for vout encoder |
| *we can ensure TVMOD SET MODULE independent with these two function. |
| */ |
| |
| int vout_func_vout_register_server(int index, |
| struct vout_server_s *mem_server) |
| { |
| struct list_head *p_iter; |
| struct vout_server_s *p_server; |
| struct vout_module_s *p_module = NULL; |
| |
| if (!mem_server) { |
| VOUTERR("vout%d: server is NULL\n", index); |
| return -1; |
| } |
| if (!mem_server->name) { |
| VOUTERR("vout%d: server name is NULL\n", index); |
| return -1; |
| } |
| VOUTPR("vout%d: register server: %s\n", index, mem_server->name); |
| |
| mutex_lock(&vout_mutex); |
| |
| if (index == 1) |
| p_module = &vout_module; |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| else if (index == 2) |
| p_module = &vout2_module; |
| #endif |
| #ifdef CONFIG_AMLOGIC_VOUT3_SERVE |
| else if (index == 3) |
| p_module = &vout3_module; |
| #endif |
| |
| if (!p_module) { |
| VOUTERR("vout%d: %s: vout_module is NULL\n", index, __func__); |
| mutex_unlock(&vout_mutex); |
| return -1; |
| } |
| list_for_each(p_iter, &p_module->vout_server_list) { |
| p_server = list_entry(p_iter, struct vout_server_s, list); |
| |
| if (p_server->name && |
| (strcmp(p_server->name, mem_server->name) == 0)) { |
| VOUTPR("vout%d: server %s is already registered\n", |
| index, mem_server->name); |
| /* vout server already registered. */ |
| mutex_unlock(&vout_mutex); |
| return -1; |
| } |
| } |
| list_add(&mem_server->list, &p_module->vout_server_list); |
| mutex_unlock(&vout_mutex); |
| return 0; |
| } |
| EXPORT_SYMBOL(vout_func_vout_register_server); |
| |
| int vout_func_vout_unregister_server(int index, |
| struct vout_server_s *mem_server) |
| { |
| struct vout_server_s *p_server; |
| struct vout_module_s *p_module = NULL; |
| |
| if (!mem_server) { |
| VOUTERR("vout%d: server is NULL\n", index); |
| return -1; |
| } |
| if (!mem_server->name) { |
| VOUTERR("vout%d: server name is NULL\n", index); |
| return -1; |
| } |
| /*VOUTPR("vout%d: unregister server: %s\n", index, mem_server->name);*/ |
| |
| mutex_lock(&vout_mutex); |
| |
| if (index == 1) |
| p_module = &vout_module; |
| #ifdef CONFIG_AMLOGIC_VOUT2_SERVE |
| else if (index == 2) |
| p_module = &vout2_module; |
| #endif |
| #ifdef CONFIG_AMLOGIC_VOUT3_SERVE |
| else if (index == 3) |
| p_module = &vout3_module; |
| #endif |
| |
| if (!p_module) { |
| VOUTERR("vout%d: %s: vout_module is NULL\n", index, __func__); |
| mutex_unlock(&vout_mutex); |
| return -1; |
| } |
| list_for_each_entry(p_server, &p_module->vout_server_list, list) { |
| if (p_server->name && mem_server->name && |
| (strcmp(p_server->name, mem_server->name) == 0)) { |
| /* |
| * We will not move current vout server pointer |
| * automatically if current vout server pointer |
| * is the one which will be deleted next. |
| * So you should change current vout server |
| * first then remove it |
| */ |
| if (p_module->curr_vout_server == p_server) |
| p_module->curr_vout_server = NULL; |
| |
| list_del(&mem_server->list); |
| mutex_unlock(&vout_mutex); |
| return 0; |
| } |
| } |
| mutex_unlock(&vout_mutex); |
| return 0; |
| } |
| EXPORT_SYMBOL(vout_func_vout_unregister_server); |
| |
| /* return vout frac */ |
| unsigned int vout_parse_vout_name(char *name) |
| { |
| char *p, *frac_str; |
| unsigned int frac = 0; |
| |
| mutex_lock(&vout_mutex); |
| |
| p = strchr(name, ','); |
| if (!p) { |
| frac = 0; |
| } else { |
| frac_str = p + 1; |
| *p = '\0'; |
| if (strcmp(frac_str, "frac") == 0) |
| frac = 1; |
| } |
| |
| mutex_unlock(&vout_mutex); |
| |
| return frac; |
| } |