X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fmedia%2Fvideo%2Fsh_mobile_ceu_camera.c;h=02f846d1908bd782f07181e4310ce8fe4109e075;hb=25c4d74ea6f07f2aaa3df537619680ba967043f5;hp=fa88d382d5b5bd917a28c309dea14247daf53072;hpb=2c0a072e3efc17c27566c28028f4b46f79c1f0ca;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index fa88d38..02f846d 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -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);