2014-12-08

From: Benoit Parrot <bparrot@ti.com>

This patch adds Video Processing Front End (VPFE) driver for

AM437X family of devices

Driver supports the following:

- V4L2 API using MMAP buffer access based on videobuf2 api

- Asynchronous sensor/decoder sub device registration

- DT support

Signed-off-by: Benoit Parrot <bparrot@ti.com>

Signed-off-by: Darren Etheridge <detheridge@ti.com>

Signed-off-by: Lad, Prabhakar <prabhakar.csengg@gmail.com>

---

Changes for v5:

1: Fixed review comments pointed out by Hans, fixing race condition.

v4l2-compliance output:-

----------------------

root@am437x-evm:~# ./v4l2-compliance -s -i 0 -vv

Driver Info:

Driver name : vpfe

Card type :[ 262.490769] vpfe 48328000.vpfe: ================= START STATUS =================

TI AM437x VPFE

Bus info : platform:vpfe [ 262.502285] vpfe 48328000.vpfe: ================== END STATUS ==================

48328000.vpfe

Driver version: 3.18.0

Capabil[ 262.515396] vpfe 48328000.vpfe: invalid input index: 1

ities : 0x85200001

Video Capture

Read/Write

Streaming

Extended Pix Format

Device Capabilities

Device Caps : 0x05200001

Video Capture

Read/Write

Streaming

Extended Pix Format

Compliance test for device /dev/video0 (not using libv4l2):

Required ioctls:

test VIDIOC_QUERYCAP: OK

Allow for multiple opens:

test second video open: OK

test VIDIOC_QUERYCAP: OK

test VIDIOC_G/S_PRIORITY: OK

Debug ioctls:

test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)

test VIDIOC_LOG_STATUS: OK

Input ioctls:

test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)

test VIDIOC_G/S_FREQUENCY: OK (Not Supported)

test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)

test VIDIOC_ENUMAUDIO: OK (Not Supported)

test VIDIOC_G/S/ENUMINPUT: OK

test VIDIOC_G/S_AUDIO: OK (Not Supported)

Inputs: 1 Audio Inputs: 0 Tuners: 0

Output ioctls:

test VIDIOC_G/S_MODULATOR: OK (Not Supported)

test VIDIOC_G/S_FREQUENCY: OK (Not Supported)

test VIDIOC_ENUMAUDOUT: OK (Not Supported)

test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)

test VIDIOC_G/S_AUDOUT: OK (Not Supported)

Outputs: 0 Audio Outputs: 0 Modulators: 0

Input/Output configuration ioctls:

test VIDIOC_ENUM/G/S/QUERY_STD: OK

test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)

test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)

test VIDIOC_G/S_EDID: OK (Not Supported)

Test input 0:

Control ioctls:

test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)

test VIDIOC_QUERYCTRL: OK (Not Supported)

test VIDIOC_G/S_CTRL: OK (Not Supported)

test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)

test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)

test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)

Standard Controls: 0 Private Controls: 0

Format ioctls:

info: found 7 framesizes for pixel format 56595559

info: found 7 framesizes for pixel format 59565955

info: found 7 framesizes for pixel format 52424752

info: found 7 framesizes for pixel format 31384142

info: found 4 formats for buftype 1

test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK

test VIDIOC_G/S_PARM: OK

test VIDIOC_G_FBUF: OK (Not Supported)

test VIDIOC_G_FMT: OK

test VIDIOC_TRY_FMT: OK

info: Could not perform global format test

test VIDIOC_S_FMT: OK

test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)

test Cropping: OK

test Composing: OK (Not Supported)

test Scaling: OK (Not Supported)

Codec ioctls:

test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)

test VIDIOC_G_ENC_INDEX: OK (Not Supported)

test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

Buffer ioctls:

info: test buftype Video Capture

test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK

test VIDIOC_EXPBUF: OK

Streaming ioctls:

test read/write: OK

Video Capture:

Buffer: 0 Sequence: 0 Field: None Timestamp: 267.115428s

Buffer: 1 Sequence: 1 Field: None Timestamp: 267.138308s

Buffer: 2 Sequence: 2 Field: None Timestamp: 267.161189s

Buffer: 3 Sequence: 3 Field: None Timestamp: 267.367114s

Buffer: 0 Sequence: 4 Field: None Timestamp: 267.389994s

Buffer: 1 Sequence: 5 Field: None Timestamp: 267.412874s

Buffer: 2 Sequence: 6 Field: None Timestamp: 267.435755s

Buffer: 3 Sequence: 7 Field: None Timestamp: 267.458635s

Buffer: 0 Sequence: 8 Field: None Timestamp: 267.481517s

Buffer: 1 Sequence: 9 Field: None Timestamp: 267.504396s

Buffer: 2 Sequence: 10 Field: None Timestamp: 267.527276s

Buffer: 3 Sequence: 11 Field: None Timestamp: 267.550157s

Buffer: 0 Sequence: 12 Field: None Timestamp: 267.573037s

Buffer: 1 Sequence: 13 Field: None Timestamp: 267.595918s

Buffer: 2 Sequence: 14 Field: None Timestamp: 267.618798s

Buffer: 3 Sequence: 15 Field: None Timestamp: 267.641680s

Buffer: 0 Sequence: 16 Field: None Timestamp: 267.664559s

Buffer: 1 Sequence: 17 Field: None Timestamp: 267.687439s

Buffer: 2 Sequence: 18 Field: None Timestamp: 267.710321s

Buffer: 3 Sequence: 19 Field: None Timestamp: 267.733200s

Buffer: 0 Sequence: 20 Field: None Timestamp: 267.756080s

Buffer: 1 Sequence: 21 Field: None Timestamp: 267.778960s

Buffer: 2 Sequence: 22 Field: None Timestamp: 267.801841s

Buffer: 3 Sequence: 23 Field: None Timestamp: 267.824721s

Buffer: 0 Sequence: 24 Field: None Timestamp: 267.847602s

Buffer: 1 Sequence: 25 Field: None Timestamp: 267.870483s

Buffer: 2 Sequence: 26 Field: None Timestamp: 267.893362s

Buffer: 3 Sequence: 27 Field: None Timestamp: 267.916243s

Buffer: 0 Sequence: 28 Field: None Timestamp: 267.939123s

Buffer: 1 Sequence: 29 Field: None Timestamp: 267.962003s

Buffer: 2 Sequence: 30 Field: None Timestamp: 267.984884s

Buffer: 3 Sequence: 31 Field: None Timestamp: 268.007762s

Buffer: 0 Sequence: 32 Field: None Timestamp: 268.030645s

Buffer: 1 Sequence: 33 Field: None Timestamp: 268.053527s

Buffer: 2 Sequence: 34 Field: None Timestamp: 268.076406s

Buffer: 3 Sequence: 35 Field: None Timestamp: 268.099286s

Buffer: 0 Sequence: 36 Field: None Timestamp: 268.122166s

Buffer: 1 Sequence: 37 Field: None Timestamp: 268.145047s

Buffer: 2 Sequence: 38 Field: None Timestamp: 268.167927s

Buffer: 3 Sequence: 39 Field: None Timestamp: 268.190808s

Buffer: 0 Sequence: 40 Field: None Timestamp: 268.213687s

Buffer: 1 Sequence: 41 Field: None Timestamp: 268.236568s

Buffer: 2 Sequence: 42 Field: None Timestamp: 268.259448s

Buffer: 3 Sequence: 43 Field: None Timestamp: 268.282329s

Buffer: 0 Sequence: 44 Field: None Timestamp: 268.305209s

Buffer: 1 Sequence: 45 Field: None Timestamp: 268.328089s

Buffer: 2 Sequence: 46 Field: None Timestamp: 268.350971s

Buffer: 3 Sequence: 47 Field: None Timestamp: 268.373850s

Buffer: 0 Sequence: 48 Field: None Timestamp: 268.396730s

Buffer: 1 Sequence: 49 Field: None Timestamp: 268.419611s

Buffer: 2 Sequence: 50 Field: None Timestamp: 268.442491s

Buffer: 3 Sequence: 51 Field: None Timestamp: 268.465372s

Buffer: 0 Sequence: 52 Field: None Timestamp: 268.488253s

Buffer: 1 Sequence: 53 Field: None Timestamp: 268.511134s

Buffer: 2 Sequence: 54 Field: None Timestamp: 268.534012s

Buffer: 3 Sequence: 55 Field: None Timestamp: 268.556893s

Buffer: 0 Sequence: 56 Field: None Timestamp: 268.579773s

Buffer: 1 Sequence: 57 Field: None Timestamp: 268.602654s

Buffer: 2 Sequence: 58 Field: None Timestamp: 268.625534s

Buffer: 3 Sequence: 59 Field: None Timestamp: 268.648415s

Video Capture (polling):

Buffer: 0 Sequence: 60 Field: None Timestamp: 268.671297s

Buffer: 1 Sequence: 61 Field: None Timestamp: 268.694175s

Buffer: 2 Sequence: 62 Field: None Timestamp: 268.717056s

Buffer: 3 Sequence: 63 Field: None Timestamp: 268.739936s

Buffer: 0 Sequence: 64 Field: None Timestamp: 268.762816s

Buffer: 1 Sequence: 65 Field: None Timestamp: 268.785697s

Buffer: 2 Sequence: 66 Field: None Timestamp: 268.808585s

Buffer: 3 Sequence: 67 Field: None Timestamp: 268.831460s

Buffer: 0 Sequence: 68 Field: None Timestamp: 268.854338s

Buffer: 1 Sequence: 69 Field: None Timestamp: 268.877218s

Buffer: 2 Sequence: 70 Field: None Timestamp: 268.900097s

Buffer: 3 Sequence: 71 Field: None Timestamp: 268.922979s

Buffer: 0 Sequence: 72 Field: None Timestamp: 268.945860s

Buffer: 1 Sequence: 73 Field: None Timestamp: 268.968740s

Buffer: 2 Sequence: 74 Field: None Timestamp: 268.991621s

Buffer: 3 Sequence: 75 Field: None Timestamp: 269.014500s

Buffer: 0 Sequence: 76 Field: None Timestamp: 269.037381s

Buffer: 1 Sequence: 77 Field: None Timestamp: 269.060264s

Buffer: 2 Sequence: 78 Field: None Timestamp: 269.083142s

Buffer: 3 Sequence: 79 Field: None Timestamp: 269.106022s

Buffer: 0 Sequence: 80 Field: None Timestamp: 269.128902s

Buffer: 1 Sequence: 81 Field: None Timestamp: 269.151783s

Buffer: 2 Sequence: 82 Field: None Timestamp: 269.174663s

Buffer: 3 Sequence: 83 Field: None Timestamp: 269.197544s

Buffer: 0 Sequence: 84 Field: None Timestamp: 269.220425s

Buffer: 1 Sequence: 85 Field: None Timestamp: 269.243304s

Buffer: 2 Sequence: 86 Field: None Timestamp: 269.266184s

Buffer: 3 Sequence: 87 Field: None Timestamp: 269.289065s

Buffer: 0 Sequence: 88 Field: None Timestamp: 269.311945s

Buffer: 1 Sequence: 89 Field: None Timestamp: 269.334826s

Buffer: 2 Sequence: 90 Field: None Timestamp: 269.357706s

Buffer: 3 Sequence: 91 Field: None Timestamp: 269.380587s

Buffer: 0 Sequence: 92 Field: None Timestamp: 269.403467s

Buffer: 1 Sequence: 93 Field: None Timestamp: 269.426347s

Buffer: 2 Sequence: 94 Field: None Timestamp: 269.449227s

Buffer: 3 Sequence: 95 Field: None Timestamp: 269.472107s

Buffer: 0 Sequence: 96 Field: None Timestamp: 269.494989s

Buffer: 1 Sequence: 97 Field: None Timestamp: 269.517869s

Buffer: 2 Sequence: 98 Field: None Timestamp: 269.540750s

Buffer: 3 Sequence: 99 Field: None Timestamp: 269.563630s

Buffer: 0 Sequence: 100 Field: None Timestamp: 269.586510s

Buffer: 1 Sequence: 101 Field: None Timestamp: 269.609390s

Buffer: 2 Sequence: 102 Field: None Timestamp: 269.632271s

Buffer: 3 Sequence: 103 Field: None Timestamp: 269.655151s

Buffer: 0 Sequence: 104 Field: None Timestamp: 269.678031s

Buffer: 1 Sequence: 105 Field: None Timestamp: 269.700914s

Buffer: 2 Sequence: 106 Field: None Timestamp: 269.723792s

Buffer: 3 Sequence: 107 Field: None Timestamp: 269.746672s

Buffer: 0 Sequence: 108 Field: None Timestamp: 269.769553s

Buffer: 1 Sequence: 109 Field: None Timestamp: 269.792433s

Buffer: 2 Sequence: 110 Field: None Timestamp: 269.815314s

Buffer: 3 Sequence: 111 Field: None Timestamp: 269.838194s

Buffer: 0 Sequence: 112 Field: None Timestamp: 269.861075s

Buffer: 1 Sequence: 113 Field: None Timestamp: 269.883955s

Buffer: 2 Sequence: 114 Field: None Timestamp: 269.906835s

Buffer: 3 Sequence: 115 Field: None Timestamp: 269.929716s

Buffer: 0 Sequence: 116 Field: None Timestamp: 269.952596s

Buffer: 1 Sequence: 117 Field: None Timestamp: 269.975476s

Buffer: 2 Sequence: 118 Field: None Timestamp: 269.998356s

Buffer: 3 Sequence: 119 Field: None Timestamp: 270.021238s

test MMAP: OK

test USERPTR: OK (Not Supported)

test DMABUF: Cannot test, specify --expbuf-device

Total: 45, Succeeded: 45, Failed: 0, Warnings: 0

root@am437x-evm:~#

.../devicetree/bindings/media/ti-am437x-vpfe.txt | 61 +

MAINTAINERS | 9 +

drivers/media/platform/Kconfig | 1 +

drivers/media/platform/Makefile | 2 +

drivers/media/platform/am437x/Kconfig | 11 +

drivers/media/platform/am437x/Makefile | 3 +

drivers/media/platform/am437x/am437x-vpfe.c | 2780 ++++++++++++++++++++

drivers/media/platform/am437x/am437x-vpfe.h | 283 ++

drivers/media/platform/am437x/am437x-vpfe_regs.h | 140 +

include/uapi/linux/Kbuild | 1 +

include/uapi/linux/am437x-vpfe.h | 122 +

11 files changed, 3413 insertions(+)

create mode 100644 Documentation/devicetree/bindings/media/ti-am437x-vpfe.txt

create mode 100644 drivers/media/platform/am437x/Kconfig

create mode 100644 drivers/media/platform/am437x/Makefile

create mode 100644 drivers/media/platform/am437x/am437x-vpfe.c

create mode 100644 drivers/media/platform/am437x/am437x-vpfe.h

create mode 100644 drivers/media/platform/am437x/am437x-vpfe_regs.h

create mode 100644 include/uapi/linux/am437x-vpfe.h

diff --git a/Documentation/devicetree/bindings/media/ti-am437x-vpfe.txt b/Documentation/devicetree/bindings/media/ti-am437x-vpfe.txt

new file mode 100644

index 0000000..3932e76

--- /dev/null

+++ b/Documentation/devicetree/bindings/media/ti-am437x-vpfe.txt

@@ -0,0 +1,61 @@

+Texas Instruments AM437x CAMERA (VPFE)

+--------------------------------------

+

+The Video Processing Front End (VPFE) is a key component for image capture

+applications. The capture module provides the system interface and the

+processing capability to connect RAW image-sensor modules and video decoders

+to the AM437x device.

+

+Required properties:

+- compatible: must be "ti,am437x-vpfe"

+- reg: physical base address and length of the registers set for the device;

+- interrupts: should contain IRQ line for the VPFE;

+- ti,am437x-vpfe-interface: can be one of the following,

+ 0 - Raw Bayer Interface.

+ 1 - 8 Bit BT656 Interface.

+ 2 - 10 Bit BT656 Interface.

+ 3 - YCbCr 8 Bit Interface.

+ 4 - YCbCr 16 Bit Interface.

+

+VPFE supports a single port node with parallel bus. It should contain one

+'port' child node with child 'endpoint' node. Please refer to the bindings

+defined in Documentation/devicetree/bindings/media/video-interfaces.txt.

+

+Example:

+ vpfe: vpfe@f0034000 {

+ compatible = "ti,am437x-vpfe";

+ reg = <0x48328000 0x2000>;

+ interrupts = <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>;

+

+ pinctrl-names = "default", "sleep";

+ pinctrl-0 = <&vpfe_pins_default>;

+ pinctrl-1 = <&vpfe_pins_sleep>;

+

+ port {

+ #address-cells = <1>;

+ #size-cells = <0>;

+

+ vpfe0_ep: endpoint {

+ remote-endpoint = <&ov2659_1>;

+ ti,am437x-vpfe-interface = <0>;

+ bus-width = <8>;

+ hsync-active = <0>;

+ vsync-active = <0>;

+ };

+ };

+ };

+

