📄 sh_mobile_ceu_camera.c
字号:
/* * V4L2 Driver for SuperH Mobile CEU interface * * Copyright (C) 2008 Magnus Damm * * Based on V4L2 Driver for PXA camera host - "pxa_camera.c", * * Copyright (C) 2006, Sascha Hauer, Pengutronix * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */#include <linux/init.h>#include <linux/module.h>#include <linux/io.h>#include <linux/delay.h>#include <linux/dma-mapping.h>#include <linux/errno.h>#include <linux/fs.h>#include <linux/interrupt.h>#include <linux/kernel.h>#include <linux/mm.h>#include <linux/moduleparam.h>#include <linux/time.h>#include <linux/version.h>#include <linux/device.h>#include <linux/platform_device.h>#include <linux/mutex.h>#include <linux/videodev2.h>#include <media/v4l2-common.h>#include <media/v4l2-dev.h>#include <media/soc_camera.h>#include <media/sh_mobile_ceu.h>#include <media/videobuf-dma-contig.h>/* register offsets for sh7722 / sh7723 */#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);/* per video frame buffer */struct sh_mobile_ceu_buffer { struct videobuf_buffer vb; /* v4l buffer must be first */ const struct soc_camera_data_format *fmt;};struct sh_mobile_ceu_dev { struct device *dev; struct soc_camera_host ici; struct soc_camera_device *icd; unsigned int irq; void __iomem *base; unsigned long video_limit; /* lock used to protect videobuf */ spinlock_t lock; struct list_head capture; struct videobuf_buffer *active; struct sh_mobile_ceu_info *pdata;};static void ceu_write(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs, unsigned long data){ iowrite32(data, priv->base + reg_offs);}static unsigned long ceu_read(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs){ return ioread32(priv->base + reg_offs);}/* * Videobuf operations */static int sh_mobile_ceu_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size){ struct soc_camera_device *icd = vq->priv_data; struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; int bytes_per_pixel = (icd->current_fmt->depth + 7) >> 3; *size = PAGE_ALIGN(icd->width * icd->height * bytes_per_pixel); if (0 == *count) *count = 2; if (pcdev->video_limit) { while (*size * *count > pcdev->video_limit) (*count)--; } dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size); return 0;}static void free_buffer(struct videobuf_queue *vq, struct sh_mobile_ceu_buffer *buf){ struct soc_camera_device *icd = vq->priv_data; dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__, &buf->vb, buf->vb.baddr, buf->vb.bsize); if (in_interrupt()) BUG(); videobuf_dma_contig_free(vq, &buf->vb); dev_dbg(&icd->dev, "%s freed\n", __func__); buf->vb.state = VIDEOBUF_NEEDS_INIT;}static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev){ ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~1); ceu_write(pcdev, CETCR, ~ceu_read(pcdev, CETCR) & 0x0317f313); ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | 1); ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~0x10000); 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 */ }}static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, enum v4l2_field field){ struct soc_camera_device *icd = vq->priv_data; struct sh_mobile_ceu_buffer *buf; int ret; buf = container_of(vb, struct sh_mobile_ceu_buffer, vb); dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__, vb, vb->baddr, vb->bsize); /* Added list head initialization on alloc */ WARN_ON(!list_empty(&vb->queue));#ifdef DEBUG /* This can be useful if you want to see if we actually fill * the buffer with something */ memset((void *)vb->baddr, 0xaa, vb->bsize);#endif BUG_ON(NULL == icd->current_fmt); if (buf->fmt != icd->current_fmt || vb->width != icd->width || vb->height != icd->height || vb->field != field) { buf->fmt = icd->current_fmt; vb->width = icd->width; vb->height = icd->height; vb->field = field; vb->state = VIDEOBUF_NEEDS_INIT; } vb->size = vb->width * vb->height * ((buf->fmt->depth + 7) >> 3); if (0 != vb->baddr && vb->bsize < vb->size) { ret = -EINVAL; goto out; } if (vb->state == VIDEOBUF_NEEDS_INIT) { ret = videobuf_iolock(vq, vb, NULL); if (ret) goto fail; vb->state = VIDEOBUF_PREPARED; } return 0;fail: free_buffer(vq, buf);out: return ret;}static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb){ struct soc_camera_device *icd = vq->priv_data; struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; unsigned long flags; dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__, vb, vb->baddr, vb->bsize); vb->state = VIDEOBUF_QUEUED; spin_lock_irqsave(&pcdev->lock, flags); list_add_tail(&vb->queue, &pcdev->capture); if (!pcdev->active) { pcdev->active = vb; sh_mobile_ceu_capture(pcdev); } spin_unlock_irqrestore(&pcdev->lock, flags);}static void sh_mobile_ceu_videobuf_release(struct videobuf_queue *vq, struct videobuf_buffer *vb){ free_buffer(vq, container_of(vb, struct sh_mobile_ceu_buffer, vb));}static struct videobuf_queue_ops sh_mobile_ceu_videobuf_ops = { .buf_setup = sh_mobile_ceu_videobuf_setup, .buf_prepare = sh_mobile_ceu_videobuf_prepare, .buf_queue = sh_mobile_ceu_videobuf_queue, .buf_release = sh_mobile_ceu_videobuf_release,};static irqreturn_t sh_mobile_ceu_irq(int irq, void *data){ struct sh_mobile_ceu_dev *pcdev = data; struct videobuf_buffer *vb; unsigned long flags; spin_lock_irqsave(&pcdev->lock, flags); vb = pcdev->active; list_del_init(&vb->queue); if (!list_empty(&pcdev->capture)) pcdev->active = list_entry(pcdev->capture.next, struct videobuf_buffer, queue); else pcdev->active = NULL; sh_mobile_ceu_capture(pcdev); vb->state = VIDEOBUF_DONE; do_gettimeofday(&vb->ts); vb->field_count++; wake_up(&vb->done); spin_unlock_irqrestore(&pcdev->lock, flags); return IRQ_HANDLED;}static int sh_mobile_ceu_add_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; int ret = -EBUSY; mutex_lock(&camera_lock); if (pcdev->icd) goto err; dev_info(&icd->dev, "SuperH Mobile CEU driver attached to camera %d\n", icd->devnum); ret = icd->ops->init(icd); if (ret) goto err; ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ while (ceu_read(pcdev, CSTSR) & 1) msleep(1); pcdev->icd = icd;err: mutex_unlock(&camera_lock); return ret;}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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -