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

1073 lines
27 KiB
C
Raw 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 <linux/videodev2.h>
#include <linux/videodev2_exynos_camera.h>
#include <linux/bug.h>
#include "fimc-is-time.h"
#include "fimc-is-core.h"
#include "fimc-is-regs.h"
#include "fimc-is-hw.h"
#include "fimc-is-interface.h"
#include "fimc-is-device-flite.h"
#ifdef SUPPORTED_EARLYBUF_DONE_SW
static void flite_early_buf_done_start(struct v4l2_subdev *subdev)
{
struct fimc_is_device_sensor *device;
device = v4l2_get_subdev_hostdata(subdev);
if (!device) {
err("device is NULL");
BUG();
}
if (device->early_buf_done_mode) {
int framerate = fimc_is_sensor_g_framerate(device);
/* timeout set : frameduration(ms) * early_buf_done_ratio(5~50%) */
u32 msec_timeout = ((1000 / framerate) *
(100 - (5 * device->early_buf_done_mode))) / 100;
pr_info("msec_timeout : %d \n" , msec_timeout);
hrtimer_start(&device->early_buf_timer,
ktime_set(0, msec_timeout * NSEC_PER_MSEC), HRTIMER_MODE_REL);
}
}
#endif
static u32 g_print_cnt;
void tasklet_flite_str_otf(unsigned long data)
{
struct v4l2_subdev *subdev;
struct fimc_is_device_flite *flite;
struct fimc_is_device_sensor *device;
struct fimc_is_device_ischain *ischain;
struct fimc_is_groupmgr *groupmgr;
struct fimc_is_group *group_3aa, *group_isp;
struct fimc_is_framemgr *framemgr;
struct fimc_is_frame *frame;
unsigned long flags;
u32 fcount, group_3aa_id, group_isp_id;
subdev = (struct v4l2_subdev *)data;
flite = v4l2_get_subdevdata(subdev);
if (!flite) {
err("flite is NULL");
BUG();
}
device = v4l2_get_subdev_hostdata(subdev);
if (!device) {
err("device is NULL");
BUG();
}
fcount = atomic_read(&flite->fcount);
ischain = device->ischain;
framemgr = flite->framemgr;
dbg_tasklet("S%d\n", fcount);
groupmgr = ischain->groupmgr;
group_3aa = &ischain->group_3aa;
group_isp = &ischain->group_isp;
group_3aa_id = group_3aa->id;
group_isp_id = group_isp->id;
if (group_3aa_id >= GROUP_ID_MAX) {
merr("group 3aa id is invalid(%d)", flite, group_3aa_id);
goto trigger_skip;
}
if (group_isp_id >= GROUP_ID_MAX) {
merr("group isp id is invalid(%d)", flite, group_isp_id);
goto trigger_skip;
}
if (group_3aa->sync_shots == 0)
goto trigger_skip;
if (unlikely(list_empty(&group_3aa->smp_trigger.wait_list))) {
atomic_set(&group_3aa->sensor_fcount, fcount + group_3aa->skip_shots);
/*
* pcount : program count
* current program count(location) in kthread
*/
if (((g_print_cnt % LOG_INTERVAL_OF_DROPS) == 0) ||
(g_print_cnt < LOG_INTERVAL_OF_DROPS)) {
info("GP%d(res %d, rcnt %d, scnt %d), "
"grp2(res %d, rcnt %d, scnt %d), "
"fcount %d pcount %d\n",
group_3aa_id,
groupmgr->gtask[group_3aa_id].smp_resource.count,
atomic_read(&group_3aa->rcount),
atomic_read(&group_3aa->scount),
groupmgr->gtask[group_isp_id].smp_resource.count,
atomic_read(&group_isp->rcount),
atomic_read(&group_isp->scount),
fcount + group_3aa->skip_shots,
group_3aa->pcount);
}
g_print_cnt++;
} else {
g_print_cnt = 0;
atomic_set(&group_3aa->sensor_fcount, fcount + group_3aa->skip_shots);
up(&group_3aa->smp_trigger);
}
trigger_skip:
framemgr_e_barrier_irqs(framemgr, FMGR_IDX_9, flags);
frame = peek_frame(framemgr, FS_REQUEST);
if (frame) {
flite_hw_set_dma_addr(flite->base_reg, 0, true,
(u32)frame->dvaddr_buffer[0]);
trans_frame(framemgr, frame, FS_PROCESS);
} else {
flite_hw_set_dma_addr(flite->base_reg, 0, false, 0);
}
framemgr_x_barrier_irqr(framemgr, FMGR_IDX_9, flags);
#if defined(SUPPORTED_EARLYBUF_DONE_SW)
flite_early_buf_done_start(subdev);
#endif
v4l2_subdev_notify(subdev, FLITE_NOTIFY_FSTART, &fcount);
}
void tasklet_flite_str_m2m(unsigned long data)
{
struct v4l2_subdev *subdev;
struct fimc_is_device_flite *flite;
struct fimc_is_framemgr *framemgr;
struct fimc_is_frame *frame;
unsigned long flags;
u32 fcount;
subdev = (struct v4l2_subdev *)data;
flite = v4l2_get_subdevdata(subdev);
if (!flite) {
err("flite is NULL");
BUG();
}
fcount = atomic_read(&flite->fcount);
framemgr = flite->framemgr;
dbg_tasklet("S%d\n", fcount);
framemgr_e_barrier_irqs(framemgr, FMGR_IDX_10, flags);
frame = peek_frame(framemgr, FS_REQUEST);
if (frame) {
flite_hw_set_dma_addr(flite->base_reg, 0, true,
(u32)frame->dvaddr_buffer[0]);
trans_frame(framemgr, frame, FS_PROCESS);
} else {
flite_hw_set_dma_addr(flite->base_reg, 0, false, 0);
}
framemgr_x_barrier_irqr(framemgr, FMGR_IDX_10, flags);
#if defined(SUPPORTED_EARLYBUF_DONE_SW)
flite_early_buf_done_start(subdev);
#endif
v4l2_subdev_notify(subdev, FLITE_NOTIFY_FSTART, &fcount);
}
static void tasklet_flite_end(unsigned long data)
{
struct fimc_is_device_flite *flite;
struct fimc_is_framemgr *framemgr;
struct fimc_is_frame *frame;
struct fimc_is_frame *frame_done;
struct v4l2_subdev *subdev;
frame_done = NULL;
subdev = (struct v4l2_subdev *)data;
flite = v4l2_get_subdevdata(subdev);
if (!flite) {
err("flite is NULL");
BUG();
}
framemgr = flite->framemgr;
if (flite_hw_get_interrupt_mask(flite->base_reg, (1 << FLITE_MASK_IRQ_OVERFLOW)))
flite_hw_set_interrupt_mask(flite->base_reg, true, (1 << FLITE_MASK_IRQ_OVERFLOW));
dbg_tasklet("E%d\n", atomic_read(&flite->fcount));
framemgr_e_barrier(framemgr, FMGR_IDX_12);
if (flite_hw_get_output_dma(flite->base_reg)) {
if (framemgr->queued_count[FS_PROCESS] == 2) {
frame = peek_frame(framemgr, FS_PROCESS);
if (frame) {
frame_done = frame;
trans_frame(framemgr, frame, FS_COMPLETE);
} else {
merr("sensor process is empty", flite);
frame_manager_print_queues(framemgr);
BUG();
}
}
} else {
frame = peek_frame(framemgr, FS_PROCESS);
if (frame) {
frame_done = frame;
trans_frame(framemgr, frame, FS_COMPLETE);
/* 2. next frame ready */
frame = peek_frame(framemgr, FS_REQUEST);
if (frame) {
if (flite_hw_get_status(flite->base_reg,
(1 << FLITE_STATUS_MIPI_VALID), false)) {
merr("over vblank", flite);
} else {
flite_hw_set_dma_addr(flite->base_reg, 0, true,
(u32)frame->dvaddr_buffer[0]);
trans_frame(framemgr, frame, FS_PROCESS);
}
} else {
mwarn("sensor request(%d) is empty", flite, atomic_read(&flite->fcount));
/* frame_manager_print_queues(framemgr); */
}
}
}
framemgr_x_barrier(framemgr, FMGR_IDX_12);
v4l2_subdev_notify(subdev, FLITE_NOTIFY_FEND, frame_done);
}
#if defined(SUPPORTED_EARLYBUF_DONE_SW)
static enum hrtimer_restart flite_early_buf_done(struct hrtimer *timer)
{
struct fimc_is_device_sensor *device = container_of(timer, struct fimc_is_device_sensor, early_buf_timer);
struct fimc_is_device_flite *flite;
flite = (struct fimc_is_device_flite *)v4l2_get_subdevdata(device->subdev_flite);
flite->sw_checker = EXPECT_FRAME_START;
tasklet_schedule(&flite->tasklet_flite_end);
return HRTIMER_NORESTART;
}
#endif
static inline void notify_fcount(struct fimc_is_device_flite *flite)
{
#ifdef ENABLE_IS_CORE
if (test_bit(FLITE_JOIN_ISCHAIN, &flite->state)) {
if (flite->csi == CSI_ID_A)
writel(atomic_read(&flite->fcount), notify_fcount_sen0);
else if (flite->csi == CSI_ID_B)
writel(atomic_read(&flite->fcount), notify_fcount_sen1);
else if (flite->csi == CSI_ID_C)
writel(atomic_read(&flite->fcount), notify_fcount_sen2);
else if (flite->csi == CSI_ID_D)
writel(atomic_read(&flite->fcount), notify_fcount_sen3);
else
err("unresolved channel(%d)", flite->instance);
}
#endif
}
irqreturn_t fimc_is_isr_flite(int irq, void *data)
{
u32 status1;
u32 frame_start_end, frame_start_line;
struct fimc_is_device_flite *flite;
flite = data;
status1 = flite_hw_get_status(flite->base_reg, ((1 << FLITE_STATUS_ALL) - 1), true);
if (test_bit(FLITE_LAST_CAPTURE, &flite->state)) {
if (status1) {
info("[CamIF%d] last status1 : 0x%08X\n", flite->instance, status1);
goto clear_status;
}
err("[CamIF%d] unintended intr is occured", flite->instance);
flite_hw_dump(flite->base_reg);
flite_hw_reset(flite->base_reg);
goto clear_status;
}
/* Both start irq and end irq occured */
frame_start_end = (1 << FLITE_STATUS_IRQ_SRC_START) | (1 << FLITE_STATUS_IRQ_SRC_END);
/* Both start irq and line irq occured */
frame_start_line = (1 << FLITE_STATUS_IRQ_SRC_START) | (1 << FLITE_STATUS_IRQ_SRC_LINE);
if ((status1 & frame_start_end) == frame_start_end ||
(status1 & frame_start_line) == frame_start_line) {
dbg_fliteisr("*");
/* frame both interrupt since latency */
if (flite->sw_checker) {
dbg_fliteisr(">");
/* frame end interrupt */
flite->sw_checker = EXPECT_FRAME_START;
tasklet_schedule(&flite->tasklet_flite_end);
dbg_fliteisr("<");
/* frame start interrupt */
flite->sw_checker = EXPECT_FRAME_END;
atomic_inc(&flite->fcount);
notify_fcount(flite);
tasklet_schedule(&flite->tasklet_flite_str);
} else {
dbg_fliteisr("<");
/* frame start interrupt */
flite->sw_checker = EXPECT_FRAME_END;
atomic_inc(&flite->fcount);
notify_fcount(flite);
tasklet_schedule(&flite->tasklet_flite_str);
dbg_fliteisr(">");
/* frame end interrupt */
flite->sw_checker = EXPECT_FRAME_START;
tasklet_schedule(&flite->tasklet_flite_end);
}
} else if (status1 & (1 << FLITE_STATUS_IRQ_SRC_START)) {
/* W/A: Skip start tasklet at interrupt lost case */
if (flite->sw_checker != EXPECT_FRAME_START) {
warn("[CamIF%d] Lost end interupt\n",
flite->instance);
goto clear_status;
}
dbg_fliteisr("<");
/* frame start interrupt */
flite->sw_checker = EXPECT_FRAME_END;
atomic_inc(&flite->fcount);
notify_fcount(flite);
tasklet_schedule(&flite->tasklet_flite_str);
} else if (status1 & (1 << FLITE_STATUS_IRQ_SRC_END)) {
/* W/A: Skip end tasklet at interrupt lost case */
if (flite->sw_checker != EXPECT_FRAME_END) {
warn("[CamIF%d] Lost start interupt\n",
flite->instance);
goto clear_status;
}
dbg_fliteisr(">");
/* frame end interrupt */
flite->sw_checker = EXPECT_FRAME_START;
tasklet_schedule(&flite->tasklet_flite_end);
} else if (status1 & (1 << FLITE_STATUS_IRQ_SRC_LINE)) {
/* For Line Interrupt */
/* W/A: Skip end tasklet at interrupt lost case */
if (flite->sw_checker != EXPECT_FRAME_END) {
warn("[CamIF%d] Lost start interupt\n",
flite->instance);
goto clear_status;
}
dbg_fliteisr(">");
/* frame end interrupt */
flite->sw_checker = EXPECT_FRAME_START;
tasklet_schedule(&flite->tasklet_flite_end);
}
clear_status:
if (status1 & (1 << FLITE_STATUS_IRQ_SRC_LAST_CAPTURE)) {
/* Last Frame Capture Interrupt */
info("[CamIF%d] Last Frame Capture(fcount : %d)\n",
flite->instance, atomic_read(&flite->fcount));
/* Notify last capture */
set_bit(FLITE_LAST_CAPTURE, &flite->state);
wake_up(&flite->wait_queue);
}
if (status1 & (1 << FLITE_STATUS_OFCR)) {
flite_hw_set_interrupt_mask(flite->base_reg, false, (1 << FLITE_MASK_IRQ_OVERFLOW));
if (flite->overflow_cnt % FLITE_OVERFLOW_COUNT == 0)
pr_err("[CamIF%d] OFCR(cnt:%u)\n", flite->instance, flite->overflow_cnt);
flite_hw_get_status(flite->base_reg, (1 << FLITE_STATUS_OFCR), true);
flite->overflow_cnt++;
}
if (status1 & (1 << FLITE_STATUS_OFCB)) {
flite_hw_set_interrupt_mask(flite->base_reg, false, (1 << FLITE_MASK_IRQ_OVERFLOW));
if (flite->overflow_cnt % FLITE_OVERFLOW_COUNT == 0)
pr_err("[CamIF%d] OFCB(cnt:%u)\n", flite->instance, flite->overflow_cnt);
flite_hw_get_status(flite->base_reg, (1 << FLITE_STATUS_OFCB), true);
flite->overflow_cnt++;
}
if (status1 & (1 << FLITE_STATUS_OFY)) {
flite_hw_set_interrupt_mask(flite->base_reg, false, (1 << FLITE_MASK_IRQ_OVERFLOW));
if (flite->overflow_cnt % FLITE_OVERFLOW_COUNT == 0)
pr_err("[CamIF%d] OFY(cnt:%u)\n", flite->instance, flite->overflow_cnt);
flite_hw_get_status(flite->base_reg, (1 << FLITE_STATUS_OFY), true);
flite->overflow_cnt++;
}
return IRQ_HANDLED;
}
static int start_fimc_lite(u32 __iomem *base_reg,
struct fimc_is_image *image,
u32 instance,
u32 bns,
u32 csi_ch,
u32 flite_ch,
struct fimc_is_device_sensor *sensor)
{
/* source size, format setting */
flite_hw_set_fmt_source(base_reg, image);
/* dma size, format setting */
flite_hw_set_fmt_dma(base_reg, image);
/* set interrupt source */
flite_hw_set_interrupt_mask(base_reg, true, ((1 << FLITE_MASK_IRQ_ALL) - 1));
#if defined(SUPPORTED_EARLYBUF_DONE_SW) || defined(SUPPORTED_EARLYBUF_DONE_HW)
/* in case of early buffer done mode, disabled end interrupt */
if (sensor->early_buf_done_mode) {
flite_hw_set_interrupt_mask(base_reg, false, (1 << FLITE_MASK_IRQ_END));
#if defined(SUPPORTED_EARLYBUF_DONE_SW)
flite_hw_set_interrupt_mask(base_reg, false, (1 << FLITE_MASK_IRQ_LINE));
#elif defined(SUPPORTED_EARLYBUF_DONE_HW)
flite_hw_s_control(base_reg, FLITE_CTRL_LINE_RATIO, sensor->early_buf_done_mode);
#endif
} else {
flite_hw_set_interrupt_mask(base_reg, false, (1 << FLITE_MASK_IRQ_LINE));
}
#endif
/* path setting */
if (sensor->ischain && !test_bit(FIMC_IS_ISCHAIN_REPROCESSING, &sensor->ischain->state))
/* set mux setting for otf between 3aa and isp */
flite_hw_set_path(base_reg, instance,
csi_ch, flite_ch,
sensor->ischain->group_3aa.id - GROUP_ID_3AA0,
sensor->ischain->group_isp.id - GROUP_ID_ISP0);
else
flite_hw_set_path(base_reg, instance,
csi_ch, flite_ch, -1, -1);
#ifdef COLORBAR_MODE
flite_hw_s_control(base_reg, FLITE_CTRL_TEST_PATTERN, 1);
#endif
/* binning scale setting */
if (bns)
flite_hw_set_bns(base_reg, true, image);
/* start fimc-bns(lite) */
flite_hw_enable(base_reg);
return 0;
}
int fimc_is_flite_open(struct v4l2_subdev *subdev,
struct fimc_is_framemgr *framemgr)
{
int ret = 0;
struct fimc_is_device_flite *flite;
struct fimc_is_device_sensor *device = v4l2_get_subdev_hostdata(subdev);
FIMC_BUG(!device);
FIMC_BUG(!subdev);
FIMC_BUG(!framemgr);
flite = v4l2_get_subdevdata(subdev);
if (!flite) {
err("flite is NULL");
ret = -EINVAL;
goto p_err;
}
flite->group = 0;
flite->framemgr = framemgr;
atomic_set(&flite->fcount, 0);
clear_bit(FLITE_JOIN_ISCHAIN, &flite->state);
clear_bit(FLITE_OTF_WITH_3AA, &flite->state);
clear_bit(FLITE_LAST_CAPTURE, &flite->state);
clear_bit(FLITE_START_STREAM, &flite->state);
if (!test_bit(FLITE_DMA_ENABLE, &flite->state))
goto p_err;
ret = request_irq(flite->irq,
fimc_is_isr_flite,
FIMC_IS_HW_IRQ_FLAG,
"fimc-lite",
flite);
if (ret)
err("request_irq(FIMC-LITE %d) failed\n", flite->irq);
p_err:
return ret;
}
int fimc_is_flite_close(struct v4l2_subdev *subdev)
{
int ret = 0;
struct fimc_is_device_flite *flite;
struct fimc_is_device_sensor *device = v4l2_get_subdev_hostdata(subdev);
FIMC_BUG(!device);
FIMC_BUG(!subdev);
flite = v4l2_get_subdevdata(subdev);
if (!flite) {
err("flite is NULL");
ret = -EINVAL;
goto p_err;
}
if (!test_bit(FLITE_DMA_ENABLE, &flite->state))
goto p_err;
free_irq(flite->irq, flite);
p_err:
return ret;
}
/* value : csi ch */
static int flite_init(struct v4l2_subdev *subdev, u32 value)
{
int ret = 0;
struct fimc_is_device_flite *flite;
FIMC_BUG(!subdev);
flite = v4l2_get_subdevdata(subdev);
if (!flite) {
err("flite is NULL");
ret = -EINVAL;
goto p_err;
}
if (flite->instance == FLITE_ID_NOTHING) {
info("can't not link fimc-lite(bns)\n");
return 0;
}
flite->csi = value;
p_err:
return ret;
}
static int flite_stream_on(struct v4l2_subdev *subdev,
struct fimc_is_device_flite *flite)
{
int ret = 0;
u32 otf_setting, bns;
bool buffer_ready;
unsigned long flags;
struct fimc_is_image *image;
struct fimc_is_framemgr *framemgr;
struct fimc_is_frame *frame;
struct fimc_is_device_sensor *device = v4l2_get_subdev_hostdata(subdev);
FIMC_BUG(!flite);
FIMC_BUG(!flite->framemgr);
FIMC_BUG(!device);
if (test_bit(FLITE_START_STREAM, &flite->state)) {
merr("already start", flite);
ret = -EINVAL;
goto p_err;
}
otf_setting = 0;
buffer_ready = false;
framemgr = flite->framemgr;
image = &flite->image;
bns = device->pdata->is_bns;
flite->overflow_cnt = 0;
flite->sw_checker = EXPECT_FRAME_START;
flite->tasklet_param_str = 0;
flite->tasklet_param_end = 0;
clear_bit(FLITE_LAST_CAPTURE, &flite->state);
/* 1. init */
flite_hw_reset(flite->base_reg);
if (!test_bit(FLITE_DMA_ENABLE, &flite->state))
goto p_dma_skip;
/* 2. dma setting */
framemgr_e_barrier_irqs(framemgr, FMGR_IDX_13, flags);
flite_hw_set_dma_addr(flite->base_reg, 0, false, 0);
if (framemgr->queued_count[FS_REQUEST] >= 1) {
frame = peek_frame(framemgr, FS_REQUEST);
if (frame) {
flite_hw_set_dma_addr(flite->base_reg, 0, true,
(u32)frame->dvaddr_buffer[0]);
trans_frame(framemgr, frame, FS_PROCESS);
buffer_ready = true;
} else {
merr("framemgr's state was invalid", device);
frame_manager_print_queues(framemgr);
ret = -EINVAL;
}
}
framemgr_x_barrier_irqr(framemgr, FMGR_IDX_13, flags);
p_dma_skip:
flite_hw_set_output_dma(flite->base_reg, buffer_ready);
/* 3. otf setting */
if (device->ischain)
set_bit(FLITE_JOIN_ISCHAIN, &flite->state);
else
clear_bit(FLITE_JOIN_ISCHAIN, &flite->state);
if (device->ischain &&
test_bit(FIMC_IS_GROUP_OTF_INPUT, &device->ischain->group_3aa.state)) {
tasklet_init(&flite->tasklet_flite_str, tasklet_flite_str_otf, (unsigned long)subdev);
tasklet_init(&flite->tasklet_flite_end, tasklet_flite_end, (unsigned long)subdev);
if (device->ischain->group_3aa.id == GROUP_ID_3AA0) {
flite->group = GROUP_ID_3AA0;
} else if (device->ischain->group_3aa.id == GROUP_ID_3AA1) {
flite->group = GROUP_ID_3AA1;
} else {
merr("invalid otf path(%d)", device, device->ischain->group_3aa.id);
ret = -EINVAL;
goto p_err;
}
mdbgd_back("Enabling OTF path. target 3aa(%d)\n", flite, flite->group);
flite_hw_set_output_otf(flite->base_reg, true);
set_bit(FLITE_OTF_WITH_3AA, &flite->state);
} else {
#if defined(SUPPORTED_EARLYBUF_DONE_SW)
if (device->early_buf_done_mode)
device->early_buf_timer.function = flite_early_buf_done;
#endif
tasklet_init(&flite->tasklet_flite_str, tasklet_flite_str_m2m, (unsigned long)subdev);
tasklet_init(&flite->tasklet_flite_end, tasklet_flite_end, (unsigned long)subdev);
flite_hw_set_output_otf(flite->base_reg, true);
clear_bit(FLITE_OTF_WITH_3AA, &flite->state);
}
/* 4. register setting */
start_fimc_lite(flite->base_reg, image, flite->instance, bns, flite->csi, flite->instance, device);
#ifdef DBG_DUMPREG
flite_hw_dump(flite->base_reg);
#endif
set_bit(FLITE_START_STREAM, &flite->state);
p_err:
return ret;
}
static int flite_stream_off(struct v4l2_subdev *subdev,
struct fimc_is_device_flite *flite,
bool nowait)
{
int ret = 0;
u32 __iomem *base_reg;
unsigned long flags;
struct fimc_is_framemgr *framemgr;
struct fimc_is_frame *frame;
struct fimc_is_device_sensor *device = v4l2_get_subdev_hostdata(subdev);
FIMC_BUG(!flite);
FIMC_BUG(!flite->base_reg);
FIMC_BUG(!flite->framemgr);
FIMC_BUG(!device);
if (!test_bit(FLITE_START_STREAM, &flite->state)) {
merr("already stop", flite);
ret = -EINVAL;
goto p_err;
}
base_reg = flite->base_reg;
framemgr = flite->framemgr;
/* for preventing invalid memory access */
flite_hw_set_dma_addr(base_reg, 0, false, 0);
flite_hw_set_output_dma(base_reg, false);
flite_hw_set_output_otf(base_reg, false);
/* stop fimc-bns(lite) */
flite_hw_disable(base_reg);
#if defined(SUPPORTED_EARLYBUF_DONE_SW)
if (device->early_buf_done_mode) {
info("early buffer done flushed..\n");
hrtimer_cancel(&device->early_buf_timer);
}
#endif
if (!nowait) {
ulong timetowait;
timetowait = wait_event_timeout(flite->wait_queue,
test_bit(FLITE_LAST_CAPTURE, &flite->state),
FIMC_IS_FLITE_STOP_TIMEOUT);
if (!timetowait) {
/* forcely stop */
flite_hw_disable(base_reg);
set_bit(FLITE_LAST_CAPTURE, &flite->state);
err("last capture timeout:%s", __func__);
msleep(200);
flite_hw_reset(base_reg);
ret = -ETIME;
}
} else {
/*
* DTP test can make iommu fault because senosr is streaming
* therefore it need force reset
*/
flite_hw_reset(base_reg);
}
/* clr interrupt source */
flite_hw_set_interrupt_mask(flite->base_reg, false, ((1 << FLITE_MASK_IRQ_ALL) - 1));
if (!test_bit(FLITE_DMA_ENABLE, &flite->state))
goto dma_skip;
framemgr_e_barrier_irqs(framemgr, FMGR_IDX_14, flags);
frame = peek_frame(framemgr, FS_PROCESS);
while (frame) {
CALL_VOPS(device->vctx, done, frame->index, VB2_BUF_STATE_ERROR);
trans_frame(framemgr, frame, FS_COMPLETE);
frame = peek_frame(framemgr, FS_PROCESS);
}
frame = peek_frame(framemgr, FS_REQUEST);
while (frame) {
CALL_VOPS(device->vctx, done, frame->index, VB2_BUF_STATE_ERROR);
trans_frame(framemgr, frame, FS_COMPLETE);
frame = peek_frame(framemgr, FS_REQUEST);
}
framemgr_x_barrier_irqr(framemgr, FMGR_IDX_14, flags);
dma_skip:
clear_bit(FLITE_START_STREAM, &flite->state);
p_err:
return ret;
}
/*
* enable
* @X0 : disable
* @X1 : enable
* @1X : no waiting flag
* @0X : waiting flag
*/
static int flite_s_stream(struct v4l2_subdev *subdev, int enable)
{
int ret = 0;
bool nowait;
struct fimc_is_device_flite *flite;
FIMC_BUG(!subdev);
nowait = (enable & FLITE_NOWAIT_MASK) >> FLITE_NOWAIT_SHIFT;
enable = enable & FLITE_ENABLE_MASK;
flite = (struct fimc_is_device_flite *)v4l2_get_subdevdata(subdev);
if (!flite) {
err("flite is NULL");
ret = -EINVAL;
goto p_err;
}
if (flite->instance == FLITE_ID_NOTHING) {
info("can't not link fimc-lite(bns)\n");
return 0;
}
/* H/W setting skip */
if (test_bit(FLITE_DUMMY, &flite->state)) {
mdbgd_back("%s(dummy)\n", flite, __func__);
goto p_err;
}
if (enable) {
ret = flite_stream_on(subdev, flite);
if (ret) {
err("flite_stream_on is fail(%d)", ret);
goto p_err;
}
} else {
ret = flite_stream_off(subdev, flite, nowait);
if (ret) {
err("flite_stream_off is fail(%d)", ret);
goto p_err;
}
}
p_err:
mdbgd_back("%s(%d, %d)\n", flite, __func__, enable, ret);
return 0;
}
static int flite_s_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
int ret = 0;
struct fimc_is_device_flite *flite;
FIMC_BUG(!subdev);
FIMC_BUG(!fmt);
flite = (struct fimc_is_device_flite *)v4l2_get_subdevdata(subdev);
if (!flite) {
err("flite is NULL");
ret = -EINVAL;
goto p_err;
}
if (flite->instance == FLITE_ID_NOTHING) {
info("can't not link fimc-lite(bns)\n");
return 0;
}
flite->image.window.offs_h = 0;
flite->image.window.offs_v = 0;
flite->image.window.width = fmt->format.width;
flite->image.window.height = fmt->format.height;
flite->image.window.o_width = fmt->format.width;
flite->image.window.o_height = fmt->format.height;
flite->image.format.pixelformat = fmt->format.code;
p_err:
mdbgd_back("%s(%dx%d, %X)\n", flite, __func__, fmt->format.width,
fmt->format.height, fmt->format.code);
return ret;
}
static int flite_s_buffer(struct v4l2_subdev *subdev, void *buf, unsigned int *size)
{
int ret = 0;
unsigned long flags;
struct fimc_is_device_flite *flite;
struct fimc_is_framemgr *framemgr;
struct fimc_is_frame *frame;
FIMC_BUG(!subdev);
flite = (struct fimc_is_device_flite *)v4l2_get_subdevdata(subdev);
if (unlikely(flite == NULL)) {
err("flite is NULL");
ret = -EINVAL;
goto p_err;
}
if (!test_bit(FLITE_DMA_ENABLE, &flite->state))
goto p_err;
framemgr = flite->framemgr;
if (unlikely(framemgr == NULL)) {
merr("framemgr is NULL", flite);
ret = -EINVAL;
goto p_err;
}
if (test_bit(FLITE_START_STREAM, &flite->state)) {
framemgr_e_barrier_irqs(framemgr, FMGR_IDX_15, flags);
frame = peek_frame(framemgr, FS_REQUEST);
if (frame) {
if (!flite_hw_get_output_dma(flite->base_reg)) {
flite_hw_set_dma_addr(flite->base_reg, 0, true,
(u32)frame->dvaddr_buffer[0]);
trans_frame(framemgr, frame, FS_PROCESS);
}
}
framemgr_x_barrier_irqr(framemgr, FMGR_IDX_15, flags);
}
p_err:
return ret;
}
static int flite_s_ctrl(struct v4l2_subdev *subdev, struct v4l2_control *ctrl)
{
int ret = 0;
struct fimc_is_device_flite *flite;
FIMC_BUG(!subdev);
FIMC_BUG(!ctrl);
flite = (struct fimc_is_device_flite *)v4l2_get_subdevdata(subdev);
if (!flite) {
err("flite is NULL");
ret = -EINVAL;
goto p_err;
}
if (flite->instance == FLITE_ID_NOTHING) {
info("can't not link fimc-lite(bns)\n");
return 0;
}
switch (ctrl->id) {
case V4L2_CID_IS_S_BNS:
{
u32 width, height, ratio;
width = flite->image.window.width;
height = flite->image.window.height;
ratio = ctrl->value;
flite->image.window.otf_width
= rounddown((width * 1000 / ratio), 4);
flite->image.window.otf_height
= rounddown((height * 1000 / ratio), 2);
}
break;
default:
err("unsupported ioctl(%d)\n", ctrl->id);
ret = -EINVAL;
break;
}
p_err:
return ret;
}
static const struct v4l2_subdev_core_ops core_ops = {
.init = flite_init,
.s_ctrl = flite_s_ctrl,
};
static const struct v4l2_subdev_video_ops video_ops = {
.s_stream = flite_s_stream,
.s_rx_buffer= flite_s_buffer
};
static const struct v4l2_subdev_pad_ops pad_ops = {
.set_fmt = flite_s_format
};
static const struct v4l2_subdev_ops subdev_ops = {
.core = &core_ops,
.video = &video_ops,
.pad = &pad_ops
};
int fimc_is_flite_probe(struct fimc_is_device_sensor *device,
u32 instance)
{
int ret = 0;
struct v4l2_subdev *subdev_flite;
struct fimc_is_device_flite *flite;
struct resource *mem_res;
struct platform_device *pdev;
FIMC_BUG(!device);
subdev_flite = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
if (!subdev_flite) {
merr("subdev_flite is NULL", device);
ret = -ENOMEM;
goto err_alloc_subdev_flite;
}
device->subdev_flite = subdev_flite;
flite = kzalloc(sizeof(struct fimc_is_device_flite), GFP_KERNEL);
if (!flite) {
merr("flite is NULL", device);
ret = -ENOMEM;
goto err_alloc_flite;
}
pdev = device->pdev;
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 10);
if (!mem_res) {
probe_err("Failed to get io memory region(%p)", mem_res);
ret = -EBUSY;
goto err_get_resource;
}
flite->regs_start = mem_res->start;
flite->regs_end = mem_res->end;
flite->base_reg = devm_ioremap_nocache(&pdev->dev, mem_res->start, resource_size(mem_res));
if (!flite->base_reg) {
probe_err("Failed to remap io region(%p)", flite->base_reg);
ret = -ENOMEM;
goto err_ioremap;
}
flite->irq = platform_get_irq(pdev, 1);
if (flite->irq < 0) {
probe_err("Failed to get flite->irq(%d)", flite->irq);
ret = -EBUSY;
goto err_get_irq;
}
/* pointer to me from device sensor */
flite->subdev = &device->subdev_flite;
flite->instance = instance;
init_waitqueue_head(&flite->wait_queue);
/* default state setting */
clear_bit(FLITE_DUMMY, &flite->state);
set_bit(FLITE_DMA_ENABLE, &flite->state);
v4l2_subdev_init(subdev_flite, &subdev_ops);
v4l2_set_subdevdata(subdev_flite, (void *)flite);
v4l2_set_subdev_hostdata(subdev_flite, device);
snprintf(subdev_flite->name, V4L2_SUBDEV_NAME_SIZE, "flite-subdev.%d", instance);
ret = v4l2_device_register_subdev(&device->v4l2_dev, subdev_flite);
if (ret) {
merr("v4l2_device_register_subdev is fail(%d)", device, ret);
goto err_reg_v4l2_subdev;
}
info("[%d][BAK:D] %s(%d)\n", instance, __func__, ret);
return 0;
err_reg_v4l2_subdev:
err_get_irq:
iounmap(flite->base_reg);
err_ioremap:
err_get_resource:
kfree(flite);
err_alloc_flite:
kfree(subdev_flite);
device->subdev_flite = NULL;
err_alloc_subdev_flite:
err("[BAK:D:%d] %s(%d)\n", instance, __func__, ret);
return ret;
}