+ i2c1: i2c@4802a000 {

+

+ ov2659@30 {

+ compatible = "ti,ov2659";

+ reg = <0x30>;

+

+ port {

+ ov2659_1: endpoint {

+ remote-endpoint = <&vpfe0_ep>;

+ bus-width = <8>;

+ mclk-frequency = <12000000>;

+ };

+ };

+ };

diff --git a/MAINTAINERS b/MAINTAINERS

index 9c49eb6..5b1cb1a 100644

--- a/MAINTAINERS

+++ b/MAINTAINERS

@@ -8543,6 +8543,15 @@ S: Maintained

F: drivers/media/platform/davinci/

F: include/media/davinci/

+TI AM437X VPFE DRIVER

+M: Lad, Prabhakar <prabhakar.csengg@gmail.com>

+L: linux-media@vger.kernel.org

+W: http://linuxtv.org/

+Q: http://patchwork.linuxtv.org/project/linux-media/list/

+T: git git://linuxtv.org/mhadli/v4l-dvb-davinci_devices.git

+S: Maintained

+F: drivers/media/platform/am437x/

+

SIS 190 ETHERNET DRIVER

M: Francois Romieu <romieu@fr.zoreil.com>

L: netdev@vger.kernel.org

diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig

index 0c61155..6d94045 100644

--- a/drivers/media/platform/Kconfig

+++ b/drivers/media/platform/Kconfig

@@ -126,6 +126,7 @@ config VIDEO_S3C_CAMIF

source "drivers/media/platform/soc_camera/Kconfig"

source "drivers/media/platform/exynos4-is/Kconfig"

source "drivers/media/platform/s5p-tv/Kconfig"

+source "drivers/media/platform/am437x/Kconfig"

endif # V4L_PLATFORM_DRIVERS

diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile

index b818afb..7bb6d46 100644

--- a/drivers/media/platform/Makefile

+++ b/drivers/media/platform/Makefile

@@ -49,4 +49,6 @@ obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/

obj-y += omap/

+obj-$(CONFIG_VIDEO_AM437X_VPFE) += am437x/

+

ccflags-y += -I$(srctree)/drivers/media/i2c

diff --git a/drivers/media/platform/am437x/Kconfig b/drivers/media/platform/am437x/Kconfig

new file mode 100644

index 0000000..7b023a76

--- /dev/null

+++ b/drivers/media/platform/am437x/Kconfig

@@ -0,0 +1,11 @@

+config VIDEO_AM437X_VPFE

+ tristate "TI AM437x VPFE video capture driver"

+ depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API

+ depends on SOC_AM43XX || COMPILE_TEST

+ select VIDEOBUF2_DMA_CONTIG

+ help

+ Support for AM437x Video Processing Front End based Video

+ Capture Driver.

+

+ To compile this driver as a module, choose M here. The module

+ will be called am437x-vpfe.

diff --git a/drivers/media/platform/am437x/Makefile b/drivers/media/platform/am437x/Makefile

new file mode 100644

index 0000000..d11fff1

--- /dev/null

+++ b/drivers/media/platform/am437x/Makefile

@@ -0,0 +1,3 @@

+# Makefile for AM437x VPFE driver

+

+obj-$(CONFIG_VIDEO_AM437X_VPFE) += am437x-vpfe.o

diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c

new file mode 100644

index 0000000..c2b29a2

--- /dev/null

+++ b/drivers/media/platform/am437x/am437x-vpfe.c

@@ -0,0 +1,2780 @@

+/*

+ * TI VPFE capture Driver

+ *

+ * Copyright (C) 2013 - 2014 Texas Instruments, Inc.

+ *

+ * Benoit Parrot <bparrot@ti.com>

+ * Lad, Prabhakar <prabhakar.csengg@gmail.com>

+ *

+ * This program is free software; you may redistribute it and/or modify

+ * it under the terms of the GNU General Public License as published by

+ * the Free Software Foundation; version 2 of the License.

+ *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,

+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF

+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND

+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS

+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN

+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN

+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE

+ * SOFTWARE.

+ */

+

+#include <linux/delay.h>

+#include <linux/err.h>

+#include <linux/init.h>

+#include <linux/interrupt.h>

+#include <linux/io.h>

+#include <linux/module.h>

+#include <linux/platform_device.h>

+#include <linux/pm_runtime.h>

+#include <linux/slab.h>

+#include <linux/uaccess.h>

+#include <linux/videodev2.h>

+

+#include <media/v4l2-common.h>

+#include <media/v4l2-ctrls.h>

+#include <media/v4l2-event.h>

+#include <media/v4l2-of.h>

+

+#include "am437x-vpfe.h"

+

+#define VPFE_MODULE_NAME "vpfe"

+#define VPFE_VERSION "0.1.0"

+

+static int debug;

+module_param(debug, int, 0644);

+MODULE_PARM_DESC(debug, "Debug level 0-8");

+

+#define vpfe_dbg(level, dev, fmt, arg...) \

+ v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ##arg)

+#define vpfe_info(dev, fmt, arg...) \

+ v4l2_info(&dev->v4l2_dev, fmt, ##arg)

+#define vpfe_err(dev, fmt, arg...) \

+ v4l2_err(&dev->v4l2_dev, fmt, ##arg)

+

+/* standard information */

+struct vpfe_standard {

+ v4l2_std_id std_id;

+ unsigned int width;

+ unsigned int height;

+ struct v4l2_fract pixelaspect;

+ int frame_format;

+};

+

+const struct vpfe_standard vpfe_standards[] = {

+ {V4L2_STD_525_60, 720, 480, {11, 10}, 1},

+ {V4L2_STD_625_50, 720, 576, {54, 59}, 1},

+};

+

+struct bus_format {

+ unsigned int width;

+ unsigned int bpp;

+};

+

+/*

+ * struct vpfe_fmt - VPFE media bus format information

+ * @name: V4L2 format description

+ * @code: V4L2 media bus format code

+ * @shifted: V4L2 media bus format code for the same pixel layout but

+ * shifted to be 8 bits per pixel. =0 if format is not shiftable.

+ * @pixelformat: V4L2 pixel format FCC identifier

+ * @width: Bits per pixel (when transferred over a bus)

+ * @bpp: Bytes per pixel (when stored in memory)

+ * @supported: Indicates format supported by subdev

+ */

+struct vpfe_fmt {

+ const char *name;

+ u32 fourcc;

+ u32 code;

+ struct bus_format l;

+ struct bus_format s;

+ bool supported;

+ u32 index;

+};

+

+static struct vpfe_fmt formats[] = {

+ {

+ .name = "YUV 4:2:2 packed, YCbYCr",

+ .fourcc = V4L2_PIX_FMT_YUYV,

+ .code = MEDIA_BUS_FMT_YUYV8_2X8,

+ .l.width = 10,

+ .l.bpp = 4,

+ .s.width = 8,

+ .s.bpp = 2,

+ .supported = false,

+ }, {

+ .name = "YUV 4:2:2 packed, CbYCrY",

+ .fourcc = V4L2_PIX_FMT_UYVY,

+ .code = MEDIA_BUS_FMT_UYVY8_2X8,

+ .l.width = 10,

+ .l.bpp = 4,

+ .s.width = 8,

+ .s.bpp = 2,

+ .supported = false,

+ }, {

+ .name = "YUV 4:2:2 packed, YCrYCb",

+ .fourcc = V4L2_PIX_FMT_YVYU,

+ .code = MEDIA_BUS_FMT_YVYU8_2X8,

+ .l.width = 10,

+ .l.bpp = 4,

+ .s.width = 8,

+ .s.bpp = 2,

+ .supported = false,

+ }, {

+ .name = "YUV 4:2:2 packed, CrYCbY",

+ .fourcc = V4L2_PIX_FMT_VYUY,

+ .code = MEDIA_BUS_FMT_VYUY8_2X8,

+ .l.width = 10,

+ .l.bpp = 4,

+ .s.width = 8,

+ .s.bpp = 2,

+ .supported = false,

+ }, {

+ .name = "RAW8 BGGR",

+ .fourcc = V4L2_PIX_FMT_SBGGR8,

+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,

+ .l.width = 10,

+ .l.bpp = 2,

+ .s.width = 8,

+ .s.bpp = 1,

+ .supported = false,

+ }, {

+ .name = "RAW8 GBRG",

+ .fourcc = V4L2_PIX_FMT_SGBRG8,

+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,

+ .l.width = 10,

+ .l.bpp = 2,

+ .s.width = 8,

+ .s.bpp = 1,

+ .supported = false,

+ }, {

+ .name = "RAW8 GRBG",

+ .fourcc = V4L2_PIX_FMT_SGRBG8,

+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,

+ .l.width = 10,

+ .l.bpp = 2,

+ .s.width = 8,

+ .s.bpp = 1,

+ .supported = false,

+ }, {

+ .name = "RAW8 RGGB",

+ .fourcc = V4L2_PIX_FMT_SRGGB8,

+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,

+ .l.width = 10,

+ .l.bpp = 2,

+ .s.width = 8,

+ .s.bpp = 1,

+ .supported = false,

+ }, {

+ .name = "RGB565 (LE)",

+ .fourcc = V4L2_PIX_FMT_RGB565,

+ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,

+ .l.width = 10,

+ .l.bpp = 4,

+ .s.width = 8,

+ .s.bpp = 2,

+ .supported = false,

+ }, {

+ .name = "RGB565 (BE)",

+ .fourcc = V4L2_PIX_FMT_RGB565X,

+ .code = MEDIA_BUS_FMT_RGB565_2X8_BE,

+ .l.width = 10,

+ .l.bpp = 4,

+ .s.width = 8,

+ .s.bpp = 2,

+ .supported = false,

+ },

+};

+

+static int

+__vpfe_get_format(struct vpfe_device *vpfe,

+ struct v4l2_format *format, unsigned int *bpp);

+

+static struct vpfe_fmt *find_format_by_code(unsigned int code)

+{

+ struct vpfe_fmt *fmt;

+ unsigned int k;

+

+ for (k = 0; k < ARRAY_SIZE(formats); k++) {

+ fmt = &formats[k];

+ if (fmt->code == code)

+ return fmt;

+ }

+

+ return NULL;

+}

+

+static struct vpfe_fmt *find_format_by_pix(unsigned int pixelformat)

+{

+ struct vpfe_fmt *fmt;

+ unsigned int k;

+

+ for (k = 0; k < ARRAY_SIZE(formats); k++) {

+ fmt = &formats[k];

+ if (fmt->fourcc == pixelformat)

+ return fmt;

+ }

+

+ return NULL;

+}

+

+static void

+mbus_to_pix(struct vpfe_device *vpfe,

+ const struct v4l2_mbus_framefmt *mbus,

+ struct v4l2_pix_format *pix, unsigned int *bpp)

+{

+ struct vpfe_subdev_info *sdinfo = vpfe->current_subdev;

+ unsigned int bus_width = sdinfo->vpfe_param.bus_width;

+ struct vpfe_fmt *fmt;

+

+ fmt = find_format_by_code(mbus->code);

+ if (WARN_ON(fmt == NULL)) {

+ pr_err("Invalid mbus code set\n");

+ *bpp = 1;

+ return;

+ }

+

+ memset(pix, 0, sizeof(*pix));

+ v4l2_fill_pix_format(pix, mbus);

+ pix->pixelformat = fmt->fourcc;

+ *bpp = (bus_width == 10) ? fmt->l.bpp : fmt->s.bpp;

+

+ /* pitch should be 32 bytes aligned */

+ pix->bytesperline = ALIGN(pix->width * *bpp, 32);

+ pix->sizeimage = pix->bytesperline * pix->height;

+}

+

+static void pix_to_mbus(struct vpfe_device *vpfe,

+ struct v4l2_pix_format *pix_fmt,

+ struct v4l2_mbus_framefmt *mbus_fmt)

+{

+ struct vpfe_fmt *fmt;

+

+ fmt = find_format_by_pix(pix_fmt->pixelformat);

+ if (!fmt) {

+ /* default to first entry */

+ vpfe_dbg(3, vpfe, "Invalid pixel code: %x, default used instead\n",

+ pix_fmt->pixelformat);

+ fmt = &formats[0];

+ }

+

+ memset(mbus_fmt, 0, sizeof(*mbus_fmt));

+ v4l2_fill_mbus_format(mbus_fmt, pix_fmt, fmt->code);

+}

+

+/* Print Four-character-code (FOURCC) */

+static char *print_fourcc(u32 fmt)

+{

+ static char code[5];

+

+ code[0] = (unsigned char)(fmt & 0xff);

+ code[1] = (unsigned char)((fmt >> 8) & 0xff);

+ code[2] = (unsigned char)((fmt >> 16) & 0xff);

+ code[3] = (unsigned char)((fmt >> 24) & 0xff);

+ code[4] = '\0';

+

+ return code;

+}

+

+static int

+cmp_v4l2_format(const struct v4l2_format *lhs, const struct v4l2_format *rhs)

+{

+ return lhs->type == rhs->type &&

+ lhs->fmt.pix.width == rhs->fmt.pix.width &&

+ lhs->fmt.pix.height == rhs->fmt.pix.height &&

+ lhs->fmt.pix.pixelformat == rhs->fmt.pix.pixelformat &&

+ lhs->fmt.pix.field == rhs->fmt.pix.field &&

+ lhs->fmt.pix.colorspace == rhs->fmt.pix.colorspace &&

+ lhs->fmt.pix.ycbcr_enc == rhs->fmt.pix.ycbcr_enc &&

+ lhs->fmt.pix.quantization == rhs->fmt.pix.quantization;

+}

+

+static inline u32 vpfe_reg_read(struct vpfe_ccdc *ccdc, u32 offset)

+{

+ return ioread32(ccdc->ccdc_cfg.base_addr + offset);

+}

+

+static inline void vpfe_reg_write(struct vpfe_ccdc *ccdc, u32 val, u32 offset)

+{

+ iowrite32(val, ccdc->ccdc_cfg.base_addr + offset);

+}

+

+static inline struct vpfe_device *to_vpfe(struct vpfe_ccdc *ccdc)

+{

+ return container_of(ccdc, struct vpfe_device, ccdc);

+}

+

+static inline struct vpfe_cap_buffer *to_vpfe_buffer(struct vb2_buffer *vb)

+{

+ return container_of(vb, struct vpfe_cap_buffer, vb);

+}

+

+static inline void vpfe_pcr_enable(struct vpfe_ccdc *ccdc, int flag)

+{

+ vpfe_reg_write(ccdc, !!flag, VPFE_PCR);

+}

+

+static void vpfe_config_enable(struct vpfe_ccdc *ccdc, int flag)

+{

+ unsigned int cfg;

+

+ if (!flag) {

+ cfg = vpfe_reg_read(ccdc, VPFE_CONFIG);

+ cfg &= ~(VPFE_CONFIG_EN_ENABLE << VPFE_CONFIG_EN_SHIFT);

+ } else {

+ cfg = VPFE_CONFIG_EN_ENABLE << VPFE_CONFIG_EN_SHIFT;

+ }

+

+ vpfe_reg_write(ccdc, cfg, VPFE_CONFIG);

+}

+

+static void vpfe_ccdc_setwin(struct vpfe_ccdc *ccdc,

+ struct v4l2_rect *image_win,

+ enum ccdc_frmfmt frm_fmt,

+ int bpp)

+{

+ int horz_start, horz_nr_pixels;

+ int vert_start, vert_nr_lines;

+ int val, mid_img;

+

+ /*

+ * ppc - per pixel count. indicates how many pixels per cell

+ * output to SDRAM. example, for ycbcr, it is one y and one c, so 2.

+ * raw capture this is 1

+ */

+ horz_start = image_win->left * bpp;

+ horz_nr_pixels = (image_win->width * bpp) - 1;

+ vpfe_reg_write(ccdc, (horz_start << VPFE_HORZ_INFO_SPH_SHIFT) |

+ horz_nr_pixels, VPFE_HORZ_INFO);

+

+ vert_start = image_win->top;

+

+ if (frm_fmt == CCDC_FRMFMT_INTERLACED) {

+ vert_nr_lines = (image_win->height >> 1) - 1;

+ vert_start >>= 1;

+ /* Since first line doesn't have any data */

+ vert_start += 1;

+ /* configure VDINT0 */

+ val = (vert_start << VPFE_VDINT_VDINT0_SHIFT);

+ } else {

+ /* Since first line doesn't have any data */

+ vert_start += 1;

+ vert_nr_lines = image_win->height - 1;

+ /*

+ * configure VDINT0 and VDINT1. VDINT1 will be at half

+ * of image height

+ */

+ mid_img = vert_start + (image_win->height / 2);

+ val = (vert_start << VPFE_VDINT_VDINT0_SHIFT) |

+ (mid_img & VPFE_VDINT_VDINT1_MASK);

+ }

+

+ vpfe_reg_write(ccdc, val, VPFE_VDINT);

+

+ vpfe_reg_write(ccdc, (vert_start << VPFE_VERT_START_SLV0_SHIFT) |

+ vert_start, VPFE_VERT_START);

+ vpfe_reg_write(ccdc, vert_nr_lines, VPFE_VERT_LINES);

+}

+

+static void vpfe_reg_dump(struct vpfe_ccdc *ccdc)

+{

+ struct vpfe_device *vpfe = to_vpfe(ccdc);

+

+ vpfe_dbg(3, vpfe, "ALAW: 0x%x\n", vpfe_reg_read(ccdc, VPFE_ALAW));

+ vpfe_dbg(3, vpfe, "CLAMP: 0x%x\n", vpfe_reg_read(ccdc, VPFE_CLAMP));

+ vpfe_dbg(3, vpfe, "DCSUB: 0x%x\n", vpfe_reg_read(ccdc, VPFE_DCSUB));

+ vpfe_dbg(3, vpfe, "BLKCMP: 0x%x\n", vpfe_reg_read(ccdc, VPFE_BLKCMP));

+ vpfe_dbg(3, vpfe, "COLPTN: 0x%x\n", vpfe_reg_read(ccdc, VPFE_COLPTN));

+ vpfe_dbg(3, vpfe, "SDOFST: 0x%x\n", vpfe_reg_read(ccdc, VPFE_SDOFST));

+ vpfe_dbg(3, vpfe, "SYN_MODE: 0x%x\n",

+ vpfe_reg_read(ccdc, VPFE_SYNMODE));

+ vpfe_dbg(3, vpfe, "HSIZE_OFF: 0x%x\n",

+ vpfe_reg_read(ccdc, VPFE_HSIZE_OFF));

+ vpfe_dbg(3, vpfe, "HORZ_INFO: 0x%x\n",

+ vpfe_reg_read(ccdc, VPFE_HORZ_INFO));

+ vpfe_dbg(3, vpfe, "VERT_START: 0x%x\n",

+ vpfe_reg_read(ccdc, VPFE_VERT_START));

+ vpfe_dbg(3, vpfe, "VERT_LINES: 0x%x\n",

+ vpfe_reg_read(ccdc, VPFE_VERT_LINES));

+}

+

+static int

+vpfe_ccdc_validate_param(struct vpfe_ccdc *ccdc,

+ struct vpfe_ccdc_config_params_raw *ccdcparam)

+{

+ struct vpfe_device *vpfe = to_vpfe(ccdc);

+ u8 max_gamma, max_data;

+

+ if (!ccdcparam->alaw.enable)

+ return 0;

+

+ max_gamma = ccdc_gamma_width_max_bit(ccdcparam->alaw.gamma_wd);

+ max_data = ccdc_data_size_max_bit(ccdcparam->data_sz);

+

+ if (ccdcparam->alaw.gamma_wd > VPFE_CCDC_GAMMA_BITS_09_0 ||

+ ccdcparam->alaw.gamma_wd < VPFE_CCDC_GAMMA_BITS_15_6 ||

+ max_gamma > max_data) {

+ vpfe_dbg(1, vpfe, "Invalid data line select\n");

+ return -EINVAL;

+ }

+

+ return 0;

+}

+

+static void

+vpfe_ccdc_update_raw_params(struct vpfe_ccdc *ccdc,

+ struct vpfe_ccdc_config_params_raw *raw_params)

+{

+ struct vpfe_ccdc_config_params_raw *config_params =

+ &ccdc->ccdc_cfg.bayer.config_params;

+

+ config_params = raw_params;

+}

+

+/*

+ * vpfe_ccdc_restore_defaults()

+ * This function will write defaults to all CCDC registers

+ */

+static void vpfe_ccdc_restore_defaults(struct vpfe_ccdc *ccdc)

+{

+ int i;

+

+ /* Disable CCDC */

+ vpfe_pcr_enable(ccdc, 0);

+

+ /* set all registers to default value */

+ for (i = 4; i <= 0x94; i += 4)

+ vpfe_reg_write(ccdc, 0, i);

+

+ vpfe_reg_write(ccdc, VPFE_NO_CULLING, VPFE_CULLING);

+ vpfe_reg_write(ccdc, VPFE_CCDC_GAMMA_BITS_11_2, VPFE_ALAW);

+}

+

+static int vpfe_ccdc_close(struct vpfe_ccdc *ccdc, struct device *dev)

+{

+ int dma_cntl, i, pcr;

+

+ /* If the CCDC module is still busy wait for it to be done */

+ for (i = 0; i < 10; i++) {

+ usleep_range(5000, 6000);

+ pcr = vpfe_reg_read(ccdc, VPFE_PCR);

+ if (!pcr)

+ break;

+

+ /* make sure it it is disabled */

+ vpfe_pcr_enable(ccdc, 0);

+ }

+

+ /* Disable CCDC by resetting all register to default POR values */

+ vpfe_ccdc_restore_defaults(ccdc);

+

+ /* if DMA_CNTL overflow bit is set. Clear it

+ * It appears to take a while for this to become quiescent ~20ms

+ */

+ for (i = 0; i < 10; i++) {

+ dma_cntl = vpfe_reg_read(ccdc, VPFE_DMA_CNTL);

+ if (!(dma_cntl & VPFE_DMA_CNTL_OVERFLOW))

+ break;

+

+ /* Clear the overflow bit */

+ vpfe_reg_write(ccdc, dma_cntl, VPFE_DMA_CNTL);

+ usleep_range(5000, 6000);

+ }

+

+ /* Disabled the module at the CONFIG level */

+ vpfe_config_enable(ccdc, 0);

+

+ pm_runtime_put_sync(dev);

+

+ return 0;

+}

+

+static int vpfe_ccdc_set_params(struct vpfe_ccdc *ccdc, void __user *params)

+{

+ struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc);

+ struct vpfe_ccdc_config_params_raw raw_params;

+ int x;

+

+ if (ccdc->ccdc_cfg.if_type != VPFE_RAW_BAYER)

+ return -EINVAL;

+

+ x = copy_from_user(&raw_params, params, sizeof(raw_params));

+ if (x) {

+ vpfe_dbg(1, vpfe,

+ "vpfe_ccdc_set_params: error in copying ccdc params, %d\n",

+ x);

+ return -EFAULT;

+ }

+

+ if (!vpfe_ccdc_validate_param(ccdc, &raw_params)) {

+ vpfe_ccdc_update_raw_params(ccdc, &raw_params);

+ return 0;

+ }

+

+ return -EINVAL;

+}

+

+/*

+ * vpfe_ccdc_config_ycbcr()

+ * This function will configure CCDC for YCbCr video capture

+ */

+static void vpfe_ccdc_config_ycbcr(struct vpfe_ccdc *ccdc)

+{

+ struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc);

+ struct ccdc_params_ycbcr *params = &ccdc->ccdc_cfg.ycbcr;

+ u32 syn_mode;

+

+ vpfe_dbg(3, vpfe, "vpfe_ccdc_config_ycbcr:\n");

+ /*

+ * first restore the CCDC registers to default values

+ * This is important since we assume default values to be set in

+ * a lot of registers that we didn't touch

+ */

+ vpfe_ccdc_restore_defaults(ccdc);

+

+ /*

+ * configure pixel format, frame format, configure video frame

+ * format, enable output to SDRAM, enable internal timing generator

+ * and 8bit pack mode

+ */

+ syn_mode = (((params->pix_fmt & VPFE_SYN_MODE_INPMOD_MASK) <<

+ VPFE_SYN_MODE_INPMOD_SHIFT) |

+ ((params->frm_fmt & VPFE_SYN_FLDMODE_MASK) <<

+ VPFE_SYN_FLDMODE_SHIFT) | VPFE_VDHDEN_ENABLE |

+ VPFE_WEN_ENABLE | VPFE_DATA_PACK_ENABLE);

+

+ /* setup BT.656 sync mode */

+ if (params->bt656_enable) {

+ vpfe_reg_write(ccdc, VPFE_REC656IF_BT656_EN, VPFE_REC656IF);

+

+ /*

+ * configure the FID, VD, HD pin polarity,

+ * fld,hd pol positive, vd negative, 8-bit data

+ */

+ syn_mode |= VPFE_SYN_MODE_VD_POL_NEGATIVE;

+ if (ccdc->ccdc_cfg.if_type == VPFE_BT656_10BIT)

+ syn_mode |= VPFE_SYN_MODE_10BITS;

+ else

+ syn_mode |= VPFE_SYN_MODE_8BITS;

+ } else {

+ /* y/c external sync mode */

+ syn_mode |= (((params->fid_pol & VPFE_FID_POL_MASK) <<

+ VPFE_FID_POL_SHIFT) |

+ ((params->hd_pol & VPFE_HD_POL_MASK) <<

+ VPFE_HD_POL_SHIFT) |

+ ((params->vd_pol & VPFE_VD_POL_MASK) <<

+ VPFE_VD_POL_SHIFT));

+ }

+ vpfe_reg_write(ccdc, syn_mode, VPFE_SYNMODE);

+

+ /* configure video window */

+ vpfe_ccdc_setwin(ccdc, &params->win,

+ params->frm_fmt, params->bytesperpixel);

+

+ /*

+ * configure the order of y cb cr in SDRAM, and disable latch

+ * internal register on vsync

+ */

+ if (ccdc->ccdc_cfg.if_type == VPFE_BT656_10BIT)

+ vpfe_reg_write(ccdc,

+ (params->pix_order << VPFE_CCDCFG_Y8POS_SHIFT) |

+ VPFE_LATCH_ON_VSYNC_DISABLE |

+ VPFE_CCDCFG_BW656_10BIT, VPFE_CCDCFG);

+ else

+ vpfe_reg_write(ccdc,

+ (params->pix_order << VPFE_CCDCFG_Y8POS_SHIFT) |

+ VPFE_LATCH_ON_VSYNC_DISABLE, VPFE_CCDCFG);

+

+ /*

+ * configure the horizontal line offset. This should be a

+ * on 32 byte boundary. So clear LSB 5 bits

+ */

+ vpfe_reg_write(ccdc, params->bytesperline, VPFE_HSIZE_OFF);

+

+ /* configure the memory line offset */

+ if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED)

+ /* two fields are interleaved in memory */

+ vpfe_reg_write(ccdc, VPFE_SDOFST_FIELD_INTERLEAVED,

+ VPFE_SDOFST);

+}

+

+static void

+vpfe_ccdc_config_black_clamp(struct vpfe_ccdc *ccdc,

+ struct vpfe_ccdc_black_clamp *bclamp)

+{

+ u32 val;

+

+ if (!bclamp->enable) {

+ /* configure DCSub */

+ val = (bclamp->dc_sub) & VPFE_BLK_DC_SUB_MASK;

+ vpfe_reg_write(ccdc, val, VPFE_DCSUB);

+ vpfe_reg_write(ccdc, VPFE_CLAMP_DEFAULT_VAL, VPFE_CLAMP);

+ return;

+ }

+ /*

+ * Configure gain, Start pixel, No of line to be avg,

+ * No of pixel/line to be avg, & Enable the Black clamping

+ */

+ val = ((bclamp->sgain & VPFE_BLK_SGAIN_MASK) |

+ ((bclamp->start_pixel & VPFE_BLK_ST_PXL_MASK) <<

+ VPFE_BLK_ST_PXL_SHIFT) |

+ ((bclamp->sample_ln & VPFE_BLK_SAMPLE_LINE_MASK) <<

+ VPFE_BLK_SAMPLE_LINE_SHIFT) |

+ ((bclamp->sample_pixel & VPFE_BLK_SAMPLE_LN_MASK) <<

+ VPFE_BLK_SAMPLE_LN_SHIFT) | VPFE_BLK_CLAMP_ENABLE);

+ vpfe_reg_write(ccdc, val, VPFE_CLAMP);

+ /* If Black clamping is enable then make dcsub 0 */

+ vpfe_reg_write(ccdc, VPFE_DCSUB_DEFAULT_VAL, VPFE_DCSUB);

+}

+

+static void

+vpfe_ccdc_config_black_compense(struct vpfe_ccdc *ccdc,

+ struct vpfe_ccdc_black_compensation *bcomp)

+{

+ u32 val;

+

+ val = ((bcomp->b & VPFE_BLK_COMP_MASK) |

+ ((bcomp->gb & VPFE_BLK_COMP_MASK) <<

+ VPFE_BLK_COMP_GB_COMP_SHIFT) |

+ ((bcomp->gr & VPFE_BLK_COMP_MASK) <<

+ VPFE_BLK_COMP_GR_COMP_SHIFT) |

+ ((bcomp->r & VPFE_BLK_COMP_MASK) <<

+ VPFE_BLK_COMP_R_COMP_SHIFT));

+ vpfe_reg_write(ccdc, val, VPFE_BLKCMP);

+}

+

+/*

+ * vpfe_ccdc_config_raw()

+ * This function will configure CCDC for Raw capture mode

+ */

+static void vpfe_ccdc_config_raw(struct vpfe_ccdc *ccdc)

+{

+ struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc);

+ struct vpfe_ccdc_config_params_raw *config_params =

+ &ccdc->ccdc_cfg.bayer.config_params;

+ struct ccdc_params_raw *params = &ccdc->ccdc_cfg.bayer;

+ unsigned int syn_mode;

+ unsigned int val;

+

+ vpfe_dbg(3, vpfe, "vpfe_ccdc_config_raw:\n");

+

+ /* Reset CCDC */

+ vpfe_ccdc_restore_defaults(ccdc);

+

+ /* Disable latching function registers on VSYNC */

+ vpfe_reg_write(ccdc, VPFE_LATCH_ON_VSYNC_DISABLE, VPFE_CCDCFG);

+

+ /*

+ * Configure the vertical sync polarity(SYN_MODE.VDPOL),

+ * horizontal sync polarity (SYN_MODE.HDPOL), frame id polarity

+ * (SYN_MODE.FLDPOL), frame format(progressive or interlace),

+ * data size(SYNMODE.DATSIZ), &pixel format (Input mode), output

+ * SDRAM, enable internal timing generator

+ */

+ syn_mode = (((params->vd_pol & VPFE_VD_POL_MASK) << VPFE_VD_POL_SHIFT) |

+ ((params->hd_pol & VPFE_HD_POL_MASK) << VPFE_HD_POL_SHIFT) |

+ ((params->fid_pol & VPFE_FID_POL_MASK) <<

+ VPFE_FID_POL_SHIFT) | ((params->frm_fmt &

+ VPFE_FRM_FMT_MASK) << VPFE_FRM_FMT_SHIFT) |

+ ((config_params->data_sz & VPFE_DATA_SZ_MASK) <<

+ VPFE_DATA_SZ_SHIFT) | ((params->pix_fmt &

+ VPFE_PIX_FMT_MASK) << VPFE_PIX_FMT_SHIFT) |

+ VPFE_WEN_ENABLE | VPFE_VDHDEN_ENABLE);

+

+ /* Enable and configure aLaw register if needed */

+ if (config_params->alaw.enable) {

+ val = ((config_params->alaw.gamma_wd &

+ VPFE_ALAW_GAMMA_WD_MASK) | VPFE_ALAW_ENABLE);

+ vpfe_reg_write(ccdc, val, VPFE_ALAW);

+ vpfe_dbg(3, vpfe, "\nWriting 0x%x to ALAW...\n", val);

+ }

+

+ /* Configure video window */

+ vpfe_ccdc_setwin(ccdc, &params->win, params->frm_fmt,

+ params->bytesperpixel);

+

+ /* Configure Black Clamp */

+ vpfe_ccdc_config_black_clamp(ccdc, &config_params->blk_clamp);

+

+ /* Configure Black level compensation */

+ vpfe_ccdc_config_black_compense(ccdc, &config_params->blk_comp);

+

+ /* If data size is 8 bit then pack the data */

+ if ((config_params->data_sz == VPFE_CCDC_DATA_8BITS) ||

+ config_params->alaw.enable)

+ syn_mode |= VPFE_DATA_PACK_ENABLE;

+

+ /*

+ * Configure Horizontal offset register. If pack 8 is enabled then

+ * 1 pixel will take 1 byte

+ */

+ vpfe_reg_write(ccdc, params->bytesperline, VPFE_HSIZE_OFF);

+

+ vpfe_dbg(3, vpfe, "Writing %d (%x) to HSIZE_OFF\n",

+ params->bytesperline, params->bytesperline);

+

+ /* Set value for SDOFST */

+ if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) {

+ if (params->image_invert_enable) {

+ /* For interlace inverse mode */

+ vpfe_reg_write(ccdc, VPFE_INTERLACED_IMAGE_INVERT,

+ VPFE_SDOFST);

+ } else {

+ /* For interlace non inverse mode */

+ vpfe_reg_write(ccdc, VPFE_INTERLACED_NO_IMAGE_INVERT,

+ VPFE_SDOFST);

+ }

+ } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) {

+ vpfe_reg_write(ccdc, VPFE_PROGRESSIVE_NO_IMAGE_INVERT,

+ VPFE_SDOFST);

+ }

+

+ vpfe_reg_write(ccdc, syn_mode, VPFE_SYNMODE);

+

+ vpfe_reg_dump(ccdc);

+}

+

+static inline int

+vpfe_ccdc_set_buftype(struct vpfe_ccdc *ccdc,

+ enum ccdc_buftype buf_type)

+{

+ if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER)

+ ccdc->ccdc_cfg.bayer.buf_type = buf_type;

+ else

+ ccdc->ccdc_cfg.ycbcr.buf_type = buf_type;

+

+ return 0;

+}

+

+static inline enum ccdc_buftype vpfe_ccdc_get_buftype(struct vpfe_ccdc *ccdc)

+{

+ if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER)

+ return ccdc->ccdc_cfg.bayer.buf_type;

+

+ return ccdc->ccdc_cfg.ycbcr.buf_type;

+}

+

+static int vpfe_ccdc_set_pixel_format(struct vpfe_ccdc *ccdc, u32 pixfmt)

+{

+ struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc);

+

+ vpfe_dbg(1, vpfe, "vpfe_ccdc_set_pixel_format: if_type: %d, pixfmt:%s\n",

+ ccdc->ccdc_cfg.if_type, print_fourcc(pixfmt));

+

+ if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) {

+ ccdc->ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;

+ /*

+ * Need to clear it in case it was left on

+ * after the last capture.

+ */

+ ccdc->ccdc_cfg.bayer.config_params.alaw.enable = 0;

+

+ switch (pixfmt) {

+ case V4L2_PIX_FMT_SBGGR8:

+ ccdc->ccdc_cfg.bayer.config_params.alaw.enable = 1;

+ break;

+

+ case V4L2_PIX_FMT_YUYV:

+ case V4L2_PIX_FMT_UYVY:

+ case V4L2_PIX_FMT_YUV420:

+ case V4L2_PIX_FMT_NV12:

+ case V4L2_PIX_FMT_RGB565X:

+ break;

+

+ case V4L2_PIX_FMT_SBGGR16:

+ default:

+ return -EINVAL;

+ }

+ } else {

+ switch (pixfmt) {

+ case V4L2_PIX_FMT_YUYV:

+ ccdc->ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR;

+ break;

+

+ case V4L2_PIX_FMT_UYVY:

+ ccdc->ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;

+ break;

+

+ default:

+ return -EINVAL;

+ }

+ }

+

+ return 0;

+}

+

+static u32 vpfe_ccdc_get_pixel_format(struct vpfe_ccdc *ccdc)

+{

+ u32 pixfmt;

+

+ if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) {

+ pixfmt = V4L2_PIX_FMT_YUYV;

+ } else {

+ if (ccdc->ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR)

+ pixfmt = V4L2_PIX_FMT_YUYV;

+ else

+ pixfmt = V4L2_PIX_FMT_UYVY;

+ }

+

+ return pixfmt;

+}

+

+static int

+vpfe_ccdc_set_image_window(struct vpfe_ccdc *ccdc,

+ struct v4l2_rect *win, unsigned int bpp)

+{

+ if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) {

+ ccdc->ccdc_cfg.bayer.win = *win;

+ ccdc->ccdc_cfg.bayer.bytesperpixel = bpp;

+ ccdc->ccdc_cfg.bayer.bytesperline = ALIGN(win->width * bpp, 32);

+ } else {

+ ccdc->ccdc_cfg.ycbcr.win = *win;

+ ccdc->ccdc_cfg.ycbcr.bytesperpixel = bpp;

+ ccdc->ccdc_cfg.ycbcr.bytesperline = ALIGN(win->width * bpp, 32);

+ }

+

+ return 0;

+}

+

+static inline void

+vpfe_ccdc_get_image_window(struct vpfe_ccdc *ccdc,

+ struct v4l2_rect *win)

+{

+ if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER)

+ *win = ccdc->ccdc_cfg.bayer.win;

+ else

+ *win = ccdc->ccdc_cfg.ycbcr.win;

