const: constify remaining dev_pm_ops
[safe/jmp/linux-2.6] / drivers / media / video / pxa_camera.c
index 1fd6ef3..51b683c 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/device.h>
 #include <linux/platform_device.h>
 #include <linux/clk.h>
+#include <linux/sched.h>
 
 #include <media/v4l2-common.h>
 #include <media/v4l2-dev.h>
@@ -225,6 +226,10 @@ struct pxa_camera_dev {
        u32                     save_cicr[5];
 };
 
+struct pxa_cam {
+       unsigned long flags;
+};
+
 static const char *pxa_cam_driver_description = "PXA_Camera";
 
 static unsigned int vid_limit = 16;    /* Video memory limit, in Mb */
@@ -239,7 +244,7 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
 
        dev_dbg(icd->dev.parent, "count=%d, size=%d\n", *count, *size);
 
-       *size = roundup(icd->rect_current.width * icd->rect_current.height *
+       *size = roundup(icd->user_width * icd->user_height *
                        ((icd->current_fmt->depth + 7) >> 3), 8);
 
        if (0 == *count)
@@ -270,7 +275,8 @@ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf)
 
        for (i = 0; i < ARRAY_SIZE(buf->dmas); i++) {
                if (buf->dmas[i].sg_cpu)
-                       dma_free_coherent(ici->v4l2_dev.dev, buf->dmas[i].sg_size,
+                       dma_free_coherent(ici->v4l2_dev.dev,
+                                         buf->dmas[i].sg_size,
                                          buf->dmas[i].sg_cpu,
                                          buf->dmas[i].sg_dma);
                buf->dmas[i].sg_cpu = NULL;
@@ -443,12 +449,12 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq,
        buf->inwork = 1;
 
        if (buf->fmt    != icd->current_fmt ||
-           vb->width   != icd->rect_current.width ||
-           vb->height  != icd->rect_current.height ||
+           vb->width   != icd->user_width ||
+           vb->height  != icd->user_height ||
            vb->field   != field) {
                buf->fmt        = icd->current_fmt;
-               vb->width       = icd->rect_current.width;
-               vb->height      = icd->rect_current.height;
+               vb->width       = icd->user_width;
+               vb->height      = icd->user_height;
                vb->field       = field;
                vb->state       = VIDEOBUF_NEEDS_INIT;
        }
@@ -839,7 +845,7 @@ static u32 mclk_get_divisor(struct platform_device *pdev,
                            struct pxa_camera_dev *pcdev)
 {
        unsigned long mclk = pcdev->mclk;
-       struct device *dev = pcdev->soc_host.v4l2_dev.dev;
+       struct device *dev = &pdev->dev;
        u32 div;
        unsigned long lcdclk;
 
@@ -1040,57 +1046,17 @@ static int test_platform_param(struct pxa_camera_dev *pcdev,
        return 0;
 }
 
-static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
+static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
+                                 unsigned long flags, __u32 pixfmt)
 {
        struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
        struct pxa_camera_dev *pcdev = ici->priv;
-       unsigned long dw, bpp, bus_flags, camera_flags, common_flags;
+       unsigned long dw, bpp;
        u32 cicr0, cicr1, cicr2, cicr3, cicr4 = 0;
-       int ret = test_platform_param(pcdev, icd->buswidth, &bus_flags);
-
-       if (ret < 0)
-               return ret;
-
-       camera_flags = icd->ops->query_bus_param(icd);
-
-       common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags);
-       if (!common_flags)
-               return -EINVAL;
-
-       pcdev->channels = 1;
-
-       /* Make choises, based on platform preferences */
-       if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
-           (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
-               if (pcdev->platform_flags & PXA_CAMERA_HSP)
-                       common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
-               else
-                       common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
-       }
-
-       if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
-           (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
-               if (pcdev->platform_flags & PXA_CAMERA_VSP)
-                       common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
-               else
-                       common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
-       }
-
-       if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
-           (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
-               if (pcdev->platform_flags & PXA_CAMERA_PCP)
-                       common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
-               else
-                       common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
-       }
-
-       ret = icd->ops->set_bus_param(icd, common_flags);
-       if (ret < 0)
-               return ret;
 
        /* Datawidth is now guaranteed to be equal to one of the three values.
         * We fix bit-per-pixel equal to data-width... */
-       switch (common_flags & SOCAM_DATAWIDTH_MASK) {
+       switch (flags & SOCAM_DATAWIDTH_MASK) {
        case SOCAM_DATAWIDTH_10:
                dw = 4;
                bpp = 0x40;
@@ -1111,18 +1077,18 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
                cicr4 |= CICR4_PCLK_EN;
        if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN)
                cicr4 |= CICR4_MCLK_EN;
-       if (common_flags & SOCAM_PCLK_SAMPLE_FALLING)
+       if (flags & SOCAM_PCLK_SAMPLE_FALLING)
                cicr4 |= CICR4_PCP;
-       if (common_flags & SOCAM_HSYNC_ACTIVE_LOW)
+       if (flags & SOCAM_HSYNC_ACTIVE_LOW)
                cicr4 |= CICR4_HSP;
-       if (common_flags & SOCAM_VSYNC_ACTIVE_LOW)
+       if (flags & SOCAM_VSYNC_ACTIVE_LOW)
                cicr4 |= CICR4_VSP;
 
        cicr0 = __raw_readl(pcdev->base + CICR0);
        if (cicr0 & CICR0_ENB)
                __raw_writel(cicr0 & ~CICR0_ENB, pcdev->base + CICR0);
 
-       cicr1 = CICR1_PPL_VAL(icd->rect_current.width - 1) | bpp | dw;
+       cicr1 = CICR1_PPL_VAL(icd->user_width - 1) | bpp | dw;
 
        switch (pixfmt) {
        case V4L2_PIX_FMT_YUV422P:
@@ -1151,7 +1117,7 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
        }
 
        cicr2 = 0;
-       cicr3 = CICR3_LPF_VAL(icd->rect_current.height - 1) |
+       cicr3 = CICR3_LPF_VAL(icd->user_height - 1) |
                CICR3_BFW_VAL(min((unsigned short)255, icd->y_skip_top));
        cicr4 |= pcdev->mclk_divisor;
 
@@ -1165,6 +1131,59 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
                CICR0_SIM_MP : (CICR0_SL_CAP_EN | CICR0_SIM_SP));
        cicr0 |= CICR0_DMAEN | CICR0_IRQ_MASK;
        __raw_writel(cicr0, pcdev->base + CICR0);
+}
+
+static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
+{
+       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, common_flags;
+       int ret = test_platform_param(pcdev, icd->buswidth, &bus_flags);
+       struct pxa_cam *cam = icd->host_priv;
+
+       if (ret < 0)
+               return ret;
+
+       camera_flags = icd->ops->query_bus_param(icd);
+
+       common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags);
+       if (!common_flags)
+               return -EINVAL;
+
+       pcdev->channels = 1;
+
+       /* Make choises, based on platform preferences */
+       if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
+           (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
+               if (pcdev->platform_flags & PXA_CAMERA_HSP)
+                       common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
+               else
+                       common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
+       }
+
+       if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
+           (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
+               if (pcdev->platform_flags & PXA_CAMERA_VSP)
+                       common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
+               else
+                       common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
+       }
+
+       if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
+           (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
+               if (pcdev->platform_flags & PXA_CAMERA_PCP)
+                       common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
+               else
+                       common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
+       }
+
+       cam->flags = common_flags;
+
+       ret = icd->ops->set_bus_param(icd, common_flags);
+       if (ret < 0)
+               return ret;
+
+       pxa_camera_setup_cicr(icd, common_flags, pixfmt);
 
        return 0;
 }
@@ -1230,6 +1249,7 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx,
 {
        struct device *dev = icd->dev.parent;
        int formats = 0, buswidth, ret;
+       struct pxa_cam *cam;
 
        buswidth = required_buswidth(icd->formats + idx);
 
@@ -1240,6 +1260,16 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx,
        if (ret < 0)
                return 0;
 
+       if (!icd->host_priv) {
+               cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+               if (!cam)
+                       return -ENOMEM;
+
+               icd->host_priv = cam;
+       } else {
+               cam = icd->host_priv;
+       }
+
        switch (icd->formats[idx].fourcc) {
        case V4L2_PIX_FMT_UYVY:
                formats++;
@@ -1284,6 +1314,19 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx,
        return formats;
 }
 
+static void pxa_camera_put_formats(struct soc_camera_device *icd)
+{
+       kfree(icd->host_priv);
+       icd->host_priv = NULL;
+}
+
+static int pxa_camera_check_frame(struct v4l2_pix_format *pix)
+{
+       /* limit to pxa hardware capabilities */
+       return pix->height < 32 || pix->height > 2048 || pix->width < 48 ||
+               pix->width > 2048 || (pix->width & 0x01);
+}
+
 static int pxa_camera_set_crop(struct soc_camera_device *icd,
                               struct v4l2_crop *a)
 {
@@ -1296,6 +1339,9 @@ static int pxa_camera_set_crop(struct soc_camera_device *icd,
                .master_clock = pcdev->mclk,
                .pixel_clock_max = pcdev->ciclk / 4,
        };
+       struct v4l2_format f;
+       struct v4l2_pix_format *pix = &f.fmt.pix, pix_tmp;
+       struct pxa_cam *cam = icd->host_priv;
        int ret;
 
        /* If PCLK is used to latch data from the sensor, check sense */
@@ -1309,7 +1355,37 @@ static int pxa_camera_set_crop(struct soc_camera_device *icd,
        if (ret < 0) {
                dev_warn(dev, "Failed to crop to %ux%u@%u:%u\n",
                         rect->width, rect->height, rect->left, rect->top);
-       } else if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) {
+               return ret;
+       }
+
+       f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+       ret = v4l2_subdev_call(sd, video, g_fmt, &f);
+       if (ret < 0)
+               return ret;
+
+       pix_tmp = *pix;
+       if (pxa_camera_check_frame(pix)) {
+               /*
+                * Camera cropping produced a frame beyond our capabilities.
+                * FIXME: just extract a subframe, that we can process.
+                */
+               v4l_bound_align_image(&pix->width, 48, 2048, 1,
+                       &pix->height, 32, 2048, 0,
+                       icd->current_fmt->fourcc == V4L2_PIX_FMT_YUV422P ?
+                               4 : 0);
+               ret = v4l2_subdev_call(sd, video, s_fmt, &f);
+               if (ret < 0)
+                       return ret;
+
+               if (pxa_camera_check_frame(pix)) {
+                       dev_warn(icd->dev.parent,
+                                "Inconsistent state. Use S_FMT to repair\n");
+                       return -EINVAL;
+               }
+       }
+
+       if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) {
                if (sense.pixel_clock > sense.pixel_clock_max) {
                        dev_err(dev,
                                "pixel clock %lu set by the camera too high!",
@@ -1319,6 +1395,11 @@ static int pxa_camera_set_crop(struct soc_camera_device *icd,
                recalculate_fifo_timeout(pcdev, sense.pixel_clock);
        }
 
+       icd->user_width = pix->width;
+       icd->user_height = pix->height;
+
+       pxa_camera_setup_cicr(icd, cam->flags, icd->current_fmt->fourcc);
+
        return ret;
 }
 
@@ -1352,13 +1433,20 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd,
                icd->sense = &sense;
 
        cam_f.fmt.pix.pixelformat = cam_fmt->fourcc;
-       ret = v4l2_subdev_call(sd, video, s_fmt, f);
+       ret = v4l2_subdev_call(sd, video, s_fmt, &cam_f);
+       cam_f.fmt.pix.pixelformat = pix->pixelformat;
+       *pix = cam_f.fmt.pix;
 
        icd->sense = NULL;
 
        if (ret < 0) {
                dev_warn(dev, "Failed to configure for format %x\n",
                         pix->pixelformat);
+       } else if (pxa_camera_check_frame(pix)) {
+               dev_warn(dev,
+                        "Camera driver produced an unsupported frame %dx%d\n",
+                        pix->width, pix->height);
+               ret = -EINVAL;
        } else if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) {
                if (sense.pixel_clock > sense.pixel_clock_max) {
                        dev_err(dev,
@@ -1402,7 +1490,7 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd,
         */
        v4l_bound_align_image(&pix->width, 48, 2048, 1,
                              &pix->height, 32, 2048, 0,
-                             xlate->host_fmt->fourcc == V4L2_PIX_FMT_YUV422P ? 4 : 0);
+                             pixfmt == V4L2_PIX_FMT_YUV422P ? 4 : 0);
 
        pix->bytesperline = pix->width *
                DIV_ROUND_UP(xlate->host_fmt->depth, 8);
@@ -1412,7 +1500,7 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd,
        pix->pixelformat = xlate->cam_fmt->fourcc;
        /* limit to sensor capabilities */
        ret = v4l2_subdev_call(sd, video, try_fmt, f);
-       pix->pixelformat = xlate->host_fmt->fourcc;
+       pix->pixelformat = pixfmt;
 
        field = pix->field;
 
@@ -1525,6 +1613,7 @@ static struct soc_camera_host_ops pxa_soc_camera_host_ops = {
        .resume         = pxa_camera_resume,
        .set_crop       = pxa_camera_set_crop,
        .get_formats    = pxa_camera_get_formats,
+       .put_formats    = pxa_camera_put_formats,
        .set_fmt        = pxa_camera_set_fmt,
        .try_fmt        = pxa_camera_try_fmt,
        .init_videobuf  = pxa_camera_init_videobuf,