V4L/DVB (9787): soc-camera: let camera host drivers decide upon pixel format
[safe/jmp/linux-2.6] / drivers / media / video / sh_mobile_ceu_camera.c
index fa88d38..02f846d 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/platform_device.h>
 #include <linux/mutex.h>
 #include <linux/videodev2.h>
+#include <linux/clk.h>
 
 #include <media/v4l2-common.h>
 #include <media/v4l2-dev.h>
@@ -89,6 +90,7 @@ struct sh_mobile_ceu_dev {
 
        unsigned int irq;
        void __iomem *base;
+       struct clk *clk;
        unsigned long video_limit;
 
        /* lock used to protect videobuf */
@@ -165,6 +167,7 @@ static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
        ceu_write(pcdev, CETCR, 0x0317f313 ^ 0x10);
 
        if (pcdev->active) {
+               pcdev->active->state = VIDEOBUF_ACTIVE;
                ceu_write(pcdev, CDAYR, videobuf_to_dma_contig(pcdev->active));
                ceu_write(pcdev, CAPSR, 0x1); /* start capture */
        }
@@ -236,7 +239,7 @@ static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq,
        dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__,
                vb, vb->baddr, vb->bsize);
 
-       vb->state = VIDEOBUF_ACTIVE;
+       vb->state = VIDEOBUF_QUEUED;
        spin_lock_irqsave(&pcdev->lock, flags);
        list_add_tail(&vb->queue, &pcdev->capture);
 
@@ -308,6 +311,8 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
        if (ret)
                goto err;
 
+       clk_enable(pcdev->clk);
+
        ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
        while (ceu_read(pcdev, CSTSR) & 1)
                msleep(1);
@@ -323,12 +328,26 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
 {
        struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
        struct sh_mobile_ceu_dev *pcdev = ici->priv;
+       unsigned long flags;
 
        BUG_ON(icd != pcdev->icd);
 
        /* disable capture, disable interrupts */
        ceu_write(pcdev, CEIER, 0);
        ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
+
+       /* make sure active buffer is canceled */
+       spin_lock_irqsave(&pcdev->lock, flags);
+       if (pcdev->active) {
+               list_del(&pcdev->active->queue);
+               pcdev->active->state = VIDEOBUF_ERROR;
+               wake_up_all(&pcdev->active->done);
+               pcdev->active = NULL;
+       }
+       spin_unlock_irqrestore(&pcdev->lock, flags);
+
+       clk_disable(pcdev->clk);
+
        icd->ops->release(icd);
 
        dev_info(&icd->dev,
@@ -434,12 +453,43 @@ static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd,
 static int sh_mobile_ceu_set_fmt_cap(struct soc_camera_device *icd,
                                     __u32 pixfmt, struct v4l2_rect *rect)
 {
-       return icd->ops->set_fmt_cap(icd, pixfmt, rect);
+       const struct soc_camera_data_format *cam_fmt;
+       int ret;
+
+       /*
+        * TODO: find a suitable supported by the SoC output format, check
+        * whether the sensor supports one of acceptable input formats.
+        */
+       if (pixfmt) {
+               cam_fmt = soc_camera_format_by_fourcc(icd, pixfmt);
+               if (!cam_fmt)
+                       return -EINVAL;
+       }
+
+       ret = icd->ops->set_fmt_cap(icd, pixfmt, rect);
+       if (pixfmt && !ret)
+               icd->current_fmt = cam_fmt;
+
+       return ret;
 }
 
 static int sh_mobile_ceu_try_fmt_cap(struct soc_camera_device *icd,
                                     struct v4l2_format *f)
 {
+       const struct soc_camera_data_format *cam_fmt;
+       int ret = sh_mobile_ceu_try_bus_param(icd, f->fmt.pix.pixelformat);
+
+       if (ret < 0)
+               return ret;
+
+       /*
+        * TODO: find a suitable supported by the SoC output format, check
+        * whether the sensor supports one of acceptable input formats.
+        */
+       cam_fmt = soc_camera_format_by_fourcc(icd, f->fmt.pix.pixelformat);
+       if (!cam_fmt)
+               return -EINVAL;
+
        /* FIXME: calculate using depth and bus width */
 
        if (f->fmt.pix.height < 4)
@@ -453,6 +503,10 @@ static int sh_mobile_ceu_try_fmt_cap(struct soc_camera_device *icd,
        f->fmt.pix.width &= ~0x01;
        f->fmt.pix.height &= ~0x03;
 
+       f->fmt.pix.bytesperline = f->fmt.pix.width *
+               DIV_ROUND_UP(cam_fmt->depth, 8);
+       f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+
        /* limit to sensor capabilities */
        return icd->ops->try_fmt_cap(icd, f);
 }
@@ -527,7 +581,6 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
        .reqbufs        = sh_mobile_ceu_reqbufs,
        .poll           = sh_mobile_ceu_poll,
        .querycap       = sh_mobile_ceu_querycap,
-       .try_bus_param  = sh_mobile_ceu_try_bus_param,
        .set_bus_param  = sh_mobile_ceu_set_bus_param,
        .init_videobuf  = sh_mobile_ceu_init_videobuf,
 };
@@ -537,6 +590,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
        struct sh_mobile_ceu_dev *pcdev;
        struct resource *res;
        void __iomem *base;
+       char clk_name[8];
        unsigned int irq;
        int err = 0;
 
@@ -596,24 +650,34 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
 
        /* request irq */
        err = request_irq(pcdev->irq, sh_mobile_ceu_irq, IRQF_DISABLED,
-                         pdev->dev.bus_id, pcdev);
+                         dev_name(&pdev->dev), pcdev);
        if (err) {
                dev_err(&pdev->dev, "Unable to register CEU interrupt.\n");
                goto exit_release_mem;
        }
 
+       snprintf(clk_name, sizeof(clk_name), "ceu%d", pdev->id);
+       pcdev->clk = clk_get(&pdev->dev, clk_name);
+       if (IS_ERR(pcdev->clk)) {
+               dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
+               err = PTR_ERR(pcdev->clk);
+               goto exit_free_irq;
+       }
+
        pcdev->ici.priv = pcdev;
        pcdev->ici.dev.parent = &pdev->dev;
        pcdev->ici.nr = pdev->id;
-       pcdev->ici.drv_name = pdev->dev.bus_id,
-       pcdev->ici.ops = &sh_mobile_ceu_host_ops,
+       pcdev->ici.drv_name = dev_name(&pdev->dev);
+       pcdev->ici.ops = &sh_mobile_ceu_host_ops;
 
        err = soc_camera_host_register(&pcdev->ici);
        if (err)
-               goto exit_free_irq;
+               goto exit_free_clk;
 
        return 0;
 
+exit_free_clk:
+       clk_put(pcdev->clk);
 exit_free_irq:
        free_irq(pcdev->irq, pcdev);
 exit_release_mem:
@@ -632,6 +696,7 @@ static int sh_mobile_ceu_remove(struct platform_device *pdev)
        struct sh_mobile_ceu_dev *pcdev = platform_get_drvdata(pdev);
 
        soc_camera_host_unregister(&pcdev->ici);
+       clk_put(pcdev->clk);
        free_irq(pcdev->irq, pcdev);
        if (platform_get_resource(pdev, IORESOURCE_MEM, 1))
                dma_release_declared_memory(&pdev->dev);