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 012005e..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>
 
 /* register offsets for sh7722 / sh7723 */
 
-#define CAPSR  0x00
-#define CAPCR  0x04
-#define CAMCR  0x08
-#define CMCYR  0x0c
-#define CAMOR  0x10
-#define CAPWR  0x14
-#define CAIFR  0x18
-#define CSTCR  0x20 /* not on sh7723 */
-#define CSECR  0x24 /* not on sh7723 */
-#define CRCNTR 0x28
-#define CRCMPR 0x2c
-#define CFLCR  0x30
-#define CFSZR  0x34
-#define CDWDR  0x38
-#define CDAYR  0x3c
-#define CDACR  0x40
-#define CDBYR  0x44
-#define CDBCR  0x48
-#define CBDSR  0x4c
-#define CFWCR  0x5c
-#define CLFCR  0x60
-#define CDOCR  0x64
-#define CDDCR  0x68
-#define CDDAR  0x6c
-#define CEIER  0x70
-#define CETCR  0x74
-#define CSTSR  0x7c
-#define CSRTR  0x80
-#define CDSSR  0x84
-#define CDAYR2 0x90
-#define CDACR2 0x94
-#define CDBYR2 0x98
-#define CDBCR2 0x9c
+#define CAPSR  0x00 /* Capture start register */
+#define CAPCR  0x04 /* Capture control register */
+#define CAMCR  0x08 /* Capture interface control register */
+#define CMCYR  0x0c /* Capture interface cycle  register */
+#define CAMOR  0x10 /* Capture interface offset register */
+#define CAPWR  0x14 /* Capture interface width register */
+#define CAIFR  0x18 /* Capture interface input format register */
+#define CSTCR  0x20 /* Camera strobe control register (<= sh7722) */
+#define CSECR  0x24 /* Camera strobe emission count register (<= sh7722) */
+#define CRCNTR 0x28 /* CEU register control register */
+#define CRCMPR 0x2c /* CEU register forcible control register */
+#define CFLCR  0x30 /* Capture filter control register */
+#define CFSZR  0x34 /* Capture filter size clip register */
+#define CDWDR  0x38 /* Capture destination width register */
+#define CDAYR  0x3c /* Capture data address Y register */
+#define CDACR  0x40 /* Capture data address C register */
+#define CDBYR  0x44 /* Capture data bottom-field address Y register */
+#define CDBCR  0x48 /* Capture data bottom-field address C register */
+#define CBDSR  0x4c /* Capture bundle destination size register */
+#define CFWCR  0x5c /* Firewall operation control register */
+#define CLFCR  0x60 /* Capture low-pass filter control register */
+#define CDOCR  0x64 /* Capture data output control register */
+#define CDDCR  0x68 /* Capture data complexity level register */
+#define CDDAR  0x6c /* Capture data complexity level address register */
+#define CEIER  0x70 /* Capture event interrupt enable register */
+#define CETCR  0x74 /* Capture event flag clear register */
+#define CSTSR  0x7c /* Capture status register */
+#define CSRTR  0x80 /* Capture software reset register */
+#define CDSSR  0x84 /* Capture data size register */
+#define CDAYR2 0x90 /* Capture data address Y register 2 */
+#define CDACR2 0x94 /* Capture data address C register 2 */
+#define CDBYR2 0x98 /* Capture data bottom-field address Y register 2 */
+#define CDBCR2 0x9c /* Capture data bottom-field address C register 2 */
 
 static DEFINE_MUTEX(camera_lock);
 
@@ -89,8 +90,10 @@ struct sh_mobile_ceu_dev {
 
        unsigned int irq;
        void __iomem *base;
+       struct clk *clk;
        unsigned long video_limit;
 
+       /* lock used to protect videobuf */
        spinlock_t lock;
        struct list_head capture;
        struct videobuf_buffer *active;
@@ -164,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 */
        }
@@ -235,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);
 
@@ -303,13 +307,12 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
                 "SuperH Mobile CEU driver attached to camera %d\n",
                 icd->devnum);
 
-       if (pcdev->pdata->enable_camera)
-               pcdev->pdata->enable_camera();
-
        ret = icd->ops->init(icd);
        if (ret)
                goto err;
 
+       clk_enable(pcdev->clk);
+
        ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
        while (ceu_read(pcdev, CSTSR) & 1)
                msleep(1);
@@ -325,15 +328,27 @@ 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);
-       if (pcdev->pdata->disable_camera)
-               pcdev->pdata->disable_camera();
 
        dev_info(&icd->dev,
                 "SuperH Mobile CEU driver detached from camera %d\n",
@@ -395,7 +410,20 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
        ceu_write(pcdev, CFLCR, 0); /* data fetch mode - no scaling */
        ceu_write(pcdev, CFSZR, (icd->height << 16) | cfszr_width);
        ceu_write(pcdev, CLFCR, 0); /* data fetch mode - no lowpass filter */
-       ceu_write(pcdev, CDOCR, 0x00000016);
+
+       /* A few words about byte order (observed in Big Endian mode)
+        *
+        * In data fetch mode bytes are received in chunks of 8 bytes.
+        * D0, D1, D2, D3, D4, D5, D6, D7 (D0 received first)
+        *
+        * The data is however by default written to memory in reverse order:
+        * D7, D6, D5, D4, D3, D2, D1, D0 (D7 written to lowest byte)
+        *
+        * The lowest three bits of CDOCR allows us to do swapping,
+        * using 7 we swap the data bytes to match the incoming order:
+        * D0, D1, D2, D3, D4, D5, D6, D7
+        */
+       ceu_write(pcdev, CDOCR, 0x00000017);
 
        ceu_write(pcdev, CDWDR, cdwdr_width);
        ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */
@@ -425,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)
@@ -444,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);
 }
@@ -518,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,
 };
@@ -528,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;
 
@@ -587,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:
@@ -623,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);
@@ -646,7 +720,7 @@ static int __init sh_mobile_ceu_init(void)
 
 static void __exit sh_mobile_ceu_exit(void)
 {
-       return platform_driver_unregister(&sh_mobile_ceu_driver);
+       platform_driver_unregister(&sh_mobile_ceu_driver);
 }
 
 module_init(sh_mobile_ceu_init);