blob: 0ea80a6fac8a7412edb681c7753f6703aab2a7aa [file] [log] [blame]
/*
* drivers/amlogic/media/vout/vout_serve/vout_func.c
*
* Copyright (C) 2017 Amlogic, Inc. 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 as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
*/
/* Linux Headers */
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/err.h>
/* Amlogic Headers */
#include <linux/amlogic/iomap.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,
};
#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,
};
#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 = VIU_MUX_MAX,
.vout_device = NULL,
};
struct vinfo_s *get_invalid_vinfo(int index)
{
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 == NULL)
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
static unsigned int vout_func_vcbus_read(unsigned int _reg)
{
return aml_read_vcbus(_reg);
};
static void vout_func_vcbus_write(unsigned int _reg, unsigned int _value)
{
aml_write_vcbus(_reg, _value);
};
static void vout_func_vcbus_setb(unsigned int _reg, unsigned int _value,
unsigned int _start, unsigned int _len)
{
vout_func_vcbus_write(_reg, ((vout_func_vcbus_read(_reg) &
~(((1L << (_len))-1) << (_start))) |
(((_value)&((1L<<(_len))-1)) << (_start))));
}
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 actived 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;
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
if (p_module == NULL) {
VOUTERR("vout%d: %s: vout_module is NULL\n", index, __func__);
mutex_unlock(&vout_mutex);
return;
}
list_for_each_entry(p_server, &p_module->vout_server_list, list) {
if (p_server->op.vmode_is_supported == NULL) {
p_server->op.disable(mode);
continue;
}
if (p_server->op.vmode_is_supported(mode) == true) {
p_module->curr_vout_server = p_server;
if (p_server->op.set_state)
p_server->op.set_state(index);
} else {
if (p_server->op.get_state) {
state = p_server->op.get_state();
if (state & (1 << index))
p_server->op.disable(mode);
}
if (p_server->op.clr_state)
p_server->op.clr_state(index);
}
}
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_bit = 0xff, mux_sel = VIU_MUX_MAX;
unsigned int clk_bit = 0xff, clk_sel = 0;
mutex_lock(&vout_mutex);
if (index == 1) {
p_module = &vout_module;
mux_bit = 0;
clk_sel = 0;
} else if (index == 2) {
#ifdef CONFIG_AMLOGIC_VOUT2_SERVE
p_module = &vout2_module;
mux_bit = 2;
clk_sel = 1;
#endif
}
if (p_module == NULL) {
VOUTERR("vout%d: %s: vout_module is NULL\n", index, __func__);
mutex_unlock(&vout_mutex);
return;
}
p_server = p_module->curr_vout_server;
if (p_server) {
if (p_server->op.get_vinfo)
vinfo = p_server->op.get_vinfo();
}
if (vinfo == NULL)
vinfo = get_invalid_vinfo(index);
mux_sel = vinfo->viu_mux;
switch (mux_sel) {
case VIU_MUX_ENCL:
clk_bit = 1;
break;
case VIU_MUX_ENCI:
clk_bit = 2;
break;
case VIU_MUX_ENCP:
clk_bit = 0;
break;
default:
break;
}
if (mux_bit < 0xff) {
vout_func_vcbus_setb(VPU_VIU_VENC_MUX_CTRL,
mux_sel, mux_bit, 2);
}
if (clk_bit < 0xff)
vout_func_vcbus_setb(VPU_VENCX_CLK_CTRL, clk_sel, clk_bit, 1);
/* special setting for dummy mode */
if (index == 1) {
if (vinfo->mode == VMODE_DUMMY_LCD) {
/* viu1 use encl_vsync */
vout_func_vcbus_setb(VPU_VIU_VENC_MUX_CTRL, 0, 4, 2);
vout_func_vcbus_setb(VPU_VIU_VENC_MUX_CTRL, 1, 20, 1);
vout_func_vcbus_setb(VPP_WRBAK_CTRL, 1, 11, 1);
} else {
vout_func_vcbus_setb(VPU_VIU_VENC_MUX_CTRL, 0, 20, 1);
vout_func_vcbus_setb(VPP_WRBAK_CTRL, 0, 11, 1);
}
}
#if 0
VOUTPR("%s: %d, mux_sel=%d, clk_sel=%d\n",
__func__, index, mux_sel, clk_sel);
#endif
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;
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
if (p_module == NULL) {
VOUTERR("vout%d: %s: vout_module is NULL\n", index, __func__);
mutex_unlock(&vout_mutex);
return -1;
}
ret = p_module->curr_vout_server->op.set_vmode(mode);
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);
/*
*interface export to client who want to set current vmode.
*/
enum vmode_e vout_func_validate_vmode(int index, char *name)
{
enum vmode_e ret = VMODE_MAX;
struct vout_server_s *p_server;
struct vout_module_s *p_module = NULL;
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
if (p_module == NULL) {
VOUTERR("vout%d: %s: vout_module is NULL\n", index, __func__);
mutex_unlock(&vout_mutex);
return VMODE_MAX;
}
list_for_each_entry(p_server, &p_module->vout_server_list, list) {
/* check state for another vout */
if (p_server->op.get_state) {
state = p_server->op.get_state();
if (vout_func_check_state(index, state, p_server))
continue;
}
if (p_server->op.validate_vmode) {
ret = p_server->op.validate_vmode(name);
if (ret != VMODE_MAX) /* valid vmode find. */
break;
}
}
mutex_unlock(&vout_mutex);
return ret;
}
EXPORT_SYMBOL(vout_func_validate_vmode);
int vout_func_set_vframe_rate_hint(int index, int duration)
{
int ret = -1;
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.set_vframe_rate_hint)
ret = p_server->op.set_vframe_rate_hint(duration);
}
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_set_vframe_rate_end_hint(int index)
{
int ret = -1;
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.set_vframe_rate_end_hint)
ret = p_server->op.set_vframe_rate_end_hint();
}
mutex_unlock(&vout_mutex);
return ret;
}
EXPORT_SYMBOL(vout_func_set_vframe_rate_end_hint);
/*
*interface export to client who want to notify about source fr_policy.
*/
int vout_func_set_vframe_rate_policy(int index, int policy)
{
int ret = -1;
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.set_vframe_rate_policy)
ret = p_server->op.set_vframe_rate_policy(policy);
}
mutex_unlock(&vout_mutex);
return ret;
}
EXPORT_SYMBOL(vout_func_set_vframe_rate_policy);
/*
*interface export to client who want to notify about source fr_policy.
*/
int vout_func_get_vframe_rate_policy(int index)
{
int ret = -1;
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.get_vframe_rate_policy)
ret = p_server->op.get_vframe_rate_policy();
}
mutex_unlock(&vout_mutex);
return ret;
}
EXPORT_SYMBOL(vout_func_get_vframe_rate_policy);
/*
* 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
if (!IS_ERR_OR_NULL(p_server)) {
if (p_server->op.set_bist)
p_server->op.set_bist(bist);
}
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();
}
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
if (!IS_ERR_OR_NULL(p_server)) {
if (p_server->op.vout_resume) {
/* ignore error when resume. */
p_server->op.vout_resume();
}
}
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
if (p_module == NULL) {
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();
}
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 == NULL) {
VOUTERR("vout%d: server is NULL\n", index);
return -1;
}
if (mem_server->name == NULL) {
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
if (p_module == NULL) {
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 == NULL) {
VOUTERR("vout%d: server 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
if (p_module == NULL) {
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);
/* fps = 9600/duration/100 hz */
static int vsource_fps_table[][2] = {
/* duration fps */
{1600, 6000,},
{1601, 5994,},
{1602, 5994,},
{1920, 5000,},
{3200, 3000,},
{3203, 2997,},
{3840, 2500,},
{4000, 2400,},
{4004, 2397,},
};
int vout_get_vsource_fps(int duration)
{
int i;
int fps = 6000;
for (i = 0; i < 9; i++) {
if (duration == vsource_fps_table[i][0]) {
fps = vsource_fps_table[i][1];
break;
}
}
return fps;
}
EXPORT_SYMBOL(vout_get_vsource_fps);
int vout_get_hpd_state(void)
{
int ret = 0;
#ifdef CONFIG_AMLOGIC_HDMITX
ret = get_hpd_state();
#endif
return ret;
}
EXPORT_SYMBOL(vout_get_hpd_state);