相关代码位置:
https://gitee.com/ldl17/v4l2-learn/tree/master/rv1126-imx291
首先看crop的初始化代码
rkcif_plat_probe() -> rkcif_plat_init() -> rkcif_stream_init()
#define RKCIF_DEFAULT_WIDTH 640
#define RKCIF_DEFAULT_HEIGHT 480
void rkcif_stream_init(struct rkcif_device *dev, u32 id)
{
struct rkcif_stream *stream = &dev->stream[id];
struct v4l2_pix_format_mplane pixm;
int i;
memset(stream, 0, sizeof(*stream));
...
for (i = 0; i < CROP_SRC_MAX; i++) {
stream->crop[i].left = 0;
stream->crop[i].top = 0;
stream->crop[i].width = RKCIF_DEFAULT_WIDTH;
stream->crop[i].height = RKCIF_DEFAULT_HEIGHT;
}
stream->crop_enable = false;
stream->crop_mask = 0x0;
...
}
可以看到初始化的时候被设置成640*480
下面ioctl操作是如何会修改stream->crop的值
以命令v4l2-ctl -d /dev/video0 --set-fmt-video=width=512,height=192,pixelformat=BG12 ... 为例
#define RKCIF_DEFAULT_WIDTH 640
#define RKCIF_DEFAULT_HEIGHT 480
static void rkcif_set_fmt(struct rkcif_stream *stream,
struct v4l2_pix_format_mplane *pixm,
bool try)
{
struct rkcif_device *dev = stream->cifdev;
const struct cif_output_fmt *fmt;
struct v4l2_rect input_rect;
unsigned int imagesize = 0, planes;
u32 xsubs = 1, ysubs = 1, i;
struct rkmodule_hdr_cfg hdr_cfg;
int ret;
/*
* 这里根据BG12
* 找到对应的fmt信息如下:
* .fourcc = V4L2_PIX_FMT_SBGGR12,
* .cplanes = 1,
* .mplanes = 1,
* .bpp = { 16 },
* .raw_bpp = 12,
* .csi_fmt_val = CSI_WRDDR_TYPE_RAW12,
* .fmt_type = CIF_FMT_TYPE_RAW,
*/
fmt = find_output_fmt(stream, pixm->pixelformat);
if (!fmt)
fmt = &out_fmts[0];
input_rect.width = RKCIF_DEFAULT_WIDTH;
input_rect.height = RKCIF_DEFAULT_HEIGHT;
if (dev->active_sensor && dev->active_sensor->sd)
/*
* 这里的一系列调用就不分析了,之前都已经分析过了
* 对应故事里的摄像头
* input_rect.top = 0
* input_rect.left = 0
* input_rect.width = 512
* input_rect.height = 192
*/
get_input_fmt(dev->active_sensor->sd,
&input_rect, stream->id + 1);
if (dev->terminal_sensor.sd) {
ret = v4l2_subdev_call(dev->terminal_sensor.sd,
core, ioctl,
RKMODULE_GET_HDR_CFG,
&hdr_cfg);
if (!ret)
dev->hdr.mode = hdr_cfg.hdr_mode;
else
dev->hdr.mode = NO_HDR;
/* terminal_sensor.raw_rect 赋值*/
dev->terminal_sensor.raw_rect = input_rect;
}
/* CIF has not scale function,
* the size should not be larger than input
*/
pixm->width = clamp_t(u32, pixm->width,
CIF_MIN_WIDTH, input_rect.width);
pixm->height = clamp_t(u32, pixm->height,
CIF_MIN_HEIGHT, input_rect.height);
pixm->num_planes = fmt->mplanes;
pixm->field = V4L2_FIELD_NONE;
pixm->quantization = V4L2_QUANTIZATION_DEFAULT;
/*
* rkcif_sync_crop_info 看下面的分析
*/
rkcif_sync_crop_info(stream);
/* calculate plane size and image size */
fcc_xysubs(fmt->fourcc, &xsubs, &ysubs);
planes = fmt->cplanes ? fmt->cplanes : fmt->mplanes;
for (i = 0; i < planes; i++) {
struct v4l2_plane_pix_format *plane_fmt;
int width, height, bpl, size, bpp;
if (i == 0) {
/*
* 根据上面分析,由于没有get_selection
* 所以stream->crop_enable值为false
*/
if (stream->crop_enable) {
width = stream->crop[CROP_SRC_ACT].width;
height = stream->crop[CROP_SRC_ACT].height;
} else {
width = pixm->width;
height = pixm->height;
}
} else {
if (stream->crop_enable) {
width = stream->crop[CROP_SRC_ACT].width / xsubs;
height = stream->crop[CROP_SRC_ACT].height / ysubs;
} else {
width = pixm->width / xsubs;
height = pixm->height / ysubs;
}
}
/* compact mode need bytesperline 4bytes align,
* align 8 to bring into correspondence with virtual width.
* to optimize reading and writing of ddr, aliged with 256.
*/
...
}
...
}
rkcif_set_fmt() -> rkcif_sync_crop_info()
static void rkcif_sync_crop_info(struct rkcif_stream *stream)
{
struct rkcif_device *dev = stream->cifdev;
struct v4l2_subdev_selection input_sel;
int ret;
if (dev->terminal_sensor.sd) {
input_sel.target = V4L2_SEL_TGT_CROP_BOUNDS;
/*
* 这里会调用sensor驱动的 pad->get_selection
* 我手里的驱动这个是没有实现的,所以会执行下面的else
*/
ret = v4l2_subdev_call(dev->terminal_sensor.sd,
pad, get_selection, NULL,
&input_sel);
if (!ret) {
stream->crop[CROP_SRC_SENSOR] = input_sel.r;
stream->crop_enable = true;
stream->crop_mask |= CROP_SRC_SENSOR_MASK;
dev->terminal_sensor.selection = input_sel;
} else {
/*
* 走到这里
*/
dev->terminal_sensor.selection.r = dev->terminal_sensor.raw_rect;
}
}
/*
* CROP_SRC_SENSOR_MASK
* 这个mask设置是在get_slection正常返回后
* 因为sensor没有 get_slection,所以没有CROP_SRC_SENSOR_MASK
* CROP_SRC_USR_MASK
* 这个mask是ioctl VIDIOC_S_CROP后设置
* 后面会分析到
*/
if ((stream->crop_mask & 0x3) == (CROP_SRC_USR_MASK | CROP_SRC_SENSOR_MASK)) {
if (stream->crop[CROP_SRC_USR].left + stream->crop[CROP_SRC_USR].width >
stream->crop[CROP_SRC_SENSOR].width ||
stream->crop[CROP_SRC_USR].top + stream->crop[CROP_SRC_USR].height >
stream->crop[CROP_SRC_SENSOR].height)
stream->crop[CROP_SRC_USR] = stream->crop[CROP_SRC_SENSOR];
}
if (stream->crop_mask & CROP_SRC_USR_MASK) {
stream->crop[CROP_SRC_ACT] = stream->crop[CROP_SRC_USR];
if (stream->crop_mask & CROP_SRC_SENSOR_MASK) {
stream->crop[CROP_SRC_ACT].left = stream->crop[CROP_SRC_USR].left +
stream->crop[CROP_SRC_SENSOR].left;
stream->crop[CROP_SRC_ACT].top = stream->crop[CROP_SRC_USR].top +
stream->crop[CROP_SRC_SENSOR].top;
}
} else {
/*
* 可以知道执行这个分支代码
* 由于没有get_selection
* 所以stream->crop[CROP_SRC_SENSOR]没有被更新
* 所以还是只是初始化的值
*/
stream->crop[CROP_SRC_ACT] = stream->crop[CROP_SRC_SENSOR];
}
}
看一下报错的代码
static int rkcif_sanity_check_fmt(struct rkcif_stream *stream,
const struct v4l2_rect *s_crop)
{
...
crop = &stream->crop[CROP_SRC_ACT];
if (crop->width + crop->left > input.width ||
crop->height + crop->top > input.height) {
v4l2_err(v4l2_dev, "crop size is bigger than input\n");
return -EINVAL;
}
...
}
由上面的分析可以知道
crop.width = 640
crop.height = 480
input.width = 512
input.height = 192
裁剪的大小大于输入的大小,所以报错
下面解决这个错误
v4l2-ctl -d /dev/video0 --set-crop top=0,left=0,width=480,height=120
驱动代码如下
static int rkcif_s_crop(struct file *file, void *fh, const struct v4l2_crop *a)
{
struct rkcif_stream *stream = video_drvdata(file);
struct rkcif_device *dev = stream->cifdev;
const struct v4l2_rect *rect = &a->c;
struct v4l2_rect sensor_crop;
int ret;
v4l2_info(&dev->v4l2_dev, "S_CROP(%ux%u@%u:%u) type: %d\n",
rect->width, rect->height, rect->left, rect->top, a->type);
/*
* 这里不跟进分析
* 可以认为没有问题
*/
ret = rkcif_sanity_check_fmt(stream, rect);
if (ret)
return ret;
/*
* sensor没有get_selection
* 所以没有设置CROP_SRC_SENSOR_MASK
*/
if (stream->crop_mask & CROP_SRC_SENSOR_MASK) {
sensor_crop = stream->crop[CROP_SRC_SENSOR];
if (sensor_crop.left + rect->left + rect->width > sensor_crop.width ||
sensor_crop.top + rect->top + rect->height > sensor_crop.height) {
v4l2_err(&dev->v4l2_dev, "crop size is bigger than sensor input:left:%d, top:%d, width:%d, height:%d\n",
sensor_crop.left, sensor_crop.top, sensor_crop.width, sensor_crop.height);
return -EINVAL;
}
}
/*
* 更新stream->crop[CROP_SRC_USR]
* 并且设置CROP_SRC_USR_MASK
*/
stream->crop[CROP_SRC_USR] = *rect;
stream->crop_enable = true;
stream->crop_mask |= CROP_SRC_USR_MASK;
stream->crop[CROP_SRC_ACT] = stream->crop[CROP_SRC_USR];
if (stream->crop_mask & CROP_SRC_SENSOR_MASK) {
stream->crop[CROP_SRC_ACT].left = sensor_crop.left + stream->crop[CROP_SRC_USR].left;
stream->crop[CROP_SRC_ACT].top = sensor_crop.top + stream->crop[CROP_SRC_USR].top;
}
return ret;
}
设置这个后,抓图为什么就ok了?
这就要再分析rkcif_set_fmt() -> rkcif_sync_crop_info()
static void rkcif_sync_crop_info(struct rkcif_stream *stream)
{
struct rkcif_device *dev = stream->cifdev;
struct v4l2_subdev_selection input_sel;
int ret;
if (dev->terminal_sensor.sd) {
input_sel.target = V4L2_SEL_TGT_CROP_BOUNDS;
/*
* 这里会调用sensor驱动的 pad->get_selection
* 我手里的驱动这个是没有实现的,所以会执行下面的else
*/
ret = v4l2_subdev_call(dev->terminal_sensor.sd,
pad, get_selection, NULL,
&input_sel);
if (!ret) {
stream->crop[CROP_SRC_SENSOR] = input_sel.r;
stream->crop_enable = true;
stream->crop_mask |= CROP_SRC_SENSOR_MASK;
dev->terminal_sensor.selection = input_sel;
} else {
/*
* 走到这里
*/
dev->terminal_sensor.selection.r = dev->terminal_sensor.raw_rect;
}
}
/*
* CROP_SRC_SENSOR_MASK
* 这个mask设置是在get_slection正常返回后
* 因为sensor没有 get_slection,所以没有CROP_SRC_SENSOR_MASK
* 执行了 ioctl VIDIOC_S_CROP 后
* CROP_SRC_USR_MASK 被设置了
*/
if ((stream->crop_mask & 0x3) == (CROP_SRC_USR_MASK | CROP_SRC_SENSOR_MASK)) {
if (stream->crop[CROP_SRC_USR].left + stream->crop[CROP_SRC_USR].width >
stream->crop[CROP_SRC_SENSOR].width ||
stream->crop[CROP_SRC_USR].top + stream->crop[CROP_SRC_USR].height >
stream->crop[CROP_SRC_SENSOR].height)
stream->crop[CROP_SRC_USR] = stream->crop[CROP_SRC_SENSOR];
}
/* CROP_SRC_USR_MASK 被设置 */
if (stream->crop_mask & CROP_SRC_USR_MASK) {
/*
* crop[CROP_SRC_ACT]值被更新
* crop区域大小在input范围内,所以不会出现之前的报错
*/
stream->crop[CROP_SRC_ACT] = stream->crop[CROP_SRC_USR];
if (stream->crop_mask & CROP_SRC_SENSOR_MASK) {
stream->crop[CROP_SRC_ACT].left = stream->crop[CROP_SRC_USR].left +
stream->crop[CROP_SRC_SENSOR].left;
stream->crop[CROP_SRC_ACT].top = stream->crop[CROP_SRC_USR].top +
stream->crop[CROP_SRC_SENSOR].top;
}
} else {
stream->crop[CROP_SRC_ACT] = stream->crop[CROP_SRC_SENSOR];
}
}
再看看VIDIOC_S_SELECTION
对应驱动
static int rkcif_s_selection(struct file *file, void *fh,
struct v4l2_selection *s)
{
struct rkcif_stream *stream = video_drvdata(file);
struct rkcif_device *dev = stream->cifdev;
struct v4l2_subdev *sensor_sd;
struct v4l2_subdev_selection sd_sel;
u16 pad = 0;
int ret = 0;
if (!s) {
v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, "sel is null\n");
goto err;
}
sensor_sd = get_remote_sensor(stream, &pad);
sd_sel.r = s->r;
sd_sel.pad = pad;
sd_sel.target = s->target;
sd_sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
ret = v4l2_subdev_call(sensor_sd, pad, set_selection, NULL, &sd_sel);
if (!ret) {
s->r = sd_sel.r;
v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, "%s: pad:%d, which:%d, target:%d\n",
__func__, pad, sd_sel.which, sd_sel.target);
}
return ret;
err:
return -EINVAL;
}
其实这里不用分析了,因为sensor也没有set_selection,所以这里的ioctl应该会报错
[root@RV1126_RV1109:/]# v4l2-ctl -d /dev/video0 --set-selection target=crop,flag
s=keep-config,top=0,left=0,width=480,height=120
VIDIOC_S_SELECTION: failed: Inappropriate ioctl for device
内核log如下
[ 1118.387010] video0: VIDIOC_S_SELECTION: error -515: type=vid-cap, target=0, flags=0x4, wxh=480x120, x,y=0,0