* [Patch 0/2] media: v4l: ti-vpe: Add CAL v4l2 camera capture driver
@ 2015-06-16 17:29 Benoit Parrot
2015-06-16 17:29 ` [Patch 1/2] " Benoit Parrot
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Benoit Parrot @ 2015-06-16 17:29 UTC (permalink / raw)
To: Hans Verkuil
Cc: linux-media, devicetree, Tony Lindgren, linux-kernel, Benoit Parrot
The Camera Adaptation Layer (CAL) is a block which consists of a dual
port CSI2/MIPI camera capture engine.
This camera engine is currently found on DRA72xx family of devices.
Port #0 can handle CSI2 camera connected to up to 4 data lanes.
Port #1 can handle CSI2 camera connected to up to 2 data lanes.
The driver implements the required API/ioctls to be V4L2 compliant.
Driver supports the following:
- V4L2 API using DMABUF/MMAP buffer access based on videobuf2 api
- Asynchronous sensor sub device registration
- DT support
Currently each port is designed to connect to a single sub-device.
In other words port aggregation is not currently supported.
Here is a sample output of the v4l2-compliance tool:
# ./v4l2-compliance -s -v -d /dev/video0
Driver Info:
Driver name : cal
Card type : cal
Bus info : platform:cal-000
Driver version: 4.1.0
Capabilities : 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 (Not Supported)
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:
info: checking v4l2_queryctrl of control 'User Controls' (0x00980001)
info: checking v4l2_queryctrl of control 'Horizontal Flip' (0x00980914)
info: checking v4l2_queryctrl of control 'Vertical Flip' (0x00980915)
info: checking v4l2_queryctrl of control 'Image Processing Controls' (0x009f0001)
info: checking v4l2_queryctrl of control 'Pixel Rate' (0x009f0902)
info: checking v4l2_queryctrl of control 'Horizontal Flip' (0x00980914)
info: checking v4l2_queryctrl of control 'Vertical Flip' (0x00980915)
test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
test VIDIOC_QUERYCTRL: OK
info: checking control 'User Controls' (0x00980001)
info: checking control 'Horizontal Flip' (0x00980914)
info: checking control 'Vertical Flip' (0x00980915)
info: checking control 'Image Processing Controls' (0x009f0001)
info: checking control 'Pixel Rate' (0x009f0902)
test VIDIOC_G/S_CTRL: OK
info: checking extended control 'User Controls' (0x00980001)
info: checking extended control 'Horizontal Flip' (0x00980914)
info: checking extended control 'Vertical Flip' (0x00980915)
info: checking extended control 'Image Processing Controls' (0x009f0001)
info: checking extended control 'Pixel Rate' (0x009f0902)
test VIDIOC_G/S/TRY_EXT_CTRLS: OK
info: checking control event 'User Controls' (0x00980001)
info: checking control event 'Horizontal Flip' (0x00980914)
info: checking control event 'Vertical Flip' (0x00980915)
info: checking control event 'Image Processing Controls' (0x009f0001)
info: checking control event 'Pixel Rate' (0x009f0902)
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
Standard Controls: 5 Private Controls: 0
Format ioctls:
info: found 1 frameintervals for pixel format 47425247 and size 1920x1080
info: found 1 framesizes for pixel format 47425247
info: found 1 formats for buftype 1
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
test VIDIOC_G/S_PARM: OK (Not Supported)
test VIDIOC_G_FBUF: OK (Not Supported)
test VIDIOC_G_FMT: OK
test VIDIOC_TRY_FMT: OK
test VIDIOC_S_FMT: OK
test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
test Cropping: OK (Not Supported)
test Composing: OK (Not Supported)
fail: v4l2-test-formats.cpp(1475): node->can_scale && node->frmsizes_count[v4l_format_g_pixelformat(&cur)]
test Scaling: OK
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 (Not Supported)
Test input 0:
Streaming ioctls:
test read/write: OK
Video Capture:
Buffer: 0 Sequence: 0 Field: None Timestamp: 66.759683s
Buffer: 1 Sequence: 1 Field: None Timestamp: 66.781665s
Buffer: 2 Sequence: 2 Field: None Timestamp: 66.803642s
Buffer: 0 Sequence: 3 Field: None Timestamp: 66.825619s
Buffer: 1 Sequence: 4 Field: None Timestamp: 66.847595s
Buffer: 2 Sequence: 5 Field: None Timestamp: 66.869572s
Buffer: 0 Sequence: 6 Field: None Timestamp: 66.891549s
Buffer: 1 Sequence: 7 Field: None Timestamp: 66.913524s
Buffer: 2 Sequence: 8 Field: None Timestamp: 66.935501s
Buffer: 0 Sequence: 9 Field: None Timestamp: 66.957477s
Buffer: 1 Sequence: 10 Field: None Timestamp: 66.979454s
Buffer: 2 Sequence: 11 Field: None Timestamp: 67.001424s
Buffer: 0 Sequence: 12 Field: None Timestamp: 67.023407s
Buffer: 1 Sequence: 13 Field: None Timestamp: 67.045377s
Buffer: 2 Sequence: 14 Field: None Timestamp: 67.067359s
Buffer: 0 Sequence: 15 Field: None Timestamp: 67.089337s
Buffer: 1 Sequence: 16 Field: None Timestamp: 67.111313s
Buffer: 2 Sequence: 17 Field: None Timestamp: 67.133290s
Buffer: 0 Sequence: 18 Field: None Timestamp: 67.155260s
Buffer: 1 Sequence: 19 Field: None Timestamp: 67.177243s
Buffer: 2 Sequence: 20 Field: None Timestamp: 67.199219s
Buffer: 0 Sequence: 21 Field: None Timestamp: 67.221195s
Buffer: 1 Sequence: 22 Field: None Timestamp: 67.243172s
Buffer: 2 Sequence: 23 Field: None Timestamp: 67.265148s
Buffer: 0 Sequence: 24 Field: None Timestamp: 67.287125s
Buffer: 1 Sequence: 25 Field: None Timestamp: 67.309102s
Buffer: 2 Sequence: 26 Field: None Timestamp: 67.331078s
Buffer: 0 Sequence: 27 Field: None Timestamp: 67.353055s
Buffer: 1 Sequence: 28 Field: None Timestamp: 67.375031s
Buffer: 2 Sequence: 29 Field: None Timestamp: 67.397008s
Buffer: 0 Sequence: 30 Field: None Timestamp: 67.418984s
Buffer: 1 Sequence: 31 Field: None Timestamp: 67.440960s
Buffer: 2 Sequence: 32 Field: None Timestamp: 67.462937s
Buffer: 0 Sequence: 33 Field: None Timestamp: 67.484913s
Buffer: 1 Sequence: 34 Field: None Timestamp: 67.506890s
Buffer: 2 Sequence: 35 Field: None Timestamp: 67.528867s
Buffer: 0 Sequence: 36 Field: None Timestamp: 67.550843s
Buffer: 1 Sequence: 37 Field: None Timestamp: 67.572819s
Buffer: 2 Sequence: 38 Field: None Timestamp: 67.594796s
Buffer: 0 Sequence: 39 Field: None Timestamp: 67.616773s
Buffer: 1 Sequence: 40 Field: None Timestamp: 67.638749s
Buffer: 2 Sequence: 41 Field: None Timestamp: 67.660725s
Buffer: 0 Sequence: 42 Field: None Timestamp: 67.682702s
Buffer: 1 Sequence: 43 Field: None Timestamp: 67.704672s
Buffer: 2 Sequence: 44 Field: None Timestamp: 67.726655s
Buffer: 0 Sequence: 45 Field: None Timestamp: 67.748632s
Buffer: 1 Sequence: 46 Field: None Timestamp: 67.770608s
Buffer: 2 Sequence: 47 Field: None Timestamp: 67.792585s
Buffer: 0 Sequence: 48 Field: None Timestamp: 67.814561s
Buffer: 1 Sequence: 49 Field: None Timestamp: 67.836538s
Buffer: 2 Sequence: 50 Field: None Timestamp: 67.858514s
Buffer: 0 Sequence: 51 Field: None Timestamp: 67.880491s
Buffer: 1 Sequence: 52 Field: None Timestamp: 67.902461s
Buffer: 2 Sequence: 53 Field: None Timestamp: 67.924444s
Buffer: 0 Sequence: 54 Field: None Timestamp: 67.946420s
Buffer: 1 Sequence: 55 Field: None Timestamp: 67.968396s
Buffer: 2 Sequence: 56 Field: None Timestamp: 67.990373s
Buffer: 0 Sequence: 57 Field: None Timestamp: 68.012350s
Buffer: 1 Sequence: 58 Field: None Timestamp: 68.034319s
Buffer: 2 Sequence: 59 Field: None Timestamp: 68.056303s
Video Capture (polling):
Buffer: 0 Sequence: 60 Field: None Timestamp: 68.078279s
Buffer: 1 Sequence: 61 Field: None Timestamp: 68.100256s
Buffer: 2 Sequence: 62 Field: None Timestamp: 68.122232s
Buffer: 0 Sequence: 63 Field: None Timestamp: 68.144208s
Buffer: 1 Sequence: 64 Field: None Timestamp: 68.166185s
Buffer: 2 Sequence: 65 Field: None Timestamp: 68.188162s
Buffer: 0 Sequence: 66 Field: None Timestamp: 68.210138s
Buffer: 1 Sequence: 67 Field: None Timestamp: 68.232108s
Buffer: 2 Sequence: 68 Field: None Timestamp: 68.254085s
Buffer: 0 Sequence: 69 Field: None Timestamp: 68.276068s
Buffer: 1 Sequence: 70 Field: None Timestamp: 68.298044s
Buffer: 2 Sequence: 71 Field: None Timestamp: 68.320021s
Buffer: 0 Sequence: 72 Field: None Timestamp: 68.341997s
Buffer: 1 Sequence: 73 Field: None Timestamp: 68.363974s
Buffer: 2 Sequence: 74 Field: None Timestamp: 68.385950s
Buffer: 0 Sequence: 75 Field: None Timestamp: 68.407920s
Buffer: 1 Sequence: 76 Field: None Timestamp: 68.429903s
Buffer: 2 Sequence: 77 Field: None Timestamp: 68.451880s
Buffer: 0 Sequence: 78 Field: None Timestamp: 68.473856s
Buffer: 1 Sequence: 79 Field: None Timestamp: 68.495833s
Buffer: 2 Sequence: 80 Field: None Timestamp: 68.517809s
Buffer: 0 Sequence: 81 Field: None Timestamp: 68.539779s
Buffer: 1 Sequence: 82 Field: None Timestamp: 68.561756s
Buffer: 2 Sequence: 83 Field: None Timestamp: 68.583738s
Buffer: 0 Sequence: 84 Field: None Timestamp: 68.605715s
Buffer: 1 Sequence: 85 Field: None Timestamp: 68.627692s
Buffer: 2 Sequence: 86 Field: None Timestamp: 68.649668s
Buffer: 0 Sequence: 87 Field: None Timestamp: 68.671645s
Buffer: 1 Sequence: 88 Field: None Timestamp: 68.693621s
Buffer: 2 Sequence: 89 Field: None Timestamp: 68.715597s
Buffer: 0 Sequence: 90 Field: None Timestamp: 68.737574s
Buffer: 1 Sequence: 91 Field: None Timestamp: 68.759550s
Buffer: 2 Sequence: 92 Field: None Timestamp: 68.781527s
Buffer: 0 Sequence: 93 Field: None Timestamp: 68.803503s
Buffer: 1 Sequence: 94 Field: None Timestamp: 68.825480s
Buffer: 2 Sequence: 95 Field: None Timestamp: 68.847450s
Buffer: 0 Sequence: 96 Field: None Timestamp: 68.869427s
Buffer: 1 Sequence: 97 Field: None Timestamp: 68.891409s
Buffer: 2 Sequence: 98 Field: None Timestamp: 68.913386s
Buffer: 0 Sequence: 99 Field: None Timestamp: 68.935363s
Buffer: 1 Sequence: 100 Field: None Timestamp: 68.957339s
Buffer: 2 Sequence: 101 Field: None Timestamp: 68.979315s
Buffer: 0 Sequence: 102 Field: None Timestamp: 69.001292s
Buffer: 1 Sequence: 103 Field: None Timestamp: 69.023269s
Buffer: 2 Sequence: 104 Field: None Timestamp: 69.045239s
Buffer: 0 Sequence: 105 Field: None Timestamp: 69.067222s
Buffer: 1 Sequence: 106 Field: None Timestamp: 69.089198s
Buffer: 2 Sequence: 107 Field: None Timestamp: 69.111175s
Buffer: 0 Sequence: 108 Field: None Timestamp: 69.133151s
Buffer: 1 Sequence: 109 Field: None Timestamp: 69.155121s
Buffer: 2 Sequence: 110 Field: None Timestamp: 69.177098s
Buffer: 0 Sequence: 111 Field: None Timestamp: 69.199074s
Buffer: 1 Sequence: 112 Field: None Timestamp: 69.221057s
Buffer: 2 Sequence: 113 Field: None Timestamp: 69.243033s
Buffer: 0 Sequence: 114 Field: None Timestamp: 69.265010s
Buffer: 1 Sequence: 115 Field: None Timestamp: 69.286986s
Buffer: 2 Sequence: 116 Field: None Timestamp: 69.308963s
Buffer: 0 Sequence: 117 Field: None Timestamp: 69.330940s
Buffer: 1 Sequence: 118 Field: None Timestamp: 69.352916s
Buffer: 2 Sequence: 119 Field: None Timestamp: 69.374892s
test MMAP: OK
test USERPTR: OK (Not Supported)
test DMABUF: Cannot test, specify --expbuf-device
Total: 45, Succeeded: 45, Failed: 0, Warnings: 0
Benoit Parrot (2):
media: v4l: ti-vpe: Add CAL v4l2 camera capture driver
media: v4l: ti-vpe: Document CAL driver
Documentation/devicetree/bindings/media/ti-cal.txt | 70 +
drivers/media/platform/Kconfig | 12 +
drivers/media/platform/Makefile | 2 +
drivers/media/platform/ti-vpe/Makefile | 4 +
drivers/media/platform/ti-vpe/cal.c | 2225 ++++++++++++++++++++
drivers/media/platform/ti-vpe/cal_regs.h | 779 +++++++
6 files changed, 3092 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/ti-cal.txt
create mode 100644 drivers/media/platform/ti-vpe/cal.c
create mode 100644 drivers/media/platform/ti-vpe/cal_regs.h
--
1.8.5.1
^ permalink raw reply [flat|nested] 8+ messages in thread
* [Patch 1/2] media: v4l: ti-vpe: Add CAL v4l2 camera capture driver
2015-06-16 17:29 [Patch 0/2] media: v4l: ti-vpe: Add CAL v4l2 camera capture driver Benoit Parrot
@ 2015-06-16 17:29 ` Benoit Parrot
2015-06-22 8:09 ` Hans Verkuil
2015-06-16 17:29 ` [Patch 2/2] media: v4l: ti-vpe: Document CAL driver Benoit Parrot
2015-06-22 7:31 ` [Patch 0/2] media: v4l: ti-vpe: Add CAL v4l2 camera capture driver Hans Verkuil
2 siblings, 1 reply; 8+ messages in thread
From: Benoit Parrot @ 2015-06-16 17:29 UTC (permalink / raw)
To: Hans Verkuil
Cc: linux-media, devicetree, Tony Lindgren, linux-kernel, Benoit Parrot
The Camera Adaptation Layer (CAL) is a block which consists of a dual
port CSI2/MIPI camera capture engine.
Port #0 can handle CSI2 camera connected to up to 4 data lanes.
Port #1 can handle CSI2 camera connected to up to 2 data lanes.
The driver implements the required API/ioctls to be V4L2 compliant.
Driver supports the following:
- V4L2 API using DMABUF/MMAP buffer access based on videobuf2 api
- Asynchronous sensor sub device registration
- DT support
Signed-off-by: Benoit Parrot <bparrot@ti.com>
---
drivers/media/platform/Kconfig | 12 +
drivers/media/platform/Makefile | 2 +
drivers/media/platform/ti-vpe/Makefile | 4 +
drivers/media/platform/ti-vpe/cal.c | 2225 ++++++++++++++++++++++++++++++
drivers/media/platform/ti-vpe/cal_regs.h | 779 +++++++++++
5 files changed, 3022 insertions(+)
create mode 100644 drivers/media/platform/ti-vpe/cal.c
create mode 100644 drivers/media/platform/ti-vpe/cal_regs.h
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 4776a8c..c4c9c18 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -120,6 +120,18 @@ source "drivers/media/platform/s5p-tv/Kconfig"
source "drivers/media/platform/am437x/Kconfig"
source "drivers/media/platform/xilinx/Kconfig"
+config VIDEO_TI_CAL
+ tristate "TI CAL (Camera Adaptation Layer) driver"
+ depends on VIDEO_DEV && VIDEO_V4L2 && SOC_DRA7XX
+ depends on VIDEO_V4L2_SUBDEV_API
+ depends on VIDEOBUF2_DMA_CONTIG
+ default n
+ ---help---
+ Support for the TI CAL (Camera Adaptation Layer) block
+ found on DRA72X SoC.
+ In TI Technical Reference Manual this module is referred as
+ Camera Interface Subsystem (CAMSS).
+
endif # V4L_PLATFORM_DRIVERS
menuconfig V4L_MEM2MEM_DRIVERS
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 114f9ab..062022f 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -18,6 +18,8 @@ obj-$(CONFIG_VIDEO_VIM2M) += vim2m.o
obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe/
+obj-$(CONFIG_VIDEO_TI_CAL) += ti-vpe/
+
obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o
obj-$(CONFIG_VIDEO_CODA) += coda/
diff --git a/drivers/media/platform/ti-vpe/Makefile b/drivers/media/platform/ti-vpe/Makefile
index be680f8..e236059 100644
--- a/drivers/media/platform/ti-vpe/Makefile
+++ b/drivers/media/platform/ti-vpe/Makefile
@@ -3,3 +3,7 @@ obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe.o
ti-vpe-y := vpe.o sc.o csc.o vpdma.o
ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) += -DDEBUG
+
+obj-$(CONFIG_VIDEO_TI_CAL) += ti-cal.o
+
+ti-cal-y := cal.o
diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c
new file mode 100644
index 0000000..cc1f61e
--- /dev/null
+++ b/drivers/media/platform/ti-vpe/cal.c
@@ -0,0 +1,2225 @@
+/*
+ * TI CAL camera interface driver
+ *
+ * Copyright (c) 2015 Texas Instruments Inc.
+ * Benoit Parrot, <bparrot@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioctl.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+
+#include <media/v4l2-of.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-common.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+#include "cal_regs.h"
+
+#define CAL_MODULE_NAME "cal"
+
+#define MAX_WIDTH 1920
+#define MAX_HEIGHT 1200
+
+#define CAL_VERSION "0.1.0"
+
+MODULE_DESCRIPTION("TI CAL driver");
+MODULE_AUTHOR("Benoit Parrot, <bparrot@ti.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(CAL_VERSION);
+
+static unsigned video_nr = -1;
+module_param(video_nr, uint, 0644);
+MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect");
+
+static unsigned debug;
+module_param(debug, uint, 0644);
+MODULE_PARM_DESC(debug, "activates debug info");
+
+/* timeperframe: min/max and default */
+static const struct v4l2_fract
+ tpf_default = {.numerator = 1001, .denominator = 30000};
+
+#define cal_dbg(level, caldev, fmt, arg...) \
+ v4l2_dbg(level, debug, &caldev->v4l2_dev, fmt, ##arg)
+#define cal_info(caldev, fmt, arg...) \
+ v4l2_info(&caldev->v4l2_dev, fmt, ##arg)
+#define cal_err(caldev, fmt, arg...) \
+ v4l2_err(&caldev->v4l2_dev, fmt, ##arg)
+
+#define ctx_dbg(level, ctx, fmt, arg...) \
+ v4l2_dbg(level, debug, &ctx->v4l2_dev, fmt, ##arg)
+#define ctx_info(ctx, fmt, arg...) \
+ v4l2_info(&ctx->v4l2_dev, fmt, ##arg)
+#define ctx_err(ctx, fmt, arg...) \
+ v4l2_err(&ctx->v4l2_dev, fmt, ##arg)
+
+#define CAL_NUM_INPUT 1
+#define CAL_NUM_CONTEXT 2
+
+/* ------------------------------------------------------------------
+ Basic structures
+ ------------------------------------------------------------------*/
+
+struct cal_fmt {
+ const char *name;
+ u32 fourcc;
+ u32 code;
+ u32 colorspace;
+ u8 depth;
+ bool supported;
+ u32 index;
+};
+
+static const struct cal_fmt formats[] = {
+ {
+ .name = "YUV 4:2:2 packed, YCbYCr",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ .depth = 16,
+ .supported = false,
+ }, {
+ .name = "YUV 4:2:2 packed, CbYCrY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ .depth = 16,
+ .supported = false,
+ }, {
+ .name = "YUV 4:2:2 packed, YCrYCb",
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .code = MEDIA_BUS_FMT_YVYU8_2X8,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ .depth = 16,
+ .supported = false,
+ }, {
+ .name = "4YUV 4:2:2 packed, CrYCbY",
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .code = MEDIA_BUS_FMT_VYUY8_2X8,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ .depth = 16,
+ .supported = false,
+ }, {
+ .name = "RGB565 (LE)",
+ .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
+ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 16,
+ .supported = false,
+ }, {
+ .name = "RGB565 (BE)",
+ .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
+ .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 16,
+ .supported = false,
+ }, {
+ .name = "RGB555 (LE)",
+ .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 16,
+ .supported = false,
+ }, {
+ .name = "RGB555 (BE)",
+ .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 16,
+ .supported = false,
+ }, {
+ .name = "RGB24 (LE)",
+ .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
+ .code = MEDIA_BUS_FMT_RGB888_2X12_LE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 24,
+ .supported = false,
+ }, {
+ .name = "RGB24 (BE)",
+ .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
+ .code = MEDIA_BUS_FMT_RGB888_2X12_BE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 24,
+ .supported = false,
+ }, {
+ .name = "RGB32",
+ .fourcc = V4L2_PIX_FMT_RGB32, /* argb */
+ .code = MEDIA_BUS_FMT_ARGB8888_1X32,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 32,
+ .supported = false,
+ }, {
+ .name = "RAW8 BGGR",
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 8,
+ .supported = false,
+ }, {
+ .name = "RAW8 GBRG",
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 8,
+ .supported = false,
+ }, {
+ .name = "RAW8 GRBG",
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 8,
+ .supported = false,
+ }, {
+ .name = "RAW8 RGGB",
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 8,
+ .supported = false,
+ }, {
+ .name = "RAW10 BGGR",
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 16,
+ .supported = false,
+ }, {
+ .name = "RAW10 GBRG",
+ .fourcc = V4L2_PIX_FMT_SGBRG10,
+ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 16,
+ .supported = false,
+ }, {
+ .name = "RAW10 GRBG",
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 16,
+ .supported = false,
+ }, {
+ .name = "RAW10 RGGB",
+ .fourcc = V4L2_PIX_FMT_SRGGB10,
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 16,
+ .supported = false,
+ }, {
+ .name = "RAW12 BGGR",
+ .fourcc = V4L2_PIX_FMT_SBGGR12,
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 16,
+ .supported = false,
+ }, {
+ .name = "RAW12 GBRG",
+ .fourcc = V4L2_PIX_FMT_SGBRG12,
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 16,
+ .supported = false,
+ }, {
+ .name = "RAW12 GRBG",
+ .fourcc = V4L2_PIX_FMT_SGRBG12,
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 16,
+ .supported = false,
+ }, {
+ .name = "RAW12 RGGB",
+ .fourcc = V4L2_PIX_FMT_SRGGB12,
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 16,
+ .supported = false,
+ },
+};
+
+static const struct cal_fmt *find_format_by_pix(u32 pixelformat)
+{
+ const struct cal_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 const struct cal_fmt *find_format_by_code(u32 code)
+{
+ const struct cal_fmt *fmt;
+ unsigned int k;
+
+ for (k = 0; k < ARRAY_SIZE(formats); k++) {
+ fmt = &formats[k];
+ if (fmt->code == code)
+ return fmt;
+ }
+
+ return NULL;
+}
+
+/* buffer for one video frame */
+struct cal_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct vb2_buffer vb;
+ struct list_head list;
+ const struct cal_fmt *fmt;
+};
+
+struct cal_dmaqueue {
+ struct list_head active;
+
+ /* Counters to control fps rate */
+ int frame;
+ int ini_jiffies;
+};
+
+struct cm_data {
+ void __iomem *base;
+ struct resource *res;
+
+ unsigned int camerrx_control;
+
+ struct platform_device *pdev;
+};
+
+struct cc_data {
+ void __iomem *base;
+ struct resource *res;
+
+ struct platform_device *pdev;
+};
+
+/*
+ * there is one cal_dev structure in the driver, it is shared by
+ * all instances.
+ */
+struct cal_dev {
+ int irq;
+ void __iomem *base;
+ struct resource *res;
+ struct platform_device *pdev;
+ struct v4l2_device v4l2_dev;
+
+ struct cm_data *cm; /* Control Module handle */
+
+ struct cal_ctx *ctx[CAL_NUM_CONTEXT];
+};
+
+/*
+ * There is one cal_ctx structure for each camera core context.
+ */
+struct cal_ctx {
+ struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct video_device vdev;
+ struct v4l2_async_notifier notifier;
+ struct v4l2_subdev *sensor;
+ struct v4l2_of_endpoint endpoint;
+
+ struct v4l2_async_subdev asd;
+ struct v4l2_async_subdev *asd_list[1];
+
+ struct v4l2_fh fh;
+ struct cal_dev *dev;
+ struct cc_data *cc;
+
+ /* v4l2_ioctl mutex */
+ struct mutex mutex;
+ /* v4l2 buffers lock */
+ spinlock_t slock;
+
+ /* Several counters */
+ unsigned long jiffies;
+
+ struct vb2_alloc_ctx *alloc_ctx;
+ struct cal_dmaqueue vidq;
+
+ /* Input Number */
+ int input;
+
+ /* video capture */
+ const struct cal_fmt *fmt;
+ struct v4l2_fract timeperframe;
+ unsigned int width, height;
+ unsigned int field;
+ unsigned int sequence;
+ unsigned int pixelsize;
+ unsigned int external_rate;
+ struct vb2_queue vb_vidq;
+ unsigned int seq_count;
+ unsigned int csi2_port;
+ unsigned int virtual_channel;
+
+ /* Pointer pointing to current v4l2_buffer */
+ struct cal_buffer *cur_frm;
+ /* Pointer pointing to next v4l2_buffer */
+ struct cal_buffer *next_frm;
+};
+
+static inline struct cal_ctx *notifier_to_ctx(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct cal_ctx, notifier);
+}
+
+/* register field read/write helpers */
+static inline int get_field(u32 value, u32 mask, int shift)
+{
+ return (value & (mask << shift)) >> shift;
+}
+
+static inline void write_field(u32 *valp, u32 field, u32 mask, int shift)
+{
+ u32 val = *valp;
+
+ val &= ~(mask << shift);
+ val |= (field & mask) << shift;
+ *valp = val;
+}
+
+static inline u32 cal_read(struct cal_dev *dev, int offset)
+{
+ return ioread32(dev->base + offset);
+}
+
+static inline void cal_write(struct cal_dev *dev, int offset, u32 value)
+{
+ iowrite32(value, dev->base + offset);
+}
+
+static inline int
+cal_read_field(struct cal_dev *dev, int offset, u32 mask, int shift)
+{
+ return get_field(cal_read(dev, offset), mask, shift);
+}
+
+static inline void cal_write_field(struct cal_dev *dev, int offset, u32 field,
+ u32 mask, int shift)
+{
+ u32 val = cal_read(dev, offset);
+
+ write_field(&val, field, mask, shift);
+
+ cal_write(dev, offset, val);
+}
+
+/*
+ * Control Module block access
+ */
+static struct cm_data *cm_create(struct cal_dev *dev)
+{
+ struct platform_device *pdev = dev->pdev;
+ struct cm_data *cm;
+
+ cal_dbg(3, dev, "cm_create\n");
+
+ cm = devm_kzalloc(&pdev->dev, sizeof(*cm), GFP_KERNEL);
+ if (!cm)
+ return ERR_PTR(-ENOMEM);
+
+ cm->res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "camerrx_control");
+ if (!cm->res)
+ return ERR_PTR(-ENODEV);
+
+ cal_dbg(1, dev, "ioresource %s at %x - %x\n",
+ cm->res->name, cm->res->start, cm->res->end);
+
+ cm->base = devm_ioremap_resource(&pdev->dev, cm->res);
+ if (!cm->base) {
+ cal_err(dev, "failed to ioremap\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ return cm;
+}
+
+static inline u32 cm_read(struct cm_data *dev, int offset)
+{
+ return ioread32(dev->base + offset);
+}
+
+static inline void cm_write(struct cm_data *dev, int offset, u32 value)
+{
+ iowrite32(value, dev->base + offset);
+}
+
+static inline void cm_write_field(struct cm_data *dev, int offset, u32 field,
+ u32 mask, int shift)
+{
+ u32 val = cm_read(dev, offset);
+
+ write_field(&val, field, mask, shift);
+
+ cm_write(dev, offset, val);
+}
+
+static void camerarx_phy_enable(struct cal_ctx *ctx)
+{
+ u32 val;
+
+ ctx_dbg(3, ctx, "%s\n", __func__);
+
+ if (!ctx->dev->cm->base) {
+ ctx_err(ctx, "cm not mapped\n");
+ return;
+ }
+
+ val = cm_read(ctx->dev->cm, CM_CTRL_CORE_CAMERRX_CONTROL);
+ if (ctx->csi2_port == 1) {
+ write_field(&val, 1, CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_MASK,
+ CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_SHIFT);
+ write_field(&val, 0, CM_CAMERRX_CTRL_CSI0_CAMMODE_MASK,
+ CM_CAMERRX_CTRL_CSI0_CAMMODE_SHIFT);
+ /* enable all lanes by default */
+ write_field(&val, 0xf, CM_CAMERRX_CTRL_CSI0_LANEENABLE_MASK,
+ CM_CAMERRX_CTRL_CSI0_LANEENABLE_SHIFT);
+ write_field(&val, 1, CM_CAMERRX_CTRL_CSI0_MODE_MASK,
+ CM_CAMERRX_CTRL_CSI0_MODE_SHIFT);
+ } else if (ctx->csi2_port == 2) {
+ write_field(&val, 1, CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_MASK,
+ CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_SHIFT);
+ write_field(&val, 0, CM_CAMERRX_CTRL_CSI1_CAMMODE_MASK,
+ CM_CAMERRX_CTRL_CSI0_CAMMODE_SHIFT);
+ /* enable all lanes by default */
+ write_field(&val, 0x3, CM_CAMERRX_CTRL_CSI1_LANEENABLE_MASK,
+ CM_CAMERRX_CTRL_CSI0_LANEENABLE_SHIFT);
+ write_field(&val, 1, CM_CAMERRX_CTRL_CSI1_MODE_MASK,
+ CM_CAMERRX_CTRL_CSI0_MODE_SHIFT);
+ }
+ cm_write(ctx->dev->cm, CM_CTRL_CORE_CAMERRX_CONTROL, val);
+}
+
+static void camerarx_phy_disable(struct cal_ctx *ctx)
+{
+ ctx_dbg(3, ctx, "%s\n", __func__);
+
+ if (!ctx->dev->cm->base) {
+ ctx_err(ctx, "cm not mapped\n");
+ return;
+ }
+
+ if (ctx->csi2_port == 1)
+ cm_write_field(ctx->dev->cm,
+ CM_CTRL_CORE_CAMERRX_CONTROL,
+ 0x0,
+ CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_MASK,
+ CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_SHIFT);
+ else if (ctx->csi2_port == 2)
+ cm_write_field(ctx->dev->cm,
+ CM_CTRL_CORE_CAMERRX_CONTROL,
+ 0x0,
+ CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_MASK,
+ CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_SHIFT);
+}
+
+/*
+ * Camera Instance access block
+ */
+static struct cc_data *cc_create(struct cal_dev *dev, unsigned int core)
+{
+ struct platform_device *pdev = dev->pdev;
+ struct cc_data *cc;
+
+ cal_dbg(3, dev, "cc_create\n");
+
+ cc = devm_kzalloc(&pdev->dev, sizeof(*cc), GFP_KERNEL);
+ if (!cc)
+ return ERR_PTR(-ENOMEM);
+
+ cc->res = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM,
+ (core == 0) ?
+ "cal_rx_core0" :
+ "cal_rx_core1");
+ if (!cc->res) {
+ cal_err(dev, "missing platform resources data\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ cal_dbg(1, dev, "ioresource %s at %x - %x\n",
+ cc->res->name, cc->res->start, cc->res->end);
+
+ cc->base = devm_ioremap_resource(&pdev->dev, cc->res);
+ if (!cc->base) {
+ cal_err(dev, "failed to ioremap\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ return cc;
+}
+
+static inline u32 cc_read(struct cc_data *dev, int offset)
+{
+ return ioread32(dev->base + offset);
+}
+
+static inline void cc_write(struct cc_data *dev, int offset, u32 value)
+{
+ iowrite32(value, dev->base + offset);
+}
+
+/*
+ * Get Revision and HW info
+ */
+static void cal_get_hwinfo(struct cal_dev *dev)
+{
+ u32 revision = 0;
+ u32 hwinfo = 0;
+
+ revision = cal_read(dev, CAL_HL_REVISION);
+ cal_dbg(3, dev, "CAL_HL_REVISION = 0x%08x (expecting 0x40000200)\n",
+ revision);
+
+ hwinfo = cal_read(dev, CAL_HL_HWINFO);
+ cal_dbg(3, dev, "CAL_HL_HWINFO = 0x%08x (expecting 0xA3C90469)\n",
+ hwinfo);
+}
+
+/*
+ * Soft-Reset the Main Cal module. Not sure if this is needed.
+ */
+/*
+static void cal_top_reset(struct cal_dev *dev)
+{
+ cal_write_field(dev,
+ CAL_HL_SYSCONFIG,
+ CAL_HL_SYSCONFIG_SOFTRESET_RESET,
+ CAL_HL_SYSCONFIG_SOFTRESET_MASK,
+ CAL_HL_SYSCONFIG_SOFTRESET_SHIFT);
+
+ while(cal_read_field(dev,
+ CAL_HL_SYSCONFIG,
+ CAL_HL_SYSCONFIG_SOFTRESET_MASK,
+ CAL_HL_SYSCONFIG_SOFTRESET_SHIFT) !=
+ CAL_HL_SYSCONFIG_SOFTRESET_DONE);
+}
+*/
+
+static void cal_quickdump_regs(struct cal_dev *dev)
+{
+ cal_info(dev, "CAL Registers @ 0x%08x:\n", dev->res->start);
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
+ dev->base, (dev->res->end - dev->res->start + 1), false);
+
+ if (!dev->ctx[0]) {
+ cal_info(dev, "CSI2 Core 0 Registers @ 0x%08x:\n",
+ dev->ctx[0]->cc->res->start);
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
+ dev->ctx[0]->cc->base,
+ (dev->ctx[0]->cc->res->end -
+ dev->ctx[0]->cc->res->start + 1),
+ false);
+ }
+
+ if (!dev->ctx[1]) {
+ cal_info(dev, "CSI2 Core 1 Registers @ 0x%08x:\n",
+ dev->ctx[1]->cc->res->start);
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
+ dev->ctx[1]->cc->base,
+ (dev->ctx[1]->cc->res->end -
+ dev->ctx[1]->cc->res->start + 1),
+ false);
+ }
+
+ cal_info(dev, "CAMERRX_Control Registers @ 0x%08x:\n",
+ dev->cm->res->start);
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
+ dev->cm->base,
+ (dev->cm->res->end - dev->cm->res->start + 1), false);
+}
+
+/*
+ * Enable the expected IRQ sources
+ */
+static void enable_irqs(struct cal_ctx *ctx)
+{
+ /* Enable IRQ_WDMA_END 0/1 */
+ cal_write_field(ctx->dev,
+ CAL_HL_IRQENABLE_SET(2),
+ CAL_HL_IRQ_ENABLE,
+ CAL_HL_IRQ_MASK(ctx->csi2_port),
+ CAL_HL_IRQ_SHIFT(ctx->csi2_port));
+ /* Enable IRQ_WDMA_START 0/1 */
+ cal_write_field(ctx->dev,
+ CAL_HL_IRQENABLE_SET(3),
+ CAL_HL_IRQ_ENABLE,
+ CAL_HL_IRQ_MASK(ctx->csi2_port),
+ CAL_HL_IRQ_SHIFT(ctx->csi2_port));
+ /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */
+ cal_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0xFF000000);
+}
+
+static void disable_irqs(struct cal_ctx *ctx)
+{
+ /* Disable IRQ_WDMA_END 0/1 */
+ cal_write_field(ctx->dev,
+ CAL_HL_IRQENABLE_CLR(2),
+ CAL_HL_IRQ_CLEAR,
+ CAL_HL_IRQ_MASK(ctx->csi2_port),
+ CAL_HL_IRQ_SHIFT(ctx->csi2_port));
+ /* Disable IRQ_WDMA_START 0/1 */
+ cal_write_field(ctx->dev,
+ CAL_HL_IRQENABLE_CLR(3),
+ CAL_HL_IRQ_ENABLE,
+ CAL_HL_IRQ_MASK(ctx->csi2_port),
+ CAL_HL_IRQ_SHIFT(ctx->csi2_port));
+ /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */
+ cal_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0);
+}
+
+static void csi2_init(struct cal_ctx *ctx)
+{
+ u32 val;
+
+ ctx_dbg(3, ctx, "%s\n", __func__);
+
+ val = cal_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port));
+ write_field(&val, CAL_GEN_ENABLE,
+ CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK,
+ CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_SHIFT);
+ write_field(&val, CAL_GEN_ENABLE,
+ CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK,
+ CAL_CSI2_TIMING_STOP_STATE_X16_IO1_SHIFT);
+ write_field(&val, CAL_GEN_DISABLE,
+ CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK,
+ CAL_CSI2_TIMING_STOP_STATE_X4_IO1_SHIFT);
+ write_field(&val, 407, CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK,
+ CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT);
+ cal_write(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), val);
+ ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x\n", ctx->csi2_port,
+ cal_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port)));
+
+ val = cal_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port));
+ write_field(&val, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL,
+ CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK,
+ CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_SHIFT);
+ write_field(&val, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON,
+ CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK,
+ CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_SHIFT);
+ cal_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val);
+ while (cal_read_field(ctx->dev,
+ CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port),
+ CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK,
+ CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_SHIFT) !=
+ CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_ON)
+ ;
+ ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", ctx->csi2_port,
+ cal_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)));
+
+ val = cal_read(ctx->dev, CAL_CTRL);
+ write_field(&val, CAL_CTRL_BURSTSIZE_BURST128,
+ CAL_CTRL_BURSTSIZE_MASK, CAL_CTRL_BURSTSIZE_SHIFT);
+ write_field(&val, 0xF,
+ CAL_CTRL_TAGCNT_MASK, CAL_CTRL_TAGCNT_SHIFT);
+ write_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED,
+ CAL_CTRL_POSTED_WRITES_MASK, CAL_CTRL_POSTED_WRITES_SHIFT);
+ write_field(&val, 0xFF,
+ CAL_CTRL_MFLAGL_MASK, CAL_CTRL_MFLAGL_SHIFT);
+ write_field(&val, 0xFF,
+ CAL_CTRL_MFLAGH_MASK, CAL_CTRL_MFLAGH_SHIFT);
+ cal_write(ctx->dev, CAL_CTRL, val);
+ ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", cal_read(ctx->dev, CAL_CTRL));
+}
+
+static void csi2_lane_config(struct cal_ctx *ctx)
+{
+ u32 val = cal_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port));
+ u32 lane_shift = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT;
+ u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK;
+ u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK;
+ struct v4l2_of_bus_mipi_csi2 *mipi_csi2 = &ctx->endpoint.bus.mipi_csi2;
+ int lane;
+
+ ctx_dbg(3, ctx, "%s\n", __func__);
+
+ write_field(&val, mipi_csi2->clock_lane + 1,
+ lane_mask, lane_shift);
+ write_field(&val, mipi_csi2->lane_polarities[0],
+ polarity_mask, lane_shift + 3);
+ for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) {
+ /*
+ * Every lane are one nibble apart starting with the
+ * clock followed by the data lanes so shift incements by 4.
+ */
+ lane_shift += 4;
+ write_field(&val, mipi_csi2->data_lanes[lane] + 1,
+ lane_mask, lane_shift);
+ write_field(&val, mipi_csi2->lane_polarities[lane + 1],
+ polarity_mask, lane_shift + 3);
+ }
+ cal_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val);
+ ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n",
+ ctx->csi2_port, val);
+}
+
+static void csi2_ppi_enable(struct cal_ctx *ctx)
+{
+ ctx_dbg(3, ctx, "%s\n", __func__);
+
+ cal_write_field(ctx->dev,
+ CAL_CSI2_PPI_CTRL(ctx->csi2_port),
+ CAL_GEN_ENABLE,
+ CAL_CSI2_PPI_CTRL_IF_EN_MASK,
+ CAL_CSI2_PPI_CTRL_IF_EN_SHIFT);
+}
+
+static void csi2_ppi_disable(struct cal_ctx *ctx)
+{
+ ctx_dbg(3, ctx, "%s\n", __func__);
+
+ cal_write_field(ctx->dev,
+ CAL_CSI2_PPI_CTRL(ctx->csi2_port),
+ CAL_GEN_DISABLE,
+ CAL_CSI2_PPI_CTRL_IF_EN_MASK,
+ CAL_CSI2_PPI_CTRL_IF_EN_SHIFT);
+}
+
+static void csi2_ctx_config(struct cal_ctx *ctx)
+{
+ u32 val;
+
+ ctx_dbg(3, ctx, "%s\n", __func__);
+
+ val = cal_read(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port));
+ write_field(&val, ctx->csi2_port, CAL_CSI2_CTX_CPORT_MASK,
+ CAL_CSI2_CTX_CPORT_SHIFT);
+ /* DT type: MIPI CSI-2 Specs
+ 1: All DT filter is disabled
+ 0x24: RGB888 1 pixel = 3 bytes
+ 0x2B: RAW10 4 pixels = 5 bytes
+ 0x2A: RAW8 1 pixel = 1 byte
+ 0x1E: YUV422 2 pixels = 4 bytes
+ */
+ write_field(&val, 0x1, CAL_CSI2_CTX_DT_MASK,
+ CAL_CSI2_CTX_DT_SHIFT);
+ /* Virtual Channel from the CSI2 sensor usually 0! */
+ write_field(&val, ctx->virtual_channel, CAL_CSI2_CTX_VC_MASK,
+ CAL_CSI2_CTX_VC_SHIFT);
+ /* NUM_LINES_PER_FRAME => 0 means we don't know */
+ write_field(&val, 0, CAL_CSI2_CTX_LINES_MASK,
+ CAL_CSI2_CTX_LINES_SHIFT);
+ write_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK,
+ CAL_CSI2_CTX_ATT_SHIFT);
+ cal_write_field(ctx->dev,
+ CAL_CSI2_CTX0(ctx->csi2_port),
+ CAL_CSI2_CTX_PACK_MODE_LINE,
+ CAL_CSI2_CTX_PACK_MODE_MASK,
+ CAL_CSI2_CTX_PACK_MODE_SHIFT);
+ write_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE,
+ CAL_CSI2_CTX_PACK_MODE_MASK, CAL_CSI2_CTX_PACK_MODE_SHIFT);
+ cal_write(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port), val);
+ ctx_dbg(3, ctx, "CAL_CSI2_CTX0(%d) = 0x%08x\n", ctx->csi2_port,
+ cal_read(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port)));
+}
+
+static void pix_proc_config(struct cal_ctx *ctx)
+{
+ u32 val;
+
+ ctx_dbg(3, ctx, "%s\n", __func__);
+
+ val = cal_read(ctx->dev, CAL_PIX_PROC(ctx->csi2_port));
+ write_field(&val, CAL_PIX_PROC_EXTRACT_B8, CAL_PIX_PROC_EXTRACT_MASK,
+ CAL_PIX_PROC_EXTRACT_SHIFT);
+ write_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK,
+ CAL_PIX_PROC_DPCMD_SHIFT);
+ write_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK,
+ CAL_PIX_PROC_DPCME_SHIFT);
+ write_field(&val, CAL_PIX_PROC_PACK_B8, CAL_PIX_PROC_PACK_MASK,
+ CAL_PIX_PROC_PACK_SHIFT);
+ write_field(&val, ctx->csi2_port, CAL_PIX_PROC_CPORT_MASK,
+ CAL_PIX_PROC_CPORT_SHIFT);
+ cal_write_field(ctx->dev,
+ CAL_PIX_PROC(ctx->csi2_port),
+ CAL_GEN_ENABLE,
+ CAL_PIX_PROC_EN_MASK,
+ CAL_PIX_PROC_EN_SHIFT);
+ write_field(&val, CAL_GEN_ENABLE, CAL_PIX_PROC_EN_MASK,
+ CAL_PIX_PROC_EN_SHIFT);
+ cal_write(ctx->dev, CAL_PIX_PROC(ctx->csi2_port), val);
+ ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->csi2_port,
+ cal_read(ctx->dev, CAL_PIX_PROC(ctx->csi2_port)));
+}
+
+#define bytes_per_line(pixel, bpp) (ALIGN(pixel * bpp, 16))
+
+static void cal_wr_dma_config(struct cal_ctx *ctx,
+ unsigned int width)
+{
+ u32 val;
+
+ ctx_dbg(3, ctx, "%s\n", __func__);
+
+ val = cal_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port));
+ write_field(&val, ctx->csi2_port, CAL_WR_DMA_CTRL_CPORT_MASK,
+ CAL_WR_DMA_CTRL_CPORT_SHIFT);
+ write_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT,
+ CAL_WR_DMA_CTRL_DTAG_MASK, CAL_WR_DMA_CTRL_DTAG_SHIFT);
+ write_field(&val, CAL_WR_DMA_CTRL_MODE_CONST,
+ CAL_WR_DMA_CTRL_MODE_MASK, CAL_WR_DMA_CTRL_MODE_SHIFT);
+ write_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR,
+ CAL_WR_DMA_CTRL_PATTERN_MASK,
+ CAL_WR_DMA_CTRL_PATTERN_SHIFT);
+ write_field(&val, CAL_GEN_ENABLE,
+ CAL_WR_DMA_CTRL_STALL_RD_MASK,
+ CAL_WR_DMA_CTRL_STALL_RD_SHIFT);
+ cal_write(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port), val);
+ ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->csi2_port,
+ cal_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port)));
+
+ /*
+ * width/16 not sure but giving it a whirl.
+ * zero does not work right
+ */
+ cal_write_field(ctx->dev,
+ CAL_WR_DMA_OFST(ctx->csi2_port),
+ (width / 16),
+ CAL_WR_DMA_OFST_MASK,
+ CAL_WR_DMA_OFST_SHIFT);
+ ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->csi2_port,
+ cal_read(ctx->dev, CAL_WR_DMA_OFST(ctx->csi2_port)));
+
+ val = cal_read(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port));
+ /* 64 bit word means no skipping */
+ write_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK,
+ CAL_WR_DMA_XSIZE_XSKIP_SHIFT);
+ /*
+ * (width*8)/64 this should be size of an entire line
+ * in 64bit word but 0 means all data until the end
+ * is detected automagically
+ */
+ write_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK,
+ CAL_WR_DMA_XSIZE_SHIFT);
+ cal_write(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port), val);
+ ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->csi2_port,
+ cal_read(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port)));
+}
+
+static void cal_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr)
+{
+ cal_write(ctx->dev, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr);
+/* ctx_dbg(3, ctx, "CAL_WR_DMA_ADDR(%d) = 0x%08x\n", ctx->csi2_port,
+ cal_read(ctx->dev,CAL_WR_DMA_ADDR(ctx->csi2_port))); */
+}
+
+/*
+ * TCLK values are OK at their reset values
+ */
+#define TCLK_TERM 0
+#define TCLK_MISS 1
+#define TCLK_SETTLE 14
+#define THS_SETTLE 15
+
+static void csi2_phy_config(struct cal_ctx *ctx)
+{
+ unsigned int reg0, reg1;
+ unsigned int ths_term, ths_settle;
+
+ ctx_dbg(3, ctx, "%s\n", __func__);
+
+#ifdef LEGACY_CSI2PHY_FORMULA
+ {
+ int csi2_ddrclk_khz;
+
+ csi2_ddrclk_khz = ctx->external_rate / 1000
+ / (2 * ctx->endpoint.bus.mipi_csi2.num_data_lanes)
+ * ctx->fmt->depth;
+
+ /*
+ * THS_TERM: Programmed value = ceil(12.5 ns/DDRClk period) - 1.
+ * THS_SETTLE: Programmed value = ceil(90 ns/DDRClk period) + 3.
+ */
+ ths_term = DIV_ROUND_UP(25 * csi2_ddrclk_khz, 2000000) - 1;
+ ths_settle = DIV_ROUND_UP(90 * csi2_ddrclk_khz, 1000000) + 3;
+ }
+#else
+ {
+ unsigned int ddrclkperiod_us;
+
+ /*
+ * THS_TERM: Programmed value = floor(20 ns/DDRClk period) - 2.
+ */
+ ddrclkperiod_us = ctx->external_rate / 2000000;
+ ddrclkperiod_us = 1000000 / ddrclkperiod_us;
+ ctx_dbg(1, ctx, "ddrclkperiod_us: %d\n", ddrclkperiod_us);
+
+ ths_term = 20000 / ddrclkperiod_us;
+ ths_term = (ths_term >= 2) ? ths_term - 2 : ths_term;
+ ctx_dbg(1, ctx, "ths_term: %d (0x%02x)\n", ths_term, ths_term);
+
+ /*
+ * THS_SETTLE: Programmed value = floor(176.3 ns/CtrlClk period) - 1.
+ * Since CtrlClk is fixed at 96Mhz then we get
+ * ths_settle = floor(176.3 / 10.416) - 1 = 15
+ * If we ever switch to a dynamic clock then this code might be useful
+ *
+ * unsigned int ctrlclkperiod_us;
+ * ctrlclkperiod_us = 96000000 / 1000000;
+ * ctrlclkperiod_us = 1000000 / ctrlclkperiod_us;
+ * ctx_dbg(1, ctx, "ctrlclkperiod_us: %d\n", ctrlclkperiod_us);
+
+ * ths_settle = 176300 / ctrlclkperiod_us;
+ * ths_settle = (ths_settle > 1) ? ths_settle - 1 : ths_settle;
+ */
+
+ ths_settle = THS_SETTLE;
+ ctx_dbg(1, ctx, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle);
+ }
+#endif
+ reg0 = cc_read(ctx->cc, CAL_CSI2_PHY_REG0);
+ write_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE,
+ CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK,
+ CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_SHIFT);
+ write_field(®0, ths_term,
+ CAL_CSI2_PHY_REG0_THS_TERM_MASK,
+ CAL_CSI2_PHY_REG0_THS_TERM_SHIFT);
+ write_field(®0, ths_settle,
+ CAL_CSI2_PHY_REG0_THS_SETTLE_MASK,
+ CAL_CSI2_PHY_REG0_THS_SETTLE_SHIFT);
+
+ ctx_dbg(1, ctx, "CSI2_%d_REG0 = 0x%08x\n", (ctx->csi2_port - 1), reg0);
+ cc_write(ctx->cc, CAL_CSI2_PHY_REG0, reg0);
+
+ reg1 = cc_read(ctx->cc, CAL_CSI2_PHY_REG1);
+ write_field(®1, TCLK_TERM,
+ CAL_CSI2_PHY_REG1_TCLK_TERM_MASK,
+ CAL_CSI2_PHY_REG1_TCLK_TERM_SHIFT);
+ write_field(®1, 0xb8,
+ CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK,
+ CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_SHIFT);
+ write_field(®1, TCLK_MISS,
+ CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK,
+ CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_SHIFT);
+ write_field(®1, TCLK_SETTLE,
+ CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK,
+ CAL_CSI2_PHY_REG1_TCLK_SETTLE_SHIFT);
+
+ ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x\n", (ctx->csi2_port - 1), reg1);
+ cc_write(ctx->cc, CAL_CSI2_PHY_REG1, reg1);
+}
+
+static int cal_get_external_info(struct cal_ctx *ctx)
+{
+ struct v4l2_ext_control ctrl_ext;
+ struct v4l2_ext_controls ctrls_ext;
+ int ret;
+
+ ctx_dbg(3, ctx, "%s\n", __func__);
+
+ memset(&ctrls_ext, 0, sizeof(ctrls_ext));
+ memset(&ctrl_ext, 0, sizeof(ctrl_ext));
+
+ ctrl_ext.id = V4L2_CID_PIXEL_RATE;
+
+ ctrls_ext.count = 1;
+ ctrls_ext.controls = &ctrl_ext;
+
+ ret = v4l2_g_ext_ctrls(&ctx->ctrl_handler, &ctrls_ext);
+ if (ret < 0) {
+ ctx_err(ctx, "no pixel rate control in subdev: %s\n",
+ ctx->sensor->name);
+ return -EPIPE;
+ }
+
+ ctx->external_rate = ctrl_ext.value64;
+ ctx_dbg(3, ctx, "sensor Pixel Rate: %d\n", ctx->external_rate);
+
+ return 0;
+}
+
+static inline void cal_schedule_next_buffer(struct cal_ctx *ctx)
+{
+ struct cal_dmaqueue *dma_q = &ctx->vidq;
+ struct cal_buffer *buf;
+ unsigned long addr;
+
+ buf = list_entry(dma_q->active.next, struct cal_buffer, list);
+ ctx->next_frm = buf;
+ list_del(&buf->list);
+
+ addr = vb2_dma_contig_plane_dma_addr(&buf->vb, 0);
+ cal_wr_dma_addr(ctx, addr);
+}
+
+static inline void cal_process_buffer_complete(struct cal_ctx *ctx)
+{
+ v4l2_get_timestamp(&ctx->cur_frm->vb.v4l2_buf.timestamp);
+ ctx->cur_frm->vb.v4l2_buf.field = ctx->field;
+ ctx->cur_frm->vb.v4l2_buf.sequence = ctx->sequence++;
+
+ vb2_buffer_done(&ctx->cur_frm->vb, VB2_BUF_STATE_DONE);
+ ctx->cur_frm = ctx->next_frm;
+}
+
+#define isvcirqset(irq, vc, ff) (irq & \
+ (CAL_CSI2_VC_IRQENABLE_ ##ff ##_IRQ_##vc ##_MASK << \
+ CAL_CSI2_VC_IRQENABLE_ ##ff ##_IRQ_##vc ##_SHIFT))
+
+#define isportirqset(irq, port) (irq & \
+ (CAL_HL_IRQ_MASK(port) << CAL_HL_IRQ_SHIFT(port)))
+
+static irqreturn_t cal_irq(int irq_cal, void *data)
+{
+ struct cal_dev *dev = (struct cal_dev *)data;
+ struct cal_ctx *ctx;
+ struct cal_dmaqueue *dma_q;
+ u32 irqst2, irqst3;
+
+ /* Check which DMA just finished */
+ irqst2 = cal_read(dev, CAL_HL_IRQSTATUS(2));
+ if (irqst2) {
+ /* Clear Interrupt status */
+ cal_write(dev, CAL_HL_IRQSTATUS(2), irqst2);
+
+ /* Need to check both port */
+ if (isportirqset(irqst2, 1)) {
+ ctx = dev->ctx[0];
+
+ if (ctx->cur_frm != ctx->next_frm)
+ cal_process_buffer_complete(ctx);
+ }
+
+ if (isportirqset(irqst2, 2)) {
+ ctx = dev->ctx[1];
+
+ if (ctx->cur_frm != ctx->next_frm)
+ cal_process_buffer_complete(ctx);
+ }
+ }
+
+ /* Check which DMA just started */
+ irqst3 = cal_read(dev, CAL_HL_IRQSTATUS(3));
+ if (irqst3) {
+ /* Clear Interrupt status */
+ cal_write(dev, CAL_HL_IRQSTATUS(3), irqst3);
+
+ /* Need to check both port */
+ if (isportirqset(irqst3, 1)) {
+ ctx = dev->ctx[0];
+ dma_q = &ctx->vidq;
+
+ spin_lock(&ctx->slock);
+ if (!list_empty(&dma_q->active) &&
+ ctx->cur_frm == ctx->next_frm)
+ cal_schedule_next_buffer(ctx);
+ spin_unlock(&ctx->slock);
+ }
+
+ if (isportirqset(irqst3, 2)) {
+ ctx = dev->ctx[1];
+ dma_q = &ctx->vidq;
+
+ spin_lock(&ctx->slock);
+ if (!list_empty(&dma_q->active) &&
+ ctx->cur_frm == ctx->next_frm)
+ cal_schedule_next_buffer(ctx);
+ spin_unlock(&ctx->slock);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * video ioctls
+ */
+static int cal_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+
+ strcpy(cap->driver, CAL_MODULE_NAME);
+ strcpy(cap->card, CAL_MODULE_NAME);
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "platform:%s", ctx->v4l2_dev.name);
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int cal_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ const struct cal_fmt *fmt = NULL;
+ u32 k;
+
+ if (f->index >= ARRAY_SIZE(formats))
+ return -EINVAL;
+
+ for (k = 0; k < ARRAY_SIZE(formats); k++) {
+ if ((formats[k].index == f->index) &&
+ (formats[k].supported)) {
+ fmt = &formats[k];
+ break;
+ }
+ }
+ if (!fmt)
+ return -EINVAL;
+
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ return 0;
+}
+
+static int __subdev_get_format(struct cal_ctx *ctx,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct v4l2_subdev_format sd_fmt;
+ struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+ int ret;
+
+ ctx_dbg(2, ctx, "%s\n", __func__);
+
+ if (!ctx->sensor)
+ return -EINVAL;
+
+ sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ sd_fmt.pad = 0;
+
+ ret = v4l2_subdev_call(ctx->sensor, pad, get_fmt, NULL, &sd_fmt);
+ if (ret)
+ return ret;
+
+ *fmt = *mbus_fmt;
+
+ ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__,
+ fmt->width, fmt->height, fmt->code);
+
+ return 0;
+}
+
+static int __subdev_set_format(struct cal_ctx *ctx,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct v4l2_subdev_format sd_fmt;
+ struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+ int ret;
+
+ ctx_dbg(2, ctx, "%s\n", __func__);
+
+ if (!ctx->sensor)
+ return -EINVAL;
+
+ sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ sd_fmt.pad = 0;
+ *mbus_fmt = *fmt;
+
+ ret = v4l2_subdev_call(ctx->sensor, pad, set_fmt, NULL, &sd_fmt);
+ if (ret)
+ return ret;
+
+ ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__,
+ fmt->width, fmt->height, fmt->code);
+
+ return 0;
+}
+
+static int cal_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+ const struct cal_fmt *fmt;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ int ret;
+
+ ret = __subdev_get_format(ctx, &mbus_fmt);
+ if (ret)
+ return ret;
+
+ fmt = find_format_by_code(mbus_fmt.code);
+ if (!fmt) {
+ ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n",
+ mbus_fmt.code);
+ /* code not found, use a working default */
+ fmt = find_format_by_code(MEDIA_BUS_FMT_YUYV8_2X8);
+ mbus_fmt.code = fmt->code;
+ mbus_fmt.colorspace = fmt->colorspace;
+ mbus_fmt.width = 1920;
+ mbus_fmt.height = 1080;
+ mbus_fmt.field = V4L2_FIELD_NONE;
+ }
+
+ if (ctx->fmt != fmt) {
+ /* looks like current format has changed, update local */
+ ctx->fmt = fmt;
+ ctx->width = mbus_fmt.width;
+ ctx->height = mbus_fmt.height;
+ ctx->field = mbus_fmt.field;
+ ctx->pixelsize = ctx->fmt->depth >> 3;
+ }
+
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->fmt.pix.width = ctx->width;
+ f->fmt.pix.height = ctx->height;
+ f->fmt.pix.field = ctx->field;
+ f->fmt.pix.pixelformat = ctx->fmt->fourcc;
+ f->fmt.pix.colorspace = ctx->fmt->colorspace;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * ctx->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+ return 0;
+}
+
+static int cal_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+ const struct cal_fmt *fmt;
+ struct v4l2_fmtdesc fmt_desc;
+
+ ctx_dbg(2, ctx, "%s\n", __func__);
+
+ fmt = find_format_by_pix(f->fmt.pix.pixelformat);
+ if (!fmt) {
+ ctx_dbg(3, ctx, "Fourcc format (0x%08x) not found.\n",
+ f->fmt.pix.pixelformat);
+
+ /* Just get the first one enumerated */
+ fmt_desc.index = 0;
+ if (cal_enum_fmt_vid_cap(file, priv, &fmt_desc)) {
+ ctx_dbg(3, ctx,
+ "no default fmt found , this should not happen.\n");
+ fmt = find_format_by_code(MEDIA_BUS_FMT_YUYV8_2X8);
+ } else {
+ fmt = find_format_by_pix(fmt_desc.pixelformat);
+ }
+ f->fmt.pix.pixelformat = fmt->fourcc;
+ }
+
+ f->fmt.pix.field = ctx->field;
+ v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 2,
+ &f->fmt.pix.height, 32, MAX_HEIGHT, 0, 0);
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = fmt->colorspace;
+ f->fmt.pix.priv = 0;
+ return 0;
+}
+
+static int cal_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+ struct vb2_queue *q = &ctx->vb_vidq;
+ const struct cal_fmt *fmt;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ int ret;
+
+ ctx_dbg(2, ctx, "%s\n", __func__);
+
+ if (vb2_is_busy(q)) {
+ ctx_dbg(3, ctx, "%s device busy\n", __func__);
+ return -EBUSY;
+ }
+
+ ret = cal_try_fmt_vid_cap(file, priv, f);
+ if (ret < 0)
+ return ret;
+
+ fmt = find_format_by_pix(f->fmt.pix.pixelformat);
+
+ v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code);
+
+ ret = __subdev_set_format(ctx, &mbus_fmt);
+ if (ret)
+ return ret;
+
+ /* Just double check nothing has gone wrong */
+ if (mbus_fmt.code != fmt->code) {
+ ctx_dbg(3, ctx,
+ "%s subdev changed format on us, this should not happen\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ ctx->fmt = fmt;
+ ctx->pixelsize = ctx->fmt->depth >> 3;
+ ctx->field = f->fmt.pix.field;
+ ctx->width = f->fmt.pix.width;
+ ctx->height = f->fmt.pix.height;
+
+ return 0;
+}
+
+static int cal_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+ const struct cal_fmt *fmt;
+ struct v4l2_subdev_frame_size_enum fse;
+ int ret;
+
+ ctx_dbg(2, ctx, "%s\n", __func__);
+
+ /* check for valid format */
+ fmt = find_format_by_pix(fsize->pixel_format);
+ if (!fmt) {
+ ctx_dbg(3, ctx, "Invalid pixel code: %x\n",
+ fsize->pixel_format);
+ return -EINVAL;
+ }
+
+ fse.index = fsize->index;
+ fse.pad = 0;
+ fse.code = fmt->code;
+
+ ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size, NULL, &fse);
+ if (ret)
+ return -EINVAL;
+
+ ctx_dbg(1, ctx, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n",
+ __func__, fse.index, fse.code, fse.min_width, fse.max_width,
+ fse.min_height, fse.max_height);
+
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = fse.max_width;
+ fsize->discrete.height = fse.max_height;
+
+ return 0;
+}
+
+static int cal_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ if (inp->index >= CAL_NUM_INPUT)
+ return -EINVAL;
+
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ sprintf(inp->name, "Camera %u", inp->index);
+ return 0;
+}
+
+static int cal_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+
+ *i = ctx->input;
+ return 0;
+}
+
+static int cal_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+
+ if (i >= CAL_NUM_INPUT)
+ return -EINVAL;
+
+ if (i == ctx->input)
+ return 0;
+
+ ctx->input = i;
+ return 0;
+}
+
+/* timeperframe is arbitrary and continuous */
+static int cal_enum_frameintervals(struct file *file, void *priv,
+ struct v4l2_frmivalenum *fival)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+ const struct cal_fmt *fmt;
+ struct v4l2_subdev_frame_size_enum fse;
+ int ret;
+
+ if (fival->index)
+ return -EINVAL;
+
+ fmt = find_format_by_pix(fival->pixel_format);
+ if (!fmt)
+ return -EINVAL;
+
+ /* check for valid width/height */
+ ret = 0;
+ fse.pad = 0;
+ fse.code = fmt->code;
+ fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ for (fse.index = 0; ; fse.index++) {
+ ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size,
+ NULL, &fse);
+ if (ret)
+ return -EINVAL;
+
+ if ((fival->width == fse.max_width) &&
+ (fival->height == fse.max_height))
+ break;
+ else if ((fival->width >= fse.min_width) &&
+ (fival->width <= fse.max_width) &&
+ (fival->height >= fse.min_height) &&
+ (fival->height <= fse.max_height))
+ break;
+
+ return -EINVAL;
+ }
+
+ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fival->discrete.numerator = 1;
+ fival->discrete.denominator = 30;
+
+ return 0;
+}
+
+/*
+ * Videobuf operations
+ */
+static int cal_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct cal_ctx *ctx = vb2_get_drv_priv(vq);
+ unsigned long size;
+
+ size = ctx->width * ctx->height * ctx->pixelsize;
+ if (fmt) {
+ if (fmt->fmt.pix.sizeimage < size)
+ return -EINVAL;
+ size = fmt->fmt.pix.sizeimage;
+ /* check against insane over 8K resolution buffers */
+ if (size > 7680 * 4320 * ctx->pixelsize)
+ return -EINVAL;
+ }
+
+ *nplanes = 1;
+ sizes[0] = size;
+ alloc_ctxs[0] = ctx->alloc_ctx;
+
+ ctx_dbg(3, ctx, "nbuffers=%d, size=%ld\n", *nbuffers, size);
+
+ return 0;
+}
+
+static int cal_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct cal_buffer *buf = container_of(vb, struct cal_buffer, vb);
+ unsigned long size;
+
+ BUG_ON(NULL == ctx->fmt);
+
+ size = ctx->width * ctx->height * ctx->pixelsize;
+ if (vb2_plane_size(vb, 0) < size) {
+ ctx_err(ctx,
+ "data will not fit into plane (%lu < %lu)\n",
+ vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(&buf->vb, 0, size);
+ return 0;
+}
+
+static void cal_buffer_queue(struct vb2_buffer *vb)
+{
+ struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct cal_buffer *buf = container_of(vb, struct cal_buffer, vb);
+ struct cal_dmaqueue *vidq = &ctx->vidq;
+ unsigned long flags = 0;
+
+ /* recheck locking */
+ spin_lock_irqsave(&ctx->slock, flags);
+ list_add_tail(&buf->list, &vidq->active);
+ spin_unlock_irqrestore(&ctx->slock, flags);
+}
+
+static int cal_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct cal_ctx *ctx = vb2_get_drv_priv(vq);
+ struct cal_dmaqueue *dma_q = &ctx->vidq;
+ struct cal_buffer *buf;
+ unsigned long addr = 0;
+ unsigned long flags;
+ int ret;
+
+ ctx_dbg(3, ctx, "%s\n", __func__);
+
+ spin_lock_irqsave(&ctx->slock, flags);
+ if (list_empty(&dma_q->active)) {
+ spin_unlock_irqrestore(&ctx->slock, flags);
+ ctx_dbg(3, ctx, "buffer queue is empty\n");
+ return -EIO;
+ }
+
+ buf = list_entry(dma_q->active.next, struct cal_buffer, list);
+ ctx->cur_frm = buf;
+ ctx->next_frm = buf;
+ list_del(&buf->list);
+ spin_unlock_irqrestore(&ctx->slock, flags);
+
+ v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
+
+ addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb, 0);
+ ctx->sequence = 0;
+
+ ctx_dbg(3, ctx, "enable_irqs\n");
+
+ ret = cal_get_external_info(ctx);
+ if (ret < 0)
+ return ret;
+
+ enable_irqs(ctx);
+ camerarx_phy_enable(ctx);
+ csi2_init(ctx);
+ csi2_phy_config(ctx);
+ csi2_lane_config(ctx);
+ csi2_ctx_config(ctx);
+ pix_proc_config(ctx);
+ cal_wr_dma_config(ctx, ALIGN((ctx->width * ctx->pixelsize), 16));
+ cal_wr_dma_addr(ctx, addr);
+ csi2_ppi_enable(ctx);
+
+ if (ctx->sensor) {
+ if (v4l2_subdev_call(ctx->sensor, video, s_stream, 1)) {
+ ctx_err(ctx, "stream on failed in subdev\n");
+ return -EINVAL;
+ }
+ }
+
+ if (debug >= 4)
+ cal_quickdump_regs(ctx->dev);
+
+ ctx_dbg(3, ctx, "returning from %s\n", __func__);
+ return 0;
+}
+
+static void cal_stop_streaming(struct vb2_queue *vq)
+{
+ struct cal_ctx *ctx = vb2_get_drv_priv(vq);
+ struct cal_dmaqueue *dma_q = &ctx->vidq;
+ unsigned long flags;
+
+ ctx_dbg(3, ctx, "%s\n", __func__);
+
+ if (ctx->sensor) {
+ if (v4l2_subdev_call(ctx->sensor, video, s_stream, 0))
+ ctx_err(ctx, "stream off failed in subdev\n");
+ }
+
+ ctx_dbg(3, ctx, "csi2_ppi_disable\n");
+ csi2_ppi_disable(ctx);
+
+ ctx_dbg(3, ctx, "disable_irqs\n");
+ disable_irqs(ctx);
+
+ /* Release all active buffers */
+ spin_lock_irqsave(&ctx->slock, flags);
+ while (!list_empty(&dma_q->active)) {
+ struct cal_buffer *buf;
+
+ buf = list_entry(dma_q->active.next, struct cal_buffer, list);
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ ctx_dbg(3, ctx, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
+ }
+ spin_unlock_irqrestore(&ctx->slock, flags);
+
+ if (ctx->cur_frm == ctx->next_frm) {
+ vb2_buffer_done(&ctx->cur_frm->vb, VB2_BUF_STATE_ERROR);
+ ctx_dbg(3, ctx, "[%p/%d] done cur_frm\n", ctx->cur_frm,
+ ctx->cur_frm->vb.v4l2_buf.index);
+ } else {
+ vb2_buffer_done(&ctx->cur_frm->vb, VB2_BUF_STATE_ERROR);
+ ctx_dbg(3, ctx, "[%p/%d] done cur_frm\n", ctx->cur_frm,
+ ctx->cur_frm->vb.v4l2_buf.index);
+ vb2_buffer_done(&ctx->next_frm->vb, VB2_BUF_STATE_ERROR);
+ ctx_dbg(3, ctx, "[%p/%d] done next_frm\n", ctx->next_frm,
+ ctx->next_frm->vb.v4l2_buf.index);
+ }
+ ctx->cur_frm = NULL;
+ ctx->next_frm = NULL;
+
+ ctx_dbg(3, ctx, "returning from %s\n", __func__);
+}
+
+static struct vb2_ops cal_video_qops = {
+ .queue_setup = cal_queue_setup,
+ .buf_prepare = cal_buffer_prepare,
+ .buf_queue = cal_buffer_queue,
+ .start_streaming = cal_start_streaming,
+ .stop_streaming = cal_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static const struct v4l2_file_operations cal_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .read = vb2_fop_read,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
+ .mmap = vb2_fop_mmap,
+};
+
+static const struct v4l2_ioctl_ops cal_ioctl_ops = {
+ .vidioc_querycap = cal_querycap,
+ .vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = cal_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = cal_s_fmt_vid_cap,
+ .vidioc_enum_framesizes = cal_enum_framesizes,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_enum_input = cal_enum_input,
+ .vidioc_g_input = cal_g_input,
+ .vidioc_s_input = cal_s_input,
+ .vidioc_enum_frameintervals = cal_enum_frameintervals,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static struct video_device cal_videodev = {
+ .name = CAL_MODULE_NAME,
+ .fops = &cal_fops,
+ .ioctl_ops = &cal_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release,
+};
+
+/* -----------------------------------------------------------------
+ Initialization and module stuff
+ ------------------------------------------------------------------*/
+static int cal_release(struct cal_dev *dev)
+{
+ struct cal_ctx *ctx;
+ int i;
+
+ for (i = 0; i < CAL_NUM_CONTEXT; i++) {
+ ctx = dev->ctx[i];
+ if (ctx) {
+ v4l2_info(&ctx->v4l2_dev, "unregistering %s\n",
+ video_device_node_name(&ctx->vdev));
+ video_unregister_device(&ctx->vdev);
+ v4l2_device_unregister(&ctx->v4l2_dev);
+ vb2_dma_contig_cleanup_ctx(ctx->alloc_ctx);
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ kfree(ctx->cc);
+ kfree(ctx);
+ }
+ }
+
+ return 0;
+}
+
+static int cal_complete_ctx(struct cal_ctx *ctx);
+
+static int cal_async_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct cal_ctx *ctx = notifier_to_ctx(notifier);
+ struct v4l2_subdev_mbus_code_enum mbus_code;
+ int i, j;
+
+ ctx_dbg(1, ctx, "cal_async_bound\n");
+
+ if (ctx->sensor) {
+ ctx_info(ctx, "Rejecting subdev %s (Already set!!)",
+ subdev->name);
+ return 0;
+ }
+
+ ctx->sensor = subdev;
+ ctx_info(ctx, "Using sensor %s for capture\n",
+ subdev->name);
+
+ /* setup the supported formats & indexes */
+ for (j = 0, i = 0; ; ++j) {
+ struct cal_fmt *fmt;
+ int ret;
+
+ memset(&mbus_code, 0, sizeof(mbus_code));
+ mbus_code.index = j;
+ ret = v4l2_subdev_call(subdev, pad, enum_mbus_code,
+ NULL, &mbus_code);
+ if (ret)
+ break;
+
+ fmt = (struct cal_fmt *)find_format_by_code(mbus_code.code);
+ if (!fmt)
+ continue;
+
+ fmt->supported = true;
+ fmt->index = i++;
+ }
+
+ cal_complete_ctx(ctx);
+
+ return 0;
+}
+
+static int cal_async_complete(struct v4l2_async_notifier *notifier)
+{
+ struct cal_ctx *ctx = notifier_to_ctx(notifier);
+
+ ctx_dbg(1, ctx, "cal_async_complete\n");
+ return 0;
+}
+
+static int cal_complete_ctx(struct cal_ctx *ctx)
+{
+ struct video_device *vfd;
+ struct vb2_queue *q;
+ int ret;
+
+ ctx->timeperframe = tpf_default;
+
+ ctx->width = 1920;
+ ctx->height = 1080;
+ ctx->field = V4L2_FIELD_NONE;
+ ctx->fmt = find_format_by_code(MEDIA_BUS_FMT_SGRBG8_1X8);
+ ctx->pixelsize = ctx->fmt->depth >> 3;
+ ctx->external_rate = 192000000;
+
+ /* initialize locks */
+ spin_lock_init(&ctx->slock);
+ mutex_init(&ctx->mutex);
+
+ /* initialize queue */
+ q = &ctx->vb_vidq;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
+ q->drv_priv = ctx;
+ q->buf_struct_size = sizeof(struct cal_buffer);
+ q->ops = &cal_video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &ctx->mutex;
+ q->min_buffers_needed = 3;
+
+ ret = vb2_queue_init(q);
+ if (ret)
+ return ret;
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&ctx->vidq.active);
+
+ vfd = &ctx->vdev;
+ *vfd = cal_videodev;
+ vfd->v4l2_dev = &ctx->v4l2_dev;
+ vfd->queue = q;
+
+ /*
+ * Provide a mutex to v4l2 core. It will be used to protect
+ * all fops and v4l2 ioctls.
+ */
+ vfd->lock = &ctx->mutex;
+ video_set_drvdata(vfd, ctx);
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
+ if (ret < 0)
+ return ret;
+
+ v4l2_info(&ctx->v4l2_dev, "V4L2 device registered as %s\n",
+ video_device_node_name(vfd));
+
+ ctx->alloc_ctx = vb2_dma_contig_init_ctx(vfd->v4l2_dev->dev);
+ if (IS_ERR(ctx->alloc_ctx)) {
+ ctx_err(ctx, "Failed to alloc vb2 context\n");
+ ret = PTR_ERR(ctx->alloc_ctx);
+ goto vdev_unreg;
+ }
+
+ return 0;
+
+vdev_unreg:
+ video_unregister_device(vfd);
+ return ret;
+}
+
+static struct device_node *
+of_get_next_port(const struct device_node *parent,
+ struct device_node *prev)
+{
+ struct device_node *port = NULL;
+
+ if (!parent)
+ return NULL;
+
+ if (!prev) {
+ struct device_node *ports;
+ /*
+ * It's the first call, we have to find a port subnode
+ * within this node or within an optional 'ports' node.
+ */
+ ports = of_get_child_by_name(parent, "ports");
+ if (ports)
+ parent = ports;
+
+ port = of_get_child_by_name(parent, "port");
+
+ /* release the 'ports' node */
+ of_node_put(ports);
+ } else {
+ struct device_node *ports;
+
+ ports = of_get_parent(prev);
+ if (!ports)
+ return NULL;
+
+ do {
+ port = of_get_next_child(ports, prev);
+ if (!port) {
+ of_node_put(ports);
+ return NULL;
+ }
+ prev = port;
+ } while (of_node_cmp(port->name, "port") != 0);
+ }
+
+ return port;
+}
+
+static struct device_node *
+of_get_next_endpoint(const struct device_node *parent,
+ struct device_node *prev)
+{
+ struct device_node *ep = NULL;
+
+ if (!parent)
+ return NULL;
+
+ do {
+ ep = of_get_next_child(parent, prev);
+ if (!ep)
+ return NULL;
+ prev = ep;
+ } while (of_node_cmp(ep->name, "endpoint") != 0);
+
+ return ep;
+}
+
+static int of_cal_create_instance(struct cal_ctx *ctx, int inst)
+{
+ struct platform_device *pdev = ctx->dev->pdev;
+ struct device_node *ep_node, *port, *remote_ep,
+ *sensor_node, *parent;
+ struct v4l2_of_endpoint *endpoint;
+ struct v4l2_async_subdev *asd;
+ u32 regval = 0;
+ int ret, index, found_port = 0, lane;
+
+ parent = pdev->dev.of_node;
+
+ asd = &ctx->asd;
+ endpoint = &ctx->endpoint;
+
+ ep_node = NULL;
+ port = NULL;
+ remote_ep = NULL;
+ sensor_node = NULL;
+ ret = -EINVAL;
+
+ ctx_dbg(3, ctx, "Scanning Port node for csi2 port: %d\n", inst);
+ for (index = 0; index < CAL_NUM_CSI2_PORTS; index++) {
+ port = of_get_next_port(parent, port);
+ if (!port) {
+ ctx_dbg(1, ctx, "No port node found for csi2 port:%d\n",
+ index);
+ goto cleanup_exit;
+ }
+
+ /* Match the slice number with <REG> */
+ of_property_read_u32(port, "reg", ®val);
+ ctx_dbg(3, ctx, "port:%d inst:%d <reg>:%d\n",
+ index, inst, regval);
+ if ((regval == inst) && (index == inst)) {
+ found_port = 1;
+ break;
+ }
+ }
+
+ if (!found_port) {
+ ctx_dbg(1, ctx, "No port node matches csi2 port:%d\n",
+ inst);
+ goto cleanup_exit;
+ }
+
+ ctx_dbg(3, ctx, "Scanning sub-device for csi2 port: %d\n",
+ inst);
+
+ ep_node = of_get_next_endpoint(port, ep_node);
+ if (!ep_node) {
+ ctx_dbg(3, ctx, "can't get next endpoint\n");
+ goto cleanup_exit;
+ }
+
+ sensor_node = of_graph_get_remote_port_parent(ep_node);
+ if (!sensor_node) {
+ ctx_dbg(3, ctx, "can't get remote parent\n");
+ goto cleanup_exit;
+ }
+ asd->match_type = V4L2_ASYNC_MATCH_OF;
+ asd->match.of.node = sensor_node;
+
+ remote_ep = of_parse_phandle(ep_node, "remote-endpoint", 0);
+ if (!remote_ep) {
+ ctx_dbg(3, ctx, "can't get remote-endpoint\n");
+ goto cleanup_exit;
+ }
+ v4l2_of_parse_endpoint(remote_ep, endpoint);
+
+ if (endpoint->bus_type != V4L2_MBUS_CSI2) {
+ ctx_err(ctx, "Port:%d sub-device %s is not a CSI2 device\n",
+ inst, sensor_node->name);
+ goto cleanup_exit;
+ }
+
+ /* Store Virtual Channel number */
+ ctx->virtual_channel = endpoint->base.id;
+
+ ctx_dbg(3, ctx, "Port:%d v4l2-endpoint: CSI2\n", inst);
+ ctx_dbg(3, ctx, "Virtual Channel=%d\n", ctx->virtual_channel);
+ ctx_dbg(3, ctx, "flags=0x%08x\n", endpoint->bus.mipi_csi2.flags);
+ ctx_dbg(3, ctx, "clock_lane=%d\n", endpoint->bus.mipi_csi2.clock_lane);
+ ctx_dbg(3, ctx, "num_data_lanes=%d\n",
+ endpoint->bus.mipi_csi2.num_data_lanes);
+ ctx_dbg(3, ctx, "data_lanes= <\n");
+ for (lane = 0; lane < endpoint->bus.mipi_csi2.num_data_lanes; lane++)
+ ctx_dbg(3, ctx, "\t%d\n",
+ endpoint->bus.mipi_csi2.data_lanes[lane]);
+ ctx_dbg(3, ctx, "\t>\n");
+
+ ctx_dbg(1, ctx, "Port: %d found sub-device %s\n",
+ inst, sensor_node->name);
+
+ ctx_dbg(1, ctx, "Asynchronous subdevice registration\n");
+ ctx->asd_list[0] = asd;
+ ctx->notifier.subdevs = ctx->asd_list;
+ ctx->notifier.num_subdevs = 1;
+ ctx->notifier.bound = cal_async_bound;
+ ctx->notifier.complete = cal_async_complete;
+ ret = v4l2_async_notifier_register(&ctx->v4l2_dev,
+ &ctx->notifier);
+ if (ret) {
+ ctx_err(ctx, "Error registering async notifier\n");
+ ret = -EINVAL;
+ }
+
+cleanup_exit:
+ if (!remote_ep)
+ of_node_put(remote_ep);
+ if (!sensor_node)
+ of_node_put(sensor_node);
+ if (!ep_node)
+ of_node_put(ep_node);
+ if (!port)
+ of_node_put(port);
+
+ return ret;
+}
+
+static struct cal_ctx *cal_create_instance(struct cal_dev *dev, int inst)
+{
+ struct cal_ctx *ctx;
+ struct v4l2_ctrl_handler *hdl;
+ int ret;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return 0;
+ /* save the cal_dev * for future ref */
+ ctx->dev = dev;
+
+ snprintf(ctx->v4l2_dev.name, sizeof(ctx->v4l2_dev.name),
+ "%s-%03d", CAL_MODULE_NAME, inst);
+ ret = v4l2_device_register(&dev->pdev->dev, &ctx->v4l2_dev);
+ if (ret)
+ goto free_ctx;
+
+ hdl = &ctx->ctrl_handler;
+ ret = v4l2_ctrl_handler_init(hdl, 11);
+ if (ret) {
+ ctx_err(ctx, "Failed to init ctrl handler\n");
+ goto free_hdl;
+ }
+ ctx->v4l2_dev.ctrl_handler = hdl;
+
+ /* Make sure Camera Core H/W register area is available */
+ ctx->cc = cc_create(dev, inst);
+ if (IS_ERR(ctx->cc)) {
+ ret = PTR_ERR(ctx->cc);
+ goto unreg_dev;
+ }
+
+ /* Store the instance id */
+ ctx->csi2_port = inst + 1;
+
+ ret = of_cal_create_instance(ctx, inst);
+ if (ret) {
+ ctx_dbg(1, ctx, "Error scanning cal instance: %d\n", inst);
+ ret = -EINVAL;
+ goto free_cc;
+ }
+ return ctx;
+
+free_cc:
+ kfree(ctx->cc);
+free_hdl:
+ v4l2_ctrl_handler_free(hdl);
+unreg_dev:
+ v4l2_device_unregister(&ctx->v4l2_dev);
+free_ctx:
+ kfree(ctx);
+ return 0;
+}
+
+static int cal_probe(struct platform_device *pdev)
+{
+ struct cal_dev *dev;
+ int ret;
+ int irq, func;
+
+ dev_info(&pdev->dev, "Probing %s\n",
+ CAL_MODULE_NAME);
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ /* set pseudo v4l2 device name so we can use v4l2_printk */
+ strcpy(dev->v4l2_dev.name, CAL_MODULE_NAME);
+
+ /* save pdev pointer */
+ dev->pdev = pdev;
+
+ dev->res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "cal_top");
+ cal_dbg(1, dev, "ioresource %s at %x - %x\n",
+ dev->res->name, dev->res->start, dev->res->end);
+
+ dev->base = devm_ioremap(&pdev->dev, dev->res->start, SZ_32K);
+ if (!dev->base) {
+ ret = -ENOMEM;
+ goto just_exit;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ cal_dbg(1, dev, "got irq# %d\n", irq);
+ ret = devm_request_irq(&pdev->dev, irq, cal_irq, 0, CAL_MODULE_NAME,
+ dev);
+ if (ret)
+ goto just_exit;
+
+ platform_set_drvdata(pdev, dev);
+
+ pm_runtime_enable(&pdev->dev);
+
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret)
+ goto just_exit;
+
+ /* Just check we can actually access the module */
+ cal_get_hwinfo(dev);
+
+ func = cal_read_field(dev, CAL_HL_REVISION, CAL_HL_REVISION_FUNC_MASK,
+ CAL_HL_REVISION_FUNC_SHIFT);
+ cal_dbg(1, dev, "CAL HL_REVISION function %x\n", func);
+
+ dev->cm = cm_create(dev);
+ if (IS_ERR(dev->cm)) {
+ ret = PTR_ERR(dev->cm);
+ goto runtime_put;
+ }
+ dev->ctx[0] = NULL;
+ dev->ctx[1] = NULL;
+
+ dev->ctx[0] = cal_create_instance(dev, 0);
+ dev->ctx[1] = cal_create_instance(dev, 1);
+ if (!dev->ctx[0] && !dev->ctx[1]) {
+ ret = -ENODEV;
+ cal_err(dev, "Neither port is configured, no point in staying up\n");
+ goto free_ctx;
+ }
+
+ return 0;
+
+free_ctx:
+ kfree(dev->ctx[0]);
+ kfree(dev->ctx[1]);
+ kfree(dev->cm);
+runtime_put:
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+just_exit:
+ return ret;
+}
+
+static int cal_remove(struct platform_device *pdev)
+{
+ struct cal_dev *dev =
+ (struct cal_dev *)platform_get_drvdata(pdev);
+
+ cal_info(dev, "Removing %s\n", CAL_MODULE_NAME);
+
+ cal_release(dev);
+
+ /* disable csi2 phy */
+ if (dev->ctx[0])
+ camerarx_phy_disable(dev->ctx[0]);
+ if (dev->ctx[1])
+ camerarx_phy_disable(dev->ctx[1]);
+ kfree(dev->cm);
+
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id cal_of_match[] = {
+ { .compatible = "ti,cal", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cal_of_match);
+#else
+#define cal_of_match NULL
+#endif
+
+static struct platform_driver cal_pdrv = {
+ .probe = cal_probe,
+ .remove = cal_remove,
+ .driver = {
+ .name = CAL_MODULE_NAME,
+ .of_match_table = cal_of_match,
+ },
+};
+
+module_platform_driver(cal_pdrv);
diff --git a/drivers/media/platform/ti-vpe/cal_regs.h b/drivers/media/platform/ti-vpe/cal_regs.h
new file mode 100644
index 0000000..096f1c1
--- /dev/null
+++ b/drivers/media/platform/ti-vpe/cal_regs.h
@@ -0,0 +1,779 @@
+/*
+ * TI CAL camera interface driver
+ *
+ * Copyright (c) 2015 Texas Instruments Inc.
+ *
+ * Benoit Parrot, <bparrot@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef __TI_CAL_REGS_H
+#define __TI_CAL_REGS_H
+
+#define CAL_NUM_CSI2_PORTS 2
+
+/* CAL register offsets */
+
+#define CAL_HL_REVISION 0x0000
+#define CAL_HL_HWINFO 0x0004
+#define CAL_HL_SYSCONFIG 0x0010
+#define CAL_HL_IRQ_EOI 0x001c
+#define CAL_HL_IRQSTATUS_RAW(m) (0x20U + ((m-1) * 0x10U))
+#define CAL_HL_IRQSTATUS(m) (0x24U + ((m-1) * 0x10U))
+#define CAL_HL_IRQENABLE_SET(m) (0x28U + ((m-1) * 0x10U))
+#define CAL_HL_IRQENABLE_CLR(m) (0x2cU + ((m-1) * 0x10U))
+#define CAL_PIX_PROC(m) (0xc0U + ((m-1) * 0x4U))
+#define CAL_CTRL 0x100
+#define CAL_CTRL1 0x104
+#define CAL_LINE_NUMBER_EVT 0x108
+#define CAL_VPORT_CTRL1 0x120
+#define CAL_VPORT_CTRL2 0x124
+#define CAL_BYS_CTRL1 0x130
+#define CAL_BYS_CTRL2 0x134
+#define CAL_RD_DMA_CTRL 0x140
+#define CAL_RD_DMA_PIX_ADDR 0x144
+#define CAL_RD_DMA_PIX_OFST 0x148
+#define CAL_RD_DMA_XSIZE 0x14c
+#define CAL_RD_DMA_YSIZE 0x150
+#define CAL_RD_DMA_INIT_ADDR 0x154
+#define CAL_RD_DMA_INIT_OFST 0x168
+#define CAL_RD_DMA_CTRL2 0x16c
+#define CAL_WR_DMA_CTRL(m) (0x200U + ((m-1) * 0x10U))
+#define CAL_WR_DMA_ADDR(m) (0x204U + ((m-1) * 0x10U))
+#define CAL_WR_DMA_OFST(m) (0x208U + ((m-1) * 0x10U))
+#define CAL_WR_DMA_XSIZE(m) (0x20cU + ((m-1) * 0x10U))
+#define CAL_CSI2_PPI_CTRL(m) (0x300U + ((m-1) * 0x80U))
+#define CAL_CSI2_COMPLEXIO_CFG(m) (0x304U + ((m-1) * 0x80U))
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS(m) (0x308U + ((m-1) * 0x80U))
+#define CAL_CSI2_SHORT_PACKET(m) (0x30cU + ((m-1) * 0x80U))
+#define CAL_CSI2_COMPLEXIO_IRQENABLE(m) (0x310U + ((m-1) * 0x80U))
+#define CAL_CSI2_TIMING(m) (0x314U + ((m-1) * 0x80U))
+#define CAL_CSI2_VC_IRQENABLE(m) (0x318U + ((m-1) * 0x80U))
+#define CAL_CSI2_VC_IRQSTATUS(m) (0x328U + ((m-1) * 0x80U))
+#define CAL_CSI2_CTX0(m) (0x330U + ((m-1) * 0x80U))
+#define CAL_CSI2_CTX1(m) (0x334U + ((m-1) * 0x80U))
+#define CAL_CSI2_CTX2(m) (0x338U + ((m-1) * 0x80U))
+#define CAL_CSI2_CTX3(m) (0x33cU + ((m-1) * 0x80U))
+#define CAL_CSI2_CTX4(m) (0x340U + ((m-1) * 0x80U))
+#define CAL_CSI2_CTX5(m) (0x344U + ((m-1) * 0x80U))
+#define CAL_CSI2_CTX6(m) (0x348U + ((m-1) * 0x80U))
+#define CAL_CSI2_CTX7(m) (0x34cU + ((m-1) * 0x80U))
+#define CAL_CSI2_STATUS0(m) (0x350U + ((m-1) * 0x80U))
+#define CAL_CSI2_STATUS1(m) (0x354U + ((m-1) * 0x80U))
+#define CAL_CSI2_STATUS2(m) (0x358U + ((m-1) * 0x80U))
+#define CAL_CSI2_STATUS3(m) (0x35cU + ((m-1) * 0x80U))
+#define CAL_CSI2_STATUS4(m) (0x360U + ((m-1) * 0x80U))
+#define CAL_CSI2_STATUS5(m) (0x364U + ((m-1) * 0x80U))
+#define CAL_CSI2_STATUS6(m) (0x368U + ((m-1) * 0x80U))
+#define CAL_CSI2_STATUS7(m) (0x36cU + ((m-1) * 0x80U))
+
+/* CAL CSI2 PHY register offsets */
+#define CAL_CSI2_PHY_REG0 0x000
+#define CAL_CSI2_PHY_REG1 0x004
+#define CAL_CSI2_PHY_REG2 0x008
+
+/* CAL Control Module Core Camerrx Control register offsets */
+#define CM_CTRL_CORE_CAMERRX_CONTROL 0x000
+
+/*********************************************************************
+* Generic value used in various field below
+*********************************************************************/
+
+#define CAL_GEN_DISABLE 0
+#define CAL_GEN_ENABLE 1
+#define CAL_GEN_FALSE 0
+#define CAL_GEN_TRUE 1
+
+/*********************************************************************
+* Field Definition Macros
+*********************************************************************/
+
+#define CAL_HL_REVISION_MINOR_MASK 0x3f
+#define CAL_HL_REVISION_MINOR_SHIFT 0
+#define CAL_HL_REVISION_CUSTOM_MASK 0x03
+#define CAL_HL_REVISION_CUSTOM_SHIFT 6
+#define CAL_HL_REVISION_MAJOR_MASK 0x07
+#define CAL_HL_REVISION_MAJOR_SHIFT 8
+#define CAL_HL_REVISION_RTL_MASK 0x1f
+#define CAL_HL_REVISION_RTL_SHIFT 11
+#define CAL_HL_REVISION_FUNC_MASK 0xfff
+#define CAL_HL_REVISION_FUNC_SHIFT 16
+#define CAL_HL_REVISION_SCHEME_MASK 0x03
+#define CAL_HL_REVISION_SCHEME_SHIFT 30
+#define CAL_HL_REVISION_SCHEME_H08 1
+#define CAL_HL_REVISION_SCHEME_LEGACY 0
+
+#define CAL_HL_HWINFO_WFIFO_MASK 0xf
+#define CAL_HL_HWINFO_WFIFO_SHIFT 0
+#define CAL_HL_HWINFO_RFIFO_MASK 0xf
+#define CAL_HL_HWINFO_RFIFO_SHIFT 4
+#define CAL_HL_HWINFO_PCTX_MASK 0x1f
+#define CAL_HL_HWINFO_PCTX_SHIFT 8
+#define CAL_HL_HWINFO_WCTX_MASK 0x3f
+#define CAL_HL_HWINFO_WCTX_SHIFT 13
+#define CAL_HL_HWINFO_VFIFO_MASK 0xf
+#define CAL_HL_HWINFO_VFIFO_SHIFT 19
+#define CAL_HL_HWINFO_NCPORT_MASK 0x1f
+#define CAL_HL_HWINFO_NCPORT_SHIFT 23
+#define CAL_HL_HWINFO_NPPI_CTXS0_MASK 0x3
+#define CAL_HL_HWINFO_NPPI_CTXS0_SHIFT 28
+#define CAL_HL_HWINFO_NPPI_CTXS1_MASK 0x3
+#define CAL_HL_HWINFO_NPPI_CTXS1_SHIFT 30
+#define CAL_HL_HWINFO_NPPI_CONTEXTS_ZERO 0
+#define CAL_HL_HWINFO_NPPI_CONTEXTS_FOUR 1
+#define CAL_HL_HWINFO_NPPI_CONTEXTS_EIGHT 2
+#define CAL_HL_HWINFO_NPPI_CONTEXTS_RESERVED 3
+
+#define CAL_HL_SYSCONFIG_SOFTRESET_MASK 0x01
+#define CAL_HL_SYSCONFIG_SOFTRESET_SHIFT 0
+#define CAL_HL_SYSCONFIG_SOFTRESET_DONE 0x0
+#define CAL_HL_SYSCONFIG_SOFTRESET_PENDING 0x1
+#define CAL_HL_SYSCONFIG_SOFTRESET_NOACTION 0x0
+#define CAL_HL_SYSCONFIG_SOFTRESET_RESET 0x1
+#define CAL_HL_SYSCONFIG_IDLE_MASK 0x03
+#define CAL_HL_SYSCONFIG_IDLE_SHIFT 2
+#define CAL_HL_SYSCONFIG_IDLEMODE_FORCE 0
+#define CAL_HL_SYSCONFIG_IDLEMODE_NO 1
+#define CAL_HL_SYSCONFIG_IDLEMODE_SMART1 2
+#define CAL_HL_SYSCONFIG_IDLEMODE_SMART2 3
+
+#define CAL_HL_IRQ_EOI_LINE_NUMBER_MASK 0x0001
+#define CAL_HL_IRQ_EOI_LINE_NUMBER_SHIFT 0
+#define CAL_HL_IRQ_EOI_LINE_NUMBER_READ0 0
+#define CAL_HL_IRQ_EOI_LINE_NUMBER_EOI0 0
+
+#define CAL_HL_IRQ_SHIFT(m) (m-1)
+/*#define CAL_HL_IRQ_MASK(m) (0x1 << (m-1)) */
+#define CAL_HL_IRQ_MASK(m) 0x1
+#define CAL_HL_IRQ_NOACTION 0x0
+#define CAL_HL_IRQ_ENABLE 0x1
+#define CAL_HL_IRQ_CLEAR 0x1
+#define CAL_HL_IRQ_DISABLED 0x0
+#define CAL_HL_IRQ_ENABLED 0x1
+#define CAL_HL_IRQ_PENDING 0x1
+
+#define CAL_PIX_PROC_EN_MASK 0x1
+#define CAL_PIX_PROC_EN_SHIFT 0
+#define CAL_PIX_PROC_EXTRACT_MASK 0xf
+#define CAL_PIX_PROC_EXTRACT_SHIFT 1
+#define CAL_PIX_PROC_EXTRACT_B6 0x0
+#define CAL_PIX_PROC_EXTRACT_B7 0x1
+#define CAL_PIX_PROC_EXTRACT_B8 0x2
+#define CAL_PIX_PROC_EXTRACT_B10 0x3
+#define CAL_PIX_PROC_EXTRACT_B10_MIPI 0x4
+#define CAL_PIX_PROC_EXTRACT_B12 0x5
+#define CAL_PIX_PROC_EXTRACT_B12_MIPI 0x6
+#define CAL_PIX_PROC_EXTRACT_B14 0x7
+#define CAL_PIX_PROC_EXTRACT_B14_MIPI 0x8
+#define CAL_PIX_PROC_EXTRACT_B16_BE 0x9
+#define CAL_PIX_PROC_EXTRACT_B16_LE 0xa
+#define CAL_PIX_PROC_DPCMD_MASK 0x1f
+#define CAL_PIX_PROC_DPCMD_SHIFT 5
+#define CAL_PIX_PROC_DPCMD_BYPASS 0x0
+#define CAL_PIX_PROC_DPCMD_DPCM_10_8_1 0x2
+#define CAL_PIX_PROC_DPCMD_DPCM_12_8_1 0x8
+#define CAL_PIX_PROC_DPCMD_DPCM_10_7_1 0x4
+#define CAL_PIX_PROC_DPCMD_DPCM_10_7_2 0x5
+#define CAL_PIX_PROC_DPCMD_DPCM_10_6_1 0x6
+#define CAL_PIX_PROC_DPCMD_DPCM_10_6_2 0x7
+#define CAL_PIX_PROC_DPCMD_DPCM_12_7_1 0xa
+#define CAL_PIX_PROC_DPCMD_DPCM_12_6_1 0xc
+#define CAL_PIX_PROC_DPCMD_DPCM_14_10 0xe
+#define CAL_PIX_PROC_DPCMD_DPCM_14_8_1 0x10
+#define CAL_PIX_PROC_DPCMD_DPCM_16_12_1 0x12
+#define CAL_PIX_PROC_DPCMD_DPCM_16_10_1 0x14
+#define CAL_PIX_PROC_DPCMD_DPCM_16_8_1 0x16
+#define CAL_PIX_PROC_DPCME_MASK 0x1f
+#define CAL_PIX_PROC_DPCME_SHIFT 11
+#define CAL_PIX_PROC_DPCME_BYPASS 0x0
+#define CAL_PIX_PROC_DPCME_DPCM_10_8_1 0x2
+#define CAL_PIX_PROC_DPCME_DPCM_12_8_1 0x8
+#define CAL_PIX_PROC_DPCME_DPCM_14_10 0xe
+#define CAL_PIX_PROC_DPCME_DPCM_14_8_1 0x10
+#define CAL_PIX_PROC_DPCME_DPCM_16_12_1 0x12
+#define CAL_PIX_PROC_DPCME_DPCM_16_10_1 0x14
+#define CAL_PIX_PROC_DPCME_DPCM_16_8_1 0x16
+#define CAL_PIX_PROC_PACK_MASK 0x7
+#define CAL_PIX_PROC_PACK_SHIFT 16
+#define CAL_PIX_PROC_PACK_B8 0x0
+#define CAL_PIX_PROC_PACK_B10_MIPI 0x2
+#define CAL_PIX_PROC_PACK_B12 0x3
+#define CAL_PIX_PROC_PACK_B12_MIPI 0x4
+#define CAL_PIX_PROC_PACK_B16 0x5
+#define CAL_PIX_PROC_PACK_ARGB 0x6
+#define CAL_PIX_PROC_CPORT_MASK 0x1f
+#define CAL_PIX_PROC_CPORT_SHIFT 19
+
+#define CAL_CTRL_POSTED_WRITES_MASK 0x1
+#define CAL_CTRL_POSTED_WRITES_SHIFT 0
+#define CAL_CTRL_POSTED_WRITES_NONPOSTED 0
+#define CAL_CTRL_POSTED_WRITES 1
+#define CAL_CTRL_TAGCNT_MASK 0xf
+#define CAL_CTRL_TAGCNT_SHIFT 1
+#define CAL_CTRL_BURSTSIZE_MASK 0x3
+#define CAL_CTRL_BURSTSIZE_SHIFT 5
+#define CAL_CTRL_BURSTSIZE_BURST16 0x0
+#define CAL_CTRL_BURSTSIZE_BURST32 0x1
+#define CAL_CTRL_BURSTSIZE_BURST64 0x2
+#define CAL_CTRL_BURSTSIZE_BURST128 0x3
+#define CAL_CTRL_LL_FORCE_STATE_MASK 0x3f
+#define CAL_CTRL_LL_FORCE_STATE_SHIFT 7
+#define CAL_CTRL_MFLAGL_MASK 0xff
+#define CAL_CTRL_MFLAGL_SHIFT 13
+#define CAL_CTRL_PWRSCPCLK_MASK 0x1
+#define CAL_CTRL_PWRSCPCLK_SHIFT 21
+#define CAL_CTRL_PWRSCPCLK_AUTO 0
+#define CAL_CTRL_PWRSCPCLK_FORCE 1
+#define CAL_CTRL_RD_DMA_STALL_MASK 0x1
+#define CAL_CTRL_RD_DMA_STALL_SHIFT 22
+#define CAL_CTRL_MFLAGH_MASK 0xff
+#define CAL_CTRL_MFLAGH_SHIFT 24
+
+#define CAL_CTRL1_PPI_GROUPING_MASK 0x3
+#define CAL_CTRL1_PPI_GROUPING_SHIFT 0
+#define CAL_CTRL1_PPI_GROUPING_DISABLED 0
+#define CAL_CTRL1_PPI_GROUPING_RESERVED 1
+#define CAL_CTRL1_PPI_GROUPING_0 2
+#define CAL_CTRL1_PPI_GROUPING_1 3
+#define CAL_CTRL1_INTERLEAVE01_MASK 0x3
+#define CAL_CTRL1_INTERLEAVE01_SHIFT 2
+#define CAL_CTRL1_INTERLEAVE01_DISABLED 0
+#define CAL_CTRL1_INTERLEAVE01_PIX1 1
+#define CAL_CTRL1_INTERLEAVE01_PIX4 2
+#define CAL_CTRL1_INTERLEAVE01_RESERVED 3
+#define CAL_CTRL1_INTERLEAVE23_MASK 0x3
+#define CAL_CTRL1_INTERLEAVE23_SHIFT 4
+#define CAL_CTRL1_INTERLEAVE23_DISABLED 0
+#define CAL_CTRL1_INTERLEAVE23_PIX1 1
+#define CAL_CTRL1_INTERLEAVE23_PIX4 2
+#define CAL_CTRL1_INTERLEAVE23_RESERVED 3
+
+#define CAL_LINE_NUMBER_EVT_CPORT_MASK 0x1f
+#define CAL_LINE_NUMBER_EVT_CPORT_SHIFT 0
+#define CAL_LINE_NUMBER_EVT_MASK 0x3fff
+#define CAL_LINE_NUMBER_EVT_SHIFT 16
+
+#define CAL_VPORT_CTRL1_PCLK_SHIFT 0
+#define CAL_VPORT_CTRL1_PCLK_MASK 0x1ffff
+#define CAL_VPORT_CTRL1_XBLK_SHIFT 17
+#define CAL_VPORT_CTRL1_XBLK_MASK 0xff
+#define CAL_VPORT_CTRL1_YBLK_SHIFT 25
+#define CAL_VPORT_CTRL1_YBLK_MASK 0x3f
+#define CAL_VPORT_CTRL1_WIDTH_SHIFT 31
+#define CAL_VPORT_CTRL1_WIDTH_MASK 0x1
+#define CAL_VPORT_CTRL1_WIDTH_ONE 0
+#define CAL_VPORT_CTRL1_WIDTH_TWO 1
+
+#define CAL_VPORT_CTRL2_CPORT_SHIFT 0
+#define CAL_VPORT_CTRL2_CPORT_MASK 0x1f
+#define CAL_VPORT_CTRL2_FREERUNNING_SHIFT 15
+#define CAL_VPORT_CTRL2_FREERUNNING_MASK 0x1
+#define CAL_VPORT_CTRL2_FREERUNNING_GATED 0
+#define CAL_VPORT_CTRL2_FREERUNNING_FREE 1
+#define CAL_VPORT_CTRL2_FS_RESETS_SHIFT 16
+#define CAL_VPORT_CTRL2_FS_RESETS_MASK 0x1
+#define CAL_VPORT_CTRL2_FS_RESETS_NO 0
+#define CAL_VPORT_CTRL2_FS_RESETS_YES 1
+#define CAL_VPORT_CTRL2_FSM_RESET_SHIFT 17
+#define CAL_VPORT_CTRL2_FSM_RESET_MASK 0x1
+#define CAL_VPORT_CTRL2_FSM_RESET_NOEFFECT 0
+#define CAL_VPORT_CTRL2_FSM_RESET 1
+#define CAL_VPORT_CTRL2_RDY_THR_SHIFT 18
+#define CAL_VPORT_CTRL2_RDY_THR_MASK 0x3fff
+
+#define CAL_BYS_CTRL1_PCLK_SHIFT 0
+#define CAL_BYS_CTRL1_PCLK_MASK 0x1ffff
+#define CAL_BYS_CTRL1_XBLK_SHIFT 17
+#define CAL_BYS_CTRL1_XBLK_MASK 0xff
+#define CAL_BYS_CTRL1_YBLK_SHIFT 25
+#define CAL_BYS_CTRL1_YBLK_MASK 0x3f
+#define CAL_BYS_CTRL1_BYSINEN_SHIFT 31
+#define CAL_BYS_CTRL1_BYSINEN_MASK 0x1
+
+#define CAL_BYS_CTRL2_CPORTIN_SHIFT 0
+#define CAL_BYS_CTRL2_CPORTIN_MASK 0x1f
+#define CAL_BYS_CTRL2_CPORTOUT_SHIFT 5
+#define CAL_BYS_CTRL2_CPORTOUT_MASK 0x1f
+#define CAL_BYS_CTRL2_DUPLICATEDDATA_SHIFT 10
+#define CAL_BYS_CTRL2_DUPLICATEDDATA_MASK 0x1
+#define CAL_BYS_CTRL2_DUPLICATEDDATA_NO 0
+#define CAL_BYS_CTRL2_DUPLICATEDDATA_YES 1
+#define CAL_BYS_CTRL2_FREERUNNING_SHIFT 11
+#define CAL_BYS_CTRL2_FREERUNNING_MASK 0x1
+#define CAL_BYS_CTRL2_FREERUNNING_NO 0
+#define CAL_BYS_CTRL2_FREERUNNING_YES 1
+
+#define CAL_RD_DMA_CTRL_GO_SHIFT 0
+#define CAL_RD_DMA_CTRL_GO_MASK 0x1
+#define CAL_RD_DMA_CTRL_GO_DIS 0
+#define CAL_RD_DMA_CTRL_GO_EN 1
+#define CAL_RD_DMA_CTRL_GO_IDLE 0
+#define CAL_RD_DMA_CTRL_GO_BUSY 1
+#define CAL_RD_DMA_CTRL_INIT_SHIFT 1
+#define CAL_RD_DMA_CTRL_INIT_MASK 0x1
+#define CAL_RD_DMA_CTRL_BW_LIMITER_SHIFT 2
+#define CAL_RD_DMA_CTRL_BW_LIMITER_MASK 0x1ff
+#define CAL_RD_DMA_CTRL_OCP_TAG_CNT_SHIFT 11
+#define CAL_RD_DMA_CTRL_OCP_TAG_CNT_MASK 0xf
+#define CAL_RD_DMA_CTRL_PCLK_SHIFT 15
+#define CAL_RD_DMA_CTRL_PCLK_MASK 0x1ffff
+
+#define CAL_RD_DMA_PIX_ADDR_SHIFT 3
+#define CAL_RD_DMA_PIX_ADDR_MASK 0x1fffffff
+
+#define CAL_RD_DMA_PIX_OFST_SHIFT 4
+#define CAL_RD_DMA_PIX_OFST_MASK 0xfffffff
+
+#define CAL_RD_DMA_XSIZE_SHIFT 19
+#define CAL_RD_DMA_XSIZE_MASK 0x1fff
+
+#define CAL_RD_DMA_YSIZE_SHIFT 16
+#define CAL_RD_DMA_YSIZE_MASK 0x3fff
+
+#define CAL_RD_DMA_INIT_ADDR_SHIFT 3
+#define CAL_RD_DMA_INIT_ADDR_MASK 0x1fffffff
+
+#define CAL_RD_DMA_INIT_OFST_SHIFT 3
+#define CAL_RD_DMA_INIT_OFST_MASK 0x1fffffff
+
+#define CAL_RD_DMA_CTRL2_CIRC_MODE_SHIFT 0
+#define CAL_RD_DMA_CTRL2_CIRC_MODE_MASK 0x7
+#define CAL_RD_DMA_CTRL2_CIRC_MODE_DIS 0
+#define CAL_RD_DMA_CTRL2_CIRC_MODE_ONE 1
+#define CAL_RD_DMA_CTRL2_CIRC_MODE_FOUR 2
+#define CAL_RD_DMA_CTRL2_CIRC_MODE_SIXTEEN 3
+#define CAL_RD_DMA_CTRL2_CIRC_MODE_SIXTYFOUR 4
+#define CAL_RD_DMA_CTRL2_CIRC_MODE_RESERVED 5
+#define CAL_RD_DMA_CTRL2_ICM_CSTART_SHIFT 3
+#define CAL_RD_DMA_CTRL2_ICM_CSTART_MASK 0x1
+#define CAL_RD_DMA_CTRL2_PATTERN_SHIFT 4
+#define CAL_RD_DMA_CTRL2_PATTERN_MASK 0x3
+#define CAL_RD_DMA_CTRL2_PATTERN_LINEAR 0
+#define CAL_RD_DMA_CTRL2_PATTERN_YUV420 1
+#define CAL_RD_DMA_CTRL2_PATTERN_RD2SKIP2 2
+#define CAL_RD_DMA_CTRL2_PATTERN_RD2SKIP4 3
+#define CAL_RD_DMA_CTRL2_BYSOUT_LE_WAIT_SHIFT 6
+#define CAL_RD_DMA_CTRL2_BYSOUT_LE_WAIT_MASK 0x1
+#define CAL_RD_DMA_CTRL2_BYSOUT_LE_WAIT_FREERUNNING 0
+#define CAL_RD_DMA_CTRL2_BYSOUT_LE_WAIT_WAITFORBYSOUT 1
+#define CAL_RD_DMA_CTRL2_CIRC_SIZE_SHIFT 16
+#define CAL_RD_DMA_CTRL2_CIRC_SIZE_MASK 0x3fff
+
+#define CAL_WR_DMA_CTRL_MODE_SHIFT 0
+#define CAL_WR_DMA_CTRL_MODE_MASK 0x7
+#define CAL_WR_DMA_CTRL_MODE_DIS 0
+#define CAL_WR_DMA_CTRL_MODE_SHD 1
+#define CAL_WR_DMA_CTRL_MODE_CNT 2
+#define CAL_WR_DMA_CTRL_MODE_CNT_INIT 3
+#define CAL_WR_DMA_CTRL_MODE_CONST 4
+#define CAL_WR_DMA_CTRL_MODE_RESERVED 5
+#define CAL_WR_DMA_CTRL_PATTERN_SHIFT 3
+#define CAL_WR_DMA_CTRL_PATTERN_MASK 0x3
+#define CAL_WR_DMA_CTRL_PATTERN_LINEAR 0
+#define CAL_WR_DMA_CTRL_PATTERN_WR2SKIP2 2
+#define CAL_WR_DMA_CTRL_PATTERN_WR2SKIP4 3
+#define CAL_WR_DMA_CTRL_PATTERN_RESERVED 1
+#define CAL_WR_DMA_CTRL_ICM_PSTART_SHIFT 5
+#define CAL_WR_DMA_CTRL_ICM_PSTART_MASK 0x1
+#define CAL_WR_DMA_CTRL_DTAG_SHIFT 6
+#define CAL_WR_DMA_CTRL_DTAG_MASK 0x7
+#define CAL_WR_DMA_CTRL_DTAG_ATT_HDR 0
+#define CAL_WR_DMA_CTRL_DTAG_ATT_DAT 1
+#define CAL_WR_DMA_CTRL_DTAG 2
+#define CAL_WR_DMA_CTRL_DTAG_PIX_HDR 3
+#define CAL_WR_DMA_CTRL_DTAG_PIX_DAT 4
+#define CAL_WR_DMA_CTRL_DTAG_D5 5
+#define CAL_WR_DMA_CTRL_DTAG_D6 6
+#define CAL_WR_DMA_CTRL_DTAG_D7 7
+#define CAL_WR_DMA_CTRL_CPORT_SHIFT 9
+#define CAL_WR_DMA_CTRL_CPORT_MASK 0x1f
+#define CAL_WR_DMA_CTRL_STALL_RD_SHIFT 14
+#define CAL_WR_DMA_CTRL_STALL_RD_MASK 0x1
+#define CAL_WR_DMA_CTRL_YSIZE_SHIFT 18
+#define CAL_WR_DMA_CTRL_YSIZE_MASK 0x3fff
+
+#define CAL_WR_DMA_ADDR_SHIFT 4
+#define CAL_WR_DMA_ADDR_MASK 0xfffffff
+
+#define CAL_WR_DMA_OFST_SHIFT 4
+#define CAL_WR_DMA_OFST_MASK 0x7fff
+#define CAL_WR_DMA_OFST_CIRC_MODE_SHIFT 22
+#define CAL_WR_DMA_OFST_CIRC_MODE_MASK 0x3
+#define CAL_WR_DMA_OFST_CIRC_MODE_ONE 1
+#define CAL_WR_DMA_OFST_CIRC_MODE_FOUR 2
+#define CAL_WR_DMA_OFST_CIRC_MODE_SIXTYFOUR 3
+#define CAL_WR_DMA_OFST_CIRC_MODE_DISABLED 0
+#define CAL_WR_DMA_OFST_CIRC_SIZE_SHIFT 24
+#define CAL_WR_DMA_OFST_CIRC_SIZE_MASK 0xff
+
+#define CAL_WR_DMA_XSIZE_XSKIP_SHIFT 3
+#define CAL_WR_DMA_XSIZE_XSKIP_MASK 0x1fff
+#define CAL_WR_DMA_XSIZE_SHIFT 19
+#define CAL_WR_DMA_XSIZE_MASK 0x1fff
+
+#define CAL_CSI2_PPI_CTRL_IF_EN_SHIFT 0
+#define CAL_CSI2_PPI_CTRL_IF_EN_MASK 0x1
+#define CAL_CSI2_PPI_CTRL_ECC_EN_SHIFT 2
+#define CAL_CSI2_PPI_CTRL_ECC_EN_MASK 0x1
+#define CAL_CSI2_PPI_CTRL_FRAME_SHIFT 3
+#define CAL_CSI2_PPI_CTRL_FRAME_MASK 0x1
+#define CAL_CSI2_PPI_CTRL_FRAME_IMMEDIATE 0
+#define CAL_CSI2_PPI_CTRL_FRAME 1
+
+#define CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT 0
+#define CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK 0x7
+#define CAL_CSI2_COMPLEXIO_CFG_POSITION_5 5
+#define CAL_CSI2_COMPLEXIO_CFG_POSITION_4 4
+#define CAL_CSI2_COMPLEXIO_CFG_POSITION_3 3
+#define CAL_CSI2_COMPLEXIO_CFG_POSITION_2 2
+#define CAL_CSI2_COMPLEXIO_CFG_POSITION_1 1
+#define CAL_CSI2_COMPLEXIO_CFG_POSITION_NOT_USED 0
+#define CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_SHIFT 3
+#define CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_CFG_POL_PLUSMINUS 0
+#define CAL_CSI2_COMPLEXIO_CFG_POL_MINUSPLUS 1
+#define CAL_CSI2_COMPLEXIO_CFG_DATA1_POSITION_SHIFT 4
+#define CAL_CSI2_COMPLEXIO_CFG_DATA1_POSITION_MASK 0x7
+#define CAL_CSI2_COMPLEXIO_CFG_DATA1_POL_SHIFT 7
+#define CAL_CSI2_COMPLEXIO_CFG_DATA1_POL_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_CFG_DATA2_POSITION_SHIFT 8
+#define CAL_CSI2_COMPLEXIO_CFG_DATA2_POSITION_MASK 0x7
+#define CAL_CSI2_COMPLEXIO_CFG_DATA2_POL_SHIFT 11
+#define CAL_CSI2_COMPLEXIO_CFG_DATA2_POL_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_CFG_DATA3_POSITION_SHIFT 12
+#define CAL_CSI2_COMPLEXIO_CFG_DATA3_POSITION_MASK 0x7
+#define CAL_CSI2_COMPLEXIO_CFG_DATA3_POL_SHIFT 15
+#define CAL_CSI2_COMPLEXIO_CFG_DATA3_POL_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_CFG_DATA4_POSITION_SHIFT 16
+#define CAL_CSI2_COMPLEXIO_CFG_DATA4_POSITION_MASK 0x7
+#define CAL_CSI2_COMPLEXIO_CFG_DATA4_POL_SHIFT 19
+#define CAL_CSI2_COMPLEXIO_CFG_DATA4_POL_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_CFG_PWR_AUTO_SHIFT 24
+#define CAL_CSI2_COMPLEXIO_CFG_PWR_AUTO_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_SHIFT 25
+#define CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK 0x3
+#define CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_OFF 0
+#define CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_ON 1
+#define CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_ULP 2
+#define CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_SHIFT 27
+#define CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK 0x3
+#define CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF 0
+#define CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON 1
+#define CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ULP 2
+#define CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_SHIFT 29
+#define CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED 1
+#define CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING 0
+#define CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_SHIFT 30
+#define CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL 0
+#define CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL 1
+
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRSOTHS1_SHIFT 0
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRSOTHS1_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRSOTHS2_SHIFT 1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRSOTHS2_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRSOTHS3_SHIFT 2
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRSOTHS3_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRSOTHS4_SHIFT 3
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRSOTHS4_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRSOTHS5_SHIFT 4
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRSOTHS5_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRSOTSYNCHS1_SHIFT 5
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRSOTSYNCHS1_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRSOTSYNCHS2_SHIFT 6
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRSOTSYNCHS2_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRSOTSYNCHS3_SHIFT 7
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRSOTSYNCHS3_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRSOTSYNCHS4_SHIFT 8
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRSOTSYNCHS4_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRSOTSYNCHS5_SHIFT 9
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRSOTSYNCHS5_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRESC1_SHIFT 10
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRESC1_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRESC2_SHIFT 11
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRESC2_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRESC3_SHIFT 12
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRESC3_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRESC4_SHIFT 13
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRESC4_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRESC5_SHIFT 14
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRESC5_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRCONTROL1_SHIFT 15
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRCONTROL1_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRCONTROL2_SHIFT 16
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRCONTROL2_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRCONTROL3_SHIFT 17
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRCONTROL3_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRCONTROL4_SHIFT 18
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRCONTROL4_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRCONTROL5_SHIFT 19
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ERRCONTROL5_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_STATEULPM1_SHIFT 20
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_STATEULPM1_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_STATEULPM2_SHIFT 21
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_STATEULPM2_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_STATEULPM3_SHIFT 22
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_STATEULPM3_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_STATEULPM4_SHIFT 23
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_STATEULPM4_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_STATEULPM5_SHIFT 24
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_STATEULPM5_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_STATEALLULPMENTER_SHIFT 25
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_STATEALLULPMENTER_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_STATEALLULPMEXIT_SHIFT 26
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_STATEALLULPMEXIT_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_FIFO_OVR_SHIFT 27
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_FIFO_OVR_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_SHORT_PACKET_SHIFT 28
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_SHORT_PACKET_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ECC_NO_CORRECTION_SHIFT 30
+#define CAL_CSI2_COMPLEXIO_IRQSTATUS_ECC_NO_CORRECTION_MASK 0x1
+
+#define CAL_CSI2_SHORT_PACKET_SHIFT 0
+#define CAL_CSI2_SHORT_PACKET_MASK 0xffffff
+
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRSOTHS1_SHIFT 0
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRSOTHS1_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRSOTHS2_SHIFT 1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRSOTHS2_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRSOTHS3_SHIFT 2
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRSOTHS3_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRSOTHS4_SHIFT 3
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRSOTHS4_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRSOTHS5_SHIFT 4
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRSOTHS5_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRSOTSYNCHS1_SHIFT 5
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRSOTSYNCHS1_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRSOTSYNCHS2_SHIFT 6
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRSOTSYNCHS2_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRSOTSYNCHS3_SHIFT 7
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRSOTSYNCHS3_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRSOTSYNCHS4_SHIFT 8
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRSOTSYNCHS4_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRSOTSYNCHS5_SHIFT 9
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRSOTSYNCHS5_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRESC1_SHIFT 10
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRESC1_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRESC2_SHIFT 11
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRESC2_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRESC3_SHIFT 12
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRESC3_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRESC4_SHIFT 13
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRESC4_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRESC5_SHIFT 14
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRESC5_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRCONTROL1_SHIFT 15
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRCONTROL1_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRCONTROL2_SHIFT 16
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRCONTROL2_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRCONTROL3_SHIFT 17
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRCONTROL3_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRCONTROL4_SHIFT 18
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRCONTROL4_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRCONTROL5_SHIFT 19
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ERRCONTROL5_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_STATEULPM1_SHIFT 20
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_STATEULPM1_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_STATEULPM2_SHIFT 21
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_STATEULPM2_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_STATEULPM3_SHIFT 22
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_STATEULPM3_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_STATEULPM4_SHIFT 23
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_STATEULPM4_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_STATEULPM5_SHIFT 24
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_STATEULPM5_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_STATEALLULPMENTER_SHIFT 25
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_STATEALLULPMENTER_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_STATEALLULPMEXIT_SHIFT 26
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_STATEALLULPMEXIT_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_FIFO_OVR_SHIFT 27
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_FIFO_OVR_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_SHORT_PACKET_SHIFT 28
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_SHORT_PACKET_MASK 0x1
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ECC_NO_CORRECTION_SHIFT 30
+#define CAL_CSI2_COMPLEXIO_IRQENABLE_ECC_NO_CORRECTION_MASK 0x1
+
+#define CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT 0
+#define CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK 0x1fff
+#define CAL_CSI2_TIMING_STOP_STATE_X4_IO1_SHIFT 13
+#define CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK 0x1
+#define CAL_CSI2_TIMING_STOP_STATE_X16_IO1_SHIFT 14
+#define CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK 0x1
+#define CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_SHIFT 15
+#define CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK 0x1
+
+#define CAL_CSI2_VC_IRQENABLE_FS_IRQ_0_SHIFT 0
+#define CAL_CSI2_VC_IRQENABLE_FS_IRQ_0_MASK 0x1
+#define CAL_CSI2_VC_IRQENABLE_FE_IRQ_0_SHIFT 1
+#define CAL_CSI2_VC_IRQENABLE_FE_IRQ_0_MASK 0x1
+#define CAL_CSI2_VC_IRQENABLE_LS_IRQ_0_SHIFT 2
+#define CAL_CSI2_VC_IRQENABLE_LS_IRQ_0_MASK 0x1
+#define CAL_CSI2_VC_IRQENABLE_LE_IRQ_0_SHIFT 3
+#define CAL_CSI2_VC_IRQENABLE_LE_IRQ_0_MASK 0x1
+#define CAL_CSI2_VC_IRQENABLE_CS_IRQ_0_SHIFT 4
+#define CAL_CSI2_VC_IRQENABLE_CS_IRQ_0_MASK 0x1
+#define CAL_CSI2_VC_IRQENABLE_ECC_CORRECTION0_IRQ_0_SHIFT 5
+#define CAL_CSI2_VC_IRQENABLE_ECC_CORRECTION0_IRQ_0_MASK 0x1
+#define CAL_CSI2_VC_IRQENABLE_FS_IRQ_1_SHIFT 8
+#define CAL_CSI2_VC_IRQENABLE_FS_IRQ_1_MASK 0x1
+#define CAL_CSI2_VC_IRQENABLE_FE_IRQ_1_SHIFT 9
+#define CAL_CSI2_VC_IRQENABLE_FE_IRQ_1_MASK 0x1
+#define CAL_CSI2_VC_IRQENABLE_LS_IRQ_1_SHIFT 10
+#define CAL_CSI2_VC_IRQENABLE_LS_IRQ_1_MASK 0x1
+#define CAL_CSI2_VC_IRQENABLE_LE_IRQ_1_SHIFT 11
+#define CAL_CSI2_VC_IRQENABLE_LE_IRQ_1_MASK 0x1
+#define CAL_CSI2_VC_IRQENABLE_CS_IRQ_1_SHIFT 12
+#define CAL_CSI2_VC_IRQENABLE_CS_IRQ_1_MASK 0x1
+#define CAL_CSI2_VC_IRQENABLE_ECC_CORRECTION0_IRQ_1_SHIFT 13
+#define CAL_CSI2_VC_IRQENABLE_ECC_CORRECTION0_IRQ_1_MASK 0x1
+#define CAL_CSI2_VC_IRQENABLE_FS_IRQ_2_SHIFT 16
+#define CAL_CSI2_VC_IRQENABLE_FS_IRQ_2_MASK 0x1
+#define CAL_CSI2_VC_IRQENABLE_FE_IRQ_2_SHIFT 17
+#define CAL_CSI2_VC_IRQENABLE_FE_IRQ_2_MASK 0x1
+#define CAL_CSI2_VC_IRQENABLE_LS_IRQ_2_SHIFT 18
+#define CAL_CSI2_VC_IRQENABLE_LS_IRQ_2_MASK 0x1
+#define CAL_CSI2_VC_IRQENABLE_LE_IRQ_2_SHIFT 19
+#define CAL_CSI2_VC_IRQENABLE_LE_IRQ_2_MASK 0x1
+#define CAL_CSI2_VC_IRQENABLE_CS_IRQ_2_SHIFT 20
+#define CAL_CSI2_VC_IRQENABLE_CS_IRQ_2_MASK 0x1
+#define CAL_CSI2_VC_IRQENABLE_ECC_CORRECTION0_IRQ_2_SHIFT 21
+#define CAL_CSI2_VC_IRQENABLE_ECC_CORRECTION0_IRQ_2_MASK 0x1
+#define CAL_CSI2_VC_IRQENABLE_FS_IRQ_3_SHIFT 24
+#define CAL_CSI2_VC_IRQENABLE_FS_IRQ_3_MASK 0x1
+#define CAL_CSI2_VC_IRQENABLE_FE_IRQ_3_SHIFT 25
+#define CAL_CSI2_VC_IRQENABLE_FE_IRQ_3_MASK 0x1
+#define CAL_CSI2_VC_IRQENABLE_LS_IRQ_3_SHIFT 26
+#define CAL_CSI2_VC_IRQENABLE_LS_IRQ_3_MASK 0x1
+#define CAL_CSI2_VC_IRQENABLE_LE_IRQ_3_SHIFT 27
+#define CAL_CSI2_VC_IRQENABLE_LE_IRQ_3_MASK 0x1
+#define CAL_CSI2_VC_IRQENABLE_CS_IRQ_3_SHIFT 28
+#define CAL_CSI2_VC_IRQENABLE_CS_IRQ_3_MASK 0x1
+#define CAL_CSI2_VC_IRQENABLE_ECC_CORRECTION0_IRQ_3_SHIFT 29
+#define CAL_CSI2_VC_IRQENABLE_ECC_CORRECTION0_IRQ_3_MASK 0x1
+
+#define CAL_CSI2_VC_IRQSTATUS_FS_IRQ_0_SHIFT 0
+#define CAL_CSI2_VC_IRQSTATUS_FS_IRQ_0_MASK 0x1
+#define CAL_CSI2_VC_IRQSTATUS_FE_IRQ_0_SHIFT 1
+#define CAL_CSI2_VC_IRQSTATUS_FE_IRQ_0_MASK 0x1
+#define CAL_CSI2_VC_IRQSTATUS_LS_IRQ_0_SHIFT 2
+#define CAL_CSI2_VC_IRQSTATUS_LS_IRQ_0_MASK 0x1
+#define CAL_CSI2_VC_IRQSTATUS_LE_IRQ_0_SHIFT 3
+#define CAL_CSI2_VC_IRQSTATUS_LE_IRQ_0_MASK 0x1
+#define CAL_CSI2_VC_IRQSTATUS_CS_IRQ_0_SHIFT 4
+#define CAL_CSI2_VC_IRQSTATUS_CS_IRQ_0_MASK 0x1
+#define CAL_CSI2_VC_IRQSTATUS_ECC_CORRECTION0_IRQ_0_SHIFT 5
+#define CAL_CSI2_VC_IRQSTATUS_ECC_CORRECTION0_IRQ_0_MASK 0x1
+#define CAL_CSI2_VC_IRQSTATUS_FS_IRQ_1_SHIFT 8
+#define CAL_CSI2_VC_IRQSTATUS_FS_IRQ_1_MASK 0x1
+#define CAL_CSI2_VC_IRQSTATUS_FE_IRQ_1_SHIFT 9
+#define CAL_CSI2_VC_IRQSTATUS_FE_IRQ_1_MASK 0x1
+#define CAL_CSI2_VC_IRQSTATUS_LS_IRQ_1_SHIFT 10
+#define CAL_CSI2_VC_IRQSTATUS_LS_IRQ_1_MASK 0x1
+#define CAL_CSI2_VC_IRQSTATUS_LE_IRQ_1_SHIFT 11
+#define CAL_CSI2_VC_IRQSTATUS_LE_IRQ_1_MASK 0x1
+#define CAL_CSI2_VC_IRQSTATUS_CS_IRQ_1_SHIFT 12
+#define CAL_CSI2_VC_IRQSTATUS_CS_IRQ_1_MASK 0x1
+#define CAL_CSI2_VC_IRQSTATUS_ECC_CORRECTION0_IRQ_1_SHIFT 13
+#define CAL_CSI2_VC_IRQSTATUS_ECC_CORRECTION0_IRQ_1_MASK 0x1
+#define CAL_CSI2_VC_IRQSTATUS_FS_IRQ_2_SHIFT 16
+#define CAL_CSI2_VC_IRQSTATUS_FS_IRQ_2_MASK 0x1
+#define CAL_CSI2_VC_IRQSTATUS_FE_IRQ_2_SHIFT 17
+#define CAL_CSI2_VC_IRQSTATUS_FE_IRQ_2_MASK 0x1
+#define CAL_CSI2_VC_IRQSTATUS_LS_IRQ_2_SHIFT 18
+#define CAL_CSI2_VC_IRQSTATUS_LS_IRQ_2_MASK 0x1
+#define CAL_CSI2_VC_IRQSTATUS_LE_IRQ_2_SHIFT 19
+#define CAL_CSI2_VC_IRQSTATUS_LE_IRQ_2_MASK 0x1
+#define CAL_CSI2_VC_IRQSTATUS_CS_IRQ_2_SHIFT 20
+#define CAL_CSI2_VC_IRQSTATUS_CS_IRQ_2_MASK 0x1
+#define CAL_CSI2_VC_IRQSTATUS_ECC_CORRECTION0_IRQ_2_SHIFT 21
+#define CAL_CSI2_VC_IRQSTATUS_ECC_CORRECTION0_IRQ_2_MASK 0x1
+#define CAL_CSI2_VC_IRQSTATUS_FS_IRQ_3_SHIFT 24
+#define CAL_CSI2_VC_IRQSTATUS_FS_IRQ_3_MASK 0x1
+#define CAL_CSI2_VC_IRQSTATUS_FE_IRQ_3_SHIFT 25
+#define CAL_CSI2_VC_IRQSTATUS_FE_IRQ_3_MASK 0x1
+#define CAL_CSI2_VC_IRQSTATUS_LS_IRQ_3_SHIFT 26
+#define CAL_CSI2_VC_IRQSTATUS_LS_IRQ_3_MASK 0x1
+#define CAL_CSI2_VC_IRQSTATUS_LE_IRQ_3_SHIFT 27
+#define CAL_CSI2_VC_IRQSTATUS_LE_IRQ_3_MASK 0x1
+#define CAL_CSI2_VC_IRQSTATUS_CS_IRQ_3_SHIFT 28
+#define CAL_CSI2_VC_IRQSTATUS_CS_IRQ_3_MASK 0x1
+#define CAL_CSI2_VC_IRQSTATUS_ECC_CORRECTION0_IRQ_3_SHIFT 29
+#define CAL_CSI2_VC_IRQSTATUS_ECC_CORRECTION0_IRQ_3_MASK 0x1
+
+#define CAL_CSI2_CTX_DT_SHIFT 0
+#define CAL_CSI2_CTX_DT_MASK 0x3f
+#define CAL_CSI2_CTX_VC_SHIFT 6
+#define CAL_CSI2_CTX_VC_MASK 0x3
+#define CAL_CSI2_CTX_CPORT_SHIFT 8
+#define CAL_CSI2_CTX_CPORT_MASK 0x1f
+#define CAL_CSI2_CTX_ATT_SHIFT 13
+#define CAL_CSI2_CTX_ATT_MASK 0x1
+#define CAL_CSI2_CTX_ATT_PIX 0
+#define CAL_CSI2_CTX_ATT 1
+#define CAL_CSI2_CTX_PACK_MODE_SHIFT 14
+#define CAL_CSI2_CTX_PACK_MODE_MASK 0x1
+#define CAL_CSI2_CTX_PACK_MODE_LINE 0
+#define CAL_CSI2_CTX_PACK_MODE_FRAME 1
+#define CAL_CSI2_CTX_LINES_SHIFT 16
+#define CAL_CSI2_CTX_LINES_MASK 0x3fff
+
+#define CAL_CSI2_STATUS_FRAME_SHIF 0
+#define CAL_CSI2_STATUS_FRAME_MASK 0xffff
+
+#define CAL_CSI2_PHY_REG0_THS_SETTLE_SHIFT 0
+#define CAL_CSI2_PHY_REG0_THS_SETTLE_MASK 0xff
+#define CAL_CSI2_PHY_REG0_THS_TERM_SHIFT 8
+#define CAL_CSI2_PHY_REG0_THS_TERM_MASK 0xff
+#define CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_SHIFT 24
+#define CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK 0x1
+#define CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE 1
+#define CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_ENABLE 0
+
+#define CAL_CSI2_PHY_REG1_TCLK_SETTLE_SHIFT 0
+#define CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK 0xff
+#define CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_SHIFT 8
+#define CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK 0x3
+#define CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_SHIFT 10
+#define CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK 0xFF
+#define CAL_CSI2_PHY_REG1_TCLK_TERM_SHIFT 18
+#define CAL_CSI2_PHY_REG1_TCLK_TERM_MASK 0x7f
+#define CAL_CSI2_PHY_REG0_CLOCK_MISS_DETECTOR_STATUS_SHIFT 25
+#define CAL_CSI2_PHY_REG0_CLOCK_MISS_DETECTOR_STATUS_MASK 0x1
+#define CAL_CSI2_PHY_REG0_CLOCK_MISS_DETECTOR_STATUS_ERROR 1
+#define CAL_CSI2_PHY_REG0_CLOCK_MISS_DETECTOR_STATUS_SUCCESS 0
+#define CAL_CSI2_PHY_REG0_RESET_DONE_STATUS_SHIFT 28
+#define CAL_CSI2_PHY_REG0_RESET_DONE_STATUS_MASK 0x3
+
+#define CAL_CSI2_PHY_REG2_CCP2_SYNC_PATTERN_SHIFT 0
+#define CAL_CSI2_PHY_REG2_CCP2_SYNC_PATTERN_MASK 0xffffff
+#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC3_SHIFT 24
+#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC3_MASK 0x3
+#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC2_SHIFT 26
+#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC2_MASK 0x3
+#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC1_SHIFT 28
+#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC1_MASK 0x3
+#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC0_SHIFT 30
+#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC0_MASK 0x3
+
+#define CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_SHIFT 0
+#define CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_MASK 0x1
+#define CM_CAMERRX_CTRL_CSI1_CAMMODE_SHIFT 1
+#define CM_CAMERRX_CTRL_CSI1_CAMMODE_MASK 0x3
+#define CM_CAMERRX_CTRL_CSI1_LANEENABLE_SHIFT 3
+#define CM_CAMERRX_CTRL_CSI1_LANEENABLE_MASK 0x3
+#define CM_CAMERRX_CTRL_CSI1_MODE_SHIFT 5
+#define CM_CAMERRX_CTRL_CSI1_MODE_MASK 0x1
+#define CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_SHIFT 10
+#define CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_MASK 0x1
+#define CM_CAMERRX_CTRL_CSI0_CAMMODE_SHIFT 11
+#define CM_CAMERRX_CTRL_CSI0_CAMMODE_MASK 0x3
+#define CM_CAMERRX_CTRL_CSI0_LANEENABLE_SHIFT 13
+#define CM_CAMERRX_CTRL_CSI0_LANEENABLE_MASK 0xf
+#define CM_CAMERRX_CTRL_CSI0_MODE_SHIFT 17
+#define CM_CAMERRX_CTRL_CSI0_MODE_MASK 0x1
+
+#endif
--
1.8.5.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [Patch 2/2] media: v4l: ti-vpe: Document CAL driver
2015-06-16 17:29 [Patch 0/2] media: v4l: ti-vpe: Add CAL v4l2 camera capture driver Benoit Parrot
2015-06-16 17:29 ` [Patch 1/2] " Benoit Parrot
@ 2015-06-16 17:29 ` Benoit Parrot
2015-06-22 7:31 ` [Patch 0/2] media: v4l: ti-vpe: Add CAL v4l2 camera capture driver Hans Verkuil
2 siblings, 0 replies; 8+ messages in thread
From: Benoit Parrot @ 2015-06-16 17:29 UTC (permalink / raw)
To: Hans Verkuil
Cc: linux-media, devicetree, Tony Lindgren, linux-kernel, Benoit Parrot
Device Tree bindings for the Camera Adaptation Layer (CAL) driver
Signed-off-by: Benoit Parrot <bparrot@ti.com>
---
Documentation/devicetree/bindings/media/ti-cal.txt | 70 ++++++++++++++++++++++
1 file changed, 70 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/ti-cal.txt
diff --git a/Documentation/devicetree/bindings/media/ti-cal.txt b/Documentation/devicetree/bindings/media/ti-cal.txt
new file mode 100644
index 0000000..680efad
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/ti-cal.txt
@@ -0,0 +1,70 @@
+Texas Instruments DRA72x CAMERA ADAPTATION LAYER (CAL)
+------------------------------------------------------
+
+The Camera Adaptation Layer (CAL) is a key component for image capture
+applications. The capture module provides the system interface and the
+processing capability to connect CSI2 image-sensor modules to the
+DRA72x device.
+
+Required properties:
+- compatible: must be "ti,cal"
+- reg: physical base address and length of the registers set for the 4
+ memory regions required;
+- reg-names: name associated with the memory regions described is <reg>;
+- interrupts: should contain IRQ line for the CAL;
+
+CAL supports 2 camera port nodes on MIPI bus. Each CSI2 camera port nodes
+should contain a 'port' child node with child 'endpoint' node. Please
+refer to the bindings defined in
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Example:
+ cal: cal@4845b000 {
+ compatible = "ti,cal";
+ ti,hwmods = "cal";
+ reg = <0x4845B000 0x400>,
+ <0x4845B800 0x40>,
+ <0x4845B900 0x40>,
+ <0x4A002e94 0x4>;
+ reg-names = "cal_top",
+ "cal_rx_core0",
+ "cal_rx_core1",
+ "camerrx_control";
+ interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ csi2_0: port@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ endpoint {
+ slave-mode;
+ remote-endpoint = <&ar0330_1>;
+ };
+ };
+ csi2_1: port@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ };
+ };
+
+ i2c5: i2c@4807c000 {
+ ar0330@10 {
+ compatible = "ti,ar0330";
+ reg = <0x10>;
+
+ port {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ar0330_1: endpoint {
+ reg = <0>;
+ clock-lanes = <1>;
+ data-lanes = <0 2 3 4>;
+ remote-endpoint = <&csi2_0>;
+ };
+ };
+ };
+ };
--
1.8.5.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [Patch 0/2] media: v4l: ti-vpe: Add CAL v4l2 camera capture driver
2015-06-16 17:29 [Patch 0/2] media: v4l: ti-vpe: Add CAL v4l2 camera capture driver Benoit Parrot
2015-06-16 17:29 ` [Patch 1/2] " Benoit Parrot
2015-06-16 17:29 ` [Patch 2/2] media: v4l: ti-vpe: Document CAL driver Benoit Parrot
@ 2015-06-22 7:31 ` Hans Verkuil
2015-06-23 19:19 ` Benoit Parrot
2 siblings, 1 reply; 8+ messages in thread
From: Hans Verkuil @ 2015-06-22 7:31 UTC (permalink / raw)
To: Benoit Parrot; +Cc: linux-media, devicetree, Tony Lindgren, linux-kernel
On 06/16/2015 07:29 PM, Benoit Parrot wrote:
> The Camera Adaptation Layer (CAL) is a block which consists of a dual
> port CSI2/MIPI camera capture engine.
> This camera engine is currently found on DRA72xx family of devices.
>
> Port #0 can handle CSI2 camera connected to up to 4 data lanes.
> Port #1 can handle CSI2 camera connected to up to 2 data lanes.
>
> The driver implements the required API/ioctls to be V4L2 compliant.
> Driver supports the following:
> - V4L2 API using DMABUF/MMAP buffer access based on videobuf2 api
> - Asynchronous sensor sub device registration
> - DT support
>
> Currently each port is designed to connect to a single sub-device.
> In other words port aggregation is not currently supported.
>
> Here is a sample output of the v4l2-compliance tool:
>
> # ./v4l2-compliance -s -v -d /dev/video0
Can you show the output of './v4l2-compliance -f' as well?
Thanks!
Hans
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Patch 1/2] media: v4l: ti-vpe: Add CAL v4l2 camera capture driver
2015-06-16 17:29 ` [Patch 1/2] " Benoit Parrot
@ 2015-06-22 8:09 ` Hans Verkuil
2015-06-23 19:36 ` Benoit Parrot
0 siblings, 1 reply; 8+ messages in thread
From: Hans Verkuil @ 2015-06-22 8:09 UTC (permalink / raw)
To: Benoit Parrot; +Cc: linux-media, devicetree, Tony Lindgren, linux-kernel
Hi Benoit,
Thanks for the patch! I do have a few comments, though. See below.
On 06/16/2015 07:29 PM, Benoit Parrot wrote:
> The Camera Adaptation Layer (CAL) is a block which consists of a dual
> port CSI2/MIPI camera capture engine.
> Port #0 can handle CSI2 camera connected to up to 4 data lanes.
> Port #1 can handle CSI2 camera connected to up to 2 data lanes.
> The driver implements the required API/ioctls to be V4L2 compliant.
> Driver supports the following:
> - V4L2 API using DMABUF/MMAP buffer access based on videobuf2 api
> - Asynchronous sensor sub device registration
> - DT support
>
> Signed-off-by: Benoit Parrot <bparrot@ti.com>
> ---
> drivers/media/platform/Kconfig | 12 +
> drivers/media/platform/Makefile | 2 +
> drivers/media/platform/ti-vpe/Makefile | 4 +
> drivers/media/platform/ti-vpe/cal.c | 2225 ++++++++++++++++++++++++++++++
> drivers/media/platform/ti-vpe/cal_regs.h | 779 +++++++++++
> 5 files changed, 3022 insertions(+)
> create mode 100644 drivers/media/platform/ti-vpe/cal.c
> create mode 100644 drivers/media/platform/ti-vpe/cal_regs.h
>
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index 4776a8c..c4c9c18 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -120,6 +120,18 @@ source "drivers/media/platform/s5p-tv/Kconfig"
> source "drivers/media/platform/am437x/Kconfig"
> source "drivers/media/platform/xilinx/Kconfig"
>
> +config VIDEO_TI_CAL
> + tristate "TI CAL (Camera Adaptation Layer) driver"
> + depends on VIDEO_DEV && VIDEO_V4L2 && SOC_DRA7XX
> + depends on VIDEO_V4L2_SUBDEV_API
> + depends on VIDEOBUF2_DMA_CONTIG
> + default n
> + ---help---
> + Support for the TI CAL (Camera Adaptation Layer) block
> + found on DRA72X SoC.
> + In TI Technical Reference Manual this module is referred as
> + Camera Interface Subsystem (CAMSS).
> +
> endif # V4L_PLATFORM_DRIVERS
>
> menuconfig V4L_MEM2MEM_DRIVERS
> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> index 114f9ab..062022f 100644
> --- a/drivers/media/platform/Makefile
> +++ b/drivers/media/platform/Makefile
> @@ -18,6 +18,8 @@ obj-$(CONFIG_VIDEO_VIM2M) += vim2m.o
>
> obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe/
>
> +obj-$(CONFIG_VIDEO_TI_CAL) += ti-vpe/
> +
> obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o
> obj-$(CONFIG_VIDEO_CODA) += coda/
>
> diff --git a/drivers/media/platform/ti-vpe/Makefile b/drivers/media/platform/ti-vpe/Makefile
> index be680f8..e236059 100644
> --- a/drivers/media/platform/ti-vpe/Makefile
> +++ b/drivers/media/platform/ti-vpe/Makefile
> @@ -3,3 +3,7 @@ obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe.o
> ti-vpe-y := vpe.o sc.o csc.o vpdma.o
>
> ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) += -DDEBUG
> +
> +obj-$(CONFIG_VIDEO_TI_CAL) += ti-cal.o
> +
> +ti-cal-y := cal.o
> diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c
> new file mode 100644
> index 0000000..cc1f61e
> --- /dev/null
> +++ b/drivers/media/platform/ti-vpe/cal.c
> @@ -0,0 +1,2225 @@
> +/*
> + * TI CAL camera interface driver
> + *
> + * Copyright (c) 2015 Texas Instruments Inc.
> + * Benoit Parrot, <bparrot@ti.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/ioctl.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/delay.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/slab.h>
> +#include <linux/videodev2.h>
> +#include <linux/of_device.h>
> +#include <linux/of_graph.h>
> +
> +#include <media/v4l2-of.h>
> +#include <media/v4l2-async.h>
> +#include <media/v4l2-common.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-fh.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-common.h>
> +#include <media/videobuf2-core.h>
> +#include <media/videobuf2-dma-contig.h>
> +#include "cal_regs.h"
> +
> +#define CAL_MODULE_NAME "cal"
> +
> +#define MAX_WIDTH 1920
> +#define MAX_HEIGHT 1200
> +
> +#define CAL_VERSION "0.1.0"
> +
> +MODULE_DESCRIPTION("TI CAL driver");
> +MODULE_AUTHOR("Benoit Parrot, <bparrot@ti.com>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_VERSION(CAL_VERSION);
> +
> +static unsigned video_nr = -1;
> +module_param(video_nr, uint, 0644);
> +MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect");
> +
> +static unsigned debug;
> +module_param(debug, uint, 0644);
> +MODULE_PARM_DESC(debug, "activates debug info");
> +
> +/* timeperframe: min/max and default */
> +static const struct v4l2_fract
> + tpf_default = {.numerator = 1001, .denominator = 30000};
> +
> +#define cal_dbg(level, caldev, fmt, arg...) \
> + v4l2_dbg(level, debug, &caldev->v4l2_dev, fmt, ##arg)
> +#define cal_info(caldev, fmt, arg...) \
> + v4l2_info(&caldev->v4l2_dev, fmt, ##arg)
> +#define cal_err(caldev, fmt, arg...) \
> + v4l2_err(&caldev->v4l2_dev, fmt, ##arg)
> +
> +#define ctx_dbg(level, ctx, fmt, arg...) \
> + v4l2_dbg(level, debug, &ctx->v4l2_dev, fmt, ##arg)
> +#define ctx_info(ctx, fmt, arg...) \
> + v4l2_info(&ctx->v4l2_dev, fmt, ##arg)
> +#define ctx_err(ctx, fmt, arg...) \
> + v4l2_err(&ctx->v4l2_dev, fmt, ##arg)
> +
> +#define CAL_NUM_INPUT 1
> +#define CAL_NUM_CONTEXT 2
> +
> +/* ------------------------------------------------------------------
> + Basic structures
> + ------------------------------------------------------------------*/
> +
> +struct cal_fmt {
> + const char *name;
Drop the description: that's set by the v4l2 core (this ensures consistent naming).
> + u32 fourcc;
> + u32 code;
> + u32 colorspace;
This is wrong. The colorspace comes from the sensor and does not belong in this
driver.
> + u8 depth;
> + bool supported;
> + u32 index;
> +};
> +
> +static const struct cal_fmt formats[] = {
> + {
> + .name = "YUV 4:2:2 packed, YCbYCr",
> + .fourcc = V4L2_PIX_FMT_YUYV,
> + .code = MEDIA_BUS_FMT_YUYV8_2X8,
> + .colorspace = V4L2_COLORSPACE_SMPTE170M,
> + .depth = 16,
> + .supported = false,
> + }, {
> + .name = "YUV 4:2:2 packed, CbYCrY",
> + .fourcc = V4L2_PIX_FMT_UYVY,
> + .code = MEDIA_BUS_FMT_UYVY8_2X8,
> + .colorspace = V4L2_COLORSPACE_SMPTE170M,
> + .depth = 16,
> + .supported = false,
> + }, {
> + .name = "YUV 4:2:2 packed, YCrYCb",
> + .fourcc = V4L2_PIX_FMT_YVYU,
> + .code = MEDIA_BUS_FMT_YVYU8_2X8,
> + .colorspace = V4L2_COLORSPACE_SMPTE170M,
> + .depth = 16,
> + .supported = false,
> + }, {
> + .name = "4YUV 4:2:2 packed, CrYCbY",
> + .fourcc = V4L2_PIX_FMT_VYUY,
> + .code = MEDIA_BUS_FMT_VYUY8_2X8,
> + .colorspace = V4L2_COLORSPACE_SMPTE170M,
> + .depth = 16,
> + .supported = false,
> + }, {
> + .name = "RGB565 (LE)",
> + .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
> + .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
> + .colorspace = V4L2_COLORSPACE_SRGB,
> + .depth = 16,
> + .supported = false,
> + }, {
> + .name = "RGB565 (BE)",
> + .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
> + .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
> + .colorspace = V4L2_COLORSPACE_SRGB,
> + .depth = 16,
> + .supported = false,
> + }, {
> + .name = "RGB555 (LE)",
> + .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
> + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
> + .colorspace = V4L2_COLORSPACE_SRGB,
> + .depth = 16,
> + .supported = false,
> + }, {
> + .name = "RGB555 (BE)",
> + .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
> + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
> + .colorspace = V4L2_COLORSPACE_SRGB,
> + .depth = 16,
> + .supported = false,
> + }, {
> + .name = "RGB24 (LE)",
> + .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
> + .code = MEDIA_BUS_FMT_RGB888_2X12_LE,
> + .colorspace = V4L2_COLORSPACE_SRGB,
> + .depth = 24,
> + .supported = false,
> + }, {
> + .name = "RGB24 (BE)",
> + .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
> + .code = MEDIA_BUS_FMT_RGB888_2X12_BE,
> + .colorspace = V4L2_COLORSPACE_SRGB,
> + .depth = 24,
> + .supported = false,
> + }, {
> + .name = "RGB32",
> + .fourcc = V4L2_PIX_FMT_RGB32, /* argb */
> + .code = MEDIA_BUS_FMT_ARGB8888_1X32,
> + .colorspace = V4L2_COLORSPACE_SRGB,
> + .depth = 32,
> + .supported = false,
> + }, {
> + .name = "RAW8 BGGR",
> + .fourcc = V4L2_PIX_FMT_SBGGR8,
> + .code = MEDIA_BUS_FMT_SBGGR8_1X8,
> + .colorspace = V4L2_COLORSPACE_SRGB,
> + .depth = 8,
> + .supported = false,
> + }, {
> + .name = "RAW8 GBRG",
> + .fourcc = V4L2_PIX_FMT_SGBRG8,
> + .code = MEDIA_BUS_FMT_SGBRG8_1X8,
> + .colorspace = V4L2_COLORSPACE_SRGB,
> + .depth = 8,
> + .supported = false,
> + }, {
> + .name = "RAW8 GRBG",
> + .fourcc = V4L2_PIX_FMT_SGRBG8,
> + .code = MEDIA_BUS_FMT_SGRBG8_1X8,
> + .colorspace = V4L2_COLORSPACE_SRGB,
> + .depth = 8,
> + .supported = false,
> + }, {
> + .name = "RAW8 RGGB",
> + .fourcc = V4L2_PIX_FMT_SRGGB8,
> + .code = MEDIA_BUS_FMT_SRGGB8_1X8,
> + .colorspace = V4L2_COLORSPACE_SRGB,
> + .depth = 8,
> + .supported = false,
> + }, {
> + .name = "RAW10 BGGR",
> + .fourcc = V4L2_PIX_FMT_SBGGR10,
> + .code = MEDIA_BUS_FMT_SBGGR10_1X10,
> + .colorspace = V4L2_COLORSPACE_SRGB,
> + .depth = 16,
> + .supported = false,
> + }, {
> + .name = "RAW10 GBRG",
> + .fourcc = V4L2_PIX_FMT_SGBRG10,
> + .code = MEDIA_BUS_FMT_SGBRG10_1X10,
> + .colorspace = V4L2_COLORSPACE_SRGB,
> + .depth = 16,
> + .supported = false,
> + }, {
> + .name = "RAW10 GRBG",
> + .fourcc = V4L2_PIX_FMT_SGRBG10,
> + .code = MEDIA_BUS_FMT_SGRBG10_1X10,
> + .colorspace = V4L2_COLORSPACE_SRGB,
> + .depth = 16,
> + .supported = false,
> + }, {
> + .name = "RAW10 RGGB",
> + .fourcc = V4L2_PIX_FMT_SRGGB10,
> + .code = MEDIA_BUS_FMT_SRGGB10_1X10,
> + .colorspace = V4L2_COLORSPACE_SRGB,
> + .depth = 16,
> + .supported = false,
> + }, {
> + .name = "RAW12 BGGR",
> + .fourcc = V4L2_PIX_FMT_SBGGR12,
> + .code = MEDIA_BUS_FMT_SBGGR12_1X12,
> + .colorspace = V4L2_COLORSPACE_SRGB,
> + .depth = 16,
> + .supported = false,
> + }, {
> + .name = "RAW12 GBRG",
> + .fourcc = V4L2_PIX_FMT_SGBRG12,
> + .code = MEDIA_BUS_FMT_SGBRG12_1X12,
> + .colorspace = V4L2_COLORSPACE_SRGB,
> + .depth = 16,
> + .supported = false,
> + }, {
> + .name = "RAW12 GRBG",
> + .fourcc = V4L2_PIX_FMT_SGRBG12,
> + .code = MEDIA_BUS_FMT_SGRBG12_1X12,
> + .colorspace = V4L2_COLORSPACE_SRGB,
> + .depth = 16,
> + .supported = false,
> + }, {
> + .name = "RAW12 RGGB",
> + .fourcc = V4L2_PIX_FMT_SRGGB12,
> + .code = MEDIA_BUS_FMT_SRGGB12_1X12,
> + .colorspace = V4L2_COLORSPACE_SRGB,
> + .depth = 16,
> + .supported = false,
> + },
> +};
> +
> +static const struct cal_fmt *find_format_by_pix(u32 pixelformat)
> +{
> + const struct cal_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 const struct cal_fmt *find_format_by_code(u32 code)
> +{
> + const struct cal_fmt *fmt;
> + unsigned int k;
> +
> + for (k = 0; k < ARRAY_SIZE(formats); k++) {
> + fmt = &formats[k];
> + if (fmt->code == code)
> + return fmt;
> + }
> +
> + return NULL;
> +}
> +
> +/* buffer for one video frame */
> +struct cal_buffer {
> + /* common v4l buffer stuff -- must be first */
> + struct vb2_buffer vb;
> + struct list_head list;
> + const struct cal_fmt *fmt;
> +};
> +
> +struct cal_dmaqueue {
> + struct list_head active;
> +
> + /* Counters to control fps rate */
> + int frame;
> + int ini_jiffies;
> +};
> +
> +struct cm_data {
> + void __iomem *base;
> + struct resource *res;
> +
> + unsigned int camerrx_control;
> +
> + struct platform_device *pdev;
> +};
> +
> +struct cc_data {
> + void __iomem *base;
> + struct resource *res;
> +
> + struct platform_device *pdev;
> +};
> +
> +/*
> + * there is one cal_dev structure in the driver, it is shared by
> + * all instances.
> + */
> +struct cal_dev {
> + int irq;
> + void __iomem *base;
> + struct resource *res;
> + struct platform_device *pdev;
> + struct v4l2_device v4l2_dev;
> +
> + struct cm_data *cm; /* Control Module handle */
> +
> + struct cal_ctx *ctx[CAL_NUM_CONTEXT];
> +};
> +
> +/*
> + * There is one cal_ctx structure for each camera core context.
> + */
> +struct cal_ctx {
> + struct v4l2_device v4l2_dev;
> + struct v4l2_ctrl_handler ctrl_handler;
> + struct video_device vdev;
> + struct v4l2_async_notifier notifier;
> + struct v4l2_subdev *sensor;
> + struct v4l2_of_endpoint endpoint;
> +
> + struct v4l2_async_subdev asd;
> + struct v4l2_async_subdev *asd_list[1];
> +
> + struct v4l2_fh fh;
> + struct cal_dev *dev;
> + struct cc_data *cc;
> +
> + /* v4l2_ioctl mutex */
> + struct mutex mutex;
> + /* v4l2 buffers lock */
> + spinlock_t slock;
> +
> + /* Several counters */
> + unsigned long jiffies;
> +
> + struct vb2_alloc_ctx *alloc_ctx;
> + struct cal_dmaqueue vidq;
> +
> + /* Input Number */
> + int input;
> +
> + /* video capture */
> + const struct cal_fmt *fmt;
> + struct v4l2_fract timeperframe;
> + unsigned int width, height;
> + unsigned int field;
> + unsigned int sequence;
> + unsigned int pixelsize;
> + unsigned int external_rate;
> + struct vb2_queue vb_vidq;
> + unsigned int seq_count;
> + unsigned int csi2_port;
> + unsigned int virtual_channel;
> +
> + /* Pointer pointing to current v4l2_buffer */
> + struct cal_buffer *cur_frm;
> + /* Pointer pointing to next v4l2_buffer */
> + struct cal_buffer *next_frm;
> +};
> +
> +static inline struct cal_ctx *notifier_to_ctx(struct v4l2_async_notifier *n)
> +{
> + return container_of(n, struct cal_ctx, notifier);
> +}
> +
> +/* register field read/write helpers */
> +static inline int get_field(u32 value, u32 mask, int shift)
> +{
> + return (value & (mask << shift)) >> shift;
> +}
> +
> +static inline void write_field(u32 *valp, u32 field, u32 mask, int shift)
> +{
> + u32 val = *valp;
> +
> + val &= ~(mask << shift);
> + val |= (field & mask) << shift;
> + *valp = val;
> +}
> +
> +static inline u32 cal_read(struct cal_dev *dev, int offset)
> +{
> + return ioread32(dev->base + offset);
> +}
> +
> +static inline void cal_write(struct cal_dev *dev, int offset, u32 value)
> +{
> + iowrite32(value, dev->base + offset);
> +}
> +
> +static inline int
> +cal_read_field(struct cal_dev *dev, int offset, u32 mask, int shift)
> +{
> + return get_field(cal_read(dev, offset), mask, shift);
> +}
> +
> +static inline void cal_write_field(struct cal_dev *dev, int offset, u32 field,
> + u32 mask, int shift)
> +{
> + u32 val = cal_read(dev, offset);
> +
> + write_field(&val, field, mask, shift);
> +
> + cal_write(dev, offset, val);
> +}
> +
> +/*
> + * Control Module block access
> + */
> +static struct cm_data *cm_create(struct cal_dev *dev)
> +{
> + struct platform_device *pdev = dev->pdev;
> + struct cm_data *cm;
> +
> + cal_dbg(3, dev, "cm_create\n");
> +
> + cm = devm_kzalloc(&pdev->dev, sizeof(*cm), GFP_KERNEL);
> + if (!cm)
> + return ERR_PTR(-ENOMEM);
> +
> + cm->res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> + "camerrx_control");
> + if (!cm->res)
> + return ERR_PTR(-ENODEV);
> +
> + cal_dbg(1, dev, "ioresource %s at %x - %x\n",
> + cm->res->name, cm->res->start, cm->res->end);
> +
> + cm->base = devm_ioremap_resource(&pdev->dev, cm->res);
> + if (!cm->base) {
> + cal_err(dev, "failed to ioremap\n");
> + return ERR_PTR(-ENOMEM);
> + }
> +
> + return cm;
> +}
> +
> +static inline u32 cm_read(struct cm_data *dev, int offset)
> +{
> + return ioread32(dev->base + offset);
> +}
> +
> +static inline void cm_write(struct cm_data *dev, int offset, u32 value)
> +{
> + iowrite32(value, dev->base + offset);
> +}
> +
> +static inline void cm_write_field(struct cm_data *dev, int offset, u32 field,
> + u32 mask, int shift)
> +{
> + u32 val = cm_read(dev, offset);
> +
> + write_field(&val, field, mask, shift);
> +
> + cm_write(dev, offset, val);
> +}
> +
> +static void camerarx_phy_enable(struct cal_ctx *ctx)
> +{
> + u32 val;
> +
> + ctx_dbg(3, ctx, "%s\n", __func__);
> +
> + if (!ctx->dev->cm->base) {
> + ctx_err(ctx, "cm not mapped\n");
> + return;
> + }
> +
> + val = cm_read(ctx->dev->cm, CM_CTRL_CORE_CAMERRX_CONTROL);
> + if (ctx->csi2_port == 1) {
> + write_field(&val, 1, CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_MASK,
> + CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_SHIFT);
> + write_field(&val, 0, CM_CAMERRX_CTRL_CSI0_CAMMODE_MASK,
> + CM_CAMERRX_CTRL_CSI0_CAMMODE_SHIFT);
> + /* enable all lanes by default */
> + write_field(&val, 0xf, CM_CAMERRX_CTRL_CSI0_LANEENABLE_MASK,
> + CM_CAMERRX_CTRL_CSI0_LANEENABLE_SHIFT);
> + write_field(&val, 1, CM_CAMERRX_CTRL_CSI0_MODE_MASK,
> + CM_CAMERRX_CTRL_CSI0_MODE_SHIFT);
> + } else if (ctx->csi2_port == 2) {
> + write_field(&val, 1, CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_MASK,
> + CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_SHIFT);
> + write_field(&val, 0, CM_CAMERRX_CTRL_CSI1_CAMMODE_MASK,
> + CM_CAMERRX_CTRL_CSI0_CAMMODE_SHIFT);
> + /* enable all lanes by default */
> + write_field(&val, 0x3, CM_CAMERRX_CTRL_CSI1_LANEENABLE_MASK,
> + CM_CAMERRX_CTRL_CSI0_LANEENABLE_SHIFT);
> + write_field(&val, 1, CM_CAMERRX_CTRL_CSI1_MODE_MASK,
> + CM_CAMERRX_CTRL_CSI0_MODE_SHIFT);
> + }
> + cm_write(ctx->dev->cm, CM_CTRL_CORE_CAMERRX_CONTROL, val);
> +}
> +
> +static void camerarx_phy_disable(struct cal_ctx *ctx)
> +{
> + ctx_dbg(3, ctx, "%s\n", __func__);
> +
> + if (!ctx->dev->cm->base) {
> + ctx_err(ctx, "cm not mapped\n");
> + return;
> + }
> +
> + if (ctx->csi2_port == 1)
> + cm_write_field(ctx->dev->cm,
> + CM_CTRL_CORE_CAMERRX_CONTROL,
> + 0x0,
> + CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_MASK,
> + CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_SHIFT);
> + else if (ctx->csi2_port == 2)
> + cm_write_field(ctx->dev->cm,
> + CM_CTRL_CORE_CAMERRX_CONTROL,
> + 0x0,
> + CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_MASK,
> + CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_SHIFT);
> +}
> +
> +/*
> + * Camera Instance access block
> + */
> +static struct cc_data *cc_create(struct cal_dev *dev, unsigned int core)
> +{
> + struct platform_device *pdev = dev->pdev;
> + struct cc_data *cc;
> +
> + cal_dbg(3, dev, "cc_create\n");
> +
> + cc = devm_kzalloc(&pdev->dev, sizeof(*cc), GFP_KERNEL);
> + if (!cc)
> + return ERR_PTR(-ENOMEM);
> +
> + cc->res = platform_get_resource_byname(pdev,
> + IORESOURCE_MEM,
> + (core == 0) ?
> + "cal_rx_core0" :
> + "cal_rx_core1");
> + if (!cc->res) {
> + cal_err(dev, "missing platform resources data\n");
> + return ERR_PTR(-ENODEV);
> + }
> +
> + cal_dbg(1, dev, "ioresource %s at %x - %x\n",
> + cc->res->name, cc->res->start, cc->res->end);
> +
> + cc->base = devm_ioremap_resource(&pdev->dev, cc->res);
> + if (!cc->base) {
> + cal_err(dev, "failed to ioremap\n");
> + return ERR_PTR(-ENOMEM);
> + }
> +
> + return cc;
> +}
> +
> +static inline u32 cc_read(struct cc_data *dev, int offset)
> +{
> + return ioread32(dev->base + offset);
> +}
> +
> +static inline void cc_write(struct cc_data *dev, int offset, u32 value)
> +{
> + iowrite32(value, dev->base + offset);
> +}
> +
> +/*
> + * Get Revision and HW info
> + */
> +static void cal_get_hwinfo(struct cal_dev *dev)
> +{
> + u32 revision = 0;
> + u32 hwinfo = 0;
> +
> + revision = cal_read(dev, CAL_HL_REVISION);
> + cal_dbg(3, dev, "CAL_HL_REVISION = 0x%08x (expecting 0x40000200)\n",
> + revision);
> +
> + hwinfo = cal_read(dev, CAL_HL_HWINFO);
> + cal_dbg(3, dev, "CAL_HL_HWINFO = 0x%08x (expecting 0xA3C90469)\n",
> + hwinfo);
> +}
> +
> +/*
> + * Soft-Reset the Main Cal module. Not sure if this is needed.
> + */
> +/*
> +static void cal_top_reset(struct cal_dev *dev)
> +{
> + cal_write_field(dev,
> + CAL_HL_SYSCONFIG,
> + CAL_HL_SYSCONFIG_SOFTRESET_RESET,
> + CAL_HL_SYSCONFIG_SOFTRESET_MASK,
> + CAL_HL_SYSCONFIG_SOFTRESET_SHIFT);
> +
> + while(cal_read_field(dev,
> + CAL_HL_SYSCONFIG,
> + CAL_HL_SYSCONFIG_SOFTRESET_MASK,
> + CAL_HL_SYSCONFIG_SOFTRESET_SHIFT) !=
> + CAL_HL_SYSCONFIG_SOFTRESET_DONE);
> +}
> +*/
> +
> +static void cal_quickdump_regs(struct cal_dev *dev)
> +{
> + cal_info(dev, "CAL Registers @ 0x%08x:\n", dev->res->start);
> + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
> + dev->base, (dev->res->end - dev->res->start + 1), false);
> +
> + if (!dev->ctx[0]) {
> + cal_info(dev, "CSI2 Core 0 Registers @ 0x%08x:\n",
> + dev->ctx[0]->cc->res->start);
> + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
> + dev->ctx[0]->cc->base,
> + (dev->ctx[0]->cc->res->end -
> + dev->ctx[0]->cc->res->start + 1),
> + false);
> + }
> +
> + if (!dev->ctx[1]) {
> + cal_info(dev, "CSI2 Core 1 Registers @ 0x%08x:\n",
> + dev->ctx[1]->cc->res->start);
> + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
> + dev->ctx[1]->cc->base,
> + (dev->ctx[1]->cc->res->end -
> + dev->ctx[1]->cc->res->start + 1),
> + false);
> + }
> +
> + cal_info(dev, "CAMERRX_Control Registers @ 0x%08x:\n",
> + dev->cm->res->start);
> + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
> + dev->cm->base,
> + (dev->cm->res->end - dev->cm->res->start + 1), false);
> +}
> +
> +/*
> + * Enable the expected IRQ sources
> + */
> +static void enable_irqs(struct cal_ctx *ctx)
> +{
> + /* Enable IRQ_WDMA_END 0/1 */
> + cal_write_field(ctx->dev,
> + CAL_HL_IRQENABLE_SET(2),
> + CAL_HL_IRQ_ENABLE,
> + CAL_HL_IRQ_MASK(ctx->csi2_port),
> + CAL_HL_IRQ_SHIFT(ctx->csi2_port));
> + /* Enable IRQ_WDMA_START 0/1 */
> + cal_write_field(ctx->dev,
> + CAL_HL_IRQENABLE_SET(3),
> + CAL_HL_IRQ_ENABLE,
> + CAL_HL_IRQ_MASK(ctx->csi2_port),
> + CAL_HL_IRQ_SHIFT(ctx->csi2_port));
> + /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */
> + cal_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0xFF000000);
> +}
> +
> +static void disable_irqs(struct cal_ctx *ctx)
> +{
> + /* Disable IRQ_WDMA_END 0/1 */
> + cal_write_field(ctx->dev,
> + CAL_HL_IRQENABLE_CLR(2),
> + CAL_HL_IRQ_CLEAR,
> + CAL_HL_IRQ_MASK(ctx->csi2_port),
> + CAL_HL_IRQ_SHIFT(ctx->csi2_port));
> + /* Disable IRQ_WDMA_START 0/1 */
> + cal_write_field(ctx->dev,
> + CAL_HL_IRQENABLE_CLR(3),
> + CAL_HL_IRQ_ENABLE,
> + CAL_HL_IRQ_MASK(ctx->csi2_port),
> + CAL_HL_IRQ_SHIFT(ctx->csi2_port));
> + /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */
> + cal_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0);
> +}
> +
> +static void csi2_init(struct cal_ctx *ctx)
> +{
> + u32 val;
> +
> + ctx_dbg(3, ctx, "%s\n", __func__);
> +
> + val = cal_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port));
> + write_field(&val, CAL_GEN_ENABLE,
> + CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK,
> + CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_SHIFT);
> + write_field(&val, CAL_GEN_ENABLE,
> + CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK,
> + CAL_CSI2_TIMING_STOP_STATE_X16_IO1_SHIFT);
> + write_field(&val, CAL_GEN_DISABLE,
> + CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK,
> + CAL_CSI2_TIMING_STOP_STATE_X4_IO1_SHIFT);
> + write_field(&val, 407, CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK,
> + CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT);
> + cal_write(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), val);
> + ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x\n", ctx->csi2_port,
> + cal_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port)));
> +
> + val = cal_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port));
> + write_field(&val, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL,
> + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK,
> + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_SHIFT);
> + write_field(&val, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON,
> + CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK,
> + CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_SHIFT);
> + cal_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val);
> + while (cal_read_field(ctx->dev,
> + CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port),
> + CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK,
> + CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_SHIFT) !=
> + CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_ON)
> + ;
> + ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", ctx->csi2_port,
> + cal_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)));
> +
> + val = cal_read(ctx->dev, CAL_CTRL);
> + write_field(&val, CAL_CTRL_BURSTSIZE_BURST128,
> + CAL_CTRL_BURSTSIZE_MASK, CAL_CTRL_BURSTSIZE_SHIFT);
> + write_field(&val, 0xF,
> + CAL_CTRL_TAGCNT_MASK, CAL_CTRL_TAGCNT_SHIFT);
> + write_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED,
> + CAL_CTRL_POSTED_WRITES_MASK, CAL_CTRL_POSTED_WRITES_SHIFT);
> + write_field(&val, 0xFF,
> + CAL_CTRL_MFLAGL_MASK, CAL_CTRL_MFLAGL_SHIFT);
> + write_field(&val, 0xFF,
> + CAL_CTRL_MFLAGH_MASK, CAL_CTRL_MFLAGH_SHIFT);
> + cal_write(ctx->dev, CAL_CTRL, val);
> + ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", cal_read(ctx->dev, CAL_CTRL));
> +}
> +
> +static void csi2_lane_config(struct cal_ctx *ctx)
> +{
> + u32 val = cal_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port));
> + u32 lane_shift = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT;
> + u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK;
> + u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK;
> + struct v4l2_of_bus_mipi_csi2 *mipi_csi2 = &ctx->endpoint.bus.mipi_csi2;
> + int lane;
> +
> + ctx_dbg(3, ctx, "%s\n", __func__);
> +
> + write_field(&val, mipi_csi2->clock_lane + 1,
> + lane_mask, lane_shift);
> + write_field(&val, mipi_csi2->lane_polarities[0],
> + polarity_mask, lane_shift + 3);
> + for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) {
> + /*
> + * Every lane are one nibble apart starting with the
> + * clock followed by the data lanes so shift incements by 4.
> + */
> + lane_shift += 4;
> + write_field(&val, mipi_csi2->data_lanes[lane] + 1,
> + lane_mask, lane_shift);
> + write_field(&val, mipi_csi2->lane_polarities[lane + 1],
> + polarity_mask, lane_shift + 3);
> + }
> + cal_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val);
> + ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n",
> + ctx->csi2_port, val);
> +}
> +
> +static void csi2_ppi_enable(struct cal_ctx *ctx)
> +{
> + ctx_dbg(3, ctx, "%s\n", __func__);
> +
> + cal_write_field(ctx->dev,
> + CAL_CSI2_PPI_CTRL(ctx->csi2_port),
> + CAL_GEN_ENABLE,
> + CAL_CSI2_PPI_CTRL_IF_EN_MASK,
> + CAL_CSI2_PPI_CTRL_IF_EN_SHIFT);
> +}
> +
> +static void csi2_ppi_disable(struct cal_ctx *ctx)
> +{
> + ctx_dbg(3, ctx, "%s\n", __func__);
> +
> + cal_write_field(ctx->dev,
> + CAL_CSI2_PPI_CTRL(ctx->csi2_port),
> + CAL_GEN_DISABLE,
> + CAL_CSI2_PPI_CTRL_IF_EN_MASK,
> + CAL_CSI2_PPI_CTRL_IF_EN_SHIFT);
> +}
> +
> +static void csi2_ctx_config(struct cal_ctx *ctx)
> +{
> + u32 val;
> +
> + ctx_dbg(3, ctx, "%s\n", __func__);
> +
> + val = cal_read(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port));
> + write_field(&val, ctx->csi2_port, CAL_CSI2_CTX_CPORT_MASK,
> + CAL_CSI2_CTX_CPORT_SHIFT);
> + /* DT type: MIPI CSI-2 Specs
> + 1: All DT filter is disabled
> + 0x24: RGB888 1 pixel = 3 bytes
> + 0x2B: RAW10 4 pixels = 5 bytes
> + 0x2A: RAW8 1 pixel = 1 byte
> + 0x1E: YUV422 2 pixels = 4 bytes
> + */
> + write_field(&val, 0x1, CAL_CSI2_CTX_DT_MASK,
> + CAL_CSI2_CTX_DT_SHIFT);
> + /* Virtual Channel from the CSI2 sensor usually 0! */
> + write_field(&val, ctx->virtual_channel, CAL_CSI2_CTX_VC_MASK,
> + CAL_CSI2_CTX_VC_SHIFT);
> + /* NUM_LINES_PER_FRAME => 0 means we don't know */
> + write_field(&val, 0, CAL_CSI2_CTX_LINES_MASK,
> + CAL_CSI2_CTX_LINES_SHIFT);
> + write_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK,
> + CAL_CSI2_CTX_ATT_SHIFT);
> + cal_write_field(ctx->dev,
> + CAL_CSI2_CTX0(ctx->csi2_port),
> + CAL_CSI2_CTX_PACK_MODE_LINE,
> + CAL_CSI2_CTX_PACK_MODE_MASK,
> + CAL_CSI2_CTX_PACK_MODE_SHIFT);
> + write_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE,
> + CAL_CSI2_CTX_PACK_MODE_MASK, CAL_CSI2_CTX_PACK_MODE_SHIFT);
> + cal_write(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port), val);
> + ctx_dbg(3, ctx, "CAL_CSI2_CTX0(%d) = 0x%08x\n", ctx->csi2_port,
> + cal_read(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port)));
> +}
> +
> +static void pix_proc_config(struct cal_ctx *ctx)
> +{
> + u32 val;
> +
> + ctx_dbg(3, ctx, "%s\n", __func__);
> +
> + val = cal_read(ctx->dev, CAL_PIX_PROC(ctx->csi2_port));
> + write_field(&val, CAL_PIX_PROC_EXTRACT_B8, CAL_PIX_PROC_EXTRACT_MASK,
> + CAL_PIX_PROC_EXTRACT_SHIFT);
> + write_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK,
> + CAL_PIX_PROC_DPCMD_SHIFT);
> + write_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK,
> + CAL_PIX_PROC_DPCME_SHIFT);
> + write_field(&val, CAL_PIX_PROC_PACK_B8, CAL_PIX_PROC_PACK_MASK,
> + CAL_PIX_PROC_PACK_SHIFT);
> + write_field(&val, ctx->csi2_port, CAL_PIX_PROC_CPORT_MASK,
> + CAL_PIX_PROC_CPORT_SHIFT);
> + cal_write_field(ctx->dev,
> + CAL_PIX_PROC(ctx->csi2_port),
> + CAL_GEN_ENABLE,
> + CAL_PIX_PROC_EN_MASK,
> + CAL_PIX_PROC_EN_SHIFT);
> + write_field(&val, CAL_GEN_ENABLE, CAL_PIX_PROC_EN_MASK,
> + CAL_PIX_PROC_EN_SHIFT);
> + cal_write(ctx->dev, CAL_PIX_PROC(ctx->csi2_port), val);
> + ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->csi2_port,
> + cal_read(ctx->dev, CAL_PIX_PROC(ctx->csi2_port)));
> +}
> +
> +#define bytes_per_line(pixel, bpp) (ALIGN(pixel * bpp, 16))
> +
> +static void cal_wr_dma_config(struct cal_ctx *ctx,
> + unsigned int width)
> +{
> + u32 val;
> +
> + ctx_dbg(3, ctx, "%s\n", __func__);
> +
> + val = cal_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port));
> + write_field(&val, ctx->csi2_port, CAL_WR_DMA_CTRL_CPORT_MASK,
> + CAL_WR_DMA_CTRL_CPORT_SHIFT);
> + write_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT,
> + CAL_WR_DMA_CTRL_DTAG_MASK, CAL_WR_DMA_CTRL_DTAG_SHIFT);
> + write_field(&val, CAL_WR_DMA_CTRL_MODE_CONST,
> + CAL_WR_DMA_CTRL_MODE_MASK, CAL_WR_DMA_CTRL_MODE_SHIFT);
> + write_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR,
> + CAL_WR_DMA_CTRL_PATTERN_MASK,
> + CAL_WR_DMA_CTRL_PATTERN_SHIFT);
> + write_field(&val, CAL_GEN_ENABLE,
> + CAL_WR_DMA_CTRL_STALL_RD_MASK,
> + CAL_WR_DMA_CTRL_STALL_RD_SHIFT);
> + cal_write(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port), val);
> + ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->csi2_port,
> + cal_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port)));
> +
> + /*
> + * width/16 not sure but giving it a whirl.
> + * zero does not work right
> + */
> + cal_write_field(ctx->dev,
> + CAL_WR_DMA_OFST(ctx->csi2_port),
> + (width / 16),
> + CAL_WR_DMA_OFST_MASK,
> + CAL_WR_DMA_OFST_SHIFT);
> + ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->csi2_port,
> + cal_read(ctx->dev, CAL_WR_DMA_OFST(ctx->csi2_port)));
> +
> + val = cal_read(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port));
> + /* 64 bit word means no skipping */
> + write_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK,
> + CAL_WR_DMA_XSIZE_XSKIP_SHIFT);
> + /*
> + * (width*8)/64 this should be size of an entire line
> + * in 64bit word but 0 means all data until the end
> + * is detected automagically
> + */
> + write_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK,
> + CAL_WR_DMA_XSIZE_SHIFT);
> + cal_write(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port), val);
> + ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->csi2_port,
> + cal_read(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port)));
> +}
> +
> +static void cal_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr)
> +{
> + cal_write(ctx->dev, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr);
> +/* ctx_dbg(3, ctx, "CAL_WR_DMA_ADDR(%d) = 0x%08x\n", ctx->csi2_port,
> + cal_read(ctx->dev,CAL_WR_DMA_ADDR(ctx->csi2_port))); */
> +}
> +
> +/*
> + * TCLK values are OK at their reset values
> + */
> +#define TCLK_TERM 0
> +#define TCLK_MISS 1
> +#define TCLK_SETTLE 14
> +#define THS_SETTLE 15
> +
> +static void csi2_phy_config(struct cal_ctx *ctx)
> +{
> + unsigned int reg0, reg1;
> + unsigned int ths_term, ths_settle;
> +
> + ctx_dbg(3, ctx, "%s\n", __func__);
> +
> +#ifdef LEGACY_CSI2PHY_FORMULA
> + {
> + int csi2_ddrclk_khz;
> +
> + csi2_ddrclk_khz = ctx->external_rate / 1000
> + / (2 * ctx->endpoint.bus.mipi_csi2.num_data_lanes)
> + * ctx->fmt->depth;
> +
> + /*
> + * THS_TERM: Programmed value = ceil(12.5 ns/DDRClk period) - 1.
> + * THS_SETTLE: Programmed value = ceil(90 ns/DDRClk period) + 3.
> + */
> + ths_term = DIV_ROUND_UP(25 * csi2_ddrclk_khz, 2000000) - 1;
> + ths_settle = DIV_ROUND_UP(90 * csi2_ddrclk_khz, 1000000) + 3;
> + }
> +#else
> + {
> + unsigned int ddrclkperiod_us;
> +
> + /*
> + * THS_TERM: Programmed value = floor(20 ns/DDRClk period) - 2.
> + */
> + ddrclkperiod_us = ctx->external_rate / 2000000;
> + ddrclkperiod_us = 1000000 / ddrclkperiod_us;
> + ctx_dbg(1, ctx, "ddrclkperiod_us: %d\n", ddrclkperiod_us);
> +
> + ths_term = 20000 / ddrclkperiod_us;
> + ths_term = (ths_term >= 2) ? ths_term - 2 : ths_term;
> + ctx_dbg(1, ctx, "ths_term: %d (0x%02x)\n", ths_term, ths_term);
> +
> + /*
> + * THS_SETTLE: Programmed value = floor(176.3 ns/CtrlClk period) - 1.
> + * Since CtrlClk is fixed at 96Mhz then we get
> + * ths_settle = floor(176.3 / 10.416) - 1 = 15
> + * If we ever switch to a dynamic clock then this code might be useful
> + *
> + * unsigned int ctrlclkperiod_us;
> + * ctrlclkperiod_us = 96000000 / 1000000;
> + * ctrlclkperiod_us = 1000000 / ctrlclkperiod_us;
> + * ctx_dbg(1, ctx, "ctrlclkperiod_us: %d\n", ctrlclkperiod_us);
> +
> + * ths_settle = 176300 / ctrlclkperiod_us;
> + * ths_settle = (ths_settle > 1) ? ths_settle - 1 : ths_settle;
> + */
> +
> + ths_settle = THS_SETTLE;
> + ctx_dbg(1, ctx, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle);
> + }
> +#endif
> + reg0 = cc_read(ctx->cc, CAL_CSI2_PHY_REG0);
> + write_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE,
> + CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK,
> + CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_SHIFT);
> + write_field(®0, ths_term,
> + CAL_CSI2_PHY_REG0_THS_TERM_MASK,
> + CAL_CSI2_PHY_REG0_THS_TERM_SHIFT);
> + write_field(®0, ths_settle,
> + CAL_CSI2_PHY_REG0_THS_SETTLE_MASK,
> + CAL_CSI2_PHY_REG0_THS_SETTLE_SHIFT);
> +
> + ctx_dbg(1, ctx, "CSI2_%d_REG0 = 0x%08x\n", (ctx->csi2_port - 1), reg0);
> + cc_write(ctx->cc, CAL_CSI2_PHY_REG0, reg0);
> +
> + reg1 = cc_read(ctx->cc, CAL_CSI2_PHY_REG1);
> + write_field(®1, TCLK_TERM,
> + CAL_CSI2_PHY_REG1_TCLK_TERM_MASK,
> + CAL_CSI2_PHY_REG1_TCLK_TERM_SHIFT);
> + write_field(®1, 0xb8,
> + CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK,
> + CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_SHIFT);
> + write_field(®1, TCLK_MISS,
> + CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK,
> + CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_SHIFT);
> + write_field(®1, TCLK_SETTLE,
> + CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK,
> + CAL_CSI2_PHY_REG1_TCLK_SETTLE_SHIFT);
> +
> + ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x\n", (ctx->csi2_port - 1), reg1);
> + cc_write(ctx->cc, CAL_CSI2_PHY_REG1, reg1);
> +}
> +
> +static int cal_get_external_info(struct cal_ctx *ctx)
> +{
> + struct v4l2_ext_control ctrl_ext;
> + struct v4l2_ext_controls ctrls_ext;
> + int ret;
> +
> + ctx_dbg(3, ctx, "%s\n", __func__);
> +
> + memset(&ctrls_ext, 0, sizeof(ctrls_ext));
> + memset(&ctrl_ext, 0, sizeof(ctrl_ext));
> +
> + ctrl_ext.id = V4L2_CID_PIXEL_RATE;
> +
> + ctrls_ext.count = 1;
> + ctrls_ext.controls = &ctrl_ext;
> +
> + ret = v4l2_g_ext_ctrls(&ctx->ctrl_handler, &ctrls_ext);
Use v4l2_ctrl_g_ctrl_int64() instead: much simpler. You do need to store the
v4l2_ctrl pointer for the pixel rate control when you create it, but that's no
problem. (or use v4l2_ctrl_find, but I prefer to just cache the pointer).
> + if (ret < 0) {
> + ctx_err(ctx, "no pixel rate control in subdev: %s\n",
> + ctx->sensor->name);
> + return -EPIPE;
> + }
> +
> + ctx->external_rate = ctrl_ext.value64;
> + ctx_dbg(3, ctx, "sensor Pixel Rate: %d\n", ctx->external_rate);
> +
> + return 0;
> +}
> +
> +static inline void cal_schedule_next_buffer(struct cal_ctx *ctx)
> +{
> + struct cal_dmaqueue *dma_q = &ctx->vidq;
> + struct cal_buffer *buf;
> + unsigned long addr;
> +
> + buf = list_entry(dma_q->active.next, struct cal_buffer, list);
> + ctx->next_frm = buf;
> + list_del(&buf->list);
> +
> + addr = vb2_dma_contig_plane_dma_addr(&buf->vb, 0);
> + cal_wr_dma_addr(ctx, addr);
> +}
> +
> +static inline void cal_process_buffer_complete(struct cal_ctx *ctx)
> +{
> + v4l2_get_timestamp(&ctx->cur_frm->vb.v4l2_buf.timestamp);
> + ctx->cur_frm->vb.v4l2_buf.field = ctx->field;
> + ctx->cur_frm->vb.v4l2_buf.sequence = ctx->sequence++;
> +
> + vb2_buffer_done(&ctx->cur_frm->vb, VB2_BUF_STATE_DONE);
> + ctx->cur_frm = ctx->next_frm;
> +}
> +
> +#define isvcirqset(irq, vc, ff) (irq & \
> + (CAL_CSI2_VC_IRQENABLE_ ##ff ##_IRQ_##vc ##_MASK << \
> + CAL_CSI2_VC_IRQENABLE_ ##ff ##_IRQ_##vc ##_SHIFT))
> +
> +#define isportirqset(irq, port) (irq & \
> + (CAL_HL_IRQ_MASK(port) << CAL_HL_IRQ_SHIFT(port)))
> +
> +static irqreturn_t cal_irq(int irq_cal, void *data)
> +{
> + struct cal_dev *dev = (struct cal_dev *)data;
> + struct cal_ctx *ctx;
> + struct cal_dmaqueue *dma_q;
> + u32 irqst2, irqst3;
> +
> + /* Check which DMA just finished */
> + irqst2 = cal_read(dev, CAL_HL_IRQSTATUS(2));
> + if (irqst2) {
> + /* Clear Interrupt status */
> + cal_write(dev, CAL_HL_IRQSTATUS(2), irqst2);
> +
> + /* Need to check both port */
> + if (isportirqset(irqst2, 1)) {
> + ctx = dev->ctx[0];
> +
> + if (ctx->cur_frm != ctx->next_frm)
> + cal_process_buffer_complete(ctx);
> + }
> +
> + if (isportirqset(irqst2, 2)) {
> + ctx = dev->ctx[1];
> +
> + if (ctx->cur_frm != ctx->next_frm)
> + cal_process_buffer_complete(ctx);
> + }
> + }
> +
> + /* Check which DMA just started */
> + irqst3 = cal_read(dev, CAL_HL_IRQSTATUS(3));
> + if (irqst3) {
> + /* Clear Interrupt status */
> + cal_write(dev, CAL_HL_IRQSTATUS(3), irqst3);
> +
> + /* Need to check both port */
> + if (isportirqset(irqst3, 1)) {
> + ctx = dev->ctx[0];
> + dma_q = &ctx->vidq;
> +
> + spin_lock(&ctx->slock);
> + if (!list_empty(&dma_q->active) &&
> + ctx->cur_frm == ctx->next_frm)
> + cal_schedule_next_buffer(ctx);
> + spin_unlock(&ctx->slock);
> + }
> +
> + if (isportirqset(irqst3, 2)) {
> + ctx = dev->ctx[1];
> + dma_q = &ctx->vidq;
> +
> + spin_lock(&ctx->slock);
> + if (!list_empty(&dma_q->active) &&
> + ctx->cur_frm == ctx->next_frm)
> + cal_schedule_next_buffer(ctx);
> + spin_unlock(&ctx->slock);
> + }
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +/*
> + * video ioctls
> + */
> +static int cal_querycap(struct file *file, void *priv,
> + struct v4l2_capability *cap)
> +{
> + struct cal_ctx *ctx = video_drvdata(file);
> +
> + strcpy(cap->driver, CAL_MODULE_NAME);
> + strcpy(cap->card, CAL_MODULE_NAME);
> + snprintf(cap->bus_info, sizeof(cap->bus_info),
> + "platform:%s", ctx->v4l2_dev.name);
> + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
> + V4L2_CAP_READWRITE;
> + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
> + return 0;
> +}
> +
> +static int cal_enum_fmt_vid_cap(struct file *file, void *priv,
> + struct v4l2_fmtdesc *f)
> +{
> + const struct cal_fmt *fmt = NULL;
> + u32 k;
> +
> + if (f->index >= ARRAY_SIZE(formats))
> + return -EINVAL;
> +
> + for (k = 0; k < ARRAY_SIZE(formats); k++) {
> + if ((formats[k].index == f->index) &&
> + (formats[k].supported)) {
> + fmt = &formats[k];
> + break;
> + }
This won't work. I am actually surprised that v4l2-compliance didn't complain
about this. If one of the formats isn't supported, then the index will get out of sync.
Also, formats is a global array, so setting the 'supported' flag when there are
two instances of this driver (one for each CSI port, right?) is not a good idea
since each sensor can have different supported formats.
What you need to do is to store which formats are supported in cal_ctx (a bitarray
would work), and store the maximum number of supported formats as well.
Then you can use that information to implement this function correctly.
> + }
> + if (!fmt)
> + return -EINVAL;
> +
> + strlcpy(f->description, fmt->name, sizeof(f->description));
Drop description: the v4l2 core will set that.
> + f->pixelformat = fmt->fourcc;
> + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> + return 0;
> +}
> +
> +static int __subdev_get_format(struct cal_ctx *ctx,
> + struct v4l2_mbus_framefmt *fmt)
> +{
> + struct v4l2_subdev_format sd_fmt;
> + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
> + int ret;
> +
> + ctx_dbg(2, ctx, "%s\n", __func__);
> +
> + if (!ctx->sensor)
> + return -EINVAL;
> +
> + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> + sd_fmt.pad = 0;
> +
> + ret = v4l2_subdev_call(ctx->sensor, pad, get_fmt, NULL, &sd_fmt);
> + if (ret)
> + return ret;
> +
> + *fmt = *mbus_fmt;
> +
> + ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__,
> + fmt->width, fmt->height, fmt->code);
> +
> + return 0;
> +}
> +
> +static int __subdev_set_format(struct cal_ctx *ctx,
> + struct v4l2_mbus_framefmt *fmt)
> +{
> + struct v4l2_subdev_format sd_fmt;
> + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
> + int ret;
> +
> + ctx_dbg(2, ctx, "%s\n", __func__);
> +
> + if (!ctx->sensor)
> + return -EINVAL;
> +
> + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> + sd_fmt.pad = 0;
> + *mbus_fmt = *fmt;
> +
> + ret = v4l2_subdev_call(ctx->sensor, pad, set_fmt, NULL, &sd_fmt);
> + if (ret)
> + return ret;
> +
> + ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__,
> + fmt->width, fmt->height, fmt->code);
> +
> + return 0;
> +}
> +
> +static int cal_g_fmt_vid_cap(struct file *file, void *priv,
> + struct v4l2_format *f)
> +{
> + struct cal_ctx *ctx = video_drvdata(file);
> + const struct cal_fmt *fmt;
> + struct v4l2_mbus_framefmt mbus_fmt;
> + int ret;
> +
> + ret = __subdev_get_format(ctx, &mbus_fmt);
> + if (ret)
> + return ret;
> +
I recommend you use v4l2_fill_pix_format() here (see include/media/v4l2-mediabus.h).
This fills in the colorspace fields from the sensor, among others.
> + fmt = find_format_by_code(mbus_fmt.code);
> + if (!fmt) {
> + ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n",
> + mbus_fmt.code);
> + /* code not found, use a working default */
> + fmt = find_format_by_code(MEDIA_BUS_FMT_YUYV8_2X8);
> + mbus_fmt.code = fmt->code;
> + mbus_fmt.colorspace = fmt->colorspace;
> + mbus_fmt.width = 1920;
> + mbus_fmt.height = 1080;
> + mbus_fmt.field = V4L2_FIELD_NONE;
> + }
> +
> + if (ctx->fmt != fmt) {
> + /* looks like current format has changed, update local */
> + ctx->fmt = fmt;
> + ctx->width = mbus_fmt.width;
> + ctx->height = mbus_fmt.height;
> + ctx->field = mbus_fmt.field;
> + ctx->pixelsize = ctx->fmt->depth >> 3;
> + }
> +
> + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> + f->fmt.pix.width = ctx->width;
> + f->fmt.pix.height = ctx->height;
> + f->fmt.pix.field = ctx->field;
> + f->fmt.pix.pixelformat = ctx->fmt->fourcc;
> + f->fmt.pix.colorspace = ctx->fmt->colorspace;
> + f->fmt.pix.bytesperline =
> + (f->fmt.pix.width * ctx->fmt->depth) >> 3;
> + f->fmt.pix.sizeimage =
> + f->fmt.pix.height * f->fmt.pix.bytesperline;
> + return 0;
> +}
> +
> +static int cal_try_fmt_vid_cap(struct file *file, void *priv,
> + struct v4l2_format *f)
> +{
> + struct cal_ctx *ctx = video_drvdata(file);
> + const struct cal_fmt *fmt;
> + struct v4l2_fmtdesc fmt_desc;
> +
> + ctx_dbg(2, ctx, "%s\n", __func__);
> +
> + fmt = find_format_by_pix(f->fmt.pix.pixelformat);
> + if (!fmt) {
> + ctx_dbg(3, ctx, "Fourcc format (0x%08x) not found.\n",
> + f->fmt.pix.pixelformat);
> +
> + /* Just get the first one enumerated */
> + fmt_desc.index = 0;
> + if (cal_enum_fmt_vid_cap(file, priv, &fmt_desc)) {
> + ctx_dbg(3, ctx,
> + "no default fmt found , this should not happen.\n");
> + fmt = find_format_by_code(MEDIA_BUS_FMT_YUYV8_2X8);
> + } else {
> + fmt = find_format_by_pix(fmt_desc.pixelformat);
> + }
> + f->fmt.pix.pixelformat = fmt->fourcc;
> + }
> +
> + f->fmt.pix.field = ctx->field;
> + v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 2,
> + &f->fmt.pix.height, 32, MAX_HEIGHT, 0, 0);
> + f->fmt.pix.bytesperline =
> + (f->fmt.pix.width * fmt->depth) >> 3;
> + f->fmt.pix.sizeimage =
> + f->fmt.pix.height * f->fmt.pix.bytesperline;
> + f->fmt.pix.colorspace = fmt->colorspace;
> + f->fmt.pix.priv = 0;
> + return 0;
> +}
> +
> +static int cal_s_fmt_vid_cap(struct file *file, void *priv,
> + struct v4l2_format *f)
> +{
> + struct cal_ctx *ctx = video_drvdata(file);
> + struct vb2_queue *q = &ctx->vb_vidq;
> + const struct cal_fmt *fmt;
> + struct v4l2_mbus_framefmt mbus_fmt;
> + int ret;
> +
> + ctx_dbg(2, ctx, "%s\n", __func__);
> +
> + if (vb2_is_busy(q)) {
> + ctx_dbg(3, ctx, "%s device busy\n", __func__);
> + return -EBUSY;
> + }
> +
> + ret = cal_try_fmt_vid_cap(file, priv, f);
> + if (ret < 0)
> + return ret;
> +
> + fmt = find_format_by_pix(f->fmt.pix.pixelformat);
> +
> + v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code);
> +
> + ret = __subdev_set_format(ctx, &mbus_fmt);
> + if (ret)
> + return ret;
> +
> + /* Just double check nothing has gone wrong */
> + if (mbus_fmt.code != fmt->code) {
> + ctx_dbg(3, ctx,
> + "%s subdev changed format on us, this should not happen\n",
> + __func__);
> + return -EINVAL;
> + }
> +
> + ctx->fmt = fmt;
> + ctx->pixelsize = ctx->fmt->depth >> 3;
> + ctx->field = f->fmt.pix.field;
> + ctx->width = f->fmt.pix.width;
> + ctx->height = f->fmt.pix.height;
> +
> + return 0;
> +}
> +
> +static int cal_enum_framesizes(struct file *file, void *fh,
> + struct v4l2_frmsizeenum *fsize)
> +{
> + struct cal_ctx *ctx = video_drvdata(file);
> + const struct cal_fmt *fmt;
> + struct v4l2_subdev_frame_size_enum fse;
> + int ret;
> +
> + ctx_dbg(2, ctx, "%s\n", __func__);
> +
> + /* check for valid format */
> + fmt = find_format_by_pix(fsize->pixel_format);
> + if (!fmt) {
> + ctx_dbg(3, ctx, "Invalid pixel code: %x\n",
> + fsize->pixel_format);
> + return -EINVAL;
> + }
> +
> + fse.index = fsize->index;
> + fse.pad = 0;
> + fse.code = fmt->code;
> +
> + ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size, NULL, &fse);
> + if (ret)
> + return -EINVAL;
> +
> + ctx_dbg(1, ctx, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n",
> + __func__, fse.index, fse.code, fse.min_width, fse.max_width,
> + fse.min_height, fse.max_height);
> +
> + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
> + fsize->discrete.width = fse.max_width;
> + fsize->discrete.height = fse.max_height;
> +
> + return 0;
> +}
> +
> +static int cal_enum_input(struct file *file, void *priv,
> + struct v4l2_input *inp)
> +{
> + if (inp->index >= CAL_NUM_INPUT)
> + return -EINVAL;
> +
> + inp->type = V4L2_INPUT_TYPE_CAMERA;
> + sprintf(inp->name, "Camera %u", inp->index);
> + return 0;
> +}
> +
> +static int cal_g_input(struct file *file, void *priv, unsigned int *i)
> +{
> + struct cal_ctx *ctx = video_drvdata(file);
> +
> + *i = ctx->input;
> + return 0;
> +}
> +
> +static int cal_s_input(struct file *file, void *priv, unsigned int i)
> +{
> + struct cal_ctx *ctx = video_drvdata(file);
> +
> + if (i >= CAL_NUM_INPUT)
> + return -EINVAL;
> +
> + if (i == ctx->input)
> + return 0;
I would drop this 'if'. It doesn't really do anything useful.
> +
> + ctx->input = i;
> + return 0;
> +}
> +
> +/* timeperframe is arbitrary and continuous */
> +static int cal_enum_frameintervals(struct file *file, void *priv,
> + struct v4l2_frmivalenum *fival)
> +{
> + struct cal_ctx *ctx = video_drvdata(file);
> + const struct cal_fmt *fmt;
> + struct v4l2_subdev_frame_size_enum fse;
> + int ret;
> +
> + if (fival->index)
> + return -EINVAL;
> +
> + fmt = find_format_by_pix(fival->pixel_format);
> + if (!fmt)
> + return -EINVAL;
> +
> + /* check for valid width/height */
> + ret = 0;
> + fse.pad = 0;
> + fse.code = fmt->code;
> + fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> + for (fse.index = 0; ; fse.index++) {
> + ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size,
> + NULL, &fse);
> + if (ret)
> + return -EINVAL;
> +
> + if ((fival->width == fse.max_width) &&
> + (fival->height == fse.max_height))
> + break;
> + else if ((fival->width >= fse.min_width) &&
> + (fival->width <= fse.max_width) &&
> + (fival->height >= fse.min_height) &&
> + (fival->height <= fse.max_height))
> + break;
> +
> + return -EINVAL;
> + }
> +
> + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
> + fival->discrete.numerator = 1;
> + fival->discrete.denominator = 30;
> +
> + return 0;
> +}
> +
> +/*
> + * Videobuf operations
> + */
> +static int cal_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
> + unsigned int *nbuffers, unsigned int *nplanes,
> + unsigned int sizes[], void *alloc_ctxs[])
> +{
> + struct cal_ctx *ctx = vb2_get_drv_priv(vq);
> + unsigned long size;
> +
> + size = ctx->width * ctx->height * ctx->pixelsize;
> + if (fmt) {
> + if (fmt->fmt.pix.sizeimage < size)
> + return -EINVAL;
> + size = fmt->fmt.pix.sizeimage;
> + /* check against insane over 8K resolution buffers */
> + if (size > 7680 * 4320 * ctx->pixelsize)
> + return -EINVAL;
> + }
> +
> + *nplanes = 1;
> + sizes[0] = size;
> + alloc_ctxs[0] = ctx->alloc_ctx;
> +
> + ctx_dbg(3, ctx, "nbuffers=%d, size=%ld\n", *nbuffers, size);
> +
> + return 0;
> +}
> +
> +static int cal_buffer_prepare(struct vb2_buffer *vb)
> +{
> + struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> + struct cal_buffer *buf = container_of(vb, struct cal_buffer, vb);
> + unsigned long size;
> +
> + BUG_ON(NULL == ctx->fmt);
> +
> + size = ctx->width * ctx->height * ctx->pixelsize;
> + if (vb2_plane_size(vb, 0) < size) {
> + ctx_err(ctx,
> + "data will not fit into plane (%lu < %lu)\n",
> + vb2_plane_size(vb, 0), size);
> + return -EINVAL;
> + }
> +
> + vb2_set_plane_payload(&buf->vb, 0, size);
> + return 0;
> +}
> +
> +static void cal_buffer_queue(struct vb2_buffer *vb)
> +{
> + struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> + struct cal_buffer *buf = container_of(vb, struct cal_buffer, vb);
> + struct cal_dmaqueue *vidq = &ctx->vidq;
> + unsigned long flags = 0;
> +
> + /* recheck locking */
> + spin_lock_irqsave(&ctx->slock, flags);
> + list_add_tail(&buf->list, &vidq->active);
> + spin_unlock_irqrestore(&ctx->slock, flags);
> +}
> +
> +static int cal_start_streaming(struct vb2_queue *vq, unsigned int count)
> +{
> + struct cal_ctx *ctx = vb2_get_drv_priv(vq);
> + struct cal_dmaqueue *dma_q = &ctx->vidq;
> + struct cal_buffer *buf;
> + unsigned long addr = 0;
> + unsigned long flags;
> + int ret;
> +
> + ctx_dbg(3, ctx, "%s\n", __func__);
> +
> + spin_lock_irqsave(&ctx->slock, flags);
> + if (list_empty(&dma_q->active)) {
> + spin_unlock_irqrestore(&ctx->slock, flags);
> + ctx_dbg(3, ctx, "buffer queue is empty\n");
> + return -EIO;
> + }
> +
> + buf = list_entry(dma_q->active.next, struct cal_buffer, list);
> + ctx->cur_frm = buf;
> + ctx->next_frm = buf;
> + list_del(&buf->list);
> + spin_unlock_irqrestore(&ctx->slock, flags);
> +
> + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
That's wrong. The timestamp should be set when the buffer is received, so that
should happen in the interrupt routine, I guess.
> +
> + addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb, 0);
> + ctx->sequence = 0;
> +
> + ctx_dbg(3, ctx, "enable_irqs\n");
> +
> + ret = cal_get_external_info(ctx);
> + if (ret < 0)
> + return ret;
The cleanup in case of an error in start_streaming is missing: in that case all
pending buffers have to be returned with state VB2_BUF_STATE_QUEUED. Otherwise
you will get warnings from the vb2 framework. It's similar to what you do in
stop_streaming in case of an error, just with a different state.
> +
> + enable_irqs(ctx);
> + camerarx_phy_enable(ctx);
> + csi2_init(ctx);
> + csi2_phy_config(ctx);
> + csi2_lane_config(ctx);
> + csi2_ctx_config(ctx);
> + pix_proc_config(ctx);
> + cal_wr_dma_config(ctx, ALIGN((ctx->width * ctx->pixelsize), 16));
> + cal_wr_dma_addr(ctx, addr);
> + csi2_ppi_enable(ctx);
> +
> + if (ctx->sensor) {
> + if (v4l2_subdev_call(ctx->sensor, video, s_stream, 1)) {
> + ctx_err(ctx, "stream on failed in subdev\n");
> + return -EINVAL;
> + }
> + }
> +
> + if (debug >= 4)
> + cal_quickdump_regs(ctx->dev);
> +
> + ctx_dbg(3, ctx, "returning from %s\n", __func__);
> + return 0;
> +}
> +
> +static void cal_stop_streaming(struct vb2_queue *vq)
> +{
> + struct cal_ctx *ctx = vb2_get_drv_priv(vq);
> + struct cal_dmaqueue *dma_q = &ctx->vidq;
> + unsigned long flags;
> +
> + ctx_dbg(3, ctx, "%s\n", __func__);
> +
> + if (ctx->sensor) {
> + if (v4l2_subdev_call(ctx->sensor, video, s_stream, 0))
> + ctx_err(ctx, "stream off failed in subdev\n");
> + }
> +
> + ctx_dbg(3, ctx, "csi2_ppi_disable\n");
> + csi2_ppi_disable(ctx);
> +
> + ctx_dbg(3, ctx, "disable_irqs\n");
> + disable_irqs(ctx);
> +
> + /* Release all active buffers */
> + spin_lock_irqsave(&ctx->slock, flags);
> + while (!list_empty(&dma_q->active)) {
> + struct cal_buffer *buf;
> +
> + buf = list_entry(dma_q->active.next, struct cal_buffer, list);
> + list_del(&buf->list);
> + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> + ctx_dbg(3, ctx, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
> + }
> + spin_unlock_irqrestore(&ctx->slock, flags);
> +
> + if (ctx->cur_frm == ctx->next_frm) {
> + vb2_buffer_done(&ctx->cur_frm->vb, VB2_BUF_STATE_ERROR);
> + ctx_dbg(3, ctx, "[%p/%d] done cur_frm\n", ctx->cur_frm,
> + ctx->cur_frm->vb.v4l2_buf.index);
> + } else {
> + vb2_buffer_done(&ctx->cur_frm->vb, VB2_BUF_STATE_ERROR);
> + ctx_dbg(3, ctx, "[%p/%d] done cur_frm\n", ctx->cur_frm,
> + ctx->cur_frm->vb.v4l2_buf.index);
> + vb2_buffer_done(&ctx->next_frm->vb, VB2_BUF_STATE_ERROR);
> + ctx_dbg(3, ctx, "[%p/%d] done next_frm\n", ctx->next_frm,
> + ctx->next_frm->vb.v4l2_buf.index);
> + }
> + ctx->cur_frm = NULL;
> + ctx->next_frm = NULL;
> +
> + ctx_dbg(3, ctx, "returning from %s\n", __func__);
> +}
> +
> +static struct vb2_ops cal_video_qops = {
> + .queue_setup = cal_queue_setup,
> + .buf_prepare = cal_buffer_prepare,
> + .buf_queue = cal_buffer_queue,
> + .start_streaming = cal_start_streaming,
> + .stop_streaming = cal_stop_streaming,
> + .wait_prepare = vb2_ops_wait_prepare,
> + .wait_finish = vb2_ops_wait_finish,
> +};
> +
> +static const struct v4l2_file_operations cal_fops = {
> + .owner = THIS_MODULE,
> + .open = v4l2_fh_open,
> + .release = vb2_fop_release,
> + .read = vb2_fop_read,
> + .poll = vb2_fop_poll,
> + .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
> + .mmap = vb2_fop_mmap,
> +};
> +
> +static const struct v4l2_ioctl_ops cal_ioctl_ops = {
> + .vidioc_querycap = cal_querycap,
> + .vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap,
> + .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap,
> + .vidioc_try_fmt_vid_cap = cal_try_fmt_vid_cap,
> + .vidioc_s_fmt_vid_cap = cal_s_fmt_vid_cap,
> + .vidioc_enum_framesizes = cal_enum_framesizes,
> + .vidioc_reqbufs = vb2_ioctl_reqbufs,
> + .vidioc_create_bufs = vb2_ioctl_create_bufs,
> + .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
> + .vidioc_querybuf = vb2_ioctl_querybuf,
> + .vidioc_qbuf = vb2_ioctl_qbuf,
> + .vidioc_dqbuf = vb2_ioctl_dqbuf,
> + .vidioc_enum_input = cal_enum_input,
> + .vidioc_g_input = cal_g_input,
> + .vidioc_s_input = cal_s_input,
> + .vidioc_enum_frameintervals = cal_enum_frameintervals,
> + .vidioc_streamon = vb2_ioctl_streamon,
> + .vidioc_streamoff = vb2_ioctl_streamoff,
> + .vidioc_log_status = v4l2_ctrl_log_status,
> + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
> + .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> +};
> +
> +static struct video_device cal_videodev = {
> + .name = CAL_MODULE_NAME,
> + .fops = &cal_fops,
> + .ioctl_ops = &cal_ioctl_ops,
> + .minor = -1,
> + .release = video_device_release,
> +};
> +
> +/* -----------------------------------------------------------------
> + Initialization and module stuff
> + ------------------------------------------------------------------*/
> +static int cal_release(struct cal_dev *dev)
> +{
> + struct cal_ctx *ctx;
> + int i;
> +
> + for (i = 0; i < CAL_NUM_CONTEXT; i++) {
> + ctx = dev->ctx[i];
> + if (ctx) {
> + v4l2_info(&ctx->v4l2_dev, "unregistering %s\n",
> + video_device_node_name(&ctx->vdev));
> + video_unregister_device(&ctx->vdev);
> + v4l2_device_unregister(&ctx->v4l2_dev);
> + vb2_dma_contig_cleanup_ctx(ctx->alloc_ctx);
> + v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> + kfree(ctx->cc);
> + kfree(ctx);
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int cal_complete_ctx(struct cal_ctx *ctx);
> +
> +static int cal_async_bound(struct v4l2_async_notifier *notifier,
> + struct v4l2_subdev *subdev,
> + struct v4l2_async_subdev *asd)
> +{
> + struct cal_ctx *ctx = notifier_to_ctx(notifier);
> + struct v4l2_subdev_mbus_code_enum mbus_code;
> + int i, j;
> +
> + ctx_dbg(1, ctx, "cal_async_bound\n");
> +
> + if (ctx->sensor) {
> + ctx_info(ctx, "Rejecting subdev %s (Already set!!)",
> + subdev->name);
> + return 0;
> + }
> +
> + ctx->sensor = subdev;
> + ctx_info(ctx, "Using sensor %s for capture\n",
> + subdev->name);
> +
> + /* setup the supported formats & indexes */
> + for (j = 0, i = 0; ; ++j) {
> + struct cal_fmt *fmt;
> + int ret;
> +
> + memset(&mbus_code, 0, sizeof(mbus_code));
> + mbus_code.index = j;
> + ret = v4l2_subdev_call(subdev, pad, enum_mbus_code,
> + NULL, &mbus_code);
> + if (ret)
> + break;
> +
> + fmt = (struct cal_fmt *)find_format_by_code(mbus_code.code);
> + if (!fmt)
> + continue;
> +
> + fmt->supported = true;
> + fmt->index = i++;
> + }
> +
> + cal_complete_ctx(ctx);
> +
> + return 0;
> +}
> +
> +static int cal_async_complete(struct v4l2_async_notifier *notifier)
> +{
> + struct cal_ctx *ctx = notifier_to_ctx(notifier);
> +
> + ctx_dbg(1, ctx, "cal_async_complete\n");
> + return 0;
> +}
> +
> +static int cal_complete_ctx(struct cal_ctx *ctx)
> +{
> + struct video_device *vfd;
> + struct vb2_queue *q;
> + int ret;
> +
> + ctx->timeperframe = tpf_default;
> +
> + ctx->width = 1920;
> + ctx->height = 1080;
> + ctx->field = V4L2_FIELD_NONE;
> + ctx->fmt = find_format_by_code(MEDIA_BUS_FMT_SGRBG8_1X8);
> + ctx->pixelsize = ctx->fmt->depth >> 3;
> + ctx->external_rate = 192000000;
> +
> + /* initialize locks */
> + spin_lock_init(&ctx->slock);
> + mutex_init(&ctx->mutex);
> +
> + /* initialize queue */
> + q = &ctx->vb_vidq;
> + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
> + q->drv_priv = ctx;
> + q->buf_struct_size = sizeof(struct cal_buffer);
> + q->ops = &cal_video_qops;
> + q->mem_ops = &vb2_dma_contig_memops;
> + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> + q->lock = &ctx->mutex;
> + q->min_buffers_needed = 3;
> +
> + ret = vb2_queue_init(q);
> + if (ret)
> + return ret;
> +
> + /* init video dma queues */
> + INIT_LIST_HEAD(&ctx->vidq.active);
> +
> + vfd = &ctx->vdev;
> + *vfd = cal_videodev;
> + vfd->v4l2_dev = &ctx->v4l2_dev;
> + vfd->queue = q;
> +
> + /*
> + * Provide a mutex to v4l2 core. It will be used to protect
> + * all fops and v4l2 ioctls.
> + */
> + vfd->lock = &ctx->mutex;
> + video_set_drvdata(vfd, ctx);
> +
> + ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
> + if (ret < 0)
> + return ret;
> +
> + v4l2_info(&ctx->v4l2_dev, "V4L2 device registered as %s\n",
> + video_device_node_name(vfd));
> +
> + ctx->alloc_ctx = vb2_dma_contig_init_ctx(vfd->v4l2_dev->dev);
> + if (IS_ERR(ctx->alloc_ctx)) {
> + ctx_err(ctx, "Failed to alloc vb2 context\n");
> + ret = PTR_ERR(ctx->alloc_ctx);
> + goto vdev_unreg;
> + }
> +
> + return 0;
> +
> +vdev_unreg:
> + video_unregister_device(vfd);
> + return ret;
> +}
> +
> +static struct device_node *
> +of_get_next_port(const struct device_node *parent,
> + struct device_node *prev)
> +{
> + struct device_node *port = NULL;
> +
> + if (!parent)
> + return NULL;
> +
> + if (!prev) {
> + struct device_node *ports;
> + /*
> + * It's the first call, we have to find a port subnode
> + * within this node or within an optional 'ports' node.
> + */
> + ports = of_get_child_by_name(parent, "ports");
> + if (ports)
> + parent = ports;
> +
> + port = of_get_child_by_name(parent, "port");
> +
> + /* release the 'ports' node */
> + of_node_put(ports);
> + } else {
> + struct device_node *ports;
> +
> + ports = of_get_parent(prev);
> + if (!ports)
> + return NULL;
> +
> + do {
> + port = of_get_next_child(ports, prev);
> + if (!port) {
> + of_node_put(ports);
> + return NULL;
> + }
> + prev = port;
> + } while (of_node_cmp(port->name, "port") != 0);
> + }
> +
> + return port;
> +}
> +
> +static struct device_node *
> +of_get_next_endpoint(const struct device_node *parent,
> + struct device_node *prev)
> +{
> + struct device_node *ep = NULL;
> +
> + if (!parent)
> + return NULL;
> +
> + do {
> + ep = of_get_next_child(parent, prev);
> + if (!ep)
> + return NULL;
> + prev = ep;
> + } while (of_node_cmp(ep->name, "endpoint") != 0);
> +
> + return ep;
> +}
> +
> +static int of_cal_create_instance(struct cal_ctx *ctx, int inst)
> +{
> + struct platform_device *pdev = ctx->dev->pdev;
> + struct device_node *ep_node, *port, *remote_ep,
> + *sensor_node, *parent;
> + struct v4l2_of_endpoint *endpoint;
> + struct v4l2_async_subdev *asd;
> + u32 regval = 0;
> + int ret, index, found_port = 0, lane;
> +
> + parent = pdev->dev.of_node;
> +
> + asd = &ctx->asd;
> + endpoint = &ctx->endpoint;
> +
> + ep_node = NULL;
> + port = NULL;
> + remote_ep = NULL;
> + sensor_node = NULL;
> + ret = -EINVAL;
> +
> + ctx_dbg(3, ctx, "Scanning Port node for csi2 port: %d\n", inst);
> + for (index = 0; index < CAL_NUM_CSI2_PORTS; index++) {
> + port = of_get_next_port(parent, port);
> + if (!port) {
> + ctx_dbg(1, ctx, "No port node found for csi2 port:%d\n",
> + index);
> + goto cleanup_exit;
> + }
> +
> + /* Match the slice number with <REG> */
> + of_property_read_u32(port, "reg", ®val);
> + ctx_dbg(3, ctx, "port:%d inst:%d <reg>:%d\n",
> + index, inst, regval);
> + if ((regval == inst) && (index == inst)) {
> + found_port = 1;
> + break;
> + }
> + }
> +
> + if (!found_port) {
> + ctx_dbg(1, ctx, "No port node matches csi2 port:%d\n",
> + inst);
> + goto cleanup_exit;
> + }
> +
> + ctx_dbg(3, ctx, "Scanning sub-device for csi2 port: %d\n",
> + inst);
> +
> + ep_node = of_get_next_endpoint(port, ep_node);
> + if (!ep_node) {
> + ctx_dbg(3, ctx, "can't get next endpoint\n");
> + goto cleanup_exit;
> + }
> +
> + sensor_node = of_graph_get_remote_port_parent(ep_node);
> + if (!sensor_node) {
> + ctx_dbg(3, ctx, "can't get remote parent\n");
> + goto cleanup_exit;
> + }
> + asd->match_type = V4L2_ASYNC_MATCH_OF;
> + asd->match.of.node = sensor_node;
> +
> + remote_ep = of_parse_phandle(ep_node, "remote-endpoint", 0);
> + if (!remote_ep) {
> + ctx_dbg(3, ctx, "can't get remote-endpoint\n");
> + goto cleanup_exit;
> + }
> + v4l2_of_parse_endpoint(remote_ep, endpoint);
> +
> + if (endpoint->bus_type != V4L2_MBUS_CSI2) {
> + ctx_err(ctx, "Port:%d sub-device %s is not a CSI2 device\n",
> + inst, sensor_node->name);
> + goto cleanup_exit;
> + }
> +
> + /* Store Virtual Channel number */
> + ctx->virtual_channel = endpoint->base.id;
> +
> + ctx_dbg(3, ctx, "Port:%d v4l2-endpoint: CSI2\n", inst);
> + ctx_dbg(3, ctx, "Virtual Channel=%d\n", ctx->virtual_channel);
> + ctx_dbg(3, ctx, "flags=0x%08x\n", endpoint->bus.mipi_csi2.flags);
> + ctx_dbg(3, ctx, "clock_lane=%d\n", endpoint->bus.mipi_csi2.clock_lane);
> + ctx_dbg(3, ctx, "num_data_lanes=%d\n",
> + endpoint->bus.mipi_csi2.num_data_lanes);
> + ctx_dbg(3, ctx, "data_lanes= <\n");
> + for (lane = 0; lane < endpoint->bus.mipi_csi2.num_data_lanes; lane++)
> + ctx_dbg(3, ctx, "\t%d\n",
> + endpoint->bus.mipi_csi2.data_lanes[lane]);
> + ctx_dbg(3, ctx, "\t>\n");
> +
> + ctx_dbg(1, ctx, "Port: %d found sub-device %s\n",
> + inst, sensor_node->name);
> +
> + ctx_dbg(1, ctx, "Asynchronous subdevice registration\n");
> + ctx->asd_list[0] = asd;
> + ctx->notifier.subdevs = ctx->asd_list;
> + ctx->notifier.num_subdevs = 1;
> + ctx->notifier.bound = cal_async_bound;
> + ctx->notifier.complete = cal_async_complete;
> + ret = v4l2_async_notifier_register(&ctx->v4l2_dev,
> + &ctx->notifier);
> + if (ret) {
> + ctx_err(ctx, "Error registering async notifier\n");
> + ret = -EINVAL;
> + }
> +
> +cleanup_exit:
> + if (!remote_ep)
> + of_node_put(remote_ep);
> + if (!sensor_node)
> + of_node_put(sensor_node);
> + if (!ep_node)
> + of_node_put(ep_node);
> + if (!port)
> + of_node_put(port);
> +
> + return ret;
> +}
> +
> +static struct cal_ctx *cal_create_instance(struct cal_dev *dev, int inst)
> +{
> + struct cal_ctx *ctx;
> + struct v4l2_ctrl_handler *hdl;
> + int ret;
> +
> + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> + if (!ctx)
> + return 0;
> + /* save the cal_dev * for future ref */
> + ctx->dev = dev;
> +
> + snprintf(ctx->v4l2_dev.name, sizeof(ctx->v4l2_dev.name),
> + "%s-%03d", CAL_MODULE_NAME, inst);
> + ret = v4l2_device_register(&dev->pdev->dev, &ctx->v4l2_dev);
> + if (ret)
> + goto free_ctx;
> +
> + hdl = &ctx->ctrl_handler;
> + ret = v4l2_ctrl_handler_init(hdl, 11);
> + if (ret) {
> + ctx_err(ctx, "Failed to init ctrl handler\n");
> + goto free_hdl;
> + }
> + ctx->v4l2_dev.ctrl_handler = hdl;
> +
> + /* Make sure Camera Core H/W register area is available */
> + ctx->cc = cc_create(dev, inst);
> + if (IS_ERR(ctx->cc)) {
> + ret = PTR_ERR(ctx->cc);
> + goto unreg_dev;
> + }
> +
> + /* Store the instance id */
> + ctx->csi2_port = inst + 1;
> +
> + ret = of_cal_create_instance(ctx, inst);
> + if (ret) {
> + ctx_dbg(1, ctx, "Error scanning cal instance: %d\n", inst);
> + ret = -EINVAL;
> + goto free_cc;
> + }
> + return ctx;
> +
> +free_cc:
> + kfree(ctx->cc);
> +free_hdl:
> + v4l2_ctrl_handler_free(hdl);
> +unreg_dev:
> + v4l2_device_unregister(&ctx->v4l2_dev);
> +free_ctx:
> + kfree(ctx);
> + return 0;
> +}
> +
> +static int cal_probe(struct platform_device *pdev)
> +{
> + struct cal_dev *dev;
> + int ret;
> + int irq, func;
> +
> + dev_info(&pdev->dev, "Probing %s\n",
> + CAL_MODULE_NAME);
> +
> + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
> + if (!dev)
> + return -ENOMEM;
> +
> + /* set pseudo v4l2 device name so we can use v4l2_printk */
> + strcpy(dev->v4l2_dev.name, CAL_MODULE_NAME);
> +
> + /* save pdev pointer */
> + dev->pdev = pdev;
> +
> + dev->res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> + "cal_top");
> + cal_dbg(1, dev, "ioresource %s at %x - %x\n",
> + dev->res->name, dev->res->start, dev->res->end);
> +
> + dev->base = devm_ioremap(&pdev->dev, dev->res->start, SZ_32K);
> + if (!dev->base) {
> + ret = -ENOMEM;
> + goto just_exit;
> + }
> +
> + irq = platform_get_irq(pdev, 0);
> + cal_dbg(1, dev, "got irq# %d\n", irq);
> + ret = devm_request_irq(&pdev->dev, irq, cal_irq, 0, CAL_MODULE_NAME,
> + dev);
> + if (ret)
> + goto just_exit;
> +
> + platform_set_drvdata(pdev, dev);
> +
> + pm_runtime_enable(&pdev->dev);
> +
> + ret = pm_runtime_get_sync(&pdev->dev);
> + if (ret)
> + goto just_exit;
> +
> + /* Just check we can actually access the module */
> + cal_get_hwinfo(dev);
> +
> + func = cal_read_field(dev, CAL_HL_REVISION, CAL_HL_REVISION_FUNC_MASK,
> + CAL_HL_REVISION_FUNC_SHIFT);
> + cal_dbg(1, dev, "CAL HL_REVISION function %x\n", func);
> +
> + dev->cm = cm_create(dev);
> + if (IS_ERR(dev->cm)) {
> + ret = PTR_ERR(dev->cm);
> + goto runtime_put;
> + }
> + dev->ctx[0] = NULL;
> + dev->ctx[1] = NULL;
> +
> + dev->ctx[0] = cal_create_instance(dev, 0);
> + dev->ctx[1] = cal_create_instance(dev, 1);
> + if (!dev->ctx[0] && !dev->ctx[1]) {
> + ret = -ENODEV;
> + cal_err(dev, "Neither port is configured, no point in staying up\n");
> + goto free_ctx;
> + }
> +
> + return 0;
> +
> +free_ctx:
> + kfree(dev->ctx[0]);
> + kfree(dev->ctx[1]);
> + kfree(dev->cm);
> +runtime_put:
> + pm_runtime_put_sync(&pdev->dev);
> + pm_runtime_disable(&pdev->dev);
> +just_exit:
> + return ret;
> +}
> +
> +static int cal_remove(struct platform_device *pdev)
> +{
> + struct cal_dev *dev =
> + (struct cal_dev *)platform_get_drvdata(pdev);
> +
> + cal_info(dev, "Removing %s\n", CAL_MODULE_NAME);
> +
> + cal_release(dev);
> +
> + /* disable csi2 phy */
> + if (dev->ctx[0])
> + camerarx_phy_disable(dev->ctx[0]);
> + if (dev->ctx[1])
> + camerarx_phy_disable(dev->ctx[1]);
> + kfree(dev->cm);
> +
> + pm_runtime_put_sync(&pdev->dev);
> + pm_runtime_disable(&pdev->dev);
> +
> + return 0;
> +}
> +
> +#if defined(CONFIG_OF)
> +static const struct of_device_id cal_of_match[] = {
> + { .compatible = "ti,cal", },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, cal_of_match);
> +#else
> +#define cal_of_match NULL
> +#endif
> +
> +static struct platform_driver cal_pdrv = {
> + .probe = cal_probe,
> + .remove = cal_remove,
> + .driver = {
> + .name = CAL_MODULE_NAME,
> + .of_match_table = cal_of_match,
> + },
> +};
> +
> +module_platform_driver(cal_pdrv);
Regards,
Hans
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Patch 0/2] media: v4l: ti-vpe: Add CAL v4l2 camera capture driver
2015-06-22 7:31 ` [Patch 0/2] media: v4l: ti-vpe: Add CAL v4l2 camera capture driver Hans Verkuil
@ 2015-06-23 19:19 ` Benoit Parrot
0 siblings, 0 replies; 8+ messages in thread
From: Benoit Parrot @ 2015-06-23 19:19 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media, devicetree, Tony Lindgren, linux-kernel
Hans Verkuil <hverkuil@xs4all.nl> wrote on Mon [2015-Jun-22 09:31:21 +0200]:
> On 06/16/2015 07:29 PM, Benoit Parrot wrote:
> > The Camera Adaptation Layer (CAL) is a block which consists of a dual
> > port CSI2/MIPI camera capture engine.
> > This camera engine is currently found on DRA72xx family of devices.
> >
> > Port #0 can handle CSI2 camera connected to up to 4 data lanes.
> > Port #1 can handle CSI2 camera connected to up to 2 data lanes.
> >
> > The driver implements the required API/ioctls to be V4L2 compliant.
> > Driver supports the following:
> > - V4L2 API using DMABUF/MMAP buffer access based on videobuf2 api
> > - Asynchronous sensor sub device registration
> > - DT support
> >
> > Currently each port is designed to connect to a single sub-device.
> > In other words port aggregation is not currently supported.
> >
> > Here is a sample output of the v4l2-compliance tool:
> >
> > # ./v4l2-compliance -s -v -d /dev/video0
>
> Can you show the output of './v4l2-compliance -f' as well?
I can but you won't see much as my test sensor (the only one I have)
only support V4L2_PIX_FMT_SGRBG8.
>
> Thanks!
>
> Hans
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Patch 1/2] media: v4l: ti-vpe: Add CAL v4l2 camera capture driver
2015-06-22 8:09 ` Hans Verkuil
@ 2015-06-23 19:36 ` Benoit Parrot
2015-09-16 21:22 ` Benoit Parrot
0 siblings, 1 reply; 8+ messages in thread
From: Benoit Parrot @ 2015-06-23 19:36 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media, devicetree, Tony Lindgren, linux-kernel
Hans,
Thanks for the feedback.
Hans Verkuil <hverkuil@xs4all.nl> wrote on Mon [2015-Jun-22 10:09:00 +0200]:
> Hi Benoit,
>
> Thanks for the patch! I do have a few comments, though. See below.
>
> On 06/16/2015 07:29 PM, Benoit Parrot wrote:
> > The Camera Adaptation Layer (CAL) is a block which consists of a dual
> > port CSI2/MIPI camera capture engine.
> > Port #0 can handle CSI2 camera connected to up to 4 data lanes.
> > Port #1 can handle CSI2 camera connected to up to 2 data lanes.
> > The driver implements the required API/ioctls to be V4L2 compliant.
> > Driver supports the following:
> > - V4L2 API using DMABUF/MMAP buffer access based on videobuf2 api
> > - Asynchronous sensor sub device registration
> > - DT support
> >
> > Signed-off-by: Benoit Parrot <bparrot@ti.com>
> > ---
> > drivers/media/platform/Kconfig | 12 +
> > drivers/media/platform/Makefile | 2 +
> > drivers/media/platform/ti-vpe/Makefile | 4 +
> > drivers/media/platform/ti-vpe/cal.c | 2225 ++++++++++++++++++++++++++++++
> > drivers/media/platform/ti-vpe/cal_regs.h | 779 +++++++++++
> > 5 files changed, 3022 insertions(+)
> > create mode 100644 drivers/media/platform/ti-vpe/cal.c
> > create mode 100644 drivers/media/platform/ti-vpe/cal_regs.h
> >
> > diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> > index 4776a8c..c4c9c18 100644
> > --- a/drivers/media/platform/Kconfig
> > +++ b/drivers/media/platform/Kconfig
> > @@ -120,6 +120,18 @@ source "drivers/media/platform/s5p-tv/Kconfig"
> > source "drivers/media/platform/am437x/Kconfig"
> > source "drivers/media/platform/xilinx/Kconfig"
> >
> > +config VIDEO_TI_CAL
> > + tristate "TI CAL (Camera Adaptation Layer) driver"
> > + depends on VIDEO_DEV && VIDEO_V4L2 && SOC_DRA7XX
> > + depends on VIDEO_V4L2_SUBDEV_API
> > + depends on VIDEOBUF2_DMA_CONTIG
> > + default n
> > + ---help---
> > + Support for the TI CAL (Camera Adaptation Layer) block
> > + found on DRA72X SoC.
> > + In TI Technical Reference Manual this module is referred as
> > + Camera Interface Subsystem (CAMSS).
> > +
> > endif # V4L_PLATFORM_DRIVERS
> >
> > menuconfig V4L_MEM2MEM_DRIVERS
> > diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> > index 114f9ab..062022f 100644
> > --- a/drivers/media/platform/Makefile
> > +++ b/drivers/media/platform/Makefile
> > @@ -18,6 +18,8 @@ obj-$(CONFIG_VIDEO_VIM2M) += vim2m.o
> >
> > obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe/
> >
> > +obj-$(CONFIG_VIDEO_TI_CAL) += ti-vpe/
> > +
> > obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o
> > obj-$(CONFIG_VIDEO_CODA) += coda/
> >
> > diff --git a/drivers/media/platform/ti-vpe/Makefile b/drivers/media/platform/ti-vpe/Makefile
> > index be680f8..e236059 100644
> > --- a/drivers/media/platform/ti-vpe/Makefile
> > +++ b/drivers/media/platform/ti-vpe/Makefile
> > @@ -3,3 +3,7 @@ obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe.o
> > ti-vpe-y := vpe.o sc.o csc.o vpdma.o
> >
> > ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) += -DDEBUG
> > +
> > +obj-$(CONFIG_VIDEO_TI_CAL) += ti-cal.o
> > +
> > +ti-cal-y := cal.o
> > diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c
> > new file mode 100644
> > index 0000000..cc1f61e
> > --- /dev/null
> > +++ b/drivers/media/platform/ti-vpe/cal.c
> > @@ -0,0 +1,2225 @@
> > +/*
> > + * TI CAL camera interface driver
> > + *
> > + * Copyright (c) 2015 Texas Instruments Inc.
> > + * Benoit Parrot, <bparrot@ti.com>
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU General Public License version 2 as published by
> > + * the Free Software Foundation
> > + */
> > +
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/ioctl.h>
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/delay.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/slab.h>
> > +#include <linux/videodev2.h>
> > +#include <linux/of_device.h>
> > +#include <linux/of_graph.h>
> > +
> > +#include <media/v4l2-of.h>
> > +#include <media/v4l2-async.h>
> > +#include <media/v4l2-common.h>
> > +#include <media/v4l2-ctrls.h>
> > +#include <media/v4l2-device.h>
> > +#include <media/v4l2-event.h>
> > +#include <media/v4l2-ioctl.h>
> > +#include <media/v4l2-ctrls.h>
> > +#include <media/v4l2-fh.h>
> > +#include <media/v4l2-event.h>
> > +#include <media/v4l2-common.h>
> > +#include <media/videobuf2-core.h>
> > +#include <media/videobuf2-dma-contig.h>
> > +#include "cal_regs.h"
> > +
> > +#define CAL_MODULE_NAME "cal"
> > +
> > +#define MAX_WIDTH 1920
> > +#define MAX_HEIGHT 1200
> > +
> > +#define CAL_VERSION "0.1.0"
> > +
> > +MODULE_DESCRIPTION("TI CAL driver");
> > +MODULE_AUTHOR("Benoit Parrot, <bparrot@ti.com>");
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_VERSION(CAL_VERSION);
> > +
> > +static unsigned video_nr = -1;
> > +module_param(video_nr, uint, 0644);
> > +MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect");
> > +
> > +static unsigned debug;
> > +module_param(debug, uint, 0644);
> > +MODULE_PARM_DESC(debug, "activates debug info");
> > +
> > +/* timeperframe: min/max and default */
> > +static const struct v4l2_fract
> > + tpf_default = {.numerator = 1001, .denominator = 30000};
> > +
> > +#define cal_dbg(level, caldev, fmt, arg...) \
> > + v4l2_dbg(level, debug, &caldev->v4l2_dev, fmt, ##arg)
> > +#define cal_info(caldev, fmt, arg...) \
> > + v4l2_info(&caldev->v4l2_dev, fmt, ##arg)
> > +#define cal_err(caldev, fmt, arg...) \
> > + v4l2_err(&caldev->v4l2_dev, fmt, ##arg)
> > +
> > +#define ctx_dbg(level, ctx, fmt, arg...) \
> > + v4l2_dbg(level, debug, &ctx->v4l2_dev, fmt, ##arg)
> > +#define ctx_info(ctx, fmt, arg...) \
> > + v4l2_info(&ctx->v4l2_dev, fmt, ##arg)
> > +#define ctx_err(ctx, fmt, arg...) \
> > + v4l2_err(&ctx->v4l2_dev, fmt, ##arg)
> > +
> > +#define CAL_NUM_INPUT 1
> > +#define CAL_NUM_CONTEXT 2
> > +
> > +/* ------------------------------------------------------------------
> > + Basic structures
> > + ------------------------------------------------------------------*/
> > +
> > +struct cal_fmt {
> > + const char *name;
>
> Drop the description: that's set by the v4l2 core (this ensures consistent naming).
Will do.
>
> > + u32 fourcc;
> > + u32 code;
> > + u32 colorspace;
>
> This is wrong. The colorspace comes from the sensor and does not belong in this
> driver.
>
Understood.
> > + u8 depth;
> > + bool supported;
> > + u32 index;
> > +};
> > +
> > +static const struct cal_fmt formats[] = {
> > + {
> > + .name = "YUV 4:2:2 packed, YCbYCr",
> > + .fourcc = V4L2_PIX_FMT_YUYV,
> > + .code = MEDIA_BUS_FMT_YUYV8_2X8,
> > + .colorspace = V4L2_COLORSPACE_SMPTE170M,
> > + .depth = 16,
> > + .supported = false,
> > + }, {
> > + .name = "YUV 4:2:2 packed, CbYCrY",
> > + .fourcc = V4L2_PIX_FMT_UYVY,
> > + .code = MEDIA_BUS_FMT_UYVY8_2X8,
> > + .colorspace = V4L2_COLORSPACE_SMPTE170M,
> > + .depth = 16,
> > + .supported = false,
> > + }, {
> > + .name = "YUV 4:2:2 packed, YCrYCb",
> > + .fourcc = V4L2_PIX_FMT_YVYU,
> > + .code = MEDIA_BUS_FMT_YVYU8_2X8,
> > + .colorspace = V4L2_COLORSPACE_SMPTE170M,
> > + .depth = 16,
> > + .supported = false,
> > + }, {
> > + .name = "4YUV 4:2:2 packed, CrYCbY",
> > + .fourcc = V4L2_PIX_FMT_VYUY,
> > + .code = MEDIA_BUS_FMT_VYUY8_2X8,
> > + .colorspace = V4L2_COLORSPACE_SMPTE170M,
> > + .depth = 16,
> > + .supported = false,
> > + }, {
> > + .name = "RGB565 (LE)",
> > + .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
> > + .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
> > + .colorspace = V4L2_COLORSPACE_SRGB,
> > + .depth = 16,
> > + .supported = false,
> > + }, {
> > + .name = "RGB565 (BE)",
> > + .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
> > + .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
> > + .colorspace = V4L2_COLORSPACE_SRGB,
> > + .depth = 16,
> > + .supported = false,
> > + }, {
> > + .name = "RGB555 (LE)",
> > + .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
> > + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
> > + .colorspace = V4L2_COLORSPACE_SRGB,
> > + .depth = 16,
> > + .supported = false,
> > + }, {
> > + .name = "RGB555 (BE)",
> > + .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
> > + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
> > + .colorspace = V4L2_COLORSPACE_SRGB,
> > + .depth = 16,
> > + .supported = false,
> > + }, {
> > + .name = "RGB24 (LE)",
> > + .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
> > + .code = MEDIA_BUS_FMT_RGB888_2X12_LE,
> > + .colorspace = V4L2_COLORSPACE_SRGB,
> > + .depth = 24,
> > + .supported = false,
> > + }, {
> > + .name = "RGB24 (BE)",
> > + .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
> > + .code = MEDIA_BUS_FMT_RGB888_2X12_BE,
> > + .colorspace = V4L2_COLORSPACE_SRGB,
> > + .depth = 24,
> > + .supported = false,
> > + }, {
> > + .name = "RGB32",
> > + .fourcc = V4L2_PIX_FMT_RGB32, /* argb */
> > + .code = MEDIA_BUS_FMT_ARGB8888_1X32,
> > + .colorspace = V4L2_COLORSPACE_SRGB,
> > + .depth = 32,
> > + .supported = false,
> > + }, {
> > + .name = "RAW8 BGGR",
> > + .fourcc = V4L2_PIX_FMT_SBGGR8,
> > + .code = MEDIA_BUS_FMT_SBGGR8_1X8,
> > + .colorspace = V4L2_COLORSPACE_SRGB,
> > + .depth = 8,
> > + .supported = false,
> > + }, {
> > + .name = "RAW8 GBRG",
> > + .fourcc = V4L2_PIX_FMT_SGBRG8,
> > + .code = MEDIA_BUS_FMT_SGBRG8_1X8,
> > + .colorspace = V4L2_COLORSPACE_SRGB,
> > + .depth = 8,
> > + .supported = false,
> > + }, {
> > + .name = "RAW8 GRBG",
> > + .fourcc = V4L2_PIX_FMT_SGRBG8,
> > + .code = MEDIA_BUS_FMT_SGRBG8_1X8,
> > + .colorspace = V4L2_COLORSPACE_SRGB,
> > + .depth = 8,
> > + .supported = false,
> > + }, {
> > + .name = "RAW8 RGGB",
> > + .fourcc = V4L2_PIX_FMT_SRGGB8,
> > + .code = MEDIA_BUS_FMT_SRGGB8_1X8,
> > + .colorspace = V4L2_COLORSPACE_SRGB,
> > + .depth = 8,
> > + .supported = false,
> > + }, {
> > + .name = "RAW10 BGGR",
> > + .fourcc = V4L2_PIX_FMT_SBGGR10,
> > + .code = MEDIA_BUS_FMT_SBGGR10_1X10,
> > + .colorspace = V4L2_COLORSPACE_SRGB,
> > + .depth = 16,
> > + .supported = false,
> > + }, {
> > + .name = "RAW10 GBRG",
> > + .fourcc = V4L2_PIX_FMT_SGBRG10,
> > + .code = MEDIA_BUS_FMT_SGBRG10_1X10,
> > + .colorspace = V4L2_COLORSPACE_SRGB,
> > + .depth = 16,
> > + .supported = false,
> > + }, {
> > + .name = "RAW10 GRBG",
> > + .fourcc = V4L2_PIX_FMT_SGRBG10,
> > + .code = MEDIA_BUS_FMT_SGRBG10_1X10,
> > + .colorspace = V4L2_COLORSPACE_SRGB,
> > + .depth = 16,
> > + .supported = false,
> > + }, {
> > + .name = "RAW10 RGGB",
> > + .fourcc = V4L2_PIX_FMT_SRGGB10,
> > + .code = MEDIA_BUS_FMT_SRGGB10_1X10,
> > + .colorspace = V4L2_COLORSPACE_SRGB,
> > + .depth = 16,
> > + .supported = false,
> > + }, {
> > + .name = "RAW12 BGGR",
> > + .fourcc = V4L2_PIX_FMT_SBGGR12,
> > + .code = MEDIA_BUS_FMT_SBGGR12_1X12,
> > + .colorspace = V4L2_COLORSPACE_SRGB,
> > + .depth = 16,
> > + .supported = false,
> > + }, {
> > + .name = "RAW12 GBRG",
> > + .fourcc = V4L2_PIX_FMT_SGBRG12,
> > + .code = MEDIA_BUS_FMT_SGBRG12_1X12,
> > + .colorspace = V4L2_COLORSPACE_SRGB,
> > + .depth = 16,
> > + .supported = false,
> > + }, {
> > + .name = "RAW12 GRBG",
> > + .fourcc = V4L2_PIX_FMT_SGRBG12,
> > + .code = MEDIA_BUS_FMT_SGRBG12_1X12,
> > + .colorspace = V4L2_COLORSPACE_SRGB,
> > + .depth = 16,
> > + .supported = false,
> > + }, {
> > + .name = "RAW12 RGGB",
> > + .fourcc = V4L2_PIX_FMT_SRGGB12,
> > + .code = MEDIA_BUS_FMT_SRGGB12_1X12,
> > + .colorspace = V4L2_COLORSPACE_SRGB,
> > + .depth = 16,
> > + .supported = false,
> > + },
> > +};
> > +
> > +static const struct cal_fmt *find_format_by_pix(u32 pixelformat)
> > +{
> > + const struct cal_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 const struct cal_fmt *find_format_by_code(u32 code)
> > +{
> > + const struct cal_fmt *fmt;
> > + unsigned int k;
> > +
> > + for (k = 0; k < ARRAY_SIZE(formats); k++) {
> > + fmt = &formats[k];
> > + if (fmt->code == code)
> > + return fmt;
> > + }
> > +
> > + return NULL;
> > +}
> > +
> > +/* buffer for one video frame */
> > +struct cal_buffer {
> > + /* common v4l buffer stuff -- must be first */
> > + struct vb2_buffer vb;
> > + struct list_head list;
> > + const struct cal_fmt *fmt;
> > +};
> > +
> > +struct cal_dmaqueue {
> > + struct list_head active;
> > +
> > + /* Counters to control fps rate */
> > + int frame;
> > + int ini_jiffies;
> > +};
> > +
> > +struct cm_data {
> > + void __iomem *base;
> > + struct resource *res;
> > +
> > + unsigned int camerrx_control;
> > +
> > + struct platform_device *pdev;
> > +};
> > +
> > +struct cc_data {
> > + void __iomem *base;
> > + struct resource *res;
> > +
> > + struct platform_device *pdev;
> > +};
> > +
> > +/*
> > + * there is one cal_dev structure in the driver, it is shared by
> > + * all instances.
> > + */
> > +struct cal_dev {
> > + int irq;
> > + void __iomem *base;
> > + struct resource *res;
> > + struct platform_device *pdev;
> > + struct v4l2_device v4l2_dev;
> > +
> > + struct cm_data *cm; /* Control Module handle */
> > +
> > + struct cal_ctx *ctx[CAL_NUM_CONTEXT];
> > +};
> > +
> > +/*
> > + * There is one cal_ctx structure for each camera core context.
> > + */
> > +struct cal_ctx {
> > + struct v4l2_device v4l2_dev;
> > + struct v4l2_ctrl_handler ctrl_handler;
> > + struct video_device vdev;
> > + struct v4l2_async_notifier notifier;
> > + struct v4l2_subdev *sensor;
> > + struct v4l2_of_endpoint endpoint;
> > +
> > + struct v4l2_async_subdev asd;
> > + struct v4l2_async_subdev *asd_list[1];
> > +
> > + struct v4l2_fh fh;
> > + struct cal_dev *dev;
> > + struct cc_data *cc;
> > +
> > + /* v4l2_ioctl mutex */
> > + struct mutex mutex;
> > + /* v4l2 buffers lock */
> > + spinlock_t slock;
> > +
> > + /* Several counters */
> > + unsigned long jiffies;
> > +
> > + struct vb2_alloc_ctx *alloc_ctx;
> > + struct cal_dmaqueue vidq;
> > +
> > + /* Input Number */
> > + int input;
> > +
> > + /* video capture */
> > + const struct cal_fmt *fmt;
> > + struct v4l2_fract timeperframe;
> > + unsigned int width, height;
> > + unsigned int field;
> > + unsigned int sequence;
> > + unsigned int pixelsize;
> > + unsigned int external_rate;
> > + struct vb2_queue vb_vidq;
> > + unsigned int seq_count;
> > + unsigned int csi2_port;
> > + unsigned int virtual_channel;
> > +
> > + /* Pointer pointing to current v4l2_buffer */
> > + struct cal_buffer *cur_frm;
> > + /* Pointer pointing to next v4l2_buffer */
> > + struct cal_buffer *next_frm;
> > +};
> > +
> > +static inline struct cal_ctx *notifier_to_ctx(struct v4l2_async_notifier *n)
> > +{
> > + return container_of(n, struct cal_ctx, notifier);
> > +}
> > +
> > +/* register field read/write helpers */
> > +static inline int get_field(u32 value, u32 mask, int shift)
> > +{
> > + return (value & (mask << shift)) >> shift;
> > +}
> > +
> > +static inline void write_field(u32 *valp, u32 field, u32 mask, int shift)
> > +{
> > + u32 val = *valp;
> > +
> > + val &= ~(mask << shift);
> > + val |= (field & mask) << shift;
> > + *valp = val;
> > +}
> > +
> > +static inline u32 cal_read(struct cal_dev *dev, int offset)
> > +{
> > + return ioread32(dev->base + offset);
> > +}
> > +
> > +static inline void cal_write(struct cal_dev *dev, int offset, u32 value)
> > +{
> > + iowrite32(value, dev->base + offset);
> > +}
> > +
> > +static inline int
> > +cal_read_field(struct cal_dev *dev, int offset, u32 mask, int shift)
> > +{
> > + return get_field(cal_read(dev, offset), mask, shift);
> > +}
> > +
> > +static inline void cal_write_field(struct cal_dev *dev, int offset, u32 field,
> > + u32 mask, int shift)
> > +{
> > + u32 val = cal_read(dev, offset);
> > +
> > + write_field(&val, field, mask, shift);
> > +
> > + cal_write(dev, offset, val);
> > +}
> > +
> > +/*
> > + * Control Module block access
> > + */
> > +static struct cm_data *cm_create(struct cal_dev *dev)
> > +{
> > + struct platform_device *pdev = dev->pdev;
> > + struct cm_data *cm;
> > +
> > + cal_dbg(3, dev, "cm_create\n");
> > +
> > + cm = devm_kzalloc(&pdev->dev, sizeof(*cm), GFP_KERNEL);
> > + if (!cm)
> > + return ERR_PTR(-ENOMEM);
> > +
> > + cm->res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> > + "camerrx_control");
> > + if (!cm->res)
> > + return ERR_PTR(-ENODEV);
> > +
> > + cal_dbg(1, dev, "ioresource %s at %x - %x\n",
> > + cm->res->name, cm->res->start, cm->res->end);
> > +
> > + cm->base = devm_ioremap_resource(&pdev->dev, cm->res);
> > + if (!cm->base) {
> > + cal_err(dev, "failed to ioremap\n");
> > + return ERR_PTR(-ENOMEM);
> > + }
> > +
> > + return cm;
> > +}
> > +
> > +static inline u32 cm_read(struct cm_data *dev, int offset)
> > +{
> > + return ioread32(dev->base + offset);
> > +}
> > +
> > +static inline void cm_write(struct cm_data *dev, int offset, u32 value)
> > +{
> > + iowrite32(value, dev->base + offset);
> > +}
> > +
> > +static inline void cm_write_field(struct cm_data *dev, int offset, u32 field,
> > + u32 mask, int shift)
> > +{
> > + u32 val = cm_read(dev, offset);
> > +
> > + write_field(&val, field, mask, shift);
> > +
> > + cm_write(dev, offset, val);
> > +}
> > +
> > +static void camerarx_phy_enable(struct cal_ctx *ctx)
> > +{
> > + u32 val;
> > +
> > + ctx_dbg(3, ctx, "%s\n", __func__);
> > +
> > + if (!ctx->dev->cm->base) {
> > + ctx_err(ctx, "cm not mapped\n");
> > + return;
> > + }
> > +
> > + val = cm_read(ctx->dev->cm, CM_CTRL_CORE_CAMERRX_CONTROL);
> > + if (ctx->csi2_port == 1) {
> > + write_field(&val, 1, CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_MASK,
> > + CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_SHIFT);
> > + write_field(&val, 0, CM_CAMERRX_CTRL_CSI0_CAMMODE_MASK,
> > + CM_CAMERRX_CTRL_CSI0_CAMMODE_SHIFT);
> > + /* enable all lanes by default */
> > + write_field(&val, 0xf, CM_CAMERRX_CTRL_CSI0_LANEENABLE_MASK,
> > + CM_CAMERRX_CTRL_CSI0_LANEENABLE_SHIFT);
> > + write_field(&val, 1, CM_CAMERRX_CTRL_CSI0_MODE_MASK,
> > + CM_CAMERRX_CTRL_CSI0_MODE_SHIFT);
> > + } else if (ctx->csi2_port == 2) {
> > + write_field(&val, 1, CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_MASK,
> > + CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_SHIFT);
> > + write_field(&val, 0, CM_CAMERRX_CTRL_CSI1_CAMMODE_MASK,
> > + CM_CAMERRX_CTRL_CSI0_CAMMODE_SHIFT);
> > + /* enable all lanes by default */
> > + write_field(&val, 0x3, CM_CAMERRX_CTRL_CSI1_LANEENABLE_MASK,
> > + CM_CAMERRX_CTRL_CSI0_LANEENABLE_SHIFT);
> > + write_field(&val, 1, CM_CAMERRX_CTRL_CSI1_MODE_MASK,
> > + CM_CAMERRX_CTRL_CSI0_MODE_SHIFT);
> > + }
> > + cm_write(ctx->dev->cm, CM_CTRL_CORE_CAMERRX_CONTROL, val);
> > +}
> > +
> > +static void camerarx_phy_disable(struct cal_ctx *ctx)
> > +{
> > + ctx_dbg(3, ctx, "%s\n", __func__);
> > +
> > + if (!ctx->dev->cm->base) {
> > + ctx_err(ctx, "cm not mapped\n");
> > + return;
> > + }
> > +
> > + if (ctx->csi2_port == 1)
> > + cm_write_field(ctx->dev->cm,
> > + CM_CTRL_CORE_CAMERRX_CONTROL,
> > + 0x0,
> > + CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_MASK,
> > + CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_SHIFT);
> > + else if (ctx->csi2_port == 2)
> > + cm_write_field(ctx->dev->cm,
> > + CM_CTRL_CORE_CAMERRX_CONTROL,
> > + 0x0,
> > + CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_MASK,
> > + CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_SHIFT);
> > +}
> > +
> > +/*
> > + * Camera Instance access block
> > + */
> > +static struct cc_data *cc_create(struct cal_dev *dev, unsigned int core)
> > +{
> > + struct platform_device *pdev = dev->pdev;
> > + struct cc_data *cc;
> > +
> > + cal_dbg(3, dev, "cc_create\n");
> > +
> > + cc = devm_kzalloc(&pdev->dev, sizeof(*cc), GFP_KERNEL);
> > + if (!cc)
> > + return ERR_PTR(-ENOMEM);
> > +
> > + cc->res = platform_get_resource_byname(pdev,
> > + IORESOURCE_MEM,
> > + (core == 0) ?
> > + "cal_rx_core0" :
> > + "cal_rx_core1");
> > + if (!cc->res) {
> > + cal_err(dev, "missing platform resources data\n");
> > + return ERR_PTR(-ENODEV);
> > + }
> > +
> > + cal_dbg(1, dev, "ioresource %s at %x - %x\n",
> > + cc->res->name, cc->res->start, cc->res->end);
> > +
> > + cc->base = devm_ioremap_resource(&pdev->dev, cc->res);
> > + if (!cc->base) {
> > + cal_err(dev, "failed to ioremap\n");
> > + return ERR_PTR(-ENOMEM);
> > + }
> > +
> > + return cc;
> > +}
> > +
> > +static inline u32 cc_read(struct cc_data *dev, int offset)
> > +{
> > + return ioread32(dev->base + offset);
> > +}
> > +
> > +static inline void cc_write(struct cc_data *dev, int offset, u32 value)
> > +{
> > + iowrite32(value, dev->base + offset);
> > +}
> > +
> > +/*
> > + * Get Revision and HW info
> > + */
> > +static void cal_get_hwinfo(struct cal_dev *dev)
> > +{
> > + u32 revision = 0;
> > + u32 hwinfo = 0;
> > +
> > + revision = cal_read(dev, CAL_HL_REVISION);
> > + cal_dbg(3, dev, "CAL_HL_REVISION = 0x%08x (expecting 0x40000200)\n",
> > + revision);
> > +
> > + hwinfo = cal_read(dev, CAL_HL_HWINFO);
> > + cal_dbg(3, dev, "CAL_HL_HWINFO = 0x%08x (expecting 0xA3C90469)\n",
> > + hwinfo);
> > +}
> > +
> > +/*
> > + * Soft-Reset the Main Cal module. Not sure if this is needed.
> > + */
> > +/*
> > +static void cal_top_reset(struct cal_dev *dev)
> > +{
> > + cal_write_field(dev,
> > + CAL_HL_SYSCONFIG,
> > + CAL_HL_SYSCONFIG_SOFTRESET_RESET,
> > + CAL_HL_SYSCONFIG_SOFTRESET_MASK,
> > + CAL_HL_SYSCONFIG_SOFTRESET_SHIFT);
> > +
> > + while(cal_read_field(dev,
> > + CAL_HL_SYSCONFIG,
> > + CAL_HL_SYSCONFIG_SOFTRESET_MASK,
> > + CAL_HL_SYSCONFIG_SOFTRESET_SHIFT) !=
> > + CAL_HL_SYSCONFIG_SOFTRESET_DONE);
> > +}
> > +*/
> > +
> > +static void cal_quickdump_regs(struct cal_dev *dev)
> > +{
> > + cal_info(dev, "CAL Registers @ 0x%08x:\n", dev->res->start);
> > + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
> > + dev->base, (dev->res->end - dev->res->start + 1), false);
> > +
> > + if (!dev->ctx[0]) {
> > + cal_info(dev, "CSI2 Core 0 Registers @ 0x%08x:\n",
> > + dev->ctx[0]->cc->res->start);
> > + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
> > + dev->ctx[0]->cc->base,
> > + (dev->ctx[0]->cc->res->end -
> > + dev->ctx[0]->cc->res->start + 1),
> > + false);
> > + }
> > +
> > + if (!dev->ctx[1]) {
> > + cal_info(dev, "CSI2 Core 1 Registers @ 0x%08x:\n",
> > + dev->ctx[1]->cc->res->start);
> > + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
> > + dev->ctx[1]->cc->base,
> > + (dev->ctx[1]->cc->res->end -
> > + dev->ctx[1]->cc->res->start + 1),
> > + false);
> > + }
> > +
> > + cal_info(dev, "CAMERRX_Control Registers @ 0x%08x:\n",
> > + dev->cm->res->start);
> > + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
> > + dev->cm->base,
> > + (dev->cm->res->end - dev->cm->res->start + 1), false);
> > +}
> > +
> > +/*
> > + * Enable the expected IRQ sources
> > + */
> > +static void enable_irqs(struct cal_ctx *ctx)
> > +{
> > + /* Enable IRQ_WDMA_END 0/1 */
> > + cal_write_field(ctx->dev,
> > + CAL_HL_IRQENABLE_SET(2),
> > + CAL_HL_IRQ_ENABLE,
> > + CAL_HL_IRQ_MASK(ctx->csi2_port),
> > + CAL_HL_IRQ_SHIFT(ctx->csi2_port));
> > + /* Enable IRQ_WDMA_START 0/1 */
> > + cal_write_field(ctx->dev,
> > + CAL_HL_IRQENABLE_SET(3),
> > + CAL_HL_IRQ_ENABLE,
> > + CAL_HL_IRQ_MASK(ctx->csi2_port),
> > + CAL_HL_IRQ_SHIFT(ctx->csi2_port));
> > + /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */
> > + cal_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0xFF000000);
> > +}
> > +
> > +static void disable_irqs(struct cal_ctx *ctx)
> > +{
> > + /* Disable IRQ_WDMA_END 0/1 */
> > + cal_write_field(ctx->dev,
> > + CAL_HL_IRQENABLE_CLR(2),
> > + CAL_HL_IRQ_CLEAR,
> > + CAL_HL_IRQ_MASK(ctx->csi2_port),
> > + CAL_HL_IRQ_SHIFT(ctx->csi2_port));
> > + /* Disable IRQ_WDMA_START 0/1 */
> > + cal_write_field(ctx->dev,
> > + CAL_HL_IRQENABLE_CLR(3),
> > + CAL_HL_IRQ_ENABLE,
> > + CAL_HL_IRQ_MASK(ctx->csi2_port),
> > + CAL_HL_IRQ_SHIFT(ctx->csi2_port));
> > + /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */
> > + cal_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0);
> > +}
> > +
> > +static void csi2_init(struct cal_ctx *ctx)
> > +{
> > + u32 val;
> > +
> > + ctx_dbg(3, ctx, "%s\n", __func__);
> > +
> > + val = cal_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port));
> > + write_field(&val, CAL_GEN_ENABLE,
> > + CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK,
> > + CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_SHIFT);
> > + write_field(&val, CAL_GEN_ENABLE,
> > + CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK,
> > + CAL_CSI2_TIMING_STOP_STATE_X16_IO1_SHIFT);
> > + write_field(&val, CAL_GEN_DISABLE,
> > + CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK,
> > + CAL_CSI2_TIMING_STOP_STATE_X4_IO1_SHIFT);
> > + write_field(&val, 407, CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK,
> > + CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT);
> > + cal_write(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), val);
> > + ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x\n", ctx->csi2_port,
> > + cal_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port)));
> > +
> > + val = cal_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port));
> > + write_field(&val, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL,
> > + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK,
> > + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_SHIFT);
> > + write_field(&val, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON,
> > + CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK,
> > + CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_SHIFT);
> > + cal_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val);
> > + while (cal_read_field(ctx->dev,
> > + CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port),
> > + CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK,
> > + CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_SHIFT) !=
> > + CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_ON)
> > + ;
> > + ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", ctx->csi2_port,
> > + cal_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)));
> > +
> > + val = cal_read(ctx->dev, CAL_CTRL);
> > + write_field(&val, CAL_CTRL_BURSTSIZE_BURST128,
> > + CAL_CTRL_BURSTSIZE_MASK, CAL_CTRL_BURSTSIZE_SHIFT);
> > + write_field(&val, 0xF,
> > + CAL_CTRL_TAGCNT_MASK, CAL_CTRL_TAGCNT_SHIFT);
> > + write_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED,
> > + CAL_CTRL_POSTED_WRITES_MASK, CAL_CTRL_POSTED_WRITES_SHIFT);
> > + write_field(&val, 0xFF,
> > + CAL_CTRL_MFLAGL_MASK, CAL_CTRL_MFLAGL_SHIFT);
> > + write_field(&val, 0xFF,
> > + CAL_CTRL_MFLAGH_MASK, CAL_CTRL_MFLAGH_SHIFT);
> > + cal_write(ctx->dev, CAL_CTRL, val);
> > + ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", cal_read(ctx->dev, CAL_CTRL));
> > +}
> > +
> > +static void csi2_lane_config(struct cal_ctx *ctx)
> > +{
> > + u32 val = cal_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port));
> > + u32 lane_shift = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT;
> > + u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK;
> > + u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK;
> > + struct v4l2_of_bus_mipi_csi2 *mipi_csi2 = &ctx->endpoint.bus.mipi_csi2;
> > + int lane;
> > +
> > + ctx_dbg(3, ctx, "%s\n", __func__);
> > +
> > + write_field(&val, mipi_csi2->clock_lane + 1,
> > + lane_mask, lane_shift);
> > + write_field(&val, mipi_csi2->lane_polarities[0],
> > + polarity_mask, lane_shift + 3);
> > + for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) {
> > + /*
> > + * Every lane are one nibble apart starting with the
> > + * clock followed by the data lanes so shift incements by 4.
> > + */
> > + lane_shift += 4;
> > + write_field(&val, mipi_csi2->data_lanes[lane] + 1,
> > + lane_mask, lane_shift);
> > + write_field(&val, mipi_csi2->lane_polarities[lane + 1],
> > + polarity_mask, lane_shift + 3);
> > + }
> > + cal_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val);
> > + ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n",
> > + ctx->csi2_port, val);
> > +}
> > +
> > +static void csi2_ppi_enable(struct cal_ctx *ctx)
> > +{
> > + ctx_dbg(3, ctx, "%s\n", __func__);
> > +
> > + cal_write_field(ctx->dev,
> > + CAL_CSI2_PPI_CTRL(ctx->csi2_port),
> > + CAL_GEN_ENABLE,
> > + CAL_CSI2_PPI_CTRL_IF_EN_MASK,
> > + CAL_CSI2_PPI_CTRL_IF_EN_SHIFT);
> > +}
> > +
> > +static void csi2_ppi_disable(struct cal_ctx *ctx)
> > +{
> > + ctx_dbg(3, ctx, "%s\n", __func__);
> > +
> > + cal_write_field(ctx->dev,
> > + CAL_CSI2_PPI_CTRL(ctx->csi2_port),
> > + CAL_GEN_DISABLE,
> > + CAL_CSI2_PPI_CTRL_IF_EN_MASK,
> > + CAL_CSI2_PPI_CTRL_IF_EN_SHIFT);
> > +}
> > +
> > +static void csi2_ctx_config(struct cal_ctx *ctx)
> > +{
> > + u32 val;
> > +
> > + ctx_dbg(3, ctx, "%s\n", __func__);
> > +
> > + val = cal_read(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port));
> > + write_field(&val, ctx->csi2_port, CAL_CSI2_CTX_CPORT_MASK,
> > + CAL_CSI2_CTX_CPORT_SHIFT);
> > + /* DT type: MIPI CSI-2 Specs
> > + 1: All DT filter is disabled
> > + 0x24: RGB888 1 pixel = 3 bytes
> > + 0x2B: RAW10 4 pixels = 5 bytes
> > + 0x2A: RAW8 1 pixel = 1 byte
> > + 0x1E: YUV422 2 pixels = 4 bytes
> > + */
> > + write_field(&val, 0x1, CAL_CSI2_CTX_DT_MASK,
> > + CAL_CSI2_CTX_DT_SHIFT);
> > + /* Virtual Channel from the CSI2 sensor usually 0! */
> > + write_field(&val, ctx->virtual_channel, CAL_CSI2_CTX_VC_MASK,
> > + CAL_CSI2_CTX_VC_SHIFT);
> > + /* NUM_LINES_PER_FRAME => 0 means we don't know */
> > + write_field(&val, 0, CAL_CSI2_CTX_LINES_MASK,
> > + CAL_CSI2_CTX_LINES_SHIFT);
> > + write_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK,
> > + CAL_CSI2_CTX_ATT_SHIFT);
> > + cal_write_field(ctx->dev,
> > + CAL_CSI2_CTX0(ctx->csi2_port),
> > + CAL_CSI2_CTX_PACK_MODE_LINE,
> > + CAL_CSI2_CTX_PACK_MODE_MASK,
> > + CAL_CSI2_CTX_PACK_MODE_SHIFT);
> > + write_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE,
> > + CAL_CSI2_CTX_PACK_MODE_MASK, CAL_CSI2_CTX_PACK_MODE_SHIFT);
> > + cal_write(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port), val);
> > + ctx_dbg(3, ctx, "CAL_CSI2_CTX0(%d) = 0x%08x\n", ctx->csi2_port,
> > + cal_read(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port)));
> > +}
> > +
> > +static void pix_proc_config(struct cal_ctx *ctx)
> > +{
> > + u32 val;
> > +
> > + ctx_dbg(3, ctx, "%s\n", __func__);
> > +
> > + val = cal_read(ctx->dev, CAL_PIX_PROC(ctx->csi2_port));
> > + write_field(&val, CAL_PIX_PROC_EXTRACT_B8, CAL_PIX_PROC_EXTRACT_MASK,
> > + CAL_PIX_PROC_EXTRACT_SHIFT);
> > + write_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK,
> > + CAL_PIX_PROC_DPCMD_SHIFT);
> > + write_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK,
> > + CAL_PIX_PROC_DPCME_SHIFT);
> > + write_field(&val, CAL_PIX_PROC_PACK_B8, CAL_PIX_PROC_PACK_MASK,
> > + CAL_PIX_PROC_PACK_SHIFT);
> > + write_field(&val, ctx->csi2_port, CAL_PIX_PROC_CPORT_MASK,
> > + CAL_PIX_PROC_CPORT_SHIFT);
> > + cal_write_field(ctx->dev,
> > + CAL_PIX_PROC(ctx->csi2_port),
> > + CAL_GEN_ENABLE,
> > + CAL_PIX_PROC_EN_MASK,
> > + CAL_PIX_PROC_EN_SHIFT);
> > + write_field(&val, CAL_GEN_ENABLE, CAL_PIX_PROC_EN_MASK,
> > + CAL_PIX_PROC_EN_SHIFT);
> > + cal_write(ctx->dev, CAL_PIX_PROC(ctx->csi2_port), val);
> > + ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->csi2_port,
> > + cal_read(ctx->dev, CAL_PIX_PROC(ctx->csi2_port)));
> > +}
> > +
> > +#define bytes_per_line(pixel, bpp) (ALIGN(pixel * bpp, 16))
> > +
> > +static void cal_wr_dma_config(struct cal_ctx *ctx,
> > + unsigned int width)
> > +{
> > + u32 val;
> > +
> > + ctx_dbg(3, ctx, "%s\n", __func__);
> > +
> > + val = cal_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port));
> > + write_field(&val, ctx->csi2_port, CAL_WR_DMA_CTRL_CPORT_MASK,
> > + CAL_WR_DMA_CTRL_CPORT_SHIFT);
> > + write_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT,
> > + CAL_WR_DMA_CTRL_DTAG_MASK, CAL_WR_DMA_CTRL_DTAG_SHIFT);
> > + write_field(&val, CAL_WR_DMA_CTRL_MODE_CONST,
> > + CAL_WR_DMA_CTRL_MODE_MASK, CAL_WR_DMA_CTRL_MODE_SHIFT);
> > + write_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR,
> > + CAL_WR_DMA_CTRL_PATTERN_MASK,
> > + CAL_WR_DMA_CTRL_PATTERN_SHIFT);
> > + write_field(&val, CAL_GEN_ENABLE,
> > + CAL_WR_DMA_CTRL_STALL_RD_MASK,
> > + CAL_WR_DMA_CTRL_STALL_RD_SHIFT);
> > + cal_write(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port), val);
> > + ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->csi2_port,
> > + cal_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port)));
> > +
> > + /*
> > + * width/16 not sure but giving it a whirl.
> > + * zero does not work right
> > + */
> > + cal_write_field(ctx->dev,
> > + CAL_WR_DMA_OFST(ctx->csi2_port),
> > + (width / 16),
> > + CAL_WR_DMA_OFST_MASK,
> > + CAL_WR_DMA_OFST_SHIFT);
> > + ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->csi2_port,
> > + cal_read(ctx->dev, CAL_WR_DMA_OFST(ctx->csi2_port)));
> > +
> > + val = cal_read(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port));
> > + /* 64 bit word means no skipping */
> > + write_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK,
> > + CAL_WR_DMA_XSIZE_XSKIP_SHIFT);
> > + /*
> > + * (width*8)/64 this should be size of an entire line
> > + * in 64bit word but 0 means all data until the end
> > + * is detected automagically
> > + */
> > + write_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK,
> > + CAL_WR_DMA_XSIZE_SHIFT);
> > + cal_write(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port), val);
> > + ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->csi2_port,
> > + cal_read(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port)));
> > +}
> > +
> > +static void cal_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr)
> > +{
> > + cal_write(ctx->dev, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr);
> > +/* ctx_dbg(3, ctx, "CAL_WR_DMA_ADDR(%d) = 0x%08x\n", ctx->csi2_port,
> > + cal_read(ctx->dev,CAL_WR_DMA_ADDR(ctx->csi2_port))); */
> > +}
> > +
> > +/*
> > + * TCLK values are OK at their reset values
> > + */
> > +#define TCLK_TERM 0
> > +#define TCLK_MISS 1
> > +#define TCLK_SETTLE 14
> > +#define THS_SETTLE 15
> > +
> > +static void csi2_phy_config(struct cal_ctx *ctx)
> > +{
> > + unsigned int reg0, reg1;
> > + unsigned int ths_term, ths_settle;
> > +
> > + ctx_dbg(3, ctx, "%s\n", __func__);
> > +
> > +#ifdef LEGACY_CSI2PHY_FORMULA
> > + {
> > + int csi2_ddrclk_khz;
> > +
> > + csi2_ddrclk_khz = ctx->external_rate / 1000
> > + / (2 * ctx->endpoint.bus.mipi_csi2.num_data_lanes)
> > + * ctx->fmt->depth;
> > +
> > + /*
> > + * THS_TERM: Programmed value = ceil(12.5 ns/DDRClk period) - 1.
> > + * THS_SETTLE: Programmed value = ceil(90 ns/DDRClk period) + 3.
> > + */
> > + ths_term = DIV_ROUND_UP(25 * csi2_ddrclk_khz, 2000000) - 1;
> > + ths_settle = DIV_ROUND_UP(90 * csi2_ddrclk_khz, 1000000) + 3;
> > + }
> > +#else
> > + {
> > + unsigned int ddrclkperiod_us;
> > +
> > + /*
> > + * THS_TERM: Programmed value = floor(20 ns/DDRClk period) - 2.
> > + */
> > + ddrclkperiod_us = ctx->external_rate / 2000000;
> > + ddrclkperiod_us = 1000000 / ddrclkperiod_us;
> > + ctx_dbg(1, ctx, "ddrclkperiod_us: %d\n", ddrclkperiod_us);
> > +
> > + ths_term = 20000 / ddrclkperiod_us;
> > + ths_term = (ths_term >= 2) ? ths_term - 2 : ths_term;
> > + ctx_dbg(1, ctx, "ths_term: %d (0x%02x)\n", ths_term, ths_term);
> > +
> > + /*
> > + * THS_SETTLE: Programmed value = floor(176.3 ns/CtrlClk period) - 1.
> > + * Since CtrlClk is fixed at 96Mhz then we get
> > + * ths_settle = floor(176.3 / 10.416) - 1 = 15
> > + * If we ever switch to a dynamic clock then this code might be useful
> > + *
> > + * unsigned int ctrlclkperiod_us;
> > + * ctrlclkperiod_us = 96000000 / 1000000;
> > + * ctrlclkperiod_us = 1000000 / ctrlclkperiod_us;
> > + * ctx_dbg(1, ctx, "ctrlclkperiod_us: %d\n", ctrlclkperiod_us);
> > +
> > + * ths_settle = 176300 / ctrlclkperiod_us;
> > + * ths_settle = (ths_settle > 1) ? ths_settle - 1 : ths_settle;
> > + */
> > +
> > + ths_settle = THS_SETTLE;
> > + ctx_dbg(1, ctx, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle);
> > + }
> > +#endif
> > + reg0 = cc_read(ctx->cc, CAL_CSI2_PHY_REG0);
> > + write_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE,
> > + CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK,
> > + CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_SHIFT);
> > + write_field(®0, ths_term,
> > + CAL_CSI2_PHY_REG0_THS_TERM_MASK,
> > + CAL_CSI2_PHY_REG0_THS_TERM_SHIFT);
> > + write_field(®0, ths_settle,
> > + CAL_CSI2_PHY_REG0_THS_SETTLE_MASK,
> > + CAL_CSI2_PHY_REG0_THS_SETTLE_SHIFT);
> > +
> > + ctx_dbg(1, ctx, "CSI2_%d_REG0 = 0x%08x\n", (ctx->csi2_port - 1), reg0);
> > + cc_write(ctx->cc, CAL_CSI2_PHY_REG0, reg0);
> > +
> > + reg1 = cc_read(ctx->cc, CAL_CSI2_PHY_REG1);
> > + write_field(®1, TCLK_TERM,
> > + CAL_CSI2_PHY_REG1_TCLK_TERM_MASK,
> > + CAL_CSI2_PHY_REG1_TCLK_TERM_SHIFT);
> > + write_field(®1, 0xb8,
> > + CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK,
> > + CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_SHIFT);
> > + write_field(®1, TCLK_MISS,
> > + CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK,
> > + CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_SHIFT);
> > + write_field(®1, TCLK_SETTLE,
> > + CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK,
> > + CAL_CSI2_PHY_REG1_TCLK_SETTLE_SHIFT);
> > +
> > + ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x\n", (ctx->csi2_port - 1), reg1);
> > + cc_write(ctx->cc, CAL_CSI2_PHY_REG1, reg1);
> > +}
> > +
> > +static int cal_get_external_info(struct cal_ctx *ctx)
> > +{
> > + struct v4l2_ext_control ctrl_ext;
> > + struct v4l2_ext_controls ctrls_ext;
> > + int ret;
> > +
> > + ctx_dbg(3, ctx, "%s\n", __func__);
> > +
> > + memset(&ctrls_ext, 0, sizeof(ctrls_ext));
> > + memset(&ctrl_ext, 0, sizeof(ctrl_ext));
> > +
> > + ctrl_ext.id = V4L2_CID_PIXEL_RATE;
> > +
> > + ctrls_ext.count = 1;
> > + ctrls_ext.controls = &ctrl_ext;
> > +
> > + ret = v4l2_g_ext_ctrls(&ctx->ctrl_handler, &ctrls_ext);
>
> Use v4l2_ctrl_g_ctrl_int64() instead: much simpler. You do need to store the
> v4l2_ctrl pointer for the pixel rate control when you create it, but that's no
> problem. (or use v4l2_ctrl_find, but I prefer to just cache the pointer).
Hmm, that's how I had it in our local 3.14 branch originally but I kept
getting 0 instead of the actual pixel rate.
I'll revert it back and test again against linux-media/master.
>
> > + if (ret < 0) {
> > + ctx_err(ctx, "no pixel rate control in subdev: %s\n",
> > + ctx->sensor->name);
> > + return -EPIPE;
> > + }
> > +
> > + ctx->external_rate = ctrl_ext.value64;
> > + ctx_dbg(3, ctx, "sensor Pixel Rate: %d\n", ctx->external_rate);
> > +
> > + return 0;
> > +}
> > +
> > +static inline void cal_schedule_next_buffer(struct cal_ctx *ctx)
> > +{
> > + struct cal_dmaqueue *dma_q = &ctx->vidq;
> > + struct cal_buffer *buf;
> > + unsigned long addr;
> > +
> > + buf = list_entry(dma_q->active.next, struct cal_buffer, list);
> > + ctx->next_frm = buf;
> > + list_del(&buf->list);
> > +
> > + addr = vb2_dma_contig_plane_dma_addr(&buf->vb, 0);
> > + cal_wr_dma_addr(ctx, addr);
> > +}
> > +
> > +static inline void cal_process_buffer_complete(struct cal_ctx *ctx)
> > +{
> > + v4l2_get_timestamp(&ctx->cur_frm->vb.v4l2_buf.timestamp);
> > + ctx->cur_frm->vb.v4l2_buf.field = ctx->field;
> > + ctx->cur_frm->vb.v4l2_buf.sequence = ctx->sequence++;
> > +
> > + vb2_buffer_done(&ctx->cur_frm->vb, VB2_BUF_STATE_DONE);
> > + ctx->cur_frm = ctx->next_frm;
> > +}
> > +
> > +#define isvcirqset(irq, vc, ff) (irq & \
> > + (CAL_CSI2_VC_IRQENABLE_ ##ff ##_IRQ_##vc ##_MASK << \
> > + CAL_CSI2_VC_IRQENABLE_ ##ff ##_IRQ_##vc ##_SHIFT))
> > +
> > +#define isportirqset(irq, port) (irq & \
> > + (CAL_HL_IRQ_MASK(port) << CAL_HL_IRQ_SHIFT(port)))
> > +
> > +static irqreturn_t cal_irq(int irq_cal, void *data)
> > +{
> > + struct cal_dev *dev = (struct cal_dev *)data;
> > + struct cal_ctx *ctx;
> > + struct cal_dmaqueue *dma_q;
> > + u32 irqst2, irqst3;
> > +
> > + /* Check which DMA just finished */
> > + irqst2 = cal_read(dev, CAL_HL_IRQSTATUS(2));
> > + if (irqst2) {
> > + /* Clear Interrupt status */
> > + cal_write(dev, CAL_HL_IRQSTATUS(2), irqst2);
> > +
> > + /* Need to check both port */
> > + if (isportirqset(irqst2, 1)) {
> > + ctx = dev->ctx[0];
> > +
> > + if (ctx->cur_frm != ctx->next_frm)
> > + cal_process_buffer_complete(ctx);
> > + }
> > +
> > + if (isportirqset(irqst2, 2)) {
> > + ctx = dev->ctx[1];
> > +
> > + if (ctx->cur_frm != ctx->next_frm)
> > + cal_process_buffer_complete(ctx);
> > + }
> > + }
> > +
> > + /* Check which DMA just started */
> > + irqst3 = cal_read(dev, CAL_HL_IRQSTATUS(3));
> > + if (irqst3) {
> > + /* Clear Interrupt status */
> > + cal_write(dev, CAL_HL_IRQSTATUS(3), irqst3);
> > +
> > + /* Need to check both port */
> > + if (isportirqset(irqst3, 1)) {
> > + ctx = dev->ctx[0];
> > + dma_q = &ctx->vidq;
> > +
> > + spin_lock(&ctx->slock);
> > + if (!list_empty(&dma_q->active) &&
> > + ctx->cur_frm == ctx->next_frm)
> > + cal_schedule_next_buffer(ctx);
> > + spin_unlock(&ctx->slock);
> > + }
> > +
> > + if (isportirqset(irqst3, 2)) {
> > + ctx = dev->ctx[1];
> > + dma_q = &ctx->vidq;
> > +
> > + spin_lock(&ctx->slock);
> > + if (!list_empty(&dma_q->active) &&
> > + ctx->cur_frm == ctx->next_frm)
> > + cal_schedule_next_buffer(ctx);
> > + spin_unlock(&ctx->slock);
> > + }
> > + }
> > +
> > + return IRQ_HANDLED;
> > +}
> > +
> > +/*
> > + * video ioctls
> > + */
> > +static int cal_querycap(struct file *file, void *priv,
> > + struct v4l2_capability *cap)
> > +{
> > + struct cal_ctx *ctx = video_drvdata(file);
> > +
> > + strcpy(cap->driver, CAL_MODULE_NAME);
> > + strcpy(cap->card, CAL_MODULE_NAME);
> > + snprintf(cap->bus_info, sizeof(cap->bus_info),
> > + "platform:%s", ctx->v4l2_dev.name);
> > + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
> > + V4L2_CAP_READWRITE;
> > + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
> > + return 0;
> > +}
> > +
> > +static int cal_enum_fmt_vid_cap(struct file *file, void *priv,
> > + struct v4l2_fmtdesc *f)
> > +{
> > + const struct cal_fmt *fmt = NULL;
> > + u32 k;
> > +
> > + if (f->index >= ARRAY_SIZE(formats))
> > + return -EINVAL;
> > +
> > + for (k = 0; k < ARRAY_SIZE(formats); k++) {
> > + if ((formats[k].index == f->index) &&
> > + (formats[k].supported)) {
> > + fmt = &formats[k];
> > + break;
> > + }
>
> This won't work. I am actually surprised that v4l2-compliance didn't complain
> about this. If one of the formats isn't supported, then the index will get out of sync.
Right, the formats[].index is consistent but as you spotted it only works if only
one port is configured....
>
> Also, formats is a global array, so setting the 'supported' flag when there are
> two instances of this driver (one for each CSI port, right?) is not a good idea
> since each sensor can have different supported formats.
>
> What you need to do is to store which formats are supported in cal_ctx (a bitarray
> would work), and store the maximum number of supported formats as well.
>
> Then you can use that information to implement this function correctly.
I'll fix that.
>
> > + }
> > + if (!fmt)
> > + return -EINVAL;
> > +
> > + strlcpy(f->description, fmt->name, sizeof(f->description));
>
> Drop description: the v4l2 core will set that.
Will do.
>
> > + f->pixelformat = fmt->fourcc;
> > + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> > + return 0;
> > +}
> > +
> > +static int __subdev_get_format(struct cal_ctx *ctx,
> > + struct v4l2_mbus_framefmt *fmt)
> > +{
> > + struct v4l2_subdev_format sd_fmt;
> > + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
> > + int ret;
> > +
> > + ctx_dbg(2, ctx, "%s\n", __func__);
> > +
> > + if (!ctx->sensor)
> > + return -EINVAL;
> > +
> > + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> > + sd_fmt.pad = 0;
> > +
> > + ret = v4l2_subdev_call(ctx->sensor, pad, get_fmt, NULL, &sd_fmt);
> > + if (ret)
> > + return ret;
> > +
> > + *fmt = *mbus_fmt;
> > +
> > + ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__,
> > + fmt->width, fmt->height, fmt->code);
> > +
> > + return 0;
> > +}
> > +
> > +static int __subdev_set_format(struct cal_ctx *ctx,
> > + struct v4l2_mbus_framefmt *fmt)
> > +{
> > + struct v4l2_subdev_format sd_fmt;
> > + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
> > + int ret;
> > +
> > + ctx_dbg(2, ctx, "%s\n", __func__);
> > +
> > + if (!ctx->sensor)
> > + return -EINVAL;
> > +
> > + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> > + sd_fmt.pad = 0;
> > + *mbus_fmt = *fmt;
> > +
> > + ret = v4l2_subdev_call(ctx->sensor, pad, set_fmt, NULL, &sd_fmt);
> > + if (ret)
> > + return ret;
> > +
> > + ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__,
> > + fmt->width, fmt->height, fmt->code);
> > +
> > + return 0;
> > +}
> > +
> > +static int cal_g_fmt_vid_cap(struct file *file, void *priv,
> > + struct v4l2_format *f)
> > +{
> > + struct cal_ctx *ctx = video_drvdata(file);
> > + const struct cal_fmt *fmt;
> > + struct v4l2_mbus_framefmt mbus_fmt;
> > + int ret;
> > +
> > + ret = __subdev_get_format(ctx, &mbus_fmt);
> > + if (ret)
> > + return ret;
> > +
>
> I recommend you use v4l2_fill_pix_format() here (see include/media/v4l2-mediabus.h).
> This fills in the colorspace fields from the sensor, among others.
>
> > + fmt = find_format_by_code(mbus_fmt.code);
> > + if (!fmt) {
> > + ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n",
> > + mbus_fmt.code);
> > + /* code not found, use a working default */
> > + fmt = find_format_by_code(MEDIA_BUS_FMT_YUYV8_2X8);
> > + mbus_fmt.code = fmt->code;
> > + mbus_fmt.colorspace = fmt->colorspace;
> > + mbus_fmt.width = 1920;
> > + mbus_fmt.height = 1080;
> > + mbus_fmt.field = V4L2_FIELD_NONE;
> > + }
> > +
> > + if (ctx->fmt != fmt) {
> > + /* looks like current format has changed, update local */
> > + ctx->fmt = fmt;
> > + ctx->width = mbus_fmt.width;
> > + ctx->height = mbus_fmt.height;
> > + ctx->field = mbus_fmt.field;
> > + ctx->pixelsize = ctx->fmt->depth >> 3;
> > + }
> > +
> > + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> > + f->fmt.pix.width = ctx->width;
> > + f->fmt.pix.height = ctx->height;
> > + f->fmt.pix.field = ctx->field;
> > + f->fmt.pix.pixelformat = ctx->fmt->fourcc;
> > + f->fmt.pix.colorspace = ctx->fmt->colorspace;
I guess in your comment above you mean I would replace the above block
with a v4l2_fill_pix_format call?
> > + f->fmt.pix.bytesperline =
> > + (f->fmt.pix.width * ctx->fmt->depth) >> 3;
> > + f->fmt.pix.sizeimage =
> > + f->fmt.pix.height * f->fmt.pix.bytesperline;
> > + return 0;
> > +}
> > +
> > +static int cal_try_fmt_vid_cap(struct file *file, void *priv,
> > + struct v4l2_format *f)
> > +{
> > + struct cal_ctx *ctx = video_drvdata(file);
> > + const struct cal_fmt *fmt;
> > + struct v4l2_fmtdesc fmt_desc;
> > +
> > + ctx_dbg(2, ctx, "%s\n", __func__);
> > +
> > + fmt = find_format_by_pix(f->fmt.pix.pixelformat);
> > + if (!fmt) {
> > + ctx_dbg(3, ctx, "Fourcc format (0x%08x) not found.\n",
> > + f->fmt.pix.pixelformat);
> > +
> > + /* Just get the first one enumerated */
> > + fmt_desc.index = 0;
> > + if (cal_enum_fmt_vid_cap(file, priv, &fmt_desc)) {
> > + ctx_dbg(3, ctx,
> > + "no default fmt found , this should not happen.\n");
> > + fmt = find_format_by_code(MEDIA_BUS_FMT_YUYV8_2X8);
> > + } else {
> > + fmt = find_format_by_pix(fmt_desc.pixelformat);
> > + }
> > + f->fmt.pix.pixelformat = fmt->fourcc;
> > + }
> > +
> > + f->fmt.pix.field = ctx->field;
> > + v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 2,
> > + &f->fmt.pix.height, 32, MAX_HEIGHT, 0, 0);
> > + f->fmt.pix.bytesperline =
> > + (f->fmt.pix.width * fmt->depth) >> 3;
> > + f->fmt.pix.sizeimage =
> > + f->fmt.pix.height * f->fmt.pix.bytesperline;
> > + f->fmt.pix.colorspace = fmt->colorspace;
> > + f->fmt.pix.priv = 0;
> > + return 0;
> > +}
> > +
> > +static int cal_s_fmt_vid_cap(struct file *file, void *priv,
> > + struct v4l2_format *f)
> > +{
> > + struct cal_ctx *ctx = video_drvdata(file);
> > + struct vb2_queue *q = &ctx->vb_vidq;
> > + const struct cal_fmt *fmt;
> > + struct v4l2_mbus_framefmt mbus_fmt;
> > + int ret;
> > +
> > + ctx_dbg(2, ctx, "%s\n", __func__);
> > +
> > + if (vb2_is_busy(q)) {
> > + ctx_dbg(3, ctx, "%s device busy\n", __func__);
> > + return -EBUSY;
> > + }
> > +
> > + ret = cal_try_fmt_vid_cap(file, priv, f);
> > + if (ret < 0)
> > + return ret;
> > +
> > + fmt = find_format_by_pix(f->fmt.pix.pixelformat);
> > +
> > + v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code);
> > +
> > + ret = __subdev_set_format(ctx, &mbus_fmt);
> > + if (ret)
> > + return ret;
> > +
> > + /* Just double check nothing has gone wrong */
> > + if (mbus_fmt.code != fmt->code) {
> > + ctx_dbg(3, ctx,
> > + "%s subdev changed format on us, this should not happen\n",
> > + __func__);
> > + return -EINVAL;
> > + }
> > +
> > + ctx->fmt = fmt;
> > + ctx->pixelsize = ctx->fmt->depth >> 3;
> > + ctx->field = f->fmt.pix.field;
> > + ctx->width = f->fmt.pix.width;
> > + ctx->height = f->fmt.pix.height;
> > +
> > + return 0;
> > +}
> > +
> > +static int cal_enum_framesizes(struct file *file, void *fh,
> > + struct v4l2_frmsizeenum *fsize)
> > +{
> > + struct cal_ctx *ctx = video_drvdata(file);
> > + const struct cal_fmt *fmt;
> > + struct v4l2_subdev_frame_size_enum fse;
> > + int ret;
> > +
> > + ctx_dbg(2, ctx, "%s\n", __func__);
> > +
> > + /* check for valid format */
> > + fmt = find_format_by_pix(fsize->pixel_format);
> > + if (!fmt) {
> > + ctx_dbg(3, ctx, "Invalid pixel code: %x\n",
> > + fsize->pixel_format);
> > + return -EINVAL;
> > + }
> > +
> > + fse.index = fsize->index;
> > + fse.pad = 0;
> > + fse.code = fmt->code;
> > +
> > + ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size, NULL, &fse);
> > + if (ret)
> > + return -EINVAL;
> > +
> > + ctx_dbg(1, ctx, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n",
> > + __func__, fse.index, fse.code, fse.min_width, fse.max_width,
> > + fse.min_height, fse.max_height);
> > +
> > + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
> > + fsize->discrete.width = fse.max_width;
> > + fsize->discrete.height = fse.max_height;
> > +
> > + return 0;
> > +}
> > +
> > +static int cal_enum_input(struct file *file, void *priv,
> > + struct v4l2_input *inp)
> > +{
> > + if (inp->index >= CAL_NUM_INPUT)
> > + return -EINVAL;
> > +
> > + inp->type = V4L2_INPUT_TYPE_CAMERA;
> > + sprintf(inp->name, "Camera %u", inp->index);
> > + return 0;
> > +}
> > +
> > +static int cal_g_input(struct file *file, void *priv, unsigned int *i)
> > +{
> > + struct cal_ctx *ctx = video_drvdata(file);
> > +
> > + *i = ctx->input;
> > + return 0;
> > +}
> > +
> > +static int cal_s_input(struct file *file, void *priv, unsigned int i)
> > +{
> > + struct cal_ctx *ctx = video_drvdata(file);
> > +
> > + if (i >= CAL_NUM_INPUT)
> > + return -EINVAL;
> > +
> > + if (i == ctx->input)
> > + return 0;
>
> I would drop this 'if'. It doesn't really do anything useful.
Right.
>
> > +
> > + ctx->input = i;
> > + return 0;
> > +}
> > +
> > +/* timeperframe is arbitrary and continuous */
> > +static int cal_enum_frameintervals(struct file *file, void *priv,
> > + struct v4l2_frmivalenum *fival)
> > +{
> > + struct cal_ctx *ctx = video_drvdata(file);
> > + const struct cal_fmt *fmt;
> > + struct v4l2_subdev_frame_size_enum fse;
> > + int ret;
> > +
> > + if (fival->index)
> > + return -EINVAL;
> > +
> > + fmt = find_format_by_pix(fival->pixel_format);
> > + if (!fmt)
> > + return -EINVAL;
> > +
> > + /* check for valid width/height */
> > + ret = 0;
> > + fse.pad = 0;
> > + fse.code = fmt->code;
> > + fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> > + for (fse.index = 0; ; fse.index++) {
> > + ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size,
> > + NULL, &fse);
> > + if (ret)
> > + return -EINVAL;
> > +
> > + if ((fival->width == fse.max_width) &&
> > + (fival->height == fse.max_height))
> > + break;
> > + else if ((fival->width >= fse.min_width) &&
> > + (fival->width <= fse.max_width) &&
> > + (fival->height >= fse.min_height) &&
> > + (fival->height <= fse.max_height))
> > + break;
> > +
> > + return -EINVAL;
> > + }
> > +
> > + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
> > + fival->discrete.numerator = 1;
> > + fival->discrete.denominator = 30;
> > +
> > + return 0;
> > +}
> > +
> > +/*
> > + * Videobuf operations
> > + */
> > +static int cal_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
> > + unsigned int *nbuffers, unsigned int *nplanes,
> > + unsigned int sizes[], void *alloc_ctxs[])
> > +{
> > + struct cal_ctx *ctx = vb2_get_drv_priv(vq);
> > + unsigned long size;
> > +
> > + size = ctx->width * ctx->height * ctx->pixelsize;
> > + if (fmt) {
> > + if (fmt->fmt.pix.sizeimage < size)
> > + return -EINVAL;
> > + size = fmt->fmt.pix.sizeimage;
> > + /* check against insane over 8K resolution buffers */
> > + if (size > 7680 * 4320 * ctx->pixelsize)
> > + return -EINVAL;
> > + }
> > +
> > + *nplanes = 1;
> > + sizes[0] = size;
> > + alloc_ctxs[0] = ctx->alloc_ctx;
> > +
> > + ctx_dbg(3, ctx, "nbuffers=%d, size=%ld\n", *nbuffers, size);
> > +
> > + return 0;
> > +}
> > +
> > +static int cal_buffer_prepare(struct vb2_buffer *vb)
> > +{
> > + struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> > + struct cal_buffer *buf = container_of(vb, struct cal_buffer, vb);
> > + unsigned long size;
> > +
> > + BUG_ON(NULL == ctx->fmt);
> > +
> > + size = ctx->width * ctx->height * ctx->pixelsize;
> > + if (vb2_plane_size(vb, 0) < size) {
> > + ctx_err(ctx,
> > + "data will not fit into plane (%lu < %lu)\n",
> > + vb2_plane_size(vb, 0), size);
> > + return -EINVAL;
> > + }
> > +
> > + vb2_set_plane_payload(&buf->vb, 0, size);
> > + return 0;
> > +}
> > +
> > +static void cal_buffer_queue(struct vb2_buffer *vb)
> > +{
> > + struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> > + struct cal_buffer *buf = container_of(vb, struct cal_buffer, vb);
> > + struct cal_dmaqueue *vidq = &ctx->vidq;
> > + unsigned long flags = 0;
> > +
> > + /* recheck locking */
> > + spin_lock_irqsave(&ctx->slock, flags);
> > + list_add_tail(&buf->list, &vidq->active);
> > + spin_unlock_irqrestore(&ctx->slock, flags);
> > +}
> > +
> > +static int cal_start_streaming(struct vb2_queue *vq, unsigned int count)
> > +{
> > + struct cal_ctx *ctx = vb2_get_drv_priv(vq);
> > + struct cal_dmaqueue *dma_q = &ctx->vidq;
> > + struct cal_buffer *buf;
> > + unsigned long addr = 0;
> > + unsigned long flags;
> > + int ret;
> > +
> > + ctx_dbg(3, ctx, "%s\n", __func__);
> > +
> > + spin_lock_irqsave(&ctx->slock, flags);
> > + if (list_empty(&dma_q->active)) {
> > + spin_unlock_irqrestore(&ctx->slock, flags);
> > + ctx_dbg(3, ctx, "buffer queue is empty\n");
> > + return -EIO;
> > + }
> > +
> > + buf = list_entry(dma_q->active.next, struct cal_buffer, list);
> > + ctx->cur_frm = buf;
> > + ctx->next_frm = buf;
> > + list_del(&buf->list);
> > + spin_unlock_irqrestore(&ctx->slock, flags);
> > +
> > + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
>
> That's wrong. The timestamp should be set when the buffer is received, so that
> should happen in the interrupt routine, I guess.
Yeah it is done in cal_process_buffer_complete. I'll remove this from here.
>
> > +
> > + addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb, 0);
> > + ctx->sequence = 0;
> > +
> > + ctx_dbg(3, ctx, "enable_irqs\n");
> > +
> > + ret = cal_get_external_info(ctx);
> > + if (ret < 0)
> > + return ret;
>
> The cleanup in case of an error in start_streaming is missing: in that case all
> pending buffers have to be returned with state VB2_BUF_STATE_QUEUED. Otherwise
> you will get warnings from the vb2 framework. It's similar to what you do in
> stop_streaming in case of an error, just with a different state.
>
Understood, I'll fix that.
> > +
> > + enable_irqs(ctx);
> > + camerarx_phy_enable(ctx);
> > + csi2_init(ctx);
> > + csi2_phy_config(ctx);
> > + csi2_lane_config(ctx);
> > + csi2_ctx_config(ctx);
> > + pix_proc_config(ctx);
> > + cal_wr_dma_config(ctx, ALIGN((ctx->width * ctx->pixelsize), 16));
> > + cal_wr_dma_addr(ctx, addr);
> > + csi2_ppi_enable(ctx);
> > +
> > + if (ctx->sensor) {
> > + if (v4l2_subdev_call(ctx->sensor, video, s_stream, 1)) {
> > + ctx_err(ctx, "stream on failed in subdev\n");
> > + return -EINVAL;
> > + }
> > + }
> > +
> > + if (debug >= 4)
> > + cal_quickdump_regs(ctx->dev);
> > +
> > + ctx_dbg(3, ctx, "returning from %s\n", __func__);
> > + return 0;
> > +}
> > +
> > +static void cal_stop_streaming(struct vb2_queue *vq)
> > +{
> > + struct cal_ctx *ctx = vb2_get_drv_priv(vq);
> > + struct cal_dmaqueue *dma_q = &ctx->vidq;
> > + unsigned long flags;
> > +
> > + ctx_dbg(3, ctx, "%s\n", __func__);
> > +
> > + if (ctx->sensor) {
> > + if (v4l2_subdev_call(ctx->sensor, video, s_stream, 0))
> > + ctx_err(ctx, "stream off failed in subdev\n");
> > + }
> > +
> > + ctx_dbg(3, ctx, "csi2_ppi_disable\n");
> > + csi2_ppi_disable(ctx);
> > +
> > + ctx_dbg(3, ctx, "disable_irqs\n");
> > + disable_irqs(ctx);
> > +
> > + /* Release all active buffers */
> > + spin_lock_irqsave(&ctx->slock, flags);
> > + while (!list_empty(&dma_q->active)) {
> > + struct cal_buffer *buf;
> > +
> > + buf = list_entry(dma_q->active.next, struct cal_buffer, list);
> > + list_del(&buf->list);
> > + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> > + ctx_dbg(3, ctx, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
> > + }
> > + spin_unlock_irqrestore(&ctx->slock, flags);
> > +
> > + if (ctx->cur_frm == ctx->next_frm) {
> > + vb2_buffer_done(&ctx->cur_frm->vb, VB2_BUF_STATE_ERROR);
> > + ctx_dbg(3, ctx, "[%p/%d] done cur_frm\n", ctx->cur_frm,
> > + ctx->cur_frm->vb.v4l2_buf.index);
> > + } else {
> > + vb2_buffer_done(&ctx->cur_frm->vb, VB2_BUF_STATE_ERROR);
> > + ctx_dbg(3, ctx, "[%p/%d] done cur_frm\n", ctx->cur_frm,
> > + ctx->cur_frm->vb.v4l2_buf.index);
> > + vb2_buffer_done(&ctx->next_frm->vb, VB2_BUF_STATE_ERROR);
> > + ctx_dbg(3, ctx, "[%p/%d] done next_frm\n", ctx->next_frm,
> > + ctx->next_frm->vb.v4l2_buf.index);
> > + }
> > + ctx->cur_frm = NULL;
> > + ctx->next_frm = NULL;
> > +
> > + ctx_dbg(3, ctx, "returning from %s\n", __func__);
> > +}
> > +
> > +static struct vb2_ops cal_video_qops = {
> > + .queue_setup = cal_queue_setup,
> > + .buf_prepare = cal_buffer_prepare,
> > + .buf_queue = cal_buffer_queue,
> > + .start_streaming = cal_start_streaming,
> > + .stop_streaming = cal_stop_streaming,
> > + .wait_prepare = vb2_ops_wait_prepare,
> > + .wait_finish = vb2_ops_wait_finish,
> > +};
> > +
> > +static const struct v4l2_file_operations cal_fops = {
> > + .owner = THIS_MODULE,
> > + .open = v4l2_fh_open,
> > + .release = vb2_fop_release,
> > + .read = vb2_fop_read,
> > + .poll = vb2_fop_poll,
> > + .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
> > + .mmap = vb2_fop_mmap,
> > +};
> > +
> > +static const struct v4l2_ioctl_ops cal_ioctl_ops = {
> > + .vidioc_querycap = cal_querycap,
> > + .vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap,
> > + .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap,
> > + .vidioc_try_fmt_vid_cap = cal_try_fmt_vid_cap,
> > + .vidioc_s_fmt_vid_cap = cal_s_fmt_vid_cap,
> > + .vidioc_enum_framesizes = cal_enum_framesizes,
> > + .vidioc_reqbufs = vb2_ioctl_reqbufs,
> > + .vidioc_create_bufs = vb2_ioctl_create_bufs,
> > + .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
> > + .vidioc_querybuf = vb2_ioctl_querybuf,
> > + .vidioc_qbuf = vb2_ioctl_qbuf,
> > + .vidioc_dqbuf = vb2_ioctl_dqbuf,
> > + .vidioc_enum_input = cal_enum_input,
> > + .vidioc_g_input = cal_g_input,
> > + .vidioc_s_input = cal_s_input,
> > + .vidioc_enum_frameintervals = cal_enum_frameintervals,
> > + .vidioc_streamon = vb2_ioctl_streamon,
> > + .vidioc_streamoff = vb2_ioctl_streamoff,
> > + .vidioc_log_status = v4l2_ctrl_log_status,
> > + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
> > + .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> > +};
> > +
> > +static struct video_device cal_videodev = {
> > + .name = CAL_MODULE_NAME,
> > + .fops = &cal_fops,
> > + .ioctl_ops = &cal_ioctl_ops,
> > + .minor = -1,
> > + .release = video_device_release,
> > +};
> > +
> > +/* -----------------------------------------------------------------
> > + Initialization and module stuff
> > + ------------------------------------------------------------------*/
> > +static int cal_release(struct cal_dev *dev)
> > +{
> > + struct cal_ctx *ctx;
> > + int i;
> > +
> > + for (i = 0; i < CAL_NUM_CONTEXT; i++) {
> > + ctx = dev->ctx[i];
> > + if (ctx) {
> > + v4l2_info(&ctx->v4l2_dev, "unregistering %s\n",
> > + video_device_node_name(&ctx->vdev));
> > + video_unregister_device(&ctx->vdev);
> > + v4l2_device_unregister(&ctx->v4l2_dev);
> > + vb2_dma_contig_cleanup_ctx(ctx->alloc_ctx);
> > + v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> > + kfree(ctx->cc);
> > + kfree(ctx);
> > + }
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int cal_complete_ctx(struct cal_ctx *ctx);
> > +
> > +static int cal_async_bound(struct v4l2_async_notifier *notifier,
> > + struct v4l2_subdev *subdev,
> > + struct v4l2_async_subdev *asd)
> > +{
> > + struct cal_ctx *ctx = notifier_to_ctx(notifier);
> > + struct v4l2_subdev_mbus_code_enum mbus_code;
> > + int i, j;
> > +
> > + ctx_dbg(1, ctx, "cal_async_bound\n");
> > +
> > + if (ctx->sensor) {
> > + ctx_info(ctx, "Rejecting subdev %s (Already set!!)",
> > + subdev->name);
> > + return 0;
> > + }
> > +
> > + ctx->sensor = subdev;
> > + ctx_info(ctx, "Using sensor %s for capture\n",
> > + subdev->name);
> > +
> > + /* setup the supported formats & indexes */
> > + for (j = 0, i = 0; ; ++j) {
> > + struct cal_fmt *fmt;
> > + int ret;
> > +
> > + memset(&mbus_code, 0, sizeof(mbus_code));
> > + mbus_code.index = j;
> > + ret = v4l2_subdev_call(subdev, pad, enum_mbus_code,
> > + NULL, &mbus_code);
> > + if (ret)
> > + break;
> > +
> > + fmt = (struct cal_fmt *)find_format_by_code(mbus_code.code);
> > + if (!fmt)
> > + continue;
> > +
> > + fmt->supported = true;
> > + fmt->index = i++;
> > + }
> > +
> > + cal_complete_ctx(ctx);
> > +
> > + return 0;
> > +}
> > +
> > +static int cal_async_complete(struct v4l2_async_notifier *notifier)
> > +{
> > + struct cal_ctx *ctx = notifier_to_ctx(notifier);
> > +
> > + ctx_dbg(1, ctx, "cal_async_complete\n");
> > + return 0;
> > +}
> > +
> > +static int cal_complete_ctx(struct cal_ctx *ctx)
> > +{
> > + struct video_device *vfd;
> > + struct vb2_queue *q;
> > + int ret;
> > +
> > + ctx->timeperframe = tpf_default;
> > +
> > + ctx->width = 1920;
> > + ctx->height = 1080;
> > + ctx->field = V4L2_FIELD_NONE;
> > + ctx->fmt = find_format_by_code(MEDIA_BUS_FMT_SGRBG8_1X8);
> > + ctx->pixelsize = ctx->fmt->depth >> 3;
> > + ctx->external_rate = 192000000;
> > +
> > + /* initialize locks */
> > + spin_lock_init(&ctx->slock);
> > + mutex_init(&ctx->mutex);
> > +
> > + /* initialize queue */
> > + q = &ctx->vb_vidq;
> > + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> > + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
> > + q->drv_priv = ctx;
> > + q->buf_struct_size = sizeof(struct cal_buffer);
> > + q->ops = &cal_video_qops;
> > + q->mem_ops = &vb2_dma_contig_memops;
> > + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> > + q->lock = &ctx->mutex;
> > + q->min_buffers_needed = 3;
> > +
> > + ret = vb2_queue_init(q);
> > + if (ret)
> > + return ret;
> > +
> > + /* init video dma queues */
> > + INIT_LIST_HEAD(&ctx->vidq.active);
> > +
> > + vfd = &ctx->vdev;
> > + *vfd = cal_videodev;
> > + vfd->v4l2_dev = &ctx->v4l2_dev;
> > + vfd->queue = q;
> > +
> > + /*
> > + * Provide a mutex to v4l2 core. It will be used to protect
> > + * all fops and v4l2 ioctls.
> > + */
> > + vfd->lock = &ctx->mutex;
> > + video_set_drvdata(vfd, ctx);
> > +
> > + ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
> > + if (ret < 0)
> > + return ret;
> > +
> > + v4l2_info(&ctx->v4l2_dev, "V4L2 device registered as %s\n",
> > + video_device_node_name(vfd));
> > +
> > + ctx->alloc_ctx = vb2_dma_contig_init_ctx(vfd->v4l2_dev->dev);
> > + if (IS_ERR(ctx->alloc_ctx)) {
> > + ctx_err(ctx, "Failed to alloc vb2 context\n");
> > + ret = PTR_ERR(ctx->alloc_ctx);
> > + goto vdev_unreg;
> > + }
> > +
> > + return 0;
> > +
> > +vdev_unreg:
> > + video_unregister_device(vfd);
> > + return ret;
> > +}
> > +
> > +static struct device_node *
> > +of_get_next_port(const struct device_node *parent,
> > + struct device_node *prev)
> > +{
> > + struct device_node *port = NULL;
> > +
> > + if (!parent)
> > + return NULL;
> > +
> > + if (!prev) {
> > + struct device_node *ports;
> > + /*
> > + * It's the first call, we have to find a port subnode
> > + * within this node or within an optional 'ports' node.
> > + */
> > + ports = of_get_child_by_name(parent, "ports");
> > + if (ports)
> > + parent = ports;
> > +
> > + port = of_get_child_by_name(parent, "port");
> > +
> > + /* release the 'ports' node */
> > + of_node_put(ports);
> > + } else {
> > + struct device_node *ports;
> > +
> > + ports = of_get_parent(prev);
> > + if (!ports)
> > + return NULL;
> > +
> > + do {
> > + port = of_get_next_child(ports, prev);
> > + if (!port) {
> > + of_node_put(ports);
> > + return NULL;
> > + }
> > + prev = port;
> > + } while (of_node_cmp(port->name, "port") != 0);
> > + }
> > +
> > + return port;
> > +}
> > +
> > +static struct device_node *
> > +of_get_next_endpoint(const struct device_node *parent,
> > + struct device_node *prev)
> > +{
> > + struct device_node *ep = NULL;
> > +
> > + if (!parent)
> > + return NULL;
> > +
> > + do {
> > + ep = of_get_next_child(parent, prev);
> > + if (!ep)
> > + return NULL;
> > + prev = ep;
> > + } while (of_node_cmp(ep->name, "endpoint") != 0);
> > +
> > + return ep;
> > +}
> > +
> > +static int of_cal_create_instance(struct cal_ctx *ctx, int inst)
> > +{
> > + struct platform_device *pdev = ctx->dev->pdev;
> > + struct device_node *ep_node, *port, *remote_ep,
> > + *sensor_node, *parent;
> > + struct v4l2_of_endpoint *endpoint;
> > + struct v4l2_async_subdev *asd;
> > + u32 regval = 0;
> > + int ret, index, found_port = 0, lane;
> > +
> > + parent = pdev->dev.of_node;
> > +
> > + asd = &ctx->asd;
> > + endpoint = &ctx->endpoint;
> > +
> > + ep_node = NULL;
> > + port = NULL;
> > + remote_ep = NULL;
> > + sensor_node = NULL;
> > + ret = -EINVAL;
> > +
> > + ctx_dbg(3, ctx, "Scanning Port node for csi2 port: %d\n", inst);
> > + for (index = 0; index < CAL_NUM_CSI2_PORTS; index++) {
> > + port = of_get_next_port(parent, port);
> > + if (!port) {
> > + ctx_dbg(1, ctx, "No port node found for csi2 port:%d\n",
> > + index);
> > + goto cleanup_exit;
> > + }
> > +
> > + /* Match the slice number with <REG> */
> > + of_property_read_u32(port, "reg", ®val);
> > + ctx_dbg(3, ctx, "port:%d inst:%d <reg>:%d\n",
> > + index, inst, regval);
> > + if ((regval == inst) && (index == inst)) {
> > + found_port = 1;
> > + break;
> > + }
> > + }
> > +
> > + if (!found_port) {
> > + ctx_dbg(1, ctx, "No port node matches csi2 port:%d\n",
> > + inst);
> > + goto cleanup_exit;
> > + }
> > +
> > + ctx_dbg(3, ctx, "Scanning sub-device for csi2 port: %d\n",
> > + inst);
> > +
> > + ep_node = of_get_next_endpoint(port, ep_node);
> > + if (!ep_node) {
> > + ctx_dbg(3, ctx, "can't get next endpoint\n");
> > + goto cleanup_exit;
> > + }
> > +
> > + sensor_node = of_graph_get_remote_port_parent(ep_node);
> > + if (!sensor_node) {
> > + ctx_dbg(3, ctx, "can't get remote parent\n");
> > + goto cleanup_exit;
> > + }
> > + asd->match_type = V4L2_ASYNC_MATCH_OF;
> > + asd->match.of.node = sensor_node;
> > +
> > + remote_ep = of_parse_phandle(ep_node, "remote-endpoint", 0);
> > + if (!remote_ep) {
> > + ctx_dbg(3, ctx, "can't get remote-endpoint\n");
> > + goto cleanup_exit;
> > + }
> > + v4l2_of_parse_endpoint(remote_ep, endpoint);
> > +
> > + if (endpoint->bus_type != V4L2_MBUS_CSI2) {
> > + ctx_err(ctx, "Port:%d sub-device %s is not a CSI2 device\n",
> > + inst, sensor_node->name);
> > + goto cleanup_exit;
> > + }
> > +
> > + /* Store Virtual Channel number */
> > + ctx->virtual_channel = endpoint->base.id;
> > +
> > + ctx_dbg(3, ctx, "Port:%d v4l2-endpoint: CSI2\n", inst);
> > + ctx_dbg(3, ctx, "Virtual Channel=%d\n", ctx->virtual_channel);
> > + ctx_dbg(3, ctx, "flags=0x%08x\n", endpoint->bus.mipi_csi2.flags);
> > + ctx_dbg(3, ctx, "clock_lane=%d\n", endpoint->bus.mipi_csi2.clock_lane);
> > + ctx_dbg(3, ctx, "num_data_lanes=%d\n",
> > + endpoint->bus.mipi_csi2.num_data_lanes);
> > + ctx_dbg(3, ctx, "data_lanes= <\n");
> > + for (lane = 0; lane < endpoint->bus.mipi_csi2.num_data_lanes; lane++)
> > + ctx_dbg(3, ctx, "\t%d\n",
> > + endpoint->bus.mipi_csi2.data_lanes[lane]);
> > + ctx_dbg(3, ctx, "\t>\n");
> > +
> > + ctx_dbg(1, ctx, "Port: %d found sub-device %s\n",
> > + inst, sensor_node->name);
> > +
> > + ctx_dbg(1, ctx, "Asynchronous subdevice registration\n");
> > + ctx->asd_list[0] = asd;
> > + ctx->notifier.subdevs = ctx->asd_list;
> > + ctx->notifier.num_subdevs = 1;
> > + ctx->notifier.bound = cal_async_bound;
> > + ctx->notifier.complete = cal_async_complete;
> > + ret = v4l2_async_notifier_register(&ctx->v4l2_dev,
> > + &ctx->notifier);
> > + if (ret) {
> > + ctx_err(ctx, "Error registering async notifier\n");
> > + ret = -EINVAL;
> > + }
> > +
> > +cleanup_exit:
> > + if (!remote_ep)
> > + of_node_put(remote_ep);
> > + if (!sensor_node)
> > + of_node_put(sensor_node);
> > + if (!ep_node)
> > + of_node_put(ep_node);
> > + if (!port)
> > + of_node_put(port);
> > +
> > + return ret;
> > +}
> > +
> > +static struct cal_ctx *cal_create_instance(struct cal_dev *dev, int inst)
> > +{
> > + struct cal_ctx *ctx;
> > + struct v4l2_ctrl_handler *hdl;
> > + int ret;
> > +
> > + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> > + if (!ctx)
> > + return 0;
> > + /* save the cal_dev * for future ref */
> > + ctx->dev = dev;
> > +
> > + snprintf(ctx->v4l2_dev.name, sizeof(ctx->v4l2_dev.name),
> > + "%s-%03d", CAL_MODULE_NAME, inst);
> > + ret = v4l2_device_register(&dev->pdev->dev, &ctx->v4l2_dev);
> > + if (ret)
> > + goto free_ctx;
> > +
> > + hdl = &ctx->ctrl_handler;
> > + ret = v4l2_ctrl_handler_init(hdl, 11);
> > + if (ret) {
> > + ctx_err(ctx, "Failed to init ctrl handler\n");
> > + goto free_hdl;
> > + }
> > + ctx->v4l2_dev.ctrl_handler = hdl;
> > +
> > + /* Make sure Camera Core H/W register area is available */
> > + ctx->cc = cc_create(dev, inst);
> > + if (IS_ERR(ctx->cc)) {
> > + ret = PTR_ERR(ctx->cc);
> > + goto unreg_dev;
> > + }
> > +
> > + /* Store the instance id */
> > + ctx->csi2_port = inst + 1;
> > +
> > + ret = of_cal_create_instance(ctx, inst);
> > + if (ret) {
> > + ctx_dbg(1, ctx, "Error scanning cal instance: %d\n", inst);
> > + ret = -EINVAL;
> > + goto free_cc;
> > + }
> > + return ctx;
> > +
> > +free_cc:
> > + kfree(ctx->cc);
> > +free_hdl:
> > + v4l2_ctrl_handler_free(hdl);
> > +unreg_dev:
> > + v4l2_device_unregister(&ctx->v4l2_dev);
> > +free_ctx:
> > + kfree(ctx);
> > + return 0;
> > +}
> > +
> > +static int cal_probe(struct platform_device *pdev)
> > +{
> > + struct cal_dev *dev;
> > + int ret;
> > + int irq, func;
> > +
> > + dev_info(&pdev->dev, "Probing %s\n",
> > + CAL_MODULE_NAME);
> > +
> > + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
> > + if (!dev)
> > + return -ENOMEM;
> > +
> > + /* set pseudo v4l2 device name so we can use v4l2_printk */
> > + strcpy(dev->v4l2_dev.name, CAL_MODULE_NAME);
> > +
> > + /* save pdev pointer */
> > + dev->pdev = pdev;
> > +
> > + dev->res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> > + "cal_top");
> > + cal_dbg(1, dev, "ioresource %s at %x - %x\n",
> > + dev->res->name, dev->res->start, dev->res->end);
> > +
> > + dev->base = devm_ioremap(&pdev->dev, dev->res->start, SZ_32K);
> > + if (!dev->base) {
> > + ret = -ENOMEM;
> > + goto just_exit;
> > + }
> > +
> > + irq = platform_get_irq(pdev, 0);
> > + cal_dbg(1, dev, "got irq# %d\n", irq);
> > + ret = devm_request_irq(&pdev->dev, irq, cal_irq, 0, CAL_MODULE_NAME,
> > + dev);
> > + if (ret)
> > + goto just_exit;
> > +
> > + platform_set_drvdata(pdev, dev);
> > +
> > + pm_runtime_enable(&pdev->dev);
> > +
> > + ret = pm_runtime_get_sync(&pdev->dev);
> > + if (ret)
> > + goto just_exit;
> > +
> > + /* Just check we can actually access the module */
> > + cal_get_hwinfo(dev);
> > +
> > + func = cal_read_field(dev, CAL_HL_REVISION, CAL_HL_REVISION_FUNC_MASK,
> > + CAL_HL_REVISION_FUNC_SHIFT);
> > + cal_dbg(1, dev, "CAL HL_REVISION function %x\n", func);
> > +
> > + dev->cm = cm_create(dev);
> > + if (IS_ERR(dev->cm)) {
> > + ret = PTR_ERR(dev->cm);
> > + goto runtime_put;
> > + }
> > + dev->ctx[0] = NULL;
> > + dev->ctx[1] = NULL;
> > +
> > + dev->ctx[0] = cal_create_instance(dev, 0);
> > + dev->ctx[1] = cal_create_instance(dev, 1);
> > + if (!dev->ctx[0] && !dev->ctx[1]) {
> > + ret = -ENODEV;
> > + cal_err(dev, "Neither port is configured, no point in staying up\n");
> > + goto free_ctx;
> > + }
> > +
> > + return 0;
> > +
> > +free_ctx:
> > + kfree(dev->ctx[0]);
> > + kfree(dev->ctx[1]);
> > + kfree(dev->cm);
> > +runtime_put:
> > + pm_runtime_put_sync(&pdev->dev);
> > + pm_runtime_disable(&pdev->dev);
> > +just_exit:
> > + return ret;
> > +}
> > +
> > +static int cal_remove(struct platform_device *pdev)
> > +{
> > + struct cal_dev *dev =
> > + (struct cal_dev *)platform_get_drvdata(pdev);
> > +
> > + cal_info(dev, "Removing %s\n", CAL_MODULE_NAME);
> > +
> > + cal_release(dev);
> > +
> > + /* disable csi2 phy */
> > + if (dev->ctx[0])
> > + camerarx_phy_disable(dev->ctx[0]);
> > + if (dev->ctx[1])
> > + camerarx_phy_disable(dev->ctx[1]);
> > + kfree(dev->cm);
> > +
> > + pm_runtime_put_sync(&pdev->dev);
> > + pm_runtime_disable(&pdev->dev);
> > +
> > + return 0;
> > +}
> > +
> > +#if defined(CONFIG_OF)
> > +static const struct of_device_id cal_of_match[] = {
> > + { .compatible = "ti,cal", },
> > + {},
> > +};
> > +MODULE_DEVICE_TABLE(of, cal_of_match);
> > +#else
> > +#define cal_of_match NULL
> > +#endif
> > +
> > +static struct platform_driver cal_pdrv = {
> > + .probe = cal_probe,
> > + .remove = cal_remove,
> > + .driver = {
> > + .name = CAL_MODULE_NAME,
> > + .of_match_table = cal_of_match,
> > + },
> > +};
> > +
> > +module_platform_driver(cal_pdrv);
>
> Regards,
>
> Hans
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Patch 1/2] media: v4l: ti-vpe: Add CAL v4l2 camera capture driver
2015-06-23 19:36 ` Benoit Parrot
@ 2015-09-16 21:22 ` Benoit Parrot
0 siblings, 0 replies; 8+ messages in thread
From: Benoit Parrot @ 2015-09-16 21:22 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media, devicetree, Tony Lindgren, linux-kernel
Hans,
I am almost done with my review comment but I am stuck here.
Benoit Parrot <bparrot@ti.com> wrote on Tue [2015-Jun-23 14:36:47 -0500]:
> > > +
> > > +static int cal_get_external_info(struct cal_ctx *ctx)
> > > +{
> > > + struct v4l2_ext_control ctrl_ext;
> > > + struct v4l2_ext_controls ctrls_ext;
> > > + int ret;
> > > +
> > > + ctx_dbg(3, ctx, "%s\n", __func__);
> > > +
> > > + memset(&ctrls_ext, 0, sizeof(ctrls_ext));
> > > + memset(&ctrl_ext, 0, sizeof(ctrl_ext));
> > > +
> > > + ctrl_ext.id = V4L2_CID_PIXEL_RATE;
> > > +
> > > + ctrls_ext.count = 1;
> > > + ctrls_ext.controls = &ctrl_ext;
> > > +
> > > + ret = v4l2_g_ext_ctrls(&ctx->ctrl_handler, &ctrls_ext);
> >
> > Use v4l2_ctrl_g_ctrl_int64() instead: much simpler. You do need to store the
> > v4l2_ctrl pointer for the pixel rate control when you create it, but that's no
> > problem. (or use v4l2_ctrl_find, but I prefer to just cache the pointer).
>
> Hmm, that's how I had it in our local 3.14 branch originally but I kept
> getting 0 instead of the actual pixel rate.
> I'll revert it back and test again against linux-media/master.
>
> >
> > > + if (ret < 0) {
> > > + ctx_err(ctx, "no pixel rate control in subdev: %s\n",
> > > + ctx->sensor->name);
> > > + return -EPIPE;
> > > + }
> > > +
> > > + ctx->external_rate = ctrl_ext.value64;
> > > + ctx_dbg(3, ctx, "sensor Pixel Rate: %d\n", ctx->external_rate);
> > > +
> > > + return 0;
> > > +}
Alright, in order to comply with this comment I rewrote the above function
as follows:
static int cal_get_external_info(struct cal_ctx *ctx)
{
struct v4l2_ctrl *ctrl;
s64 val;
ctx_dbg(3, ctx, "%s\n", __func__);
ctrl = v4l2_ctrl_find(&ctx->ctrl_handler, V4L2_CID_PIXEL_RATE);
// ctrl = v4l2_ctrl_find(ctx->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE);
if (ctrl == NULL) {
ctx_err(ctx, "no pixel rate control in subdev: %s\n",
ctx->sensor->name);
return -EPIPE;
}
// ctx->external_rate = v4l2_ctrl_g_ctrl_int64(ctrl);
val = v4l2_ctrl_g_ctrl_int64(ctrl);
ctx_dbg(3, ctx, "sensor Pixel Rate: s64 %lld\n", val);
ctx->external_rate = (unsigned int)val;
ctx_dbg(3, ctx, "sensor Pixel Rate: %d\n", ctx->external_rate);
return 0;
}
But in all cases the value I get from v4l2_ctrl_g_ctrl_int64 is always zero!
Now I have tripple check with debug code that the subdev is in fact setting
the p_cur and p_new value to something useful (i.e. not 0).
I have traced it down to the get_ctrl() call inside of v4l2_ctrl_g_ctrl_int64.
static int get_ctrl(struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c)
{
struct v4l2_ctrl *master = ctrl->cluster[0];
int ret = 0;
int i;
printk("BENOIT: ==> get_ctrl: ctrl name:%s cur: %lld new: %lld\n",
ctrl->name, *ctrl->p_cur.p_s64, *ctrl->p_new.p_s64);
/* Compound controls are not supported. The new_to_user() and
* cur_to_user() calls below would need to be modified not to access
* userspace memory when called from get_ctrl().
*/
printk("BENOIT: ==> get_ctrl: ctrl name:%s is_int: %d\n", ctrl->name, ctrl->is_int);
if (!ctrl->is_int)
return -EINVAL;
**** It always exits here, because for int64_integer the is_int is not set!
**** which means on the way back out the value reported will always be 0!
printk("BENOIT: ==> get_ctrl: ctrl name:%s flags: 0x%08lx\n", ctrl->name, ctrl->flags);
if (ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
return -EACCES;
printk("BENOIT: ==> get_ctrl: ctrl name:%s before ctrl_lock:\n", ctrl->name);
v4l2_ctrl_lock(master);
/* g_volatile_ctrl will update the current control values */
if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE) {
for (i = 0; i < master->ncontrols; i++)
cur_to_new(master->cluster[i]);
ret = call_op(master, g_volatile_ctrl);
new_to_user(c, ctrl);
} else {
cur_to_user(c, ctrl);
}
printk("BENOIT: <== get_ctrl: ctrl name:%s cur: %lld new: %lld\n",
ctrl->name, *ctrl->p_cur.p_s64, *ctrl->p_new.p_s64);
v4l2_ctrl_unlock(master);
return ret;
}
As anyone ever used this successfully to read a V4L2_CID_PIXEL_RATE control value?
Regards,
Benoit
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2015-09-16 21:22 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-06-16 17:29 [Patch 0/2] media: v4l: ti-vpe: Add CAL v4l2 camera capture driver Benoit Parrot
2015-06-16 17:29 ` [Patch 1/2] " Benoit Parrot
2015-06-22 8:09 ` Hans Verkuil
2015-06-23 19:36 ` Benoit Parrot
2015-09-16 21:22 ` Benoit Parrot
2015-06-16 17:29 ` [Patch 2/2] media: v4l: ti-vpe: Document CAL driver Benoit Parrot
2015-06-22 7:31 ` [Patch 0/2] media: v4l: ti-vpe: Add CAL v4l2 camera capture driver Hans Verkuil
2015-06-23 19:19 ` Benoit Parrot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).