forked from Minki/linux
Merge branch 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
* 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (144 commits) [media] saa7134.h: Suppress compiler warnings when CONFIG_VIDEO_SAA7134_RC is not set [media] it913x [VER 1.07] Support for single ITE 9135 devices [media] Support for Terratec G1 [media] cx25821: off by one in cx25821_vidioc_s_input() [media] media: tea5764: reconcile Kconfig symbol and macro [media] omap_vout: Add poll() support [media] omap3isp: preview: Add crop support on the sink pad [media] omap3isp: preview: Rename min/max input/output sizes defines [media] omap3isp: preview: Remove horizontal averager support [media] omap3isp: Report the ISP revision through the media controller API [media] omap3isp: ccdc: remove redundant operation [media] omap3isp: Fix memory leaks in initialization error paths [media] omap3isp: Add missing mutex_destroy() calls [media] omap3isp: Move *_init_entities() functions to the init/cleanup section [media] omap3isp: Move media_entity_cleanup() from unregister() to cleanup() [media] MFC: Change MFC firmware binary name [media] vb2: add vb2_get_unmapped_area in vb2 core [media] v4l: Add v4l2 subdev driver for S5K6AAFX sensor [media] v4l: Add AUTO option for the V4L2_CID_POWER_LINE_FREQUENCY control [media] media: ov6650: stylistic improvements ...
This commit is contained in:
commit
1046a2c428
@ -2486,6 +2486,9 @@ ioctls.</para>
|
||||
<listitem>
|
||||
<para>Flash API. <xref linkend="flash-controls" /></para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>&VIDIOC-CREATE-BUFS; and &VIDIOC-PREPARE-BUF; ioctls.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
|
||||
|
@ -232,8 +232,9 @@ control is deprecated. New drivers and applications should use the
|
||||
<entry>Enables a power line frequency filter to avoid
|
||||
flicker. Possible values for <constant>enum v4l2_power_line_frequency</constant> are:
|
||||
<constant>V4L2_CID_POWER_LINE_FREQUENCY_DISABLED</constant> (0),
|
||||
<constant>V4L2_CID_POWER_LINE_FREQUENCY_50HZ</constant> (1) and
|
||||
<constant>V4L2_CID_POWER_LINE_FREQUENCY_60HZ</constant> (2).</entry>
|
||||
<constant>V4L2_CID_POWER_LINE_FREQUENCY_50HZ</constant> (1),
|
||||
<constant>V4L2_CID_POWER_LINE_FREQUENCY_60HZ</constant> (2) and
|
||||
<constant>V4L2_CID_POWER_LINE_FREQUENCY_AUTO</constant> (3).</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>V4L2_CID_HUE_AUTO</constant></entry>
|
||||
|
@ -927,6 +927,33 @@ ioctl is called.</entry>
|
||||
Applications set or clear this flag before calling the
|
||||
<constant>VIDIOC_QBUF</constant> ioctl.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>V4L2_BUF_FLAG_PREPARED</constant></entry>
|
||||
<entry>0x0400</entry>
|
||||
<entry>The buffer has been prepared for I/O and can be queued by the
|
||||
application. Drivers set or clear this flag when the
|
||||
<link linkend="vidioc-querybuf">VIDIOC_QUERYBUF</link>, <link
|
||||
linkend="vidioc-qbuf">VIDIOC_PREPARE_BUF</link>, <link
|
||||
linkend="vidioc-qbuf">VIDIOC_QBUF</link> or <link
|
||||
linkend="vidioc-qbuf">VIDIOC_DQBUF</link> ioctl is called.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>V4L2_BUF_FLAG_NO_CACHE_INVALIDATE</constant></entry>
|
||||
<entry>0x0400</entry>
|
||||
<entry>Caches do not have to be invalidated for this buffer.
|
||||
Typically applications shall use this flag if the data captured in the buffer
|
||||
is not going to be touched by the CPU, instead the buffer will, probably, be
|
||||
passed on to a DMA-capable hardware unit for further processing or output.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><constant>V4L2_BUF_FLAG_NO_CACHE_CLEAN</constant></entry>
|
||||
<entry>0x0800</entry>
|
||||
<entry>Caches do not have to be cleaned for this buffer.
|
||||
Typically applications shall use this flag for output buffers if the data
|
||||
in this buffer has not been created by the CPU but by some DMA-capable unit,
|
||||
in which case caches have not been used.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
@ -469,6 +469,7 @@ and discussions on the V4L mailing list.</revremark>
|
||||
&sub-close;
|
||||
&sub-ioctl;
|
||||
<!-- All ioctls go here. -->
|
||||
&sub-create-bufs;
|
||||
&sub-cropcap;
|
||||
&sub-dbg-g-chip-ident;
|
||||
&sub-dbg-g-register;
|
||||
@ -511,6 +512,7 @@ and discussions on the V4L mailing list.</revremark>
|
||||
&sub-queryctrl;
|
||||
&sub-query-dv-preset;
|
||||
&sub-querystd;
|
||||
&sub-prepare-buf;
|
||||
&sub-reqbufs;
|
||||
&sub-s-hw-freq-seek;
|
||||
&sub-streamon;
|
||||
|
139
Documentation/DocBook/media/v4l/vidioc-create-bufs.xml
Normal file
139
Documentation/DocBook/media/v4l/vidioc-create-bufs.xml
Normal file
@ -0,0 +1,139 @@
|
||||
<refentry id="vidioc-create-bufs">
|
||||
<refmeta>
|
||||
<refentrytitle>ioctl VIDIOC_CREATE_BUFS</refentrytitle>
|
||||
&manvol;
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>VIDIOC_CREATE_BUFS</refname>
|
||||
<refpurpose>Create buffers for Memory Mapped or User Pointer I/O</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<funcsynopsis>
|
||||
<funcprototype>
|
||||
<funcdef>int <function>ioctl</function></funcdef>
|
||||
<paramdef>int <parameter>fd</parameter></paramdef>
|
||||
<paramdef>int <parameter>request</parameter></paramdef>
|
||||
<paramdef>struct v4l2_create_buffers *<parameter>argp</parameter></paramdef>
|
||||
</funcprototype>
|
||||
</funcsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Arguments</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><parameter>fd</parameter></term>
|
||||
<listitem>
|
||||
<para>&fd;</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><parameter>request</parameter></term>
|
||||
<listitem>
|
||||
<para>VIDIOC_CREATE_BUFS</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><parameter>argp</parameter></term>
|
||||
<listitem>
|
||||
<para></para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para>This ioctl is used to create buffers for <link linkend="mmap">memory
|
||||
mapped</link> or <link linkend="userp">user pointer</link>
|
||||
I/O. It can be used as an alternative or in addition to the
|
||||
<constant>VIDIOC_REQBUFS</constant> ioctl, when a tighter control over buffers
|
||||
is required. This ioctl can be called multiple times to create buffers of
|
||||
different sizes.</para>
|
||||
|
||||
<para>To allocate device buffers applications initialize relevant fields of
|
||||
the <structname>v4l2_create_buffers</structname> structure. They set the
|
||||
<structfield>type</structfield> field in the
|
||||
<structname>v4l2_format</structname> structure, embedded in this
|
||||
structure, to the respective stream or buffer type.
|
||||
<structfield>count</structfield> must be set to the number of required buffers.
|
||||
<structfield>memory</structfield> specifies the required I/O method. The
|
||||
<structfield>format</structfield> field shall typically be filled in using
|
||||
either the <constant>VIDIOC_TRY_FMT</constant> or
|
||||
<constant>VIDIOC_G_FMT</constant> ioctl(). Additionally, applications can adjust
|
||||
<structfield>sizeimage</structfield> fields to fit their specific needs. The
|
||||
<structfield>reserved</structfield> array must be zeroed.</para>
|
||||
|
||||
<para>When the ioctl is called with a pointer to this structure the driver
|
||||
will attempt to allocate up to the requested number of buffers and store the
|
||||
actual number allocated and the starting index in the
|
||||
<structfield>count</structfield> and the <structfield>index</structfield> fields
|
||||
respectively. On return <structfield>count</structfield> can be smaller than
|
||||
the number requested. The driver may also increase buffer sizes if required,
|
||||
however, it will not update <structfield>sizeimage</structfield> field values.
|
||||
The user has to use <constant>VIDIOC_QUERYBUF</constant> to retrieve that
|
||||
information.</para>
|
||||
|
||||
<table pgwide="1" frame="none" id="v4l2-create-buffers">
|
||||
<title>struct <structname>v4l2_create_buffers</structname></title>
|
||||
<tgroup cols="3">
|
||||
&cs-str;
|
||||
<tbody valign="top">
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>index</structfield></entry>
|
||||
<entry>The starting buffer index, returned by the driver.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>count</structfield></entry>
|
||||
<entry>The number of buffers requested or granted.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>&v4l2-memory;</entry>
|
||||
<entry><structfield>memory</structfield></entry>
|
||||
<entry>Applications set this field to
|
||||
<constant>V4L2_MEMORY_MMAP</constant> or
|
||||
<constant>V4L2_MEMORY_USERPTR</constant>.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>&v4l2-format;</entry>
|
||||
<entry><structfield>format</structfield></entry>
|
||||
<entry>Filled in by the application, preserved by the driver.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>__u32</entry>
|
||||
<entry><structfield>reserved</structfield>[8]</entry>
|
||||
<entry>A place holder for future extensions.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
&return-value;
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><errorcode>ENOMEM</errorcode></term>
|
||||
<listitem>
|
||||
<para>No memory to allocate buffers for <link linkend="mmap">memory
|
||||
mapped</link> I/O.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><errorcode>EINVAL</errorcode></term>
|
||||
<listitem>
|
||||
<para>The buffer type (<structfield>type</structfield> field) or the
|
||||
requested I/O method (<structfield>memory</structfield>) is not
|
||||
supported.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
</refentry>
|
88
Documentation/DocBook/media/v4l/vidioc-prepare-buf.xml
Normal file
88
Documentation/DocBook/media/v4l/vidioc-prepare-buf.xml
Normal file
@ -0,0 +1,88 @@
|
||||
<refentry id="vidioc-prepare-buf">
|
||||
<refmeta>
|
||||
<refentrytitle>ioctl VIDIOC_PREPARE_BUF</refentrytitle>
|
||||
&manvol;
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>VIDIOC_PREPARE_BUF</refname>
|
||||
<refpurpose>Prepare a buffer for I/O</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<funcsynopsis>
|
||||
<funcprototype>
|
||||
<funcdef>int <function>ioctl</function></funcdef>
|
||||
<paramdef>int <parameter>fd</parameter></paramdef>
|
||||
<paramdef>int <parameter>request</parameter></paramdef>
|
||||
<paramdef>struct v4l2_buffer *<parameter>argp</parameter></paramdef>
|
||||
</funcprototype>
|
||||
</funcsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Arguments</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><parameter>fd</parameter></term>
|
||||
<listitem>
|
||||
<para>&fd;</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><parameter>request</parameter></term>
|
||||
<listitem>
|
||||
<para>VIDIOC_PREPARE_BUF</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><parameter>argp</parameter></term>
|
||||
<listitem>
|
||||
<para></para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para>Applications can optionally call the
|
||||
<constant>VIDIOC_PREPARE_BUF</constant> ioctl to pass ownership of the buffer
|
||||
to the driver before actually enqueuing it, using the
|
||||
<constant>VIDIOC_QBUF</constant> ioctl, and to prepare it for future I/O.
|
||||
Such preparations may include cache invalidation or cleaning. Performing them
|
||||
in advance saves time during the actual I/O. In case such cache operations are
|
||||
not required, the application can use one of
|
||||
<constant>V4L2_BUF_FLAG_NO_CACHE_INVALIDATE</constant> and
|
||||
<constant>V4L2_BUF_FLAG_NO_CACHE_CLEAN</constant> flags to skip the respective
|
||||
step.</para>
|
||||
|
||||
<para>The <structname>v4l2_buffer</structname> structure is
|
||||
specified in <xref linkend="buffer" />.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
&return-value;
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><errorcode>EBUSY</errorcode></term>
|
||||
<listitem>
|
||||
<para>File I/O is in progress.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><errorcode>EINVAL</errorcode></term>
|
||||
<listitem>
|
||||
<para>The buffer <structfield>type</structfield> is not
|
||||
supported, or the <structfield>index</structfield> is out of bounds,
|
||||
or no buffers have been allocated yet, or the
|
||||
<structfield>userptr</structfield> or
|
||||
<structfield>length</structfield> are invalid.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
</refentry>
|
@ -394,9 +394,9 @@ static int pcm990_camera_set_bus_param(struct soc_camera_link *link,
|
||||
}
|
||||
|
||||
if (flags & SOCAM_DATAWIDTH_8)
|
||||
gpio_set_value(gpio_bus_switch, 1);
|
||||
gpio_set_value_cansleep(gpio_bus_switch, 1);
|
||||
else
|
||||
gpio_set_value(gpio_bus_switch, 0);
|
||||
gpio_set_value_cansleep(gpio_bus_switch, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -933,7 +933,7 @@ static struct platform_device ap4evb_camera = {
|
||||
static struct sh_csi2_client_config csi2_clients[] = {
|
||||
{
|
||||
.phy = SH_CSI2_PHY_MAIN,
|
||||
.lanes = 3,
|
||||
.lanes = 0, /* default: 2 lanes */
|
||||
.channel = 0,
|
||||
.pdev = &ap4evb_camera,
|
||||
},
|
||||
|
@ -1223,9 +1223,10 @@ static struct soc_camera_platform_info camera_info = {
|
||||
.width = 640,
|
||||
.height = 480,
|
||||
},
|
||||
.bus_param = SOCAM_PCLK_SAMPLE_RISING | SOCAM_HSYNC_ACTIVE_HIGH |
|
||||
SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_MASTER | SOCAM_DATAWIDTH_8 |
|
||||
SOCAM_DATA_ACTIVE_HIGH,
|
||||
.mbus_param = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
|
||||
V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
|
||||
V4L2_MBUS_DATA_ACTIVE_HIGH,
|
||||
.mbus_type = V4L2_MBUS_PARALLEL,
|
||||
.set_capture = camera_set_capture,
|
||||
};
|
||||
|
||||
|
@ -345,9 +345,10 @@ static struct soc_camera_platform_info camera_info = {
|
||||
.width = 640,
|
||||
.height = 480,
|
||||
},
|
||||
.bus_param = SOCAM_PCLK_SAMPLE_RISING | SOCAM_HSYNC_ACTIVE_HIGH |
|
||||
SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_MASTER | SOCAM_DATAWIDTH_8 |
|
||||
SOCAM_DATA_ACTIVE_HIGH,
|
||||
.mbus_param = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
|
||||
V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
|
||||
V4L2_MBUS_DATA_ACTIVE_HIGH,
|
||||
.mbus_type = V4L2_MBUS_PARALLEL,
|
||||
.set_capture = camera_set_capture,
|
||||
};
|
||||
|
||||
@ -501,8 +502,7 @@ static struct i2c_board_info ap325rxa_i2c_camera[] = {
|
||||
};
|
||||
|
||||
static struct ov772x_camera_info ov7725_info = {
|
||||
.flags = OV772X_FLAG_VFLIP | OV772X_FLAG_HFLIP | \
|
||||
OV772X_FLAG_8BIT,
|
||||
.flags = OV772X_FLAG_VFLIP | OV772X_FLAG_HFLIP,
|
||||
.edgectrl = OV772X_AUTO_EDGECTRL(0xf, 0),
|
||||
};
|
||||
|
||||
|
@ -448,9 +448,7 @@ static struct i2c_board_info migor_i2c_camera[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct ov772x_camera_info ov7725_info = {
|
||||
.flags = OV772X_FLAG_8BIT,
|
||||
};
|
||||
static struct ov772x_camera_info ov7725_info;
|
||||
|
||||
static struct soc_camera_link ov7725_link = {
|
||||
.power = ov7725_power,
|
||||
|
@ -1307,6 +1307,7 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id)
|
||||
ipu_submit_buffer(ichan, descnew, sgnew, ichan->active_buffer) < 0) {
|
||||
callback = descnew->txd.callback;
|
||||
callback_param = descnew->txd.callback_param;
|
||||
list_del_init(&descnew->list);
|
||||
spin_unlock(&ichan->lock);
|
||||
if (callback)
|
||||
callback(callback_param);
|
||||
@ -1428,39 +1429,58 @@ static int __idmac_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
|
||||
{
|
||||
struct idmac_channel *ichan = to_idmac_chan(chan);
|
||||
struct idmac *idmac = to_idmac(chan->device);
|
||||
struct ipu *ipu = to_ipu(idmac);
|
||||
struct list_head *list, *tmp;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
/* Only supports DMA_TERMINATE_ALL */
|
||||
if (cmd != DMA_TERMINATE_ALL)
|
||||
return -ENXIO;
|
||||
switch (cmd) {
|
||||
case DMA_PAUSE:
|
||||
spin_lock_irqsave(&ipu->lock, flags);
|
||||
ipu_ic_disable_task(ipu, chan->chan_id);
|
||||
|
||||
ipu_disable_channel(idmac, ichan,
|
||||
ichan->status >= IPU_CHANNEL_ENABLED);
|
||||
/* Return all descriptors into "prepared" state */
|
||||
list_for_each_safe(list, tmp, &ichan->queue)
|
||||
list_del_init(list);
|
||||
|
||||
tasklet_disable(&to_ipu(idmac)->tasklet);
|
||||
ichan->sg[0] = NULL;
|
||||
ichan->sg[1] = NULL;
|
||||
|
||||
/* ichan->queue is modified in ISR, have to spinlock */
|
||||
spin_lock_irqsave(&ichan->lock, flags);
|
||||
list_splice_init(&ichan->queue, &ichan->free_list);
|
||||
spin_unlock_irqrestore(&ipu->lock, flags);
|
||||
|
||||
if (ichan->desc)
|
||||
for (i = 0; i < ichan->n_tx_desc; i++) {
|
||||
struct idmac_tx_desc *desc = ichan->desc + i;
|
||||
if (list_empty(&desc->list))
|
||||
/* Descriptor was prepared, but not submitted */
|
||||
list_add(&desc->list, &ichan->free_list);
|
||||
ichan->status = IPU_CHANNEL_INITIALIZED;
|
||||
break;
|
||||
case DMA_TERMINATE_ALL:
|
||||
ipu_disable_channel(idmac, ichan,
|
||||
ichan->status >= IPU_CHANNEL_ENABLED);
|
||||
|
||||
async_tx_clear_ack(&desc->txd);
|
||||
}
|
||||
tasklet_disable(&ipu->tasklet);
|
||||
|
||||
ichan->sg[0] = NULL;
|
||||
ichan->sg[1] = NULL;
|
||||
spin_unlock_irqrestore(&ichan->lock, flags);
|
||||
/* ichan->queue is modified in ISR, have to spinlock */
|
||||
spin_lock_irqsave(&ichan->lock, flags);
|
||||
list_splice_init(&ichan->queue, &ichan->free_list);
|
||||
|
||||
tasklet_enable(&to_ipu(idmac)->tasklet);
|
||||
if (ichan->desc)
|
||||
for (i = 0; i < ichan->n_tx_desc; i++) {
|
||||
struct idmac_tx_desc *desc = ichan->desc + i;
|
||||
if (list_empty(&desc->list))
|
||||
/* Descriptor was prepared, but not submitted */
|
||||
list_add(&desc->list, &ichan->free_list);
|
||||
|
||||
ichan->status = IPU_CHANNEL_INITIALIZED;
|
||||
async_tx_clear_ack(&desc->txd);
|
||||
}
|
||||
|
||||
ichan->sg[0] = NULL;
|
||||
ichan->sg[1] = NULL;
|
||||
spin_unlock_irqrestore(&ichan->lock, flags);
|
||||
|
||||
tasklet_enable(&ipu->tasklet);
|
||||
|
||||
ichan->status = IPU_CHANNEL_INITIALIZED;
|
||||
break;
|
||||
default:
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1663,7 +1683,6 @@ static void __exit ipu_idmac_exit(struct ipu *ipu)
|
||||
struct idmac_channel *ichan = ipu->channel + i;
|
||||
|
||||
idmac_control(&ichan->dma_chan, DMA_TERMINATE_ALL, 0);
|
||||
idmac_prep_slave_sg(&ichan->dma_chan, NULL, 0, DMA_NONE, 0);
|
||||
}
|
||||
|
||||
dma_async_device_unregister(&idmac->dma);
|
||||
|
@ -11,4 +11,4 @@ ccflags-y += -Idrivers/media/dvb/frontends/
|
||||
ccflags-y += -Idrivers/media/common/tuners/
|
||||
|
||||
# For the staging CI driver cxd2099
|
||||
ccflags-y += -Idrivers/staging/cxd2099/
|
||||
ccflags-y += -Idrivers/staging/media/cxd2099/
|
||||
|
@ -102,6 +102,7 @@ obj-$(CONFIG_DVB_USB_IT913X) += dvb-usb-it913x.o
|
||||
|
||||
dvb-usb-mxl111sf-objs = mxl111sf.o mxl111sf-phy.o mxl111sf-i2c.o mxl111sf-gpio.o
|
||||
obj-$(CONFIG_DVB_USB_MXL111SF) += dvb-usb-mxl111sf.o
|
||||
obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-demod.o
|
||||
obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o
|
||||
|
||||
ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
|
||||
|
@ -37,6 +37,7 @@
|
||||
#define USB_VID_HAUPPAUGE 0x2040
|
||||
#define USB_VID_HYPER_PALTEK 0x1025
|
||||
#define USB_VID_INTEL 0x8086
|
||||
#define USB_VID_ITETECH 0x048d
|
||||
#define USB_VID_KWORLD 0xeb2a
|
||||
#define USB_VID_KWORLD_2 0x1b80
|
||||
#define USB_VID_KYE 0x0458
|
||||
@ -126,6 +127,7 @@
|
||||
#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0
|
||||
#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1
|
||||
#define USB_PID_INTEL_CE9500 0x9500
|
||||
#define USB_PID_ITETECH_IT9135 0x9135
|
||||
#define USB_PID_KWORLD_399U 0xe399
|
||||
#define USB_PID_KWORLD_399U_2 0xe400
|
||||
#define USB_PID_KWORLD_395U 0xe396
|
||||
|
@ -60,6 +60,17 @@ struct it913x_state {
|
||||
u8 id;
|
||||
};
|
||||
|
||||
struct ite_config {
|
||||
u8 chip_ver;
|
||||
u16 chip_type;
|
||||
u32 firmware;
|
||||
u8 tuner_id_0;
|
||||
u8 tuner_id_1;
|
||||
u8 dual_mode;
|
||||
};
|
||||
|
||||
struct ite_config it913x_config;
|
||||
|
||||
static int it913x_bulk_write(struct usb_device *dev,
|
||||
u8 *snd, int len, u8 pipe)
|
||||
{
|
||||
@ -191,18 +202,23 @@ static int it913x_read_reg(struct usb_device *udev, u32 reg)
|
||||
static u32 it913x_query(struct usb_device *udev, u8 pro)
|
||||
{
|
||||
int ret;
|
||||
u32 res = 0;
|
||||
u8 data[4];
|
||||
ret = it913x_io(udev, READ_LONG, pro, CMD_DEMOD_READ,
|
||||
0x1222, 0, &data[0], 1);
|
||||
if (data[0] == 0x1) {
|
||||
ret = it913x_io(udev, READ_SHORT, pro,
|
||||
CMD_QUERYINFO, 0, 0x1, &data[0], 4);
|
||||
res = (data[0] << 24) + (data[1] << 16) +
|
||||
(data[2] << 8) + data[3];
|
||||
}
|
||||
0x1222, 0, &data[0], 3);
|
||||
|
||||
return (ret < 0) ? 0 : res;
|
||||
it913x_config.chip_ver = data[0];
|
||||
it913x_config.chip_type = (u16)(data[2] << 8) + data[1];
|
||||
|
||||
info("Chip Version=%02x Chip Type=%04x", it913x_config.chip_ver,
|
||||
it913x_config.chip_type);
|
||||
|
||||
ret |= it913x_io(udev, READ_SHORT, pro,
|
||||
CMD_QUERYINFO, 0, 0x1, &data[0], 4);
|
||||
|
||||
it913x_config.firmware = (data[0] << 24) + (data[1] << 16) +
|
||||
(data[2] << 8) + data[3];
|
||||
|
||||
return (ret < 0) ? 0 : it913x_config.firmware;
|
||||
}
|
||||
|
||||
static int it913x_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
|
||||
@ -336,26 +352,35 @@ static int it913x_identify_state(struct usb_device *udev,
|
||||
int *cold)
|
||||
{
|
||||
int ret = 0, firm_no;
|
||||
u8 reg, adap, ep, tun0, tun1;
|
||||
u8 reg, remote;
|
||||
|
||||
firm_no = it913x_return_status(udev);
|
||||
|
||||
ep = it913x_read_reg(udev, 0x49ac);
|
||||
adap = it913x_read_reg(udev, 0x49c5);
|
||||
tun0 = it913x_read_reg(udev, 0x49d0);
|
||||
info("No. Adapters=%x Endpoints=%x Tuner Type=%x", adap, ep, tun0);
|
||||
/* checnk for dual mode */
|
||||
it913x_config.dual_mode = it913x_read_reg(udev, 0x49c5);
|
||||
|
||||
/* TODO different remotes */
|
||||
remote = it913x_read_reg(udev, 0x49ac); /* Remote */
|
||||
if (remote == 0)
|
||||
props->rc.core.rc_codes = NULL;
|
||||
|
||||
/* TODO at the moment tuner_id is always assigned to 0x38 */
|
||||
it913x_config.tuner_id_0 = it913x_read_reg(udev, 0x49d0);
|
||||
|
||||
info("Dual mode=%x Remote=%x Tuner Type=%x", it913x_config.dual_mode
|
||||
, remote, it913x_config.tuner_id_0);
|
||||
|
||||
if (firm_no > 0) {
|
||||
*cold = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (adap > 2) {
|
||||
tun1 = it913x_read_reg(udev, 0x49e0);
|
||||
if (it913x_config.dual_mode) {
|
||||
it913x_config.tuner_id_1 = it913x_read_reg(udev, 0x49e0);
|
||||
ret = it913x_wr_reg(udev, DEV_0, GPIOH1_EN, 0x1);
|
||||
ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_ON, 0x1);
|
||||
ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_O, 0x1);
|
||||
msleep(50); /* Delay noticed reset cycle ? */
|
||||
msleep(50);
|
||||
ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_O, 0x0);
|
||||
msleep(50);
|
||||
reg = it913x_read_reg(udev, GPIOH1_O);
|
||||
@ -366,14 +391,19 @@ static int it913x_identify_state(struct usb_device *udev,
|
||||
ret = it913x_wr_reg(udev, DEV_0,
|
||||
GPIOH1_O, 0x0);
|
||||
}
|
||||
props->num_adapters = 2;
|
||||
} else
|
||||
props->num_adapters = 1;
|
||||
|
||||
reg = it913x_read_reg(udev, IO_MUX_POWER_CLK);
|
||||
|
||||
ret |= it913x_wr_reg(udev, DEV_0, 0x4bfb, CHIP2_I2C_ADDR);
|
||||
|
||||
ret |= it913x_wr_reg(udev, DEV_0, CLK_O_EN, 0x1);
|
||||
if (it913x_config.dual_mode) {
|
||||
ret |= it913x_wr_reg(udev, DEV_0, 0x4bfb, CHIP2_I2C_ADDR);
|
||||
ret |= it913x_wr_reg(udev, DEV_0, CLK_O_EN, 0x1);
|
||||
} else {
|
||||
ret |= it913x_wr_reg(udev, DEV_0, 0x4bfb, 0x0);
|
||||
ret |= it913x_wr_reg(udev, DEV_0, CLK_O_EN, 0x0);
|
||||
}
|
||||
|
||||
*cold = 1;
|
||||
|
||||
@ -403,13 +433,11 @@ static int it913x_download_firmware(struct usb_device *udev,
|
||||
const struct firmware *fw)
|
||||
{
|
||||
int ret = 0, i;
|
||||
u8 packet_size, dlen, tun1;
|
||||
u8 packet_size, dlen;
|
||||
u8 *fw_data;
|
||||
|
||||
packet_size = 0x29;
|
||||
|
||||
tun1 = it913x_read_reg(udev, 0x49e0);
|
||||
|
||||
ret = it913x_wr_reg(udev, DEV_0, I2C_CLK, I2C_CLK_100);
|
||||
|
||||
info("FRM Starting Firmware Download");
|
||||
@ -444,11 +472,12 @@ static int it913x_download_firmware(struct usb_device *udev,
|
||||
ret |= it913x_wr_reg(udev, DEV_0, I2C_CLK, I2C_CLK_400);
|
||||
|
||||
/* Tuner function */
|
||||
ret |= it913x_wr_reg(udev, DEV_0_DMOD , 0xec4c, 0xa0);
|
||||
if (it913x_config.dual_mode)
|
||||
ret |= it913x_wr_reg(udev, DEV_0_DMOD , 0xec4c, 0xa0);
|
||||
|
||||
ret |= it913x_wr_reg(udev, DEV_0, PADODPU, 0x0);
|
||||
ret |= it913x_wr_reg(udev, DEV_0, AGC_O_D, 0x0);
|
||||
if (tun1 > 0) {
|
||||
if (it913x_config.dual_mode) {
|
||||
ret |= it913x_wr_reg(udev, DEV_1, PADODPU, 0x0);
|
||||
ret |= it913x_wr_reg(udev, DEV_1, AGC_O_D, 0x0);
|
||||
}
|
||||
@ -475,9 +504,28 @@ static int it913x_frontend_attach(struct dvb_usb_adapter *adap)
|
||||
u8 adf = it913x_read_reg(udev, IO_MUX_POWER_CLK);
|
||||
u8 adap_addr = I2C_BASE_ADDR + (adap->id << 5);
|
||||
u16 ep_size = adap->props.fe[0].stream.u.bulk.buffersize;
|
||||
u8 tuner_id, tuner_type;
|
||||
|
||||
if (adap->id == 0)
|
||||
tuner_id = it913x_config.tuner_id_0;
|
||||
else
|
||||
tuner_id = it913x_config.tuner_id_1;
|
||||
|
||||
/* TODO we always use IT9137 possible references here*/
|
||||
/* Documentation suggests don't care */
|
||||
switch (tuner_id) {
|
||||
case 0x51:
|
||||
case 0x52:
|
||||
case 0x60:
|
||||
case 0x61:
|
||||
case 0x62:
|
||||
default:
|
||||
case 0x38:
|
||||
tuner_type = IT9137;
|
||||
}
|
||||
|
||||
adap->fe_adap[0].fe = dvb_attach(it913x_fe_attach,
|
||||
&adap->dev->i2c_adap, adap_addr, adf, IT9137);
|
||||
&adap->dev->i2c_adap, adap_addr, adf, tuner_type);
|
||||
|
||||
if (adap->id == 0 && adap->fe_adap[0].fe) {
|
||||
ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2_SW_RST, 0x1);
|
||||
@ -533,6 +581,7 @@ static int it913x_probe(struct usb_interface *intf,
|
||||
|
||||
static struct usb_device_id it913x_table[] = {
|
||||
{ USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB499_2T_T09) },
|
||||
{ USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135) },
|
||||
{} /* Terminating entry */
|
||||
};
|
||||
|
||||
@ -608,12 +657,14 @@ static struct dvb_usb_device_properties it913x_properties = {
|
||||
.rc_codes = RC_MAP_KWORLD_315U,
|
||||
},
|
||||
.i2c_algo = &it913x_i2c_algo,
|
||||
.num_device_descs = 1,
|
||||
.num_device_descs = 2,
|
||||
.devices = {
|
||||
{ "Kworld UB499-2T T09(IT9137)",
|
||||
{ &it913x_table[0], NULL },
|
||||
},
|
||||
|
||||
{ "ITE 9135 Generic",
|
||||
{ &it913x_table[1], NULL },
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@ -647,5 +698,5 @@ module_exit(it913x_module_exit);
|
||||
|
||||
MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>");
|
||||
MODULE_DESCRIPTION("it913x USB 2 Driver");
|
||||
MODULE_VERSION("1.06");
|
||||
MODULE_VERSION("1.07");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
614
drivers/media/dvb/dvb-usb/mxl111sf-demod.c
Normal file
614
drivers/media/dvb/dvb-usb/mxl111sf-demod.c
Normal file
@ -0,0 +1,614 @@
|
||||
/*
|
||||
* mxl111sf-demod.c - driver for the MaxLinear MXL111SF DVB-T demodulator
|
||||
*
|
||||
* Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "mxl111sf-demod.h"
|
||||
#include "mxl111sf-reg.h"
|
||||
|
||||
/* debug */
|
||||
static int mxl111sf_demod_debug;
|
||||
module_param_named(debug, mxl111sf_demod_debug, int, 0644);
|
||||
MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
|
||||
|
||||
#define mxl_dbg(fmt, arg...) \
|
||||
if (mxl111sf_demod_debug) \
|
||||
mxl_printk(KERN_DEBUG, fmt, ##arg)
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
struct mxl111sf_demod_state {
|
||||
struct mxl111sf_state *mxl_state;
|
||||
|
||||
struct mxl111sf_demod_config *cfg;
|
||||
|
||||
struct dvb_frontend fe;
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static int mxl111sf_demod_read_reg(struct mxl111sf_demod_state *state,
|
||||
u8 addr, u8 *data)
|
||||
{
|
||||
return (state->cfg->read_reg) ?
|
||||
state->cfg->read_reg(state->mxl_state, addr, data) :
|
||||
-EINVAL;
|
||||
}
|
||||
|
||||
static int mxl111sf_demod_write_reg(struct mxl111sf_demod_state *state,
|
||||
u8 addr, u8 data)
|
||||
{
|
||||
return (state->cfg->write_reg) ?
|
||||
state->cfg->write_reg(state->mxl_state, addr, data) :
|
||||
-EINVAL;
|
||||
}
|
||||
|
||||
static
|
||||
int mxl111sf_demod_program_regs(struct mxl111sf_demod_state *state,
|
||||
struct mxl111sf_reg_ctrl_info *ctrl_reg_info)
|
||||
{
|
||||
return (state->cfg->program_regs) ?
|
||||
state->cfg->program_regs(state->mxl_state, ctrl_reg_info) :
|
||||
-EINVAL;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* TPS */
|
||||
|
||||
static
|
||||
int mxl1x1sf_demod_get_tps_code_rate(struct mxl111sf_demod_state *state,
|
||||
fe_code_rate_t *code_rate)
|
||||
{
|
||||
u8 val;
|
||||
int ret = mxl111sf_demod_read_reg(state, V6_CODE_RATE_TPS_REG, &val);
|
||||
/* bit<2:0> - 000:1/2, 001:2/3, 010:3/4, 011:5/6, 100:7/8 */
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
|
||||
switch (val & V6_CODE_RATE_TPS_MASK) {
|
||||
case 0:
|
||||
*code_rate = FEC_1_2;
|
||||
break;
|
||||
case 1:
|
||||
*code_rate = FEC_2_3;
|
||||
break;
|
||||
case 2:
|
||||
*code_rate = FEC_3_4;
|
||||
break;
|
||||
case 3:
|
||||
*code_rate = FEC_5_6;
|
||||
break;
|
||||
case 4:
|
||||
*code_rate = FEC_7_8;
|
||||
break;
|
||||
}
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static
|
||||
int mxl1x1sf_demod_get_tps_constellation(struct mxl111sf_demod_state *state,
|
||||
fe_modulation_t *constellation)
|
||||
{
|
||||
u8 val;
|
||||
int ret = mxl111sf_demod_read_reg(state, V6_MODORDER_TPS_REG, &val);
|
||||
/* Constellation, 00 : QPSK, 01 : 16QAM, 10:64QAM */
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
|
||||
switch ((val & V6_PARAM_CONSTELLATION_MASK) >> 4) {
|
||||
case 0:
|
||||
*constellation = QPSK;
|
||||
break;
|
||||
case 1:
|
||||
*constellation = QAM_16;
|
||||
break;
|
||||
case 2:
|
||||
*constellation = QAM_64;
|
||||
break;
|
||||
}
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static
|
||||
int mxl1x1sf_demod_get_tps_guard_fft_mode(struct mxl111sf_demod_state *state,
|
||||
fe_transmit_mode_t *fft_mode)
|
||||
{
|
||||
u8 val;
|
||||
int ret = mxl111sf_demod_read_reg(state, V6_MODE_TPS_REG, &val);
|
||||
/* FFT Mode, 00:2K, 01:8K, 10:4K */
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
|
||||
switch ((val & V6_PARAM_FFT_MODE_MASK) >> 2) {
|
||||
case 0:
|
||||
*fft_mode = TRANSMISSION_MODE_2K;
|
||||
break;
|
||||
case 1:
|
||||
*fft_mode = TRANSMISSION_MODE_8K;
|
||||
break;
|
||||
case 2:
|
||||
*fft_mode = TRANSMISSION_MODE_4K;
|
||||
break;
|
||||
}
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static
|
||||
int mxl1x1sf_demod_get_tps_guard_interval(struct mxl111sf_demod_state *state,
|
||||
fe_guard_interval_t *guard)
|
||||
{
|
||||
u8 val;
|
||||
int ret = mxl111sf_demod_read_reg(state, V6_CP_TPS_REG, &val);
|
||||
/* 00:1/32, 01:1/16, 10:1/8, 11:1/4 */
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
|
||||
switch ((val & V6_PARAM_GI_MASK) >> 4) {
|
||||
case 0:
|
||||
*guard = GUARD_INTERVAL_1_32;
|
||||
break;
|
||||
case 1:
|
||||
*guard = GUARD_INTERVAL_1_16;
|
||||
break;
|
||||
case 2:
|
||||
*guard = GUARD_INTERVAL_1_8;
|
||||
break;
|
||||
case 3:
|
||||
*guard = GUARD_INTERVAL_1_4;
|
||||
break;
|
||||
}
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static
|
||||
int mxl1x1sf_demod_get_tps_hierarchy(struct mxl111sf_demod_state *state,
|
||||
fe_hierarchy_t *hierarchy)
|
||||
{
|
||||
u8 val;
|
||||
int ret = mxl111sf_demod_read_reg(state, V6_TPS_HIERACHY_REG, &val);
|
||||
/* bit<6:4> - 000:Non hierarchy, 001:1, 010:2, 011:4 */
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
|
||||
switch ((val & V6_TPS_HIERARCHY_INFO_MASK) >> 6) {
|
||||
case 0:
|
||||
*hierarchy = HIERARCHY_NONE;
|
||||
break;
|
||||
case 1:
|
||||
*hierarchy = HIERARCHY_1;
|
||||
break;
|
||||
case 2:
|
||||
*hierarchy = HIERARCHY_2;
|
||||
break;
|
||||
case 3:
|
||||
*hierarchy = HIERARCHY_4;
|
||||
break;
|
||||
}
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* LOCKS */
|
||||
|
||||
static
|
||||
int mxl1x1sf_demod_get_sync_lock_status(struct mxl111sf_demod_state *state,
|
||||
int *sync_lock)
|
||||
{
|
||||
u8 val = 0;
|
||||
int ret = mxl111sf_demod_read_reg(state, V6_SYNC_LOCK_REG, &val);
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
*sync_lock = (val & SYNC_LOCK_MASK) >> 4;
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static
|
||||
int mxl1x1sf_demod_get_rs_lock_status(struct mxl111sf_demod_state *state,
|
||||
int *rs_lock)
|
||||
{
|
||||
u8 val = 0;
|
||||
int ret = mxl111sf_demod_read_reg(state, V6_RS_LOCK_DET_REG, &val);
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
*rs_lock = (val & RS_LOCK_DET_MASK) >> 3;
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static
|
||||
int mxl1x1sf_demod_get_tps_lock_status(struct mxl111sf_demod_state *state,
|
||||
int *tps_lock)
|
||||
{
|
||||
u8 val = 0;
|
||||
int ret = mxl111sf_demod_read_reg(state, V6_TPS_LOCK_REG, &val);
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
*tps_lock = (val & V6_PARAM_TPS_LOCK_MASK) >> 6;
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static
|
||||
int mxl1x1sf_demod_get_fec_lock_status(struct mxl111sf_demod_state *state,
|
||||
int *fec_lock)
|
||||
{
|
||||
u8 val = 0;
|
||||
int ret = mxl111sf_demod_read_reg(state, V6_IRQ_STATUS_REG, &val);
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
*fec_lock = (val & IRQ_MASK_FEC_LOCK) >> 4;
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static
|
||||
int mxl1x1sf_demod_get_cp_lock_status(struct mxl111sf_demod_state *state,
|
||||
int *cp_lock)
|
||||
{
|
||||
u8 val = 0;
|
||||
int ret = mxl111sf_demod_read_reg(state, V6_CP_LOCK_DET_REG, &val);
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
*cp_lock = (val & V6_CP_LOCK_DET_MASK) >> 2;
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int mxl1x1sf_demod_reset_irq_status(struct mxl111sf_demod_state *state)
|
||||
{
|
||||
return mxl111sf_demod_write_reg(state, 0x0e, 0xff);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static int mxl111sf_demod_set_frontend(struct dvb_frontend *fe,
|
||||
struct dvb_frontend_parameters *param)
|
||||
{
|
||||
struct mxl111sf_demod_state *state = fe->demodulator_priv;
|
||||
int ret = 0;
|
||||
|
||||
struct mxl111sf_reg_ctrl_info phy_pll_patch[] = {
|
||||
{0x00, 0xff, 0x01}, /* change page to 1 */
|
||||
{0x40, 0xff, 0x05},
|
||||
{0x40, 0xff, 0x01},
|
||||
{0x41, 0xff, 0xca},
|
||||
{0x41, 0xff, 0xc0},
|
||||
{0x00, 0xff, 0x00}, /* change page to 0 */
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
mxl_dbg("()");
|
||||
|
||||
if (fe->ops.tuner_ops.set_params) {
|
||||
ret = fe->ops.tuner_ops.set_params(fe, param);
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
msleep(50);
|
||||
}
|
||||
ret = mxl111sf_demod_program_regs(state, phy_pll_patch);
|
||||
mxl_fail(ret);
|
||||
msleep(50);
|
||||
ret = mxl1x1sf_demod_reset_irq_status(state);
|
||||
mxl_fail(ret);
|
||||
msleep(100);
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#if 0
|
||||
/* resets TS Packet error count */
|
||||
/* After setting 7th bit of V5_PER_COUNT_RESET_REG, it should be reset to 0. */
|
||||
static
|
||||
int mxl1x1sf_demod_reset_packet_error_count(struct mxl111sf_demod_state *state)
|
||||
{
|
||||
struct mxl111sf_reg_ctrl_info reset_per_count[] = {
|
||||
{0x20, 0x01, 0x01},
|
||||
{0x20, 0x01, 0x00},
|
||||
{0, 0, 0}
|
||||
};
|
||||
return mxl111sf_demod_program_regs(state, reset_per_count);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* returns TS Packet error count */
|
||||
/* PER Count = FEC_PER_COUNT * (2 ** (FEC_PER_SCALE * 4)) */
|
||||
static int mxl111sf_demod_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
|
||||
{
|
||||
struct mxl111sf_demod_state *state = fe->demodulator_priv;
|
||||
u32 fec_per_count, fec_per_scale;
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
*ucblocks = 0;
|
||||
|
||||
/* FEC_PER_COUNT Register */
|
||||
ret = mxl111sf_demod_read_reg(state, V6_FEC_PER_COUNT_REG, &val);
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
|
||||
fec_per_count = val;
|
||||
|
||||
/* FEC_PER_SCALE Register */
|
||||
ret = mxl111sf_demod_read_reg(state, V6_FEC_PER_SCALE_REG, &val);
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
|
||||
val &= V6_FEC_PER_SCALE_MASK;
|
||||
val *= 4;
|
||||
|
||||
fec_per_scale = 1 << val;
|
||||
|
||||
fec_per_count *= fec_per_scale;
|
||||
|
||||
*ucblocks = fec_per_count;
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef MXL111SF_DEMOD_ENABLE_CALCULATIONS
|
||||
/* FIXME: leaving this enabled breaks the build on some architectures,
|
||||
* and we shouldn't have any floating point math in the kernel, anyway.
|
||||
*
|
||||
* These macros need to be re-written, but it's harmless to simply
|
||||
* return zero for now. */
|
||||
#define CALCULATE_BER(avg_errors, count) \
|
||||
((u32)(avg_errors * 4)/(count*64*188*8))
|
||||
#define CALCULATE_SNR(data) \
|
||||
((u32)((10 * (u32)data / 64) - 2.5))
|
||||
#else
|
||||
#define CALCULATE_BER(avg_errors, count) 0
|
||||
#define CALCULATE_SNR(data) 0
|
||||
#endif
|
||||
|
||||
static int mxl111sf_demod_read_ber(struct dvb_frontend *fe, u32 *ber)
|
||||
{
|
||||
struct mxl111sf_demod_state *state = fe->demodulator_priv;
|
||||
u8 val1, val2, val3;
|
||||
int ret;
|
||||
|
||||
*ber = 0;
|
||||
|
||||
ret = mxl111sf_demod_read_reg(state, V6_RS_AVG_ERRORS_LSB_REG, &val1);
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
ret = mxl111sf_demod_read_reg(state, V6_RS_AVG_ERRORS_MSB_REG, &val2);
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
ret = mxl111sf_demod_read_reg(state, V6_N_ACCUMULATE_REG, &val3);
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
|
||||
*ber = CALCULATE_BER((val1 | (val2 << 8)), val3);
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxl111sf_demod_calc_snr(struct mxl111sf_demod_state *state,
|
||||
u16 *snr)
|
||||
{
|
||||
u8 val1, val2;
|
||||
int ret;
|
||||
|
||||
*snr = 0;
|
||||
|
||||
ret = mxl111sf_demod_read_reg(state, V6_SNR_RB_LSB_REG, &val1);
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
ret = mxl111sf_demod_read_reg(state, V6_SNR_RB_MSB_REG, &val2);
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
|
||||
*snr = CALCULATE_SNR(val1 | ((val2 & 0x03) << 8));
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxl111sf_demod_read_snr(struct dvb_frontend *fe, u16 *snr)
|
||||
{
|
||||
struct mxl111sf_demod_state *state = fe->demodulator_priv;
|
||||
|
||||
int ret = mxl111sf_demod_calc_snr(state, snr);
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
|
||||
*snr /= 10; /* 0.1 dB */
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxl111sf_demod_read_status(struct dvb_frontend *fe,
|
||||
fe_status_t *status)
|
||||
{
|
||||
struct mxl111sf_demod_state *state = fe->demodulator_priv;
|
||||
int ret, locked, cr_lock, sync_lock, fec_lock;
|
||||
|
||||
*status = 0;
|
||||
|
||||
ret = mxl1x1sf_demod_get_rs_lock_status(state, &locked);
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
ret = mxl1x1sf_demod_get_tps_lock_status(state, &cr_lock);
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
ret = mxl1x1sf_demod_get_sync_lock_status(state, &sync_lock);
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
ret = mxl1x1sf_demod_get_fec_lock_status(state, &fec_lock);
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
|
||||
if (locked)
|
||||
*status |= FE_HAS_SIGNAL;
|
||||
if (cr_lock)
|
||||
*status |= FE_HAS_CARRIER;
|
||||
if (sync_lock)
|
||||
*status |= FE_HAS_SYNC;
|
||||
if (fec_lock) /* false positives? */
|
||||
*status |= FE_HAS_VITERBI;
|
||||
|
||||
if ((locked) && (cr_lock) && (sync_lock))
|
||||
*status |= FE_HAS_LOCK;
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxl111sf_demod_read_signal_strength(struct dvb_frontend *fe,
|
||||
u16 *signal_strength)
|
||||
{
|
||||
struct mxl111sf_demod_state *state = fe->demodulator_priv;
|
||||
fe_modulation_t constellation;
|
||||
u16 snr;
|
||||
|
||||
mxl111sf_demod_calc_snr(state, &snr);
|
||||
mxl1x1sf_demod_get_tps_constellation(state, &constellation);
|
||||
|
||||
switch (constellation) {
|
||||
case QPSK:
|
||||
*signal_strength = (snr >= 1300) ?
|
||||
min(65535, snr * 44) : snr * 38;
|
||||
break;
|
||||
case QAM_16:
|
||||
*signal_strength = (snr >= 1500) ?
|
||||
min(65535, snr * 38) : snr * 33;
|
||||
break;
|
||||
case QAM_64:
|
||||
*signal_strength = (snr >= 2000) ?
|
||||
min(65535, snr * 29) : snr * 25;
|
||||
break;
|
||||
default:
|
||||
*signal_strength = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxl111sf_demod_get_frontend(struct dvb_frontend *fe,
|
||||
struct dvb_frontend_parameters *p)
|
||||
{
|
||||
struct mxl111sf_demod_state *state = fe->demodulator_priv;
|
||||
|
||||
mxl_dbg("()");
|
||||
#if 0
|
||||
p->inversion = /* FIXME */ ? INVERSION_ON : INVERSION_OFF;
|
||||
#endif
|
||||
if (fe->ops.tuner_ops.get_bandwidth)
|
||||
fe->ops.tuner_ops.get_bandwidth(fe, &p->u.ofdm.bandwidth);
|
||||
if (fe->ops.tuner_ops.get_frequency)
|
||||
fe->ops.tuner_ops.get_frequency(fe, &p->frequency);
|
||||
mxl1x1sf_demod_get_tps_code_rate(state, &p->u.ofdm.code_rate_HP);
|
||||
mxl1x1sf_demod_get_tps_code_rate(state, &p->u.ofdm.code_rate_LP);
|
||||
mxl1x1sf_demod_get_tps_constellation(state, &p->u.ofdm.constellation);
|
||||
mxl1x1sf_demod_get_tps_guard_fft_mode(state,
|
||||
&p->u.ofdm.transmission_mode);
|
||||
mxl1x1sf_demod_get_tps_guard_interval(state,
|
||||
&p->u.ofdm.guard_interval);
|
||||
mxl1x1sf_demod_get_tps_hierarchy(state,
|
||||
&p->u.ofdm.hierarchy_information);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int mxl111sf_demod_get_tune_settings(struct dvb_frontend *fe,
|
||||
struct dvb_frontend_tune_settings *tune)
|
||||
{
|
||||
tune->min_delay_ms = 1000;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mxl111sf_demod_release(struct dvb_frontend *fe)
|
||||
{
|
||||
struct mxl111sf_demod_state *state = fe->demodulator_priv;
|
||||
mxl_dbg("()");
|
||||
kfree(state);
|
||||
fe->demodulator_priv = NULL;
|
||||
}
|
||||
|
||||
static struct dvb_frontend_ops mxl111sf_demod_ops = {
|
||||
|
||||
.info = {
|
||||
.name = "MaxLinear MxL111SF DVB-T demodulator",
|
||||
.type = FE_OFDM,
|
||||
.frequency_min = 177000000,
|
||||
.frequency_max = 858000000,
|
||||
.frequency_stepsize = 166666,
|
||||
.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
|
||||
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
|
||||
FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
|
||||
FE_CAN_QAM_AUTO |
|
||||
FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
|
||||
FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER
|
||||
},
|
||||
.release = mxl111sf_demod_release,
|
||||
#if 0
|
||||
.init = mxl111sf_init,
|
||||
.i2c_gate_ctrl = mxl111sf_i2c_gate_ctrl,
|
||||
#endif
|
||||
.set_frontend = mxl111sf_demod_set_frontend,
|
||||
.get_frontend = mxl111sf_demod_get_frontend,
|
||||
.get_tune_settings = mxl111sf_demod_get_tune_settings,
|
||||
.read_status = mxl111sf_demod_read_status,
|
||||
.read_signal_strength = mxl111sf_demod_read_signal_strength,
|
||||
.read_ber = mxl111sf_demod_read_ber,
|
||||
.read_snr = mxl111sf_demod_read_snr,
|
||||
.read_ucblocks = mxl111sf_demod_read_ucblocks,
|
||||
};
|
||||
|
||||
struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state,
|
||||
struct mxl111sf_demod_config *cfg)
|
||||
{
|
||||
struct mxl111sf_demod_state *state = NULL;
|
||||
|
||||
mxl_dbg("()");
|
||||
|
||||
state = kzalloc(sizeof(struct mxl111sf_demod_state), GFP_KERNEL);
|
||||
if (state == NULL)
|
||||
return NULL;
|
||||
|
||||
state->mxl_state = mxl_state;
|
||||
state->cfg = cfg;
|
||||
|
||||
memcpy(&state->fe.ops, &mxl111sf_demod_ops,
|
||||
sizeof(struct dvb_frontend_ops));
|
||||
|
||||
state->fe.demodulator_priv = state;
|
||||
return &state->fe;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mxl111sf_demod_attach);
|
||||
|
||||
MODULE_DESCRIPTION("MaxLinear MxL111SF DVB-T demodulator driver");
|
||||
MODULE_AUTHOR("Michael Krufky <mkrufky@kernellabs.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION("0.1");
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
55
drivers/media/dvb/dvb-usb/mxl111sf-demod.h
Normal file
55
drivers/media/dvb/dvb-usb/mxl111sf-demod.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* mxl111sf-demod.h - driver for the MaxLinear MXL111SF DVB-T demodulator
|
||||
*
|
||||
* Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef __MXL111SF_DEMOD_H__
|
||||
#define __MXL111SF_DEMOD_H__
|
||||
|
||||
#include "dvb_frontend.h"
|
||||
#include "mxl111sf.h"
|
||||
|
||||
struct mxl111sf_demod_config {
|
||||
int (*read_reg)(struct mxl111sf_state *state, u8 addr, u8 *data);
|
||||
int (*write_reg)(struct mxl111sf_state *state, u8 addr, u8 data);
|
||||
int (*program_regs)(struct mxl111sf_state *state,
|
||||
struct mxl111sf_reg_ctrl_info *ctrl_reg_info);
|
||||
};
|
||||
|
||||
#if defined(CONFIG_DVB_USB_MXL111SF) || \
|
||||
(defined(CONFIG_DVB_USB_MXL111SF_MODULE) && defined(MODULE))
|
||||
extern
|
||||
struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state,
|
||||
struct mxl111sf_demod_config *cfg);
|
||||
#else
|
||||
static inline
|
||||
struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state,
|
||||
struct mxl111sf_demod_config *cfg)
|
||||
{
|
||||
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
#endif /* CONFIG_DVB_USB_MXL111SF */
|
||||
|
||||
#endif /* __MXL111SF_DEMOD_H__ */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
@ -17,6 +17,7 @@
|
||||
#include "mxl111sf-i2c.h"
|
||||
#include "mxl111sf-gpio.h"
|
||||
|
||||
#include "mxl111sf-demod.h"
|
||||
#include "mxl111sf-tuner.h"
|
||||
|
||||
#include "lgdt3305.h"
|
||||
@ -362,6 +363,22 @@ static int mxl111sf_ep6_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxl111sf_ep4_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
|
||||
{
|
||||
struct dvb_usb_device *d = adap->dev;
|
||||
struct mxl111sf_state *state = d->priv;
|
||||
int ret = 0;
|
||||
|
||||
deb_info("%s(%d)\n", __func__, onoff);
|
||||
|
||||
if (onoff) {
|
||||
ret = mxl111sf_enable_usb_output(state);
|
||||
mxl_fail(ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static struct lgdt3305_config hauppauge_lgdt3305_config = {
|
||||
@ -438,6 +455,70 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct mxl111sf_demod_config mxl_demod_config = {
|
||||
.read_reg = mxl111sf_read_reg,
|
||||
.write_reg = mxl111sf_write_reg,
|
||||
.program_regs = mxl111sf_ctrl_program_regs,
|
||||
};
|
||||
|
||||
static int mxl111sf_attach_demod(struct dvb_usb_adapter *adap)
|
||||
{
|
||||
struct dvb_usb_device *d = adap->dev;
|
||||
struct mxl111sf_state *state = d->priv;
|
||||
int fe_id = adap->num_frontends_initialized;
|
||||
struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv;
|
||||
int ret;
|
||||
|
||||
deb_adv("%s()\n", __func__);
|
||||
|
||||
/* save a pointer to the dvb_usb_device in device state */
|
||||
state->d = d;
|
||||
adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 1 : 2;
|
||||
state->alt_mode = adap_state->alt_mode;
|
||||
|
||||
if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0)
|
||||
err("set interface failed");
|
||||
|
||||
state->gpio_mode = MXL111SF_GPIO_MOD_DVBT;
|
||||
adap_state->gpio_mode = state->gpio_mode;
|
||||
adap_state->device_mode = MXL_SOC_MODE;
|
||||
adap_state->ep6_clockphase = 1;
|
||||
|
||||
ret = mxl1x1sf_soft_reset(state);
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
ret = mxl111sf_init_tuner_demod(state);
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
|
||||
ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
|
||||
ret = mxl111sf_enable_usb_output(state);
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
ret = mxl1x1sf_top_master_ctrl(state, 1);
|
||||
if (mxl_fail(ret))
|
||||
goto fail;
|
||||
|
||||
/* dont care if this fails */
|
||||
mxl111sf_init_port_expander(state);
|
||||
|
||||
adap->fe_adap[fe_id].fe = dvb_attach(mxl111sf_demod_attach, state,
|
||||
&mxl_demod_config);
|
||||
if (adap->fe_adap[fe_id].fe) {
|
||||
adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init;
|
||||
adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init;
|
||||
adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep;
|
||||
adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep;
|
||||
return 0;
|
||||
}
|
||||
ret = -EIO;
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int mxl111sf_set_ant_path(struct mxl111sf_state *state,
|
||||
int antpath)
|
||||
{
|
||||
@ -567,7 +648,8 @@ struct i2c_algorithm mxl111sf_i2c_algo = {
|
||||
#endif
|
||||
};
|
||||
|
||||
/* DVB USB Driver stuff */
|
||||
static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties;
|
||||
static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties;
|
||||
static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties;
|
||||
static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties;
|
||||
|
||||
@ -580,8 +662,14 @@ static int mxl111sf_probe(struct usb_interface *intf,
|
||||
|
||||
if (((dvb_usb_mxl111sf_isoc) &&
|
||||
(0 == dvb_usb_device_init(intf,
|
||||
&mxl111sf_dvbt_isoc_properties,
|
||||
THIS_MODULE, &d, adapter_nr) ||
|
||||
0 == dvb_usb_device_init(intf,
|
||||
&mxl111sf_atsc_isoc_properties,
|
||||
THIS_MODULE, &d, adapter_nr))) ||
|
||||
0 == dvb_usb_device_init(intf,
|
||||
&mxl111sf_dvbt_bulk_properties,
|
||||
THIS_MODULE, &d, adapter_nr) ||
|
||||
0 == dvb_usb_device_init(intf,
|
||||
&mxl111sf_atsc_bulk_properties,
|
||||
THIS_MODULE, &d, adapter_nr) || 0) {
|
||||
@ -669,6 +757,36 @@ static struct usb_device_id mxl111sf_table[] = {
|
||||
MODULE_DEVICE_TABLE(usb, mxl111sf_table);
|
||||
|
||||
|
||||
#define MXL111SF_EP4_BULK_STREAMING_CONFIG \
|
||||
.streaming_ctrl = mxl111sf_ep4_streaming_ctrl, \
|
||||
.stream = { \
|
||||
.type = USB_BULK, \
|
||||
.count = 5, \
|
||||
.endpoint = 0x04, \
|
||||
.u = { \
|
||||
.bulk = { \
|
||||
.buffersize = 8192, \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
/* FIXME: works for v6 but not v8 silicon */
|
||||
#define MXL111SF_EP4_ISOC_STREAMING_CONFIG \
|
||||
.streaming_ctrl = mxl111sf_ep4_streaming_ctrl, \
|
||||
.stream = { \
|
||||
.type = USB_ISOC, \
|
||||
.count = 5, \
|
||||
.endpoint = 0x04, \
|
||||
.u = { \
|
||||
.isoc = { \
|
||||
.framesperurb = 96, \
|
||||
/* FIXME: v6 SILICON: */ \
|
||||
.framesize = 564, \
|
||||
.interval = 1, \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#define MXL111SF_EP6_BULK_STREAMING_CONFIG \
|
||||
.streaming_ctrl = mxl111sf_ep6_streaming_ctrl, \
|
||||
.stream = { \
|
||||
@ -712,7 +830,7 @@ MODULE_DEVICE_TABLE(usb, mxl111sf_table);
|
||||
.generic_bulk_ctrl_endpoint_response = MXL_EP1_REG_READ, \
|
||||
.size_of_priv = sizeof(struct mxl111sf_state)
|
||||
|
||||
static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = {
|
||||
static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties = {
|
||||
MXL111SF_DEFAULT_DEVICE_PROPERTIES,
|
||||
|
||||
.num_adapters = 1,
|
||||
@ -723,10 +841,106 @@ static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = {
|
||||
.fe = {{
|
||||
.size_of_priv = sizeof(struct mxl111sf_adap_state),
|
||||
|
||||
.frontend_attach = mxl111sf_attach_demod,
|
||||
.tuner_attach = mxl111sf_attach_tuner,
|
||||
|
||||
MXL111SF_EP4_BULK_STREAMING_CONFIG,
|
||||
} },
|
||||
},
|
||||
},
|
||||
.num_device_descs = 4,
|
||||
.devices = {
|
||||
{ "Hauppauge 126xxx DVBT (bulk)",
|
||||
{ NULL },
|
||||
{ &mxl111sf_table[4], &mxl111sf_table[8],
|
||||
NULL },
|
||||
},
|
||||
{ "Hauppauge 117xxx DVBT (bulk)",
|
||||
{ NULL },
|
||||
{ &mxl111sf_table[15], &mxl111sf_table[18],
|
||||
NULL },
|
||||
},
|
||||
{ "Hauppauge 138xxx DVBT (bulk)",
|
||||
{ NULL },
|
||||
{ &mxl111sf_table[20], &mxl111sf_table[22],
|
||||
&mxl111sf_table[24], &mxl111sf_table[26],
|
||||
NULL },
|
||||
},
|
||||
{ "Hauppauge 126xxx (tp-bulk)",
|
||||
{ NULL },
|
||||
{ &mxl111sf_table[28], &mxl111sf_table[30],
|
||||
NULL },
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties = {
|
||||
MXL111SF_DEFAULT_DEVICE_PROPERTIES,
|
||||
|
||||
.num_adapters = 1,
|
||||
.adapter = {
|
||||
{
|
||||
.fe_ioctl_override = mxl111sf_fe_ioctl_override,
|
||||
.num_frontends = 1,
|
||||
.fe = {{
|
||||
.size_of_priv = sizeof(struct mxl111sf_adap_state),
|
||||
|
||||
.frontend_attach = mxl111sf_attach_demod,
|
||||
.tuner_attach = mxl111sf_attach_tuner,
|
||||
|
||||
MXL111SF_EP4_ISOC_STREAMING_CONFIG,
|
||||
} },
|
||||
},
|
||||
},
|
||||
.num_device_descs = 4,
|
||||
.devices = {
|
||||
{ "Hauppauge 126xxx DVBT (isoc)",
|
||||
{ NULL },
|
||||
{ &mxl111sf_table[4], &mxl111sf_table[8],
|
||||
NULL },
|
||||
},
|
||||
{ "Hauppauge 117xxx DVBT (isoc)",
|
||||
{ NULL },
|
||||
{ &mxl111sf_table[15], &mxl111sf_table[18],
|
||||
NULL },
|
||||
},
|
||||
{ "Hauppauge 138xxx DVBT (isoc)",
|
||||
{ NULL },
|
||||
{ &mxl111sf_table[20], &mxl111sf_table[22],
|
||||
&mxl111sf_table[24], &mxl111sf_table[26],
|
||||
NULL },
|
||||
},
|
||||
{ "Hauppauge 126xxx (tp-isoc)",
|
||||
{ NULL },
|
||||
{ &mxl111sf_table[28], &mxl111sf_table[30],
|
||||
NULL },
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = {
|
||||
MXL111SF_DEFAULT_DEVICE_PROPERTIES,
|
||||
|
||||
.num_adapters = 1,
|
||||
.adapter = {
|
||||
{
|
||||
.fe_ioctl_override = mxl111sf_fe_ioctl_override,
|
||||
.num_frontends = 2,
|
||||
.fe = {{
|
||||
.size_of_priv = sizeof(struct mxl111sf_adap_state),
|
||||
|
||||
.frontend_attach = mxl111sf_lgdt3305_frontend_attach,
|
||||
.tuner_attach = mxl111sf_attach_tuner,
|
||||
|
||||
MXL111SF_EP6_BULK_STREAMING_CONFIG,
|
||||
},
|
||||
{
|
||||
.size_of_priv = sizeof(struct mxl111sf_adap_state),
|
||||
|
||||
.frontend_attach = mxl111sf_attach_demod,
|
||||
.tuner_attach = mxl111sf_attach_tuner,
|
||||
|
||||
MXL111SF_EP4_BULK_STREAMING_CONFIG,
|
||||
}},
|
||||
},
|
||||
},
|
||||
@ -776,7 +990,7 @@ static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties = {
|
||||
.adapter = {
|
||||
{
|
||||
.fe_ioctl_override = mxl111sf_fe_ioctl_override,
|
||||
.num_frontends = 1,
|
||||
.num_frontends = 2,
|
||||
.fe = {{
|
||||
.size_of_priv = sizeof(struct mxl111sf_adap_state),
|
||||
|
||||
@ -784,6 +998,14 @@ static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties = {
|
||||
.tuner_attach = mxl111sf_attach_tuner,
|
||||
|
||||
MXL111SF_EP6_ISOC_STREAMING_CONFIG,
|
||||
},
|
||||
{
|
||||
.size_of_priv = sizeof(struct mxl111sf_adap_state),
|
||||
|
||||
.frontend_attach = mxl111sf_attach_demod,
|
||||
.tuner_attach = mxl111sf_attach_tuner,
|
||||
|
||||
MXL111SF_EP4_ISOC_STREAMING_CONFIG,
|
||||
}},
|
||||
},
|
||||
},
|
||||
|
@ -133,7 +133,7 @@ extern int dvb_usb_mxl111sf_debug;
|
||||
/* The following allows the mxl_fail() macro defined below to work
|
||||
* in externel modules, such as mxl111sf-tuner.ko, even though
|
||||
* dvb_usb_mxl111sf_debug is not defined within those modules */
|
||||
#ifdef __MXL111SF_TUNER_H__
|
||||
#if (defined(__MXL111SF_TUNER_H__)) || (defined(__MXL111SF_DEMOD_H__))
|
||||
#define MXL_ADV_DEBUG_ENABLED MXL_ADV_DBG
|
||||
#else
|
||||
#define MXL_ADV_DEBUG_ENABLED dvb_usb_mxl111sf_debug
|
||||
|
@ -11,4 +11,4 @@ ccflags-y += -Idrivers/media/dvb/frontends/
|
||||
ccflags-y += -Idrivers/media/common/tuners/
|
||||
|
||||
# For the staging CI driver cxd2099
|
||||
ccflags-y += -Idrivers/staging/cxd2099/
|
||||
ccflags-y += -Idrivers/staging/media/cxd2099/
|
||||
|
@ -128,8 +128,10 @@ struct tea5764_write_regs {
|
||||
u16 rdsbbl; /* PAUSEDET & RDSBBL */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#ifndef RADIO_TEA5764_XTAL
|
||||
#ifdef CONFIG_RADIO_TEA5764_XTAL
|
||||
#define RADIO_TEA5764_XTAL 1
|
||||
#else
|
||||
#define RADIO_TEA5764_XTAL 0
|
||||
#endif
|
||||
|
||||
static int radio_nr = -1;
|
||||
|
@ -517,6 +517,13 @@ config VIDEO_NOON010PC30
|
||||
|
||||
source "drivers/media/video/m5mols/Kconfig"
|
||||
|
||||
config VIDEO_S5K6AA
|
||||
tristate "Samsung S5K6AAFX sensor support"
|
||||
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||
---help---
|
||||
This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M
|
||||
camera sensor with an embedded SoC image signal processor.
|
||||
|
||||
comment "Flash devices"
|
||||
|
||||
config VIDEO_ADP1653
|
||||
@ -736,6 +743,8 @@ source "drivers/media/video/cx88/Kconfig"
|
||||
|
||||
source "drivers/media/video/cx23885/Kconfig"
|
||||
|
||||
source "drivers/media/video/cx25821/Kconfig"
|
||||
|
||||
source "drivers/media/video/au0828/Kconfig"
|
||||
|
||||
source "drivers/media/video/ivtv/Kconfig"
|
||||
|
@ -72,6 +72,7 @@ obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
|
||||
obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o
|
||||
obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o
|
||||
obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/
|
||||
obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o
|
||||
obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o
|
||||
|
||||
obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o
|
||||
@ -104,6 +105,7 @@ obj-$(CONFIG_VIDEO_CX88) += cx88/
|
||||
obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
|
||||
obj-$(CONFIG_VIDEO_TLG2300) += tlg2300/
|
||||
obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/
|
||||
obj-$(CONFIG_VIDEO_CX25821) += cx25821/
|
||||
obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
|
||||
obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
|
||||
obj-$(CONFIG_VIDEO_CPIA2) += cpia2/
|
||||
|
@ -94,6 +94,7 @@ struct atmel_isi {
|
||||
unsigned int irq;
|
||||
|
||||
struct isi_platform_data *pdata;
|
||||
u16 width_flags; /* max 12 bits */
|
||||
|
||||
struct list_head video_buffer_list;
|
||||
struct frame_buffer *active;
|
||||
@ -248,9 +249,9 @@ static int atmel_isi_wait_status(struct atmel_isi *isi, int wait_reset)
|
||||
/* ------------------------------------------------------------------
|
||||
Videobuf operations
|
||||
------------------------------------------------------------------*/
|
||||
static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
|
||||
unsigned int *nplanes, unsigned int sizes[],
|
||||
void *alloc_ctxs[])
|
||||
static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
|
||||
unsigned int *nbuffers, unsigned int *nplanes,
|
||||
unsigned int sizes[], void *alloc_ctxs[])
|
||||
{
|
||||
struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
|
||||
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
||||
@ -647,50 +648,42 @@ static bool isi_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt)
|
||||
fmt->packing == SOC_MBUS_PACKING_EXTEND16);
|
||||
}
|
||||
|
||||
static unsigned long make_bus_param(struct atmel_isi *isi)
|
||||
{
|
||||
unsigned long flags;
|
||||
/*
|
||||
* Platform specified synchronization and pixel clock polarities are
|
||||
* only a recommendation and are only used during probing. Atmel ISI
|
||||
* camera interface only works in master mode, i.e., uses HSYNC and
|
||||
* VSYNC signals from the sensor
|
||||
*/
|
||||
flags = SOCAM_MASTER |
|
||||
SOCAM_HSYNC_ACTIVE_HIGH |
|
||||
SOCAM_HSYNC_ACTIVE_LOW |
|
||||
SOCAM_VSYNC_ACTIVE_HIGH |
|
||||
SOCAM_VSYNC_ACTIVE_LOW |
|
||||
SOCAM_PCLK_SAMPLE_RISING |
|
||||
SOCAM_PCLK_SAMPLE_FALLING |
|
||||
SOCAM_DATA_ACTIVE_HIGH;
|
||||
|
||||
if (isi->pdata->data_width_flags & ISI_DATAWIDTH_10)
|
||||
flags |= SOCAM_DATAWIDTH_10;
|
||||
|
||||
if (isi->pdata->data_width_flags & ISI_DATAWIDTH_8)
|
||||
flags |= SOCAM_DATAWIDTH_8;
|
||||
|
||||
if (flags & SOCAM_DATAWIDTH_MASK)
|
||||
return flags;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#define ISI_BUS_PARAM (V4L2_MBUS_MASTER | \
|
||||
V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
|
||||
V4L2_MBUS_HSYNC_ACTIVE_LOW | \
|
||||
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)
|
||||
|
||||
static int isi_camera_try_bus_param(struct soc_camera_device *icd,
|
||||
unsigned char buswidth)
|
||||
{
|
||||
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
|
||||
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
||||
struct atmel_isi *isi = ici->priv;
|
||||
unsigned long camera_flags;
|
||||
struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
|
||||
unsigned long common_flags;
|
||||
int ret;
|
||||
|
||||
camera_flags = icd->ops->query_bus_param(icd);
|
||||
ret = soc_camera_bus_param_compatible(camera_flags,
|
||||
make_bus_param(isi));
|
||||
if (!ret)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
|
||||
if (!ret) {
|
||||
common_flags = soc_mbus_config_compatible(&cfg,
|
||||
ISI_BUS_PARAM);
|
||||
if (!common_flags) {
|
||||
dev_warn(icd->parent,
|
||||
"Flags incompatible: camera 0x%x, host 0x%x\n",
|
||||
cfg.flags, ISI_BUS_PARAM);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (ret != -ENOIOCTLCMD) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((1 << (buswidth - 1)) & isi->width_flags)
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
@ -812,59 +805,71 @@ static int isi_camera_querycap(struct soc_camera_host *ici,
|
||||
|
||||
static int isi_camera_set_bus_param(struct soc_camera_device *icd, u32 pixfmt)
|
||||
{
|
||||
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
|
||||
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
||||
struct atmel_isi *isi = ici->priv;
|
||||
unsigned long bus_flags, camera_flags, common_flags;
|
||||
struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
|
||||
unsigned long common_flags;
|
||||
int ret;
|
||||
u32 cfg1 = 0;
|
||||
|
||||
camera_flags = icd->ops->query_bus_param(icd);
|
||||
|
||||
bus_flags = make_bus_param(isi);
|
||||
common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags);
|
||||
dev_dbg(icd->parent, "Flags cam: 0x%lx host: 0x%lx common: 0x%lx\n",
|
||||
camera_flags, bus_flags, common_flags);
|
||||
if (!common_flags)
|
||||
return -EINVAL;
|
||||
ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
|
||||
if (!ret) {
|
||||
common_flags = soc_mbus_config_compatible(&cfg,
|
||||
ISI_BUS_PARAM);
|
||||
if (!common_flags) {
|
||||
dev_warn(icd->parent,
|
||||
"Flags incompatible: camera 0x%x, host 0x%x\n",
|
||||
cfg.flags, ISI_BUS_PARAM);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (ret != -ENOIOCTLCMD) {
|
||||
return ret;
|
||||
} else {
|
||||
common_flags = ISI_BUS_PARAM;
|
||||
}
|
||||
dev_dbg(icd->parent, "Flags cam: 0x%x host: 0x%x common: 0x%lx\n",
|
||||
cfg.flags, ISI_BUS_PARAM, common_flags);
|
||||
|
||||
/* Make choises, based on platform preferences */
|
||||
if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
|
||||
(common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
|
||||
if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
|
||||
(common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
|
||||
if (isi->pdata->hsync_act_low)
|
||||
common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
|
||||
common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
|
||||
else
|
||||
common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
|
||||
common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
|
||||
}
|
||||
|
||||
if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
|
||||
(common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
|
||||
if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
|
||||
(common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
|
||||
if (isi->pdata->vsync_act_low)
|
||||
common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
|
||||
common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
|
||||
else
|
||||
common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
|
||||
common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
|
||||
}
|
||||
|
||||
if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
|
||||
(common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
|
||||
if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
|
||||
(common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
|
||||
if (isi->pdata->pclk_act_falling)
|
||||
common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
|
||||
common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
|
||||
else
|
||||
common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
|
||||
common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
|
||||
}
|
||||
|
||||
ret = icd->ops->set_bus_param(icd, common_flags);
|
||||
if (ret < 0) {
|
||||
dev_dbg(icd->parent, "Camera set_bus_param(%lx) returned %d\n",
|
||||
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;
|
||||
}
|
||||
|
||||
/* set bus param for ISI */
|
||||
if (common_flags & SOCAM_HSYNC_ACTIVE_LOW)
|
||||
if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
|
||||
cfg1 |= ISI_CFG1_HSYNC_POL_ACTIVE_LOW;
|
||||
if (common_flags & SOCAM_VSYNC_ACTIVE_LOW)
|
||||
if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
|
||||
cfg1 |= ISI_CFG1_VSYNC_POL_ACTIVE_LOW;
|
||||
if (common_flags & SOCAM_PCLK_SAMPLE_FALLING)
|
||||
if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
|
||||
cfg1 |= ISI_CFG1_PIXCLK_POL_ACTIVE_FALLING;
|
||||
|
||||
if (isi->pdata->has_emb_sync)
|
||||
@ -983,6 +988,11 @@ static int __devinit atmel_isi_probe(struct platform_device *pdev)
|
||||
goto err_ioremap;
|
||||
}
|
||||
|
||||
if (pdata->data_width_flags & ISI_DATAWIDTH_8)
|
||||
isi->width_flags = 1 << 7;
|
||||
if (pdata->data_width_flags & ISI_DATAWIDTH_10)
|
||||
isi->width_flags |= 1 << 9;
|
||||
|
||||
isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
|
@ -1085,6 +1085,8 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev,
|
||||
setup.addr = ADDR_UNSET;
|
||||
setup.type = cx->options.tuner;
|
||||
setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */
|
||||
if (cx->options.radio > 0)
|
||||
setup.mode_mask |= T_RADIO;
|
||||
setup.tuner_callback = (setup.type == TUNER_XC2028) ?
|
||||
cx18_reset_tuner_gpio : NULL;
|
||||
cx18_call_all(cx, tuner, s_type_addr, &setup);
|
||||
|
@ -1312,7 +1312,7 @@ int cx25821_vidioc_s_input(struct file *file, void *priv, unsigned int i)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (i > 2) {
|
||||
if (i >= CX25821_NR_INPUT) {
|
||||
dprintk(1, "%s(): -EINVAL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
@ -98,6 +98,7 @@
|
||||
#define CX25821_BOARD_CONEXANT_ATHENA10 1
|
||||
#define MAX_VID_CHANNEL_NUM 12
|
||||
#define VID_CHANNEL_NUM 8
|
||||
#define CX25821_NR_INPUT 2
|
||||
|
||||
struct cx25821_fmt {
|
||||
char *name;
|
||||
@ -196,7 +197,7 @@ struct cx25821_board {
|
||||
unsigned char radio_addr;
|
||||
|
||||
u32 clk_freq;
|
||||
struct cx25821_input input[2];
|
||||
struct cx25821_input input[CX25821_NR_INPUT];
|
||||
};
|
||||
|
||||
struct cx25821_subid {
|
@ -1923,6 +1923,8 @@ struct usb_device_id em28xx_id_table[] = {
|
||||
.driver_info = EM2860_BOARD_TERRATEC_AV350 },
|
||||
{ USB_DEVICE(0x0ccd, 0x0096),
|
||||
.driver_info = EM2860_BOARD_TERRATEC_GRABBY },
|
||||
{ USB_DEVICE(0x0ccd, 0x10AF),
|
||||
.driver_info = EM2860_BOARD_TERRATEC_GRABBY },
|
||||
{ USB_DEVICE(0x0fd9, 0x0033),
|
||||
.driver_info = EM2860_BOARD_ELGATO_VIDEO_CAPTURE},
|
||||
{ USB_DEVICE(0x185b, 0x2870),
|
||||
|
@ -12,11 +12,11 @@
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/v4l2-mediabus.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include <media/soc_camera.h>
|
||||
#include <media/soc_mediabus.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
#include <media/v4l2-chip-ident.h>
|
||||
|
||||
@ -267,6 +267,17 @@ static int imx074_g_chip_ident(struct v4l2_subdev *sd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx074_g_mbus_config(struct v4l2_subdev *sd,
|
||||
struct v4l2_mbus_config *cfg)
|
||||
{
|
||||
cfg->type = V4L2_MBUS_CSI2;
|
||||
cfg->flags = V4L2_MBUS_CSI2_2_LANE |
|
||||
V4L2_MBUS_CSI2_CHANNEL_0 |
|
||||
V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct v4l2_subdev_video_ops imx074_subdev_video_ops = {
|
||||
.s_stream = imx074_s_stream,
|
||||
.s_mbus_fmt = imx074_s_fmt,
|
||||
@ -275,6 +286,7 @@ static struct v4l2_subdev_video_ops imx074_subdev_video_ops = {
|
||||
.enum_mbus_fmt = imx074_enum_fmt,
|
||||
.g_crop = imx074_g_crop,
|
||||
.cropcap = imx074_cropcap,
|
||||
.g_mbus_config = imx074_g_mbus_config,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_core_ops imx074_subdev_core_ops = {
|
||||
@ -286,28 +298,7 @@ static struct v4l2_subdev_ops imx074_subdev_ops = {
|
||||
.video = &imx074_subdev_video_ops,
|
||||
};
|
||||
|
||||
/*
|
||||
* We have to provide soc-camera operations, but we don't have anything to say
|
||||
* there. The MIPI CSI2 driver will provide .query_bus_param and .set_bus_param
|
||||
*/
|
||||
static unsigned long imx074_query_bus_param(struct soc_camera_device *icd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx074_set_bus_param(struct soc_camera_device *icd,
|
||||
unsigned long flags)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct soc_camera_ops imx074_ops = {
|
||||
.query_bus_param = imx074_query_bus_param,
|
||||
.set_bus_param = imx074_set_bus_param,
|
||||
};
|
||||
|
||||
static int imx074_video_probe(struct soc_camera_device *icd,
|
||||
struct i2c_client *client)
|
||||
static int imx074_video_probe(struct i2c_client *client)
|
||||
{
|
||||
int ret;
|
||||
u16 id;
|
||||
@ -417,17 +408,10 @@ static int imx074_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *did)
|
||||
{
|
||||
struct imx074 *priv;
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct soc_camera_link *icl;
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
int ret;
|
||||
|
||||
if (!icd) {
|
||||
dev_err(&client->dev, "IMX074: missing soc-camera data!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
icl = to_soc_camera_link(icd);
|
||||
if (!icl) {
|
||||
dev_err(&client->dev, "IMX074: missing platform data!\n");
|
||||
return -EINVAL;
|
||||
@ -445,12 +429,10 @@ static int imx074_probe(struct i2c_client *client,
|
||||
|
||||
v4l2_i2c_subdev_init(&priv->subdev, client, &imx074_subdev_ops);
|
||||
|
||||
icd->ops = &imx074_ops;
|
||||
priv->fmt = &imx074_colour_fmts[0];
|
||||
|
||||
ret = imx074_video_probe(icd, client);
|
||||
ret = imx074_video_probe(client);
|
||||
if (ret < 0) {
|
||||
icd->ops = NULL;
|
||||
kfree(priv);
|
||||
return ret;
|
||||
}
|
||||
@ -461,10 +443,8 @@ static int imx074_probe(struct i2c_client *client,
|
||||
static int imx074_remove(struct i2c_client *client)
|
||||
{
|
||||
struct imx074 *priv = to_imx074(client);
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
struct soc_camera_link *icl = to_soc_camera_link(icd);
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
|
||||
icd->ops = NULL;
|
||||
if (icl->free_bus)
|
||||
icl->free_bus(icl);
|
||||
kfree(priv);
|
||||
|
@ -1180,6 +1180,8 @@ static int __devinit ivtv_probe(struct pci_dev *pdev,
|
||||
setup.addr = ADDR_UNSET;
|
||||
setup.type = itv->options.tuner;
|
||||
setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */
|
||||
if (itv->options.radio > 0)
|
||||
setup.mode_mask |= T_RADIO;
|
||||
setup.tuner_callback = (setup.type == TUNER_XC2028) ?
|
||||
ivtv_reset_tuner_gpio : NULL;
|
||||
ivtv_call_all(itv, tuner, s_type_addr, &setup);
|
||||
|
@ -883,7 +883,8 @@ static int mcam_read_setup(struct mcam_camera *cam)
|
||||
* Videobuf2 interface code.
|
||||
*/
|
||||
|
||||
static int mcam_vb_queue_setup(struct vb2_queue *vq, unsigned int *nbufs,
|
||||
static int mcam_vb_queue_setup(struct vb2_queue *vq,
|
||||
const struct v4l2_format *fmt, unsigned int *nbufs,
|
||||
unsigned int *num_planes, unsigned int sizes[],
|
||||
void *alloc_ctxs[])
|
||||
{
|
||||
|
@ -738,9 +738,10 @@ static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = {
|
||||
* Queue operations
|
||||
*/
|
||||
|
||||
static int m2mtest_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
|
||||
unsigned int *nplanes, unsigned int sizes[],
|
||||
void *alloc_ctxs[])
|
||||
static int m2mtest_queue_setup(struct vb2_queue *vq,
|
||||
const struct v4l2_format *fmt,
|
||||
unsigned int *nbuffers, unsigned int *nplanes,
|
||||
unsigned int sizes[], void *alloc_ctxs[])
|
||||
{
|
||||
struct m2mtest_ctx *ctx = vb2_get_drv_priv(vq);
|
||||
struct m2mtest_q_data *q_data;
|
||||
|
@ -13,9 +13,11 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/log2.h>
|
||||
|
||||
#include <media/soc_camera.h>
|
||||
#include <media/soc_mediabus.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
#include <media/v4l2-chip-ident.h>
|
||||
#include <media/soc_camera.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
|
||||
/*
|
||||
* mt9m001 i2c address 0x5d
|
||||
@ -84,15 +86,19 @@ static const struct mt9m001_datafmt mt9m001_monochrome_fmts[] = {
|
||||
|
||||
struct mt9m001 {
|
||||
struct v4l2_subdev subdev;
|
||||
struct v4l2_ctrl_handler hdl;
|
||||
struct {
|
||||
/* exposure/auto-exposure cluster */
|
||||
struct v4l2_ctrl *autoexposure;
|
||||
struct v4l2_ctrl *exposure;
|
||||
};
|
||||
struct v4l2_rect rect; /* Sensor window */
|
||||
const struct mt9m001_datafmt *fmt;
|
||||
const struct mt9m001_datafmt *fmts;
|
||||
int num_fmts;
|
||||
int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */
|
||||
unsigned int gain;
|
||||
unsigned int exposure;
|
||||
unsigned int total_h;
|
||||
unsigned short y_skip_top; /* Lines to skip at the top */
|
||||
unsigned char autoexposure;
|
||||
};
|
||||
|
||||
static struct mt9m001 *to_mt9m001(const struct i2c_client *client)
|
||||
@ -165,54 +171,13 @@ static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt9m001_set_bus_param(struct soc_camera_device *icd,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct soc_camera_link *icl = to_soc_camera_link(icd);
|
||||
unsigned long width_flag = flags & SOCAM_DATAWIDTH_MASK;
|
||||
|
||||
/* Only one width bit may be set */
|
||||
if (!is_power_of_2(width_flag))
|
||||
return -EINVAL;
|
||||
|
||||
if (icl->set_bus_param)
|
||||
return icl->set_bus_param(icl, width_flag);
|
||||
|
||||
/*
|
||||
* Without board specific bus width settings we only support the
|
||||
* sensors native bus width
|
||||
*/
|
||||
if (width_flag == SOCAM_DATAWIDTH_10)
|
||||
return 0;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd)
|
||||
{
|
||||
struct soc_camera_link *icl = to_soc_camera_link(icd);
|
||||
/* MT9M001 has all capture_format parameters fixed */
|
||||
unsigned long flags = SOCAM_PCLK_SAMPLE_FALLING |
|
||||
SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
|
||||
SOCAM_DATA_ACTIVE_HIGH | SOCAM_MASTER;
|
||||
|
||||
if (icl->query_bus_param)
|
||||
flags |= icl->query_bus_param(icl) & SOCAM_DATAWIDTH_MASK;
|
||||
else
|
||||
flags |= SOCAM_DATAWIDTH_10;
|
||||
|
||||
return soc_camera_apply_sensor_flags(icl, flags);
|
||||
}
|
||||
|
||||
static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct mt9m001 *mt9m001 = to_mt9m001(client);
|
||||
struct v4l2_rect rect = a->c;
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
int ret;
|
||||
const u16 hblank = 9, vblank = 25;
|
||||
unsigned int total_h;
|
||||
|
||||
if (mt9m001->fmts == mt9m001_colour_fmts)
|
||||
/*
|
||||
@ -231,7 +196,7 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
|
||||
soc_camera_limit_side(&rect.top, &rect.height,
|
||||
MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT);
|
||||
|
||||
total_h = rect.height + mt9m001->y_skip_top + vblank;
|
||||
mt9m001->total_h = rect.height + mt9m001->y_skip_top + vblank;
|
||||
|
||||
/* Blanking and start values - default... */
|
||||
ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank);
|
||||
@ -240,7 +205,7 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
|
||||
|
||||
/*
|
||||
* The caller provides a supported format, as verified per
|
||||
* call to icd->try_fmt()
|
||||
* call to .try_mbus_fmt()
|
||||
*/
|
||||
if (!ret)
|
||||
ret = reg_write(client, MT9M001_COLUMN_START, rect.left);
|
||||
@ -251,17 +216,8 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
|
||||
if (!ret)
|
||||
ret = reg_write(client, MT9M001_WINDOW_HEIGHT,
|
||||
rect.height + mt9m001->y_skip_top - 1);
|
||||
if (!ret && mt9m001->autoexposure) {
|
||||
ret = reg_write(client, MT9M001_SHUTTER_WIDTH, total_h);
|
||||
if (!ret) {
|
||||
const struct v4l2_queryctrl *qctrl =
|
||||
soc_camera_find_qctrl(icd->ops,
|
||||
V4L2_CID_EXPOSURE);
|
||||
mt9m001->exposure = (524 + (total_h - 1) *
|
||||
(qctrl->maximum - qctrl->minimum)) /
|
||||
1048 + qctrl->minimum;
|
||||
}
|
||||
}
|
||||
if (!ret && v4l2_ctrl_g_ctrl(mt9m001->autoexposure) == V4L2_EXPOSURE_AUTO)
|
||||
ret = reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h);
|
||||
|
||||
if (!ret)
|
||||
mt9m001->rect = rect;
|
||||
@ -421,107 +377,48 @@ static int mt9m001_s_register(struct v4l2_subdev *sd,
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct v4l2_queryctrl mt9m001_controls[] = {
|
||||
{
|
||||
.id = V4L2_CID_VFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Flip Vertically",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
}, {
|
||||
.id = V4L2_CID_GAIN,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Gain",
|
||||
.minimum = 0,
|
||||
.maximum = 127,
|
||||
.step = 1,
|
||||
.default_value = 64,
|
||||
.flags = V4L2_CTRL_FLAG_SLIDER,
|
||||
}, {
|
||||
.id = V4L2_CID_EXPOSURE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Exposure",
|
||||
.minimum = 1,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
.default_value = 255,
|
||||
.flags = V4L2_CTRL_FLAG_SLIDER,
|
||||
}, {
|
||||
.id = V4L2_CID_EXPOSURE_AUTO,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Automatic Exposure",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 1,
|
||||
}
|
||||
};
|
||||
|
||||
static struct soc_camera_ops mt9m001_ops = {
|
||||
.set_bus_param = mt9m001_set_bus_param,
|
||||
.query_bus_param = mt9m001_query_bus_param,
|
||||
.controls = mt9m001_controls,
|
||||
.num_controls = ARRAY_SIZE(mt9m001_controls),
|
||||
};
|
||||
|
||||
static int mt9m001_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
static int mt9m001_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct mt9m001 *mt9m001 = to_mt9m001(client);
|
||||
int data;
|
||||
struct mt9m001 *mt9m001 = container_of(ctrl->handler,
|
||||
struct mt9m001, hdl);
|
||||
s32 min, max;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_VFLIP:
|
||||
data = reg_read(client, MT9M001_READ_OPTIONS2);
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
ctrl->value = !!(data & 0x8000);
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE_AUTO:
|
||||
ctrl->value = mt9m001->autoexposure;
|
||||
break;
|
||||
case V4L2_CID_GAIN:
|
||||
ctrl->value = mt9m001->gain;
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE:
|
||||
ctrl->value = mt9m001->exposure;
|
||||
min = mt9m001->exposure->minimum;
|
||||
max = mt9m001->exposure->maximum;
|
||||
mt9m001->exposure->val =
|
||||
(524 + (mt9m001->total_h - 1) * (max - min)) / 1048 + min;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct mt9m001 *mt9m001 = container_of(ctrl->handler,
|
||||
struct mt9m001, hdl);
|
||||
struct v4l2_subdev *sd = &mt9m001->subdev;
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct mt9m001 *mt9m001 = to_mt9m001(client);
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
const struct v4l2_queryctrl *qctrl;
|
||||
struct v4l2_ctrl *exp = mt9m001->exposure;
|
||||
int data;
|
||||
|
||||
qctrl = soc_camera_find_qctrl(&mt9m001_ops, ctrl->id);
|
||||
|
||||
if (!qctrl)
|
||||
return -EINVAL;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_VFLIP:
|
||||
if (ctrl->value)
|
||||
if (ctrl->val)
|
||||
data = reg_set(client, MT9M001_READ_OPTIONS2, 0x8000);
|
||||
else
|
||||
data = reg_clear(client, MT9M001_READ_OPTIONS2, 0x8000);
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
break;
|
||||
return 0;
|
||||
|
||||
case V4L2_CID_GAIN:
|
||||
if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
|
||||
return -EINVAL;
|
||||
/* See Datasheet Table 7, Gain settings. */
|
||||
if (ctrl->value <= qctrl->default_value) {
|
||||
if (ctrl->val <= ctrl->default_value) {
|
||||
/* Pack it into 0..1 step 0.125, register values 0..8 */
|
||||
unsigned long range = qctrl->default_value - qctrl->minimum;
|
||||
data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range;
|
||||
unsigned long range = ctrl->default_value - ctrl->minimum;
|
||||
data = ((ctrl->val - ctrl->minimum) * 8 + range / 2) / range;
|
||||
|
||||
dev_dbg(&client->dev, "Setting gain %d\n", data);
|
||||
data = reg_write(client, MT9M001_GLOBAL_GAIN, data);
|
||||
@ -530,8 +427,8 @@ static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
} else {
|
||||
/* Pack it into 1.125..15 variable step, register values 9..67 */
|
||||
/* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */
|
||||
unsigned long range = qctrl->maximum - qctrl->default_value - 1;
|
||||
unsigned long gain = ((ctrl->value - qctrl->default_value - 1) *
|
||||
unsigned long range = ctrl->maximum - ctrl->default_value - 1;
|
||||
unsigned long gain = ((ctrl->val - ctrl->default_value - 1) *
|
||||
111 + range / 2) / range + 9;
|
||||
|
||||
if (gain <= 32)
|
||||
@ -547,66 +444,44 @@ static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
|
||||
/* Success */
|
||||
mt9m001->gain = ctrl->value;
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE:
|
||||
/* mt9m001 has maximum == default */
|
||||
if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
|
||||
return -EINVAL;
|
||||
else {
|
||||
unsigned long range = qctrl->maximum - qctrl->minimum;
|
||||
unsigned long shutter = ((ctrl->value - qctrl->minimum) * 1048 +
|
||||
case V4L2_CID_EXPOSURE_AUTO:
|
||||
if (ctrl->val == V4L2_EXPOSURE_MANUAL) {
|
||||
unsigned long range = exp->maximum - exp->minimum;
|
||||
unsigned long shutter = ((exp->val - exp->minimum) * 1048 +
|
||||
range / 2) / range + 1;
|
||||
|
||||
dev_dbg(&client->dev,
|
||||
"Setting shutter width from %d to %lu\n",
|
||||
reg_read(client, MT9M001_SHUTTER_WIDTH),
|
||||
shutter);
|
||||
reg_read(client, MT9M001_SHUTTER_WIDTH), shutter);
|
||||
if (reg_write(client, MT9M001_SHUTTER_WIDTH, shutter) < 0)
|
||||
return -EIO;
|
||||
mt9m001->exposure = ctrl->value;
|
||||
mt9m001->autoexposure = 0;
|
||||
}
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE_AUTO:
|
||||
if (ctrl->value) {
|
||||
} else {
|
||||
const u16 vblank = 25;
|
||||
unsigned int total_h = mt9m001->rect.height +
|
||||
|
||||
mt9m001->total_h = mt9m001->rect.height +
|
||||
mt9m001->y_skip_top + vblank;
|
||||
if (reg_write(client, MT9M001_SHUTTER_WIDTH,
|
||||
total_h) < 0)
|
||||
if (reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h) < 0)
|
||||
return -EIO;
|
||||
qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE);
|
||||
mt9m001->exposure = (524 + (total_h - 1) *
|
||||
(qctrl->maximum - qctrl->minimum)) /
|
||||
1048 + qctrl->minimum;
|
||||
mt9m001->autoexposure = 1;
|
||||
} else
|
||||
mt9m001->autoexposure = 0;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Interface active, can use i2c. If it fails, it can indeed mean, that
|
||||
* this wasn't our capture interface, so, we wait for the right one
|
||||
*/
|
||||
static int mt9m001_video_probe(struct soc_camera_device *icd,
|
||||
static int mt9m001_video_probe(struct soc_camera_link *icl,
|
||||
struct i2c_client *client)
|
||||
{
|
||||
struct mt9m001 *mt9m001 = to_mt9m001(client);
|
||||
struct soc_camera_link *icl = to_soc_camera_link(icd);
|
||||
s32 data;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
/* We must have a parent by now. And it cannot be a wrong one. */
|
||||
BUG_ON(!icd->parent ||
|
||||
to_soc_camera_host(icd->parent)->nr != icd->iface);
|
||||
|
||||
/* Enable the chip */
|
||||
data = reg_write(client, MT9M001_CHIP_ENABLE, 1);
|
||||
dev_dbg(&client->dev, "write: %d\n", data);
|
||||
@ -661,18 +536,11 @@ static int mt9m001_video_probe(struct soc_camera_device *icd,
|
||||
dev_err(&client->dev, "Failed to initialise the camera\n");
|
||||
|
||||
/* mt9m001_init() has reset the chip, returning registers to defaults */
|
||||
mt9m001->gain = 64;
|
||||
mt9m001->exposure = 255;
|
||||
|
||||
return ret;
|
||||
return v4l2_ctrl_handler_setup(&mt9m001->hdl);
|
||||
}
|
||||
|
||||
static void mt9m001_video_remove(struct soc_camera_device *icd)
|
||||
static void mt9m001_video_remove(struct soc_camera_link *icl)
|
||||
{
|
||||
struct soc_camera_link *icl = to_soc_camera_link(icd);
|
||||
|
||||
dev_dbg(icd->pdev, "Video removed: %p, %p\n",
|
||||
icd->parent, icd->vdev);
|
||||
if (icl->free_bus)
|
||||
icl->free_bus(icl);
|
||||
}
|
||||
@ -687,9 +555,12 @@ static int mt9m001_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct v4l2_ctrl_ops mt9m001_ctrl_ops = {
|
||||
.g_volatile_ctrl = mt9m001_g_volatile_ctrl,
|
||||
.s_ctrl = mt9m001_s_ctrl,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = {
|
||||
.g_ctrl = mt9m001_g_ctrl,
|
||||
.s_ctrl = mt9m001_s_ctrl,
|
||||
.g_chip_ident = mt9m001_g_chip_ident,
|
||||
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
||||
.g_register = mt9m001_g_register,
|
||||
@ -710,6 +581,40 @@ static int mt9m001_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt9m001_g_mbus_config(struct v4l2_subdev *sd,
|
||||
struct v4l2_mbus_config *cfg)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
|
||||
/* MT9M001 has all capture_format parameters fixed */
|
||||
cfg->flags = V4L2_MBUS_PCLK_SAMPLE_FALLING |
|
||||
V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
|
||||
V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_MASTER;
|
||||
cfg->type = V4L2_MBUS_PARALLEL;
|
||||
cfg->flags = soc_camera_apply_board_flags(icl, cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt9m001_s_mbus_config(struct v4l2_subdev *sd,
|
||||
const struct v4l2_mbus_config *cfg)
|
||||
{
|
||||
const struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
struct mt9m001 *mt9m001 = to_mt9m001(client);
|
||||
unsigned int bps = soc_mbus_get_fmtdesc(mt9m001->fmt->code)->bits_per_sample;
|
||||
|
||||
if (icl->set_bus_param)
|
||||
return icl->set_bus_param(icl, 1 << (bps - 1));
|
||||
|
||||
/*
|
||||
* Without board specific bus width settings we only support the
|
||||
* sensors native bus width
|
||||
*/
|
||||
return bps == 10 ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = {
|
||||
.s_stream = mt9m001_s_stream,
|
||||
.s_mbus_fmt = mt9m001_s_fmt,
|
||||
@ -719,6 +624,8 @@ static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = {
|
||||
.g_crop = mt9m001_g_crop,
|
||||
.cropcap = mt9m001_cropcap,
|
||||
.enum_mbus_fmt = mt9m001_enum_fmt,
|
||||
.g_mbus_config = mt9m001_g_mbus_config,
|
||||
.s_mbus_config = mt9m001_s_mbus_config,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = {
|
||||
@ -735,17 +642,10 @@ static int mt9m001_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *did)
|
||||
{
|
||||
struct mt9m001 *mt9m001;
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct soc_camera_link *icl;
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
int ret;
|
||||
|
||||
if (!icd) {
|
||||
dev_err(&client->dev, "MT9M001: missing soc-camera data!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
icl = to_soc_camera_link(icd);
|
||||
if (!icl) {
|
||||
dev_err(&client->dev, "MT9M001 driver needs platform data\n");
|
||||
return -EINVAL;
|
||||
@ -762,25 +662,40 @@ static int mt9m001_probe(struct i2c_client *client,
|
||||
return -ENOMEM;
|
||||
|
||||
v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops);
|
||||
v4l2_ctrl_handler_init(&mt9m001->hdl, 4);
|
||||
v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
|
||||
V4L2_CID_VFLIP, 0, 1, 1, 0);
|
||||
v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
|
||||
V4L2_CID_GAIN, 0, 127, 1, 64);
|
||||
mt9m001->exposure = v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
|
||||
V4L2_CID_EXPOSURE, 1, 255, 1, 255);
|
||||
/*
|
||||
* Simulated autoexposure. If enabled, we calculate shutter width
|
||||
* ourselves in the driver based on vertical blanking and frame width
|
||||
*/
|
||||
mt9m001->autoexposure = v4l2_ctrl_new_std_menu(&mt9m001->hdl,
|
||||
&mt9m001_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
|
||||
V4L2_EXPOSURE_AUTO);
|
||||
mt9m001->subdev.ctrl_handler = &mt9m001->hdl;
|
||||
if (mt9m001->hdl.error) {
|
||||
int err = mt9m001->hdl.error;
|
||||
|
||||
kfree(mt9m001);
|
||||
return err;
|
||||
}
|
||||
v4l2_ctrl_auto_cluster(2, &mt9m001->autoexposure,
|
||||
V4L2_EXPOSURE_MANUAL, true);
|
||||
|
||||
/* Second stage probe - when a capture adapter is there */
|
||||
icd->ops = &mt9m001_ops;
|
||||
|
||||
mt9m001->y_skip_top = 0;
|
||||
mt9m001->rect.left = MT9M001_COLUMN_SKIP;
|
||||
mt9m001->rect.top = MT9M001_ROW_SKIP;
|
||||
mt9m001->rect.width = MT9M001_MAX_WIDTH;
|
||||
mt9m001->rect.height = MT9M001_MAX_HEIGHT;
|
||||
|
||||
/*
|
||||
* Simulated autoexposure. If enabled, we calculate shutter width
|
||||
* ourselves in the driver based on vertical blanking and frame width
|
||||
*/
|
||||
mt9m001->autoexposure = 1;
|
||||
|
||||
ret = mt9m001_video_probe(icd, client);
|
||||
ret = mt9m001_video_probe(icl, client);
|
||||
if (ret) {
|
||||
icd->ops = NULL;
|
||||
v4l2_ctrl_handler_free(&mt9m001->hdl);
|
||||
kfree(mt9m001);
|
||||
}
|
||||
|
||||
@ -790,10 +705,11 @@ static int mt9m001_probe(struct i2c_client *client,
|
||||
static int mt9m001_remove(struct i2c_client *client)
|
||||
{
|
||||
struct mt9m001 *mt9m001 = to_mt9m001(client);
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
|
||||
icd->ops = NULL;
|
||||
mt9m001_video_remove(icd);
|
||||
v4l2_device_unregister_subdev(&mt9m001->subdev);
|
||||
v4l2_ctrl_handler_free(&mt9m001->hdl);
|
||||
mt9m001_video_remove(icl);
|
||||
kfree(mt9m001);
|
||||
|
||||
return 0;
|
||||
|
@ -13,10 +13,12 @@
|
||||
#include <linux/log2.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/v4l2-mediabus.h>
|
||||
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/v4l2-chip-ident.h>
|
||||
#include <media/soc_camera.h>
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-chip-ident.h>
|
||||
|
||||
/*
|
||||
* MT9M111, MT9M112 and MT9M131:
|
||||
@ -177,6 +179,8 @@ enum mt9m111_context {
|
||||
|
||||
struct mt9m111 {
|
||||
struct v4l2_subdev subdev;
|
||||
struct v4l2_ctrl_handler hdl;
|
||||
struct v4l2_ctrl *gain;
|
||||
int model; /* V4L2_IDENT_MT9M111 or V4L2_IDENT_MT9M112 code
|
||||
* from v4l2-chip-ident.h */
|
||||
enum mt9m111_context context;
|
||||
@ -185,13 +189,8 @@ struct mt9m111 {
|
||||
int power_count;
|
||||
const struct mt9m111_datafmt *fmt;
|
||||
int lastpage; /* PageMap cache value */
|
||||
unsigned int gain;
|
||||
unsigned char autoexposure;
|
||||
unsigned char datawidth;
|
||||
unsigned int powered:1;
|
||||
unsigned int hflip:1;
|
||||
unsigned int vflip:1;
|
||||
unsigned int autowhitebalance:1;
|
||||
};
|
||||
|
||||
static struct mt9m111 *to_mt9m111(const struct i2c_client *client)
|
||||
@ -363,21 +362,6 @@ static int mt9m111_reset(struct mt9m111 *mt9m111)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned long mt9m111_query_bus_param(struct soc_camera_device *icd)
|
||||
{
|
||||
struct soc_camera_link *icl = to_soc_camera_link(icd);
|
||||
unsigned long flags = SOCAM_MASTER | SOCAM_PCLK_SAMPLE_RISING |
|
||||
SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
|
||||
SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
|
||||
|
||||
return soc_camera_apply_sensor_flags(icl, flags);
|
||||
}
|
||||
|
||||
static int mt9m111_set_bus_param(struct soc_camera_device *icd, unsigned long f)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt9m111_make_rect(struct mt9m111 *mt9m111,
|
||||
struct v4l2_rect *rect)
|
||||
{
|
||||
@ -660,50 +644,6 @@ static int mt9m111_s_register(struct v4l2_subdev *sd,
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct v4l2_queryctrl mt9m111_controls[] = {
|
||||
{
|
||||
.id = V4L2_CID_VFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Flip Verticaly",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
}, {
|
||||
.id = V4L2_CID_HFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Flip Horizontaly",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
}, { /* gain = 1/32*val (=>gain=1 if val==32) */
|
||||
.id = V4L2_CID_GAIN,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Gain",
|
||||
.minimum = 0,
|
||||
.maximum = 63 * 2 * 2,
|
||||
.step = 1,
|
||||
.default_value = 32,
|
||||
.flags = V4L2_CTRL_FLAG_SLIDER,
|
||||
}, {
|
||||
.id = V4L2_CID_EXPOSURE_AUTO,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Auto Exposure",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 1,
|
||||
}
|
||||
};
|
||||
|
||||
static struct soc_camera_ops mt9m111_ops = {
|
||||
.query_bus_param = mt9m111_query_bus_param,
|
||||
.set_bus_param = mt9m111_set_bus_param,
|
||||
.controls = mt9m111_controls,
|
||||
.num_controls = ARRAY_SIZE(mt9m111_controls),
|
||||
};
|
||||
|
||||
static int mt9m111_set_flip(struct mt9m111 *mt9m111, int flip, int mask)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
|
||||
@ -744,7 +684,6 @@ static int mt9m111_set_global_gain(struct mt9m111 *mt9m111, int gain)
|
||||
if (gain > 63 * 2 * 2)
|
||||
return -EINVAL;
|
||||
|
||||
mt9m111->gain = gain;
|
||||
if ((gain >= 64 * 2) && (gain < 63 * 2 * 2))
|
||||
val = (1 << 10) | (1 << 9) | (gain / 4);
|
||||
else if ((gain >= 64) && (gain < 64 * 2))
|
||||
@ -758,118 +697,47 @@ static int mt9m111_set_global_gain(struct mt9m111 *mt9m111, int gain)
|
||||
static int mt9m111_set_autoexposure(struct mt9m111 *mt9m111, int on)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
|
||||
int ret;
|
||||
|
||||
if (on)
|
||||
ret = reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
|
||||
else
|
||||
ret = reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
|
||||
|
||||
if (!ret)
|
||||
mt9m111->autoexposure = on;
|
||||
|
||||
return ret;
|
||||
return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
|
||||
return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
|
||||
}
|
||||
|
||||
static int mt9m111_set_autowhitebalance(struct mt9m111 *mt9m111, int on)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
|
||||
int ret;
|
||||
|
||||
if (on)
|
||||
ret = reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
|
||||
else
|
||||
ret = reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
|
||||
|
||||
if (!ret)
|
||||
mt9m111->autowhitebalance = on;
|
||||
|
||||
return ret;
|
||||
return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
|
||||
return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
|
||||
}
|
||||
|
||||
static int mt9m111_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
static int mt9m111_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
|
||||
int data;
|
||||
struct mt9m111 *mt9m111 = container_of(ctrl->handler,
|
||||
struct mt9m111, hdl);
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_VFLIP:
|
||||
if (mt9m111->context == HIGHPOWER)
|
||||
data = reg_read(READ_MODE_B);
|
||||
else
|
||||
data = reg_read(READ_MODE_A);
|
||||
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
ctrl->value = !!(data & MT9M111_RMB_MIRROR_ROWS);
|
||||
break;
|
||||
case V4L2_CID_HFLIP:
|
||||
if (mt9m111->context == HIGHPOWER)
|
||||
data = reg_read(READ_MODE_B);
|
||||
else
|
||||
data = reg_read(READ_MODE_A);
|
||||
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
ctrl->value = !!(data & MT9M111_RMB_MIRROR_COLS);
|
||||
break;
|
||||
case V4L2_CID_GAIN:
|
||||
data = mt9m111_get_global_gain(mt9m111);
|
||||
if (data < 0)
|
||||
return data;
|
||||
ctrl->value = data;
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE_AUTO:
|
||||
ctrl->value = mt9m111->autoexposure;
|
||||
break;
|
||||
case V4L2_CID_AUTO_WHITE_BALANCE:
|
||||
ctrl->value = mt9m111->autowhitebalance;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt9m111_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
{
|
||||
struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
|
||||
const struct v4l2_queryctrl *qctrl;
|
||||
int ret;
|
||||
|
||||
qctrl = soc_camera_find_qctrl(&mt9m111_ops, ctrl->id);
|
||||
if (!qctrl)
|
||||
return -EINVAL;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_VFLIP:
|
||||
mt9m111->vflip = ctrl->value;
|
||||
ret = mt9m111_set_flip(mt9m111, ctrl->value,
|
||||
return mt9m111_set_flip(mt9m111, ctrl->val,
|
||||
MT9M111_RMB_MIRROR_ROWS);
|
||||
break;
|
||||
case V4L2_CID_HFLIP:
|
||||
mt9m111->hflip = ctrl->value;
|
||||
ret = mt9m111_set_flip(mt9m111, ctrl->value,
|
||||
return mt9m111_set_flip(mt9m111, ctrl->val,
|
||||
MT9M111_RMB_MIRROR_COLS);
|
||||
break;
|
||||
case V4L2_CID_GAIN:
|
||||
ret = mt9m111_set_global_gain(mt9m111, ctrl->value);
|
||||
break;
|
||||
return mt9m111_set_global_gain(mt9m111, ctrl->val);
|
||||
case V4L2_CID_EXPOSURE_AUTO:
|
||||
ret = mt9m111_set_autoexposure(mt9m111, ctrl->value);
|
||||
break;
|
||||
return mt9m111_set_autoexposure(mt9m111, ctrl->val);
|
||||
case V4L2_CID_AUTO_WHITE_BALANCE:
|
||||
ret = mt9m111_set_autowhitebalance(mt9m111, ctrl->value);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
return mt9m111_set_autowhitebalance(mt9m111, ctrl->val);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mt9m111_suspend(struct mt9m111 *mt9m111)
|
||||
{
|
||||
mt9m111->gain = mt9m111_get_global_gain(mt9m111);
|
||||
v4l2_ctrl_s_ctrl(mt9m111->gain, mt9m111_get_global_gain(mt9m111));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -879,11 +747,7 @@ static void mt9m111_restore_state(struct mt9m111 *mt9m111)
|
||||
mt9m111_set_context(mt9m111, mt9m111->context);
|
||||
mt9m111_set_pixfmt(mt9m111, mt9m111->fmt->code);
|
||||
mt9m111_setup_rect(mt9m111, &mt9m111->rect);
|
||||
mt9m111_set_flip(mt9m111, mt9m111->hflip, MT9M111_RMB_MIRROR_COLS);
|
||||
mt9m111_set_flip(mt9m111, mt9m111->vflip, MT9M111_RMB_MIRROR_ROWS);
|
||||
mt9m111_set_global_gain(mt9m111, mt9m111->gain);
|
||||
mt9m111_set_autoexposure(mt9m111, mt9m111->autoexposure);
|
||||
mt9m111_set_autowhitebalance(mt9m111, mt9m111->autowhitebalance);
|
||||
v4l2_ctrl_handler_setup(&mt9m111->hdl);
|
||||
}
|
||||
|
||||
static int mt9m111_resume(struct mt9m111 *mt9m111)
|
||||
@ -911,8 +775,6 @@ static int mt9m111_init(struct mt9m111 *mt9m111)
|
||||
ret = mt9m111_reset(mt9m111);
|
||||
if (!ret)
|
||||
ret = mt9m111_set_context(mt9m111, mt9m111->context);
|
||||
if (!ret)
|
||||
ret = mt9m111_set_autoexposure(mt9m111, mt9m111->autoexposure);
|
||||
if (ret)
|
||||
dev_err(&client->dev, "mt9m111 init failed: %d\n", ret);
|
||||
return ret;
|
||||
@ -922,22 +784,12 @@ static int mt9m111_init(struct mt9m111 *mt9m111)
|
||||
* Interface active, can use i2c. If it fails, it can indeed mean, that
|
||||
* this wasn't our capture interface, so, we wait for the right one
|
||||
*/
|
||||
static int mt9m111_video_probe(struct soc_camera_device *icd,
|
||||
struct i2c_client *client)
|
||||
static int mt9m111_video_probe(struct i2c_client *client)
|
||||
{
|
||||
struct mt9m111 *mt9m111 = to_mt9m111(client);
|
||||
s32 data;
|
||||
int ret;
|
||||
|
||||
/* We must have a parent by now. And it cannot be a wrong one. */
|
||||
BUG_ON(!icd->parent ||
|
||||
to_soc_camera_host(icd->parent)->nr != icd->iface);
|
||||
|
||||
mt9m111->lastpage = -1;
|
||||
|
||||
mt9m111->autoexposure = 1;
|
||||
mt9m111->autowhitebalance = 1;
|
||||
|
||||
data = reg_read(CHIP_VERSION);
|
||||
|
||||
switch (data) {
|
||||
@ -951,17 +803,16 @@ static int mt9m111_video_probe(struct soc_camera_device *icd,
|
||||
dev_info(&client->dev, "Detected a MT9M112 chip ID %x\n", data);
|
||||
break;
|
||||
default:
|
||||
ret = -ENODEV;
|
||||
dev_err(&client->dev,
|
||||
"No MT9M111/MT9M112/MT9M131 chip detected register read %x\n",
|
||||
data);
|
||||
goto ei2c;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = mt9m111_init(mt9m111);
|
||||
|
||||
ei2c:
|
||||
return ret;
|
||||
if (ret)
|
||||
return ret;
|
||||
return v4l2_ctrl_handler_setup(&mt9m111->hdl);
|
||||
}
|
||||
|
||||
static int mt9m111_s_power(struct v4l2_subdev *sd, int on)
|
||||
@ -998,9 +849,11 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct v4l2_ctrl_ops mt9m111_ctrl_ops = {
|
||||
.s_ctrl = mt9m111_s_ctrl,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = {
|
||||
.g_ctrl = mt9m111_g_ctrl,
|
||||
.s_ctrl = mt9m111_s_ctrl,
|
||||
.g_chip_ident = mt9m111_g_chip_ident,
|
||||
.s_power = mt9m111_s_power,
|
||||
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
||||
@ -1019,6 +872,21 @@ static int mt9m111_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt9m111_g_mbus_config(struct v4l2_subdev *sd,
|
||||
struct v4l2_mbus_config *cfg)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
|
||||
cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
|
||||
V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
|
||||
V4L2_MBUS_DATA_ACTIVE_HIGH;
|
||||
cfg->type = V4L2_MBUS_PARALLEL;
|
||||
cfg->flags = soc_camera_apply_board_flags(icl, cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = {
|
||||
.s_mbus_fmt = mt9m111_s_fmt,
|
||||
.g_mbus_fmt = mt9m111_g_fmt,
|
||||
@ -1027,6 +895,7 @@ static struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = {
|
||||
.g_crop = mt9m111_g_crop,
|
||||
.cropcap = mt9m111_cropcap,
|
||||
.enum_mbus_fmt = mt9m111_enum_fmt,
|
||||
.g_mbus_config = mt9m111_g_mbus_config,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_ops mt9m111_subdev_ops = {
|
||||
@ -1038,17 +907,10 @@ static int mt9m111_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *did)
|
||||
{
|
||||
struct mt9m111 *mt9m111;
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct soc_camera_link *icl;
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
int ret;
|
||||
|
||||
if (!icd) {
|
||||
dev_err(&client->dev, "mt9m111: soc-camera data missing!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
icl = to_soc_camera_link(icd);
|
||||
if (!icl) {
|
||||
dev_err(&client->dev, "mt9m111: driver needs platform data\n");
|
||||
return -EINVAL;
|
||||
@ -1065,19 +927,37 @@ static int mt9m111_probe(struct i2c_client *client,
|
||||
return -ENOMEM;
|
||||
|
||||
v4l2_i2c_subdev_init(&mt9m111->subdev, client, &mt9m111_subdev_ops);
|
||||
v4l2_ctrl_handler_init(&mt9m111->hdl, 5);
|
||||
v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
|
||||
V4L2_CID_VFLIP, 0, 1, 1, 0);
|
||||
v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
|
||||
V4L2_CID_HFLIP, 0, 1, 1, 0);
|
||||
v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
|
||||
V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
|
||||
mt9m111->gain = v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
|
||||
V4L2_CID_GAIN, 0, 63 * 2 * 2, 1, 32);
|
||||
v4l2_ctrl_new_std_menu(&mt9m111->hdl,
|
||||
&mt9m111_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
|
||||
V4L2_EXPOSURE_AUTO);
|
||||
mt9m111->subdev.ctrl_handler = &mt9m111->hdl;
|
||||
if (mt9m111->hdl.error) {
|
||||
int err = mt9m111->hdl.error;
|
||||
|
||||
kfree(mt9m111);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Second stage probe - when a capture adapter is there */
|
||||
icd->ops = &mt9m111_ops;
|
||||
|
||||
mt9m111->rect.left = MT9M111_MIN_DARK_COLS;
|
||||
mt9m111->rect.top = MT9M111_MIN_DARK_ROWS;
|
||||
mt9m111->rect.width = MT9M111_MAX_WIDTH;
|
||||
mt9m111->rect.height = MT9M111_MAX_HEIGHT;
|
||||
mt9m111->fmt = &mt9m111_colour_fmts[0];
|
||||
mt9m111->lastpage = -1;
|
||||
|
||||
ret = mt9m111_video_probe(icd, client);
|
||||
ret = mt9m111_video_probe(client);
|
||||
if (ret) {
|
||||
icd->ops = NULL;
|
||||
v4l2_ctrl_handler_free(&mt9m111->hdl);
|
||||
kfree(mt9m111);
|
||||
}
|
||||
|
||||
@ -1087,9 +967,9 @@ static int mt9m111_probe(struct i2c_client *client,
|
||||
static int mt9m111_remove(struct i2c_client *client)
|
||||
{
|
||||
struct mt9m111 *mt9m111 = to_mt9m111(client);
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
|
||||
icd->ops = NULL;
|
||||
v4l2_device_unregister_subdev(&mt9m111->subdev);
|
||||
v4l2_ctrl_handler_free(&mt9m111->hdl);
|
||||
kfree(mt9m111);
|
||||
|
||||
return 0;
|
||||
|
@ -13,11 +13,20 @@
|
||||
#include <linux/log2.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/v4l2-mediabus.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include <media/soc_camera.h>
|
||||
#include <media/v4l2-chip-ident.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
|
||||
/*
|
||||
* ATTENTION: this driver still cannot be used outside of the soc-camera
|
||||
* framework because of its PM implementation, using the video_device node.
|
||||
* If hardware becomes available for testing, alternative PM approaches shall
|
||||
* be considered and tested.
|
||||
*/
|
||||
|
||||
/*
|
||||
* mt9t031 i2c address 0x5d
|
||||
@ -57,21 +66,20 @@
|
||||
#define MT9T031_COLUMN_SKIP 32
|
||||
#define MT9T031_ROW_SKIP 20
|
||||
|
||||
#define MT9T031_BUS_PARAM (SOCAM_PCLK_SAMPLE_RISING | \
|
||||
SOCAM_PCLK_SAMPLE_FALLING | SOCAM_HSYNC_ACTIVE_HIGH | \
|
||||
SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH | \
|
||||
SOCAM_MASTER | SOCAM_DATAWIDTH_10)
|
||||
|
||||
struct mt9t031 {
|
||||
struct v4l2_subdev subdev;
|
||||
struct v4l2_ctrl_handler hdl;
|
||||
struct {
|
||||
/* exposure/auto-exposure cluster */
|
||||
struct v4l2_ctrl *autoexposure;
|
||||
struct v4l2_ctrl *exposure;
|
||||
};
|
||||
struct v4l2_rect rect; /* Sensor window */
|
||||
int model; /* V4L2_IDENT_MT9T031* codes from v4l2-chip-ident.h */
|
||||
u16 xskip;
|
||||
u16 yskip;
|
||||
unsigned int gain;
|
||||
unsigned int total_h;
|
||||
unsigned short y_skip_top; /* Lines to skip at the top */
|
||||
unsigned int exposure;
|
||||
unsigned char autoexposure;
|
||||
};
|
||||
|
||||
static struct mt9t031 *to_mt9t031(const struct i2c_client *client)
|
||||
@ -179,95 +187,6 @@ static int mt9t031_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt9t031_set_bus_param(struct soc_camera_device *icd,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
|
||||
|
||||
/* The caller should have queried our parameters, check anyway */
|
||||
if (flags & ~MT9T031_BUS_PARAM)
|
||||
return -EINVAL;
|
||||
|
||||
if (flags & SOCAM_PCLK_SAMPLE_FALLING)
|
||||
reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
|
||||
else
|
||||
reg_set(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long mt9t031_query_bus_param(struct soc_camera_device *icd)
|
||||
{
|
||||
struct soc_camera_link *icl = to_soc_camera_link(icd);
|
||||
|
||||
return soc_camera_apply_sensor_flags(icl, MT9T031_BUS_PARAM);
|
||||
}
|
||||
|
||||
enum {
|
||||
MT9T031_CTRL_VFLIP,
|
||||
MT9T031_CTRL_HFLIP,
|
||||
MT9T031_CTRL_GAIN,
|
||||
MT9T031_CTRL_EXPOSURE,
|
||||
MT9T031_CTRL_EXPOSURE_AUTO,
|
||||
};
|
||||
|
||||
static const struct v4l2_queryctrl mt9t031_controls[] = {
|
||||
[MT9T031_CTRL_VFLIP] = {
|
||||
.id = V4L2_CID_VFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Flip Vertically",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
[MT9T031_CTRL_HFLIP] = {
|
||||
.id = V4L2_CID_HFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Flip Horizontally",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
[MT9T031_CTRL_GAIN] = {
|
||||
.id = V4L2_CID_GAIN,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Gain",
|
||||
.minimum = 0,
|
||||
.maximum = 127,
|
||||
.step = 1,
|
||||
.default_value = 64,
|
||||
.flags = V4L2_CTRL_FLAG_SLIDER,
|
||||
},
|
||||
[MT9T031_CTRL_EXPOSURE] = {
|
||||
.id = V4L2_CID_EXPOSURE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Exposure",
|
||||
.minimum = 1,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
.default_value = 255,
|
||||
.flags = V4L2_CTRL_FLAG_SLIDER,
|
||||
},
|
||||
[MT9T031_CTRL_EXPOSURE_AUTO] = {
|
||||
.id = V4L2_CID_EXPOSURE_AUTO,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Automatic Exposure",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 1,
|
||||
}
|
||||
};
|
||||
|
||||
static struct soc_camera_ops mt9t031_ops = {
|
||||
.set_bus_param = mt9t031_set_bus_param,
|
||||
.query_bus_param = mt9t031_query_bus_param,
|
||||
.controls = mt9t031_controls,
|
||||
.num_controls = ARRAY_SIZE(mt9t031_controls),
|
||||
};
|
||||
|
||||
/* target must be _even_ */
|
||||
static u16 mt9t031_skip(s32 *source, s32 target, s32 max)
|
||||
{
|
||||
@ -353,7 +272,7 @@ static int mt9t031_set_params(struct i2c_client *client,
|
||||
|
||||
/*
|
||||
* The caller provides a supported format, as guaranteed by
|
||||
* icd->try_fmt_cap(), soc_camera_s_crop() and soc_camera_cropcap()
|
||||
* .try_mbus_fmt(), soc_camera_s_crop() and soc_camera_cropcap()
|
||||
*/
|
||||
if (ret >= 0)
|
||||
ret = reg_write(client, MT9T031_COLUMN_START, rect->left);
|
||||
@ -364,17 +283,10 @@ static int mt9t031_set_params(struct i2c_client *client,
|
||||
if (ret >= 0)
|
||||
ret = reg_write(client, MT9T031_WINDOW_HEIGHT,
|
||||
rect->height + mt9t031->y_skip_top - 1);
|
||||
if (ret >= 0 && mt9t031->autoexposure) {
|
||||
unsigned int total_h = rect->height + mt9t031->y_skip_top + vblank;
|
||||
ret = set_shutter(client, total_h);
|
||||
if (ret >= 0) {
|
||||
const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank;
|
||||
const struct v4l2_queryctrl *qctrl =
|
||||
&mt9t031_controls[MT9T031_CTRL_EXPOSURE];
|
||||
mt9t031->exposure = (shutter_max / 2 + (total_h - 1) *
|
||||
(qctrl->maximum - qctrl->minimum)) /
|
||||
shutter_max + qctrl->minimum;
|
||||
}
|
||||
if (ret >= 0 && v4l2_ctrl_g_ctrl(mt9t031->autoexposure) == V4L2_EXPOSURE_AUTO) {
|
||||
mt9t031->total_h = rect->height + mt9t031->y_skip_top + vblank;
|
||||
|
||||
ret = set_shutter(client, mt9t031->total_h);
|
||||
}
|
||||
|
||||
/* Re-enable register update, commit all changes */
|
||||
@ -543,71 +455,57 @@ static int mt9t031_s_register(struct v4l2_subdev *sd,
|
||||
}
|
||||
#endif
|
||||
|
||||
static int mt9t031_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
static int mt9t031_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct mt9t031 *mt9t031 = to_mt9t031(client);
|
||||
int data;
|
||||
struct mt9t031 *mt9t031 = container_of(ctrl->handler,
|
||||
struct mt9t031, hdl);
|
||||
const u32 shutter_max = MT9T031_MAX_HEIGHT + MT9T031_VERTICAL_BLANK;
|
||||
s32 min, max;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_VFLIP:
|
||||
data = reg_read(client, MT9T031_READ_MODE_2);
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
ctrl->value = !!(data & 0x8000);
|
||||
break;
|
||||
case V4L2_CID_HFLIP:
|
||||
data = reg_read(client, MT9T031_READ_MODE_2);
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
ctrl->value = !!(data & 0x4000);
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE_AUTO:
|
||||
ctrl->value = mt9t031->autoexposure;
|
||||
break;
|
||||
case V4L2_CID_GAIN:
|
||||
ctrl->value = mt9t031->gain;
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE:
|
||||
ctrl->value = mt9t031->exposure;
|
||||
min = mt9t031->exposure->minimum;
|
||||
max = mt9t031->exposure->maximum;
|
||||
mt9t031->exposure->val =
|
||||
(shutter_max / 2 + (mt9t031->total_h - 1) * (max - min))
|
||||
/ shutter_max + min;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
static int mt9t031_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct mt9t031 *mt9t031 = container_of(ctrl->handler,
|
||||
struct mt9t031, hdl);
|
||||
struct v4l2_subdev *sd = &mt9t031->subdev;
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct mt9t031 *mt9t031 = to_mt9t031(client);
|
||||
const struct v4l2_queryctrl *qctrl;
|
||||
struct v4l2_ctrl *exp = mt9t031->exposure;
|
||||
int data;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_VFLIP:
|
||||
if (ctrl->value)
|
||||
if (ctrl->val)
|
||||
data = reg_set(client, MT9T031_READ_MODE_2, 0x8000);
|
||||
else
|
||||
data = reg_clear(client, MT9T031_READ_MODE_2, 0x8000);
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
break;
|
||||
return 0;
|
||||
case V4L2_CID_HFLIP:
|
||||
if (ctrl->value)
|
||||
if (ctrl->val)
|
||||
data = reg_set(client, MT9T031_READ_MODE_2, 0x4000);
|
||||
else
|
||||
data = reg_clear(client, MT9T031_READ_MODE_2, 0x4000);
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
break;
|
||||
return 0;
|
||||
case V4L2_CID_GAIN:
|
||||
qctrl = &mt9t031_controls[MT9T031_CTRL_GAIN];
|
||||
if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
|
||||
return -EINVAL;
|
||||
/* See Datasheet Table 7, Gain settings. */
|
||||
if (ctrl->value <= qctrl->default_value) {
|
||||
if (ctrl->val <= ctrl->default_value) {
|
||||
/* Pack it into 0..1 step 0.125, register values 0..8 */
|
||||
unsigned long range = qctrl->default_value - qctrl->minimum;
|
||||
data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range;
|
||||
unsigned long range = ctrl->default_value - ctrl->minimum;
|
||||
data = ((ctrl->val - ctrl->minimum) * 8 + range / 2) / range;
|
||||
|
||||
dev_dbg(&client->dev, "Setting gain %d\n", data);
|
||||
data = reg_write(client, MT9T031_GLOBAL_GAIN, data);
|
||||
@ -616,9 +514,9 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
} else {
|
||||
/* Pack it into 1.125..128 variable step, register values 9..0x7860 */
|
||||
/* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */
|
||||
unsigned long range = qctrl->maximum - qctrl->default_value - 1;
|
||||
unsigned long range = ctrl->maximum - ctrl->default_value - 1;
|
||||
/* calculated gain: map 65..127 to 9..1024 step 0.125 */
|
||||
unsigned long gain = ((ctrl->value - qctrl->default_value - 1) *
|
||||
unsigned long gain = ((ctrl->val - ctrl->default_value - 1) *
|
||||
1015 + range / 2) / range + 9;
|
||||
|
||||
if (gain <= 32) /* calculated gain 9..32 -> 9..32 */
|
||||
@ -635,19 +533,13 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
|
||||
/* Success */
|
||||
mt9t031->gain = ctrl->value;
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE:
|
||||
qctrl = &mt9t031_controls[MT9T031_CTRL_EXPOSURE];
|
||||
/* mt9t031 has maximum == default */
|
||||
if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
|
||||
return -EINVAL;
|
||||
else {
|
||||
const unsigned long range = qctrl->maximum - qctrl->minimum;
|
||||
const u32 shutter = ((ctrl->value - qctrl->minimum) * 1048 +
|
||||
range / 2) / range + 1;
|
||||
case V4L2_CID_EXPOSURE_AUTO:
|
||||
if (ctrl->val == V4L2_EXPOSURE_MANUAL) {
|
||||
unsigned int range = exp->maximum - exp->minimum;
|
||||
unsigned int shutter = ((exp->val - exp->minimum) * 1048 +
|
||||
range / 2) / range + 1;
|
||||
u32 old;
|
||||
|
||||
get_shutter(client, &old);
|
||||
@ -655,27 +547,15 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
old, shutter);
|
||||
if (set_shutter(client, shutter) < 0)
|
||||
return -EIO;
|
||||
mt9t031->exposure = ctrl->value;
|
||||
mt9t031->autoexposure = 0;
|
||||
}
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE_AUTO:
|
||||
if (ctrl->value) {
|
||||
} else {
|
||||
const u16 vblank = MT9T031_VERTICAL_BLANK;
|
||||
const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank;
|
||||
unsigned int total_h = mt9t031->rect.height +
|
||||
mt9t031->total_h = mt9t031->rect.height +
|
||||
mt9t031->y_skip_top + vblank;
|
||||
|
||||
if (set_shutter(client, total_h) < 0)
|
||||
if (set_shutter(client, mt9t031->total_h) < 0)
|
||||
return -EIO;
|
||||
qctrl = &mt9t031_controls[MT9T031_CTRL_EXPOSURE];
|
||||
mt9t031->exposure = (shutter_max / 2 + (total_h - 1) *
|
||||
(qctrl->maximum - qctrl->minimum)) /
|
||||
shutter_max + qctrl->minimum;
|
||||
mt9t031->autoexposure = 1;
|
||||
} else
|
||||
mt9t031->autoexposure = 0;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -700,8 +580,7 @@ static int mt9t031_runtime_suspend(struct device *dev)
|
||||
static int mt9t031_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct video_device *vdev = to_video_device(dev);
|
||||
struct soc_camera_device *icd = dev_get_drvdata(vdev->parent);
|
||||
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
|
||||
struct v4l2_subdev *sd = soc_camera_vdev_to_subdev(vdev);
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct mt9t031 *mt9t031 = to_mt9t031(client);
|
||||
|
||||
@ -734,6 +613,19 @@ static struct device_type mt9t031_dev_type = {
|
||||
.pm = &mt9t031_dev_pm_ops,
|
||||
};
|
||||
|
||||
static int mt9t031_s_power(struct v4l2_subdev *sd, int on)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct video_device *vdev = soc_camera_i2c_to_vdev(client);
|
||||
|
||||
if (on)
|
||||
vdev->dev.type = &mt9t031_dev_type;
|
||||
else
|
||||
vdev->dev.type = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Interface active, can use i2c. If it fails, it can indeed mean, that
|
||||
* this wasn't our capture interface, so, we wait for the right one
|
||||
@ -741,7 +633,6 @@ static struct device_type mt9t031_dev_type = {
|
||||
static int mt9t031_video_probe(struct i2c_client *client)
|
||||
{
|
||||
struct mt9t031 *mt9t031 = to_mt9t031(client);
|
||||
struct video_device *vdev = soc_camera_i2c_to_vdev(client);
|
||||
s32 data;
|
||||
int ret;
|
||||
|
||||
@ -768,11 +659,7 @@ static int mt9t031_video_probe(struct i2c_client *client)
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "Failed to initialise the camera\n");
|
||||
else
|
||||
vdev->dev.type = &mt9t031_dev_type;
|
||||
|
||||
/* mt9t031_idle() has reset the chip to default. */
|
||||
mt9t031->exposure = 255;
|
||||
mt9t031->gain = 64;
|
||||
v4l2_ctrl_handler_setup(&mt9t031->hdl);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -787,10 +674,14 @@ static int mt9t031_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct v4l2_ctrl_ops mt9t031_ctrl_ops = {
|
||||
.g_volatile_ctrl = mt9t031_g_volatile_ctrl,
|
||||
.s_ctrl = mt9t031_s_ctrl,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = {
|
||||
.g_ctrl = mt9t031_g_ctrl,
|
||||
.s_ctrl = mt9t031_s_ctrl,
|
||||
.g_chip_ident = mt9t031_g_chip_ident,
|
||||
.s_power = mt9t031_s_power,
|
||||
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
||||
.g_register = mt9t031_g_register,
|
||||
.s_register = mt9t031_s_register,
|
||||
@ -807,6 +698,34 @@ static int mt9t031_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt9t031_g_mbus_config(struct v4l2_subdev *sd,
|
||||
struct v4l2_mbus_config *cfg)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
|
||||
cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
|
||||
V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
|
||||
V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH;
|
||||
cfg->type = V4L2_MBUS_PARALLEL;
|
||||
cfg->flags = soc_camera_apply_board_flags(icl, cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt9t031_s_mbus_config(struct v4l2_subdev *sd,
|
||||
const struct v4l2_mbus_config *cfg)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
|
||||
if (soc_camera_apply_board_flags(icl, cfg) &
|
||||
V4L2_MBUS_PCLK_SAMPLE_FALLING)
|
||||
return reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
|
||||
else
|
||||
return reg_set(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
|
||||
}
|
||||
|
||||
static struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = {
|
||||
.s_stream = mt9t031_s_stream,
|
||||
.s_mbus_fmt = mt9t031_s_fmt,
|
||||
@ -816,6 +735,8 @@ static struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = {
|
||||
.g_crop = mt9t031_g_crop,
|
||||
.cropcap = mt9t031_cropcap,
|
||||
.enum_mbus_fmt = mt9t031_enum_fmt,
|
||||
.g_mbus_config = mt9t031_g_mbus_config,
|
||||
.s_mbus_config = mt9t031_s_mbus_config,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_sensor_ops mt9t031_subdev_sensor_ops = {
|
||||
@ -832,18 +753,13 @@ static int mt9t031_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *did)
|
||||
{
|
||||
struct mt9t031 *mt9t031;
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
int ret;
|
||||
|
||||
if (icd) {
|
||||
struct soc_camera_link *icl = to_soc_camera_link(icd);
|
||||
if (!icl) {
|
||||
dev_err(&client->dev, "MT9T031 driver needs platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
icd->ops = &mt9t031_ops;
|
||||
if (!icl) {
|
||||
dev_err(&client->dev, "MT9T031 driver needs platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
|
||||
@ -857,6 +773,33 @@ static int mt9t031_probe(struct i2c_client *client,
|
||||
return -ENOMEM;
|
||||
|
||||
v4l2_i2c_subdev_init(&mt9t031->subdev, client, &mt9t031_subdev_ops);
|
||||
v4l2_ctrl_handler_init(&mt9t031->hdl, 5);
|
||||
v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
|
||||
V4L2_CID_VFLIP, 0, 1, 1, 0);
|
||||
v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
|
||||
V4L2_CID_HFLIP, 0, 1, 1, 0);
|
||||
v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
|
||||
V4L2_CID_GAIN, 0, 127, 1, 64);
|
||||
|
||||
/*
|
||||
* Simulated autoexposure. If enabled, we calculate shutter width
|
||||
* ourselves in the driver based on vertical blanking and frame width
|
||||
*/
|
||||
mt9t031->autoexposure = v4l2_ctrl_new_std_menu(&mt9t031->hdl,
|
||||
&mt9t031_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
|
||||
V4L2_EXPOSURE_AUTO);
|
||||
mt9t031->exposure = v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
|
||||
V4L2_CID_EXPOSURE, 1, 255, 1, 255);
|
||||
|
||||
mt9t031->subdev.ctrl_handler = &mt9t031->hdl;
|
||||
if (mt9t031->hdl.error) {
|
||||
int err = mt9t031->hdl.error;
|
||||
|
||||
kfree(mt9t031);
|
||||
return err;
|
||||
}
|
||||
v4l2_ctrl_auto_cluster(2, &mt9t031->autoexposure,
|
||||
V4L2_EXPOSURE_MANUAL, true);
|
||||
|
||||
mt9t031->y_skip_top = 0;
|
||||
mt9t031->rect.left = MT9T031_COLUMN_SKIP;
|
||||
@ -864,12 +807,6 @@ static int mt9t031_probe(struct i2c_client *client,
|
||||
mt9t031->rect.width = MT9T031_MAX_WIDTH;
|
||||
mt9t031->rect.height = MT9T031_MAX_HEIGHT;
|
||||
|
||||
/*
|
||||
* Simulated autoexposure. If enabled, we calculate shutter width
|
||||
* ourselves in the driver based on vertical blanking and frame width
|
||||
*/
|
||||
mt9t031->autoexposure = 1;
|
||||
|
||||
mt9t031->xskip = 1;
|
||||
mt9t031->yskip = 1;
|
||||
|
||||
@ -880,8 +817,7 @@ static int mt9t031_probe(struct i2c_client *client,
|
||||
mt9t031_disable(client);
|
||||
|
||||
if (ret) {
|
||||
if (icd)
|
||||
icd->ops = NULL;
|
||||
v4l2_ctrl_handler_free(&mt9t031->hdl);
|
||||
kfree(mt9t031);
|
||||
}
|
||||
|
||||
@ -891,10 +827,9 @@ static int mt9t031_probe(struct i2c_client *client,
|
||||
static int mt9t031_remove(struct i2c_client *client)
|
||||
{
|
||||
struct mt9t031 *mt9t031 = to_mt9t031(client);
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
|
||||
if (icd)
|
||||
icd->ops = NULL;
|
||||
v4l2_device_unregister_subdev(&mt9t031->subdev);
|
||||
v4l2_ctrl_handler_free(&mt9t031->hdl);
|
||||
kfree(mt9t031);
|
||||
|
||||
return 0;
|
||||
|
@ -22,11 +22,11 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/v4l2-mediabus.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include <media/mt9t112.h>
|
||||
#include <media/soc_camera.h>
|
||||
#include <media/soc_mediabus.h>
|
||||
#include <media/v4l2-chip-ident.h>
|
||||
#include <media/v4l2-common.h>
|
||||
|
||||
@ -34,11 +34,7 @@
|
||||
/* #define EXT_CLOCK 24000000 */
|
||||
|
||||
/************************************************************************
|
||||
|
||||
|
||||
macro
|
||||
|
||||
|
||||
************************************************************************/
|
||||
/*
|
||||
* frame size
|
||||
@ -80,17 +76,8 @@
|
||||
#define VAR8(id, offset) _VAR(id, offset, 0x8000)
|
||||
|
||||
/************************************************************************
|
||||
|
||||
|
||||
struct
|
||||
|
||||
|
||||
************************************************************************/
|
||||
struct mt9t112_frame_size {
|
||||
u16 width;
|
||||
u16 height;
|
||||
};
|
||||
|
||||
struct mt9t112_format {
|
||||
enum v4l2_mbus_pixelcode code;
|
||||
enum v4l2_colorspace colorspace;
|
||||
@ -102,21 +89,17 @@ struct mt9t112_priv {
|
||||
struct v4l2_subdev subdev;
|
||||
struct mt9t112_camera_info *info;
|
||||
struct i2c_client *client;
|
||||
struct soc_camera_device icd;
|
||||
struct mt9t112_frame_size frame;
|
||||
struct v4l2_rect frame;
|
||||
const struct mt9t112_format *format;
|
||||
int model;
|
||||
u32 flags;
|
||||
/* for flags */
|
||||
#define INIT_DONE (1<<0)
|
||||
#define INIT_DONE (1 << 0)
|
||||
#define PCLK_RISING (1 << 1)
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
|
||||
|
||||
supported format
|
||||
|
||||
|
||||
************************************************************************/
|
||||
|
||||
static const struct mt9t112_format mt9t112_cfmts[] = {
|
||||
@ -154,11 +137,7 @@ static const struct mt9t112_format mt9t112_cfmts[] = {
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
|
||||
|
||||
general function
|
||||
|
||||
|
||||
************************************************************************/
|
||||
static struct mt9t112_priv *to_mt9t112(const struct i2c_client *client)
|
||||
{
|
||||
@ -326,50 +305,47 @@ static int mt9t112_clock_info(const struct i2c_client *client, u32 ext)
|
||||
n = (n >> 8) & 0x003f;
|
||||
|
||||
enable = ((6000 > ext) || (54000 < ext)) ? "X" : "";
|
||||
dev_info(&client->dev, "EXTCLK : %10u K %s\n", ext, enable);
|
||||
dev_dbg(&client->dev, "EXTCLK : %10u K %s\n", ext, enable);
|
||||
|
||||
vco = 2 * m * ext / (n+1);
|
||||
enable = ((384000 > vco) || (768000 < vco)) ? "X" : "";
|
||||
dev_info(&client->dev, "VCO : %10u K %s\n", vco, enable);
|
||||
dev_dbg(&client->dev, "VCO : %10u K %s\n", vco, enable);
|
||||
|
||||
clk = vco / (p1+1) / (p2+1);
|
||||
enable = (96000 < clk) ? "X" : "";
|
||||
dev_info(&client->dev, "PIXCLK : %10u K %s\n", clk, enable);
|
||||
dev_dbg(&client->dev, "PIXCLK : %10u K %s\n", clk, enable);
|
||||
|
||||
clk = vco / (p3+1);
|
||||
enable = (768000 < clk) ? "X" : "";
|
||||
dev_info(&client->dev, "MIPICLK : %10u K %s\n", clk, enable);
|
||||
dev_dbg(&client->dev, "MIPICLK : %10u K %s\n", clk, enable);
|
||||
|
||||
clk = vco / (p6+1);
|
||||
enable = (96000 < clk) ? "X" : "";
|
||||
dev_info(&client->dev, "MCU CLK : %10u K %s\n", clk, enable);
|
||||
dev_dbg(&client->dev, "MCU CLK : %10u K %s\n", clk, enable);
|
||||
|
||||
clk = vco / (p5+1);
|
||||
enable = (54000 < clk) ? "X" : "";
|
||||
dev_info(&client->dev, "SOC CLK : %10u K %s\n", clk, enable);
|
||||
dev_dbg(&client->dev, "SOC CLK : %10u K %s\n", clk, enable);
|
||||
|
||||
clk = vco / (p4+1);
|
||||
enable = (70000 < clk) ? "X" : "";
|
||||
dev_info(&client->dev, "Sensor CLK : %10u K %s\n", clk, enable);
|
||||
dev_dbg(&client->dev, "Sensor CLK : %10u K %s\n", clk, enable);
|
||||
|
||||
clk = vco / (p7+1);
|
||||
dev_info(&client->dev, "External sensor : %10u K\n", clk);
|
||||
dev_dbg(&client->dev, "External sensor : %10u K\n", clk);
|
||||
|
||||
clk = ext / (n+1);
|
||||
enable = ((2000 > clk) || (24000 < clk)) ? "X" : "";
|
||||
dev_info(&client->dev, "PFD : %10u K %s\n", clk, enable);
|
||||
dev_dbg(&client->dev, "PFD : %10u K %s\n", clk, enable);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void mt9t112_frame_check(u32 *width, u32 *height)
|
||||
static void mt9t112_frame_check(u32 *width, u32 *height, u32 *left, u32 *top)
|
||||
{
|
||||
if (*width > MAX_WIDTH)
|
||||
*width = MAX_WIDTH;
|
||||
|
||||
if (*height > MAX_HEIGHT)
|
||||
*height = MAX_HEIGHT;
|
||||
soc_camera_limit_side(left, width, 0, 0, MAX_WIDTH);
|
||||
soc_camera_limit_side(top, height, 0, 0, MAX_HEIGHT);
|
||||
}
|
||||
|
||||
static int mt9t112_set_a_frame_size(const struct i2c_client *client,
|
||||
@ -758,48 +734,7 @@ static int mt9t112_init_camera(const struct i2c_client *client)
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
|
||||
|
||||
soc_camera_ops
|
||||
|
||||
|
||||
************************************************************************/
|
||||
static int mt9t112_set_bus_param(struct soc_camera_device *icd,
|
||||
unsigned long flags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long mt9t112_query_bus_param(struct soc_camera_device *icd)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
|
||||
struct mt9t112_priv *priv = to_mt9t112(client);
|
||||
struct soc_camera_link *icl = to_soc_camera_link(icd);
|
||||
unsigned long flags = SOCAM_MASTER | SOCAM_VSYNC_ACTIVE_HIGH |
|
||||
SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH;
|
||||
|
||||
flags |= (priv->info->flags & MT9T112_FLAG_PCLK_RISING_EDGE) ?
|
||||
SOCAM_PCLK_SAMPLE_RISING : SOCAM_PCLK_SAMPLE_FALLING;
|
||||
|
||||
if (priv->info->flags & MT9T112_FLAG_DATAWIDTH_8)
|
||||
flags |= SOCAM_DATAWIDTH_8;
|
||||
else
|
||||
flags |= SOCAM_DATAWIDTH_10;
|
||||
|
||||
return soc_camera_apply_sensor_flags(icl, flags);
|
||||
}
|
||||
|
||||
static struct soc_camera_ops mt9t112_ops = {
|
||||
.set_bus_param = mt9t112_set_bus_param,
|
||||
.query_bus_param = mt9t112_query_bus_param,
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
|
||||
|
||||
v4l2_subdev_core_ops
|
||||
|
||||
|
||||
************************************************************************/
|
||||
static int mt9t112_g_chip_ident(struct v4l2_subdev *sd,
|
||||
struct v4l2_dbg_chip_ident *id)
|
||||
@ -850,11 +785,7 @@ static struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = {
|
||||
|
||||
|
||||
/************************************************************************
|
||||
|
||||
|
||||
v4l2_subdev_video_ops
|
||||
|
||||
|
||||
************************************************************************/
|
||||
static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
{
|
||||
@ -877,8 +808,7 @@ static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
}
|
||||
|
||||
if (!(priv->flags & INIT_DONE)) {
|
||||
u16 param = (MT9T112_FLAG_PCLK_RISING_EDGE &
|
||||
priv->info->flags) ? 0x0001 : 0x0000;
|
||||
u16 param = PCLK_RISING & priv->flags ? 0x0001 : 0x0000;
|
||||
|
||||
ECHECKER(ret, mt9t112_init_camera(client));
|
||||
|
||||
@ -910,19 +840,12 @@ static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mt9t112_set_params(struct i2c_client *client, u32 width, u32 height,
|
||||
static int mt9t112_set_params(struct mt9t112_priv *priv,
|
||||
const struct v4l2_rect *rect,
|
||||
enum v4l2_mbus_pixelcode code)
|
||||
{
|
||||
struct mt9t112_priv *priv = to_mt9t112(client);
|
||||
int i;
|
||||
|
||||
priv->format = NULL;
|
||||
|
||||
/*
|
||||
* frame size check
|
||||
*/
|
||||
mt9t112_frame_check(&width, &height);
|
||||
|
||||
/*
|
||||
* get color format
|
||||
*/
|
||||
@ -933,8 +856,13 @@ static int mt9t112_set_params(struct i2c_client *client, u32 width, u32 height,
|
||||
if (i == ARRAY_SIZE(mt9t112_cfmts))
|
||||
return -EINVAL;
|
||||
|
||||
priv->frame.width = (u16)width;
|
||||
priv->frame.height = (u16)height;
|
||||
priv->frame = *rect;
|
||||
|
||||
/*
|
||||
* frame size check
|
||||
*/
|
||||
mt9t112_frame_check(&priv->frame.width, &priv->frame.height,
|
||||
&priv->frame.left, &priv->frame.top);
|
||||
|
||||
priv->format = mt9t112_cfmts + i;
|
||||
|
||||
@ -945,9 +873,12 @@ static int mt9t112_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
|
||||
{
|
||||
a->bounds.left = 0;
|
||||
a->bounds.top = 0;
|
||||
a->bounds.width = VGA_WIDTH;
|
||||
a->bounds.height = VGA_HEIGHT;
|
||||
a->defrect = a->bounds;
|
||||
a->bounds.width = MAX_WIDTH;
|
||||
a->bounds.height = MAX_HEIGHT;
|
||||
a->defrect.left = 0;
|
||||
a->defrect.top = 0;
|
||||
a->defrect.width = VGA_WIDTH;
|
||||
a->defrect.height = VGA_HEIGHT;
|
||||
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
a->pixelaspect.numerator = 1;
|
||||
a->pixelaspect.denominator = 1;
|
||||
@ -957,11 +888,11 @@ static int mt9t112_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
|
||||
|
||||
static int mt9t112_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
|
||||
{
|
||||
a->c.left = 0;
|
||||
a->c.top = 0;
|
||||
a->c.width = VGA_WIDTH;
|
||||
a->c.height = VGA_HEIGHT;
|
||||
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct mt9t112_priv *priv = to_mt9t112(client);
|
||||
|
||||
a->c = priv->frame;
|
||||
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -969,10 +900,10 @@ static int mt9t112_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
|
||||
static int mt9t112_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct mt9t112_priv *priv = to_mt9t112(client);
|
||||
struct v4l2_rect *rect = &a->c;
|
||||
|
||||
return mt9t112_set_params(client, rect->width, rect->height,
|
||||
V4L2_MBUS_FMT_UYVY8_2X8);
|
||||
return mt9t112_set_params(priv, rect, priv->format->code);
|
||||
}
|
||||
|
||||
static int mt9t112_g_fmt(struct v4l2_subdev *sd,
|
||||
@ -981,16 +912,9 @@ static int mt9t112_g_fmt(struct v4l2_subdev *sd,
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct mt9t112_priv *priv = to_mt9t112(client);
|
||||
|
||||
if (!priv->format) {
|
||||
int ret = mt9t112_set_params(client, VGA_WIDTH, VGA_HEIGHT,
|
||||
V4L2_MBUS_FMT_UYVY8_2X8);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
mf->width = priv->frame.width;
|
||||
mf->height = priv->frame.height;
|
||||
/* TODO: set colorspace */
|
||||
mf->colorspace = priv->format->colorspace;
|
||||
mf->code = priv->format->code;
|
||||
mf->field = V4L2_FIELD_NONE;
|
||||
|
||||
@ -1001,17 +925,42 @@ static int mt9t112_s_fmt(struct v4l2_subdev *sd,
|
||||
struct v4l2_mbus_framefmt *mf)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct mt9t112_priv *priv = to_mt9t112(client);
|
||||
struct v4l2_rect rect = {
|
||||
.width = mf->width,
|
||||
.height = mf->height,
|
||||
.left = priv->frame.left,
|
||||
.top = priv->frame.top,
|
||||
};
|
||||
int ret;
|
||||
|
||||
/* TODO: set colorspace */
|
||||
return mt9t112_set_params(client, mf->width, mf->height, mf->code);
|
||||
ret = mt9t112_set_params(priv, &rect, mf->code);
|
||||
|
||||
if (!ret)
|
||||
mf->colorspace = priv->format->colorspace;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mt9t112_try_fmt(struct v4l2_subdev *sd,
|
||||
struct v4l2_mbus_framefmt *mf)
|
||||
{
|
||||
mt9t112_frame_check(&mf->width, &mf->height);
|
||||
unsigned int top, left;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mt9t112_cfmts); i++)
|
||||
if (mt9t112_cfmts[i].code == mf->code)
|
||||
break;
|
||||
|
||||
if (i == ARRAY_SIZE(mt9t112_cfmts)) {
|
||||
mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
|
||||
mf->colorspace = V4L2_COLORSPACE_JPEG;
|
||||
} else {
|
||||
mf->colorspace = mt9t112_cfmts[i].colorspace;
|
||||
}
|
||||
|
||||
mt9t112_frame_check(&mf->width, &mf->height, &left, &top);
|
||||
|
||||
/* TODO: set colorspace */
|
||||
mf->field = V4L2_FIELD_NONE;
|
||||
|
||||
return 0;
|
||||
@ -1024,6 +973,35 @@ static int mt9t112_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
|
||||
return -EINVAL;
|
||||
|
||||
*code = mt9t112_cfmts[index].code;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt9t112_g_mbus_config(struct v4l2_subdev *sd,
|
||||
struct v4l2_mbus_config *cfg)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
|
||||
cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
|
||||
V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH |
|
||||
V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING;
|
||||
cfg->type = V4L2_MBUS_PARALLEL;
|
||||
cfg->flags = soc_camera_apply_board_flags(icl, cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt9t112_s_mbus_config(struct v4l2_subdev *sd,
|
||||
const struct v4l2_mbus_config *cfg)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
struct mt9t112_priv *priv = to_mt9t112(client);
|
||||
|
||||
if (soc_camera_apply_board_flags(icl, cfg) & V4L2_MBUS_PCLK_SAMPLE_RISING)
|
||||
priv->flags |= PCLK_RISING;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1036,31 +1014,24 @@ static struct v4l2_subdev_video_ops mt9t112_subdev_video_ops = {
|
||||
.g_crop = mt9t112_g_crop,
|
||||
.s_crop = mt9t112_s_crop,
|
||||
.enum_mbus_fmt = mt9t112_enum_fmt,
|
||||
.g_mbus_config = mt9t112_g_mbus_config,
|
||||
.s_mbus_config = mt9t112_s_mbus_config,
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
|
||||
|
||||
i2c driver
|
||||
|
||||
|
||||
************************************************************************/
|
||||
static struct v4l2_subdev_ops mt9t112_subdev_ops = {
|
||||
.core = &mt9t112_subdev_core_ops,
|
||||
.video = &mt9t112_subdev_video_ops,
|
||||
};
|
||||
|
||||
static int mt9t112_camera_probe(struct soc_camera_device *icd,
|
||||
struct i2c_client *client)
|
||||
static int mt9t112_camera_probe(struct i2c_client *client)
|
||||
{
|
||||
struct mt9t112_priv *priv = to_mt9t112(client);
|
||||
const char *devname;
|
||||
int chipid;
|
||||
|
||||
/* We must have a parent by now. And it cannot be a wrong one. */
|
||||
BUG_ON(!icd->parent ||
|
||||
to_soc_camera_host(icd->parent)->nr != icd->iface);
|
||||
|
||||
/*
|
||||
* check and show chip ID
|
||||
*/
|
||||
@ -1088,20 +1059,21 @@ static int mt9t112_camera_probe(struct soc_camera_device *icd,
|
||||
static int mt9t112_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *did)
|
||||
{
|
||||
struct mt9t112_priv *priv;
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
struct soc_camera_link *icl;
|
||||
int ret;
|
||||
struct mt9t112_priv *priv;
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
struct v4l2_rect rect = {
|
||||
.width = VGA_WIDTH,
|
||||
.height = VGA_HEIGHT,
|
||||
.left = (MAX_WIDTH - VGA_WIDTH) / 2,
|
||||
.top = (MAX_HEIGHT - VGA_HEIGHT) / 2,
|
||||
};
|
||||
int ret;
|
||||
|
||||
if (!icd) {
|
||||
dev_err(&client->dev, "mt9t112: missing soc-camera data!\n");
|
||||
if (!icl || !icl->priv) {
|
||||
dev_err(&client->dev, "mt9t112: missing platform data!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
icl = to_soc_camera_link(icd);
|
||||
if (!icl || !icl->priv)
|
||||
return -EINVAL;
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
@ -1110,13 +1082,12 @@ static int mt9t112_probe(struct i2c_client *client,
|
||||
|
||||
v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops);
|
||||
|
||||
icd->ops = &mt9t112_ops;
|
||||
|
||||
ret = mt9t112_camera_probe(icd, client);
|
||||
if (ret) {
|
||||
icd->ops = NULL;
|
||||
ret = mt9t112_camera_probe(client);
|
||||
if (ret)
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
/* Cannot fail: using the default supported pixel code */
|
||||
mt9t112_set_params(priv, &rect, V4L2_MBUS_FMT_UYVY8_2X8);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -1124,9 +1095,7 @@ static int mt9t112_probe(struct i2c_client *client,
|
||||
static int mt9t112_remove(struct i2c_client *client)
|
||||
{
|
||||
struct mt9t112_priv *priv = to_mt9t112(client);
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
|
||||
icd->ops = NULL;
|
||||
kfree(priv);
|
||||
return 0;
|
||||
}
|
||||
@ -1147,11 +1116,7 @@ static struct i2c_driver mt9t112_i2c_driver = {
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
|
||||
|
||||
module function
|
||||
|
||||
|
||||
************************************************************************/
|
||||
static int __init mt9t112_module_init(void)
|
||||
{
|
||||
|
@ -14,9 +14,11 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/log2.h>
|
||||
|
||||
#include <media/soc_camera.h>
|
||||
#include <media/soc_mediabus.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
#include <media/v4l2-chip-ident.h>
|
||||
#include <media/soc_camera.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
|
||||
/*
|
||||
* mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c
|
||||
@ -100,6 +102,17 @@ static const struct mt9v022_datafmt mt9v022_monochrome_fmts[] = {
|
||||
|
||||
struct mt9v022 {
|
||||
struct v4l2_subdev subdev;
|
||||
struct v4l2_ctrl_handler hdl;
|
||||
struct {
|
||||
/* exposure/auto-exposure cluster */
|
||||
struct v4l2_ctrl *autoexposure;
|
||||
struct v4l2_ctrl *exposure;
|
||||
};
|
||||
struct {
|
||||
/* gain/auto-gain cluster */
|
||||
struct v4l2_ctrl *autogain;
|
||||
struct v4l2_ctrl *gain;
|
||||
};
|
||||
struct v4l2_rect rect; /* Sensor window */
|
||||
const struct mt9v022_datafmt *fmt;
|
||||
const struct mt9v022_datafmt *fmts;
|
||||
@ -178,6 +191,8 @@ static int mt9v022_init(struct i2c_client *client)
|
||||
ret = reg_clear(client, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1);
|
||||
if (!ret)
|
||||
ret = reg_write(client, MT9V022_DIGITAL_TEST_PATTERN, 0);
|
||||
if (!ret)
|
||||
return v4l2_ctrl_handler_setup(&mt9v022->hdl);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -199,78 +214,6 @@ static int mt9v022_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt9v022_set_bus_param(struct soc_camera_device *icd,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
|
||||
struct mt9v022 *mt9v022 = to_mt9v022(client);
|
||||
struct soc_camera_link *icl = to_soc_camera_link(icd);
|
||||
unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK;
|
||||
int ret;
|
||||
u16 pixclk = 0;
|
||||
|
||||
/* Only one width bit may be set */
|
||||
if (!is_power_of_2(width_flag))
|
||||
return -EINVAL;
|
||||
|
||||
if (icl->set_bus_param) {
|
||||
ret = icl->set_bus_param(icl, width_flag);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
/*
|
||||
* Without board specific bus width settings we only support the
|
||||
* sensors native bus width
|
||||
*/
|
||||
if (width_flag != SOCAM_DATAWIDTH_10)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
flags = soc_camera_apply_sensor_flags(icl, flags);
|
||||
|
||||
if (flags & SOCAM_PCLK_SAMPLE_FALLING)
|
||||
pixclk |= 0x10;
|
||||
|
||||
if (!(flags & SOCAM_HSYNC_ACTIVE_HIGH))
|
||||
pixclk |= 0x1;
|
||||
|
||||
if (!(flags & SOCAM_VSYNC_ACTIVE_HIGH))
|
||||
pixclk |= 0x2;
|
||||
|
||||
ret = reg_write(client, MT9V022_PIXCLK_FV_LV, pixclk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!(flags & SOCAM_MASTER))
|
||||
mt9v022->chip_control &= ~0x8;
|
||||
|
||||
ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_dbg(&client->dev, "Calculated pixclk 0x%x, chip control 0x%x\n",
|
||||
pixclk, mt9v022->chip_control);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd)
|
||||
{
|
||||
struct soc_camera_link *icl = to_soc_camera_link(icd);
|
||||
unsigned int flags = SOCAM_MASTER | SOCAM_SLAVE |
|
||||
SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING |
|
||||
SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW |
|
||||
SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW |
|
||||
SOCAM_DATA_ACTIVE_HIGH;
|
||||
|
||||
if (icl->query_bus_param)
|
||||
flags |= icl->query_bus_param(icl) & SOCAM_DATAWIDTH_MASK;
|
||||
else
|
||||
flags |= SOCAM_DATAWIDTH_10;
|
||||
|
||||
return soc_camera_apply_sensor_flags(icl, flags);
|
||||
}
|
||||
|
||||
static int mt9v022_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
@ -389,7 +332,7 @@ static int mt9v022_s_fmt(struct v4l2_subdev *sd,
|
||||
|
||||
/*
|
||||
* The caller provides a supported format, as verified per call to
|
||||
* icd->try_fmt(), datawidth is from our supported format list
|
||||
* .try_mbus_fmt(), datawidth is from our supported format list
|
||||
*/
|
||||
switch (mf->code) {
|
||||
case V4L2_MBUS_FMT_Y8_1X8:
|
||||
@ -502,236 +445,131 @@ static int mt9v022_s_register(struct v4l2_subdev *sd,
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct v4l2_queryctrl mt9v022_controls[] = {
|
||||
{
|
||||
.id = V4L2_CID_VFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Flip Vertically",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
}, {
|
||||
.id = V4L2_CID_HFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Flip Horizontally",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
}, {
|
||||
.id = V4L2_CID_GAIN,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Analog Gain",
|
||||
.minimum = 64,
|
||||
.maximum = 127,
|
||||
.step = 1,
|
||||
.default_value = 64,
|
||||
.flags = V4L2_CTRL_FLAG_SLIDER,
|
||||
}, {
|
||||
.id = V4L2_CID_EXPOSURE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Exposure",
|
||||
.minimum = 1,
|
||||
.maximum = 255,
|
||||
.step = 1,
|
||||
.default_value = 255,
|
||||
.flags = V4L2_CTRL_FLAG_SLIDER,
|
||||
}, {
|
||||
.id = V4L2_CID_AUTOGAIN,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Automatic Gain",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 1,
|
||||
}, {
|
||||
.id = V4L2_CID_EXPOSURE_AUTO,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Automatic Exposure",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 1,
|
||||
}
|
||||
};
|
||||
|
||||
static struct soc_camera_ops mt9v022_ops = {
|
||||
.set_bus_param = mt9v022_set_bus_param,
|
||||
.query_bus_param = mt9v022_query_bus_param,
|
||||
.controls = mt9v022_controls,
|
||||
.num_controls = ARRAY_SIZE(mt9v022_controls),
|
||||
};
|
||||
|
||||
static int mt9v022_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct mt9v022 *mt9v022 = container_of(ctrl->handler,
|
||||
struct mt9v022, hdl);
|
||||
struct v4l2_subdev *sd = &mt9v022->subdev;
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
const struct v4l2_queryctrl *qctrl;
|
||||
struct v4l2_ctrl *gain = mt9v022->gain;
|
||||
struct v4l2_ctrl *exp = mt9v022->exposure;
|
||||
unsigned long range;
|
||||
int data;
|
||||
|
||||
qctrl = soc_camera_find_qctrl(&mt9v022_ops, ctrl->id);
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_VFLIP:
|
||||
data = reg_read(client, MT9V022_READ_MODE);
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
ctrl->value = !!(data & 0x10);
|
||||
break;
|
||||
case V4L2_CID_HFLIP:
|
||||
data = reg_read(client, MT9V022_READ_MODE);
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
ctrl->value = !!(data & 0x20);
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE_AUTO:
|
||||
data = reg_read(client, MT9V022_AEC_AGC_ENABLE);
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
ctrl->value = !!(data & 0x1);
|
||||
break;
|
||||
case V4L2_CID_AUTOGAIN:
|
||||
data = reg_read(client, MT9V022_AEC_AGC_ENABLE);
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
ctrl->value = !!(data & 0x2);
|
||||
break;
|
||||
case V4L2_CID_GAIN:
|
||||
data = reg_read(client, MT9V022_ANALOG_GAIN);
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
|
||||
range = qctrl->maximum - qctrl->minimum;
|
||||
ctrl->value = ((data - 16) * range + 24) / 48 + qctrl->minimum;
|
||||
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE:
|
||||
range = gain->maximum - gain->minimum;
|
||||
gain->val = ((data - 16) * range + 24) / 48 + gain->minimum;
|
||||
return 0;
|
||||
case V4L2_CID_EXPOSURE_AUTO:
|
||||
data = reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH);
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
|
||||
range = qctrl->maximum - qctrl->minimum;
|
||||
ctrl->value = ((data - 1) * range + 239) / 479 + qctrl->minimum;
|
||||
|
||||
break;
|
||||
range = exp->maximum - exp->minimum;
|
||||
exp->val = ((data - 1) * range + 239) / 479 + exp->minimum;
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mt9v022_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
static int mt9v022_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
int data;
|
||||
struct mt9v022 *mt9v022 = container_of(ctrl->handler,
|
||||
struct mt9v022, hdl);
|
||||
struct v4l2_subdev *sd = &mt9v022->subdev;
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
const struct v4l2_queryctrl *qctrl;
|
||||
|
||||
qctrl = soc_camera_find_qctrl(&mt9v022_ops, ctrl->id);
|
||||
if (!qctrl)
|
||||
return -EINVAL;
|
||||
int data;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_VFLIP:
|
||||
if (ctrl->value)
|
||||
if (ctrl->val)
|
||||
data = reg_set(client, MT9V022_READ_MODE, 0x10);
|
||||
else
|
||||
data = reg_clear(client, MT9V022_READ_MODE, 0x10);
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
break;
|
||||
return 0;
|
||||
case V4L2_CID_HFLIP:
|
||||
if (ctrl->value)
|
||||
if (ctrl->val)
|
||||
data = reg_set(client, MT9V022_READ_MODE, 0x20);
|
||||
else
|
||||
data = reg_clear(client, MT9V022_READ_MODE, 0x20);
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
break;
|
||||
case V4L2_CID_GAIN:
|
||||
/* mt9v022 has minimum == default */
|
||||
if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
|
||||
return -EINVAL;
|
||||
else {
|
||||
unsigned long range = qctrl->maximum - qctrl->minimum;
|
||||
return 0;
|
||||
case V4L2_CID_AUTOGAIN:
|
||||
if (ctrl->val) {
|
||||
if (reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0)
|
||||
return -EIO;
|
||||
} else {
|
||||
struct v4l2_ctrl *gain = mt9v022->gain;
|
||||
/* mt9v022 has minimum == default */
|
||||
unsigned long range = gain->maximum - gain->minimum;
|
||||
/* Valid values 16 to 64, 32 to 64 must be even. */
|
||||
unsigned long gain = ((ctrl->value - qctrl->minimum) *
|
||||
unsigned long gain_val = ((gain->val - gain->minimum) *
|
||||
48 + range / 2) / range + 16;
|
||||
if (gain >= 32)
|
||||
gain &= ~1;
|
||||
|
||||
if (gain_val >= 32)
|
||||
gain_val &= ~1;
|
||||
|
||||
/*
|
||||
* The user wants to set gain manually, hope, she
|
||||
* knows, what she's doing... Switch AGC off.
|
||||
*/
|
||||
|
||||
if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0)
|
||||
return -EIO;
|
||||
|
||||
dev_dbg(&client->dev, "Setting gain from %d to %lu\n",
|
||||
reg_read(client, MT9V022_ANALOG_GAIN), gain);
|
||||
if (reg_write(client, MT9V022_ANALOG_GAIN, gain) < 0)
|
||||
reg_read(client, MT9V022_ANALOG_GAIN), gain_val);
|
||||
if (reg_write(client, MT9V022_ANALOG_GAIN, gain_val) < 0)
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE:
|
||||
/* mt9v022 has maximum == default */
|
||||
if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
|
||||
return -EINVAL;
|
||||
else {
|
||||
unsigned long range = qctrl->maximum - qctrl->minimum;
|
||||
unsigned long shutter = ((ctrl->value - qctrl->minimum) *
|
||||
479 + range / 2) / range + 1;
|
||||
return 0;
|
||||
case V4L2_CID_EXPOSURE_AUTO:
|
||||
if (ctrl->val == V4L2_EXPOSURE_AUTO) {
|
||||
data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x1);
|
||||
} else {
|
||||
struct v4l2_ctrl *exp = mt9v022->exposure;
|
||||
unsigned long range = exp->maximum - exp->minimum;
|
||||
unsigned long shutter = ((exp->val - exp->minimum) *
|
||||
479 + range / 2) / range + 1;
|
||||
|
||||
/*
|
||||
* The user wants to set shutter width manually, hope,
|
||||
* she knows, what she's doing... Switch AEC off.
|
||||
*/
|
||||
|
||||
if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1) < 0)
|
||||
data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1);
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
|
||||
dev_dbg(&client->dev, "Shutter width from %d to %lu\n",
|
||||
reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH),
|
||||
shutter);
|
||||
reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH),
|
||||
shutter);
|
||||
if (reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH,
|
||||
shutter) < 0)
|
||||
shutter) < 0)
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
case V4L2_CID_AUTOGAIN:
|
||||
if (ctrl->value)
|
||||
data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x2);
|
||||
else
|
||||
data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2);
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE_AUTO:
|
||||
if (ctrl->value)
|
||||
data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x1);
|
||||
else
|
||||
data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1);
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
break;
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Interface active, can use i2c. If it fails, it can indeed mean, that
|
||||
* this wasn't our capture interface, so, we wait for the right one
|
||||
*/
|
||||
static int mt9v022_video_probe(struct soc_camera_device *icd,
|
||||
struct i2c_client *client)
|
||||
static int mt9v022_video_probe(struct i2c_client *client)
|
||||
{
|
||||
struct mt9v022 *mt9v022 = to_mt9v022(client);
|
||||
struct soc_camera_link *icl = to_soc_camera_link(icd);
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
s32 data;
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
/* We must have a parent by now. And it cannot be a wrong one. */
|
||||
BUG_ON(!icd->parent ||
|
||||
to_soc_camera_host(icd->parent)->nr != icd->iface);
|
||||
|
||||
/* Read out the chip version register */
|
||||
data = reg_read(client, MT9V022_CHIP_VERSION);
|
||||
|
||||
@ -805,16 +643,6 @@ ei2c:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mt9v022_video_remove(struct soc_camera_device *icd)
|
||||
{
|
||||
struct soc_camera_link *icl = to_soc_camera_link(icd);
|
||||
|
||||
dev_dbg(icd->pdev, "Video removed: %p, %p\n",
|
||||
icd->parent, icd->vdev);
|
||||
if (icl->free_bus)
|
||||
icl->free_bus(icl);
|
||||
}
|
||||
|
||||
static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
@ -825,9 +653,12 @@ static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct v4l2_ctrl_ops mt9v022_ctrl_ops = {
|
||||
.g_volatile_ctrl = mt9v022_g_volatile_ctrl,
|
||||
.s_ctrl = mt9v022_s_ctrl,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = {
|
||||
.g_ctrl = mt9v022_g_ctrl,
|
||||
.s_ctrl = mt9v022_s_ctrl,
|
||||
.g_chip_ident = mt9v022_g_chip_ident,
|
||||
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
||||
.g_register = mt9v022_g_register,
|
||||
@ -848,6 +679,72 @@ static int mt9v022_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt9v022_g_mbus_config(struct v4l2_subdev *sd,
|
||||
struct v4l2_mbus_config *cfg)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
|
||||
cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE |
|
||||
V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
|
||||
V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW |
|
||||
V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
|
||||
V4L2_MBUS_DATA_ACTIVE_HIGH;
|
||||
cfg->type = V4L2_MBUS_PARALLEL;
|
||||
cfg->flags = soc_camera_apply_board_flags(icl, cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt9v022_s_mbus_config(struct v4l2_subdev *sd,
|
||||
const struct v4l2_mbus_config *cfg)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
struct mt9v022 *mt9v022 = to_mt9v022(client);
|
||||
unsigned long flags = soc_camera_apply_board_flags(icl, cfg);
|
||||
unsigned int bps = soc_mbus_get_fmtdesc(mt9v022->fmt->code)->bits_per_sample;
|
||||
int ret;
|
||||
u16 pixclk = 0;
|
||||
|
||||
if (icl->set_bus_param) {
|
||||
ret = icl->set_bus_param(icl, 1 << (bps - 1));
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (bps != 10) {
|
||||
/*
|
||||
* Without board specific bus width settings we only support the
|
||||
* sensors native bus width
|
||||
*/
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
|
||||
pixclk |= 0x10;
|
||||
|
||||
if (!(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH))
|
||||
pixclk |= 0x1;
|
||||
|
||||
if (!(flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH))
|
||||
pixclk |= 0x2;
|
||||
|
||||
ret = reg_write(client, MT9V022_PIXCLK_FV_LV, pixclk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!(flags & V4L2_MBUS_MASTER))
|
||||
mt9v022->chip_control &= ~0x8;
|
||||
|
||||
ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_dbg(&client->dev, "Calculated pixclk 0x%x, chip control 0x%x\n",
|
||||
pixclk, mt9v022->chip_control);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = {
|
||||
.s_stream = mt9v022_s_stream,
|
||||
.s_mbus_fmt = mt9v022_s_fmt,
|
||||
@ -857,6 +754,8 @@ static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = {
|
||||
.g_crop = mt9v022_g_crop,
|
||||
.cropcap = mt9v022_cropcap,
|
||||
.enum_mbus_fmt = mt9v022_enum_fmt,
|
||||
.g_mbus_config = mt9v022_g_mbus_config,
|
||||
.s_mbus_config = mt9v022_s_mbus_config,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_sensor_ops mt9v022_subdev_sensor_ops = {
|
||||
@ -873,17 +772,10 @@ static int mt9v022_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *did)
|
||||
{
|
||||
struct mt9v022 *mt9v022;
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct soc_camera_link *icl;
|
||||
int ret;
|
||||
|
||||
if (!icd) {
|
||||
dev_err(&client->dev, "MT9V022: missing soc-camera data!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
icl = to_soc_camera_link(icd);
|
||||
if (!icl) {
|
||||
dev_err(&client->dev, "MT9V022 driver needs platform data\n");
|
||||
return -EINVAL;
|
||||
@ -900,10 +792,39 @@ static int mt9v022_probe(struct i2c_client *client,
|
||||
return -ENOMEM;
|
||||
|
||||
v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops);
|
||||
v4l2_ctrl_handler_init(&mt9v022->hdl, 6);
|
||||
v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
|
||||
V4L2_CID_VFLIP, 0, 1, 1, 0);
|
||||
v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
|
||||
V4L2_CID_HFLIP, 0, 1, 1, 0);
|
||||
mt9v022->autogain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
|
||||
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
|
||||
mt9v022->gain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
|
||||
V4L2_CID_GAIN, 0, 127, 1, 64);
|
||||
|
||||
/*
|
||||
* Simulated autoexposure. If enabled, we calculate shutter width
|
||||
* ourselves in the driver based on vertical blanking and frame width
|
||||
*/
|
||||
mt9v022->autoexposure = v4l2_ctrl_new_std_menu(&mt9v022->hdl,
|
||||
&mt9v022_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
|
||||
V4L2_EXPOSURE_AUTO);
|
||||
mt9v022->exposure = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
|
||||
V4L2_CID_EXPOSURE, 1, 255, 1, 255);
|
||||
|
||||
mt9v022->subdev.ctrl_handler = &mt9v022->hdl;
|
||||
if (mt9v022->hdl.error) {
|
||||
int err = mt9v022->hdl.error;
|
||||
|
||||
kfree(mt9v022);
|
||||
return err;
|
||||
}
|
||||
v4l2_ctrl_auto_cluster(2, &mt9v022->autoexposure,
|
||||
V4L2_EXPOSURE_MANUAL, true);
|
||||
v4l2_ctrl_auto_cluster(2, &mt9v022->autogain, 0, true);
|
||||
|
||||
mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT;
|
||||
|
||||
icd->ops = &mt9v022_ops;
|
||||
/*
|
||||
* MT9V022 _really_ corrupts the first read out line.
|
||||
* TODO: verify on i.MX31
|
||||
@ -914,9 +835,9 @@ static int mt9v022_probe(struct i2c_client *client,
|
||||
mt9v022->rect.width = MT9V022_MAX_WIDTH;
|
||||
mt9v022->rect.height = MT9V022_MAX_HEIGHT;
|
||||
|
||||
ret = mt9v022_video_probe(icd, client);
|
||||
ret = mt9v022_video_probe(client);
|
||||
if (ret) {
|
||||
icd->ops = NULL;
|
||||
v4l2_ctrl_handler_free(&mt9v022->hdl);
|
||||
kfree(mt9v022);
|
||||
}
|
||||
|
||||
@ -926,10 +847,12 @@ static int mt9v022_probe(struct i2c_client *client,
|
||||
static int mt9v022_remove(struct i2c_client *client)
|
||||
{
|
||||
struct mt9v022 *mt9v022 = to_mt9v022(client);
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
|
||||
icd->ops = NULL;
|
||||
mt9v022_video_remove(icd);
|
||||
v4l2_device_unregister_subdev(&mt9v022->subdev);
|
||||
if (icl->free_bus)
|
||||
icl->free_bus(icl);
|
||||
v4l2_ctrl_handler_free(&mt9v022->hdl);
|
||||
kfree(mt9v022);
|
||||
|
||||
return 0;
|
||||
|
@ -78,11 +78,10 @@
|
||||
#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 (SOCAM_MASTER | SOCAM_HSYNC_ACTIVE_HIGH | \
|
||||
SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW | \
|
||||
SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | \
|
||||
SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_LOW | \
|
||||
SOCAM_DATAWIDTH_8)
|
||||
#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 */
|
||||
|
||||
@ -490,59 +489,73 @@ static int mx1_camera_set_crop(struct soc_camera_device *icd,
|
||||
|
||||
static int mx1_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
|
||||
{
|
||||
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;
|
||||
unsigned long camera_flags, common_flags;
|
||||
struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
|
||||
unsigned long common_flags;
|
||||
unsigned int csicr1;
|
||||
int ret;
|
||||
|
||||
camera_flags = icd->ops->query_bus_param(icd);
|
||||
|
||||
/* MX1 supports only 8bit buswidth */
|
||||
common_flags = soc_camera_bus_param_compatible(camera_flags,
|
||||
CSI_BUS_FLAGS);
|
||||
if (!common_flags)
|
||||
return -EINVAL;
|
||||
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 & SOCAM_VSYNC_ACTIVE_HIGH) &&
|
||||
(common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
|
||||
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 &= ~SOCAM_VSYNC_ACTIVE_LOW;
|
||||
common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
|
||||
else
|
||||
common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
|
||||
common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
|
||||
}
|
||||
|
||||
if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
|
||||
(common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
|
||||
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 &= ~SOCAM_PCLK_SAMPLE_FALLING;
|
||||
common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
|
||||
else
|
||||
common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
|
||||
common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
|
||||
}
|
||||
|
||||
if ((common_flags & SOCAM_DATA_ACTIVE_HIGH) &&
|
||||
(common_flags & SOCAM_DATA_ACTIVE_LOW)) {
|
||||
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 &= ~SOCAM_DATA_ACTIVE_LOW;
|
||||
common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW;
|
||||
else
|
||||
common_flags &= ~SOCAM_DATA_ACTIVE_HIGH;
|
||||
common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH;
|
||||
}
|
||||
|
||||
ret = icd->ops->set_bus_param(icd, common_flags);
|
||||
if (ret < 0)
|
||||
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 & SOCAM_PCLK_SAMPLE_RISING)
|
||||
if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
|
||||
csicr1 |= CSICR1_REDGE;
|
||||
if (common_flags & SOCAM_VSYNC_ACTIVE_HIGH)
|
||||
if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
|
||||
csicr1 |= CSICR1_SOF_POL;
|
||||
if (common_flags & SOCAM_DATA_ACTIVE_LOW)
|
||||
if (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)
|
||||
csicr1 |= CSICR1_DATA_POL;
|
||||
|
||||
__raw_writel(csicr1, pcdev->base + CSICR1);
|
||||
|
@ -686,16 +686,15 @@ static void mx2_camera_init_videobuf(struct videobuf_queue *q,
|
||||
icd, &icd->video_lock);
|
||||
}
|
||||
|
||||
#define MX2_BUS_FLAGS (SOCAM_DATAWIDTH_8 | \
|
||||
SOCAM_MASTER | \
|
||||
SOCAM_VSYNC_ACTIVE_HIGH | \
|
||||
SOCAM_VSYNC_ACTIVE_LOW | \
|
||||
SOCAM_HSYNC_ACTIVE_HIGH | \
|
||||
SOCAM_HSYNC_ACTIVE_LOW | \
|
||||
SOCAM_PCLK_SAMPLE_RISING | \
|
||||
SOCAM_PCLK_SAMPLE_FALLING | \
|
||||
SOCAM_DATA_ACTIVE_HIGH | \
|
||||
SOCAM_DATA_ACTIVE_LOW)
|
||||
#define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \
|
||||
V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
|
||||
V4L2_MBUS_VSYNC_ACTIVE_LOW | \
|
||||
V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
|
||||
V4L2_MBUS_HSYNC_ACTIVE_LOW | \
|
||||
V4L2_MBUS_PCLK_SAMPLE_RISING | \
|
||||
V4L2_MBUS_PCLK_SAMPLE_FALLING | \
|
||||
V4L2_MBUS_DATA_ACTIVE_HIGH | \
|
||||
V4L2_MBUS_DATA_ACTIVE_LOW)
|
||||
|
||||
static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev)
|
||||
{
|
||||
@ -770,46 +769,59 @@ static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
|
||||
static int mx2_camera_set_bus_param(struct soc_camera_device *icd,
|
||||
__u32 pixfmt)
|
||||
{
|
||||
struct soc_camera_host *ici =
|
||||
to_soc_camera_host(icd->parent);
|
||||
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
|
||||
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
||||
struct mx2_camera_dev *pcdev = ici->priv;
|
||||
unsigned long camera_flags, common_flags;
|
||||
int ret = 0;
|
||||
struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
|
||||
unsigned long common_flags;
|
||||
int ret;
|
||||
int bytesperline;
|
||||
u32 csicr1 = pcdev->csicr1;
|
||||
|
||||
camera_flags = icd->ops->query_bus_param(icd);
|
||||
|
||||
common_flags = soc_camera_bus_param_compatible(camera_flags,
|
||||
MX2_BUS_FLAGS);
|
||||
if (!common_flags)
|
||||
return -EINVAL;
|
||||
|
||||
if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
|
||||
(common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
|
||||
if (pcdev->platform_flags & MX2_CAMERA_HSYNC_HIGH)
|
||||
common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
|
||||
else
|
||||
common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
|
||||
}
|
||||
|
||||
if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
|
||||
(common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
|
||||
if (pcdev->platform_flags & MX2_CAMERA_PCLK_SAMPLE_RISING)
|
||||
common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
|
||||
else
|
||||
common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
|
||||
}
|
||||
|
||||
ret = icd->ops->set_bus_param(icd, common_flags);
|
||||
if (ret < 0)
|
||||
ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
|
||||
if (!ret) {
|
||||
common_flags = soc_mbus_config_compatible(&cfg, MX2_BUS_FLAGS);
|
||||
if (!common_flags) {
|
||||
dev_warn(icd->parent,
|
||||
"Flags incompatible: camera 0x%x, host 0x%x\n",
|
||||
cfg.flags, MX2_BUS_FLAGS);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (ret != -ENOIOCTLCMD) {
|
||||
return ret;
|
||||
} else {
|
||||
common_flags = MX2_BUS_FLAGS;
|
||||
}
|
||||
|
||||
if (common_flags & SOCAM_PCLK_SAMPLE_RISING)
|
||||
if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
|
||||
(common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
|
||||
if (pcdev->platform_flags & MX2_CAMERA_HSYNC_HIGH)
|
||||
common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
|
||||
else
|
||||
common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
|
||||
}
|
||||
|
||||
if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
|
||||
(common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
|
||||
if (pcdev->platform_flags & MX2_CAMERA_PCLK_SAMPLE_RISING)
|
||||
common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
|
||||
else
|
||||
common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
|
||||
csicr1 |= CSICR1_REDGE;
|
||||
if (common_flags & SOCAM_VSYNC_ACTIVE_HIGH)
|
||||
if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
|
||||
csicr1 |= CSICR1_SOF_POL;
|
||||
if (common_flags & SOCAM_HSYNC_ACTIVE_HIGH)
|
||||
if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
|
||||
csicr1 |= CSICR1_HSYNC_POL;
|
||||
if (pcdev->platform_flags & MX2_CAMERA_SWAP16)
|
||||
csicr1 |= CSICR1_SWAP16_EN;
|
||||
|
@ -109,10 +109,12 @@ struct mx3_camera_dev {
|
||||
|
||||
unsigned long platform_flags;
|
||||
unsigned long mclk;
|
||||
u16 width_flags; /* max 15 bits */
|
||||
|
||||
struct list_head capture;
|
||||
spinlock_t lock; /* Protects video buffer lists */
|
||||
struct mx3_camera_buffer *active;
|
||||
size_t buf_total;
|
||||
struct vb2_alloc_ctx *alloc_ctx;
|
||||
enum v4l2_field field;
|
||||
int sequence;
|
||||
@ -190,79 +192,53 @@ static void mx3_cam_dma_done(void *arg)
|
||||
* Calculate the __buffer__ (not data) size and number of buffers.
|
||||
*/
|
||||
static int mx3_videobuf_setup(struct vb2_queue *vq,
|
||||
const struct v4l2_format *fmt,
|
||||
unsigned int *count, unsigned int *num_planes,
|
||||
unsigned int sizes[], void *alloc_ctxs[])
|
||||
{
|
||||
struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
|
||||
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
||||
struct mx3_camera_dev *mx3_cam = ici->priv;
|
||||
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
|
||||
icd->current_fmt->host_fmt);
|
||||
|
||||
if (bytes_per_line < 0)
|
||||
return bytes_per_line;
|
||||
int bytes_per_line;
|
||||
unsigned int height;
|
||||
|
||||
if (!mx3_cam->idmac_channel[0])
|
||||
return -EINVAL;
|
||||
|
||||
*num_planes = 1;
|
||||
|
||||
mx3_cam->sequence = 0;
|
||||
sizes[0] = bytes_per_line * icd->user_height;
|
||||
alloc_ctxs[0] = mx3_cam->alloc_ctx;
|
||||
|
||||
if (!*count)
|
||||
*count = 32;
|
||||
|
||||
if (sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024)
|
||||
*count = MAX_VIDEO_MEM * 1024 * 1024 / sizes[0];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mx3_videobuf_prepare(struct vb2_buffer *vb)
|
||||
{
|
||||
struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
|
||||
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
||||
struct mx3_camera_dev *mx3_cam = ici->priv;
|
||||
struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
|
||||
struct scatterlist *sg;
|
||||
struct mx3_camera_buffer *buf;
|
||||
size_t new_size;
|
||||
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
|
||||
if (fmt) {
|
||||
const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd,
|
||||
fmt->fmt.pix.pixelformat);
|
||||
if (!xlate)
|
||||
return -EINVAL;
|
||||
bytes_per_line = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
|
||||
xlate->host_fmt);
|
||||
height = fmt->fmt.pix.height;
|
||||
} else {
|
||||
/* Called from VIDIOC_REQBUFS or in compatibility mode */
|
||||
bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
|
||||
icd->current_fmt->host_fmt);
|
||||
|
||||
height = icd->user_height;
|
||||
}
|
||||
if (bytes_per_line < 0)
|
||||
return bytes_per_line;
|
||||
|
||||
buf = to_mx3_vb(vb);
|
||||
sg = &buf->sg;
|
||||
sizes[0] = bytes_per_line * height;
|
||||
|
||||
new_size = bytes_per_line * icd->user_height;
|
||||
alloc_ctxs[0] = mx3_cam->alloc_ctx;
|
||||
|
||||
if (vb2_plane_size(vb, 0) < new_size) {
|
||||
dev_err(icd->parent, "Buffer too small (%lu < %zu)\n",
|
||||
vb2_plane_size(vb, 0), new_size);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
if (!vq->num_buffers)
|
||||
mx3_cam->sequence = 0;
|
||||
|
||||
if (buf->state == CSI_BUF_NEEDS_INIT) {
|
||||
sg_dma_address(sg) = vb2_dma_contig_plane_dma_addr(vb, 0);
|
||||
sg_dma_len(sg) = new_size;
|
||||
if (!*count)
|
||||
*count = 2;
|
||||
|
||||
buf->txd = ichan->dma_chan.device->device_prep_slave_sg(
|
||||
&ichan->dma_chan, sg, 1, DMA_FROM_DEVICE,
|
||||
DMA_PREP_INTERRUPT);
|
||||
if (!buf->txd)
|
||||
return -EIO;
|
||||
/* If *num_planes != 0, we have already verified *count. */
|
||||
if (!*num_planes &&
|
||||
sizes[0] * *count + mx3_cam->buf_total > MAX_VIDEO_MEM * 1024 * 1024)
|
||||
*count = (MAX_VIDEO_MEM * 1024 * 1024 - mx3_cam->buf_total) /
|
||||
sizes[0];
|
||||
|
||||
buf->txd->callback_param = buf->txd;
|
||||
buf->txd->callback = mx3_cam_dma_done;
|
||||
|
||||
buf->state = CSI_BUF_PREPARED;
|
||||
}
|
||||
|
||||
vb2_set_plane_payload(vb, 0, new_size);
|
||||
*num_planes = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -286,28 +262,58 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)
|
||||
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
||||
struct mx3_camera_dev *mx3_cam = ici->priv;
|
||||
struct mx3_camera_buffer *buf = to_mx3_vb(vb);
|
||||
struct dma_async_tx_descriptor *txd = buf->txd;
|
||||
struct idmac_channel *ichan = to_idmac_chan(txd->chan);
|
||||
struct scatterlist *sg = &buf->sg;
|
||||
struct dma_async_tx_descriptor *txd;
|
||||
struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
|
||||
struct idmac_video_param *video = &ichan->params.video;
|
||||
dma_cookie_t cookie;
|
||||
u32 fourcc = icd->current_fmt->host_fmt->fourcc;
|
||||
const struct soc_mbus_pixelfmt *host_fmt = icd->current_fmt->host_fmt;
|
||||
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, host_fmt);
|
||||
unsigned long flags;
|
||||
dma_cookie_t cookie;
|
||||
size_t new_size;
|
||||
|
||||
BUG_ON(bytes_per_line <= 0);
|
||||
|
||||
new_size = bytes_per_line * icd->user_height;
|
||||
|
||||
if (vb2_plane_size(vb, 0) < new_size) {
|
||||
dev_err(icd->parent, "Buffer #%d too small (%lu < %zu)\n",
|
||||
vb->v4l2_buf.index, vb2_plane_size(vb, 0), new_size);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (buf->state == CSI_BUF_NEEDS_INIT) {
|
||||
sg_dma_address(sg) = vb2_dma_contig_plane_dma_addr(vb, 0);
|
||||
sg_dma_len(sg) = new_size;
|
||||
|
||||
txd = ichan->dma_chan.device->device_prep_slave_sg(
|
||||
&ichan->dma_chan, sg, 1, DMA_FROM_DEVICE,
|
||||
DMA_PREP_INTERRUPT);
|
||||
if (!txd)
|
||||
goto error;
|
||||
|
||||
txd->callback_param = txd;
|
||||
txd->callback = mx3_cam_dma_done;
|
||||
|
||||
buf->state = CSI_BUF_PREPARED;
|
||||
buf->txd = txd;
|
||||
} else {
|
||||
txd = buf->txd;
|
||||
}
|
||||
|
||||
vb2_set_plane_payload(vb, 0, new_size);
|
||||
|
||||
/* This is the configuration of one sg-element */
|
||||
video->out_pixel_fmt = fourcc_to_ipu_pix(fourcc);
|
||||
video->out_pixel_fmt = fourcc_to_ipu_pix(host_fmt->fourcc);
|
||||
|
||||
if (video->out_pixel_fmt == IPU_PIX_FMT_GENERIC) {
|
||||
/*
|
||||
* If the IPU DMA channel is configured to transport
|
||||
* generic 8-bit data, we have to set up correctly the
|
||||
* geometry parameters upon the current pixel format.
|
||||
* So, since the DMA horizontal parameters are expressed
|
||||
* in bytes not pixels, convert these in the right unit.
|
||||
* If the IPU DMA channel is configured to transfer generic
|
||||
* 8-bit data, we have to set up the geometry parameters
|
||||
* correctly, according to the current pixel format. The DMA
|
||||
* horizontal parameters in this case are expressed in bytes,
|
||||
* not in pixels.
|
||||
*/
|
||||
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
|
||||
icd->current_fmt->host_fmt);
|
||||
BUG_ON(bytes_per_line <= 0);
|
||||
|
||||
video->out_width = bytes_per_line;
|
||||
video->out_height = icd->user_height;
|
||||
video->out_stride = bytes_per_line;
|
||||
@ -351,6 +357,7 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)
|
||||
mx3_cam->active = NULL;
|
||||
|
||||
spin_unlock_irqrestore(&mx3_cam->lock, flags);
|
||||
error:
|
||||
vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
|
||||
}
|
||||
|
||||
@ -384,17 +391,24 @@ static void mx3_videobuf_release(struct vb2_buffer *vb)
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&mx3_cam->lock, flags);
|
||||
|
||||
mx3_cam->buf_total -= vb2_plane_size(vb, 0);
|
||||
}
|
||||
|
||||
static int mx3_videobuf_init(struct vb2_buffer *vb)
|
||||
{
|
||||
struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
|
||||
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
||||
struct mx3_camera_dev *mx3_cam = ici->priv;
|
||||
struct mx3_camera_buffer *buf = to_mx3_vb(vb);
|
||||
|
||||
/* This is for locking debugging only */
|
||||
INIT_LIST_HEAD(&buf->queue);
|
||||
sg_init_table(&buf->sg, 1);
|
||||
|
||||
buf->state = CSI_BUF_NEEDS_INIT;
|
||||
buf->txd = NULL;
|
||||
|
||||
mx3_cam->buf_total += vb2_plane_size(vb, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -405,13 +419,12 @@ static int mx3_stop_streaming(struct vb2_queue *q)
|
||||
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
||||
struct mx3_camera_dev *mx3_cam = ici->priv;
|
||||
struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
|
||||
struct dma_chan *chan;
|
||||
struct mx3_camera_buffer *buf, *tmp;
|
||||
unsigned long flags;
|
||||
|
||||
if (ichan) {
|
||||
chan = &ichan->dma_chan;
|
||||
chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
|
||||
struct dma_chan *chan = &ichan->dma_chan;
|
||||
chan->device->device_control(chan, DMA_PAUSE, 0);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&mx3_cam->lock, flags);
|
||||
@ -419,8 +432,8 @@ static int mx3_stop_streaming(struct vb2_queue *q)
|
||||
mx3_cam->active = NULL;
|
||||
|
||||
list_for_each_entry_safe(buf, tmp, &mx3_cam->capture, queue) {
|
||||
buf->state = CSI_BUF_NEEDS_INIT;
|
||||
list_del_init(&buf->queue);
|
||||
vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&mx3_cam->lock, flags);
|
||||
@ -430,7 +443,6 @@ static int mx3_stop_streaming(struct vb2_queue *q)
|
||||
|
||||
static struct vb2_ops mx3_videobuf_ops = {
|
||||
.queue_setup = mx3_videobuf_setup,
|
||||
.buf_prepare = mx3_videobuf_prepare,
|
||||
.buf_queue = mx3_videobuf_queue,
|
||||
.buf_cleanup = mx3_videobuf_release,
|
||||
.buf_init = mx3_videobuf_init,
|
||||
@ -514,6 +526,7 @@ static int mx3_camera_add_device(struct soc_camera_device *icd)
|
||||
|
||||
mx3_camera_activate(mx3_cam, icd);
|
||||
|
||||
mx3_cam->buf_total = 0;
|
||||
mx3_cam->icd = icd;
|
||||
|
||||
dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n",
|
||||
@ -547,59 +560,28 @@ static void mx3_camera_remove_device(struct soc_camera_device *icd)
|
||||
static int test_platform_param(struct mx3_camera_dev *mx3_cam,
|
||||
unsigned char buswidth, unsigned long *flags)
|
||||
{
|
||||
/*
|
||||
* If requested data width is supported by the platform, use it or any
|
||||
* possible lower value - i.MX31 is smart enough to shift bits
|
||||
*/
|
||||
if (buswidth > fls(mx3_cam->width_flags))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Platform specified synchronization and pixel clock polarities are
|
||||
* only a recommendation and are only used during probing. MX3x
|
||||
* camera interface only works in master mode, i.e., uses HSYNC and
|
||||
* VSYNC signals from the sensor
|
||||
*/
|
||||
*flags = SOCAM_MASTER |
|
||||
SOCAM_HSYNC_ACTIVE_HIGH |
|
||||
SOCAM_HSYNC_ACTIVE_LOW |
|
||||
SOCAM_VSYNC_ACTIVE_HIGH |
|
||||
SOCAM_VSYNC_ACTIVE_LOW |
|
||||
SOCAM_PCLK_SAMPLE_RISING |
|
||||
SOCAM_PCLK_SAMPLE_FALLING |
|
||||
SOCAM_DATA_ACTIVE_HIGH |
|
||||
SOCAM_DATA_ACTIVE_LOW;
|
||||
|
||||
/*
|
||||
* If requested data width is supported by the platform, use it or any
|
||||
* possible lower value - i.MX31 is smart enough to schift bits
|
||||
*/
|
||||
if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
|
||||
*flags |= SOCAM_DATAWIDTH_15 | SOCAM_DATAWIDTH_10 |
|
||||
SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_4;
|
||||
else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
|
||||
*flags |= SOCAM_DATAWIDTH_10 | SOCAM_DATAWIDTH_8 |
|
||||
SOCAM_DATAWIDTH_4;
|
||||
else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
|
||||
*flags |= SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_4;
|
||||
else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)
|
||||
*flags |= SOCAM_DATAWIDTH_4;
|
||||
|
||||
switch (buswidth) {
|
||||
case 15:
|
||||
if (!(*flags & SOCAM_DATAWIDTH_15))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case 10:
|
||||
if (!(*flags & SOCAM_DATAWIDTH_10))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case 8:
|
||||
if (!(*flags & SOCAM_DATAWIDTH_8))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case 4:
|
||||
if (!(*flags & SOCAM_DATAWIDTH_4))
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
dev_warn(mx3_cam->soc_host.v4l2_dev.dev,
|
||||
"Unsupported bus width %d\n", buswidth);
|
||||
return -EINVAL;
|
||||
}
|
||||
*flags = V4L2_MBUS_MASTER |
|
||||
V4L2_MBUS_HSYNC_ACTIVE_HIGH |
|
||||
V4L2_MBUS_HSYNC_ACTIVE_LOW |
|
||||
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;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -607,9 +589,11 @@ static int test_platform_param(struct mx3_camera_dev *mx3_cam,
|
||||
static int mx3_camera_try_bus_param(struct soc_camera_device *icd,
|
||||
const unsigned int depth)
|
||||
{
|
||||
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
|
||||
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
||||
struct mx3_camera_dev *mx3_cam = ici->priv;
|
||||
unsigned long bus_flags, camera_flags;
|
||||
struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
|
||||
unsigned long bus_flags, common_flags;
|
||||
int ret = test_platform_param(mx3_cam, depth, &bus_flags);
|
||||
|
||||
dev_dbg(icd->parent, "request bus width %d bit: %d\n", depth, ret);
|
||||
@ -617,15 +601,21 @@ static int mx3_camera_try_bus_param(struct soc_camera_device *icd,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
camera_flags = icd->ops->query_bus_param(icd);
|
||||
ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
|
||||
if (!ret) {
|
||||
common_flags = soc_mbus_config_compatible(&cfg,
|
||||
bus_flags);
|
||||
if (!common_flags) {
|
||||
dev_warn(icd->parent,
|
||||
"Flags incompatible: camera 0x%x, host 0x%lx\n",
|
||||
cfg.flags, bus_flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (ret != -ENOIOCTLCMD) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = soc_camera_bus_param_compatible(camera_flags, bus_flags);
|
||||
if (ret < 0)
|
||||
dev_warn(icd->parent,
|
||||
"Flags incompatible: camera %lx, host %lx\n",
|
||||
camera_flags, bus_flags);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool chan_filter(struct dma_chan *chan, void *arg)
|
||||
@ -994,9 +984,11 @@ static int mx3_camera_querycap(struct soc_camera_host *ici,
|
||||
|
||||
static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
|
||||
{
|
||||
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
|
||||
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
||||
struct mx3_camera_dev *mx3_cam = ici->priv;
|
||||
unsigned long bus_flags, camera_flags, common_flags;
|
||||
struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
|
||||
unsigned long bus_flags, common_flags;
|
||||
u32 dw, sens_conf;
|
||||
const struct soc_mbus_pixelfmt *fmt;
|
||||
int buswidth;
|
||||
@ -1008,83 +1000,76 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
|
||||
if (!fmt)
|
||||
return -EINVAL;
|
||||
|
||||
buswidth = fmt->bits_per_sample;
|
||||
ret = test_platform_param(mx3_cam, buswidth, &bus_flags);
|
||||
|
||||
xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
|
||||
if (!xlate) {
|
||||
dev_warn(dev, "Format %x not found\n", pixfmt);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
buswidth = fmt->bits_per_sample;
|
||||
ret = test_platform_param(mx3_cam, buswidth, &bus_flags);
|
||||
|
||||
dev_dbg(dev, "requested bus width %d bit: %d\n", buswidth, ret);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
camera_flags = icd->ops->query_bus_param(icd);
|
||||
|
||||
common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags);
|
||||
dev_dbg(dev, "Flags cam: 0x%lx host: 0x%lx common: 0x%lx\n",
|
||||
camera_flags, bus_flags, common_flags);
|
||||
if (!common_flags) {
|
||||
dev_dbg(dev, "no common flags");
|
||||
return -EINVAL;
|
||||
ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
|
||||
if (!ret) {
|
||||
common_flags = soc_mbus_config_compatible(&cfg,
|
||||
bus_flags);
|
||||
if (!common_flags) {
|
||||
dev_warn(icd->parent,
|
||||
"Flags incompatible: camera 0x%x, host 0x%lx\n",
|
||||
cfg.flags, bus_flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (ret != -ENOIOCTLCMD) {
|
||||
return ret;
|
||||
} else {
|
||||
common_flags = bus_flags;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "Flags cam: 0x%x host: 0x%lx common: 0x%lx\n",
|
||||
cfg.flags, bus_flags, common_flags);
|
||||
|
||||
/* Make choices, based on platform preferences */
|
||||
if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
|
||||
(common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
|
||||
if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
|
||||
(common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
|
||||
if (mx3_cam->platform_flags & MX3_CAMERA_HSP)
|
||||
common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
|
||||
common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
|
||||
else
|
||||
common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
|
||||
common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
|
||||
}
|
||||
|
||||
if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
|
||||
(common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
|
||||
if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
|
||||
(common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
|
||||
if (mx3_cam->platform_flags & MX3_CAMERA_VSP)
|
||||
common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
|
||||
common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
|
||||
else
|
||||
common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
|
||||
common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
|
||||
}
|
||||
|
||||
if ((common_flags & SOCAM_DATA_ACTIVE_HIGH) &&
|
||||
(common_flags & SOCAM_DATA_ACTIVE_LOW)) {
|
||||
if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) &&
|
||||
(common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) {
|
||||
if (mx3_cam->platform_flags & MX3_CAMERA_DP)
|
||||
common_flags &= ~SOCAM_DATA_ACTIVE_HIGH;
|
||||
common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH;
|
||||
else
|
||||
common_flags &= ~SOCAM_DATA_ACTIVE_LOW;
|
||||
common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW;
|
||||
}
|
||||
|
||||
if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
|
||||
(common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
|
||||
if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
|
||||
(common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
|
||||
if (mx3_cam->platform_flags & MX3_CAMERA_PCP)
|
||||
common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
|
||||
common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
|
||||
else
|
||||
common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
|
||||
common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make the camera work in widest common mode, we'll take care of
|
||||
* the rest
|
||||
*/
|
||||
if (common_flags & SOCAM_DATAWIDTH_15)
|
||||
common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) |
|
||||
SOCAM_DATAWIDTH_15;
|
||||
else if (common_flags & SOCAM_DATAWIDTH_10)
|
||||
common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) |
|
||||
SOCAM_DATAWIDTH_10;
|
||||
else if (common_flags & SOCAM_DATAWIDTH_8)
|
||||
common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) |
|
||||
SOCAM_DATAWIDTH_8;
|
||||
else
|
||||
common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) |
|
||||
SOCAM_DATAWIDTH_4;
|
||||
|
||||
ret = icd->ops->set_bus_param(icd, common_flags);
|
||||
if (ret < 0) {
|
||||
dev_dbg(dev, "camera set_bus_param(%lx) returned %d\n",
|
||||
cfg.flags = common_flags;
|
||||
ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
|
||||
if (ret < 0 && ret != -ENOIOCTLCMD) {
|
||||
dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n",
|
||||
common_flags, ret);
|
||||
return ret;
|
||||
}
|
||||
@ -1108,13 +1093,13 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
|
||||
/* This has been set in mx3_camera_activate(), but we clear it above */
|
||||
sens_conf |= CSI_SENS_CONF_DATA_FMT_BAYER;
|
||||
|
||||
if (common_flags & SOCAM_PCLK_SAMPLE_FALLING)
|
||||
if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
|
||||
sens_conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT;
|
||||
if (common_flags & SOCAM_HSYNC_ACTIVE_LOW)
|
||||
if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
|
||||
sens_conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT;
|
||||
if (common_flags & SOCAM_VSYNC_ACTIVE_LOW)
|
||||
if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
|
||||
sens_conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT;
|
||||
if (common_flags & SOCAM_DATA_ACTIVE_LOW)
|
||||
if (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)
|
||||
sens_conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT;
|
||||
|
||||
/* Just do what we're asked to do */
|
||||
@ -1199,6 +1184,14 @@ static int __devinit mx3_camera_probe(struct platform_device *pdev)
|
||||
"data widths, using default 8 bit\n");
|
||||
mx3_cam->platform_flags |= MX3_CAMERA_DATAWIDTH_8;
|
||||
}
|
||||
if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)
|
||||
mx3_cam->width_flags = 1 << 3;
|
||||
if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
|
||||
mx3_cam->width_flags |= 1 << 7;
|
||||
if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
|
||||
mx3_cam->width_flags |= 1 << 9;
|
||||
if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
|
||||
mx3_cam->width_flags |= 1 << 14;
|
||||
|
||||
mx3_cam->mclk = mx3_cam->pdata->mclk_10khz * 10000;
|
||||
if (!mx3_cam->mclk) {
|
||||
@ -1281,8 +1274,6 @@ static int __devexit mx3_camera_remove(struct platform_device *pdev)
|
||||
|
||||
dmaengine_put();
|
||||
|
||||
dev_info(&pdev->dev, "i.MX3x Camera driver unloaded\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -833,6 +833,15 @@ static void omap_vout_buffer_release(struct videobuf_queue *q,
|
||||
/*
|
||||
* File operations
|
||||
*/
|
||||
static unsigned int omap_vout_poll(struct file *file,
|
||||
struct poll_table_struct *wait)
|
||||
{
|
||||
struct omap_vout_device *vout = file->private_data;
|
||||
struct videobuf_queue *q = &vout->vbq;
|
||||
|
||||
return videobuf_poll_stream(file, q, wait);
|
||||
}
|
||||
|
||||
static void omap_vout_vm_open(struct vm_area_struct *vma)
|
||||
{
|
||||
struct omap_vout_device *vout = vma->vm_private_data;
|
||||
@ -1861,6 +1870,7 @@ static const struct v4l2_ioctl_ops vout_ioctl_ops = {
|
||||
|
||||
static const struct v4l2_file_operations omap_vout_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.poll = omap_vout_poll,
|
||||
.unlocked_ioctl = video_ioctl2,
|
||||
.mmap = omap_vout_mmap,
|
||||
.open = omap_vout_open,
|
||||
|
@ -102,10 +102,10 @@
|
||||
/* end of OMAP1 Camera Interface registers */
|
||||
|
||||
|
||||
#define SOCAM_BUS_FLAGS (SOCAM_MASTER | \
|
||||
SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | \
|
||||
SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | \
|
||||
SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8)
|
||||
#define SOCAM_BUS_FLAGS (V4L2_MBUS_MASTER | \
|
||||
V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
|
||||
V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | \
|
||||
V4L2_MBUS_DATA_ACTIVE_HIGH)
|
||||
|
||||
|
||||
#define FIFO_SIZE ((THRESHOLD_MASK >> THRESHOLD_SHIFT) + 1)
|
||||
@ -1438,41 +1438,55 @@ static int omap1_cam_querycap(struct soc_camera_host *ici,
|
||||
static int omap1_cam_set_bus_param(struct soc_camera_device *icd,
|
||||
__u32 pixfmt)
|
||||
{
|
||||
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
|
||||
struct device *dev = icd->parent;
|
||||
struct soc_camera_host *ici = to_soc_camera_host(dev);
|
||||
struct omap1_cam_dev *pcdev = ici->priv;
|
||||
const struct soc_camera_format_xlate *xlate;
|
||||
const struct soc_mbus_pixelfmt *fmt;
|
||||
unsigned long camera_flags, common_flags;
|
||||
struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
|
||||
unsigned long common_flags;
|
||||
u32 ctrlclock, mode;
|
||||
int ret;
|
||||
|
||||
camera_flags = icd->ops->query_bus_param(icd);
|
||||
|
||||
common_flags = soc_camera_bus_param_compatible(camera_flags,
|
||||
SOCAM_BUS_FLAGS);
|
||||
if (!common_flags)
|
||||
return -EINVAL;
|
||||
|
||||
/* Make choices, possibly based on platform configuration */
|
||||
if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
|
||||
(common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
|
||||
if (!pcdev->pdata ||
|
||||
pcdev->pdata->flags & OMAP1_CAMERA_LCLK_RISING)
|
||||
common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
|
||||
else
|
||||
common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
|
||||
ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
|
||||
if (!ret) {
|
||||
common_flags = soc_mbus_config_compatible(&cfg, SOCAM_BUS_FLAGS);
|
||||
if (!common_flags) {
|
||||
dev_warn(dev,
|
||||
"Flags incompatible: camera 0x%x, host 0x%x\n",
|
||||
cfg.flags, SOCAM_BUS_FLAGS);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (ret != -ENOIOCTLCMD) {
|
||||
return ret;
|
||||
} else {
|
||||
common_flags = SOCAM_BUS_FLAGS;
|
||||
}
|
||||
|
||||
ret = icd->ops->set_bus_param(icd, common_flags);
|
||||
if (ret < 0)
|
||||
/* Make choices, possibly based on platform configuration */
|
||||
if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
|
||||
(common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
|
||||
if (!pcdev->pdata ||
|
||||
pcdev->pdata->flags & OMAP1_CAMERA_LCLK_RISING)
|
||||
common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
|
||||
else
|
||||
common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
|
||||
}
|
||||
|
||||
cfg.flags = common_flags;
|
||||
ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
|
||||
if (ret < 0 && ret != -ENOIOCTLCMD) {
|
||||
dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n",
|
||||
common_flags, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
|
||||
if (ctrlclock & LCLK_EN)
|
||||
CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
|
||||
|
||||
if (common_flags & SOCAM_PCLK_SAMPLE_RISING) {
|
||||
if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) {
|
||||
dev_dbg(dev, "CTRLCLOCK_REG |= POLCLK\n");
|
||||
ctrlclock |= POLCLK;
|
||||
} else {
|
||||
@ -1565,10 +1579,10 @@ static int __init omap1_cam_probe(struct platform_device *pdev)
|
||||
pcdev->clk = clk;
|
||||
|
||||
pcdev->pdata = pdev->dev.platform_data;
|
||||
pcdev->pflags = pcdev->pdata->flags;
|
||||
|
||||
if (pcdev->pdata)
|
||||
if (pcdev->pdata) {
|
||||
pcdev->pflags = pcdev->pdata->flags;
|
||||
pcdev->camexclk = pcdev->pdata->camexclk_khz * 1000;
|
||||
}
|
||||
|
||||
switch (pcdev->camexclk) {
|
||||
case 6000000:
|
||||
@ -1578,6 +1592,7 @@ static int __init omap1_cam_probe(struct platform_device *pdev)
|
||||
case 24000000:
|
||||
break;
|
||||
default:
|
||||
/* pcdev->camexclk != 0 => pcdev->pdata != NULL */
|
||||
dev_warn(&pdev->dev,
|
||||
"Incorrect sensor clock frequency %ld kHz, "
|
||||
"should be one of 0, 6, 8, 9.6, 12 or 24 MHz, "
|
||||
@ -1585,8 +1600,7 @@ static int __init omap1_cam_probe(struct platform_device *pdev)
|
||||
pcdev->pdata->camexclk_khz);
|
||||
pcdev->camexclk = 0;
|
||||
case 0:
|
||||
dev_info(&pdev->dev,
|
||||
"Not providing sensor clock\n");
|
||||
dev_info(&pdev->dev, "Not providing sensor clock\n");
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&pcdev->capture);
|
||||
@ -1716,5 +1730,5 @@ MODULE_PARM_DESC(sg_mode, "videobuf mode, 0: dma-contig (default), 1: dma-sg");
|
||||
MODULE_DESCRIPTION("OMAP1 Camera Interface driver");
|
||||
MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_LICENSE(DRIVER_VERSION);
|
||||
MODULE_VERSION(DRIVER_VERSION);
|
||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
||||
|
@ -1704,6 +1704,7 @@ static int isp_register_entities(struct isp_device *isp)
|
||||
isp->media_dev.dev = isp->dev;
|
||||
strlcpy(isp->media_dev.model, "TI OMAP3 ISP",
|
||||
sizeof(isp->media_dev.model));
|
||||
isp->media_dev.hw_revision = isp->revision;
|
||||
isp->media_dev.link_notify = isp_pipeline_link_notify;
|
||||
ret = media_device_register(&isp->media_dev);
|
||||
if (ret < 0) {
|
||||
@ -2210,6 +2211,8 @@ error:
|
||||
regulator_put(isp->isp_csiphy2.vdd);
|
||||
regulator_put(isp->isp_csiphy1.vdd);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
mutex_destroy(&isp->isp_mutex);
|
||||
kfree(isp);
|
||||
|
||||
return ret;
|
||||
|
@ -1836,7 +1836,7 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
|
||||
* callers to request an output size bigger than the input size
|
||||
* up to the nearest multiple of 16.
|
||||
*/
|
||||
fmt->width = clamp_t(u32, width, 32, (fmt->width + 15) & ~15);
|
||||
fmt->width = clamp_t(u32, width, 32, fmt->width + 15);
|
||||
fmt->width &= ~15;
|
||||
fmt->height = clamp_t(u32, height, 32, fmt->height);
|
||||
break;
|
||||
@ -2152,6 +2152,37 @@ static const struct media_entity_operations ccdc_media_ops = {
|
||||
.link_setup = ccdc_link_setup,
|
||||
};
|
||||
|
||||
void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc)
|
||||
{
|
||||
v4l2_device_unregister_subdev(&ccdc->subdev);
|
||||
omap3isp_video_unregister(&ccdc->video_out);
|
||||
}
|
||||
|
||||
int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc,
|
||||
struct v4l2_device *vdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Register the subdev and video node. */
|
||||
ret = v4l2_device_register_subdev(vdev, &ccdc->subdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = omap3isp_video_register(&ccdc->video_out, vdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
omap3isp_ccdc_unregister_entities(ccdc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* ISP CCDC initialisation and cleanup
|
||||
*/
|
||||
|
||||
/*
|
||||
* ccdc_init_entities - Initialize V4L2 subdev and media entity
|
||||
* @ccdc: ISP CCDC module
|
||||
@ -2193,50 +2224,23 @@ static int ccdc_init_entities(struct isp_ccdc_device *ccdc)
|
||||
|
||||
ret = omap3isp_video_init(&ccdc->video_out, "CCDC");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto error_video;
|
||||
|
||||
/* Connect the CCDC subdev to the video node. */
|
||||
ret = media_entity_create_link(&ccdc->subdev.entity, CCDC_PAD_SOURCE_OF,
|
||||
&ccdc->video_out.video.entity, 0, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc)
|
||||
{
|
||||
media_entity_cleanup(&ccdc->subdev.entity);
|
||||
|
||||
v4l2_device_unregister_subdev(&ccdc->subdev);
|
||||
omap3isp_video_unregister(&ccdc->video_out);
|
||||
}
|
||||
|
||||
int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc,
|
||||
struct v4l2_device *vdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Register the subdev and video node. */
|
||||
ret = v4l2_device_register_subdev(vdev, &ccdc->subdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = omap3isp_video_register(&ccdc->video_out, vdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
goto error_link;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
omap3isp_ccdc_unregister_entities(ccdc);
|
||||
error_link:
|
||||
omap3isp_video_cleanup(&ccdc->video_out);
|
||||
error_video:
|
||||
media_entity_cleanup(me);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* ISP CCDC initialisation and cleanup
|
||||
*/
|
||||
|
||||
/*
|
||||
* omap3isp_ccdc_init - CCDC module initialization.
|
||||
* @dev: Device pointer specific to the OMAP3 ISP.
|
||||
@ -2248,6 +2252,7 @@ error:
|
||||
int omap3isp_ccdc_init(struct isp_device *isp)
|
||||
{
|
||||
struct isp_ccdc_device *ccdc = &isp->isp_ccdc;
|
||||
int ret;
|
||||
|
||||
spin_lock_init(&ccdc->lock);
|
||||
init_waitqueue_head(&ccdc->wait);
|
||||
@ -2276,7 +2281,13 @@ int omap3isp_ccdc_init(struct isp_device *isp)
|
||||
ccdc->update = OMAP3ISP_CCDC_BLCLAMP;
|
||||
ccdc_apply_controls(ccdc);
|
||||
|
||||
return ccdc_init_entities(ccdc);
|
||||
ret = ccdc_init_entities(ccdc);
|
||||
if (ret < 0) {
|
||||
mutex_destroy(&ccdc->ioctl_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2287,6 +2298,9 @@ void omap3isp_ccdc_cleanup(struct isp_device *isp)
|
||||
{
|
||||
struct isp_ccdc_device *ccdc = &isp->isp_ccdc;
|
||||
|
||||
omap3isp_video_cleanup(&ccdc->video_out);
|
||||
media_entity_cleanup(&ccdc->subdev.entity);
|
||||
|
||||
/* Free LSC requests. As the CCDC is stopped there's no active request,
|
||||
* so only the pending request and the free queue need to be handled.
|
||||
*/
|
||||
@ -2296,4 +2310,6 @@ void omap3isp_ccdc_cleanup(struct isp_device *isp)
|
||||
|
||||
if (ccdc->fpc.fpcaddr != 0)
|
||||
omap_iommu_vfree(isp->domain, isp->iommu, ccdc->fpc.fpcaddr);
|
||||
|
||||
mutex_destroy(&ccdc->ioctl_lock);
|
||||
}
|
||||
|
@ -1031,6 +1031,48 @@ static const struct media_entity_operations ccp2_media_ops = {
|
||||
.link_setup = ccp2_link_setup,
|
||||
};
|
||||
|
||||
/*
|
||||
* omap3isp_ccp2_unregister_entities - Unregister media entities: subdev
|
||||
* @ccp2: Pointer to ISP CCP2 device
|
||||
*/
|
||||
void omap3isp_ccp2_unregister_entities(struct isp_ccp2_device *ccp2)
|
||||
{
|
||||
v4l2_device_unregister_subdev(&ccp2->subdev);
|
||||
omap3isp_video_unregister(&ccp2->video_in);
|
||||
}
|
||||
|
||||
/*
|
||||
* omap3isp_ccp2_register_entities - Register the subdev media entity
|
||||
* @ccp2: Pointer to ISP CCP2 device
|
||||
* @vdev: Pointer to v4l device
|
||||
* return negative error code or zero on success
|
||||
*/
|
||||
|
||||
int omap3isp_ccp2_register_entities(struct isp_ccp2_device *ccp2,
|
||||
struct v4l2_device *vdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Register the subdev and video nodes. */
|
||||
ret = v4l2_device_register_subdev(vdev, &ccp2->subdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = omap3isp_video_register(&ccp2->video_in, vdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
omap3isp_ccp2_unregister_entities(ccp2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* ISP ccp2 initialisation and cleanup
|
||||
*/
|
||||
|
||||
/*
|
||||
* ccp2_init_entities - Initialize ccp2 subdev and media entity.
|
||||
* @ccp2: Pointer to ISP CCP2 device
|
||||
@ -1083,72 +1125,23 @@ static int ccp2_init_entities(struct isp_ccp2_device *ccp2)
|
||||
|
||||
ret = omap3isp_video_init(&ccp2->video_in, "CCP2");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto error_video;
|
||||
|
||||
/* Connect the video node to the ccp2 subdev. */
|
||||
ret = media_entity_create_link(&ccp2->video_in.video.entity, 0,
|
||||
&ccp2->subdev.entity, CCP2_PAD_SINK, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto error_link;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* omap3isp_ccp2_unregister_entities - Unregister media entities: subdev
|
||||
* @ccp2: Pointer to ISP CCP2 device
|
||||
*/
|
||||
void omap3isp_ccp2_unregister_entities(struct isp_ccp2_device *ccp2)
|
||||
{
|
||||
error_link:
|
||||
omap3isp_video_cleanup(&ccp2->video_in);
|
||||
error_video:
|
||||
media_entity_cleanup(&ccp2->subdev.entity);
|
||||
|
||||
v4l2_device_unregister_subdev(&ccp2->subdev);
|
||||
omap3isp_video_unregister(&ccp2->video_in);
|
||||
}
|
||||
|
||||
/*
|
||||
* omap3isp_ccp2_register_entities - Register the subdev media entity
|
||||
* @ccp2: Pointer to ISP CCP2 device
|
||||
* @vdev: Pointer to v4l device
|
||||
* return negative error code or zero on success
|
||||
*/
|
||||
|
||||
int omap3isp_ccp2_register_entities(struct isp_ccp2_device *ccp2,
|
||||
struct v4l2_device *vdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Register the subdev and video nodes. */
|
||||
ret = v4l2_device_register_subdev(vdev, &ccp2->subdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = omap3isp_video_register(&ccp2->video_in, vdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
omap3isp_ccp2_unregister_entities(ccp2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* ISP ccp2 initialisation and cleanup
|
||||
*/
|
||||
|
||||
/*
|
||||
* omap3isp_ccp2_cleanup - CCP2 un-initialization
|
||||
* @isp : Pointer to ISP device
|
||||
*/
|
||||
void omap3isp_ccp2_cleanup(struct isp_device *isp)
|
||||
{
|
||||
struct isp_ccp2_device *ccp2 = &isp->isp_ccp2;
|
||||
|
||||
regulator_put(ccp2->vdds_csib);
|
||||
}
|
||||
|
||||
/*
|
||||
* omap3isp_ccp2_init - CCP2 initialization.
|
||||
* @isp : Pointer to ISP device
|
||||
@ -1184,13 +1177,25 @@ int omap3isp_ccp2_init(struct isp_device *isp)
|
||||
}
|
||||
|
||||
ret = ccp2_init_entities(ccp2);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
if (ret < 0) {
|
||||
regulator_put(ccp2->vdds_csib);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ccp2_reset(ccp2);
|
||||
out:
|
||||
if (ret)
|
||||
omap3isp_ccp2_cleanup(isp);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* omap3isp_ccp2_cleanup - CCP2 un-initialization
|
||||
* @isp : Pointer to ISP device
|
||||
*/
|
||||
void omap3isp_ccp2_cleanup(struct isp_device *isp)
|
||||
{
|
||||
struct isp_ccp2_device *ccp2 = &isp->isp_ccp2;
|
||||
|
||||
omap3isp_video_cleanup(&ccp2->video_in);
|
||||
media_entity_cleanup(&ccp2->subdev.entity);
|
||||
|
||||
regulator_put(ccp2->vdds_csib);
|
||||
}
|
||||
|
@ -1187,6 +1187,37 @@ static const struct media_entity_operations csi2_media_ops = {
|
||||
.link_setup = csi2_link_setup,
|
||||
};
|
||||
|
||||
void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2)
|
||||
{
|
||||
v4l2_device_unregister_subdev(&csi2->subdev);
|
||||
omap3isp_video_unregister(&csi2->video_out);
|
||||
}
|
||||
|
||||
int omap3isp_csi2_register_entities(struct isp_csi2_device *csi2,
|
||||
struct v4l2_device *vdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Register the subdev and video nodes. */
|
||||
ret = v4l2_device_register_subdev(vdev, &csi2->subdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = omap3isp_video_register(&csi2->video_out, vdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
omap3isp_csi2_unregister_entities(csi2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* ISP CSI2 initialisation and cleanup
|
||||
*/
|
||||
|
||||
/*
|
||||
* csi2_init_entities - Initialize subdev and media entity.
|
||||
* @csi2: Pointer to csi2 structure.
|
||||
@ -1228,57 +1259,23 @@ static int csi2_init_entities(struct isp_csi2_device *csi2)
|
||||
|
||||
ret = omap3isp_video_init(&csi2->video_out, "CSI2a");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto error_video;
|
||||
|
||||
/* Connect the CSI2 subdev to the video node. */
|
||||
ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE,
|
||||
&csi2->video_out.video.entity, 0, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto error_link;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2)
|
||||
{
|
||||
error_link:
|
||||
omap3isp_video_cleanup(&csi2->video_out);
|
||||
error_video:
|
||||
media_entity_cleanup(&csi2->subdev.entity);
|
||||
|
||||
v4l2_device_unregister_subdev(&csi2->subdev);
|
||||
omap3isp_video_unregister(&csi2->video_out);
|
||||
}
|
||||
|
||||
int omap3isp_csi2_register_entities(struct isp_csi2_device *csi2,
|
||||
struct v4l2_device *vdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Register the subdev and video nodes. */
|
||||
ret = v4l2_device_register_subdev(vdev, &csi2->subdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = omap3isp_video_register(&csi2->video_out, vdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
omap3isp_csi2_unregister_entities(csi2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* ISP CSI2 initialisation and cleanup
|
||||
*/
|
||||
|
||||
/*
|
||||
* omap3isp_csi2_cleanup - Routine for module driver cleanup
|
||||
*/
|
||||
void omap3isp_csi2_cleanup(struct isp_device *isp)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* omap3isp_csi2_init - Routine for module driver init
|
||||
*/
|
||||
@ -1298,7 +1295,7 @@ int omap3isp_csi2_init(struct isp_device *isp)
|
||||
|
||||
ret = csi2_init_entities(csi2a);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
return ret;
|
||||
|
||||
if (isp->revision == ISP_REVISION_15_0) {
|
||||
csi2c->isp = isp;
|
||||
@ -1311,7 +1308,15 @@ int omap3isp_csi2_init(struct isp_device *isp)
|
||||
}
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
omap3isp_csi2_cleanup(isp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* omap3isp_csi2_cleanup - Routine for module driver cleanup
|
||||
*/
|
||||
void omap3isp_csi2_cleanup(struct isp_device *isp)
|
||||
{
|
||||
struct isp_csi2_device *csi2a = &isp->isp_csi2a;
|
||||
|
||||
omap3isp_video_cleanup(&csi2a->video_out);
|
||||
media_entity_cleanup(&csi2a->subdev.entity);
|
||||
}
|
||||
|
@ -370,5 +370,5 @@ void omap3isp_h3a_aewb_cleanup(struct isp_device *isp)
|
||||
{
|
||||
kfree(isp->isp_aewb.priv);
|
||||
kfree(isp->isp_aewb.recover_priv);
|
||||
omap3isp_stat_free(&isp->isp_aewb);
|
||||
omap3isp_stat_cleanup(&isp->isp_aewb);
|
||||
}
|
||||
|
@ -425,5 +425,5 @@ void omap3isp_h3a_af_cleanup(struct isp_device *isp)
|
||||
{
|
||||
kfree(isp->isp_af.priv);
|
||||
kfree(isp->isp_af.recover_priv);
|
||||
omap3isp_stat_free(&isp->isp_af);
|
||||
omap3isp_stat_cleanup(&isp->isp_af);
|
||||
}
|
||||
|
@ -516,5 +516,5 @@ void omap3isp_hist_cleanup(struct isp_device *isp)
|
||||
if (HIST_USING_DMA(&isp->isp_hist))
|
||||
omap_free_dma(isp->isp_hist.dma_ch);
|
||||
kfree(isp->isp_hist.priv);
|
||||
omap3isp_stat_free(&isp->isp_hist);
|
||||
omap3isp_stat_cleanup(&isp->isp_hist);
|
||||
}
|
||||
|
@ -76,9 +76,51 @@ static struct omap3isp_prev_csc flr_prev_csc = {
|
||||
|
||||
#define DEF_DETECT_CORRECT_VAL 0xe
|
||||
|
||||
#define PREV_MIN_WIDTH 64
|
||||
#define PREV_MIN_HEIGHT 8
|
||||
#define PREV_MAX_HEIGHT 16384
|
||||
/*
|
||||
* Margins and image size limits.
|
||||
*
|
||||
* The preview engine crops several rows and columns internally depending on
|
||||
* which filters are enabled. To avoid format changes when the filters are
|
||||
* enabled or disabled (which would prevent them from being turned on or off
|
||||
* during streaming), the driver assumes all the filters are enabled when
|
||||
* computing sink crop and source format limits.
|
||||
*
|
||||
* If a filter is disabled, additional cropping is automatically added at the
|
||||
* preview engine input by the driver to avoid overflow at line and frame end.
|
||||
* This is completely transparent for applications.
|
||||
*
|
||||
* Median filter 4 pixels
|
||||
* Noise filter,
|
||||
* Faulty pixels correction 4 pixels, 4 lines
|
||||
* CFA filter 4 pixels, 4 lines in Bayer mode
|
||||
* 2 lines in other modes
|
||||
* Color suppression 2 pixels
|
||||
* or luma enhancement
|
||||
* -------------------------------------------------------------
|
||||
* Maximum total 14 pixels, 8 lines
|
||||
*
|
||||
* The color suppression and luma enhancement filters are applied after bayer to
|
||||
* YUV conversion. They thus can crop one pixel on the left and one pixel on the
|
||||
* right side of the image without changing the color pattern. When both those
|
||||
* filters are disabled, the driver must crop the two pixels on the same side of
|
||||
* the image to avoid changing the bayer pattern. The left margin is thus set to
|
||||
* 8 pixels and the right margin to 6 pixels.
|
||||
*/
|
||||
|
||||
#define PREV_MARGIN_LEFT 8
|
||||
#define PREV_MARGIN_RIGHT 6
|
||||
#define PREV_MARGIN_TOP 4
|
||||
#define PREV_MARGIN_BOTTOM 4
|
||||
|
||||
#define PREV_MIN_IN_WIDTH 64
|
||||
#define PREV_MIN_IN_HEIGHT 8
|
||||
#define PREV_MAX_IN_HEIGHT 16384
|
||||
|
||||
#define PREV_MIN_OUT_WIDTH 0
|
||||
#define PREV_MIN_OUT_HEIGHT 0
|
||||
#define PREV_MAX_OUT_WIDTH 1280
|
||||
#define PREV_MAX_OUT_WIDTH_ES2 3300
|
||||
#define PREV_MAX_OUT_WIDTH_3630 4096
|
||||
|
||||
/*
|
||||
* Coeficient Tables for the submodules in Preview.
|
||||
@ -979,52 +1021,36 @@ static void preview_config_averager(struct isp_prev_device *prev, u8 average)
|
||||
* enabled when reporting source pad formats to userspace. If this assumption is
|
||||
* not true, rows and columns must be manually cropped at the preview engine
|
||||
* input to avoid overflows at the end of lines and frames.
|
||||
*
|
||||
* See the explanation at the PREV_MARGIN_* definitions for more details.
|
||||
*/
|
||||
static void preview_config_input_size(struct isp_prev_device *prev)
|
||||
{
|
||||
struct isp_device *isp = to_isp_device(prev);
|
||||
struct prev_params *params = &prev->params;
|
||||
struct v4l2_mbus_framefmt *format = &prev->formats[PREV_PAD_SINK];
|
||||
unsigned int sph = 0;
|
||||
unsigned int eph = format->width - 1;
|
||||
unsigned int slv = 0;
|
||||
unsigned int elv = format->height - 1;
|
||||
unsigned int sph = prev->crop.left;
|
||||
unsigned int eph = prev->crop.left + prev->crop.width - 1;
|
||||
unsigned int slv = prev->crop.top;
|
||||
unsigned int elv = prev->crop.top + prev->crop.height - 1;
|
||||
|
||||
if (prev->input == PREVIEW_INPUT_CCDC) {
|
||||
sph += 2;
|
||||
eph -= 2;
|
||||
if (params->features & PREV_CFA) {
|
||||
sph -= 2;
|
||||
eph += 2;
|
||||
slv -= 2;
|
||||
elv += 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Median filter 4 pixels
|
||||
* Noise filter 4 pixels, 4 lines
|
||||
* or faulty pixels correction
|
||||
* CFA filter 4 pixels, 4 lines in Bayer mode
|
||||
* 2 lines in other modes
|
||||
* Color suppression 2 pixels
|
||||
* or luma enhancement
|
||||
* -------------------------------------------------------------
|
||||
* Maximum total 14 pixels, 8 lines
|
||||
*/
|
||||
|
||||
if (!(params->features & PREV_CFA)) {
|
||||
sph += 2;
|
||||
eph -= 2;
|
||||
slv += 2;
|
||||
elv -= 2;
|
||||
if (params->features & (PREV_DEFECT_COR | PREV_NOISE_FILTER)) {
|
||||
sph -= 2;
|
||||
eph += 2;
|
||||
slv -= 2;
|
||||
elv += 2;
|
||||
}
|
||||
if (!(params->features & (PREV_DEFECT_COR | PREV_NOISE_FILTER))) {
|
||||
sph += 2;
|
||||
eph -= 2;
|
||||
slv += 2;
|
||||
elv -= 2;
|
||||
if (params->features & PREV_HORZ_MEDIAN_FILTER) {
|
||||
sph -= 2;
|
||||
eph += 2;
|
||||
}
|
||||
if (!(params->features & PREV_HORZ_MEDIAN_FILTER)) {
|
||||
sph += 2;
|
||||
eph -= 2;
|
||||
}
|
||||
if (!(params->features & (PREV_CHROMA_SUPPRESS | PREV_LUMA_ENHANCE)))
|
||||
sph += 2;
|
||||
if (params->features & (PREV_CHROMA_SUPPRESS | PREV_LUMA_ENHANCE))
|
||||
sph -= 2;
|
||||
|
||||
isp_reg_writel(isp, (sph << ISPPRV_HORZ_INFO_SPH_SHIFT) | eph,
|
||||
OMAP3_ISP_IOMEM_PREV, ISPPRV_HORZ_INFO);
|
||||
@ -1228,7 +1254,6 @@ static void preview_init_params(struct isp_prev_device *prev)
|
||||
/* Init values */
|
||||
params->contrast = ISPPRV_CONTRAST_DEF * ISPPRV_CONTRAST_UNITS;
|
||||
params->brightness = ISPPRV_BRIGHT_DEF * ISPPRV_BRIGHT_UNITS;
|
||||
params->average = NO_AVE;
|
||||
params->cfa.format = OMAP3ISP_CFAFMT_BAYER;
|
||||
memcpy(params->cfa.table, cfa_coef_table,
|
||||
sizeof(params->cfa.table));
|
||||
@ -1281,14 +1306,14 @@ static unsigned int preview_max_out_width(struct isp_prev_device *prev)
|
||||
|
||||
switch (isp->revision) {
|
||||
case ISP_REVISION_1_0:
|
||||
return ISPPRV_MAXOUTPUT_WIDTH;
|
||||
return PREV_MAX_OUT_WIDTH;
|
||||
|
||||
case ISP_REVISION_2_0:
|
||||
default:
|
||||
return ISPPRV_MAXOUTPUT_WIDTH_ES2;
|
||||
return PREV_MAX_OUT_WIDTH_ES2;
|
||||
|
||||
case ISP_REVISION_15_0:
|
||||
return ISPPRV_MAXOUTPUT_WIDTH_3630;
|
||||
return PREV_MAX_OUT_WIDTH_3630;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1296,8 +1321,6 @@ static void preview_configure(struct isp_prev_device *prev)
|
||||
{
|
||||
struct isp_device *isp = to_isp_device(prev);
|
||||
struct v4l2_mbus_framefmt *format;
|
||||
unsigned int max_out_width;
|
||||
unsigned int format_avg;
|
||||
|
||||
preview_setup_hw(prev);
|
||||
|
||||
@ -1335,10 +1358,7 @@ static void preview_configure(struct isp_prev_device *prev)
|
||||
preview_config_outlineoffset(prev,
|
||||
ALIGN(format->width, 0x10) * 2);
|
||||
|
||||
max_out_width = preview_max_out_width(prev);
|
||||
|
||||
format_avg = fls(DIV_ROUND_UP(format->width, max_out_width) - 1);
|
||||
preview_config_averager(prev, format_avg);
|
||||
preview_config_averager(prev, 0);
|
||||
preview_config_ycpos(prev, format->code);
|
||||
}
|
||||
|
||||
@ -1597,6 +1617,16 @@ __preview_get_format(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh,
|
||||
return &prev->formats[pad];
|
||||
}
|
||||
|
||||
static struct v4l2_rect *
|
||||
__preview_get_crop(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh,
|
||||
enum v4l2_subdev_format_whence which)
|
||||
{
|
||||
if (which == V4L2_SUBDEV_FORMAT_TRY)
|
||||
return v4l2_subdev_get_try_crop(fh, PREV_PAD_SINK);
|
||||
else
|
||||
return &prev->crop;
|
||||
}
|
||||
|
||||
/* previewer format descriptions */
|
||||
static const unsigned int preview_input_fmts[] = {
|
||||
V4L2_MBUS_FMT_SGRBG10_1X10,
|
||||
@ -1611,24 +1641,25 @@ static const unsigned int preview_output_fmts[] = {
|
||||
};
|
||||
|
||||
/*
|
||||
* preview_try_format - Handle try format by pad subdev method
|
||||
* @prev: ISP preview device
|
||||
* @fh : V4L2 subdev file handle
|
||||
* @pad: pad num
|
||||
* @fmt: pointer to v4l2 format structure
|
||||
* preview_try_format - Validate a format
|
||||
* @prev: ISP preview engine
|
||||
* @fh: V4L2 subdev file handle
|
||||
* @pad: pad number
|
||||
* @fmt: format to be validated
|
||||
* @which: try/active format selector
|
||||
*
|
||||
* Validate and adjust the given format for the given pad based on the preview
|
||||
* engine limits and the format and crop rectangles on other pads.
|
||||
*/
|
||||
static void preview_try_format(struct isp_prev_device *prev,
|
||||
struct v4l2_subdev_fh *fh, unsigned int pad,
|
||||
struct v4l2_mbus_framefmt *fmt,
|
||||
enum v4l2_subdev_format_whence which)
|
||||
{
|
||||
struct v4l2_mbus_framefmt *format;
|
||||
unsigned int max_out_width;
|
||||
enum v4l2_mbus_pixelcode pixelcode;
|
||||
struct v4l2_rect *crop;
|
||||
unsigned int i;
|
||||
|
||||
max_out_width = preview_max_out_width(prev);
|
||||
|
||||
switch (pad) {
|
||||
case PREV_PAD_SINK:
|
||||
/* When reading data from the CCDC, the input size has already
|
||||
@ -1641,10 +1672,11 @@ static void preview_try_format(struct isp_prev_device *prev,
|
||||
* filter array interpolation.
|
||||
*/
|
||||
if (prev->input == PREVIEW_INPUT_MEMORY) {
|
||||
fmt->width = clamp_t(u32, fmt->width, PREV_MIN_WIDTH,
|
||||
max_out_width * 8);
|
||||
fmt->height = clamp_t(u32, fmt->height, PREV_MIN_HEIGHT,
|
||||
PREV_MAX_HEIGHT);
|
||||
fmt->width = clamp_t(u32, fmt->width, PREV_MIN_IN_WIDTH,
|
||||
preview_max_out_width(prev));
|
||||
fmt->height = clamp_t(u32, fmt->height,
|
||||
PREV_MIN_IN_HEIGHT,
|
||||
PREV_MAX_IN_HEIGHT);
|
||||
}
|
||||
|
||||
fmt->colorspace = V4L2_COLORSPACE_SRGB;
|
||||
@ -1661,15 +1693,8 @@ static void preview_try_format(struct isp_prev_device *prev,
|
||||
|
||||
case PREV_PAD_SOURCE:
|
||||
pixelcode = fmt->code;
|
||||
format = __preview_get_format(prev, fh, PREV_PAD_SINK, which);
|
||||
memcpy(fmt, format, sizeof(*fmt));
|
||||
*fmt = *__preview_get_format(prev, fh, PREV_PAD_SINK, which);
|
||||
|
||||
/* The preview module output size is configurable through the
|
||||
* input interface (horizontal and vertical cropping) and the
|
||||
* averager (horizontal scaling by 1/1, 1/2, 1/4 or 1/8). In
|
||||
* spite of this, hardcode the output size to the biggest
|
||||
* possible value for simplicity reasons.
|
||||
*/
|
||||
switch (pixelcode) {
|
||||
case V4L2_MBUS_FMT_YUYV8_1X16:
|
||||
case V4L2_MBUS_FMT_UYVY8_1X16:
|
||||
@ -1681,31 +1706,14 @@ static void preview_try_format(struct isp_prev_device *prev,
|
||||
break;
|
||||
}
|
||||
|
||||
/* The TRM states (12.1.4.7.1.2) that 2 pixels must be cropped
|
||||
* from the left and right sides when the input source is the
|
||||
* CCDC. This seems not to be needed in practice, investigation
|
||||
* is required.
|
||||
/* The preview module output size is configurable through the
|
||||
* averager (horizontal scaling by 1/1, 1/2, 1/4 or 1/8). This
|
||||
* is not supported yet, hardcode the output size to the crop
|
||||
* rectangle size.
|
||||
*/
|
||||
if (prev->input == PREVIEW_INPUT_CCDC)
|
||||
fmt->width -= 4;
|
||||
|
||||
/* The preview module can output a maximum of 3312 pixels
|
||||
* horizontally due to fixed memory-line sizes. Compute the
|
||||
* horizontal averaging factor accordingly. Note that the limit
|
||||
* applies to the noise filter and CFA interpolation blocks, so
|
||||
* it doesn't take cropping by further blocks into account.
|
||||
*
|
||||
* ES 1.0 hardware revision is limited to 1280 pixels
|
||||
* horizontally.
|
||||
*/
|
||||
fmt->width >>= fls(DIV_ROUND_UP(fmt->width, max_out_width) - 1);
|
||||
|
||||
/* Assume that all blocks are enabled and crop pixels and lines
|
||||
* accordingly. See preview_config_input_size() for more
|
||||
* information.
|
||||
*/
|
||||
fmt->width -= 14;
|
||||
fmt->height -= 8;
|
||||
crop = __preview_get_crop(prev, fh, which);
|
||||
fmt->width = crop->width;
|
||||
fmt->height = crop->height;
|
||||
|
||||
fmt->colorspace = V4L2_COLORSPACE_JPEG;
|
||||
break;
|
||||
@ -1714,6 +1722,49 @@ static void preview_try_format(struct isp_prev_device *prev,
|
||||
fmt->field = V4L2_FIELD_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* preview_try_crop - Validate a crop rectangle
|
||||
* @prev: ISP preview engine
|
||||
* @sink: format on the sink pad
|
||||
* @crop: crop rectangle to be validated
|
||||
*
|
||||
* The preview engine crops lines and columns for its internal operation,
|
||||
* depending on which filters are enabled. Enforce minimum crop margins to
|
||||
* handle that transparently for userspace.
|
||||
*
|
||||
* See the explanation at the PREV_MARGIN_* definitions for more details.
|
||||
*/
|
||||
static void preview_try_crop(struct isp_prev_device *prev,
|
||||
const struct v4l2_mbus_framefmt *sink,
|
||||
struct v4l2_rect *crop)
|
||||
{
|
||||
unsigned int left = PREV_MARGIN_LEFT;
|
||||
unsigned int right = sink->width - PREV_MARGIN_RIGHT;
|
||||
unsigned int top = PREV_MARGIN_TOP;
|
||||
unsigned int bottom = sink->height - PREV_MARGIN_BOTTOM;
|
||||
|
||||
/* When processing data on-the-fly from the CCDC, at least 2 pixels must
|
||||
* be cropped from the left and right sides of the image. As we don't
|
||||
* know which filters will be enabled, increase the left and right
|
||||
* margins by two.
|
||||
*/
|
||||
if (prev->input == PREVIEW_INPUT_CCDC) {
|
||||
left += 2;
|
||||
right -= 2;
|
||||
}
|
||||
|
||||
/* Restrict left/top to even values to keep the Bayer pattern. */
|
||||
crop->left &= ~1;
|
||||
crop->top &= ~1;
|
||||
|
||||
crop->left = clamp_t(u32, crop->left, left, right - PREV_MIN_OUT_WIDTH);
|
||||
crop->top = clamp_t(u32, crop->top, top, bottom - PREV_MIN_OUT_HEIGHT);
|
||||
crop->width = clamp_t(u32, crop->width, PREV_MIN_OUT_WIDTH,
|
||||
right - crop->left);
|
||||
crop->height = clamp_t(u32, crop->height, PREV_MIN_OUT_HEIGHT,
|
||||
bottom - crop->top);
|
||||
}
|
||||
|
||||
/*
|
||||
* preview_enum_mbus_code - Handle pixel format enumeration
|
||||
* @sd : pointer to v4l2 subdev structure
|
||||
@ -1775,6 +1826,60 @@ static int preview_enum_frame_size(struct v4l2_subdev *sd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* preview_get_crop - Retrieve the crop rectangle on a pad
|
||||
* @sd: ISP preview V4L2 subdevice
|
||||
* @fh: V4L2 subdev file handle
|
||||
* @crop: crop rectangle
|
||||
*
|
||||
* Return 0 on success or a negative error code otherwise.
|
||||
*/
|
||||
static int preview_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
|
||||
struct v4l2_subdev_crop *crop)
|
||||
{
|
||||
struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
|
||||
|
||||
/* Cropping is only supported on the sink pad. */
|
||||
if (crop->pad != PREV_PAD_SINK)
|
||||
return -EINVAL;
|
||||
|
||||
crop->rect = *__preview_get_crop(prev, fh, crop->which);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* preview_set_crop - Retrieve the crop rectangle on a pad
|
||||
* @sd: ISP preview V4L2 subdevice
|
||||
* @fh: V4L2 subdev file handle
|
||||
* @crop: crop rectangle
|
||||
*
|
||||
* Return 0 on success or a negative error code otherwise.
|
||||
*/
|
||||
static int preview_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
|
||||
struct v4l2_subdev_crop *crop)
|
||||
{
|
||||
struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
|
||||
struct v4l2_mbus_framefmt *format;
|
||||
|
||||
/* Cropping is only supported on the sink pad. */
|
||||
if (crop->pad != PREV_PAD_SINK)
|
||||
return -EINVAL;
|
||||
|
||||
/* The crop rectangle can't be changed while streaming. */
|
||||
if (prev->state != ISP_PIPELINE_STREAM_STOPPED)
|
||||
return -EBUSY;
|
||||
|
||||
format = __preview_get_format(prev, fh, PREV_PAD_SINK, crop->which);
|
||||
preview_try_crop(prev, format, &crop->rect);
|
||||
*__preview_get_crop(prev, fh, crop->which) = crop->rect;
|
||||
|
||||
/* Update the source format. */
|
||||
format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, crop->which);
|
||||
preview_try_format(prev, fh, PREV_PAD_SOURCE, format, crop->which);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* preview_get_format - Handle get format by pads subdev method
|
||||
* @sd : pointer to v4l2 subdev structure
|
||||
@ -1808,6 +1913,7 @@ static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
|
||||
{
|
||||
struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
|
||||
struct v4l2_mbus_framefmt *format;
|
||||
struct v4l2_rect *crop;
|
||||
|
||||
format = __preview_get_format(prev, fh, fmt->pad, fmt->which);
|
||||
if (format == NULL)
|
||||
@ -1818,9 +1924,18 @@ static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
|
||||
|
||||
/* Propagate the format from sink to source */
|
||||
if (fmt->pad == PREV_PAD_SINK) {
|
||||
/* Reset the crop rectangle. */
|
||||
crop = __preview_get_crop(prev, fh, fmt->which);
|
||||
crop->left = 0;
|
||||
crop->top = 0;
|
||||
crop->width = fmt->format.width;
|
||||
crop->height = fmt->format.height;
|
||||
|
||||
preview_try_crop(prev, &fmt->format, crop);
|
||||
|
||||
/* Update the source format. */
|
||||
format = __preview_get_format(prev, fh, PREV_PAD_SOURCE,
|
||||
fmt->which);
|
||||
*format = fmt->format;
|
||||
preview_try_format(prev, fh, PREV_PAD_SOURCE, format,
|
||||
fmt->which);
|
||||
}
|
||||
@ -1869,6 +1984,8 @@ static const struct v4l2_subdev_pad_ops preview_v4l2_pad_ops = {
|
||||
.enum_frame_size = preview_enum_frame_size,
|
||||
.get_fmt = preview_get_format,
|
||||
.set_fmt = preview_set_format,
|
||||
.get_crop = preview_get_crop,
|
||||
.set_crop = preview_set_crop,
|
||||
};
|
||||
|
||||
/* subdev operations */
|
||||
@ -1966,8 +2083,44 @@ static const struct media_entity_operations preview_media_ops = {
|
||||
.link_setup = preview_link_setup,
|
||||
};
|
||||
|
||||
void omap3isp_preview_unregister_entities(struct isp_prev_device *prev)
|
||||
{
|
||||
v4l2_device_unregister_subdev(&prev->subdev);
|
||||
omap3isp_video_unregister(&prev->video_in);
|
||||
omap3isp_video_unregister(&prev->video_out);
|
||||
}
|
||||
|
||||
int omap3isp_preview_register_entities(struct isp_prev_device *prev,
|
||||
struct v4l2_device *vdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Register the subdev and video nodes. */
|
||||
ret = v4l2_device_register_subdev(vdev, &prev->subdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = omap3isp_video_register(&prev->video_in, vdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = omap3isp_video_register(&prev->video_out, vdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
omap3isp_preview_unregister_entities(prev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* ISP previewer initialisation and cleanup
|
||||
*/
|
||||
|
||||
/*
|
||||
* review_init_entities - Initialize subdev and media entity.
|
||||
* preview_init_entities - Initialize subdev and media entity.
|
||||
* @prev : Pointer to preview structure
|
||||
* return -ENOMEM or zero on success
|
||||
*/
|
||||
@ -2024,69 +2177,34 @@ static int preview_init_entities(struct isp_prev_device *prev)
|
||||
|
||||
ret = omap3isp_video_init(&prev->video_in, "preview");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto error_video_in;
|
||||
|
||||
ret = omap3isp_video_init(&prev->video_out, "preview");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto error_video_out;
|
||||
|
||||
/* Connect the video nodes to the previewer subdev. */
|
||||
ret = media_entity_create_link(&prev->video_in.video.entity, 0,
|
||||
&prev->subdev.entity, PREV_PAD_SINK, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto error_link;
|
||||
|
||||
ret = media_entity_create_link(&prev->subdev.entity, PREV_PAD_SOURCE,
|
||||
&prev->video_out.video.entity, 0, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto error_link;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void omap3isp_preview_unregister_entities(struct isp_prev_device *prev)
|
||||
{
|
||||
error_link:
|
||||
omap3isp_video_cleanup(&prev->video_out);
|
||||
error_video_out:
|
||||
omap3isp_video_cleanup(&prev->video_in);
|
||||
error_video_in:
|
||||
media_entity_cleanup(&prev->subdev.entity);
|
||||
|
||||
v4l2_device_unregister_subdev(&prev->subdev);
|
||||
v4l2_ctrl_handler_free(&prev->ctrls);
|
||||
omap3isp_video_unregister(&prev->video_in);
|
||||
omap3isp_video_unregister(&prev->video_out);
|
||||
}
|
||||
|
||||
int omap3isp_preview_register_entities(struct isp_prev_device *prev,
|
||||
struct v4l2_device *vdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Register the subdev and video nodes. */
|
||||
ret = v4l2_device_register_subdev(vdev, &prev->subdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = omap3isp_video_register(&prev->video_in, vdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = omap3isp_video_register(&prev->video_out, vdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
omap3isp_preview_unregister_entities(prev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* ISP previewer initialisation and cleanup
|
||||
*/
|
||||
|
||||
void omap3isp_preview_cleanup(struct isp_device *isp)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* isp_preview_init - Previewer initialization.
|
||||
* @dev : Pointer to ISP device
|
||||
@ -2095,19 +2213,20 @@ void omap3isp_preview_cleanup(struct isp_device *isp)
|
||||
int omap3isp_preview_init(struct isp_device *isp)
|
||||
{
|
||||
struct isp_prev_device *prev = &isp->isp_prev;
|
||||
int ret;
|
||||
|
||||
spin_lock_init(&prev->lock);
|
||||
init_waitqueue_head(&prev->wait);
|
||||
preview_init_params(prev);
|
||||
|
||||
ret = preview_init_entities(prev);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
if (ret)
|
||||
omap3isp_preview_cleanup(isp);
|
||||
|
||||
return ret;
|
||||
return preview_init_entities(prev);
|
||||
}
|
||||
|
||||
void omap3isp_preview_cleanup(struct isp_device *isp)
|
||||
{
|
||||
struct isp_prev_device *prev = &isp->isp_prev;
|
||||
|
||||
v4l2_ctrl_handler_free(&prev->ctrls);
|
||||
omap3isp_video_cleanup(&prev->video_in);
|
||||
omap3isp_video_cleanup(&prev->video_out);
|
||||
media_entity_cleanup(&prev->subdev.entity);
|
||||
}
|
||||
|
@ -45,11 +45,6 @@
|
||||
#define ISPPRV_CONTRAST_HIGH 0xFF
|
||||
#define ISPPRV_CONTRAST_UNITS 0x1
|
||||
|
||||
#define NO_AVE 0x0
|
||||
#define AVE_2_PIX 0x1
|
||||
#define AVE_4_PIX 0x2
|
||||
#define AVE_8_PIX 0x3
|
||||
|
||||
/* Features list */
|
||||
#define PREV_LUMA_ENHANCE OMAP3ISP_PREV_LUMAENH
|
||||
#define PREV_INVERSE_ALAW OMAP3ISP_PREV_INVALAW
|
||||
@ -106,7 +101,6 @@ enum preview_ycpos_mode {
|
||||
* @rgb2ycbcr: RGB to ycbcr parameters.
|
||||
* @hmed: Horizontal median filter.
|
||||
* @yclimit: YC limits parameters.
|
||||
* @average: Downsampling rate for averager.
|
||||
* @contrast: Contrast.
|
||||
* @brightness: Brightness.
|
||||
*/
|
||||
@ -124,7 +118,6 @@ struct prev_params {
|
||||
struct omap3isp_prev_csc rgb2ycbcr;
|
||||
struct omap3isp_prev_hmed hmed;
|
||||
struct omap3isp_prev_yclimit yclimit;
|
||||
u8 average;
|
||||
u8 contrast;
|
||||
u8 brightness;
|
||||
};
|
||||
@ -159,6 +152,7 @@ struct isptables_update {
|
||||
* @subdev: V4L2 subdevice
|
||||
* @pads: Media entity pads
|
||||
* @formats: Active formats at the subdev pad
|
||||
* @crop: Active crop rectangle
|
||||
* @input: Module currently connected to the input pad
|
||||
* @output: Bitmask of the active output
|
||||
* @video_in: Input video entity
|
||||
@ -177,6 +171,7 @@ struct isp_prev_device {
|
||||
struct v4l2_subdev subdev;
|
||||
struct media_pad pads[PREV_PADS_NUM];
|
||||
struct v4l2_mbus_framefmt formats[PREV_PADS_NUM];
|
||||
struct v4l2_rect crop;
|
||||
|
||||
struct v4l2_ctrl_handler ctrls;
|
||||
|
||||
|
@ -402,9 +402,6 @@
|
||||
#define ISPPRV_YENH_TABLE_ADDR 0x1000
|
||||
#define ISPPRV_CFA_TABLE_ADDR 0x1400
|
||||
|
||||
#define ISPPRV_MAXOUTPUT_WIDTH 1280
|
||||
#define ISPPRV_MAXOUTPUT_WIDTH_ES2 3300
|
||||
#define ISPPRV_MAXOUTPUT_WIDTH_3630 4096
|
||||
#define ISPRSZ_MIN_OUTPUT 64
|
||||
#define ISPRSZ_MAX_OUTPUT 3312
|
||||
|
||||
|
@ -1608,6 +1608,42 @@ static const struct media_entity_operations resizer_media_ops = {
|
||||
.link_setup = resizer_link_setup,
|
||||
};
|
||||
|
||||
void omap3isp_resizer_unregister_entities(struct isp_res_device *res)
|
||||
{
|
||||
v4l2_device_unregister_subdev(&res->subdev);
|
||||
omap3isp_video_unregister(&res->video_in);
|
||||
omap3isp_video_unregister(&res->video_out);
|
||||
}
|
||||
|
||||
int omap3isp_resizer_register_entities(struct isp_res_device *res,
|
||||
struct v4l2_device *vdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Register the subdev and video nodes. */
|
||||
ret = v4l2_device_register_subdev(vdev, &res->subdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = omap3isp_video_register(&res->video_in, vdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = omap3isp_video_register(&res->video_out, vdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
omap3isp_resizer_unregister_entities(res);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* ISP resizer initialization and cleanup
|
||||
*/
|
||||
|
||||
/*
|
||||
* resizer_init_entities - Initialize resizer subdev and media entity.
|
||||
* @res : Pointer to resizer device structure
|
||||
@ -1652,68 +1688,34 @@ static int resizer_init_entities(struct isp_res_device *res)
|
||||
|
||||
ret = omap3isp_video_init(&res->video_in, "resizer");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto error_video_in;
|
||||
|
||||
ret = omap3isp_video_init(&res->video_out, "resizer");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto error_video_out;
|
||||
|
||||
/* Connect the video nodes to the resizer subdev. */
|
||||
ret = media_entity_create_link(&res->video_in.video.entity, 0,
|
||||
&res->subdev.entity, RESZ_PAD_SINK, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto error_link;
|
||||
|
||||
ret = media_entity_create_link(&res->subdev.entity, RESZ_PAD_SOURCE,
|
||||
&res->video_out.video.entity, 0, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto error_link;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void omap3isp_resizer_unregister_entities(struct isp_res_device *res)
|
||||
{
|
||||
error_link:
|
||||
omap3isp_video_cleanup(&res->video_out);
|
||||
error_video_out:
|
||||
omap3isp_video_cleanup(&res->video_in);
|
||||
error_video_in:
|
||||
media_entity_cleanup(&res->subdev.entity);
|
||||
|
||||
v4l2_device_unregister_subdev(&res->subdev);
|
||||
omap3isp_video_unregister(&res->video_in);
|
||||
omap3isp_video_unregister(&res->video_out);
|
||||
}
|
||||
|
||||
int omap3isp_resizer_register_entities(struct isp_res_device *res,
|
||||
struct v4l2_device *vdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Register the subdev and video nodes. */
|
||||
ret = v4l2_device_register_subdev(vdev, &res->subdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = omap3isp_video_register(&res->video_in, vdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = omap3isp_video_register(&res->video_out, vdev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
omap3isp_resizer_unregister_entities(res);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* ISP resizer initialization and cleanup
|
||||
*/
|
||||
|
||||
void omap3isp_resizer_cleanup(struct isp_device *isp)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* isp_resizer_init - Resizer initialization.
|
||||
* @isp : Pointer to ISP device
|
||||
@ -1722,17 +1724,17 @@ void omap3isp_resizer_cleanup(struct isp_device *isp)
|
||||
int omap3isp_resizer_init(struct isp_device *isp)
|
||||
{
|
||||
struct isp_res_device *res = &isp->isp_res;
|
||||
int ret;
|
||||
|
||||
init_waitqueue_head(&res->wait);
|
||||
atomic_set(&res->stopping, 0);
|
||||
ret = resizer_init_entities(res);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
if (ret)
|
||||
omap3isp_resizer_cleanup(isp);
|
||||
|
||||
return ret;
|
||||
return resizer_init_entities(res);
|
||||
}
|
||||
|
||||
void omap3isp_resizer_cleanup(struct isp_device *isp)
|
||||
{
|
||||
struct isp_res_device *res = &isp->isp_res;
|
||||
|
||||
omap3isp_video_cleanup(&res->video_in);
|
||||
omap3isp_video_cleanup(&res->video_out);
|
||||
media_entity_cleanup(&res->subdev.entity);
|
||||
}
|
||||
|
@ -1023,24 +1023,6 @@ void omap3isp_stat_dma_isr(struct ispstat *stat)
|
||||
__stat_isr(stat, 1);
|
||||
}
|
||||
|
||||
static int isp_stat_init_entities(struct ispstat *stat, const char *name,
|
||||
const struct v4l2_subdev_ops *sd_ops)
|
||||
{
|
||||
struct v4l2_subdev *subdev = &stat->subdev;
|
||||
struct media_entity *me = &subdev->entity;
|
||||
|
||||
v4l2_subdev_init(subdev, sd_ops);
|
||||
snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "OMAP3 ISP %s", name);
|
||||
subdev->grp_id = 1 << 16; /* group ID for isp subdevs */
|
||||
subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
|
||||
v4l2_set_subdevdata(subdev, stat);
|
||||
|
||||
stat->pad.flags = MEDIA_PAD_FL_SINK;
|
||||
me->ops = NULL;
|
||||
|
||||
return media_entity_init(me, 1, &stat->pad, 0);
|
||||
}
|
||||
|
||||
int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev,
|
||||
struct v4l2_fh *fh,
|
||||
struct v4l2_event_subscription *sub)
|
||||
@ -1062,7 +1044,6 @@ int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev,
|
||||
|
||||
void omap3isp_stat_unregister_entities(struct ispstat *stat)
|
||||
{
|
||||
media_entity_cleanup(&stat->subdev.entity);
|
||||
v4l2_device_unregister_subdev(&stat->subdev);
|
||||
}
|
||||
|
||||
@ -1072,21 +1053,50 @@ int omap3isp_stat_register_entities(struct ispstat *stat,
|
||||
return v4l2_device_register_subdev(vdev, &stat->subdev);
|
||||
}
|
||||
|
||||
static int isp_stat_init_entities(struct ispstat *stat, const char *name,
|
||||
const struct v4l2_subdev_ops *sd_ops)
|
||||
{
|
||||
struct v4l2_subdev *subdev = &stat->subdev;
|
||||
struct media_entity *me = &subdev->entity;
|
||||
|
||||
v4l2_subdev_init(subdev, sd_ops);
|
||||
snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "OMAP3 ISP %s", name);
|
||||
subdev->grp_id = 1 << 16; /* group ID for isp subdevs */
|
||||
subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
|
||||
v4l2_set_subdevdata(subdev, stat);
|
||||
|
||||
stat->pad.flags = MEDIA_PAD_FL_SINK;
|
||||
me->ops = NULL;
|
||||
|
||||
return media_entity_init(me, 1, &stat->pad, 0);
|
||||
}
|
||||
|
||||
int omap3isp_stat_init(struct ispstat *stat, const char *name,
|
||||
const struct v4l2_subdev_ops *sd_ops)
|
||||
{
|
||||
int ret;
|
||||
|
||||
stat->buf = kcalloc(STAT_MAX_BUFS, sizeof(*stat->buf), GFP_KERNEL);
|
||||
if (!stat->buf)
|
||||
return -ENOMEM;
|
||||
|
||||
isp_stat_buf_clear(stat);
|
||||
mutex_init(&stat->ioctl_lock);
|
||||
atomic_set(&stat->buf_err, 0);
|
||||
|
||||
return isp_stat_init_entities(stat, name, sd_ops);
|
||||
ret = isp_stat_init_entities(stat, name, sd_ops);
|
||||
if (ret < 0) {
|
||||
mutex_destroy(&stat->ioctl_lock);
|
||||
kfree(stat->buf);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void omap3isp_stat_free(struct ispstat *stat)
|
||||
void omap3isp_stat_cleanup(struct ispstat *stat)
|
||||
{
|
||||
media_entity_cleanup(&stat->subdev.entity);
|
||||
mutex_destroy(&stat->ioctl_lock);
|
||||
isp_stat_bufs_free(stat);
|
||||
kfree(stat->buf);
|
||||
}
|
||||
|
@ -144,7 +144,7 @@ int omap3isp_stat_request_statistics(struct ispstat *stat,
|
||||
struct omap3isp_stat_data *data);
|
||||
int omap3isp_stat_init(struct ispstat *stat, const char *name,
|
||||
const struct v4l2_subdev_ops *sd_ops);
|
||||
void omap3isp_stat_free(struct ispstat *stat);
|
||||
void omap3isp_stat_cleanup(struct ispstat *stat);
|
||||
int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev,
|
||||
struct v4l2_fh *fh,
|
||||
struct v4l2_event_subscription *sub);
|
||||
|
@ -1325,6 +1325,13 @@ int omap3isp_video_init(struct isp_video *video, const char *name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void omap3isp_video_cleanup(struct isp_video *video)
|
||||
{
|
||||
media_entity_cleanup(&video->video.entity);
|
||||
mutex_destroy(&video->stream_lock);
|
||||
mutex_destroy(&video->mutex);
|
||||
}
|
||||
|
||||
int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev)
|
||||
{
|
||||
int ret;
|
||||
@ -1341,8 +1348,6 @@ int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev)
|
||||
|
||||
void omap3isp_video_unregister(struct isp_video *video)
|
||||
{
|
||||
if (video_is_registered(&video->video)) {
|
||||
media_entity_cleanup(&video->video.entity);
|
||||
if (video_is_registered(&video->video))
|
||||
video_unregister_device(&video->video);
|
||||
}
|
||||
}
|
||||
|
@ -190,6 +190,7 @@ struct isp_video_fh {
|
||||
container_of(q, struct isp_video_fh, queue)
|
||||
|
||||
int omap3isp_video_init(struct isp_video *video, const char *name);
|
||||
void omap3isp_video_cleanup(struct isp_video *video);
|
||||
int omap3isp_video_register(struct isp_video *video,
|
||||
struct v4l2_device *vdev);
|
||||
void omap3isp_video_unregister(struct isp_video *video);
|
||||
|
@ -18,11 +18,13 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/v4l2-mediabus.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include <media/soc_camera.h>
|
||||
#include <media/v4l2-chip-ident.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
#include <media/soc_camera.h>
|
||||
#include <media/soc_mediabus.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
|
||||
#define VAL_SET(x, mask, rshift, lshift) \
|
||||
((((x) >> rshift) & mask) << lshift)
|
||||
@ -299,12 +301,10 @@ struct ov2640_win_size {
|
||||
|
||||
struct ov2640_priv {
|
||||
struct v4l2_subdev subdev;
|
||||
struct ov2640_camera_info *info;
|
||||
struct v4l2_ctrl_handler hdl;
|
||||
enum v4l2_mbus_pixelcode cfmt_code;
|
||||
const struct ov2640_win_size *win;
|
||||
int model;
|
||||
u16 flag_vflip:1;
|
||||
u16 flag_hflip:1;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -609,29 +609,6 @@ static enum v4l2_mbus_pixelcode ov2640_codes[] = {
|
||||
V4L2_MBUS_FMT_RGB565_2X8_LE,
|
||||
};
|
||||
|
||||
/*
|
||||
* Supported controls
|
||||
*/
|
||||
static const struct v4l2_queryctrl ov2640_controls[] = {
|
||||
{
|
||||
.id = V4L2_CID_VFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Flip Vertically",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
}, {
|
||||
.id = V4L2_CID_HFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Flip Horizontally",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* General functions
|
||||
*/
|
||||
@ -701,81 +678,23 @@ static int ov2640_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ov2640_set_bus_param(struct soc_camera_device *icd,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct soc_camera_link *icl = to_soc_camera_link(icd);
|
||||
unsigned long width_flag = flags & SOCAM_DATAWIDTH_MASK;
|
||||
|
||||
/* Only one width bit may be set */
|
||||
if (!is_power_of_2(width_flag))
|
||||
return -EINVAL;
|
||||
|
||||
if (icl->set_bus_param)
|
||||
return icl->set_bus_param(icl, width_flag);
|
||||
|
||||
/*
|
||||
* Without board specific bus width settings we support only the
|
||||
* sensors native bus width witch are tested working
|
||||
*/
|
||||
if (width_flag & (SOCAM_DATAWIDTH_10 | SOCAM_DATAWIDTH_8))
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long ov2640_query_bus_param(struct soc_camera_device *icd)
|
||||
{
|
||||
struct soc_camera_link *icl = to_soc_camera_link(icd);
|
||||
unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
|
||||
SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
|
||||
SOCAM_DATA_ACTIVE_HIGH;
|
||||
|
||||
if (icl->query_bus_param)
|
||||
flags |= icl->query_bus_param(icl) & SOCAM_DATAWIDTH_MASK;
|
||||
else
|
||||
flags |= SOCAM_DATAWIDTH_10;
|
||||
|
||||
return soc_camera_apply_sensor_flags(icl, flags);
|
||||
}
|
||||
|
||||
static int ov2640_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
static int ov2640_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct v4l2_subdev *sd =
|
||||
&container_of(ctrl->handler, struct ov2640_priv, hdl)->subdev;
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct ov2640_priv *priv = to_ov2640(client);
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_VFLIP:
|
||||
ctrl->value = priv->flag_vflip;
|
||||
break;
|
||||
case V4L2_CID_HFLIP:
|
||||
ctrl->value = priv->flag_hflip;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ov2640_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct ov2640_priv *priv = to_ov2640(client);
|
||||
int ret = 0;
|
||||
u8 val;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_VFLIP:
|
||||
val = ctrl->value ? REG04_VFLIP_IMG : 0x00;
|
||||
priv->flag_vflip = ctrl->value ? 1 : 0;
|
||||
ret = ov2640_mask_set(client, REG04, REG04_VFLIP_IMG, val);
|
||||
break;
|
||||
val = ctrl->val ? REG04_VFLIP_IMG : 0x00;
|
||||
return ov2640_mask_set(client, REG04, REG04_VFLIP_IMG, val);
|
||||
case V4L2_CID_HFLIP:
|
||||
val = ctrl->value ? REG04_HFLIP_IMG : 0x00;
|
||||
priv->flag_hflip = ctrl->value ? 1 : 0;
|
||||
ret = ov2640_mask_set(client, REG04, REG04_HFLIP_IMG, val);
|
||||
break;
|
||||
val = ctrl->val ? REG04_HFLIP_IMG : 0x00;
|
||||
return ov2640_mask_set(client, REG04, REG04_HFLIP_IMG, val);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ov2640_g_chip_ident(struct v4l2_subdev *sd,
|
||||
@ -1023,18 +942,13 @@ static int ov2640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ov2640_video_probe(struct soc_camera_device *icd,
|
||||
struct i2c_client *client)
|
||||
static int ov2640_video_probe(struct i2c_client *client)
|
||||
{
|
||||
struct ov2640_priv *priv = to_ov2640(client);
|
||||
u8 pid, ver, midh, midl;
|
||||
const char *devname;
|
||||
int ret;
|
||||
|
||||
/* We must have a parent by now. And it cannot be a wrong one. */
|
||||
BUG_ON(!icd->parent ||
|
||||
to_soc_camera_host(icd->parent)->nr != icd->iface);
|
||||
|
||||
/*
|
||||
* check and show product ID and manufacturer ID
|
||||
*/
|
||||
@ -1060,22 +974,17 @@ static int ov2640_video_probe(struct soc_camera_device *icd,
|
||||
"%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
|
||||
devname, pid, ver, midh, midl);
|
||||
|
||||
return 0;
|
||||
return v4l2_ctrl_handler_setup(&priv->hdl);
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct soc_camera_ops ov2640_ops = {
|
||||
.set_bus_param = ov2640_set_bus_param,
|
||||
.query_bus_param = ov2640_query_bus_param,
|
||||
.controls = ov2640_controls,
|
||||
.num_controls = ARRAY_SIZE(ov2640_controls),
|
||||
static const struct v4l2_ctrl_ops ov2640_ctrl_ops = {
|
||||
.s_ctrl = ov2640_s_ctrl,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_core_ops ov2640_subdev_core_ops = {
|
||||
.g_ctrl = ov2640_g_ctrl,
|
||||
.s_ctrl = ov2640_s_ctrl,
|
||||
.g_chip_ident = ov2640_g_chip_ident,
|
||||
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
||||
.g_register = ov2640_g_register,
|
||||
@ -1083,6 +992,21 @@ static struct v4l2_subdev_core_ops ov2640_subdev_core_ops = {
|
||||
#endif
|
||||
};
|
||||
|
||||
static int ov2640_g_mbus_config(struct v4l2_subdev *sd,
|
||||
struct v4l2_mbus_config *cfg)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
|
||||
cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
|
||||
V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
|
||||
V4L2_MBUS_DATA_ACTIVE_HIGH;
|
||||
cfg->type = V4L2_MBUS_PARALLEL;
|
||||
cfg->flags = soc_camera_apply_board_flags(icl, cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct v4l2_subdev_video_ops ov2640_subdev_video_ops = {
|
||||
.s_stream = ov2640_s_stream,
|
||||
.g_mbus_fmt = ov2640_g_fmt,
|
||||
@ -1091,6 +1015,7 @@ static struct v4l2_subdev_video_ops ov2640_subdev_video_ops = {
|
||||
.cropcap = ov2640_cropcap,
|
||||
.g_crop = ov2640_g_crop,
|
||||
.enum_mbus_fmt = ov2640_enum_fmt,
|
||||
.g_mbus_config = ov2640_g_mbus_config,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_ops ov2640_subdev_ops = {
|
||||
@ -1104,18 +1029,11 @@ static struct v4l2_subdev_ops ov2640_subdev_ops = {
|
||||
static int ov2640_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *did)
|
||||
{
|
||||
struct ov2640_priv *priv;
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct soc_camera_link *icl;
|
||||
int ret;
|
||||
struct ov2640_priv *priv;
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
int ret;
|
||||
|
||||
if (!icd) {
|
||||
dev_err(&adapter->dev, "OV2640: missing soc-camera data!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
icl = to_soc_camera_link(icd);
|
||||
if (!icl) {
|
||||
dev_err(&adapter->dev,
|
||||
"OV2640: Missing platform_data for driver\n");
|
||||
@ -1135,15 +1053,23 @@ static int ov2640_probe(struct i2c_client *client,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->info = icl->priv;
|
||||
|
||||
v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops);
|
||||
v4l2_ctrl_handler_init(&priv->hdl, 2);
|
||||
v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
|
||||
V4L2_CID_VFLIP, 0, 1, 1, 0);
|
||||
v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
|
||||
V4L2_CID_HFLIP, 0, 1, 1, 0);
|
||||
priv->subdev.ctrl_handler = &priv->hdl;
|
||||
if (priv->hdl.error) {
|
||||
int err = priv->hdl.error;
|
||||
|
||||
icd->ops = &ov2640_ops;
|
||||
kfree(priv);
|
||||
return err;
|
||||
}
|
||||
|
||||
ret = ov2640_video_probe(icd, client);
|
||||
ret = ov2640_video_probe(client);
|
||||
if (ret) {
|
||||
icd->ops = NULL;
|
||||
v4l2_ctrl_handler_free(&priv->hdl);
|
||||
kfree(priv);
|
||||
} else {
|
||||
dev_info(&adapter->dev, "OV2640 Probed\n");
|
||||
@ -1155,9 +1081,9 @@ static int ov2640_probe(struct i2c_client *client,
|
||||
static int ov2640_remove(struct i2c_client *client)
|
||||
{
|
||||
struct ov2640_priv *priv = to_ov2640(client);
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
|
||||
icd->ops = NULL;
|
||||
v4l2_device_unregister_subdev(&priv->subdev);
|
||||
v4l2_ctrl_handler_free(&priv->hdl);
|
||||
kfree(priv);
|
||||
return 0;
|
||||
}
|
||||
|
@ -14,14 +14,16 @@
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/v4l2-mediabus.h>
|
||||
|
||||
#include <media/soc_camera.h>
|
||||
#include <media/soc_mediabus.h>
|
||||
#include <media/v4l2-chip-ident.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
|
||||
@ -35,7 +37,7 @@
|
||||
#define REG_WINDOW_START_Y_LOW 0x3803
|
||||
#define REG_WINDOW_WIDTH_HIGH 0x3804
|
||||
#define REG_WINDOW_WIDTH_LOW 0x3805
|
||||
#define REG_WINDOW_HEIGHT_HIGH 0x3806
|
||||
#define REG_WINDOW_HEIGHT_HIGH 0x3806
|
||||
#define REG_WINDOW_HEIGHT_LOW 0x3807
|
||||
#define REG_OUT_WIDTH_HIGH 0x3808
|
||||
#define REG_OUT_WIDTH_LOW 0x3809
|
||||
@ -45,20 +47,45 @@
|
||||
#define REG_OUT_TOTAL_WIDTH_LOW 0x380d
|
||||
#define REG_OUT_TOTAL_HEIGHT_HIGH 0x380e
|
||||
#define REG_OUT_TOTAL_HEIGHT_LOW 0x380f
|
||||
#define REG_OUTPUT_FORMAT 0x4300
|
||||
#define REG_ISP_CTRL_01 0x5001
|
||||
#define REG_AVG_WINDOW_END_X_HIGH 0x5682
|
||||
#define REG_AVG_WINDOW_END_X_LOW 0x5683
|
||||
#define REG_AVG_WINDOW_END_Y_HIGH 0x5686
|
||||
#define REG_AVG_WINDOW_END_Y_LOW 0x5687
|
||||
|
||||
/*
|
||||
* define standard resolution.
|
||||
* Works currently only for up to 720 lines
|
||||
* eg. 320x240, 640x480, 800x600, 1280x720, 2048x720
|
||||
*/
|
||||
|
||||
#define OV5642_WIDTH 1280
|
||||
#define OV5642_HEIGHT 720
|
||||
#define OV5642_TOTAL_WIDTH 3200
|
||||
#define OV5642_TOTAL_HEIGHT 2000
|
||||
/* active pixel array size */
|
||||
#define OV5642_SENSOR_SIZE_X 2592
|
||||
#define OV5642_SENSOR_SIZE_Y 1944
|
||||
|
||||
/*
|
||||
* About OV5642 resolution, cropping and binning:
|
||||
* This sensor supports it all, at least in the feature description.
|
||||
* Unfortunately, no combination of appropriate registers settings could make
|
||||
* the chip work the intended way. As it works with predefined register lists,
|
||||
* some undocumented registers are presumably changed there to achieve their
|
||||
* goals.
|
||||
* This driver currently only works for resolutions up to 720 lines with a
|
||||
* 1:1 scale. Hopefully these restrictions will be removed in the future.
|
||||
*/
|
||||
#define OV5642_MAX_WIDTH OV5642_SENSOR_SIZE_X
|
||||
#define OV5642_MAX_HEIGHT 720
|
||||
|
||||
/* default sizes */
|
||||
#define OV5642_DEFAULT_WIDTH 1280
|
||||
#define OV5642_DEFAULT_HEIGHT OV5642_MAX_HEIGHT
|
||||
|
||||
/* minimum extra blanking */
|
||||
#define BLANKING_EXTRA_WIDTH 500
|
||||
#define BLANKING_EXTRA_HEIGHT 20
|
||||
|
||||
/*
|
||||
* the sensor's autoexposure is buggy when setting total_height low.
|
||||
* It tries to expose longer than 1 frame period without taking care of it
|
||||
* and this leads to weird output. So we set 1000 lines as minimum.
|
||||
*/
|
||||
#define BLANKING_MIN_HEIGHT 1000
|
||||
|
||||
struct regval_list {
|
||||
u16 reg_num;
|
||||
u8 value;
|
||||
@ -582,6 +609,11 @@ struct ov5642_datafmt {
|
||||
struct ov5642 {
|
||||
struct v4l2_subdev subdev;
|
||||
const struct ov5642_datafmt *fmt;
|
||||
struct v4l2_rect crop_rect;
|
||||
|
||||
/* blanking information */
|
||||
int total_width;
|
||||
int total_height;
|
||||
};
|
||||
|
||||
static const struct ov5642_datafmt ov5642_colour_fmts[] = {
|
||||
@ -642,6 +674,21 @@ static int reg_write(struct i2c_client *client, u16 reg, u8 val)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* convenience function to write 16 bit register values that are split up
|
||||
* into two consecutive high and low parts
|
||||
*/
|
||||
static int reg_write16(struct i2c_client *client, u16 reg, u16 val16)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = reg_write(client, reg, val16 >> 8);
|
||||
if (ret)
|
||||
return ret;
|
||||
return reg_write(client, reg + 1, val16 & 0x00ff);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
||||
static int ov5642_get_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
|
||||
{
|
||||
@ -685,58 +732,55 @@ static int ov5642_write_array(struct i2c_client *client,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ov5642_set_resolution(struct i2c_client *client)
|
||||
static int ov5642_set_resolution(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct ov5642 *priv = to_ov5642(client);
|
||||
int width = priv->crop_rect.width;
|
||||
int height = priv->crop_rect.height;
|
||||
int total_width = priv->total_width;
|
||||
int total_height = priv->total_height;
|
||||
int start_x = (OV5642_SENSOR_SIZE_X - width) / 2;
|
||||
int start_y = (OV5642_SENSOR_SIZE_Y - height) / 2;
|
||||
int ret;
|
||||
u8 start_x_high = ((OV5642_SENSOR_SIZE_X - OV5642_WIDTH) / 2) >> 8;
|
||||
u8 start_x_low = ((OV5642_SENSOR_SIZE_X - OV5642_WIDTH) / 2) & 0xff;
|
||||
u8 start_y_high = ((OV5642_SENSOR_SIZE_Y - OV5642_HEIGHT) / 2) >> 8;
|
||||
u8 start_y_low = ((OV5642_SENSOR_SIZE_Y - OV5642_HEIGHT) / 2) & 0xff;
|
||||
|
||||
u8 width_high = OV5642_WIDTH >> 8;
|
||||
u8 width_low = OV5642_WIDTH & 0xff;
|
||||
u8 height_high = OV5642_HEIGHT >> 8;
|
||||
u8 height_low = OV5642_HEIGHT & 0xff;
|
||||
|
||||
u8 total_width_high = OV5642_TOTAL_WIDTH >> 8;
|
||||
u8 total_width_low = OV5642_TOTAL_WIDTH & 0xff;
|
||||
u8 total_height_high = OV5642_TOTAL_HEIGHT >> 8;
|
||||
u8 total_height_low = OV5642_TOTAL_HEIGHT & 0xff;
|
||||
|
||||
ret = reg_write(client, REG_WINDOW_START_X_HIGH, start_x_high);
|
||||
/*
|
||||
* This should set the starting point for cropping.
|
||||
* Doesn't work so far.
|
||||
*/
|
||||
ret = reg_write16(client, REG_WINDOW_START_X_HIGH, start_x);
|
||||
if (!ret)
|
||||
ret = reg_write(client, REG_WINDOW_START_X_LOW, start_x_low);
|
||||
if (!ret)
|
||||
ret = reg_write(client, REG_WINDOW_START_Y_HIGH, start_y_high);
|
||||
if (!ret)
|
||||
ret = reg_write(client, REG_WINDOW_START_Y_LOW, start_y_low);
|
||||
ret = reg_write16(client, REG_WINDOW_START_Y_HIGH, start_y);
|
||||
if (!ret) {
|
||||
priv->crop_rect.left = start_x;
|
||||
priv->crop_rect.top = start_y;
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
ret = reg_write(client, REG_WINDOW_WIDTH_HIGH, width_high);
|
||||
ret = reg_write16(client, REG_WINDOW_WIDTH_HIGH, width);
|
||||
if (!ret)
|
||||
ret = reg_write(client, REG_WINDOW_WIDTH_LOW , width_low);
|
||||
if (!ret)
|
||||
ret = reg_write(client, REG_WINDOW_HEIGHT_HIGH, height_high);
|
||||
if (!ret)
|
||||
ret = reg_write(client, REG_WINDOW_HEIGHT_LOW, height_low);
|
||||
ret = reg_write16(client, REG_WINDOW_HEIGHT_HIGH, height);
|
||||
if (ret)
|
||||
return ret;
|
||||
priv->crop_rect.width = width;
|
||||
priv->crop_rect.height = height;
|
||||
|
||||
/* Set the output window size. Only 1:1 scale is supported so far. */
|
||||
ret = reg_write16(client, REG_OUT_WIDTH_HIGH, width);
|
||||
if (!ret)
|
||||
ret = reg_write(client, REG_OUT_WIDTH_HIGH, width_high);
|
||||
if (!ret)
|
||||
ret = reg_write(client, REG_OUT_WIDTH_LOW , width_low);
|
||||
if (!ret)
|
||||
ret = reg_write(client, REG_OUT_HEIGHT_HIGH, height_high);
|
||||
if (!ret)
|
||||
ret = reg_write(client, REG_OUT_HEIGHT_LOW, height_low);
|
||||
ret = reg_write16(client, REG_OUT_HEIGHT_HIGH, height);
|
||||
|
||||
/* Total width = output size + blanking */
|
||||
if (!ret)
|
||||
ret = reg_write(client, REG_OUT_TOTAL_WIDTH_HIGH, total_width_high);
|
||||
ret = reg_write16(client, REG_OUT_TOTAL_WIDTH_HIGH, total_width);
|
||||
if (!ret)
|
||||
ret = reg_write(client, REG_OUT_TOTAL_WIDTH_LOW, total_width_low);
|
||||
ret = reg_write16(client, REG_OUT_TOTAL_HEIGHT_HIGH, total_height);
|
||||
|
||||
/* Sets the window for AWB calculations */
|
||||
if (!ret)
|
||||
ret = reg_write(client, REG_OUT_TOTAL_HEIGHT_HIGH, total_height_high);
|
||||
ret = reg_write16(client, REG_AVG_WINDOW_END_X_HIGH, width);
|
||||
if (!ret)
|
||||
ret = reg_write(client, REG_OUT_TOTAL_HEIGHT_LOW, total_height_low);
|
||||
ret = reg_write16(client, REG_AVG_WINDOW_END_Y_HIGH, height);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -744,18 +788,18 @@ static int ov5642_set_resolution(struct i2c_client *client)
|
||||
static int ov5642_try_fmt(struct v4l2_subdev *sd,
|
||||
struct v4l2_mbus_framefmt *mf)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct ov5642 *priv = to_ov5642(client);
|
||||
const struct ov5642_datafmt *fmt = ov5642_find_datafmt(mf->code);
|
||||
|
||||
dev_dbg(sd->v4l2_dev->dev, "%s(%u) width: %u heigth: %u\n",
|
||||
__func__, mf->code, mf->width, mf->height);
|
||||
mf->width = priv->crop_rect.width;
|
||||
mf->height = priv->crop_rect.height;
|
||||
|
||||
if (!fmt) {
|
||||
mf->code = ov5642_colour_fmts[0].code;
|
||||
mf->colorspace = ov5642_colour_fmts[0].colorspace;
|
||||
}
|
||||
|
||||
mf->width = OV5642_WIDTH;
|
||||
mf->height = OV5642_HEIGHT;
|
||||
mf->field = V4L2_FIELD_NONE;
|
||||
|
||||
return 0;
|
||||
@ -767,20 +811,13 @@ static int ov5642_s_fmt(struct v4l2_subdev *sd,
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct ov5642 *priv = to_ov5642(client);
|
||||
|
||||
dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code);
|
||||
|
||||
/* MIPI CSI could have changed the format, double-check */
|
||||
if (!ov5642_find_datafmt(mf->code))
|
||||
return -EINVAL;
|
||||
|
||||
ov5642_try_fmt(sd, mf);
|
||||
|
||||
priv->fmt = ov5642_find_datafmt(mf->code);
|
||||
|
||||
ov5642_write_array(client, ov5642_default_regs_init);
|
||||
ov5642_set_resolution(client);
|
||||
ov5642_write_array(client, ov5642_default_regs_finalise);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -794,8 +831,8 @@ static int ov5642_g_fmt(struct v4l2_subdev *sd,
|
||||
|
||||
mf->code = fmt->code;
|
||||
mf->colorspace = fmt->colorspace;
|
||||
mf->width = OV5642_WIDTH;
|
||||
mf->height = OV5642_HEIGHT;
|
||||
mf->width = priv->crop_rect.width;
|
||||
mf->height = priv->crop_rect.height;
|
||||
mf->field = V4L2_FIELD_NONE;
|
||||
|
||||
return 0;
|
||||
@ -828,15 +865,44 @@ static int ov5642_g_chip_ident(struct v4l2_subdev *sd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ov5642_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct ov5642 *priv = to_ov5642(client);
|
||||
struct v4l2_rect *rect = &a->c;
|
||||
int ret;
|
||||
|
||||
v4l_bound_align_image(&rect->width, 48, OV5642_MAX_WIDTH, 1,
|
||||
&rect->height, 32, OV5642_MAX_HEIGHT, 1, 0);
|
||||
|
||||
priv->crop_rect.width = rect->width;
|
||||
priv->crop_rect.height = rect->height;
|
||||
priv->total_width = rect->width + BLANKING_EXTRA_WIDTH;
|
||||
priv->total_height = max_t(int, rect->height +
|
||||
BLANKING_EXTRA_HEIGHT,
|
||||
BLANKING_MIN_HEIGHT);
|
||||
priv->crop_rect.width = rect->width;
|
||||
priv->crop_rect.height = rect->height;
|
||||
|
||||
ret = ov5642_write_array(client, ov5642_default_regs_init);
|
||||
if (!ret)
|
||||
ret = ov5642_set_resolution(sd);
|
||||
if (!ret)
|
||||
ret = ov5642_write_array(client, ov5642_default_regs_finalise);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ov5642_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct ov5642 *priv = to_ov5642(client);
|
||||
struct v4l2_rect *rect = &a->c;
|
||||
|
||||
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
rect->top = 0;
|
||||
rect->left = 0;
|
||||
rect->width = OV5642_WIDTH;
|
||||
rect->height = OV5642_HEIGHT;
|
||||
if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
||||
return -EINVAL;
|
||||
|
||||
*rect = priv->crop_rect;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -845,8 +911,8 @@ static int ov5642_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
|
||||
{
|
||||
a->bounds.left = 0;
|
||||
a->bounds.top = 0;
|
||||
a->bounds.width = OV5642_WIDTH;
|
||||
a->bounds.height = OV5642_HEIGHT;
|
||||
a->bounds.width = OV5642_MAX_WIDTH;
|
||||
a->bounds.height = OV5642_MAX_HEIGHT;
|
||||
a->defrect = a->bounds;
|
||||
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
a->pixelaspect.numerator = 1;
|
||||
@ -855,16 +921,47 @@ static int ov5642_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ov5642_g_mbus_config(struct v4l2_subdev *sd,
|
||||
struct v4l2_mbus_config *cfg)
|
||||
{
|
||||
cfg->type = V4L2_MBUS_CSI2;
|
||||
cfg->flags = V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
|
||||
V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ov5642_s_power(struct v4l2_subdev *sd, int on)
|
||||
{
|
||||
struct i2c_client *client;
|
||||
int ret;
|
||||
|
||||
if (!on)
|
||||
return 0;
|
||||
|
||||
client = v4l2_get_subdevdata(sd);
|
||||
ret = ov5642_write_array(client, ov5642_default_regs_init);
|
||||
if (!ret)
|
||||
ret = ov5642_set_resolution(sd);
|
||||
if (!ret)
|
||||
ret = ov5642_write_array(client, ov5642_default_regs_finalise);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct v4l2_subdev_video_ops ov5642_subdev_video_ops = {
|
||||
.s_mbus_fmt = ov5642_s_fmt,
|
||||
.g_mbus_fmt = ov5642_g_fmt,
|
||||
.try_mbus_fmt = ov5642_try_fmt,
|
||||
.enum_mbus_fmt = ov5642_enum_fmt,
|
||||
.s_crop = ov5642_s_crop,
|
||||
.g_crop = ov5642_g_crop,
|
||||
.cropcap = ov5642_cropcap,
|
||||
.g_mbus_config = ov5642_g_mbus_config,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_core_ops ov5642_subdev_core_ops = {
|
||||
.s_power = ov5642_s_power,
|
||||
.g_chip_ident = ov5642_g_chip_ident,
|
||||
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
||||
.g_register = ov5642_get_register,
|
||||
@ -877,28 +974,7 @@ static struct v4l2_subdev_ops ov5642_subdev_ops = {
|
||||
.video = &ov5642_subdev_video_ops,
|
||||
};
|
||||
|
||||
/*
|
||||
* We have to provide soc-camera operations, but we don't have anything to say
|
||||
* there. The MIPI CSI2 driver will provide .query_bus_param and .set_bus_param
|
||||
*/
|
||||
static unsigned long soc_ov5642_query_bus_param(struct soc_camera_device *icd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int soc_ov5642_set_bus_param(struct soc_camera_device *icd,
|
||||
unsigned long flags)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct soc_camera_ops soc_ov5642_ops = {
|
||||
.query_bus_param = soc_ov5642_query_bus_param,
|
||||
.set_bus_param = soc_ov5642_set_bus_param,
|
||||
};
|
||||
|
||||
static int ov5642_video_probe(struct soc_camera_device *icd,
|
||||
struct i2c_client *client)
|
||||
static int ov5642_video_probe(struct i2c_client *client)
|
||||
{
|
||||
int ret;
|
||||
u8 id_high, id_low;
|
||||
@ -929,16 +1005,9 @@ static int ov5642_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *did)
|
||||
{
|
||||
struct ov5642 *priv;
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
struct soc_camera_link *icl;
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
int ret;
|
||||
|
||||
if (!icd) {
|
||||
dev_err(&client->dev, "OV5642: missing soc-camera data!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
icl = to_soc_camera_link(icd);
|
||||
if (!icl) {
|
||||
dev_err(&client->dev, "OV5642: missing platform data!\n");
|
||||
return -EINVAL;
|
||||
@ -950,17 +1019,24 @@ static int ov5642_probe(struct i2c_client *client,
|
||||
|
||||
v4l2_i2c_subdev_init(&priv->subdev, client, &ov5642_subdev_ops);
|
||||
|
||||
icd->ops = &soc_ov5642_ops;
|
||||
priv->fmt = &ov5642_colour_fmts[0];
|
||||
priv->fmt = &ov5642_colour_fmts[0];
|
||||
|
||||
ret = ov5642_video_probe(icd, client);
|
||||
priv->crop_rect.width = OV5642_DEFAULT_WIDTH;
|
||||
priv->crop_rect.height = OV5642_DEFAULT_HEIGHT;
|
||||
priv->crop_rect.left = (OV5642_MAX_WIDTH - OV5642_DEFAULT_WIDTH) / 2;
|
||||
priv->crop_rect.top = (OV5642_MAX_HEIGHT - OV5642_DEFAULT_HEIGHT) / 2;
|
||||
priv->crop_rect.width = OV5642_DEFAULT_WIDTH;
|
||||
priv->crop_rect.height = OV5642_DEFAULT_HEIGHT;
|
||||
priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH;
|
||||
priv->total_height = BLANKING_MIN_HEIGHT;
|
||||
|
||||
ret = ov5642_video_probe(client);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
icd->ops = NULL;
|
||||
kfree(priv);
|
||||
return ret;
|
||||
}
|
||||
@ -968,10 +1044,8 @@ error:
|
||||
static int ov5642_remove(struct i2c_client *client)
|
||||
{
|
||||
struct ov5642 *priv = to_ov5642(client);
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
struct soc_camera_link *icl = to_soc_camera_link(icd);
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
|
||||
icd->ops = NULL;
|
||||
if (icl->free_bus)
|
||||
icl->free_bus(icl);
|
||||
kfree(priv);
|
||||
|
@ -28,10 +28,11 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/v4l2-mediabus.h>
|
||||
|
||||
#include <media/soc_camera.h>
|
||||
#include <media/v4l2-chip-ident.h>
|
||||
|
||||
#include <media/v4l2-ctrls.h>
|
||||
|
||||
/* Register definitions */
|
||||
#define REG_GAIN 0x00 /* range 00 - 3F */
|
||||
@ -177,20 +178,23 @@ struct ov6650_reg {
|
||||
|
||||
struct ov6650 {
|
||||
struct v4l2_subdev subdev;
|
||||
|
||||
int gain;
|
||||
int blue;
|
||||
int red;
|
||||
int saturation;
|
||||
int hue;
|
||||
int brightness;
|
||||
int exposure;
|
||||
int gamma;
|
||||
int aec;
|
||||
bool vflip;
|
||||
bool hflip;
|
||||
bool awb;
|
||||
bool agc;
|
||||
struct v4l2_ctrl_handler hdl;
|
||||
struct {
|
||||
/* exposure/autoexposure cluster */
|
||||
struct v4l2_ctrl *autoexposure;
|
||||
struct v4l2_ctrl *exposure;
|
||||
};
|
||||
struct {
|
||||
/* gain/autogain cluster */
|
||||
struct v4l2_ctrl *autogain;
|
||||
struct v4l2_ctrl *gain;
|
||||
};
|
||||
struct {
|
||||
/* blue/red/autowhitebalance cluster */
|
||||
struct v4l2_ctrl *autowb;
|
||||
struct v4l2_ctrl *blue;
|
||||
struct v4l2_ctrl *red;
|
||||
};
|
||||
bool half_scale; /* scale down output by 2 */
|
||||
struct v4l2_rect rect; /* sensor cropping window */
|
||||
unsigned long pclk_limit; /* from host */
|
||||
@ -210,126 +214,6 @@ static enum v4l2_mbus_pixelcode ov6650_codes[] = {
|
||||
V4L2_MBUS_FMT_Y8_1X8,
|
||||
};
|
||||
|
||||
static const struct v4l2_queryctrl ov6650_controls[] = {
|
||||
{
|
||||
.id = V4L2_CID_AUTOGAIN,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "AGC",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 1,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_GAIN,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Gain",
|
||||
.minimum = 0,
|
||||
.maximum = 0x3f,
|
||||
.step = 1,
|
||||
.default_value = DEF_GAIN,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_AUTO_WHITE_BALANCE,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "AWB",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 1,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_BLUE_BALANCE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Blue",
|
||||
.minimum = 0,
|
||||
.maximum = 0xff,
|
||||
.step = 1,
|
||||
.default_value = DEF_BLUE,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_RED_BALANCE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Red",
|
||||
.minimum = 0,
|
||||
.maximum = 0xff,
|
||||
.step = 1,
|
||||
.default_value = DEF_RED,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_SATURATION,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Saturation",
|
||||
.minimum = 0,
|
||||
.maximum = 0xf,
|
||||
.step = 1,
|
||||
.default_value = 0x8,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_HUE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Hue",
|
||||
.minimum = 0,
|
||||
.maximum = HUE_MASK,
|
||||
.step = 1,
|
||||
.default_value = DEF_HUE,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_BRIGHTNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Brightness",
|
||||
.minimum = 0,
|
||||
.maximum = 0xff,
|
||||
.step = 1,
|
||||
.default_value = 0x80,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_EXPOSURE_AUTO,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "AEC",
|
||||
.minimum = 0,
|
||||
.maximum = 3,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_EXPOSURE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Exposure",
|
||||
.minimum = 0,
|
||||
.maximum = 0xff,
|
||||
.step = 1,
|
||||
.default_value = DEF_AECH,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_GAMMA,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Gamma",
|
||||
.minimum = 0,
|
||||
.maximum = 0xff,
|
||||
.step = 1,
|
||||
.default_value = 0x12,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_VFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Flip Vertically",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_HFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Flip Horizontally",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
};
|
||||
|
||||
/* read a register */
|
||||
static int ov6650_reg_read(struct i2c_client *client, u8 reg, u8 *val)
|
||||
{
|
||||
@ -419,213 +303,90 @@ static int ov6650_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Alter bus settings on camera side */
|
||||
static int ov6650_set_bus_param(struct soc_camera_device *icd,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct soc_camera_link *icl = to_soc_camera_link(icd);
|
||||
struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
|
||||
int ret;
|
||||
|
||||
flags = soc_camera_apply_sensor_flags(icl, flags);
|
||||
|
||||
if (flags & SOCAM_PCLK_SAMPLE_RISING)
|
||||
ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_PCLK_RISING, 0);
|
||||
else
|
||||
ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_PCLK_RISING);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (flags & SOCAM_HSYNC_ACTIVE_LOW)
|
||||
ret = ov6650_reg_rmw(client, REG_COMF, COMF_HREF_LOW, 0);
|
||||
else
|
||||
ret = ov6650_reg_rmw(client, REG_COMF, 0, COMF_HREF_LOW);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (flags & SOCAM_VSYNC_ACTIVE_HIGH)
|
||||
ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_VSYNC_HIGH, 0);
|
||||
else
|
||||
ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_VSYNC_HIGH);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Request bus settings on camera side */
|
||||
static unsigned long ov6650_query_bus_param(struct soc_camera_device *icd)
|
||||
{
|
||||
struct soc_camera_link *icl = to_soc_camera_link(icd);
|
||||
|
||||
unsigned long flags = SOCAM_MASTER |
|
||||
SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING |
|
||||
SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW |
|
||||
SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW |
|
||||
SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
|
||||
|
||||
return soc_camera_apply_sensor_flags(icl, flags);
|
||||
}
|
||||
|
||||
/* Get status of additional camera capabilities */
|
||||
static int ov6650_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
static int ov6550_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl);
|
||||
struct v4l2_subdev *sd = &priv->subdev;
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct ov6650 *priv = to_ov6650(client);
|
||||
uint8_t reg;
|
||||
int ret = 0;
|
||||
uint8_t reg, reg2;
|
||||
int ret;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_AUTOGAIN:
|
||||
ctrl->value = priv->agc;
|
||||
break;
|
||||
case V4L2_CID_GAIN:
|
||||
if (priv->agc) {
|
||||
ret = ov6650_reg_read(client, REG_GAIN, ®);
|
||||
ctrl->value = reg;
|
||||
} else {
|
||||
ctrl->value = priv->gain;
|
||||
}
|
||||
break;
|
||||
ret = ov6650_reg_read(client, REG_GAIN, ®);
|
||||
if (!ret)
|
||||
priv->gain->val = reg;
|
||||
return ret;
|
||||
case V4L2_CID_AUTO_WHITE_BALANCE:
|
||||
ctrl->value = priv->awb;
|
||||
break;
|
||||
case V4L2_CID_BLUE_BALANCE:
|
||||
if (priv->awb) {
|
||||
ret = ov6650_reg_read(client, REG_BLUE, ®);
|
||||
ctrl->value = reg;
|
||||
} else {
|
||||
ctrl->value = priv->blue;
|
||||
ret = ov6650_reg_read(client, REG_BLUE, ®);
|
||||
if (!ret)
|
||||
ret = ov6650_reg_read(client, REG_RED, ®2);
|
||||
if (!ret) {
|
||||
priv->blue->val = reg;
|
||||
priv->red->val = reg2;
|
||||
}
|
||||
break;
|
||||
case V4L2_CID_RED_BALANCE:
|
||||
if (priv->awb) {
|
||||
ret = ov6650_reg_read(client, REG_RED, ®);
|
||||
ctrl->value = reg;
|
||||
} else {
|
||||
ctrl->value = priv->red;
|
||||
}
|
||||
break;
|
||||
case V4L2_CID_SATURATION:
|
||||
ctrl->value = priv->saturation;
|
||||
break;
|
||||
case V4L2_CID_HUE:
|
||||
ctrl->value = priv->hue;
|
||||
break;
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
ctrl->value = priv->brightness;
|
||||
break;
|
||||
return ret;
|
||||
case V4L2_CID_EXPOSURE_AUTO:
|
||||
ctrl->value = priv->aec;
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE:
|
||||
if (priv->aec) {
|
||||
ret = ov6650_reg_read(client, REG_AECH, ®);
|
||||
ctrl->value = reg;
|
||||
} else {
|
||||
ctrl->value = priv->exposure;
|
||||
}
|
||||
break;
|
||||
case V4L2_CID_GAMMA:
|
||||
ctrl->value = priv->gamma;
|
||||
break;
|
||||
case V4L2_CID_VFLIP:
|
||||
ctrl->value = priv->vflip;
|
||||
break;
|
||||
case V4L2_CID_HFLIP:
|
||||
ctrl->value = priv->hflip;
|
||||
break;
|
||||
ret = ov6650_reg_read(client, REG_AECH, ®);
|
||||
if (!ret)
|
||||
priv->exposure->val = reg;
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Set status of additional camera capabilities */
|
||||
static int ov6650_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
static int ov6550_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl);
|
||||
struct v4l2_subdev *sd = &priv->subdev;
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct ov6650 *priv = to_ov6650(client);
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_AUTOGAIN:
|
||||
ret = ov6650_reg_rmw(client, REG_COMB,
|
||||
ctrl->value ? COMB_AGC : 0, COMB_AGC);
|
||||
if (!ret)
|
||||
priv->agc = ctrl->value;
|
||||
break;
|
||||
case V4L2_CID_GAIN:
|
||||
ret = ov6650_reg_write(client, REG_GAIN, ctrl->value);
|
||||
if (!ret)
|
||||
priv->gain = ctrl->value;
|
||||
break;
|
||||
ctrl->val ? COMB_AGC : 0, COMB_AGC);
|
||||
if (!ret && !ctrl->val)
|
||||
ret = ov6650_reg_write(client, REG_GAIN, priv->gain->val);
|
||||
return ret;
|
||||
case V4L2_CID_AUTO_WHITE_BALANCE:
|
||||
ret = ov6650_reg_rmw(client, REG_COMB,
|
||||
ctrl->value ? COMB_AWB : 0, COMB_AWB);
|
||||
if (!ret)
|
||||
priv->awb = ctrl->value;
|
||||
break;
|
||||
case V4L2_CID_BLUE_BALANCE:
|
||||
ret = ov6650_reg_write(client, REG_BLUE, ctrl->value);
|
||||
if (!ret)
|
||||
priv->blue = ctrl->value;
|
||||
break;
|
||||
case V4L2_CID_RED_BALANCE:
|
||||
ret = ov6650_reg_write(client, REG_RED, ctrl->value);
|
||||
if (!ret)
|
||||
priv->red = ctrl->value;
|
||||
break;
|
||||
case V4L2_CID_SATURATION:
|
||||
ret = ov6650_reg_rmw(client, REG_SAT, SET_SAT(ctrl->value),
|
||||
SAT_MASK);
|
||||
if (!ret)
|
||||
priv->saturation = ctrl->value;
|
||||
break;
|
||||
case V4L2_CID_HUE:
|
||||
ret = ov6650_reg_rmw(client, REG_HUE, SET_HUE(ctrl->value),
|
||||
HUE_MASK);
|
||||
if (!ret)
|
||||
priv->hue = ctrl->value;
|
||||
break;
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
ret = ov6650_reg_write(client, REG_BRT, ctrl->value);
|
||||
if (!ret)
|
||||
priv->brightness = ctrl->value;
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE_AUTO:
|
||||
switch (ctrl->value) {
|
||||
case V4L2_EXPOSURE_AUTO:
|
||||
ret = ov6650_reg_rmw(client, REG_COMB, COMB_AEC, 0);
|
||||
break;
|
||||
default:
|
||||
ret = ov6650_reg_rmw(client, REG_COMB, 0, COMB_AEC);
|
||||
break;
|
||||
ctrl->val ? COMB_AWB : 0, COMB_AWB);
|
||||
if (!ret && !ctrl->val) {
|
||||
ret = ov6650_reg_write(client, REG_BLUE, priv->blue->val);
|
||||
if (!ret)
|
||||
ret = ov6650_reg_write(client, REG_RED,
|
||||
priv->red->val);
|
||||
}
|
||||
if (!ret)
|
||||
priv->aec = ctrl->value;
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE:
|
||||
ret = ov6650_reg_write(client, REG_AECH, ctrl->value);
|
||||
if (!ret)
|
||||
priv->exposure = ctrl->value;
|
||||
break;
|
||||
return ret;
|
||||
case V4L2_CID_SATURATION:
|
||||
return ov6650_reg_rmw(client, REG_SAT, SET_SAT(ctrl->val),
|
||||
SAT_MASK);
|
||||
case V4L2_CID_HUE:
|
||||
return ov6650_reg_rmw(client, REG_HUE, SET_HUE(ctrl->val),
|
||||
HUE_MASK);
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
return ov6650_reg_write(client, REG_BRT, ctrl->val);
|
||||
case V4L2_CID_EXPOSURE_AUTO:
|
||||
ret = ov6650_reg_rmw(client, REG_COMB, ctrl->val ==
|
||||
V4L2_EXPOSURE_AUTO ? COMB_AEC : 0, COMB_AEC);
|
||||
if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL)
|
||||
ret = ov6650_reg_write(client, REG_AECH,
|
||||
priv->exposure->val);
|
||||
return ret;
|
||||
case V4L2_CID_GAMMA:
|
||||
ret = ov6650_reg_write(client, REG_GAM1, ctrl->value);
|
||||
if (!ret)
|
||||
priv->gamma = ctrl->value;
|
||||
break;
|
||||
return ov6650_reg_write(client, REG_GAM1, ctrl->val);
|
||||
case V4L2_CID_VFLIP:
|
||||
ret = ov6650_reg_rmw(client, REG_COMB,
|
||||
ctrl->value ? COMB_FLIP_V : 0, COMB_FLIP_V);
|
||||
if (!ret)
|
||||
priv->vflip = ctrl->value;
|
||||
break;
|
||||
return ov6650_reg_rmw(client, REG_COMB,
|
||||
ctrl->val ? COMB_FLIP_V : 0, COMB_FLIP_V);
|
||||
case V4L2_CID_HFLIP:
|
||||
ret = ov6650_reg_rmw(client, REG_COMB,
|
||||
ctrl->value ? COMB_FLIP_H : 0, COMB_FLIP_H);
|
||||
if (!ret)
|
||||
priv->hflip = ctrl->value;
|
||||
break;
|
||||
return ov6650_reg_rmw(client, REG_COMB,
|
||||
ctrl->val ? COMB_FLIP_H : 0, COMB_FLIP_H);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Get chip identification */
|
||||
@ -778,7 +539,7 @@ static u8 to_clkrc(struct v4l2_fract *timeperframe,
|
||||
static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
struct soc_camera_device *icd = (struct soc_camera_device *)sd->grp_id;
|
||||
struct soc_camera_sense *sense = icd->sense;
|
||||
struct ov6650 *priv = to_ov6650(client);
|
||||
bool half_scale = !is_unscaled_ok(mf->width, mf->height, &priv->rect);
|
||||
@ -1057,8 +818,7 @@ static int ov6650_prog_dflt(struct i2c_client *client)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ov6650_video_probe(struct soc_camera_device *icd,
|
||||
struct i2c_client *client)
|
||||
static int ov6650_video_probe(struct i2c_client *client)
|
||||
{
|
||||
u8 pidh, pidl, midh, midl;
|
||||
int ret = 0;
|
||||
@ -1094,16 +854,12 @@ static int ov6650_video_probe(struct soc_camera_device *icd,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct soc_camera_ops ov6650_ops = {
|
||||
.set_bus_param = ov6650_set_bus_param,
|
||||
.query_bus_param = ov6650_query_bus_param,
|
||||
.controls = ov6650_controls,
|
||||
.num_controls = ARRAY_SIZE(ov6650_controls),
|
||||
static const struct v4l2_ctrl_ops ov6550_ctrl_ops = {
|
||||
.g_volatile_ctrl = ov6550_g_volatile_ctrl,
|
||||
.s_ctrl = ov6550_s_ctrl,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_core_ops ov6650_core_ops = {
|
||||
.g_ctrl = ov6650_g_ctrl,
|
||||
.s_ctrl = ov6650_s_ctrl,
|
||||
.g_chip_ident = ov6650_g_chip_ident,
|
||||
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
||||
.g_register = ov6650_get_register,
|
||||
@ -1111,6 +867,55 @@ static struct v4l2_subdev_core_ops ov6650_core_ops = {
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Request bus settings on camera side */
|
||||
static int ov6650_g_mbus_config(struct v4l2_subdev *sd,
|
||||
struct v4l2_mbus_config *cfg)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
|
||||
cfg->flags = V4L2_MBUS_MASTER |
|
||||
V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
|
||||
V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW |
|
||||
V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
|
||||
V4L2_MBUS_DATA_ACTIVE_HIGH;
|
||||
cfg->type = V4L2_MBUS_PARALLEL;
|
||||
cfg->flags = soc_camera_apply_board_flags(icl, cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Alter bus settings on camera side */
|
||||
static int ov6650_s_mbus_config(struct v4l2_subdev *sd,
|
||||
const struct v4l2_mbus_config *cfg)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
unsigned long flags = soc_camera_apply_board_flags(icl, cfg);
|
||||
int ret;
|
||||
|
||||
if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
|
||||
ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_PCLK_RISING, 0);
|
||||
else
|
||||
ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_PCLK_RISING);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
|
||||
ret = ov6650_reg_rmw(client, REG_COMF, COMF_HREF_LOW, 0);
|
||||
else
|
||||
ret = ov6650_reg_rmw(client, REG_COMF, 0, COMF_HREF_LOW);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
|
||||
ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_VSYNC_HIGH, 0);
|
||||
else
|
||||
ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_VSYNC_HIGH);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct v4l2_subdev_video_ops ov6650_video_ops = {
|
||||
.s_stream = ov6650_s_stream,
|
||||
.g_mbus_fmt = ov6650_g_fmt,
|
||||
@ -1122,6 +927,8 @@ static struct v4l2_subdev_video_ops ov6650_video_ops = {
|
||||
.s_crop = ov6650_s_crop,
|
||||
.g_parm = ov6650_g_parm,
|
||||
.s_parm = ov6650_s_parm,
|
||||
.g_mbus_config = ov6650_g_mbus_config,
|
||||
.s_mbus_config = ov6650_s_mbus_config,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_ops ov6650_subdev_ops = {
|
||||
@ -1136,16 +943,9 @@ static int ov6650_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *did)
|
||||
{
|
||||
struct ov6650 *priv;
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
struct soc_camera_link *icl;
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
int ret;
|
||||
|
||||
if (!icd) {
|
||||
dev_err(&client->dev, "Missing soc-camera data!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
icl = to_soc_camera_link(icd);
|
||||
if (!icl) {
|
||||
dev_err(&client->dev, "Missing platform_data for driver\n");
|
||||
return -EINVAL;
|
||||
@ -1159,8 +959,46 @@ static int ov6650_probe(struct i2c_client *client,
|
||||
}
|
||||
|
||||
v4l2_i2c_subdev_init(&priv->subdev, client, &ov6650_subdev_ops);
|
||||
v4l2_ctrl_handler_init(&priv->hdl, 13);
|
||||
v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
|
||||
V4L2_CID_VFLIP, 0, 1, 1, 0);
|
||||
v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
|
||||
V4L2_CID_HFLIP, 0, 1, 1, 0);
|
||||
priv->autogain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
|
||||
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
|
||||
priv->gain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
|
||||
V4L2_CID_GAIN, 0, 0x3f, 1, DEF_GAIN);
|
||||
priv->autowb = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
|
||||
V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
|
||||
priv->blue = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
|
||||
V4L2_CID_BLUE_BALANCE, 0, 0xff, 1, DEF_BLUE);
|
||||
priv->red = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
|
||||
V4L2_CID_RED_BALANCE, 0, 0xff, 1, DEF_RED);
|
||||
v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
|
||||
V4L2_CID_SATURATION, 0, 0xf, 1, 0x8);
|
||||
v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
|
||||
V4L2_CID_HUE, 0, HUE_MASK, 1, DEF_HUE);
|
||||
v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, 0, 0xff, 1, 0x80);
|
||||
priv->autoexposure = v4l2_ctrl_new_std_menu(&priv->hdl,
|
||||
&ov6550_ctrl_ops, V4L2_CID_EXPOSURE_AUTO,
|
||||
V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO);
|
||||
priv->exposure = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
|
||||
V4L2_CID_EXPOSURE, 0, 0xff, 1, DEF_AECH);
|
||||
v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
|
||||
V4L2_CID_GAMMA, 0, 0xff, 1, 0x12);
|
||||
|
||||
icd->ops = &ov6650_ops;
|
||||
priv->subdev.ctrl_handler = &priv->hdl;
|
||||
if (priv->hdl.error) {
|
||||
int err = priv->hdl.error;
|
||||
|
||||
kfree(priv);
|
||||
return err;
|
||||
}
|
||||
v4l2_ctrl_auto_cluster(2, &priv->autogain, 0, true);
|
||||
v4l2_ctrl_auto_cluster(3, &priv->autowb, 0, true);
|
||||
v4l2_ctrl_auto_cluster(2, &priv->autoexposure,
|
||||
V4L2_EXPOSURE_MANUAL, true);
|
||||
|
||||
priv->rect.left = DEF_HSTRT << 1;
|
||||
priv->rect.top = DEF_VSTRT << 1;
|
||||
@ -1170,10 +1008,12 @@ static int ov6650_probe(struct i2c_client *client,
|
||||
priv->code = V4L2_MBUS_FMT_YUYV8_2X8;
|
||||
priv->colorspace = V4L2_COLORSPACE_JPEG;
|
||||
|
||||
ret = ov6650_video_probe(icd, client);
|
||||
ret = ov6650_video_probe(client);
|
||||
if (!ret)
|
||||
ret = v4l2_ctrl_handler_setup(&priv->hdl);
|
||||
|
||||
if (ret) {
|
||||
icd->ops = NULL;
|
||||
v4l2_ctrl_handler_free(&priv->hdl);
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
@ -1184,6 +1024,8 @@ static int ov6650_remove(struct i2c_client *client)
|
||||
{
|
||||
struct ov6650 *priv = to_ov6650(client);
|
||||
|
||||
v4l2_device_unregister_subdev(&priv->subdev);
|
||||
v4l2_ctrl_handler_free(&priv->hdl);
|
||||
kfree(priv);
|
||||
return 0;
|
||||
}
|
||||
|
@ -20,12 +20,14 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/v4l2-mediabus.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include <media/ov772x.h>
|
||||
#include <media/soc_camera.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-chip-ident.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
#include <media/soc_camera.h>
|
||||
#include <media/soc_mediabus.h>
|
||||
#include <media/ov772x.h>
|
||||
|
||||
/*
|
||||
* register offset
|
||||
@ -400,6 +402,7 @@ struct ov772x_win_size {
|
||||
|
||||
struct ov772x_priv {
|
||||
struct v4l2_subdev subdev;
|
||||
struct v4l2_ctrl_handler hdl;
|
||||
struct ov772x_camera_info *info;
|
||||
const struct ov772x_color_format *cfmt;
|
||||
const struct ov772x_win_size *win;
|
||||
@ -517,36 +520,6 @@ static const struct ov772x_win_size ov772x_win_qvga = {
|
||||
.regs = ov772x_qvga_regs,
|
||||
};
|
||||
|
||||
static const struct v4l2_queryctrl ov772x_controls[] = {
|
||||
{
|
||||
.id = V4L2_CID_VFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Flip Vertically",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_HFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Flip Horizontally",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_BAND_STOP_FILTER,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Band-stop filter",
|
||||
.minimum = 0,
|
||||
.maximum = 256,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* general function
|
||||
*/
|
||||
@ -620,75 +593,30 @@ static int ov772x_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ov772x_set_bus_param(struct soc_camera_device *icd,
|
||||
unsigned long flags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
|
||||
struct ov772x_priv *priv = i2c_get_clientdata(client);
|
||||
struct soc_camera_link *icl = to_soc_camera_link(icd);
|
||||
unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
|
||||
SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
|
||||
SOCAM_DATA_ACTIVE_HIGH;
|
||||
|
||||
if (priv->info->flags & OV772X_FLAG_8BIT)
|
||||
flags |= SOCAM_DATAWIDTH_8;
|
||||
else
|
||||
flags |= SOCAM_DATAWIDTH_10;
|
||||
|
||||
return soc_camera_apply_sensor_flags(icl, flags);
|
||||
}
|
||||
|
||||
static int ov772x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
{
|
||||
struct ov772x_priv *priv = container_of(sd, struct ov772x_priv, subdev);
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_VFLIP:
|
||||
ctrl->value = priv->flag_vflip;
|
||||
break;
|
||||
case V4L2_CID_HFLIP:
|
||||
ctrl->value = priv->flag_hflip;
|
||||
break;
|
||||
case V4L2_CID_BAND_STOP_FILTER:
|
||||
ctrl->value = priv->band_filter;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ov772x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct ov772x_priv *priv = container_of(ctrl->handler,
|
||||
struct ov772x_priv, hdl);
|
||||
struct v4l2_subdev *sd = &priv->subdev;
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct ov772x_priv *priv = container_of(sd, struct ov772x_priv, subdev);
|
||||
int ret = 0;
|
||||
u8 val;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_VFLIP:
|
||||
val = ctrl->value ? VFLIP_IMG : 0x00;
|
||||
priv->flag_vflip = ctrl->value;
|
||||
val = ctrl->val ? VFLIP_IMG : 0x00;
|
||||
priv->flag_vflip = ctrl->val;
|
||||
if (priv->info->flags & OV772X_FLAG_VFLIP)
|
||||
val ^= VFLIP_IMG;
|
||||
ret = ov772x_mask_set(client, COM3, VFLIP_IMG, val);
|
||||
break;
|
||||
return ov772x_mask_set(client, COM3, VFLIP_IMG, val);
|
||||
case V4L2_CID_HFLIP:
|
||||
val = ctrl->value ? HFLIP_IMG : 0x00;
|
||||
priv->flag_hflip = ctrl->value;
|
||||
val = ctrl->val ? HFLIP_IMG : 0x00;
|
||||
priv->flag_hflip = ctrl->val;
|
||||
if (priv->info->flags & OV772X_FLAG_HFLIP)
|
||||
val ^= HFLIP_IMG;
|
||||
ret = ov772x_mask_set(client, COM3, HFLIP_IMG, val);
|
||||
break;
|
||||
return ov772x_mask_set(client, COM3, HFLIP_IMG, val);
|
||||
case V4L2_CID_BAND_STOP_FILTER:
|
||||
if ((unsigned)ctrl->value > 256)
|
||||
ctrl->value = 256;
|
||||
if (ctrl->value == priv->band_filter)
|
||||
break;
|
||||
if (!ctrl->value) {
|
||||
if (!ctrl->val) {
|
||||
/* Switch the filter off, it is on now */
|
||||
ret = ov772x_mask_set(client, BDBASE, 0xff, 0xff);
|
||||
if (!ret)
|
||||
@ -696,7 +624,7 @@ static int ov772x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
BNDF_ON_OFF, 0);
|
||||
} else {
|
||||
/* Switch the filter on, set AEC low limit */
|
||||
val = 256 - ctrl->value;
|
||||
val = 256 - ctrl->val;
|
||||
ret = ov772x_mask_set(client, COM8,
|
||||
BNDF_ON_OFF, BNDF_ON_OFF);
|
||||
if (!ret)
|
||||
@ -704,11 +632,11 @@ static int ov772x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
0xff, val);
|
||||
}
|
||||
if (!ret)
|
||||
priv->band_filter = ctrl->value;
|
||||
break;
|
||||
priv->band_filter = ctrl->val;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ov772x_g_chip_ident(struct v4l2_subdev *sd,
|
||||
@ -822,13 +750,13 @@ static int ov772x_set_params(struct i2c_client *client, u32 *width, u32 *height,
|
||||
goto ov772x_set_fmt_error;
|
||||
|
||||
ret = ov772x_mask_set(client,
|
||||
EDGE_TRSHLD, EDGE_THRESHOLD_MASK,
|
||||
EDGE_TRSHLD, OV772X_EDGE_THRESHOLD_MASK,
|
||||
priv->info->edgectrl.threshold);
|
||||
if (ret < 0)
|
||||
goto ov772x_set_fmt_error;
|
||||
|
||||
ret = ov772x_mask_set(client,
|
||||
EDGE_STRNGT, EDGE_STRENGTH_MASK,
|
||||
EDGE_STRNGT, OV772X_EDGE_STRENGTH_MASK,
|
||||
priv->info->edgectrl.strength);
|
||||
if (ret < 0)
|
||||
goto ov772x_set_fmt_error;
|
||||
@ -840,13 +768,13 @@ static int ov772x_set_params(struct i2c_client *client, u32 *width, u32 *height,
|
||||
* set upper and lower limit
|
||||
*/
|
||||
ret = ov772x_mask_set(client,
|
||||
EDGE_UPPER, EDGE_UPPER_MASK,
|
||||
EDGE_UPPER, OV772X_EDGE_UPPER_MASK,
|
||||
priv->info->edgectrl.upper);
|
||||
if (ret < 0)
|
||||
goto ov772x_set_fmt_error;
|
||||
|
||||
ret = ov772x_mask_set(client,
|
||||
EDGE_LOWER, EDGE_LOWER_MASK,
|
||||
EDGE_LOWER, OV772X_EDGE_LOWER_MASK,
|
||||
priv->info->edgectrl.lower);
|
||||
if (ret < 0)
|
||||
goto ov772x_set_fmt_error;
|
||||
@ -1025,17 +953,12 @@ static int ov772x_try_fmt(struct v4l2_subdev *sd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ov772x_video_probe(struct soc_camera_device *icd,
|
||||
struct i2c_client *client)
|
||||
static int ov772x_video_probe(struct i2c_client *client)
|
||||
{
|
||||
struct ov772x_priv *priv = to_ov772x(client);
|
||||
u8 pid, ver;
|
||||
const char *devname;
|
||||
|
||||
/* We must have a parent by now. And it cannot be a wrong one. */
|
||||
BUG_ON(!icd->parent ||
|
||||
to_soc_camera_host(icd->parent)->nr != icd->iface);
|
||||
|
||||
/*
|
||||
* check and show product ID and manufacturer ID
|
||||
*/
|
||||
@ -1064,20 +987,14 @@ static int ov772x_video_probe(struct soc_camera_device *icd,
|
||||
ver,
|
||||
i2c_smbus_read_byte_data(client, MIDH),
|
||||
i2c_smbus_read_byte_data(client, MIDL));
|
||||
|
||||
return 0;
|
||||
return v4l2_ctrl_handler_setup(&priv->hdl);
|
||||
}
|
||||
|
||||
static struct soc_camera_ops ov772x_ops = {
|
||||
.set_bus_param = ov772x_set_bus_param,
|
||||
.query_bus_param = ov772x_query_bus_param,
|
||||
.controls = ov772x_controls,
|
||||
.num_controls = ARRAY_SIZE(ov772x_controls),
|
||||
static const struct v4l2_ctrl_ops ov772x_ctrl_ops = {
|
||||
.s_ctrl = ov772x_s_ctrl,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_core_ops ov772x_subdev_core_ops = {
|
||||
.g_ctrl = ov772x_g_ctrl,
|
||||
.s_ctrl = ov772x_s_ctrl,
|
||||
.g_chip_ident = ov772x_g_chip_ident,
|
||||
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
||||
.g_register = ov772x_g_register,
|
||||
@ -1095,6 +1012,21 @@ static int ov772x_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ov772x_g_mbus_config(struct v4l2_subdev *sd,
|
||||
struct v4l2_mbus_config *cfg)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
|
||||
cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
|
||||
V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
|
||||
V4L2_MBUS_DATA_ACTIVE_HIGH;
|
||||
cfg->type = V4L2_MBUS_PARALLEL;
|
||||
cfg->flags = soc_camera_apply_board_flags(icl, cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct v4l2_subdev_video_ops ov772x_subdev_video_ops = {
|
||||
.s_stream = ov772x_s_stream,
|
||||
.g_mbus_fmt = ov772x_g_fmt,
|
||||
@ -1103,6 +1035,7 @@ static struct v4l2_subdev_video_ops ov772x_subdev_video_ops = {
|
||||
.cropcap = ov772x_cropcap,
|
||||
.g_crop = ov772x_g_crop,
|
||||
.enum_mbus_fmt = ov772x_enum_fmt,
|
||||
.g_mbus_config = ov772x_g_mbus_config,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_ops ov772x_subdev_ops = {
|
||||
@ -1117,21 +1050,16 @@ static struct v4l2_subdev_ops ov772x_subdev_ops = {
|
||||
static int ov772x_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *did)
|
||||
{
|
||||
struct ov772x_priv *priv;
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct soc_camera_link *icl;
|
||||
int ret;
|
||||
struct ov772x_priv *priv;
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
int ret;
|
||||
|
||||
if (!icd) {
|
||||
dev_err(&client->dev, "OV772X: missing soc-camera data!\n");
|
||||
if (!icl || !icl->priv) {
|
||||
dev_err(&client->dev, "OV772X: missing platform data!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
icl = to_soc_camera_link(icd);
|
||||
if (!icl || !icl->priv)
|
||||
return -EINVAL;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
|
||||
dev_err(&adapter->dev,
|
||||
"I2C-Adapter doesn't support "
|
||||
@ -1146,12 +1074,24 @@ static int ov772x_probe(struct i2c_client *client,
|
||||
priv->info = icl->priv;
|
||||
|
||||
v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops);
|
||||
v4l2_ctrl_handler_init(&priv->hdl, 3);
|
||||
v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
|
||||
V4L2_CID_VFLIP, 0, 1, 1, 0);
|
||||
v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
|
||||
V4L2_CID_HFLIP, 0, 1, 1, 0);
|
||||
v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
|
||||
V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0);
|
||||
priv->subdev.ctrl_handler = &priv->hdl;
|
||||
if (priv->hdl.error) {
|
||||
int err = priv->hdl.error;
|
||||
|
||||
icd->ops = &ov772x_ops;
|
||||
kfree(priv);
|
||||
return err;
|
||||
}
|
||||
|
||||
ret = ov772x_video_probe(icd, client);
|
||||
ret = ov772x_video_probe(client);
|
||||
if (ret) {
|
||||
icd->ops = NULL;
|
||||
v4l2_ctrl_handler_free(&priv->hdl);
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
@ -1161,9 +1101,9 @@ static int ov772x_probe(struct i2c_client *client,
|
||||
static int ov772x_remove(struct i2c_client *client)
|
||||
{
|
||||
struct ov772x_priv *priv = to_ov772x(client);
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
|
||||
icd->ops = NULL;
|
||||
v4l2_device_unregister_subdev(&priv->subdev);
|
||||
v4l2_ctrl_handler_free(&priv->hdl);
|
||||
kfree(priv);
|
||||
return 0;
|
||||
}
|
||||
|
@ -24,10 +24,13 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/v4l2-mediabus.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include <media/soc_camera.h>
|
||||
#include <media/v4l2-chip-ident.h>
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/soc_camera.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
|
||||
#include "ov9640.h"
|
||||
|
||||
@ -162,27 +165,6 @@ static enum v4l2_mbus_pixelcode ov9640_codes[] = {
|
||||
V4L2_MBUS_FMT_RGB565_2X8_LE,
|
||||
};
|
||||
|
||||
static const struct v4l2_queryctrl ov9640_controls[] = {
|
||||
{
|
||||
.id = V4L2_CID_VFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Flip Vertically",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_HFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Flip Horizontally",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
};
|
||||
|
||||
/* read a register */
|
||||
static int ov9640_reg_read(struct i2c_client *client, u8 reg, u8 *val)
|
||||
{
|
||||
@ -284,75 +266,25 @@ static int ov9640_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Alter bus settings on camera side */
|
||||
static int ov9640_set_bus_param(struct soc_camera_device *icd,
|
||||
unsigned long flags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Request bus settings on camera side */
|
||||
static unsigned long ov9640_query_bus_param(struct soc_camera_device *icd)
|
||||
{
|
||||
struct soc_camera_link *icl = to_soc_camera_link(icd);
|
||||
|
||||
/*
|
||||
* REVISIT: the camera probably can do 10 bit transfers, but I don't
|
||||
* have those pins connected on my hardware.
|
||||
*/
|
||||
unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
|
||||
SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
|
||||
SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
|
||||
|
||||
return soc_camera_apply_sensor_flags(icl, flags);
|
||||
}
|
||||
|
||||
/* Get status of additional camera capabilities */
|
||||
static int ov9640_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
{
|
||||
struct ov9640_priv *priv = to_ov9640_sensor(sd);
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_VFLIP:
|
||||
ctrl->value = priv->flag_vflip;
|
||||
break;
|
||||
case V4L2_CID_HFLIP:
|
||||
ctrl->value = priv->flag_hflip;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set status of additional camera capabilities */
|
||||
static int ov9640_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
static int ov9640_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct ov9640_priv *priv = to_ov9640_sensor(sd);
|
||||
|
||||
int ret = 0;
|
||||
struct ov9640_priv *priv = container_of(ctrl->handler, struct ov9640_priv, hdl);
|
||||
struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev);
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_VFLIP:
|
||||
priv->flag_vflip = ctrl->value;
|
||||
if (ctrl->value)
|
||||
ret = ov9640_reg_rmw(client, OV9640_MVFP,
|
||||
if (ctrl->val)
|
||||
return ov9640_reg_rmw(client, OV9640_MVFP,
|
||||
OV9640_MVFP_V, 0);
|
||||
else
|
||||
ret = ov9640_reg_rmw(client, OV9640_MVFP,
|
||||
0, OV9640_MVFP_V);
|
||||
break;
|
||||
return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_V);
|
||||
case V4L2_CID_HFLIP:
|
||||
priv->flag_hflip = ctrl->value;
|
||||
if (ctrl->value)
|
||||
ret = ov9640_reg_rmw(client, OV9640_MVFP,
|
||||
if (ctrl->val)
|
||||
return ov9640_reg_rmw(client, OV9640_MVFP,
|
||||
OV9640_MVFP_H, 0);
|
||||
else
|
||||
ret = ov9640_reg_rmw(client, OV9640_MVFP,
|
||||
0, OV9640_MVFP_H);
|
||||
break;
|
||||
return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_H);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Get chip identification */
|
||||
@ -646,10 +578,7 @@ static int ov9640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int ov9640_video_probe(struct soc_camera_device *icd,
|
||||
struct i2c_client *client)
|
||||
static int ov9640_video_probe(struct i2c_client *client)
|
||||
{
|
||||
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
||||
struct ov9640_priv *priv = to_ov9640_sensor(sd);
|
||||
@ -657,29 +586,19 @@ static int ov9640_video_probe(struct soc_camera_device *icd,
|
||||
const char *devname;
|
||||
int ret = 0;
|
||||
|
||||
/* We must have a parent by now. And it cannot be a wrong one. */
|
||||
BUG_ON(!icd->parent ||
|
||||
to_soc_camera_host(icd->parent)->nr != icd->iface);
|
||||
|
||||
/*
|
||||
* check and show product ID and manufacturer ID
|
||||
*/
|
||||
|
||||
ret = ov9640_reg_read(client, OV9640_PID, &pid);
|
||||
if (!ret)
|
||||
ret = ov9640_reg_read(client, OV9640_VER, &ver);
|
||||
if (!ret)
|
||||
ret = ov9640_reg_read(client, OV9640_MIDH, &midh);
|
||||
if (!ret)
|
||||
ret = ov9640_reg_read(client, OV9640_MIDL, &midl);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = ov9640_reg_read(client, OV9640_VER, &ver);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = ov9640_reg_read(client, OV9640_MIDH, &midh);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = ov9640_reg_read(client, OV9640_MIDL, &midl);
|
||||
if (ret)
|
||||
goto err;
|
||||
return ret;
|
||||
|
||||
switch (VERSION(pid, ver)) {
|
||||
case OV9640_V2:
|
||||
@ -693,27 +612,20 @@ static int ov9640_video_probe(struct soc_camera_device *icd,
|
||||
break;
|
||||
default:
|
||||
dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver);
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
|
||||
devname, pid, ver, midh, midl);
|
||||
|
||||
err:
|
||||
return ret;
|
||||
return v4l2_ctrl_handler_setup(&priv->hdl);
|
||||
}
|
||||
|
||||
static struct soc_camera_ops ov9640_ops = {
|
||||
.set_bus_param = ov9640_set_bus_param,
|
||||
.query_bus_param = ov9640_query_bus_param,
|
||||
.controls = ov9640_controls,
|
||||
.num_controls = ARRAY_SIZE(ov9640_controls),
|
||||
static const struct v4l2_ctrl_ops ov9640_ctrl_ops = {
|
||||
.s_ctrl = ov9640_s_ctrl,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_core_ops ov9640_core_ops = {
|
||||
.g_ctrl = ov9640_g_ctrl,
|
||||
.s_ctrl = ov9640_s_ctrl,
|
||||
.g_chip_ident = ov9640_g_chip_ident,
|
||||
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
||||
.g_register = ov9640_get_register,
|
||||
@ -722,6 +634,22 @@ static struct v4l2_subdev_core_ops ov9640_core_ops = {
|
||||
|
||||
};
|
||||
|
||||
/* Request bus settings on camera side */
|
||||
static int ov9640_g_mbus_config(struct v4l2_subdev *sd,
|
||||
struct v4l2_mbus_config *cfg)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
|
||||
cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
|
||||
V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
|
||||
V4L2_MBUS_DATA_ACTIVE_HIGH;
|
||||
cfg->type = V4L2_MBUS_PARALLEL;
|
||||
cfg->flags = soc_camera_apply_board_flags(icl, cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct v4l2_subdev_video_ops ov9640_video_ops = {
|
||||
.s_stream = ov9640_s_stream,
|
||||
.s_mbus_fmt = ov9640_s_fmt,
|
||||
@ -729,7 +657,7 @@ static struct v4l2_subdev_video_ops ov9640_video_ops = {
|
||||
.enum_mbus_fmt = ov9640_enum_fmt,
|
||||
.cropcap = ov9640_cropcap,
|
||||
.g_crop = ov9640_g_crop,
|
||||
|
||||
.g_mbus_config = ov9640_g_mbus_config,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_ops ov9640_subdev_ops = {
|
||||
@ -744,16 +672,9 @@ static int ov9640_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *did)
|
||||
{
|
||||
struct ov9640_priv *priv;
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
struct soc_camera_link *icl;
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
int ret;
|
||||
|
||||
if (!icd) {
|
||||
dev_err(&client->dev, "Missing soc-camera data!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
icl = to_soc_camera_link(icd);
|
||||
if (!icl) {
|
||||
dev_err(&client->dev, "Missing platform_data for driver\n");
|
||||
return -EINVAL;
|
||||
@ -768,12 +689,23 @@ static int ov9640_probe(struct i2c_client *client,
|
||||
|
||||
v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops);
|
||||
|
||||
icd->ops = &ov9640_ops;
|
||||
v4l2_ctrl_handler_init(&priv->hdl, 2);
|
||||
v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops,
|
||||
V4L2_CID_VFLIP, 0, 1, 1, 0);
|
||||
v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops,
|
||||
V4L2_CID_HFLIP, 0, 1, 1, 0);
|
||||
priv->subdev.ctrl_handler = &priv->hdl;
|
||||
if (priv->hdl.error) {
|
||||
int err = priv->hdl.error;
|
||||
|
||||
ret = ov9640_video_probe(icd, client);
|
||||
kfree(priv);
|
||||
return err;
|
||||
}
|
||||
|
||||
ret = ov9640_video_probe(client);
|
||||
|
||||
if (ret) {
|
||||
icd->ops = NULL;
|
||||
v4l2_ctrl_handler_free(&priv->hdl);
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
@ -785,6 +717,8 @@ static int ov9640_remove(struct i2c_client *client)
|
||||
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
||||
struct ov9640_priv *priv = to_ov9640_sensor(sd);
|
||||
|
||||
v4l2_device_unregister_subdev(&priv->subdev);
|
||||
v4l2_ctrl_handler_free(&priv->hdl);
|
||||
kfree(priv);
|
||||
return 0;
|
||||
}
|
||||
|
@ -198,12 +198,10 @@ struct ov9640_reg {
|
||||
|
||||
struct ov9640_priv {
|
||||
struct v4l2_subdev subdev;
|
||||
struct v4l2_ctrl_handler hdl;
|
||||
|
||||
int model;
|
||||
int revision;
|
||||
|
||||
bool flag_vflip;
|
||||
bool flag_hflip;
|
||||
};
|
||||
|
||||
#endif /* __DRIVERS_MEDIA_VIDEO_OV9640_H__ */
|
||||
|
@ -14,8 +14,11 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <media/v4l2-chip-ident.h>
|
||||
#include <linux/v4l2-mediabus.h>
|
||||
|
||||
#include <media/soc_camera.h>
|
||||
#include <media/v4l2-chip-ident.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
|
||||
#define to_ov9740(sd) container_of(sd, struct ov9740_priv, subdev)
|
||||
|
||||
@ -192,6 +195,7 @@ struct ov9740_reg {
|
||||
|
||||
struct ov9740_priv {
|
||||
struct v4l2_subdev subdev;
|
||||
struct v4l2_ctrl_handler hdl;
|
||||
|
||||
int ident;
|
||||
u16 model;
|
||||
@ -392,27 +396,6 @@ static enum v4l2_mbus_pixelcode ov9740_codes[] = {
|
||||
V4L2_MBUS_FMT_YUYV8_2X8,
|
||||
};
|
||||
|
||||
static const struct v4l2_queryctrl ov9740_controls[] = {
|
||||
{
|
||||
.id = V4L2_CID_VFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Flip Vertically",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_HFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Flip Horizontally",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
};
|
||||
|
||||
/* read a register */
|
||||
static int ov9740_reg_read(struct i2c_client *client, u16 reg, u8 *val)
|
||||
{
|
||||
@ -560,25 +543,6 @@ static int ov9740_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Alter bus settings on camera side */
|
||||
static int ov9740_set_bus_param(struct soc_camera_device *icd,
|
||||
unsigned long flags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Request bus settings on camera side */
|
||||
static unsigned long ov9740_query_bus_param(struct soc_camera_device *icd)
|
||||
{
|
||||
struct soc_camera_link *icl = to_soc_camera_link(icd);
|
||||
|
||||
unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
|
||||
SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
|
||||
SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
|
||||
|
||||
return soc_camera_apply_sensor_flags(icl, flags);
|
||||
}
|
||||
|
||||
/* select nearest higher resolution for capture */
|
||||
static void ov9740_res_roundup(u32 *width, u32 *height)
|
||||
{
|
||||
@ -788,36 +752,18 @@ static int ov9740_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get status of additional camera capabilities */
|
||||
static int ov9740_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
{
|
||||
struct ov9740_priv *priv = to_ov9740(sd);
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_VFLIP:
|
||||
ctrl->value = priv->flag_vflip;
|
||||
break;
|
||||
case V4L2_CID_HFLIP:
|
||||
ctrl->value = priv->flag_hflip;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set status of additional camera capabilities */
|
||||
static int ov9740_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
static int ov9740_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct ov9740_priv *priv = to_ov9740(sd);
|
||||
struct ov9740_priv *priv =
|
||||
container_of(ctrl->handler, struct ov9740_priv, hdl);
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_VFLIP:
|
||||
priv->flag_vflip = ctrl->value;
|
||||
priv->flag_vflip = ctrl->val;
|
||||
break;
|
||||
case V4L2_CID_HFLIP:
|
||||
priv->flag_hflip = ctrl->value;
|
||||
priv->flag_hflip = ctrl->val;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -890,18 +836,13 @@ static int ov9740_set_register(struct v4l2_subdev *sd,
|
||||
}
|
||||
#endif
|
||||
|
||||
static int ov9740_video_probe(struct soc_camera_device *icd,
|
||||
struct i2c_client *client)
|
||||
static int ov9740_video_probe(struct i2c_client *client)
|
||||
{
|
||||
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
||||
struct ov9740_priv *priv = to_ov9740(sd);
|
||||
u8 modelhi, modello;
|
||||
int ret;
|
||||
|
||||
/* We must have a parent by now. And it cannot be a wrong one. */
|
||||
BUG_ON(!icd->parent ||
|
||||
to_soc_camera_host(icd->parent)->nr != icd->iface);
|
||||
|
||||
/*
|
||||
* check and show product ID and manufacturer ID
|
||||
*/
|
||||
@ -942,25 +883,33 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct soc_camera_ops ov9740_ops = {
|
||||
.set_bus_param = ov9740_set_bus_param,
|
||||
.query_bus_param = ov9740_query_bus_param,
|
||||
.controls = ov9740_controls,
|
||||
.num_controls = ARRAY_SIZE(ov9740_controls),
|
||||
};
|
||||
/* Request bus settings on camera side */
|
||||
static int ov9740_g_mbus_config(struct v4l2_subdev *sd,
|
||||
struct v4l2_mbus_config *cfg)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
|
||||
cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
|
||||
V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
|
||||
V4L2_MBUS_DATA_ACTIVE_HIGH;
|
||||
cfg->type = V4L2_MBUS_PARALLEL;
|
||||
cfg->flags = soc_camera_apply_board_flags(icl, cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct v4l2_subdev_video_ops ov9740_video_ops = {
|
||||
.s_stream = ov9740_s_stream,
|
||||
.s_mbus_fmt = ov9740_s_fmt,
|
||||
.try_mbus_fmt = ov9740_try_fmt,
|
||||
.enum_mbus_fmt = ov9740_enum_fmt,
|
||||
.cropcap = ov9740_cropcap,
|
||||
.g_crop = ov9740_g_crop,
|
||||
.s_stream = ov9740_s_stream,
|
||||
.s_mbus_fmt = ov9740_s_fmt,
|
||||
.try_mbus_fmt = ov9740_try_fmt,
|
||||
.enum_mbus_fmt = ov9740_enum_fmt,
|
||||
.cropcap = ov9740_cropcap,
|
||||
.g_crop = ov9740_g_crop,
|
||||
.g_mbus_config = ov9740_g_mbus_config,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_core_ops ov9740_core_ops = {
|
||||
.g_ctrl = ov9740_g_ctrl,
|
||||
.s_ctrl = ov9740_s_ctrl,
|
||||
.g_chip_ident = ov9740_g_chip_ident,
|
||||
.s_power = ov9740_s_power,
|
||||
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
||||
@ -974,6 +923,10 @@ static struct v4l2_subdev_ops ov9740_subdev_ops = {
|
||||
.video = &ov9740_video_ops,
|
||||
};
|
||||
|
||||
static const struct v4l2_ctrl_ops ov9740_ctrl_ops = {
|
||||
.s_ctrl = ov9740_s_ctrl,
|
||||
};
|
||||
|
||||
/*
|
||||
* i2c_driver function
|
||||
*/
|
||||
@ -981,16 +934,9 @@ static int ov9740_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *did)
|
||||
{
|
||||
struct ov9740_priv *priv;
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
struct soc_camera_link *icl;
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
int ret;
|
||||
|
||||
if (!icd) {
|
||||
dev_err(&client->dev, "Missing soc-camera data!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
icl = to_soc_camera_link(icd);
|
||||
if (!icl) {
|
||||
dev_err(&client->dev, "Missing platform_data for driver\n");
|
||||
return -EINVAL;
|
||||
@ -1003,12 +949,24 @@ static int ov9740_probe(struct i2c_client *client,
|
||||
}
|
||||
|
||||
v4l2_i2c_subdev_init(&priv->subdev, client, &ov9740_subdev_ops);
|
||||
v4l2_ctrl_handler_init(&priv->hdl, 13);
|
||||
v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops,
|
||||
V4L2_CID_VFLIP, 0, 1, 1, 0);
|
||||
v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops,
|
||||
V4L2_CID_HFLIP, 0, 1, 1, 0);
|
||||
priv->subdev.ctrl_handler = &priv->hdl;
|
||||
if (priv->hdl.error) {
|
||||
int err = priv->hdl.error;
|
||||
|
||||
icd->ops = &ov9740_ops;
|
||||
kfree(priv);
|
||||
return err;
|
||||
}
|
||||
|
||||
ret = ov9740_video_probe(icd, client);
|
||||
ret = ov9740_video_probe(client);
|
||||
if (!ret)
|
||||
ret = v4l2_ctrl_handler_setup(&priv->hdl);
|
||||
if (ret < 0) {
|
||||
icd->ops = NULL;
|
||||
v4l2_ctrl_handler_free(&priv->hdl);
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
@ -1019,8 +977,9 @@ static int ov9740_remove(struct i2c_client *client)
|
||||
{
|
||||
struct ov9740_priv *priv = i2c_get_clientdata(client);
|
||||
|
||||
v4l2_device_unregister_subdev(&priv->subdev);
|
||||
v4l2_ctrl_handler_free(&priv->hdl);
|
||||
kfree(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -744,9 +744,9 @@ static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
/***************************************************************************/
|
||||
/* Videobuf2 operations */
|
||||
|
||||
static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
|
||||
unsigned int *nplanes, unsigned int sizes[],
|
||||
void *alloc_ctxs[])
|
||||
static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
|
||||
unsigned int *nbuffers, unsigned int *nplanes,
|
||||
unsigned int sizes[], void *alloc_ctxs[])
|
||||
{
|
||||
struct pwc_device *pdev = vb2_get_drv_priv(vq);
|
||||
|
||||
|
@ -214,6 +214,7 @@ struct pxa_camera_dev {
|
||||
unsigned long ciclk;
|
||||
unsigned long mclk;
|
||||
u32 mclk_divisor;
|
||||
u16 width_flags; /* max 10 bits */
|
||||
|
||||
struct list_head capture;
|
||||
|
||||
@ -1020,37 +1021,20 @@ static int test_platform_param(struct pxa_camera_dev *pcdev,
|
||||
* quick capture interface supports both.
|
||||
*/
|
||||
*flags = (pcdev->platform_flags & PXA_CAMERA_MASTER ?
|
||||
SOCAM_MASTER : SOCAM_SLAVE) |
|
||||
SOCAM_HSYNC_ACTIVE_HIGH |
|
||||
SOCAM_HSYNC_ACTIVE_LOW |
|
||||
SOCAM_VSYNC_ACTIVE_HIGH |
|
||||
SOCAM_VSYNC_ACTIVE_LOW |
|
||||
SOCAM_DATA_ACTIVE_HIGH |
|
||||
SOCAM_PCLK_SAMPLE_RISING |
|
||||
SOCAM_PCLK_SAMPLE_FALLING;
|
||||
V4L2_MBUS_MASTER : V4L2_MBUS_SLAVE) |
|
||||
V4L2_MBUS_HSYNC_ACTIVE_HIGH |
|
||||
V4L2_MBUS_HSYNC_ACTIVE_LOW |
|
||||
V4L2_MBUS_VSYNC_ACTIVE_HIGH |
|
||||
V4L2_MBUS_VSYNC_ACTIVE_LOW |
|
||||
V4L2_MBUS_DATA_ACTIVE_HIGH |
|
||||
V4L2_MBUS_PCLK_SAMPLE_RISING |
|
||||
V4L2_MBUS_PCLK_SAMPLE_FALLING;
|
||||
|
||||
/* If requested data width is supported by the platform, use it */
|
||||
switch (buswidth) {
|
||||
case 10:
|
||||
if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_10))
|
||||
return -EINVAL;
|
||||
*flags |= SOCAM_DATAWIDTH_10;
|
||||
break;
|
||||
case 9:
|
||||
if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_9))
|
||||
return -EINVAL;
|
||||
*flags |= SOCAM_DATAWIDTH_9;
|
||||
break;
|
||||
case 8:
|
||||
if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8))
|
||||
return -EINVAL;
|
||||
*flags |= SOCAM_DATAWIDTH_8;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if ((1 << (buswidth - 1)) & pcdev->width_flags)
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
|
||||
@ -1070,12 +1054,12 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
|
||||
* Datawidth is now guaranteed to be equal to one of the three values.
|
||||
* We fix bit-per-pixel equal to data-width...
|
||||
*/
|
||||
switch (flags & SOCAM_DATAWIDTH_MASK) {
|
||||
case SOCAM_DATAWIDTH_10:
|
||||
switch (icd->current_fmt->host_fmt->bits_per_sample) {
|
||||
case 10:
|
||||
dw = 4;
|
||||
bpp = 0x40;
|
||||
break;
|
||||
case SOCAM_DATAWIDTH_9:
|
||||
case 9:
|
||||
dw = 3;
|
||||
bpp = 0x20;
|
||||
break;
|
||||
@ -1084,7 +1068,7 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
|
||||
* Actually it can only be 8 now,
|
||||
* default is just to silence compiler warnings
|
||||
*/
|
||||
case SOCAM_DATAWIDTH_8:
|
||||
case 8:
|
||||
dw = 2;
|
||||
bpp = 0;
|
||||
}
|
||||
@ -1093,11 +1077,11 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
|
||||
cicr4 |= CICR4_PCLK_EN;
|
||||
if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN)
|
||||
cicr4 |= CICR4_MCLK_EN;
|
||||
if (flags & SOCAM_PCLK_SAMPLE_FALLING)
|
||||
if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
|
||||
cicr4 |= CICR4_PCP;
|
||||
if (flags & SOCAM_HSYNC_ACTIVE_LOW)
|
||||
if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
|
||||
cicr4 |= CICR4_HSP;
|
||||
if (flags & SOCAM_VSYNC_ACTIVE_LOW)
|
||||
if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
|
||||
cicr4 |= CICR4_VSP;
|
||||
|
||||
cicr0 = __raw_readl(pcdev->base + CICR0);
|
||||
@ -1151,9 +1135,11 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
|
||||
|
||||
static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
|
||||
{
|
||||
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
|
||||
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
||||
struct pxa_camera_dev *pcdev = ici->priv;
|
||||
unsigned long bus_flags, camera_flags, common_flags;
|
||||
struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
|
||||
unsigned long bus_flags, common_flags;
|
||||
int ret;
|
||||
struct pxa_cam *cam = icd->host_priv;
|
||||
|
||||
@ -1162,45 +1148,59 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
|
||||
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;
|
||||
ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
|
||||
if (!ret) {
|
||||
common_flags = soc_mbus_config_compatible(&cfg,
|
||||
bus_flags);
|
||||
if (!common_flags) {
|
||||
dev_warn(icd->parent,
|
||||
"Flags incompatible: camera 0x%x, host 0x%lx\n",
|
||||
cfg.flags, bus_flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (ret != -ENOIOCTLCMD) {
|
||||
return ret;
|
||||
} else {
|
||||
common_flags = bus_flags;
|
||||
}
|
||||
|
||||
pcdev->channels = 1;
|
||||
|
||||
/* Make choises, based on platform preferences */
|
||||
if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
|
||||
(common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
|
||||
if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
|
||||
(common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
|
||||
if (pcdev->platform_flags & PXA_CAMERA_HSP)
|
||||
common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
|
||||
common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
|
||||
else
|
||||
common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
|
||||
common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
|
||||
}
|
||||
|
||||
if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
|
||||
(common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
|
||||
if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
|
||||
(common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
|
||||
if (pcdev->platform_flags & PXA_CAMERA_VSP)
|
||||
common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
|
||||
common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
|
||||
else
|
||||
common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
|
||||
common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
|
||||
}
|
||||
|
||||
if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
|
||||
(common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
|
||||
if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
|
||||
(common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
|
||||
if (pcdev->platform_flags & PXA_CAMERA_PCP)
|
||||
common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
|
||||
common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
|
||||
else
|
||||
common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
|
||||
common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
@ -1209,17 +1209,31 @@ 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,
|
||||
unsigned char buswidth)
|
||||
{
|
||||
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
|
||||
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
||||
struct pxa_camera_dev *pcdev = ici->priv;
|
||||
unsigned long bus_flags, camera_flags;
|
||||
struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
|
||||
unsigned long bus_flags, common_flags;
|
||||
int ret = test_platform_param(pcdev, buswidth, &bus_flags);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
camera_flags = icd->ops->query_bus_param(icd);
|
||||
ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
|
||||
if (!ret) {
|
||||
common_flags = soc_mbus_config_compatible(&cfg,
|
||||
bus_flags);
|
||||
if (!common_flags) {
|
||||
dev_warn(icd->parent,
|
||||
"Flags incompatible: camera 0x%x, host 0x%lx\n",
|
||||
cfg.flags, bus_flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (ret == -ENOIOCTLCMD) {
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return soc_camera_bus_param_compatible(camera_flags, bus_flags) ? 0 : -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct soc_mbus_pixelfmt pxa_camera_formats[] = {
|
||||
@ -1687,6 +1701,12 @@ static int __devinit pxa_camera_probe(struct platform_device *pdev)
|
||||
"data widths, using default 10 bit\n");
|
||||
pcdev->platform_flags |= PXA_CAMERA_DATAWIDTH_10;
|
||||
}
|
||||
if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8)
|
||||
pcdev->width_flags = 1 << 7;
|
||||
if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_9)
|
||||
pcdev->width_flags |= 1 << 8;
|
||||
if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_10)
|
||||
pcdev->width_flags |= 1 << 9;
|
||||
pcdev->mclk = pcdev->pdata->mclk_10khz * 10000;
|
||||
if (!pcdev->mclk) {
|
||||
dev_warn(&pdev->dev,
|
||||
|
@ -11,13 +11,14 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/v4l2-mediabus.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include <media/rj54n1cb0c.h>
|
||||
#include <media/soc_camera.h>
|
||||
#include <media/soc_mediabus.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
#include <media/v4l2-chip-ident.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
|
||||
#define RJ54N1_DEV_CODE 0x0400
|
||||
#define RJ54N1_DEV_CODE2 0x0401
|
||||
@ -148,6 +149,7 @@ struct rj54n1_clock_div {
|
||||
|
||||
struct rj54n1 {
|
||||
struct v4l2_subdev subdev;
|
||||
struct v4l2_ctrl_handler hdl;
|
||||
struct rj54n1_clock_div clk_div;
|
||||
const struct rj54n1_datafmt *fmt;
|
||||
struct v4l2_rect rect; /* Sensor window */
|
||||
@ -499,31 +501,6 @@ static int rj54n1_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
return reg_set(client, RJ54N1_STILL_CONTROL, (!enable) << 7, 0x80);
|
||||
}
|
||||
|
||||
static int rj54n1_set_bus_param(struct soc_camera_device *icd,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
/* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */
|
||||
|
||||
if (flags & SOCAM_PCLK_SAMPLE_RISING)
|
||||
return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4);
|
||||
else
|
||||
return reg_write(client, RJ54N1_OUT_SIGPO, 0);
|
||||
}
|
||||
|
||||
static unsigned long rj54n1_query_bus_param(struct soc_camera_device *icd)
|
||||
{
|
||||
struct soc_camera_link *icl = to_soc_camera_link(icd);
|
||||
const unsigned long flags =
|
||||
SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING |
|
||||
SOCAM_MASTER | SOCAM_DATAWIDTH_8 |
|
||||
SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
|
||||
SOCAM_DATA_ACTIVE_HIGH;
|
||||
|
||||
return soc_camera_apply_sensor_flags(icl, flags);
|
||||
}
|
||||
|
||||
static int rj54n1_set_rect(struct i2c_client *client,
|
||||
u16 reg_x, u16 reg_y, u16 reg_xy,
|
||||
u32 width, u32 height)
|
||||
@ -1202,134 +1179,51 @@ static int rj54n1_s_register(struct v4l2_subdev *sd,
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct v4l2_queryctrl rj54n1_controls[] = {
|
||||
{
|
||||
.id = V4L2_CID_VFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Flip Vertically",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
}, {
|
||||
.id = V4L2_CID_HFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Flip Horizontally",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
}, {
|
||||
.id = V4L2_CID_GAIN,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Gain",
|
||||
.minimum = 0,
|
||||
.maximum = 127,
|
||||
.step = 1,
|
||||
.default_value = 66,
|
||||
.flags = V4L2_CTRL_FLAG_SLIDER,
|
||||
}, {
|
||||
.id = V4L2_CID_AUTO_WHITE_BALANCE,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Auto white balance",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static struct soc_camera_ops rj54n1_ops = {
|
||||
.set_bus_param = rj54n1_set_bus_param,
|
||||
.query_bus_param = rj54n1_query_bus_param,
|
||||
.controls = rj54n1_controls,
|
||||
.num_controls = ARRAY_SIZE(rj54n1_controls),
|
||||
};
|
||||
|
||||
static int rj54n1_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
static int rj54n1_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct rj54n1 *rj54n1 = container_of(ctrl->handler, struct rj54n1, hdl);
|
||||
struct v4l2_subdev *sd = &rj54n1->subdev;
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct rj54n1 *rj54n1 = to_rj54n1(client);
|
||||
int data;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_VFLIP:
|
||||
data = reg_read(client, RJ54N1_MIRROR_STILL_MODE);
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
ctrl->value = !(data & 1);
|
||||
break;
|
||||
case V4L2_CID_HFLIP:
|
||||
data = reg_read(client, RJ54N1_MIRROR_STILL_MODE);
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
ctrl->value = !(data & 2);
|
||||
break;
|
||||
case V4L2_CID_GAIN:
|
||||
data = reg_read(client, RJ54N1_Y_GAIN);
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
|
||||
ctrl->value = data / 2;
|
||||
break;
|
||||
case V4L2_CID_AUTO_WHITE_BALANCE:
|
||||
ctrl->value = rj54n1->auto_wb;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rj54n1_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
{
|
||||
int data;
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct rj54n1 *rj54n1 = to_rj54n1(client);
|
||||
const struct v4l2_queryctrl *qctrl;
|
||||
|
||||
qctrl = soc_camera_find_qctrl(&rj54n1_ops, ctrl->id);
|
||||
if (!qctrl)
|
||||
return -EINVAL;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_VFLIP:
|
||||
if (ctrl->value)
|
||||
if (ctrl->val)
|
||||
data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 1);
|
||||
else
|
||||
data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 1, 1);
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
break;
|
||||
return 0;
|
||||
case V4L2_CID_HFLIP:
|
||||
if (ctrl->value)
|
||||
if (ctrl->val)
|
||||
data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 2);
|
||||
else
|
||||
data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 2, 2);
|
||||
if (data < 0)
|
||||
return -EIO;
|
||||
break;
|
||||
return 0;
|
||||
case V4L2_CID_GAIN:
|
||||
if (ctrl->value > qctrl->maximum ||
|
||||
ctrl->value < qctrl->minimum)
|
||||
return -EINVAL;
|
||||
else if (reg_write(client, RJ54N1_Y_GAIN, ctrl->value * 2) < 0)
|
||||
if (reg_write(client, RJ54N1_Y_GAIN, ctrl->val * 2) < 0)
|
||||
return -EIO;
|
||||
break;
|
||||
return 0;
|
||||
case V4L2_CID_AUTO_WHITE_BALANCE:
|
||||
/* Auto WB area - whole image */
|
||||
if (reg_set(client, RJ54N1_WB_SEL_WEIGHT_I, ctrl->value << 7,
|
||||
if (reg_set(client, RJ54N1_WB_SEL_WEIGHT_I, ctrl->val << 7,
|
||||
0x80) < 0)
|
||||
return -EIO;
|
||||
rj54n1->auto_wb = ctrl->value;
|
||||
break;
|
||||
rj54n1->auto_wb = ctrl->val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct v4l2_ctrl_ops rj54n1_ctrl_ops = {
|
||||
.s_ctrl = rj54n1_s_ctrl,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = {
|
||||
.g_ctrl = rj54n1_g_ctrl,
|
||||
.s_ctrl = rj54n1_s_ctrl,
|
||||
.g_chip_ident = rj54n1_g_chip_ident,
|
||||
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
||||
.g_register = rj54n1_g_register,
|
||||
@ -1337,6 +1231,36 @@ static struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = {
|
||||
#endif
|
||||
};
|
||||
|
||||
static int rj54n1_g_mbus_config(struct v4l2_subdev *sd,
|
||||
struct v4l2_mbus_config *cfg)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
|
||||
cfg->flags =
|
||||
V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
|
||||
V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH |
|
||||
V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH;
|
||||
cfg->type = V4L2_MBUS_PARALLEL;
|
||||
cfg->flags = soc_camera_apply_board_flags(icl, cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rj54n1_s_mbus_config(struct v4l2_subdev *sd,
|
||||
const struct v4l2_mbus_config *cfg)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
|
||||
/* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */
|
||||
if (soc_camera_apply_board_flags(icl, cfg) &
|
||||
V4L2_MBUS_PCLK_SAMPLE_RISING)
|
||||
return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4);
|
||||
else
|
||||
return reg_write(client, RJ54N1_OUT_SIGPO, 0);
|
||||
}
|
||||
|
||||
static struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = {
|
||||
.s_stream = rj54n1_s_stream,
|
||||
.s_mbus_fmt = rj54n1_s_fmt,
|
||||
@ -1346,6 +1270,8 @@ static struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = {
|
||||
.g_crop = rj54n1_g_crop,
|
||||
.s_crop = rj54n1_s_crop,
|
||||
.cropcap = rj54n1_cropcap,
|
||||
.g_mbus_config = rj54n1_g_mbus_config,
|
||||
.s_mbus_config = rj54n1_s_mbus_config,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_ops rj54n1_subdev_ops = {
|
||||
@ -1357,17 +1283,12 @@ static struct v4l2_subdev_ops rj54n1_subdev_ops = {
|
||||
* Interface active, can use i2c. If it fails, it can indeed mean, that
|
||||
* this wasn't our capture interface, so, we wait for the right one
|
||||
*/
|
||||
static int rj54n1_video_probe(struct soc_camera_device *icd,
|
||||
struct i2c_client *client,
|
||||
static int rj54n1_video_probe(struct i2c_client *client,
|
||||
struct rj54n1_pdata *priv)
|
||||
{
|
||||
int data1, data2;
|
||||
int ret;
|
||||
|
||||
/* We must have a parent by now. And it cannot be a wrong one. */
|
||||
BUG_ON(!icd->parent ||
|
||||
to_soc_camera_host(icd->parent)->nr != icd->iface);
|
||||
|
||||
/* Read out the chip version register */
|
||||
data1 = reg_read(client, RJ54N1_DEV_CODE);
|
||||
data2 = reg_read(client, RJ54N1_DEV_CODE2);
|
||||
@ -1395,18 +1316,11 @@ static int rj54n1_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *did)
|
||||
{
|
||||
struct rj54n1 *rj54n1;
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct soc_camera_link *icl;
|
||||
struct rj54n1_pdata *rj54n1_priv;
|
||||
int ret;
|
||||
|
||||
if (!icd) {
|
||||
dev_err(&client->dev, "RJ54N1CB0C: missing soc-camera data!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
icl = to_soc_camera_link(icd);
|
||||
if (!icl || !icl->priv) {
|
||||
dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n");
|
||||
return -EINVAL;
|
||||
@ -1425,8 +1339,22 @@ static int rj54n1_probe(struct i2c_client *client,
|
||||
return -ENOMEM;
|
||||
|
||||
v4l2_i2c_subdev_init(&rj54n1->subdev, client, &rj54n1_subdev_ops);
|
||||
v4l2_ctrl_handler_init(&rj54n1->hdl, 4);
|
||||
v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
|
||||
V4L2_CID_VFLIP, 0, 1, 1, 0);
|
||||
v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
|
||||
V4L2_CID_HFLIP, 0, 1, 1, 0);
|
||||
v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
|
||||
V4L2_CID_GAIN, 0, 127, 1, 66);
|
||||
v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
|
||||
V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
|
||||
rj54n1->subdev.ctrl_handler = &rj54n1->hdl;
|
||||
if (rj54n1->hdl.error) {
|
||||
int err = rj54n1->hdl.error;
|
||||
|
||||
icd->ops = &rj54n1_ops;
|
||||
kfree(rj54n1);
|
||||
return err;
|
||||
}
|
||||
|
||||
rj54n1->clk_div = clk_div;
|
||||
rj54n1->rect.left = RJ54N1_COLUMN_SKIP;
|
||||
@ -1440,25 +1368,24 @@ static int rj54n1_probe(struct i2c_client *client,
|
||||
rj54n1->tgclk_mhz = (rj54n1_priv->mclk_freq / PLL_L * PLL_N) /
|
||||
(clk_div.ratio_tg + 1) / (clk_div.ratio_t + 1);
|
||||
|
||||
ret = rj54n1_video_probe(icd, client, rj54n1_priv);
|
||||
ret = rj54n1_video_probe(client, rj54n1_priv);
|
||||
if (ret < 0) {
|
||||
icd->ops = NULL;
|
||||
v4l2_ctrl_handler_free(&rj54n1->hdl);
|
||||
kfree(rj54n1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return v4l2_ctrl_handler_setup(&rj54n1->hdl);
|
||||
}
|
||||
|
||||
static int rj54n1_remove(struct i2c_client *client)
|
||||
{
|
||||
struct rj54n1 *rj54n1 = to_rj54n1(client);
|
||||
struct soc_camera_device *icd = client->dev.platform_data;
|
||||
struct soc_camera_link *icl = to_soc_camera_link(icd);
|
||||
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
|
||||
|
||||
icd->ops = NULL;
|
||||
v4l2_device_unregister_subdev(&rj54n1->subdev);
|
||||
if (icl->free_bus)
|
||||
icl->free_bus(icl);
|
||||
v4l2_ctrl_handler_free(&rj54n1->hdl);
|
||||
kfree(rj54n1);
|
||||
|
||||
return 0;
|
||||
|
1680
drivers/media/video/s5k6aa.c
Normal file
1680
drivers/media/video/s5k6aa.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -246,9 +246,9 @@ static unsigned int get_plane_size(struct fimc_frame *fr, unsigned int plane)
|
||||
return fr->f_width * fr->f_height * fr->fmt->depth[plane] / 8;
|
||||
}
|
||||
|
||||
static int queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
|
||||
unsigned int *num_planes, unsigned int sizes[],
|
||||
void *allocators[])
|
||||
static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
|
||||
unsigned int *num_buffers, unsigned int *num_planes,
|
||||
unsigned int sizes[], void *allocators[])
|
||||
{
|
||||
struct fimc_ctx *ctx = vq->drv_priv;
|
||||
struct fimc_fmt *fmt = ctx->d_frame.fmt;
|
||||
|
@ -670,9 +670,9 @@ static void fimc_job_abort(void *priv)
|
||||
fimc_m2m_shutdown(priv);
|
||||
}
|
||||
|
||||
static int fimc_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
|
||||
unsigned int *num_planes, unsigned int sizes[],
|
||||
void *allocators[])
|
||||
static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
|
||||
unsigned int *num_buffers, unsigned int *num_planes,
|
||||
unsigned int sizes[], void *allocators[])
|
||||
{
|
||||
struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
|
||||
struct fimc_frame *f;
|
||||
|
@ -38,7 +38,7 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev)
|
||||
* into kernel. */
|
||||
mfc_debug_enter();
|
||||
err = request_firmware((const struct firmware **)&fw_blob,
|
||||
"s5pc110-mfc.fw", dev->v4l2_dev.dev);
|
||||
"s5p-mfc.fw", dev->v4l2_dev.dev);
|
||||
if (err != 0) {
|
||||
mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n");
|
||||
return -EINVAL;
|
||||
@ -116,7 +116,7 @@ int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev)
|
||||
* into kernel. */
|
||||
mfc_debug_enter();
|
||||
err = request_firmware((const struct firmware **)&fw_blob,
|
||||
"s5pc110-mfc.fw", dev->v4l2_dev.dev);
|
||||
"s5p-mfc.fw", dev->v4l2_dev.dev);
|
||||
if (err != 0) {
|
||||
mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n");
|
||||
return -EINVAL;
|
||||
|
@ -744,9 +744,10 @@ static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = {
|
||||
.vidioc_g_crop = vidioc_g_crop,
|
||||
};
|
||||
|
||||
static int s5p_mfc_queue_setup(struct vb2_queue *vq, unsigned int *buf_count,
|
||||
unsigned int *plane_count, unsigned int psize[],
|
||||
void *allocators[])
|
||||
static int s5p_mfc_queue_setup(struct vb2_queue *vq,
|
||||
const struct v4l2_format *fmt, unsigned int *buf_count,
|
||||
unsigned int *plane_count, unsigned int psize[],
|
||||
void *allocators[])
|
||||
{
|
||||
struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
|
||||
|
||||
|
@ -1513,8 +1513,9 @@ static int check_vb_with_fmt(struct s5p_mfc_fmt *fmt, struct vb2_buffer *vb)
|
||||
}
|
||||
|
||||
static int s5p_mfc_queue_setup(struct vb2_queue *vq,
|
||||
unsigned int *buf_count, unsigned int *plane_count,
|
||||
unsigned int psize[], void *allocators[])
|
||||
const struct v4l2_format *fmt,
|
||||
unsigned int *buf_count, unsigned int *plane_count,
|
||||
unsigned int psize[], void *allocators[])
|
||||
{
|
||||
struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
|
||||
|
||||
|
@ -727,8 +727,8 @@ static const struct v4l2_file_operations mxr_fops = {
|
||||
.unlocked_ioctl = video_ioctl2,
|
||||
};
|
||||
|
||||
static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
|
||||
unsigned int *nplanes, unsigned int sizes[],
|
||||
static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
|
||||
unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[],
|
||||
void *alloc_ctxs[])
|
||||
{
|
||||
struct mxr_layer *layer = vb2_get_drv_priv(vq);
|
||||
|
@ -843,10 +843,10 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev);
|
||||
int saa7134_ir_start(struct saa7134_dev *dev);
|
||||
void saa7134_ir_stop(struct saa7134_dev *dev);
|
||||
#else
|
||||
#define saa7134_input_init1(dev) (0)
|
||||
#define saa7134_input_fini(dev) (0)
|
||||
#define saa7134_input_irq(dev) (0)
|
||||
#define saa7134_probe_i2c_ir(dev) (0)
|
||||
#define saa7134_ir_start(dev) (0)
|
||||
#define saa7134_ir_stop(dev) (0)
|
||||
#define saa7134_input_init1(dev) ((void)0)
|
||||
#define saa7134_input_fini(dev) ((void)0)
|
||||
#define saa7134_input_irq(dev) ((void)0)
|
||||
#define saa7134_probe_i2c_ir(dev) ((void)0)
|
||||
#define saa7134_ir_start(dev) ((void)0)
|
||||
#define saa7134_ir_stop(dev) ((void)0)
|
||||
#endif
|
||||
|
@ -90,7 +90,6 @@
|
||||
struct sh_mobile_ceu_buffer {
|
||||
struct vb2_buffer vb; /* v4l buffer must be first */
|
||||
struct list_head queue;
|
||||
enum v4l2_mbus_pixelcode code;
|
||||
};
|
||||
|
||||
struct sh_mobile_ceu_dev {
|
||||
@ -100,7 +99,8 @@ struct sh_mobile_ceu_dev {
|
||||
|
||||
unsigned int irq;
|
||||
void __iomem *base;
|
||||
unsigned long video_limit;
|
||||
size_t video_limit;
|
||||
size_t buf_total;
|
||||
|
||||
spinlock_t lock; /* Protects video buffer lists */
|
||||
struct list_head capture;
|
||||
@ -121,7 +121,7 @@ struct sh_mobile_ceu_dev {
|
||||
};
|
||||
|
||||
struct sh_mobile_ceu_cam {
|
||||
/* CEU offsets within scaled by the CEU camera output */
|
||||
/* CEU offsets within the camera output, before the CEU scaler */
|
||||
unsigned int ceu_left;
|
||||
unsigned int ceu_top;
|
||||
/* Client output, as seen by the CEU */
|
||||
@ -144,30 +144,6 @@ static struct sh_mobile_ceu_buffer *to_ceu_vb(struct vb2_buffer *vb)
|
||||
return container_of(vb, struct sh_mobile_ceu_buffer, vb);
|
||||
}
|
||||
|
||||
static unsigned long make_bus_param(struct sh_mobile_ceu_dev *pcdev)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
flags = SOCAM_MASTER |
|
||||
SOCAM_PCLK_SAMPLE_RISING |
|
||||
SOCAM_HSYNC_ACTIVE_HIGH |
|
||||
SOCAM_HSYNC_ACTIVE_LOW |
|
||||
SOCAM_VSYNC_ACTIVE_HIGH |
|
||||
SOCAM_VSYNC_ACTIVE_LOW |
|
||||
SOCAM_DATA_ACTIVE_HIGH;
|
||||
|
||||
if (pcdev->pdata->flags & SH_CEU_FLAG_USE_8BIT_BUS)
|
||||
flags |= SOCAM_DATAWIDTH_8;
|
||||
|
||||
if (pcdev->pdata->flags & SH_CEU_FLAG_USE_16BIT_BUS)
|
||||
flags |= SOCAM_DATAWIDTH_16;
|
||||
|
||||
if (flags & SOCAM_DATAWIDTH_MASK)
|
||||
return flags;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ceu_write(struct sh_mobile_ceu_dev *priv,
|
||||
unsigned long reg_offs, u32 data)
|
||||
{
|
||||
@ -216,33 +192,61 @@ static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
|
||||
/*
|
||||
* Videobuf operations
|
||||
*/
|
||||
|
||||
/*
|
||||
* .queue_setup() is called to check, whether the driver can accept the
|
||||
* requested number of buffers and to fill in plane sizes
|
||||
* for the current frame format if required
|
||||
*/
|
||||
static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq,
|
||||
const struct v4l2_format *fmt,
|
||||
unsigned int *count, unsigned int *num_planes,
|
||||
unsigned int sizes[], void *alloc_ctxs[])
|
||||
{
|
||||
struct soc_camera_device *icd = container_of(vq, struct soc_camera_device, vb2_vidq);
|
||||
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
||||
struct sh_mobile_ceu_dev *pcdev = ici->priv;
|
||||
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
|
||||
icd->current_fmt->host_fmt);
|
||||
int bytes_per_line;
|
||||
unsigned int height;
|
||||
|
||||
if (fmt) {
|
||||
const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd,
|
||||
fmt->fmt.pix.pixelformat);
|
||||
if (!xlate)
|
||||
return -EINVAL;
|
||||
bytes_per_line = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
|
||||
xlate->host_fmt);
|
||||
height = fmt->fmt.pix.height;
|
||||
} else {
|
||||
/* Called from VIDIOC_REQBUFS or in compatibility mode */
|
||||
bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
|
||||
icd->current_fmt->host_fmt);
|
||||
height = icd->user_height;
|
||||
}
|
||||
if (bytes_per_line < 0)
|
||||
return bytes_per_line;
|
||||
|
||||
*num_planes = 1;
|
||||
sizes[0] = bytes_per_line * height;
|
||||
|
||||
pcdev->sequence = 0;
|
||||
sizes[0] = bytes_per_line * icd->user_height;
|
||||
alloc_ctxs[0] = pcdev->alloc_ctx;
|
||||
|
||||
if (!vq->num_buffers)
|
||||
pcdev->sequence = 0;
|
||||
|
||||
if (!*count)
|
||||
*count = 2;
|
||||
|
||||
if (pcdev->video_limit) {
|
||||
if (PAGE_ALIGN(sizes[0]) * *count > pcdev->video_limit)
|
||||
*count = pcdev->video_limit / PAGE_ALIGN(sizes[0]);
|
||||
/* If *num_planes != 0, we have already verified *count. */
|
||||
if (pcdev->video_limit && !*num_planes) {
|
||||
size_t size = PAGE_ALIGN(sizes[0]) * *count;
|
||||
|
||||
if (size + pcdev->buf_total > pcdev->video_limit)
|
||||
*count = (pcdev->video_limit - pcdev->buf_total) /
|
||||
PAGE_ALIGN(sizes[0]);
|
||||
}
|
||||
|
||||
*num_planes = 1;
|
||||
|
||||
dev_dbg(icd->parent, "count=%d, size=%u\n", *count, sizes[0]);
|
||||
|
||||
return 0;
|
||||
@ -267,6 +271,7 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
|
||||
unsigned long top1, top2;
|
||||
unsigned long bottom1, bottom2;
|
||||
u32 status;
|
||||
bool planar;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
@ -314,17 +319,29 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
|
||||
|
||||
phys_addr_top = vb2_dma_contig_plane_dma_addr(pcdev->active, 0);
|
||||
|
||||
ceu_write(pcdev, top1, phys_addr_top);
|
||||
if (V4L2_FIELD_NONE != pcdev->field) {
|
||||
phys_addr_bottom = phys_addr_top + icd->user_width;
|
||||
ceu_write(pcdev, bottom1, phys_addr_bottom);
|
||||
}
|
||||
|
||||
switch (icd->current_fmt->host_fmt->fourcc) {
|
||||
case V4L2_PIX_FMT_NV12:
|
||||
case V4L2_PIX_FMT_NV21:
|
||||
case V4L2_PIX_FMT_NV16:
|
||||
case V4L2_PIX_FMT_NV61:
|
||||
planar = true;
|
||||
break;
|
||||
default:
|
||||
planar = false;
|
||||
}
|
||||
|
||||
ceu_write(pcdev, top1, phys_addr_top);
|
||||
if (V4L2_FIELD_NONE != pcdev->field) {
|
||||
if (planar)
|
||||
phys_addr_bottom = phys_addr_top + icd->user_width;
|
||||
else
|
||||
phys_addr_bottom = phys_addr_top +
|
||||
soc_mbus_bytes_per_line(icd->user_width,
|
||||
icd->current_fmt->host_fmt);
|
||||
ceu_write(pcdev, bottom1, phys_addr_bottom);
|
||||
}
|
||||
|
||||
if (planar) {
|
||||
phys_addr_top += icd->user_width *
|
||||
icd->user_height;
|
||||
ceu_write(pcdev, top2, phys_addr_top);
|
||||
@ -341,44 +358,11 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
|
||||
|
||||
static int sh_mobile_ceu_videobuf_prepare(struct vb2_buffer *vb)
|
||||
{
|
||||
struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq);
|
||||
struct sh_mobile_ceu_buffer *buf;
|
||||
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
|
||||
icd->current_fmt->host_fmt);
|
||||
unsigned long size;
|
||||
|
||||
if (bytes_per_line < 0)
|
||||
return bytes_per_line;
|
||||
|
||||
buf = to_ceu_vb(vb);
|
||||
|
||||
dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
|
||||
vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
|
||||
struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
|
||||
|
||||
/* Added list head initialization on alloc */
|
||||
WARN(!list_empty(&buf->queue), "Buffer %p on queue!\n", vb);
|
||||
|
||||
#ifdef DEBUG
|
||||
/*
|
||||
* This can be useful if you want to see if we actually fill
|
||||
* the buffer with something
|
||||
*/
|
||||
if (vb2_plane_vaddr(vb, 0))
|
||||
memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0));
|
||||
#endif
|
||||
|
||||
BUG_ON(NULL == icd->current_fmt);
|
||||
|
||||
size = icd->user_height * bytes_per_line;
|
||||
|
||||
if (vb2_plane_size(vb, 0) < size) {
|
||||
dev_err(icd->parent, "Buffer too small (%lu < %lu)\n",
|
||||
vb2_plane_size(vb, 0), size);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
vb2_set_plane_payload(vb, 0, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -388,10 +372,35 @@ static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb)
|
||||
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
||||
struct sh_mobile_ceu_dev *pcdev = ici->priv;
|
||||
struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
|
||||
unsigned long size;
|
||||
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
|
||||
icd->current_fmt->host_fmt);
|
||||
|
||||
if (bytes_per_line < 0)
|
||||
goto error;
|
||||
|
||||
size = icd->user_height * bytes_per_line;
|
||||
|
||||
if (vb2_plane_size(vb, 0) < size) {
|
||||
dev_err(icd->parent, "Buffer #%d too small (%lu < %lu)\n",
|
||||
vb->v4l2_buf.index, vb2_plane_size(vb, 0), size);
|
||||
goto error;
|
||||
}
|
||||
|
||||
vb2_set_plane_payload(vb, 0, size);
|
||||
|
||||
dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
|
||||
vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
|
||||
|
||||
#ifdef DEBUG
|
||||
/*
|
||||
* This can be useful if you want to see if we actually fill
|
||||
* the buffer with something
|
||||
*/
|
||||
if (vb2_plane_vaddr(vb, 0))
|
||||
memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0));
|
||||
#endif
|
||||
|
||||
spin_lock_irq(&pcdev->lock);
|
||||
list_add_tail(&buf->queue, &pcdev->capture);
|
||||
|
||||
@ -405,6 +414,11 @@ static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb)
|
||||
sh_mobile_ceu_capture(pcdev);
|
||||
}
|
||||
spin_unlock_irq(&pcdev->lock);
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
|
||||
}
|
||||
|
||||
static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb)
|
||||
@ -429,11 +443,23 @@ static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb)
|
||||
if (buf->queue.next)
|
||||
list_del_init(&buf->queue);
|
||||
|
||||
pcdev->buf_total -= PAGE_ALIGN(vb2_plane_size(vb, 0));
|
||||
dev_dbg(icd->parent, "%s() %zu bytes buffers\n", __func__,
|
||||
pcdev->buf_total);
|
||||
|
||||
spin_unlock_irq(&pcdev->lock);
|
||||
}
|
||||
|
||||
static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb)
|
||||
{
|
||||
struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq);
|
||||
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
||||
struct sh_mobile_ceu_dev *pcdev = ici->priv;
|
||||
|
||||
pcdev->buf_total += PAGE_ALIGN(vb2_plane_size(vb, 0));
|
||||
dev_dbg(icd->parent, "%s() %zu bytes buffers\n", __func__,
|
||||
pcdev->buf_total);
|
||||
|
||||
/* This is for locking debugging only */
|
||||
INIT_LIST_HEAD(&to_ceu_vb(vb)->queue);
|
||||
return 0;
|
||||
@ -535,19 +561,29 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
|
||||
|
||||
pm_runtime_get_sync(ici->v4l2_dev.dev);
|
||||
|
||||
pcdev->buf_total = 0;
|
||||
|
||||
ret = sh_mobile_ceu_soft_reset(pcdev);
|
||||
|
||||
csi2_sd = find_csi2(pcdev);
|
||||
if (csi2_sd)
|
||||
csi2_sd->grp_id = (long)icd;
|
||||
|
||||
ret = v4l2_subdev_call(csi2_sd, core, s_power, 1);
|
||||
if (ret != -ENODEV && ret != -ENOIOCTLCMD && ret < 0) {
|
||||
if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) {
|
||||
pm_runtime_put_sync(ici->v4l2_dev.dev);
|
||||
} else {
|
||||
pcdev->icd = icd;
|
||||
ret = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
/*
|
||||
* -ENODEV is special: either csi2_sd == NULL or the CSI-2 driver
|
||||
* has not found this soc-camera device among its clients
|
||||
*/
|
||||
if (ret == -ENODEV && csi2_sd)
|
||||
csi2_sd->grp_id = 0;
|
||||
pcdev->icd = icd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called with .video_lock held */
|
||||
@ -560,6 +596,8 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
|
||||
BUG_ON(icd != pcdev->icd);
|
||||
|
||||
v4l2_subdev_call(csi2_sd, core, s_power, 0);
|
||||
if (csi2_sd)
|
||||
csi2_sd->grp_id = 0;
|
||||
/* disable capture, disable interrupts */
|
||||
ceu_write(pcdev, CEIER, 0);
|
||||
sh_mobile_ceu_soft_reset(pcdev);
|
||||
@ -628,22 +666,22 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
|
||||
left_offset = cam->ceu_left;
|
||||
top_offset = cam->ceu_top;
|
||||
|
||||
/* CEU cropping (CFSZR) is applied _after_ the scaling filter (CFLCR) */
|
||||
WARN_ON(icd->user_width & 3 || icd->user_height & 3);
|
||||
|
||||
width = icd->user_width;
|
||||
|
||||
if (pcdev->image_mode) {
|
||||
in_width = cam->width;
|
||||
if (!pcdev->is_16bit) {
|
||||
in_width *= 2;
|
||||
left_offset *= 2;
|
||||
}
|
||||
width = icd->user_width;
|
||||
cdwdr_width = icd->user_width;
|
||||
cdwdr_width = width;
|
||||
} else {
|
||||
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
|
||||
int bytes_per_line = soc_mbus_bytes_per_line(width,
|
||||
icd->current_fmt->host_fmt);
|
||||
unsigned int w_factor;
|
||||
|
||||
width = icd->user_width;
|
||||
|
||||
switch (icd->current_fmt->host_fmt->packing) {
|
||||
case SOC_MBUS_PACKING_2X8_PADHI:
|
||||
w_factor = 2;
|
||||
@ -653,10 +691,10 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
|
||||
}
|
||||
|
||||
in_width = cam->width * w_factor;
|
||||
left_offset = left_offset * w_factor;
|
||||
left_offset *= w_factor;
|
||||
|
||||
if (bytes_per_line < 0)
|
||||
cdwdr_width = icd->user_width;
|
||||
cdwdr_width = width;
|
||||
else
|
||||
cdwdr_width = bytes_per_line;
|
||||
}
|
||||
@ -664,7 +702,7 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
|
||||
height = icd->user_height;
|
||||
in_height = cam->height;
|
||||
if (V4L2_FIELD_NONE != pcdev->field) {
|
||||
height /= 2;
|
||||
height = (height / 2) & ~3;
|
||||
in_height /= 2;
|
||||
top_offset /= 2;
|
||||
cdwdr_width *= 2;
|
||||
@ -686,6 +724,7 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
|
||||
|
||||
ceu_write(pcdev, CAMOR, camor);
|
||||
ceu_write(pcdev, CAPWR, (in_height << 16) | in_width);
|
||||
/* CFSZR clipping is applied _after_ the scaling filter (CFLCR) */
|
||||
ceu_write(pcdev, CFSZR, (height << 16) | width);
|
||||
ceu_write(pcdev, CDWDR, cdwdr_width);
|
||||
}
|
||||
@ -723,66 +762,93 @@ static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr)
|
||||
ceu_write(pcdev, CAPSR, capsr);
|
||||
}
|
||||
|
||||
/* Find the bus subdevice driver, e.g., CSI2 */
|
||||
static struct v4l2_subdev *find_bus_subdev(struct sh_mobile_ceu_dev *pcdev,
|
||||
struct soc_camera_device *icd)
|
||||
{
|
||||
if (pcdev->csi2_pdev) {
|
||||
struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
|
||||
if (csi2_sd && csi2_sd->grp_id == (u32)icd)
|
||||
return csi2_sd;
|
||||
}
|
||||
|
||||
return soc_camera_to_subdev(icd);
|
||||
}
|
||||
|
||||
#define CEU_BUS_FLAGS (V4L2_MBUS_MASTER | \
|
||||
V4L2_MBUS_PCLK_SAMPLE_RISING | \
|
||||
V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
|
||||
V4L2_MBUS_HSYNC_ACTIVE_LOW | \
|
||||
V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
|
||||
V4L2_MBUS_VSYNC_ACTIVE_LOW | \
|
||||
V4L2_MBUS_DATA_ACTIVE_HIGH)
|
||||
|
||||
/* Capture is not running, no interrupts, no locking needed */
|
||||
static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
|
||||
__u32 pixfmt)
|
||||
{
|
||||
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
||||
struct sh_mobile_ceu_dev *pcdev = ici->priv;
|
||||
int ret;
|
||||
unsigned long camera_flags, common_flags, value;
|
||||
int yuv_lineskip;
|
||||
struct v4l2_subdev *sd = find_bus_subdev(pcdev, icd);
|
||||
struct sh_mobile_ceu_cam *cam = icd->host_priv;
|
||||
struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
|
||||
unsigned long value, common_flags = CEU_BUS_FLAGS;
|
||||
u32 capsr = capture_save_reset(pcdev);
|
||||
unsigned int yuv_lineskip;
|
||||
int ret;
|
||||
|
||||
camera_flags = icd->ops->query_bus_param(icd);
|
||||
common_flags = soc_camera_bus_param_compatible(camera_flags,
|
||||
make_bus_param(pcdev));
|
||||
if (!common_flags)
|
||||
return -EINVAL;
|
||||
/*
|
||||
* If the client doesn't implement g_mbus_config, we just use our
|
||||
* platform data
|
||||
*/
|
||||
ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
|
||||
if (!ret) {
|
||||
common_flags = soc_mbus_config_compatible(&cfg,
|
||||
common_flags);
|
||||
if (!common_flags)
|
||||
return -EINVAL;
|
||||
} else if (ret != -ENOIOCTLCMD) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Make choises, based on platform preferences */
|
||||
if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
|
||||
(common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
|
||||
if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
|
||||
(common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
|
||||
if (pcdev->pdata->flags & SH_CEU_FLAG_HSYNC_LOW)
|
||||
common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
|
||||
common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
|
||||
else
|
||||
common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
|
||||
common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
|
||||
}
|
||||
|
||||
if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
|
||||
(common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
|
||||
if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
|
||||
(common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
|
||||
if (pcdev->pdata->flags & SH_CEU_FLAG_VSYNC_LOW)
|
||||
common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
|
||||
common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
|
||||
else
|
||||
common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
|
||||
common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
|
||||
}
|
||||
|
||||
ret = icd->ops->set_bus_param(icd, common_flags);
|
||||
if (ret < 0)
|
||||
cfg.flags = common_flags;
|
||||
ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
|
||||
if (ret < 0 && ret != -ENOIOCTLCMD)
|
||||
return ret;
|
||||
|
||||
switch (common_flags & SOCAM_DATAWIDTH_MASK) {
|
||||
case SOCAM_DATAWIDTH_8:
|
||||
pcdev->is_16bit = 0;
|
||||
break;
|
||||
case SOCAM_DATAWIDTH_16:
|
||||
if (icd->current_fmt->host_fmt->bits_per_sample > 8)
|
||||
pcdev->is_16bit = 1;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
else
|
||||
pcdev->is_16bit = 0;
|
||||
|
||||
ceu_write(pcdev, CRCNTR, 0);
|
||||
ceu_write(pcdev, CRCMPR, 0);
|
||||
|
||||
value = 0x00000010; /* data fetch by default */
|
||||
yuv_lineskip = 0;
|
||||
yuv_lineskip = 0x10;
|
||||
|
||||
switch (icd->current_fmt->host_fmt->fourcc) {
|
||||
case V4L2_PIX_FMT_NV12:
|
||||
case V4L2_PIX_FMT_NV21:
|
||||
yuv_lineskip = 1; /* skip for NV12/21, no skip for NV16/61 */
|
||||
/* convert 4:2:2 -> 4:2:0 */
|
||||
yuv_lineskip = 0; /* skip for NV12/21, no skip for NV16/61 */
|
||||
/* fall-through */
|
||||
case V4L2_PIX_FMT_NV16:
|
||||
case V4L2_PIX_FMT_NV61:
|
||||
@ -808,8 +874,8 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
|
||||
icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV61)
|
||||
value ^= 0x00000100; /* swap U, V to change from NV1x->NVx1 */
|
||||
|
||||
value |= common_flags & SOCAM_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
|
||||
value |= common_flags & SOCAM_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
|
||||
value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
|
||||
value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
|
||||
value |= pcdev->is_16bit ? 1 << 12 : 0;
|
||||
|
||||
/* CSI2 mode */
|
||||
@ -852,9 +918,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
|
||||
* using 7 we swap the data bytes to match the incoming order:
|
||||
* D0, D1, D2, D3, D4, D5, D6, D7
|
||||
*/
|
||||
value = 0x00000017;
|
||||
if (yuv_lineskip)
|
||||
value &= ~0x00000010; /* convert 4:2:2 -> 4:2:0 */
|
||||
value = 0x00000007 | yuv_lineskip;
|
||||
|
||||
ceu_write(pcdev, CDOCR, value);
|
||||
ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */
|
||||
@ -875,13 +939,19 @@ static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd,
|
||||
{
|
||||
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
||||
struct sh_mobile_ceu_dev *pcdev = ici->priv;
|
||||
unsigned long camera_flags, common_flags;
|
||||
struct v4l2_subdev *sd = find_bus_subdev(pcdev, icd);
|
||||
unsigned long common_flags = CEU_BUS_FLAGS;
|
||||
struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
|
||||
int ret;
|
||||
|
||||
camera_flags = icd->ops->query_bus_param(icd);
|
||||
common_flags = soc_camera_bus_param_compatible(camera_flags,
|
||||
make_bus_param(pcdev));
|
||||
if (!common_flags || buswidth > 16 ||
|
||||
(buswidth > 8 && !(common_flags & SOCAM_DATAWIDTH_16)))
|
||||
ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
|
||||
if (!ret)
|
||||
common_flags = soc_mbus_config_compatible(&cfg,
|
||||
common_flags);
|
||||
else if (ret != -ENOIOCTLCMD)
|
||||
return ret;
|
||||
|
||||
if (!common_flags || buswidth > 16)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
@ -891,26 +961,26 @@ static const struct soc_mbus_pixelfmt sh_mobile_ceu_formats[] = {
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_NV12,
|
||||
.name = "NV12",
|
||||
.bits_per_sample = 12,
|
||||
.packing = SOC_MBUS_PACKING_NONE,
|
||||
.bits_per_sample = 8,
|
||||
.packing = SOC_MBUS_PACKING_1_5X8,
|
||||
.order = SOC_MBUS_ORDER_LE,
|
||||
}, {
|
||||
.fourcc = V4L2_PIX_FMT_NV21,
|
||||
.name = "NV21",
|
||||
.bits_per_sample = 12,
|
||||
.packing = SOC_MBUS_PACKING_NONE,
|
||||
.bits_per_sample = 8,
|
||||
.packing = SOC_MBUS_PACKING_1_5X8,
|
||||
.order = SOC_MBUS_ORDER_LE,
|
||||
}, {
|
||||
.fourcc = V4L2_PIX_FMT_NV16,
|
||||
.name = "NV16",
|
||||
.bits_per_sample = 16,
|
||||
.packing = SOC_MBUS_PACKING_NONE,
|
||||
.bits_per_sample = 8,
|
||||
.packing = SOC_MBUS_PACKING_2X8_PADHI,
|
||||
.order = SOC_MBUS_ORDER_LE,
|
||||
}, {
|
||||
.fourcc = V4L2_PIX_FMT_NV61,
|
||||
.name = "NV61",
|
||||
.bits_per_sample = 16,
|
||||
.packing = SOC_MBUS_PACKING_NONE,
|
||||
.bits_per_sample = 8,
|
||||
.packing = SOC_MBUS_PACKING_2X8_PADHI,
|
||||
.order = SOC_MBUS_ORDER_LE,
|
||||
},
|
||||
};
|
||||
@ -919,6 +989,8 @@ static const struct soc_mbus_pixelfmt sh_mobile_ceu_formats[] = {
|
||||
static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt)
|
||||
{
|
||||
return fmt->packing == SOC_MBUS_PACKING_NONE ||
|
||||
(fmt->bits_per_sample == 8 &&
|
||||
fmt->packing == SOC_MBUS_PACKING_1_5X8) ||
|
||||
(fmt->bits_per_sample == 8 &&
|
||||
fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
|
||||
(fmt->bits_per_sample > 8 &&
|
||||
@ -927,6 +999,38 @@ static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt)
|
||||
|
||||
static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect);
|
||||
|
||||
static struct soc_camera_device *ctrl_to_icd(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
return container_of(ctrl->handler, struct soc_camera_device,
|
||||
ctrl_handler);
|
||||
}
|
||||
|
||||
static int sh_mobile_ceu_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct soc_camera_device *icd = ctrl_to_icd(ctrl);
|
||||
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
||||
struct sh_mobile_ceu_dev *pcdev = ici->priv;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_SHARPNESS:
|
||||
switch (icd->current_fmt->host_fmt->fourcc) {
|
||||
case V4L2_PIX_FMT_NV12:
|
||||
case V4L2_PIX_FMT_NV21:
|
||||
case V4L2_PIX_FMT_NV16:
|
||||
case V4L2_PIX_FMT_NV61:
|
||||
ceu_write(pcdev, CLFCR, !ctrl->val);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct v4l2_ctrl_ops sh_mobile_ceu_ctrl_ops = {
|
||||
.s_ctrl = sh_mobile_ceu_s_ctrl,
|
||||
};
|
||||
|
||||
static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int idx,
|
||||
struct soc_camera_format_xlate *xlate)
|
||||
{
|
||||
@ -952,6 +1056,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
|
||||
}
|
||||
|
||||
if (!pcdev->pdata->csi2) {
|
||||
/* Are there any restrictions in the CSI-2 case? */
|
||||
ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
@ -962,6 +1067,12 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
|
||||
struct v4l2_rect rect;
|
||||
int shift = 0;
|
||||
|
||||
/* Add our control */
|
||||
v4l2_ctrl_new_std(&icd->ctrl_handler, &sh_mobile_ceu_ctrl_ops,
|
||||
V4L2_CID_SHARPNESS, 0, 1, 1, 0);
|
||||
if (icd->ctrl_handler.error)
|
||||
return icd->ctrl_handler.error;
|
||||
|
||||
/* FIXME: subwindow is lost between close / open */
|
||||
|
||||
/* Cache current client geometry */
|
||||
@ -1004,9 +1115,6 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
|
||||
cam->width = mf.width;
|
||||
cam->height = mf.height;
|
||||
|
||||
cam->width = mf.width;
|
||||
cam->height = mf.height;
|
||||
|
||||
icd->host_priv = cam;
|
||||
} else {
|
||||
cam = icd->host_priv;
|
||||
@ -1278,6 +1386,7 @@ static int client_s_fmt(struct soc_camera_device *icd,
|
||||
unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h;
|
||||
unsigned int max_width, max_height;
|
||||
struct v4l2_cropcap cap;
|
||||
bool ceu_1to1;
|
||||
int ret;
|
||||
|
||||
ret = v4l2_device_call_until_err(sd->v4l2_dev, (long)icd, video,
|
||||
@ -1287,7 +1396,14 @@ static int client_s_fmt(struct soc_camera_device *icd,
|
||||
|
||||
dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height);
|
||||
|
||||
if ((width == mf->width && height == mf->height) || !ceu_can_scale)
|
||||
if (width == mf->width && height == mf->height) {
|
||||
/* Perfect! The client has done it all. */
|
||||
ceu_1to1 = true;
|
||||
goto update_cache;
|
||||
}
|
||||
|
||||
ceu_1to1 = false;
|
||||
if (!ceu_can_scale)
|
||||
goto update_cache;
|
||||
|
||||
cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
@ -1327,7 +1443,10 @@ update_cache:
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
update_subrect(cam);
|
||||
if (ceu_1to1)
|
||||
cam->subrect = cam->rect;
|
||||
else
|
||||
update_subrect(cam);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1414,7 +1533,10 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd,
|
||||
capsr = capture_save_reset(pcdev);
|
||||
dev_dbg(dev, "CAPSR 0x%x, CFLCR 0x%x\n", capsr, pcdev->cflcr);
|
||||
|
||||
/* 1. - 2. Apply iterative camera S_CROP for new input window. */
|
||||
/*
|
||||
* 1. - 2. Apply iterative camera S_CROP for new input window, read back
|
||||
* actual camera rectangle.
|
||||
*/
|
||||
ret = client_s_crop(icd, a, &cam_crop);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -1498,8 +1620,9 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd,
|
||||
ceu_write(pcdev, CFLCR, cflcr);
|
||||
}
|
||||
|
||||
icd->user_width = out_width;
|
||||
icd->user_height = out_height;
|
||||
icd->user_width = out_width & ~3;
|
||||
icd->user_height = out_height & ~3;
|
||||
/* Offsets are applied at the CEU scaling filter input */
|
||||
cam->ceu_left = scale_down(rect->left - cam_rect->left, scale_cam_h) & ~1;
|
||||
cam->ceu_top = scale_down(rect->top - cam_rect->top, scale_cam_v) & ~1;
|
||||
|
||||
@ -1538,7 +1661,7 @@ static int sh_mobile_ceu_get_crop(struct soc_camera_device *icd,
|
||||
* CEU crop, mapped backed onto the client input (subrect).
|
||||
*/
|
||||
static void calculate_client_output(struct soc_camera_device *icd,
|
||||
struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf)
|
||||
const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf)
|
||||
{
|
||||
struct sh_mobile_ceu_cam *cam = icd->host_priv;
|
||||
struct device *dev = icd->parent;
|
||||
@ -1574,8 +1697,8 @@ static void calculate_client_output(struct soc_camera_device *icd,
|
||||
dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v);
|
||||
|
||||
/*
|
||||
* 4. Calculate client output window by applying combined scales to real
|
||||
* input window.
|
||||
* 4. Calculate desired client output window by applying combined scales
|
||||
* to client (real) input window.
|
||||
*/
|
||||
mf->width = scale_down(cam->rect.width, scale_h);
|
||||
mf->height = scale_down(cam->rect.height, scale_v);
|
||||
@ -1600,8 +1723,6 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
|
||||
bool image_mode;
|
||||
enum v4l2_field field;
|
||||
|
||||
dev_geo(dev, "S_FMT(pix=0x%x, %ux%u)\n", pixfmt, pix->width, pix->height);
|
||||
|
||||
switch (pix->field) {
|
||||
default:
|
||||
pix->field = V4L2_FIELD_NONE;
|
||||
@ -1622,8 +1743,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* 1.-4. Calculate client output geometry */
|
||||
calculate_client_output(icd, &f->fmt.pix, &mf);
|
||||
/* 1.-4. Calculate desired client output geometry */
|
||||
calculate_client_output(icd, pix, &mf);
|
||||
mf.field = pix->field;
|
||||
mf.colorspace = pix->colorspace;
|
||||
mf.code = xlate->code;
|
||||
@ -1639,6 +1760,9 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
|
||||
image_mode = false;
|
||||
}
|
||||
|
||||
dev_geo(dev, "S_FMT(pix=0x%x, fld 0x%x, code 0x%x, %ux%u)\n", pixfmt, mf.field, mf.code,
|
||||
pix->width, pix->height);
|
||||
|
||||
dev_geo(dev, "4: request camera output %ux%u\n", mf.width, mf.height);
|
||||
|
||||
/* 5. - 9. */
|
||||
@ -1700,6 +1824,10 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
|
||||
pcdev->field = field;
|
||||
pcdev->image_mode = image_mode;
|
||||
|
||||
/* CFSZR requirement */
|
||||
pix->width &= ~3;
|
||||
pix->height &= ~3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1725,7 +1853,8 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
|
||||
|
||||
/* FIXME: calculate using depth and bus width */
|
||||
|
||||
v4l_bound_align_image(&pix->width, 2, 2560, 1,
|
||||
/* CFSZR requires height and width to be 4-pixel aligned */
|
||||
v4l_bound_align_image(&pix->width, 2, 2560, 2,
|
||||
&pix->height, 4, 1920, 2, 0);
|
||||
|
||||
width = pix->width;
|
||||
@ -1778,6 +1907,9 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
|
||||
pix->height = height;
|
||||
}
|
||||
|
||||
pix->width &= ~3;
|
||||
pix->height &= ~3;
|
||||
|
||||
dev_geo(icd->parent, "%s(): return %d, fmt 0x%x, %ux%u\n",
|
||||
__func__, ret, pix->pixelformat, pix->width, pix->height);
|
||||
|
||||
@ -1824,8 +1956,8 @@ static int sh_mobile_ceu_set_livecrop(struct soc_camera_device *icd,
|
||||
out_height != f.fmt.pix.height))
|
||||
ret = -EINVAL;
|
||||
if (!ret) {
|
||||
icd->user_width = out_width;
|
||||
icd->user_height = out_height;
|
||||
icd->user_width = out_width & ~3;
|
||||
icd->user_height = out_height & ~3;
|
||||
ret = sh_mobile_ceu_set_bus_param(icd,
|
||||
icd->current_fmt->host_fmt->fourcc);
|
||||
}
|
||||
@ -1869,55 +2001,6 @@ static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q,
|
||||
return vb2_queue_init(q);
|
||||
}
|
||||
|
||||
static int sh_mobile_ceu_get_ctrl(struct soc_camera_device *icd,
|
||||
struct v4l2_control *ctrl)
|
||||
{
|
||||
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
||||
struct sh_mobile_ceu_dev *pcdev = ici->priv;
|
||||
u32 val;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_SHARPNESS:
|
||||
val = ceu_read(pcdev, CLFCR);
|
||||
ctrl->value = val ^ 1;
|
||||
return 0;
|
||||
}
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
static int sh_mobile_ceu_set_ctrl(struct soc_camera_device *icd,
|
||||
struct v4l2_control *ctrl)
|
||||
{
|
||||
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
||||
struct sh_mobile_ceu_dev *pcdev = ici->priv;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_SHARPNESS:
|
||||
switch (icd->current_fmt->host_fmt->fourcc) {
|
||||
case V4L2_PIX_FMT_NV12:
|
||||
case V4L2_PIX_FMT_NV21:
|
||||
case V4L2_PIX_FMT_NV16:
|
||||
case V4L2_PIX_FMT_NV61:
|
||||
ceu_write(pcdev, CLFCR, !ctrl->value);
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
static const struct v4l2_queryctrl sh_mobile_ceu_controls[] = {
|
||||
{
|
||||
.id = V4L2_CID_SHARPNESS,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Low-pass filter",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
};
|
||||
|
||||
static struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.add = sh_mobile_ceu_add_device,
|
||||
@ -1929,14 +2012,10 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
|
||||
.set_livecrop = sh_mobile_ceu_set_livecrop,
|
||||
.set_fmt = sh_mobile_ceu_set_fmt,
|
||||
.try_fmt = sh_mobile_ceu_try_fmt,
|
||||
.set_ctrl = sh_mobile_ceu_set_ctrl,
|
||||
.get_ctrl = sh_mobile_ceu_get_ctrl,
|
||||
.poll = sh_mobile_ceu_poll,
|
||||
.querycap = sh_mobile_ceu_querycap,
|
||||
.set_bus_param = sh_mobile_ceu_set_bus_param,
|
||||
.init_videobuf2 = sh_mobile_ceu_init_videobuf,
|
||||
.controls = sh_mobile_ceu_controls,
|
||||
.num_controls = ARRAY_SIZE(sh_mobile_ceu_controls),
|
||||
};
|
||||
|
||||
struct bus_wait {
|
||||
|
@ -19,6 +19,7 @@
|
||||
#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>
|
||||
@ -35,11 +36,10 @@ 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;
|
||||
unsigned long (*query_bus_param)(struct soc_camera_device *);
|
||||
int (*set_bus_param)(struct soc_camera_device *, unsigned long);
|
||||
};
|
||||
|
||||
static int sh_csi2_try_fmt(struct v4l2_subdev *sd,
|
||||
@ -127,9 +127,34 @@ static int sh_csi2_s_fmt(struct v4l2_subdev *sd,
|
||||
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 = (struct soc_camera_device *)sd->grp_id;
|
||||
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)
|
||||
@ -144,11 +169,21 @@ static void sh_csi2_hwinit(struct sh_csi2 *priv)
|
||||
udelay(5);
|
||||
iowrite32(0x00000000, priv->base + SH_CSI2_SRST);
|
||||
|
||||
if (priv->client->lanes & 3)
|
||||
tmp |= priv->client->lanes & 3;
|
||||
else
|
||||
/* Default - both lanes */
|
||||
tmp |= 3;
|
||||
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;
|
||||
@ -163,38 +198,18 @@ static void sh_csi2_hwinit(struct sh_csi2 *priv)
|
||||
iowrite32(tmp, priv->base + SH_CSI2_CHKSUM);
|
||||
}
|
||||
|
||||
static int sh_csi2_set_bus_param(struct soc_camera_device *icd,
|
||||
unsigned long flags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long sh_csi2_query_bus_param(struct soc_camera_device *icd)
|
||||
{
|
||||
struct soc_camera_link *icl = to_soc_camera_link(icd);
|
||||
const unsigned long flags = SOCAM_PCLK_SAMPLE_RISING |
|
||||
SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
|
||||
SOCAM_MASTER | SOCAM_DATAWIDTH_8 | SOCAM_DATA_ACTIVE_HIGH;
|
||||
|
||||
return soc_camera_apply_sensor_flags(icl, flags);
|
||||
}
|
||||
|
||||
static int sh_csi2_client_connect(struct sh_csi2 *priv)
|
||||
{
|
||||
struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
|
||||
struct v4l2_subdev *sd, *csi2_sd = &priv->subdev;
|
||||
struct soc_camera_device *icd = NULL;
|
||||
struct soc_camera_device *icd = (struct soc_camera_device *)priv->subdev.grp_id;
|
||||
struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
|
||||
struct device *dev = v4l2_get_subdevdata(&priv->subdev);
|
||||
int i;
|
||||
struct v4l2_mbus_config cfg;
|
||||
unsigned long common_flags, csi2_flags;
|
||||
int i, ret;
|
||||
|
||||
v4l2_device_for_each_subdev(sd, csi2_sd->v4l2_dev)
|
||||
if (sd->grp_id) {
|
||||
icd = (struct soc_camera_device *)sd->grp_id;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!icd)
|
||||
return -EINVAL;
|
||||
if (priv->client)
|
||||
return -EBUSY;
|
||||
|
||||
for (i = 0; i < pdata->num_clients; i++)
|
||||
if (&pdata->clients[i].pdev->dev == icd->pdev)
|
||||
@ -205,15 +220,42 @@ static int sh_csi2_client_connect(struct sh_csi2 *priv)
|
||||
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;
|
||||
|
||||
priv->set_bus_param = icd->ops->set_bus_param;
|
||||
priv->query_bus_param = icd->ops->query_bus_param;
|
||||
icd->ops->set_bus_param = sh_csi2_set_bus_param;
|
||||
icd->ops->query_bus_param = sh_csi2_query_bus_param;
|
||||
|
||||
csi2_sd->grp_id = (long)icd;
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
sh_csi2_hwinit(priv);
|
||||
@ -223,16 +265,10 @@ static int sh_csi2_client_connect(struct sh_csi2 *priv)
|
||||
|
||||
static void sh_csi2_client_disconnect(struct sh_csi2 *priv)
|
||||
{
|
||||
struct soc_camera_device *icd = (struct soc_camera_device *)priv->subdev.grp_id;
|
||||
if (!priv->client)
|
||||
return;
|
||||
|
||||
priv->client = NULL;
|
||||
priv->subdev.grp_id = 0;
|
||||
|
||||
/* Driver is about to be unbound */
|
||||
icd->ops->set_bus_param = priv->set_bus_param;
|
||||
icd->ops->query_bus_param = priv->query_bus_param;
|
||||
priv->set_bus_param = NULL;
|
||||
priv->query_bus_param = NULL;
|
||||
|
||||
pm_runtime_put(v4l2_get_subdevdata(&priv->subdev));
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user