Commit cf34cba7 authored by Guennadi Liakhovetski's avatar Guennadi Liakhovetski Committed by Mauro Carvalho Chehab

V4L/DVB (10075): pxa-camera: setup the FIFO inactivity time-out register

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: default avatarRobert Jarzmik <robert.jarzmik@free.fr>
Signed-off-by: default avatarGuennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent a9bef518
...@@ -215,7 +215,9 @@ struct pxa_camera_dev { ...@@ -215,7 +215,9 @@ struct pxa_camera_dev {
struct pxacamera_platform_data *pdata; struct pxacamera_platform_data *pdata;
struct resource *res; struct resource *res;
unsigned long platform_flags; unsigned long platform_flags;
unsigned long platform_mclk_10khz; unsigned long ciclk;
unsigned long mclk;
u32 mclk_divisor;
struct list_head capture; struct list_head capture;
...@@ -707,24 +709,43 @@ static void pxa_camera_init_videobuf(struct videobuf_queue *q, ...@@ -707,24 +709,43 @@ static void pxa_camera_init_videobuf(struct videobuf_queue *q,
sizeof(struct pxa_buffer), icd); 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 mclk = pcdev->mclk;
unsigned long div; u32 div;
unsigned long lcdclk; 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 /* mclk <= ciclk / 4 (27.4.2) */
* they get a nice Oops */ if (mclk > lcdclk / 4) {
div = (lcdclk + 2 * mclk_10khz - 1) / (2 * mclk_10khz) - 1; 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, " dev_dbg(pcdev->dev, "LCD clock %luHz, target freq %luHz, "
"divisor %lu\n", lcdclk * 10, mclk_10khz * 10, div); "divisor %u\n", lcdclk, mclk, div);
return 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) static void pxa_camera_activate(struct pxa_camera_dev *pcdev)
{ {
struct pxacamera_platform_data *pdata = pcdev->pdata; struct pxacamera_platform_data *pdata = pcdev->pdata;
...@@ -752,8 +773,14 @@ static void pxa_camera_activate(struct pxa_camera_dev *pcdev) ...@@ -752,8 +773,14 @@ static void pxa_camera_activate(struct pxa_camera_dev *pcdev)
if (pcdev->platform_flags & PXA_CAMERA_VSP) if (pcdev->platform_flags & PXA_CAMERA_VSP)
cicr4 |= CICR4_VSP; cicr4 |= CICR4_VSP;
cicr4 |= mclk_get_divisor(pcdev); __raw_writel(pcdev->mclk_divisor | cicr4, pcdev->base + CICR4);
__raw_writel(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); clk_enable(pcdev->clk);
} }
...@@ -1000,7 +1027,7 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) ...@@ -1000,7 +1027,7 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
cicr2 = 0; cicr2 = 0;
cicr3 = CICR3_LPF_VAL(icd->height - 1) | cicr3 = CICR3_LPF_VAL(icd->height - 1) |
CICR3_BFW_VAL(min((unsigned short)255, icd->y_skip_top)); 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(cicr1, pcdev->base + CICR1);
__raw_writel(cicr2, pcdev->base + CICR2); __raw_writel(cicr2, pcdev->base + CICR2);
...@@ -1019,8 +1046,7 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) ...@@ -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, static int pxa_camera_try_bus_param(struct soc_camera_device *icd,
unsigned char buswidth) unsigned char buswidth)
{ {
struct soc_camera_host *ici = struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv; struct pxa_camera_dev *pcdev = ici->priv;
unsigned long bus_flags, camera_flags; unsigned long bus_flags, camera_flags;
int ret = test_platform_param(pcdev, buswidth, &bus_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, ...@@ -1136,8 +1162,13 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd,
__u32 pixfmt, struct v4l2_rect *rect) __u32 pixfmt, struct v4l2_rect *rect)
{ {
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;
const struct soc_camera_data_format *host_fmt, *cam_fmt = NULL; const struct soc_camera_data_format *host_fmt, *cam_fmt = NULL;
const struct soc_camera_format_xlate *xlate; 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; int ret, buswidth;
xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
...@@ -1150,6 +1181,10 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd, ...@@ -1150,6 +1181,10 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd,
host_fmt = xlate->host_fmt; host_fmt = xlate->host_fmt;
cam_fmt = xlate->cam_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) { switch (pixfmt) {
case 0: /* Only geometry change */ case 0: /* Only geometry change */
ret = icd->ops->set_fmt(icd, pixfmt, rect); ret = icd->ops->set_fmt(icd, pixfmt, rect);
...@@ -1158,9 +1193,20 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd, ...@@ -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); 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", dev_warn(&ici->dev, "Failed to configure for format %x\n",
pixfmt); 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) { if (pixfmt && !ret) {
icd->buswidth = buswidth; icd->buswidth = buswidth;
...@@ -1369,14 +1415,17 @@ static int pxa_camera_probe(struct platform_device *pdev) ...@@ -1369,14 +1415,17 @@ static int pxa_camera_probe(struct platform_device *pdev)
"data widths, using default 10 bit\n"); "data widths, using default 10 bit\n");
pcdev->platform_flags |= PXA_CAMERA_DATAWIDTH_10; pcdev->platform_flags |= PXA_CAMERA_DATAWIDTH_10;
} }
pcdev->platform_mclk_10khz = pcdev->pdata->mclk_10khz; pcdev->mclk = pcdev->pdata->mclk_10khz * 10000;
if (!pcdev->platform_mclk_10khz) { if (!pcdev->mclk) {
dev_warn(&pdev->dev, dev_warn(&pdev->dev,
"mclk_10khz == 0! Please, fix your platform data. " "mclk == 0! Please, fix your platform data. "
"Using default 20MHz\n"); "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); INIT_LIST_HEAD(&pcdev->capture);
spin_lock_init(&pcdev->lock); spin_lock_init(&pcdev->lock);
...@@ -1396,7 +1445,6 @@ static int pxa_camera_probe(struct platform_device *pdev) ...@@ -1396,7 +1445,6 @@ static int pxa_camera_probe(struct platform_device *pdev)
} }
pcdev->irq = irq; pcdev->irq = irq;
pcdev->base = base; pcdev->base = base;
pcdev->dev = &pdev->dev;
/* request dma */ /* request dma */
err = pxa_request_dma("CI_Y", DMA_PRIO_HIGH, err = pxa_request_dma("CI_Y", DMA_PRIO_HIGH,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment