625 lines
16 KiB
C
625 lines
16 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd. */
|
|
|
|
#include <linux/delay.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_graph.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/slab.h>
|
|
#include <media/v4l2-common.h>
|
|
#include <media/v4l2-event.h>
|
|
#include <media/v4l2-fh.h>
|
|
#include <media/v4l2-ioctl.h>
|
|
#include <media/v4l2-subdev.h>
|
|
#include <media/videobuf2-dma-contig.h>
|
|
#include <linux/rk-camera-module.h>
|
|
#include "dev.h"
|
|
#include "regs.h"
|
|
|
|
static inline
|
|
struct rkisp_bridge_buf *to_bridge_buf(struct rkisp_ispp_buf *dbufs)
|
|
{
|
|
return container_of(dbufs, struct rkisp_bridge_buf, dbufs);
|
|
}
|
|
|
|
static void free_bridge_buf(struct rkisp_bridge_device *dev)
|
|
{
|
|
struct rkisp_hw_dev *hw = dev->ispdev->hw_dev;
|
|
struct rkisp_bridge_buf *buf;
|
|
struct rkisp_ispp_buf *dbufs;
|
|
unsigned long lock_flags = 0;
|
|
int i, j;
|
|
|
|
spin_lock_irqsave(&hw->buf_lock, lock_flags);
|
|
if (--hw->buf_init_cnt > 0) {
|
|
spin_unlock_irqrestore(&hw->buf_lock, lock_flags);
|
|
return;
|
|
}
|
|
|
|
v4l2_dbg(1, rkisp_debug, &dev->ispdev->v4l2_dev,
|
|
"%s\n", __func__);
|
|
|
|
if (hw->cur_buf) {
|
|
list_add_tail(&hw->cur_buf->list, &hw->list);
|
|
if (hw->cur_buf == hw->nxt_buf)
|
|
hw->nxt_buf = NULL;
|
|
hw->cur_buf = NULL;
|
|
}
|
|
|
|
if (hw->nxt_buf) {
|
|
list_add_tail(&hw->nxt_buf->list, &hw->list);
|
|
hw->nxt_buf = NULL;
|
|
}
|
|
|
|
if (dev->ispdev->cur_fbcgain) {
|
|
list_add_tail(&dev->ispdev->cur_fbcgain->list, &hw->list);
|
|
dev->ispdev->cur_fbcgain = NULL;
|
|
}
|
|
|
|
while (!list_empty(&hw->rpt_list)) {
|
|
dbufs = list_first_entry(&hw->rpt_list,
|
|
struct rkisp_ispp_buf, list);
|
|
list_del(&dbufs->list);
|
|
list_add_tail(&dbufs->list, &hw->list);
|
|
}
|
|
|
|
while (!list_empty(&hw->list)) {
|
|
dbufs = list_first_entry(&hw->list,
|
|
struct rkisp_ispp_buf, list);
|
|
list_del(&dbufs->list);
|
|
}
|
|
|
|
hw->is_buf_init = false;
|
|
spin_unlock_irqrestore(&hw->buf_lock, lock_flags);
|
|
for (i = 0; i < BRIDGE_BUF_MAX; i++) {
|
|
buf = &hw->bufs[i];
|
|
for (j = 0; j < GROUP_BUF_MAX; j++)
|
|
rkisp_free_buffer(dev->ispdev, &buf->dummy[j]);
|
|
}
|
|
|
|
rkisp_free_common_dummy_buf(dev->ispdev);
|
|
}
|
|
|
|
static int init_buf(struct rkisp_bridge_device *dev, u32 pic_size, u32 gain_size)
|
|
{
|
|
struct v4l2_subdev *sd = v4l2_get_subdev_hostdata(&dev->sd);
|
|
struct rkisp_hw_dev *hw = dev->ispdev->hw_dev;
|
|
struct rkisp_bridge_buf *buf;
|
|
struct rkisp_dummy_buffer *dummy;
|
|
int i, j, val, ret = 0;
|
|
unsigned long lock_flags = 0;
|
|
bool is_direct = (hw->isp_ver == ISP_V20) ? true : false;
|
|
|
|
spin_lock_irqsave(&hw->buf_lock, lock_flags);
|
|
if (++hw->buf_init_cnt > 1) {
|
|
spin_unlock_irqrestore(&hw->buf_lock, lock_flags);
|
|
return 0;
|
|
}
|
|
spin_unlock_irqrestore(&hw->buf_lock, lock_flags);
|
|
|
|
v4l2_dbg(1, rkisp_debug, &dev->ispdev->v4l2_dev,
|
|
"%s pic size:%d gain size:%d\n",
|
|
__func__, pic_size, gain_size);
|
|
|
|
INIT_LIST_HEAD(&hw->list);
|
|
for (i = 0; i < dev->buf_num; i++) {
|
|
buf = &hw->bufs[i];
|
|
for (j = 0; j < GROUP_BUF_MAX; j++) {
|
|
if (j && hw->isp_ver == ISP_V30)
|
|
continue;
|
|
dummy = &buf->dummy[j];
|
|
dummy->is_need_vaddr = true;
|
|
dummy->is_need_dbuf = true;
|
|
dummy->size = PAGE_ALIGN(!j ? pic_size : gain_size);
|
|
ret = rkisp_alloc_buffer(dev->ispdev, dummy);
|
|
if (ret)
|
|
goto err;
|
|
buf->dbufs.dbuf[j] = dummy->dbuf;
|
|
buf->dbufs.didx[j] = i * GROUP_BUF_MAX + j;
|
|
buf->dbufs.gain_size = PAGE_ALIGN(gain_size);
|
|
buf->dbufs.mfbc_size = PAGE_ALIGN(pic_size);
|
|
}
|
|
list_add_tail(&buf->dbufs.list, &hw->list);
|
|
ret = v4l2_subdev_call(sd, video, s_rx_buffer, &buf->dbufs, NULL);
|
|
if (ret)
|
|
goto err;
|
|
}
|
|
|
|
for (i = 0; i < hw->dev_num; i++) {
|
|
struct rkisp_device *isp = hw->isp[i];
|
|
|
|
if (!isp ||
|
|
(isp && !(isp->isp_inp & INP_CSI)))
|
|
continue;
|
|
ret = rkisp_alloc_common_dummy_buf(isp);
|
|
if (ret < 0)
|
|
goto err;
|
|
else
|
|
break;
|
|
}
|
|
|
|
hw->cur_buf = list_first_entry(&hw->list, struct rkisp_ispp_buf, list);
|
|
list_del(&hw->cur_buf->list);
|
|
buf = to_bridge_buf(hw->cur_buf);
|
|
val = buf->dummy[GROUP_BUF_PIC].dma_addr;
|
|
rkisp_write(dev->ispdev, dev->cfg->reg.y0_base, val, is_direct);
|
|
val += dev->cfg->offset;
|
|
rkisp_write(dev->ispdev, dev->cfg->reg.uv0_base, val, is_direct);
|
|
if (hw->isp_ver == ISP_V20) {
|
|
val = buf->dummy[GROUP_BUF_GAIN].dma_addr;
|
|
rkisp_write(dev->ispdev, dev->cfg->reg.g0_base, val, is_direct);
|
|
}
|
|
|
|
if (!list_empty(&hw->list)) {
|
|
hw->nxt_buf = list_first_entry(&hw->list,
|
|
struct rkisp_ispp_buf, list);
|
|
list_del(&hw->nxt_buf->list);
|
|
}
|
|
if (hw->nxt_buf && (dev->work_mode & ISP_ISPP_QUICK)) {
|
|
buf = to_bridge_buf(hw->nxt_buf);
|
|
val = buf->dummy[GROUP_BUF_PIC].dma_addr;
|
|
rkisp_write(dev->ispdev, dev->cfg->reg.y1_base, val, true);
|
|
val += dev->cfg->offset;
|
|
rkisp_write(dev->ispdev, dev->cfg->reg.uv1_base, val, true);
|
|
val = buf->dummy[GROUP_BUF_GAIN].dma_addr;
|
|
rkisp_write(dev->ispdev, dev->cfg->reg.g1_base, val, true);
|
|
rkisp_set_bits(dev->ispdev, MI_WR_CTRL2,
|
|
0, SW_GAIN_WR_PINGPONG, true);
|
|
}
|
|
|
|
rkisp_set_bits(dev->ispdev, CIF_VI_DPCL, 0,
|
|
CIF_VI_DPCL_CHAN_MODE_MP |
|
|
CIF_VI_DPCL_MP_MUX_MRSZ_MI, true);
|
|
rkisp_set_bits(dev->ispdev, MI_WR_CTRL, 0,
|
|
CIF_MI_CTRL_INIT_BASE_EN |
|
|
CIF_MI_CTRL_INIT_OFFSET_EN, true);
|
|
rkisp_set_bits(dev->ispdev, MI_IMSC, 0,
|
|
dev->cfg->frame_end_id, true);
|
|
|
|
spin_lock_irqsave(&hw->buf_lock, lock_flags);
|
|
hw->is_buf_init = true;
|
|
spin_unlock_irqrestore(&hw->buf_lock, lock_flags);
|
|
return 0;
|
|
err:
|
|
free_bridge_buf(dev);
|
|
v4l2_err(&dev->sd, "%s fail:%d\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
static int config_mode(struct rkisp_bridge_device *dev)
|
|
{
|
|
struct rkisp_hw_dev *hw = dev->ispdev->hw_dev;
|
|
u32 w = hw->max_in.w ? hw->max_in.w : dev->crop.width;
|
|
u32 h = hw->max_in.h ? hw->max_in.h : dev->crop.height;
|
|
u32 offs = w * h;
|
|
u32 pic_size = 0, gain_size = 0;
|
|
|
|
if (dev->work_mode == ISP_ISPP_INIT_FAIL) {
|
|
free_bridge_buf(dev);
|
|
return 0;
|
|
}
|
|
|
|
if (!dev->linked || !dev->ispdev->isp_inp) {
|
|
v4l2_err(&dev->sd,
|
|
"invalid: link:%d or isp input:0x%x\n",
|
|
dev->linked,
|
|
dev->ispdev->isp_inp);
|
|
return -EINVAL;
|
|
}
|
|
|
|
v4l2_dbg(1, rkisp_debug, &dev->sd,
|
|
"work mode:0x%x buf num:%d\n",
|
|
dev->work_mode, dev->buf_num);
|
|
|
|
if (hw->isp_ver == ISP_V20) {
|
|
gain_size = ALIGN(w, 64) * ALIGN(h, 128) >> 4;
|
|
rkisp_bridge_init_ops_v20(dev);
|
|
} else {
|
|
dev->work_mode &= ~(ISP_ISPP_FBC | ISP_ISPP_QUICK);
|
|
rkisp_bridge_init_ops_v30(dev);
|
|
}
|
|
|
|
if (dev->work_mode & ISP_ISPP_FBC) {
|
|
w = ALIGN(w, 16);
|
|
h = ALIGN(h, 16);
|
|
offs = w * h >> 4;
|
|
pic_size = offs;
|
|
}
|
|
if (dev->work_mode & ISP_ISPP_422)
|
|
pic_size += w * h * 2;
|
|
else
|
|
pic_size += w * h * 3 >> 1;
|
|
dev->cfg->offset = offs;
|
|
|
|
if (hw->isp_ver == ISP_V20) {
|
|
pic_size += RKISP_MOTION_DECT_TS_SIZE;
|
|
gain_size += RKISP_MOTION_DECT_TS_SIZE;
|
|
}
|
|
return init_buf(dev, pic_size, gain_size);
|
|
}
|
|
|
|
static int bridge_start_stream(struct v4l2_subdev *sd)
|
|
{
|
|
struct rkisp_bridge_device *dev = v4l2_get_subdevdata(sd);
|
|
int ret = -EINVAL;
|
|
|
|
if (WARN_ON(dev->en))
|
|
return -EBUSY;
|
|
|
|
if (dev->ispdev->isp_sdev.out_fmt.fmt_type == FMT_BAYER) {
|
|
v4l2_err(sd, "no support raw from isp to ispp\n");
|
|
goto free_buf;
|
|
}
|
|
|
|
if (dev->ispdev->isp_inp & INP_CSI ||
|
|
dev->ispdev->isp_inp & INP_DVP ||
|
|
dev->ispdev->isp_inp & INP_LVDS ||
|
|
dev->ispdev->isp_inp & INP_CIF) {
|
|
/* Always update sensor info in case media topology changed */
|
|
ret = rkisp_update_sensor_info(dev->ispdev);
|
|
if (ret < 0) {
|
|
v4l2_err(sd, "update sensor info failed %d\n", ret);
|
|
goto free_buf;
|
|
}
|
|
}
|
|
|
|
/* enable clocks/power-domains */
|
|
ret = dev->ispdev->pipe.open(&dev->ispdev->pipe, &sd->entity, true);
|
|
if (ret < 0)
|
|
goto free_buf;
|
|
|
|
ret = dev->ops->start(dev);
|
|
if (ret)
|
|
goto close_pipe;
|
|
|
|
/* start sub-devices */
|
|
ret = dev->ispdev->pipe.set_stream(&dev->ispdev->pipe, true);
|
|
if (ret < 0)
|
|
goto stop_bridge;
|
|
|
|
return 0;
|
|
stop_bridge:
|
|
dev->ops->stop(dev);
|
|
close_pipe:
|
|
dev->ispdev->pipe.close(&dev->ispdev->pipe);
|
|
hdr_destroy_buf(dev->ispdev);
|
|
free_buf:
|
|
free_bridge_buf(dev);
|
|
v4l2_err(&dev->sd, "%s fail:%d\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
static void bridge_destroy_buf(struct rkisp_bridge_device *dev)
|
|
{
|
|
free_bridge_buf(dev);
|
|
hdr_destroy_buf(dev->ispdev);
|
|
}
|
|
|
|
static int bridge_stop_stream(struct v4l2_subdev *sd)
|
|
{
|
|
struct rkisp_bridge_device *dev = v4l2_get_subdevdata(sd);
|
|
|
|
dev->ops->stop(dev);
|
|
dev->ispdev->pipe.set_stream(&dev->ispdev->pipe, false);
|
|
dev->ispdev->pipe.close(&dev->ispdev->pipe);
|
|
bridge_destroy_buf(dev);
|
|
return 0;
|
|
}
|
|
|
|
static int bridge_get_set_fmt(struct v4l2_subdev *sd,
|
|
struct v4l2_subdev_state *sd_state,
|
|
struct v4l2_subdev_format *fmt)
|
|
{
|
|
struct rkisp_bridge_device *dev = v4l2_get_subdevdata(sd);
|
|
|
|
if (!fmt)
|
|
return -EINVAL;
|
|
|
|
/* get isp out format */
|
|
fmt->pad = RKISP_ISP_PAD_SOURCE_PATH;
|
|
fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
|
|
return v4l2_subdev_call(&dev->ispdev->isp_sdev.sd,
|
|
pad, get_fmt, NULL, fmt);
|
|
}
|
|
|
|
static int bridge_set_selection(struct v4l2_subdev *sd,
|
|
struct v4l2_subdev_state *sd_state,
|
|
struct v4l2_subdev_selection *sel)
|
|
{
|
|
struct rkisp_bridge_device *dev = v4l2_get_subdevdata(sd);
|
|
struct rkisp_isp_subdev *isp_sd = &dev->ispdev->isp_sdev;
|
|
u32 src_w = isp_sd->out_crop.width;
|
|
u32 src_h = isp_sd->out_crop.height;
|
|
struct v4l2_rect *crop;
|
|
|
|
if (!sel)
|
|
return -EINVAL;
|
|
if (sel->target != V4L2_SEL_TGT_CROP)
|
|
return -EINVAL;
|
|
|
|
crop = &sel->r;
|
|
crop->left = clamp_t(u32, crop->left, 0, src_w);
|
|
crop->top = clamp_t(u32, crop->top, 0, src_h);
|
|
crop->width = clamp_t(u32, crop->width,
|
|
CIF_ISP_OUTPUT_W_MIN, src_w - crop->left);
|
|
crop->height = clamp_t(u32, crop->height,
|
|
CIF_ISP_OUTPUT_H_MIN, src_h - crop->top);
|
|
|
|
dev->crop = *crop;
|
|
return 0;
|
|
}
|
|
|
|
static int bridge_get_selection(struct v4l2_subdev *sd,
|
|
struct v4l2_subdev_state *sd_state,
|
|
struct v4l2_subdev_selection *sel)
|
|
{
|
|
struct rkisp_bridge_device *dev = v4l2_get_subdevdata(sd);
|
|
struct rkisp_isp_subdev *isp_sd = &dev->ispdev->isp_sdev;
|
|
struct v4l2_rect *crop;
|
|
|
|
if (!sel)
|
|
return -EINVAL;
|
|
|
|
crop = &sel->r;
|
|
switch (sel->target) {
|
|
case V4L2_SEL_TGT_CROP_BOUNDS:
|
|
*crop = isp_sd->out_crop;
|
|
break;
|
|
case V4L2_SEL_TGT_CROP:
|
|
*crop = dev->crop;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bridge_s_rx_buffer(struct v4l2_subdev *sd,
|
|
void *buf, unsigned int *size)
|
|
{
|
|
struct rkisp_bridge_device *dev = v4l2_get_subdevdata(sd);
|
|
struct rkisp_hw_dev *hw = dev->ispdev->hw_dev;
|
|
struct rkisp_ispp_buf *dbufs = buf;
|
|
unsigned long lock_flags = 0;
|
|
|
|
spin_lock_irqsave(&hw->buf_lock, lock_flags);
|
|
/* size isn't using now */
|
|
if (!dbufs || !hw->buf_init_cnt) {
|
|
spin_unlock_irqrestore(&hw->buf_lock, lock_flags);
|
|
return -EINVAL;
|
|
}
|
|
list_add_tail(&dbufs->list, &hw->list);
|
|
spin_unlock_irqrestore(&hw->buf_lock, lock_flags);
|
|
return 0;
|
|
}
|
|
|
|
static int bridge_s_stream(struct v4l2_subdev *sd, int on)
|
|
{
|
|
struct rkisp_bridge_device *dev = v4l2_get_subdevdata(sd);
|
|
struct rkisp_hw_dev *hw = dev->ispdev->hw_dev;
|
|
int ret = 0;
|
|
|
|
v4l2_dbg(1, rkisp_debug, sd,
|
|
"%s %d\n", __func__, on);
|
|
|
|
mutex_lock(&hw->dev_lock);
|
|
if (on) {
|
|
memset(&dev->dbg, 0, sizeof(dev->dbg));
|
|
atomic_inc(&dev->ispdev->cap_dev.refcnt);
|
|
ret = bridge_start_stream(sd);
|
|
} else {
|
|
if (dev->en)
|
|
ret = bridge_stop_stream(sd);
|
|
atomic_dec(&dev->ispdev->cap_dev.refcnt);
|
|
}
|
|
mutex_unlock(&hw->dev_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int bridge_s_power(struct v4l2_subdev *sd, int on)
|
|
{
|
|
int ret = 0;
|
|
|
|
v4l2_dbg(1, rkisp_debug, sd,
|
|
"%s %d\n", __func__, on);
|
|
|
|
if (on)
|
|
ret = v4l2_pipeline_pm_get(&sd->entity);
|
|
else
|
|
v4l2_pipeline_pm_put(&sd->entity);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static long bridge_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
|
|
{
|
|
struct rkisp_bridge_device *dev = v4l2_get_subdevdata(sd);
|
|
struct rkisp_ispp_mode *mode;
|
|
struct max_input *max_in;
|
|
long ret = 0;
|
|
|
|
switch (cmd) {
|
|
case RKISP_ISPP_CMD_SET_FMT:
|
|
max_in = arg;
|
|
dev->ispdev->hw_dev->max_in = *max_in;
|
|
break;
|
|
case RKISP_ISPP_CMD_SET_MODE:
|
|
mode = arg;
|
|
dev->work_mode = mode->work_mode;
|
|
dev->buf_num = mode->buf_num;
|
|
ret = config_mode(dev);
|
|
rkisp_chk_tb_over(dev->ispdev);
|
|
break;
|
|
default:
|
|
ret = -ENOIOCTLCMD;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct v4l2_subdev_pad_ops bridge_pad_ops = {
|
|
.set_fmt = bridge_get_set_fmt,
|
|
.get_fmt = bridge_get_set_fmt,
|
|
.get_selection = bridge_get_selection,
|
|
.set_selection = bridge_set_selection,
|
|
};
|
|
|
|
static const struct v4l2_subdev_video_ops bridge_video_ops = {
|
|
.s_rx_buffer = bridge_s_rx_buffer,
|
|
.s_stream = bridge_s_stream,
|
|
};
|
|
|
|
static const struct v4l2_subdev_core_ops bridge_core_ops = {
|
|
.s_power = bridge_s_power,
|
|
.ioctl = bridge_ioctl,
|
|
};
|
|
|
|
static struct v4l2_subdev_ops bridge_v4l2_ops = {
|
|
.core = &bridge_core_ops,
|
|
.video = &bridge_video_ops,
|
|
.pad = &bridge_pad_ops,
|
|
};
|
|
|
|
void rkisp_bridge_update_mi(struct rkisp_device *dev, u32 isp_mis)
|
|
{
|
|
struct rkisp_bridge_device *br = &dev->br_dev;
|
|
struct rkisp_hw_dev *hw = dev->hw_dev;
|
|
unsigned long lock_flags = 0;
|
|
|
|
if ((dev->isp_ver != ISP_V20 && dev->isp_ver != ISP_V30) ||
|
|
!br->en || br->work_mode & ISP_ISPP_QUICK ||
|
|
isp_mis & CIF_ISP_FRAME)
|
|
return;
|
|
|
|
br->fs_ns = ktime_get_ns();
|
|
spin_lock_irqsave(&hw->buf_lock, lock_flags);
|
|
if (!hw->nxt_buf && !list_empty(&hw->list)) {
|
|
hw->nxt_buf = list_first_entry(&hw->list,
|
|
struct rkisp_ispp_buf, list);
|
|
list_del(&hw->nxt_buf->list);
|
|
}
|
|
spin_unlock_irqrestore(&hw->buf_lock, lock_flags);
|
|
|
|
br->ops->update_mi(br);
|
|
}
|
|
|
|
void rkisp_bridge_isr(u32 *mis_val, struct rkisp_device *dev)
|
|
{
|
|
struct rkisp_bridge_device *bridge = &dev->br_dev;
|
|
void __iomem *base = dev->base_addr;
|
|
u32 irq;
|
|
|
|
if (!bridge->en)
|
|
return;
|
|
|
|
if (!bridge->cfg ||
|
|
(bridge->cfg &&
|
|
!(*mis_val & bridge->cfg->frame_end_id)))
|
|
return;
|
|
|
|
irq = bridge->cfg->frame_end_id;
|
|
*mis_val &= ~irq;
|
|
writel(irq, base + CIF_MI_ICR);
|
|
|
|
irq = (irq == MI_MPFBC_FRAME) ? ISP_FRAME_MPFBC : ISP_FRAME_MP;
|
|
bridge->ops->frame_end(bridge, FRAME_IRQ);
|
|
|
|
rkisp_check_idle(dev, irq);
|
|
}
|
|
|
|
static int check_remote_node(struct rkisp_device *ispdev)
|
|
{
|
|
struct device *dev = ispdev->dev;
|
|
struct device_node *parent = dev->of_node;
|
|
struct device_node *remote = NULL;
|
|
int i, j;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
for (j = 0; j < 2; j++) {
|
|
remote = of_graph_get_remote_node(parent, i, j);
|
|
if (!remote)
|
|
continue;
|
|
of_node_put(remote);
|
|
if (strstr(of_node_full_name(remote), "ispp"))
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -ENODEV;
|
|
}
|
|
|
|
int rkisp_register_bridge_subdev(struct rkisp_device *dev,
|
|
struct v4l2_device *v4l2_dev)
|
|
{
|
|
struct rkisp_bridge_device *bridge = &dev->br_dev;
|
|
struct v4l2_subdev *sd;
|
|
struct media_entity *source, *sink;
|
|
int ret;
|
|
|
|
memset(bridge, 0, sizeof(*bridge));
|
|
if ((dev->isp_ver != ISP_V20 && dev->isp_ver != ISP_V30) ||
|
|
check_remote_node(dev) < 0)
|
|
return 0;
|
|
|
|
bridge->ispdev = dev;
|
|
sd = &bridge->sd;
|
|
v4l2_subdev_init(sd, &bridge_v4l2_ops);
|
|
//sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
|
sd->entity.obj_type = 0;
|
|
snprintf(sd->name, sizeof(sd->name), "%s", BRIDGE_DEV_NAME);
|
|
bridge->pad.flags = MEDIA_PAD_FL_SINK;
|
|
ret = media_entity_pads_init(&sd->entity, 1, &bridge->pad);
|
|
if (ret < 0)
|
|
return ret;
|
|
sd->owner = THIS_MODULE;
|
|
v4l2_set_subdevdata(sd, bridge);
|
|
sd->grp_id = GRP_ID_ISP_BRIDGE;
|
|
ret = v4l2_device_register_subdev(v4l2_dev, sd);
|
|
if (ret < 0) {
|
|
v4l2_err(sd, "Failed to register subdev\n");
|
|
goto free_media;
|
|
}
|
|
bridge->crop = dev->isp_sdev.out_crop;
|
|
/* bridge links */
|
|
bridge->linked = true;
|
|
source = &dev->isp_sdev.sd.entity;
|
|
sink = &sd->entity;
|
|
ret = media_create_pad_link(source, RKISP_ISP_PAD_SOURCE_PATH,
|
|
sink, 0, bridge->linked);
|
|
init_waitqueue_head(&bridge->done);
|
|
bridge->wq = alloc_workqueue("rkisp bridge workqueue",
|
|
WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
|
|
hrtimer_init(&bridge->frame_qst, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
|
return ret;
|
|
|
|
free_media:
|
|
media_entity_cleanup(&sd->entity);
|
|
return ret;
|
|
}
|
|
|
|
void rkisp_unregister_bridge_subdev(struct rkisp_device *dev)
|
|
{
|
|
struct v4l2_subdev *sd = &dev->br_dev.sd;
|
|
|
|
if ((dev->isp_ver != ISP_V20 && dev->isp_ver != ISP_V30) ||
|
|
check_remote_node(dev) < 0)
|
|
return;
|
|
v4l2_device_unregister_subdev(sd);
|
|
media_entity_cleanup(&sd->entity);
|
|
}
|
|
|
|
void rkisp_get_bridge_sd(struct platform_device *dev,
|
|
struct v4l2_subdev **sd)
|
|
{
|
|
struct rkisp_device *isp_dev = platform_get_drvdata(dev);
|
|
|
|
if (isp_dev)
|
|
*sd = &isp_dev->br_dev.sd;
|
|
else
|
|
*sd = NULL;
|
|
}
|
|
EXPORT_SYMBOL(rkisp_get_bridge_sd);
|