]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ENGR00219173 V42L overlay Add back ground overlay support
authorYuxi Sun <b36102@freescale.com>
Fri, 10 Aug 2012 05:52:32 +0000 (13:52 +0800)
committerOliver Wendt <ow@karo-electronics.de>
Mon, 30 Sep 2013 12:12:41 +0000 (14:12 +0200)
Add back ground overlay support based on ipu device driver.
Default using this driver instead of prp_vf driver for back
ground overlay. when want using prp_vf back ground overlay,
unselect ipu deviece overlay drivers and choose to build
related driver as build-in.

Signed-off-by: Yuxi Sun <b36102@freescale.com>
drivers/media/video/mxc/capture/Makefile
drivers/media/video/mxc/capture/ipu_bg_overlay_sdc.c [new file with mode: 0644]
drivers/media/video/mxc/capture/ipu_prp_sw.h
drivers/media/video/mxc/capture/mxc_v4l2_capture.c

index 04b715352886ba28dca1cf01a9028c78a2bc7c22..bdef5b401485596d5f44b1307516c0efd5af3fbf 100644 (file)
@@ -1,7 +1,7 @@
 ifeq ($(CONFIG_VIDEO_MXC_IPU_CAMERA),y)
        obj-$(CONFIG_VIDEO_MXC_CAMERA) += mxc_v4l2_capture.o
        obj-$(CONFIG_MXC_IPU_PRP_VF_SDC) += ipu_prp_vf_sdc.o ipu_prp_vf_sdc_bg.o
-       obj-$(CONFIG_MXC_IPU_DEVICE_QUEUE_SDC) += ipu_fg_overlay_sdc.o ipu_prp_vf_sdc_bg.o
+       obj-$(CONFIG_MXC_IPU_DEVICE_QUEUE_SDC) += ipu_fg_overlay_sdc.o ipu_bg_overlay_sdc.o
        obj-$(CONFIG_MXC_IPU_PRP_ENC) += ipu_prp_enc.o ipu_still.o
        obj-$(CONFIG_MXC_IPU_CSI_ENC) += ipu_csi_enc.o ipu_still.o
 endif
diff --git a/drivers/media/video/mxc/capture/ipu_bg_overlay_sdc.c b/drivers/media/video/mxc/capture/ipu_bg_overlay_sdc.c
new file mode 100644 (file)
index 0000000..f22d04a
--- /dev/null
@@ -0,0 +1,549 @@
+
+/*
+ * Copyright 2004-2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file ipu_bg_overlay_sdc_bg.c
+ *
+ * @brief IPU Use case for PRP-VF back-ground
+ *
+ * @ingroup IPU
+ */
+#include <linux/dma-mapping.h>
+#include <linux/fb.h>
+#include <linux/ipu.h>
+#include <mach/mipi_csi2.h>
+#include "mxc_v4l2_capture.h"
+#include "ipu_prp_sw.h"
+
+static int csi_buffer_num;
+static u32 bpp, csi_mem_bufsize = 3;
+static u32 out_format;
+static struct ipu_soc *disp_ipu;
+static u32 offset;
+
+static void csi_buf_work_func(struct work_struct *work)
+{
+       int err = 0;
+       cam_data *cam =
+               container_of(work, struct _cam_data, csi_work_struct);
+
+       struct ipu_task task;
+       memset(&task, 0, sizeof(task));
+
+       if (csi_buffer_num)
+               task.input.paddr = cam->vf_bufs[0];
+       else
+               task.input.paddr = cam->vf_bufs[1];
+       task.input.width = cam->crop_current.width;
+       task.input.height = cam->crop_current.height;
+       task.input.format = IPU_PIX_FMT_UYVY;
+
+       task.output.paddr = offset;
+       task.output.width = cam->overlay_fb->var.xres;
+       task.output.height = cam->overlay_fb->var.yres;
+       task.output.format = out_format;
+       task.output.rotate = cam->rotation;
+       task.output.crop.pos.x = cam->win.w.left;
+       task.output.crop.pos.y = cam->win.w.top;
+       if (cam->win.w.width > 1024 || cam->win.w.height > 1024) {
+               task.output.crop.w = cam->overlay_fb->var.xres;
+               task.output.crop.h = cam->overlay_fb->var.yres;
+       } else {
+               task.output.crop.w = cam->win.w.width;
+               task.output.crop.h = cam->win.w.height;
+       }
+again:
+       err = ipu_check_task(&task);
+       if (err != IPU_CHECK_OK) {
+               if (err > IPU_CHECK_ERR_MIN) {
+                       if (err == IPU_CHECK_ERR_SPLIT_INPUTW_OVER) {
+                               task.input.crop.w -= 8;
+                               goto again;
+                       }
+                       if (err == IPU_CHECK_ERR_SPLIT_INPUTH_OVER) {
+                               task.input.crop.h -= 8;
+                               goto again;
+                       }
+                       if (err == IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER) {
+                                       task.output.width -= 8;
+                                       task.output.crop.w = task.output.width;
+                               goto again;
+                       }
+                       if (err == IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER) {
+                                       task.output.height -= 8;
+                                       task.output.crop.h = task.output.height;
+                               goto again;
+                       }
+                       printk(KERN_ERR "check ipu taks fail\n");
+                       return;
+               }
+               printk(KERN_ERR "check ipu taks fail\n");
+               return;
+       }
+       err = ipu_queue_task(&task);
+       if (err < 0)
+               printk(KERN_ERR "queue ipu task error\n");
+}
+
+static void get_disp_ipu(cam_data *cam)
+{
+       if (cam->output > 2)
+               disp_ipu = ipu_get_soc(1); /* using DISP4 */
+       else
+               disp_ipu = ipu_get_soc(0);
+}
+
+
+/*!
+ * csi ENC callback function.
+ *
+ * @param irq       int irq line
+ * @param dev_id    void * device id
+ *
+ * @return status   IRQ_HANDLED for handled
+ */
+static irqreturn_t csi_enc_callback(int irq, void *dev_id)
+{
+       cam_data *cam = (cam_data *) dev_id;
+
+       ipu_select_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, csi_buffer_num);
+       schedule_work(&cam->csi_work_struct);
+       csi_buffer_num = (csi_buffer_num == 0) ? 1 : 0;
+       return IRQ_HANDLED;
+}
+
+static int csi_enc_setup(cam_data *cam)
+{
+       ipu_channel_params_t params;
+       u32 pixel_fmt;
+       int err = 0, sensor_protocol = 0;
+#ifdef CONFIG_MXC_MIPI_CSI2
+       void *mipi_csi2_info;
+       int ipu_id;
+       int csi_id;
+#endif
+
+       if (!cam) {
+               printk(KERN_ERR "cam private is NULL\n");
+               return -ENXIO;
+       }
+
+       memset(&params, 0, sizeof(ipu_channel_params_t));
+       params.csi_mem.csi = cam->csi;
+
+       sensor_protocol = ipu_csi_get_sensor_protocol(cam->ipu, cam->csi);
+       switch (sensor_protocol) {
+       case IPU_CSI_CLK_MODE_GATED_CLK:
+       case IPU_CSI_CLK_MODE_NONGATED_CLK:
+       case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE:
+       case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR:
+       case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR:
+               params.csi_mem.interlaced = false;
+               break;
+       case IPU_CSI_CLK_MODE_CCIR656_INTERLACED:
+       case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR:
+       case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR:
+               params.csi_mem.interlaced = true;
+               break;
+       default:
+               printk(KERN_ERR "sensor protocol unsupported\n");
+               return -EINVAL;
+       }
+
+       ipu_csi_enable_mclk_if(cam->ipu, CSI_MCLK_ENC, cam->csi, true, true);
+
+#ifdef CONFIG_MXC_MIPI_CSI2
+       mipi_csi2_info = mipi_csi2_get_info();
+
+       if (mipi_csi2_info) {
+               if (mipi_csi2_get_status(mipi_csi2_info)) {
+                       ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
+                       csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
+
+                       if (cam->ipu == ipu_get_soc(ipu_id)
+                               && cam->csi == csi_id) {
+                               params.csi_mem.mipi_en = true;
+                               params.csi_mem.mipi_vc =
+                               mipi_csi2_get_virtual_channel(mipi_csi2_info);
+                               params.csi_mem.mipi_id =
+                               mipi_csi2_get_datatype(mipi_csi2_info);
+
+                               mipi_csi2_pixelclk_enable(mipi_csi2_info);
+                       } else {
+                               params.csi_mem.mipi_en = false;
+                               params.csi_mem.mipi_vc = 0;
+                               params.csi_mem.mipi_id = 0;
+                       }
+               } else {
+                       params.csi_mem.mipi_en = false;
+                       params.csi_mem.mipi_vc = 0;
+                       params.csi_mem.mipi_id = 0;
+               }
+       } else {
+               printk(KERN_ERR "Fail to get mipi_csi2_info!\n");
+               return -EPERM;
+       }
+#endif
+
+       if (cam->vf_bufs_vaddr[0]) {
+               dma_free_coherent(0, cam->vf_bufs_size[0],
+                                 cam->vf_bufs_vaddr[0],
+                                 (dma_addr_t) cam->vf_bufs[0]);
+       }
+       if (cam->vf_bufs_vaddr[1]) {
+               dma_free_coherent(0, cam->vf_bufs_size[1],
+                                 cam->vf_bufs_vaddr[1],
+                                 (dma_addr_t) cam->vf_bufs[1]);
+       }
+       csi_mem_bufsize = cam->crop_current.width * cam->crop_current.height * 2;
+       cam->vf_bufs_size[0] = PAGE_ALIGN(csi_mem_bufsize);
+       cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0,
+                                                          cam->vf_bufs_size[0],
+                                                          (dma_addr_t *) &
+                                                          cam->vf_bufs[0],
+                                                          GFP_DMA |
+                                                          GFP_KERNEL);
+       if (cam->vf_bufs_vaddr[0] == NULL) {
+               printk(KERN_ERR "Error to allocate vf buffer\n");
+               err = -ENOMEM;
+               goto out_2;
+       }
+       cam->vf_bufs_size[1] = PAGE_ALIGN(csi_mem_bufsize);
+       cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0,
+                                                          cam->vf_bufs_size[1],
+                                                          (dma_addr_t *) &
+                                                          cam->vf_bufs[1],
+                                                          GFP_DMA |
+                                                          GFP_KERNEL);
+       if (cam->vf_bufs_vaddr[1] == NULL) {
+               printk(KERN_ERR "Error to allocate vf buffer\n");
+               err = -ENOMEM;
+               goto out_1;
+       }
+       pr_debug("vf_bufs %x %x\n", cam->vf_bufs[0], cam->vf_bufs[1]);
+
+       err = ipu_init_channel(cam->ipu, CSI_MEM, &params);
+       if (err != 0) {
+               printk(KERN_ERR "ipu_init_channel %d\n", err);
+               goto out_1;
+       }
+
+       pixel_fmt = IPU_PIX_FMT_UYVY;
+       err = ipu_init_channel_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER,
+                                     pixel_fmt, cam->crop_current.width,
+                                     cam->crop_current.height,
+                                     cam->crop_current.width, IPU_ROTATE_NONE,
+                                     cam->vf_bufs[0], cam->vf_bufs[1], 0,
+                                     cam->offset.u_offset, cam->offset.u_offset);
+       if (err != 0) {
+               printk(KERN_ERR "CSI_MEM output buffer\n");
+               goto out_1;
+       }
+       err = ipu_enable_channel(cam->ipu, CSI_MEM);
+       if (err < 0) {
+               printk(KERN_ERR "ipu_enable_channel CSI_MEM\n");
+               goto out_1;
+       }
+
+       csi_buffer_num = 0;
+
+       ipu_select_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, 0);
+       ipu_select_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, 1);
+       return err;
+out_1:
+       if (cam->vf_bufs_vaddr[0]) {
+               dma_free_coherent(0, cam->vf_bufs_size[0],
+                                 cam->vf_bufs_vaddr[0],
+                                 (dma_addr_t) cam->vf_bufs[0]);
+               cam->vf_bufs_vaddr[0] = NULL;
+               cam->vf_bufs[0] = 0;
+       }
+       if (cam->vf_bufs_vaddr[1]) {
+               dma_free_coherent(0, cam->vf_bufs_size[1],
+                                 cam->vf_bufs_vaddr[1],
+                                 (dma_addr_t) cam->vf_bufs[1]);
+               cam->vf_bufs_vaddr[1] = NULL;
+               cam->vf_bufs[1] = 0;
+       }
+out_2:
+       return err;
+}
+
+/*!
+ * Enable encoder task
+ * @param private       struct cam_data * mxc capture instance
+ *
+ * @return  status
+ */
+static int csi_enc_enabling_tasks(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+       int err = 0;
+
+       ipu_clear_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF);
+       err = ipu_request_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF,
+                             csi_enc_callback, 0, "Mxc Camera", cam);
+       if (err != 0) {
+               printk(KERN_ERR "Error registering CSI0_OUT_EOF irq\n");
+               return err;
+       }
+
+       INIT_WORK(&cam->csi_work_struct, csi_buf_work_func);
+
+       err = csi_enc_setup(cam);
+       if (err != 0) {
+               printk(KERN_ERR "csi_enc_setup %d\n", err);
+               goto out1;
+       }
+
+       return err;
+out1:
+       ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, cam);
+       return err;
+}
+
+/*!
+ * bg_overlay_start - start the overlay task
+ *
+ * @param private    cam_data * mxc v4l2 main structure
+ *
+ */
+static int bg_overlay_start(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+       int err = 0;
+
+       if (!cam) {
+               printk(KERN_ERR "private is NULL\n");
+               return -EIO;
+       }
+
+       if (cam->overlay_active == true) {
+               pr_debug("already start.\n");
+               return 0;
+       }
+
+       get_disp_ipu(cam);
+
+       out_format = cam->v4l2_fb.fmt.pixelformat;
+       if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR24) {
+               bpp = 3, csi_mem_bufsize = 3;
+               pr_info("BGR24\n");
+       } else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_RGB565) {
+               bpp = 2, csi_mem_bufsize = 2;
+               pr_info("RGB565\n");
+       } else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR32) {
+               bpp = 4, csi_mem_bufsize = 4;
+               pr_info("BGR32\n");
+       } else {
+               printk(KERN_ERR
+                      "unsupported fix format from the framebuffer.\n");
+               return -EINVAL;
+       }
+
+       offset = cam->v4l2_fb.fmt.bytesperline * cam->win.w.top +
+           csi_mem_bufsize * cam->win.w.left;
+
+       if (cam->v4l2_fb.base == 0) {
+               printk(KERN_ERR "invalid frame buffer address.\n");
+       } else {
+               offset += (u32) cam->v4l2_fb.base;
+       }
+
+       csi_mem_bufsize = cam->win.w.width * cam->win.w.height * csi_mem_bufsize;
+
+       err = csi_enc_enabling_tasks(cam);
+       if (err != 0) {
+               printk(KERN_ERR "Error csi enc enable fail\n");
+               return err;
+       }
+
+       cam->overlay_active = true;
+       return err;
+}
+
+/*!
+ * bg_overlay_stop - stop the overlay task
+ *
+ * @param private    cam_data * mxc v4l2 main structure
+ *
+ */
+static int bg_overlay_stop(void *private)
+{
+       int err = 0;
+       cam_data *cam = (cam_data *) private;
+#ifdef CONFIG_MXC_MIPI_CSI2
+       void *mipi_csi2_info;
+       int ipu_id;
+       int csi_id;
+#endif
+
+       if (cam->overlay_active == false)
+               return 0;
+
+       ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, cam);
+
+       err = ipu_disable_channel(cam->ipu, CSI_MEM, true);
+
+       ipu_uninit_channel(cam->ipu, CSI_MEM);
+
+       csi_buffer_num = 0;
+
+#ifdef CONFIG_MXC_MIPI_CSI2
+       mipi_csi2_info = mipi_csi2_get_info();
+
+       if (mipi_csi2_info) {
+               if (mipi_csi2_get_status(mipi_csi2_info)) {
+                       ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
+                       csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
+
+                       if (cam->ipu == ipu_get_soc(ipu_id)
+                               && cam->csi == csi_id)
+                               mipi_csi2_pixelclk_disable(mipi_csi2_info);
+               }
+       } else {
+               printk(KERN_ERR "Fail to get mipi_csi2_info!\n");
+               return -EPERM;
+       }
+#endif
+
+       ipu_csi_enable_mclk_if(cam->ipu, CSI_MCLK_VF, cam->csi, false, false);
+
+       if (cam->vf_bufs_vaddr[0]) {
+               dma_free_coherent(0, cam->vf_bufs_size[0],
+                                 cam->vf_bufs_vaddr[0], cam->vf_bufs[0]);
+               cam->vf_bufs_vaddr[0] = NULL;
+               cam->vf_bufs[0] = 0;
+       }
+       if (cam->vf_bufs_vaddr[1]) {
+               dma_free_coherent(0, cam->vf_bufs_size[1],
+                                 cam->vf_bufs_vaddr[1], cam->vf_bufs[1]);
+               cam->vf_bufs_vaddr[1] = NULL;
+               cam->vf_bufs[1] = 0;
+       }
+       if (cam->rot_vf_bufs_vaddr[0]) {
+               dma_free_coherent(0, cam->rot_vf_buf_size[0],
+                                 cam->rot_vf_bufs_vaddr[0],
+                                 cam->rot_vf_bufs[0]);
+               cam->rot_vf_bufs_vaddr[0] = NULL;
+               cam->rot_vf_bufs[0] = 0;
+       }
+       if (cam->rot_vf_bufs_vaddr[1]) {
+               dma_free_coherent(0, cam->rot_vf_buf_size[1],
+                                 cam->rot_vf_bufs_vaddr[1],
+                                 cam->rot_vf_bufs[1]);
+               cam->rot_vf_bufs_vaddr[1] = NULL;
+               cam->rot_vf_bufs[1] = 0;
+       }
+
+       cam->overlay_active = false;
+       return err;
+}
+
+/*!
+ * Enable csi
+ * @param private       struct cam_data * mxc capture instance
+ *
+ * @return  status
+ */
+static int bg_overlay_enable_csi(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+
+       return ipu_enable_csi(cam->ipu, cam->csi);
+}
+
+/*!
+ * Disable csi
+ * @param private       struct cam_data * mxc capture instance
+ *
+ * @return  status
+ */
+static int bg_overlay_disable_csi(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+
+       return ipu_disable_csi(cam->ipu, cam->csi);
+}
+
+/*!
+ * function to select bg as the working path
+ *
+ * @param private    cam_data * mxc v4l2 main structure
+ *
+ * @return  status
+ */
+int bg_overlay_sdc_select(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+
+       if (cam) {
+               cam->vf_start_sdc = bg_overlay_start;
+               cam->vf_stop_sdc = bg_overlay_stop;
+               cam->vf_enable_csi = bg_overlay_enable_csi;
+               cam->vf_disable_csi = bg_overlay_disable_csi;
+               cam->overlay_active = false;
+       }
+
+       return 0;
+}
+
+/*!
+ * function to de-select bg as the working path
+ *
+ * @param private    cam_data * mxc v4l2 main structure
+ *
+ * @return  status
+ */
+int bg_overlay_sdc_deselect(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+
+       if (cam) {
+               cam->vf_start_sdc = NULL;
+               cam->vf_stop_sdc = NULL;
+               cam->vf_enable_csi = NULL;
+               cam->vf_disable_csi = NULL;
+       }
+       return 0;
+}
+
+/*!
+ * Init background overlay task.
+ *
+ * @return  Error code indicating success or failure
+ */
+__init int bg_overlay_sdc_init(void)
+{
+       return 0;
+}
+
+/*!
+ * Deinit background overlay task.
+ *
+ * @return  Error code indicating success or failure
+ */
+void __exit bg_overlay_sdc_exit(void)
+{
+}
+
+module_init(bg_overlay_sdc_init);
+module_exit(bg_overlay_sdc_exit);
+
+EXPORT_SYMBOL(bg_overlay_sdc_select);
+EXPORT_SYMBOL(bg_overlay_sdc_deselect);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("IPU PRP VF SDC Backgroud Driver");
+MODULE_LICENSE("GPL");
index 7cdc521711cb6ad76edca94ea215c458a7c5f66b..cba47baa4abe42cbaf48b349bfe288c311f9043d 100644 (file)
@@ -29,12 +29,14 @@ int prp_enc_deselect(void *private);
 #ifdef CONFIG_MXC_IPU_PRP_VF_SDC
 int prp_vf_sdc_select(void *private);
 int prp_vf_sdc_deselect(void *private);
