android13/kernel-5.10/drivers/media/platform/rockchip/ispp/ispp.c

501 lines
13 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd. */
#include <linux/compat.h>
#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/videobuf2-dma-contig.h>
#include <media/v4l2-event.h>
#include "dev.h"
#include "regs.h"
u32 cal_fec_mesh(u32 width, u32 height, u32 mode)
{
u32 mesh_size, mesh_left_height;
u32 w = ALIGN(width, 32);
u32 h = ALIGN(height, 32);
u32 spb_num = (h + 127) >> 7;
u32 left_height = h & 127;
u32 mesh_width = mode ? (w / 32 + 1) : (w / 16 + 1);
u32 mesh_height = mode ? 9 : 17;
if (!left_height)
left_height = 128;
mesh_left_height = mode ? (left_height / 16 + 1) :
(left_height / 8 + 1);
mesh_size = (spb_num - 1) * mesh_width * mesh_height +
mesh_width * mesh_left_height;
return mesh_size;
}
static const struct isppsd_fmt rkispp_formats[] = {
{
.mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
.fourcc = V4L2_PIX_FMT_NV16,
.wr_fmt = FMT_YUV422,
},
};
static const struct isppsd_fmt *find_fmt(u32 mbus_code)
{
const struct isppsd_fmt *fmt;
int i, array_size = ARRAY_SIZE(rkispp_formats);
for (i = 0; i < array_size; i++) {
fmt = &rkispp_formats[i];
if (fmt->mbus_code == mbus_code)
return fmt;
}
return NULL;
}
static int rkispp_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 rkispp_subdev *ispp_sdev;
struct rkispp_device *dev;
struct rkispp_stream_vdev *vdev;
struct rkispp_stream *stream = NULL;
if (local->index != RKISPP_PAD_SINK &&
local->index != RKISPP_PAD_SOURCE)
return 0;
if (!sd)
return -ENODEV;
ispp_sdev = v4l2_get_subdevdata(sd);
dev = ispp_sdev->dev;
vdev = &dev->stream_vdev;
if (!strcmp(remote->entity->name, II_VDEV_NAME)) {
stream = &vdev->stream[STREAM_II];
if (ispp_sdev->state & ISPP_START)
return -EBUSY;
if (flags & MEDIA_LNK_FL_ENABLED)
dev->inp = INP_DDR;
else if (ispp_sdev->remote_sd)
dev->inp = INP_ISP;
else
dev->inp = INP_INVAL;
stream->linked = flags & MEDIA_LNK_FL_ENABLED;
v4l2_dbg(1, rkispp_debug, &dev->v4l2_dev,
"input:%d\n", dev->inp);
} else if (!strcmp(remote->entity->name, MB_VDEV_NAME)) {
stream = &vdev->stream[STREAM_MB];
} else if (!strcmp(remote->entity->name, S0_VDEV_NAME)) {
stream = &vdev->stream[STREAM_S0];
} else if (!strcmp(remote->entity->name, S1_VDEV_NAME)) {
stream = &vdev->stream[STREAM_S1];
} else if (!strcmp(remote->entity->name, S2_VDEV_NAME)) {
stream = &vdev->stream[STREAM_S2];
}
if (stream && dev->stream_sync) {
stream->linked = flags & MEDIA_LNK_FL_ENABLED;
v4l2_dbg(1, rkispp_debug, &dev->v4l2_dev,
"stream:%d linked:%d\n",
stream->id, stream->linked);
}
return 0;
}
static int rkispp_sd_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct rkispp_subdev *ispp_sdev = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *mf;
const struct isppsd_fmt *ispp_fmt;
int ret = 0;
if (!fmt)
goto err;
if (fmt->pad != RKISPP_PAD_SINK &&
fmt->pad != RKISPP_PAD_SOURCE)
goto err;
mf = &fmt->format;
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
if (!cfg)
goto err;
mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
}
*mf = ispp_sdev->in_fmt;
if (fmt->pad == RKISPP_PAD_SINK && ispp_sdev->dev->inp == INP_ISP) {
ret = v4l2_subdev_call(ispp_sdev->remote_sd,
pad, get_fmt, cfg, fmt);
if (!ret) {
ispp_fmt = find_fmt(fmt->format.code);
if (!ispp_fmt)
goto err;
if (ispp_sdev->in_fmt.width != mf->width ||
ispp_sdev->in_fmt.height != mf->height) {
ispp_sdev->out_fmt = *ispp_fmt;
ispp_sdev->out_fmt.width = mf->width;
ispp_sdev->out_fmt.height = mf->height;
}
ispp_sdev->in_fmt = *mf;
}
} else {
*mf = ispp_sdev->in_fmt;
mf->width = ispp_sdev->out_fmt.width;
mf->height = ispp_sdev->out_fmt.height;
}
return ret;
err:
return -EINVAL;
}
static int rkispp_sd_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct rkispp_subdev *ispp_sdev = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *mf;
if (!fmt)
return -EINVAL;
/* format from isp output */
if (fmt->pad == RKISPP_PAD_SINK && ispp_sdev->dev->inp == INP_ISP)
return 0;
mf = &fmt->format;
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
if (!cfg)
return -EINVAL;
mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
}
if (fmt->pad == RKISPP_PAD_SINK) {
ispp_sdev->in_fmt = *mf;
} else {
ispp_sdev->out_fmt.width = mf->width;
ispp_sdev->out_fmt.height = mf->height;
}
return 0;
}
static int rkispp_sd_s_stream(struct v4l2_subdev *sd, int on)
{
struct rkispp_subdev *ispp_sdev = v4l2_get_subdevdata(sd);
struct rkispp_device *dev = ispp_sdev->dev;
int ret = 0;
v4l2_dbg(1, rkispp_debug, &ispp_sdev->dev->v4l2_dev,
"s_stream on:%d\n", on);
if (on) {
ispp_sdev->state = ISPP_START;
ispp_sdev->frm_sync_seq = -1;
ispp_sdev->frame_timestamp = 0;
rkispp_event_handle(dev, CMD_STREAM, &ispp_sdev->state);
}
if (dev->inp == INP_ISP)
ret = v4l2_subdev_call(ispp_sdev->remote_sd, video, s_stream, on);
if ((on && ret) || (!on && !ret)) {
ispp_sdev->state = ISPP_STOP;
if (dev->stream_vdev.monitor.is_en) {
dev->stream_vdev.monitor.is_en = false;
if (!completion_done(&dev->stream_vdev.monitor.cmpl))
complete(&dev->stream_vdev.monitor.cmpl);
if (!completion_done(&dev->stream_vdev.monitor.tnr.cmpl))
complete(&dev->stream_vdev.monitor.tnr.cmpl);
if (!completion_done(&dev->stream_vdev.monitor.nr.cmpl))
complete(&dev->stream_vdev.monitor.nr.cmpl);
if (!completion_done(&dev->stream_vdev.monitor.fec.cmpl))
complete(&dev->stream_vdev.monitor.fec.cmpl);
}
rkispp_event_handle(dev, CMD_STREAM, &ispp_sdev->state);
}
return ret;
}
static int rkispp_sd_s_rx_buffer(struct v4l2_subdev *sd,
void *buf, unsigned int *size)
{
struct rkispp_subdev *ispp_sdev = v4l2_get_subdevdata(sd);
struct rkispp_device *dev = ispp_sdev->dev;
u32 cmd = CMD_INIT_POOL;
/* size isn't using now */
if (!buf)
return -EINVAL;
if (ispp_sdev->state == ISPP_START) {
struct rkisp_ispp_buf *dbufs = buf;
struct rkispp_stream_vdev *vdev = &dev->stream_vdev;
u64 ns = ktime_get_ns();
vdev->dbg.interval = ns - vdev->dbg.timestamp;
vdev->dbg.timestamp = ns;
vdev->dbg.delay = ns - dbufs->frame_timestamp;
vdev->dbg.id = dbufs->frame_id;
cmd = CMD_QUEUE_DMABUF;
}
return rkispp_event_handle(dev, cmd, buf);
}
static int rkispp_sd_s_power(struct v4l2_subdev *sd, int on)
{
struct rkispp_subdev *ispp_sdev = v4l2_get_subdevdata(sd);
struct rkispp_device *ispp_dev = ispp_sdev->dev;
int ret;
v4l2_dbg(1, rkispp_debug, &ispp_dev->v4l2_dev,
"s_power on:%d\n", on);
if (on) {
if (ispp_dev->inp == INP_ISP) {
struct v4l2_subdev_format fmt;
/* update format, if ispp input change */
fmt.pad = RKISPP_PAD_SINK;
fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
if (ret) {
v4l2_err(&ispp_dev->v4l2_dev,
"%s get format fail:%d\n",
__func__, ret);
return ret;
}
ret = v4l2_subdev_call(ispp_sdev->remote_sd,
core, s_power, 1);
if (ret < 0) {
v4l2_err(&ispp_dev->v4l2_dev,
"%s set isp power on fail:%d\n",
__func__, ret);
return ret;
}
}
ret = pm_runtime_get_sync(ispp_dev->dev);
if (ret < 0) {
v4l2_err(&ispp_dev->v4l2_dev,
"%s runtime get failed:%d\n",
__func__, ret);
if (ispp_dev->inp == INP_ISP)
v4l2_subdev_call(ispp_sdev->remote_sd,
core, s_power, 0);
return ret;
}
} else {
if (ispp_dev->inp == INP_ISP)
v4l2_subdev_call(ispp_sdev->remote_sd, core, s_power, 0);
ret = pm_runtime_put_sync(ispp_dev->dev);
if (ret < 0)
v4l2_err(&ispp_dev->v4l2_dev,
"%s runtime put failed:%d\n",
__func__, ret);
}
return ret;
}
static long rkispp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
struct rkispp_subdev *ispp_sdev = v4l2_get_subdevdata(sd);
struct rkispp_device *ispp_dev = ispp_sdev->dev;
struct rkispp_fecbuf_info *fecbuf;
struct rkispp_fecbuf_size *fecsize;
struct rkisp_ispp_reg **reg_buf;
bool *rkispp_reg_withstream;
long ret = 0;
if (!arg)
return -EINVAL;
switch (cmd) {
case RKISPP_CMD_SET_INIT_MODULE:
ispp_dev->stream_vdev.module_ens = *((int *)arg);
if (ispp_dev->hw_dev->is_fec_ext)
ispp_dev->stream_vdev.module_ens &= ~ISPP_MODULE_FEC_ST;
break;
case RKISPP_CMD_GET_FECBUF_INFO:
fecbuf = (struct rkispp_fecbuf_info *)arg;
rkispp_params_get_fecbuf_inf(&ispp_dev->params_vdev[PARAM_VDEV_FEC], fecbuf);
break;
case RKISPP_CMD_SET_FECBUF_SIZE:
fecsize = (struct rkispp_fecbuf_size *)arg;
rkispp_params_set_fecbuf_size(&ispp_dev->params_vdev[PARAM_VDEV_FEC], fecsize);
break;
case RKISP_ISPP_CMD_REQUEST_REGBUF:
reg_buf = (struct rkisp_ispp_reg **)arg;
rkispp_request_regbuf(ispp_dev, reg_buf);
break;
case RKISP_ISPP_CMD_GET_REG_WITHSTREAM:
rkispp_reg_withstream = arg;
*rkispp_reg_withstream = rkispp_is_reg_withstream_global();
break;
#if IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_ISPP_VERSION_V10)
case RKISPP_CMD_GET_TNRBUF_FD:
ret = rkispp_get_tnrbuf_fd(ispp_dev, (struct rkispp_buf_idxfd *)arg);
break;
case RKISPP_CMD_GET_NRBUF_FD:
ret = rkispp_get_nrbuf_fd(ispp_dev, (struct rkispp_buf_idxfd *)arg);
break;
case RKISPP_CMD_TRIGGER_MODE:
rkispp_set_trigger_mode(ispp_dev, (struct rkispp_trigger_mode *)arg);
break;
#endif
default:
ret = -ENOIOCTLCMD;
}
return ret;
}
#ifdef CONFIG_COMPAT
static long rkispp_compat_ioctl32(struct v4l2_subdev *sd,
unsigned int cmd, unsigned long arg)
{
void __user *up = compat_ptr(arg);
struct rkispp_fecbuf_info fecbuf;
struct rkispp_fecbuf_size fecsize;
struct rkispp_buf_idxfd idxfd;
struct rkispp_trigger_mode t_mode;
long ret = 0;
if (!up)
return -EINVAL;
switch (cmd) {
case RKISPP_CMD_GET_FECBUF_INFO:
ret = rkispp_ioctl(sd, cmd, &fecbuf);
if (!ret && copy_to_user(up, &fecbuf, sizeof(fecbuf)))
ret = -EFAULT;
break;
case RKISPP_CMD_SET_FECBUF_SIZE:
if (copy_from_user(&fecsize, up, sizeof(fecsize)))
return -EFAULT;
ret = rkispp_ioctl(sd, cmd, &fecsize);
break;
case RKISPP_CMD_GET_TNRBUF_FD:
case RKISPP_CMD_GET_NRBUF_FD:
ret = rkispp_ioctl(sd, cmd, &idxfd);
if (!ret && copy_to_user(up, &idxfd, sizeof(idxfd)))
ret = -EFAULT;
break;
case RKISPP_CMD_TRIGGER_MODE:
if (copy_from_user(&t_mode, up, sizeof(t_mode)))
return -EFAULT;
ret = rkispp_ioctl(sd, cmd, &t_mode);
break;
default:
ret = -ENOIOCTLCMD;
}
return ret;
}
#endif
static int rkispp_subscribe_event(struct v4l2_subdev *sd,
struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
{
switch (sub->type) {
case RKISPP_V4L2_EVENT_TNR_COMPLETE:
return v4l2_event_subscribe(fh, sub, RKISPP_BUF_MAX, NULL);
default:
return -EINVAL;
}
}
static const struct media_entity_operations rkispp_sd_media_ops = {
.link_setup = rkispp_subdev_link_setup,
.link_validate = v4l2_subdev_link_validate,
};
static const struct v4l2_subdev_pad_ops rkispp_sd_pad_ops = {
.get_fmt = rkispp_sd_get_fmt,
.set_fmt = rkispp_sd_set_fmt,
};
static const struct v4l2_subdev_video_ops rkispp_sd_video_ops = {
.s_stream = rkispp_sd_s_stream,
.s_rx_buffer = rkispp_sd_s_rx_buffer,
};
static const struct v4l2_subdev_core_ops rkispp_sd_core_ops = {
.s_power = rkispp_sd_s_power,
.ioctl = rkispp_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl32 = rkispp_compat_ioctl32,
#endif
.subscribe_event = rkispp_subscribe_event,
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
};
static struct v4l2_subdev_ops rkispp_sd_ops = {
.core = &rkispp_sd_core_ops,
.video = &rkispp_sd_video_ops,
.pad = &rkispp_sd_pad_ops,
};
int rkispp_register_subdev(struct rkispp_device *dev,
struct v4l2_device *v4l2_dev)
{
struct rkispp_subdev *ispp_sdev = &dev->ispp_sdev;
struct v4l2_subdev *sd;
int ret;
memset(ispp_sdev, 0, sizeof(*ispp_sdev));
ispp_sdev->dev = dev;
sd = &ispp_sdev->sd;
ispp_sdev->state = ISPP_STOP;
v4l2_subdev_init(sd, &rkispp_sd_ops);
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
sd->entity.ops = &rkispp_sd_media_ops;
snprintf(sd->name, sizeof(sd->name), "rkispp-subdev");
sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_COMPOSER;
ispp_sdev->pads[RKISPP_PAD_SINK].flags =
MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
ispp_sdev->pads[RKISPP_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK;
ispp_sdev->pads[RKISPP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
ispp_sdev->pads[RKISPP_PAD_SOURCE_STATS].flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_pads_init(&sd->entity, RKISPP_PAD_MAX,
ispp_sdev->pads);
if (ret < 0)
return ret;
sd->owner = THIS_MODULE;
v4l2_set_subdevdata(sd, ispp_sdev);
sd->grp_id = GRP_ID_ISPP;
ret = v4l2_device_register_subdev(v4l2_dev, sd);
if (ret < 0)
goto free_media;
ret = v4l2_device_register_subdev_nodes(v4l2_dev);
if (ret < 0)
goto free_subdev;
return ret;
free_subdev:
v4l2_device_unregister_subdev(sd);
free_media:
media_entity_cleanup(&sd->entity);
v4l2_err(sd, "Failed to register subdev, ret:%d\n", ret);
return ret;
}
void rkispp_unregister_subdev(struct rkispp_device *dev)
{
struct v4l2_subdev *sd = &dev->ispp_sdev.sd;
v4l2_device_unregister_subdev(sd);
media_entity_cleanup(&sd->entity);
}