371 lines
8.6 KiB
C
Executable File
371 lines
8.6 KiB
C
Executable File
/*
|
|
* Samsung Exynos5 SoC series FIMC-IS driver
|
|
*
|
|
* exynos5 fimc-is video functions
|
|
*
|
|
* Copyright (c) 2011 Samsung Electronics Co., Ltd
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/time.h>
|
|
|
|
#include "fimc-is-time.h"
|
|
#include "fimc-is-groupmgr.h"
|
|
#include "fimc-is-device-sensor.h"
|
|
#include "fimc-is-device-ischain.h"
|
|
|
|
static struct timeval itime[10];
|
|
|
|
#define JITTER_CNT 50
|
|
static u64 jitter_array[JITTER_CNT];
|
|
static u64 jitter_prio;
|
|
static u64 jitter_cnt = 0;
|
|
|
|
void TIME_STR(unsigned int index)
|
|
{
|
|
do_gettimeofday(&itime[index]);
|
|
}
|
|
|
|
void TIME_END(unsigned int index, const char *name)
|
|
{
|
|
long time;
|
|
struct timeval temp;
|
|
|
|
do_gettimeofday(&temp);
|
|
time = (temp.tv_sec - itime[index].tv_sec)*1000000 +
|
|
(temp.tv_usec - itime[index].tv_usec);
|
|
|
|
info("TIME(%s) : %ld us\n", name, time);
|
|
}
|
|
|
|
void fimc_is_jitter(u64 timestamp)
|
|
{
|
|
if (jitter_cnt == 0) {
|
|
jitter_prio = timestamp;
|
|
jitter_cnt++;
|
|
return;
|
|
}
|
|
|
|
jitter_array[jitter_cnt-1] = timestamp - jitter_prio;
|
|
jitter_prio = timestamp;
|
|
|
|
if (jitter_cnt >= JITTER_CNT) {
|
|
u64 i, variance, tot = 0, square_tot = 0, avg = 0, square_avg = 0;;
|
|
|
|
for (i = 0; i < JITTER_CNT; ++i) {
|
|
tot += jitter_array[i];
|
|
square_tot += (jitter_array[i] * jitter_array[i]);
|
|
}
|
|
|
|
avg = tot / JITTER_CNT;
|
|
square_avg = square_tot / JITTER_CNT;
|
|
variance = square_avg - (avg * avg);
|
|
|
|
info("[TIM] variance : %lld, average : %lld\n", variance, avg);
|
|
jitter_cnt = 0;
|
|
} else {
|
|
jitter_cnt++;
|
|
}
|
|
}
|
|
|
|
u64 fimc_is_get_timestamp(void)
|
|
{
|
|
struct timespec curtime;
|
|
|
|
ktime_get_ts(&curtime);
|
|
|
|
return (u64)curtime.tv_sec*1000000000 + curtime.tv_nsec;
|
|
}
|
|
|
|
u64 fimc_is_get_timestamp_boot(void)
|
|
{
|
|
struct timespec curtime;
|
|
|
|
curtime = ktime_to_timespec(ktime_get_boottime());
|
|
|
|
return (u64)curtime.tv_sec*1000000000 + curtime.tv_nsec;
|
|
}
|
|
|
|
static inline u32 fimc_is_get_time(struct timeval *str, struct timeval *end)
|
|
{
|
|
return (end->tv_sec - str->tv_sec)*1000000 + (end->tv_usec - str->tv_usec);
|
|
}
|
|
|
|
#ifdef MEASURE_TIME
|
|
#ifdef MONITOR_TIME
|
|
void monitor_init(struct fimc_is_time *time)
|
|
{
|
|
time->time_count = 0;
|
|
memset(&time->t_dq[0], 0, sizeof(unsigned long long) * 10);
|
|
time->t_dqq_max = 0;
|
|
time->t_dqq_tot = 0;
|
|
time->time1_max = 0;
|
|
time->time1_tot = 0;
|
|
time->time2_max = 0;
|
|
time->time2_tot = 0;
|
|
time->time3_max = 0;
|
|
time->time3_tot = 0;
|
|
time->time4_cur = 0;
|
|
time->time4_old = 0;
|
|
time->time4_tot = 0;
|
|
}
|
|
|
|
static void monitor_report(void *group_data,
|
|
void *frame_data)
|
|
{
|
|
unsigned int avg_cnt;
|
|
u32 index, shotindex;
|
|
unsigned long long fduration, ctime, dtime;
|
|
unsigned long long temp_s = 0, temp_sd = 0, temp_d = 0, temp_dqq = 0, shot_to_shot = 0;
|
|
struct fimc_is_monitor *mp;
|
|
struct fimc_is_device_ischain *device;
|
|
struct fimc_is_group *group;
|
|
struct fimc_is_frame *frame;
|
|
struct fimc_is_time *time;
|
|
bool valid = true;
|
|
u32 f_dqq;
|
|
|
|
FIMC_BUG_VOID(!group_data);
|
|
FIMC_BUG_VOID(!frame_data);
|
|
|
|
group = group_data;
|
|
device = group->device;
|
|
time = &group->time;
|
|
frame = frame_data;
|
|
mp = frame->mpoint;
|
|
fduration = (1000000 / fimc_is_sensor_g_framerate(device->sensor)) + MONITOR_TIMEOUT;
|
|
ctime = 5000;
|
|
dtime = 5000;
|
|
f_dqq = frame->fcount % 10;
|
|
|
|
/* Shot kthread */
|
|
if (!frame->result && mp[TMS_Q].check && mp[TMS_SHOT1].check) {
|
|
temp_s = (mp[TMS_SHOT1].time - mp[TMS_Q].time) / 1000;
|
|
if (!test_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state) && temp_s > ctime)
|
|
mgrinfo("[TIM] late S(%llu us > %llu us)\n", device, group, frame, temp_s, ctime);
|
|
if (time->t_dq[f_dqq])
|
|
temp_dqq = (mp[TMS_Q].time - time->t_dq[f_dqq]) / 1000;
|
|
} else {
|
|
valid = false;
|
|
}
|
|
|
|
/* Shot - Done */
|
|
if (test_bit(FIMC_IS_GROUP_OTF_INPUT, &group->state))
|
|
shotindex = TMS_SHOT2;
|
|
else
|
|
shotindex = TMS_SHOT1;
|
|
|
|
if (!frame->result && mp[shotindex].check && mp[TMS_SDONE].check) {
|
|
temp_sd = (mp[TMS_SDONE].time - mp[shotindex].time) / 1000;
|
|
if (temp_sd > fduration)
|
|
mgrinfo("[TIM] late S-D(%llu us > %llu us)\n", device, group, frame, temp_sd, fduration);
|
|
} else {
|
|
valid = false;
|
|
}
|
|
|
|
/* Done - Deque */
|
|
if (!frame->result && mp[TMS_SDONE].check && mp[TMS_DQ].check) {
|
|
temp_d = (mp[TMS_DQ].time - mp[TMS_SDONE].time) / 1000;
|
|
if (temp_d > dtime)
|
|
mgrinfo("[TIM] late D(%llu us > %llu us)\n", device, group, frame, temp_d, dtime);
|
|
if (group->gnext)
|
|
group->gnext->time.t_dq[f_dqq] = mp[TMS_DQ].time;
|
|
} else {
|
|
valid = false;
|
|
}
|
|
|
|
for (index = 0; index < TMS_END; ++index)
|
|
mp[index].check = false;
|
|
|
|
if (!valid)
|
|
return;
|
|
|
|
if (!time->time_count) {
|
|
time->t_dqq_max = temp_dqq;
|
|
time->time1_max = temp_s;
|
|
time->time2_max = temp_sd;
|
|
time->time3_max = temp_d;
|
|
} else {
|
|
if (time->t_dqq_max < temp_dqq)
|
|
time->t_dqq_max = temp_dqq;
|
|
|
|
if (time->time1_max < temp_s)
|
|
time->time1_max = temp_s;
|
|
|
|
if (time->time2_max < temp_sd)
|
|
time->time2_max = temp_sd;
|
|
|
|
if (time->time3_max < temp_d)
|
|
time->time3_max = temp_d;
|
|
}
|
|
|
|
time->t_dqq_tot += temp_dqq;
|
|
time->time1_tot += temp_s;
|
|
time->time2_tot += temp_sd;
|
|
time->time3_tot += temp_d;
|
|
|
|
time->time4_cur = mp[shotindex].time;
|
|
time->time4_tot += (time->time4_cur - time->time4_old) / 1000;
|
|
time->time4_old = time->time4_cur;
|
|
|
|
time->time_count++;
|
|
|
|
if (!test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &device->state)) {
|
|
avg_cnt = debug_time_shot;
|
|
if (time->time_count % avg_cnt)
|
|
return;
|
|
} else {
|
|
avg_cnt = 1;
|
|
}
|
|
|
|
shot_to_shot = time->time4_tot / avg_cnt;
|
|
|
|
mginfo("[TIM] DQ-Q(avg: %05llu max: %05llu), S(avg: %05llu max: %05llu), S-D(avg: %05llu max: %05llu), D(avg: %05llu max: %05llu): %llu(%llufps)\n",
|
|
device, group,
|
|
time->t_dqq_tot / avg_cnt, time->t_dqq_max,
|
|
time->time1_tot / avg_cnt, time->time1_max,
|
|
time->time2_tot / avg_cnt, time->time2_max,
|
|
time->time3_tot / avg_cnt, time->time3_max,
|
|
shot_to_shot, 1000000 / shot_to_shot);
|
|
|
|
time->time_count = 0;
|
|
time->t_dqq_tot = 0;
|
|
time->time1_tot = 0;
|
|
time->time2_tot = 0;
|
|
time->time3_tot = 0;
|
|
time->time4_tot = 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef INTERFACE_TIME
|
|
void measure_init(struct fimc_is_interface_time *time, u32 cmd)
|
|
{
|
|
time->cmd = cmd;
|
|
time->time_max = 0;
|
|
time->time_min = 0;
|
|
time->time_tot = 0;
|
|
time->time_cnt = 0;
|
|
}
|
|
|
|
void measure_time(struct fimc_is_interface_time *time,
|
|
u32 instance,
|
|
u32 group,
|
|
struct timeval *start,
|
|
struct timeval *end)
|
|
{
|
|
u32 temp;
|
|
|
|
temp = (end->tv_sec - start->tv_sec)*1000000 + (end->tv_usec - start->tv_usec);
|
|
|
|
if (time->time_cnt) {
|
|
time->time_max = temp;
|
|
time->time_min = temp;
|
|
} else {
|
|
if (time->time_min > temp)
|
|
time->time_min = temp;
|
|
|
|
if (time->time_max < temp)
|
|
time->time_max = temp;
|
|
}
|
|
|
|
time->time_tot += temp;
|
|
time->time_cnt++;
|
|
|
|
pr_info("cmd[%d][%d](%d) : curr(%d), max(%d), avg(%d)\n",
|
|
instance, group, time->cmd, temp, time->time_max, time->time_tot / time->time_cnt);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
void monitor_point(void *group_data,
|
|
u32 mpoint)
|
|
{
|
|
struct fimc_is_group *group;
|
|
|
|
FIMC_BUG_VOID(!group_data);
|
|
|
|
group = group_data;
|
|
group->pcount = mpoint;
|
|
}
|
|
|
|
void monitor_time_shot(void *group_data,
|
|
void *frame_data,
|
|
u32 mpoint)
|
|
{
|
|
#if defined(MEASURE_TIME) && defined(MONITOR_TIME)
|
|
struct fimc_is_frame *frame;
|
|
struct fimc_is_monitor *point;
|
|
|
|
FIMC_BUG_VOID(!frame_data);
|
|
|
|
frame = frame_data;
|
|
point = &frame->mpoint[mpoint];
|
|
point->time = sched_clock();
|
|
point->check = true;
|
|
|
|
if (mpoint == TMS_DQ)
|
|
monitor_report(group_data, frame_data);
|
|
#endif
|
|
}
|
|
|
|
void monitor_time_queue(void *vctx_data,
|
|
u32 mpoint)
|
|
{
|
|
#if defined(MEASURE_TIME) && defined(MONITOR_TIME)
|
|
unsigned int avg_cnt = debug_time_queue;
|
|
struct fimc_is_video_ctx *vctx;
|
|
struct fimc_is_device_ischain *ischain;
|
|
struct fimc_is_video *video;
|
|
unsigned long long temp;
|
|
|
|
vctx = vctx_data;
|
|
if (!vctx)
|
|
return;
|
|
|
|
ischain = GET_DEVICE_ISCHAIN(vctx);
|
|
if (!ischain)
|
|
return;
|
|
|
|
video = vctx->video;
|
|
if (!video)
|
|
return;
|
|
|
|
temp = sched_clock();
|
|
|
|
if (mpoint == TMQ_DQ) {
|
|
if (vctx->time[mpoint] == 0) { /* Normal: DQ --> Q --> DQ --> Q */
|
|
vctx->time[mpoint] = temp;
|
|
vctx->time_cnt++;
|
|
} else { /* Abnormal: DQ --> DQ */
|
|
minfo("[TIM] late Q(DQ --> DQ(%llu))\n", vctx,
|
|
(temp - vctx->time[mpoint]) / 1000);
|
|
}
|
|
} else {
|
|
if (vctx->time[mpoint - 1]) { /* check pre-point */
|
|
vctx->time[mpoint] = temp;
|
|
vctx->time_total[mpoint] += vctx->time[mpoint] - vctx->time[mpoint - 1];
|
|
|
|
if (mpoint == TMQ_QE) { /* reset at final step */
|
|
if (vctx->time_cnt % avg_cnt == 0) {
|
|
minfo("[TIM][%s] DQ-Q(%05llu us), Q(%05llu us)\n",
|
|
vctx, vctx->queue.name,
|
|
vctx->time_total[TMQ_QS] / avg_cnt / 1000,
|
|
vctx->time_total[TMQ_QE] / avg_cnt / 1000);
|
|
vctx->time_total[TMQ_QS] = 0;
|
|
vctx->time_total[TMQ_QE] = 0;
|
|
vctx->time_cnt = 0;
|
|
}
|
|
|
|
vctx->time[TMQ_DQ] = 0;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|