+}

+

+static inline unsigned int vpfe_ccdc_get_line_length(struct vpfe_ccdc *ccdc)

+{

+ if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER)

+ return ccdc->ccdc_cfg.bayer.bytesperline;

+

+ return ccdc->ccdc_cfg.ycbcr.bytesperline;

+}

+

+static inline int

+vpfe_ccdc_set_frame_format(struct vpfe_ccdc *ccdc,

+ enum ccdc_frmfmt frm_fmt)

+{

+ if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER)

+ ccdc->ccdc_cfg.bayer.frm_fmt = frm_fmt;

+ else

+ ccdc->ccdc_cfg.ycbcr.frm_fmt = frm_fmt;

+

+ return 0;

+}

+

+static inline enum ccdc_frmfmt

+vpfe_ccdc_get_frame_format(struct vpfe_ccdc *ccdc)

+{

+ if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER)

+ return ccdc->ccdc_cfg.bayer.frm_fmt;

+

+ return ccdc->ccdc_cfg.ycbcr.frm_fmt;

+}

+

+static inline int vpfe_ccdc_getfid(struct vpfe_ccdc *ccdc)

+{

+ return (vpfe_reg_read(ccdc, VPFE_SYNMODE) >> 15) & 1;

+}

+

+static inline void vpfe_set_sdr_addr(struct vpfe_ccdc *ccdc, unsigned long addr)

+{

+ vpfe_reg_write(ccdc, addr & 0xffffffe0, VPFE_SDR_ADDR);

+}

+

+static int vpfe_ccdc_set_hw_if_params(struct vpfe_ccdc *ccdc,

+ struct vpfe_hw_if_param *params)

+{

+ struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc);

+

+ ccdc->ccdc_cfg.if_type = params->if_type;

+

+ switch (params->if_type) {

+ case VPFE_BT656:

+ case VPFE_YCBCR_SYNC_16:

+ case VPFE_YCBCR_SYNC_8:

+ case VPFE_BT656_10BIT:

+ ccdc->ccdc_cfg.ycbcr.vd_pol = params->vdpol;

+ ccdc->ccdc_cfg.ycbcr.hd_pol = params->hdpol;

+ break;

+

+ case VPFE_RAW_BAYER:

+ ccdc->ccdc_cfg.bayer.vd_pol = params->vdpol;

+ ccdc->ccdc_cfg.bayer.hd_pol = params->hdpol;

+ if (params->bus_width == 10)

+ ccdc->ccdc_cfg.bayer.config_params.data_sz =

+ VPFE_CCDC_DATA_10BITS;

+ else

+ ccdc->ccdc_cfg.bayer.config_params.data_sz =

+ VPFE_CCDC_DATA_8BITS;

+ vpfe_dbg(1, vpfe, "params.bus_width: %d\n",

+ params->bus_width);

+ vpfe_dbg(1, vpfe, "config_params.data_sz: %d\n",

+ ccdc->ccdc_cfg.bayer.config_params.data_sz);

+ break;

+

+ default:

+ return -EINVAL;

+ }

+

+ return 0;

+}

+

+static void vpfe_clear_intr(struct vpfe_ccdc *ccdc, int vdint)

+{

+ unsigned int vpfe_int_status;

+

+ vpfe_int_status = vpfe_reg_read(ccdc, VPFE_IRQ_STS);

+

+ switch (vdint) {

+ /* VD0 interrupt */

+ case VPFE_VDINT0:

+ vpfe_int_status &= ~VPFE_VDINT0;

+ vpfe_int_status |= VPFE_VDINT0;

+ break;

+

+ /* VD1 interrupt */

+ case VPFE_VDINT1:

+ vpfe_int_status &= ~VPFE_VDINT1;

+ vpfe_int_status |= VPFE_VDINT1;

+ break;

+

+ /* VD2 interrupt */

+ case VPFE_VDINT2:

+ vpfe_int_status &= ~VPFE_VDINT2;

+ vpfe_int_status |= VPFE_VDINT2;

+ break;

+

+ /* Clear all interrupts */

+ default:

+ vpfe_int_status &= ~(VPFE_VDINT0 |

+ VPFE_VDINT1 |

+ VPFE_VDINT2);

+ vpfe_int_status |= (VPFE_VDINT0 |

+ VPFE_VDINT1 |

+ VPFE_VDINT2);

+ break;

+ }

+ /* Clear specific VDINT from the status register */

+ vpfe_reg_write(ccdc, vpfe_int_status, VPFE_IRQ_STS);

+

+ vpfe_int_status = vpfe_reg_read(ccdc, VPFE_IRQ_STS);

+

+ /* Acknowledge that we are done with all interrupts */

+ vpfe_reg_write(ccdc, 1, VPFE_IRQ_EOI);

+}

+

+static void vpfe_ccdc_config_defaults(struct vpfe_ccdc *ccdc)

+{

+ ccdc->ccdc_cfg.if_type = VPFE_RAW_BAYER;

+

+ ccdc->ccdc_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_8BIT;

+ ccdc->ccdc_cfg.ycbcr.frm_fmt = CCDC_FRMFMT_INTERLACED;

+ ccdc->ccdc_cfg.ycbcr.fid_pol = VPFE_PINPOL_POSITIVE;

+ ccdc->ccdc_cfg.ycbcr.vd_pol = VPFE_PINPOL_POSITIVE;

+ ccdc->ccdc_cfg.ycbcr.hd_pol = VPFE_PINPOL_POSITIVE;

+ ccdc->ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;

+ ccdc->ccdc_cfg.ycbcr.buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED;

+

+ ccdc->ccdc_cfg.ycbcr.win.left = 0;

+ ccdc->ccdc_cfg.ycbcr.win.top = 0;

+ ccdc->ccdc_cfg.ycbcr.win.width = 720;

+ ccdc->ccdc_cfg.ycbcr.win.height = 576;

+ ccdc->ccdc_cfg.ycbcr.bt656_enable = 1;

+

+ ccdc->ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;

+ ccdc->ccdc_cfg.bayer.frm_fmt = CCDC_FRMFMT_PROGRESSIVE;

+ ccdc->ccdc_cfg.bayer.fid_pol = VPFE_PINPOL_POSITIVE;

+ ccdc->ccdc_cfg.bayer.vd_pol = VPFE_PINPOL_POSITIVE;

+ ccdc->ccdc_cfg.bayer.hd_pol = VPFE_PINPOL_POSITIVE;

+

+ ccdc->ccdc_cfg.bayer.win.left = 0;

+ ccdc->ccdc_cfg.bayer.win.top = 0;

+ ccdc->ccdc_cfg.bayer.win.width = 800;

+ ccdc->ccdc_cfg.bayer.win.height = 600;

+ ccdc->ccdc_cfg.bayer.config_params.data_sz = VPFE_CCDC_DATA_8BITS;

+ ccdc->ccdc_cfg.bayer.config_params.alaw.gamma_wd =

+ VPFE_CCDC_GAMMA_BITS_09_0;

+}

+

+/*

+ * vpfe_get_ccdc_image_format - Get image parameters based on CCDC settings

+ */

+static int vpfe_get_ccdc_image_format(struct vpfe_device *vpfe,

+ struct v4l2_format *f)

+{

+ struct v4l2_rect image_win;

+ enum ccdc_buftype buf_type;

+ enum ccdc_frmfmt frm_fmt;

+

+ memset(f, 0, sizeof(*f));

+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

+ vpfe_ccdc_get_image_window(&vpfe->ccdc, &image_win);

+ f->fmt.pix.width = image_win.width;

+ f->fmt.pix.height = image_win.height;

+ f->fmt.pix.bytesperline = vpfe_ccdc_get_line_length(&vpfe->ccdc);

+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *

+ f->fmt.pix.height;

+ buf_type = vpfe_ccdc_get_buftype(&vpfe->ccdc);

+ f->fmt.pix.pixelformat = vpfe_ccdc_get_pixel_format(&vpfe->ccdc);

+ frm_fmt = vpfe_ccdc_get_frame_format(&vpfe->ccdc);

+

+ if (frm_fmt == CCDC_FRMFMT_PROGRESSIVE) {

+ f->fmt.pix.field = V4L2_FIELD_NONE;

+ } else if (frm_fmt == CCDC_FRMFMT_INTERLACED) {

+ if (buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) {

+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;

+ } else if (buf_type == CCDC_BUFTYPE_FLD_SEPARATED) {

+ f->fmt.pix.field = V4L2_FIELD_SEQ_TB;

+ } else {

+ vpfe_err(vpfe, "Invalid buf_type\n");

+ return -EINVAL;

+ }

+ } else {

+ vpfe_err(vpfe, "Invalid frm_fmt\n");

+ return -EINVAL;

+ }

+ return 0;

+}

+

+static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe)

