|
@@ -33,6 +33,7 @@
|
|
|
#include <media/v4l2-fwnode.h>
|
|
|
#include <media/v4l2-image-sizes.h>
|
|
|
#include <media/v4l2-ioctl.h>
|
|
|
+#include <media/v4l2-rect.h>
|
|
|
#include <media/videobuf2-dma-contig.h>
|
|
|
|
|
|
#define DRV_NAME "stm32-dcmi"
|
|
@@ -107,6 +108,11 @@ struct dcmi_format {
|
|
|
u8 bpp;
|
|
|
};
|
|
|
|
|
|
+struct dcmi_framesize {
|
|
|
+ u32 width;
|
|
|
+ u32 height;
|
|
|
+};
|
|
|
+
|
|
|
struct dcmi_buf {
|
|
|
struct vb2_v4l2_buffer vb;
|
|
|
bool prepared;
|
|
@@ -131,10 +137,16 @@ struct stm32_dcmi {
|
|
|
struct v4l2_async_notifier notifier;
|
|
|
struct dcmi_graph_entity entity;
|
|
|
struct v4l2_format fmt;
|
|
|
+ struct v4l2_rect crop;
|
|
|
+ bool do_crop;
|
|
|
|
|
|
const struct dcmi_format **sd_formats;
|
|
|
unsigned int num_of_sd_formats;
|
|
|
const struct dcmi_format *sd_format;
|
|
|
+ struct dcmi_framesize *sd_framesizes;
|
|
|
+ unsigned int num_of_sd_framesizes;
|
|
|
+ struct dcmi_framesize sd_framesize;
|
|
|
+ struct v4l2_rect sd_bounds;
|
|
|
|
|
|
/* Protect this data structure */
|
|
|
struct mutex lock;
|
|
@@ -325,6 +337,28 @@ static int dcmi_start_capture(struct stm32_dcmi *dcmi)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static void dcmi_set_crop(struct stm32_dcmi *dcmi)
|
|
|
+{
|
|
|
+ u32 size, start;
|
|
|
+
|
|
|
+ /* Crop resolution */
|
|
|
+ size = ((dcmi->crop.height - 1) << 16) |
|
|
|
+ ((dcmi->crop.width << 1) - 1);
|
|
|
+ reg_write(dcmi->regs, DCMI_CWSIZE, size);
|
|
|
+
|
|
|
+ /* Crop start point */
|
|
|
+ start = ((dcmi->crop.top) << 16) |
|
|
|
+ ((dcmi->crop.left << 1));
|
|
|
+ reg_write(dcmi->regs, DCMI_CWSTRT, start);
|
|
|
+
|
|
|
+ dev_dbg(dcmi->dev, "Cropping to %ux%u@%u:%u\n",
|
|
|
+ dcmi->crop.width, dcmi->crop.height,
|
|
|
+ dcmi->crop.left, dcmi->crop.top);
|
|
|
+
|
|
|
+ /* Enable crop */
|
|
|
+ reg_set(dcmi->regs, DCMI_CR, CR_CROP);
|
|
|
+}
|
|
|
+
|
|
|
static irqreturn_t dcmi_irq_thread(int irq, void *arg)
|
|
|
{
|
|
|
struct stm32_dcmi *dcmi = arg;
|
|
@@ -540,6 +574,10 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count)
|
|
|
|
|
|
reg_write(dcmi->regs, DCMI_CR, val);
|
|
|
|
|
|
+ /* Set crop */
|
|
|
+ if (dcmi->do_crop)
|
|
|
+ dcmi_set_crop(dcmi);
|
|
|
+
|
|
|
/* Enable dcmi */
|
|
|
reg_set(dcmi->regs, DCMI_CR, CR_ENABLE);
|
|
|
|
|
@@ -697,10 +735,37 @@ static const struct dcmi_format *find_format_by_fourcc(struct stm32_dcmi *dcmi,
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
+static void __find_outer_frame_size(struct stm32_dcmi *dcmi,
|
|
|
+ struct v4l2_pix_format *pix,
|
|
|
+ struct dcmi_framesize *framesize)
|
|
|
+{
|
|
|
+ struct dcmi_framesize *match = NULL;
|
|
|
+ unsigned int i;
|
|
|
+ unsigned int min_err = UINT_MAX;
|
|
|
+
|
|
|
+ for (i = 0; i < dcmi->num_of_sd_framesizes; i++) {
|
|
|
+ struct dcmi_framesize *fsize = &dcmi->sd_framesizes[i];
|
|
|
+ int w_err = (fsize->width - pix->width);
|
|
|
+ int h_err = (fsize->height - pix->height);
|
|
|
+ int err = w_err + h_err;
|
|
|
+
|
|
|
+ if ((w_err >= 0) && (h_err >= 0) && (err < min_err)) {
|
|
|
+ min_err = err;
|
|
|
+ match = fsize;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!match)
|
|
|
+ match = &dcmi->sd_framesizes[0];
|
|
|
+
|
|
|
+ *framesize = *match;
|
|
|
+}
|
|
|
+
|
|
|
static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f,
|
|
|
- const struct dcmi_format **sd_format)
|
|
|
+ const struct dcmi_format **sd_format,
|
|
|
+ struct dcmi_framesize *sd_framesize)
|
|
|
{
|
|
|
const struct dcmi_format *sd_fmt;
|
|
|
+ struct dcmi_framesize sd_fsize;
|
|
|
struct v4l2_pix_format *pix = &f->fmt.pix;
|
|
|
struct v4l2_subdev_pad_config pad_cfg;
|
|
|
struct v4l2_subdev_format format = {
|
|
@@ -718,6 +783,17 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f,
|
|
|
pix->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH);
|
|
|
pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT);
|
|
|
|
|
|
+ if (dcmi->do_crop && dcmi->num_of_sd_framesizes) {
|
|
|
+ struct dcmi_framesize outer_sd_fsize;
|
|
|
+ /*
|
|
|
+ * If crop is requested and sensor have discrete frame sizes,
|
|
|
+ * select the frame size that is just larger than request
|
|
|
+ */
|
|
|
+ __find_outer_frame_size(dcmi, pix, &outer_sd_fsize);
|
|
|
+ pix->width = outer_sd_fsize.width;
|
|
|
+ pix->height = outer_sd_fsize.height;
|
|
|
+ }
|
|
|
+
|
|
|
v4l2_fill_mbus_format(&format.format, pix, sd_fmt->mbus_code);
|
|
|
ret = v4l2_subdev_call(dcmi->entity.subdev, pad, set_fmt,
|
|
|
&pad_cfg, &format);
|
|
@@ -727,6 +803,31 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f,
|
|
|
/* Update pix regarding to what sensor can do */
|
|
|
v4l2_fill_pix_format(pix, &format.format);
|
|
|
|
|
|
+ /* Save resolution that sensor can actually do */
|
|
|
+ sd_fsize.width = pix->width;
|
|
|
+ sd_fsize.height = pix->height;
|
|
|
+
|
|
|
+ if (dcmi->do_crop) {
|
|
|
+ struct v4l2_rect c = dcmi->crop;
|
|
|
+ struct v4l2_rect max_rect;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Adjust crop by making the intersection between
|
|
|
+ * format resolution request and crop request
|
|
|
+ */
|
|
|
+ max_rect.top = 0;
|
|
|
+ max_rect.left = 0;
|
|
|
+ max_rect.width = pix->width;
|
|
|
+ max_rect.height = pix->height;
|
|
|
+ v4l2_rect_map_inside(&c, &max_rect);
|
|
|
+ c.top = clamp_t(s32, c.top, 0, pix->height - c.height);
|
|
|
+ c.left = clamp_t(s32, c.left, 0, pix->width - c.width);
|
|
|
+ dcmi->crop = c;
|
|
|
+
|
|
|
+ /* Adjust format resolution request to crop */
|
|
|
+ pix->width = dcmi->crop.width;
|
|
|
+ pix->height = dcmi->crop.height;
|
|
|
+ }
|
|
|
|
|
|
pix->field = V4L2_FIELD_NONE;
|
|
|
pix->bytesperline = pix->width * sd_fmt->bpp;
|
|
@@ -734,6 +835,8 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f,
|
|
|
|
|
|
if (sd_format)
|
|
|
*sd_format = sd_fmt;
|
|
|
+ if (sd_framesize)
|
|
|
+ *sd_framesize = sd_fsize;
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -744,24 +847,41 @@ static int dcmi_set_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f)
|
|
|
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
|
|
|
};
|
|
|
const struct dcmi_format *sd_format;
|
|
|
+ struct dcmi_framesize sd_framesize;
|
|
|
struct v4l2_mbus_framefmt *mf = &format.format;
|
|
|
struct v4l2_pix_format *pix = &f->fmt.pix;
|
|
|
int ret;
|
|
|
|
|
|
- ret = dcmi_try_fmt(dcmi, f, &sd_format);
|
|
|
+ /*
|
|
|
+ * Try format, fmt.width/height could have been changed
|
|
|
+ * to match sensor capability or crop request
|
|
|
+ * sd_format & sd_framesize will contain what subdev
|
|
|
+ * can do for this request.
|
|
|
+ */
|
|
|
+ ret = dcmi_try_fmt(dcmi, f, &sd_format, &sd_framesize);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
|
/* pix to mbus format */
|
|
|
v4l2_fill_mbus_format(mf, pix,
|
|
|
sd_format->mbus_code);
|
|
|
+ mf->width = sd_framesize.width;
|
|
|
+ mf->height = sd_framesize.height;
|
|
|
+
|
|
|
ret = v4l2_subdev_call(dcmi->entity.subdev, pad,
|
|
|
set_fmt, NULL, &format);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
|
+ dev_dbg(dcmi->dev, "Sensor format set to 0x%x %ux%u\n",
|
|
|
+ mf->code, mf->width, mf->height);
|
|
|
+ dev_dbg(dcmi->dev, "Buffer format set to %4.4s %ux%u\n",
|
|
|
+ (char *)&pix->pixelformat,
|
|
|
+ pix->width, pix->height);
|
|
|
+
|
|
|
dcmi->fmt = *f;
|
|
|
dcmi->sd_format = sd_format;
|
|
|
+ dcmi->sd_framesize = sd_framesize;
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -782,7 +902,7 @@ static int dcmi_try_fmt_vid_cap(struct file *file, void *priv,
|
|
|
{
|
|
|
struct stm32_dcmi *dcmi = video_drvdata(file);
|
|
|
|
|
|
- return dcmi_try_fmt(dcmi, f, NULL);
|
|
|
+ return dcmi_try_fmt(dcmi, f, NULL, NULL);
|
|
|
}
|
|
|
|
|
|
static int dcmi_enum_fmt_vid_cap(struct file *file, void *priv,
|
|
@@ -797,6 +917,193 @@ static int dcmi_enum_fmt_vid_cap(struct file *file, void *priv,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int dcmi_get_sensor_format(struct stm32_dcmi *dcmi,
|
|
|
+ struct v4l2_pix_format *pix)
|
|
|
+{
|
|
|
+ struct v4l2_subdev_format fmt = {
|
|
|
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
|
|
|
+ };
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = v4l2_subdev_call(dcmi->entity.subdev, pad, get_fmt, NULL, &fmt);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ v4l2_fill_pix_format(pix, &fmt.format);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int dcmi_set_sensor_format(struct stm32_dcmi *dcmi,
|
|
|
+ struct v4l2_pix_format *pix)
|
|
|
+{
|
|
|
+ const struct dcmi_format *sd_fmt;
|
|
|
+ struct v4l2_subdev_format format = {
|
|
|
+ .which = V4L2_SUBDEV_FORMAT_TRY,
|
|
|
+ };
|
|
|
+ struct v4l2_subdev_pad_config pad_cfg;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ sd_fmt = find_format_by_fourcc(dcmi, pix->pixelformat);
|
|
|
+ if (!sd_fmt) {
|
|
|
+ sd_fmt = dcmi->sd_formats[dcmi->num_of_sd_formats - 1];
|
|
|
+ pix->pixelformat = sd_fmt->fourcc;
|
|
|
+ }
|
|
|
+
|
|
|
+ v4l2_fill_mbus_format(&format.format, pix, sd_fmt->mbus_code);
|
|
|
+ ret = v4l2_subdev_call(dcmi->entity.subdev, pad, set_fmt,
|
|
|
+ &pad_cfg, &format);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int dcmi_get_sensor_bounds(struct stm32_dcmi *dcmi,
|
|
|
+ struct v4l2_rect *r)
|
|
|
+{
|
|
|
+ struct v4l2_subdev_selection bounds = {
|
|
|
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
|
|
|
+ .target = V4L2_SEL_TGT_CROP_BOUNDS,
|
|
|
+ };
|
|
|
+ unsigned int max_width, max_height, max_pixsize;
|
|
|
+ struct v4l2_pix_format pix;
|
|
|
+ unsigned int i;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Get sensor bounds first
|
|
|
+ */
|
|
|
+ ret = v4l2_subdev_call(dcmi->entity.subdev, pad, get_selection,
|
|
|
+ NULL, &bounds);
|
|
|
+ if (!ret)
|
|
|
+ *r = bounds.r;
|
|
|
+ if (ret != -ENOIOCTLCMD)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If selection is not implemented,
|
|
|
+ * fallback by enumerating sensor frame sizes
|
|
|
+ * and take the largest one
|
|
|
+ */
|
|
|
+ max_width = 0;
|
|
|
+ max_height = 0;
|
|
|
+ max_pixsize = 0;
|
|
|
+ for (i = 0; i < dcmi->num_of_sd_framesizes; i++) {
|
|
|
+ struct dcmi_framesize *fsize = &dcmi->sd_framesizes[i];
|
|
|
+ unsigned int pixsize = fsize->width * fsize->height;
|
|
|
+
|
|
|
+ if (pixsize > max_pixsize) {
|
|
|
+ max_pixsize = pixsize;
|
|
|
+ max_width = fsize->width;
|
|
|
+ max_height = fsize->height;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (max_pixsize > 0) {
|
|
|
+ r->top = 0;
|
|
|
+ r->left = 0;
|
|
|
+ r->width = max_width;
|
|
|
+ r->height = max_height;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If frame sizes enumeration is not implemented,
|
|
|
+ * fallback by getting current sensor frame size
|
|
|
+ */
|
|
|
+ ret = dcmi_get_sensor_format(dcmi, &pix);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ r->top = 0;
|
|
|
+ r->left = 0;
|
|
|
+ r->width = pix.width;
|
|
|
+ r->height = pix.height;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int dcmi_g_selection(struct file *file, void *fh,
|
|
|
+ struct v4l2_selection *s)
|
|
|
+{
|
|
|
+ struct stm32_dcmi *dcmi = video_drvdata(file);
|
|
|
+
|
|
|
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ switch (s->target) {
|
|
|
+ case V4L2_SEL_TGT_CROP_DEFAULT:
|
|
|
+ case V4L2_SEL_TGT_CROP_BOUNDS:
|
|
|
+ s->r = dcmi->sd_bounds;
|
|
|
+ return 0;
|
|
|
+ case V4L2_SEL_TGT_CROP:
|
|
|
+ if (dcmi->do_crop) {
|
|
|
+ s->r = dcmi->crop;
|
|
|
+ } else {
|
|
|
+ s->r.top = 0;
|
|
|
+ s->r.left = 0;
|
|
|
+ s->r.width = dcmi->fmt.fmt.pix.width;
|
|
|
+ s->r.height = dcmi->fmt.fmt.pix.height;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int dcmi_s_selection(struct file *file, void *priv,
|
|
|
+ struct v4l2_selection *s)
|
|
|
+{
|
|
|
+ struct stm32_dcmi *dcmi = video_drvdata(file);
|
|
|
+ struct v4l2_rect r = s->r;
|
|
|
+ struct v4l2_rect max_rect;
|
|
|
+ struct v4l2_pix_format pix;
|
|
|
+
|
|
|
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
|
|
|
+ s->target != V4L2_SEL_TGT_CROP)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ /* Reset sensor resolution to max resolution */
|
|
|
+ pix.pixelformat = dcmi->fmt.fmt.pix.pixelformat;
|
|
|
+ pix.width = dcmi->sd_bounds.width;
|
|
|
+ pix.height = dcmi->sd_bounds.height;
|
|
|
+ dcmi_set_sensor_format(dcmi, &pix);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Make the intersection between
|
|
|
+ * sensor resolution
|
|
|
+ * and crop request
|
|
|
+ */
|
|
|
+ max_rect.top = 0;
|
|
|
+ max_rect.left = 0;
|
|
|
+ max_rect.width = pix.width;
|
|
|
+ max_rect.height = pix.height;
|
|
|
+ v4l2_rect_map_inside(&r, &max_rect);
|
|
|
+ r.top = clamp_t(s32, r.top, 0, pix.height - r.height);
|
|
|
+ r.left = clamp_t(s32, r.left, 0, pix.width - r.width);
|
|
|
+
|
|
|
+ if (!((r.top == dcmi->sd_bounds.top) &&
|
|
|
+ (r.left == dcmi->sd_bounds.left) &&
|
|
|
+ (r.width == dcmi->sd_bounds.width) &&
|
|
|
+ (r.height == dcmi->sd_bounds.height))) {
|
|
|
+ /* Crop if request is different than sensor resolution */
|
|
|
+ dcmi->do_crop = true;
|
|
|
+ dcmi->crop = r;
|
|
|
+ dev_dbg(dcmi->dev, "s_selection: crop %ux%u@(%u,%u) from %ux%u\n",
|
|
|
+ r.width, r.height, r.left, r.top,
|
|
|
+ pix.width, pix.height);
|
|
|
+ } else {
|
|
|
+ /* Disable crop */
|
|
|
+ dcmi->do_crop = false;
|
|
|
+ dev_dbg(dcmi->dev, "s_selection: crop is disabled\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ s->r = r;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int dcmi_querycap(struct file *file, void *priv,
|
|
|
struct v4l2_capability *cap)
|
|
|
{
|
|
@@ -955,6 +1262,8 @@ static const struct v4l2_ioctl_ops dcmi_ioctl_ops = {
|
|
|
.vidioc_g_fmt_vid_cap = dcmi_g_fmt_vid_cap,
|
|
|
.vidioc_s_fmt_vid_cap = dcmi_s_fmt_vid_cap,
|
|
|
.vidioc_enum_fmt_vid_cap = dcmi_enum_fmt_vid_cap,
|
|
|
+ .vidioc_g_selection = dcmi_g_selection,
|
|
|
+ .vidioc_s_selection = dcmi_s_selection,
|
|
|
|
|
|
.vidioc_enum_input = dcmi_enum_input,
|
|
|
.vidioc_g_input = dcmi_g_input,
|
|
@@ -1004,7 +1313,7 @@ static int dcmi_set_default_fmt(struct stm32_dcmi *dcmi)
|
|
|
};
|
|
|
int ret;
|
|
|
|
|
|
- ret = dcmi_try_fmt(dcmi, &f, NULL);
|
|
|
+ ret = dcmi_try_fmt(dcmi, &f, NULL, NULL);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
dcmi->sd_format = dcmi->sd_formats[0];
|
|
@@ -1075,6 +1384,51 @@ static int dcmi_formats_init(struct stm32_dcmi *dcmi)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int dcmi_framesizes_init(struct stm32_dcmi *dcmi)
|
|
|
+{
|
|
|
+ unsigned int num_fsize = 0;
|
|
|
+ struct v4l2_subdev *subdev = dcmi->entity.subdev;
|
|
|
+ struct v4l2_subdev_frame_size_enum fse = {
|
|
|
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
|
|
|
+ .code = dcmi->sd_format->mbus_code,
|
|
|
+ };
|
|
|
+ unsigned int ret;
|
|
|
+ unsigned int i;
|
|
|
+
|
|
|
+ /* Allocate discrete framesizes array */
|
|
|
+ while (!v4l2_subdev_call(subdev, pad, enum_frame_size,
|
|
|
+ NULL, &fse))
|
|
|
+ fse.index++;
|
|
|
+
|
|
|
+ num_fsize = fse.index;
|
|
|
+ if (!num_fsize)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ dcmi->num_of_sd_framesizes = num_fsize;
|
|
|
+ dcmi->sd_framesizes = devm_kcalloc(dcmi->dev, num_fsize,
|
|
|
+ sizeof(struct dcmi_framesize),
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (!dcmi->sd_framesizes) {
|
|
|
+ dev_err(dcmi->dev, "Could not allocate memory\n");
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Fill array with sensor supported framesizes */
|
|
|
+ dev_dbg(dcmi->dev, "Sensor supports %u frame sizes:\n", num_fsize);
|
|
|
+ for (i = 0; i < dcmi->num_of_sd_framesizes; i++) {
|
|
|
+ fse.index = i;
|
|
|
+ ret = v4l2_subdev_call(subdev, pad, enum_frame_size,
|
|
|
+ NULL, &fse);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ dcmi->sd_framesizes[fse.index].width = fse.max_width;
|
|
|
+ dcmi->sd_framesizes[fse.index].height = fse.max_height;
|
|
|
+ dev_dbg(dcmi->dev, "%ux%u\n", fse.max_width, fse.max_height);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int dcmi_graph_notify_complete(struct v4l2_async_notifier *notifier)
|
|
|
{
|
|
|
struct stm32_dcmi *dcmi = notifier_to_dcmi(notifier);
|
|
@@ -1087,6 +1441,18 @@ static int dcmi_graph_notify_complete(struct v4l2_async_notifier *notifier)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+ ret = dcmi_framesizes_init(dcmi);
|
|
|
+ if (ret) {
|
|
|
+ dev_err(dcmi->dev, "Could not initialize framesizes\n");
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = dcmi_get_sensor_bounds(dcmi, &dcmi->sd_bounds);
|
|
|
+ if (ret) {
|
|
|
+ dev_err(dcmi->dev, "Could not get sensor bounds\n");
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
ret = dcmi_set_default_fmt(dcmi);
|
|
|
if (ret) {
|
|
|
dev_err(dcmi->dev, "Could not set default format\n");
|