489 lines
14 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/iommu.h>
#include <linux/pm_runtime.h>
#include <linux/videodev2.h>
#include <media/media-entity.h>
#include <media/v4l2-event.h>
#include "dev.h"
#include "regs.h"
static const struct vpsssd_fmt rkvpss_formats[] = {
{
.mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
.fourcc = V4L2_PIX_FMT_NV16,
},
};
static int rkvpss_subdev_link_setup(struct media_entity *entity,
const struct media_pad *local,
const struct media_pad *remote,
u32 flags)
{
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
struct rkvpss_subdev *vpss_sdev;
struct rkvpss_device *dev;
struct rkvpss_stream_vdev *vdev;
struct rkvpss_stream *stream = NULL;
if (local->index != RKVPSS_PAD_SINK &&
local->index != RKVPSS_PAD_SOURCE)
return 0;
if (!sd)
return -ENODEV;
vpss_sdev = v4l2_get_subdevdata(sd);
if (!vpss_sdev)
return -ENODEV;
dev = vpss_sdev->dev;
if (!dev)
return -ENODEV;
vdev = &dev->stream_vdev;
if (vpss_sdev->state & VPSS_START)
return -EBUSY;
if (!strcmp(remote->entity->name, S0_VDEV_NAME)) {
stream = &vdev->stream[RKVPSS_OUTPUT_CH0];
} else if (!strcmp(remote->entity->name, S1_VDEV_NAME)) {
stream = &vdev->stream[RKVPSS_OUTPUT_CH1];
} else if (!strcmp(remote->entity->name, S2_VDEV_NAME)) {
stream = &vdev->stream[RKVPSS_OUTPUT_CH2];
} else if (!strcmp(remote->entity->name, S3_VDEV_NAME)) {
stream = &vdev->stream[RKVPSS_OUTPUT_CH3];
} else if (strstr(remote->entity->name, "rkisp")) {
if (flags & MEDIA_LNK_FL_ENABLED)
dev->inp = INP_ISP;
else
dev->inp = INP_INVAL;
}
if (stream)
stream->linked = flags & MEDIA_LNK_FL_ENABLED;
v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, "input:%d\n", dev->inp);
return 0;
}
static int rkvpss_sd_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
struct rkvpss_subdev *sdev = v4l2_get_subdevdata(sd);
struct rkvpss_device *dev = sdev->dev;
struct v4l2_mbus_framefmt *mf;
int ret = 0;
if (!fmt)
return -EINVAL;
if (fmt->pad != RKVPSS_PAD_SINK &&
fmt->pad != RKVPSS_PAD_SOURCE)
return -EINVAL;
mf = &fmt->format;
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
if (!sd_state)
return -EINVAL;
mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
}
*mf = sdev->in_fmt;
if (fmt->pad == RKVPSS_PAD_SINK && sdev->dev->inp == INP_ISP) {
ret = v4l2_subdev_call(dev->remote_sd, pad, get_fmt, sd_state, fmt);
if (!ret) {
if (sdev->in_fmt.width != mf->width ||
sdev->in_fmt.height != mf->height) {
sdev->out_fmt.width = mf->width;
sdev->out_fmt.height = mf->height;
rkvpss_pipeline_default_fmt(dev);
}
sdev->in_fmt = *mf;
}
} else {
*mf = sdev->in_fmt;
mf->width = sdev->out_fmt.width;
mf->height = sdev->out_fmt.height;
}
return ret;
}
static int rkvpss_sd_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
struct rkvpss_subdev *sdev = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *mf;
if (!fmt)
return -EINVAL;
/* format from isp output */
if (fmt->pad == RKVPSS_PAD_SINK && sdev->dev->inp == INP_ISP)
return rkvpss_sd_get_fmt(sd, sd_state, fmt);
mf = &fmt->format;
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
if (!sd_state)
return -EINVAL;
mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
}
if (fmt->pad == RKVPSS_PAD_SINK) {
sdev->in_fmt = *mf;
} else {
sdev->out_fmt.width = mf->width;
sdev->out_fmt.height = mf->height;
}
return 0;
}
static int rkvpss_sd_s_stream(struct v4l2_subdev *sd, int on)
{
struct rkvpss_subdev *sdev = v4l2_get_subdevdata(sd);
struct rkvpss_device *dev = sdev->dev;
u32 val, w = sdev->in_fmt.width, h = sdev->in_fmt.height;
v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev,
"s_stream on:%d %dx%d\n", on, w, h);
if (!on) {
rkvpss_unite_clear_bits(dev, RKVPSS_VPSS_ONLINE, RKVPSS_ONLINE_MODE_MASK);
rkvpss_unite_clear_bits(dev, RKVPSS_VPSS_IMSC, RKVPSS_ISP_ALL_FRM_END);
sdev->state = VPSS_STOP;
atomic_dec(&dev->hw_dev->refcnt);
return 0;
}
sdev->frame_seq = -1;
sdev->frame_timestamp = 0;
dev->isr_cnt = 0;
dev->irq_ends = 0;
atomic_inc(&dev->hw_dev->refcnt);
dev->cmsc_upd = true;
rkvpss_cmsc_config(dev, true);
if (dev->unite_mode)
w = w / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL;
rkvpss_unite_write(dev, RKVPSS_VPSS_ONLINE2_SIZE, h << 16 | w);
val = RKVPSS_CFG_FORCE_UPD | RKVPSS_CFG_GEN_UPD | RKVPSS_MIR_GEN_UPD;
if (!dev->hw_dev->is_ofl_cmsc)
val |= RKVPSS_MIR_FORCE_UPD;
rkvpss_unite_write(dev, RKVPSS_VPSS_UPDATE, val);
rkvpss_unite_set_bits(dev, RKVPSS_VPSS_IMSC, 0, RKVPSS_ISP_ALL_FRM_END);
sdev->state |= VPSS_START;
return 0;
}
static int rkvpss_sd_s_rx_buffer(struct v4l2_subdev *sd,
void *buf, unsigned int *size)
{
return 0;
}
static int rkvpss_sd_s_power(struct v4l2_subdev *sd, int on)
{
struct rkvpss_subdev *sdev = v4l2_get_subdevdata(sd);
struct rkvpss_device *dev = sdev->dev;
int ret;
v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev,
"power on:%d\n", on);
if (on) {
if (dev->inp == INP_ISP) {
struct v4l2_subdev_format fmt = {
.pad = RKVPSS_PAD_SINK,
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
};
ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
if (ret) {
v4l2_err(&dev->v4l2_dev,
"%s get isp format fail:%d\n", __func__, ret);
return ret;
}
ret = v4l2_subdev_call(dev->remote_sd, core, s_power, 1);
if (ret < 0) {
v4l2_err(&dev->v4l2_dev,
"%s set isp power on fail:%d\n", __func__, ret);
return ret;
}
}
v4l2_subdev_call(dev->remote_sd, core, ioctl, RKISP_VPSS_GET_UNITE_MODE,
&dev->unite_mode);
ret = pm_runtime_get_sync(dev->dev);
if (ret < 0) {
v4l2_err(&dev->v4l2_dev,
"%s runtime get fail:%d\n", __func__, ret);
if (dev->inp == INP_ISP)
v4l2_subdev_call(dev->remote_sd, core, s_power, 0);
}
} else {
if (dev->inp == INP_ISP)
v4l2_subdev_call(dev->remote_sd, core, s_power, 0);
ret = pm_runtime_put_sync(dev->dev);
}
if (ret < 0)
v4l2_err(sd, "%s on:%d failed:%d\n", __func__, on, ret);
return ret;
}
static int rkvpss_sof(struct rkvpss_subdev *sdev, struct rkisp_vpss_sof *info)
{
struct rkvpss_device *dev = sdev->dev;
struct rkvpss_hw_dev *hw = dev->hw_dev;
struct rkvpss_stream *stream;
int i;
u32 vpss_online, val = 0;
if (!info)
return -EINVAL;
sdev->frame_seq = info->seq;
sdev->frame_timestamp = info->timestamp;
dev->unite_index = info->unite_index;
dev->is_idle = false;
v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev,
"%s unite_mode:%u, unite_indev:%u seq:%d\n", __func__,
dev->unite_mode, dev->unite_index, info->seq);
rkvpss_cmsc_config(dev, !info->irq);
for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) {
stream = &dev->stream_vdev.stream[i];
if (!stream->streaming)
continue;
if (stream->ops->frame_start)
stream->ops->frame_start(stream, info->irq);
}
if (!info->irq && (!hw->is_single || dev->unite_mode)) {
hw->cur_dev_id = dev->dev_id;
rkvpss_update_regs(dev, RKVPSS_VPSS_CTRL, RKVPSS_VPSS_ONLINE2_SIZE);
rkvpss_update_regs(dev, RKVPSS_VPSS_IRQ_CFG, RKVPSS_VPSS_IMSC);
rkvpss_update_regs(dev, RKVPSS_VPSS_Y2R_COE00, RKVPSS_VPSS_Y2R_OFF2);
rkvpss_update_regs(dev, RKVPSS_CMSC_INTSCT_CORR, RKVPSS_CMSC_WIN7_L3_SLP);
rkvpss_update_regs(dev, RKVPSS_CROP1_0_H_OFFS, RKVPSS_CROP1_3_V_SIZE);
rkvpss_update_regs(dev, RKVPSS_ZME_Y_HOR_COE0_10, RKVPSS_ZME_UV_VER_COE16_76);
rkvpss_update_regs(dev, RKVPSS_ZME_H_SIZE, RKVPSS_ZME_UV_YSCL_FACTOR);
rkvpss_update_regs(dev, RKVPSS_SCALE1_SRC_SIZE, RKVPSS_SCALE1_IN_CROP_OFFSET);
rkvpss_update_regs(dev, RKVPSS_SCALE2_SRC_SIZE, RKVPSS_SCALE2_IN_CROP_OFFSET);
rkvpss_update_regs(dev, RKVPSS_SCALE3_SRC_SIZE, RKVPSS_SCALE3_IN_CROP_OFFSET);
rkvpss_update_regs(dev, RKVPSS_MI_WR_WRAP_CTRL, RKVPSS_MI_IMSC);
rkvpss_update_regs(dev, RKVPSS_MI_CHN0_WR_CTRL, RKVPSS_MI_CHN3_WR_LINE_CNT);
rkvpss_update_regs(dev, RKVPSS_MI_WR_CTRL, RKVPSS_MI_WR_CTRL);
rkvpss_update_regs(dev, RKVPSS_SCALE3_CTRL, RKVPSS_SCALE3_UPDATE);
rkvpss_update_regs(dev, RKVPSS_SCALE2_CTRL, RKVPSS_SCALE2_UPDATE);
rkvpss_update_regs(dev, RKVPSS_SCALE1_CTRL, RKVPSS_SCALE1_UPDATE);
rkvpss_update_regs(dev, RKVPSS_ZME_CTRL, RKVPSS_ZME_UPDATE);
rkvpss_update_regs(dev, RKVPSS_CROP1_CTRL, RKVPSS_CROP1_UPDATE);
rkvpss_update_regs(dev, RKVPSS_CMSC_CTRL, RKVPSS_CMSC_UPDATE);
rkvpss_update_regs(dev, RKVPSS_VPSS_UPDATE, RKVPSS_VPSS_UPDATE);
/* force update mi write */
vpss_online = rkvpss_hw_read(hw, RKVPSS_VPSS_ONLINE);
for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) {
if (((vpss_online >> (2 * i)) & 0x3) == 0x2)
val |= BIT(i);
}
rkvpss_unite_write(dev, RKVPSS_MI_WR_INIT, val << 4);
rkvpss_update_regs(dev, RKVPSS_MI_WR_INIT, RKVPSS_MI_WR_INIT);
}
dev->irq_ends_mask = VPSS_FRAME_END;
for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) {
if (hw->is_ofl_ch[i])
continue;
if (rkvpss_hw_read(dev->hw_dev, RKVPSS_MI_CHN0_WR_CTRL_SHD + i * 0x100) & 0x1)
dev->irq_ends_mask |= BIT(i+3);
}
return 0;
}
static void rkvpss_reset_handle(struct rkvpss_device *vpss_dev)
{
dev_info(vpss_dev->dev, "%s enter\n", __func__);
rkvpss_hw_reg_save(vpss_dev->hw_dev);
rkvpss_soft_reset(vpss_dev->hw_dev);
rkvpss_hw_reg_restore(vpss_dev->hw_dev);
dev_info(vpss_dev->dev, "%s exit\n", __func__);
}
static long rkvpss_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
struct rkvpss_subdev *sdev = v4l2_get_subdevdata(sd);
struct rkvpss_device *vpss_dev = sdev->dev;
long ret = 0;
switch (cmd) {
case RKISP_VPSS_CMD_SOF:
ret = rkvpss_sof(sdev, arg);
break;
case RKISP_VPSS_RESET_NOTIFY_VPSS:
rkvpss_reset_handle(vpss_dev);
break;
default:
ret = -ENOIOCTLCMD;
}
return ret;
}
#ifdef CONFIG_COMPAT
static long rkvpss_compat_ioctl32(struct v4l2_subdev *sd,
unsigned int cmd, unsigned long arg)
{
long ret = 0;
return ret;
}
#endif
static const struct media_entity_operations rkvpss_sd_media_ops = {
.link_setup = rkvpss_subdev_link_setup,
.link_validate = v4l2_subdev_link_validate,
};
static const struct v4l2_subdev_pad_ops rkvpss_sd_pad_ops = {
.get_fmt = rkvpss_sd_get_fmt,
.set_fmt = rkvpss_sd_set_fmt,
};
static const struct v4l2_subdev_video_ops rkvpss_sd_video_ops = {
.s_stream = rkvpss_sd_s_stream,
.s_rx_buffer = rkvpss_sd_s_rx_buffer,
};
static const struct v4l2_subdev_core_ops rkvpss_sd_core_ops = {
.s_power = rkvpss_sd_s_power,
.ioctl = rkvpss_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl32 = rkvpss_compat_ioctl32,
#endif
};
static const struct v4l2_subdev_ops rkvpss_sd_ops = {
.core = &rkvpss_sd_core_ops,
.video = &rkvpss_sd_video_ops,
.pad = &rkvpss_sd_pad_ops,
};
static void rkvpss_subdev_default_fmt(struct rkvpss_subdev *sdev)
{
struct v4l2_mbus_framefmt *in_fmt = &sdev->in_fmt;
struct vpsssd_fmt *out_fmt = &sdev->out_fmt;
in_fmt->width = RKVPSS_DEFAULT_WIDTH;
in_fmt->height = RKVPSS_DEFAULT_HEIGHT;
in_fmt->code = MEDIA_BUS_FMT_YUYV8_2X8;
*out_fmt = rkvpss_formats[0];
out_fmt->width = RKVPSS_DEFAULT_WIDTH;
out_fmt->height = RKVPSS_DEFAULT_HEIGHT;
}
static void rkvpss_end_notify_isp(struct rkvpss_device *dev)
{
int stopping;
v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev,
"%s stopping:%d unite_index:%d\n", __func__, dev->stopping, dev->unite_index);
if (dev->stopping) {
if (dev->unite_mode) {
if (dev->unite_index == VPSS_UNITE_LEFT) {
stopping = 0;
v4l2_subdev_call(dev->remote_sd, core, ioctl, RKISP_VPSS_CMD_EOF,
&stopping);
} else {
dev->stopping = false;
wake_up(&dev->stop_done);
stopping = 1;
v4l2_subdev_call(dev->remote_sd, core, ioctl, RKISP_VPSS_CMD_EOF,
&stopping);
}
} else {
dev->stopping = false;
wake_up(&dev->stop_done);
stopping = 1;
v4l2_subdev_call(dev->remote_sd, core, ioctl, RKISP_VPSS_CMD_EOF,
&stopping);
}
} else {
stopping = 0;
v4l2_subdev_call(dev->remote_sd, core, ioctl, RKISP_VPSS_CMD_EOF, &stopping);
}
}
void rkvpss_check_idle(struct rkvpss_device *dev, u32 irq)
{
dev->irq_ends |= (irq & dev->irq_ends_mask);
v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev,
"%s irq:0x%x ends:0x%x mask:0x%x\n",
__func__, irq, dev->irq_ends, dev->irq_ends_mask);
if ((dev->irq_ends & dev->irq_ends_mask) != dev->irq_ends_mask)
return;
dev->irq_ends = 0;
rkvpss_end_notify_isp(dev);
dev->is_idle = true;
if (dev->is_suspend)
complete(&dev->pm_suspend_wait_fe);
}
int rkvpss_register_subdev(struct rkvpss_device *dev,
struct v4l2_device *v4l2_dev)
{
struct rkvpss_subdev *vpss_sdev = &dev->vpss_sdev;
struct v4l2_subdev *sd;
int ret;
spin_lock_init(&dev->cmsc_lock);
memset(vpss_sdev, 0, sizeof(*vpss_sdev));
vpss_sdev->dev = dev;
sd = &vpss_sdev->sd;
vpss_sdev->state = VPSS_STOP;
v4l2_subdev_init(sd, &rkvpss_sd_ops);
//sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
sd->entity.ops = &rkvpss_sd_media_ops;
snprintf(sd->name, sizeof(sd->name), "rkvpss-subdev");
sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_COMPOSER;
vpss_sdev->pads[RKVPSS_PAD_SINK].flags =
MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
vpss_sdev->pads[RKVPSS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_pads_init(&sd->entity, RKVPSS_PAD_MAX, vpss_sdev->pads);
if (ret < 0)
return ret;
sd->owner = THIS_MODULE;
v4l2_set_subdevdata(sd, vpss_sdev);
sd->grp_id = GRP_ID_VPSS;
ret = v4l2_device_register_subdev(v4l2_dev, sd);
if (ret < 0)
goto free_media;
rkvpss_subdev_default_fmt(vpss_sdev);
init_completion(&dev->pm_suspend_wait_fe);
return ret;
free_media:
media_entity_cleanup(&sd->entity);
v4l2_err(sd, "Failed to register subdev, ret:%d\n", ret);
return ret;
}
void rkvpss_unregister_subdev(struct rkvpss_device *dev)
{
struct v4l2_subdev *sd = &dev->vpss_sdev.sd;
v4l2_device_unregister_subdev(sd);
media_entity_cleanup(&sd->entity);
}