+{

+ enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED;

+ int ret;

+

+ vpfe_dbg(2, vpfe, "vpfe_config_ccdc_image_format\n");

+

+ vpfe_dbg(1, vpfe, "pixelformat: %s\n",

+ print_fourcc(vpfe->fmt.fmt.pix.pixelformat));

+

+ if (vpfe_ccdc_set_pixel_format(&vpfe->ccdc,

+ vpfe->fmt.fmt.pix.pixelformat) < 0) {

+ vpfe_err(vpfe, "couldn't set pix format in ccdc\n");

+ return -EINVAL;

+ }

+

+ /* configure the image window */

+ vpfe_ccdc_set_image_window(&vpfe->ccdc, &vpfe->crop, vpfe->bpp);

+

+ switch (vpfe->fmt.fmt.pix.field) {

+ case V4L2_FIELD_INTERLACED:

+ /* do nothing, since it is default */

+ ret = vpfe_ccdc_set_buftype(

+ &vpfe->ccdc,

+ CCDC_BUFTYPE_FLD_INTERLEAVED);

+ break;

+

+ case V4L2_FIELD_NONE:

+ frm_fmt = CCDC_FRMFMT_PROGRESSIVE;

+ /* buffer type only applicable for interlaced scan */

+ break;

+

+ case V4L2_FIELD_SEQ_TB:

+ ret = vpfe_ccdc_set_buftype(

+ &vpfe->ccdc,

+ CCDC_BUFTYPE_FLD_SEPARATED);

+ break;

+

+ default:

+ return -EINVAL;

+ }

+

+ if (ret)

+ return ret;

+

+ return vpfe_ccdc_set_frame_format(&vpfe->ccdc, frm_fmt);

+}

+

+/*

+ * vpfe_config_image_format()

+ * For a given standard, this functions sets up the default

+ * pix format & crop values in the vpfe device and ccdc. It first

+ * starts with defaults based values from the standard table.

+ * It then checks if sub device support g_mbus_fmt and then override the

+ * values based on that.Sets crop values to match with scan resolution

+ * starting at 0,0. It calls vpfe_config_ccdc_image_format() set the

+ * values in ccdc

+ */

+static int vpfe_config_image_format(struct vpfe_device *vpfe,

+ v4l2_std_id std_id)

+{

+ struct v4l2_pix_format *pix = &vpfe->fmt.fmt.pix;

+ int i, ret;

+

+ for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) {

+ if (vpfe_standards.std_id & std_id) {

+ vpfe->std_info.active_pixels =

+ vpfe_standards.width;

+ vpfe->std_info.active_lines =

+ vpfe_standards.height;

+ vpfe->std_info.frame_format =

+ vpfe_standards.frame_format;

+ vpfe->std_index = i;

+

+ break;

+ }

+ }

+

+ if (i == ARRAY_SIZE(vpfe_standards)) {

+ vpfe_err(vpfe, "standard not supported\n");

+ return -EINVAL;

+ }

+

+ vpfe->crop.top = vpfe->crop.left = 0;

+ vpfe->crop.width = vpfe->std_info.active_pixels;

+ vpfe->crop.height = vpfe->std_info.active_lines;

+ pix->width = vpfe->crop.width;

+ pix->height = vpfe->crop.height;

+ pix->pixelformat = V4L2_PIX_FMT_YUYV;

+

+ /* first field and frame format based on standard frame format */

+ if (vpfe->std_info.frame_format)

+ pix->field = V4L2_FIELD_INTERLACED;

+ else

+ pix->field = V4L2_FIELD_NONE;

+

+ ret = __vpfe_get_format(vpfe, &vpfe->fmt, &vpfe->bpp);

+ if (ret)

+ return ret;

+

+ /* Update the crop window based on found values */

+ vpfe->crop.width = pix->width;

+ vpfe->crop.height = pix->height;

+

+ return vpfe_config_ccdc_image_format(vpfe);

+}

+

+static int vpfe_initialize_device(struct vpfe_device *vpfe)

+{

+ struct vpfe_subdev_info *sdinfo;

+ int ret;

+

+ sdinfo = &vpfe->cfg->sub_devs[0];

+ sdinfo->sd = vpfe->sd[0];

+ vpfe->current_input = 0;

+ vpfe->std_index = 0;

+ /* Configure the default format information */

+ ret = vpfe_config_image_format(vpfe,

+ vpfe_standards[vpfe->std_index].std_id);

+ if (ret)

+ return ret;

+

+ pm_runtime_get_sync(vpfe->pdev);

+

+ vpfe_config_enable(&vpfe->ccdc, 1);

+

+ vpfe_ccdc_restore_defaults(&vpfe->ccdc);

+

+ /* Clear all VPFE interrupts */

+ vpfe_clear_intr(&vpfe->ccdc, -1);

+

+ return ret;

+}

+

+/*

+ * vpfe_release : This function is based on the vb2_fop_release

+ * helper function.

+ * It has been augmented to handle module power management,

+ * by disabling/enabling h/w module fcntl clock when necessary.

+ */

+static int vpfe_release(struct file *file)

+{

+ struct vpfe_device *vpfe = video_drvdata(file);

+ bool close = v4l2_fh_is_singular_file(file);

+ int ret;

+

+ vpfe_dbg(2, vpfe, "vpfe_release\n");

+

+ mutex_lock(&vpfe->lock);

+

+ ret = _vb2_fop_release(file, NULL);

+ if (close)

+ vpfe_ccdc_close(&vpfe->ccdc, vpfe->pdev);

+

+ mutex_unlock(&vpfe->lock);

+

+ return ret;

+}

+

+/*

+ * vpfe_open : This function is based on the v4l2_fh_open helper function.

+ * It has been augmented to handle module power management,

+ * by disabling/enabling h/w module fcntl clock when necessary.

+ */

+static int vpfe_open(struct file *file)

+{

+ struct vpfe_device *vpfe = video_drvdata(file);

+ int ret;

+

+ ret = v4l2_fh_open(file);

+ if (ret) {

+ vpfe_err(vpfe, "v4l2_fh_open failed\n");

+ return ret;

+ }

+

+ mutex_lock(&vpfe->lock);

+

+ if (!v4l2_fh_is_singular_file(file))

+ goto unlock;

+

+ if (vpfe_initialize_device(vpfe)) {

+ v4l2_fh_release(file);

+ ret = -ENODEV;

+ }

+

+unlock:

+ mutex_unlock(&vpfe->lock);

+ return ret;

+}

+

+/**

+ * vpfe_schedule_next_buffer: set next buffer address for capture

+ * @vpfe : ptr to vpfe device

+ *

+ * This function will get next buffer from the dma queue and

+ * set the buffer address in the vpfe register for capture.

+ * the buffer is marked active

+ *

+ * Assumes caller is holding vpfe->dma_queue_lock already

+ */

+static inline void vpfe_schedule_next_buffer(struct vpfe_device *vpfe)

+{

+ vpfe->next_frm = list_entry(vpfe->dma_queue.next,

+ struct vpfe_cap_buffer, list);

+ list_del(&vpfe->next_frm->list);

+

+ vpfe_set_sdr_addr(&vpfe->ccdc,

+ vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb, 0));

+}

+

+static inline void vpfe_schedule_bottom_field(struct vpfe_device *vpfe)

+{

+ unsigned long addr;

+

+ addr = vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb, 0) +

+ vpfe->field_off;

+

+ vpfe_set_sdr_addr(&vpfe->ccdc, addr);

+}

+

+/*

+ * vpfe_process_buffer_complete: process a completed buffer

+ * @vpfe : ptr to vpfe device

+ *

+ * This function time stamp the buffer and mark it as DONE. It also

+ * wake up any process waiting on the QUEUE and set the next buffer

+ * as current

+ */

+static inline void vpfe_process_buffer_complete(struct vpfe_device *vpfe)

+{

+ v4l2_get_timestamp(&vpfe->cur_frm->vb.v4l2_buf.timestamp);

+ vpfe->cur_frm->vb.v4l2_buf.field = vpfe->fmt.fmt.pix.field;

+ vpfe->cur_frm->vb.v4l2_buf.sequence = vpfe->sequence++;

+ vb2_buffer_done(&vpfe->cur_frm->vb, VB2_BUF_STATE_DONE);

+ vpfe->cur_frm = vpfe->next_frm;

+}

+

+/*

+ * vpfe_isr : ISR handler for vpfe capture (VINT0)

+ * @irq: irq number

+ * @dev_id: dev_id ptr

+ *

+ * It changes status of the captured buffer, takes next buffer from the queue

+ * and sets its address in VPFE registers

+ */

+static irqreturn_t vpfe_isr(int irq, void *dev)

+{

+ struct vpfe_device *vpfe = (struct vpfe_device *)dev;

+ enum v4l2_field field;

+ int intr_status;

+ int fid;

+

+ intr_status = vpfe_reg_read(&vpfe->ccdc, VPFE_IRQ_STS);

+

+ if (intr_status & VPFE_VDINT0) {

+ field = vpfe->fmt.fmt.pix.field;

+

+ if (field == V4L2_FIELD_NONE) {

+ /* handle progressive frame capture */

+ if (vpfe->cur_frm != vpfe->next_frm)

+ vpfe_process_buffer_complete(vpfe);

+ goto next_intr;

+ }

+

+ /* interlaced or TB capture check which field

+ we are in hardware */

+ fid = vpfe_ccdc_getfid(&vpfe->ccdc);

+

+ /* switch the software maintained field id */

+ vpfe->field ^= 1;

+ if (fid == vpfe->field) {

+ /* we are in-sync here,continue */

+ if (fid == 0) {

+ /*

+ * One frame is just being captured. If the

+ * next frame is av

Show more