Hi,
I am working on bring up of camera OV5640 with the following system setup.
Board : VIM3L
OS : Debian10 (Build by fenix)
Kernel : 4.9
I could probe camera and faced issue https://forum.khadas.com/t/vim3-a311d-with-gc2145-camera/11445?u=kd_1993.
I solved this issue by using direct memory allocation instead of using function vm_init_resource
.
Now I can read setting using v4l2-ctl -d /dev/video0 -all
but when try to capture the picture it sink too much memory and gstreamer pipeline killed by out of memory killer.
Can you provide some pointers related to this?
Also, While deselect ANDROID option from kernel-config, facing issues in compilation. Is there any possibility of any issue due to this option selection and not using android on Khadas VIM3L?
[EDIT] - to resolve error of CMA I have applied patch as per attached as a alternative into vmalloc.
diff --git a/drivers/amlogic/media/camera/ov5640.c b/drivers/amlogic/media/camera/ov5640.c
index e58387a5..29d202ba 100644
--- a/drivers/amlogic/media/camera/ov5640.c
+++ b/drivers/amlogic/media/camera/ov5640.c
@@ -38,6 +38,7 @@
#include <linux/highmem.h>
#include <linux/freezer.h>
#include <linux/amlogic/media/v4l_util/videobuf-res.h>
+#include <media/videobuf-vmalloc.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#ifdef CONFIG_HAS_WAKELOCK
@@ -2192,8 +2193,13 @@ static void ov5640_fillbuff(struct ov5640_fh *fh, struct ov5640_buffer *buf)
{
int ret;
struct ov5640_device *dev = fh->dev;
- void *vbuf = (void *)videobuf_to_res(&buf->vb);
+ void *vbuf;
struct vm_output_para para = {0};
+ if (dev->vminfo.mem_alloc_succeed)
+ vbuf = (void *)videobuf_to_res(&buf->vb);
+ else
+ vbuf = videobuf_to_vmalloc(&buf->vb);
+
dprintk(dev, 1, "%s\n", __func__);
if (!vbuf)
@@ -2267,6 +2273,26 @@ static void ov5640_thread_tick(struct ov5640_fh *fh)
spin_unlock_irqrestore(&dev->slock, flags);
}
+static void free_vmall_buffer(struct videobuf_queue *vq,
+ struct ov5640_buffer *buf)
+{
+ struct ov5640_fh *fh;
+ //struct ov5640_device *dev;
+
+ fh = vq->priv_data;
+
+ printk( "%s, state: %i\n", __func__, buf->vb.state);
+
+ videobuf_waiton(vq, &buf->vb, 0, 0);
+ if (in_interrupt())
+ WARN_ON(1);
+ videobuf_vmalloc_free(&buf->vb);
+ printk("free_vmall_buffer: freed\n");
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+
+
static void ov5640_sleep(struct ov5640_fh *fh)
{
struct ov5640_device *dev = fh->dev;
@@ -2345,6 +2371,106 @@ static void ov5640_stop_thread(struct ov5640_dmaqueue *dma_q)
}
}
+static int
+vmall_buffer_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct ov5640_fh *fh = vq->priv_data;
+ struct ov5640_device *dev = fh->dev;
+ /* int bytes = fh->fmt->depth >> 3 ; */
+ *size = (fh->width * fh->height * fh->fmt->depth) >> 3;
+ if (*count == 0)
+ *count = 32;
+
+ while (*size * *count > vid_limit * 1024 * 1024)
+ (*count)--;
+
+ dprintk(dev, 1, "%s, count=%d, size=%d\n", __func__,
+ *count, *size);
+
+ return 0;
+}
+
+#define norm_maxw() 1920
+#define norm_maxh() 1600
+static int
+vmall_buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct ov5640_fh *fh;
+ struct ov5640_device *dev;
+ struct ov5640_buffer *buf;
+ int rc;
+
+ fh = vq->priv_data;
+ dev = fh->dev;
+ buf = container_of(vb, struct ov5640_buffer, vb);
+ /* int bytes = fh->fmt->depth >> 3 ; */
+ dprintk(dev, 1, "%s, field=%d\n", __func__, field);
+
+ WARN_ON(fh->fmt == NULL);
+
+ if (fh->width < 48 || fh->width > norm_maxw() ||
+ fh->height < 32 || fh->height > norm_maxh())
+ return -EINVAL;
+
+ buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3;
+ if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ /* These properties only change when queue is idle, see s_fmt */
+ buf->fmt = fh->fmt;
+ buf->vb.width = fh->width;
+ buf->vb.height = fh->height;
+ buf->vb.field = field;
+
+ /* precalculate_bars(fh); */
+
+ if (buf->vb.state == VIDEOBUF_NEEDS_INIT) {
+ rc = videobuf_iolock(vq, &buf->vb, NULL);
+ if (rc < 0)
+ goto fail;
+ }
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+
+ return 0;
+
+fail:
+ free_vmall_buffer(vq, buf);
+ return rc;
+}
+
+static void
+vmall_buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct ov5640_fh *fh;
+ struct ov5640_device *dev;
+ struct ov5640_dmaqueue *vidq;
+ struct ov5640_buffer *buf = container_of(vb, struct ov5640_buffer, vb);
+
+ fh = vq->priv_data;
+ dev = fh->dev;
+ vidq = &dev->vidq;
+
+ dprintk(dev, 1, "%s\n", __func__);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->vb.queue, &vidq->active);
+}
+
+static void vmall_buffer_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct ov5460_fh *fh;
+ struct ov5640_buffer *buf =
+ container_of(vb, struct ov5640_buffer, vb);
+ fh = vq->priv_data;
+
+ printk("%s\n", __func__);
+
+ free_vmall_buffer(vq, buf);
+}
+
static int
buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
{
@@ -2385,8 +2511,8 @@ static void free_buffer(struct videobuf_queue *vq, struct ov5640_buffer *buf)
buf->vb.state = VIDEOBUF_NEEDS_INIT;
}
-#define norm_maxw() 3000
-#define norm_maxh() 3000
+//#define norm_maxw() 3000
+//#define norm_maxh() 3000
static int
buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
enum v4l2_field field)
@@ -2458,6 +2584,14 @@ static void buffer_release(struct videobuf_queue *vq,
free_buffer(vq, buf);
}
+
+static struct videobuf_queue_ops ov5640_video_vmall_qops = {
+ .buf_setup = vmall_buffer_setup,
+ .buf_prepare = vmall_buffer_prepare,
+ .buf_queue = vmall_buffer_queue,
+ .buf_release = vmall_buffer_release,
+};
+
static struct videobuf_queue_ops ov5640_video_qops = {
.buf_setup = buffer_setup,
.buf_prepare = buffer_prepare,
@@ -2470,7 +2604,7 @@ static int vidioc_querycap(struct file *file, void *priv,
{
struct ov5640_fh *fh = priv;
struct ov5640_device *dev = fh->dev;
-
strcpy(cap->driver, "ov5640");
strcpy(cap->card, "ov5640.canvas");
if (dev->cam_info.front_back == 0)
@@ -2507,6 +2641,7 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
{
struct ov5640_fh *fh = priv;
pr_info("%s, fh->width =%d,fh->height=%d\n",
__func__, fh->width, fh->height);
f->fmt.pix.width = fh->width;
@@ -2595,6 +2730,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
int ret;
int cap_fps, pre_fps;
f->fmt.pix.width = (f->fmt.pix.width + (CANVAS_WIDTH_ALIGN - 1)) &
(~(CANVAS_WIDTH_ALIGN - 1));
if ((f->fmt.pix.pixelformat == V4L2_PIX_FMT_YVU420) ||
@@ -2669,6 +2805,7 @@ static int vidioc_g_parm(struct file *file, void *priv,
struct ov5640_device *dev = fh->dev;
struct v4l2_captureparm *cp = &parms->parm.capture;
dprintk(dev, 3, "vidioc_g_parm\n");
if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
@@ -3005,26 +3142,30 @@ static int ov5640_open(struct file *file)
struct ov5640_fh *fh = NULL;
int retval = 0;
dev->vminfo.vdin_id = dev->cam_info.vdin_path;
dev->vminfo.bt_path_count = dev->cam_info.bt_path_count;
#ifdef CONFIG_CMA
- retval = vm_init_resource(24 * SZ_1M, &dev->vminfo);
- if (retval < 0) {
- pr_err("error: no cma memory\n");
- return -1;
- }
+ dev->vminfo.mem_alloc_succeed = false;
+ //vm_init_resource(24 * SZ_1M, &dev->vminfo);
#endif
mutex_lock(&firmware_mutex);
ov5640_have_opened = 1;
mutex_unlock(&firmware_mutex);
aml_cam_init(&dev->cam_info);
OV5640_init_regs(dev);
msleep(20);
mutex_lock(&dev->mutex);
dev->users++;
if (dev->users > 1) {
@@ -3033,12 +3174,16 @@ static int ov5640_open(struct file *file)
return -EBUSY;
}
dprintk(dev, 1, "open %s type=%s users=%d\n",
video_device_node_name(dev->vdev),
v4l2_type_names[V4L2_BUF_TYPE_VIDEO_CAPTURE], dev->users);
INIT_LIST_HEAD(&dev->vidq.active);
init_waitqueue_head(&dev->vidq.wq);
spin_lock_init(&dev->slock);
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
@@ -3048,9 +3193,11 @@ static int ov5640_open(struct file *file)
}
mutex_unlock(&dev->mutex);
if (retval)
return retval;
#ifdef CONFIG_HAS_WAKELOCK
wake_lock(&(dev->wake_lock));
#endif
@@ -3067,19 +3214,31 @@ static int ov5640_open(struct file *file)
dev->jiffies = jiffies;
if (dev->vminfo.mem_alloc_succeed) {
fh->res.start = dev->vminfo.buffer_start;
fh->res.end = dev->vminfo.buffer_start +
dev->vminfo.vm_buf_size - 1;
fh->res.magic = MAGIC_RE_MEM;
fh->res.priv = NULL;
videobuf_queue_res_init(&fh->vb_vidq, &ov5640_video_qops,
NULL, &dev->slock, fh->type,
V4L2_FIELD_INTERLACED,
sizeof(struct ov5640_buffer),
(void *)&fh->res, NULL);
+ } else {
+ videobuf_queue_vmalloc_init(&fh->vb_vidq,
+ &ov5640_video_vmall_qops, NULL,
+ &dev->slock, fh->type,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct ov5640_buffer), fh, NULL);
+
}
bDoingAutoFocusMode = false;
ov5640_start_thread(fh);
return 0;
@@ -3244,13 +3403,15 @@ static int ov5640_probe(struct i2c_client *client,
int ret;
struct ov5640_device *t;
struct v4l2_subdev *sd;
-
+
vops = get_vdin_v4l2_ops();
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
t = kzalloc(sizeof(*t), GFP_KERNEL);
if (t == NULL) {
pr_err("ov5640 probe kzalloc failed.\n");
return -ENOMEM;
}
@@ -3260,6 +3421,7 @@ static int ov5640_probe(struct i2c_client *client,
ret = v4l2_device_register(NULL, &t->v4l2_dev);
if (ret) {
pr_info("%s, v4l2 device register failed", __func__);
kfree(t);
return ret;
}
@@ -3271,6 +3433,7 @@ static int ov5640_probe(struct i2c_client *client,
t->vdev = video_device_alloc();
if (t->vdev == NULL) {
kfree(t);
return -ENOMEM;
}
@@ -3278,6 +3441,7 @@ static int ov5640_probe(struct i2c_client *client,
t->vdev->v4l2_dev = &t->v4l2_dev;
video_set_drvdata(t->vdev, t);
#ifdef CONFIG_HAS_WAKELOCK
wake_lock_init(&(t->wake_lock), WAKE_LOCK_SUSPEND, "ov5640");
#endif
@@ -3285,16 +3449,19 @@ static int ov5640_probe(struct i2c_client *client,
plat_dat = (struct aml_cam_info_s *)client->dev.platform_data;
if (plat_dat) {
memcpy(&t->cam_info, plat_dat, sizeof(struct aml_cam_info_s));
pr_info("%s, front_back = %d\n",
__func__, plat_dat->front_back);
video_nr = plat_dat->front_back;
} else {
pr_err("camera ov5640: have no platform data\n");
video_device_release(t->vdev);
kfree(t);
return -1;
}
t->cam_info.version = OV5640_DRIVER_VERSION;
if (aml_cam_info_reg(&t->cam_info) < 0)
pr_err("reg caminfo error\n");
@@ -3302,12 +3469,14 @@ static int ov5640_probe(struct i2c_client *client,
pr_info("t->vdev = %p, video_nr = %d\n", t->vdev, video_nr);
err = video_register_device(t->vdev, VFL_TYPE_GRABBER, video_nr);
if (err < 0) {
video_device_release(t->vdev);
kfree(t);
return err;
}
pr_info("ov5640_probe successful.\n");
return 0;
}