Initial commit; kernel source import

This commit is contained in:
Nathan
2025-04-06 23:50:55 -05:00
commit 25c6d769f4
45093 changed files with 18199410 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
config SOC_CAMERA
tristate "SoC camera support"
depends on VIDEO_V4L2 && HAS_DMA && I2C
select VIDEOBUF_GEN
select VIDEOBUF2_CORE
help
SoC Camera is a common API to several cameras, not connecting
over a bus like PCI or USB. For example some i2c camera connected
directly to the data bus of an SoC.
config SOC_CAMERA_PLATFORM
tristate "platform camera support"
depends on SOC_CAMERA
help
This is a generic SoC camera platform driver, useful for testing
config MX1_VIDEO
bool
config VIDEO_MX1
tristate "i.MX1/i.MXL CMOS Sensor Interface driver"
depends on BROKEN
depends on VIDEO_DEV && ARCH_MX1 && SOC_CAMERA
select FIQ
select VIDEOBUF_DMA_CONTIG
select MX1_VIDEO
---help---
This is a v4l2 driver for the i.MX1/i.MXL CMOS Sensor Interface
config MX3_VIDEO
bool
config VIDEO_MX3
tristate "i.MX3x Camera Sensor Interface driver"
depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA
select VIDEOBUF2_DMA_CONTIG
select MX3_VIDEO
---help---
This is a v4l2 driver for the i.MX3x Camera Sensor Interface
config VIDEO_PXA27x
tristate "PXA27x Quick Capture Interface driver"
depends on VIDEO_DEV && PXA27x && SOC_CAMERA
select VIDEOBUF_DMA_SG
---help---
This is a v4l2 driver for the PXA27x Quick Capture Interface
config VIDEO_SH_MOBILE_CSI2
tristate "SuperH Mobile MIPI CSI-2 Interface driver"
depends on VIDEO_DEV && SOC_CAMERA && HAVE_CLK
---help---
This is a v4l2 driver for the SuperH MIPI CSI-2 Interface
config VIDEO_SH_MOBILE_CEU
tristate "SuperH Mobile CEU Interface driver"
depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK
select VIDEOBUF2_DMA_CONTIG
---help---
This is a v4l2 driver for the SuperH Mobile CEU Interface
config VIDEO_OMAP1
tristate "OMAP1 Camera Interface driver"
depends on VIDEO_DEV && ARCH_OMAP1 && SOC_CAMERA
select VIDEOBUF_DMA_CONTIG
select VIDEOBUF_DMA_SG
---help---
This is a v4l2 driver for the TI OMAP1 camera interface
config VIDEO_MX2_HOSTSUPPORT
bool
config VIDEO_MX2
tristate "i.MX27 Camera Sensor Interface driver"
depends on VIDEO_DEV && SOC_CAMERA && MACH_MX27
select VIDEOBUF2_DMA_CONTIG
select VIDEO_MX2_HOSTSUPPORT
---help---
This is a v4l2 driver for the i.MX27 Camera Sensor Interface
config VIDEO_ATMEL_ISI
tristate "ATMEL Image Sensor Interface (ISI) support"
depends on VIDEO_DEV && SOC_CAMERA && ARCH_AT91
select VIDEOBUF2_DMA_CONTIG
---help---
This module makes the ATMEL Image Sensor Interface available
as a v4l2 device.

View File

@@ -0,0 +1,14 @@
obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o
obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o
# soc-camera host drivers have to be linked after camera drivers
obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o
obj-$(CONFIG_VIDEO_MX1) += mx1_camera.o
obj-$(CONFIG_VIDEO_MX2) += mx2_camera.o
obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o
obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o
obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o
obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o
obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o
ccflags-y += -I$(srctree)/drivers/media/i2c/soc_camera

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,868 @@
/*
* V4L2 Driver for i.MXL/i.MXL camera (CSI) host
*
* Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
* Copyright (C) 2009, Darius Augulis <augulis.darius@gmail.com>
*
* Based on PXA SoC camera driver
* 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 version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/videodev2.h>
#include <media/soc_camera.h>
#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
#include <media/videobuf-dma-contig.h>
#include <media/soc_mediabus.h>
#include <asm/dma.h>
#include <asm/fiq.h>
#include <mach/dma-mx1-mx2.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
#include <linux/platform_data/camera-mx1.h>
/*
* CSI registers
*/
#define CSICR1 0x00 /* CSI Control Register 1 */
#define CSISR 0x08 /* CSI Status Register */
#define CSIRXR 0x10 /* CSI RxFIFO Register */
#define CSICR1_RXFF_LEVEL(x) (((x) & 0x3) << 19)
#define CSICR1_SOF_POL (1 << 17)
#define CSICR1_SOF_INTEN (1 << 16)
#define CSICR1_MCLKDIV(x) (((x) & 0xf) << 12)
#define CSICR1_MCLKEN (1 << 9)
#define CSICR1_FCC (1 << 8)
#define CSICR1_BIG_ENDIAN (1 << 7)
#define CSICR1_CLR_RXFIFO (1 << 5)
#define CSICR1_GCLK_MODE (1 << 4)
#define CSICR1_DATA_POL (1 << 2)
#define CSICR1_REDGE (1 << 1)
#define CSICR1_EN (1 << 0)
#define CSISR_SFF_OR_INT (1 << 25)
#define CSISR_RFF_OR_INT (1 << 24)
#define CSISR_STATFF_INT (1 << 21)
#define CSISR_RXFF_INT (1 << 18)
#define CSISR_SOF_INT (1 << 16)
#define CSISR_DRDY (1 << 0)
#define DRIVER_VERSION "0.0.2"
#define DRIVER_NAME "mx1-camera"
#define CSI_IRQ_MASK (CSISR_SFF_OR_INT | CSISR_RFF_OR_INT | \
CSISR_STATFF_INT | CSISR_RXFF_INT | CSISR_SOF_INT)
#define CSI_BUS_FLAGS (V4L2_MBUS_MASTER | V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | \
V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | \
V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_LOW)
#define MAX_VIDEO_MEM 16 /* Video memory limit in megabytes */
/*
* Structures
*/
/* buffer for one video frame */
struct mx1_buffer {
/* common v4l buffer stuff -- must be first */
struct videobuf_buffer vb;
enum v4l2_mbus_pixelcode code;
int inwork;
};
/*
* i.MX1/i.MXL is only supposed to handle one camera on its Camera Sensor
* Interface. If anyone ever builds hardware to enable more than
* one camera, they will have to modify this driver too
*/
struct mx1_camera_dev {
struct soc_camera_host soc_host;
struct soc_camera_device *icd;
struct mx1_camera_pdata *pdata;
struct mx1_buffer *active;
struct resource *res;
struct clk *clk;
struct list_head capture;
void __iomem *base;
int dma_chan;
unsigned int irq;
unsigned long mclk;
spinlock_t lock;
};
/*
* Videobuf operations
*/
static int mx1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
unsigned int *size)
{
struct soc_camera_device *icd = vq->priv_data;
*size = icd->sizeimage;
if (!*count)
*count = 32;
if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024)
*count = (MAX_VIDEO_MEM * 1024 * 1024) / *size;
dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size);
return 0;
}
static void free_buffer(struct videobuf_queue *vq, struct mx1_buffer *buf)
{
struct soc_camera_device *icd = vq->priv_data;
struct videobuf_buffer *vb = &buf->vb;
BUG_ON(in_interrupt());
dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
/*
* This waits until this buffer is out of danger, i.e., until it is no
* longer in STATE_QUEUED or STATE_ACTIVE
*/
videobuf_waiton(vq, vb, 0, 0);
videobuf_dma_contig_free(vq, vb);
vb->state = VIDEOBUF_NEEDS_INIT;
}
static int mx1_videobuf_prepare(struct videobuf_queue *vq,
struct videobuf_buffer *vb, enum v4l2_field field)
{
struct soc_camera_device *icd = vq->priv_data;
struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb);
int ret;
dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
/* Added list head initialization on alloc */
WARN_ON(!list_empty(&vb->queue));
BUG_ON(NULL == icd->current_fmt);
/*
* I think, in buf_prepare you only have to protect global data,
* the actual buffer is yours
*/
buf->inwork = 1;
if (buf->code != icd->current_fmt->code ||
vb->width != icd->user_width ||
vb->height != icd->user_height ||
vb->field != field) {
buf->code = icd->current_fmt->code;
vb->width = icd->user_width;
vb->height = icd->user_height;
vb->field = field;
vb->state = VIDEOBUF_NEEDS_INIT;
}
vb->size = icd->sizeimage;
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;
}
buf->inwork = 0;
return 0;
fail:
free_buffer(vq, buf);
out:
buf->inwork = 0;
return ret;
}
static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev)
{
struct videobuf_buffer *vbuf = &pcdev->active->vb;
struct device *dev = pcdev->icd->parent;
int ret;
if (unlikely(!pcdev->active)) {
dev_err(dev, "DMA End IRQ with no active buffer\n");
return -EFAULT;
}
/* setup sg list for future DMA */
ret = imx_dma_setup_single(pcdev->dma_chan,
videobuf_to_dma_contig(vbuf),
vbuf->size, pcdev->res->start +
CSIRXR, DMA_MODE_READ);
if (unlikely(ret))
dev_err(dev, "Failed to setup DMA sg list\n");
return ret;
}
/* Called under spinlock_irqsave(&pcdev->lock, ...) */
static void mx1_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->parent);
struct mx1_camera_dev *pcdev = ici->priv;
struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb);
dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
list_add_tail(&vb->queue, &pcdev->capture);
vb->state = VIDEOBUF_ACTIVE;
if (!pcdev->active) {
pcdev->active = buf;
/* setup sg list for future DMA */
if (!mx1_camera_setup_dma(pcdev)) {
unsigned int temp;
/* enable SOF irq */
temp = __raw_readl(pcdev->base + CSICR1) |
CSICR1_SOF_INTEN;
__raw_writel(temp, pcdev->base + CSICR1);
}
}
}
static void mx1_videobuf_release(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb);
#ifdef DEBUG
struct soc_camera_device *icd = vq->priv_data;
struct device *dev = icd->parent;
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(dev, "%s (active)\n", __func__);
break;
case VIDEOBUF_QUEUED:
dev_dbg(dev, "%s (queued)\n", __func__);
break;
case VIDEOBUF_PREPARED:
dev_dbg(dev, "%s (prepared)\n", __func__);
break;
default:
dev_dbg(dev, "%s (unknown)\n", __func__);
break;
}
#endif
free_buffer(vq, buf);
}
static void mx1_camera_wakeup(struct mx1_camera_dev *pcdev,
struct videobuf_buffer *vb,
struct mx1_buffer *buf)
{
/* _init is used to debug races, see comment in mx1_camera_reqbufs() */
list_del_init(&vb->queue);
vb->state = VIDEOBUF_DONE;
v4l2_get_timestamp(&vb->ts);
vb->field_count++;
wake_up(&vb->done);
if (list_empty(&pcdev->capture)) {
pcdev->active = NULL;
return;
}
pcdev->active = list_entry(pcdev->capture.next,
struct mx1_buffer, vb.queue);
/* setup sg list for future DMA */
if (likely(!mx1_camera_setup_dma(pcdev))) {
unsigned int temp;
/* enable SOF irq */
temp = __raw_readl(pcdev->base + CSICR1) | CSICR1_SOF_INTEN;
__raw_writel(temp, pcdev->base + CSICR1);
}
}
static void mx1_camera_dma_irq(int channel, void *data)
{
struct mx1_camera_dev *pcdev = data;
struct device *dev = pcdev->icd->parent;
struct mx1_buffer *buf;
struct videobuf_buffer *vb;
unsigned long flags;
spin_lock_irqsave(&pcdev->lock, flags);
imx_dma_disable(channel);
if (unlikely(!pcdev->active)) {
dev_err(dev, "DMA End IRQ with no active buffer\n");
goto out;
}
vb = &pcdev->active->vb;
buf = container_of(vb, struct mx1_buffer, vb);
WARN_ON(buf->inwork || list_empty(&vb->queue));
dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
mx1_camera_wakeup(pcdev, vb, buf);
out:
spin_unlock_irqrestore(&pcdev->lock, flags);
}
static struct videobuf_queue_ops mx1_videobuf_ops = {
.buf_setup = mx1_videobuf_setup,
.buf_prepare = mx1_videobuf_prepare,
.buf_queue = mx1_videobuf_queue,
.buf_release = mx1_videobuf_release,
};
static void mx1_camera_init_videobuf(struct videobuf_queue *q,
struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx1_camera_dev *pcdev = ici->priv;
videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, icd->parent,
&pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_NONE,
sizeof(struct mx1_buffer), icd, &ici->host_lock);
}
static int mclk_get_divisor(struct mx1_camera_dev *pcdev)
{
unsigned int mclk = pcdev->mclk;
unsigned long div;
unsigned long lcdclk;
lcdclk = clk_get_rate(pcdev->clk);
/*
* We verify platform_mclk_10khz != 0, so if anyone breaks it, here
* they get a nice Oops
*/
div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1;
dev_dbg(pcdev->icd->parent,
"System clock %lukHz, target freq %dkHz, divisor %lu\n",
lcdclk / 1000, mclk / 1000, div);
return div;
}
static void mx1_camera_activate(struct mx1_camera_dev *pcdev)
{
unsigned int csicr1 = CSICR1_EN;
dev_dbg(pcdev->icd->parent, "Activate device\n");
clk_prepare_enable(pcdev->clk);
/* enable CSI before doing anything else */
__raw_writel(csicr1, pcdev->base + CSICR1);
csicr1 |= CSICR1_MCLKEN | CSICR1_FCC | CSICR1_GCLK_MODE;
csicr1 |= CSICR1_MCLKDIV(mclk_get_divisor(pcdev));
csicr1 |= CSICR1_RXFF_LEVEL(2); /* 16 words */
__raw_writel(csicr1, pcdev->base + CSICR1);
}
static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev)
{
dev_dbg(pcdev->icd->parent, "Deactivate device\n");
/* Disable all CSI interface */
__raw_writel(0x00, pcdev->base + CSICR1);
clk_disable_unprepare(pcdev->clk);
}
/*
* The following two functions absolutely depend on the fact, that
* there can be only one camera on i.MX1/i.MXL camera sensor interface
*/
static int mx1_camera_add_device(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx1_camera_dev *pcdev = ici->priv;
if (pcdev->icd)
return -EBUSY;
dev_info(icd->parent, "MX1 Camera driver attached to camera %d\n",
icd->devnum);
mx1_camera_activate(pcdev);
pcdev->icd = icd;
return 0;
}
static void mx1_camera_remove_device(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx1_camera_dev *pcdev = ici->priv;
unsigned int csicr1;
BUG_ON(icd != pcdev->icd);
/* disable interrupts */
csicr1 = __raw_readl(pcdev->base + CSICR1) & ~CSI_IRQ_MASK;
__raw_writel(csicr1, pcdev->base + CSICR1);
/* Stop DMA engine */
imx_dma_disable(pcdev->dma_chan);
dev_info(icd->parent, "MX1 Camera driver detached from camera %d\n",
icd->devnum);
mx1_camera_deactivate(pcdev);
pcdev->icd = NULL;
}
static int mx1_camera_set_bus_param(struct soc_camera_device *icd)
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx1_camera_dev *pcdev = ici->priv;
struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
unsigned long common_flags;
unsigned int csicr1;
int ret;
/* MX1 supports only 8bit buswidth */
ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
if (!ret) {
common_flags = soc_mbus_config_compatible(&cfg, CSI_BUS_FLAGS);
if (!common_flags) {
dev_warn(icd->parent,
"Flags incompatible: camera 0x%x, host 0x%x\n",
cfg.flags, CSI_BUS_FLAGS);
return -EINVAL;
}
} else if (ret != -ENOIOCTLCMD) {
return ret;
} else {
common_flags = CSI_BUS_FLAGS;
}
/* Make choises, based on platform choice */
if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
(common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
if (!pcdev->pdata ||
pcdev->pdata->flags & MX1_CAMERA_VSYNC_HIGH)
common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
else
common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
}
if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
(common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
if (!pcdev->pdata ||
pcdev->pdata->flags & MX1_CAMERA_PCLK_RISING)
common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
else
common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
}
if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) &&
(common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) {
if (!pcdev->pdata ||
pcdev->pdata->flags & MX1_CAMERA_DATA_HIGH)
common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW;
else
common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH;
}
cfg.flags = common_flags;
ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
if (ret < 0 && ret != -ENOIOCTLCMD) {
dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
common_flags, ret);
return ret;
}
csicr1 = __raw_readl(pcdev->base + CSICR1);
if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
csicr1 |= CSICR1_REDGE;
if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
csicr1 |= CSICR1_SOF_POL;
if (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)
csicr1 |= CSICR1_DATA_POL;
__raw_writel(csicr1, pcdev->base + CSICR1);
return 0;
}
static int mx1_camera_set_fmt(struct soc_camera_device *icd,
struct v4l2_format *f)
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
const struct soc_camera_format_xlate *xlate;
struct v4l2_pix_format *pix = &f->fmt.pix;
struct v4l2_mbus_framefmt mf;
int ret, buswidth;
xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
if (!xlate) {
dev_warn(icd->parent, "Format %x not found\n",
pix->pixelformat);
return -EINVAL;
}
buswidth = xlate->host_fmt->bits_per_sample;
if (buswidth > 8) {
dev_warn(icd->parent,
"bits-per-sample %d for format %x unsupported\n",
buswidth, pix->pixelformat);
return -EINVAL;
}
mf.width = pix->width;
mf.height = pix->height;
mf.field = pix->field;
mf.colorspace = pix->colorspace;
mf.code = xlate->code;
ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
if (ret < 0)
return ret;
if (mf.code != xlate->code)
return -EINVAL;
pix->width = mf.width;
pix->height = mf.height;
pix->field = mf.field;
pix->colorspace = mf.colorspace;
icd->current_fmt = xlate;
return ret;
}
static int mx1_camera_try_fmt(struct soc_camera_device *icd,
struct v4l2_format *f)
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
const struct soc_camera_format_xlate *xlate;
struct v4l2_pix_format *pix = &f->fmt.pix;
struct v4l2_mbus_framefmt mf;
int ret;
/* TODO: limit to mx1 hardware capabilities */
xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
if (!xlate) {
dev_warn(icd->parent, "Format %x not found\n",
pix->pixelformat);
return -EINVAL;
}
mf.width = pix->width;
mf.height = pix->height;
mf.field = pix->field;
mf.colorspace = pix->colorspace;
mf.code = xlate->code;
/* limit to sensor capabilities */
ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
if (ret < 0)
return ret;
pix->width = mf.width;
pix->height = mf.height;
pix->field = mf.field;
pix->colorspace = mf.colorspace;
return 0;
}
static int mx1_camera_reqbufs(struct soc_camera_device *icd,
struct v4l2_requestbuffers *p)
{
int i;
/*
* This is for locking debugging only. I removed spinlocks and now I
* check whether .prepare is ever called on a linked buffer, or whether
* a dma IRQ can occur for an in-work or unlinked buffer. Until now
* it hadn't triggered
*/
for (i = 0; i < p->count; i++) {
struct mx1_buffer *buf = container_of(icd->vb_vidq.bufs[i],
struct mx1_buffer, vb);
buf->inwork = 0;
INIT_LIST_HEAD(&buf->vb.queue);
}
return 0;
}
static unsigned int mx1_camera_poll(struct file *file, poll_table *pt)
{
struct soc_camera_device *icd = file->private_data;
struct mx1_buffer *buf;
buf = list_entry(icd->vb_vidq.stream.next, struct mx1_buffer,
vb.stream);
poll_wait(file, &buf->vb.done, pt);
if (buf->vb.state == VIDEOBUF_DONE ||
buf->vb.state == VIDEOBUF_ERROR)
return POLLIN | POLLRDNORM;
return 0;
}
static int mx1_camera_querycap(struct soc_camera_host *ici,
struct v4l2_capability *cap)
{
/* cap->name is set by the friendly caller:-> */
strlcpy(cap->card, "i.MX1/i.MXL Camera", sizeof(cap->card));
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
return 0;
}
static struct soc_camera_host_ops mx1_soc_camera_host_ops = {
.owner = THIS_MODULE,
.add = mx1_camera_add_device,
.remove = mx1_camera_remove_device,
.set_bus_param = mx1_camera_set_bus_param,
.set_fmt = mx1_camera_set_fmt,
.try_fmt = mx1_camera_try_fmt,
.init_videobuf = mx1_camera_init_videobuf,
.reqbufs = mx1_camera_reqbufs,
.poll = mx1_camera_poll,
.querycap = mx1_camera_querycap,
};
static struct fiq_handler fh = {
.name = "csi_sof"
};
static int __init mx1_camera_probe(struct platform_device *pdev)
{
struct mx1_camera_dev *pcdev;
struct resource *res;
struct pt_regs regs;
struct clk *clk;
void __iomem *base;
unsigned int irq;
int err = 0;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
if (!res || (int)irq <= 0) {
err = -ENODEV;
goto exit;
}
clk = clk_get(&pdev->dev, "csi_clk");
if (IS_ERR(clk)) {
err = PTR_ERR(clk);
goto exit;
}
pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL);
if (!pcdev) {
dev_err(&pdev->dev, "Could not allocate pcdev\n");
err = -ENOMEM;
goto exit_put_clk;
}
pcdev->res = res;
pcdev->clk = clk;
pcdev->pdata = pdev->dev.platform_data;
if (pcdev->pdata)
pcdev->mclk = pcdev->pdata->mclk_10khz * 10000;
if (!pcdev->mclk) {
dev_warn(&pdev->dev,
"mclk_10khz == 0! Please, fix your platform data. "
"Using default 20MHz\n");
pcdev->mclk = 20000000;
}
INIT_LIST_HEAD(&pcdev->capture);
spin_lock_init(&pcdev->lock);
/*
* Request the regions.
*/
if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME)) {
err = -EBUSY;
goto exit_kfree;
}
base = ioremap(res->start, resource_size(res));
if (!base) {
err = -ENOMEM;
goto exit_release;
}
pcdev->irq = irq;
pcdev->base = base;
/* request dma */
pcdev->dma_chan = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_HIGH);
if (pcdev->dma_chan < 0) {
dev_err(&pdev->dev, "Can't request DMA for MX1 CSI\n");
err = -EBUSY;
goto exit_iounmap;
}
dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_chan);
imx_dma_setup_handlers(pcdev->dma_chan, mx1_camera_dma_irq, NULL,
pcdev);
imx_dma_config_channel(pcdev->dma_chan, IMX_DMA_TYPE_FIFO,
IMX_DMA_MEMSIZE_32, MX1_DMA_REQ_CSI_R, 0);
/* burst length : 16 words = 64 bytes */
imx_dma_config_burstlen(pcdev->dma_chan, 0);
/* request irq */
err = claim_fiq(&fh);
if (err) {
dev_err(&pdev->dev, "Camera interrupt register failed\n");
goto exit_free_dma;
}
set_fiq_handler(&mx1_camera_sof_fiq_start, &mx1_camera_sof_fiq_end -
&mx1_camera_sof_fiq_start);
regs.ARM_r8 = (long)MX1_DMA_DIMR;
regs.ARM_r9 = (long)MX1_DMA_CCR(pcdev->dma_chan);
regs.ARM_r10 = (long)pcdev->base + CSICR1;
regs.ARM_fp = (long)pcdev->base + CSISR;
regs.ARM_sp = 1 << pcdev->dma_chan;
set_fiq_regs(&regs);
mxc_set_irq_fiq(irq, 1);
enable_fiq(irq);
pcdev->soc_host.drv_name = DRIVER_NAME;
pcdev->soc_host.ops = &mx1_soc_camera_host_ops;
pcdev->soc_host.priv = pcdev;
pcdev->soc_host.v4l2_dev.dev = &pdev->dev;
pcdev->soc_host.nr = pdev->id;
err = soc_camera_host_register(&pcdev->soc_host);
if (err)
goto exit_free_irq;
dev_info(&pdev->dev, "MX1 Camera driver loaded\n");
return 0;
exit_free_irq:
disable_fiq(irq);
mxc_set_irq_fiq(irq, 0);
release_fiq(&fh);
exit_free_dma:
imx_dma_free(pcdev->dma_chan);
exit_iounmap:
iounmap(base);
exit_release:
release_mem_region(res->start, resource_size(res));
exit_kfree:
kfree(pcdev);
exit_put_clk:
clk_put(clk);
exit:
return err;
}
static int __exit mx1_camera_remove(struct platform_device *pdev)
{
struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
struct mx1_camera_dev *pcdev = container_of(soc_host,
struct mx1_camera_dev, soc_host);
struct resource *res;
imx_dma_free(pcdev->dma_chan);
disable_fiq(pcdev->irq);
mxc_set_irq_fiq(pcdev->irq, 0);
release_fiq(&fh);
clk_put(pcdev->clk);
soc_camera_host_unregister(soc_host);
iounmap(pcdev->base);
res = pcdev->res;
release_mem_region(res->start, resource_size(res));
kfree(pcdev);
dev_info(&pdev->dev, "MX1 Camera driver unloaded\n");
return 0;
}
static struct platform_driver mx1_camera_driver = {
.driver = {
.name = DRIVER_NAME,
},
.remove = __exit_p(mx1_camera_remove),
};
module_platform_driver_probe(mx1_camera_driver, mx1_camera_probe);
MODULE_DESCRIPTION("i.MX1/i.MXL SoC Camera Host driver");
MODULE_AUTHOR("Paulius Zaleckas <paulius.zaleckas@teltonika.lt>");
MODULE_LICENSE("GPL v2");
MODULE_VERSION(DRIVER_VERSION);
MODULE_ALIAS("platform:" DRIVER_NAME);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,382 @@
/*
* Driver for the SH-Mobile MIPI CSI-2 unit
*
* Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <linux/module.h>
#include <media/sh_mobile_ceu.h>
#include <media/sh_mobile_csi2.h>
#include <media/soc_camera.h>
#include <media/soc_mediabus.h>
#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mediabus.h>
#include <media/v4l2-subdev.h>
#define SH_CSI2_TREF 0x00
#define SH_CSI2_SRST 0x04
#define SH_CSI2_PHYCNT 0x08
#define SH_CSI2_CHKSUM 0x0C
#define SH_CSI2_VCDT 0x10
struct sh_csi2 {
struct v4l2_subdev subdev;
struct list_head list;
unsigned int irq;
unsigned long mipi_flags;
void __iomem *base;
struct platform_device *pdev;
struct sh_csi2_client_config *client;
};
static int sh_csi2_try_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
if (mf->width > 8188)
mf->width = 8188;
else if (mf->width & 1)
mf->width &= ~1;
switch (pdata->type) {
case SH_CSI2C:
switch (mf->code) {
case V4L2_MBUS_FMT_UYVY8_2X8: /* YUV422 */
case V4L2_MBUS_FMT_YUYV8_1_5X8: /* YUV420 */
case V4L2_MBUS_FMT_Y8_1X8: /* RAW8 */
case V4L2_MBUS_FMT_SBGGR8_1X8:
case V4L2_MBUS_FMT_SGRBG8_1X8:
break;
default:
/* All MIPI CSI-2 devices must support one of primary formats */
mf->code = V4L2_MBUS_FMT_YUYV8_2X8;
}
break;
case SH_CSI2I:
switch (mf->code) {
case V4L2_MBUS_FMT_Y8_1X8: /* RAW8 */
case V4L2_MBUS_FMT_SBGGR8_1X8:
case V4L2_MBUS_FMT_SGRBG8_1X8:
case V4L2_MBUS_FMT_SBGGR10_1X10: /* RAW10 */
case V4L2_MBUS_FMT_SBGGR12_1X12: /* RAW12 */
break;
default:
/* All MIPI CSI-2 devices must support one of primary formats */
mf->code = V4L2_MBUS_FMT_SBGGR8_1X8;
}
break;
}
return 0;
}
/*
* We have done our best in try_fmt to try and tell the sensor, which formats
* we support. If now the configuration is unsuitable for us we can only
* error out.
*/
static int sh_csi2_s_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
u32 tmp = (priv->client->channel & 3) << 8;
dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code);
if (mf->width > 8188 || mf->width & 1)
return -EINVAL;
switch (mf->code) {
case V4L2_MBUS_FMT_UYVY8_2X8:
tmp |= 0x1e; /* YUV422 8 bit */
break;
case V4L2_MBUS_FMT_YUYV8_1_5X8:
tmp |= 0x18; /* YUV420 8 bit */
break;
case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE:
tmp |= 0x21; /* RGB555 */
break;
case V4L2_MBUS_FMT_RGB565_2X8_BE:
tmp |= 0x22; /* RGB565 */
break;
case V4L2_MBUS_FMT_Y8_1X8:
case V4L2_MBUS_FMT_SBGGR8_1X8:
case V4L2_MBUS_FMT_SGRBG8_1X8:
tmp |= 0x2a; /* RAW8 */
break;
default:
return -EINVAL;
}
iowrite32(tmp, priv->base + SH_CSI2_VCDT);
return 0;
}
static int sh_csi2_g_mbus_config(struct v4l2_subdev *sd,
struct v4l2_mbus_config *cfg)
{
cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING |
V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH;
cfg->type = V4L2_MBUS_PARALLEL;
return 0;
}
static int sh_csi2_s_mbus_config(struct v4l2_subdev *sd,
const struct v4l2_mbus_config *cfg)
{
struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd);
struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,
.flags = priv->mipi_flags};
return v4l2_subdev_call(client_sd, video, s_mbus_config, &client_cfg);
}
static struct v4l2_subdev_video_ops sh_csi2_subdev_video_ops = {
.s_mbus_fmt = sh_csi2_s_fmt,
.try_mbus_fmt = sh_csi2_try_fmt,
.g_mbus_config = sh_csi2_g_mbus_config,
.s_mbus_config = sh_csi2_s_mbus_config,
};
static void sh_csi2_hwinit(struct sh_csi2 *priv)
{
struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
__u32 tmp = 0x10; /* Enable MIPI CSI clock lane */
/* Reflect registers immediately */
iowrite32(0x00000001, priv->base + SH_CSI2_TREF);
/* reset CSI2 harware */
iowrite32(0x00000001, priv->base + SH_CSI2_SRST);
udelay(5);
iowrite32(0x00000000, priv->base + SH_CSI2_SRST);
switch (pdata->type) {
case SH_CSI2C:
if (priv->client->lanes == 1)
tmp |= 1;
else
/* Default - both lanes */
tmp |= 3;
break;
case SH_CSI2I:
if (!priv->client->lanes || priv->client->lanes > 4)
/* Default - all 4 lanes */
tmp |= 0xf;
else
tmp |= (1 << priv->client->lanes) - 1;
}
if (priv->client->phy == SH_CSI2_PHY_MAIN)
tmp |= 0x8000;
iowrite32(tmp, priv->base + SH_CSI2_PHYCNT);
tmp = 0;
if (pdata->flags & SH_CSI2_ECC)
tmp |= 2;
if (pdata->flags & SH_CSI2_CRC)
tmp |= 1;
iowrite32(tmp, priv->base + SH_CSI2_CHKSUM);
}
static int sh_csi2_client_connect(struct sh_csi2 *priv)
{
struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
struct soc_camera_device *icd = v4l2_get_subdev_hostdata(&priv->subdev);
struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
struct device *dev = v4l2_get_subdevdata(&priv->subdev);
struct v4l2_mbus_config cfg;
unsigned long common_flags, csi2_flags;
int i, ret;
if (priv->client)
return -EBUSY;
for (i = 0; i < pdata->num_clients; i++)
if (&pdata->clients[i].pdev->dev == icd->pdev)
break;
dev_dbg(dev, "%s(%p): found #%d\n", __func__, dev, i);
if (i == pdata->num_clients)
return -ENODEV;
/* Check if we can support this camera */
csi2_flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK | V4L2_MBUS_CSI2_1_LANE;
switch (pdata->type) {
case SH_CSI2C:
if (pdata->clients[i].lanes != 1)
csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
break;
case SH_CSI2I:
switch (pdata->clients[i].lanes) {
default:
csi2_flags |= V4L2_MBUS_CSI2_4_LANE;
case 3:
csi2_flags |= V4L2_MBUS_CSI2_3_LANE;
case 2:
csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
}
}
cfg.type = V4L2_MBUS_CSI2;
ret = v4l2_subdev_call(client_sd, video, g_mbus_config, &cfg);
if (ret == -ENOIOCTLCMD)
common_flags = csi2_flags;
else if (!ret)
common_flags = soc_mbus_config_compatible(&cfg,
csi2_flags);
else
common_flags = 0;
if (!common_flags)
return -EINVAL;
/* All good: camera MIPI configuration supported */
priv->mipi_flags = common_flags;
priv->client = pdata->clients + i;
pm_runtime_get_sync(dev);
sh_csi2_hwinit(priv);
return 0;
}
static void sh_csi2_client_disconnect(struct sh_csi2 *priv)
{
if (!priv->client)
return;
priv->client = NULL;
pm_runtime_put(v4l2_get_subdevdata(&priv->subdev));
}
static int sh_csi2_s_power(struct v4l2_subdev *sd, int on)
{
struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
if (on)
return sh_csi2_client_connect(priv);
sh_csi2_client_disconnect(priv);
return 0;
}
static struct v4l2_subdev_core_ops sh_csi2_subdev_core_ops = {
.s_power = sh_csi2_s_power,
};
static struct v4l2_subdev_ops sh_csi2_subdev_ops = {
.core = &sh_csi2_subdev_core_ops,
.video = &sh_csi2_subdev_video_ops,
};
static int sh_csi2_probe(struct platform_device *pdev)
{
struct resource *res;
unsigned int irq;
int ret;
struct sh_csi2 *priv;
/* Platform data specify the PHY, lanes, ECC, CRC */
struct sh_csi2_pdata *pdata = pdev->dev.platform_data;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
/* Interrupt unused so far */
irq = platform_get_irq(pdev, 0);
if (!res || (int)irq <= 0 || !pdata) {
dev_err(&pdev->dev, "Not enough CSI2 platform resources.\n");
return -ENODEV;
}
/* TODO: Add support for CSI2I. Careful: different register layout! */
if (pdata->type != SH_CSI2C) {
dev_err(&pdev->dev, "Only CSI2C supported ATM.\n");
return -EINVAL;
}
priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_csi2), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->irq = irq;
priv->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
priv->pdev = pdev;
platform_set_drvdata(pdev, priv);
v4l2_subdev_init(&priv->subdev, &sh_csi2_subdev_ops);
v4l2_set_subdevdata(&priv->subdev, &pdev->dev);
snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.mipi-csi",
dev_name(pdata->v4l2_dev->dev));
ret = v4l2_device_register_subdev(pdata->v4l2_dev, &priv->subdev);
dev_dbg(&pdev->dev, "%s(%p): ret(register_subdev) = %d\n", __func__, priv, ret);
if (ret < 0)
goto esdreg;
pm_runtime_enable(&pdev->dev);
dev_dbg(&pdev->dev, "CSI2 probed.\n");
return 0;
esdreg:
platform_set_drvdata(pdev, NULL);
return ret;
}
static int sh_csi2_remove(struct platform_device *pdev)
{
struct sh_csi2 *priv = platform_get_drvdata(pdev);
v4l2_device_unregister_subdev(&priv->subdev);
pm_runtime_disable(&pdev->dev);
platform_set_drvdata(pdev, NULL);
return 0;
}
static struct platform_driver __refdata sh_csi2_pdrv = {
.remove = sh_csi2_remove,
.probe = sh_csi2_probe,
.driver = {
.name = "sh-mobile-csi2",
.owner = THIS_MODULE,
},
};
module_platform_driver(sh_csi2_pdrv);
MODULE_DESCRIPTION("SH-Mobile MIPI CSI-2 driver");
MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:sh-mobile-csi2");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,204 @@
/*
* Generic Platform Camera Driver
*
* Copyright (C) 2008 Magnus Damm
* Based on mt9m001 driver,
* 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 version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/videodev2.h>
#include <media/v4l2-subdev.h>
#include <media/soc_camera.h>
#include <media/soc_camera_platform.h>
struct soc_camera_platform_priv {
struct v4l2_subdev subdev;
};
static struct soc_camera_platform_priv *get_priv(struct platform_device *pdev)
{
struct v4l2_subdev *subdev = platform_get_drvdata(pdev);
return container_of(subdev, struct soc_camera_platform_priv, subdev);
}
static int soc_camera_platform_s_stream(struct v4l2_subdev *sd, int enable)
{
struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
return p->set_capture(p, enable);
}
static int soc_camera_platform_fill_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
mf->width = p->format.width;
mf->height = p->format.height;
mf->code = p->format.code;
mf->colorspace = p->format.colorspace;
mf->field = p->format.field;
return 0;
}
static int soc_camera_platform_s_power(struct v4l2_subdev *sd, int on)
{
struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
return soc_camera_set_power(p->icd->control, &p->icd->sdesc->subdev_desc, on);
}
static struct v4l2_subdev_core_ops platform_subdev_core_ops = {
.s_power = soc_camera_platform_s_power,
};
static int soc_camera_platform_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
enum v4l2_mbus_pixelcode *code)
{
struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
if (index)
return -EINVAL;
*code = p->format.code;
return 0;
}
static int soc_camera_platform_g_crop(struct v4l2_subdev *sd,
struct v4l2_crop *a)
{
struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
a->c.left = 0;
a->c.top = 0;
a->c.width = p->format.width;
a->c.height = p->format.height;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
return 0;
}
static int soc_camera_platform_cropcap(struct v4l2_subdev *sd,
struct v4l2_cropcap *a)
{
struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
a->bounds.left = 0;
a->bounds.top = 0;
a->bounds.width = p->format.width;
a->bounds.height = p->format.height;
a->defrect = a->bounds;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
a->pixelaspect.numerator = 1;
a->pixelaspect.denominator = 1;
return 0;
}
static int soc_camera_platform_g_mbus_config(struct v4l2_subdev *sd,
struct v4l2_mbus_config *cfg)
{
struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
cfg->flags = p->mbus_param;
cfg->type = p->mbus_type;
return 0;
}
static struct v4l2_subdev_video_ops platform_subdev_video_ops = {
.s_stream = soc_camera_platform_s_stream,
.enum_mbus_fmt = soc_camera_platform_enum_fmt,
.cropcap = soc_camera_platform_cropcap,
.g_crop = soc_camera_platform_g_crop,
.try_mbus_fmt = soc_camera_platform_fill_fmt,
.g_mbus_fmt = soc_camera_platform_fill_fmt,
.s_mbus_fmt = soc_camera_platform_fill_fmt,
.g_mbus_config = soc_camera_platform_g_mbus_config,
};
static struct v4l2_subdev_ops platform_subdev_ops = {
.core = &platform_subdev_core_ops,
.video = &platform_subdev_video_ops,
};
static int soc_camera_platform_probe(struct platform_device *pdev)
{
struct soc_camera_host *ici;
struct soc_camera_platform_priv *priv;
struct soc_camera_platform_info *p = pdev->dev.platform_data;
struct soc_camera_device *icd;
int ret;
if (!p)
return -EINVAL;
if (!p->icd) {
dev_err(&pdev->dev,
"Platform has not set soc_camera_device pointer!\n");
return -EINVAL;
}
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
icd = p->icd;
/* soc-camera convention: control's drvdata points to the subdev */
platform_set_drvdata(pdev, &priv->subdev);
/* Set the control device reference */
icd->control = &pdev->dev;
ici = to_soc_camera_host(icd->parent);
v4l2_subdev_init(&priv->subdev, &platform_subdev_ops);
v4l2_set_subdevdata(&priv->subdev, p);
strncpy(priv->subdev.name, dev_name(&pdev->dev), V4L2_SUBDEV_NAME_SIZE);
ret = v4l2_device_register_subdev(&ici->v4l2_dev, &priv->subdev);
if (ret)
goto evdrs;
return ret;
evdrs:
platform_set_drvdata(pdev, NULL);
return ret;
}
static int soc_camera_platform_remove(struct platform_device *pdev)
{
struct soc_camera_platform_priv *priv = get_priv(pdev);
struct soc_camera_platform_info *p = v4l2_get_subdevdata(&priv->subdev);
p->icd->control = NULL;
v4l2_device_unregister_subdev(&priv->subdev);
platform_set_drvdata(pdev, NULL);
return 0;
}
static struct platform_driver soc_camera_platform_driver = {
.driver = {
.name = "soc_camera_platform",
.owner = THIS_MODULE,
},
.probe = soc_camera_platform_probe,
.remove = soc_camera_platform_remove,
};
module_platform_driver(soc_camera_platform_driver);
MODULE_DESCRIPTION("SoC Camera Platform driver");
MODULE_AUTHOR("Magnus Damm");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:soc_camera_platform");

View File

@@ -0,0 +1,529 @@
/*
* soc-camera media bus helper routines
*
* Copyright (C) 2009, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mediabus.h>
#include <media/soc_mediabus.h>
static const struct soc_mbus_lookup mbus_fmt[] = {
{
.code = V4L2_MBUS_FMT_YUYV8_2X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_YUYV,
.name = "YUYV",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_YVYU8_2X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_YVYU,
.name = "YVYU",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_UYVY8_2X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_UYVY,
.name = "UYVY",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_VYUY8_2X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_VYUY,
.name = "VYUY",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
.fmt = {
.fourcc = V4L2_PIX_FMT_RGB555,
.name = "RGB555",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE,
.fmt = {
.fourcc = V4L2_PIX_FMT_RGB555X,
.name = "RGB555X",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_BE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_RGB565_2X8_LE,
.fmt = {
.fourcc = V4L2_PIX_FMT_RGB565,
.name = "RGB565",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_RGB565_2X8_BE,
.fmt = {
.fourcc = V4L2_PIX_FMT_RGB565X,
.name = "RGB565X",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_BE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_RGB666_1X18,
.fmt = {
.fourcc = V4L2_PIX_FMT_RGB32,
.name = "RGB666/32bpp",
.bits_per_sample = 18,
.packing = SOC_MBUS_PACKING_EXTEND32,
.order = SOC_MBUS_ORDER_LE,
},
}, {
.code = V4L2_MBUS_FMT_RGB888_1X24,
.fmt = {
.fourcc = V4L2_PIX_FMT_RGB32,
.name = "RGB888/32bpp",
.bits_per_sample = 24,
.packing = SOC_MBUS_PACKING_EXTEND32,
.order = SOC_MBUS_ORDER_LE,
},
}, {
.code = V4L2_MBUS_FMT_RGB888_2X12_BE,
.fmt = {
.fourcc = V4L2_PIX_FMT_RGB32,
.name = "RGB888/32bpp",
.bits_per_sample = 12,
.packing = SOC_MBUS_PACKING_EXTEND32,
.order = SOC_MBUS_ORDER_BE,
},
}, {
.code = V4L2_MBUS_FMT_RGB888_2X12_LE,
.fmt = {
.fourcc = V4L2_PIX_FMT_RGB32,
.name = "RGB888/32bpp",
.bits_per_sample = 12,
.packing = SOC_MBUS_PACKING_EXTEND32,
.order = SOC_MBUS_ORDER_LE,
},
}, {
.code = V4L2_MBUS_FMT_SBGGR8_1X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_SBGGR8,
.name = "Bayer 8 BGGR",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_NONE,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SBGGR10_1X10,
.fmt = {
.fourcc = V4L2_PIX_FMT_SBGGR10,
.name = "Bayer 10 BGGR",
.bits_per_sample = 10,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_Y8_1X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_GREY,
.name = "Grey",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_NONE,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_Y10_1X10,
.fmt = {
.fourcc = V4L2_PIX_FMT_Y10,
.name = "Grey 10bit",
.bits_per_sample = 10,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE,
.fmt = {
.fourcc = V4L2_PIX_FMT_SBGGR10,
.name = "Bayer 10 BGGR",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE,
.fmt = {
.fourcc = V4L2_PIX_FMT_SBGGR10,
.name = "Bayer 10 BGGR",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADLO,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE,
.fmt = {
.fourcc = V4L2_PIX_FMT_SBGGR10,
.name = "Bayer 10 BGGR",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_BE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE,
.fmt = {
.fourcc = V4L2_PIX_FMT_SBGGR10,
.name = "Bayer 10 BGGR",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADLO,
.order = SOC_MBUS_ORDER_BE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_JPEG_1X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_JPEG,
.name = "JPEG",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_VARIABLE,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE,
.fmt = {
.fourcc = V4L2_PIX_FMT_RGB444,
.name = "RGB444",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_BE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_YUYV8_1_5X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_YUV420,
.name = "YUYV 4:2:0",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_1_5X8,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_YVYU8_1_5X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_YVU420,
.name = "YVYU 4:2:0",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_1_5X8,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_UYVY8_1X16,
.fmt = {
.fourcc = V4L2_PIX_FMT_UYVY,
.name = "UYVY 16bit",
.bits_per_sample = 16,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_VYUY8_1X16,
.fmt = {
.fourcc = V4L2_PIX_FMT_VYUY,
.name = "VYUY 16bit",
.bits_per_sample = 16,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_YUYV8_1X16,
.fmt = {
.fourcc = V4L2_PIX_FMT_YUYV,
.name = "YUYV 16bit",
.bits_per_sample = 16,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_YVYU8_1X16,
.fmt = {
.fourcc = V4L2_PIX_FMT_YVYU,
.name = "YVYU 16bit",
.bits_per_sample = 16,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SGRBG8_1X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_SGRBG8,
.name = "Bayer 8 GRBG",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_NONE,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_SGRBG10DPCM8,
.name = "Bayer 10 BGGR DPCM 8",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_NONE,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SGBRG10_1X10,
.fmt = {
.fourcc = V4L2_PIX_FMT_SGBRG10,
.name = "Bayer 10 GBRG",
.bits_per_sample = 10,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SGRBG10_1X10,
.fmt = {
.fourcc = V4L2_PIX_FMT_SGRBG10,
.name = "Bayer 10 GRBG",
.bits_per_sample = 10,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SRGGB10_1X10,
.fmt = {
.fourcc = V4L2_PIX_FMT_SRGGB10,
.name = "Bayer 10 RGGB",
.bits_per_sample = 10,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SBGGR12_1X12,
.fmt = {
.fourcc = V4L2_PIX_FMT_SBGGR12,
.name = "Bayer 12 BGGR",
.bits_per_sample = 12,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SGBRG12_1X12,
.fmt = {
.fourcc = V4L2_PIX_FMT_SGBRG12,
.name = "Bayer 12 GBRG",
.bits_per_sample = 12,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SGRBG12_1X12,
.fmt = {
.fourcc = V4L2_PIX_FMT_SGRBG12,
.name = "Bayer 12 GRBG",
.bits_per_sample = 12,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SRGGB12_1X12,
.fmt = {
.fourcc = V4L2_PIX_FMT_SRGGB12,
.name = "Bayer 12 RGGB",
.bits_per_sample = 12,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
},
};
int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf,
unsigned int *numerator, unsigned int *denominator)
{
switch (mf->packing) {
case SOC_MBUS_PACKING_NONE:
case SOC_MBUS_PACKING_EXTEND16:
*numerator = 1;
*denominator = 1;
return 0;
case SOC_MBUS_PACKING_EXTEND32:
*numerator = 1;
*denominator = 1;
return 0;
case SOC_MBUS_PACKING_2X8_PADHI:
case SOC_MBUS_PACKING_2X8_PADLO:
*numerator = 2;
*denominator = 1;
return 0;
case SOC_MBUS_PACKING_1_5X8:
*numerator = 3;
*denominator = 2;
return 0;
case SOC_MBUS_PACKING_VARIABLE:
*numerator = 0;
*denominator = 1;
return 0;
}
return -EINVAL;
}
EXPORT_SYMBOL(soc_mbus_samples_per_pixel);
s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf)
{
if (mf->layout != SOC_MBUS_LAYOUT_PACKED)
return width * mf->bits_per_sample / 8;
switch (mf->packing) {
case SOC_MBUS_PACKING_NONE:
return width * mf->bits_per_sample / 8;
case SOC_MBUS_PACKING_2X8_PADHI:
case SOC_MBUS_PACKING_2X8_PADLO:
case SOC_MBUS_PACKING_EXTEND16:
return width * 2;
case SOC_MBUS_PACKING_1_5X8:
return width * 3 / 2;
case SOC_MBUS_PACKING_VARIABLE:
return 0;
case SOC_MBUS_PACKING_EXTEND32:
return width * 4;
}
return -EINVAL;
}
EXPORT_SYMBOL(soc_mbus_bytes_per_line);
s32 soc_mbus_image_size(const struct soc_mbus_pixelfmt *mf,
u32 bytes_per_line, u32 height)
{
if (mf->layout == SOC_MBUS_LAYOUT_PACKED)
return bytes_per_line * height;
switch (mf->packing) {
case SOC_MBUS_PACKING_2X8_PADHI:
case SOC_MBUS_PACKING_2X8_PADLO:
return bytes_per_line * height * 2;
case SOC_MBUS_PACKING_1_5X8:
return bytes_per_line * height * 3 / 2;
default:
return -EINVAL;
}
}
EXPORT_SYMBOL(soc_mbus_image_size);
const struct soc_mbus_pixelfmt *soc_mbus_find_fmtdesc(
enum v4l2_mbus_pixelcode code,
const struct soc_mbus_lookup *lookup,
int n)
{
int i;
for (i = 0; i < n; i++)
if (lookup[i].code == code)
return &lookup[i].fmt;
return NULL;
}
EXPORT_SYMBOL(soc_mbus_find_fmtdesc);
const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc(
enum v4l2_mbus_pixelcode code)
{
return soc_mbus_find_fmtdesc(code, mbus_fmt, ARRAY_SIZE(mbus_fmt));
}
EXPORT_SYMBOL(soc_mbus_get_fmtdesc);
unsigned int soc_mbus_config_compatible(const struct v4l2_mbus_config *cfg,
unsigned int flags)
{
unsigned long common_flags;
bool hsync = true, vsync = true, pclk, data, mode;
bool mipi_lanes, mipi_clock;
common_flags = cfg->flags & flags;
switch (cfg->type) {
case V4L2_MBUS_PARALLEL:
hsync = common_flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH |
V4L2_MBUS_HSYNC_ACTIVE_LOW);
vsync = common_flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH |
V4L2_MBUS_VSYNC_ACTIVE_LOW);
case V4L2_MBUS_BT656:
pclk = common_flags & (V4L2_MBUS_PCLK_SAMPLE_RISING |
V4L2_MBUS_PCLK_SAMPLE_FALLING);
data = common_flags & (V4L2_MBUS_DATA_ACTIVE_HIGH |
V4L2_MBUS_DATA_ACTIVE_LOW);
mode = common_flags & (V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE);
return (!hsync || !vsync || !pclk || !data || !mode) ?
0 : common_flags;
case V4L2_MBUS_CSI2:
mipi_lanes = common_flags & V4L2_MBUS_CSI2_LANES;
mipi_clock = common_flags & (V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK |
V4L2_MBUS_CSI2_CONTINUOUS_CLOCK);
return (!mipi_lanes || !mipi_clock) ? 0 : common_flags;
}
return 0;
}
EXPORT_SYMBOL(soc_mbus_config_compatible);
static int __init soc_mbus_init(void)
{
return 0;
}
static void __exit soc_mbus_exit(void)
{
}
module_init(soc_mbus_init);
module_exit(soc_mbus_exit);
MODULE_DESCRIPTION("soc-camera media bus interface");
MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
MODULE_LICENSE("GPL v2");