V4L/DVB (10075): pxa-camera: setup the FIFO inactivity time-out register
authorGuennadi Liakhovetski <g.liakhovetski@gmx.de>
Thu, 18 Dec 2008 14:38:03 +0000 (11:38 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Tue, 30 Dec 2008 11:40:19 +0000 (09:40 -0200)
Using PXA270's FIFO inactivity time-out register (CITOR) reduces FIFO overruns.
The time-out is calculated in CICLK / LCDCLK ticks and has to be longer than
one pixel time. For this we have to know the pixel clock frequency, which
usually is provided by the camera. We use the struct soc_camera_sense to
request PCLK frequency from the camera driver upon each data format change.

Tested-by: Robert Jarzmik <robert.jarzmik@free.fr>
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/pxa_camera.c

index 45040f99031d9c8e3cde76aa888cf120189e5c0c..28ea13f7b2b039a007bfe2f362b75b922bfb99a1 100644 (file)
@@ -215,7 +215,9 @@ struct pxa_camera_dev {
        struct pxacamera_platform_data *pdata;
        struct resource         *res;
        unsigned long           platform_flags;
-       unsigned long           platform_mclk_10khz;
+       unsigned long           ciclk;
+       unsigned long           mclk;
+       u32                     mclk_divisor;
 
        struct list_head        capture;
 
@@ -707,24 +709,43 @@ static void pxa_camera_init_videobuf(struct videobuf_queue *q,
                                sizeof(struct pxa_buffer), icd);
 }
 
-static int mclk_get_divisor(struct pxa_camera_dev *pcdev)
+static u32 mclk_get_divisor(struct pxa_camera_dev *pcdev)
 {
-       unsigned int mclk_10khz = pcdev->platform_mclk_10khz;
-       unsigned long div;
+       unsigned long mclk = pcdev->mclk;
+       u32 div;
        unsigned long lcdclk;
 
-       lcdclk = clk_get_rate(pcdev->clk) / 10000;
+       lcdclk = clk_get_rate(pcdev->clk);
+       pcdev->ciclk = lcdclk;
 
-       /* We verify platform_mclk_10khz != 0, so if anyone breaks it, here
-        * they get a nice Oops */
-       div = (lcdclk + 2 * mclk_10khz - 1) / (2 * mclk_10khz) - 1;
+       /* mclk <= ciclk / 4 (27.4.2) */
+       if (mclk > lcdclk / 4) {
+               mclk = lcdclk / 4;
+               dev_warn(pcdev->dev, "Limiting master clock to %lu\n", mclk);
+       }
+
+       /* We verify mclk != 0, so if anyone breaks it, here comes their Oops */
+       div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1;
+
+       /* If we're not supplying MCLK, leave it at 0 */
+       if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN)
+               pcdev->mclk = lcdclk / (2 * (div + 1));
 
-       dev_dbg(pcdev->dev, "LCD clock %lukHz, target freq %dkHz, "
-               "divisor %lu\n", lcdclk * 10, mclk_10khz * 10, div);
+       dev_dbg(pcdev->dev, "LCD clock %luHz, target freq %luHz, "
+               "divisor %u\n", lcdclk, mclk, div);
 
        return div;
 }
 
+static void recalculate_fifo_timeout(struct pxa_camera_dev *pcdev,
+                                    unsigned long pclk)
+{
+       /* We want a timeout > 1 pixel time, not ">=" */
+       u32 ciclk_per_pixel = pcdev->ciclk / pclk + 1;
+
+       __raw_writel(ciclk_per_pixel, pcdev->base + CITOR);
+}
+
 static void pxa_camera_activate(struct pxa_camera_dev *pcdev)
 {
        struct pxacamera_platform_data *pdata = pcdev->pdata;
@@ -752,8 +773,14 @@ static void pxa_camera_activate(struct pxa_camera_dev *pcdev)
        if (pcdev->platform_flags & PXA_CAMERA_VSP)
                cicr4 |= CICR4_VSP;
 
-       cicr4 |= mclk_get_divisor(pcdev);
-       __raw_writel(cicr4, pcdev->base + CICR4);
+       __raw_writel(pcdev->mclk_divisor | cicr4, pcdev->base + CICR4);
+
+       if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN)
+               /* Initialise the timeout under the assumption pclk = mclk */
+               recalculate_fifo_timeout(pcdev, pcdev->mclk);
+       else
+               /* "Safe default" - 13MHz */
+               recalculate_fifo_timeout(pcdev, 13000000);
 
        clk_enable(pcdev->clk);
 }
@@ -1000,7 +1027,7 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
        cicr2 = 0;
        cicr3 = CICR3_LPF_VAL(icd->height - 1) |
                CICR3_BFW_VAL(min((unsigned short)255, icd->y_skip_top));
-       cicr4 |= mclk_get_divisor(pcdev);
+       cicr4 |= pcdev->mclk_divisor;
 
        __raw_writel(cicr1, pcdev->base + CICR1);
        __raw_writel(cicr2, pcdev->base + CICR2);
@@ -1019,8 +1046,7 @@ static int pxa_camera_set_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 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, buswidth, &bus_flags);
@@ -1136,8 +1162,13 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd,
                              __u32 pixfmt, struct v4l2_rect *rect)
 {
        struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+       struct pxa_camera_dev *pcdev = ici->priv;
        const struct soc_camera_data_format *host_fmt, *cam_fmt = NULL;
        const struct soc_camera_format_xlate *xlate;
+       struct soc_camera_sense sense = {
+               .master_clock = pcdev->mclk,
+               .pixel_clock_max = pcdev->ciclk / 4,
+       };
        int ret, buswidth;
 
        xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
@@ -1150,6 +1181,10 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd,
        host_fmt = xlate->host_fmt;
        cam_fmt = xlate->cam_fmt;
 
+       /* If PCLK is used to latch data from the sensor, check sense */
+       if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN)
+               icd->sense = &sense;
+
        switch (pixfmt) {
        case 0:                         /* Only geometry change */
                ret = icd->ops->set_fmt(icd, pixfmt, rect);
@@ -1158,9 +1193,20 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd,
                ret = icd->ops->set_fmt(icd, cam_fmt->fourcc, rect);
        }
 
-       if (ret < 0)
+       icd->sense = NULL;
+
+       if (ret < 0) {
                dev_warn(&ici->dev, "Failed to configure for format %x\n",
                         pixfmt);
+       } else if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) {
+               if (sense.pixel_clock > sense.pixel_clock_max) {
+                       dev_err(&ici->dev,
+                               "pixel clock %lu set by the camera too high!",
+                               sense.pixel_clock);
+                       return -EIO;
+               }
+               recalculate_fifo_timeout(pcdev, sense.pixel_clock);
+       }
 
        if (pixfmt && !ret) {
                icd->buswidth = buswidth;
@@ -1369,14 +1415,17 @@ static int pxa_camera_probe(struct platform_device *pdev)
                         "data widths, using default 10 bit\n");
                pcdev->platform_flags |= PXA_CAMERA_DATAWIDTH_10;
        }
-       pcdev->platform_mclk_10khz = pcdev->pdata->mclk_10khz;
-       if (!pcdev->platform_mclk_10khz) {
+       pcdev->mclk = pcdev->pdata->mclk_10khz * 10000;
+       if (!pcdev->mclk) {
                dev_warn(&pdev->dev,
-                        "mclk_10khz == 0! Please, fix your platform data. "
+                        "mclk == 0! Please, fix your platform data. "
                         "Using default 20MHz\n");
-               pcdev->platform_mclk_10khz = 2000;
+               pcdev->mclk = 20000000;
        }
 
+       pcdev->dev = &pdev->dev;
+       pcdev->mclk_divisor = mclk_get_divisor(pcdev);
+
        INIT_LIST_HEAD(&pcdev->capture);
        spin_lock_init(&pcdev->lock);
 
@@ -1396,7 +1445,6 @@ static int pxa_camera_probe(struct platform_device *pdev)
        }
        pcdev->irq = irq;
        pcdev->base = base;
-       pcdev->dev = &pdev->dev;
 
        /* request dma */
        err = pxa_request_dma("CI_Y", DMA_PRIO_HIGH,