media: merged code from c502feca7 on the amlogic-3.14-dev [1/3]

PD#150739: merged code from c502feca7 on the amlogic-3.14-dev
1.codec_mm: fixed tvp first wait long time bug
2.pcrmaster: fix some avsync issue in pcrmaster mode
3.vfm: fix vf provider crash issue

Change-Id: I64202a38a3ed0c50ba665fb477caea26c67ddcb0
Signed-off-by: Nanxin Qin <nanxin.qin@amlogic.com>
This commit is contained in:
Nanxin Qin
2017-09-03 22:47:21 +08:00
committed by Jianxin Pan
parent fba1030374
commit 6470478320
20 changed files with 881 additions and 200 deletions

View File

@@ -14079,3 +14079,8 @@ F: arch/arm64/configs/meson64_smarthome_defconfig
AMLOGIC MESONGXL ADD P241 DTS
M: Lianghu Su <lianghu.su@amlogic.com>
F: arch/arm64/boot/dts/amlogic/gxl_p241_1g_buildroot.dts
AMLOGIC tee
M: Nanxin Qin <nanxin.qin@amlogic.com>
F: drivers/amlogic/tee/*
F: include/linux/amlogic/tee.h

View File

@@ -312,6 +312,7 @@ CONFIG_AMLOGIC_IRBLASTER=y
CONFIG_AMLOGIC_IIO=y
CONFIG_AMLOGIC_SARADC=y
CONFIG_AMLOGIC_DDR_WINDOW_TOOL=m
CONFIG_AMLOGIC_TEE=y
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y

View File

@@ -114,5 +114,6 @@ source "drivers/amlogic/drm/Kconfig"
source "drivers/amlogic/secure_monitor/Kconfig"
source "drivers/amlogic/tee/Kconfig"
endmenu
endif

View File

@@ -104,3 +104,5 @@ obj-$(CONFIG_AMLOGIC_DDR_WINDOW_TOOL) += ddr_window/
obj-$(CONFIG_DRM_MESON) += drm/
obj-$(CONFIG_AMLOGIC_M8B_SM) += secure_monitor/
obj-$(CONFIG_AMLOGIC_TEE) += tee/

View File

@@ -638,7 +638,7 @@ void codec_mm_release(struct codec_mm_s *mem, const char *owner)
if (!mem)
return;
spin_lock_irqsave(&mem->lock, flags);
spin_lock_irqsave(&mgt->lock, flags);
index = atomic_dec_return(&mem->use_cnt);
max_owner = mem->owner[index];
for (i = 0; i < index; i++) {
@@ -651,13 +651,13 @@ void codec_mm_release(struct codec_mm_s *mem, const char *owner)
mem->from_flags, index);
mem->owner[index] = NULL;
if (index == 0) {
spin_unlock_irqrestore(&mem->lock, flags);
codec_mm_free_in(mgt, mem);
list_del(&mem->list);
spin_unlock_irqrestore(&mgt->lock, flags);
codec_mm_free_in(mgt, mem);
kfree(mem);
return;
}
spin_unlock_irqrestore(&mem->lock, flags);
spin_unlock_irqrestore(&mgt->lock, flags);
}
EXPORT_SYMBOL(codec_mm_release);

View File

@@ -127,6 +127,7 @@ struct codec_mm_scatter_s {
u32 enable_slot_from_sys;
u32 no_cache_size_M;
u32 support_from_slot_sys;
u32 no_alloc_from_sys;
};
struct codec_mm_scatter_mgt {
@@ -144,6 +145,7 @@ struct codec_mm_scatter_mgt {
int alloc_from_cma_first;
u32 enable_slot_from_sys;
u32 no_cache_size_M;
u32 no_alloc_from_sys;
u32 support_from_slot_sys;
int one_page_cnt;
int scatters_cnt;
@@ -577,8 +579,9 @@ static struct codec_mm_slot *codec_mm_slot_alloc(
break; /*ignore codec_mm */
if ((try_alloc_size <= 0 ||
try_alloc_size > 64 * 1024) && /*must > 512K. */
codec_mm_get_free_size() >
smgt->reserved_block_mm_M * SZ_1M) {
(smgt->tvp_mode ||
(codec_mm_get_free_size() >
smgt->reserved_block_mm_M * SZ_1M))) {
/*try from codec_mm */
if (try_alloc_size <= 0) {
try_alloc_size =
@@ -626,9 +629,10 @@ static struct codec_mm_slot *codec_mm_slot_alloc(
goto error; /*don't alloc 1 page with slot. */
}
}
if (!have_alloced && smgt->tvp_mode) {
if (!have_alloced) {
/*tvp not support alloc from sys.*/
goto error;
if (smgt->tvp_mode || smgt->no_alloc_from_sys)
goto error;
}
while (!have_alloced) {
/*don't alloc 1 page with slot. */
@@ -857,10 +861,11 @@ static int codec_mm_page_alloc_from_slot(
int n;
codec_mm_list_lock(smgt);
if (list_empty(&smgt->free_list) &&
(codec_mm_get_free_size() < /*no codec mm */
smgt->reserved_block_mm_M * SZ_1M) &&
!smgt->support_from_slot_sys) { /*no sys */
if (!smgt->tvp_mode &&
list_empty(&smgt->free_list) &&
(codec_mm_get_free_size() </*no codec mm*/
smgt->reserved_block_mm_M * SZ_1M) &&
!smgt->support_from_slot_sys) {/*no sys*/
codec_mm_list_unlock(smgt);
return 0;
}
@@ -1023,7 +1028,7 @@ static int codec_mm_page_alloc_all_locked(
num - alloced);
if (new_alloc <= 0)
can_from_slot = 0;
} else if (!smgt->tvp_mode) {
} else if (!smgt->no_alloc_from_sys && !smgt->tvp_mode) {
new_alloc = codec_mm_page_alloc_from_one_pages(
smgt,
pages + alloced,
@@ -1049,7 +1054,7 @@ static int codec_mm_pages_free_to_scatter(
if (src_mms->page_used >= src_mms->page_cnt)
return -1; /*no need free. */
dst_mms = smgt->cache_sc;
if (!dst_mms)
if (!dst_mms || src_mms == dst_mms)
return 0;
codec_mm_scatter_lock(dst_mms);
moved = min(src_mms->page_cnt - src_mms->page_used,
@@ -1142,37 +1147,74 @@ static int codec_mm_scatter_free_pages_in_locked(
}
codec_mm_list_lock(smgt);
smgt->alloced_page_num -= freeNum;
if (smgt->cache_sc == mms)
smgt->cached_pages = mms->page_cnt;
codec_mm_list_unlock(smgt);
return 0;
}
/*
*free pages from id(include id);
*/
*fast_mode:
*1: set page_used: call by owner
*2: free to scatter: call by owner
*3: free to slot or others: call by owner
*
*free_mode:
*0: free from id pages.
*1: free not used pages;call by moniter.
*2: less pages.: call by moniter for cache
* id is negative.
*/
static int codec_mm_scatter_free_tail_pages_in(
struct codec_mm_scatter *mms,
int start_free_id,
int fast)
int id,
int fast_mode,
int free_mode)
{
int id = start_free_id;
struct codec_mm_scatter_mgt *smgt;
if (!mms || id < 0 || id >= mms->page_cnt || mms->page_tail < 0) {
if (mms)
ERR_LOG("free mm scatters error id %d,page_cnt=%d\n",
id, mms->page_cnt);
int start_free_id = id;
if (!mms)
return -1;
codec_mm_scatter_lock(mms);
if (free_mode == 1)
start_free_id = mms->page_used;
if (free_mode == 2) {
if (id > 0) {
if (id >= mms->page_cnt)
start_free_id = 0;
else
start_free_id = mms->page_cnt - id;
} else
start_free_id = -1;
}
if ((start_free_id < 0) ||
(start_free_id >= mms->page_cnt) ||
(mms->page_tail < 0)) {
if (mms &&
start_free_id != mms->page_cnt) {
ERR_LOG(
"mms[%p],free error id %d(%d),cnt=%d ,=m:%d,%d\n",
mms,
id,
start_free_id,
mms->page_cnt,
fast_mode,
free_mode);
}
codec_mm_scatter_unlock(mms);
return -1;
}
smgt = (struct codec_mm_scatter_mgt *)mms->manager;
codec_mm_scatter_lock(mms);
mms->page_used = start_free_id;
if (fast == 1) {
if (fast_mode == 1) {
codec_mm_scatter_unlock(mms);
return 0;
}
if (fast == 2 || fast == 3) {
if (fast_mode == 2 || fast_mode == 3) {
codec_mm_pages_free_to_scatter(smgt, mms);
if (fast == 2 || mms->page_used == mms->page_cnt) {
if (fast_mode == 2 || mms->page_used == mms->page_cnt) {
codec_mm_scatter_unlock(mms);
return 0;
}
@@ -1184,14 +1226,12 @@ static int codec_mm_scatter_free_tail_pages_in(
int codec_mm_scatter_free_tail_pages(
struct codec_mm_scatter *mms,
int start_free_id)
int start_id)
{
int ret = 0;
if (start_free_id < mms->page_cnt)
ret = codec_mm_scatter_free_tail_pages_in(
ret = codec_mm_scatter_free_tail_pages_in(
mms,
start_free_id, 0);
start_id, 0, 0);
return ret;
}
EXPORT_SYMBOL(codec_mm_scatter_free_tail_pages);
@@ -1206,7 +1246,7 @@ int codec_mm_scatter_free_tail_pages_fast(
if (start_free_id < mms->page_cnt)
ret = codec_mm_scatter_free_tail_pages_in(
mms,
start_free_id, 2);
start_free_id, 2, 0);
codec_mm_schedule_delay_work(
(struct codec_mm_scatter_mgt *)mms->manager,
100, 0);
@@ -1217,14 +1257,23 @@ EXPORT_SYMBOL(codec_mm_scatter_free_tail_pages_fast);
int codec_mm_scatter_free_unused_pages(struct codec_mm_scatter *mms)
{
int ret = 0;
if (mms->page_used < mms->page_cnt)
ret = codec_mm_scatter_free_tail_pages_in(mms,
mms->page_used, 3);
ret = codec_mm_scatter_free_tail_pages_in(mms,
0, 0, 1);
return ret;
}
EXPORT_SYMBOL(codec_mm_scatter_free_unused_pages);
int codec_mm_scatter_less_pages(struct codec_mm_scatter *mms,
int nums)
{
int ret = 0;
ret = codec_mm_scatter_free_tail_pages_in(mms,
nums, 0, 2);
return ret;
}
EXPORT_SYMBOL(codec_mm_scatter_less_pages);
/*free all pages only
*don't free scatter
*/
@@ -1315,7 +1364,7 @@ static int codec_mm_scatter_free_on_nouser_ext(
codec_mm_list_unlock(smgt);
codec_mm_scatter_unlock(mms);
if (mms->page_cnt > 0)
ret = codec_mm_scatter_free_tail_pages_in(mms, 0, 0);
ret = codec_mm_scatter_free_tail_pages_in(mms, 0, 0, 0);
if (free >= 256 &&
(smgt->try_alloc_in_sys_page_cnt <
smgt->try_alloc_in_sys_page_cnt_max) &&
@@ -1364,7 +1413,6 @@ static int codec_mm_scatter_inc_user_in1(
codec_mm_list_unlock(smgt);
return ret <= 0 ? ret : 0; /*must add before user cnt >= 0 */
}
static int codec_mm_scatter_inc_user_in(
struct codec_mm_scatter *mms,
int cnt)
@@ -1394,6 +1442,7 @@ static int codec_mm_scatter_dec_user_in1(
int delay_free_ms, int cnt)
{
int after_users = 1;
codec_mm_list_lock(smgt);
if (!codec_mm_scatter_valid_locked(smgt, mms)) {
codec_mm_list_unlock(smgt);
@@ -1424,6 +1473,7 @@ static int codec_mm_scatter_dec_user_in(
{
struct codec_mm_scatter_mgt *smgt;
int ret;
if (!mms)
return -1;
smgt = codec_mm_get_scatter_mgt(0);
@@ -1440,6 +1490,7 @@ static int codec_mm_scatter_dec_user_in(
}
return ret;
}
/*
*maybe a render/sink.video/osd/
*/
@@ -2084,6 +2135,7 @@ int codec_mm_scatter_update_config(struct codec_mm_scatter_mgt *smgt)
smgt->enable_slot_from_sys = g_scatter.enable_slot_from_sys;
smgt->support_from_slot_sys = g_scatter.support_from_slot_sys;
smgt->no_cache_size_M = g_scatter.no_cache_size_M;
smgt->no_alloc_from_sys = g_scatter.no_alloc_from_sys;
return 0;
}
@@ -2207,13 +2259,9 @@ static void codec_mm_scatter_cache_manage(
smgt->delay_free_timeout_jiffies64)) {
/*wait time out can free.*/
mms = smgt->cache_sc;
if (mms) { /*only free some 1M cache */
int free_start = smgt->cached_pages - 256;
if (free_start < smgt->keep_size_PAGE)
free_start = smgt->keep_size_PAGE;
codec_mm_scatter_free_tail_pages(mms,
free_start);
if (mms) {/*only free some 1M cache*/
codec_mm_scatter_less_pages(mms,
256);
}
codec_mm_free_all_free_slots_in(smgt);
/*free some slots.*/
@@ -2481,6 +2529,7 @@ static struct mconfig codec_mm_sc_configs[] = {
MC_PU32("enable_slot_from_sys",
&g_scatter.enable_slot_from_sys),
MC_PU32("no_cache_size_M", &g_scatter.no_cache_size_M),
MC_PU32("no_alloc_from_sys", &g_scatter.no_alloc_from_sys),
};
static struct mconfig_node codec_mm_sc;
@@ -2504,7 +2553,7 @@ int codec_mm_scatter_mgt_init(void)
g_scatter.enable_slot_from_sys = smgt->enable_slot_from_sys;
g_scatter.support_from_slot_sys = smgt->support_from_slot_sys;
g_scatter.no_cache_size_M = smgt->no_cache_size_M;
g_scatter.no_alloc_from_sys = smgt->no_cache_size_M;
INIT_REG_NODE_CONFIGS("media.codec_mm",
&codec_mm_sc, "scatter",
codec_mm_sc_configs,

View File

@@ -31,7 +31,59 @@
#include "vftrace.h"
#define MAX_PROVIDER_NUM 32
struct vframe_provider_s *provider_table[MAX_PROVIDER_NUM];
static struct vframe_provider_s *provider_table[MAX_PROVIDER_NUM];
static atomic_t provider_used = ATOMIC_INIT(0);
#define providers_lock() atomic_inc(&provider_used)
#define providers_unlock() atomic_dec(&provider_used)
#define providers_used() atomic_read(&provider_used)
static DEFINE_MUTEX(provider_table_mutex);
#define TABLE_LOCK() mutex_lock(&provider_table_mutex)
#define TABLE_UNLOCK() mutex_unlock(&provider_table_mutex)
#define VFPROVIER_DEBUG
#ifdef VFPROVIER_DEBUG
static DEFINE_SPINLOCK(provider_lock);
static const char *last_receiver;
static const char *last_provider;
void provider_update_caller(const char *receiver, const char *provider)
{
unsigned long flags;
spin_lock_irqsave(&provider_lock, flags);
last_receiver = receiver;
last_provider = provider;
spin_unlock_irqrestore(&provider_lock, flags);
}
void provider_print_last_info(void)
{
unsigned long flags;
struct vframe_provider_s *p;
int i;
spin_lock_irqsave(&provider_lock, flags);
pr_info("last receiver: %s\n",
last_receiver ? last_receiver : "null");
pr_info("last provider: %s\n",
last_provider ? last_provider : "null");
pr_info("register provider:\n");
for (i = 0; i < MAX_PROVIDER_NUM; i++) {
p = provider_table[i];
if (p)
pr_info("%s: user_cnt:%d\n", p->name,
atomic_read(&p->use_cnt));
}
spin_unlock_irqrestore(&provider_lock, flags);
}
#else
void provider_update_caller(
const char *receiver,
const char *provider) {return }
void provider_print_last_info(void) {return}
#endif
int provider_list(char *buf)
{
@@ -135,6 +187,7 @@ static int vf_provider_close(struct vframe_provider_s *prov)
if (ret > CLOSED_CNT) {
pr_err("**ERR***,release, provider %s not finised,%d, wait=%d\n",
prov->name, ret, wait_max);
provider_print_last_info();
}
return 0;
}
@@ -208,11 +261,14 @@ int vf_reg_provider(struct vframe_provider_s *prov)
prov->traceget = NULL;
prov->traceput = NULL;
atomic_set(&prov->use_cnt, 0);/*set it ready for use.*/
TABLE_LOCK();
for (i = 0; i < MAX_PROVIDER_NUM; i++) {
p = provider_table[i];
if (p) {
if (!strcmp(p->name, prov->name))
if (!strcmp(p->name, prov->name)) {
TABLE_UNLOCK();
return -1;
}
}
}
for (i = 0; i < MAX_PROVIDER_NUM; i++) {
@@ -221,6 +277,7 @@ int vf_reg_provider(struct vframe_provider_s *prov)
break;
}
}
TABLE_UNLOCK();
if (i < MAX_PROVIDER_NUM) {
vf_update_active_map();
receiver = vf_get_receiver(prov->name);
@@ -253,18 +310,20 @@ void vf_unreg_provider(struct vframe_provider_s *prov)
int i;
for (i = 0; i < MAX_PROVIDER_NUM; i++) {
TABLE_LOCK();
p = provider_table[i];
if (p && !strcmp(p->name, prov->name)) {
provider_table[i] = NULL;
TABLE_UNLOCK();
if (p->traceget) {
vftrace_free_trace(p->traceget);
vftrace_free_trace(prov->traceget);
p->traceget = NULL;
}
if (p->traceput) {
vftrace_free_trace(p->traceput);
vftrace_free_trace(prov->traceput);
p->traceput = NULL;
}
vf_provider_close(prov);
provider_table[i] = NULL;
if (vfm_debug_flag & 1)
pr_err("%s:%s\n", __func__, prov->name);
receiver = vf_get_receiver(prov->name);
@@ -281,6 +340,27 @@ void vf_unreg_provider(struct vframe_provider_s *prov)
vf_update_active_map();
break;
}
TABLE_UNLOCK();
}
{
/*
* wait no one used provider,
* if some one have on used.
* the provider memory may free,
* become unreachable.
*/
int cnt = 0;
while (providers_used()) {
schedule();
cnt++;
if (cnt > 10000) {
pr_err("unreg provider locked %s,%d!\n",
prov->name, cnt);
provider_print_last_info();
break;
}
}
}
}
EXPORT_SYMBOL(vf_unreg_provider);
@@ -316,9 +396,11 @@ void vf_ext_light_unreg_provider(struct vframe_provider_s *prov)
int i;
for (i = 0; i < MAX_PROVIDER_NUM; i++) {
TABLE_LOCK();
p = provider_table[i];
if (p && !strcmp(p->name, prov->name)) {
provider_table[i] = NULL;
TABLE_UNLOCK();
if (vfm_debug_flag & 1)
pr_err("%s:%s\n", __func__, prov->name);
@@ -332,6 +414,7 @@ void vf_ext_light_unreg_provider(struct vframe_provider_s *prov)
vf_update_active_map();
break;
}
TABLE_UNLOCK();
}
}
EXPORT_SYMBOL(vf_ext_light_unreg_provider);
@@ -340,12 +423,14 @@ struct vframe_s *vf_peek(const char *receiver)
{
struct vframe_provider_s *vfp;
struct vframe_s *vf = NULL;
providers_lock();
vfp = vf_get_provider(receiver);
if (use_provider(vfp)) {
if (vfp->ops && vfp->ops->peek)
vf = vfp->ops->peek(vfp->op_arg);
unuse_provider(vfp);
}
providers_unlock();
return vf;
}
EXPORT_SYMBOL(vf_peek);
@@ -355,14 +440,20 @@ struct vframe_s *vf_get(const char *receiver)
struct vframe_provider_s *vfp;
struct vframe_s *vf = NULL;
providers_lock();
vfp = vf_get_provider(receiver);
if (use_provider(vfp)) {
provider_update_caller(receiver, vfp->name);
if (vfp->ops && vfp->ops->get)
vf = vfp->ops->get(vfp->op_arg);
if (vf)
vftrace_info_in(vfp->traceget, vf);
unuse_provider(vfp);
}
} else
provider_update_caller(receiver, NULL);
providers_unlock();
return vf;
}
EXPORT_SYMBOL(vf_get);
@@ -370,15 +461,19 @@ EXPORT_SYMBOL(vf_get);
void vf_put(struct vframe_s *vf, const char *receiver)
{
struct vframe_provider_s *vfp;
providers_lock();
vfp = vf_get_provider(receiver);
if (use_provider(vfp)) {
provider_update_caller(receiver, vfp->name);
if (vfp->ops && vfp->ops->put)
vfp->ops->put(vf, vfp->op_arg);
if (vf)
vftrace_info_in(vfp->traceput, vf);
unuse_provider(vfp);
} else {
provider_update_caller(receiver, NULL);
}
providers_unlock();
}
EXPORT_SYMBOL(vf_put);
@@ -386,12 +481,14 @@ int vf_get_states(struct vframe_provider_s *vfp,
struct vframe_states *states)
{
int ret = -1;
providers_lock();
if (use_provider(vfp)) {
provider_update_caller(NULL, vfp->name);
if (vfp->ops && vfp->ops->vf_states)
ret = vfp->ops->vf_states(states, vfp->op_arg);
unuse_provider(vfp);
}
providers_unlock();
return ret;
}
EXPORT_SYMBOL(vf_get_states);

View File

@@ -19,6 +19,7 @@
#include <linux/amlogic/media/frame_sync/tsync.h>
#include <linux/amlogic/media/utils/vdec_reg.h>
#include <linux/amlogic/media/registers/register.h>
#include <linux/amlogic/media/vout/vout_notify.h>
u32 acc_apts_inc;
@@ -200,6 +201,19 @@ void timestamp_pcrscr_set_adj(s32 inc)
}
EXPORT_SYMBOL(timestamp_pcrscr_set_adj);
void timestamp_pcrscr_set_adj_pcr(s32 inc)
{
const struct vinfo_s *info = get_current_vinfo();
if (inc != 0) {
system_time_inc_adj =
900 * info->sync_duration_den /
(info->sync_duration_num*inc);
} else
system_time_inc_adj = 0;
}
EXPORT_SYMBOL(timestamp_pcrscr_set_adj_pcr);
void timestamp_pcrscr_enable(u32 enable)
{
system_time_up = enable;

View File

@@ -330,7 +330,7 @@ u32 tsync_pcr_get_min_checkinpts(void)
u32 last_checkin_vpts = get_last_checkin_pts(PTS_TYPE_VIDEO);
u32 last_checkin_apts = get_last_checkin_pts(PTS_TYPE_AUDIO);
if (last_checkin_apts > 0 && last_checkin_apts > 0)
if (last_checkin_vpts > 0 && last_checkin_apts > 0)
return min(last_checkin_vpts, last_checkin_apts);
else if (last_checkin_apts > 0)
return last_checkin_apts;
@@ -407,7 +407,9 @@ void tsync_pcr_pcrscr_set(void)
if (cur_pcr && !(tsync_pcr_inited_flag & complete_init_flag)
&& (min_checkinpts != 0)) {
tsync_pcr_inited_flag |= TSYNC_PCR_INITCHECK_PCR;
ref_pcr = cur_pcr - tsync_pcr_adj_value * 15;
if (cur_pcr > tsync_pcr_adj_value * 15)
ref_pcr = cur_pcr - tsync_pcr_adj_value * 15;
timestamp_pcrscr_set(ref_pcr);
tsync_pcr_usepcr = 1;
@@ -431,12 +433,14 @@ void tsync_pcr_pcrscr_set(void)
ref_pcr = first_apts;
/* add it for dolby av sync */
if (cur_pcr > 0)
if (cur_pcr > tsync_apts_adj_value)
ref_pcr = cur_pcr - tsync_apts_adj_value;
timestamp_pcrscr_set(ref_pcr);
if (cur_pcr > 0)
tsync_pcr_usepcr = 1;
else
tsync_pcr_usepcr = 0;
timestamp_pcrscr_enable(1);
pr_info
@@ -456,10 +460,14 @@ void tsync_pcr_pcrscr_set(void)
if (first_vpts && !(tsync_pcr_inited_flag & complete_init_flag)
&& (min_checkinpts != 0)) {
tsync_pcr_inited_flag |= TSYNC_PCR_INITCHECK_VPTS;
ref_pcr = first_vpts - tsync_pcr_ref_latency * 15;
if (first_vpts > tsync_pcr_ref_latency * 15)
ref_pcr = first_vpts - tsync_pcr_ref_latency * 15;
timestamp_pcrscr_set(ref_pcr);
if (cur_pcr > 0)
tsync_pcr_usepcr = 1;
else
tsync_pcr_usepcr = 0;
timestamp_pcrscr_enable(1);
pr_info
@@ -1181,19 +1189,16 @@ static unsigned long tsync_pcr_check(void)
}
if (need_recovery && !tsync_pcr_vpause_flag) {
if (play_mode == PLAY_MODE_SLOW) {
timestamp_pcrscr_set(timestamp_pcrscr_get() -
tsync_pcr_recovery_span);
} else if (play_mode == PLAY_MODE_FORCE_SLOW) {
timestamp_pcrscr_set(timestamp_pcrscr_get() -
FORCE_RECOVERY_SPAN);
} else if (play_mode == PLAY_MODE_SPEED) {
timestamp_pcrscr_set(timestamp_pcrscr_get() +
tsync_pcr_recovery_span);
} else if (play_mode == PLAY_MODE_FORCE_SPEED) {
timestamp_pcrscr_set(timestamp_pcrscr_get() +
FORCE_RECOVERY_SPAN);
}
if (play_mode == PLAY_MODE_SLOW)
timestamp_pcrscr_set_adj_pcr(-1);
else if (play_mode == PLAY_MODE_FORCE_SLOW)
timestamp_pcrscr_set_adj_pcr(-2);
else if (play_mode == PLAY_MODE_SPEED)
timestamp_pcrscr_set_adj_pcr(1);
else if (play_mode == PLAY_MODE_FORCE_SPEED)
timestamp_pcrscr_set_adj_pcr(2);
else if (play_mode == PLAY_MODE_NORMAL)
timestamp_pcrscr_set_adj_pcr(0);
}
/* } */
@@ -1294,6 +1299,7 @@ void tsync_pcr_stop(void)
del_timer_sync(&tsync_pcr_check_timer);
pr_info("[tsync_pcr_stop]PCRMASTER stop success.\n");
}
timestamp_pcrscr_set(0);
tsync_pcr_freerun_mode = 0;
tsync_pcr_started = 0;
}

View File

@@ -95,6 +95,8 @@ int video_vsync = -ENXIO;
#define DEBUG_TMP 0
static int video_global_output = 1;
/* video_pause_global: 0 is play, 1 is pause, 2 is invalid */
static int video_pause_global = 1;
#ifdef CONFIG_GE2D_KEEP_FRAME
/* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */
@@ -764,7 +766,7 @@ int get_video0_frame_info(struct vframe_s *vf)
}
EXPORT_SYMBOL(get_video0_frame_info);
static struct vframe_s vf_local;
static struct vframe_s vf_local, vf_local2;
static u32 vsync_pts_inc;
static u32 vsync_pts_inc_scale;
static u32 vsync_pts_inc_scale_base = 1;
@@ -808,6 +810,7 @@ u32 trickmode_fffb;
atomic_t trickmode_framedone = ATOMIC_INIT(0);
atomic_t video_sizechange = ATOMIC_INIT(0);
atomic_t video_unreg_flag = ATOMIC_INIT(0);
atomic_t video_inirq_flag = ATOMIC_INIT(0);
atomic_t video_pause_flag = ATOMIC_INIT(0);
int trickmode_duration;
int trickmode_duration_count;
@@ -1503,7 +1506,6 @@ static void zoom_get_vert_pos(struct vframe_s *vf, u32 vpp_3d_mode, u32 *ls,
*rs = *ls + (height >> 1);
*re = *le + (height >> 1);
}
}
if ((process_3d_type & MODE_3D_TO_2D_MASK)
|| (process_3d_type & MODE_3D_OUT_LR)) {
/* same width,half height */
@@ -1511,6 +1513,7 @@ static void zoom_get_vert_pos(struct vframe_s *vf, u32 vpp_3d_mode, u32 *ls,
*le = zoom_end_y_lines;
*rs = zoom_start_y_lines + (height >> 1);
*re = zoom_end_y_lines + (height >> 1);
}
}
break;
case VPP_3D_MODE_LR:
@@ -1562,7 +1565,7 @@ static void zoom_get_vert_pos(struct vframe_s *vf, u32 vpp_3d_mode, u32 *ls,
#endif
static void zoom_display_horz(int hscale)
{
u32 ls, le, rs, re;
u32 ls = 0, le = 0, rs = 0, re = 0;
#ifdef TV_REVERSE
int content_w, content_l, content_r;
#endif
@@ -1622,7 +1625,8 @@ static void zoom_display_horz(int hscale)
if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL) {
VSYNC_WR_MPEG_REG(AFBC_SIZE_OUT,
(VSYNC_RD_MPEG_REG(AFBC_SIZE_OUT) & 0xffff) |
(VSYNC_RD_MPEG_REG(AFBC_SIZE_OUT)
& 0xffff) |
(((r_aligned - l_aligned) / h_skip) << 16));
}
#ifdef TV_REVERSE
@@ -1681,25 +1685,28 @@ static void vd2_zoom_display_horz(int hscale)
if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXBB) {
int l_aligned;
int r_aligned;
int h_skip = cur_frame_par->hscale_skip_count + 1;
if ((zoom2_start_x_lines > 0) ||
(zoom2_end_x_lines < ori2_end_x_lines)) {
l_aligned = round_down(ori2_start_x_lines, 32);
r_aligned = round_up(ori2_end_x_lines + 1, 32);
} else {
l_aligned = round_down(ori2_start_x_lines, 32);
r_aligned = round_up(ori2_end_x_lines + 1, 32);
l_aligned = round_down(zoom2_start_x_lines, 32);
r_aligned = round_up(zoom2_end_x_lines + 1, 32);
}
VSYNC_WR_MPEG_REG(VD2_AFBC_VD_CFMT_W,
((r_aligned - l_aligned) << 16) |
(r_aligned / 2 - l_aligned / 2));
(((r_aligned - l_aligned) / h_skip) << 16) |
((r_aligned / 2 - l_aligned / 2) / h_skip));
VSYNC_WR_MPEG_REG(VD2_AFBC_MIF_HOR_SCOPE,
((l_aligned / 32) << 16) |
((r_aligned / 32) - 1));
if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL) {
VSYNC_WR_MPEG_REG(VD2_AFBC_SIZE_OUT,
(VSYNC_RD_MPEG_REG(VD2_AFBC_SIZE_OUT) &
0xffff) | ((r_aligned - l_aligned) << 16));
(VSYNC_RD_MPEG_REG(VD2_AFBC_SIZE_OUT)
& 0xffff) |
(((r_aligned - l_aligned) / h_skip) << 16));
}
#ifdef TV_REVERSE
if (reverse) {
@@ -1909,13 +1916,18 @@ static void vd2_zoom_display_vert(void)
if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXBB) {
int t_aligned;
int b_aligned;
int ori_t_aligned;
int ori_b_aligned;
int v_skip = cur_frame_par->vscale_skip_count + 1;
t_aligned = round_down(zoom2_start_y_lines, 4);
b_aligned = round_up(zoom2_end_y_lines + 1, 4);
ori_t_aligned = round_down(ori2_start_y_lines, 4);
ori_b_aligned = round_up(ori2_end_y_lines + 1, 4);
/* TODO: afbc setting only support 420 for now */
VSYNC_WR_MPEG_REG(VD2_AFBC_VD_CFMT_H,
(b_aligned - t_aligned) / 2);
(b_aligned - t_aligned) / 2 / v_skip);
VSYNC_WR_MPEG_REG(VD2_AFBC_MIF_VER_SCOPE,
((t_aligned / 4) << 16) |
@@ -1926,12 +1938,14 @@ static void vd2_zoom_display_vert(void)
(zoom2_end_y_lines - t_aligned));
VSYNC_WR_MPEG_REG(VD2_AFBC_SIZE_IN,
(VSYNC_RD_MPEG_REG(VD2_AFBC_SIZE_IN) & 0xffff0000) |
(b_aligned - t_aligned));
(VSYNC_RD_MPEG_REG(VD2_AFBC_SIZE_IN)
& 0xffff0000) |
(ori_b_aligned - ori_t_aligned));
if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL) {
VSYNC_WR_MPEG_REG(VD2_AFBC_SIZE_OUT,
(VSYNC_RD_MPEG_REG(VD2_AFBC_SIZE_OUT) & 0xffff0000)
| (b_aligned - t_aligned));
(VSYNC_RD_MPEG_REG(VD2_AFBC_SIZE_OUT)
& 0xffff0000) |
((b_aligned - t_aligned) / v_skip));
}
}
}
@@ -2593,6 +2607,7 @@ static void viu_set_dcu(struct vpp_frame_par_s *frame_par, struct vframe_s *vf)
u32 type = vf->type, bit_mode = 0;
bool vf_with_el = false;
pr_debug("set dcu for vd1 %p, type:0x%x\n", vf, type);
if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXBB) {
if (frame_par->nocomp)
type &= ~VIDTYPE_COMPRESS;
@@ -2642,10 +2657,8 @@ static void viu_set_dcu(struct vpp_frame_par_s *frame_par, struct vframe_s *vf)
VFORMATTER_EN);
else
VSYNC_WR_MPEG_REG(AFBC_VD_CFMT_CTRL,
(is_dolby_vision_on() ?
HFORMATTER_REPEAT |
(0xc << VFORMATTER_INIPHASE_BIT) :
HFORMATTER_RRT_PIXEL0) |
HFORMATTER_REPEAT |
(0xc << VFORMATTER_INIPHASE_BIT) |
HFORMATTER_YC_RATIO_2_1 |
HFORMATTER_EN |
VFORMATTER_RPTLINE0_EN |
@@ -2668,15 +2681,13 @@ static void viu_set_dcu(struct vpp_frame_par_s *frame_par, struct vframe_s *vf)
VFORMATTER_EN);
else
VSYNC_WR_MPEG_REG(AFBC_VD_CFMT_CTRL,
(is_dolby_vision_on() ?
HFORMATTER_REPEAT |
(0xc << VFORMATTER_INIPHASE_BIT) :
HFORMATTER_RRT_PIXEL0) |
HFORMATTER_YC_RATIO_2_1 |
HFORMATTER_EN |
VFORMATTER_RPTLINE0_EN |
(0x8 << VFORMATTER_PHASE_BIT) |
VFORMATTER_EN);
HFORMATTER_REPEAT |
(0xc << VFORMATTER_INIPHASE_BIT) |
HFORMATTER_YC_RATIO_2_1 |
HFORMATTER_EN |
VFORMATTER_RPTLINE0_EN |
(0x8 << VFORMATTER_PHASE_BIT) |
VFORMATTER_EN);
}
if ((VSYNC_RD_MPEG_REG(DI_POST_CTRL) & 0x100) == 0)
VSYNC_WR_MPEG_REG_BITS(VIU_MISC_CTRL0 +
@@ -2684,6 +2695,8 @@ static void viu_set_dcu(struct vpp_frame_par_s *frame_par, struct vframe_s *vf)
VSYNC_WR_MPEG_REG_BITS(VIU_MISC_CTRL0 +
cur_dev->viu_off, 1, 20, 1);
VSYNC_WR_MPEG_REG(VD1_IF0_GEN_REG +
cur_dev->viu_off, 0);
return;
} else {
@@ -2702,6 +2715,10 @@ static void viu_set_dcu(struct vpp_frame_par_s *frame_par, struct vframe_s *vf)
}
VSYNC_WR_MPEG_REG_BITS(VD1_IF0_GEN_REG3,
(bit_mode&0x3), 8, 2);
if ((vf->type & VIDTYPE_MVC) && (!vf_with_el))
VSYNC_WR_MPEG_REG_BITS(
VD2_IF0_GEN_REG3,
(bit_mode & 0x3), 8, 2);
VSYNC_WR_MPEG_REG_BITS(DI_IF1_GEN_REG3,
(bit_mode&0x3), 8, 2);
if (is_meson_txl_cpu() || is_meson_txlx_cpu())
@@ -2836,7 +2853,7 @@ static void viu_set_dcu(struct vpp_frame_par_s *frame_par, struct vframe_s *vf)
((type & VIDTYPE_VIU_422) ?
0 :
VFORMATTER_EN));
pr_info("\tvd1 set fmt(dovi tv)\n");
pr_debug("\tvd1 set fmt(dovi tv)\n");
} else if (is_meson_gxtvbb_cpu() || is_meson_txl_cpu() ||
is_meson_txlx_cpu()) {
if ((vf->width >= 3840) &&
@@ -3128,9 +3145,8 @@ static void vd2_set_dcu(struct vpp_frame_par_s *frame_par, struct vframe_s *vf)
static const u32 vpat[] = { 0, 0x8, 0x9, 0xa, 0xb, 0xc };
u32 u, v;
u32 type = vf->type, bit_mode = 0;
u32 skip_count = 0;
pr_info("set dcu for vd2 %p, type:0x%x\n", vf, type);
pr_debug("set dcu for vd2 %p, type:0x%x\n", vf, type);
if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXBB) {
if (type & VIDTYPE_COMPRESS) {
r = (3 << 24) |
@@ -3138,9 +3154,9 @@ static void vd2_set_dcu(struct vpp_frame_par_s *frame_par, struct vframe_s *vf)
(1 << 14) | /*burst1 1*/
(vf->bitdepth & BITDEPTH_MASK);
if (skip_count)
if (frame_par->hscale_skip_count)
r |= 0x33;
if (skip_count)
if (frame_par->vscale_skip_count)
r |= 0xcc;
#ifdef TV_REVERSE
@@ -3219,6 +3235,8 @@ static void vd2_set_dcu(struct vpp_frame_par_s *frame_par, struct vframe_s *vf)
#endif
VSYNC_WR_MPEG_REG_BITS(VIU_MISC_CTRL1 +
cur_dev->viu_off, 1, 1, 1);
VSYNC_WR_MPEG_REG(VD2_IF0_GEN_REG +
cur_dev->viu_off, 0);
return;
} else {
if ((vf->bitdepth & BITDEPTH_Y10) &&
@@ -3268,7 +3286,7 @@ static void vd2_set_dcu(struct vpp_frame_par_s *frame_par, struct vframe_s *vf)
}
}
if (skip_count)
if (frame_par->hscale_skip_count)
r |= VDIF_CHROMA_HZ_AVG | VDIF_LUMA_HZ_AVG;
VSYNC_WR_MPEG_REG(VD2_IF0_GEN_REG, r);
@@ -3307,7 +3325,7 @@ static void vd2_set_dcu(struct vpp_frame_par_s *frame_par, struct vframe_s *vf)
(((type & VIDTYPE_VIU_422) ? 0x10 : 0x08)
<< VFORMATTER_PHASE_BIT) |
VFORMATTER_EN);
pr_info("\tvd2 set fmt(dovi tv)\n");
pr_debug("\tvd2 set fmt(dovi tv)\n");
} else if (is_meson_gxtvbb_cpu() || is_meson_txl_cpu() ||
is_meson_txlx_cpu()) {
if ((vf->width >= 3840) &&
@@ -3394,13 +3412,13 @@ static void vd2_set_dcu(struct vpp_frame_par_s *frame_par, struct vframe_s *vf)
}
}
/* LOOP/SKIP pattern */
pat = vpat[skip_count];
pat = vpat[frame_par->vscale_skip_count];
if (type & VIDTYPE_VIU_FIELD) {
loop = 0;
if (type & VIDTYPE_INTERLACE)
pat = vpat[skip_count >> 1];
pat = vpat[frame_par->vscale_skip_count >> 1];
} else if (type & VIDTYPE_MVC) {
loop = 0x11;
pat = 0x80;
@@ -3652,10 +3670,12 @@ static inline bool vpts_expire(struct vframe_s *cur_vf,
tsync_avevent_locked(VIDEO_TSTAMP_DISCONTINUITY,
pts);
pr_info("discontinue, systime=0x%x vpts=0x%x next_vf->pts = 0x%x\n",
systime,
pts,
next_vf->pts);
/* pr_info("discontinue,
* systime=0x%x vpts=0x%x next_vf->pts = 0x%x\n",
* systime,
* pts,
* next_vf->pts);
*/
/* pts==0 is a keep frame maybe. */
if (systime > next_vf->pts || next_vf->pts == 0)
@@ -4128,10 +4148,113 @@ static void dmc_adjust_for_mali_vpu(unsigned int width, unsigned int height)
}
}
void correct_vd1_mif_size_for_DV(struct vpp_frame_par_s *par)
{
u32 aligned_mask = 0xfffffffe;
u32 old_len;
if ((is_dolby_vision_on() == true) &&
(par->VPP_line_in_length_ > 0)) {
if (cur_dispbuf2) {
/*
*if (cur_dispbuf2->type
* & VIDTYPE_COMPRESS)
* aligned_mask = 0xffffffc0;
*else
*/
aligned_mask = 0xfffffffc;
}
#if 0 /* def TV_REVERSE */
if (reverse) {
par->VPP_line_in_length_
&= 0xfffffffe;
par->VPP_hd_end_lines_
&= 0xfffffffe;
par->VPP_hd_start_lines_ =
par->VPP_hd_end_lines_ + 1
- par->VPP_line_in_length_;
} else
#endif
{
par->VPP_line_in_length_
&= aligned_mask;
par->VPP_hd_start_lines_
&= aligned_mask;
par->VPP_hd_end_lines_ =
par->VPP_hd_start_lines_ +
par->VPP_line_in_length_ - 1;
/* if have el layer, need 2 pixel align by height */
if (cur_dispbuf2) {
old_len =
par->VPP_vd_end_lines_ -
par->VPP_vd_start_lines_ + 1;
if (old_len & 1)
par->VPP_vd_end_lines_--;
if (par->VPP_vd_start_lines_ & 1) {
par->VPP_vd_start_lines_--;
par->VPP_vd_end_lines_--;
}
old_len =
par->VPP_vd_end_lines_ -
par->VPP_vd_start_lines_ + 1;
old_len = old_len >> par->vscale_skip_count;
if (par->VPP_pic_in_height_ < old_len)
par->VPP_pic_in_height_ = old_len;
}
}
}
}
#if DEBUG_TMP
void correct_vd2_mif_size_for_DV(
struct vpp_frame_par_s *par,
struct vframe_s *bl_vf)
{
int width_bl, width_el, line_in_length;
int shift;
if ((is_dolby_vision_on() == true) &&
(par->VPP_line_in_length_ > 0)) {
width_el = (cur_dispbuf2->type
& VIDTYPE_COMPRESS) ?
cur_dispbuf2->compWidth :
cur_dispbuf2->width;
width_bl = (bl_vf->type
& VIDTYPE_COMPRESS) ?
bl_vf->compWidth :
bl_vf->width;
if (width_el >= width_bl)
shift = 0;
else
shift = 1;
zoom2_start_x_lines =
par->VPP_hd_start_lines_ >> shift;
line_in_length =
par->VPP_line_in_length_ >> shift;
zoom2_end_x_lines
&= 0xfffffffe;
line_in_length
&= 0xfffffffe;
if (line_in_length > 1)
zoom2_end_x_lines =
zoom2_start_x_lines +
line_in_length - 1;
else
zoom2_end_x_lines = zoom2_start_x_lines;
zoom2_start_y_lines =
par->VPP_vd_start_lines_ >> shift;
if (zoom2_start_y_lines >= zoom2_end_y_lines)
zoom2_end_y_lines = zoom2_start_y_lines;
/* TODO: if el len is 0, need disable bl */
}
}
#endif
#ifdef FIQ_VSYNC
void vsync_fisr(void)
void vsync_fisr_in(void)
#else
static irqreturn_t vsync_isr(int irq, void *dev_id)
static irqreturn_t vsync_isr_in(int irq, void *dev_id)
#endif
{
int hold_line;
@@ -4152,10 +4275,11 @@ static irqreturn_t vsync_isr(int irq, void *dev_id)
#endif
#if DEBUG_TMP
struct vframe_s *toggle_vf = NULL;
int ret;
#endif
struct vframe_s *toggle_frame = NULL;
int video1_off_req = 0;
struct vframe_s *cur_dispbuf_back = cur_dispbuf;
static struct vframe_s *pause_vf;
if (debug_flag & DEBUG_FLAG_VSYNC_DONONE)
return IRQ_HANDLED;
@@ -4263,9 +4387,6 @@ static irqreturn_t vsync_isr(int irq, void *dev_id)
/* check video frame before VECM process */
if (is_dolby_vision_enable() && vf)
dolby_vision_check_hdr10(vf);
#if defined(CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_VECM)
amvecm_on_vs(vf);
#endif
#endif
#ifdef CONFIG_TVIN_VDIN
@@ -4340,7 +4461,9 @@ static irqreturn_t vsync_isr(int irq, void *dev_id)
if (atomic_read(&video_unreg_flag))
goto exit;
if (atomic_read(&video_pause_flag))
if (atomic_read(&video_pause_flag)
&& (!((video_global_output == 1)
&& (video_enabled != video_status_saved))))
goto exit;
#ifdef CONFIG_AMLOGIC_MEDIA_VSYNC_RDMA
if (is_vsync_rdma_enable())
@@ -4484,6 +4607,11 @@ static irqreturn_t vsync_isr(int irq, void *dev_id)
#if defined(CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_VECM)
refresh_on_vs(vf);
if (amvecm_on_vs(
(cur_dispbuf != &vf_local)
? cur_dispbuf : NULL,
vf, CSC_FLAG_CHECK_OUTPUT) == 1)
break;
#endif
if (is_dolby_vision_enable()
@@ -4498,12 +4626,12 @@ static irqreturn_t vsync_isr(int irq, void *dev_id)
*/
if (vf && hdmiin_frame_check &&
(vf->source_type == VFRAME_SOURCE_TYPE_HDMI) &&
(video_vf_disp_mode_get(vf) !=
VFRAME_DISP_MODE_OK) &&
(video_vf_disp_mode_get(vf) ==
VFRAME_DISP_MODE_UNKNOWN) &&
(hdmiin_frame_check_cnt++ < 10))
break;
hdmiin_frame_check_cnt = 0;
else
hdmiin_frame_check_cnt = 0;
vf = video_vf_get();
if (!vf)
@@ -4528,13 +4656,15 @@ static irqreturn_t vsync_isr(int irq, void *dev_id)
}
}
vsync_toggle_frame(vf);
#if DEBUG_TMP
if (is_dolby_vision_enable())
toggle_vf = dolby_vision_toggle_frame(vf);
else
#endif
toggle_frame = vf;
if (0/*is_dolby_vision_enable()*/) { /*DEBUG_TMP*/
//toggle_vf = dolby_vision_toggle_frame(vf);
video_pause_global = 0;
} else {
cur_dispbuf2 = NULL;
video_pause_global = 2;
pause_vf = NULL;
}
if (trickmode_fffb == 1) {
trickmode_vpts = vf->pts;
#ifdef CONFIG_AMLOGIC_MEDIA_VSYNC_RDMA
@@ -4594,11 +4724,20 @@ static irqreturn_t vsync_isr(int irq, void *dev_id)
if (is_dolby_vision_enable()
&& dolby_vision_need_wait())
break;
#if defined(CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_VECM)
refresh_on_vs(vf);
if (amvecm_on_vs(
(cur_dispbuf != &vf_local)
? cur_dispbuf : NULL,
vf, CSC_FLAG_CHECK_OUTPUT) == 1)
break;
#endif
#endif
vf = video_vf_get();
if (!vf)
break;
vsync_toggle_frame(vf);
toggle_frame = vf;
#if DEBUG_TMP
if (is_dolby_vision_enable())
toggle_vf =
@@ -4633,8 +4772,23 @@ static irqreturn_t vsync_isr(int irq, void *dev_id)
cur_dispbuf);
} else
vsync_toggle_frame(cur_dispbuf);
/*DEBUG_TMP*/
if (0/*is_dolby_vision_enable()*/) {
pause_vf = cur_dispbuf;
video_pause_global = 1;
} else {
pause_vf = NULL;
video_pause_global = 2;
}
}
#if DEBUG_TMP
if (pause_vf && (video_pause_global == 1)
&& is_dolby_vision_enable()) {
toggle_vf = pause_vf;
dolby_vision_parse_metadata(cur_dispbuf, true);
dolby_vision_set_toggle_flag(1);
}
#endif
break;
}
@@ -4657,22 +4811,50 @@ static irqreturn_t vsync_isr(int irq, void *dev_id)
SET_FILTER:
#if DEBUG_TMP
if (is_dolby_vision_enable()) {
u32 skip_mode = 0;
if (cur_frame_par)
skip_mode =
(cur_frame_par->hscale_skip_count << 16)
| cur_frame_par->vscale_skip_count;
dolby_vision_process(toggle_vf, skip_mode);
dolby_vision_update_setting();
}
#if defined(CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_VECM)
amvecm_on_vs(
(cur_dispbuf != &vf_local) ? cur_dispbuf : NULL,
toggle_frame,
toggle_frame ? CSC_FLAG_TOGGLE_FRAME : 0);
#endif
#endif
/* filter setting management */
if ((frame_par_ready_to_set) || (frame_par_force_to_set)) {
cur_frame_par = next_frame_par;
frame_par_di_set = 1;
}
#if DEBUG_TMP
if (is_dolby_vision_enable()) {
u32 frame_size = 0, h_size, v_size;
if (cur_frame_par) {
if (cur_frame_par->VPP_hd_start_lines_
>= cur_frame_par->VPP_hd_end_lines_)
h_size = 0;
else
h_size = cur_frame_par->VPP_hd_end_lines_
- cur_frame_par->VPP_hd_start_lines_ + 1;
h_size /= (cur_frame_par->hscale_skip_count + 1);
if (cur_frame_par->VPP_vd_start_lines_
>= cur_frame_par->VPP_vd_end_lines_)
v_size = 0;
else
v_size = cur_frame_par->VPP_vd_end_lines_
- cur_frame_par->VPP_vd_start_lines_ + 1;
v_size /= (cur_frame_par->vscale_skip_count + 1);
frame_size = (h_size << 16) | v_size;
} else if (toggle_vf) {
h_size = (toggle_vf->type & VIDTYPE_COMPRESS) ?
toggle_vf->compWidth : toggle_vf->width;
v_size = (toggle_vf->type & VIDTYPE_COMPRESS) ?
toggle_vf->compHeight : toggle_vf->height;
frame_size = (h_size << 16) | v_size;
}
dolby_vision_process(toggle_vf, frame_size);
dolby_vision_update_setting();
}
#endif
if ((platform_type == 1) || (platform_type == 0)) {
if (mode_3d_changed) {
mode_3d_changed = 0;
@@ -4880,7 +5062,7 @@ SET_FILTER:
if (cur_dispbuf) {
u32 zoom_start_y, zoom_end_y;
correct_vd1_mif_size_for_DV(cur_frame_par);
if (cur_dispbuf->type & VIDTYPE_INTERLACE) {
if (cur_dispbuf->type & VIDTYPE_VIU_FIELD) {
zoom_start_y =
@@ -4929,9 +5111,11 @@ SET_FILTER:
if (is_dolby_vision_enable() && cur_dispbuf2) {
zoom2_start_x_lines = ori2_start_x_lines;
zoom2_end_x_lines = ori2_end_x_lines;
vd2_zoom_display_horz(0);
zoom2_start_y_lines = ori2_start_y_lines;
zoom2_end_y_lines = ori2_end_y_lines;
correct_vd2_mif_size_for_DV(
cur_frame_par, cur_dispbuf);
vd2_zoom_display_horz(0);
vd2_zoom_display_vert();
}
#endif
@@ -5108,6 +5292,13 @@ SET_FILTER:
cur_frame_par->supsc0_vert_ratio) & 0x1fff));
}
#endif
/* work around to cut the last green line*/
/*when two layer dv display and do vskip */
if (is_dolby_vision_on() &&
(cur_frame_par->vscale_skip_count > 0)
&& cur_dispbuf2
&& (cur_frame_par->VPP_pic_in_height_ > 0))
cur_frame_par->VPP_pic_in_height_--;
VSYNC_WR_MPEG_REG(VPP_PIC_IN_HEIGHT + cur_dev->vpp_off,
cur_frame_par->VPP_pic_in_height_);
@@ -5397,6 +5588,9 @@ SET_FILTER:
if (debug_flag & DEBUG_FLAG_BLACKOUT)
pr_info("AFBC off now.\n");
VSYNC_WR_MPEG_REG(AFBC_ENABLE, 0);
VSYNC_WR_MPEG_REG(VD2_AFBC_ENABLE, 0);
VSYNC_WR_MPEG_REG(VD1_IF0_GEN_REG, 0);
VSYNC_WR_MPEG_REG(VD2_IF0_GEN_REG, 0);
}
#ifdef CONFIG_AMLOGIC_MEDIA_VSYNC_RDMA
@@ -5461,6 +5655,24 @@ SET_FILTER:
}
#ifdef FIQ_VSYNC
void vsync_fisr(void)
{
atomic_set(&video_inirq_flag, 1);
vsync_fisr_in();
atomic_set(&video_inirq_flag, 0);
}
#else
static irqreturn_t vsync_isr(int irq, void *dev_id)
{
irqreturn_t ret;
atomic_set(&video_inirq_flag, 1);
ret = vsync_isr_in(irq, dev_id);
atomic_set(&video_inirq_flag, 0);
return ret;
}
#endif
/*********************************************************
@@ -5554,11 +5766,14 @@ EXPORT_SYMBOL(query_video_status);
static void video_vf_unreg_provider(void)
{
ulong flags;
struct vframe_s *el_vf = NULL;
new_frame_count = 0;
first_frame_toggled = 0;
atomic_set(&video_unreg_flag, 1);
while (atomic_read(&video_inirq_flag) > 0)
schedule();
spin_lock_irqsave(&lock, flags);
#ifdef CONFIG_AMLOGIC_MEDIA_VSYNC_RDMA
@@ -5574,10 +5789,15 @@ static void video_vf_unreg_provider(void)
cur_dispbuf = &vf_local;
cur_dispbuf->video_angle = 0;
}
#if DEBUG_TMP
if (is_dolby_vision_enable())
if (0/*is_dolby_vision_enable()*/) {/*DEBUG_TMP*/
if (cur_dispbuf2 == &vf_local2)
cur_dispbuf2 = NULL;
else if (cur_dispbuf2 != NULL) {
vf_local2 = *cur_dispbuf2;
el_vf = &vf_local2;
}
cur_dispbuf2 = NULL;
#endif
}
if (trickmode_fffb) {
atomic_set(&trickmode_framedone, 0);
to_notify_trick_wait = false;
@@ -5599,7 +5819,7 @@ static void video_vf_unreg_provider(void)
if (cur_dispbuf) {
/* TODO: mod gate */
/* switch_mod_gate_by_name("ge2d", 1); */
vf_keep_current(cur_dispbuf);
vf_keep_current(cur_dispbuf, el_vf);
/* TODO: mod gate */
/* switch_mod_gate_by_name("ge2d", 0); */
}
@@ -5608,7 +5828,7 @@ static void video_vf_unreg_provider(void)
#else
/* if (!trickmode_fffb) */
if (cur_dispbuf)
vf_keep_current(cur_dispbuf);
vf_keep_current(cur_dispbuf, el_vf);
if (hdmi_in_onvideo == 0)
tsync_avevent(VIDEO_STOP, 0);
#endif

View File

@@ -66,6 +66,8 @@ static unsigned long keep_y_addr, keep_u_addr, keep_v_addr;
static int keep_video_on;
static int keep_id;
static int keep_head_id;
static int keep_el_id;
static int keep_el_head_id;
#define Y_BUFFER_SIZE 0x400000 /* for 1920*1088 */
#define U_BUFFER_SIZE 0x100000 /* compatible with NV21 */
@@ -602,8 +604,9 @@ static int canvas_dup(ulong dst, ulong src_paddr, ulong size)
#ifdef RESERVE_CLR_FRAME
static int free_alloced_keep_buffer(void)
{
pr_info("free_alloced_keep_buffer %p.%p.%p\n",
/*pr_info("free_alloced_keep_buffer %p.%p.%p\n",
(void *)keep_y_addr, (void *)keep_u_addr, (void *)keep_v_addr);
*/
if (keep_y_addr) {
codec_mm_free_for_dma(MEM_NAME, keep_y_addr);
keep_y_addr = 0;
@@ -690,9 +693,8 @@ void try_free_keep_video(int flags)
int free_scatter_keeper = flags & 0x1;
if (keep_video_on || free_scatter_keeper) {
pr_info("disbled keep video before free keep buffer.\n");
/*pr_info("disbled keep video before free keep buffer.\n");*/
keep_video_on = 0;
update_cur_dispbuf(NULL);
if (!get_video_enabled()) {
/*if not disable video,changed to 2 for */
pr_info("disbled video for next before free keep buffer!\n");
@@ -701,46 +703,101 @@ void try_free_keep_video(int flags)
safe_disble_videolayer();
}
}
if (free_scatter_keeper && keep_id > 0) {
pr_info("try_free_keep_video keepid\n");
codec_mm_keeper_unmask_keeper(keep_id, 0);
keep_id = -1;
}
if (free_scatter_keeper && keep_head_id > 0) {
pr_info("try_free_keep_video keep_head_id\n");
codec_mm_keeper_unmask_keeper(keep_head_id, 0);
keep_head_id = -1;
}
mutex_lock(&video_keeper_mutex);
video_keeper_new_frame_notify();
free_alloced_keep_buffer();
mutex_unlock(&video_keeper_mutex);
}
EXPORT_SYMBOL(try_free_keep_video);
#endif
static void video_keeper_update_keeper_mem(
void *mem_handle, int type,
int *id)
{
int ret;
int old_id = *id;
if (!mem_handle)
return;
ret = codec_mm_keeper_mask_keep_mem(mem_handle,
type);
if (ret > 0) {
if (old_id > 0 && ret != old_id) {
/*wait 80 ms for vsync post.*/
codec_mm_keeper_unmask_keeper(old_id, 120);
}
*id = ret;
}
}
static void video_keeper_frame_keep_locked(
struct vframe_s *cur_dispbuf,
struct vframe_s *cur_dispbuf_el)
{
int type = MEM_TYPE_CODEC_MM;
if (cur_dispbuf->type & VIDTYPE_SCATTER)
type = MEM_TYPE_CODEC_MM_SCATTER;
video_keeper_update_keeper_mem(
cur_dispbuf->mem_handle,
type,
&keep_id);
video_keeper_update_keeper_mem(
cur_dispbuf->mem_head_handle,
MEM_TYPE_CODEC_MM,
&keep_head_id);
if (cur_dispbuf_el) {
if (cur_dispbuf->type & VIDTYPE_SCATTER)
type = MEM_TYPE_CODEC_MM_SCATTER;
else
type = MEM_TYPE_CODEC_MM;
video_keeper_update_keeper_mem(
cur_dispbuf_el->mem_handle,
type,
&keep_el_id);
video_keeper_update_keeper_mem(
cur_dispbuf_el->mem_head_handle,
MEM_TYPE_CODEC_MM,
&keep_el_head_id);
}
}
/*
* call in irq.
*don't used mutex
*/
void video_keeper_new_frame_notify(void)
{
if (keep_video_on) {
pr_info("new toggle keep_id\n");
pr_info("new frame show, free keeper\n");
keep_video_on = 0;
}
if (keep_id > 0) {
/*wait 80 ms for vsync post.*/
pr_info("new frame show, free keeper\n");
codec_mm_keeper_unmask_keeper(keep_id, 120);
keep_id = -1;
}
if (keep_head_id > 0) {
/*wait 80 ms for vsync post.*/
pr_info("new frame show, free keeper head\n");
codec_mm_keeper_unmask_keeper(keep_head_id, 120);
keep_head_id = -1;
}
if (keep_el_id > 0) {
/*wait 80 ms for vsync post.*/
codec_mm_keeper_unmask_keeper(keep_el_id, 120);
keep_el_id = -1;
}
if (keep_el_head_id > 0) {
/*wait 80 ms for vsync post.*/
codec_mm_keeper_unmask_keeper(keep_el_head_id, 120);
keep_el_head_id = -1;
}
return;
}
static unsigned int vf_keep_current_locked(struct vframe_s *cur_dispbuf)
static unsigned int vf_keep_current_locked(
struct vframe_s *cur_dispbuf,
struct vframe_s *cur_dispbuf_el)
{
u32 cur_index;
u32 y_index, u_index, v_index;
@@ -777,36 +834,8 @@ static unsigned int vf_keep_current_locked(struct vframe_s *cur_dispbuf)
pr_info("keep exit is skip VPP_VD1_POSTBLEND\n");
return 0;
}
if (1) {
int ret;
int old_keep = keep_id;
int old_head_keep = keep_head_id;
int type = MEM_TYPE_CODEC_MM;
if (cur_dispbuf->type & VIDTYPE_SCATTER)
type = MEM_TYPE_CODEC_MM_SCATTER;
ret = codec_mm_keeper_mask_keep_mem(cur_dispbuf->mem_handle,
type);
if (ret > 0) {
keep_id = ret;
if (old_keep > 0 && keep_id != old_keep) {
/*wait 80 ms for vsync post.*/
codec_mm_keeper_unmask_keeper(old_keep, 120);
}
}
ret = codec_mm_keeper_mask_keep_mem(
cur_dispbuf->mem_head_handle,
MEM_TYPE_CODEC_MM);
if (ret > 0) {
keep_head_id = ret;
if (old_head_keep > 0 &&
keep_head_id != old_head_keep) {
/*wait 80 ms for vsync post.*/
codec_mm_keeper_unmask_keeper(old_head_keep,
120);
}
}
}
video_keeper_frame_keep_locked(cur_dispbuf,
cur_dispbuf_el);
#ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC
if (codec_mm_video_tvp_enabled()) {
@@ -999,12 +1028,15 @@ static unsigned int vf_keep_current_locked(struct vframe_s *cur_dispbuf)
return 0;
}
unsigned int vf_keep_current(struct vframe_s *cur_dispbuf)
unsigned int vf_keep_current(
struct vframe_s *cur_dispbuf,
struct vframe_s *cur_dispbuf2)
{
unsigned int ret;
mutex_lock(&video_keeper_mutex);
ret = vf_keep_current_locked(cur_dispbuf);
ret = vf_keep_current_locked(cur_dispbuf,
cur_dispbuf2);
mutex_unlock(&video_keeper_mutex);
return ret;
}

View File

@@ -1803,6 +1803,11 @@ static void vpp_set_scaler(u32 process_3d_type, u32 src_width,
next_frame_par->supsc1_hori_ratio = 0;
else
next_frame_par->supsc1_hori_ratio = 1;
/* disble sp1 for this case */
if ((vpp_wide_mode == VIDEO_WIDEOPTION_NONLINEAR)
&& (next_frame_par->supscl_path
== sup0_pp_sp1_scpath))
next_frame_par->supsc1_hori_ratio = 0;
next_frame_par->supsc0_enable =
(next_frame_par->supsc0_hori_ratio ||
next_frame_par->supsc0_enable) ? 1 : 0;
@@ -1965,6 +1970,21 @@ static void vpp_set_scaler(u32 process_3d_type, u32 src_width,
next_frame_par->supsc1_vert_ratio;
}
if ((vpp_wide_mode == VIDEO_WIDEOPTION_NONLINEAR) &&
(next_frame_par->VPP_hsc_endp >
next_frame_par->VPP_hsc_startp)) {
s32 start, end;
struct vppfilter_mode_s *filter =
&next_frame_par->vpp_filter;
start = next_frame_par->VPP_hsc_startp;
end = next_frame_par->VPP_hsc_endp;
calculate_non_linear_ratio(
(filter->vpp_hsc_start_phase_step >> 6),
end - start,
next_frame_par);
next_frame_par->VPP_hsc_linear_startp =
next_frame_par->VPP_hsc_linear_endp = (start + end) / 2;
}
if (h_crop_enable) {
next_frame_par->VPP_hd_start_lines_ += video_source_crop_left;
next_frame_par->VPP_hd_end_lines_ += video_source_crop_left;

View File

@@ -0,0 +1,8 @@
config AMLOGIC_TEE
tristate "TEE Support for Amlogic"
default n
help
Amlogic TEE driver
Provide TEE APIs and Debug interface in sysfs, depends on the
tee os.

View File

@@ -0,0 +1 @@
obj-$(CONFIG_AMLOGIC_TEE) += tee.o

193
drivers/amlogic/tee/tee.c Normal file
View File

@@ -0,0 +1,193 @@
/*
* drivers/amlogic/tee/tee.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.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/arm-smccc.h>
#include <linux/platform_device.h>
#include <linux/amlogic/tee.h>
#define DRIVER_NAME "tee_info"
#define DRIVER_DESC "Amlogic tee driver"
#define TEE_MSG_UID_0 0x384fb3e0
#define TEE_MSG_UID_1 0xe7f811e3
#define TEE_MSG_UID_2 0xaf630002
#define TEE_MSG_UID_3 0xa5d5c51b
#define TEE_SMC_FUNCID_CALLS_REVISION 0xFF03
#define TEE_SMC_CALLS_REVISION \
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \
ARM_SMCCC_OWNER_TRUSTED_OS_END, \
TEE_SMC_FUNCID_CALLS_REVISION)
#define TEE_SMC_FUNCID_CALLS_UID 0xFF01
#define TEE_SMC_CALLS_UID \
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \
ARM_SMCCC_OWNER_TRUSTED_OS_END, \
TEE_SMC_FUNCID_CALLS_UID)
#define TEE_SMC_FAST_CALL_VAL(func_num) \
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \
ARM_SMCCC_OWNER_TRUSTED_OS, (func_num))
#define TEE_SMC_FUNCID_GET_OS_REVISION 0x0001
#define TEE_SMC_CALL_GET_OS_REVISION \
TEE_SMC_FAST_CALL_VAL(TEE_SMC_FUNCID_GET_OS_REVISION)
#define TEE_SMC_FUNCID_LOAD_VIDEO_FW 15
#define TEE_SMC_LOAD_VIDEO_FW \
TEE_SMC_FAST_CALL_VAL(TEE_SMC_FUNCID_LOAD_VIDEO_FW)
static struct class *tee_sys_class;
struct tee_smc_calls_revision_result {
unsigned long major;
unsigned long minor;
unsigned long reserved0;
unsigned long reserved1;
};
static int tee_msg_os_revision(uint32_t *major, uint32_t *minor)
{
union {
struct arm_smccc_res smccc;
struct tee_smc_calls_revision_result result;
} res;
arm_smccc_smc(TEE_SMC_CALL_GET_OS_REVISION,
0, 0, 0, 0, 0, 0, 0, &res.smccc);
*major = res.result.major;
*minor = res.result.minor;
return 0;
}
static int tee_msg_api_revision(uint32_t *major, uint32_t *minor)
{
union {
struct arm_smccc_res smccc;
struct tee_smc_calls_revision_result result;
} res;
arm_smccc_smc(TEE_SMC_CALLS_REVISION,
0, 0, 0, 0, 0, 0, 0, &res.smccc);
*major = res.result.major;
*minor = res.result.minor;
return 0;
}
static ssize_t tee_os_version_show(struct class *class,
struct class_attribute *attr, char *buf)
{
int ret;
uint32_t major, minor;
ret = tee_msg_os_revision(&major, &minor);
if (ret)
return 0;
ret = sprintf(buf, "os version: V%d.%d\n", major, minor);
return ret;
}
static ssize_t tee_api_version_show(struct class *class,
struct class_attribute *attr, char *buf)
{
int ret;
uint32_t major, minor;
ret = tee_msg_api_revision(&major, &minor);
if (ret)
return 0;
ret = sprintf(buf, "api version: V%d.%d\n", major, minor);
return ret;
}
static CLASS_ATTR(os_version, 0644, tee_os_version_show,
NULL);
static CLASS_ATTR(api_version, 0644, tee_api_version_show,
NULL);
int tee_load_video_fw(uint32_t index)
{
struct arm_smccc_res res;
arm_smccc_smc(TEE_SMC_LOAD_VIDEO_FW,
index, 0, 0, 0, 0, 0, 0, &res);
return res.a0;
}
EXPORT_SYMBOL(tee_load_video_fw);
bool tee_enabled(void)
{
struct arm_smccc_res res;
arm_smccc_smc(TEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res);
if (res.a0 == TEE_MSG_UID_0 && res.a1 == TEE_MSG_UID_1 &&
res.a2 == TEE_MSG_UID_2 && res.a3 == TEE_MSG_UID_3)
return true;
return false;
}
EXPORT_SYMBOL(tee_enabled);
int tee_create_sysfs(void)
{
int ret;
tee_sys_class = class_create(THIS_MODULE, DRIVER_NAME);
ret = class_create_file(tee_sys_class, &class_attr_os_version);
if (ret != 0) {
pr_err("create class file os_version fail\n");
return ret;
}
ret = class_create_file(tee_sys_class, &class_attr_api_version);
if (ret != 0) {
pr_err("create class file os_version fail\n");
return ret;
}
return ret;
}
static int __init aml_tee_modinit(void)
{
return tee_create_sysfs();
}
arch_initcall(aml_tee_modinit);
static void __exit aml_tee_modexit(void)
{
class_destroy(tee_sys_class);
}
module_exit(aml_tee_modexit);
MODULE_AUTHOR("pengguang.zhu<pengguang.zhu@amlogic.com>");
MODULE_DESCRIPTION(DRIVER_TEE);
MODULE_LICENSE("GPL");

View File

@@ -48,6 +48,8 @@ extern u32 timestamp_pcrscr_enable_state(void);
extern void timestamp_pcrscr_set_adj(s32 inc);
extern void timestamp_pcrscr_set_adj_pcr(s32 inc);
extern void timestamp_apts_enable(u32 enable);
extern void timestamp_apts_start(u32 enable);

View File

@@ -451,6 +451,7 @@ struct userdata_poc_info_t {
#define AMSTREAM_SET_FRAME_BASE_PATH 0x174
#define AMSTREAM_SET_EOS 0x175
#define AMSTREAM_SET_RECEIVE_ID 0x176
#define AMSTREAM_SET_IS_RESET 0x177
/* video set ex cmd */
#define AMSTREAM_SET_EX_VIDEO_AXIS 0x260

View File

@@ -252,6 +252,8 @@ extern int power_key_pressed;
#ifdef CONFIG_AM_VIDEO2
extern void set_clone_frame_rate(unsigned int frame_rate, unsigned int delay);
#endif
extern struct vframe_provider_s *vf_get_provider_by_name(
const char *provider_name);
extern void prot_get_parameter(u32 wide_mode, struct vframe_s *vf,
struct vpp_frame_par_s *next_frame_par,

View File

@@ -25,6 +25,8 @@ void try_free_keep_video(int flags);
int __init video_keeper_init(void);
void __exit video_keeper_exit(void);
unsigned int vf_keep_current(struct vframe_s *cur_dispbuf);
unsigned int vf_keep_current(
struct vframe_s *cur_dispbuf,
struct vframe_s *cur_dispbuf2);
#endif

View File

@@ -0,0 +1,25 @@
/*
* include/linux/amlogic/tee.h
*
* 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.
*
*/
#ifndef __TEE_H__
#define __TEE_H__
extern bool tee_enabled(void);
extern int is_secload_get(void);
extern int tee_load_video_fw(uint32_t index);
#endif /* __TEE_H__ */