X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fmedia%2Fvideo%2Fpxa_camera.c;h=51b683c63b70b54bafad3c9f43e583b76b686a49;hb=471452104b8520337ae2fb48c4e61cd4896e025d;hp=4bc2a4f81f79942a89a01208124e45fee5245c55;hpb=c9c1f1c0dbe90b82939917fdc3e4c9ccad42342d;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 4bc2a4f..51b683c 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -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 */ @@ -237,9 +242,9 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, { struct soc_camera_device *icd = vq->priv_data; - dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size); + 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) @@ -259,7 +264,7 @@ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf) BUG_ON(in_interrupt()); - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, &buf->vb, buf->vb.baddr, buf->vb.bsize); /* This waits until this buffer is out of danger, i.e., until it is no @@ -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; } @@ -540,7 +546,8 @@ static void pxa_dma_start_channels(struct pxa_camera_dev *pcdev) active = pcdev->active; for (i = 0; i < pcdev->channels; i++) { - dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s (channel=%d) ddadr=%08x\n", __func__, + dev_dbg(pcdev->soc_host.v4l2_dev.dev, + "%s (channel=%d) ddadr=%08x\n", __func__, i, active->dmas[i].sg_dma); DDADR(pcdev->dma_chans[i]) = active->dmas[i].sg_dma; DCSR(pcdev->dma_chans[i]) = DCSR_RUN; @@ -552,7 +559,8 @@ static void pxa_dma_stop_channels(struct pxa_camera_dev *pcdev) int i; for (i = 0; i < pcdev->channels; i++) { - dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s (channel=%d)\n", __func__, i); + dev_dbg(pcdev->soc_host.v4l2_dev.dev, + "%s (channel=%d)\n", __func__, i); DCSR(pcdev->dma_chans[i]) = 0; } } @@ -620,8 +628,8 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq, struct pxa_camera_dev *pcdev = ici->priv; struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d active=%p\n", __func__, - vb, vb->baddr, vb->bsize, pcdev->active); + dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d active=%p\n", + __func__, vb, vb->baddr, vb->bsize, pcdev->active); list_add_tail(&vb->queue, &pcdev->capture); @@ -638,22 +646,23 @@ static void pxa_videobuf_release(struct videobuf_queue *vq, struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); #ifdef DEBUG struct soc_camera_device *icd = vq->priv_data; + struct device *dev = icd->dev.parent; - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); switch (vb->state) { case VIDEOBUF_ACTIVE: - dev_dbg(&icd->dev, "%s (active)\n", __func__); + dev_dbg(dev, "%s (active)\n", __func__); break; case VIDEOBUF_QUEUED: - dev_dbg(&icd->dev, "%s (queued)\n", __func__); + dev_dbg(dev, "%s (queued)\n", __func__); break; case VIDEOBUF_PREPARED: - dev_dbg(&icd->dev, "%s (prepared)\n", __func__); + dev_dbg(dev, "%s (prepared)\n", __func__); break; default: - dev_dbg(&icd->dev, "%s (unknown)\n", __func__); + dev_dbg(dev, "%s (unknown)\n", __func__); break; } #endif @@ -836,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; @@ -924,7 +933,8 @@ static irqreturn_t pxa_camera_irq(int irq, void *data) struct videobuf_buffer *vb; status = __raw_readl(pcdev->base + CISR); - dev_dbg(pcdev->soc_host.v4l2_dev.dev, "Camera interrupt status 0x%lx\n", status); + dev_dbg(pcdev->soc_host.v4l2_dev.dev, + "Camera interrupt status 0x%lx\n", status); if (!status) return IRQ_NONE; @@ -964,7 +974,7 @@ static int pxa_camera_add_device(struct soc_camera_device *icd) pcdev->icd = icd; - dev_info(&icd->dev, "PXA Camera driver attached to camera %d\n", + dev_info(icd->dev.parent, "PXA Camera driver attached to camera %d\n", icd->devnum); return 0; @@ -978,7 +988,7 @@ static void pxa_camera_remove_device(struct soc_camera_device *icd) BUG_ON(icd != pcdev->icd); - dev_info(&icd->dev, "PXA Camera driver detached from camera %d\n", + dev_info(icd->dev.parent, "PXA Camera driver detached from camera %d\n", icd->devnum); /* disable capture, disable interrupts */ @@ -1036,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; @@ -1107,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: @@ -1147,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; @@ -1161,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; } @@ -1224,8 +1247,9 @@ static int required_buswidth(const struct soc_camera_data_format *fmt) static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx, struct soc_camera_format_xlate *xlate) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct device *dev = icd->dev.parent; int formats = 0, buswidth, ret; + struct pxa_cam *cam; buswidth = required_buswidth(icd->formats + idx); @@ -1236,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++; @@ -1244,7 +1278,7 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx, xlate->cam_fmt = icd->formats + idx; xlate->buswidth = buswidth; xlate++; - dev_dbg(ici->v4l2_dev.dev, "Providing format %s using %s\n", + dev_dbg(dev, "Providing format %s using %s\n", pxa_camera_formats[0].name, icd->formats[idx].name); } @@ -1259,7 +1293,7 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx, xlate->cam_fmt = icd->formats + idx; xlate->buswidth = buswidth; xlate++; - dev_dbg(ici->v4l2_dev.dev, "Providing format %s packed\n", + dev_dbg(dev, "Providing format %s packed\n", icd->formats[idx].name); } break; @@ -1271,7 +1305,7 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx, xlate->cam_fmt = icd->formats + idx; xlate->buswidth = icd->formats[idx].depth; xlate++; - dev_dbg(ici->v4l2_dev.dev, + dev_dbg(dev, "Providing format %s in pass-through mode\n", icd->formats[idx].name); } @@ -1280,17 +1314,34 @@ 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) { struct v4l2_rect *rect = &a->c; struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; + struct device *dev = icd->dev.parent; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct soc_camera_sense sense = { .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 */ @@ -1302,11 +1353,41 @@ static int pxa_camera_set_crop(struct soc_camera_device *icd, icd->sense = NULL; if (ret < 0) { - dev_warn(ici->v4l2_dev.dev, "Failed to crop to %ux%u@%u:%u\n", + 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(ici->v4l2_dev.dev, + dev_err(dev, "pixel clock %lu set by the camera too high!", sense.pixel_clock); return -EIO; @@ -1314,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; } @@ -1322,6 +1408,7 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd, { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; + struct device *dev = icd->dev.parent; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_camera_data_format *cam_fmt = NULL; const struct soc_camera_format_xlate *xlate = NULL; @@ -1335,7 +1422,7 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); if (!xlate) { - dev_warn(ici->v4l2_dev.dev, "Format %x not found\n", pix->pixelformat); + dev_warn(dev, "Format %x not found\n", pix->pixelformat); return -EINVAL; } @@ -1346,16 +1433,23 @@ 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(ici->v4l2_dev.dev, "Failed to configure for format %x\n", + 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(ici->v4l2_dev.dev, + dev_err(dev, "pixel clock %lu set by the camera too high!", sense.pixel_clock); return -EIO; @@ -1396,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); @@ -1406,14 +1500,14 @@ 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; if (field == V4L2_FIELD_ANY) { pix->field = V4L2_FIELD_NONE; } else if (field != V4L2_FIELD_NONE) { - dev_err(&icd->dev, "Field type %d unsupported.\n", field); + dev_err(icd->dev.parent, "Field type %d unsupported.\n", field); return -EINVAL; } @@ -1519,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,