| /* |
| * 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/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> |
| #define KERNEL_ATRACE_TAG KERNEL_ATRACE_TAG_TSYNC |
| #include <trace/events/meson_atrace.h> |
| |
| |
| u32 acc_apts_inc; |
| u32 acc_apts_dec; |
| u32 acc_pcrscr_inc; |
| u32 acc_pcrscr_dec; |
| |
| static s32 system_time_inc_adj; |
| 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_lantcy = 200*90; |
| static u32 video_pts; |
| static u64 video_pts_u64; |
| static u32 audio_pts; |
| static u32 avsync_counts; |
| static u32 last_apts_gap; |
| static u32 last_vpts_gap; |
| |
| static u32 system_time_scale_base = 1; |
| static u32 system_time_scale_remainder; |
| |
| #ifdef MODIFY_TIMESTAMP_INC_WITH_PLL |
| #define PLL_FACTOR 10000 |
| static u32 timestamp_inc_factor = PLL_FACTOR; |
| |
| void set_timestamp_inc_factor(u32 factor) |
| { |
| timestamp_inc_factor = factor; |
| } |
| #endif |
| |
| u32 timestamp_vpts_get(void) |
| { |
| return video_pts; |
| } |
| EXPORT_SYMBOL(timestamp_vpts_get); |
| |
| void timestamp_vpts_set(u32 pts) |
| { |
| video_pts = pts; |
| ATRACE_COUNTER("VPTS", video_pts); |
| } |
| EXPORT_SYMBOL(timestamp_vpts_set); |
| |
| void timestamp_vpts_inc(s32 val) |
| { |
| video_pts += val; |
| ATRACE_COUNTER("VPTS", video_pts); |
| } |
| EXPORT_SYMBOL(timestamp_vpts_inc); |
| |
| u64 timestamp_vpts_get_u64(void) |
| { |
| return video_pts_u64; |
| } |
| EXPORT_SYMBOL(timestamp_vpts_get_u64); |
| |
| void timestamp_vpts_set_u64(u64 pts) |
| { |
| video_pts_u64 = pts; |
| } |
| EXPORT_SYMBOL(timestamp_vpts_set_u64); |
| |
| void timestamp_vpts_inc_u64(s32 val) |
| { |
| video_pts_u64 += val; |
| } |
| EXPORT_SYMBOL(timestamp_vpts_inc_u64); |
| |
| u32 timestamp_apts_get(void) |
| { |
| return audio_pts; |
| } |
| EXPORT_SYMBOL(timestamp_apts_get); |
| |
| u64 timestamp_apts_get_u64(void) |
| { |
| u64 pts_val_video; |
| u64 pts_val; |
| |
| pts_val_video = div64_u64(timestamp_vpts_get_u64() * 9, 100); |
| pts_val = ((u64)timestamp_apts_get()) & 0x00000000FFFFFFFF; |
| if (pts_val_video & (1LL << 32)) |
| pts_val |= (1LL << 32); |
| |
| pts_val_video = div64_u64(pts_val * 100, 9); |
| |
| return pts_val_video; |
| } |
| EXPORT_SYMBOL(timestamp_apts_get_u64); |
| |
| void timestamp_apts_set(u32 pts) |
| { |
| audio_pts = pts; |
| ATRACE_COUNTER("APTS", audio_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; |
| ATRACE_COUNTER("APTS", audio_pts); |
| } |
| } |
| 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) |
| { |
| u32 tmp_pcr; |
| if (tsync_get_mode() != TSYNC_MODE_PCRMASTER) |
| return system_time; |
| |
| if (tsync_get_demux_pcrscr_valid()) { |
| if (tsync_pcr_demux_pcr_used() == 0) { |
| return system_time; |
| } |
| else { |
| if (tsync_get_demux_pcr(&tmp_pcr)) { |
| if (tmp_pcr > pcrscr_lantcy) |
| return |
| tmp_pcr - pcrscr_lantcy; |
| else |
| return 0; |
| } |
| else |
| return system_time; |
| } |
| } else |
| return system_time; |
| } |
| EXPORT_SYMBOL(timestamp_pcrscr_get); |
| |
| u64 timestamp_pcrscr_get_u64(void) |
| { |
| u64 pts_val_video; |
| u64 pts_val; |
| |
| pts_val_video = div64_u64(timestamp_vpts_get_u64() * 9, 100); |
| pts_val = ((u64)timestamp_pcrscr_get()) & 0x00000000FFFFFFFF; |
| if (pts_val_video & (1LL << 32)) |
| pts_val |= (1LL << 32); |
| |
| pts_val_video = div64_u64(pts_val * 100, 9); |
| |
| return pts_val_video; |
| } |
| EXPORT_SYMBOL(timestamp_pcrscr_get_u64); |
| |
| u32 timestamp_avsync_counter_get(void) |
| { |
| return avsync_counts; |
| } |
| EXPORT_SYMBOL(timestamp_avsync_counter_get); |
| |
| void timestamp_avsync_counter_set(u32 counts) |
| { |
| avsync_counts = counts; |
| } |
| EXPORT_SYMBOL(timestamp_avsync_counter_set); |
| |
| void timestamp_set_pcrlatency(u32 latency) |
| { |
| if (latency < 3000 * 90) |
| pcrscr_lantcy = latency; |
| else |
| pcrscr_lantcy = 3000 * 90; |
| } |
| EXPORT_SYMBOL(timestamp_set_pcrlatency); |
| |
| u32 timestamp_get_pcrlatency(void) |
| { |
| return pcrscr_lantcy; |
| } |
| EXPORT_SYMBOL(timestamp_get_pcrlatency); |
| |
| void timestamp_clac_pts_latency(u8 type, u32 pts) |
| { |
| u32 demux_pcr = 0; |
| u32 t1, t2, pts_diff; |
| |
| if (tsync_get_mode() != TSYNC_MODE_PCRMASTER) |
| return; |
| if (tsync_get_demux_pcrscr_valid() |
| && tsync_pcr_demux_pcr_used()) { |
| if (tsync_get_demux_pcr(&demux_pcr) == 0) |
| return; |
| if (demux_pcr == 0 || |
| demux_pcr == 0xffffffff) { |
| last_apts_gap = 0; |
| last_vpts_gap = 0; |
| return; |
| } |
| if (type == 0) { |
| if (demux_pcr > pts) { |
| last_vpts_gap = 0; |
| return; |
| } |
| pts_diff = pts - demux_pcr; |
| if (pts_diff > 500 * 90) |
| return; |
| t1 = ((last_vpts_gap >> 16) & 0xff); |
| if (t1 > 5) |
| t1 = 0; |
| t2 = (last_vpts_gap & 0xffff); |
| if (t2 == 0) { |
| last_vpts_gap = pts_diff; |
| return; |
| } |
| if (abs(t2 - pts_diff) < 30 * 90) { |
| last_vpts_gap = t2; |
| return; |
| } |
| t1++; |
| if (t1 >= 5) |
| last_vpts_gap = pts_diff; |
| else |
| last_vpts_gap = ((t1 << 16) | t2); |
| } else if (type == 1) { |
| if (demux_pcr > pts) { |
| last_apts_gap = 0; |
| return; |
| } |
| pts_diff = pts - demux_pcr; |
| if (pts_diff > 500 * 90) |
| return; |
| t1 = ((last_apts_gap >> 16) & 0xff); |
| if (t1 > 5) |
| t1 = 0; |
| t2 = (last_apts_gap & 0xffff); |
| if (t2 == 0) { |
| last_apts_gap = pts_diff; |
| return; |
| } |
| if (abs(t2 - pts_diff) < 30 * 90) { |
| last_apts_gap = t2; |
| return; |
| } |
| t1++; |
| if (t1 >= 5) |
| last_apts_gap = pts_diff; |
| else |
| last_apts_gap = ((t1 << 16) | t2); |
| } |
| } else { |
| last_apts_gap = 0; |
| last_vpts_gap = 0; |
| } |
| } |
| EXPORT_SYMBOL(timestamp_clac_pts_latency); |
| |
| u32 timestamp_get_pts_latency(u8 type) |
| { |
| if (type == 0) |
| return (last_vpts_gap & 0xffff); |
| else if (type == 1) |
| return (last_apts_gap & 0xffff); |
| return 0; |
| } |
| EXPORT_SYMBOL(timestamp_get_pts_latency); |
| |
| void timestamp_clean_pts_latency(u8 type) |
| { |
| if (type == 0) |
| last_vpts_gap = 0; |
| else if (type == 1) |
| last_apts_gap = 0; |
| } |
| EXPORT_SYMBOL(timestamp_clean_pts_latency); |
| |
| u32 timestamp_tsdemux_pcr_get(void) |
| { |
| u32 tmp_pcr = 0; |
| |
| if (tsync_get_demux_pcr(&tmp_pcr)) |
| return tmp_pcr; |
| |
| 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);*/ |
| system_time = pts; |
| ATRACE_COUNTER("PCRSCR", pts); |
| } |
| EXPORT_SYMBOL(timestamp_pcrscr_set); |
| |
| 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); |
| timestamp_clean_pts_latency(0); |
| } |
| 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); |
| timestamp_clean_pts_latency(1); |
| } |
| 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); |
| |
| void timestamp_pcrscr_inc(s32 inc) |
| { |
| if (system_time_up) { |
| #ifdef MODIFY_TIMESTAMP_INC_WITH_PLL |
| inc = inc * (s32)timestamp_inc_factor / PLL_FACTOR; |
| #endif |
| system_time += inc + system_time_inc_adj; |
| ATRACE_COUNTER("PCRSCR", system_time); |
| } |
| } |
| EXPORT_SYMBOL(timestamp_pcrscr_inc); |
| |
| void timestamp_pcrscr_inc_scale(s32 inc, u32 base) |
| { |
| 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; |
| |
| 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; |
| } |
| 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_enable(u32 enable) |
| { |
| system_time_up = enable; |
| } |
| 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>"); |