]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/media/video/pxa_camera.c
V4L/DVB (10036): m5602 - ov9650: Prepare the sensor to set multiple resolutions
[linux-2.6-omap-h63xx.git] / drivers / media / video / pxa_camera.c
index cf96b2cc4f1c4b70f59ef279ad8cfee8b4535f1a..97923e1bd06d9af2b0a55e06c114f0f5faca691b 100644 (file)
@@ -629,17 +629,6 @@ static void pxa_camera_activate(struct pxa_camera_dev *pcdev)
                pdata->init(pcdev->dev);
        }
 
-       if (pdata && pdata->power) {
-               dev_dbg(pcdev->dev, "%s: Power on camera\n", __func__);
-               pdata->power(pcdev->dev, 1);
-       }
-
-       if (pdata && pdata->reset) {
-               dev_dbg(pcdev->dev, "%s: Releasing camera reset\n",
-                       __func__);
-               pdata->reset(pcdev->dev, 1);
-       }
-
        CICR0 = 0x3FF;   /* disable all interrupts */
 
        if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN)
@@ -660,20 +649,7 @@ static void pxa_camera_activate(struct pxa_camera_dev *pcdev)
 
 static void pxa_camera_deactivate(struct pxa_camera_dev *pcdev)
 {
-       struct pxacamera_platform_data *board = pcdev->pdata;
-
        clk_disable(pcdev->clk);
-
-       if (board && board->reset) {
-               dev_dbg(pcdev->dev, "%s: Asserting camera reset\n",
-                       __func__);
-               board->reset(pcdev->dev, 0);
-       }
-
-       if (board && board->power) {
-               dev_dbg(pcdev->dev, "%s: Power off camera\n", __func__);
-               board->power(pcdev->dev, 0);
-       }
 }
 
 static irqreturn_t pxa_camera_irq(int irq, void *data)
@@ -789,6 +765,9 @@ static int test_platform_param(struct pxa_camera_dev *pcdev,
                if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8))
                        return -EINVAL;
                *flags |= SOCAM_DATAWIDTH_8;
+               break;
+       default:
+               return -EINVAL;
        }
 
        return 0;
@@ -847,12 +826,10 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
         * We fix bit-per-pixel equal to data-width... */
        switch (common_flags & SOCAM_DATAWIDTH_MASK) {
        case SOCAM_DATAWIDTH_10:
-               icd->buswidth = 10;
                dw = 4;
                bpp = 0x40;
                break;
        case SOCAM_DATAWIDTH_9:
-               icd->buswidth = 9;
                dw = 3;
                bpp = 0x20;
                break;
@@ -860,7 +837,6 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
                /* Actually it can only be 8 now,
                 * default is just to silence compiler warnings */
        case SOCAM_DATAWIDTH_8:
-               icd->buswidth = 8;
                dw = 2;
                bpp = 0;
        }
@@ -886,7 +862,17 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
        case V4L2_PIX_FMT_YUV422P:
                pcdev->channels = 3;
                cicr1 |= CICR1_YCBCR_F;
+               /*
+                * Normally, pxa bus wants as input UYVY format. We allow all
+                * reorderings of the YUV422 format, as no processing is done,
+                * and the YUV stream is just passed through without any
+                * transformation. Note that UYVY is the only format that
+                * should be used if pxa framebuffer Overlay2 is used.
+                */
+       case V4L2_PIX_FMT_UYVY:
+       case V4L2_PIX_FMT_VYUY:
        case V4L2_PIX_FMT_YUYV:
+       case V4L2_PIX_FMT_YVYU:
                cicr1 |= CICR1_COLOR_SP_VAL(2);
                break;
        case V4L2_PIX_FMT_RGB555:
@@ -912,13 +898,14 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
        return 0;
 }
 
-static int pxa_camera_try_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
+static int pxa_camera_try_bus_param(struct soc_camera_device *icd,
+                                   unsigned char buswidth)
 {
        struct soc_camera_host *ici =
                to_soc_camera_host(icd->dev.parent);
        struct pxa_camera_dev *pcdev = ici->priv;
        unsigned long bus_flags, camera_flags;
-       int ret = test_platform_param(pcdev, icd->buswidth, &bus_flags);
+       int ret = test_platform_param(pcdev, buswidth, &bus_flags);
 
        if (ret < 0)
                return ret;
@@ -928,28 +915,174 @@ static int pxa_camera_try_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
        return soc_camera_bus_param_compatible(camera_flags, bus_flags) ? 0 : -EINVAL;
 }
 
-static int pxa_camera_set_fmt_cap(struct soc_camera_device *icd,
-                                 __u32 pixfmt, struct v4l2_rect *rect)
+static const struct soc_camera_data_format pxa_camera_formats[] = {
+       {
+               .name           = "Planar YUV422 16 bit",
+               .depth          = 16,
+               .fourcc         = V4L2_PIX_FMT_YUV422P,
+               .colorspace     = V4L2_COLORSPACE_JPEG,
+       },
+};
+
+static bool buswidth_supported(struct soc_camera_device *icd, int depth)
+{
+       struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+       struct pxa_camera_dev *pcdev = ici->priv;
+
+       switch (depth) {
+       case 8:
+               return !!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8);
+       case 9:
+               return !!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_9);
+       case 10:
+               return !!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_10);
+       }
+       return false;
+}
+
+static int required_buswidth(const struct soc_camera_data_format *fmt)
+{
+       switch (fmt->fourcc) {
+       case V4L2_PIX_FMT_UYVY:
+       case V4L2_PIX_FMT_VYUY:
+       case V4L2_PIX_FMT_YUYV:
+       case V4L2_PIX_FMT_YVYU:
+       case V4L2_PIX_FMT_RGB565:
+       case V4L2_PIX_FMT_RGB555:
+               return 8;
+       default:
+               return fmt->depth;
+       }
+}
+
+static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx,
+                                 struct soc_camera_format_xlate *xlate)
+{
+       struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+       int formats = 0, buswidth, ret;
+
+       buswidth = required_buswidth(icd->formats + idx);
+
+       if (!buswidth_supported(icd, buswidth))
+               return 0;
+
+       ret = pxa_camera_try_bus_param(icd, buswidth);
+       if (ret < 0)
+               return 0;
+
+       switch (icd->formats[idx].fourcc) {
+       case V4L2_PIX_FMT_UYVY:
+               formats++;
+               if (xlate) {
+                       xlate->host_fmt = &pxa_camera_formats[0];
+                       xlate->cam_fmt = icd->formats + idx;
+                       xlate->buswidth = buswidth;
+                       xlate++;
+                       dev_dbg(&ici->dev, "Providing format %s using %s\n",
+                               pxa_camera_formats[0].name,
+                               icd->formats[idx].name);
+               }
+       case V4L2_PIX_FMT_VYUY:
+       case V4L2_PIX_FMT_YUYV:
+       case V4L2_PIX_FMT_YVYU:
+       case V4L2_PIX_FMT_RGB565:
+       case V4L2_PIX_FMT_RGB555:
+               formats++;
+               if (xlate) {
+                       xlate->host_fmt = icd->formats + idx;
+                       xlate->cam_fmt = icd->formats + idx;
+                       xlate->buswidth = buswidth;
+                       xlate++;
+                       dev_dbg(&ici->dev, "Providing format %s packed\n",
+                               icd->formats[idx].name);
+               }
+               break;
+       default:
+               /* Generic pass-through */
+               formats++;
+               if (xlate) {
+                       xlate->host_fmt = icd->formats + idx;
+                       xlate->cam_fmt = icd->formats + idx;
+                       xlate->buswidth = icd->formats[idx].depth;
+                       xlate++;
+                       dev_dbg(&ici->dev,
+                               "Providing format %s in pass-through mode\n",
+                               icd->formats[idx].name);
+               }
+       }
+
+       return formats;
+}
+
+static int pxa_camera_set_fmt(struct soc_camera_device *icd,
+                             __u32 pixfmt, struct v4l2_rect *rect)
 {
-       return icd->ops->set_fmt_cap(icd, pixfmt, rect);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+       const struct soc_camera_data_format *host_fmt, *cam_fmt = NULL;
+       const struct soc_camera_format_xlate *xlate;
+       int ret, buswidth;
+
+       xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+       if (!xlate) {
+               dev_warn(&ici->dev, "Format %x not found\n", pixfmt);
+               return -EINVAL;
+       }
+
+       buswidth = xlate->buswidth;
+       host_fmt = xlate->host_fmt;
+       cam_fmt = xlate->cam_fmt;
+
+       switch (pixfmt) {
+       case 0:                         /* Only geometry change */
+               ret = icd->ops->set_fmt(icd, pixfmt, rect);
+               break;
+       default:
+               ret = icd->ops->set_fmt(icd, cam_fmt->fourcc, rect);
+       }
+
+       if (ret < 0)
+               dev_warn(&ici->dev, "Failed to configure for format %x\n",
+                        pixfmt);
+
+       if (pixfmt && !ret) {
+               icd->buswidth = buswidth;
+               icd->current_fmt = host_fmt;
+       }
+
+       return ret;
 }
 
