lineage_kernel_xcoverpro/drivers/media/platform/exynos/fimc-is2/fimc-is-framemgr.c

353 lines
8.2 KiB
C
Raw Permalink Normal View History

2023-06-18 22:53:49 +00:00
/*
* 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/delay.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <video/videonode.h>
#include <asm/cacheflush.h>
#include <asm/pgtable.h>
#include <linux/firmware.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/videodev2.h>
#include <linux/videodev2_exynos_camera.h>
#include <linux/v4l2-mediabus.h>
#include <linux/bug.h>
#include "fimc-is-core.h"
#include "fimc-is-cmd.h"
#include "fimc-is-regs.h"
#include "fimc-is-err.h"
#include "fimc-is-video.h"
#include "fimc-is-device-sensor.h"
ulong frame_fcount(struct fimc_is_frame *frame, void *data)
{
return (ulong)frame->fcount - (ulong)data;
}
int put_frame(struct fimc_is_framemgr *this, struct fimc_is_frame *frame,
enum fimc_is_frame_state state)
{
if (state == FS_INVALID)
return -EINVAL;
if (!frame) {
err("invalid frame");
return -EFAULT;
}
frame->state = state;
list_add_tail(&frame->list, &this->queued_list[state]);
this->queued_count[state]++;
#ifdef TRACE_FRAME
print_frame_queue(this, state);
#endif
return 0;
}
struct fimc_is_frame *get_frame(struct fimc_is_framemgr *this,
enum fimc_is_frame_state state)
{
struct fimc_is_frame *frame;
if (state == FS_INVALID)
return NULL;
if (!this->queued_count[state])
return NULL;
frame = list_first_entry(&this->queued_list[state],
struct fimc_is_frame, list);
list_del(&frame->list);
this->queued_count[state]--;
frame->state = FS_INVALID;
return frame;
}
int trans_frame(struct fimc_is_framemgr *this, struct fimc_is_frame *frame,
enum fimc_is_frame_state state)
{
if (!frame) {
err("invalid frame");
return -EFAULT;
}
if ((frame->state == FS_INVALID) || (state == FS_INVALID))
return -EINVAL;
if (!this->queued_count[frame->state]) {
err("%s frame queue is empty (%s)", frame_state_name[frame->state],
this->name);
return -EINVAL;
}
list_del(&frame->list);
this->queued_count[frame->state]--;
if (state == FS_PROCESS && (!(this->id & FRAMEMGR_ID_HW)))
frame->bak_flag = frame->out_flag;
return put_frame(this, frame, state);
}
struct fimc_is_frame *peek_frame(struct fimc_is_framemgr *this,
enum fimc_is_frame_state state)
{
if (state == FS_INVALID)
return NULL;
if (!this->queued_count[state])
return NULL;
return list_first_entry(&this->queued_list[state],
struct fimc_is_frame, list);
}
struct fimc_is_frame *peek_frame_tail(struct fimc_is_framemgr *this,
enum fimc_is_frame_state state)
{
if (state == FS_INVALID)
return NULL;
if (!this->queued_count[state])
return NULL;
return list_last_entry(&this->queued_list[state],
struct fimc_is_frame, list);
}
struct fimc_is_frame *find_frame(struct fimc_is_framemgr *this,
enum fimc_is_frame_state state,
ulong (*fn)(struct fimc_is_frame *, void *), void *data)
{
struct fimc_is_frame *frame;
if (state == FS_INVALID)
return NULL;
if (!this->queued_count[state]) {
err("[F%lu] %s frame queue is empty (%s)",
(ulong)data, frame_state_name[state], this->name);
return NULL;
}
list_for_each_entry(frame, &this->queued_list[state], list) {
if (!fn(frame, data))
return frame;
}
return NULL;
}
struct fimc_is_frame *find_stripe_process_frame(struct fimc_is_framemgr *framemgr,
u32 fcount)
{
struct fimc_is_frame *frame = NULL;
if (framemgr->queued_count[FS_STRIPE_PROCESS]) {
frame = find_frame(framemgr, FS_STRIPE_PROCESS, frame_fcount,
(void *)(ulong)fcount);
}
return frame;
}
void print_frame_queue(struct fimc_is_framemgr *this,
enum fimc_is_frame_state state)
{
struct fimc_is_frame *frame, *temp;
if (!(TRACE_ID & this->id))
return;
pr_info("[FRM] %s(%s, %d) :", frame_state_name[state],
this->name, this->queued_count[state]);
list_for_each_entry_safe(frame, temp, &this->queued_list[state], list)
pr_cont("%d[%d]->", frame->index, frame->fcount);
pr_cont("X\n");
}
#ifndef ENABLE_IS_CORE
void print_frame_info_queue(struct fimc_is_framemgr *this,
enum fimc_is_frame_state state)
{
unsigned long long when[MAX_FRAME_INFO];
unsigned long usec[MAX_FRAME_INFO];
struct fimc_is_frame *frame, *temp;
if (!(TRACE_ID & this->id))
return;
pr_info("[FRM_INFO] %s(%s, %d) :", hw_frame_state_name[state],
this->name, this->queued_count[state]);
list_for_each_entry_safe(frame, temp, &this->queued_list[state], list) {
when[INFO_FRAME_START] = frame->frame_info[INFO_FRAME_START].when;
when[INFO_CONFIG_LOCK] = frame->frame_info[INFO_CONFIG_LOCK].when;
when[INFO_FRAME_END_PROC] = frame->frame_info[INFO_FRAME_END_PROC].when;
usec[INFO_FRAME_START] = do_div(when[INFO_FRAME_START], NSEC_PER_SEC);
usec[INFO_CONFIG_LOCK] = do_div(when[INFO_CONFIG_LOCK], NSEC_PER_SEC);
usec[INFO_FRAME_END_PROC] = do_div(when[INFO_FRAME_END_PROC], NSEC_PER_SEC);
pr_cont("%d[%d][%d]([%5lu.%06lu],[%5lu.%06lu],[%5lu.%06lu][C:0x%lx])->",
frame->index, frame->fcount, frame->type,
(unsigned long)when[INFO_FRAME_START], usec[INFO_FRAME_START] / NSEC_PER_USEC,
(unsigned long)when[INFO_CONFIG_LOCK], usec[INFO_CONFIG_LOCK] / NSEC_PER_USEC,
(unsigned long)when[INFO_FRAME_END_PROC], usec[INFO_FRAME_END_PROC] / NSEC_PER_USEC,
frame->core_flag);
}
pr_cont("X\n");
}
#endif
int frame_manager_probe(struct fimc_is_framemgr *this, u32 id, const char *name)
{
this->id = id;
snprintf(this->name, sizeof(this->name), "%s", name);
spin_lock_init(&this->slock);
this->frames = NULL;
return 0;
}
static void default_frame_work_fn(struct kthread_work *work)
{
struct fimc_is_groupmgr *groupmgr;
struct fimc_is_group *group;
struct fimc_is_frame *frame;
frame = container_of(work, struct fimc_is_frame, work);
groupmgr = frame->groupmgr;
group = frame->group;
fimc_is_group_shot(groupmgr, group, frame);
}
int frame_manager_open(struct fimc_is_framemgr *this, u32 buffers)
{
u32 i;
unsigned long flag;
/*
* We already have frames allocated, so we should free them first.
* reqbufs(n) could be called multiple times from userspace after
* each video node was opened.
*/
if (this->frames)
vfree(this->frames);
this->frames = vzalloc(sizeof(struct fimc_is_frame) * buffers);
if (!this->frames) {
err("failed to allocate frames");
return -ENOMEM;
}
spin_lock_irqsave(&this->slock, flag);
this->num_frames = buffers;
for (i = 0; i < NR_FRAME_STATE; i++) {
this->queued_count[i] = 0;
INIT_LIST_HEAD(&this->queued_list[i]);
}
for (i = 0; i < buffers; ++i) {
this->frames[i].index = i;
put_frame(this, &this->frames[i], FS_FREE);
kthread_init_work(&this->frames[i].work, default_frame_work_fn);
}
spin_unlock_irqrestore(&this->slock, flag);
return 0;
}
int frame_manager_close(struct fimc_is_framemgr *this)
{
u32 i;
unsigned long flag;
spin_lock_irqsave(&this->slock, flag);
if (this->frames) {
vfree_atomic(this->frames);
this->frames = NULL;
}
this->num_frames = 0;
for (i = 0; i < NR_FRAME_STATE; i++) {
this->queued_count[i] = 0;
INIT_LIST_HEAD(&this->queued_list[i]);
}
spin_unlock_irqrestore(&this->slock, flag);
return 0;
}
int frame_manager_flush(struct fimc_is_framemgr *this)
{
unsigned long flag;
struct fimc_is_frame *frame, *temp;
enum fimc_is_frame_state i;
spin_lock_irqsave(&this->slock, flag);
for (i = FS_REQUEST; i < FS_INVALID; i++) {
list_for_each_entry_safe(frame, temp, &this->queued_list[i], list)
trans_frame(this, frame, FS_FREE);
}
spin_unlock_irqrestore(&this->slock, flag);
FIMC_BUG(this->queued_count[FS_FREE] != this->num_frames);
return 0;
}
void frame_manager_print_queues(struct fimc_is_framemgr *this)
{
int i;
for (i = 0; i < NR_FRAME_STATE; i++)
print_frame_queue(this, (enum fimc_is_frame_state)i);
}
void frame_manager_print_info_queues(struct fimc_is_framemgr *this)
{
#ifndef ENABLE_IS_CORE
int i;
for (i = 0; i < NR_FRAME_STATE; i++)
print_frame_info_queue(this, (enum fimc_is_frame_state)i);
#endif
}