mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-27 12:32:50 +00:00
In order to support Qualcomm ISP hardware architectures that diverge from older architectures, the VFE subdevice driver needs to be refactored to better abstract the different ISP architectures. Gen1 represents the CAMSS ISP architecture. The ISP architecture developed after CAMSS, Titan, will be referred to as Gen2. Signed-off-by: Robert Foss <robert.foss@linaro.org> Reviewed-by: Andrey Konovalov <andrey.konovalov@linaro.org> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
743 lines
18 KiB
C
743 lines
18 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* camss-vfe-gen1.c
|
|
*
|
|
* Qualcomm MSM Camera Subsystem - VFE Common functionality for Gen 1 versions of hw (4.1, 4.7..)
|
|
*
|
|
* Copyright (C) 2020 Linaro Ltd.
|
|
*/
|
|
|
|
#include "camss.h"
|
|
#include "camss-vfe.h"
|
|
#include "camss-vfe-gen1.h"
|
|
|
|
/* Max number of frame drop updates per frame */
|
|
#define VFE_FRAME_DROP_UPDATES 2
|
|
#define VFE_NEXT_SOF_MS 500
|
|
|
|
int vfe_gen1_halt(struct vfe_device *vfe)
|
|
{
|
|
unsigned long time;
|
|
|
|
reinit_completion(&vfe->halt_complete);
|
|
|
|
vfe->ops_gen1->halt_request(vfe);
|
|
|
|
time = wait_for_completion_timeout(&vfe->halt_complete,
|
|
msecs_to_jiffies(VFE_HALT_TIMEOUT_MS));
|
|
if (!time) {
|
|
dev_err(vfe->camss->dev, "VFE halt timeout\n");
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vfe_disable_output(struct vfe_line *line)
|
|
{
|
|
struct vfe_device *vfe = to_vfe(line);
|
|
struct vfe_output *output = &line->output;
|
|
const struct vfe_hw_ops *ops = vfe->ops;
|
|
unsigned long flags;
|
|
unsigned long time;
|
|
unsigned int i;
|
|
|
|
spin_lock_irqsave(&vfe->output_lock, flags);
|
|
|
|
output->gen1.wait_sof = 1;
|
|
spin_unlock_irqrestore(&vfe->output_lock, flags);
|
|
|
|
time = wait_for_completion_timeout(&output->sof, msecs_to_jiffies(VFE_NEXT_SOF_MS));
|
|
if (!time)
|
|
dev_err(vfe->camss->dev, "VFE sof timeout\n");
|
|
|
|
spin_lock_irqsave(&vfe->output_lock, flags);
|
|
for (i = 0; i < output->wm_num; i++)
|
|
vfe->ops_gen1->wm_enable(vfe, output->wm_idx[i], 0);
|
|
|
|
ops->reg_update(vfe, line->id);
|
|
output->wait_reg_update = 1;
|
|
spin_unlock_irqrestore(&vfe->output_lock, flags);
|
|
|
|
time = wait_for_completion_timeout(&output->reg_update, msecs_to_jiffies(VFE_NEXT_SOF_MS));
|
|
if (!time)
|
|
dev_err(vfe->camss->dev, "VFE reg update timeout\n");
|
|
|
|
spin_lock_irqsave(&vfe->output_lock, flags);
|
|
|
|
if (line->id != VFE_LINE_PIX) {
|
|
vfe->ops_gen1->wm_frame_based(vfe, output->wm_idx[0], 0);
|
|
vfe->ops_gen1->bus_disconnect_wm_from_rdi(vfe, output->wm_idx[0], line->id);
|
|
vfe->ops_gen1->enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 0);
|
|
vfe->ops_gen1->set_cgc_override(vfe, output->wm_idx[0], 0);
|
|
spin_unlock_irqrestore(&vfe->output_lock, flags);
|
|
} else {
|
|
for (i = 0; i < output->wm_num; i++) {
|
|
vfe->ops_gen1->wm_line_based(vfe, output->wm_idx[i], NULL, i, 0);
|
|
vfe->ops_gen1->set_cgc_override(vfe, output->wm_idx[i], 0);
|
|
}
|
|
|
|
vfe->ops_gen1->enable_irq_pix_line(vfe, 0, line->id, 0);
|
|
vfe->ops_gen1->set_module_cfg(vfe, 0);
|
|
vfe->ops_gen1->set_realign_cfg(vfe, line, 0);
|
|
vfe->ops_gen1->set_xbar_cfg(vfe, output, 0);
|
|
vfe->ops_gen1->set_camif_cmd(vfe, 0);
|
|
|
|
spin_unlock_irqrestore(&vfe->output_lock, flags);
|
|
|
|
vfe->ops_gen1->camif_wait_for_stop(vfe, vfe->camss->dev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* vfe_gen1_disable - Disable streaming on VFE line
|
|
* @line: VFE line
|
|
*
|
|
* Return 0 on success or a negative error code otherwise
|
|
*/
|
|
int vfe_gen1_disable(struct vfe_line *line)
|
|
{
|
|
struct vfe_device *vfe = to_vfe(line);
|
|
|
|
vfe_disable_output(line);
|
|
|
|
vfe_put_output(line);
|
|
|
|
mutex_lock(&vfe->stream_lock);
|
|
|
|
if (vfe->stream_count == 1)
|
|
vfe->ops_gen1->bus_enable_wr_if(vfe, 0);
|
|
|
|
vfe->stream_count--;
|
|
|
|
mutex_unlock(&vfe->stream_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void vfe_output_init_addrs(struct vfe_device *vfe,
|
|
struct vfe_output *output, u8 sync,
|
|
struct vfe_line *line)
|
|
{
|
|
u32 ping_addr;
|
|
u32 pong_addr;
|
|
unsigned int i;
|
|
|
|
output->gen1.active_buf = 0;
|
|
|
|
for (i = 0; i < output->wm_num; i++) {
|
|
if (output->buf[0])
|
|
ping_addr = output->buf[0]->addr[i];
|
|
else
|
|
ping_addr = 0;
|
|
|
|
if (output->buf[1])
|
|
pong_addr = output->buf[1]->addr[i];
|
|
else
|
|
pong_addr = ping_addr;
|
|
|
|
vfe->ops_gen1->wm_set_ping_addr(vfe, output->wm_idx[i], ping_addr);
|
|
vfe->ops_gen1->wm_set_pong_addr(vfe, output->wm_idx[i], pong_addr);
|
|
if (sync)
|
|
vfe->ops_gen1->bus_reload_wm(vfe, output->wm_idx[i]);
|
|
}
|
|
}
|
|
|
|
static void vfe_output_frame_drop(struct vfe_device *vfe,
|
|
struct vfe_output *output,
|
|
u32 drop_pattern)
|
|
{
|
|
u8 drop_period;
|
|
unsigned int i;
|
|
|
|
/* We need to toggle update period to be valid on next frame */
|
|
output->drop_update_idx++;
|
|
output->drop_update_idx %= VFE_FRAME_DROP_UPDATES;
|
|
drop_period = VFE_FRAME_DROP_VAL + output->drop_update_idx;
|
|
|
|
for (i = 0; i < output->wm_num; i++) {
|
|
vfe->ops_gen1->wm_set_framedrop_period(vfe, output->wm_idx[i], drop_period);
|
|
vfe->ops_gen1->wm_set_framedrop_pattern(vfe, output->wm_idx[i], drop_pattern);
|
|
}
|
|
|
|
vfe->ops->reg_update(vfe, container_of(output, struct vfe_line, output)->id);
|
|
}
|
|
|
|
static int vfe_enable_output(struct vfe_line *line)
|
|
{
|
|
struct vfe_device *vfe = to_vfe(line);
|
|
struct vfe_output *output = &line->output;
|
|
const struct vfe_hw_ops *ops = vfe->ops;
|
|
struct media_entity *sensor;
|
|
unsigned long flags;
|
|
unsigned int frame_skip = 0;
|
|
unsigned int i;
|
|
u16 ub_size;
|
|
|
|
ub_size = vfe->ops_gen1->get_ub_size(vfe->id);
|
|
if (!ub_size)
|
|
return -EINVAL;
|
|
|
|
sensor = camss_find_sensor(&line->subdev.entity);
|
|
if (sensor) {
|
|
struct v4l2_subdev *subdev = media_entity_to_v4l2_subdev(sensor);
|
|
|
|
v4l2_subdev_call(subdev, sensor, g_skip_frames, &frame_skip);
|
|
/* Max frame skip is 29 frames */
|
|
if (frame_skip > VFE_FRAME_DROP_VAL - 1)
|
|
frame_skip = VFE_FRAME_DROP_VAL - 1;
|
|
}
|
|
|
|
spin_lock_irqsave(&vfe->output_lock, flags);
|
|
|
|
ops->reg_update_clear(vfe, line->id);
|
|
|
|
if (output->state != VFE_OUTPUT_RESERVED) {
|
|
dev_err(vfe->camss->dev, "Output is not in reserved state %d\n", output->state);
|
|
spin_unlock_irqrestore(&vfe->output_lock, flags);
|
|
return -EINVAL;
|
|
}
|
|
output->state = VFE_OUTPUT_IDLE;
|
|
|
|
output->buf[0] = vfe_buf_get_pending(output);
|
|
output->buf[1] = vfe_buf_get_pending(output);
|
|
|
|
if (!output->buf[0] && output->buf[1]) {
|
|
output->buf[0] = output->buf[1];
|
|
output->buf[1] = NULL;
|
|
}
|
|
|
|
if (output->buf[0])
|
|
output->state = VFE_OUTPUT_SINGLE;
|
|
|
|
if (output->buf[1])
|
|
output->state = VFE_OUTPUT_CONTINUOUS;
|
|
|
|
switch (output->state) {
|
|
case VFE_OUTPUT_SINGLE:
|
|
vfe_output_frame_drop(vfe, output, 1 << frame_skip);
|
|
break;
|
|
case VFE_OUTPUT_CONTINUOUS:
|
|
vfe_output_frame_drop(vfe, output, 3 << frame_skip);
|
|
break;
|
|
default:
|
|
vfe_output_frame_drop(vfe, output, 0);
|
|
break;
|
|
}
|
|
|
|
output->sequence = 0;
|
|
output->gen1.wait_sof = 0;
|
|
output->wait_reg_update = 0;
|
|
reinit_completion(&output->sof);
|
|
reinit_completion(&output->reg_update);
|
|
|
|
vfe_output_init_addrs(vfe, output, 0, line);
|
|
|
|
if (line->id != VFE_LINE_PIX) {
|
|
vfe->ops_gen1->set_cgc_override(vfe, output->wm_idx[0], 1);
|
|
vfe->ops_gen1->enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 1);
|
|
vfe->ops_gen1->bus_connect_wm_to_rdi(vfe, output->wm_idx[0], line->id);
|
|
vfe->ops_gen1->wm_set_subsample(vfe, output->wm_idx[0]);
|
|
vfe->ops_gen1->set_rdi_cid(vfe, line->id, 0);
|
|
vfe->ops_gen1->wm_set_ub_cfg(vfe, output->wm_idx[0],
|
|
(ub_size + 1) * output->wm_idx[0], ub_size);
|
|
vfe->ops_gen1->wm_frame_based(vfe, output->wm_idx[0], 1);
|
|
vfe->ops_gen1->wm_enable(vfe, output->wm_idx[0], 1);
|
|
vfe->ops_gen1->bus_reload_wm(vfe, output->wm_idx[0]);
|
|
} else {
|
|
ub_size /= output->wm_num;
|
|
for (i = 0; i < output->wm_num; i++) {
|
|
vfe->ops_gen1->set_cgc_override(vfe, output->wm_idx[i], 1);
|
|
vfe->ops_gen1->wm_set_subsample(vfe, output->wm_idx[i]);
|
|
vfe->ops_gen1->wm_set_ub_cfg(vfe, output->wm_idx[i],
|
|
(ub_size + 1) * output->wm_idx[i], ub_size);
|
|
vfe->ops_gen1->wm_line_based(vfe, output->wm_idx[i],
|
|
&line->video_out.active_fmt.fmt.pix_mp, i, 1);
|
|
vfe->ops_gen1->wm_enable(vfe, output->wm_idx[i], 1);
|
|
vfe->ops_gen1->bus_reload_wm(vfe, output->wm_idx[i]);
|
|
}
|
|
vfe->ops_gen1->enable_irq_pix_line(vfe, 0, line->id, 1);
|
|
vfe->ops_gen1->set_module_cfg(vfe, 1);
|
|
vfe->ops_gen1->set_camif_cfg(vfe, line);
|
|
vfe->ops_gen1->set_realign_cfg(vfe, line, 1);
|
|
vfe->ops_gen1->set_xbar_cfg(vfe, output, 1);
|
|
vfe->ops_gen1->set_demux_cfg(vfe, line);
|
|
vfe->ops_gen1->set_scale_cfg(vfe, line);
|
|
vfe->ops_gen1->set_crop_cfg(vfe, line);
|
|
vfe->ops_gen1->set_clamp_cfg(vfe);
|
|
vfe->ops_gen1->set_camif_cmd(vfe, 1);
|
|
}
|
|
|
|
ops->reg_update(vfe, line->id);
|
|
|
|
spin_unlock_irqrestore(&vfe->output_lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vfe_get_output(struct vfe_line *line)
|
|
{
|
|
struct vfe_device *vfe = to_vfe(line);
|
|
struct vfe_output *output;
|
|
struct v4l2_format *f = &line->video_out.active_fmt;
|
|
unsigned long flags;
|
|
int i;
|
|
int wm_idx;
|
|
|
|
spin_lock_irqsave(&vfe->output_lock, flags);
|
|
|
|
output = &line->output;
|
|
if (output->state != VFE_OUTPUT_OFF) {
|
|
dev_err(vfe->camss->dev, "Output is running\n");
|
|
goto error;
|
|
}
|
|
output->state = VFE_OUTPUT_RESERVED;
|
|
|
|
output->gen1.active_buf = 0;
|
|
|
|
switch (f->fmt.pix_mp.pixelformat) {
|
|
case V4L2_PIX_FMT_NV12:
|
|
case V4L2_PIX_FMT_NV21:
|
|
case V4L2_PIX_FMT_NV16:
|
|
case V4L2_PIX_FMT_NV61:
|
|
output->wm_num = 2;
|
|
break;
|
|
default:
|
|
output->wm_num = 1;
|
|
break;
|
|
}
|
|
|
|
for (i = 0; i < output->wm_num; i++) {
|
|
wm_idx = vfe_reserve_wm(vfe, line->id);
|
|
if (wm_idx < 0) {
|
|
dev_err(vfe->camss->dev, "Can not reserve wm\n");
|
|
goto error_get_wm;
|
|
}
|
|
output->wm_idx[i] = wm_idx;
|
|
}
|
|
|
|
output->drop_update_idx = 0;
|
|
|
|
spin_unlock_irqrestore(&vfe->output_lock, flags);
|
|
|
|
return 0;
|
|
|
|
error_get_wm:
|
|
for (i--; i >= 0; i--)
|
|
vfe_release_wm(vfe, output->wm_idx[i]);
|
|
output->state = VFE_OUTPUT_OFF;
|
|
error:
|
|
spin_unlock_irqrestore(&vfe->output_lock, flags);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
int vfe_gen1_enable(struct vfe_line *line)
|
|
{
|
|
struct vfe_device *vfe = to_vfe(line);
|
|
int ret;
|
|
|
|
mutex_lock(&vfe->stream_lock);
|
|
|
|
if (!vfe->stream_count) {
|
|
vfe->ops_gen1->enable_irq_common(vfe);
|
|
vfe->ops_gen1->bus_enable_wr_if(vfe, 1);
|
|
vfe->ops_gen1->set_qos(vfe);
|
|
vfe->ops_gen1->set_ds(vfe);
|
|
}
|
|
|
|
vfe->stream_count++;
|
|
|
|
mutex_unlock(&vfe->stream_lock);
|
|
|
|
ret = vfe_get_output(line);
|
|
if (ret < 0)
|
|
goto error_get_output;
|
|
|
|
ret = vfe_enable_output(line);
|
|
if (ret < 0)
|
|
goto error_enable_output;
|
|
|
|
vfe->was_streaming = 1;
|
|
|
|
return 0;
|
|
|
|
error_enable_output:
|
|
vfe_put_output(line);
|
|
|
|
error_get_output:
|
|
mutex_lock(&vfe->stream_lock);
|
|
|
|
if (vfe->stream_count == 1)
|
|
vfe->ops_gen1->bus_enable_wr_if(vfe, 0);
|
|
|
|
vfe->stream_count--;
|
|
|
|
mutex_unlock(&vfe->stream_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void vfe_output_update_ping_addr(struct vfe_device *vfe,
|
|
struct vfe_output *output, u8 sync,
|
|
struct vfe_line *line)
|
|
{
|
|
u32 addr;
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < output->wm_num; i++) {
|
|
if (output->buf[0])
|
|
addr = output->buf[0]->addr[i];
|
|
else
|
|
addr = 0;
|
|
|
|
vfe->ops_gen1->wm_set_ping_addr(vfe, output->wm_idx[i], addr);
|
|
if (sync)
|
|
vfe->ops_gen1->bus_reload_wm(vfe, output->wm_idx[i]);
|
|
}
|
|
}
|
|
|
|
static void vfe_output_update_pong_addr(struct vfe_device *vfe,
|
|
struct vfe_output *output, u8 sync,
|
|
struct vfe_line *line)
|
|
{
|
|
u32 addr;
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < output->wm_num; i++) {
|
|
if (output->buf[1])
|
|
addr = output->buf[1]->addr[i];
|
|
else
|
|
addr = 0;
|
|
|
|
vfe->ops_gen1->wm_set_pong_addr(vfe, output->wm_idx[i], addr);
|
|
if (sync)
|
|
vfe->ops_gen1->bus_reload_wm(vfe, output->wm_idx[i]);
|
|
}
|
|
}
|
|
|
|
static void vfe_buf_update_wm_on_next(struct vfe_device *vfe,
|
|
struct vfe_output *output)
|
|
{
|
|
switch (output->state) {
|
|
case VFE_OUTPUT_CONTINUOUS:
|
|
vfe_output_frame_drop(vfe, output, 3);
|
|
break;
|
|
case VFE_OUTPUT_SINGLE:
|
|
default:
|
|
dev_err_ratelimited(vfe->camss->dev,
|
|
"Next buf in wrong state! %d\n",
|
|
output->state);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void vfe_buf_update_wm_on_last(struct vfe_device *vfe,
|
|
struct vfe_output *output)
|
|
{
|
|
switch (output->state) {
|
|
case VFE_OUTPUT_CONTINUOUS:
|
|
output->state = VFE_OUTPUT_SINGLE;
|
|
vfe_output_frame_drop(vfe, output, 1);
|
|
break;
|
|
case VFE_OUTPUT_SINGLE:
|
|
output->state = VFE_OUTPUT_STOPPING;
|
|
vfe_output_frame_drop(vfe, output, 0);
|
|
break;
|
|
default:
|
|
dev_err_ratelimited(vfe->camss->dev,
|
|
"Last buff in wrong state! %d\n",
|
|
output->state);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void vfe_buf_update_wm_on_new(struct vfe_device *vfe,
|
|
struct vfe_output *output,
|
|
struct camss_buffer *new_buf,
|
|
struct vfe_line *line)
|
|
{
|
|
int inactive_idx;
|
|
|
|
switch (output->state) {
|
|
case VFE_OUTPUT_SINGLE:
|
|
inactive_idx = !output->gen1.active_buf;
|
|
|
|
if (!output->buf[inactive_idx]) {
|
|
output->buf[inactive_idx] = new_buf;
|
|
|
|
if (inactive_idx)
|
|
vfe_output_update_pong_addr(vfe, output, 0, line);
|
|
else
|
|
vfe_output_update_ping_addr(vfe, output, 0, line);
|
|
|
|
vfe_output_frame_drop(vfe, output, 3);
|
|
output->state = VFE_OUTPUT_CONTINUOUS;
|
|
} else {
|
|
vfe_buf_add_pending(output, new_buf);
|
|
dev_err_ratelimited(vfe->camss->dev,
|
|
"Inactive buffer is busy\n");
|
|
}
|
|
break;
|
|
|
|
case VFE_OUTPUT_IDLE:
|
|
if (!output->buf[0]) {
|
|
output->buf[0] = new_buf;
|
|
|
|
vfe_output_init_addrs(vfe, output, 1, line);
|
|
vfe_output_frame_drop(vfe, output, 1);
|
|
|
|
output->state = VFE_OUTPUT_SINGLE;
|
|
} else {
|
|
vfe_buf_add_pending(output, new_buf);
|
|
dev_err_ratelimited(vfe->camss->dev,
|
|
"Output idle with buffer set!\n");
|
|
}
|
|
break;
|
|
|
|
case VFE_OUTPUT_CONTINUOUS:
|
|
default:
|
|
vfe_buf_add_pending(output, new_buf);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* vfe_isr_halt_ack - Process halt ack
|
|
* @vfe: VFE Device
|
|
*/
|
|
static void vfe_isr_halt_ack(struct vfe_device *vfe)
|
|
{
|
|
complete(&vfe->halt_complete);
|
|
vfe->ops_gen1->halt_clear(vfe);
|
|
}
|
|
|
|
/*
|
|
* vfe_isr_sof - Process start of frame interrupt
|
|
* @vfe: VFE Device
|
|
* @line_id: VFE line
|
|
*/
|
|
static void vfe_isr_sof(struct vfe_device *vfe, enum vfe_line_id line_id)
|
|
{
|
|
struct vfe_output *output;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&vfe->output_lock, flags);
|
|
output = &vfe->line[line_id].output;
|
|
if (output->gen1.wait_sof) {
|
|
output->gen1.wait_sof = 0;
|
|
complete(&output->sof);
|
|
}
|
|
spin_unlock_irqrestore(&vfe->output_lock, flags);
|
|
}
|
|
|
|
/*
|
|
* vfe_isr_reg_update - Process reg update interrupt
|
|
* @vfe: VFE Device
|
|
* @line_id: VFE line
|
|
*/
|
|
static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
|
|
{
|
|
struct vfe_output *output;
|
|
struct vfe_line *line = &vfe->line[line_id];
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&vfe->output_lock, flags);
|
|
vfe->ops->reg_update_clear(vfe, line_id);
|
|
|
|
output = &line->output;
|
|
|
|
if (output->wait_reg_update) {
|
|
output->wait_reg_update = 0;
|
|
complete(&output->reg_update);
|
|
spin_unlock_irqrestore(&vfe->output_lock, flags);
|
|
return;
|
|
}
|
|
|
|
if (output->state == VFE_OUTPUT_STOPPING) {
|
|
/* Release last buffer when hw is idle */
|
|
if (output->last_buffer) {
|
|
vb2_buffer_done(&output->last_buffer->vb.vb2_buf,
|
|
VB2_BUF_STATE_DONE);
|
|
output->last_buffer = NULL;
|
|
}
|
|
output->state = VFE_OUTPUT_IDLE;
|
|
|
|
/* Buffers received in stopping state are queued in */
|
|
/* dma pending queue, start next capture here */
|
|
|
|
output->buf[0] = vfe_buf_get_pending(output);
|
|
output->buf[1] = vfe_buf_get_pending(output);
|
|
|
|
if (!output->buf[0] && output->buf[1]) {
|
|
output->buf[0] = output->buf[1];
|
|
output->buf[1] = NULL;
|
|
}
|
|
|
|
if (output->buf[0])
|
|
output->state = VFE_OUTPUT_SINGLE;
|
|
|
|
if (output->buf[1])
|
|
output->state = VFE_OUTPUT_CONTINUOUS;
|
|
|
|
switch (output->state) {
|
|
case VFE_OUTPUT_SINGLE:
|
|
vfe_output_frame_drop(vfe, output, 2);
|
|
break;
|
|
case VFE_OUTPUT_CONTINUOUS:
|
|
vfe_output_frame_drop(vfe, output, 3);
|
|
break;
|
|
default:
|
|
vfe_output_frame_drop(vfe, output, 0);
|
|
break;
|
|
}
|
|
|
|
vfe_output_init_addrs(vfe, output, 1, &vfe->line[line_id]);
|
|
}
|
|
|
|
spin_unlock_irqrestore(&vfe->output_lock, flags);
|
|
}
|
|
|
|
/*
|
|
* vfe_isr_wm_done - Process write master done interrupt
|
|
* @vfe: VFE Device
|
|
* @wm: Write master id
|
|
*/
|
|
static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm)
|
|
{
|
|
struct camss_buffer *ready_buf;
|
|
struct vfe_output *output;
|
|
dma_addr_t *new_addr;
|
|
unsigned long flags;
|
|
u32 active_index;
|
|
u64 ts = ktime_get_ns();
|
|
unsigned int i;
|
|
|
|
active_index = vfe->ops_gen1->wm_get_ping_pong_status(vfe, wm);
|
|
|
|
spin_lock_irqsave(&vfe->output_lock, flags);
|
|
|
|
if (vfe->wm_output_map[wm] == VFE_LINE_NONE) {
|
|
dev_err_ratelimited(vfe->camss->dev,
|
|
"Received wm done for unmapped index\n");
|
|
goto out_unlock;
|
|
}
|
|
output = &vfe->line[vfe->wm_output_map[wm]].output;
|
|
|
|
if (output->gen1.active_buf == active_index && 0) {
|
|
dev_err_ratelimited(vfe->camss->dev,
|
|
"Active buffer mismatch!\n");
|
|
goto out_unlock;
|
|
}
|
|
output->gen1.active_buf = active_index;
|
|
|
|
ready_buf = output->buf[!active_index];
|
|
if (!ready_buf) {
|
|
dev_err_ratelimited(vfe->camss->dev,
|
|
"Missing ready buf %d %d!\n",
|
|
!active_index, output->state);
|
|
goto out_unlock;
|
|
}
|
|
|
|
ready_buf->vb.vb2_buf.timestamp = ts;
|
|
ready_buf->vb.sequence = output->sequence++;
|
|
|
|
/* Get next buffer */
|
|
output->buf[!active_index] = vfe_buf_get_pending(output);
|
|
if (!output->buf[!active_index]) {
|
|
/* No next buffer - set same address */
|
|
new_addr = ready_buf->addr;
|
|
vfe_buf_update_wm_on_last(vfe, output);
|
|
} else {
|
|
new_addr = output->buf[!active_index]->addr;
|
|
vfe_buf_update_wm_on_next(vfe, output);
|
|
}
|
|
|
|
if (active_index)
|
|
for (i = 0; i < output->wm_num; i++)
|
|
vfe->ops_gen1->wm_set_ping_addr(vfe, output->wm_idx[i], new_addr[i]);
|
|
else
|
|
for (i = 0; i < output->wm_num; i++)
|
|
vfe->ops_gen1->wm_set_pong_addr(vfe, output->wm_idx[i], new_addr[i]);
|
|
|
|
spin_unlock_irqrestore(&vfe->output_lock, flags);
|
|
|
|
if (output->state == VFE_OUTPUT_STOPPING)
|
|
output->last_buffer = ready_buf;
|
|
else
|
|
vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
|
|
|
|
return;
|
|
|
|
out_unlock:
|
|
spin_unlock_irqrestore(&vfe->output_lock, flags);
|
|
}
|
|
|
|
/*
|
|
* vfe_queue_buffer - Add empty buffer
|
|
* @vid: Video device structure
|
|
* @buf: Buffer to be enqueued
|
|
*
|
|
* Add an empty buffer - depending on the current number of buffers it will be
|
|
* put in pending buffer queue or directly given to the hardware to be filled.
|
|
*
|
|
* Return 0 on success or a negative error code otherwise
|
|
*/
|
|
static int vfe_queue_buffer(struct camss_video *vid, struct camss_buffer *buf)
|
|
{
|
|
struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
|
|
struct vfe_device *vfe = to_vfe(line);
|
|
struct vfe_output *output;
|
|
unsigned long flags;
|
|
|
|
output = &line->output;
|
|
|
|
spin_lock_irqsave(&vfe->output_lock, flags);
|
|
|
|
vfe_buf_update_wm_on_new(vfe, output, buf, line);
|
|
|
|
spin_unlock_irqrestore(&vfe->output_lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define CALC_WORD(width, M, N) (((width) * (M) + (N) - 1) / (N))
|
|
|
|
int vfe_word_per_line(u32 format, u32 width)
|
|
{
|
|
int val = 0;
|
|
|
|
switch (format) {
|
|
case V4L2_PIX_FMT_NV12:
|
|
case V4L2_PIX_FMT_NV21:
|
|
case V4L2_PIX_FMT_NV16:
|
|
case V4L2_PIX_FMT_NV61:
|
|
val = CALC_WORD(width, 1, 8);
|
|
break;
|
|
case V4L2_PIX_FMT_YUYV:
|
|
case V4L2_PIX_FMT_YVYU:
|
|
case V4L2_PIX_FMT_UYVY:
|
|
case V4L2_PIX_FMT_VYUY:
|
|
val = CALC_WORD(width, 2, 8);
|
|
break;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
const struct vfe_isr_ops vfe_isr_ops_gen1 = {
|
|
.reset_ack = vfe_isr_reset_ack,
|
|
.halt_ack = vfe_isr_halt_ack,
|
|
.reg_update = vfe_isr_reg_update,
|
|
.sof = vfe_isr_sof,
|
|
.comp_done = vfe_isr_comp_done,
|
|
.wm_done = vfe_isr_wm_done,
|
|
};
|
|
|
|
const struct camss_video_ops vfe_video_ops_gen1 = {
|
|
.queue_buffer = vfe_queue_buffer,
|
|
.flush_buffers = vfe_flush_buffers,
|
|
};
|