-static int pxa_camera_try_fmt_cap(struct soc_camera_device *icd,
-                                 struct v4l2_format *f)
+static int pxa_camera_try_fmt(struct soc_camera_device *icd,
+                             struct v4l2_format *f)
 {
+       struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+       const struct soc_camera_format_xlate *xlate;
+       struct v4l2_pix_format *pix = &f->fmt.pix;
+       __u32 pixfmt = pix->pixelformat;
+
+       xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+       if (!xlate) {
+               dev_warn(&ici->dev, "Format %x not found\n", pixfmt);
+               return -EINVAL;
+       }
+
        /* limit to pxa hardware capabilities */
-       if (f->fmt.pix.height < 32)
-               f->fmt.pix.height = 32;
-       if (f->fmt.pix.height > 2048)
-               f->fmt.pix.height = 2048;
-       if (f->fmt.pix.width < 48)
-               f->fmt.pix.width = 48;
-       if (f->fmt.pix.width > 2048)
-               f->fmt.pix.width = 2048;
-       f->fmt.pix.width &= ~0x01;
+       if (pix->height < 32)
+               pix->height = 32;
+       if (pix->height > 2048)
+               pix->height = 2048;
+       if (pix->width < 48)
+               pix->width = 48;
+       if (pix->width > 2048)
+               pix->width = 2048;
+       pix->width &= ~0x01;
+
+       pix->bytesperline = pix->width *
+               DIV_ROUND_UP(xlate->host_fmt->depth, 8);
+       pix->sizeimage = pix->height * pix->bytesperline;
 
        /* limit to sensor capabilities */
-       return icd->ops->try_fmt_cap(icd, f);
+       return icd->ops->try_fmt(icd, f);
 }
 
 static int pxa_camera_reqbufs(struct soc_camera_file *icf,
@@ -1057,13 +1190,13 @@ static struct soc_camera_host_ops pxa_soc_camera_host_ops = {
        .remove         = pxa_camera_remove_device,
        .suspend        = pxa_camera_suspend,
        .resume         = pxa_camera_resume,
-       .set_fmt_cap    = pxa_camera_set_fmt_cap,
-       .try_fmt_cap    = pxa_camera_try_fmt_cap,
+       .get_formats    = pxa_camera_get_formats,
+       .set_fmt        = pxa_camera_set_fmt,
+       .try_fmt        = pxa_camera_try_fmt,
        .init_videobuf  = pxa_camera_init_videobuf,
        .reqbufs        = pxa_camera_reqbufs,
        .poll           = pxa_camera_poll,
        .querycap       = pxa_camera_querycap,
-       .try_bus_param  = pxa_camera_try_bus_param,
        .set_bus_param  = pxa_camera_set_bus_param,
 };
 
@@ -1144,31 +1277,31 @@ static int pxa_camera_probe(struct platform_device *pdev)
        pcdev->dev = &pdev->dev;
 
        /* request dma */
-       pcdev->dma_chans[0] = pxa_request_dma("CI_Y", DMA_PRIO_HIGH,
-                                             pxa_camera_dma_irq_y, pcdev);
-       if (pcdev->dma_chans[0] < 0) {
+       err = pxa_request_dma("CI_Y", DMA_PRIO_HIGH,
+                             pxa_camera_dma_irq_y, pcdev);
+       if (err < 0) {
                dev_err(pcdev->dev, "Can't request DMA for Y\n");
-               err = -ENOMEM;
                goto exit_iounmap;
        }
+       pcdev->dma_chans[0] = err;
        dev_dbg(pcdev->dev, "got DMA channel %d\n", pcdev->dma_chans[0]);
 
-       pcdev->dma_chans[1] = pxa_request_dma("CI_U", DMA_PRIO_HIGH,
-                                             pxa_camera_dma_irq_u, pcdev);
-       if (pcdev->dma_chans[1] < 0) {
+       err = pxa_request_dma("CI_U", DMA_PRIO_HIGH,
+                             pxa_camera_dma_irq_u, pcdev);
+       if (err < 0) {
                dev_err(pcdev->dev, "Can't request DMA for U\n");
-               err = -ENOMEM;
                goto exit_free_dma_y;
        }
+       pcdev->dma_chans[1] = err;
        dev_dbg(pcdev->dev, "got DMA channel (U) %d\n", pcdev->dma_chans[1]);
 
-       pcdev->dma_chans[2] = pxa_request_dma("CI_V", DMA_PRIO_HIGH,
-                                             pxa_camera_dma_irq_v, pcdev);
-       if (pcdev->dma_chans[0] < 0) {
+       err = pxa_request_dma("CI_V", DMA_PRIO_HIGH,
+                             pxa_camera_dma_irq_v, pcdev);
+       if (err < 0) {
                dev_err(pcdev->dev, "Can't request DMA for V\n");
-               err = -ENOMEM;
                goto exit_free_dma_u;
        }
+       pcdev->dma_chans[2] = err;
        dev_dbg(pcdev->dev, "got DMA channel (V) %d\n", pcdev->dma_chans[2]);
 
        DRCMR(68) = pcdev->dma_chans[0] | DRCMR_MAPVLD;