+int prp_vf_sdc_select_bg(void *private);
+int prp_vf_sdc_deselect_bg(void *private);
 #else
 int foreground_sdc_select(void *private);
 int foreground_sdc_deselect(void *private);
+int bg_overlay_sdc_select(void *private);
+int bg_overlay_sdc_deselect(void *private);
 #endif
-int prp_vf_sdc_select_bg(void *private);
-int prp_vf_sdc_deselect_bg(void *private);
 int prp_still_select(void *private);
 int prp_still_deselect(void *private);
 
index 44818bce574039c05244f1c2c20cddde14b4f4d7..880f97b01b84aa057824abb54e0069c62368c00f 100644 (file)
@@ -655,7 +655,11 @@ static int start_preview(cam_data *cam)
                err = foreground_sdc_select(cam);
        #endif
        else if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_PRIMARY)
+       #ifdef CONFIG_MXC_IPU_PRP_VF_SDC
                err = prp_vf_sdc_select_bg(cam);
+       #else
+               err = bg_overlay_sdc_select(cam);
+       #endif
        if (err != 0)
                return err;
 
@@ -714,7 +718,11 @@ static int stop_preview(cam_data *cam)
                err = foreground_sdc_deselect(cam);
        #endif
        else if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_PRIMARY)
+       #ifdef CONFIG_MXC_IPU_PRP_VF_SDC
                err = prp_vf_sdc_deselect_bg(cam);
+       #else
+               err = bg_overlay_sdc_deselect(cam);
+       #endif
 
        return err;
 }