blob: ae5e6ae4b096ce5d7ebf894335af76e6b98783be [file] [log] [blame]
/*
* drivers/amlogic/media/frame_sync/timestamp.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.
*
*/
#define DEBUG
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/time64.h>
#include <linux/math64.h>
#include <linux/amlogic/media/frame_sync/tsync.h>
#include <linux/amlogic/media/frame_sync/tsync_pcr.h>
#include <linux/amlogic/media/utils/vdec_reg.h>
#include <linux/amlogic/media/registers/register.h>
#include <linux/amlogic/media/vout/vout_notify.h>
#include <trace/events/meson_atrace.h>
u32 acc_apts_inc;
u32 acc_apts_dec;
u32 acc_pcrscr_inc;
u32 acc_pcrscr_dec;
static DEFINE_SPINLOCK(system_time_lock);
static DEFINE_SPINLOCK(vpts_set_lock);
static s32 system_time_inc_adj;
static struct timespec64 system_time_ts;
static struct timespec64 vpts_set_ts;
static u32 system_time_ts_pts;
static u32 system_time;
static u32 system_time_up;
static u32 audio_pts_up;
static u32 audio_pts_started;
static u32 first_vpts;
static u32 first_checkin_vpts;
static u32 first_checkin_apts;
static u32 first_apts;
static u32 pcrscr_pause_pts;
static bool pcrscr_pause_pts_enable;
static u32 pcrscr_lantcy = 200*90;
static bool vpts_started;
static bool reftime_pause;
static u32 video_pts;
static u32 video_pts_ts;
static u32 audio_pts;
static u32 system_time_scale_base = 1;
static u32 system_time_scale_remainder;
#ifdef MODIFY_TIMESTAMP_INC_WITH_PLL
#define PLL_FACTOR 10000
#define HALF_VSYNC_PERIOD 750
static u32 timestamp_inc_factor = PLL_FACTOR;
void set_timestamp_inc_factor(u32 factor)
{
timestamp_inc_factor = factor;
}
#endif
bool timestamp_vpts_get_started(void)
{
return vpts_started;
}
void timestamp_vpts_set_started(bool start)
{
vpts_started = start;
}
void timestamp_reftime_pause(bool pause)
{
unsigned long flags;
spin_lock_irqsave(&system_time_lock, flags);
reftime_pause = pause;
if (pause)
system_time_up = false;
spin_unlock_irqrestore(&system_time_lock, flags);
}
u32 timestamp_vpts_get(void)
{
return video_pts;
}
EXPORT_SYMBOL(timestamp_vpts_get);
u32 timestamp_vpts_ts_get(struct timespec64 *ts)
{
unsigned long flags;
u32 r;
spin_lock_irqsave(&vpts_set_lock, flags);
*ts = vpts_set_ts;
r = video_pts_ts;
spin_unlock_irqrestore(&vpts_set_lock, flags);
return r;
}
EXPORT_SYMBOL(timestamp_vpts_ts_get);
void timestamp_vpts_reset(u32 pts)
{
video_pts = pts;
}
EXPORT_SYMBOL(timestamp_vpts_reset);
void timestamp_vpts_set(u32 pts)
{
unsigned long flags;
spin_lock_irqsave(&vpts_set_lock, flags);
getrawmonotonic64(&vpts_set_ts);
video_pts = pts;
video_pts_ts = pts;
if (!vpts_started) {
vpts_started = true;
pr_info("%lu:%lu first vpts ready\n",
vpts_set_ts.tv_sec, vpts_set_ts.tv_nsec);
}
spin_unlock_irqrestore(&vpts_set_lock, flags);
}
EXPORT_SYMBOL(timestamp_vpts_set);
void timestamp_vpts_inc(s32 val)
{
video_pts += val;
}
EXPORT_SYMBOL(timestamp_vpts_inc);
u32 timestamp_apts_get(void)
{
return audio_pts;
}
EXPORT_SYMBOL(timestamp_apts_get);
void timestamp_apts_set(u32 pts)
{
audio_pts = pts;
}
EXPORT_SYMBOL(timestamp_apts_set);
void timestamp_apts_inc(s32 inc)
{
if (audio_pts_up) {
#ifdef MODIFY_TIMESTAMP_INC_WITH_PLL
inc = inc * timestamp_inc_factor / PLL_FACTOR;
#endif
audio_pts += inc;
}
}
EXPORT_SYMBOL(timestamp_apts_inc);
void timestamp_apts_enable(u32 enable)
{
audio_pts_up = enable;
pr_info("timestamp_apts_enable enable:%x,\n", enable);
}
EXPORT_SYMBOL(timestamp_apts_enable);
void timestamp_apts_start(u32 enable)
{
audio_pts_started = enable;
pr_info("audio pts started::::::: %d\n", enable);
}
EXPORT_SYMBOL(timestamp_apts_start);
u32 timestamp_apts_started(void)
{
return audio_pts_started;
}
EXPORT_SYMBOL(timestamp_apts_started);
u32 timestamp_pcrscr_get(void)
{
if (tsync_get_mode() == TSYNC_MODE_AMASTER)
return system_time;
if (tsdemux_pcrscr_valid_cb && tsdemux_pcrscr_valid_cb()) {
if (tsync_pcr_demux_pcr_used() == 0) {
return system_time;
}
else {
if (tsdemux_pcrscr_get_cb)
return tsdemux_pcrscr_get_cb()-pcrscr_lantcy;
else
return system_time;
}
} else
return system_time;
}
EXPORT_SYMBOL(timestamp_pcrscr_get);
u32 timestamp_tsdemux_pcr_get(void)
{
if (tsdemux_pcrscr_get_cb)
return tsdemux_pcrscr_get_cb();
return (u32)-1;
}
EXPORT_SYMBOL(timestamp_tsdemux_pcr_get);
void timestamp_pcrscr_set(u32 pts)
{
/*pr_info("timestamp_pcrscr_set system time = %x\n", pts);*/
ATRACE_COUNTER("PCRSCR", pts);
system_time = pts;
}
EXPORT_SYMBOL(timestamp_pcrscr_set);
void timestamp_pcrscr_set_ts(unsigned long tv_sec, unsigned long tv_nsec,
u32 pts)
{
unsigned long flags;
struct timespec64 ts;
getrawmonotonic64(&ts);
spin_lock_irqsave(&system_time_lock, flags);
system_time_ts_pts = pts;
system_time_ts.tv_sec = tv_sec;
system_time_ts.tv_nsec = tv_nsec;
spin_unlock_irqrestore(&system_time_lock, flags);
pr_debug("timestamp_pcrscr_set_ts %lu.%.9lu: 0x%x\n",
tv_sec, tv_nsec, pts);
}
EXPORT_SYMBOL(timestamp_pcrscr_set_ts);
void timestamp_firstvpts_set(u32 pts)
{
first_vpts = pts;
pr_info("video first pts = %x\n", first_vpts);
}
EXPORT_SYMBOL(timestamp_firstvpts_set);
u32 timestamp_firstvpts_get(void)
{
return first_vpts;
}
EXPORT_SYMBOL(timestamp_firstvpts_get);
void timestamp_checkin_firstvpts_set(u32 pts)
{
first_checkin_vpts = pts;
pr_info("video first checkin pts = %x\n", first_checkin_vpts);
}
EXPORT_SYMBOL(timestamp_checkin_firstvpts_set);
void timestamp_checkin_firstapts_set(u32 pts)
{
first_checkin_apts = pts;
pr_info("audio first checkin pts =%x\n", first_checkin_apts);
}
EXPORT_SYMBOL(timestamp_checkin_firstapts_set);
u32 timestamp_checkin_firstvpts_get(void)
{
return first_checkin_vpts;
}
EXPORT_SYMBOL(timestamp_checkin_firstvpts_get);
u32 timestamp_checkin_firstapts_get(void)
{
return first_checkin_apts;
}
EXPORT_SYMBOL(timestamp_checkin_firstapts_get);
void timestamp_firstapts_set(u32 pts)
{
first_apts = pts;
pr_info("audio first pts = %x\n", first_apts);
}
EXPORT_SYMBOL(timestamp_firstapts_set);
u32 timestamp_firstapts_get(void)
{
return first_apts;
}
EXPORT_SYMBOL(timestamp_firstapts_get);
static void timestamp_reftime_setup(void)
{
unsigned long flags;
struct timespec64 t;
getrawmonotonic64(&t);
spin_lock_irqsave(&system_time_lock, flags);
if (system_time_ts.tv_sec || system_time_ts.tv_nsec) {
if (timespec64_compare(&t, &system_time_ts) > 0) {
struct timespec64 t1 = timespec64_sub(t,
system_time_ts);
s64 delay_ns = timespec64_to_ns(&t1);
u32 delay_pts = div64_s64(delay_ns * 90, 1000000LL) &
0xffffffff;
pr_info("timestamp_reftime_setup: %lu:%lu > %lu:%lu\n",
t.tv_sec, t.tv_nsec,
system_time_ts.tv_sec, system_time_ts.tv_nsec);
timestamp_pcrscr_set(system_time_ts_pts + delay_pts
- HALF_VSYNC_PERIOD);
pr_info("pcrscr set to %x->%x\n",
system_time_ts_pts,
system_time_ts_pts + delay_pts
- HALF_VSYNC_PERIOD);
system_time_ts.tv_sec = 0;
system_time_ts.tv_nsec = 0;
/* whether we start system time or not is
* determined by current decoder status is
* in paused status or not
*/
if (!reftime_pause)
system_time_up = true;
}
}
spin_unlock_irqrestore(&system_time_lock, flags);
}
void timestamp_pcrscr_inc(s32 inc)
{
timestamp_reftime_setup();
if (system_time_up) {
#ifdef MODIFY_TIMESTAMP_INC_WITH_PLL
inc = inc * timestamp_inc_factor / PLL_FACTOR;
#endif
if ((tsync_get_mode() == TSYNC_MODE_EXTERNAL) &&
(tsync_get_external_rate() >= 0)) {
system_time += tsync_get_external_rate();
} else {
system_time += inc + system_time_inc_adj;
ATRACE_COUNTER("PCRSCR", system_time);
}
}
if (pcrscr_pause_pts_enable) {
if ((s32)(system_time - pcrscr_pause_pts) >= 0) {
system_time_up = 0;
pcrscr_pause_pts_enable = 0;
}
}
}
EXPORT_SYMBOL(timestamp_pcrscr_inc);
void timestamp_pcrscr_inc_scale(s32 inc, u32 base)
{
timestamp_reftime_setup();
if (system_time_scale_base != base) {
system_time_scale_remainder =
system_time_scale_remainder *
base / system_time_scale_base;
system_time_scale_base = base;
}
if (system_time_up) {
u32 r;
if ((tsync_get_mode() == TSYNC_MODE_EXTERNAL) &&
tsync_get_external_rate() >= 0) {
system_time += tsync_get_external_rate();
} else {
system_time +=
div_u64_rem(90000ULL * inc, base, &r) +
system_time_inc_adj;
system_time_scale_remainder += r;
if (system_time_scale_remainder >=
system_time_scale_base) {
system_time++;
system_time_scale_remainder -=
system_time_scale_base;
}
}
}
if (pcrscr_pause_pts_enable) {
if ((s32)(system_time - pcrscr_pause_pts) >= 0) {
system_time_up = 0;
pcrscr_pause_pts_enable = 0;
}
ATRACE_COUNTER("PCRSCR", system_time);
}
}
EXPORT_SYMBOL(timestamp_pcrscr_inc_scale);
void timestamp_pcrscr_set_adj(s32 inc)
{
system_time_inc_adj = 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_pause_pts(u32 pts)
{
pcrscr_pause_pts = pts;
pcrscr_pause_pts_enable = true;
}
EXPORT_SYMBOL(timestamp_pcrscr_pause_pts);
void timestamp_pcrscr_enable(u32 enable)
{
system_time_up = enable;
pcrscr_pause_pts_enable = false;
}
EXPORT_SYMBOL(timestamp_pcrscr_enable);
u32 timestamp_pcrscr_enable_state(void)
{
return system_time_up;
}
EXPORT_SYMBOL(timestamp_pcrscr_enable_state);
MODULE_DESCRIPTION("AMLOGIC time sync management driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tim Yao <timyao@amlogic.com>");