All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver
@ 2017-07-17 10:33 Todor Tomov
  2017-07-17 10:33 ` [PATCH v3 01/23] [media] media: Make parameter of media_entity_remote_pad() const Todor Tomov
                   ` (24 more replies)
  0 siblings, 25 replies; 60+ messages in thread
From: Todor Tomov @ 2017-07-17 10:33 UTC (permalink / raw)
  To: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm
  Cc: Todor Tomov

This patchset adds basic support for the Qualcomm Camera Subsystem found
on Qualcomm MSM8916 and APQ8016 processors.

The driver implements V4L2, Media controller and V4L2 subdev interfaces.
Camera sensor using V4L2 subdev interface in the kernel is supported.

The driver is implemented using as a reference the Qualcomm Camera
Subsystem driver for Android as found in Code Aurora [1].

The driver is tested on Dragonboard 410C (APQ8016) with one and two
OV5645 camera sensors. media-ctl [2] and yavta [3] applications were
used for testing. Also Gstreamer 1.10.4 with v4l2src plugin is supported.

More information is present in the document added by the third patch.

[1] https://source.codeaurora.org/quic/la/kernel/msm-3.10/
[2] https://git.linuxtv.org//v4l-utils.git
[3] http://git.ideasonboard.org/yavta.git

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

Patchset Changelog:

Version 3:
- use V4L2 fwnode framework;
- remove settle count parameter from DT and add logic to calculate it instead;
- refactor video device node format initialization;
- fixed copyright information;
- shorter clock names;
- remove redundant memset usage;
- print error code when error happens;
- do not check format type on g_fmt/s_fmt;
- add busy check on s_fmt;
- rename files to add camss- prefix;
- other small code fixes.

Version 2:
- patches 01-10 are updated from v1 following the review received and bugs
  and limitaitons found after v1 was posted. The updates include:
  - return buffers on unsuccessful stream on;
  - fill device capabilities in struct video_device;
  - simplify v4l2 file handle usage - no custom struct for file handle;
  - use vb2_fop_poll and vb2_fop_mmap v4l2 file operations;
  - add support for read/write I/O;
  - add support for DMABUF streaming I/O;
  - add support for EXPBUF and PREPARE_BUF ioctl;
  - avoid a race condition between device unbind and userspace access
    to the video node;
  - use non-contiguous memory for video buffers;
  - switch to V4L2 multi-planar API;
  - add useful error messages in case of an overflow in ISPIF;
  - other small and style fixes.

- patches 11-19 are new (they were not ready/posted with v1). I'm including
  these in this patchset as they add valuable features and may be desired
  for a real world usage of the driver.

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

The driver depends on patches:
- [media] media: Make parameter of media_entity_remote_pad() const
- [media] v4l2-mediabus: Add helper functions
- v4l: Add packed Bayer raw12 pixel formats 

They are included in this patchset to ensure successful compilation.

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

V4L2 compliance test result:

$ v4l2-compliance -d /dev/video1 -s
v4l2-compliance SHA   : 8e68406dae2233e811032dc8e7714c09c818e893

Driver Info:
        Driver name   : qcom-camss
        Card type     : Qualcomm Camera Subsystem
        Bus info      : platform:1b0ac00.camss
        Driver version: 4.9.34
        Capabilities  : 0x85201000
                Video Capture Multiplanar
                Read/Write
                Streaming
                Extended Pix Format
                Device Capabilities
        Device Caps   : 0x05201000
                Video Capture Multiplanar
                Read/Write
                Streaming
                Extended Pix Format

Compliance test for device /dev/video1 (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
        test for unlimited opens: OK

Debug ioctls:
        test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
        test VIDIOC_LOG_STATUS: OK (Not Supported)

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:
                test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
                test VIDIOC_QUERYCTRL: OK (Not Supported)
                test VIDIOC_G/S_CTRL: OK (Not Supported)
                test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
                test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
                test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
                Standard Controls: 0 Private Controls: 0

        Format ioctls:
                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)
                test Scaling: OK (Not Supported)

        Codec ioctls:
                test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
                test VIDIOC_G_ENC_INDEX: OK (Not Supported)
                test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

        Buffer ioctls:
                test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
                test VIDIOC_EXPBUF: OK

Test input 0:

Streaming ioctls:
        test read/write: OK
        test MMAP: OK                                     
        test USERPTR: OK (Not Supported)
        test DMABUF: Cannot test, specify --expbuf-device


Total: 46, Succeeded: 46, Failed: 0, Warnings: 0 

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

Sakari Ailus (1):
  v4l: Add packed Bayer raw12 pixel formats

Todor Tomov (22):
  [media] media: Make parameter of media_entity_remote_pad() const
  [media] v4l2-mediabus: Add helper functions
  dt-bindings: media: Binding document for Qualcomm Camera subsystem
    driver
  MAINTAINERS: Add Qualcomm Camera subsystem driver
  doc: media/v4l-drivers: Add Qualcomm Camera Subsystem driver document
  media: camss: Add CSIPHY files
  media: camss: Add CSID files
  media: camss: Add ISPIF files
  media: camss: Add VFE files
  media: camss: Add files which handle the video device nodes
  media: camms: Add core files
  media: camss: Enable building
  camss: vfe: Format conversion support using PIX interface
  doc: media/v4l-drivers: Qualcomm Camera Subsystem - PIX Interface
  camss: vfe: Support for frame padding
  camss: vfe: Add interface for scaling
  camss: vfe: Configure scaler module in VFE
  camss: vfe: Add interface for cropping
  camss: vfe: Configure crop module in VFE
  doc: media/v4l-drivers: Qualcomm Camera Subsystem - Scale and crop
  camss: Use optimal clock frequency rates
  doc: media/v4l-drivers: Qualcomm Camera Subsystem - Media graph

 .../devicetree/bindings/media/qcom,camss.txt       |  191 ++
 Documentation/media/uapi/v4l/pixfmt-rgb.rst        |    1 +
 Documentation/media/uapi/v4l/pixfmt-srggb12p.rst   |  104 +
 Documentation/media/v4l-drivers/qcom_camss.rst     |  150 +
 .../media/v4l-drivers/qcom_camss_graph.dot         |   41 +
 MAINTAINERS                                        |    8 +
 drivers/media/media-entity.c                       |    2 +-
 drivers/media/platform/Kconfig                     |    7 +
 drivers/media/platform/Makefile                    |    2 +
 drivers/media/platform/qcom/camss-8x16/Makefile    |   11 +
 .../media/platform/qcom/camss-8x16/camss-csid.c    | 1181 ++++++++
 .../media/platform/qcom/camss-8x16/camss-csid.h    |   82 +
 .../media/platform/qcom/camss-8x16/camss-csiphy.c  |  882 ++++++
 .../media/platform/qcom/camss-8x16/camss-csiphy.h  |   77 +
 .../media/platform/qcom/camss-8x16/camss-ispif.c   | 1138 ++++++++
 .../media/platform/qcom/camss-8x16/camss-ispif.h   |   85 +
 drivers/media/platform/qcom/camss-8x16/camss-vfe.c | 3069 ++++++++++++++++++++
 drivers/media/platform/qcom/camss-8x16/camss-vfe.h |  123 +
 .../media/platform/qcom/camss-8x16/camss-video.c   |  842 ++++++
 .../media/platform/qcom/camss-8x16/camss-video.h   |   74 +
 drivers/media/platform/qcom/camss-8x16/camss.c     |  736 +++++
 drivers/media/platform/qcom/camss-8x16/camss.h     |  108 +
 drivers/media/v4l2-core/v4l2-ioctl.c               |   12 +-
 include/media/media-entity.h                       |    2 +-
 include/media/v4l2-mediabus.h                      |   26 +
 include/uapi/linux/videodev2.h                     |    5 +
 26 files changed, 8953 insertions(+), 6 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/media/qcom,camss.txt
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-srggb12p.rst
 create mode 100644 Documentation/media/v4l-drivers/qcom_camss.rst
 create mode 100644 Documentation/media/v4l-drivers/qcom_camss_graph.dot
 create mode 100644 drivers/media/platform/qcom/camss-8x16/Makefile
 create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-csid.c
 create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-csid.h
 create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-csiphy.c
 create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-csiphy.h
 create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-ispif.c
 create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-ispif.h
 create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-vfe.c
 create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-vfe.h
 create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-video.c
 create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-video.h
 create mode 100644 drivers/media/platform/qcom/camss-8x16/camss.c
 create mode 100644 drivers/media/platform/qcom/camss-8x16/camss.h

-- 
2.7.4

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [PATCH v3 01/23] [media] media: Make parameter of media_entity_remote_pad() const
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
@ 2017-07-17 10:33 ` Todor Tomov
  2017-07-17 10:33 ` [PATCH v3 02/23] [media] v4l2-mediabus: Add helper functions Todor Tomov
                   ` (23 subsequent siblings)
  24 siblings, 0 replies; 60+ messages in thread
From: Todor Tomov @ 2017-07-17 10:33 UTC (permalink / raw)
  To: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm
  Cc: Todor Tomov

The local pad parameter in media_entity_remote_pad() is not modified.
Make that explicit by adding a const modifier.

Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/media-entity.c | 2 +-
 include/media/media-entity.h | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c
index dd0f0ea..2ace041 100644
--- a/drivers/media/media-entity.c
+++ b/drivers/media/media-entity.c
@@ -917,7 +917,7 @@ media_entity_find_link(struct media_pad *source, struct media_pad *sink)
 }
 EXPORT_SYMBOL_GPL(media_entity_find_link);
 
-struct media_pad *media_entity_remote_pad(struct media_pad *pad)
+struct media_pad *media_entity_remote_pad(const struct media_pad *pad)
 {
 	struct media_link *link;
 
diff --git a/include/media/media-entity.h b/include/media/media-entity.h
index 754182d..222d379 100644
--- a/include/media/media-entity.h
+++ b/include/media/media-entity.h
@@ -805,7 +805,7 @@ struct media_link *media_entity_find_link(struct media_pad *source,
  * Return: returns a pointer to the pad at the remote end of the first found
  * enabled link, or %NULL if no enabled link has been found.
  */
-struct media_pad *media_entity_remote_pad(struct media_pad *pad);
+struct media_pad *media_entity_remote_pad(const struct media_pad *pad);
 
 /**
  * media_entity_get - Get a reference to the parent module
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v3 02/23] [media] v4l2-mediabus: Add helper functions
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
  2017-07-17 10:33 ` [PATCH v3 01/23] [media] media: Make parameter of media_entity_remote_pad() const Todor Tomov
@ 2017-07-17 10:33 ` Todor Tomov
  2017-07-17 10:33 ` [PATCH v3 03/23] v4l: Add packed Bayer raw12 pixel formats Todor Tomov
                   ` (22 subsequent siblings)
  24 siblings, 0 replies; 60+ messages in thread
From: Todor Tomov @ 2017-07-17 10:33 UTC (permalink / raw)
  To: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm
  Cc: Todor Tomov

Add helper functions for mbus to/from mplane pixel format conversion.

Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
---
 include/media/v4l2-mediabus.h | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/include/media/v4l2-mediabus.h b/include/media/v4l2-mediabus.h
index 34cc99e..125afcd 100644
--- a/include/media/v4l2-mediabus.h
+++ b/include/media/v4l2-mediabus.h
@@ -113,4 +113,30 @@ static inline void v4l2_fill_mbus_format(struct v4l2_mbus_framefmt *mbus_fmt,
 	mbus_fmt->code = code;
 }
 
+static inline void v4l2_fill_pix_format_mplane(
+				struct v4l2_pix_format_mplane *pix_mp_fmt,
+				const struct v4l2_mbus_framefmt *mbus_fmt)
+{
+	pix_mp_fmt->width = mbus_fmt->width;
+	pix_mp_fmt->height = mbus_fmt->height;
+	pix_mp_fmt->field = mbus_fmt->field;
+	pix_mp_fmt->colorspace = mbus_fmt->colorspace;
+	pix_mp_fmt->ycbcr_enc = mbus_fmt->ycbcr_enc;
+	pix_mp_fmt->quantization = mbus_fmt->quantization;
+	pix_mp_fmt->xfer_func = mbus_fmt->xfer_func;
+}
+
+static inline void v4l2_fill_mbus_format_mplane(
+				struct v4l2_mbus_framefmt *mbus_fmt,
+				const struct v4l2_pix_format_mplane *pix_mp_fmt)
+{
+	mbus_fmt->width = pix_mp_fmt->width;
+	mbus_fmt->height = pix_mp_fmt->height;
+	mbus_fmt->field = pix_mp_fmt->field;
+	mbus_fmt->colorspace = pix_mp_fmt->colorspace;
+	mbus_fmt->ycbcr_enc = pix_mp_fmt->ycbcr_enc;
+	mbus_fmt->quantization = pix_mp_fmt->quantization;
+	mbus_fmt->xfer_func = pix_mp_fmt->xfer_func;
+}
+
 #endif
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v3 03/23] v4l: Add packed Bayer raw12 pixel formats
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
  2017-07-17 10:33 ` [PATCH v3 01/23] [media] media: Make parameter of media_entity_remote_pad() const Todor Tomov
  2017-07-17 10:33 ` [PATCH v3 02/23] [media] v4l2-mediabus: Add helper functions Todor Tomov
@ 2017-07-17 10:33 ` Todor Tomov
  2017-07-17 10:33 ` [PATCH v3 04/23] dt-bindings: media: Binding document for Qualcomm Camera subsystem driver Todor Tomov
                   ` (21 subsequent siblings)
  24 siblings, 0 replies; 60+ messages in thread
From: Todor Tomov @ 2017-07-17 10:33 UTC (permalink / raw)
  To: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm
  Cc: Sakari Ailus

From: Sakari Ailus <sakari.ailus@linux.intel.com>

These formats are compressed 12-bit raw bayer formats with four different
pixel orders. They are similar to 10-bit variants. The formats added by
this patch are

	V4L2_PIX_FMT_SBGGR12P
	V4L2_PIX_FMT_SGBRG12P
	V4L2_PIX_FMT_SGRBG12P
	V4L2_PIX_FMT_SRGGB12P

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 Documentation/media/uapi/v4l/pixfmt-rgb.rst      |   1 +
 Documentation/media/uapi/v4l/pixfmt-srggb12p.rst | 104 +++++++++++++++++++++++
 drivers/media/v4l2-core/v4l2-ioctl.c             |  12 ++-
 include/uapi/linux/videodev2.h                   |   5 ++
 4 files changed, 118 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-srggb12p.rst

diff --git a/Documentation/media/uapi/v4l/pixfmt-rgb.rst b/Documentation/media/uapi/v4l/pixfmt-rgb.rst
index b0f3513..4cc2719 100644
--- a/Documentation/media/uapi/v4l/pixfmt-rgb.rst
+++ b/Documentation/media/uapi/v4l/pixfmt-rgb.rst
@@ -17,4 +17,5 @@ RGB Formats
     pixfmt-srggb10alaw8
     pixfmt-srggb10dpcm8
     pixfmt-srggb12
+    pixfmt-srggb12p
     pixfmt-srggb16
diff --git a/Documentation/media/uapi/v4l/pixfmt-srggb12p.rst b/Documentation/media/uapi/v4l/pixfmt-srggb12p.rst
new file mode 100644
index 0000000..c3c19f9e
--- /dev/null
+++ b/Documentation/media/uapi/v4l/pixfmt-srggb12p.rst
@@ -0,0 +1,104 @@
+.. -*- coding: utf-8; mode: rst -*-
+
+.. _V4L2-PIX-FMT-SRGGB12P:
+.. _v4l2-pix-fmt-sbggr12p:
+.. _v4l2-pix-fmt-sgbrg12p:
+.. _v4l2-pix-fmt-sgrbg12p:
+
+*******************************************************************************************************************************
+V4L2_PIX_FMT_SRGGB12P ('pRAA'), V4L2_PIX_FMT_SGRBG12P ('pgAA'), V4L2_PIX_FMT_SGBRG12P ('pGAA'), V4L2_PIX_FMT_SBGGR12P ('pBAA'),
+*******************************************************************************************************************************
+
+
+12-bit packed Bayer formats
+
+
+Description
+===========
+
+These four pixel formats are packed raw sRGB / Bayer formats with 12
+bits per colour. Every two consecutive samples are packed into three
+bytes. Each of the first two bytes contain the 8 high order bits of
+the pixels, and the third byte contains the four least significants
+bits of each pixel, in the same order.
+
+Each n-pixel row contains n/2 green samples and n/2 blue or red
+samples, with alternating green-red and green-blue rows. They are
+conventionally described as GRGR... BGBG..., RGRG... GBGB..., etc.
+Below is an example of a small V4L2_PIX_FMT_SBGGR12P image:
+
+**Byte Order.**
+Each cell is one byte.
+
+
+
+.. flat-table::
+    :header-rows:  0
+    :stub-columns: 0
+    :widths:       2 1 1 1 1 1 1
+
+
+    -  .. row 1
+
+       -  start + 0:
+
+       -  B\ :sub:`00high`
+
+       -  G\ :sub:`01high`
+
+       -  G\ :sub:`01low`\ (bits 7--4) B\ :sub:`00low`\ (bits 3--0)
+
+       -  B\ :sub:`02high`
+
+       -  G\ :sub:`03high`
+
+       -  G\ :sub:`03low`\ (bits 7--4) B\ :sub:`02low`\ (bits 3--0)
+
+    -  .. row 2
+
+       -  start + 6:
+
+       -  G\ :sub:`10high`
+
+       -  R\ :sub:`11high`
+
+       -  R\ :sub:`11low`\ (bits 7--4) G\ :sub:`10low`\ (bits 3--0)
+
+       -  G\ :sub:`12high`
+
+       -  R\ :sub:`13high`
+
+       -  R\ :sub:`13low`\ (bits 3--2) G\ :sub:`12low`\ (bits 3--0)
+
+    -  .. row 3
+
+       -  start + 12:
+
+       -  B\ :sub:`20high`
+
+       -  G\ :sub:`21high`
+
+       -  G\ :sub:`21low`\ (bits 7--4) B\ :sub:`20low`\ (bits 3--0)
+
+       -  B\ :sub:`22high`
+
+       -  G\ :sub:`23high`
+
+       -  G\ :sub:`23low`\ (bits 7--4) B\ :sub:`22low`\ (bits 3--0)
+
+    -  .. row 4
+
+       -  start + 18:
+
+       -  G\ :sub:`30high`
+
+       -  R\ :sub:`31high`
+
+       -  R\ :sub:`31low`\ (bits 7--4) G\ :sub:`30low`\ (bits 3--0)
+
+       -  G\ :sub:`32high`
+
+       -  R\ :sub:`33high`
+
+       -  R\ :sub:`33low`\ (bits 3--2) G\ :sub:`32low`\ (bits 3--0)
+
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index cab63bb..b60a6b0 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1195,10 +1195,6 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
 	case V4L2_PIX_FMT_SGBRG10:	descr = "10-bit Bayer GBGB/RGRG"; break;
 	case V4L2_PIX_FMT_SGRBG10:	descr = "10-bit Bayer GRGR/BGBG"; break;
 	case V4L2_PIX_FMT_SRGGB10:	descr = "10-bit Bayer RGRG/GBGB"; break;
-	case V4L2_PIX_FMT_SBGGR12:	descr = "12-bit Bayer BGBG/GRGR"; break;
-	case V4L2_PIX_FMT_SGBRG12:	descr = "12-bit Bayer GBGB/RGRG"; break;
-	case V4L2_PIX_FMT_SGRBG12:	descr = "12-bit Bayer GRGR/BGBG"; break;
-	case V4L2_PIX_FMT_SRGGB12:	descr = "12-bit Bayer RGRG/GBGB"; break;
 	case V4L2_PIX_FMT_SBGGR10P:	descr = "10-bit Bayer BGBG/GRGR Packed"; break;
 	case V4L2_PIX_FMT_SGBRG10P:	descr = "10-bit Bayer GBGB/RGRG Packed"; break;
 	case V4L2_PIX_FMT_SGRBG10P:	descr = "10-bit Bayer GRGR/BGBG Packed"; break;
@@ -1211,6 +1207,14 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
 	case V4L2_PIX_FMT_SGBRG10DPCM8:	descr = "8-bit Bayer GBGB/RGRG (DPCM)"; break;
 	case V4L2_PIX_FMT_SGRBG10DPCM8:	descr = "8-bit Bayer GRGR/BGBG (DPCM)"; break;
 	case V4L2_PIX_FMT_SRGGB10DPCM8:	descr = "8-bit Bayer RGRG/GBGB (DPCM)"; break;
+	case V4L2_PIX_FMT_SBGGR12:	descr = "12-bit Bayer BGBG/GRGR"; break;
+	case V4L2_PIX_FMT_SGBRG12:	descr = "12-bit Bayer GBGB/RGRG"; break;
+	case V4L2_PIX_FMT_SGRBG12:	descr = "12-bit Bayer GRGR/BGBG"; break;
+	case V4L2_PIX_FMT_SRGGB12:	descr = "12-bit Bayer RGRG/GBGB"; break;
+	case V4L2_PIX_FMT_SBGGR12P:	descr = "12-bit Bayer BGBG/GRGR Packed"; break;
+	case V4L2_PIX_FMT_SGBRG12P:	descr = "12-bit Bayer GBGB/RGRG Packed"; break;
+	case V4L2_PIX_FMT_SGRBG12P:	descr = "12-bit Bayer GRGR/BGBG Packed"; break;
+	case V4L2_PIX_FMT_SRGGB12P:	descr = "12-bit Bayer RGRG/GBGB Packed"; break;
 	case V4L2_PIX_FMT_SBGGR16:	descr = "16-bit Bayer BGBG/GRGR"; break;
 	case V4L2_PIX_FMT_SGBRG16:	descr = "16-bit Bayer GBGB/RGRG"; break;
 	case V4L2_PIX_FMT_SGRBG16:	descr = "16-bit Bayer GRGR/BGBG"; break;
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 45cf735..185d6a0 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -603,6 +603,11 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_SGBRG12 v4l2_fourcc('G', 'B', '1', '2') /* 12  GBGB.. RGRG.. */
 #define V4L2_PIX_FMT_SGRBG12 v4l2_fourcc('B', 'A', '1', '2') /* 12  GRGR.. BGBG.. */
 #define V4L2_PIX_FMT_SRGGB12 v4l2_fourcc('R', 'G', '1', '2') /* 12  RGRG.. GBGB.. */
+	/* 12bit raw bayer packed, 6 bytes for every 4 pixels */
+#define V4L2_PIX_FMT_SBGGR12P v4l2_fourcc('p', 'B', 'C', 'C')
+#define V4L2_PIX_FMT_SGBRG12P v4l2_fourcc('p', 'G', 'C', 'C')
+#define V4L2_PIX_FMT_SGRBG12P v4l2_fourcc('p', 'g', 'C', 'C')
+#define V4L2_PIX_FMT_SRGGB12P v4l2_fourcc('p', 'R', 'C', 'C')
 #define V4L2_PIX_FMT_SBGGR16 v4l2_fourcc('B', 'Y', 'R', '2') /* 16  BGBG.. GRGR.. */
 #define V4L2_PIX_FMT_SGBRG16 v4l2_fourcc('G', 'B', '1', '6') /* 16  GBGB.. RGRG.. */
 #define V4L2_PIX_FMT_SGRBG16 v4l2_fourcc('G', 'R', '1', '6') /* 16  GRGR.. BGBG.. */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v3 04/23] dt-bindings: media: Binding document for Qualcomm Camera subsystem driver
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
                   ` (2 preceding siblings ...)
  2017-07-17 10:33 ` [PATCH v3 03/23] v4l: Add packed Bayer raw12 pixel formats Todor Tomov
@ 2017-07-17 10:33 ` Todor Tomov
  2017-07-20 10:13   ` Sakari Ailus
                     ` (2 more replies)
  2017-07-17 10:33 ` [PATCH v3 05/23] MAINTAINERS: Add " Todor Tomov
                   ` (20 subsequent siblings)
  24 siblings, 3 replies; 60+ messages in thread
From: Todor Tomov @ 2017-07-17 10:33 UTC (permalink / raw)
  To: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm
  Cc: Todor Tomov, Rob Herring, devicetree

Add DT binding document for Qualcomm Camera subsystem driver.

CC: Rob Herring <robh+dt@kernel.org>
CC: devicetree@vger.kernel.org
Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
---
 .../devicetree/bindings/media/qcom,camss.txt       | 191 +++++++++++++++++++++
 1 file changed, 191 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/qcom,camss.txt

diff --git a/Documentation/devicetree/bindings/media/qcom,camss.txt b/Documentation/devicetree/bindings/media/qcom,camss.txt
new file mode 100644
index 0000000..f698498
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/qcom,camss.txt
@@ -0,0 +1,191 @@
+Qualcomm Camera Subsystem
+
+* Properties
+
+- compatible:
+	Usage: required
+	Value type: <stringlist>
+	Definition: Should contain:
+		- "qcom,msm8916-camss"
+- reg:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: Register ranges as listed in the reg-names property.
+- reg-names:
+	Usage: required
+	Value type: <stringlist>
+	Definition: Should contain the following entries:
+		- "csiphy0"
+		- "csiphy0_clk_mux"
+		- "csiphy1"
+		- "csiphy1_clk_mux"
+		- "csid0"
+		- "csid1"
+		- "ispif"
+		- "csi_clk_mux"
+		- "vfe0"
+- interrupts:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: Interrupts as listed in the interrupt-names property.
+- interrupt-names:
+	Usage: required
+	Value type: <stringlist>
+	Definition: Should contain the following entries:
+		- "csiphy0"
+		- "csiphy1"
+		- "csid0"
+		- "csid1"
+		- "ispif"
+		- "vfe0"
+- power-domains:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: A phandle and power domain specifier pairs to the
+		    power domain which is responsible for collapsing
+		    and restoring power to the peripheral.
+- clocks:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: A list of phandle and clock specifier pairs as listed
+		    in clock-names property.
+- clock-names:
+	Usage: required
+	Value type: <stringlist>
+	Definition: Should contain the following entries:
+                - "camss_top_ahb"
+                - "ispif_ahb"
+                - "csiphy0_timer"
+                - "csiphy1_timer"
+                - "csi0_ahb"
+                - "csi0"
+                - "csi0_phy"
+                - "csi0_pix"
+                - "csi0_rdi"
+                - "csi1_ahb"
+                - "csi1"
+                - "csi1_phy"
+                - "csi1_pix"
+                - "csi1_rdi"
+                - "camss_ahb"
+                - "camss_vfe_vfe"
+                - "camss_csi_vfe"
+                - "iface"
+                - "bus"
+- vdda-supply:
+	Usage: required
+	Value type: <phandle>
+	Definition: A phandle to voltage supply for CSI2.
+- iommus:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: A list of phandle and IOMMU specifier pairs.
+
+* Nodes
+
+- ports:
+	Usage: required
+	Definition: As described in video-interfaces.txt in same directory.
+	Properties:
+		- reg:
+			Usage: required
+			Value type: <u32>
+			Definition: Selects CSI2 PHY interface - PHY0 or PHY1.
+	Endpoint node properties:
+		- clock-lanes:
+			Usage: required
+			Value type: <u32>
+			Definition: The clock lane.
+		- data-lanes:
+			Usage: required
+			Value type: <prop-encoded-array>
+			Definition: An array of data lanes.
+
+* An Example
+
+	camss: camss@1b00000 {
+		compatible = "qcom,msm8916-camss";
+		reg = <0x1b0ac00 0x200>,
+			<0x1b00030 0x4>,
+			<0x1b0b000 0x200>,
+			<0x1b00038 0x4>,
+			<0x1b08000 0x100>,
+			<0x1b08400 0x100>,
+			<0x1b0a000 0x500>,
+			<0x1b00020 0x10>,
+			<0x1b10000 0x1000>;
+		reg-names = "csiphy0",
+			"csiphy0_clk_mux",
+			"csiphy1",
+			"csiphy1_clk_mux",
+			"csid0",
+			"csid1",
+			"ispif",
+			"csi_clk_mux",
+			"vfe0";
+		interrupts = <GIC_SPI 78 0>,
+			<GIC_SPI 79 0>,
+			<GIC_SPI 51 0>,
+			<GIC_SPI 52 0>,
+			<GIC_SPI 55 0>,
+			<GIC_SPI 57 0>;
+		interrupt-names = "csiphy0",
+			"csiphy1",
+			"csid0",
+			"csid1",
+			"ispif",
+			"vfe0";
+		power-domains = <&gcc VFE_GDSC>;
+		clocks = <&gcc GCC_CAMSS_TOP_AHB_CLK>,
+			<&gcc GCC_CAMSS_ISPIF_AHB_CLK>,
+			<&gcc GCC_CAMSS_CSI0PHYTIMER_CLK>,
+			<&gcc GCC_CAMSS_CSI1PHYTIMER_CLK>,
+			<&gcc GCC_CAMSS_CSI0_AHB_CLK>,
+			<&gcc GCC_CAMSS_CSI0_CLK>,
+			<&gcc GCC_CAMSS_CSI0PHY_CLK>,
+			<&gcc GCC_CAMSS_CSI0PIX_CLK>,
+			<&gcc GCC_CAMSS_CSI0RDI_CLK>,
+			<&gcc GCC_CAMSS_CSI1_AHB_CLK>,
+			<&gcc GCC_CAMSS_CSI1_CLK>,
+			<&gcc GCC_CAMSS_CSI1PHY_CLK>,
+			<&gcc GCC_CAMSS_CSI1PIX_CLK>,
+			<&gcc GCC_CAMSS_CSI1RDI_CLK>,
+			<&gcc GCC_CAMSS_AHB_CLK>,
+			<&gcc GCC_CAMSS_VFE0_CLK>,
+			<&gcc GCC_CAMSS_CSI_VFE0_CLK>,
+			<&gcc GCC_CAMSS_VFE_AHB_CLK>,
+			<&gcc GCC_CAMSS_VFE_AXI_CLK>;
+                clock-names = "camss_top_ahb",
+                        "ispif_ahb",
+                        "csiphy0_timer",
+                        "csiphy1_timer",
+                        "csi0_ahb",
+                        "csi0",
+                        "csi0_phy",
+                        "csi0_pix",
+                        "csi0_rdi",
+                        "csi1_ahb",
+                        "csi1",
+                        "csi1_phy",
+                        "csi1_pix",
+                        "csi1_rdi",
+                        "camss_ahb",
+                        "camss_vfe_vfe",
+                        "camss_csi_vfe",
+                        "iface",
+                        "bus";
+		vdda-supply = <&pm8916_l2>;
+		iommus = <&apps_iommu 3>;
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			port@0 {
+				reg = <0>;
+				csiphy0_ep: endpoint {
+					clock-lanes = <1>;
+					data-lanes = <0 2>;
+					remote-endpoint = <&ov5645_ep>;
+				};
+			};
+		};
+	};
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v3 05/23] MAINTAINERS: Add Qualcomm Camera subsystem driver
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
                   ` (3 preceding siblings ...)
  2017-07-17 10:33 ` [PATCH v3 04/23] dt-bindings: media: Binding document for Qualcomm Camera subsystem driver Todor Tomov
@ 2017-07-17 10:33 ` Todor Tomov
  2017-07-17 10:33 ` [PATCH v3 06/23] doc: media/v4l-drivers: Add Qualcomm Camera Subsystem driver document Todor Tomov
                   ` (19 subsequent siblings)
  24 siblings, 0 replies; 60+ messages in thread
From: Todor Tomov @ 2017-07-17 10:33 UTC (permalink / raw)
  To: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm
  Cc: Todor Tomov

Add an entry for Qualcomm Camera subsystem driver.

Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 5a9f0f6..4cb978a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10887,6 +10887,14 @@ T:	git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git
 S:	Supported
 F:	drivers/net/wireless/ath/ath10k/
 
+QUALCOMM CAMERA SUBSYSTEM DRIVER
+M:	Todor Tomov <todor.tomov@linaro.org>
+L:	linux-media@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/media/qcom,camss.txt
+F:	Documentation/media/v4l-drivers/qcom_camss.rst
+F:	drivers/media/platform/qcom/camss-8x16/
+
 QUALCOMM EMAC GIGABIT ETHERNET DRIVER
 M:	Timur Tabi <timur@codeaurora.org>
 L:	netdev@vger.kernel.org
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v3 06/23] doc: media/v4l-drivers: Add Qualcomm Camera Subsystem driver document
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
                   ` (4 preceding siblings ...)
  2017-07-17 10:33 ` [PATCH v3 05/23] MAINTAINERS: Add " Todor Tomov
@ 2017-07-17 10:33 ` Todor Tomov
  2017-07-17 10:33 ` [PATCH v3 07/23] media: camss: Add CSIPHY files Todor Tomov
                   ` (18 subsequent siblings)
  24 siblings, 0 replies; 60+ messages in thread
From: Todor Tomov @ 2017-07-17 10:33 UTC (permalink / raw)
  To: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm
  Cc: Todor Tomov

Add a document to describe Qualcomm Camera Subsystem driver.

Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
---
 Documentation/media/v4l-drivers/qcom_camss.rst | 124 +++++++++++++++++++++++++
 1 file changed, 124 insertions(+)
 create mode 100644 Documentation/media/v4l-drivers/qcom_camss.rst

diff --git a/Documentation/media/v4l-drivers/qcom_camss.rst b/Documentation/media/v4l-drivers/qcom_camss.rst
new file mode 100644
index 0000000..4707ea7
--- /dev/null
+++ b/Documentation/media/v4l-drivers/qcom_camss.rst
@@ -0,0 +1,124 @@
+.. include:: <isonum.txt>
+
+Qualcomm Camera Subsystem driver
+================================
+
+Introduction
+------------
+
+This file documents the Qualcomm Camera Subsystem driver located under
+drivers/media/platform/qcom/camss-8x16.
+
+The current version of the driver supports the Camera Subsystem found on
+Qualcomm MSM8916 and APQ8016 processors.
+
+The driver implements V4L2, Media controller and V4L2 subdev interfaces.
+Camera sensor using V4L2 subdev interface in the kernel is supported.
+
+The driver is implemented using as a reference the Qualcomm Camera Subsystem
+driver for Android as found in Code Aurora [#f1]_.
+
+
+Qualcomm Camera Subsystem hardware
+----------------------------------
+
+The Camera Subsystem hardware found on 8x16 processors and supported by the
+driver consists of:
+
+- 2 CSIPHY modules. They handle the Physical layer of the CSI2 receivers.
+  A separate camera sensor can be connected to each of the CSIPHY module;
+- 2 CSID (CSI Decoder) modules. They handle the Protocol and Application layer
+  of the CSI2 receivers. A CSID can decode data stream from any of the CSIPHY.
+  Each CSID also contains a TG (Test Generator) block which can generate
+  artificial input data for test purposes;
+- ISPIF (ISP Interface) module. Handles the routing of the data streams from
+  the CSIDs to the inputs of the VFE;
+- VFE (Video Front End) module. Contains a pipeline of image processing hardware
+  blocks. The VFE has different input interfaces. The PIX input interface feeds
+  the input data to the image processing pipeline. Three RDI input interfaces
+  bypass the image processing pipeline. The VFE also contains the AXI bus
+  interface which writes the output data to memory.
+
+
+Supported functionality
+-----------------------
+
+The current version of the driver supports:
+
+- input from camera sensor via CSIPHY;
+- generation of test input data by the TG in CSID;
+- raw dump of the input data to memory. RDI interface of VFE is supported.
+  PIX interface (ISP processing, statistics engines, resize/crop, format
+  conversion) is not supported in the current version;
+- concurrent and independent usage of two data inputs - could be camera sensors
+  and/or TG.
+
+
+Driver Architecture and Design
+------------------------------
+
+The driver implements the V4L2 subdev interface. With the goal to model the
+hardware links between the modules and to expose a clean, logical and usable
+interface, the driver is split into V4L2 sub-devices as follows:
+
+- 2 CSIPHY sub-devices - each CSIPHY is represented by a single sub-device;
+- 2 CSID sub-devices - each CSID is represented by a single sub-device;
+- 2 ISPIF sub-devices - ISPIF is represented by a number of sub-devices equal
+  to the number of CSID sub-devices;
+- 3 VFE sub-devices - VFE is represented by a number of sub-devices equal to
+  the number of RDI input interfaces.
+
+The considerations to split the driver in this particular way are as follows:
+
+- representing CSIPHY and CSID modules by a separate sub-device for each module
+  allows to model the hardware links between these modules;
+- representing VFE by a separate sub-devices for each RDI input interface allows
+  to use the three RDI interfaces concurently and independently as this is
+  supported by the hardware;
+- representing ISPIF by a number of sub-devices equal to the number of CSID
+  sub-devices allows to create linear media controller pipelines when using two
+  cameras simultaneously. This avoids branches in the pipelines which otherwise
+  will require a) userspace and b) media framework (e.g. power on/off
+  operations) to  make assumptions about the data flow from a sink pad to a
+  source pad on a single media entity.
+
+Each VFE sub-device is linked to a separate video device node.
+
+The complete list of the media entities (V4L2 sub-devices and video device
+nodes) is as follows:
+
+- msm_csiphy0
+- msm_csiphy1
+- msm_csid0
+- msm_csid1
+- msm_ispif0
+- msm_ispif1
+- msm_vfe0_rdi0
+- msm_vfe0_video0
+- msm_vfe0_rdi1
+- msm_vfe0_video1
+- msm_vfe0_rdi2
+- msm_vfe0_video2
+
+
+Implementation
+--------------
+
+Runtime configuration of the hardware (updating settings while streaming) is
+not required to implement the currently supported functionality. The complete
+configuration on each hardware module is applied on STREAMON ioctl based on
+the current active media links, formats and controls set.
+
+
+Documentation
+-------------
+
+APQ8016 Specification:
+https://developer.qualcomm.com/download/sd410/snapdragon-410-processor-device-specification.pdf
+Referenced 2016-11-24.
+
+
+References
+----------
+
+.. [#f1] https://source.codeaurora.org/quic/la/kernel/msm-3.10/
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v3 07/23] media: camss: Add CSIPHY files
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
                   ` (5 preceding siblings ...)
  2017-07-17 10:33 ` [PATCH v3 06/23] doc: media/v4l-drivers: Add Qualcomm Camera Subsystem driver document Todor Tomov
@ 2017-07-17 10:33 ` Todor Tomov
  2017-07-20 12:15   ` Sakari Ailus
  2017-07-17 10:33 ` [PATCH v3 08/23] media: camss: Add CSID files Todor Tomov
                   ` (17 subsequent siblings)
  24 siblings, 1 reply; 60+ messages in thread
From: Todor Tomov @ 2017-07-17 10:33 UTC (permalink / raw)
  To: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm
  Cc: Todor Tomov

These files control the CSIPHY modules which are responsible for the physical
layer of the CSI2 receivers.

Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
---
 .../media/platform/qcom/camss-8x16/camss-csiphy.c  | 816 +++++++++++++++++++++
 .../media/platform/qcom/camss-8x16/camss-csiphy.h  |  77 ++
 2 files changed, 893 insertions(+)
 create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-csiphy.c
 create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-csiphy.h

diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c b/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c
new file mode 100644
index 0000000..4e7ddc4
--- /dev/null
+++ b/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c
@@ -0,0 +1,816 @@
+/*
+ * camss-csiphy.c
+ *
+ * Qualcomm MSM Camera Subsystem - CSIPHY Module
+ *
+ * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2016-2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <media/media-entity.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include "camss-csiphy.h"
+#include "camss.h"
+
+#define MSM_CSIPHY_NAME "msm_csiphy"
+
+#define CAMSS_CSI_PHY_LNn_CFG2(n)		(0x004 + 0x40 * (n))
+#define CAMSS_CSI_PHY_LNn_CFG3(n)		(0x008 + 0x40 * (n))
+#define CAMSS_CSI_PHY_GLBL_RESET		0x140
+#define CAMSS_CSI_PHY_GLBL_PWR_CFG		0x144
+#define CAMSS_CSI_PHY_GLBL_IRQ_CMD		0x164
+#define CAMSS_CSI_PHY_HW_VERSION		0x188
+#define CAMSS_CSI_PHY_INTERRUPT_STATUSn(n)	(0x18c + 0x4 * (n))
+#define CAMSS_CSI_PHY_INTERRUPT_MASKn(n)	(0x1ac + 0x4 * (n))
+#define CAMSS_CSI_PHY_INTERRUPT_CLEARn(n)	(0x1cc + 0x4 * (n))
+#define CAMSS_CSI_PHY_GLBL_T_INIT_CFG0		0x1ec
+#define CAMSS_CSI_PHY_T_WAKEUP_CFG0		0x1f4
+
+static const struct {
+	u32 code;
+	u8 bpp;
+} csiphy_formats[] = {
+	{
+		MEDIA_BUS_FMT_UYVY8_2X8,
+		8,
+	},
+	{
+		MEDIA_BUS_FMT_VYUY8_2X8,
+		8,
+	},
+	{
+		MEDIA_BUS_FMT_YUYV8_2X8,
+		8,
+	},
+	{
+		MEDIA_BUS_FMT_YVYU8_2X8,
+		8,
+	},
+	{
+		MEDIA_BUS_FMT_SBGGR8_1X8,
+		8,
+	},
+	{
+		MEDIA_BUS_FMT_SGBRG8_1X8,
+		8,
+	},
+	{
+		MEDIA_BUS_FMT_SGRBG8_1X8,
+		8,
+	},
+	{
+		MEDIA_BUS_FMT_SRGGB8_1X8,
+		8,
+	},
+	{
+		MEDIA_BUS_FMT_SBGGR10_1X10,
+		10,
+	},
+	{
+		MEDIA_BUS_FMT_SGBRG10_1X10,
+		10,
+	},
+	{
+		MEDIA_BUS_FMT_SGRBG10_1X10,
+		10,
+	},
+	{
+		MEDIA_BUS_FMT_SRGGB10_1X10,
+		10,
+	},
+	{
+		MEDIA_BUS_FMT_SBGGR12_1X12,
+		12,
+	},
+	{
+		MEDIA_BUS_FMT_SGBRG12_1X12,
+		12,
+	},
+	{
+		MEDIA_BUS_FMT_SGRBG12_1X12,
+		12,
+	},
+	{
+		MEDIA_BUS_FMT_SRGGB12_1X12,
+		12,
+	}
+};
+
+/*
+ * csiphy_get_bpp - map media bus format to bits per pixel
+ * @code: media bus format code
+ *
+ * Return number of bits per pixel
+ */
+static u8 csiphy_get_bpp(u32 code)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(csiphy_formats); i++)
+		if (code == csiphy_formats[i].code)
+			break;
+
+	return csiphy_formats[i].bpp;
+}
+
+/*
+ * csiphy_isr - CSIPHY module interrupt handler
+ * @irq: Interrupt line
+ * @dev: CSIPHY device
+ *
+ * Return IRQ_HANDLED on success
+ */
+static irqreturn_t csiphy_isr(int irq, void *dev)
+{
+	struct csiphy_device *csiphy = dev;
+	u8 i;
+
+	for (i = 0; i < 8; i++) {
+		u8 val = readl_relaxed(csiphy->base +
+				       CAMSS_CSI_PHY_INTERRUPT_STATUSn(i));
+		writel_relaxed(val, csiphy->base +
+			       CAMSS_CSI_PHY_INTERRUPT_CLEARn(i));
+		writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_IRQ_CMD);
+		writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_IRQ_CMD);
+		writel_relaxed(0x0, csiphy->base +
+			       CAMSS_CSI_PHY_INTERRUPT_CLEARn(i));
+	}
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * csiphy_reset - Perform software reset on CSIPHY module
+ * @csiphy: CSIPHY device
+ */
+static void csiphy_reset(struct csiphy_device *csiphy)
+{
+	writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET);
+	usleep_range(5000, 8000);
+	writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET);
+}
+
+/*
+ * csiphy_set_power - Power on/off CSIPHY module
+ * @sd: CSIPHY V4L2 subdevice
+ * @on: Requested power state
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int csiphy_set_power(struct v4l2_subdev *sd, int on)
+{
+	struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
+	struct device *dev = to_device_index(csiphy, csiphy->id);
+	int ret;
+
+	if (on) {
+		u8 hw_version;
+
+		ret = camss_enable_clocks(csiphy->nclocks, csiphy->clock, dev);
+		if (ret < 0)
+			return ret;
+
+		enable_irq(csiphy->irq);
+
+		csiphy_reset(csiphy);
+
+		hw_version = readl_relaxed(csiphy->base +
+					   CAMSS_CSI_PHY_HW_VERSION);
+		dev_dbg(dev, "CSIPHY HW Version = 0x%02x\n", hw_version);
+	} else {
+		disable_irq(csiphy->irq);
+
+		camss_disable_clocks(csiphy->nclocks, csiphy->clock);
+	}
+
+	return 0;
+}
+
+/*
+ * csiphy_get_lane_mask - Calculate CSI2 lane mask configuration parameter
+ * @lane_cfg - CSI2 lane configuration
+ *
+ * Return lane mask
+ */
+static u8 csiphy_get_lane_mask(struct csiphy_lanes_cfg *lane_cfg)
+{
+	u8 lane_mask;
+	int i;
+
+	lane_mask = 1 << lane_cfg->clk.pos;
+
+	for (i = 0; i < lane_cfg->num_data; i++)
+		lane_mask |= 1 << lane_cfg->data[i].pos;
+
+	return lane_mask;
+}
+
+/*
+ * csiphy_settle_cnt_calc - Calculate settle count value
+ * @csiphy: CSIPHY device
+ *
+ * Helper function to calculate settle count value. This is
+ * based on the CSI2 T_hs_settle parameter which in turn
+ * is calculated based on the CSI2 transmitter pixel clock
+ * frequency.
+ *
+ * Return settle count value or 0 if the CSI2 pixel clock
+ * frequency is not available
+ */
+static u8 csiphy_settle_cnt_calc(struct csiphy_device *csiphy)
+{
+	u8 bpp = csiphy_get_bpp(
+			csiphy->fmt[MSM_CSIPHY_PAD_SINK].code);
+	u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data;
+	u32 pixel_clock; /* Hz */
+	u32 mipi_clock; /* Hz */
+	u32 ui; /* ps */
+	u32 timer_period; /* ps */
+	u32 t_hs_prepare_max; /* ps */
+	u32 t_hs_prepare_zero_min; /* ps */
+	u32 t_hs_settle; /* ps */
+	u8 settle_cnt;
+	int ret;
+
+	ret = camss_get_pixel_clock(&csiphy->subdev.entity, &pixel_clock);
+	if (ret) {
+		dev_err(to_device_index(csiphy, csiphy->id),
+			"Cannot get CSI2 transmitter's pixel clock\n");
+		return 0;
+	}
+
+	mipi_clock = pixel_clock * bpp / (2 * num_lanes);
+	ui = 1000000000000 / (mipi_clock * 2);
+	t_hs_prepare_max = 85000 + 6 * ui;
+	t_hs_prepare_zero_min = 145000 + 10 * ui;
+	t_hs_settle = (t_hs_prepare_max + t_hs_prepare_zero_min) / 2;
+
+	timer_period = 1000000000000 / csiphy->timer_clk_rate;
+	settle_cnt = t_hs_settle / timer_period;
+
+	return settle_cnt;
+}
+
+/*
+ * csiphy_stream_on - Enable streaming on CSIPHY module
+ * @csiphy: CSIPHY device
+ *
+ * Helper function to enable streaming on CSIPHY module.
+ * Main configuration of CSIPHY module is also done here.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int csiphy_stream_on(struct csiphy_device *csiphy)
+{
+	struct csiphy_config *cfg = &csiphy->cfg;
+	u8 lane_mask = csiphy_get_lane_mask(&cfg->csi2->lane_cfg);
+	u8 settle_cnt;
+	u8 val;
+	int i = 0;
+
+	settle_cnt = csiphy_settle_cnt_calc(csiphy);
+	if (!settle_cnt)
+		return -EINVAL;
+
+	val = readl_relaxed(csiphy->base_clk_mux);
+	if (cfg->combo_mode && (lane_mask & 0x18) == 0x18) {
+		val &= ~0xf0;
+		val |= cfg->csid_id << 4;
+	} else {
+		val &= ~0xf;
+		val |= cfg->csid_id;
+	}
+	writel_relaxed(val, csiphy->base_clk_mux);
+
+	writel_relaxed(0x1, csiphy->base +
+		       CAMSS_CSI_PHY_GLBL_T_INIT_CFG0);
+	writel_relaxed(0x1, csiphy->base +
+		       CAMSS_CSI_PHY_T_WAKEUP_CFG0);
+
+	val = 0x1;
+	val |= lane_mask << 1;
+	writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG);
+
+	val = cfg->combo_mode << 4;
+	writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET);
+
+	while (lane_mask) {
+		if (lane_mask & 0x1) {
+			writel_relaxed(0x10, csiphy->base +
+				       CAMSS_CSI_PHY_LNn_CFG2(i));
+			writel_relaxed(settle_cnt, csiphy->base +
+				       CAMSS_CSI_PHY_LNn_CFG3(i));
+			writel_relaxed(0x3f, csiphy->base +
+				       CAMSS_CSI_PHY_INTERRUPT_MASKn(i));
+			writel_relaxed(0x3f, csiphy->base +
+				       CAMSS_CSI_PHY_INTERRUPT_CLEARn(i));
+		}
+
+		lane_mask >>= 1;
+		i++;
+	}
+
+	return 0;
+}
+
+/*
+ * csiphy_stream_off - Disable streaming on CSIPHY module
+ * @csiphy: CSIPHY device
+ *
+ * Helper function to disable streaming on CSIPHY module
+ */
+static void csiphy_stream_off(struct csiphy_device *csiphy)
+{
+	u8 lane_mask = csiphy_get_lane_mask(&csiphy->cfg.csi2->lane_cfg);
+	int i = 0;
+
+	while (lane_mask) {
+		if (lane_mask & 0x1)
+			writel_relaxed(0x0, csiphy->base +
+				       CAMSS_CSI_PHY_LNn_CFG2(i));
+
+		lane_mask >>= 1;
+		i++;
+	}
+
+	writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG);
+}
+
+
+/*
+ * csiphy_set_stream - Enable/disable streaming on CSIPHY module
+ * @sd: CSIPHY V4L2 subdevice
+ * @enable: Requested streaming state
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int csiphy_set_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
+	int ret = 0;
+
+	if (enable)
+		ret = csiphy_stream_on(csiphy);
+	else
+		csiphy_stream_off(csiphy);
+
+	return ret;
+}
+
+/*
+ * __csiphy_get_format - Get pointer to format structure
+ * @csiphy: CSIPHY device
+ * @cfg: V4L2 subdev pad configuration
+ * @pad: pad from which format is requested
+ * @which: TRY or ACTIVE format
+ *
+ * Return pointer to TRY or ACTIVE format structure
+ */
+static struct v4l2_mbus_framefmt *
+__csiphy_get_format(struct csiphy_device *csiphy,
+		    struct v4l2_subdev_pad_config *cfg,
+		    unsigned int pad,
+		    enum v4l2_subdev_format_whence which)
+{
+	if (which == V4L2_SUBDEV_FORMAT_TRY)
+		return v4l2_subdev_get_try_format(&csiphy->subdev, cfg, pad);
+
+	return &csiphy->fmt[pad];
+}
+
+/*
+ * csiphy_try_format - Handle try format by pad subdev method
+ * @csiphy: CSIPHY device
+ * @cfg: V4L2 subdev pad configuration
+ * @pad: pad on which format is requested
+ * @fmt: pointer to v4l2 format structure
+ * @which: wanted subdev format
+ */
+static void csiphy_try_format(struct csiphy_device *csiphy,
+			      struct v4l2_subdev_pad_config *cfg,
+			      unsigned int pad,
+			      struct v4l2_mbus_framefmt *fmt,
+			      enum v4l2_subdev_format_whence which)
+{
+	unsigned int i;
+
+	switch (pad) {
+	case MSM_CSIPHY_PAD_SINK:
+		/* Set format on sink pad */
+
+		for (i = 0; i < ARRAY_SIZE(csiphy_formats); i++)
+			if (fmt->code == csiphy_formats[i].code)
+				break;
+
+		/* If not found, use UYVY as default */
+		if (i >= ARRAY_SIZE(csiphy_formats))
+			fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
+
+		fmt->width = clamp_t(u32, fmt->width, 1, 8191);
+		fmt->height = clamp_t(u32, fmt->height, 1, 8191);
+
+		if (fmt->field == V4L2_FIELD_ANY)
+			fmt->field = V4L2_FIELD_NONE;
+
+		fmt->colorspace = V4L2_COLORSPACE_SRGB;
+
+		break;
+
+	case MSM_CSIPHY_PAD_SRC:
+		/* Set and return a format same as sink pad */
+
+		*fmt = *__csiphy_get_format(csiphy, cfg, MSM_CSID_PAD_SINK,
+					    which);
+
+		break;
+	}
+}
+
+/*
+ * csiphy_enum_mbus_code - Handle pixel format enumeration
+ * @sd: CSIPHY V4L2 subdevice
+ * @cfg: V4L2 subdev pad configuration
+ * @code: pointer to v4l2_subdev_mbus_code_enum structure
+ * return -EINVAL or zero on success
+ */
+static int csiphy_enum_mbus_code(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt *format;
+
+	if (code->pad == MSM_CSIPHY_PAD_SINK) {
+		if (code->index >= ARRAY_SIZE(csiphy_formats))
+			return -EINVAL;
+
+		code->code = csiphy_formats[code->index].code;
+	} else {
+		if (code->index > 0)
+			return -EINVAL;
+
+		format = __csiphy_get_format(csiphy, cfg, MSM_CSIPHY_PAD_SINK,
+					     code->which);
+
+		code->code = format->code;
+	}
+
+	return 0;
+}
+
+/*
+ * csiphy_enum_frame_size - Handle frame size enumeration
+ * @sd: CSIPHY V4L2 subdevice
+ * @cfg: V4L2 subdev pad configuration
+ * @fse: pointer to v4l2_subdev_frame_size_enum structure
+ * return -EINVAL or zero on success
+ */
+static int csiphy_enum_frame_size(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt format;
+
+	if (fse->index != 0)
+		return -EINVAL;
+
+	format.code = fse->code;
+	format.width = 1;
+	format.height = 1;
+	csiphy_try_format(csiphy, cfg, fse->pad, &format, fse->which);
+	fse->min_width = format.width;
+	fse->min_height = format.height;
+
+	if (format.code != fse->code)
+		return -EINVAL;
+
+	format.code = fse->code;
+	format.width = -1;
+	format.height = -1;
+	csiphy_try_format(csiphy, cfg, fse->pad, &format, fse->which);
+	fse->max_width = format.width;
+	fse->max_height = format.height;
+
+	return 0;
+}
+
+/*
+ * csiphy_get_format - Handle get format by pads subdev method
+ * @sd: CSIPHY V4L2 subdevice
+ * @cfg: V4L2 subdev pad configuration
+ * @fmt: pointer to v4l2 subdev format structure
+ *
+ * Return -EINVAL or zero on success
+ */
+static int csiphy_get_format(struct v4l2_subdev *sd,
+			     struct v4l2_subdev_pad_config *cfg,
+			     struct v4l2_subdev_format *fmt)
+{
+	struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt *format;
+
+	format = __csiphy_get_format(csiphy, cfg, fmt->pad, fmt->which);
+	if (format == NULL)
+		return -EINVAL;
+
+	fmt->format = *format;
+
+	return 0;
+}
+
+/*
+ * csiphy_set_format - Handle set format by pads subdev method
+ * @sd: CSIPHY V4L2 subdevice
+ * @cfg: V4L2 subdev pad configuration
+ * @fmt: pointer to v4l2 subdev format structure
+ *
+ * Return -EINVAL or zero on success
+ */
+static int csiphy_set_format(struct v4l2_subdev *sd,
+			     struct v4l2_subdev_pad_config *cfg,
+			     struct v4l2_subdev_format *fmt)
+{
+	struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt *format;
+
+	format = __csiphy_get_format(csiphy, cfg, fmt->pad, fmt->which);
+	if (format == NULL)
+		return -EINVAL;
+
+	csiphy_try_format(csiphy, cfg, fmt->pad, &fmt->format, fmt->which);
+	*format = fmt->format;
+
+	/* Propagate the format from sink to source */
+	if (fmt->pad == MSM_CSIPHY_PAD_SINK) {
+		format = __csiphy_get_format(csiphy, cfg, MSM_CSIPHY_PAD_SRC,
+					     fmt->which);
+
+		*format = fmt->format;
+		csiphy_try_format(csiphy, cfg, MSM_CSIPHY_PAD_SRC, format,
+				  fmt->which);
+	}
+
+	return 0;
+}
+
+/*
+ * csiphy_init_formats - Initialize formats on all pads
+ * @sd: CSIPHY V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int csiphy_init_formats(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_fh *fh)
+{
+	struct v4l2_subdev_format format = {
+		.pad = MSM_CSIPHY_PAD_SINK,
+		.which = fh ? V4L2_SUBDEV_FORMAT_TRY :
+			      V4L2_SUBDEV_FORMAT_ACTIVE,
+		.format = {
+			.code = MEDIA_BUS_FMT_UYVY8_2X8,
+			.width = 1920,
+			.height = 1080
+		}
+	};
+
+	return csiphy_set_format(sd, fh ? fh->pad : NULL, &format);
+}
+
+/*
+ * msm_csiphy_subdev_init - Initialize CSIPHY device structure and resources
+ * @csiphy: CSIPHY device
+ * @res: CSIPHY module resources table
+ * @id: CSIPHY module id
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int msm_csiphy_subdev_init(struct csiphy_device *csiphy,
+			   const struct resources *res, u8 id)
+{
+	struct device *dev = to_device_index(csiphy, id);
+	struct platform_device *pdev = to_platform_device(dev);
+	struct resource *r;
+	int i;
+	int ret;
+
+	csiphy->id = id;
+	csiphy->cfg.combo_mode = 0;
+
+	/* Memory */
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]);
+	csiphy->base = devm_ioremap_resource(dev, r);
+	if (IS_ERR(csiphy->base)) {
+		dev_err(dev, "could not map memory\n");
+		return PTR_ERR(csiphy->base);
+	}
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[1]);
+	csiphy->base_clk_mux = devm_ioremap_resource(dev, r);
+	if (IS_ERR(csiphy->base_clk_mux)) {
+		dev_err(dev, "could not map memory\n");
+		return PTR_ERR(csiphy->base_clk_mux);
+	}
+
+	/* Interrupt */
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+					 res->interrupt[0]);
+	if (!r) {
+		dev_err(dev, "missing IRQ\n");
+		return -EINVAL;
+	}
+
+	csiphy->irq = r->start;
+	snprintf(csiphy->irq_name, sizeof(csiphy->irq_name), "%s_%s%d",
+		 dev_name(dev), MSM_CSIPHY_NAME, csiphy->id);
+	ret = devm_request_irq(dev, csiphy->irq, csiphy_isr,
+			       IRQF_TRIGGER_RISING, csiphy->irq_name, csiphy);
+	if (ret < 0) {
+		dev_err(dev, "request_irq failed: %d\n", ret);
+		return ret;
+	}
+
+	disable_irq(csiphy->irq);
+
+	/* Clocks */
+
+	csiphy->nclocks = 0;
+	while (res->clock[csiphy->nclocks])
+		csiphy->nclocks++;
+
+	csiphy->clock = devm_kzalloc(dev, csiphy->nclocks *
+				     sizeof(*csiphy->clock), GFP_KERNEL);
+	if (!csiphy->clock)
+		return -ENOMEM;
+
+	for (i = 0; i < csiphy->nclocks; i++) {
+		csiphy->clock[i] = devm_clk_get(dev, res->clock[i]);
+		if (IS_ERR(csiphy->clock[i]))
+			return PTR_ERR(csiphy->clock[i]);
+
+		if (res->clock_rate[i]) {
+			long clk_rate = clk_round_rate(csiphy->clock[i],
+						       res->clock_rate[i]);
+			if (clk_rate < 0) {
+				dev_err(to_device_index(csiphy, csiphy->id),
+					"clk round rate failed: %ld\n",
+					clk_rate);
+				return -EINVAL;
+			}
+			ret = clk_set_rate(csiphy->clock[i], clk_rate);
+			if (ret < 0) {
+				dev_err(to_device_index(csiphy, csiphy->id),
+					"clk set rate failed: %d\n", ret);
+				return ret;
+			}
+
+			if (!strcmp(res->clock[i], "csiphy0_timer") ||
+					!strcmp(res->clock[i], "csiphy1_timer"))
+				csiphy->timer_clk_rate = clk_rate;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * csiphy_link_setup - Setup CSIPHY connections
+ * @entity: Pointer to media entity structure
+ * @local: Pointer to local pad
+ * @remote: Pointer to remote pad
+ * @flags: Link flags
+ *
+ * Rreturn 0 on success
+ */
+static int csiphy_link_setup(struct media_entity *entity,
+			     const struct media_pad *local,
+			     const struct media_pad *remote, u32 flags)
+{
+	if ((local->flags & MEDIA_PAD_FL_SOURCE) &&
+	    (flags & MEDIA_LNK_FL_ENABLED)) {
+		struct v4l2_subdev *sd;
+		struct csiphy_device *csiphy;
+		struct csid_device *csid;
+
+		if (media_entity_remote_pad(local))
+			return -EBUSY;
+
+		sd = media_entity_to_v4l2_subdev(entity);
+		csiphy = v4l2_get_subdevdata(sd);
+
+		sd = media_entity_to_v4l2_subdev(remote->entity);
+		csid = v4l2_get_subdevdata(sd);
+
+		csiphy->cfg.csid_id = csid->id;
+	}
+
+	return 0;
+}
+
+static const struct v4l2_subdev_core_ops csiphy_core_ops = {
+	.s_power = csiphy_set_power,
+};
+
+static const struct v4l2_subdev_video_ops csiphy_video_ops = {
+	.s_stream = csiphy_set_stream,
+};
+
+static const struct v4l2_subdev_pad_ops csiphy_pad_ops = {
+	.enum_mbus_code = csiphy_enum_mbus_code,
+	.enum_frame_size = csiphy_enum_frame_size,
+	.get_fmt = csiphy_get_format,
+	.set_fmt = csiphy_set_format,
+};
+
+static const struct v4l2_subdev_ops csiphy_v4l2_ops = {
+	.core = &csiphy_core_ops,
+	.video = &csiphy_video_ops,
+	.pad = &csiphy_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops csiphy_v4l2_internal_ops = {
+	.open = csiphy_init_formats,
+};
+
+static const struct media_entity_operations csiphy_media_ops = {
+	.link_setup = csiphy_link_setup,
+	.link_validate = v4l2_subdev_link_validate,
+};
+
+/*
+ * msm_csiphy_register_entity - Register subdev node for CSIPHY module
+ * @csiphy: CSIPHY device
+ * @v4l2_dev: V4L2 device
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int msm_csiphy_register_entity(struct csiphy_device *csiphy,
+			       struct v4l2_device *v4l2_dev)
+{
+	struct v4l2_subdev *sd = &csiphy->subdev;
+	struct media_pad *pads = csiphy->pads;
+	struct device *dev = to_device_index(csiphy, csiphy->id);
+	int ret;
+
+	v4l2_subdev_init(sd, &csiphy_v4l2_ops);
+	sd->internal_ops = &csiphy_v4l2_internal_ops;
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d",
+		 MSM_CSIPHY_NAME, csiphy->id);
+	v4l2_set_subdevdata(sd, csiphy);
+
+	ret = csiphy_init_formats(sd, NULL);
+	if (ret < 0) {
+		dev_err(dev, "Failed to init format: %d\n", ret);
+		return ret;
+	}
+
+	pads[MSM_CSIPHY_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+	pads[MSM_CSIPHY_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
+
+	sd->entity.function = MEDIA_ENT_F_IO_V4L;
+	sd->entity.ops = &csiphy_media_ops;
+	ret = media_entity_pads_init(&sd->entity, MSM_CSIPHY_PADS_NUM, pads);
+	if (ret < 0) {
+		dev_err(dev, "Failed to init media entity: %d\n", ret);
+		return ret;
+	}
+
+	ret = v4l2_device_register_subdev(v4l2_dev, sd);
+	if (ret < 0) {
+		dev_err(dev, "Failed to register subdev: %d\n", ret);
+		media_entity_cleanup(&sd->entity);
+	}
+
+	return ret;
+}
+
+/*
+ * msm_csiphy_unregister_entity - Unregister CSIPHY module subdev node
+ * @csiphy: CSIPHY device
+ */
+void msm_csiphy_unregister_entity(struct csiphy_device *csiphy)
+{
+	v4l2_device_unregister_subdev(&csiphy->subdev);
+}
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csiphy.h b/drivers/media/platform/qcom/camss-8x16/camss-csiphy.h
new file mode 100644
index 0000000..a6f23ed
--- /dev/null
+++ b/drivers/media/platform/qcom/camss-8x16/camss-csiphy.h
@@ -0,0 +1,77 @@
+/*
+ * camss-csiphy.h
+ *
+ * Qualcomm MSM Camera Subsystem - CSIPHY Module
+ *
+ * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2016-2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef QC_MSM_CAMSS_CSIPHY_H
+#define QC_MSM_CAMSS_CSIPHY_H
+
+#include <linux/clk.h>
+#include <media/media-entity.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mediabus.h>
+#include <media/v4l2-subdev.h>
+
+#define MSM_CSIPHY_PAD_SINK 0
+#define MSM_CSIPHY_PAD_SRC 1
+#define MSM_CSIPHY_PADS_NUM 2
+
+struct csiphy_lane {
+	u8 pos;
+	u8 pol;
+};
+
+struct csiphy_lanes_cfg {
+	int num_data;
+	struct csiphy_lane *data;
+	struct csiphy_lane clk;
+};
+
+struct csiphy_csi2_cfg {
+	struct csiphy_lanes_cfg lane_cfg;
+};
+
+struct csiphy_config {
+	u8 combo_mode;
+	u8 csid_id;
+	struct csiphy_csi2_cfg *csi2;
+};
+
+struct csiphy_device {
+	u8 id;
+	struct v4l2_subdev subdev;
+	struct media_pad pads[MSM_CSIPHY_PADS_NUM];
+	void __iomem *base;
+	void __iomem *base_clk_mux;
+	u32 irq;
+	char irq_name[30];
+	struct clk **clock;
+	int nclocks;
+	long timer_clk_rate;
+	struct csiphy_config cfg;
+	struct v4l2_mbus_framefmt fmt[MSM_CSIPHY_PADS_NUM];
+};
+
+struct resources;
+
+int msm_csiphy_subdev_init(struct csiphy_device *csiphy,
+			   const struct resources *res, u8 id);
+
+int msm_csiphy_register_entity(struct csiphy_device *csiphy,
+			       struct v4l2_device *v4l2_dev);
+
+void msm_csiphy_unregister_entity(struct csiphy_device *csiphy);
+
+#endif /* QC_MSM_CAMSS_CSIPHY_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v3 08/23] media: camss: Add CSID files
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
                   ` (6 preceding siblings ...)
  2017-07-17 10:33 ` [PATCH v3 07/23] media: camss: Add CSIPHY files Todor Tomov
@ 2017-07-17 10:33 ` Todor Tomov
  2017-07-20 14:13   ` Sakari Ailus
  2017-07-17 10:33 ` [PATCH v3 09/23] media: camss: Add ISPIF files Todor Tomov
                   ` (16 subsequent siblings)
  24 siblings, 1 reply; 60+ messages in thread
From: Todor Tomov @ 2017-07-17 10:33 UTC (permalink / raw)
  To: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm
  Cc: Todor Tomov

These files control the CSID modules which handle the protocol and application
layer of the CSI2 receivers.

Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
---
 .../media/platform/qcom/camss-8x16/camss-csid.c    | 1073 ++++++++++++++++++++
 .../media/platform/qcom/camss-8x16/camss-csid.h    |   82 ++
 2 files changed, 1155 insertions(+)
 create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-csid.c
 create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-csid.h

diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csid.c b/drivers/media/platform/qcom/camss-8x16/camss-csid.c
new file mode 100644
index 0000000..2bf3415
--- /dev/null
+++ b/drivers/media/platform/qcom/camss-8x16/camss-csid.c
@@ -0,0 +1,1073 @@
+/*
+ * camss-csid.c
+ *
+ * Qualcomm MSM Camera Subsystem - CSID Module
+ *
+ * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2015-2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <media/media-entity.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include "camss-csid.h"
+#include "camss.h"
+
+#define MSM_CSID_NAME "msm_csid"
+
+#define CAMSS_CSID_HW_VERSION		0x0
+#define CAMSS_CSID_CORE_CTRL_0		0x004
+#define CAMSS_CSID_CORE_CTRL_1		0x008
+#define CAMSS_CSID_RST_CMD		0x00c
+#define CAMSS_CSID_CID_LUT_VC_n(n)	(0x010 + 0x4 * (n))
+#define CAMSS_CSID_CID_n_CFG(n)		(0x020 + 0x4 * (n))
+#define CAMSS_CSID_IRQ_CLEAR_CMD	0x060
+#define CAMSS_CSID_IRQ_MASK		0x064
+#define CAMSS_CSID_IRQ_STATUS		0x068
+#define CAMSS_CSID_TG_CTRL		0x0a0
+#define CAMSS_CSID_TG_VC_CFG		0x0a4
+#define CAMSS_CSID_TG_VC_CFG_H_BLANKING		0x3ff
+#define CAMSS_CSID_TG_VC_CFG_V_BLANKING		0x7f
+#define CAMSS_CSID_TG_DT_n_CGG_0(n)	(0x0ac + 0xc * (n))
+#define CAMSS_CSID_TG_DT_n_CGG_1(n)	(0x0b0 + 0xc * (n))
+#define CAMSS_CSID_TG_DT_n_CGG_2(n)	(0x0b4 + 0xc * (n))
+
+#define DATA_TYPE_EMBEDDED_DATA_8BIT	0x12
+#define DATA_TYPE_YUV422_8BIT		0x1e
+#define DATA_TYPE_RAW_6BIT		0x28
+#define DATA_TYPE_RAW_8BIT		0x2a
+#define DATA_TYPE_RAW_10BIT		0x2b
+#define DATA_TYPE_RAW_12BIT		0x2c
+
+#define DECODE_FORMAT_UNCOMPRESSED_6_BIT	0x0
+#define DECODE_FORMAT_UNCOMPRESSED_8_BIT	0x1
+#define DECODE_FORMAT_UNCOMPRESSED_10_BIT	0x2
+#define DECODE_FORMAT_UNCOMPRESSED_12_BIT	0x3
+
+#define CSID_RESET_TIMEOUT_MS 500
+
+static const struct {
+	u32 code;
+	u32 uncompressed;
+	u8 data_type;
+	u8 decode_format;
+	u8 uncompr_bpp;
+} csid_input_fmts[] = {
+	{
+		MEDIA_BUS_FMT_UYVY8_2X8,
+		MEDIA_BUS_FMT_UYVY8_2X8,
+		DATA_TYPE_YUV422_8BIT,
+		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+		16
+	},
+	{
+		MEDIA_BUS_FMT_VYUY8_2X8,
+		MEDIA_BUS_FMT_VYUY8_2X8,
+		DATA_TYPE_YUV422_8BIT,
+		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+		16
+	},
+	{
+		MEDIA_BUS_FMT_YUYV8_2X8,
+		MEDIA_BUS_FMT_YUYV8_2X8,
+		DATA_TYPE_YUV422_8BIT,
+		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+		16
+	},
+	{
+		MEDIA_BUS_FMT_YVYU8_2X8,
+		MEDIA_BUS_FMT_YVYU8_2X8,
+		DATA_TYPE_YUV422_8BIT,
+		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+		16
+	},
+	{
+		MEDIA_BUS_FMT_SBGGR8_1X8,
+		MEDIA_BUS_FMT_SBGGR8_1X8,
+		DATA_TYPE_RAW_8BIT,
+		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+		8
+	},
+	{
+		MEDIA_BUS_FMT_SGBRG8_1X8,
+		MEDIA_BUS_FMT_SGBRG8_1X8,
+		DATA_TYPE_RAW_8BIT,
+		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+		8
+	},
+	{
+		MEDIA_BUS_FMT_SGRBG8_1X8,
+		MEDIA_BUS_FMT_SGRBG8_1X8,
+		DATA_TYPE_RAW_8BIT,
+		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+		8
+	},
+	{
+		MEDIA_BUS_FMT_SRGGB8_1X8,
+		MEDIA_BUS_FMT_SRGGB8_1X8,
+		DATA_TYPE_RAW_8BIT,
+		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+		8
+	},
+	{
+		MEDIA_BUS_FMT_SBGGR10_1X10,
+		MEDIA_BUS_FMT_SBGGR10_1X10,
+		DATA_TYPE_RAW_10BIT,
+		DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+		10
+	},
+	{
+		MEDIA_BUS_FMT_SGBRG10_1X10,
+		MEDIA_BUS_FMT_SGBRG10_1X10,
+		DATA_TYPE_RAW_10BIT,
+		DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+		10
+	},
+	{
+		MEDIA_BUS_FMT_SGRBG10_1X10,
+		MEDIA_BUS_FMT_SGRBG10_1X10,
+		DATA_TYPE_RAW_10BIT,
+		DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+		10
+	},
+	{
+		MEDIA_BUS_FMT_SRGGB10_1X10,
+		MEDIA_BUS_FMT_SRGGB10_1X10,
+		DATA_TYPE_RAW_10BIT,
+		DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+		10
+	},
+	{
+		MEDIA_BUS_FMT_SBGGR12_1X12,
+		MEDIA_BUS_FMT_SBGGR12_1X12,
+		DATA_TYPE_RAW_12BIT,
+		DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+		12
+	},
+	{
+		MEDIA_BUS_FMT_SGBRG12_1X12,
+		MEDIA_BUS_FMT_SGBRG12_1X12,
+		DATA_TYPE_RAW_12BIT,
+		DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+		12
+	},
+	{
+		MEDIA_BUS_FMT_SGRBG12_1X12,
+		MEDIA_BUS_FMT_SGRBG12_1X12,
+		DATA_TYPE_RAW_12BIT,
+		DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+		12
+	},
+	{
+		MEDIA_BUS_FMT_SRGGB12_1X12,
+		MEDIA_BUS_FMT_SRGGB12_1X12,
+		DATA_TYPE_RAW_12BIT,
+		DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+		12
+	}
+};
+
+/*
+ * csid_isr - CSID module interrupt handler
+ * @irq: Interrupt line
+ * @dev: CSID device
+ *
+ * Return IRQ_HANDLED on success
+ */
+static irqreturn_t csid_isr(int irq, void *dev)
+{
+	struct csid_device *csid = dev;
+	u32 value;
+
+	value = readl_relaxed(csid->base + CAMSS_CSID_IRQ_STATUS);
+	writel_relaxed(value, csid->base + CAMSS_CSID_IRQ_CLEAR_CMD);
+
+	if ((value >> 11) & 0x1)
+		complete(&csid->reset_complete);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * csid_reset - Trigger reset on CSID module and wait to complete
+ * @csid: CSID device
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int csid_reset(struct csid_device *csid)
+{
+	unsigned long time;
+
+	reinit_completion(&csid->reset_complete);
+
+	writel_relaxed(0x7fff, csid->base + CAMSS_CSID_RST_CMD);
+
+	time = wait_for_completion_timeout(&csid->reset_complete,
+		msecs_to_jiffies(CSID_RESET_TIMEOUT_MS));
+	if (!time) {
+		dev_err(to_device_index(csid, csid->id),
+			"CSID reset timeout\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/*
+ * csid_set_power - Power on/off CSID module
+ * @sd: CSID V4L2 subdevice
+ * @on: Requested power state
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int csid_set_power(struct v4l2_subdev *sd, int on)
+{
+	struct csid_device *csid = v4l2_get_subdevdata(sd);
+	struct device *dev = to_device_index(csid, csid->id);
+	int ret;
+
+	if (on) {
+		u32 hw_version;
+
+		ret = regulator_enable(csid->vdda);
+		if (ret < 0)
+			return ret;
+
+		ret = camss_enable_clocks(csid->nclocks, csid->clock, dev);
+		if (ret < 0) {
+			regulator_disable(csid->vdda);
+			return ret;
+		}
+
+		enable_irq(csid->irq);
+
+		ret = csid_reset(csid);
+		if (ret < 0) {
+			disable_irq(csid->irq);
+			camss_disable_clocks(csid->nclocks, csid->clock);
+			regulator_disable(csid->vdda);
+			return ret;
+		}
+
+		hw_version = readl_relaxed(csid->base + CAMSS_CSID_HW_VERSION);
+		dev_dbg(dev, "CSID HW Version = 0x%08x\n", hw_version);
+	} else {
+		disable_irq(csid->irq);
+		camss_disable_clocks(csid->nclocks, csid->clock);
+		ret = regulator_disable(csid->vdda);
+	}
+
+	return ret;
+}
+
+/*
+ * csid_get_uncompressed - map media bus format to uncompressed media bus format
+ * @code: media bus format code
+ *
+ * Return uncompressed media bus format code
+ */
+static u32 csid_get_uncompressed(u32 code)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
+		if (code == csid_input_fmts[i].code)
+			break;
+
+	return csid_input_fmts[i].uncompressed;
+}
+
+/*
+ * csid_get_data_type - map media bus format to data type
+ * @code: media bus format code
+ *
+ * Return data type code
+ */
+static u8 csid_get_data_type(u32 code)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
+		if (code == csid_input_fmts[i].code)
+			break;
+
+	return csid_input_fmts[i].data_type;
+}
+
+/*
+ * csid_get_decode_format - map media bus format to decode format
+ * @code: media bus format code
+ *
+ * Return decode format code
+ */
+static u8 csid_get_decode_format(u32 code)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
+		if (code == csid_input_fmts[i].code)
+			break;
+
+	return csid_input_fmts[i].decode_format;
+}
+
+/*
+ * csid_get_bpp - map media bus format to bits per pixel
+ * @code: media bus format code
+ *
+ * Return number of bits per pixel
+ */
+static u8 csid_get_bpp(u32 code)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
+		if (code == csid_input_fmts[i].uncompressed)
+			break;
+
+	return csid_input_fmts[i].uncompr_bpp;
+}
+
+/*
+ * csid_set_stream - Enable/disable streaming on CSID module
+ * @sd: CSID V4L2 subdevice
+ * @enable: Requested streaming state
+ *
+ * Main configuration of CSID module is also done here.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int csid_set_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct csid_device *csid = v4l2_get_subdevdata(sd);
+	struct csid_testgen_config *tg = &csid->testgen;
+	u32 val;
+
+	if (enable) {
+		u8 vc = 0; /* Virtual Channel 0 */
+		u8 cid = vc * 4;
+		u8 dt, dt_shift, df;
+		int ret;
+
+		ret = v4l2_ctrl_handler_setup(&csid->ctrls);
+		if (ret < 0) {
+			dev_err(to_device_index(csid, csid->id),
+				"could not sync v4l2 controls: %d\n", ret);
+			return ret;
+		}
+
+		if (!tg->enabled &&
+		    !media_entity_remote_pad(&csid->pads[MSM_CSID_PAD_SINK])) {
+			return -ENOLINK;
+		}
+
+		dt = csid_get_data_type(csid->fmt[MSM_CSID_PAD_SRC].code);
+
+		if (tg->enabled) {
+			/* Config Test Generator */
+			u8 bpp = csid_get_bpp(csid->fmt[MSM_CSID_PAD_SRC].code);
+			u32 num_bytes_per_line =
+				csid->fmt[MSM_CSID_PAD_SRC].width * bpp / 8;
+			u32 num_lines = csid->fmt[MSM_CSID_PAD_SRC].height;
+
+			/* 31:24 V blank, 23:13 H blank, 3:2 num of active DT */
+			/* 1:0 VC */
+			val = ((CAMSS_CSID_TG_VC_CFG_V_BLANKING & 0xff) << 24) |
+			      ((CAMSS_CSID_TG_VC_CFG_H_BLANKING & 0x7ff) << 13);
+			writel_relaxed(val, csid->base + CAMSS_CSID_TG_VC_CFG);
+
+			/* 28:16 bytes per lines, 12:0 num of lines */
+			val = ((num_bytes_per_line & 0x1fff) << 16) |
+			      (num_lines & 0x1fff);
+			writel_relaxed(val, csid->base +
+				       CAMSS_CSID_TG_DT_n_CGG_0(0));
+
+			/* 5:0 data type */
+			val = dt;
+			writel_relaxed(val, csid->base +
+				       CAMSS_CSID_TG_DT_n_CGG_1(0));
+
+			/* 2:0 output test pattern */
+			val = tg->payload_mode;
+			writel_relaxed(val, csid->base +
+				       CAMSS_CSID_TG_DT_n_CGG_2(0));
+		} else {
+			struct csid_phy_config *phy = &csid->phy;
+
+			val = phy->lane_cnt - 1;
+			val |= phy->lane_assign << 4;
+
+			writel_relaxed(val,
+				       csid->base + CAMSS_CSID_CORE_CTRL_0);
+
+			val = phy->csiphy_id << 17;
+			val |= 0x9;
+
+			writel_relaxed(val,
+				       csid->base + CAMSS_CSID_CORE_CTRL_1);
+		}
+
+		/* Config LUT */
+
+		dt_shift = (cid % 4) * 8;
+		df = csid_get_decode_format(csid->fmt[MSM_CSID_PAD_SINK].code);
+
+		val = readl_relaxed(csid->base + CAMSS_CSID_CID_LUT_VC_n(vc));
+		val &= ~(0xff << dt_shift);
+		val |= dt << dt_shift;
+		writel_relaxed(val, csid->base + CAMSS_CSID_CID_LUT_VC_n(vc));
+
+		val = (df << 4) | 0x3;
+		writel_relaxed(val, csid->base + CAMSS_CSID_CID_n_CFG(cid));
+
+		if (tg->enabled) {
+			val = 0x00a06437;
+			writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL);
+		}
+	} else {
+		if (tg->enabled) {
+			val = 0x00a06436;
+			writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL);
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * __csid_get_format - Get pointer to format structure
+ * @csid: CSID device
+ * @cfg: V4L2 subdev pad configuration
+ * @pad: pad from which format is requested
+ * @which: TRY or ACTIVE format
+ *
+ * Return pointer to TRY or ACTIVE format structure
+ */
+static struct v4l2_mbus_framefmt *
+__csid_get_format(struct csid_device *csid,
+		  struct v4l2_subdev_pad_config *cfg,
+		  unsigned int pad,
+		  enum v4l2_subdev_format_whence which)
+{
+	if (which == V4L2_SUBDEV_FORMAT_TRY)
+		return v4l2_subdev_get_try_format(&csid->subdev, cfg, pad);
+
+	return &csid->fmt[pad];
+}
+
+/*
+ * csid_try_format - Handle try format by pad subdev method
+ * @csid: CSID device
+ * @cfg: V4L2 subdev pad configuration
+ * @pad: pad on which format is requested
+ * @fmt: pointer to v4l2 format structure
+ * @which: wanted subdev format
+ */
+static void csid_try_format(struct csid_device *csid,
+			    struct v4l2_subdev_pad_config *cfg,
+			    unsigned int pad,
+			    struct v4l2_mbus_framefmt *fmt,
+			    enum v4l2_subdev_format_whence which)
+{
+	unsigned int i;
+
+	switch (pad) {
+	case MSM_CSID_PAD_SINK:
+		/* Set format on sink pad */
+
+		for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
+			if (fmt->code == csid_input_fmts[i].code)
+				break;
+
+		/* If not found, use UYVY as default */
+		if (i >= ARRAY_SIZE(csid_input_fmts))
+			fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
+
+		fmt->width = clamp_t(u32, fmt->width, 1, 8191);
+		fmt->height = clamp_t(u32, fmt->height, 1, 8191);
+
+		if (fmt->field == V4L2_FIELD_ANY)
+			fmt->field = V4L2_FIELD_NONE;
+
+		break;
+
+	case MSM_CSID_PAD_SRC:
+		if (csid->testgen_mode->cur.val == 0) {
+			/* Test generator is disabled, keep pad formats */
+			/* in sync - set and return a format same as sink pad */
+			struct v4l2_mbus_framefmt format;
+
+			format = *__csid_get_format(csid, cfg,
+						    MSM_CSID_PAD_SINK, which);
+			format.code = csid_get_uncompressed(format.code);
+			*fmt = format;
+		} else {
+			/* Test generator is enabled, set format on source*/
+			/* pad to allow test generator usage */
+
+			for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
+				if (csid_input_fmts[i].uncompressed ==
+								fmt->code)
+					break;
+
+			/* If not found, use UYVY as default */
+			if (i >= ARRAY_SIZE(csid_input_fmts))
+				fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
+
+			fmt->width = clamp_t(u32, fmt->width, 1, 8191);
+			fmt->height = clamp_t(u32, fmt->height, 1, 8191);
+
+			fmt->field = V4L2_FIELD_NONE;
+		}
+		break;
+	}
+
+	fmt->colorspace = V4L2_COLORSPACE_SRGB;
+}
+
+/*
+ * csid_enum_mbus_code - Handle pixel format enumeration
+ * @sd: CSID V4L2 subdevice
+ * @cfg: V4L2 subdev pad configuration
+ * @code: pointer to v4l2_subdev_mbus_code_enum structure
+ * return -EINVAL or zero on success
+ */
+static int csid_enum_mbus_code(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_pad_config *cfg,
+			       struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct csid_device *csid = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt *format;
+
+	if (code->pad == MSM_CSID_PAD_SINK) {
+		if (code->index >= ARRAY_SIZE(csid_input_fmts))
+			return -EINVAL;
+
+		code->code = csid_input_fmts[code->index].code;
+	} else {
+		if (csid->testgen_mode->cur.val == 0) {
+			if (code->index > 0)
+				return -EINVAL;
+
+			format = __csid_get_format(csid, cfg, MSM_CSID_PAD_SINK,
+						   code->which);
+
+			code->code = csid_get_uncompressed(format->code);
+		} else {
+			if (code->index >= ARRAY_SIZE(csid_input_fmts))
+				return -EINVAL;
+
+			code->code = csid_input_fmts[code->index].uncompressed;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * csid_enum_frame_size - Handle frame size enumeration
+ * @sd: CSID V4L2 subdevice
+ * @cfg: V4L2 subdev pad configuration
+ * @fse: pointer to v4l2_subdev_frame_size_enum structure
+ * return -EINVAL or zero on success
+ */
+static int csid_enum_frame_size(struct v4l2_subdev *sd,
+				struct v4l2_subdev_pad_config *cfg,
+				struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct csid_device *csid = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt format;
+
+	if (fse->index != 0)
+		return -EINVAL;
+
+	format.code = fse->code;
+	format.width = 1;
+	format.height = 1;
+	csid_try_format(csid, cfg, fse->pad, &format, fse->which);
+	fse->min_width = format.width;
+	fse->min_height = format.height;
+
+	if (format.code != fse->code)
+		return -EINVAL;
+
+	format.code = fse->code;
+	format.width = -1;
+	format.height = -1;
+	csid_try_format(csid, cfg, fse->pad, &format, fse->which);
+	fse->max_width = format.width;
+	fse->max_height = format.height;
+
+	return 0;
+}
+
+/*
+ * csid_get_format - Handle get format by pads subdev method
+ * @sd: CSID V4L2 subdevice
+ * @cfg: V4L2 subdev pad configuration
+ * @fmt: pointer to v4l2 subdev format structure
+ *
+ * Return -EINVAL or zero on success
+ */
+static int csid_get_format(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *fmt)
+{
+	struct csid_device *csid = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt *format;
+
+	format = __csid_get_format(csid, cfg, fmt->pad, fmt->which);
+	if (format == NULL)
+		return -EINVAL;
+
+	fmt->format = *format;
+
+	return 0;
+}
+
+/*
+ * csid_set_format - Handle set format by pads subdev method
+ * @sd: CSID V4L2 subdevice
+ * @cfg: V4L2 subdev pad configuration
+ * @fmt: pointer to v4l2 subdev format structure
+ *
+ * Return -EINVAL or zero on success
+ */
+static int csid_set_format(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *fmt)
+{
+	struct csid_device *csid = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt *format;
+
+	format = __csid_get_format(csid, cfg, fmt->pad, fmt->which);
+	if (format == NULL)
+		return -EINVAL;
+
+	csid_try_format(csid, cfg, fmt->pad, &fmt->format, fmt->which);
+	*format = fmt->format;
+
+	/* Propagate the format from sink to source */
+	if (fmt->pad == MSM_CSID_PAD_SINK) {
+		format = __csid_get_format(csid, cfg, MSM_CSID_PAD_SRC,
+					   fmt->which);
+
+		*format = fmt->format;
+		csid_try_format(csid, cfg, MSM_CSID_PAD_SRC, format,
+				fmt->which);
+	}
+
+	return 0;
+}
+
+/*
+ * csid_init_formats - Initialize formats on all pads
+ * @sd: CSID V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int csid_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct v4l2_subdev_format format = {
+		.pad = MSM_CSID_PAD_SINK,
+		.which = fh ? V4L2_SUBDEV_FORMAT_TRY :
+			      V4L2_SUBDEV_FORMAT_ACTIVE,
+		.format = {
+			.code = MEDIA_BUS_FMT_UYVY8_2X8,
+			.width = 1920,
+			.height = 1080
+		}
+	};
+
+	return csid_set_format(sd, fh ? fh->pad : NULL, &format);
+}
+
+static const char * const csid_test_pattern_menu[] = {
+	"Disabled",
+	"Incrementing",
+	"Alternating 55/AA",
+	"All Zeros",
+	"All Ones",
+	"Random Data",
+};
+
+/*
+ * csid_set_test_pattern - Set test generator's pattern mode
+ * @csid: CSID device
+ * @value: desired test pattern mode
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int csid_set_test_pattern(struct csid_device *csid, s32 value)
+{
+	struct csid_testgen_config *tg = &csid->testgen;
+
+	/* If CSID is linked to CSIPHY, do not allow to enable test generator */
+	if (value && media_entity_remote_pad(&csid->pads[MSM_CSID_PAD_SINK]))
+		return -EBUSY;
+
+	tg->enabled = !!value;
+
+	switch (value) {
+	case 1:
+		tg->payload_mode = CSID_PAYLOAD_MODE_INCREMENTING;
+		break;
+	case 2:
+		tg->payload_mode = CSID_PAYLOAD_MODE_ALTERNATING_55_AA;
+		break;
+	case 3:
+		tg->payload_mode = CSID_PAYLOAD_MODE_ALL_ZEROES;
+		break;
+	case 4:
+		tg->payload_mode = CSID_PAYLOAD_MODE_ALL_ONES;
+		break;
+	case 5:
+		tg->payload_mode = CSID_PAYLOAD_MODE_RANDOM;
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * csid_s_ctrl - Handle set control subdev method
+ * @ctrl: pointer to v4l2 control structure
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int csid_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct csid_device *csid = container_of(ctrl->handler,
+						struct csid_device, ctrls);
+	int ret = -EINVAL;
+
+	switch (ctrl->id) {
+	case V4L2_CID_TEST_PATTERN:
+		ret = csid_set_test_pattern(csid, ctrl->val);
+		break;
+	}
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops csid_ctrl_ops = {
+	.s_ctrl = csid_s_ctrl,
+};
+
+/*
+ * msm_csid_subdev_init - Initialize CSID device structure and resources
+ * @csid: CSID device
+ * @res: CSID module resources table
+ * @id: CSID module id
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int msm_csid_subdev_init(struct csid_device *csid,
+			 const struct resources *res, u8 id)
+{
+	struct device *dev = to_device_index(csid, id);
+	struct platform_device *pdev = to_platform_device(dev);
+	struct resource *r;
+	int i;
+	int ret;
+
+	csid->id = id;
+
+	/* Memory */
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]);
+	csid->base = devm_ioremap_resource(dev, r);
+	if (IS_ERR(csid->base)) {
+		dev_err(dev, "could not map memory\n");
+		return PTR_ERR(csid->base);
+	}
+
+	/* Interrupt */
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+					 res->interrupt[0]);
+	if (!r) {
+		dev_err(dev, "missing IRQ\n");
+		return -EINVAL;
+	}
+
+	csid->irq = r->start;
+	snprintf(csid->irq_name, sizeof(csid->irq_name), "%s_%s%d",
+		 dev_name(dev), MSM_CSID_NAME, csid->id);
+	ret = devm_request_irq(dev, csid->irq, csid_isr,
+		IRQF_TRIGGER_RISING, csid->irq_name, csid);
+	if (ret < 0) {
+		dev_err(dev, "request_irq failed: %d\n", ret);
+		return ret;
+	}
+
+	disable_irq(csid->irq);
+
+	/* Clocks */
+
+	csid->nclocks = 0;
+	while (res->clock[csid->nclocks])
+		csid->nclocks++;
+
+	csid->clock = devm_kzalloc(dev, csid->nclocks * sizeof(*csid->clock),
+				    GFP_KERNEL);
+	if (!csid->clock)
+		return -ENOMEM;
+
+	for (i = 0; i < csid->nclocks; i++) {
+		csid->clock[i] = devm_clk_get(dev, res->clock[i]);
+		if (IS_ERR(csid->clock[i]))
+			return PTR_ERR(csid->clock[i]);
+
+		if (res->clock_rate[i]) {
+			long clk_rate = clk_round_rate(csid->clock[i],
+						       res->clock_rate[i]);
+			if (clk_rate < 0) {
+				dev_err(to_device_index(csid, csid->id),
+					"clk round rate failed: %ld\n",
+					clk_rate);
+				return -EINVAL;
+			}
+			ret = clk_set_rate(csid->clock[i], clk_rate);
+			if (ret < 0) {
+				dev_err(to_device_index(csid, csid->id),
+					"clk set rate failed: %d\n", ret);
+				return ret;
+			}
+		}
+	}
+
+	/* Regulator */
+
+	csid->vdda = devm_regulator_get(dev, res->regulator[0]);
+	if (IS_ERR(csid->vdda)) {
+		dev_err(dev, "could not get regulator\n");
+		return PTR_ERR(csid->vdda);
+	}
+
+	init_completion(&csid->reset_complete);
+
+	return 0;
+}
+
+/*
+ * msm_csid_get_csid_id - Get CSID HW module id
+ * @entity: Pointer to CSID media entity structure
+ * @id: Return CSID HW module id here
+ */
+void msm_csid_get_csid_id(struct media_entity *entity, u8 *id)
+{
+	struct v4l2_subdev *sd;
+	struct csid_device *csid;
+
+	sd = media_entity_to_v4l2_subdev(entity);
+	csid = v4l2_get_subdevdata(sd);
+
+	*id = csid->id;
+}
+
+/*
+ * csid_get_lane_assign - Calculate CSI2 lane assign configuration parameter
+ * @lane_cfg - CSI2 lane configuration
+ *
+ * Return lane assign
+ */
+static u32 csid_get_lane_assign(struct csiphy_lanes_cfg *lane_cfg)
+{
+	u32 lane_assign = 0;
+	int i;
+
+	for (i = 0; i < lane_cfg->num_data; i++)
+		lane_assign |= lane_cfg->data[i].pos << (i * 4);
+
+	return lane_assign;
+}
+
+/*
+ * csid_link_setup - Setup CSID connections
+ * @entity: Pointer to media entity structure
+ * @local: Pointer to local pad
+ * @remote: Pointer to remote pad
+ * @flags: Link flags
+ *
+ * Return 0 on success
+ */
+static int csid_link_setup(struct media_entity *entity,
+			   const struct media_pad *local,
+			   const struct media_pad *remote, u32 flags)
+{
+	if (flags & MEDIA_LNK_FL_ENABLED)
+		if (media_entity_remote_pad(local))
+			return -EBUSY;
+
+	if ((local->flags & MEDIA_PAD_FL_SINK) &&
+	    (flags & MEDIA_LNK_FL_ENABLED)) {
+		struct v4l2_subdev *sd;
+		struct csid_device *csid;
+		struct csiphy_device *csiphy;
+		struct csiphy_lanes_cfg *lane_cfg;
+		struct v4l2_subdev_format format = { 0 };
+
+		sd = media_entity_to_v4l2_subdev(entity);
+		csid = v4l2_get_subdevdata(sd);
+
+		/* If test generator is enabled */
+		/* do not allow a link from CSIPHY to CSID */
+		if (csid->testgen_mode->cur.val != 0)
+			return -EBUSY;
+
+		sd = media_entity_to_v4l2_subdev(remote->entity);
+		csiphy = v4l2_get_subdevdata(sd);
+
+		/* If a sensor is not linked to CSIPHY */
+		/* do no allow a link from CSIPHY to CSID */
+		if (!csiphy->cfg.csi2)
+			return -EPERM;
+
+		csid->phy.csiphy_id = csiphy->id;
+
+		lane_cfg = &csiphy->cfg.csi2->lane_cfg;
+		csid->phy.lane_cnt = lane_cfg->num_data;
+		csid->phy.lane_assign = csid_get_lane_assign(lane_cfg);
+
+		/* Reset format on source pad to sink pad format */
+		format.pad = MSM_CSID_PAD_SRC;
+		format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+		csid_set_format(&csid->subdev, NULL, &format);
+	}
+
+	return 0;
+}
+
+static const struct v4l2_subdev_core_ops csid_core_ops = {
+	.s_power = csid_set_power,
+};
+
+static const struct v4l2_subdev_video_ops csid_video_ops = {
+	.s_stream = csid_set_stream,
+};
+
+static const struct v4l2_subdev_pad_ops csid_pad_ops = {
+	.enum_mbus_code = csid_enum_mbus_code,
+	.enum_frame_size = csid_enum_frame_size,
+	.get_fmt = csid_get_format,
+	.set_fmt = csid_set_format,
+};
+
+static const struct v4l2_subdev_ops csid_v4l2_ops = {
+	.core = &csid_core_ops,
+	.video = &csid_video_ops,
+	.pad = &csid_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops csid_v4l2_internal_ops = {
+	.open = csid_init_formats,
+};
+
+static const struct media_entity_operations csid_media_ops = {
+	.link_setup = csid_link_setup,
+	.link_validate = v4l2_subdev_link_validate,
+};
+
+/*
+ * msm_csid_register_entity - Register subdev node for CSID module
+ * @csid: CSID device
+ * @v4l2_dev: V4L2 device
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int msm_csid_register_entity(struct csid_device *csid,
+			     struct v4l2_device *v4l2_dev)
+{
+	struct v4l2_subdev *sd = &csid->subdev;
+	struct media_pad *pads = csid->pads;
+	struct device *dev = to_device_index(csid, csid->id);
+	int ret;
+
+	v4l2_subdev_init(sd, &csid_v4l2_ops);
+	sd->internal_ops = &csid_v4l2_internal_ops;
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d",
+		 MSM_CSID_NAME, csid->id);
+	v4l2_set_subdevdata(sd, csid);
+
+	ret = v4l2_ctrl_handler_init(&csid->ctrls, 1);
+	if (ret < 0) {
+		dev_err(dev, "Failed to init ctrl handler: %d\n", ret);
+		return ret;
+	}
+
+	csid->testgen_mode = v4l2_ctrl_new_std_menu_items(&csid->ctrls,
+				&csid_ctrl_ops, V4L2_CID_TEST_PATTERN,
+				ARRAY_SIZE(csid_test_pattern_menu) - 1, 0, 0,
+				csid_test_pattern_menu);
+
+	if (csid->ctrls.error) {
+		dev_err(dev, "Failed to init ctrl: %d\n", csid->ctrls.error);
+		ret = csid->ctrls.error;
+		goto free_ctrl;
+	}
+
+	csid->subdev.ctrl_handler = &csid->ctrls;
+
+	ret = csid_init_formats(sd, NULL);
+	if (ret < 0) {
+		dev_err(dev, "Failed to init format: %d\n", ret);
+		goto free_ctrl;
+	}
+
+	pads[MSM_CSID_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+	pads[MSM_CSID_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
+
+	sd->entity.function = MEDIA_ENT_F_IO_V4L;
+	sd->entity.ops = &csid_media_ops;
+	ret = media_entity_pads_init(&sd->entity, MSM_CSID_PADS_NUM, pads);
+	if (ret < 0) {
+		dev_err(dev, "Failed to init media entity: %d\n", ret);
+		goto free_ctrl;
+	}
+
+	ret = v4l2_device_register_subdev(v4l2_dev, sd);
+	if (ret < 0) {
+		dev_err(dev, "Failed to register subdev: %d\n", ret);
+		goto media_cleanup;
+	}
+
+	return 0;
+
+media_cleanup:
+	media_entity_cleanup(&sd->entity);
+free_ctrl:
+	v4l2_ctrl_handler_free(&csid->ctrls);
+
+	return ret;
+}
+
+/*
+ * msm_csid_unregister_entity - Unregister CSID module subdev node
+ * @csid: CSID device
+ */
+void msm_csid_unregister_entity(struct csid_device *csid)
+{
+	v4l2_device_unregister_subdev(&csid->subdev);
+	v4l2_ctrl_handler_free(&csid->ctrls);
+}
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csid.h b/drivers/media/platform/qcom/camss-8x16/camss-csid.h
new file mode 100644
index 0000000..3186c11
--- /dev/null
+++ b/drivers/media/platform/qcom/camss-8x16/camss-csid.h
@@ -0,0 +1,82 @@
+/*
+ * camss-csid.h
+ *
+ * Qualcomm MSM Camera Subsystem - CSID Module
+ *
+ * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2015-2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef QC_MSM_CAMSS_CSID_H
+#define QC_MSM_CAMSS_CSID_H
+
+#include <linux/clk.h>
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mediabus.h>
+#include <media/v4l2-subdev.h>
+
+#define MSM_CSID_PAD_SINK 0
+#define MSM_CSID_PAD_SRC 1
+#define MSM_CSID_PADS_NUM 2
+
+enum csid_payload_mode {
+	CSID_PAYLOAD_MODE_INCREMENTING = 0,
+	CSID_PAYLOAD_MODE_ALTERNATING_55_AA = 1,
+	CSID_PAYLOAD_MODE_ALL_ZEROES = 2,
+	CSID_PAYLOAD_MODE_ALL_ONES = 3,
+	CSID_PAYLOAD_MODE_RANDOM = 4,
+	CSID_PAYLOAD_MODE_USER_SPECIFIED = 5,
+};
+
+struct csid_testgen_config {
+	u8 enabled;
+	enum csid_payload_mode payload_mode;
+};
+
+struct csid_phy_config {
+	u8 csiphy_id;
+	u8 lane_cnt;
+	u32 lane_assign;
+};
+
+struct csid_device {
+	u8 id;
+	struct v4l2_subdev subdev;
+	struct media_pad pads[MSM_CSID_PADS_NUM];
+	void __iomem *base;
+	u32 irq;
+	char irq_name[30];
+	struct clk **clock;
+	int nclocks;
+	struct regulator *vdda;
+	struct completion reset_complete;
+	struct csid_testgen_config testgen;
+	struct csid_phy_config phy;
+	struct v4l2_mbus_framefmt fmt[MSM_CSID_PADS_NUM];
+	struct v4l2_ctrl_handler ctrls;
+	struct v4l2_ctrl *testgen_mode;
+};
+
+struct resources;
+
+int msm_csid_subdev_init(struct csid_device *csid,
+			 const struct resources *res, u8 id);
+
+int msm_csid_register_entity(struct csid_device *csid,
+			     struct v4l2_device *v4l2_dev);
+
+void msm_csid_unregister_entity(struct csid_device *csid);
+
+void msm_csid_get_csid_id(struct media_entity *entity, u8 *id);
+
+#endif /* QC_MSM_CAMSS_CSID_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v3 09/23] media: camss: Add ISPIF files
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
                   ` (7 preceding siblings ...)
  2017-07-17 10:33 ` [PATCH v3 08/23] media: camss: Add CSID files Todor Tomov
@ 2017-07-17 10:33 ` Todor Tomov
  2017-07-20 14:51   ` Sakari Ailus
  2017-07-17 10:33 ` [PATCH v3 10/23] media: camss: Add VFE files Todor Tomov
                   ` (15 subsequent siblings)
  24 siblings, 1 reply; 60+ messages in thread
From: Todor Tomov @ 2017-07-17 10:33 UTC (permalink / raw)
  To: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm
  Cc: Todor Tomov

These files control the ISPIF module which handles the routing of the data
streams from the CSIDs to the inputs of the VFE.

Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
---
 .../media/platform/qcom/camss-8x16/camss-ispif.c   | 1127 ++++++++++++++++++++
 .../media/platform/qcom/camss-8x16/camss-ispif.h   |   85 ++
 2 files changed, 1212 insertions(+)
 create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-ispif.c
 create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-ispif.h

diff --git a/drivers/media/platform/qcom/camss-8x16/camss-ispif.c b/drivers/media/platform/qcom/camss-8x16/camss-ispif.c
new file mode 100644
index 0000000..cc32085
--- /dev/null
+++ b/drivers/media/platform/qcom/camss-8x16/camss-ispif.c
@@ -0,0 +1,1127 @@
+/*
+ * camss-ispif.c
+ *
+ * Qualcomm MSM Camera Subsystem - ISPIF Module
+ *
+ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2015-2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <media/media-entity.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include "camss-ispif.h"
+#include "camss.h"
+
+#define MSM_ISPIF_NAME "msm_ispif"
+
+#define ispif_line_array(ptr_line)	\
+	((const struct ispif_line (*)[]) &(ptr_line[-(ptr_line->id)]))
+
+#define to_ispif(ptr_line)	\
+	container_of(ispif_line_array(ptr_line), struct ispif_device, ptr_line)
+
+#define ISPIF_RST_CMD_0			0x008
+#define ISPIF_IRQ_GLOBAL_CLEAR_CMD	0x01c
+#define ISPIF_VFE_m_CTRL_0(m)		(0x200 + 0x200 * (m))
+#define ISPIF_VFE_m_CTRL_0_PIX0_LINE_BUF_EN	(1 << 6)
+#define ISPIF_VFE_m_IRQ_MASK_0(m)	(0x208 + 0x200 * (m))
+#define ISPIF_VFE_m_IRQ_MASK_0_PIX0_ENABLE	0x00001249
+#define ISPIF_VFE_m_IRQ_MASK_0_PIX0_MASK	0x00001fff
+#define ISPIF_VFE_m_IRQ_MASK_0_RDI0_ENABLE	0x02492000
+#define ISPIF_VFE_m_IRQ_MASK_0_RDI0_MASK	0x03ffe000
+#define ISPIF_VFE_m_IRQ_MASK_1(m)	(0x20c + 0x200 * (m))
+#define ISPIF_VFE_m_IRQ_MASK_1_PIX1_ENABLE	0x00001249
+#define ISPIF_VFE_m_IRQ_MASK_1_PIX1_MASK	0x00001fff
+#define ISPIF_VFE_m_IRQ_MASK_1_RDI1_ENABLE	0x02492000
+#define ISPIF_VFE_m_IRQ_MASK_1_RDI1_MASK	0x03ffe000
+#define ISPIF_VFE_m_IRQ_MASK_2(m)	(0x210 + 0x200 * (m))
+#define ISPIF_VFE_m_IRQ_MASK_2_RDI2_ENABLE	0x00001249
+#define ISPIF_VFE_m_IRQ_MASK_2_RDI2_MASK	0x00001fff
+#define ISPIF_VFE_m_IRQ_STATUS_0(m)	(0x21c + 0x200 * (m))
+#define ISPIF_VFE_m_IRQ_STATUS_0_PIX0_OVERFLOW	(1 << 12)
+#define ISPIF_VFE_m_IRQ_STATUS_0_RDI0_OVERFLOW	(1 << 25)
+#define ISPIF_VFE_m_IRQ_STATUS_1(m)	(0x220 + 0x200 * (m))
+#define ISPIF_VFE_m_IRQ_STATUS_1_PIX1_OVERFLOW	(1 << 12)
+#define ISPIF_VFE_m_IRQ_STATUS_1_RDI1_OVERFLOW	(1 << 25)
+#define ISPIF_VFE_m_IRQ_STATUS_2(m)	(0x224 + 0x200 * (m))
+#define ISPIF_VFE_m_IRQ_STATUS_2_RDI2_OVERFLOW	(1 << 12)
+#define ISPIF_VFE_m_IRQ_CLEAR_0(m)	(0x230 + 0x200 * (m))
+#define ISPIF_VFE_m_IRQ_CLEAR_1(m)	(0x234 + 0x200 * (m))
+#define ISPIF_VFE_m_IRQ_CLEAR_2(m)	(0x238 + 0x200 * (m))
+#define ISPIF_VFE_m_INTF_INPUT_SEL(m)	(0x244 + 0x200 * (m))
+#define ISPIF_VFE_m_INTF_CMD_0(m)	(0x248 + 0x200 * (m))
+#define ISPIF_VFE_m_INTF_CMD_1(m)	(0x24c + 0x200 * (m))
+#define ISPIF_VFE_m_PIX_INTF_n_CID_MASK(m, n)	\
+					(0x254 + 0x200 * (m) + 0x4 * (n))
+#define ISPIF_VFE_m_RDI_INTF_n_CID_MASK(m, n)	\
+					(0x264 + 0x200 * (m) + 0x4 * (n))
+#define ISPIF_VFE_m_PIX_INTF_n_STATUS(m, n)	\
+					(0x2c0 + 0x200 * (m) + 0x4 * (n))
+#define ISPIF_VFE_m_RDI_INTF_n_STATUS(m, n)	\
+					(0x2d0 + 0x200 * (m) + 0x4 * (n))
+
+#define CSI_PIX_CLK_MUX_SEL		0x000
+#define CSI_RDI_CLK_MUX_SEL		0x008
+
+#define ISPIF_TIMEOUT_SLEEP_US		1000
+#define ISPIF_TIMEOUT_ALL_US		1000000
+#define ISPIF_RESET_TIMEOUT_MS		500
+
+enum ispif_intf_cmd {
+	CMD_DISABLE_FRAME_BOUNDARY = 0x0,
+	CMD_ENABLE_FRAME_BOUNDARY = 0x1,
+	CMD_DISABLE_IMMEDIATELY = 0x2,
+	CMD_ALL_DISABLE_IMMEDIATELY = 0xaaaaaaaa,
+	CMD_ALL_NO_CHANGE = 0xffffffff,
+};
+
+static const u32 ispif_formats[] = {
+	MEDIA_BUS_FMT_UYVY8_2X8,
+	MEDIA_BUS_FMT_VYUY8_2X8,
+	MEDIA_BUS_FMT_YUYV8_2X8,
+	MEDIA_BUS_FMT_YVYU8_2X8,
+	MEDIA_BUS_FMT_SBGGR8_1X8,
+	MEDIA_BUS_FMT_SGBRG8_1X8,
+	MEDIA_BUS_FMT_SGRBG8_1X8,
+	MEDIA_BUS_FMT_SRGGB8_1X8,
+	MEDIA_BUS_FMT_SBGGR10_1X10,
+	MEDIA_BUS_FMT_SGBRG10_1X10,
+	MEDIA_BUS_FMT_SGRBG10_1X10,
+	MEDIA_BUS_FMT_SRGGB10_1X10,
+	MEDIA_BUS_FMT_SBGGR12_1X12,
+	MEDIA_BUS_FMT_SGBRG12_1X12,
+	MEDIA_BUS_FMT_SGRBG12_1X12,
+	MEDIA_BUS_FMT_SRGGB12_1X12,
+};
+
+/*
+ * ispif_isr - ISPIF module interrupt handler
+ * @irq: Interrupt line
+ * @dev: ISPIF device
+ *
+ * Return IRQ_HANDLED on success
+ */
+static irqreturn_t ispif_isr(int irq, void *dev)
+{
+	struct ispif_device *ispif = dev;
+	u32 value0, value1, value2;
+
+	value0 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_0(0));
+	value1 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_1(0));
+	value2 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_2(0));
+
+	writel_relaxed(value0, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(0));
+	writel_relaxed(value1, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(0));
+	writel_relaxed(value2, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_2(0));
+
+	writel(0x1, ispif->base + ISPIF_IRQ_GLOBAL_CLEAR_CMD);
+
+	if ((value0 >> 27) & 0x1)
+		complete(&ispif->reset_complete);
+
+	if (unlikely(value0 & ISPIF_VFE_m_IRQ_STATUS_0_PIX0_OVERFLOW))
+		dev_err_ratelimited(to_device(ispif), "VFE0 pix0 overflow\n");
+
+	if (unlikely(value0 & ISPIF_VFE_m_IRQ_STATUS_0_RDI0_OVERFLOW))
+		dev_err_ratelimited(to_device(ispif), "VFE0 rdi0 overflow\n");
+
+	if (unlikely(value1 & ISPIF_VFE_m_IRQ_STATUS_1_PIX1_OVERFLOW))
+		dev_err_ratelimited(to_device(ispif), "VFE0 pix1 overflow\n");
+
+	if (unlikely(value1 & ISPIF_VFE_m_IRQ_STATUS_1_RDI1_OVERFLOW))
+		dev_err_ratelimited(to_device(ispif), "VFE0 rdi1 overflow\n");
+
+	if (unlikely(value2 & ISPIF_VFE_m_IRQ_STATUS_2_RDI2_OVERFLOW))
+		dev_err_ratelimited(to_device(ispif), "VFE0 rdi2 overflow\n");
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * ispif_reset - Trigger reset on ISPIF module and wait to complete
+ * @ispif: ISPIF device
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int ispif_reset(struct ispif_device *ispif)
+{
+	unsigned long time;
+	int ret;
+
+	ret = camss_enable_clocks(ispif->nclocks_for_reset,
+				  ispif->clock_for_reset,
+				  to_device(ispif));
+	if (ret < 0)
+		return ret;
+
+	reinit_completion(&ispif->reset_complete);
+
+	writel_relaxed(0x000f1fff, ispif->base + ISPIF_RST_CMD_0);
+
+	time = wait_for_completion_timeout(&ispif->reset_complete,
+		msecs_to_jiffies(ISPIF_RESET_TIMEOUT_MS));
+	if (!time) {
+		dev_err(to_device(ispif), "ISPIF reset timeout\n");
+		return -EIO;
+	}
+
+	camss_disable_clocks(ispif->nclocks_for_reset, ispif->clock_for_reset);
+
+	return 0;
+}
+
+/*
+ * ispif_set_power - Power on/off ISPIF module
+ * @sd: ISPIF V4L2 subdevice
+ * @on: Requested power state
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int ispif_set_power(struct v4l2_subdev *sd, int on)
+{
+	struct ispif_line *line = v4l2_get_subdevdata(sd);
+	struct ispif_device *ispif = to_ispif(line);
+	struct device *dev = to_device(ispif);
+	int ret = 0;
+
+	mutex_lock(&ispif->power_lock);
+
+	if (on) {
+		if (ispif->power_count) {
+			/* Power is already on */
+			ispif->power_count++;
+			goto exit;
+		}
+
+		ret = camss_enable_clocks(ispif->nclocks, ispif->clock, dev);
+		if (ret < 0)
+			goto exit;
+
+		ret = ispif_reset(ispif);
+		if (ret < 0) {
+			camss_disable_clocks(ispif->nclocks, ispif->clock);
+			goto exit;
+		}
+
+		ispif->intf_cmd[line->vfe_id].cmd_0 = CMD_ALL_NO_CHANGE;
+		ispif->intf_cmd[line->vfe_id].cmd_1 = CMD_ALL_NO_CHANGE;
+
+		ispif->power_count++;
+	} else {
+		if (ispif->power_count == 0) {
+			dev_err(dev, "ispif power off on power_count == 0\n");
+			goto exit;
+		} else if (ispif->power_count == 1) {
+			camss_disable_clocks(ispif->nclocks, ispif->clock);
+		}
+
+		ispif->power_count--;
+	}
+
+exit:
+	mutex_unlock(&ispif->power_lock);
+
+	return ret;
+}
+
+/*
+ * ispif_select_clk_mux - Select clock for PIX/RDI interface
+ * @ispif: ISPIF device
+ * @intf: VFE interface
+ * @csid: CSID HW module id
+ * @vfe: VFE HW module id
+ * @enable: enable or disable the selected clock
+ */
+static void ispif_select_clk_mux(struct ispif_device *ispif,
+				 enum ispif_intf intf, u8 csid,
+				 u8 vfe, u8 enable)
+{
+	u32 val;
+
+	switch (intf) {
+	case PIX0:
+		val = readl_relaxed(ispif->base_clk_mux + CSI_PIX_CLK_MUX_SEL);
+		val &= ~(0xf << (vfe * 8));
+		if (enable)
+			val |= (csid << (vfe * 8));
+		writel_relaxed(val, ispif->base_clk_mux + CSI_PIX_CLK_MUX_SEL);
+		break;
+
+	case RDI0:
+		val = readl_relaxed(ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL);
+		val &= ~(0xf << (vfe * 12));
+		if (enable)
+			val |= (csid << (vfe * 12));
+		writel_relaxed(val, ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL);
+		break;
+
+	case PIX1:
+		val = readl_relaxed(ispif->base_clk_mux + CSI_PIX_CLK_MUX_SEL);
+		val &= ~(0xf << (4 + (vfe * 8)));
+		if (enable)
+			val |= (csid << (4 + (vfe * 8)));
+		writel_relaxed(val, ispif->base_clk_mux + CSI_PIX_CLK_MUX_SEL);
+		break;
+
+	case RDI1:
+		val = readl_relaxed(ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL);
+		val &= ~(0xf << (4 + (vfe * 12)));
+		if (enable)
+			val |= (csid << (4 + (vfe * 12)));
+		writel_relaxed(val, ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL);
+		break;
+
+	case RDI2:
+		val = readl_relaxed(ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL);
+		val &= ~(0xf << (8 + (vfe * 12)));
+		if (enable)
+			val |= (csid << (8 + (vfe * 12)));
+		writel_relaxed(val, ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL);
+		break;
+	}
+
+	mb();
+}
+
+/*
+ * ispif_validate_intf_status - Validate current status of PIX/RDI interface
+ * @ispif: ISPIF device
+ * @intf: VFE interface
+ * @vfe: VFE HW module id
+ *
+ * Return 0 when interface is idle or -EBUSY otherwise
+ */
+static int ispif_validate_intf_status(struct ispif_device *ispif,
+				      enum ispif_intf intf, u8 vfe)
+{
+	int ret = 0;
+	u32 val = 0;
+
+	switch (intf) {
+	case PIX0:
+		val = readl_relaxed(ispif->base +
+			ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe, 0));
+		break;
+	case RDI0:
+		val = readl_relaxed(ispif->base +
+			ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 0));
+		break;
+	case PIX1:
+		val = readl_relaxed(ispif->base +
+			ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe, 1));
+		break;
+	case RDI1:
+		val = readl_relaxed(ispif->base +
+			ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 1));
+		break;
+	case RDI2:
+		val = readl_relaxed(ispif->base +
+			ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 2));
+		break;
+	}
+
+	if ((val & 0xf) != 0xf) {
+		dev_err(to_device(ispif), "%s: ispif is busy: 0x%x\n",
+			__func__, val);
+		ret = -EBUSY;
+	}
+
+	return ret;
+}
+
+/*
+ * ispif_wait_for_stop - Wait for PIX/RDI interface to stop
+ * @ispif: ISPIF device
+ * @intf: VFE interface
+ * @vfe: VFE HW module id
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int ispif_wait_for_stop(struct ispif_device *ispif,
+			       enum ispif_intf intf, u8 vfe)
+{
+	u32 addr = 0;
+	u32 stop_flag = 0;
+	int ret;
+
+	switch (intf) {
+	case PIX0:
+		addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe, 0);
+		break;
+	case RDI0:
+		addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 0);
+		break;
+	case PIX1:
+		addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe, 1);
+		break;
+	case RDI1:
+		addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 1);
+		break;
+	case RDI2:
+		addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 2);
+		break;
+	}
+
+	ret = readl_poll_timeout(ispif->base + addr,
+				 stop_flag,
+				 (stop_flag & 0xf) == 0xf,
+				 ISPIF_TIMEOUT_SLEEP_US,
+				 ISPIF_TIMEOUT_ALL_US);
+	if (ret < 0)
+		dev_err(to_device(ispif), "%s: ispif stop timeout\n",
+			__func__);
+
+	return ret;
+}
+
+/*
+ * ispif_select_csid - Select CSID HW module for input from
+ * @ispif: ISPIF device
+ * @intf: VFE interface
+ * @csid: CSID HW module id
+ * @vfe: VFE HW module id
+ * @enable: enable or disable the selected input
+ */
+static void ispif_select_csid(struct ispif_device *ispif, enum ispif_intf intf,
+			      u8 csid, u8 vfe, u8 enable)
+{
+	u32 val;
+
+	val = readl_relaxed(ispif->base + ISPIF_VFE_m_INTF_INPUT_SEL(vfe));
+	switch (intf) {
+	case PIX0:
+		val &= ~(BIT(1) | BIT(0));
+		if (enable)
+			val |= csid;
+		break;
+	case RDI0:
+		val &= ~(BIT(5) | BIT(4));
+		if (enable)
+			val |= (csid << 4);
+		break;
+	case PIX1:
+		val &= ~(BIT(9) | BIT(8));
+		if (enable)
+			val |= (csid << 8);
+		break;
+	case RDI1:
+		val &= ~(BIT(13) | BIT(12));
+		if (enable)
+			val |= (csid << 12);
+		break;
+	case RDI2:
+		val &= ~(BIT(21) | BIT(20));
+		if (enable)
+			val |= (csid << 20);
+		break;
+	}
+
+	writel(val, ispif->base + ISPIF_VFE_m_INTF_INPUT_SEL(vfe));
+}
+
+/*
+ * ispif_select_cid - Enable/disable desired CID
+ * @ispif: ISPIF device
+ * @intf: VFE interface
+ * @cid: desired CID to enable/disable
+ * @vfe: VFE HW module id
+ * @enable: enable or disable the desired CID
+ */
+static void ispif_select_cid(struct ispif_device *ispif, enum ispif_intf intf,
+			     u8 cid, u8 vfe, u8 enable)
+{
+	u32 cid_mask = 1 << cid;
+	u32 addr = 0;
+	u32 val;
+
+	switch (intf) {
+	case PIX0:
+		addr = ISPIF_VFE_m_PIX_INTF_n_CID_MASK(vfe, 0);
+		break;
+	case RDI0:
+		addr = ISPIF_VFE_m_RDI_INTF_n_CID_MASK(vfe, 0);
+		break;
+	case PIX1:
+		addr = ISPIF_VFE_m_PIX_INTF_n_CID_MASK(vfe, 1);
+		break;
+	case RDI1:
+		addr = ISPIF_VFE_m_RDI_INTF_n_CID_MASK(vfe, 1);
+		break;
+	case RDI2:
+		addr = ISPIF_VFE_m_RDI_INTF_n_CID_MASK(vfe, 2);
+		break;
+	}
+
+	val = readl_relaxed(ispif->base + addr);
+	if (enable)
+		val |= cid_mask;
+	else
+		val &= ~cid_mask;
+
+	writel(val, ispif->base + addr);
+}
+
+/*
+ * ispif_config_irq - Enable/disable interrupts for PIX/RDI interface
+ * @ispif: ISPIF device
+ * @intf: VFE interface
+ * @vfe: VFE HW module id
+ * @enable: enable or disable
+ */
+static void ispif_config_irq(struct ispif_device *ispif, enum ispif_intf intf,
+			     u8 vfe, u8 enable)
+{
+	u32 val;
+
+	switch (intf) {
+	case PIX0:
+		val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_0(vfe));
+		val &= ~ISPIF_VFE_m_IRQ_MASK_0_PIX0_MASK;
+		if (enable)
+			val |= ISPIF_VFE_m_IRQ_MASK_0_PIX0_ENABLE;
+		writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_0(vfe));
+		writel_relaxed(ISPIF_VFE_m_IRQ_MASK_0_PIX0_ENABLE,
+			       ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(vfe));
+		break;
+	case RDI0:
+		val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_0(vfe));
+		val &= ~ISPIF_VFE_m_IRQ_MASK_0_RDI0_MASK;
+		if (enable)
+			val |= ISPIF_VFE_m_IRQ_MASK_0_RDI0_ENABLE;
+		writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_0(vfe));
+		writel_relaxed(ISPIF_VFE_m_IRQ_MASK_0_RDI0_ENABLE,
+			       ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(vfe));
+		break;
+	case PIX1:
+		val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_1(vfe));
+		val &= ~ISPIF_VFE_m_IRQ_MASK_1_PIX1_MASK;
+		if (enable)
+			val |= ISPIF_VFE_m_IRQ_MASK_1_PIX1_ENABLE;
+		writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_1(vfe));
+		writel_relaxed(ISPIF_VFE_m_IRQ_MASK_1_PIX1_ENABLE,
+			       ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(vfe));
+		break;
+	case RDI1:
+		val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_1(vfe));
+		val &= ~ISPIF_VFE_m_IRQ_MASK_1_RDI1_MASK;
+		if (enable)
+			val |= ISPIF_VFE_m_IRQ_MASK_1_RDI1_ENABLE;
+		writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_1(vfe));
+		writel_relaxed(ISPIF_VFE_m_IRQ_MASK_1_RDI1_ENABLE,
+			       ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(vfe));
+		break;
+	case RDI2:
+		val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_2(vfe));
+		val &= ~ISPIF_VFE_m_IRQ_MASK_2_RDI2_MASK;
+		if (enable)
+			val |= ISPIF_VFE_m_IRQ_MASK_2_RDI2_ENABLE;
+		writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_2(vfe));
+		writel_relaxed(ISPIF_VFE_m_IRQ_MASK_2_RDI2_ENABLE,
+			       ispif->base + ISPIF_VFE_m_IRQ_CLEAR_2(vfe));
+		break;
+	}
+
+	writel(0x1, ispif->base + ISPIF_IRQ_GLOBAL_CLEAR_CMD);
+}
+
+/*
+ * ispif_set_intf_cmd - Set command to enable/disable interface
+ * @ispif: ISPIF device
+ * @cmd: interface command
+ * @intf: VFE interface
+ * @vfe: VFE HW module id
+ * @vc: virtual channel
+ */
+static void ispif_set_intf_cmd(struct ispif_device *ispif, u8 cmd,
+			       enum ispif_intf intf, u8 vfe, u8 vc)
+{
+	u32 *val;
+
+	if (intf == RDI2) {
+		val = &ispif->intf_cmd[vfe].cmd_1;
+		*val &= ~(0x3 << (vc * 2 + 8));
+		*val |= (cmd << (vc * 2 + 8));
+		wmb();
+		writel_relaxed(*val, ispif->base + ISPIF_VFE_m_INTF_CMD_1(vfe));
+		wmb();
+	} else {
+		val = &ispif->intf_cmd[vfe].cmd_0;
+		*val &= ~(0x3 << (vc * 2 + intf * 8));
+		*val |= (cmd << (vc * 2 + intf * 8));
+		wmb();
+		writel_relaxed(*val, ispif->base + ISPIF_VFE_m_INTF_CMD_0(vfe));
+		wmb();
+	}
+}
+
+/*
+ * ispif_set_stream - Enable/disable streaming on ISPIF module
+ * @sd: ISPIF V4L2 subdevice
+ * @enable: Requested streaming state
+ *
+ * Main configuration of ISPIF module is also done here.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int ispif_set_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct ispif_line *line = v4l2_get_subdevdata(sd);
+	struct ispif_device *ispif = to_ispif(line);
+	enum ispif_intf intf = line->interface;
+	u8 csid = line->csid_id;
+	u8 vfe = line->vfe_id;
+	u8 vc = 0; /* Virtual Channel 0 */
+	u8 cid = vc * 4;
+	int ret;
+
+	if (enable) {
+		if (!media_entity_remote_pad(
+					&line->pads[MSM_ISPIF_PAD_SINK])) {
+			return -ENOLINK;
+		}
+
+		/* Config */
+
+		mutex_lock(&ispif->config_lock);
+		ispif_select_clk_mux(ispif, intf, csid, vfe, 1);
+
+		ret = ispif_validate_intf_status(ispif, intf, vfe);
+		if (ret < 0) {
+			mutex_unlock(&ispif->config_lock);
+			return ret;
+		}
+
+		ispif_select_csid(ispif, intf, csid, vfe, 1);
+		ispif_select_cid(ispif, intf, cid, vfe, 1);
+		ispif_config_irq(ispif, intf, vfe, 1);
+		ispif_set_intf_cmd(ispif, CMD_ENABLE_FRAME_BOUNDARY,
+				   intf, vfe, vc);
+	} else {
+		mutex_lock(&ispif->config_lock);
+		ispif_set_intf_cmd(ispif, CMD_DISABLE_FRAME_BOUNDARY,
+				   intf, vfe, vc);
+		mutex_unlock(&ispif->config_lock);
+
+		ret = ispif_wait_for_stop(ispif, intf, vfe);
+		if (ret < 0)
+			return ret;
+
+		mutex_lock(&ispif->config_lock);
+		ispif_config_irq(ispif, intf, vfe, 0);
+		ispif_select_cid(ispif, intf, cid, vfe, 0);
+		ispif_select_csid(ispif, intf, csid, vfe, 0);
+		ispif_select_clk_mux(ispif, intf, csid, vfe, 0);
+	}
+
+	mutex_unlock(&ispif->config_lock);
+
+	return 0;
+}
+
+/*
+ * __ispif_get_format - Get pointer to format structure
+ * @ispif: ISPIF line
+ * @cfg: V4L2 subdev pad configuration
+ * @pad: pad from which format is requested
+ * @which: TRY or ACTIVE format
+ *
+ * Return pointer to TRY or ACTIVE format structure
+ */
+static struct v4l2_mbus_framefmt *
+__ispif_get_format(struct ispif_line *line,
+		   struct v4l2_subdev_pad_config *cfg,
+		   unsigned int pad,
+		   enum v4l2_subdev_format_whence which)
+{
+	if (which == V4L2_SUBDEV_FORMAT_TRY)
+		return v4l2_subdev_get_try_format(&line->subdev, cfg, pad);
+
+	return &line->fmt[pad];
+}
+
+/*
+ * ispif_try_format - Handle try format by pad subdev method
+ * @ispif: ISPIF line
+ * @cfg: V4L2 subdev pad configuration
+ * @pad: pad on which format is requested
+ * @fmt: pointer to v4l2 format structure
+ * @which: wanted subdev format
+ */
+static void ispif_try_format(struct ispif_line *line,
+			     struct v4l2_subdev_pad_config *cfg,
+			     unsigned int pad,
+			     struct v4l2_mbus_framefmt *fmt,
+			     enum v4l2_subdev_format_whence which)
+{
+	unsigned int i;
+
+	switch (pad) {
+	case MSM_ISPIF_PAD_SINK:
+		/* Set format on sink pad */
+
+		for (i = 0; i < ARRAY_SIZE(ispif_formats); i++)
+			if (fmt->code == ispif_formats[i])
+				break;
+
+		/* If not found, use UYVY as default */
+		if (i >= ARRAY_SIZE(ispif_formats))
+			fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
+
+		fmt->width = clamp_t(u32, fmt->width, 1, 8191);
+		fmt->height = clamp_t(u32, fmt->height, 1, 8191);
+
+		if (fmt->field == V4L2_FIELD_ANY)
+			fmt->field = V4L2_FIELD_NONE;
+
+		break;
+
+	case MSM_ISPIF_PAD_SRC:
+		/* Set and return a format same as sink pad */
+
+		*fmt = *__ispif_get_format(line, cfg, MSM_ISPIF_PAD_SINK,
+					   which);
+
+		break;
+	}
+
+	fmt->colorspace = V4L2_COLORSPACE_SRGB;
+}
+
+/*
+ * ispif_enum_mbus_code - Handle pixel format enumeration
+ * @sd: ISPIF V4L2 subdevice
+ * @cfg: V4L2 subdev pad configuration
+ * @code: pointer to v4l2_subdev_mbus_code_enum structure
+ * return -EINVAL or zero on success
+ */
+static int ispif_enum_mbus_code(struct v4l2_subdev *sd,
+				struct v4l2_subdev_pad_config *cfg,
+				struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct ispif_line *line = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt *format;
+
+	if (code->pad == MSM_ISPIF_PAD_SINK) {
+		if (code->index >= ARRAY_SIZE(ispif_formats))
+			return -EINVAL;
+
+		code->code = ispif_formats[code->index];
+	} else {
+		if (code->index > 0)
+			return -EINVAL;
+
+		format = __ispif_get_format(line, cfg, MSM_ISPIF_PAD_SINK,
+					    code->which);
+
+		code->code = format->code;
+	}
+
+	return 0;
+}
+
+/*
+ * ispif_enum_frame_size - Handle frame size enumeration
+ * @sd: ISPIF V4L2 subdevice
+ * @cfg: V4L2 subdev pad configuration
+ * @fse: pointer to v4l2_subdev_frame_size_enum structure
+ * return -EINVAL or zero on success
+ */
+static int ispif_enum_frame_size(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct ispif_line *line = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt format;
+
+	if (fse->index != 0)
+		return -EINVAL;
+
+	format.code = fse->code;
+	format.width = 1;
+	format.height = 1;
+	ispif_try_format(line, cfg, fse->pad, &format, fse->which);
+	fse->min_width = format.width;
+	fse->min_height = format.height;
+
+	if (format.code != fse->code)
+		return -EINVAL;
+
+	format.code = fse->code;
+	format.width = -1;
+	format.height = -1;
+	ispif_try_format(line, cfg, fse->pad, &format, fse->which);
+	fse->max_width = format.width;
+	fse->max_height = format.height;
+
+	return 0;
+}
+
+/*
+ * ispif_get_format - Handle get format by pads subdev method
+ * @sd: ISPIF V4L2 subdevice
+ * @cfg: V4L2 subdev pad configuration
+ * @fmt: pointer to v4l2 subdev format structure
+ *
+ * Return -EINVAL or zero on success
+ */
+static int ispif_get_format(struct v4l2_subdev *sd,
+			    struct v4l2_subdev_pad_config *cfg,
+			    struct v4l2_subdev_format *fmt)
+{
+	struct ispif_line *line = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt *format;
+
+	format = __ispif_get_format(line, cfg, fmt->pad, fmt->which);
+	if (format == NULL)
+		return -EINVAL;
+
+	fmt->format = *format;
+
+	return 0;
+}
+
+/*
+ * ispif_set_format - Handle set format by pads subdev method
+ * @sd: ISPIF V4L2 subdevice
+ * @cfg: V4L2 subdev pad configuration
+ * @fmt: pointer to v4l2 subdev format structure
+ *
+ * Return -EINVAL or zero on success
+ */
+static int ispif_set_format(struct v4l2_subdev *sd,
+			    struct v4l2_subdev_pad_config *cfg,
+			    struct v4l2_subdev_format *fmt)
+{
+	struct ispif_line *line = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt *format;
+
+	format = __ispif_get_format(line, cfg, fmt->pad, fmt->which);
+	if (format == NULL)
+		return -EINVAL;
+
+	ispif_try_format(line, cfg, fmt->pad, &fmt->format, fmt->which);
+	*format = fmt->format;
+
+	/* Propagate the format from sink to source */
+	if (fmt->pad == MSM_ISPIF_PAD_SINK) {
+		format = __ispif_get_format(line, cfg, MSM_ISPIF_PAD_SRC,
+					    fmt->which);
+
+		*format = fmt->format;
+		ispif_try_format(line, cfg, MSM_ISPIF_PAD_SRC, format,
+				 fmt->which);
+	}
+
+	return 0;
+}
+
+/*
+ * ispif_init_formats - Initialize formats on all pads
+ * @sd: ISPIF V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int ispif_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct v4l2_subdev_format format = {
+		.pad = MSM_ISPIF_PAD_SINK,
+		.which = fh ? V4L2_SUBDEV_FORMAT_TRY :
+			      V4L2_SUBDEV_FORMAT_ACTIVE,
+		.format = {
+			.code = MEDIA_BUS_FMT_UYVY8_2X8,
+			.width = 1920,
+			.height = 1080
+		}
+	};
+
+	return ispif_set_format(sd, fh ? fh->pad : NULL, &format);
+}
+
+/*
+ * msm_ispif_subdev_init - Initialize ISPIF device structure and resources
+ * @ispif: ISPIF device
+ * @res: ISPIF module resources table
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int msm_ispif_subdev_init(struct ispif_device *ispif,
+			  const struct resources_ispif *res)
+{
+	struct device *dev = to_device(ispif);
+	struct platform_device *pdev = to_platform_device(dev);
+	struct resource *r;
+	int i;
+	int ret;
+
+	for (i = 0; i < ARRAY_SIZE(ispif->line); i++)
+		ispif->line[i].id = i;
+
+	mutex_init(&ispif->power_lock);
+	ispif->power_count = 0;
+
+	mutex_init(&ispif->config_lock);
+
+	/* Memory */
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]);
+	ispif->base = devm_ioremap_resource(dev, r);
+	if (IS_ERR(ispif->base)) {
+		dev_err(dev, "could not map memory\n");
+		return PTR_ERR(ispif->base);
+	}
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[1]);
+	ispif->base_clk_mux = devm_ioremap_resource(dev, r);
+	if (IS_ERR(ispif->base_clk_mux)) {
+		dev_err(dev, "could not map memory\n");
+		return PTR_ERR(ispif->base_clk_mux);
+	}
+
+	/* Interrupt */
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res->interrupt);
+
+	if (!r) {
+		dev_err(dev, "missing IRQ\n");
+		return -EINVAL;
+	}
+
+	ispif->irq = r->start;
+	snprintf(ispif->irq_name, sizeof(ispif->irq_name), "%s_%s",
+		 dev_name(dev), MSM_ISPIF_NAME);
+	ret = devm_request_irq(dev, ispif->irq, ispif_isr,
+			       IRQF_TRIGGER_RISING, ispif->irq_name, ispif);
+	if (ret < 0) {
+		dev_err(dev, "request_irq failed: %d\n", ret);
+		return ret;
+	}
+
+	/* Clocks */
+
+	ispif->nclocks = 0;
+	while (res->clock[ispif->nclocks])
+		ispif->nclocks++;
+
+	ispif->clock = devm_kzalloc(dev, ispif->nclocks * sizeof(*ispif->clock),
+				    GFP_KERNEL);
+	if (!ispif->clock)
+		return -ENOMEM;
+
+	for (i = 0; i < ispif->nclocks; i++) {
+		ispif->clock[i] = devm_clk_get(dev, res->clock[i]);
+		if (IS_ERR(ispif->clock[i]))
+			return PTR_ERR(ispif->clock[i]);
+	}
+
+	ispif->nclocks_for_reset = 0;
+	while (res->clock_for_reset[ispif->nclocks_for_reset])
+		ispif->nclocks_for_reset++;
+
+	ispif->clock_for_reset = devm_kzalloc(dev, ispif->nclocks_for_reset *
+			sizeof(*ispif->clock_for_reset), GFP_KERNEL);
+	if (!ispif->clock_for_reset)
+		return -ENOMEM;
+
+	for (i = 0; i < ispif->nclocks_for_reset; i++) {
+		ispif->clock_for_reset[i] = devm_clk_get(dev,
+						res->clock_for_reset[i]);
+		if (IS_ERR(ispif->clock_for_reset[i]))
+			return PTR_ERR(ispif->clock_for_reset[i]);
+	}
+
+	init_completion(&ispif->reset_complete);
+
+	return 0;
+}
+
+/*
+ * ispif_get_intf - Get ISPIF interface to use by VFE line id
+ * @line_id: VFE line id that the ISPIF line is connected to
+ *
+ * Return ISPIF interface to use
+ */
+static enum ispif_intf ispif_get_intf(enum vfe_line_id line_id)
+{
+	switch (line_id) {
+	case (VFE_LINE_RDI0):
+		return RDI0;
+	case (VFE_LINE_RDI1):
+		return RDI1;
+	case (VFE_LINE_RDI2):
+		return RDI2;
+	default:
+		return RDI0;
+	}
+}
+
+/*
+ * ispif_link_setup - Setup ISPIF connections
+ * @entity: Pointer to media entity structure
+ * @local: Pointer to local pad
+ * @remote: Pointer to remote pad
+ * @flags: Link flags
+ *
+ * Return 0 on success
+ */
+static int ispif_link_setup(struct media_entity *entity,
+			    const struct media_pad *local,
+			    const struct media_pad *remote, u32 flags)
+{
+	if (flags & MEDIA_LNK_FL_ENABLED) {
+		if (media_entity_remote_pad(local))
+			return -EBUSY;
+
+		if (local->flags & MEDIA_PAD_FL_SINK) {
+			struct v4l2_subdev *sd;
+			struct ispif_line *line;
+
+			sd = media_entity_to_v4l2_subdev(entity);
+			line = v4l2_get_subdevdata(sd);
+
+			msm_csid_get_csid_id(remote->entity, &line->csid_id);
+		} else { /* MEDIA_PAD_FL_SOURCE */
+			struct v4l2_subdev *sd;
+			struct ispif_line *line;
+			enum vfe_line_id id;
+
+			sd = media_entity_to_v4l2_subdev(entity);
+			line = v4l2_get_subdevdata(sd);
+
+			msm_vfe_get_vfe_id(remote->entity, &line->vfe_id);
+			msm_vfe_get_vfe_line_id(remote->entity, &id);
+			line->interface = ispif_get_intf(id);
+		}
+	}
+
+	return 0;
+}
+
+static const struct v4l2_subdev_core_ops ispif_core_ops = {
+	.s_power = ispif_set_power,
+};
+
+static const struct v4l2_subdev_video_ops ispif_video_ops = {
+	.s_stream = ispif_set_stream,
+};
+
+static const struct v4l2_subdev_pad_ops ispif_pad_ops = {
+	.enum_mbus_code = ispif_enum_mbus_code,
+	.enum_frame_size = ispif_enum_frame_size,
+	.get_fmt = ispif_get_format,
+	.set_fmt = ispif_set_format,
+};
+
+static const struct v4l2_subdev_ops ispif_v4l2_ops = {
+	.core = &ispif_core_ops,
+	.video = &ispif_video_ops,
+	.pad = &ispif_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops ispif_v4l2_internal_ops = {
+	.open = ispif_init_formats,
+};
+
+static const struct media_entity_operations ispif_media_ops = {
+	.link_setup = ispif_link_setup,
+	.link_validate = v4l2_subdev_link_validate,
+};
+
+/*
+ * msm_ispif_register_entities - Register subdev node for ISPIF module
+ * @ispif: ISPIF device
+ * @v4l2_dev: V4L2 device
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int msm_ispif_register_entities(struct ispif_device *ispif,
+				struct v4l2_device *v4l2_dev)
+{
+	struct device *dev = to_device(ispif);
+	int ret;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ispif->line); i++) {
+		struct v4l2_subdev *sd = &ispif->line[i].subdev;
+		struct media_pad *pads = ispif->line[i].pads;
+
+		v4l2_subdev_init(sd, &ispif_v4l2_ops);
+		sd->internal_ops = &ispif_v4l2_internal_ops;
+		sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+		snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d",
+			 MSM_ISPIF_NAME, i);
+		v4l2_set_subdevdata(sd, &ispif->line[i]);
+
+		ret = ispif_init_formats(sd, NULL);
+		if (ret < 0) {
+			dev_err(dev, "Failed to init format: %d\n", ret);
+			goto error;
+		}
+
+		pads[MSM_ISPIF_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+		pads[MSM_ISPIF_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
+
+		sd->entity.function = MEDIA_ENT_F_IO_V4L;
+		sd->entity.ops = &ispif_media_ops;
+		ret = media_entity_pads_init(&sd->entity, MSM_ISPIF_PADS_NUM,
+					     pads);
+		if (ret < 0) {
+			dev_err(dev, "Failed to init media entity: %d\n", ret);
+			goto error;
+		}
+
+		ret = v4l2_device_register_subdev(v4l2_dev, sd);
+		if (ret < 0) {
+			dev_err(dev, "Failed to register subdev: %d\n", ret);
+			media_entity_cleanup(&sd->entity);
+			goto error;
+		}
+	}
+
+	return 0;
+
+error:
+	for (i--; i >= 0; i--) {
+		struct v4l2_subdev *sd = &ispif->line[i].subdev;
+
+		v4l2_device_unregister_subdev(sd);
+		media_entity_cleanup(&sd->entity);
+	}
+
+	return ret;
+}
+
+/*
+ * msm_ispif_unregister_entities - Unregister ISPIF module subdev node
+ * @ispif: ISPIF device
+ */
+void msm_ispif_unregister_entities(struct ispif_device *ispif)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ispif->line); i++) {
+		struct v4l2_subdev *sd = &ispif->line[i].subdev;
+
+		v4l2_device_unregister_subdev(sd);
+		media_entity_cleanup(&sd->entity);
+	}
+}
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-ispif.h b/drivers/media/platform/qcom/camss-8x16/camss-ispif.h
new file mode 100644
index 0000000..6a1c9bd
--- /dev/null
+++ b/drivers/media/platform/qcom/camss-8x16/camss-ispif.h
@@ -0,0 +1,85 @@
+/*
+ * camss-ispif.h
+ *
+ * Qualcomm MSM Camera Subsystem - ISPIF Module
+ *
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2015-2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef QC_MSM_CAMSS_ISPIF_H
+#define QC_MSM_CAMSS_ISPIF_H
+
+#include <linux/clk.h>
+#include <media/media-entity.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+/* Number of ISPIF lines - same as number of CSID hardware modules */
+#define MSM_ISPIF_LINE_NUM 2
+
+#define MSM_ISPIF_PAD_SINK 0
+#define MSM_ISPIF_PAD_SRC 1
+#define MSM_ISPIF_PADS_NUM 2
+
+#define MSM_ISPIF_VFE_NUM 1
+
+enum ispif_intf {
+	PIX0,
+	RDI0,
+	PIX1,
+	RDI1,
+	RDI2
+};
+
+struct ispif_intf_cmd_reg {
+	u32 cmd_0;
+	u32 cmd_1;
+};
+
+struct ispif_line {
+	u8 id;
+	u8 csid_id;
+	u8 vfe_id;
+	enum ispif_intf interface;
+	struct v4l2_subdev subdev;
+	struct media_pad pads[MSM_ISPIF_PADS_NUM];
+	struct v4l2_mbus_framefmt fmt[MSM_ISPIF_PADS_NUM];
+};
+
+struct ispif_device {
+	void __iomem *base;
+	void __iomem *base_clk_mux;
+	u32 irq;
+	char irq_name[30];
+	struct clk **clock;
+	int nclocks;
+	struct clk **clock_for_reset;
+	int nclocks_for_reset;
+	struct completion reset_complete;
+	int power_count;
+	struct mutex power_lock;
+	struct ispif_intf_cmd_reg intf_cmd[MSM_ISPIF_VFE_NUM];
+	struct mutex config_lock;
+	struct ispif_line line[MSM_ISPIF_LINE_NUM];
+};
+
+struct resources_ispif;
+
+int msm_ispif_subdev_init(struct ispif_device *ispif,
+			  const struct resources_ispif *res);
+
+int msm_ispif_register_entities(struct ispif_device *ispif,
+				struct v4l2_device *v4l2_dev);
+
+void msm_ispif_unregister_entities(struct ispif_device *ispif);
+
+#endif /* QC_MSM_CAMSS_ISPIF_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v3 10/23] media: camss: Add VFE files
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
                   ` (8 preceding siblings ...)
  2017-07-17 10:33 ` [PATCH v3 09/23] media: camss: Add ISPIF files Todor Tomov
@ 2017-07-17 10:33 ` Todor Tomov
  2017-07-20 14:59   ` Sakari Ailus
  2017-07-17 10:33 ` [PATCH v3 11/23] media: camss: Add files which handle the video device nodes Todor Tomov
                   ` (14 subsequent siblings)
  24 siblings, 1 reply; 60+ messages in thread
From: Todor Tomov @ 2017-07-17 10:33 UTC (permalink / raw)
  To: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm
  Cc: Todor Tomov

These files control the VFE module. The VFE has different input interfaces.
The PIX input interface feeds the input data to an image processing pipeline.
Three RDI input interfaces bypass the image processing pipeline. The VFE also
contains the AXI bus interface which writes the output data to memory.

RDI interfaces are supported in this version. PIX interface is not supported.

Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
---
 drivers/media/platform/qcom/camss-8x16/camss-vfe.c | 1913 ++++++++++++++++++++
 drivers/media/platform/qcom/camss-8x16/camss-vfe.h |  114 ++
 2 files changed, 2027 insertions(+)
 create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-vfe.c
 create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-vfe.h

diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
new file mode 100644
index 0000000..b6dd29b
--- /dev/null
+++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
@@ -0,0 +1,1913 @@
+/*
+ * camss-vfe.c
+ *
+ * Qualcomm MSM Camera Subsystem - VFE Module
+ *
+ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2015-2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/iommu.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock_types.h>
+#include <linux/spinlock.h>
+#include <media/media-entity.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include "camss-vfe.h"
+#include "camss.h"
+
+#define MSM_VFE_NAME "msm_vfe"
+
+#define vfe_line_array(ptr_line)	\
+	((const struct vfe_line (*)[]) &(ptr_line[-(ptr_line->id)]))
+
+#define to_vfe(ptr_line)	\
+	container_of(vfe_line_array(ptr_line), struct vfe_device, ptr_line)
+
+#define VFE_0_HW_VERSION		0x000
+
+#define VFE_0_GLOBAL_RESET_CMD		0x00c
+#define VFE_0_GLOBAL_RESET_CMD_CORE	(1 << 0)
+#define VFE_0_GLOBAL_RESET_CMD_CAMIF	(1 << 1)
+#define VFE_0_GLOBAL_RESET_CMD_BUS	(1 << 2)
+#define VFE_0_GLOBAL_RESET_CMD_BUS_BDG	(1 << 3)
+#define VFE_0_GLOBAL_RESET_CMD_REGISTER	(1 << 4)
+#define VFE_0_GLOBAL_RESET_CMD_TIMER	(1 << 5)
+#define VFE_0_GLOBAL_RESET_CMD_PM	(1 << 6)
+#define VFE_0_GLOBAL_RESET_CMD_BUS_MISR	(1 << 7)
+#define VFE_0_GLOBAL_RESET_CMD_TESTGEN	(1 << 8)
+
+#define VFE_0_IRQ_CMD			0x024
+#define VFE_0_IRQ_CMD_GLOBAL_CLEAR	(1 << 0)
+
+#define VFE_0_IRQ_MASK_0		0x028
+#define VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n)		(1 << ((n) + 5))
+#define VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(n)	(1 << ((n) + 8))
+#define VFE_0_IRQ_MASK_0_RESET_ACK			(1 << 31)
+#define VFE_0_IRQ_MASK_1		0x02c
+#define VFE_0_IRQ_MASK_1_VIOLATION			(1 << 7)
+#define VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK		(1 << 8)
+#define VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(n)	(1 << ((n) + 9))
+
+#define VFE_0_IRQ_CLEAR_0		0x030
+#define VFE_0_IRQ_CLEAR_1		0x034
+
+#define VFE_0_IRQ_STATUS_0		0x038
+#define VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n)		(1 << ((n) + 5))
+#define VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(n)	(1 << ((n) + 8))
+#define VFE_0_IRQ_STATUS_0_RESET_ACK			(1 << 31)
+#define VFE_0_IRQ_STATUS_1		0x03c
+#define VFE_0_IRQ_STATUS_1_VIOLATION			(1 << 7)
+#define VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK		(1 << 8)
+
+#define VFE_0_VIOLATION_STATUS		0x48
+
+#define VFE_0_BUS_CMD			0x4c
+#define VFE_0_BUS_CMD_Mx_RLD_CMD(x)	(1 << (x))
+
+#define VFE_0_BUS_CFG			0x050
+
+#define VFE_0_BUS_XBAR_CFG_x(x)		(0x58 + 0x4 * ((x) / 2))
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT		8
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0	5
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1	6
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2	7
+
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(n)		(0x06c + 0x24 * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT	0
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT	1
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(n)	(0x070 + 0x24 * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(n)	(0x074 + 0x24 * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(n)		(0x078 + 0x24 * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT	2
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK	(0x1F << 2)
+
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(n)		(0x07c + 0x24 * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT	16
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(n)	\
+							(0x088 + 0x24 * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(n)	\
+							(0x08c + 0x24 * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF	0xffffffff
+
+#define VFE_0_BUS_PING_PONG_STATUS	0x268
+
+#define VFE_0_BUS_BDG_CMD		0x2c0
+#define VFE_0_BUS_BDG_CMD_HALT_REQ	1
+
+#define VFE_0_BUS_BDG_QOS_CFG_0		0x2c4
+#define VFE_0_BUS_BDG_QOS_CFG_1		0x2c8
+#define VFE_0_BUS_BDG_QOS_CFG_2		0x2cc
+#define VFE_0_BUS_BDG_QOS_CFG_3		0x2d0
+#define VFE_0_BUS_BDG_QOS_CFG_4		0x2d4
+#define VFE_0_BUS_BDG_QOS_CFG_5		0x2d8
+#define VFE_0_BUS_BDG_QOS_CFG_6		0x2dc
+#define VFE_0_BUS_BDG_QOS_CFG_7		0x2e0
+
+#define VFE_0_RDI_CFG_x(x)		(0x2e8 + (0x4 * (x)))
+#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT	28
+#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK	(0xf << 28)
+#define VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT	4
+#define VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK		(0xf << 4)
+#define VFE_0_RDI_CFG_x_RDI_EN_BIT		(1 << 2)
+#define VFE_0_RDI_CFG_x_MIPI_EN_BITS		0x3
+#define VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(r)	(1 << (16 + (r)))
+
+#define VFE_0_REG_UPDATE			0x378
+#define VFE_0_REG_UPDATE_RDIn(n)		(1 << (1 + (n)))
+
+#define VFE_0_CGC_OVERRIDE_1			0x974
+#define VFE_0_CGC_OVERRIDE_1_IMAGE_Mx_CGC_OVERRIDE(x)	(1 << (x))
+
+/* VFE reset timeout */
+#define VFE_RESET_TIMEOUT_MS 50
+/* VFE halt timeout */
+#define VFE_HALT_TIMEOUT_MS 100
+/* Max number of frame drop updates per frame */
+#define VFE_FRAME_DROP_UPDATES 5
+/* Frame drop value. NOTE: VAL + UPDATES should not exceed 31 */
+#define VFE_FRAME_DROP_VAL 20
+
+static const u32 vfe_formats[] = {
+	MEDIA_BUS_FMT_UYVY8_2X8,
+	MEDIA_BUS_FMT_VYUY8_2X8,
+	MEDIA_BUS_FMT_YUYV8_2X8,
+	MEDIA_BUS_FMT_YVYU8_2X8,
+	MEDIA_BUS_FMT_SBGGR8_1X8,
+	MEDIA_BUS_FMT_SGBRG8_1X8,
+	MEDIA_BUS_FMT_SGRBG8_1X8,
+	MEDIA_BUS_FMT_SRGGB8_1X8,
+	MEDIA_BUS_FMT_SBGGR10_1X10,
+	MEDIA_BUS_FMT_SGBRG10_1X10,
+	MEDIA_BUS_FMT_SGRBG10_1X10,
+	MEDIA_BUS_FMT_SRGGB10_1X10,
+	MEDIA_BUS_FMT_SBGGR12_1X12,
+	MEDIA_BUS_FMT_SGBRG12_1X12,
+	MEDIA_BUS_FMT_SGRBG12_1X12,
+	MEDIA_BUS_FMT_SRGGB12_1X12,
+};
+
+static inline void vfe_reg_clr(struct vfe_device *vfe, u32 reg, u32 clr_bits)
+{
+	u32 bits = readl_relaxed(vfe->base + reg);
+
+	writel_relaxed(bits & ~clr_bits, vfe->base + reg);
+}
+
+static inline void vfe_reg_set(struct vfe_device *vfe, u32 reg, u32 set_bits)
+{
+	u32 bits = readl_relaxed(vfe->base + reg);
+
+	writel_relaxed(bits | set_bits, vfe->base + reg);
+}
+
+static void vfe_global_reset(struct vfe_device *vfe)
+{
+	u32 reset_bits = VFE_0_GLOBAL_RESET_CMD_TESTGEN		|
+			 VFE_0_GLOBAL_RESET_CMD_BUS_MISR	|
+			 VFE_0_GLOBAL_RESET_CMD_PM		|
+			 VFE_0_GLOBAL_RESET_CMD_TIMER		|
+			 VFE_0_GLOBAL_RESET_CMD_REGISTER	|
+			 VFE_0_GLOBAL_RESET_CMD_BUS_BDG		|
+			 VFE_0_GLOBAL_RESET_CMD_BUS		|
+			 VFE_0_GLOBAL_RESET_CMD_CAMIF		|
+			 VFE_0_GLOBAL_RESET_CMD_CORE;
+
+	writel_relaxed(reset_bits, vfe->base + VFE_0_GLOBAL_RESET_CMD);
+}
+
+static void vfe_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable)
+{
+	if (enable)
+		vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm),
+			    1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT);
+	else
+		vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm),
+			    1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT);
+}
+
+static void vfe_wm_frame_based(struct vfe_device *vfe, u8 wm, u8 enable)
+{
+	if (enable)
+		vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm),
+			1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT);
+	else
+		vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm),
+			1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT);
+}
+
+static void vfe_wm_set_framedrop_period(struct vfe_device *vfe, u8 wm, u8 per)
+{
+	u32 reg;
+
+	reg = readl_relaxed(vfe->base +
+			    VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm));
+
+	reg &= ~(VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK);
+
+	reg |= (per << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT)
+		& VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK;
+
+	writel_relaxed(reg,
+		       vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm));
+}
+
+static void vfe_wm_set_framedrop_pattern(struct vfe_device *vfe, u8 wm,
+					 u32 pattern)
+{
+	writel_relaxed(pattern,
+	       vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(wm));
+}
+
+static void vfe_wm_set_ub_cfg(struct vfe_device *vfe, u8 wm, u16 offset,
+			      u16 depth)
+{
+	u32 reg;
+
+	reg = (offset << VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT) |
+		depth;
+	writel_relaxed(reg, vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(wm));
+}
+
+static void vfe_bus_reload_wm(struct vfe_device *vfe, u8 wm)
+{
+	wmb();
+	writel_relaxed(VFE_0_BUS_CMD_Mx_RLD_CMD(wm), vfe->base + VFE_0_BUS_CMD);
+	wmb();
+}
+
+static void vfe_wm_set_ping_addr(struct vfe_device *vfe, u8 wm, u32 addr)
+{
+	writel_relaxed(addr,
+		       vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(wm));
+}
+
+static void vfe_wm_set_pong_addr(struct vfe_device *vfe, u8 wm, u32 addr)
+{
+	writel_relaxed(addr,
+		       vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(wm));
+}
+
+static int vfe_wm_get_ping_pong_status(struct vfe_device *vfe, u8 wm)
+{
+	u32 reg;
+
+	reg = readl_relaxed(vfe->base + VFE_0_BUS_PING_PONG_STATUS);
+
+	return (reg >> wm) & 0x1;
+}
+
+static void vfe_bus_enable_wr_if(struct vfe_device *vfe, u8 enable)
+{
+	if (enable)
+		writel_relaxed(0x10000009, vfe->base + VFE_0_BUS_CFG);
+	else
+		writel_relaxed(0, vfe->base + VFE_0_BUS_CFG);
+}
+
+static void vfe_bus_connect_wm_to_rdi(struct vfe_device *vfe, u8 wm,
+				      enum vfe_line_id id)
+{
+	u32 reg;
+
+	reg = VFE_0_RDI_CFG_x_MIPI_EN_BITS;
+	reg |= VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(id);
+	vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), reg);
+
+	reg = VFE_0_RDI_CFG_x_RDI_EN_BIT;
+	reg |= ((3 * id) << VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT) &
+		VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK;
+	vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id), reg);
+
+	switch (id) {
+	case VFE_LINE_RDI0:
+	default:
+		reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 <<
+		      VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+		break;
+	case VFE_LINE_RDI1:
+		reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 <<
+		      VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+		break;
+	case VFE_LINE_RDI2:
+		reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 <<
+		      VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+		break;
+	}
+
+	if (wm % 2 == 1)
+		reg <<= 16;
+
+	vfe_reg_set(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg);
+
+	writel_relaxed(VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF,
+	       vfe->base +
+	       VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(wm));
+}
+
+static void vfe_bus_disconnect_wm_from_rdi(struct vfe_device *vfe, u8 wm,
+					   enum vfe_line_id id)
+{
+	u32 reg;
+
+	reg = VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(id);
+	vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(0), reg);
+
+	reg = VFE_0_RDI_CFG_x_RDI_EN_BIT;
+	vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), reg);
+
+	switch (id) {
+	case VFE_LINE_RDI0:
+	default:
+		reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 <<
+		      VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+		break;
+	case VFE_LINE_RDI1:
+		reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 <<
+		      VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+		break;
+	case VFE_LINE_RDI2:
+		reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 <<
+		      VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+		break;
+	}
+
+	if (wm % 2 == 1)
+		reg <<= 16;
+
+	vfe_reg_clr(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg);
+}
+
+static void vfe_set_rdi_cid(struct vfe_device *vfe, enum vfe_line_id id, u8 cid)
+{
+	vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id),
+		    VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK);
+
+	vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id),
+		    cid << VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT);
+}
+
+static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
+{
+	vfe->reg_update |= VFE_0_REG_UPDATE_RDIn(line_id);
+	wmb();
+	writel_relaxed(vfe->reg_update, vfe->base + VFE_0_REG_UPDATE);
+	wmb();
+}
+
+static void vfe_enable_irq_wm_line(struct vfe_device *vfe, u8 wm,
+				   enum vfe_line_id line_id, u8 enable)
+{
+	u32 irq_en0 = VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(wm) |
+		      VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(line_id);
+	u32 irq_en1 = VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(wm);
+
+	if (enable) {
+		vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+		vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+	} else {
+		vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+		vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+	}
+}
+
+static void vfe_enable_irq_common(struct vfe_device *vfe)
+{
+	u32 irq_en0 = VFE_0_IRQ_MASK_0_RESET_ACK;
+	u32 irq_en1 = VFE_0_IRQ_MASK_1_VIOLATION |
+		      VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK;
+
+	vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+	vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+}
+
+/*
+ * vfe_reset - Trigger reset on VFE module and wait to complete
+ * @vfe: VFE device
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int vfe_reset(struct vfe_device *vfe)
+{
+	unsigned long time;
+
+	reinit_completion(&vfe->reset_complete);
+
+	vfe_global_reset(vfe);
+
+	time = wait_for_completion_timeout(&vfe->reset_complete,
+		msecs_to_jiffies(VFE_RESET_TIMEOUT_MS));
+	if (!time) {
+		dev_err(to_device(vfe), "VFE reset timeout\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/*
+ * vfe_halt - Trigger halt on VFE module and wait to complete
+ * @vfe: VFE device
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int vfe_halt(struct vfe_device *vfe)
+{
+	unsigned long time;
+
+	reinit_completion(&vfe->halt_complete);
+
+	writel_relaxed(VFE_0_BUS_BDG_CMD_HALT_REQ,
+		       vfe->base + VFE_0_BUS_BDG_CMD);
+
+	time = wait_for_completion_timeout(&vfe->halt_complete,
+		msecs_to_jiffies(VFE_HALT_TIMEOUT_MS));
+	if (!time) {
+		dev_err(to_device(vfe), "VFE halt timeout\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void vfe_init_outputs(struct vfe_device *vfe)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(vfe->line); i++) {
+		struct vfe_output *output = &vfe->line[i].output;
+
+		output->state = VFE_OUTPUT_OFF;
+		output->buf[0] = NULL;
+		output->buf[1] = NULL;
+		INIT_LIST_HEAD(&output->pending_bufs);
+	}
+}
+
+static void vfe_reset_output_maps(struct vfe_device *vfe)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++)
+		vfe->wm_output_map[i] = VFE_LINE_NONE;
+}
+
+static void vfe_set_qos(struct vfe_device *vfe)
+{
+	u32 val = 0xaaa5aaa5;
+	u32 val7 = 0x0001aaa5;
+
+	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_0);
+	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_1);
+	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_2);
+	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_3);
+	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_4);
+	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_5);
+	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_6);
+	writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7);
+}
+
+static void vfe_set_cgc_override(struct vfe_device *vfe, u8 wm, u8 enable)
+{
+	u32 val = VFE_0_CGC_OVERRIDE_1_IMAGE_Mx_CGC_OVERRIDE(wm);
+
+	if (enable)
+		vfe_reg_set(vfe, VFE_0_CGC_OVERRIDE_1, val);
+	else
+		vfe_reg_clr(vfe, VFE_0_CGC_OVERRIDE_1, val);
+
+	wmb();
+}
+
+static void vfe_output_init_addrs(struct vfe_device *vfe,
+				  struct vfe_output *output, u8 sync)
+{
+	u32 ping_addr = 0;
+	u32 pong_addr = 0;
+
+	output->active_buf = 0;
+
+	if (output->buf[0])
+		ping_addr = output->buf[0]->addr;
+
+	if (output->buf[1])
+		pong_addr = output->buf[1]->addr;
+	else
+		pong_addr = ping_addr;
+
+	vfe_wm_set_ping_addr(vfe, output->wm_idx, ping_addr);
+	vfe_wm_set_pong_addr(vfe, output->wm_idx, pong_addr);
+	if (sync)
+		vfe_bus_reload_wm(vfe, output->wm_idx);
+}
+
+static void vfe_output_update_ping_addr(struct vfe_device *vfe,
+					struct vfe_output *output, u8 sync)
+{
+	u32 addr = 0;
+
+	if (output->buf[0])
+		addr = output->buf[0]->addr;
+
+	vfe_wm_set_ping_addr(vfe, output->wm_idx, addr);
+	if (sync)
+		vfe_bus_reload_wm(vfe, output->wm_idx);
+}
+
+static void vfe_output_update_pong_addr(struct vfe_device *vfe,
+					struct vfe_output *output, u8 sync)
+{
+	u32 addr = 0;
+
+	if (output->buf[1])
+		addr = output->buf[1]->addr;
+
+	vfe_wm_set_pong_addr(vfe, output->wm_idx, addr);
+	if (sync)
+		vfe_bus_reload_wm(vfe, output->wm_idx);
+
+}
+
+static int vfe_reserve_wm(struct vfe_device *vfe, enum vfe_line_id line_id)
+{
+	int ret = -EBUSY;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++) {
+		if (vfe->wm_output_map[i] == VFE_LINE_NONE) {
+			vfe->wm_output_map[i] = line_id;
+			ret = i;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int vfe_release_wm(struct vfe_device *vfe, u8 wm)
+{
+	if (wm > ARRAY_SIZE(vfe->wm_output_map))
+		return -EINVAL;
+
+	vfe->wm_output_map[wm] = VFE_LINE_NONE;
+
+	return 0;
+}
+
+static void vfe_output_frame_drop(struct vfe_device *vfe,
+				  struct vfe_output *output,
+				  u32 drop_pattern)
+{
+	u8 drop_period;
+
+	/* We need to toggle update period to be valid on next frame */
+	output->drop_update_idx++;
+	output->drop_update_idx %= VFE_FRAME_DROP_UPDATES;
+	drop_period = VFE_FRAME_DROP_VAL + output->drop_update_idx;
+
+	vfe_wm_set_framedrop_period(vfe, output->wm_idx, drop_period);
+	vfe_wm_set_framedrop_pattern(vfe, output->wm_idx, drop_pattern);
+	vfe_reg_update(vfe, container_of(output, struct vfe_line, output)->id);
+
+}
+
+static struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output)
+{
+	struct camss_buffer *buffer = NULL;
+
+	if (!list_empty(&output->pending_bufs)) {
+		buffer = list_first_entry(&output->pending_bufs,
+					  struct camss_buffer,
+					  queue);
+		list_del(&buffer->queue);
+	}
+
+	return buffer;
+}
+
+/*
+ * vfe_buf_add_pending - Add output buffer to list of pending
+ * @output: VFE output
+ * @buffer: Video buffer
+ */
+static void vfe_buf_add_pending(struct vfe_output *output,
+				struct camss_buffer *buffer)
+{
+	INIT_LIST_HEAD(&buffer->queue);
+	list_add_tail(&buffer->queue, &output->pending_bufs);
+}
+
+/*
+ * vfe_buf_flush_pending - Flush all pending buffers.
+ * @output: VFE output
+ * @state: vb2 buffer state
+ */
+static void vfe_buf_flush_pending(struct vfe_output *output,
+				  enum vb2_buffer_state state)
+{
+	struct camss_buffer *buf;
+	struct camss_buffer *t;
+
+	list_for_each_entry_safe(buf, t, &output->pending_bufs, queue) {
+		vb2_buffer_done(&buf->vb.vb2_buf, state);
+		list_del(&buf->queue);
+	}
+}
+
+static void vfe_buf_update_wm_on_next(struct vfe_device *vfe,
+				      struct vfe_output *output)
+{
+	switch (output->state) {
+	case VFE_OUTPUT_CONTINUOUS:
+		vfe_output_frame_drop(vfe, output, 3);
+		break;
+	case VFE_OUTPUT_SINGLE:
+	default:
+		dev_err_ratelimited(to_device(vfe),
+				    "Next buf in wrong state! %d\n",
+				    output->state);
+		break;
+	}
+}
+
+static void vfe_buf_update_wm_on_last(struct vfe_device *vfe,
+				      struct vfe_output *output)
+{
+	switch (output->state) {
+	case VFE_OUTPUT_CONTINUOUS:
+		output->state = VFE_OUTPUT_SINGLE;
+		vfe_output_frame_drop(vfe, output, 1);
+		break;
+	case VFE_OUTPUT_SINGLE:
+		output->state = VFE_OUTPUT_STOPPING;
+		vfe_output_frame_drop(vfe, output, 0);
+		break;
+	default:
+		dev_err_ratelimited(to_device(vfe),
+				    "Last buff in wrong state! %d\n",
+				    output->state);
+		break;
+	}
+}
+
+static void vfe_buf_update_wm_on_new(struct vfe_device *vfe,
+				     struct vfe_output *output,
+				     struct camss_buffer *new_buf)
+{
+	int inactive_idx;
+
+	switch (output->state) {
+	case VFE_OUTPUT_SINGLE:
+		inactive_idx = !output->active_buf;
+
+		if (!output->buf[inactive_idx]) {
+			output->buf[inactive_idx] = new_buf;
+
+			if (inactive_idx)
+				vfe_output_update_pong_addr(vfe, output, 0);
+			else
+				vfe_output_update_ping_addr(vfe, output, 0);
+
+			vfe_output_frame_drop(vfe, output, 3);
+			output->state = VFE_OUTPUT_CONTINUOUS;
+		} else {
+			vfe_buf_add_pending(output, new_buf);
+			dev_err_ratelimited(to_device(vfe),
+					    "Inactive buffer is busy\n");
+		}
+		break;
+
+	case VFE_OUTPUT_IDLE:
+		if (!output->buf[0]) {
+			output->buf[0] = new_buf;
+
+			vfe_output_init_addrs(vfe, output, 1);
+
+			vfe_output_frame_drop(vfe, output, 1);
+			output->state = VFE_OUTPUT_SINGLE;
+		} else {
+			vfe_buf_add_pending(output, new_buf);
+			dev_err_ratelimited(to_device(vfe),
+					    "Output idle with buffer set!\n");
+		}
+		break;
+
+	case VFE_OUTPUT_CONTINUOUS:
+	default:
+		vfe_buf_add_pending(output, new_buf);
+		break;
+	}
+}
+
+static int vfe_get_output(struct vfe_line *line)
+{
+	struct vfe_device *vfe = to_vfe(line);
+	struct vfe_output *output;
+	unsigned long flags;
+	int wm_idx;
+
+	spin_lock_irqsave(&vfe->output_lock, flags);
+
+	output = &line->output;
+	if (output->state != VFE_OUTPUT_OFF) {
+		dev_err(to_device(vfe), "Output is running\n");
+		goto error;
+	}
+	output->state = VFE_OUTPUT_RESERVED;
+
+	output->active_buf = 0;
+
+	/* We will use only one wm per output for now */
+	wm_idx = vfe_reserve_wm(vfe, line->id);
+	if (wm_idx < 0) {
+		dev_err(to_device(vfe), "Can not reserve wm\n");
+		goto error_get_wm;
+	}
+	output->drop_update_idx = 0;
+	output->wm_idx = wm_idx;
+
+	spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+	return 0;
+
+error_get_wm:
+	output->state = VFE_OUTPUT_OFF;
+error:
+	spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+	return -EINVAL;
+}
+
+static int vfe_put_output(struct vfe_line *line)
+{
+	struct vfe_device *vfe = to_vfe(line);
+	struct vfe_output *output = &line->output;
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&vfe->output_lock, flags);
+
+	ret = vfe_release_wm(vfe, output->wm_idx);
+	if (ret < 0)
+		goto out;
+
+	output->state = VFE_OUTPUT_OFF;
+
+out:
+	spin_unlock_irqrestore(&vfe->output_lock, flags);
+	return ret;
+}
+
+static int vfe_enable_output(struct vfe_line *line)
+{
+	struct vfe_device *vfe = to_vfe(line);
+	struct vfe_output *output = &line->output;
+	unsigned long flags;
+	u16 ub_size;
+
+	switch (vfe->id) {
+	case 0:
+		ub_size = MSM_VFE_VFE0_UB_SIZE_RDI;
+		break;
+	case 1:
+		ub_size = MSM_VFE_VFE1_UB_SIZE_RDI;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&vfe->output_lock, flags);
+
+	vfe->reg_update &= ~VFE_0_REG_UPDATE_RDIn(line->id);
+
+	if (output->state != VFE_OUTPUT_RESERVED) {
+		dev_err(to_device(vfe), "Output is not in reserved state %d\n",
+			output->state);
+		spin_unlock_irqrestore(&vfe->output_lock, flags);
+		return -EINVAL;
+	}
+	output->state = VFE_OUTPUT_IDLE;
+
+	output->buf[0] = vfe_buf_get_pending(output);
+	output->buf[1] = vfe_buf_get_pending(output);
+
+	if (!output->buf[0] && output->buf[1]) {
+		output->buf[0] = output->buf[1];
+		output->buf[1] = NULL;
+	}
+
+	if (output->buf[0])
+		output->state = VFE_OUTPUT_SINGLE;
+
+	if (output->buf[1])
+		output->state = VFE_OUTPUT_CONTINUOUS;
+
+	switch (output->state) {
+	case VFE_OUTPUT_SINGLE:
+		vfe_output_frame_drop(vfe, output, 1);
+		break;
+	case VFE_OUTPUT_CONTINUOUS:
+		vfe_output_frame_drop(vfe, output, 3);
+		break;
+	default:
+		vfe_output_frame_drop(vfe, output, 0);
+		break;
+	}
+
+	output->sequence = 0;
+
+	vfe_output_init_addrs(vfe, output, 0);
+
+	vfe_set_cgc_override(vfe, output->wm_idx, 1);
+
+	vfe_enable_irq_wm_line(vfe, output->wm_idx, line->id, 1);
+
+	vfe_bus_connect_wm_to_rdi(vfe, output->wm_idx, line->id);
+
+	vfe_set_rdi_cid(vfe, line->id, 0);
+
+	vfe_wm_set_ub_cfg(vfe, output->wm_idx,
+			  (ub_size + 1) * output->wm_idx, ub_size);
+
+	vfe_wm_frame_based(vfe, output->wm_idx, 1);
+	vfe_wm_enable(vfe, output->wm_idx, 1);
+
+	vfe_bus_reload_wm(vfe, output->wm_idx);
+
+	vfe_reg_update(vfe, line->id);
+
+	spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+	return 0;
+}
+
+static int vfe_disable_output(struct vfe_line *line)
+{
+	struct vfe_device *vfe = to_vfe(line);
+	struct vfe_output *output = &line->output;
+	unsigned long flags;
+
+	spin_lock_irqsave(&vfe->output_lock, flags);
+
+	vfe_wm_enable(vfe, output->wm_idx, 0);
+	vfe_bus_disconnect_wm_from_rdi(vfe, output->wm_idx, line->id);
+	vfe_reg_update(vfe, line->id);
+
+	spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+	return 0;
+}
+
+/*
+ * vfe_enable - Enable streaming on VFE line
+ * @line: VFE line
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int vfe_enable(struct vfe_line *line)
+{
+	struct vfe_device *vfe = to_vfe(line);
+	int ret;
+
+	mutex_lock(&vfe->stream_lock);
+
+	if (!vfe->stream_count) {
+		vfe_enable_irq_common(vfe);
+
+		vfe_bus_enable_wr_if(vfe, 1);
+
+		vfe_set_qos(vfe);
+	}
+
+	vfe->stream_count++;
+
+	mutex_unlock(&vfe->stream_lock);
+
+	ret = vfe_get_output(line);
+	if (ret < 0)
+		goto error_get_output;
+
+	ret = vfe_enable_output(line);
+	if (ret < 0)
+		goto error_enable_output;
+
+	vfe->was_streaming = 1;
+
+	return 0;
+
+
+error_enable_output:
+	vfe_put_output(line);
+
+error_get_output:
+	mutex_lock(&vfe->stream_lock);
+
+	if (vfe->stream_count == 1)
+		vfe_bus_enable_wr_if(vfe, 0);
+
+	vfe->stream_count--;
+
+	mutex_unlock(&vfe->stream_lock);
+
+	return ret;
+}
+
+/*
+ * vfe_disable - Disable streaming on VFE line
+ * @line: VFE line
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int vfe_disable(struct vfe_line *line)
+{
+	struct vfe_device *vfe = to_vfe(line);
+
+	mutex_lock(&vfe->stream_lock);
+
+	if (vfe->stream_count == 1)
+		vfe_bus_enable_wr_if(vfe, 0);
+
+	vfe->stream_count--;
+
+	mutex_unlock(&vfe->stream_lock);
+
+	vfe_disable_output(line);
+
+	vfe_put_output(line);
+
+	return 0;
+}
+
+/*
+ * vfe_isr_reg_update - Process reg update interrupt
+ * @vfe: VFE Device
+ * @line_id: VFE line
+ */
+static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
+{
+	struct vfe_output *output;
+	unsigned long flags;
+
+	spin_lock_irqsave(&vfe->output_lock, flags);
+	vfe->reg_update &= ~VFE_0_REG_UPDATE_RDIn(line_id);
+
+	output = &vfe->line[line_id].output;
+	if (output->state == VFE_OUTPUT_STOPPING) {
+		/* Release last buffer when hw is idle */
+		if (output->last_buffer) {
+			vb2_buffer_done(&output->last_buffer->vb.vb2_buf,
+					VB2_BUF_STATE_DONE);
+			output->last_buffer = NULL;
+		}
+		output->state = VFE_OUTPUT_IDLE;
+
+		/* Buffers received in stopping state are queued in */
+		/* dma pending queue, start next capture here */
+
+		output->buf[0] = vfe_buf_get_pending(output);
+		output->buf[1] = vfe_buf_get_pending(output);
+
+		if (!output->buf[0] && output->buf[1]) {
+			output->buf[0] = output->buf[1];
+			output->buf[1] = NULL;
+		}
+
+		if (output->buf[0])
+			output->state = VFE_OUTPUT_SINGLE;
+
+		if (output->buf[1])
+			output->state = VFE_OUTPUT_CONTINUOUS;
+
+		switch (output->state) {
+		case VFE_OUTPUT_SINGLE:
+			vfe_output_frame_drop(vfe, output, 2);
+			break;
+		case VFE_OUTPUT_CONTINUOUS:
+			vfe_output_frame_drop(vfe, output, 3);
+			break;
+		default:
+			vfe_output_frame_drop(vfe, output, 0);
+			break;
+		}
+
+		vfe_output_init_addrs(vfe, output, 1);
+	}
+
+	spin_unlock_irqrestore(&vfe->output_lock, flags);
+}
+
+/*
+ * vfe_isr_wm_done - Process write master done interrupt
+ * @vfe: VFE Device
+ * @wm: Write master id
+ */
+static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm)
+{
+	struct camss_buffer *ready_buf;
+	struct vfe_output *output;
+	dma_addr_t new_addr;
+	unsigned long flags;
+	u32 active_index;
+	u64 ts = ktime_get_ns();
+
+	active_index = vfe_wm_get_ping_pong_status(vfe, wm);
+
+	spin_lock_irqsave(&vfe->output_lock, flags);
+
+	if (vfe->wm_output_map[wm] == VFE_LINE_NONE) {
+		dev_err_ratelimited(to_device(vfe),
+				    "Received wm done for unmapped index\n");
+		goto out_unlock;
+	}
+	output = &vfe->line[vfe->wm_output_map[wm]].output;
+
+	if (output->active_buf == active_index) {
+		dev_err_ratelimited(to_device(vfe),
+				    "Active buffer mismatch!\n");
+		goto out_unlock;
+	}
+	output->active_buf = active_index;
+
+	ready_buf = output->buf[!active_index];
+	if (!ready_buf) {
+		dev_err_ratelimited(to_device(vfe),
+				    "Missing ready buf %d %d!\n",
+				    !active_index, output->state);
+		goto out_unlock;
+	}
+
+	ready_buf->vb.vb2_buf.timestamp = ts;
+	ready_buf->vb.sequence = output->sequence++;
+
+	/* Get next buffer */
+	output->buf[!active_index] = vfe_buf_get_pending(output);
+	if (!output->buf[!active_index]) {
+		/* No next buffer - set same address */
+		new_addr = ready_buf->addr;
+		vfe_buf_update_wm_on_last(vfe, output);
+	} else {
+		new_addr = output->buf[!active_index]->addr;
+		vfe_buf_update_wm_on_next(vfe, output);
+	}
+
+	if (active_index)
+		vfe_wm_set_ping_addr(vfe, wm, new_addr);
+	else
+		vfe_wm_set_pong_addr(vfe, wm, new_addr);
+
+	spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+	if (output->state == VFE_OUTPUT_STOPPING)
+		output->last_buffer = ready_buf;
+	else
+		vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+
+	return;
+
+out_unlock:
+	spin_unlock_irqrestore(&vfe->output_lock, flags);
+}
+
+/*
+ * vfe_isr - ISPIF module interrupt handler
+ * @irq: Interrupt line
+ * @dev: VFE device
+ *
+ * Return IRQ_HANDLED on success
+ */
+static irqreturn_t vfe_isr(int irq, void *dev)
+{
+	struct vfe_device *vfe = dev;
+	u32 value0, value1;
+	u32 violation;
+	int i;
+
+	value0 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_0);
+	value1 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_1);
+
+	writel_relaxed(value0, vfe->base + VFE_0_IRQ_CLEAR_0);
+	writel_relaxed(value1, vfe->base + VFE_0_IRQ_CLEAR_1);
+
+	wmb();
+	writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD);
+
+	if (value0 & VFE_0_IRQ_STATUS_0_RESET_ACK)
+		complete(&vfe->reset_complete);
+
+	if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION) {
+		violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS);
+		dev_err_ratelimited(to_device(vfe),
+				    "VFE: violation = 0x%08x\n", violation);
+	}
+
+	if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK) {
+		complete(&vfe->halt_complete);
+		writel_relaxed(0x0, vfe->base + VFE_0_BUS_BDG_CMD);
+	}
+
+	for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++)
+		if (value0 & VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(i))
+			vfe_isr_reg_update(vfe, i);
+
+	for (i = 0; i < MSM_VFE_IMAGE_MASTERS_NUM; i++)
+		if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(i))
+			vfe_isr_wm_done(vfe, i);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * vfe_get - Power up and reset VFE module
+ * @vfe: VFE Device
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int vfe_get(struct vfe_device *vfe)
+{
+	int ret;
+
+	mutex_lock(&vfe->power_lock);
+
+	if (vfe->power_count == 0) {
+		ret = camss_enable_clocks(vfe->nclocks, vfe->clock,
+					  to_device(vfe));
+		if (ret < 0)
+			goto error_clocks;
+
+		ret = vfe_reset(vfe);
+		if (ret < 0)
+			goto error_reset;
+
+		vfe_reset_output_maps(vfe);
+
+		vfe_init_outputs(vfe);
+	}
+	vfe->power_count++;
+
+	mutex_unlock(&vfe->power_lock);
+
+	return 0;
+
+error_reset:
+	camss_disable_clocks(vfe->nclocks, vfe->clock);
+
+error_clocks:
+	mutex_unlock(&vfe->power_lock);
+
+	return ret;
+}
+
+/*
+ * vfe_put - Power down VFE module
+ * @vfe: VFE Device
+ */
+static void vfe_put(struct vfe_device *vfe)
+{
+	mutex_lock(&vfe->power_lock);
+
+	if (vfe->power_count == 0) {
+		dev_err(to_device(vfe), "vfe power off on power_count == 0\n");
+		goto exit;
+	} else if (vfe->power_count == 1) {
+		if (vfe->was_streaming) {
+			vfe->was_streaming = 0;
+			vfe_halt(vfe);
+		}
+		camss_disable_clocks(vfe->nclocks, vfe->clock);
+	}
+
+	vfe->power_count--;
+
+exit:
+	mutex_unlock(&vfe->power_lock);
+}
+
+/*
+ * vfe_video_pad_to_line - Get pointer to VFE line by media pad
+ * @pad: Media pad
+ *
+ * Return pointer to vfe line structure
+ */
+static struct vfe_line *vfe_video_pad_to_line(struct media_pad *pad)
+{
+	struct media_pad *vfe_pad;
+	struct v4l2_subdev *subdev;
+
+	vfe_pad = media_entity_remote_pad(pad);
+	if (vfe_pad == NULL)
+		return NULL;
+
+	subdev = media_entity_to_v4l2_subdev(vfe_pad->entity);
+
+	return container_of(subdev, struct vfe_line, subdev);
+}
+
+/*
+ * vfe_queue_buffer - Add empty buffer
+ * @vid: Video device structure
+ * @buf: Buffer to be enqueued
+ *
+ * Add an empty buffer - depending on the current number of buffers it will be
+ * put in pending buffer queue or directly given to the hardware to be filled.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int vfe_queue_buffer(struct camss_video *vid,
+			    struct camss_buffer *buf)
+{
+	struct vfe_device *vfe = &vid->camss->vfe;
+	struct vfe_line *line;
+	struct vfe_output *output;
+	unsigned long flags;
+
+	line = vfe_video_pad_to_line(&vid->pad);
+	if (!line) {
+		dev_err(to_device(vfe), "Can not queue buffer\n");
+		return -1;
+	}
+	output = &line->output;
+
+	spin_lock_irqsave(&vfe->output_lock, flags);
+
+	vfe_buf_update_wm_on_new(vfe, output, buf);
+
+	spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+	return 0;
+}
+
+/*
+ * vfe_flush_buffers - Return all vb2 buffers
+ * @vid: Video device structure
+ * @state: vb2 buffer state of the returned buffers
+ *
+ * Return all buffers to vb2. This includes queued pending buffers (still
+ * unused) and any buffers given to the hardware but again still not used.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int vfe_flush_buffers(struct camss_video *vid,
+			     enum vb2_buffer_state state)
+{
+	struct vfe_device *vfe = &vid->camss->vfe;
+	struct vfe_line *line;
+	struct vfe_output *output;
+	unsigned long flags;
+
+	line = vfe_video_pad_to_line(&vid->pad);
+	if (!line) {
+		dev_err(to_device(vfe),	"Can not flush buffers\n");
+		return -1;
+	}
+	output = &line->output;
+
+	spin_lock_irqsave(&vfe->output_lock, flags);
+
+	vfe_buf_flush_pending(output, state);
+
+	if (output->buf[0])
+		vb2_buffer_done(&output->buf[0]->vb.vb2_buf, state);
+
+	if (output->buf[1])
+		vb2_buffer_done(&output->buf[1]->vb.vb2_buf, state);
+
+	if (output->last_buffer) {
+		vb2_buffer_done(&output->last_buffer->vb.vb2_buf, state);
+		output->last_buffer = NULL;
+	}
+
+	spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+	return 0;
+}
+
+/*
+ * vfe_set_power - Power on/off VFE module
+ * @sd: VFE V4L2 subdevice
+ * @on: Requested power state
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int vfe_set_power(struct v4l2_subdev *sd, int on)
+{
+	struct vfe_line *line = v4l2_get_subdevdata(sd);
+	struct vfe_device *vfe = to_vfe(line);
+	int ret;
+
+	if (on) {
+		u32 hw_version;
+
+		ret = vfe_get(vfe);
+		if (ret < 0)
+			return ret;
+
+		hw_version = readl_relaxed(vfe->base + VFE_0_HW_VERSION);
+		dev_dbg(to_device(vfe),
+			"VFE HW Version = 0x%08x\n", hw_version);
+	} else {
+		vfe_put(vfe);
+	}
+
+	return 0;
+}
+
+/*
+ * vfe_set_stream - Enable/disable streaming on VFE module
+ * @sd: VFE V4L2 subdevice
+ * @enable: Requested streaming state
+ *
+ * Main configuration of VFE module is triggered here.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int vfe_set_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct vfe_line *line = v4l2_get_subdevdata(sd);
+	struct vfe_device *vfe = to_vfe(line);
+	int ret;
+
+	if (enable) {
+		ret = vfe_enable(line);
+		if (ret < 0)
+			dev_err(to_device(vfe),
+				"Failed to enable vfe outputs\n");
+	} else {
+		ret = vfe_disable(line);
+		if (ret < 0)
+			dev_err(to_device(vfe),
+				"Failed to disable vfe outputs\n");
+	}
+
+	return ret;
+}
+
+/*
+ * __vfe_get_format - Get pointer to format structure
+ * @line: VFE line
+ * @cfg: V4L2 subdev pad configuration
+ * @pad: pad from which format is requested
+ * @which: TRY or ACTIVE format
+ *
+ * Return pointer to TRY or ACTIVE format structure
+ */
+static struct v4l2_mbus_framefmt *
+__vfe_get_format(struct vfe_line *line,
+		 struct v4l2_subdev_pad_config *cfg,
+		 unsigned int pad,
+		 enum v4l2_subdev_format_whence which)
+{
+	if (which == V4L2_SUBDEV_FORMAT_TRY)
+		return v4l2_subdev_get_try_format(&line->subdev, cfg, pad);
+
+	return &line->fmt[pad];
+}
+
+
+/*
+ * vfe_try_format - Handle try format by pad subdev method
+ * @line: VFE line
+ * @cfg: V4L2 subdev pad configuration
+ * @pad: pad on which format is requested
+ * @fmt: pointer to v4l2 format structure
+ * @which: wanted subdev format
+ */
+static void vfe_try_format(struct vfe_line *line,
+			   struct v4l2_subdev_pad_config *cfg,
+			   unsigned int pad,
+			   struct v4l2_mbus_framefmt *fmt,
+			   enum v4l2_subdev_format_whence which)
+{
+	unsigned int i;
+
+	switch (pad) {
+	case MSM_VFE_PAD_SINK:
+		/* Set format on sink pad */
+
+		for (i = 0; i < ARRAY_SIZE(vfe_formats); i++)
+			if (fmt->code == vfe_formats[i])
+				break;
+
+		/* If not found, use UYVY as default */
+		if (i >= ARRAY_SIZE(vfe_formats))
+			fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
+
+		fmt->width = clamp_t(u32, fmt->width, 1, 8191);
+		fmt->height = clamp_t(u32, fmt->height, 1, 8191);
+
+		if (fmt->field == V4L2_FIELD_ANY)
+			fmt->field = V4L2_FIELD_NONE;
+
+		break;
+
+	case MSM_VFE_PAD_SRC:
+		/* Set and return a format same as sink pad */
+
+		*fmt = *__vfe_get_format(line, cfg, MSM_VFE_PAD_SINK,
+					 which);
+
+		break;
+	}
+
+	fmt->colorspace = V4L2_COLORSPACE_SRGB;
+}
+
+/*
+ * vfe_enum_mbus_code - Handle pixel format enumeration
+ * @sd: VFE V4L2 subdevice
+ * @cfg: V4L2 subdev pad configuration
+ * @code: pointer to v4l2_subdev_mbus_code_enum structure
+ *
+ * return -EINVAL or zero on success
+ */
+static int vfe_enum_mbus_code(struct v4l2_subdev *sd,
+			      struct v4l2_subdev_pad_config *cfg,
+			      struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct vfe_line *line = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt *format;
+
+	if (code->pad == MSM_VFE_PAD_SINK) {
+		if (code->index >= ARRAY_SIZE(vfe_formats))
+			return -EINVAL;
+
+		code->code = vfe_formats[code->index];
+	} else {
+		if (code->index > 0)
+			return -EINVAL;
+
+		format = __vfe_get_format(line, cfg, MSM_VFE_PAD_SINK,
+					  code->which);
+
+		code->code = format->code;
+	}
+
+	return 0;
+}
+
+/*
+ * vfe_enum_frame_size - Handle frame size enumeration
+ * @sd: VFE V4L2 subdevice
+ * @cfg: V4L2 subdev pad configuration
+ * @fse: pointer to v4l2_subdev_frame_size_enum structure
+ *
+ * Return -EINVAL or zero on success
+ */
+static int vfe_enum_frame_size(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_pad_config *cfg,
+			       struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct vfe_line *line = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt format;
+
+	if (fse->index != 0)
+		return -EINVAL;
+
+	format.code = fse->code;
+	format.width = 1;
+	format.height = 1;
+	vfe_try_format(line, cfg, fse->pad, &format, fse->which);
+	fse->min_width = format.width;
+	fse->min_height = format.height;
+
+	if (format.code != fse->code)
+		return -EINVAL;
+
+	format.code = fse->code;
+	format.width = -1;
+	format.height = -1;
+	vfe_try_format(line, cfg, fse->pad, &format, fse->which);
+	fse->max_width = format.width;
+	fse->max_height = format.height;
+
+	return 0;
+}
+
+/*
+ * vfe_get_format - Handle get format by pads subdev method
+ * @sd: VFE V4L2 subdevice
+ * @cfg: V4L2 subdev pad configuration
+ * @fmt: pointer to v4l2 subdev format structure
+ *
+ * Return -EINVAL or zero on success
+ */
+static int vfe_get_format(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct vfe_line *line = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt *format;
+
+	format = __vfe_get_format(line, cfg, fmt->pad, fmt->which);
+	if (format == NULL)
+		return -EINVAL;
+
+	fmt->format = *format;
+
+	return 0;
+}
+
+/*
+ * vfe_set_format - Handle set format by pads subdev method
+ * @sd: VFE V4L2 subdevice
+ * @cfg: V4L2 subdev pad configuration
+ * @fmt: pointer to v4l2 subdev format structure
+ *
+ * Return -EINVAL or zero on success
+ */
+static int vfe_set_format(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct vfe_line *line = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt *format;
+
+	format = __vfe_get_format(line, cfg, fmt->pad, fmt->which);
+	if (format == NULL)
+		return -EINVAL;
+
+	vfe_try_format(line, cfg, fmt->pad, &fmt->format, fmt->which);
+	*format = fmt->format;
+
+	/* Propagate the format from sink to source */
+	if (fmt->pad == MSM_VFE_PAD_SINK) {
+		format = __vfe_get_format(line, cfg, MSM_VFE_PAD_SRC,
+					  fmt->which);
+
+		*format = fmt->format;
+		vfe_try_format(line, cfg, MSM_VFE_PAD_SRC, format,
+			       fmt->which);
+	}
+
+	return 0;
+}
+
+/*
+ * vfe_init_formats - Initialize formats on all pads
+ * @sd: VFE V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int vfe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct v4l2_subdev_format format = {
+		.pad = MSM_VFE_PAD_SINK,
+		.which = fh ? V4L2_SUBDEV_FORMAT_TRY :
+			      V4L2_SUBDEV_FORMAT_ACTIVE,
+		.format = {
+			.code = MEDIA_BUS_FMT_UYVY8_2X8,
+			.width = 1920,
+			.height = 1080
+		}
+	};
+
+	return vfe_set_format(sd, fh ? fh->pad : NULL, &format);
+}
+
+/*
+ * msm_vfe_subdev_init - Initialize VFE device structure and resources
+ * @vfe: VFE device
+ * @res: VFE module resources table
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res)
+{
+	struct device *dev = to_device(vfe);
+	struct platform_device *pdev = to_platform_device(dev);
+	struct resource *r;
+	struct camss *camss = to_camss(vfe);
+
+	int i;
+	int ret;
+
+	mutex_init(&vfe->power_lock);
+	vfe->power_count = 0;
+
+	mutex_init(&vfe->stream_lock);
+	vfe->stream_count = 0;
+
+	spin_lock_init(&vfe->output_lock);
+
+	vfe->id = 0;
+	vfe->reg_update = 0;
+
+	for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++) {
+		vfe->line[i].video_out.type =
+					V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+		vfe->line[i].video_out.camss = camss;
+		vfe->line[i].id = i;
+	}
+
+	/* Memory */
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]);
+	vfe->base = devm_ioremap_resource(dev, r);
+	if (IS_ERR(vfe->base)) {
+		dev_err(dev, "could not map memory\n");
+		return PTR_ERR(vfe->base);
+	}
+
+	/* Interrupt */
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+					 res->interrupt[0]);
+	if (!r) {
+		dev_err(dev, "missing IRQ\n");
+		return -EINVAL;
+	}
+
+	vfe->irq = r->start;
+	snprintf(vfe->irq_name, sizeof(vfe->irq_name), "%s_%s%d",
+		 dev_name(dev), MSM_VFE_NAME, vfe->id);
+	ret = devm_request_irq(dev, vfe->irq, vfe_isr,
+			       IRQF_TRIGGER_RISING, vfe->irq_name, vfe);
+	if (ret < 0) {
+		dev_err(dev, "request_irq failed: %d\n", ret);
+		return ret;
+	}
+
+	/* Clocks */
+
+	vfe->nclocks = 0;
+	while (res->clock[vfe->nclocks])
+		vfe->nclocks++;
+
+	vfe->clock = devm_kzalloc(dev, vfe->nclocks * sizeof(*vfe->clock),
+				  GFP_KERNEL);
+	if (!vfe->clock)
+		return -ENOMEM;
+
+	for (i = 0; i < vfe->nclocks; i++) {
+		vfe->clock[i] = devm_clk_get(dev, res->clock[i]);
+		if (IS_ERR(vfe->clock[i]))
+			return PTR_ERR(vfe->clock[i]);
+
+		if (res->clock_rate[i]) {
+			long clk_rate = clk_round_rate(vfe->clock[i],
+						       res->clock_rate[i]);
+			if (clk_rate < 0) {
+				dev_err(dev, "clk round rate failed\n");
+				return -EINVAL;
+			}
+			ret = clk_set_rate(vfe->clock[i], clk_rate);
+			if (ret < 0) {
+				dev_err(dev, "clk set rate failed\n");
+				return ret;
+			}
+		}
+	}
+
+	init_completion(&vfe->reset_complete);
+	init_completion(&vfe->halt_complete);
+
+	return 0;
+}
+
+/*
+ * msm_vfe_get_vfe_id - Get VFE HW module id
+ * @entity: Pointer to VFE media entity structure
+ * @id: Return CSID HW module id here
+ */
+void msm_vfe_get_vfe_id(struct media_entity *entity, u8 *id)
+{
+	struct v4l2_subdev *sd;
+	struct vfe_line *line;
+	struct vfe_device *vfe;
+
+	sd = media_entity_to_v4l2_subdev(entity);
+	line = v4l2_get_subdevdata(sd);
+	vfe = to_vfe(line);
+
+	*id = vfe->id;
+}
+
+/*
+ * msm_vfe_get_vfe_line_id - Get VFE line id by media entity
+ * @entity: Pointer to VFE media entity structure
+ * @id: Return VFE line id here
+ */
+void msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id)
+{
+	struct v4l2_subdev *sd;
+	struct vfe_line *line;
+
+	sd = media_entity_to_v4l2_subdev(entity);
+	line = v4l2_get_subdevdata(sd);
+
+	*id = line->id;
+}
+
+/*
+ * vfe_link_setup - Setup VFE connections
+ * @entity: Pointer to media entity structure
+ * @local: Pointer to local pad
+ * @remote: Pointer to remote pad
+ * @flags: Link flags
+ *
+ * Return 0 on success
+ */
+static int vfe_link_setup(struct media_entity *entity,
+			  const struct media_pad *local,
+			  const struct media_pad *remote, u32 flags)
+{
+	if (flags & MEDIA_LNK_FL_ENABLED)
+		if (media_entity_remote_pad(local))
+			return -EBUSY;
+
+	return 0;
+}
+
+static const struct v4l2_subdev_core_ops vfe_core_ops = {
+	.s_power = vfe_set_power,
+};
+
+static const struct v4l2_subdev_video_ops vfe_video_ops = {
+	.s_stream = vfe_set_stream,
+};
+
+static const struct v4l2_subdev_pad_ops vfe_pad_ops = {
+	.enum_mbus_code = vfe_enum_mbus_code,
+	.enum_frame_size = vfe_enum_frame_size,
+	.get_fmt = vfe_get_format,
+	.set_fmt = vfe_set_format,
+};
+
+static const struct v4l2_subdev_ops vfe_v4l2_ops = {
+	.core = &vfe_core_ops,
+	.video = &vfe_video_ops,
+	.pad = &vfe_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops vfe_v4l2_internal_ops = {
+	.open = vfe_init_formats,
+};
+
+static const struct media_entity_operations vfe_media_ops = {
+	.link_setup = vfe_link_setup,
+	.link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct camss_video_ops camss_vfe_video_ops = {
+	.queue_buffer = vfe_queue_buffer,
+	.flush_buffers = vfe_flush_buffers,
+};
+
+void msm_vfe_stop_streaming(struct vfe_device *vfe)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(vfe->line); i++)
+		msm_video_stop_streaming(&vfe->line[i].video_out);
+}
+
+/*
+ * msm_vfe_register_entities - Register subdev node for VFE module
+ * @vfe: VFE device
+ * @v4l2_dev: V4L2 device
+ *
+ * Initialize and register a subdev node for the VFE module. Then
+ * call msm_video_register() to register the video device node which
+ * will be connected to this subdev node. Then actually create the
+ * media link between them.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int msm_vfe_register_entities(struct vfe_device *vfe,
+			      struct v4l2_device *v4l2_dev)
+{
+	struct device *dev = to_device(vfe);
+	struct v4l2_subdev *sd;
+	struct media_pad *pads;
+	struct camss_video *video_out;
+	int ret;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(vfe->line); i++) {
+		char name[32];
+
+		sd = &vfe->line[i].subdev;
+		pads = vfe->line[i].pads;
+		video_out = &vfe->line[i].video_out;
+
+		v4l2_subdev_init(sd, &vfe_v4l2_ops);
+		sd->internal_ops = &vfe_v4l2_internal_ops;
+		sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+		snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s%d",
+			 MSM_VFE_NAME, vfe->id, "rdi", i);
+		v4l2_set_subdevdata(sd, &vfe->line[i]);
+
+		ret = vfe_init_formats(sd, NULL);
+		if (ret < 0) {
+			dev_err(dev, "Failed to init format: %d\n", ret);
+			goto error_init;
+		}
+
+		pads[MSM_VFE_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+		pads[MSM_VFE_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
+
+		sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
+		sd->entity.ops = &vfe_media_ops;
+		ret = media_entity_pads_init(&sd->entity, MSM_VFE_PADS_NUM,
+					     pads);
+		if (ret < 0) {
+			dev_err(dev, "Failed to init media entity: %d\n", ret);
+			goto error_init;
+		}
+
+		ret = v4l2_device_register_subdev(v4l2_dev, sd);
+		if (ret < 0) {
+			dev_err(dev, "Failed to register subdev: %d\n", ret);
+			goto error_reg_subdev;
+		}
+
+		video_out->ops = &camss_vfe_video_ops;
+		snprintf(name, ARRAY_SIZE(name), "%s%d_%s%d",
+			 MSM_VFE_NAME, vfe->id, "video", i);
+		ret = msm_video_register(video_out, v4l2_dev, name);
+		if (ret < 0) {
+			dev_err(dev, "Failed to register video node: %d\n",
+				ret);
+			goto error_reg_video;
+		}
+
+		ret = media_create_pad_link(
+				&sd->entity, MSM_VFE_PAD_SRC,
+				&video_out->vdev.entity, 0,
+				MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
+		if (ret < 0) {
+			dev_err(dev, "Failed to link %s->%s entities: %d\n",
+				sd->entity.name, video_out->vdev.entity.name,
+				ret);
+			goto error_link;
+		}
+
+		ret = msm_video_init_format(video_out);
+		if (ret < 0) {
+			dev_err(dev, "Failed to init format: %d\n", ret);
+			goto error_link;
+		}
+
+	}
+
+	return 0;
+
+error_link:
+	msm_video_unregister(video_out);
+
+error_reg_video:
+	v4l2_device_unregister_subdev(sd);
+
+error_reg_subdev:
+	media_entity_cleanup(&sd->entity);
+
+error_init:
+	for (i--; i >= 0; i--) {
+		sd = &vfe->line[i].subdev;
+		video_out = &vfe->line[i].video_out;
+
+		msm_video_unregister(video_out);
+		v4l2_device_unregister_subdev(sd);
+		media_entity_cleanup(&sd->entity);
+	}
+
+	return ret;
+}
+
+/*
+ * msm_vfe_unregister_entities - Unregister VFE module subdev node
+ * @vfe: VFE device
+ */
+void msm_vfe_unregister_entities(struct vfe_device *vfe)
+{
+	int i;
+
+	mutex_destroy(&vfe->power_lock);
+	mutex_destroy(&vfe->stream_lock);
+
+	for (i = 0; i < ARRAY_SIZE(vfe->line); i++) {
+		struct v4l2_subdev *sd = &vfe->line[i].subdev;
+		struct camss_video *video_out = &vfe->line[i].video_out;
+
+		msm_video_unregister(video_out);
+		v4l2_device_unregister_subdev(sd);
+		media_entity_cleanup(&sd->entity);
+	}
+}
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.h b/drivers/media/platform/qcom/camss-8x16/camss-vfe.h
new file mode 100644
index 0000000..6d2fc57
--- /dev/null
+++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.h
@@ -0,0 +1,114 @@
+/*
+ * camss-vfe.h
+ *
+ * Qualcomm MSM Camera Subsystem - VFE Module
+ *
+ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2015-2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef QC_MSM_CAMSS_VFE_H
+#define QC_MSM_CAMSS_VFE_H
+
+#include <linux/clk.h>
+#include <linux/spinlock_types.h>
+#include <media/media-entity.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include "camss-video.h"
+
+#define MSM_VFE_PAD_SINK 0
+#define MSM_VFE_PAD_SRC 1
+#define MSM_VFE_PADS_NUM 2
+
+#define MSM_VFE_LINE_NUM 3
+#define MSM_VFE_IMAGE_MASTERS_NUM 7
+
+#define MSM_VFE_VFE0_UB_SIZE 1023
+#define MSM_VFE_VFE0_UB_SIZE_RDI (MSM_VFE_VFE0_UB_SIZE / 3)
+#define MSM_VFE_VFE1_UB_SIZE 1535
+#define MSM_VFE_VFE1_UB_SIZE_RDI (MSM_VFE_VFE1_UB_SIZE / 3)
+
+enum vfe_output_state {
+	VFE_OUTPUT_OFF,
+	VFE_OUTPUT_RESERVED,
+	VFE_OUTPUT_SINGLE,
+	VFE_OUTPUT_CONTINUOUS,
+	VFE_OUTPUT_IDLE,
+	VFE_OUTPUT_STOPPING
+};
+
+enum vfe_line_id {
+	VFE_LINE_NONE = -1,
+	VFE_LINE_RDI0 = 0,
+	VFE_LINE_RDI1 = 1,
+	VFE_LINE_RDI2 = 2
+};
+
+struct vfe_output {
+	u8 wm_idx;
+
+	int active_buf;
+	struct camss_buffer *buf[2];
+	struct camss_buffer *last_buffer;
+	struct list_head pending_bufs;
+
+	unsigned int drop_update_idx;
+
+	enum vfe_output_state state;
+	unsigned int sequence;
+};
+
+struct vfe_line {
+	enum vfe_line_id id;
+	struct v4l2_subdev subdev;
+	struct media_pad pads[MSM_VFE_PADS_NUM];
+	struct v4l2_mbus_framefmt fmt[MSM_VFE_PADS_NUM];
+	struct camss_video video_out;
+	struct vfe_output output;
+};
+
+struct vfe_device {
+	u8 id;
+	void __iomem *base;
+	u32 irq;
+	char irq_name[30];
+	struct clk **clock;
+	int nclocks;
+	struct completion reset_complete;
+	struct completion halt_complete;
+	struct mutex power_lock;
+	int power_count;
+	struct mutex stream_lock;
+	int stream_count;
+	spinlock_t output_lock;
+	enum vfe_line_id wm_output_map[MSM_VFE_IMAGE_MASTERS_NUM];
+	struct vfe_line line[MSM_VFE_LINE_NUM];
+	u32 reg_update;
+	u8 was_streaming;
+};
+
+struct resources;
+
+int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res);
+
+int msm_vfe_register_entities(struct vfe_device *vfe,
+			      struct v4l2_device *v4l2_dev);
+
+void msm_vfe_unregister_entities(struct vfe_device *vfe);
+
+void msm_vfe_get_vfe_id(struct media_entity *entity, u8 *id);
+void msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id);
+
+void msm_vfe_stop_streaming(struct vfe_device *vfe);
+
+#endif /* QC_MSM_CAMSS_VFE_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v3 11/23] media: camss: Add files which handle the video device nodes
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
                   ` (9 preceding siblings ...)
  2017-07-17 10:33 ` [PATCH v3 10/23] media: camss: Add VFE files Todor Tomov
@ 2017-07-17 10:33 ` Todor Tomov
  2017-07-17 10:33 ` [PATCH v3 12/23] media: camms: Add core files Todor Tomov
                   ` (13 subsequent siblings)
  24 siblings, 0 replies; 60+ messages in thread
From: Todor Tomov @ 2017-07-17 10:33 UTC (permalink / raw)
  To: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm
  Cc: Todor Tomov

These files handle the video device nodes of the camss driver.

Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
---
 .../media/platform/qcom/camss-8x16/camss-video.c   | 627 +++++++++++++++++++++
 .../media/platform/qcom/camss-8x16/camss-video.h   |  66 +++
 2 files changed, 693 insertions(+)
 create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-video.c
 create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-video.h

diff --git a/drivers/media/platform/qcom/camss-8x16/camss-video.c b/drivers/media/platform/qcom/camss-8x16/camss-video.c
new file mode 100644
index 0000000..29483a4
--- /dev/null
+++ b/drivers/media/platform/qcom/camss-8x16/camss-video.c
@@ -0,0 +1,627 @@
+/*
+ * camss-video.c
+ *
+ * Qualcomm MSM Camera Subsystem - V4L2 device node
+ *
+ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2015-2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/slab.h>
+#include <media/media-entity.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/videobuf-core.h>
+#include <media/videobuf2-dma-sg.h>
+
+#include "camss-video.h"
+#include "camss.h"
+
+/*
+ * struct format_info - ISP media bus format information
+ * @code: V4L2 media bus format code
+ * @pixelformat: V4L2 pixel format FCC identifier
+ * @bpp: Bits per pixel when stored in memory
+ */
+static const struct format_info {
+	u32 code;
+	u32 pixelformat;
+	unsigned int bpp;
+} formats[] = {
+	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 16 },
+	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 16 },
+	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 16 },
+	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 16 },
+	{ MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 8 },
+	{ MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 8 },
+	{ MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 8 },
+	{ MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 8 },
+	{ MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 10 },
+	{ MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 10 },
+	{ MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 10 },
+	{ MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 10 },
+	{ MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SRGGB12P, 12 },
+	{ MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 12 },
+	{ MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 12 },
+	{ MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 12 }
+};
+
+/* -----------------------------------------------------------------------------
+ * Helper functions
+ */
+
+/*
+ * video_mbus_to_pix_mp - Convert v4l2_mbus_framefmt to v4l2_pix_format_mplane
+ * @mbus: v4l2_mbus_framefmt format (input)
+ * @pix: v4l2_pix_format_mplane format (output)
+ *
+ * Fill the output pix structure with information from the input mbus format.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static unsigned int video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt *mbus,
+					 struct v4l2_pix_format_mplane *pix)
+{
+	unsigned int i;
+	u32 bytesperline;
+
+	memset(pix, 0, sizeof(*pix));
+	v4l2_fill_pix_format_mplane(pix, mbus);
+
+	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+		if (formats[i].code == mbus->code)
+			break;
+	}
+
+	if (WARN_ON(i == ARRAY_SIZE(formats)))
+		return -EINVAL;
+
+	pix->pixelformat = formats[i].pixelformat;
+	pix->num_planes = 1;
+	bytesperline = pix->width * formats[i].bpp / 8;
+	bytesperline = ALIGN(bytesperline, 8);
+	pix->plane_fmt[0].bytesperline = bytesperline;
+	pix->plane_fmt[0].sizeimage = bytesperline * pix->height;
+
+	return 0;
+}
+
+static struct v4l2_subdev *video_remote_subdev(struct camss_video *video,
+					       u32 *pad)
+{
+	struct media_pad *remote;
+
+	remote = media_entity_remote_pad(&video->pad);
+
+	if (!remote || !is_media_entity_v4l2_subdev(remote->entity))
+		return NULL;
+
+	if (pad)
+		*pad = remote->index;
+
+	return media_entity_to_v4l2_subdev(remote->entity);
+}
+
+static int video_get_subdev_format(struct camss_video *video,
+				   struct v4l2_format *format)
+{
+	struct v4l2_subdev_format fmt;
+	struct v4l2_subdev *subdev;
+	u32 pad;
+	int ret;
+
+	subdev = video_remote_subdev(video, &pad);
+	if (subdev == NULL)
+		return -EINVAL;
+
+	fmt.pad = pad;
+	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+
+	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
+	if (ret)
+		return ret;
+
+	format->type = video->type;
+	return video_mbus_to_pix_mp(&fmt.format, &format->fmt.pix_mp);
+}
+
+/* -----------------------------------------------------------------------------
+ * Video queue operations
+ */
+
+static int video_queue_setup(struct vb2_queue *q,
+	unsigned int *num_buffers, unsigned int *num_planes,
+	unsigned int sizes[], struct device *alloc_devs[])
+{
+	struct camss_video *video = vb2_get_drv_priv(q);
+
+	if (*num_planes) {
+		if (*num_planes != 1)
+			return -EINVAL;
+
+		if (sizes[0] < video->active_fmt.fmt.pix_mp.plane_fmt[0].sizeimage)
+			return -EINVAL;
+
+		return 0;
+	}
+
+	*num_planes = 1;
+
+	sizes[0] = video->active_fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
+
+	return 0;
+}
+
+static int video_buf_prepare(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue);
+	struct camss_buffer *buffer = container_of(vbuf, struct camss_buffer,
+						   vb);
+	struct sg_table *sgt;
+
+	if (video->active_fmt.fmt.pix_mp.plane_fmt[0].sizeimage >
+							vb2_plane_size(vb, 0))
+		return -EINVAL;
+
+	vb2_set_plane_payload(vb, 0,
+			video->active_fmt.fmt.pix_mp.plane_fmt[0].sizeimage);
+
+	sgt = vb2_dma_sg_plane_desc(vb, 0);
+	if (!sgt)
+		return -EFAULT;
+
+	buffer->addr = sg_dma_address(sgt->sgl);
+
+	vbuf->field = V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+static void video_buf_queue(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue);
+	struct camss_buffer *buffer = container_of(vbuf, struct camss_buffer,
+						   vb);
+
+	video->ops->queue_buffer(video, buffer);
+}
+
+static int video_check_format(struct camss_video *video)
+{
+	struct v4l2_pix_format_mplane *pix = &video->active_fmt.fmt.pix_mp;
+	struct v4l2_pix_format_mplane *sd_pix;
+	struct v4l2_format format;
+	int ret;
+
+	ret = video_get_subdev_format(video, &format);
+	if (ret < 0)
+		return ret;
+
+	sd_pix = &format.fmt.pix_mp;
+	if (pix->pixelformat != sd_pix->pixelformat ||
+	    pix->height != sd_pix->height ||
+	    pix->width != sd_pix->width ||
+	    pix->num_planes != sd_pix->num_planes ||
+	    pix->num_planes != 1 ||
+	    pix->plane_fmt[0].bytesperline != sd_pix->plane_fmt[0].bytesperline ||
+	    pix->plane_fmt[0].sizeimage != sd_pix->plane_fmt[0].sizeimage ||
+	    pix->field != format.fmt.pix_mp.field)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int video_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+	struct camss_video *video = vb2_get_drv_priv(q);
+	struct video_device *vdev = &video->vdev;
+	struct media_entity *entity;
+	struct media_pad *pad;
+	struct v4l2_subdev *subdev;
+	int ret;
+
+	ret = media_pipeline_start(&vdev->entity, &video->pipe);
+	if (ret < 0)
+		return ret;
+
+	ret = video_check_format(video);
+	if (ret < 0)
+		goto error;
+
+	entity = &vdev->entity;
+	while (1) {
+		pad = &entity->pads[0];
+		if (!(pad->flags & MEDIA_PAD_FL_SINK))
+			break;
+
+		pad = media_entity_remote_pad(pad);
+		if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
+			break;
+
+		entity = pad->entity;
+		subdev = media_entity_to_v4l2_subdev(entity);
+
+		ret = v4l2_subdev_call(subdev, video, s_stream, 1);
+		if (ret < 0 && ret != -ENOIOCTLCMD)
+			goto error;
+	}
+
+	return 0;
+
+error:
+	media_pipeline_stop(&vdev->entity);
+
+	video->ops->flush_buffers(video, VB2_BUF_STATE_QUEUED);
+
+	return ret;
+}
+
+static void video_stop_streaming(struct vb2_queue *q)
+{
+	struct camss_video *video = vb2_get_drv_priv(q);
+	struct video_device *vdev = &video->vdev;
+	struct media_entity *entity;
+	struct media_pad *pad;
+	struct v4l2_subdev *subdev;
+	struct v4l2_subdev *subdev_vfe = NULL;
+
+	entity = &vdev->entity;
+	while (1) {
+		pad = &entity->pads[0];
+		if (!(pad->flags & MEDIA_PAD_FL_SINK))
+			break;
+
+		pad = media_entity_remote_pad(pad);
+		if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
+			break;
+
+		entity = pad->entity;
+		subdev = media_entity_to_v4l2_subdev(entity);
+
+		if (strstr(subdev->name, "vfe")) {
+			subdev_vfe = subdev;
+		} else if (strstr(subdev->name, "ispif")) {
+			v4l2_subdev_call(subdev, video, s_stream, 0);
+			v4l2_subdev_call(subdev_vfe, video, s_stream, 0);
+		} else {
+			v4l2_subdev_call(subdev, video, s_stream, 0);
+		}
+	}
+
+	media_pipeline_stop(&vdev->entity);
+
+	video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR);
+}
+
+static const struct vb2_ops msm_video_vb2_q_ops = {
+	.queue_setup     = video_queue_setup,
+	.wait_prepare    = vb2_ops_wait_prepare,
+	.wait_finish     = vb2_ops_wait_finish,
+	.buf_prepare     = video_buf_prepare,
+	.buf_queue       = video_buf_queue,
+	.start_streaming = video_start_streaming,
+	.stop_streaming  = video_stop_streaming,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 ioctls
+ */
+
+static int video_querycap(struct file *file, void *fh,
+			  struct v4l2_capability *cap)
+{
+	struct camss_video *video = video_drvdata(file);
+
+	strlcpy(cap->driver, "qcom-camss", sizeof(cap->driver));
+	strlcpy(cap->card, "Qualcomm Camera Subsystem", sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+		 dev_name(video->camss->dev));
+
+	return 0;
+}
+
+static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+	struct camss_video *video = video_drvdata(file);
+	struct v4l2_format format;
+	int ret;
+
+	if (f->type != video->type)
+		return -EINVAL;
+
+	if (f->index)
+		return -EINVAL;
+
+	ret = video_get_subdev_format(video, &format);
+	if (ret < 0)
+		return ret;
+
+	f->pixelformat = format.fmt.pix_mp.pixelformat;
+
+	return 0;
+}
+
+static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+	struct camss_video *video = video_drvdata(file);
+
+	*f = video->active_fmt;
+
+	return 0;
+}
+
+static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+	struct camss_video *video = video_drvdata(file);
+	int ret;
+
+	if (vb2_is_busy(&video->vb2_q))
+		return -EBUSY;
+
+	ret = video_get_subdev_format(video, f);
+	if (ret < 0)
+		return ret;
+
+	video->active_fmt = *f;
+
+	return 0;
+}
+
+static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+	struct camss_video *video = video_drvdata(file);
+
+	return video_get_subdev_format(video, f);
+}
+
+static int video_enum_input(struct file *file, void *fh,
+			    struct v4l2_input *input)
+{
+	if (input->index > 0)
+		return -EINVAL;
+
+	strlcpy(input->name, "camera", sizeof(input->name));
+	input->type = V4L2_INPUT_TYPE_CAMERA;
+
+	return 0;
+}
+
+static int video_g_input(struct file *file, void *fh, unsigned int *input)
+{
+	*input = 0;
+
+	return 0;
+}
+
+static int video_s_input(struct file *file, void *fh, unsigned int input)
+{
+	return input == 0 ? 0 : -EINVAL;
+}
+
+static const struct v4l2_ioctl_ops msm_vid_ioctl_ops = {
+	.vidioc_querycap		= video_querycap,
+	.vidioc_enum_fmt_vid_cap_mplane	= video_enum_fmt,
+	.vidioc_g_fmt_vid_cap_mplane	= video_g_fmt,
+	.vidioc_s_fmt_vid_cap_mplane	= video_s_fmt,
+	.vidioc_try_fmt_vid_cap_mplane	= video_try_fmt,
+	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
+	.vidioc_querybuf		= vb2_ioctl_querybuf,
+	.vidioc_qbuf			= vb2_ioctl_qbuf,
+	.vidioc_expbuf			= vb2_ioctl_expbuf,
+	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
+	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
+	.vidioc_streamon		= vb2_ioctl_streamon,
+	.vidioc_streamoff		= vb2_ioctl_streamoff,
+	.vidioc_enum_input		= video_enum_input,
+	.vidioc_g_input			= video_g_input,
+	.vidioc_s_input			= video_s_input,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 file operations
+ */
+
+static int video_open(struct file *file)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct camss_video *video = video_drvdata(file);
+	struct v4l2_fh *vfh;
+	int ret;
+
+	mutex_lock(&video->lock);
+
+	vfh = kzalloc(sizeof(*vfh), GFP_KERNEL);
+	if (vfh == NULL) {
+		ret = -ENOMEM;
+		goto error_alloc;
+	}
+
+	v4l2_fh_init(vfh, vdev);
+	v4l2_fh_add(vfh);
+
+	file->private_data = vfh;
+
+	ret = v4l2_pipeline_pm_use(&vdev->entity, 1);
+	if (ret < 0) {
+		dev_err(video->camss->dev, "Failed to power up pipeline: %d\n",
+			ret);
+		goto error_pm_use;
+	}
+
+	mutex_unlock(&video->lock);
+
+	return 0;
+
+error_pm_use:
+	v4l2_fh_release(file);
+
+error_alloc:
+	mutex_unlock(&video->lock);
+
+	return ret;
+}
+
+static int video_release(struct file *file)
+{
+	struct video_device *vdev = video_devdata(file);
+
+	vb2_fop_release(file);
+
+	v4l2_pipeline_pm_use(&vdev->entity, 0);
+
+	file->private_data = NULL;
+
+	return 0;
+}
+
+static const struct v4l2_file_operations msm_vid_fops = {
+	.owner          = THIS_MODULE,
+	.unlocked_ioctl = video_ioctl2,
+	.open           = video_open,
+	.release        = video_release,
+	.poll           = vb2_fop_poll,
+	.mmap		= vb2_fop_mmap,
+	.read		= vb2_fop_read,
+};
+
+/* -----------------------------------------------------------------------------
+ * CAMSS video core
+ */
+
+static void msm_video_release(struct video_device *vdev)
+{
+	struct camss_video *video = video_get_drvdata(vdev);
+
+	media_entity_cleanup(&vdev->entity);
+
+	mutex_destroy(&video->q_lock);
+	mutex_destroy(&video->lock);
+
+	if (atomic_dec_and_test(&video->camss->ref_count))
+		camss_delete(video->camss);
+}
+
+/*
+ * msm_video_register - Register a video device node
+ * @video: struct camss_video
+ * @v4l2_dev: V4L2 device
+ * @name: name to be used for the video device node
+ *
+ * Initialize and register a video device node to a V4L2 device. Also
+ * initialize the vb2 queue.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+
+int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev,
+		       const char *name)
+{
+	struct media_pad *pad = &video->pad;
+	struct video_device *vdev;
+	struct vb2_queue *q;
+	int ret;
+
+	vdev = &video->vdev;
+
+	mutex_init(&video->q_lock);
+
+	q = &video->vb2_q;
+	q->drv_priv = video;
+	q->mem_ops = &vb2_dma_sg_memops;
+	q->ops = &msm_video_vb2_q_ops;
+	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+	q->io_modes = VB2_DMABUF | VB2_MMAP | VB2_READ;
+	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->buf_struct_size = sizeof(struct camss_buffer);
+	q->dev = video->camss->dev;
+	q->lock = &video->q_lock;
+	ret = vb2_queue_init(q);
+	if (ret < 0) {
+		dev_err(v4l2_dev->dev, "Failed to init vb2 queue: %d\n", ret);
+		goto error_vb2_init;
+	}
+
+	pad->flags = MEDIA_PAD_FL_SINK;
+	ret = media_entity_pads_init(&vdev->entity, 1, pad);
+	if (ret < 0) {
+		dev_err(v4l2_dev->dev, "Failed to init video entity: %d\n",
+			ret);
+		goto error_media_init;
+	}
+
+	mutex_init(&video->lock);
+
+	vdev->fops = &msm_vid_fops;
+	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING |
+							V4L2_CAP_READWRITE;
+	vdev->ioctl_ops = &msm_vid_ioctl_ops;
+	vdev->release = msm_video_release;
+	vdev->v4l2_dev = v4l2_dev;
+	vdev->vfl_dir = VFL_DIR_RX;
+	vdev->queue = &video->vb2_q;
+	vdev->lock = &video->lock;
+	strlcpy(vdev->name, name, sizeof(vdev->name));
+
+	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+	if (ret < 0) {
+		dev_err(v4l2_dev->dev, "Failed to register video device: %d\n",
+			ret);
+		goto error_video_register;
+	}
+
+	video_set_drvdata(vdev, video);
+	atomic_inc(&video->camss->ref_count);
+
+	return 0;
+
+error_video_register:
+	media_entity_cleanup(&vdev->entity);
+	mutex_destroy(&video->lock);
+error_media_init:
+	vb2_queue_release(&video->vb2_q);
+error_vb2_init:
+	mutex_destroy(&video->q_lock);
+
+	return ret;
+}
+
+/*
+ * msm_video_init_format - Helper function to initialize format
+ * @video: struct camss_video
+ *
+ * Initialize pad format with default value. Default format is aqcuired
+ * and converted to from the subdev pad linked to this video device node.
+ * Note: media link must be already created when calling this function.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int msm_video_init_format(struct camss_video *video)
+{
+	return video_get_subdev_format(video, &video->active_fmt);
+}
+
+void msm_video_stop_streaming(struct camss_video *video)
+{
+	if (vb2_is_streaming(&video->vb2_q))
+		vb2_queue_release(&video->vb2_q);
+}
+
+void msm_video_unregister(struct camss_video *video)
+{
+	atomic_inc(&video->camss->ref_count);
+	video_unregister_device(&video->vdev);
+	atomic_dec(&video->camss->ref_count);
+}
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-video.h b/drivers/media/platform/qcom/camss-8x16/camss-video.h
new file mode 100644
index 0000000..bca04a1
--- /dev/null
+++ b/drivers/media/platform/qcom/camss-8x16/camss-video.h
@@ -0,0 +1,66 @@
+/*
+ * camss-video.h
+ *
+ * Qualcomm MSM Camera Subsystem - V4L2 device node
+ *
+ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2015-2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef QC_MSM_CAMSS_VIDEO_H
+#define QC_MSM_CAMSS_VIDEO_H
+
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <media/media-entity.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-mediabus.h>
+#include <media/videobuf2-v4l2.h>
+
+struct camss_buffer {
+	struct vb2_v4l2_buffer vb;
+	dma_addr_t addr;
+	struct list_head queue;
+};
+
+struct camss_video;
+
+struct camss_video_ops {
+	int (*queue_buffer)(struct camss_video *vid, struct camss_buffer *buf);
+	int (*flush_buffers)(struct camss_video *vid,
+			     enum vb2_buffer_state state);
+};
+
+struct camss_video {
+	struct camss *camss;
+	struct vb2_queue vb2_q;
+	struct video_device vdev;
+	struct media_pad pad;
+	struct v4l2_format active_fmt;
+	enum v4l2_buf_type type;
+	struct media_pipeline pipe;
+	const struct camss_video_ops *ops;
+	struct mutex lock;
+	struct mutex q_lock;
+};
+
+void msm_video_stop_streaming(struct camss_video *video);
+
+int msm_video_init_format(struct camss_video *video);
+
+int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev,
+		       const char *name);
+
+void msm_video_unregister(struct camss_video *video);
+
+#endif /* QC_MSM_CAMSS_VIDEO_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v3 12/23] media: camms: Add core files
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
                   ` (10 preceding siblings ...)
  2017-07-17 10:33 ` [PATCH v3 11/23] media: camss: Add files which handle the video device nodes Todor Tomov
@ 2017-07-17 10:33 ` Todor Tomov
  2017-07-19 10:44   ` Hans Verkuil
  2017-07-17 10:33 ` [PATCH v3 13/23] media: camss: Enable building Todor Tomov
                   ` (12 subsequent siblings)
  24 siblings, 1 reply; 60+ messages in thread
From: Todor Tomov @ 2017-07-17 10:33 UTC (permalink / raw)
  To: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm
  Cc: Todor Tomov

These files implement the platform driver code.

Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
---
 drivers/media/platform/qcom/camss-8x16/camss.c | 705 +++++++++++++++++++++++++
 drivers/media/platform/qcom/camss-8x16/camss.h |  97 ++++
 2 files changed, 802 insertions(+)
 create mode 100644 drivers/media/platform/qcom/camss-8x16/camss.c
 create mode 100644 drivers/media/platform/qcom/camss-8x16/camss.h

diff --git a/drivers/media/platform/qcom/camss-8x16/camss.c b/drivers/media/platform/qcom/camss-8x16/camss.c
new file mode 100644
index 0000000..097d4ec
--- /dev/null
+++ b/drivers/media/platform/qcom/camss-8x16/camss.c
@@ -0,0 +1,705 @@
+/*
+ * camss.c
+ *
+ * Qualcomm MSM Camera Subsystem - Core
+ *
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2015-2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/clk.h>
+#include <linux/media-bus-format.h>
+#include <linux/media.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/media-device.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-fwnode.h>
+
+#include "camss.h"
+
+static const struct resources csiphy_res[] = {
+	/* CSIPHY0 */
+	{
+		.regulator = { NULL },
+		.clock = { "camss_top_ahb", "ispif_ahb",
+			   "camss_ahb", "csiphy0_timer" },
+		.clock_rate = { 0, 0, 0, 200000000 },
+		.reg = { "csiphy0", "csiphy0_clk_mux" },
+		.interrupt = { "csiphy0" }
+	},
+
+	/* CSIPHY1 */
+	{
+		.regulator = { NULL },
+		.clock = { "camss_top_ahb", "ispif_ahb",
+			   "camss_ahb", "csiphy1_timer" },
+		.clock_rate = { 0, 0, 0, 200000000 },
+		.reg = { "csiphy1", "csiphy1_clk_mux" },
+		.interrupt = { "csiphy1" }
+	}
+};
+
+static const struct resources csid_res[] = {
+	/* CSID0 */
+	{
+		.regulator = { "vdda" },
+		.clock = { "camss_top_ahb", "ispif_ahb",
+			   "csi0_ahb", "camss_ahb",
+			   "csi0", "csi0_phy", "csi0_pix", "csi0_rdi" },
+		.clock_rate = { 0, 0, 0, 0, 200000000, 0, 0, 0 },
+		.reg = { "csid0" },
+		.interrupt = { "csid0" }
+	},
+
+	/* CSID1 */
+	{
+		.regulator = { "vdda" },
+		.clock = { "camss_top_ahb", "ispif_ahb",
+			   "csi1_ahb", "camss_ahb",
+			   "csi1", "csi1_phy", "csi1_pix", "csi1_rdi" },
+		.clock_rate = { 0, 0, 0, 0, 200000000, 0, 0, 0 },
+		.reg = { "csid1" },
+		.interrupt = { "csid1" }
+	},
+};
+
+static const struct resources_ispif ispif_res = {
+	/* ISPIF */
+	.clock = { "camss_top_ahb", "camss_ahb", "ispif_ahb",
+		   "csi0", "csi0_pix", "csi0_rdi",
+		   "csi1", "csi1_pix", "csi1_rdi" },
+	.clock_for_reset = { "camss_vfe_vfe", "camss_csi_vfe" },
+	.reg = { "ispif", "csi_clk_mux" },
+	.interrupt = "ispif"
+
+};
+
+static const struct resources vfe_res = {
+	/* VFE0 */
+	.regulator = { NULL },
+	.clock = { "camss_top_ahb", "camss_vfe_vfe", "camss_csi_vfe",
+		   "iface", "bus", "camss_ahb" },
+	.clock_rate = { 0, 320000000, 0, 0, 0, 0, 0, 0 },
+	.reg = { "vfe0" },
+	.interrupt = { "vfe0" }
+};
+
+/*
+ * camss_enable_clocks - Enable multiple clocks
+ * @nclocks: Number of clocks in clock array
+ * @clock: Clock array
+ * @dev: Device
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int camss_enable_clocks(int nclocks, struct clk **clock, struct device *dev)
+{
+	int ret;
+	int i;
+
+	for (i = 0; i < nclocks; i++) {
+		ret = clk_prepare_enable(clock[i]);
+		if (ret) {
+			dev_err(dev, "clock enable failed: %d\n", ret);
+			goto error;
+		}
+	}
+
+	return 0;
+
+error:
+	for (i--; i >= 0; i--)
+		clk_disable_unprepare(clock[i]);
+
+	return ret;
+}
+
+/*
+ * camss_disable_clocks - Disable multiple clocks
+ * @nclocks: Number of clocks in clock array
+ * @clock: Clock array
+ */
+void camss_disable_clocks(int nclocks, struct clk **clock)
+{
+	int i;
+
+	for (i = nclocks - 1; i >= 0; i--)
+		clk_disable_unprepare(clock[i]);
+}
+
+/*
+ * camss_find_sensor - Find a linked media entity which represents a sensor
+ * @entity: Media entity to start searching from
+ *
+ * Return a pointer to sensor media entity or NULL if not found
+ */
+static struct media_entity *camss_find_sensor(struct media_entity *entity)
+{
+	struct media_pad *pad;
+
+	while (1) {
+		pad = &entity->pads[0];
+		if (!(pad->flags & MEDIA_PAD_FL_SINK))
+			return NULL;
+
+		pad = media_entity_remote_pad(pad);
+		if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
+			return NULL;
+
+		entity = pad->entity;
+
+		if (entity->function == MEDIA_ENT_F_CAM_SENSOR)
+			return entity;
+	}
+}
+
+/*
+ * camss_get_pixel_clock - Get pixel clock rate from sensor
+ * @entity: Media entity in the current pipeline
+ * @pixel_clock: Received pixel clock value
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock)
+{
+	struct media_entity *sensor;
+	struct v4l2_subdev *subdev;
+	struct v4l2_ext_controls ctrls = { { 0 } };
+	struct v4l2_ext_control ctrl = { 0 };
+	int ret;
+
+	sensor = camss_find_sensor(entity);
+	if (!sensor)
+		return -ENODEV;
+
+	subdev = media_entity_to_v4l2_subdev(sensor);
+
+	ctrl.id = V4L2_CID_PIXEL_RATE;
+
+	ctrls.count = 1;
+	ctrls.controls = &ctrl;
+
+	ret = v4l2_g_ext_ctrls(subdev->ctrl_handler, &ctrls);
+	if (ret < 0)
+		return ret;
+
+	*pixel_clock = ctrl.value64;
+
+	return 0;
+}
+
+/*
+ * camss_of_parse_endpoint_node - Parse port endpoint node
+ * @dev: Device
+ * @node: Device node to be parsed
+ * @csd: Parsed data from port endpoint node
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+static int camss_of_parse_endpoint_node(struct device *dev,
+					struct device_node *node,
+					struct camss_async_subdev *csd)
+{
+	struct csiphy_lanes_cfg *lncfg = &csd->interface.csi2.lane_cfg;
+	struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2;
+	struct v4l2_fwnode_endpoint vep = { { 0 } };
+	unsigned int i;
+
+	v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep);
+
+	csd->interface.csiphy_id = vep.base.port;
+
+	mipi_csi2 = &vep.bus.mipi_csi2;
+	lncfg->clk.pos = mipi_csi2->clock_lane;
+	lncfg->clk.pol = mipi_csi2->lane_polarities[0];
+	lncfg->num_data = mipi_csi2->num_data_lanes;
+
+	lncfg->data = devm_kzalloc(dev, lncfg->num_data * sizeof(*lncfg->data),
+				   GFP_KERNEL);
+	if (!lncfg->data)
+		return -ENOMEM;
+
+	for (i = 0; i < lncfg->num_data; i++) {
+		lncfg->data[i].pos = mipi_csi2->data_lanes[i];
+		lncfg->data[i].pol = mipi_csi2->lane_polarities[i + 1];
+	}
+
+	return 0;
+}
+
+/*
+ * camss_of_parse_ports - Parse ports node
+ * @dev: Device
+ * @notifier: v4l2_device notifier data
+ *
+ * Return number of "port" nodes found in "ports" node
+ */
+static int camss_of_parse_ports(struct device *dev,
+				struct v4l2_async_notifier *notifier)
+{
+	struct device_node *node = NULL;
+	struct device_node *remote = NULL;
+	unsigned int size, i;
+	int ret;
+
+	while ((node = of_graph_get_next_endpoint(dev->of_node, node)))
+		if (of_device_is_available(node))
+			notifier->num_subdevs++;
+
+	size = sizeof(*notifier->subdevs) * notifier->num_subdevs;
+	notifier->subdevs = devm_kzalloc(dev, size, GFP_KERNEL);
+	if (!notifier->subdevs) {
+		dev_err(dev, "Failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	i = 0;
+	while ((node = of_graph_get_next_endpoint(dev->of_node, node))) {
+		struct camss_async_subdev *csd;
+
+		if (!of_device_is_available(node))
+			continue;
+
+		csd = devm_kzalloc(dev, sizeof(*csd), GFP_KERNEL);
+		if (!csd) {
+			of_node_put(node);
+			dev_err(dev, "Failed to allocate memory\n");
+			return -ENOMEM;
+		}
+
+		notifier->subdevs[i++] = &csd->asd;
+
+		ret = camss_of_parse_endpoint_node(dev, node, csd);
+		if (ret < 0) {
+			of_node_put(node);
+			return ret;
+		}
+
+		remote = of_graph_get_remote_port_parent(node);
+		of_node_put(node);
+
+		if (!remote) {
+			dev_err(dev, "Cannot get remote parent\n");
+			return -EINVAL;
+		}
+
+		csd->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
+		csd->asd.match.fwnode.fwnode = of_fwnode_handle(remote);
+	}
+
+	return notifier->num_subdevs;
+}
+
+/*
+ * camss_init_subdevices - Initialize subdev structures and resources
+ * @camss: CAMSS device
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+static int camss_init_subdevices(struct camss *camss)
+{
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < ARRAY_SIZE(camss->csiphy); i++) {
+		ret = msm_csiphy_subdev_init(&camss->csiphy[i],
+					     &csiphy_res[i], i);
+		if (ret < 0) {
+			dev_err(camss->dev,
+				"Failed to init csiphy%d sub-device: %d\n",
+				i, ret);
+			return ret;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(camss->csid); i++) {
+		ret = msm_csid_subdev_init(&camss->csid[i],
+					   &csid_res[i], i);
+		if (ret < 0) {
+			dev_err(camss->dev,
+				"Failed to init csid%d sub-device: %d\n",
+				i, ret);
+			return ret;
+		}
+	}
+
+	ret = msm_ispif_subdev_init(&camss->ispif, &ispif_res);
+	if (ret < 0) {
+		dev_err(camss->dev, "Failed to init ispif sub-device: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = msm_vfe_subdev_init(&camss->vfe, &vfe_res);
+	if (ret < 0) {
+		dev_err(camss->dev, "Fail to init vfe sub-device: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * camss_register_entities - Register subdev nodes and create links
+ * @camss: CAMSS device
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+static int camss_register_entities(struct camss *camss)
+{
+	int i, j;
+	int ret;
+
+	for (i = 0; i < ARRAY_SIZE(camss->csiphy); i++) {
+		ret = msm_csiphy_register_entity(&camss->csiphy[i],
+						 &camss->v4l2_dev);
+		if (ret < 0) {
+			dev_err(camss->dev,
+				"Failed to register csiphy%d entity: %d\n",
+				i, ret);
+			goto err_reg_csiphy;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(camss->csid); i++) {
+		ret = msm_csid_register_entity(&camss->csid[i],
+					       &camss->v4l2_dev);
+		if (ret < 0) {
+			dev_err(camss->dev,
+				"Failed to register csid%d entity: %d\n",
+				i, ret);
+			goto err_reg_csid;
+		}
+	}
+
+	ret = msm_ispif_register_entities(&camss->ispif, &camss->v4l2_dev);
+	if (ret < 0) {
+		dev_err(camss->dev, "Failed to register ispif entities: %d\n",
+			ret);
+		goto err_reg_ispif;
+	}
+
+	ret = msm_vfe_register_entities(&camss->vfe, &camss->v4l2_dev);
+	if (ret < 0) {
+		dev_err(camss->dev, "Failed to register vfe entities: %d\n",
+			ret);
+		goto err_reg_vfe;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(camss->csiphy); i++) {
+		for (j = 0; j < ARRAY_SIZE(camss->csid); j++) {
+			ret = media_create_pad_link(
+				&camss->csiphy[i].subdev.entity,
+				MSM_CSIPHY_PAD_SRC,
+				&camss->csid[j].subdev.entity,
+				MSM_CSID_PAD_SINK,
+				0);
+			if (ret < 0) {
+				dev_err(camss->dev,
+					"Failed to link %s->%s entities: %d\n",
+					camss->csiphy[i].subdev.entity.name,
+					camss->csid[j].subdev.entity.name,
+					ret);
+				goto err_link;
+			}
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(camss->csid); i++) {
+		for (j = 0; j < ARRAY_SIZE(camss->ispif.line); j++) {
+			ret = media_create_pad_link(
+				&camss->csid[i].subdev.entity,
+				MSM_CSID_PAD_SRC,
+				&camss->ispif.line[j].subdev.entity,
+				MSM_ISPIF_PAD_SINK,
+				0);
+			if (ret < 0) {
+				dev_err(camss->dev,
+					"Failed to link %s->%s entities: %d\n",
+					camss->csid[i].subdev.entity.name,
+					camss->ispif.line[j].subdev.entity.name,
+					ret);
+				goto err_link;
+			}
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(camss->ispif.line); i++) {
+		for (j = 0; j < ARRAY_SIZE(camss->vfe.line); j++) {
+			ret = media_create_pad_link(
+				&camss->ispif.line[i].subdev.entity,
+				MSM_ISPIF_PAD_SRC,
+				&camss->vfe.line[j].subdev.entity,
+				MSM_VFE_PAD_SINK,
+				0);
+			if (ret < 0) {
+				dev_err(camss->dev,
+					"Failed to link %s->%s entities: %d\n",
+					camss->ispif.line[i].subdev.entity.name,
+					camss->vfe.line[j].subdev.entity.name,
+					ret);
+				goto err_link;
+			}
+		}
+	}
+
+	return 0;
+
+err_link:
+	msm_vfe_unregister_entities(&camss->vfe);
+err_reg_vfe:
+	msm_ispif_unregister_entities(&camss->ispif);
+err_reg_ispif:
+
+	i = ARRAY_SIZE(camss->csid);
+err_reg_csid:
+	for (i--; i >= 0; i--)
+		msm_csid_unregister_entity(&camss->csid[i]);
+
+	i = ARRAY_SIZE(camss->csiphy);
+err_reg_csiphy:
+	for (i--; i >= 0; i--)
+		msm_csiphy_unregister_entity(&camss->csiphy[i]);
+
+	return ret;
+}
+
+/*
+ * camss_unregister_entities - Unregister subdev nodes
+ * @camss: CAMSS device
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+static void camss_unregister_entities(struct camss *camss)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(camss->csiphy); i++)
+		msm_csiphy_unregister_entity(&camss->csiphy[i]);
+
+	for (i = 0; i < ARRAY_SIZE(camss->csid); i++)
+		msm_csid_unregister_entity(&camss->csid[i]);
+
+	msm_ispif_unregister_entities(&camss->ispif);
+	msm_vfe_unregister_entities(&camss->vfe);
+}
+
+static int camss_subdev_notifier_bound(struct v4l2_async_notifier *async,
+				       struct v4l2_subdev *subdev,
+				       struct v4l2_async_subdev *asd)
+{
+	struct camss *camss = container_of(async, struct camss, notifier);
+	struct camss_async_subdev *csd =
+		container_of(asd, struct camss_async_subdev, asd);
+	u8 id = csd->interface.csiphy_id;
+	struct csiphy_device *csiphy = &camss->csiphy[id];
+
+	csiphy->cfg.csi2 = &csd->interface.csi2;
+	subdev->host_priv = csiphy;
+
+	return 0;
+}
+
+static int camss_subdev_notifier_complete(struct v4l2_async_notifier *async)
+{
+	struct camss *camss = container_of(async, struct camss, notifier);
+	struct v4l2_device *v4l2_dev = &camss->v4l2_dev;
+	struct v4l2_subdev *sd;
+	int ret;
+
+	list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
+		if (sd->host_priv) {
+			struct media_entity *sensor = &sd->entity;
+			struct csiphy_device *csiphy =
+					(struct csiphy_device *) sd->host_priv;
+			struct media_entity *input = &csiphy->subdev.entity;
+			unsigned int i;
+
+			for (i = 0; i < sensor->num_pads; i++) {
+				if (sensor->pads[i].flags & MEDIA_PAD_FL_SOURCE)
+					break;
+			}
+			if (i == sensor->num_pads) {
+				dev_err(camss->dev,
+					"No source pad in external entity\n");
+				return -EINVAL;
+			}
+
+			ret = media_create_pad_link(sensor, i,
+				input, MSM_CSIPHY_PAD_SINK,
+				MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
+			if (ret < 0) {
+				dev_err(camss->dev,
+					"Failed to link %s->%s entities: %d\n",
+					sensor->name, input->name, ret);
+				return ret;
+			}
+		}
+	}
+
+	ret = v4l2_device_register_subdev_nodes(&camss->v4l2_dev);
+	if (ret < 0)
+		return ret;
+
+	return media_device_register(&camss->media_dev);
+}
+
+static const struct media_device_ops camss_media_ops = {
+	.link_notify = v4l2_pipeline_link_notify,
+};
+
+/*
+ * camss_probe - Probe CAMSS platform device
+ * @pdev: Pointer to CAMSS platform device
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+static int camss_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct camss *camss;
+	int ret;
+
+	camss = kzalloc(sizeof(*camss), GFP_KERNEL);
+	if (!camss)
+		return -ENOMEM;
+
+	atomic_set(&camss->ref_count, 0);
+	camss->dev = dev;
+	platform_set_drvdata(pdev, camss);
+
+	ret = camss_of_parse_ports(dev, &camss->notifier);
+	if (ret < 0)
+		return ret;
+
+	ret = camss_init_subdevices(camss);
+	if (ret < 0)
+		return ret;
+
+	ret = dma_set_mask_and_coherent(dev, 0xffffffff);
+	if (ret)
+		return ret;
+
+	camss->media_dev.dev = camss->dev;
+	strlcpy(camss->media_dev.model, "Qualcomm Camera Subsystem",
+		sizeof(camss->media_dev.model));
+	camss->media_dev.ops = &camss_media_ops;
+	media_device_init(&camss->media_dev);
+
+	camss->v4l2_dev.mdev = &camss->media_dev;
+	ret = v4l2_device_register(camss->dev, &camss->v4l2_dev);
+	if (ret < 0) {
+		dev_err(dev, "Failed to register V4L2 device: %d\n", ret);
+		return ret;
+	}
+
+	ret = camss_register_entities(camss);
+	if (ret < 0)
+		goto err_register_entities;
+
+	if (camss->notifier.num_subdevs) {
+		camss->notifier.bound = camss_subdev_notifier_bound;
+		camss->notifier.complete = camss_subdev_notifier_complete;
+
+		ret = v4l2_async_notifier_register(&camss->v4l2_dev,
+						   &camss->notifier);
+		if (ret) {
+			dev_err(dev,
+				"Failed to register async subdev nodes: %d\n",
+				ret);
+			goto err_register_subdevs;
+		}
+	} else {
+		ret = v4l2_device_register_subdev_nodes(&camss->v4l2_dev);
+		if (ret < 0) {
+			dev_err(dev, "Failed to register subdev nodes: %d\n",
+				ret);
+			goto err_register_subdevs;
+		}
+
+		ret = media_device_register(&camss->media_dev);
+		if (ret < 0) {
+			dev_err(dev, "Failed to register media device: %d\n",
+				ret);
+			goto err_register_subdevs;
+		}
+	}
+
+	return 0;
+
+err_register_subdevs:
+	camss_unregister_entities(camss);
+err_register_entities:
+	v4l2_device_unregister(&camss->v4l2_dev);
+
+	return ret;
+}
+
+void camss_delete(struct camss *camss)
+{
+	v4l2_device_unregister(&camss->v4l2_dev);
+	media_device_unregister(&camss->media_dev);
+	media_device_cleanup(&camss->media_dev);
+
+	kfree(camss);
+}
+
+/*
+ * camss_remove - Remove CAMSS platform device
+ * @pdev: Pointer to CAMSS platform device
+ *
+ * Always returns 0.
+ */
+static int camss_remove(struct platform_device *pdev)
+{
+	struct camss *camss = platform_get_drvdata(pdev);
+
+	msm_vfe_stop_streaming(&camss->vfe);
+
+	v4l2_async_notifier_unregister(&camss->notifier);
+	camss_unregister_entities(camss);
+
+	if (atomic_read(&camss->ref_count) == 0)
+		camss_delete(camss);
+
+	return 0;
+}
+
+static const struct of_device_id camss_dt_match[] = {
+	{ .compatible = "qcom,msm8916-camss" },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, camss_dt_match);
+
+static struct platform_driver qcom_camss_driver = {
+	.probe = camss_probe,
+	.remove = camss_remove,
+	.driver = {
+		.name = "qcom-camss",
+		.of_match_table = camss_dt_match,
+	},
+};
+
+module_platform_driver(qcom_camss_driver);
+
+MODULE_ALIAS("platform:qcom-camss");
+MODULE_DESCRIPTION("Qualcomm Camera Subsystem driver");
+MODULE_AUTHOR("Todor Tomov <todor.tomov@linaro.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/qcom/camss-8x16/camss.h b/drivers/media/platform/qcom/camss-8x16/camss.h
new file mode 100644
index 0000000..4619d7634
--- /dev/null
+++ b/drivers/media/platform/qcom/camss-8x16/camss.h
@@ -0,0 +1,97 @@
+/*
+ * camss.h
+ *
+ * Qualcomm MSM Camera Subsystem - Core
+ *
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2015-2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef QC_MSM_CAMSS_H
+#define QC_MSM_CAMSS_H
+
+#include <linux/types.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/media-device.h>
+#include <media/media-entity.h>
+#include <linux/device.h>
+
+#include "camss-csid.h"
+#include "camss-csiphy.h"
+#include "camss-ispif.h"
+#include "camss-vfe.h"
+
+#define CAMSS_CSID_NUM 2
+#define CAMSS_CSIPHY_NUM 2
+
+#define to_camss(ptr_module)	\
+	container_of(ptr_module, struct camss, ptr_module)
+
+#define to_device(ptr_module)	\
+	(to_camss(ptr_module)->dev)
+
+#define module_pointer(ptr_module, index)	\
+	((const struct ptr_module##_device (*)[]) &(ptr_module[-(index)]))
+
+#define to_camss_index(ptr_module, index)	\
+	container_of(module_pointer(ptr_module, index),	\
+		     struct camss, ptr_module)
+
+#define to_device_index(ptr_module, index)	\
+	(to_camss_index(ptr_module, index)->dev)
+
+#define CAMSS_RES_MAX 15
+
+struct resources {
+	char *regulator[CAMSS_RES_MAX];
+	char *clock[CAMSS_RES_MAX];
+	s32 clock_rate[CAMSS_RES_MAX];
+	char *reg[CAMSS_RES_MAX];
+	char *interrupt[CAMSS_RES_MAX];
+};
+
+struct resources_ispif {
+	char *clock[CAMSS_RES_MAX];
+	char *clock_for_reset[CAMSS_RES_MAX];
+	char *reg[CAMSS_RES_MAX];
+	char *interrupt;
+};
+
+struct camss {
+	struct v4l2_device v4l2_dev;
+	struct v4l2_async_notifier notifier;
+	struct media_device media_dev;
+	struct device *dev;
+	struct csiphy_device csiphy[CAMSS_CSIPHY_NUM];
+	struct csid_device csid[CAMSS_CSID_NUM];
+	struct ispif_device ispif;
+	struct vfe_device vfe;
+	atomic_t ref_count;
+};
+
+struct camss_camera_interface {
+	u8 csiphy_id;
+	struct csiphy_csi2_cfg csi2;
+};
+
+struct camss_async_subdev {
+	struct camss_camera_interface interface;
+	struct v4l2_async_subdev asd;
+};
+
+int camss_enable_clocks(int nclocks, struct clk **clock, struct device *dev);
+void camss_disable_clocks(int nclocks, struct clk **clock);
+int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock);
+void camss_delete(struct camss *camss);
+
+#endif /* QC_MSM_CAMSS_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v3 13/23] media: camss: Enable building
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
                   ` (11 preceding siblings ...)
  2017-07-17 10:33 ` [PATCH v3 12/23] media: camms: Add core files Todor Tomov
@ 2017-07-17 10:33 ` Todor Tomov
  2017-07-19 13:17     ` kbuild test robot
  2017-07-17 10:33 ` [PATCH v3 14/23] camss: vfe: Format conversion support using PIX interface Todor Tomov
                   ` (11 subsequent siblings)
  24 siblings, 1 reply; 60+ messages in thread
From: Todor Tomov @ 2017-07-17 10:33 UTC (permalink / raw)
  To: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm
  Cc: Todor Tomov

Add Makefile and update platform/Kconfig and platform/Makefile
to enable building of the QCom CAMSS driver.

Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
---
 drivers/media/platform/Kconfig                  |  7 +++++++
 drivers/media/platform/Makefile                 |  2 ++
 drivers/media/platform/qcom/camss-8x16/Makefile | 11 +++++++++++
 3 files changed, 20 insertions(+)
 create mode 100644 drivers/media/platform/qcom/camss-8x16/Makefile

diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 1313cd5..87be341 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -109,6 +109,13 @@ config VIDEO_PXA27x
 	---help---
 	  This is a v4l2 driver for the PXA27x Quick Capture Interface
 
+config VIDEO_QCOM_CAMSS
+	tristate "Qualcomm 8x16 V4L2 Camera Subsystem driver"
+	depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST
+	select VIDEOBUF2_DMA_SG
+	select V4L2_FWNODE
+
 config VIDEO_S3C_CAMIF
 	tristate "Samsung S3C24XX/S3C64XX SoC Camera Interface driver"
 	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 9beadc7..10c099c 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -85,4 +85,6 @@ obj-$(CONFIG_VIDEO_MEDIATEK_MDP)	+= mtk-mdp/
 
 obj-$(CONFIG_VIDEO_MEDIATEK_JPEG)	+= mtk-jpeg/
 
+obj-$(CONFIG_VIDEO_QCOM_CAMSS)		+= qcom/camss-8x16/
+
 obj-$(CONFIG_VIDEO_QCOM_VENUS)		+= qcom/venus/
diff --git a/drivers/media/platform/qcom/camss-8x16/Makefile b/drivers/media/platform/qcom/camss-8x16/Makefile
new file mode 100644
index 0000000..3c4024f
--- /dev/null
+++ b/drivers/media/platform/qcom/camss-8x16/Makefile
@@ -0,0 +1,11 @@
+# Makefile for Qualcomm CAMSS driver
+
+qcom-camss-objs += \
+		camss.o \
+		camss-csid.o \
+		camss-csiphy.o \
+		camss-ispif.o \
+		camss-vfe.o \
+		camss-video.o \
+
+obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom-camss.o
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v3 14/23] camss: vfe: Format conversion support using PIX interface
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
                   ` (12 preceding siblings ...)
  2017-07-17 10:33 ` [PATCH v3 13/23] media: camss: Enable building Todor Tomov
@ 2017-07-17 10:33 ` Todor Tomov
  2017-07-20 15:11   ` Sakari Ailus
  2017-07-17 10:33 ` [PATCH v3 15/23] doc: media/v4l-drivers: Qualcomm Camera Subsystem - PIX Interface Todor Tomov
                   ` (10 subsequent siblings)
  24 siblings, 1 reply; 60+ messages in thread
From: Todor Tomov @ 2017-07-17 10:33 UTC (permalink / raw)
  To: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm
  Cc: Todor Tomov

Use VFE PIX input interface and do format conversion in VFE.

Supported input format is UYVY (single plane YUV 4:2:2) and
its different sample order variations.

Supported output formats are:
- NV12/NV21 (two plane YUV 4:2:0)
- NV16/NV61 (two plane YUV 4:2:2)

Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
---
 .../media/platform/qcom/camss-8x16/camss-ispif.c   |   2 +
 drivers/media/platform/qcom/camss-8x16/camss-vfe.c | 673 ++++++++++++++++++---
 drivers/media/platform/qcom/camss-8x16/camss-vfe.h |  13 +-
 .../media/platform/qcom/camss-8x16/camss-video.c   | 332 +++++++---
 .../media/platform/qcom/camss-8x16/camss-video.h   |   8 +-
 5 files changed, 875 insertions(+), 153 deletions(-)

diff --git a/drivers/media/platform/qcom/camss-8x16/camss-ispif.c b/drivers/media/platform/qcom/camss-8x16/camss-ispif.c
index cc32085..04918c0 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-ispif.c
+++ b/drivers/media/platform/qcom/camss-8x16/camss-ispif.c
@@ -969,6 +969,8 @@ static enum ispif_intf ispif_get_intf(enum vfe_line_id line_id)
 		return RDI1;
 	case (VFE_LINE_RDI2):
 		return RDI2;
+	case (VFE_LINE_PIX):
+		return PIX0;
 	default:
 		return RDI0;
 	}
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
index b6dd29b..bef0209 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
@@ -19,6 +19,7 @@
 #include <linux/completion.h>
 #include <linux/interrupt.h>
 #include <linux/iommu.h>
+#include <linux/iopoll.h>
 #include <linux/mutex.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
@@ -52,29 +53,53 @@
 #define VFE_0_GLOBAL_RESET_CMD_BUS_MISR	(1 << 7)
 #define VFE_0_GLOBAL_RESET_CMD_TESTGEN	(1 << 8)
 
+#define VFE_0_MODULE_CFG		0x018
+#define VFE_0_MODULE_CFG_DEMUX			(1 << 2)
+#define VFE_0_MODULE_CFG_CHROMA_UPSAMPLE	(1 << 3)
+#define VFE_0_MODULE_CFG_SCALE_ENC		(1 << 23)
+
+#define VFE_0_CORE_CFG			0x01c
+#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR	0x4
+#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB	0x5
+#define VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY	0x6
+#define VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY	0x7
+
 #define VFE_0_IRQ_CMD			0x024
 #define VFE_0_IRQ_CMD_GLOBAL_CLEAR	(1 << 0)
 
 #define VFE_0_IRQ_MASK_0		0x028
+#define VFE_0_IRQ_MASK_0_CAMIF_SOF			(1 << 0)
+#define VFE_0_IRQ_MASK_0_CAMIF_EOF			(1 << 1)
 #define VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n)		(1 << ((n) + 5))
+#define VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(n)		\
+	((n) == VFE_LINE_PIX ? (1 << 4) : VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n))
 #define VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(n)	(1 << ((n) + 8))
+#define VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(n)	(1 << ((n) + 25))
 #define VFE_0_IRQ_MASK_0_RESET_ACK			(1 << 31)
 #define VFE_0_IRQ_MASK_1		0x02c
+#define VFE_0_IRQ_MASK_1_CAMIF_ERROR			(1 << 0)
 #define VFE_0_IRQ_MASK_1_VIOLATION			(1 << 7)
 #define VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK		(1 << 8)
 #define VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(n)	(1 << ((n) + 9))
+#define VFE_0_IRQ_MASK_1_RDIn_SOF(n)			(1 << ((n) + 29))
 
 #define VFE_0_IRQ_CLEAR_0		0x030
 #define VFE_0_IRQ_CLEAR_1		0x034
 
 #define VFE_0_IRQ_STATUS_0		0x038
+#define VFE_0_IRQ_STATUS_0_CAMIF_SOF			(1 << 0)
 #define VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n)		(1 << ((n) + 5))
+#define VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(n)		\
+	((n) == VFE_LINE_PIX ? (1 << 4) : VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n))
 #define VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(n)	(1 << ((n) + 8))
+#define VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(n)	(1 << ((n) + 25))
 #define VFE_0_IRQ_STATUS_0_RESET_ACK			(1 << 31)
 #define VFE_0_IRQ_STATUS_1		0x03c
 #define VFE_0_IRQ_STATUS_1_VIOLATION			(1 << 7)
 #define VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK		(1 << 8)
+#define VFE_0_IRQ_STATUS_1_RDIn_SOF(n)			(1 << ((n) + 29))
 
+#define VFE_0_IRQ_COMPOSITE_MASK_0	0x40
 #define VFE_0_VIOLATION_STATUS		0x48
 
 #define VFE_0_BUS_CMD			0x4c
@@ -83,7 +108,10 @@
 #define VFE_0_BUS_CFG			0x050
 
 #define VFE_0_BUS_XBAR_CFG_x(x)		(0x58 + 0x4 * ((x) / 2))
+#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN			(1 << 1)
+#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA	(0x3 << 4)
 #define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT		8
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA		0
 #define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0	5
 #define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1	6
 #define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2	7
@@ -99,6 +127,8 @@
 
 #define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(n)		(0x07c + 0x24 * (n))
 #define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT	16
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(n)	(0x080 + 0x24 * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(n)	(0x084 + 0x24 * (n))
 #define VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(n)	\
 							(0x088 + 0x24 * (n))
 #define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(n)	\
@@ -128,8 +158,41 @@
 #define VFE_0_RDI_CFG_x_MIPI_EN_BITS		0x3
 #define VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(r)	(1 << (16 + (r)))
 
+#define VFE_0_CAMIF_CMD				0x2f4
+#define VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY	0
+#define VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY	1
+#define VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS	(1 << 2)
+#define VFE_0_CAMIF_CFG				0x2f8
+#define VFE_0_CAMIF_CFG_VFE_OUTPUT_EN		(1 << 6)
+#define VFE_0_CAMIF_FRAME_CFG			0x300
+#define VFE_0_CAMIF_WINDOW_WIDTH_CFG		0x304
+#define VFE_0_CAMIF_WINDOW_HEIGHT_CFG		0x308
+#define VFE_0_CAMIF_SUBSAMPLE_CFG_0		0x30c
+#define VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN	0x314
+#define VFE_0_CAMIF_STATUS			0x31c
+#define VFE_0_CAMIF_STATUS_HALT			(1 << 31)
+
 #define VFE_0_REG_UPDATE			0x378
 #define VFE_0_REG_UPDATE_RDIn(n)		(1 << (1 + (n)))
+#define VFE_0_REG_UPDATE_line_n(n)		\
+			((n) == VFE_LINE_PIX ? 1 : VFE_0_REG_UPDATE_RDIn(n))
+
+#define VFE_0_DEMUX_CFG				0x424
+#define VFE_0_DEMUX_GAIN_0			0x428
+#define VFE_0_DEMUX_GAIN_1			0x42c
+#define VFE_0_DEMUX_EVEN_CFG			0x438
+#define VFE_0_DEMUX_ODD_CFG			0x43c
+
+#define VFE_0_SCALE_ENC_CBCR_CFG		0x778
+#define VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE	0x77c
+#define VFE_0_SCALE_ENC_CBCR_H_PHASE		0x780
+#define VFE_0_SCALE_ENC_CBCR_H_PAD		0x78c
+#define VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE	0x790
+#define VFE_0_SCALE_ENC_CBCR_V_PHASE		0x794
+#define VFE_0_SCALE_ENC_CBCR_V_PAD		0x7a0
+
+#define VFE_0_CLAMP_ENC_MAX_CFG			0x874
+#define VFE_0_CLAMP_ENC_MIN_CFG			0x878
 
 #define VFE_0_CGC_OVERRIDE_1			0x974
 #define VFE_0_CGC_OVERRIDE_1_IMAGE_Mx_CGC_OVERRIDE(x)	(1 << (x))
@@ -143,6 +206,11 @@
 /* Frame drop value. NOTE: VAL + UPDATES should not exceed 31 */
 #define VFE_FRAME_DROP_VAL 20
 
+#define VFE_NEXT_SOF_MS 500
+
+#define CAMIF_TIMEOUT_SLEEP_US 1000
+#define CAMIF_TIMEOUT_ALL_US 1000000
+
 static const u32 vfe_formats[] = {
 	MEDIA_BUS_FMT_UYVY8_2X8,
 	MEDIA_BUS_FMT_VYUY8_2X8,
@@ -211,6 +279,32 @@ static void vfe_wm_frame_based(struct vfe_device *vfe, u8 wm, u8 enable)
 			1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT);
 }
 
+static void vfe_wm_line_based(struct vfe_device *vfe, u32 wm,
+			      u16 width, u16 height, u32 enable)
+{
+	u32 reg;
+
+	if (enable) {
+		reg = height - 1;
+		reg |= (width / 16 - 1) << 16;
+
+		writel_relaxed(reg, vfe->base +
+			       VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm));
+
+		reg = 0x3;
+		reg |= (height - 1) << 4;
+		reg |= (width / 8) << 16;
+
+		writel_relaxed(reg, vfe->base +
+			       VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm));
+	} else {
+		writel_relaxed(0, vfe->base +
+			       VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm));
+		writel_relaxed(0, vfe->base +
+			       VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm));
+	}
+}
+
 static void vfe_wm_set_framedrop_period(struct vfe_device *vfe, u8 wm, u8 per)
 {
 	u32 reg;
@@ -314,7 +408,10 @@ static void vfe_bus_connect_wm_to_rdi(struct vfe_device *vfe, u8 wm,
 		reg <<= 16;
 
 	vfe_reg_set(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg);
+}
 
+static void vfe_wm_set_subsample(struct vfe_device *vfe, u8 wm)
+{
 	writel_relaxed(VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF,
 	       vfe->base +
 	       VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(wm));
@@ -353,6 +450,38 @@ static void vfe_bus_disconnect_wm_from_rdi(struct vfe_device *vfe, u8 wm,
 	vfe_reg_clr(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg);
 }
 
+static void vfe_set_xbar_cfg(struct vfe_device *vfe, struct vfe_output *output,
+			     u8 enable)
+{
+	struct vfe_line *line = container_of(output, struct vfe_line, output);
+	u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
+	u32 reg;
+	unsigned int i;
+
+	for (i = 0; i < output->wm_num; i++) {
+		if (i == 0) {
+			reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA <<
+				VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+		} else if (i == 1) {
+			reg = VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN;
+			if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV16)
+				reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA;
+		}
+
+		if (output->wm_idx[i] % 2 == 1)
+			reg <<= 16;
+
+		if (enable)
+			vfe_reg_set(vfe,
+				    VFE_0_BUS_XBAR_CFG_x(output->wm_idx[i]),
+				    reg);
+		else
+			vfe_reg_clr(vfe,
+				    VFE_0_BUS_XBAR_CFG_x(output->wm_idx[i]),
+				    reg);
+	}
+}
+
 static void vfe_set_rdi_cid(struct vfe_device *vfe, enum vfe_line_id id, u8 cid)
 {
 	vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id),
@@ -364,7 +493,7 @@ static void vfe_set_rdi_cid(struct vfe_device *vfe, enum vfe_line_id id, u8 cid)
 
 static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
 {
-	vfe->reg_update |= VFE_0_REG_UPDATE_RDIn(line_id);
+	vfe->reg_update |= VFE_0_REG_UPDATE_line_n(line_id);
 	wmb();
 	writel_relaxed(vfe->reg_update, vfe->base + VFE_0_REG_UPDATE);
 	wmb();
@@ -374,8 +503,9 @@ static void vfe_enable_irq_wm_line(struct vfe_device *vfe, u8 wm,
 				   enum vfe_line_id line_id, u8 enable)
 {
 	u32 irq_en0 = VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(wm) |
-		      VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(line_id);
-	u32 irq_en1 = VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(wm);
+		      VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id);
+	u32 irq_en1 = VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(wm) |
+		      VFE_0_IRQ_MASK_1_RDIn_SOF(line_id);
 
 	if (enable) {
 		vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0);
@@ -386,6 +516,37 @@ static void vfe_enable_irq_wm_line(struct vfe_device *vfe, u8 wm,
 	}
 }
 
+static void vfe_enable_irq_pix_line(struct vfe_device *vfe, u8 comp,
+				    enum vfe_line_id line_id, u8 enable)
+{
+	struct vfe_output *output = &vfe->line[line_id].output;
+	unsigned int i;
+	u32 irq_en0;
+	u32 irq_en1;
+	u32 comp_mask = 0;
+
+	irq_en0 = VFE_0_IRQ_MASK_0_CAMIF_SOF;
+	irq_en0 |= VFE_0_IRQ_MASK_0_CAMIF_EOF;
+	irq_en0 |= VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(comp);
+	irq_en0 |= VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id);
+	irq_en1 = VFE_0_IRQ_MASK_1_CAMIF_ERROR;
+	for (i = 0; i < output->wm_num; i++) {
+		irq_en1 |= VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(
+							output->wm_idx[i]);
+		comp_mask |= (1 << output->wm_idx[i]) << comp * 8;
+	}
+
+	if (enable) {
+		vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+		vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+		vfe_reg_set(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask);
+	} else {
+		vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+		vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+		vfe_reg_clr(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask);
+	}
+}
+
 static void vfe_enable_irq_common(struct vfe_device *vfe)
 {
 	u32 irq_en0 = VFE_0_IRQ_MASK_0_RESET_ACK;
@@ -396,6 +557,83 @@ static void vfe_enable_irq_common(struct vfe_device *vfe)
 	vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1);
 }
 
+static void vfe_set_demux_cfg(struct vfe_device *vfe, struct vfe_line *line)
+{
+	u32 even_cfg, odd_cfg;
+
+	writel_relaxed(0x3, vfe->base + VFE_0_DEMUX_CFG);
+	writel_relaxed(0x800080, vfe->base + VFE_0_DEMUX_GAIN_0);
+	writel_relaxed(0x800080, vfe->base + VFE_0_DEMUX_GAIN_1);
+
+	switch (line->fmt[MSM_VFE_PAD_SINK].code) {
+	case MEDIA_BUS_FMT_YUYV8_2X8:
+		even_cfg = 0x9cac;
+		odd_cfg = 0x9cac;
+		break;
+	case MEDIA_BUS_FMT_YVYU8_2X8:
+		even_cfg = 0xac9c;
+		odd_cfg = 0xac9c;
+		break;
+	case MEDIA_BUS_FMT_UYVY8_2X8:
+	default:
+		even_cfg = 0xc9ca;
+		odd_cfg = 0xc9ca;
+		break;
+	case MEDIA_BUS_FMT_VYUY8_2X8:
+		even_cfg = 0xcac9;
+		odd_cfg = 0xcac9;
+		break;
+	}
+
+	writel_relaxed(even_cfg, vfe->base + VFE_0_DEMUX_EVEN_CFG);
+	writel_relaxed(odd_cfg, vfe->base + VFE_0_DEMUX_ODD_CFG);
+}
+
+static void vfe_set_scale_cfg(struct vfe_device *vfe, struct vfe_line *line)
+{
+	u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
+	u32 reg;
+	u16 input, output;
+	u8 interp_reso;
+	u32 phase_mult;
+
+	writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_CBCR_CFG);
+
+	input = line->fmt[MSM_VFE_PAD_SINK].width;
+	output = line->fmt[MSM_VFE_PAD_SRC].width / 2;
+	reg = (output << 16) | input;
+	writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE);
+
+	interp_reso = 3;
+	phase_mult = input * (1 << (13 + interp_reso)) / output;
+	reg = (interp_reso << 20) | phase_mult;
+	writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_PHASE);
+
+	reg = input;
+	writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_PAD);
+
+	input = line->fmt[MSM_VFE_PAD_SINK].height;
+	output = line->fmt[MSM_VFE_PAD_SRC].height;
+	if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21)
+		output = line->fmt[MSM_VFE_PAD_SRC].height / 2;
+	reg = (output << 16) | input;
+	writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE);
+
+	interp_reso = 3;
+	phase_mult = input * (1 << (13 + interp_reso)) / output;
+	reg = (interp_reso << 20) | phase_mult;
+	writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_PHASE);
+
+	reg = input;
+	writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_PAD);
+}
+
+static void vfe_set_clamp_cfg(struct vfe_device *vfe)
+{
+	writel_relaxed(0x00ffffff, vfe->base + VFE_0_CLAMP_ENC_MAX_CFG);
+	writel_relaxed(0x0, vfe->base + VFE_0_CLAMP_ENC_MIN_CFG);
+}
+
 /*
  * vfe_reset - Trigger reset on VFE module and wait to complete
  * @vfe: VFE device
@@ -456,6 +694,10 @@ static void vfe_init_outputs(struct vfe_device *vfe)
 		output->buf[0] = NULL;
 		output->buf[1] = NULL;
 		INIT_LIST_HEAD(&output->pending_bufs);
+
+		output->wm_num = 1;
+		if (vfe->line[i].id == VFE_LINE_PIX)
+			output->wm_num = 2;
 	}
 }
 
@@ -494,52 +736,148 @@ static void vfe_set_cgc_override(struct vfe_device *vfe, u8 wm, u8 enable)
 	wmb();
 }
 
+static void vfe_set_module_cfg(struct vfe_device *vfe, u8 enable)
+{
+	u32 val = VFE_0_MODULE_CFG_DEMUX |
+		  VFE_0_MODULE_CFG_CHROMA_UPSAMPLE |
+		  VFE_0_MODULE_CFG_SCALE_ENC;
+
+	if (enable)
+		writel_relaxed(val, vfe->base + VFE_0_MODULE_CFG);
+	else
+		writel_relaxed(0x0, vfe->base + VFE_0_MODULE_CFG);
+}
+
+static void vfe_set_camif_cfg(struct vfe_device *vfe, struct vfe_line *line)
+{
+	u32 val;
+
+	switch (line->fmt[MSM_VFE_PAD_SINK].code) {
+	case MEDIA_BUS_FMT_YUYV8_2X8:
+		val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR;
+		break;
+	case MEDIA_BUS_FMT_YVYU8_2X8:
+		val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB;
+		break;
+	case MEDIA_BUS_FMT_UYVY8_2X8:
+	default:
+		val = VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY;
+		break;
+	case MEDIA_BUS_FMT_VYUY8_2X8:
+		val = VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY;
+		break;
+	}
+
+	writel_relaxed(val, vfe->base + VFE_0_CORE_CFG);
+
+	val = line->fmt[MSM_VFE_PAD_SINK].width * 2;
+	val |= line->fmt[MSM_VFE_PAD_SINK].height << 16;
+	writel_relaxed(val, vfe->base + VFE_0_CAMIF_FRAME_CFG);
+
+	val = line->fmt[MSM_VFE_PAD_SINK].width * 2 - 1;
+	writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_WIDTH_CFG);
+
+	val = line->fmt[MSM_VFE_PAD_SINK].height - 1;
+	writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_HEIGHT_CFG);
+
+	val = 0xffffffff;
+	writel_relaxed(val, vfe->base + VFE_0_CAMIF_SUBSAMPLE_CFG_0);
+
+	val = 0xffffffff;
+	writel_relaxed(val, vfe->base + VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN);
+
+	val = VFE_0_RDI_CFG_x_MIPI_EN_BITS;
+	vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), val);
+
+	val = VFE_0_CAMIF_CFG_VFE_OUTPUT_EN;
+	writel_relaxed(val, vfe->base + VFE_0_CAMIF_CFG);
+}
+
+static void vfe_set_camif_cmd(struct vfe_device *vfe, u32 cmd)
+{
+	writel_relaxed(VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS,
+		       vfe->base + VFE_0_CAMIF_CMD);
+
+	writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD);
+}
+
+static int vfe_camif_wait_for_stop(struct vfe_device *vfe)
+{
+	u32 val;
+	int ret;
+
+	ret = readl_poll_timeout(vfe->base + VFE_0_CAMIF_STATUS,
+				 val,
+				 (val & VFE_0_CAMIF_STATUS_HALT),
+				 CAMIF_TIMEOUT_SLEEP_US,
+				 CAMIF_TIMEOUT_ALL_US);
+	if (ret < 0)
+		dev_err(to_device(vfe), "%s: camif stop timeout\n", __func__);
+
+	return ret;
+}
+
 static void vfe_output_init_addrs(struct vfe_device *vfe,
 				  struct vfe_output *output, u8 sync)
 {
-	u32 ping_addr = 0;
-	u32 pong_addr = 0;
+	u32 ping_addr;
+	u32 pong_addr;
+	unsigned int i;
 
 	output->active_buf = 0;
 
-	if (output->buf[0])
-		ping_addr = output->buf[0]->addr;
-
-	if (output->buf[1])
-		pong_addr = output->buf[1]->addr;
-	else
-		pong_addr = ping_addr;
+	for (i = 0; i < output->wm_num; i++) {
+		if (output->buf[0])
+			ping_addr = output->buf[0]->addr[i];
+		else
+			ping_addr = 0;
 
-	vfe_wm_set_ping_addr(vfe, output->wm_idx, ping_addr);
-	vfe_wm_set_pong_addr(vfe, output->wm_idx, pong_addr);
-	if (sync)
-		vfe_bus_reload_wm(vfe, output->wm_idx);
+		if (output->buf[1])
+			pong_addr = output->buf[1]->addr[i];
+		else
+			pong_addr = ping_addr;
+
+		vfe_wm_set_ping_addr(vfe, output->wm_idx[i], ping_addr);
+		vfe_wm_set_pong_addr(vfe, output->wm_idx[i], pong_addr);
+		if (sync)
+			vfe_bus_reload_wm(vfe, output->wm_idx[i]);
+	}
 }
 
 static void vfe_output_update_ping_addr(struct vfe_device *vfe,
 					struct vfe_output *output, u8 sync)
 {
-	u32 addr = 0;
+	u32 addr;
+	unsigned int i;
 
-	if (output->buf[0])
-		addr = output->buf[0]->addr;
+	for (i = 0; i < output->wm_num; i++) {
+		if (output->buf[0])
+			addr = output->buf[0]->addr[i];
+		else
+			addr = 0;
 
-	vfe_wm_set_ping_addr(vfe, output->wm_idx, addr);
-	if (sync)
-		vfe_bus_reload_wm(vfe, output->wm_idx);
+		vfe_wm_set_ping_addr(vfe, output->wm_idx[i], addr);
+		if (sync)
+			vfe_bus_reload_wm(vfe, output->wm_idx[i]);
+	}
 }
 
 static void vfe_output_update_pong_addr(struct vfe_device *vfe,
 					struct vfe_output *output, u8 sync)
 {
-	u32 addr = 0;
+	u32 addr;
+	unsigned int i;
 
-	if (output->buf[1])
-		addr = output->buf[1]->addr;
+	for (i = 0; i < output->wm_num; i++) {
+		if (output->buf[1])
+			addr = output->buf[1]->addr[i];
+		else
+			addr = 0;
 
-	vfe_wm_set_pong_addr(vfe, output->wm_idx, addr);
-	if (sync)
-		vfe_bus_reload_wm(vfe, output->wm_idx);
+		vfe_wm_set_pong_addr(vfe, output->wm_idx[i], addr);
+		if (sync)
+			vfe_bus_reload_wm(vfe, output->wm_idx[i]);
+	}
 
 }
 
@@ -574,14 +912,19 @@ static void vfe_output_frame_drop(struct vfe_device *vfe,
 				  u32 drop_pattern)
 {
 	u8 drop_period;
+	unsigned int i;
 
 	/* We need to toggle update period to be valid on next frame */
 	output->drop_update_idx++;
 	output->drop_update_idx %= VFE_FRAME_DROP_UPDATES;
 	drop_period = VFE_FRAME_DROP_VAL + output->drop_update_idx;
 
-	vfe_wm_set_framedrop_period(vfe, output->wm_idx, drop_period);
-	vfe_wm_set_framedrop_pattern(vfe, output->wm_idx, drop_pattern);
+	for (i = 0; i < output->wm_num; i++) {
+		vfe_wm_set_framedrop_period(vfe, output->wm_idx[i],
+					    drop_period);
+		vfe_wm_set_framedrop_pattern(vfe, output->wm_idx[i],
+					     drop_pattern);
+	}
 	vfe_reg_update(vfe, container_of(output, struct vfe_line, output)->id);
 
 }
@@ -719,6 +1062,7 @@ static int vfe_get_output(struct vfe_line *line)
 	struct vfe_device *vfe = to_vfe(line);
 	struct vfe_output *output;
 	unsigned long flags;
+	int i;
 	int wm_idx;
 
 	spin_lock_irqsave(&vfe->output_lock, flags);
@@ -732,20 +1076,24 @@ static int vfe_get_output(struct vfe_line *line)
 
 	output->active_buf = 0;
 
-	/* We will use only one wm per output for now */
-	wm_idx = vfe_reserve_wm(vfe, line->id);
-	if (wm_idx < 0) {
-		dev_err(to_device(vfe), "Can not reserve wm\n");
-		goto error_get_wm;
+	for (i = 0; i < output->wm_num; i++) {
+		wm_idx = vfe_reserve_wm(vfe, line->id);
+		if (wm_idx < 0) {
+			dev_err(to_device(vfe), "Can not reserve wm\n");
+			goto error_get_wm;
+		}
+		output->wm_idx[i] = wm_idx;
 	}
+
 	output->drop_update_idx = 0;
-	output->wm_idx = wm_idx;
 
 	spin_unlock_irqrestore(&vfe->output_lock, flags);
 
 	return 0;
 
 error_get_wm:
+	for (i--; i >= 0; i--)
+		vfe_release_wm(vfe, output->wm_idx[i]);
 	output->state = VFE_OUTPUT_OFF;
 error:
 	spin_unlock_irqrestore(&vfe->output_lock, flags);
@@ -758,19 +1106,17 @@ static int vfe_put_output(struct vfe_line *line)
 	struct vfe_device *vfe = to_vfe(line);
 	struct vfe_output *output = &line->output;
 	unsigned long flags;
-	int ret;
+	unsigned int i;
 
 	spin_lock_irqsave(&vfe->output_lock, flags);
 
-	ret = vfe_release_wm(vfe, output->wm_idx);
-	if (ret < 0)
-		goto out;
+	for (i = 0; i < output->wm_num; i++)
+		vfe_release_wm(vfe, output->wm_idx[i]);
 
 	output->state = VFE_OUTPUT_OFF;
 
-out:
 	spin_unlock_irqrestore(&vfe->output_lock, flags);
-	return ret;
+	return 0;
 }
 
 static int vfe_enable_output(struct vfe_line *line)
@@ -778,6 +1124,7 @@ static int vfe_enable_output(struct vfe_line *line)
 	struct vfe_device *vfe = to_vfe(line);
 	struct vfe_output *output = &line->output;
 	unsigned long flags;
+	unsigned int i;
 	u16 ub_size;
 
 	switch (vfe->id) {
@@ -793,7 +1140,7 @@ static int vfe_enable_output(struct vfe_line *line)
 
 	spin_lock_irqsave(&vfe->output_lock, flags);
 
-	vfe->reg_update &= ~VFE_0_REG_UPDATE_RDIn(line->id);
+	vfe->reg_update &= ~VFE_0_REG_UPDATE_line_n(line->id);
 
 	if (output->state != VFE_OUTPUT_RESERVED) {
 		dev_err(to_device(vfe), "Output is not in reserved state %d\n",
@@ -830,24 +1177,58 @@ static int vfe_enable_output(struct vfe_line *line)
 	}
 
 	output->sequence = 0;
+	output->wait_sof = 0;
+	output->wait_reg_update = 0;
+	reinit_completion(&output->sof);
+	reinit_completion(&output->reg_update);
 
 	vfe_output_init_addrs(vfe, output, 0);
 
-	vfe_set_cgc_override(vfe, output->wm_idx, 1);
-
-	vfe_enable_irq_wm_line(vfe, output->wm_idx, line->id, 1);
-
-	vfe_bus_connect_wm_to_rdi(vfe, output->wm_idx, line->id);
-
-	vfe_set_rdi_cid(vfe, line->id, 0);
-
-	vfe_wm_set_ub_cfg(vfe, output->wm_idx,
-			  (ub_size + 1) * output->wm_idx, ub_size);
-
-	vfe_wm_frame_based(vfe, output->wm_idx, 1);
-	vfe_wm_enable(vfe, output->wm_idx, 1);
+	if (line->id != VFE_LINE_PIX) {
+		vfe_set_cgc_override(vfe, output->wm_idx[0], 1);
+		vfe_enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 1);
+		vfe_bus_connect_wm_to_rdi(vfe, output->wm_idx[0], line->id);
+		vfe_wm_set_subsample(vfe, output->wm_idx[0]);
+		vfe_set_rdi_cid(vfe, line->id, 0);
+		vfe_wm_set_ub_cfg(vfe, output->wm_idx[0],
+				  (ub_size + 1) * output->wm_idx[0], ub_size);
+		vfe_wm_frame_based(vfe, output->wm_idx[0], 1);
+		vfe_wm_enable(vfe, output->wm_idx[0], 1);
+		vfe_bus_reload_wm(vfe, output->wm_idx[0]);
+	} else {
+		ub_size /= output->wm_num;
+		for (i = 0; i < output->wm_num; i++) {
+			u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
+
+			vfe_set_cgc_override(vfe, output->wm_idx[i], 1);
+			vfe_wm_set_subsample(vfe, output->wm_idx[i]);
+			vfe_wm_set_ub_cfg(vfe, output->wm_idx[i],
+					  (ub_size + 1) * output->wm_idx[i],
+					  ub_size);
+			if ((i == 1) &&	(p == V4L2_PIX_FMT_NV12 ||
+						p == V4L2_PIX_FMT_NV21))
+				vfe_wm_line_based(vfe, output->wm_idx[i],
+						  line->fmt[MSM_VFE_PAD_SRC].width,
+						  line->fmt[MSM_VFE_PAD_SRC].height / 2,
+						  1);
+			else
+				vfe_wm_line_based(vfe, output->wm_idx[i],
+						  line->fmt[MSM_VFE_PAD_SRC].width,
+						  line->fmt[MSM_VFE_PAD_SRC].height,
+						  1);
 
-	vfe_bus_reload_wm(vfe, output->wm_idx);
+			vfe_wm_enable(vfe, output->wm_idx[i], 1);
+			vfe_bus_reload_wm(vfe, output->wm_idx[i]);
+		}
+		vfe_enable_irq_pix_line(vfe, 0, line->id, 1);
+		vfe_set_module_cfg(vfe, 1);
+		vfe_set_camif_cfg(vfe, line);
+		vfe_set_xbar_cfg(vfe, output, 1);
+		vfe_set_demux_cfg(vfe, line);
+		vfe_set_scale_cfg(vfe, line);
+		vfe_set_clamp_cfg(vfe);
+		vfe_set_camif_cmd(vfe, VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY);
+	}
 
 	vfe_reg_update(vfe, line->id);
 
@@ -861,15 +1242,56 @@ static int vfe_disable_output(struct vfe_line *line)
 	struct vfe_device *vfe = to_vfe(line);
 	struct vfe_output *output = &line->output;
 	unsigned long flags;
+	unsigned long time;
+	unsigned int i;
 
 	spin_lock_irqsave(&vfe->output_lock, flags);
 
-	vfe_wm_enable(vfe, output->wm_idx, 0);
-	vfe_bus_disconnect_wm_from_rdi(vfe, output->wm_idx, line->id);
-	vfe_reg_update(vfe, line->id);
+	output->wait_sof = 1;
+	spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+	time = wait_for_completion_timeout(&output->sof,
+					   msecs_to_jiffies(VFE_NEXT_SOF_MS));
+	if (!time)
+		dev_err(to_device(vfe), "VFE sof timeout\n");
+
+	spin_lock_irqsave(&vfe->output_lock, flags);
+	for (i = 0; i < output->wm_num; i++)
+		vfe_wm_enable(vfe, output->wm_idx[i], 0);
 
+	vfe_reg_update(vfe, line->id);
+	output->wait_reg_update = 1;
 	spin_unlock_irqrestore(&vfe->output_lock, flags);
 
+	time = wait_for_completion_timeout(&output->reg_update,
+					   msecs_to_jiffies(VFE_NEXT_SOF_MS));
+	if (!time)
+		dev_err(to_device(vfe), "VFE reg update timeout\n");
+
+	spin_lock_irqsave(&vfe->output_lock, flags);
+
+	if (line->id != VFE_LINE_PIX) {
+		vfe_wm_frame_based(vfe, output->wm_idx[0], 0);
+		vfe_bus_disconnect_wm_from_rdi(vfe, output->wm_idx[0], line->id);
+		vfe_enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 0);
+		vfe_set_cgc_override(vfe, output->wm_idx[0], 0);
+		spin_unlock_irqrestore(&vfe->output_lock, flags);
+	} else {
+		for (i = 0; i < output->wm_num; i++) {
+			vfe_wm_line_based(vfe, output->wm_idx[i], 0, 0, 0);
+			vfe_set_cgc_override(vfe, output->wm_idx[i], 0);
+		}
+
+		vfe_enable_irq_pix_line(vfe, 0, line->id, 0);
+		vfe_set_module_cfg(vfe, 0);
+		vfe_set_xbar_cfg(vfe, output, 0);
+
+		vfe_set_camif_cmd(vfe, VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY);
+		spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+		vfe_camif_wait_for_stop(vfe);
+	}
+
 	return 0;
 }
 
@@ -937,6 +1359,10 @@ static int vfe_disable(struct vfe_line *line)
 {
 	struct vfe_device *vfe = to_vfe(line);
 
+	vfe_disable_output(line);
+
+	vfe_put_output(line);
+
 	mutex_lock(&vfe->stream_lock);
 
 	if (vfe->stream_count == 1)
@@ -946,11 +1372,26 @@ static int vfe_disable(struct vfe_line *line)
 
 	mutex_unlock(&vfe->stream_lock);
 
-	vfe_disable_output(line);
+	return 0;
+}
 
-	vfe_put_output(line);
+/*
+ * vfe_isr_sof - Process start of frame interrupt
+ * @vfe: VFE Device
+ * @line_id: VFE line
+ */
+static void vfe_isr_sof(struct vfe_device *vfe, enum vfe_line_id line_id)
+{
+	struct vfe_output *output;
+	unsigned long flags;
 
-	return 0;
+	spin_lock_irqsave(&vfe->output_lock, flags);
+	output = &vfe->line[line_id].output;
+	if (output->wait_sof) {
+		output->wait_sof = 0;
+		complete(&output->sof);
+	}
+	spin_unlock_irqrestore(&vfe->output_lock, flags);
 }
 
 /*
@@ -964,9 +1405,17 @@ static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
 	unsigned long flags;
 
 	spin_lock_irqsave(&vfe->output_lock, flags);
-	vfe->reg_update &= ~VFE_0_REG_UPDATE_RDIn(line_id);
+	vfe->reg_update &= ~VFE_0_REG_UPDATE_line_n(line_id);
 
 	output = &vfe->line[line_id].output;
+
+	if (output->wait_reg_update) {
+		output->wait_reg_update = 0;
+		complete(&output->reg_update);
+		spin_unlock_irqrestore(&vfe->output_lock, flags);
+		return;
+	}
+
 	if (output->state == VFE_OUTPUT_STOPPING) {
 		/* Release last buffer when hw is idle */
 		if (output->last_buffer) {
@@ -1020,10 +1469,11 @@ static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm)
 {
 	struct camss_buffer *ready_buf;
 	struct vfe_output *output;
-	dma_addr_t new_addr;
+	dma_addr_t *new_addr;
 	unsigned long flags;
 	u32 active_index;
 	u64 ts = ktime_get_ns();
+	unsigned int i;
 
 	active_index = vfe_wm_get_ping_pong_status(vfe, wm);
 
@@ -1066,9 +1516,13 @@ static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm)
 	}
 
 	if (active_index)
-		vfe_wm_set_ping_addr(vfe, wm, new_addr);
+		for (i = 0; i < output->wm_num; i++)
+			vfe_wm_set_ping_addr(vfe, output->wm_idx[i],
+					     new_addr[i]);
 	else
-		vfe_wm_set_pong_addr(vfe, wm, new_addr);
+		for (i = 0; i < output->wm_num; i++)
+			vfe_wm_set_pong_addr(vfe, output->wm_idx[i],
+					     new_addr[i]);
 
 	spin_unlock_irqrestore(&vfe->output_lock, flags);
 
@@ -1084,6 +1538,22 @@ static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm)
 }
 
 /*
+ * vfe_isr_wm_done - Process composite image done interrupt
+ * @vfe: VFE Device
+ * @comp: Composite image id
+ */
+static void vfe_isr_comp_done(struct vfe_device *vfe, u8 comp)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++)
+		if (vfe->wm_output_map[i] == VFE_LINE_PIX) {
+			vfe_isr_wm_done(vfe, i);
+			break;
+		}
+}
+
+/*
  * vfe_isr - ISPIF module interrupt handler
  * @irq: Interrupt line
  * @dev: VFE device
@@ -1095,7 +1565,7 @@ static irqreturn_t vfe_isr(int irq, void *dev)
 	struct vfe_device *vfe = dev;
 	u32 value0, value1;
 	u32 violation;
-	int i;
+	int i, j;
 
 	value0 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_0);
 	value1 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_1);
@@ -1120,10 +1590,25 @@ static irqreturn_t vfe_isr(int irq, void *dev)
 		writel_relaxed(0x0, vfe->base + VFE_0_BUS_BDG_CMD);
 	}
 
-	for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++)
-		if (value0 & VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(i))
+	for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++)
+		if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i))
 			vfe_isr_reg_update(vfe, i);
 
+	if (value0 & VFE_0_IRQ_STATUS_0_CAMIF_SOF)
+		vfe_isr_sof(vfe, VFE_LINE_PIX);
+
+	for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++)
+		if (value1 & VFE_0_IRQ_STATUS_1_RDIn_SOF(i))
+			vfe_isr_sof(vfe, i);
+
+	for (i = 0; i < MSM_VFE_COMPOSITE_IRQ_NUM; i++)
+		if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(i)) {
+			vfe_isr_comp_done(vfe, i);
+			for (j = 0; j < ARRAY_SIZE(vfe->wm_output_map); j++)
+				if (vfe->wm_output_map[j] == VFE_LINE_PIX)
+					value0 &= ~VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(j);
+		}
+
 	for (i = 0; i < MSM_VFE_IMAGE_MASTERS_NUM; i++)
 		if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(i))
 			vfe_isr_wm_done(vfe, i);
@@ -1393,6 +1878,7 @@ static void vfe_try_format(struct vfe_line *line,
 			   enum v4l2_subdev_format_whence which)
 {
 	unsigned int i;
+	u32 code;
 
 	switch (pad) {
 	case MSM_VFE_PAD_SINK:
@@ -1417,9 +1903,40 @@ static void vfe_try_format(struct vfe_line *line,
 	case MSM_VFE_PAD_SRC:
 		/* Set and return a format same as sink pad */
 
+		code = fmt->code;
+
 		*fmt = *__vfe_get_format(line, cfg, MSM_VFE_PAD_SINK,
 					 which);
 
+		if (line->id == VFE_LINE_PIX)
+			switch (fmt->code) {
+			case MEDIA_BUS_FMT_YUYV8_2X8:
+				if (code == MEDIA_BUS_FMT_YUYV8_1_5X8)
+					fmt->code = MEDIA_BUS_FMT_YUYV8_1_5X8;
+				else
+					fmt->code = MEDIA_BUS_FMT_YUYV8_2X8;
+				break;
+			case MEDIA_BUS_FMT_YVYU8_2X8:
+				if (code == MEDIA_BUS_FMT_YVYU8_1_5X8)
+					fmt->code = MEDIA_BUS_FMT_YVYU8_1_5X8;
+				else
+					fmt->code = MEDIA_BUS_FMT_YVYU8_2X8;
+				break;
+			case MEDIA_BUS_FMT_UYVY8_2X8:
+			default:
+				if (code == MEDIA_BUS_FMT_UYVY8_1_5X8)
+					fmt->code = MEDIA_BUS_FMT_UYVY8_1_5X8;
+				else
+					fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
+				break;
+			case MEDIA_BUS_FMT_VYUY8_2X8:
+				if (code == MEDIA_BUS_FMT_VYUY8_1_5X8)
+					fmt->code = MEDIA_BUS_FMT_VYUY8_1_5X8;
+				else
+					fmt->code = MEDIA_BUS_FMT_VYUY8_2X8;
+				break;
+			}
+
 		break;
 	}
 
@@ -1609,11 +2126,13 @@ int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res)
 	vfe->id = 0;
 	vfe->reg_update = 0;
 
-	for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++) {
+	for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) {
 		vfe->line[i].video_out.type =
 					V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
 		vfe->line[i].video_out.camss = camss;
 		vfe->line[i].id = i;
+		init_completion(&vfe->line[i].output.sof);
+		init_completion(&vfe->line[i].output.reg_update);
 	}
 
 	/* Memory */
@@ -1810,8 +2329,13 @@ int msm_vfe_register_entities(struct vfe_device *vfe,
 		v4l2_subdev_init(sd, &vfe_v4l2_ops);
 		sd->internal_ops = &vfe_v4l2_internal_ops;
 		sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
-		snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s%d",
-			 MSM_VFE_NAME, vfe->id, "rdi", i);
+		if (i == VFE_LINE_PIX)
+			snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s",
+				 MSM_VFE_NAME, vfe->id, "pix");
+		else
+			snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s%d",
+				 MSM_VFE_NAME, vfe->id, "rdi", i);
+
 		v4l2_set_subdevdata(sd, &vfe->line[i]);
 
 		ret = vfe_init_formats(sd, NULL);
@@ -1839,6 +2363,9 @@ int msm_vfe_register_entities(struct vfe_device *vfe,
 		}
 
 		video_out->ops = &camss_vfe_video_ops;
+		video_out->fmt_tag = CAMSS_FMT_TAG_RDI;
+		if (i == VFE_LINE_PIX)
+			video_out->fmt_tag = CAMSS_FMT_TAG_PIX;
 		snprintf(name, ARRAY_SIZE(name), "%s%d_%s%d",
 			 MSM_VFE_NAME, vfe->id, "video", i);
 		ret = msm_video_register(video_out, v4l2_dev, name);
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.h b/drivers/media/platform/qcom/camss-8x16/camss-vfe.h
index 6d2fc57..b0598e4 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.h
+++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.h
@@ -30,8 +30,9 @@
 #define MSM_VFE_PAD_SRC 1
 #define MSM_VFE_PADS_NUM 2
 
-#define MSM_VFE_LINE_NUM 3
+#define MSM_VFE_LINE_NUM 4
 #define MSM_VFE_IMAGE_MASTERS_NUM 7
+#define MSM_VFE_COMPOSITE_IRQ_NUM 4
 
 #define MSM_VFE_VFE0_UB_SIZE 1023
 #define MSM_VFE_VFE0_UB_SIZE_RDI (MSM_VFE_VFE0_UB_SIZE / 3)
@@ -51,11 +52,13 @@ enum vfe_line_id {
 	VFE_LINE_NONE = -1,
 	VFE_LINE_RDI0 = 0,
 	VFE_LINE_RDI1 = 1,
-	VFE_LINE_RDI2 = 2
+	VFE_LINE_RDI2 = 2,
+	VFE_LINE_PIX = 3
 };
 
 struct vfe_output {
-	u8 wm_idx;
+	u8 wm_num;
+	u8 wm_idx[3];
 
 	int active_buf;
 	struct camss_buffer *buf[2];
@@ -66,6 +69,10 @@ struct vfe_output {
 
 	enum vfe_output_state state;
 	unsigned int sequence;
+	int wait_sof;
+	int wait_reg_update;
+	struct completion sof;
+	struct completion reg_update;
 };
 
 struct vfe_line {
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-video.c b/drivers/media/platform/qcom/camss-8x16/camss-video.c
index 29483a4..c5ebf5c 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-video.c
+++ b/drivers/media/platform/qcom/camss-8x16/camss-video.c
@@ -27,72 +27,200 @@
 #include "camss-video.h"
 #include "camss.h"
 
+struct fract {
+	u8 numerator;
+	u8 denominator;
+};
+
 /*
  * struct format_info - ISP media bus format information
  * @code: V4L2 media bus format code
  * @pixelformat: V4L2 pixel format FCC identifier
- * @bpp: Bits per pixel when stored in memory
+ * @hsub: Horizontal subsampling (for each plane)
+ * @vsub: Vertical subsampling (for each plane)
+ * @bpp: Bits per pixel when stored in memory (for each plane)
+ * @fmt_tags: Tags that indicate for which output this format can be used
  */
 static const struct format_info {
 	u32 code;
 	u32 pixelformat;
-	unsigned int bpp;
+	u8 planes;
+	struct fract hsub[3];
+	struct fract vsub[3];
+	unsigned int bpp[3];
+	u8 fmt_tags;
 } formats[] = {
-	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 16 },
-	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 16 },
-	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 16 },
-	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 16 },
-	{ MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 8 },
-	{ MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 8 },
-	{ MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 8 },
-	{ MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 8 },
-	{ MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 10 },
-	{ MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 10 },
-	{ MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 10 },
-	{ MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 10 },
-	{ MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SRGGB12P, 12 },
-	{ MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 12 },
-	{ MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 12 },
-	{ MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 12 }
+	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 16 },
+	  CAMSS_FMT_TAG_RDI },
+	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 16 },
+	  CAMSS_FMT_TAG_RDI },
+	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 16 },
+	  CAMSS_FMT_TAG_RDI },
+	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 16 },
+	  CAMSS_FMT_TAG_RDI },
+	{ MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 8 },
+	  CAMSS_FMT_TAG_RDI },
+	{ MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 8 },
+	  CAMSS_FMT_TAG_RDI },
+	{ MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 8 },
+	  CAMSS_FMT_TAG_RDI },
+	{ MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 8 },
+	  CAMSS_FMT_TAG_RDI },
+	{ MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 10 },
+	  CAMSS_FMT_TAG_RDI },
+	{ MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 10 },
+	  CAMSS_FMT_TAG_RDI },
+	{ MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 10 },
+	  CAMSS_FMT_TAG_RDI },
+	{ MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 10 },
+	  CAMSS_FMT_TAG_RDI },
+	{ MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SRGGB12P, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 12 },
+	  CAMSS_FMT_TAG_RDI },
+	{ MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 12 },
+	  CAMSS_FMT_TAG_RDI },
+	{ MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 12 },
+	  CAMSS_FMT_TAG_RDI },
+	{ MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1,
+	  { { 1, 1 } }, { { 1, 1 } }, { 12 },
+	  CAMSS_FMT_TAG_RDI },
+	{ MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1,
+	  { { 1, 1 } }, { { 2, 3 } }, { 8 },
+	  CAMSS_FMT_TAG_PIX },
+	{ MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1,
+	  { { 1, 1 } }, { { 2, 3 } }, { 8 },
+	  CAMSS_FMT_TAG_PIX },
+	{ MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV12, 1,
+	  { { 1, 1 } }, { { 2, 3 } }, { 8 },
+	  CAMSS_FMT_TAG_PIX },
+	{ MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV12, 1,
+	  { { 1, 1 } }, { { 2, 3 } }, { 8 },
+	  CAMSS_FMT_TAG_PIX },
+	{ MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV21, 1,
+	  { { 1, 1 } }, { { 2, 3 } }, { 8 },
+	  CAMSS_FMT_TAG_PIX },
+	{ MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV21, 1,
+	  { { 1, 1 } }, { { 2, 3 } }, { 8 },
+	  CAMSS_FMT_TAG_PIX },
+	{ MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV21, 1,
+	  { { 1, 1 } }, { { 2, 3 } }, { 8 },
+	  CAMSS_FMT_TAG_PIX },
+	{ MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV21, 1,
+	  { { 1, 1 } }, { { 2, 3 } }, { 8 },
+	  CAMSS_FMT_TAG_PIX },
+	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV16, 1,
+	  { { 1, 1 } }, { { 1, 2 } }, { 8 },
+	  CAMSS_FMT_TAG_PIX },
+	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV16, 1,
+	  { { 1, 1 } }, { { 1, 2 } }, { 8 },
+	  CAMSS_FMT_TAG_PIX },
+	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV16, 1,
+	  { { 1, 1 } }, { { 1, 2 } }, { 8 },
+	  CAMSS_FMT_TAG_PIX },
+	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV16, 1,
+	  { { 1, 1 } }, { { 1, 2 } }, { 8 },
+	  CAMSS_FMT_TAG_PIX },
+	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV61, 1,
+	  { { 1, 1 } }, { { 1, 2 } }, { 8 },
+	  CAMSS_FMT_TAG_PIX },
+	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV61, 1,
+	  { { 1, 1 } }, { { 1, 2 } }, { 8 },
+	  CAMSS_FMT_TAG_PIX },
+	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV61, 1,
+	  { { 1, 1 } }, { { 1, 2 } }, { 8 },
+	  CAMSS_FMT_TAG_PIX },
+	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV61, 1,
+	  { { 1, 1 } }, { { 1, 2 } }, { 8 },
+	  CAMSS_FMT_TAG_PIX },
 };
 
 /* -----------------------------------------------------------------------------
  * Helper functions
  */
 
+static int video_find_format(u32 code, u32 pixelformat, enum camss_fmt_tag tag)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(formats); i++) {
+		if (formats[i].code == code &&
+		    formats[i].fmt_tags & tag &&
+		    formats[i].pixelformat == pixelformat)
+			return i;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(formats); i++)
+		if (formats[i].code == code &&
+		    formats[i].fmt_tags & tag)
+			return i;
+
+	WARN_ON(1);
+
+	return -EINVAL;
+}
+
+static int video_find_format_n(u32 code, u32 index, enum camss_fmt_tag tag)
+{
+	int i;
+	u32 n = 0;
+
+	for (i = 0; i < ARRAY_SIZE(formats); i++)
+		if (formats[i].code == code &&
+		    formats[i].fmt_tags & tag) {
+			if (n == index)
+				return i;
+			n++;
+		}
+
+	return -EINVAL;
+}
+
 /*
  * video_mbus_to_pix_mp - Convert v4l2_mbus_framefmt to v4l2_pix_format_mplane
- * @mbus: v4l2_mbus_framefmt format (input)
+ * @mbus: v4l2_mbus_framefmt format
  * @pix: v4l2_pix_format_mplane format (output)
+ * @index: index of an entry in formats array to be used for the conversion
  *
  * Fill the output pix structure with information from the input mbus format.
  *
  * Return 0 on success or a negative error code otherwise
  */
-static unsigned int video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt *mbus,
-					 struct v4l2_pix_format_mplane *pix)
+static int video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt *mbus,
+				struct v4l2_pix_format_mplane *pix, int index)
 {
+	const struct format_info *f;
 	unsigned int i;
 	u32 bytesperline;
 
+	f = &formats[index];
 	memset(pix, 0, sizeof(*pix));
 	v4l2_fill_pix_format_mplane(pix, mbus);
-
-	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
-		if (formats[i].code == mbus->code)
-			break;
+	pix->pixelformat = f->pixelformat;
+	pix->num_planes = f->planes;
+	for (i = 0; i < pix->num_planes; i++) {
+		bytesperline = pix->width / f->hsub[i].numerator *
+			f->hsub[i].denominator * f->bpp[i] / 8;
+		bytesperline = ALIGN(bytesperline, 8);
+		pix->plane_fmt[i].bytesperline = bytesperline;
+		pix->plane_fmt[i].sizeimage = pix->height /
+				f->vsub[i].numerator * f->vsub[i].denominator *
+				bytesperline;
 	}
 
-	if (WARN_ON(i == ARRAY_SIZE(formats)))
-		return -EINVAL;
-
-	pix->pixelformat = formats[i].pixelformat;
-	pix->num_planes = 1;
-	bytesperline = pix->width * formats[i].bpp / 8;
-	bytesperline = ALIGN(bytesperline, 8);
-	pix->plane_fmt[0].bytesperline = bytesperline;
-	pix->plane_fmt[0].sizeimage = bytesperline * pix->height;
-
 	return 0;
 }
 
@@ -132,7 +260,42 @@ static int video_get_subdev_format(struct camss_video *video,
 		return ret;
 
 	format->type = video->type;
-	return video_mbus_to_pix_mp(&fmt.format, &format->fmt.pix_mp);
+
+	ret = video_find_format(fmt.format.code,
+				format->fmt.pix_mp.pixelformat,
+				video->fmt_tag);
+	if (ret < 0)
+		return ret;
+
+	return video_mbus_to_pix_mp(&fmt.format, &format->fmt.pix_mp, ret);
+}
+
+static int video_get_pixelformat(struct camss_video *video, u32 *pixelformat,
+				 u32 index)
+{
+	struct v4l2_subdev_format fmt;
+	struct v4l2_subdev *subdev;
+	u32 pad;
+	int ret;
+
+	subdev = video_remote_subdev(video, &pad);
+	if (subdev == NULL)
+		return -EINVAL;
+
+	fmt.pad = pad;
+	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+
+	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
+	if (ret)
+		return ret;
+
+	ret = video_find_format_n(fmt.format.code, index, video->fmt_tag);
+	if (ret < 0)
+		return ret;
+
+	*pixelformat = formats[ret].pixelformat;
+
+	return 0;
 }
 
 /* -----------------------------------------------------------------------------
@@ -144,44 +307,73 @@ static int video_queue_setup(struct vb2_queue *q,
 	unsigned int sizes[], struct device *alloc_devs[])
 {
 	struct camss_video *video = vb2_get_drv_priv(q);
+	const struct v4l2_pix_format_mplane *format =
+						&video->active_fmt.fmt.pix_mp;
+	unsigned int i;
 
 	if (*num_planes) {
-		if (*num_planes != 1)
+		if (*num_planes != format->num_planes)
 			return -EINVAL;
 
-		if (sizes[0] < video->active_fmt.fmt.pix_mp.plane_fmt[0].sizeimage)
-			return -EINVAL;
+		for (i = 0; i < *num_planes; i++)
+			if (sizes[i] < format->plane_fmt[i].sizeimage)
+				return -EINVAL;
 
 		return 0;
 	}
 
-	*num_planes = 1;
+	*num_planes = format->num_planes;
 
-	sizes[0] = video->active_fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
+	for (i = 0; i < *num_planes; i++)
+		sizes[i] = format->plane_fmt[i].sizeimage;
 
 	return 0;
 }
 
-static int video_buf_prepare(struct vb2_buffer *vb)
+static int video_buf_init(struct vb2_buffer *vb)
 {
 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
 	struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue);
 	struct camss_buffer *buffer = container_of(vbuf, struct camss_buffer,
 						   vb);
+	const struct v4l2_pix_format_mplane *format =
+						&video->active_fmt.fmt.pix_mp;
 	struct sg_table *sgt;
+	unsigned int i;
 
-	if (video->active_fmt.fmt.pix_mp.plane_fmt[0].sizeimage >
-							vb2_plane_size(vb, 0))
-		return -EINVAL;
+	for (i = 0; i < format->num_planes; i++) {
+		sgt = vb2_dma_sg_plane_desc(vb, i);
+		if (!sgt)
+			return -EFAULT;
+
+		buffer->addr[i] = sg_dma_address(sgt->sgl);
+	}
 
-	vb2_set_plane_payload(vb, 0,
-			video->active_fmt.fmt.pix_mp.plane_fmt[0].sizeimage);
+	if (format->pixelformat == V4L2_PIX_FMT_NV12 ||
+			format->pixelformat == V4L2_PIX_FMT_NV21 ||
+			format->pixelformat == V4L2_PIX_FMT_NV16 ||
+			format->pixelformat == V4L2_PIX_FMT_NV61)
+		buffer->addr[1] = buffer->addr[0] +
+				format->plane_fmt[0].bytesperline *
+				format->height;
 
-	sgt = vb2_dma_sg_plane_desc(vb, 0);
-	if (!sgt)
-		return -EFAULT;
+	return 0;
+}
 
-	buffer->addr = sg_dma_address(sgt->sgl);
+static int video_buf_prepare(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct camss_video *video = vb2_get_drv_priv(vb->vb2_queue);
+	const struct v4l2_pix_format_mplane *format =
+						&video->active_fmt.fmt.pix_mp;
+	unsigned int i;
+
+	for (i = 0; i < format->num_planes; i++) {
+		if (format->plane_fmt[i].sizeimage > vb2_plane_size(vb, i))
+			return -EINVAL;
+
+		vb2_set_plane_payload(vb, i, format->plane_fmt[i].sizeimage);
+	}
 
 	vbuf->field = V4L2_FIELD_NONE;
 
@@ -203,23 +395,29 @@ static int video_check_format(struct camss_video *video)
 	struct v4l2_pix_format_mplane *pix = &video->active_fmt.fmt.pix_mp;
 	struct v4l2_pix_format_mplane *sd_pix;
 	struct v4l2_format format;
+	unsigned int i;
 	int ret;
 
+	sd_pix = &format.fmt.pix_mp;
+	sd_pix->pixelformat = pix->pixelformat;
 	ret = video_get_subdev_format(video, &format);
 	if (ret < 0)
 		return ret;
 
-	sd_pix = &format.fmt.pix_mp;
 	if (pix->pixelformat != sd_pix->pixelformat ||
 	    pix->height != sd_pix->height ||
 	    pix->width != sd_pix->width ||
 	    pix->num_planes != sd_pix->num_planes ||
-	    pix->num_planes != 1 ||
-	    pix->plane_fmt[0].bytesperline != sd_pix->plane_fmt[0].bytesperline ||
-	    pix->plane_fmt[0].sizeimage != sd_pix->plane_fmt[0].sizeimage ||
 	    pix->field != format.fmt.pix_mp.field)
 		return -EINVAL;
 
+	for (i = 0; i < pix->num_planes; i++)
+		if (pix->plane_fmt[i].bytesperline !=
+				sd_pix->plane_fmt[i].bytesperline ||
+		    pix->plane_fmt[i].sizeimage !=
+				sd_pix->plane_fmt[i].sizeimage)
+			return -EINVAL;
+
 	return 0;
 }
 
@@ -275,7 +473,6 @@ static void video_stop_streaming(struct vb2_queue *q)
 	struct media_entity *entity;
 	struct media_pad *pad;
 	struct v4l2_subdev *subdev;
-	struct v4l2_subdev *subdev_vfe = NULL;
 
 	entity = &vdev->entity;
 	while (1) {
@@ -290,14 +487,7 @@ static void video_stop_streaming(struct vb2_queue *q)
 		entity = pad->entity;
 		subdev = media_entity_to_v4l2_subdev(entity);
 
-		if (strstr(subdev->name, "vfe")) {
-			subdev_vfe = subdev;
-		} else if (strstr(subdev->name, "ispif")) {
-			v4l2_subdev_call(subdev, video, s_stream, 0);
-			v4l2_subdev_call(subdev_vfe, video, s_stream, 0);
-		} else {
-			v4l2_subdev_call(subdev, video, s_stream, 0);
-		}
+		v4l2_subdev_call(subdev, video, s_stream, 0);
 	}
 
 	media_pipeline_stop(&vdev->entity);
@@ -309,6 +499,7 @@ static const struct vb2_ops msm_video_vb2_q_ops = {
 	.queue_setup     = video_queue_setup,
 	.wait_prepare    = vb2_ops_wait_prepare,
 	.wait_finish     = vb2_ops_wait_finish,
+	.buf_init        = video_buf_init,
 	.buf_prepare     = video_buf_prepare,
 	.buf_queue       = video_buf_queue,
 	.start_streaming = video_start_streaming,
@@ -335,22 +526,11 @@ static int video_querycap(struct file *file, void *fh,
 static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f)
 {
 	struct camss_video *video = video_drvdata(file);
-	struct v4l2_format format;
-	int ret;
 
 	if (f->type != video->type)
 		return -EINVAL;
 
-	if (f->index)
-		return -EINVAL;
-
-	ret = video_get_subdev_format(video, &format);
-	if (ret < 0)
-		return ret;
-
-	f->pixelformat = format.fmt.pix_mp.pixelformat;
-
-	return 0;
+	return video_get_pixelformat(video, &f->pixelformat, f->index);
 }
 
 static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-video.h b/drivers/media/platform/qcom/camss-8x16/camss-video.h
index bca04a1..eff6b3d 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-video.h
+++ b/drivers/media/platform/qcom/camss-8x16/camss-video.h
@@ -29,7 +29,7 @@
 
 struct camss_buffer {
 	struct vb2_v4l2_buffer vb;
-	dma_addr_t addr;
+	dma_addr_t addr[3];
 	struct list_head queue;
 };
 
@@ -41,6 +41,11 @@ struct camss_video_ops {
 			     enum vb2_buffer_state state);
 };
 
+enum camss_fmt_tag {
+	CAMSS_FMT_TAG_RDI = 1 << 0,
+	CAMSS_FMT_TAG_PIX = 1 << 1
+};
+
 struct camss_video {
 	struct camss *camss;
 	struct vb2_queue vb2_q;
@@ -52,6 +57,7 @@ struct camss_video {
 	const struct camss_video_ops *ops;
 	struct mutex lock;
 	struct mutex q_lock;
+	enum camss_fmt_tag fmt_tag;
 };
 
 void msm_video_stop_streaming(struct camss_video *video);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v3 15/23] doc: media/v4l-drivers: Qualcomm Camera Subsystem - PIX Interface
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
                   ` (13 preceding siblings ...)
  2017-07-17 10:33 ` [PATCH v3 14/23] camss: vfe: Format conversion support using PIX interface Todor Tomov
@ 2017-07-17 10:33 ` Todor Tomov
  2017-07-20 15:13   ` Sakari Ailus
  2017-07-17 10:33 ` [PATCH v3 16/23] camss: vfe: Support for frame padding Todor Tomov
                   ` (9 subsequent siblings)
  24 siblings, 1 reply; 60+ messages in thread
From: Todor Tomov @ 2017-07-17 10:33 UTC (permalink / raw)
  To: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm
  Cc: Todor Tomov

Update Qualcomm Camera Subsystem driver document for the PIX interface
and format conversion support.

Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
---
 Documentation/media/v4l-drivers/qcom_camss.rst | 41 +++++++++++++++++++-------
 1 file changed, 31 insertions(+), 10 deletions(-)

diff --git a/Documentation/media/v4l-drivers/qcom_camss.rst b/Documentation/media/v4l-drivers/qcom_camss.rst
index 4707ea7..4df5655 100644
--- a/Documentation/media/v4l-drivers/qcom_camss.rst
+++ b/Documentation/media/v4l-drivers/qcom_camss.rst
@@ -45,12 +45,31 @@ Supported functionality
 
 The current version of the driver supports:
 
-- input from camera sensor via CSIPHY;
-- generation of test input data by the TG in CSID;
-- raw dump of the input data to memory. RDI interface of VFE is supported.
-  PIX interface (ISP processing, statistics engines, resize/crop, format
-  conversion) is not supported in the current version;
-- concurrent and independent usage of two data inputs - could be camera sensors
+- Input from camera sensor via CSIPHY;
+- Generation of test input data by the TG in CSID;
+- RDI interface of VFE - raw dump of the input data to memory.
+
+  Supported formats:
+
+  - YUYV/UYVY/YVYU/VYUY (packed YUV 4:2:2);
+  - MIPI RAW8 (8bit Bayer RAW);
+  - MIPI RAW10 (10bit packed Bayer RAW);
+  - MIPI RAW12 (12bit packed Bayer RAW).
+
+- PIX interface of VFE
+
+  - Format conversion of the input data.
+
+    Supported input formats:
+
+    - YUYV/UYVY/YVYU/VYUY (packed YUV 4:2:2).
+
+    Supported output formats:
+
+    - NV12/NV21 (two plane YUV 4:2:0);
+    - NV16/NV61 (two plane YUV 4:2:2).
+
+- Concurrent and independent usage of two data inputs - could be camera sensors
   and/or TG.
 
 
@@ -65,15 +84,15 @@ interface, the driver is split into V4L2 sub-devices as follows:
 - 2 CSID sub-devices - each CSID is represented by a single sub-device;
 - 2 ISPIF sub-devices - ISPIF is represented by a number of sub-devices equal
   to the number of CSID sub-devices;
-- 3 VFE sub-devices - VFE is represented by a number of sub-devices equal to
-  the number of RDI input interfaces.
+- 4 VFE sub-devices - VFE is represented by a number of sub-devices equal to
+  the number of the input interfaces (3 RDI and 1 PIX).
 
 The considerations to split the driver in this particular way are as follows:
 
 - representing CSIPHY and CSID modules by a separate sub-device for each module
   allows to model the hardware links between these modules;
-- representing VFE by a separate sub-devices for each RDI input interface allows
-  to use the three RDI interfaces concurently and independently as this is
+- representing VFE by a separate sub-devices for each input interface allows
+  to use the input interfaces concurently and independently as this is
   supported by the hardware;
 - representing ISPIF by a number of sub-devices equal to the number of CSID
   sub-devices allows to create linear media controller pipelines when using two
@@ -99,6 +118,8 @@ nodes) is as follows:
 - msm_vfe0_video1
 - msm_vfe0_rdi2
 - msm_vfe0_video2
+- msm_vfe0_pix
+- msm_vfe0_video3
 
 
 Implementation
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v3 16/23] camss: vfe: Support for frame padding
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
                   ` (14 preceding siblings ...)
  2017-07-17 10:33 ` [PATCH v3 15/23] doc: media/v4l-drivers: Qualcomm Camera Subsystem - PIX Interface Todor Tomov
@ 2017-07-17 10:33 ` Todor Tomov
  2017-07-20 15:17   ` Sakari Ailus
  2017-07-17 10:33 ` [PATCH v3 17/23] camss: vfe: Add interface for scaling Todor Tomov
                   ` (8 subsequent siblings)
  24 siblings, 1 reply; 60+ messages in thread
From: Todor Tomov @ 2017-07-17 10:33 UTC (permalink / raw)
  To: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm
  Cc: Todor Tomov

Add support for horizontal and vertical frame padding.

Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
---
 drivers/media/platform/qcom/camss-8x16/camss-vfe.c | 86 +++++++++++++++++-----
 .../media/platform/qcom/camss-8x16/camss-video.c   | 69 ++++++++++++-----
 .../media/platform/qcom/camss-8x16/camss-video.h   |  2 +
 3 files changed, 121 insertions(+), 36 deletions(-)

diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
index bef0209..327f158 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
@@ -279,21 +279,75 @@ static void vfe_wm_frame_based(struct vfe_device *vfe, u8 wm, u8 enable)
 			1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT);
 }
 
+#define CALC_WORD(width, M, N) (((width) * (M) + (N) - 1) / (N))
+
+static int vfe_word_per_line(uint32_t format, uint32_t pixel_per_line)
+{
+	int val = 0;
+
+	switch (format) {
+	case V4L2_PIX_FMT_NV12:
+	case V4L2_PIX_FMT_NV21:
+	case V4L2_PIX_FMT_NV16:
+	case V4L2_PIX_FMT_NV61:
+		val = CALC_WORD(pixel_per_line, 1, 8);
+		break;
+	case V4L2_PIX_FMT_YUYV:
+	case V4L2_PIX_FMT_YVYU:
+	case V4L2_PIX_FMT_UYVY:
+	case V4L2_PIX_FMT_VYUY:
+		val = CALC_WORD(pixel_per_line, 2, 8);
+		break;
+	}
+
+	return val;
+}
+
+static void vfe_get_wm_sizes(struct v4l2_pix_format_mplane *pix, u8 plane,
+			     u16 *width, u16 *height, u16 *bytesperline)
+{
+	switch (pix->pixelformat) {
+	case V4L2_PIX_FMT_NV12:
+	case V4L2_PIX_FMT_NV21:
+		*width = pix->width;
+		*height = pix->height;
+		*bytesperline = pix->plane_fmt[0].bytesperline;
+		if (plane == 1)
+			*height /= 2;
+		break;
+	case V4L2_PIX_FMT_NV16:
+	case V4L2_PIX_FMT_NV61:
+		*width = pix->width;
+		*height = pix->height;
+		*bytesperline = pix->plane_fmt[0].bytesperline;
+		break;
+	}
+}
+
 static void vfe_wm_line_based(struct vfe_device *vfe, u32 wm,
-			      u16 width, u16 height, u32 enable)
+			      struct v4l2_pix_format_mplane *pix,
+			      u8 plane, u32 enable)
 {
 	u32 reg;
 
 	if (enable) {
+		u16 width = 0, height = 0, bytesperline = 0, wpl;
+
+		vfe_get_wm_sizes(pix, plane, &width, &height, &bytesperline);
+
+		wpl = vfe_word_per_line(pix->pixelformat, width);
+
 		reg = height - 1;
-		reg |= (width / 16 - 1) << 16;
+		reg |= ((wpl + 1) / 2 - 1) << 16;
 
 		writel_relaxed(reg, vfe->base +
 			       VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm));
 
+		wpl = vfe_word_per_line(pix->pixelformat, bytesperline);
+
 		reg = 0x3;
 		reg |= (height - 1) << 4;
-		reg |= (width / 8) << 16;
+		reg |= wpl << 16;
 
 		writel_relaxed(reg, vfe->base +
 			       VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm));
@@ -1198,25 +1252,14 @@ static int vfe_enable_output(struct vfe_line *line)
 	} else {
 		ub_size /= output->wm_num;
 		for (i = 0; i < output->wm_num; i++) {
-			u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
-
 			vfe_set_cgc_override(vfe, output->wm_idx[i], 1);
 			vfe_wm_set_subsample(vfe, output->wm_idx[i]);
 			vfe_wm_set_ub_cfg(vfe, output->wm_idx[i],
 					  (ub_size + 1) * output->wm_idx[i],
 					  ub_size);
-			if ((i == 1) &&	(p == V4L2_PIX_FMT_NV12 ||
-						p == V4L2_PIX_FMT_NV21))
-				vfe_wm_line_based(vfe, output->wm_idx[i],
-						  line->fmt[MSM_VFE_PAD_SRC].width,
-						  line->fmt[MSM_VFE_PAD_SRC].height / 2,
-						  1);
-			else
-				vfe_wm_line_based(vfe, output->wm_idx[i],
-						  line->fmt[MSM_VFE_PAD_SRC].width,
-						  line->fmt[MSM_VFE_PAD_SRC].height,
-						  1);
-
+			vfe_wm_line_based(vfe, output->wm_idx[i],
+					&line->video_out.active_fmt.fmt.pix_mp,
+					i, 1);
 			vfe_wm_enable(vfe, output->wm_idx[i], 1);
 			vfe_bus_reload_wm(vfe, output->wm_idx[i]);
 		}
@@ -1278,7 +1321,7 @@ static int vfe_disable_output(struct vfe_line *line)
 		spin_unlock_irqrestore(&vfe->output_lock, flags);
 	} else {
 		for (i = 0; i < output->wm_num; i++) {
-			vfe_wm_line_based(vfe, output->wm_idx[i], 0, 0, 0);
+			vfe_wm_line_based(vfe, output->wm_idx[i], NULL, i, 0);
 			vfe_set_cgc_override(vfe, output->wm_idx[i], 0);
 		}
 
@@ -2363,9 +2406,14 @@ int msm_vfe_register_entities(struct vfe_device *vfe,
 		}
 
 		video_out->ops = &camss_vfe_video_ops;
+		video_out->bpl_alignment = 8;
+		video_out->line_based = 0;
 		video_out->fmt_tag = CAMSS_FMT_TAG_RDI;
-		if (i == VFE_LINE_PIX)
+		if (i == VFE_LINE_PIX) {
+			video_out->bpl_alignment = 16;
+			video_out->line_based = 1;
 			video_out->fmt_tag = CAMSS_FMT_TAG_PIX;
+		}
 		snprintf(name, ARRAY_SIZE(name), "%s%d_%s%d",
 			 MSM_VFE_NAME, vfe->id, "video", i);
 		ret = msm_video_register(video_out, v4l2_dev, name);
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-video.c b/drivers/media/platform/qcom/camss-8x16/camss-video.c
index c5ebf5c..5a2bf18 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-video.c
+++ b/drivers/media/platform/qcom/camss-8x16/camss-video.c
@@ -194,13 +194,15 @@ static int video_find_format_n(u32 code, u32 index, enum camss_fmt_tag tag)
  * @mbus: v4l2_mbus_framefmt format
  * @pix: v4l2_pix_format_mplane format (output)
  * @index: index of an entry in formats array to be used for the conversion
+ * @alignment: bytesperline alignment value
  *
  * Fill the output pix structure with information from the input mbus format.
  *
  * Return 0 on success or a negative error code otherwise
  */
 static int video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt *mbus,
-				struct v4l2_pix_format_mplane *pix, int index)
+				struct v4l2_pix_format_mplane *pix, int index,
+				unsigned int alignment)
 {
 	const struct format_info *f;
 	unsigned int i;
@@ -214,7 +216,7 @@ static int video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt *mbus,
 	for (i = 0; i < pix->num_planes; i++) {
 		bytesperline = pix->width / f->hsub[i].numerator *
 			f->hsub[i].denominator * f->bpp[i] / 8;
-		bytesperline = ALIGN(bytesperline, 8);
+		bytesperline = ALIGN(bytesperline, alignment);
 		pix->plane_fmt[i].bytesperline = bytesperline;
 		pix->plane_fmt[i].sizeimage = pix->height /
 				f->vsub[i].numerator * f->vsub[i].denominator *
@@ -267,7 +269,8 @@ static int video_get_subdev_format(struct camss_video *video,
 	if (ret < 0)
 		return ret;
 
-	return video_mbus_to_pix_mp(&fmt.format, &format->fmt.pix_mp, ret);
+	return video_mbus_to_pix_mp(&fmt.format, &format->fmt.pix_mp, ret,
+				    video->bpl_alignment);
 }
 
 static int video_get_pixelformat(struct camss_video *video, u32 *pixelformat,
@@ -395,7 +398,6 @@ static int video_check_format(struct camss_video *video)
 	struct v4l2_pix_format_mplane *pix = &video->active_fmt.fmt.pix_mp;
 	struct v4l2_pix_format_mplane *sd_pix;
 	struct v4l2_format format;
-	unsigned int i;
 	int ret;
 
 	sd_pix = &format.fmt.pix_mp;
@@ -411,13 +413,6 @@ static int video_check_format(struct camss_video *video)
 	    pix->field != format.fmt.pix_mp.field)
 		return -EINVAL;
 
-	for (i = 0; i < pix->num_planes; i++)
-		if (pix->plane_fmt[i].bytesperline !=
-				sd_pix->plane_fmt[i].bytesperline ||
-		    pix->plane_fmt[i].sizeimage !=
-				sd_pix->plane_fmt[i].sizeimage)
-			return -EINVAL;
-
 	return 0;
 }
 
@@ -542,28 +537,68 @@ static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
 	return 0;
 }
 
-static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
+static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
 {
 	struct camss_video *video = video_drvdata(file);
+	struct v4l2_plane_pix_format *p;
+	u32 bytesperline[3] = { 0 };
+	u32 sizeimage[3] = { 0 };
+	u32 lines;
 	int ret;
+	int i;
 
-	if (vb2_is_busy(&video->vb2_q))
-		return -EBUSY;
+	if (video->line_based)
+		for (i = 0; i < f->fmt.pix_mp.num_planes && i < 3; i++) {
+			p = &f->fmt.pix_mp.plane_fmt[i];
+			bytesperline[i] = clamp_t(u32, p->bytesperline,
+						  1, 65528);
+			sizeimage[i] = clamp_t(u32, p->sizeimage,
+					       bytesperline[i],
+					       bytesperline[i] * 4096);
+		}
 
 	ret = video_get_subdev_format(video, f);
 	if (ret < 0)
 		return ret;
 
-	video->active_fmt = *f;
+	if (video->line_based)
+		for (i = 0; i < f->fmt.pix_mp.num_planes; i++) {
+			p = &f->fmt.pix_mp.plane_fmt[i];
+			p->bytesperline = clamp_t(u32, p->bytesperline,
+						  1, 65528);
+			p->sizeimage = clamp_t(u32, p->sizeimage,
+					       p->bytesperline,
+					       p->bytesperline * 4096);
+			lines = p->sizeimage / p->bytesperline;
+
+			if (p->bytesperline < bytesperline[i])
+				p->bytesperline = ALIGN(bytesperline[i], 8);
+
+			if (p->sizeimage < p->bytesperline * lines)
+				p->sizeimage = p->bytesperline * lines;
+
+			if (p->sizeimage < sizeimage[i])
+				p->sizeimage = sizeimage[i];
+		}
 
 	return 0;
 }
 
-static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
+static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
 {
 	struct camss_video *video = video_drvdata(file);
+	int ret;
+
+	if (vb2_is_busy(&video->vb2_q))
+		return -EBUSY;
 
-	return video_get_subdev_format(video, f);
+	ret = video_try_fmt(file, fh, f);
+	if (ret < 0)
+		return ret;
+
+	video->active_fmt = *f;
+
+	return 0;
 }
 
 static int video_enum_input(struct file *file, void *fh,
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-video.h b/drivers/media/platform/qcom/camss-8x16/camss-video.h
index eff6b3d..e36a75b 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-video.h
+++ b/drivers/media/platform/qcom/camss-8x16/camss-video.h
@@ -57,6 +57,8 @@ struct camss_video {
 	const struct camss_video_ops *ops;
 	struct mutex lock;
 	struct mutex q_lock;
+	unsigned int bpl_alignment;
+	unsigned int line_based;
 	enum camss_fmt_tag fmt_tag;
 };
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v3 17/23] camss: vfe: Add interface for scaling
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
                   ` (15 preceding siblings ...)
  2017-07-17 10:33 ` [PATCH v3 16/23] camss: vfe: Support for frame padding Todor Tomov
@ 2017-07-17 10:33 ` Todor Tomov
  2017-07-20 15:20   ` Sakari Ailus
  2017-07-17 10:33 ` [PATCH v3 18/23] camss: vfe: Configure scaler module in VFE Todor Tomov
                   ` (7 subsequent siblings)
  24 siblings, 1 reply; 60+ messages in thread
From: Todor Tomov @ 2017-07-17 10:33 UTC (permalink / raw)
  To: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm
  Cc: Todor Tomov

Add compose selection ioctls to handle scaling configuration.

Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
---
 drivers/media/platform/qcom/camss-8x16/camss-vfe.c | 189 ++++++++++++++++++++-
 drivers/media/platform/qcom/camss-8x16/camss-vfe.h |   1 +
 2 files changed, 188 insertions(+), 2 deletions(-)

diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
index 327f158..8ec6ce7 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
@@ -211,6 +211,8 @@
 #define CAMIF_TIMEOUT_SLEEP_US 1000
 #define CAMIF_TIMEOUT_ALL_US 1000000
 
+#define SCALER_RATIO_MAX 16
+
 static const u32 vfe_formats[] = {
 	MEDIA_BUS_FMT_UYVY8_2X8,
 	MEDIA_BUS_FMT_VYUY8_2X8,
@@ -1905,6 +1907,25 @@ __vfe_get_format(struct vfe_line *line,
 	return &line->fmt[pad];
 }
 
+/*
+ * __vfe_get_compose - Get pointer to compose selection structure
+ * @line: VFE line
+ * @cfg: V4L2 subdev pad configuration
+ * @which: TRY or ACTIVE format
+ *
+ * Return pointer to TRY or ACTIVE compose rectangle structure
+ */
+static struct v4l2_rect *
+__vfe_get_compose(struct vfe_line *line,
+		  struct v4l2_subdev_pad_config *cfg,
+		  enum v4l2_subdev_format_whence which)
+{
+	if (which == V4L2_SUBDEV_FORMAT_TRY)
+		return v4l2_subdev_get_try_compose(&line->subdev, cfg,
+						   MSM_VFE_PAD_SINK);
+
+	return &line->compose;
+}
 
 /*
  * vfe_try_format - Handle try format by pad subdev method
@@ -1951,7 +1972,14 @@ static void vfe_try_format(struct vfe_line *line,
 		*fmt = *__vfe_get_format(line, cfg, MSM_VFE_PAD_SINK,
 					 which);
 
-		if (line->id == VFE_LINE_PIX)
+		if (line->id == VFE_LINE_PIX) {
+			struct v4l2_rect *rect;
+
+			rect = __vfe_get_compose(line, cfg, which);
+
+			fmt->width = rect->width;
+			fmt->height = rect->height;
+
 			switch (fmt->code) {
 			case MEDIA_BUS_FMT_YUYV8_2X8:
 				if (code == MEDIA_BUS_FMT_YUYV8_1_5X8)
@@ -1979,6 +2007,7 @@ static void vfe_try_format(struct vfe_line *line,
 					fmt->code = MEDIA_BUS_FMT_VYUY8_2X8;
 				break;
 			}
+		}
 
 		break;
 	}
@@ -1987,6 +2016,50 @@ static void vfe_try_format(struct vfe_line *line,
 }
 
 /*
+ * vfe_try_compose - Handle try compose selection by pad subdev method
+ * @line: VFE line
+ * @cfg: V4L2 subdev pad configuration
+ * @rect: pointer to v4l2 rect structure
+ * @which: wanted subdev format
+ */
+static void vfe_try_compose(struct vfe_line *line,
+			    struct v4l2_subdev_pad_config *cfg,
+			    struct v4l2_rect *rect,
+			    enum v4l2_subdev_format_whence which)
+{
+	struct v4l2_mbus_framefmt *fmt;
+
+	rect->width = rect->width - rect->left;
+	rect->left = 0;
+	rect->height = rect->height - rect->top;
+	rect->top = 0;
+
+	fmt = __vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, which);
+
+	if (rect->width > fmt->width)
+		rect->width = fmt->width;
+
+	if (rect->height > fmt->height)
+		rect->height = fmt->height;
+
+	if (fmt->width > rect->width * SCALER_RATIO_MAX)
+		rect->width = (fmt->width + SCALER_RATIO_MAX - 1) /
+							SCALER_RATIO_MAX;
+
+	rect->width &= ~0x1;
+
+	if (fmt->height > rect->height * SCALER_RATIO_MAX)
+		rect->height = (fmt->height + SCALER_RATIO_MAX - 1) /
+							SCALER_RATIO_MAX;
+
+	if (rect->width < 16)
+		rect->width = 16;
+
+	if (rect->height < 4)
+		rect->height = 4;
+}
+
+/*
  * vfe_enum_mbus_code - Handle pixel format enumeration
  * @sd: VFE V4L2 subdevice
  * @cfg: V4L2 subdev pad configuration
@@ -2081,6 +2154,10 @@ static int vfe_get_format(struct v4l2_subdev *sd,
 	return 0;
 }
 
+static int vfe_set_selection(struct v4l2_subdev *sd,
+			     struct v4l2_subdev_pad_config *cfg,
+			     struct v4l2_subdev_selection *sel);
+
 /*
  * vfe_set_format - Handle set format by pads subdev method
  * @sd: VFE V4L2 subdevice
@@ -2103,20 +2180,126 @@ static int vfe_set_format(struct v4l2_subdev *sd,
 	vfe_try_format(line, cfg, fmt->pad, &fmt->format, fmt->which);
 	*format = fmt->format;
 
-	/* Propagate the format from sink to source */
 	if (fmt->pad == MSM_VFE_PAD_SINK) {
+		struct v4l2_subdev_selection sel = { 0 };
+		int ret;
+
+		/* Propagate the format from sink to source */
 		format = __vfe_get_format(line, cfg, MSM_VFE_PAD_SRC,
 					  fmt->which);
 
 		*format = fmt->format;
 		vfe_try_format(line, cfg, MSM_VFE_PAD_SRC, format,
 			       fmt->which);
+
+		if (line->id != VFE_LINE_PIX)
+			return 0;
+
+		/* Reset sink pad compose selection */
+		sel.which = fmt->which;
+		sel.pad = MSM_VFE_PAD_SINK;
+		sel.target = V4L2_SEL_TGT_COMPOSE;
+		sel.r.width = fmt->format.width;
+		sel.r.height = fmt->format.height;
+		ret = vfe_set_selection(sd, cfg, &sel);
+		if (ret < 0)
+			return ret;
 	}
 
 	return 0;
 }
 
 /*
+ * vfe_get_selection - Handle get selection by pads subdev method
+ * @sd: VFE V4L2 subdevice
+ * @cfg: V4L2 subdev pad configuration
+ * @sel: pointer to v4l2 subdev selection structure
+ *
+ * Return -EINVAL or zero on success
+ */
+static int vfe_get_selection(struct v4l2_subdev *sd,
+			     struct v4l2_subdev_pad_config *cfg,
+			     struct v4l2_subdev_selection *sel)
+{
+	struct vfe_line *line = v4l2_get_subdevdata(sd);
+	struct v4l2_subdev_format fmt = { 0 };
+	struct v4l2_rect *compose;
+	int ret;
+
+	if (line->id != VFE_LINE_PIX || sel->pad != MSM_VFE_PAD_SINK)
+		return -EINVAL;
+
+	switch (sel->target) {
+	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+		fmt.pad = sel->pad;
+		fmt.which = sel->which;
+		ret = vfe_get_format(sd, cfg, &fmt);
+		if (ret < 0)
+			return ret;
+		sel->r.left = 0;
+		sel->r.top = 0;
+		sel->r.width = fmt.format.width;
+		sel->r.height = fmt.format.height;
+		break;
+	case V4L2_SEL_TGT_COMPOSE:
+		compose = __vfe_get_compose(line, cfg, sel->which);
+		if (compose == NULL)
+			return -EINVAL;
+
+		sel->r = *compose;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * vfe_set_selection - Handle set selection by pads subdev method
+ * @sd: VFE V4L2 subdevice
+ * @cfg: V4L2 subdev pad configuration
+ * @sel: pointer to v4l2 subdev selection structure
+ *
+ * Return -EINVAL or zero on success
+ */
+int vfe_set_selection(struct v4l2_subdev *sd,
+			     struct v4l2_subdev_pad_config *cfg,
+			     struct v4l2_subdev_selection *sel)
+{
+	struct vfe_line *line = v4l2_get_subdevdata(sd);
+	struct v4l2_rect *compose;
+	struct v4l2_subdev_format fmt = { 0 };
+	int ret;
+
+	if (line->id != VFE_LINE_PIX || sel->pad != MSM_VFE_PAD_SINK)
+		return -EINVAL;
+
+	if (sel->target != V4L2_SEL_TGT_COMPOSE)
+		return -EINVAL;
+
+	compose = __vfe_get_compose(line, cfg, sel->which);
+	if (compose == NULL)
+		return -EINVAL;
+
+	vfe_try_compose(line, cfg, &sel->r, sel->which);
+	*compose = sel->r;
+
+	/* Reset source pad format width and height */
+	fmt.which = sel->which;
+	fmt.pad = MSM_VFE_PAD_SRC;
+	ret = vfe_get_format(sd, cfg, &fmt);
+	if (ret < 0)
+		return ret;
+
+	fmt.format.width = compose->width;
+	fmt.format.height = compose->height;
+	ret = vfe_set_format(sd, cfg, &fmt);
+
+	return ret;
+}
+
+/*
  * vfe_init_formats - Initialize formats on all pads
  * @sd: VFE V4L2 subdevice
  * @fh: V4L2 subdev file handle
@@ -2310,6 +2493,8 @@ static const struct v4l2_subdev_pad_ops vfe_pad_ops = {
 	.enum_frame_size = vfe_enum_frame_size,
 	.get_fmt = vfe_get_format,
 	.set_fmt = vfe_set_format,
+	.get_selection = vfe_get_selection,
+	.set_selection = vfe_set_selection,
 };
 
 static const struct v4l2_subdev_ops vfe_v4l2_ops = {
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.h b/drivers/media/platform/qcom/camss-8x16/camss-vfe.h
index b0598e4..6518c7a 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.h
+++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.h
@@ -80,6 +80,7 @@ struct vfe_line {
 	struct v4l2_subdev subdev;
 	struct media_pad pads[MSM_VFE_PADS_NUM];
 	struct v4l2_mbus_framefmt fmt[MSM_VFE_PADS_NUM];
+	struct v4l2_rect compose;
 	struct camss_video video_out;
 	struct vfe_output output;
 };
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v3 18/23] camss: vfe: Configure scaler module in VFE
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
                   ` (16 preceding siblings ...)
  2017-07-17 10:33 ` [PATCH v3 17/23] camss: vfe: Add interface for scaling Todor Tomov
@ 2017-07-17 10:33 ` Todor Tomov
  2017-07-17 10:33 ` [PATCH v3 19/23] camss: vfe: Add interface for cropping Todor Tomov
                   ` (6 subsequent siblings)
  24 siblings, 0 replies; 60+ messages in thread
From: Todor Tomov @ 2017-07-17 10:33 UTC (permalink / raw)
  To: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm
  Cc: Todor Tomov

Add scaler module configuration support to be able to apply scaling.

Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
---
 drivers/media/platform/qcom/camss-8x16/camss-vfe.c | 59 +++++++++++++++++-----
 1 file changed, 46 insertions(+), 13 deletions(-)

diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
index 8ec6ce7..f994b35 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
@@ -183,13 +183,16 @@
 #define VFE_0_DEMUX_EVEN_CFG			0x438
 #define VFE_0_DEMUX_ODD_CFG			0x43c
 
+#define VFE_0_SCALE_ENC_Y_CFG			0x75c
+#define VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE		0x760
+#define VFE_0_SCALE_ENC_Y_H_PHASE		0x764
+#define VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE		0x76c
+#define VFE_0_SCALE_ENC_Y_V_PHASE		0x770
 #define VFE_0_SCALE_ENC_CBCR_CFG		0x778
 #define VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE	0x77c
 #define VFE_0_SCALE_ENC_CBCR_H_PHASE		0x780
-#define VFE_0_SCALE_ENC_CBCR_H_PAD		0x78c
 #define VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE	0x790
 #define VFE_0_SCALE_ENC_CBCR_V_PHASE		0x794
-#define VFE_0_SCALE_ENC_CBCR_V_PAD		0x7a0
 
 #define VFE_0_CLAMP_ENC_MAX_CFG			0x874
 #define VFE_0_CLAMP_ENC_MIN_CFG			0x878
@@ -645,6 +648,20 @@ static void vfe_set_demux_cfg(struct vfe_device *vfe, struct vfe_line *line)
 	writel_relaxed(odd_cfg, vfe->base + VFE_0_DEMUX_ODD_CFG);
 }
 
+static inline u8 vfe_calc_interp_reso(u16 input, u16 output)
+{
+	if (input / output >= 16)
+		return 0;
+
+	if (input / output >= 8)
+		return 1;
+
+	if (input / output >= 4)
+		return 2;
+
+	return 3;
+}
+
 static void vfe_set_scale_cfg(struct vfe_device *vfe, struct vfe_line *line)
 {
 	u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
@@ -653,35 +670,51 @@ static void vfe_set_scale_cfg(struct vfe_device *vfe, struct vfe_line *line)
 	u8 interp_reso;
 	u32 phase_mult;
 
+	writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_Y_CFG);
+
+	input = line->fmt[MSM_VFE_PAD_SINK].width;
+	output = line->compose.width;
+	reg = (output << 16) | input;
+	writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE);
+
+	interp_reso = vfe_calc_interp_reso(input, output);
+	phase_mult = input * (1 << (13 + interp_reso)) / output;
+	reg = (interp_reso << 20) | phase_mult;
+	writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_PHASE);
+
+	input = line->fmt[MSM_VFE_PAD_SINK].height;
+	output = line->compose.height;
+	reg = (output << 16) | input;
+	writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE);
+
+	interp_reso = vfe_calc_interp_reso(input, output);
+	phase_mult = input * (1 << (13 + interp_reso)) / output;
+	reg = (interp_reso << 20) | phase_mult;
+	writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_PHASE);
+
 	writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_CBCR_CFG);
 
 	input = line->fmt[MSM_VFE_PAD_SINK].width;
-	output = line->fmt[MSM_VFE_PAD_SRC].width / 2;
+	output = line->compose.width / 2;
 	reg = (output << 16) | input;
 	writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE);
 
-	interp_reso = 3;
+	interp_reso = vfe_calc_interp_reso(input, output);
 	phase_mult = input * (1 << (13 + interp_reso)) / output;
 	reg = (interp_reso << 20) | phase_mult;
 	writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_PHASE);
 
-	reg = input;
-	writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_PAD);
-
 	input = line->fmt[MSM_VFE_PAD_SINK].height;
-	output = line->fmt[MSM_VFE_PAD_SRC].height;
+	output = line->compose.height;
 	if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21)
-		output = line->fmt[MSM_VFE_PAD_SRC].height / 2;
+		output = line->compose.height / 2;
 	reg = (output << 16) | input;
 	writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE);
 
-	interp_reso = 3;
+	interp_reso = vfe_calc_interp_reso(input, output);
 	phase_mult = input * (1 << (13 + interp_reso)) / output;
 	reg = (interp_reso << 20) | phase_mult;
 	writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_PHASE);
-
-	reg = input;
-	writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_PAD);
 }
 
 static void vfe_set_clamp_cfg(struct vfe_device *vfe)
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v3 19/23] camss: vfe: Add interface for cropping
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
                   ` (17 preceding siblings ...)
  2017-07-17 10:33 ` [PATCH v3 18/23] camss: vfe: Configure scaler module in VFE Todor Tomov
@ 2017-07-17 10:33 ` Todor Tomov
  2017-07-17 10:33 ` [PATCH v3 20/23] camss: vfe: Configure crop module in VFE Todor Tomov
                   ` (5 subsequent siblings)
  24 siblings, 0 replies; 60+ messages in thread
From: Todor Tomov @ 2017-07-17 10:33 UTC (permalink / raw)
  To: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm
  Cc: Todor Tomov

Extend selection ioctls to handle cropping configuration.

Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
---
 drivers/media/platform/qcom/camss-8x16/camss-vfe.c | 191 ++++++++++++++++-----
 drivers/media/platform/qcom/camss-8x16/camss-vfe.h |   1 +
 2 files changed, 150 insertions(+), 42 deletions(-)

diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
index f994b35..17617c0 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
@@ -1961,6 +1961,26 @@ __vfe_get_compose(struct vfe_line *line,
 }
 
 /*
+ * __vfe_get_crop - Get pointer to crop selection structure
+ * @line: VFE line
+ * @cfg: V4L2 subdev pad configuration
+ * @which: TRY or ACTIVE format
+ *
+ * Return pointer to TRY or ACTIVE crop rectangle structure
+ */
+static struct v4l2_rect *
+__vfe_get_crop(struct vfe_line *line,
+	       struct v4l2_subdev_pad_config *cfg,
+	       enum v4l2_subdev_format_whence which)
+{
+	if (which == V4L2_SUBDEV_FORMAT_TRY)
+		return v4l2_subdev_get_try_crop(&line->subdev, cfg,
+						MSM_VFE_PAD_SRC);
+
+	return &line->crop;
+}
+
+/*
  * vfe_try_format - Handle try format by pad subdev method
  * @line: VFE line
  * @cfg: V4L2 subdev pad configuration
@@ -2008,7 +2028,7 @@ static void vfe_try_format(struct vfe_line *line,
 		if (line->id == VFE_LINE_PIX) {
 			struct v4l2_rect *rect;
 
-			rect = __vfe_get_compose(line, cfg, which);
+			rect = __vfe_get_crop(line, cfg, which);
 
 			fmt->width = rect->width;
 			fmt->height = rect->height;
@@ -2093,6 +2113,49 @@ static void vfe_try_compose(struct vfe_line *line,
 }
 
 /*
+ * vfe_try_crop - Handle try crop selection by pad subdev method
+ * @line: VFE line
+ * @cfg: V4L2 subdev pad configuration
+ * @rect: pointer to v4l2 rect structure
+ * @which: wanted subdev format
+ */
+static void vfe_try_crop(struct vfe_line *line,
+			 struct v4l2_subdev_pad_config *cfg,
+			 struct v4l2_rect *rect,
+			 enum v4l2_subdev_format_whence which)
+{
+	struct v4l2_rect *compose;
+
+	compose = __vfe_get_compose(line, cfg, which);
+
+	if (rect->width > compose->width)
+		rect->width = compose->width;
+
+	if (rect->width + rect->left > compose->width)
+		rect->left = compose->width - rect->width;
+
+	if (rect->height > compose->height)
+		rect->height = compose->height;
+
+	if (rect->height + rect->top > compose->height)
+		rect->top = compose->height - rect->height;
+
+	/* wm in line based mode writes multiple of 16 horizontally */
+	rect->left += (rect->width & 0xf) >> 1;
+	rect->width &= ~0xf;
+
+	if (rect->width < 16) {
+		rect->left = 0;
+		rect->width = 16;
+	}
+
+	if (rect->height < 4) {
+		rect->top = 0;
+		rect->height = 4;
+	}
+}
+
+/*
  * vfe_enum_mbus_code - Handle pixel format enumeration
  * @sd: VFE V4L2 subdevice
  * @cfg: V4L2 subdev pad configuration
@@ -2256,34 +2319,58 @@ static int vfe_get_selection(struct v4l2_subdev *sd,
 {
 	struct vfe_line *line = v4l2_get_subdevdata(sd);
 	struct v4l2_subdev_format fmt = { 0 };
-	struct v4l2_rect *compose;
+	struct v4l2_rect *rect;
 	int ret;
 
-	if (line->id != VFE_LINE_PIX || sel->pad != MSM_VFE_PAD_SINK)
+	if (line->id != VFE_LINE_PIX)
 		return -EINVAL;
 
-	switch (sel->target) {
-	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
-		fmt.pad = sel->pad;
-		fmt.which = sel->which;
-		ret = vfe_get_format(sd, cfg, &fmt);
-		if (ret < 0)
-			return ret;
-		sel->r.left = 0;
-		sel->r.top = 0;
-		sel->r.width = fmt.format.width;
-		sel->r.height = fmt.format.height;
-		break;
-	case V4L2_SEL_TGT_COMPOSE:
-		compose = __vfe_get_compose(line, cfg, sel->which);
-		if (compose == NULL)
+	if (sel->pad == MSM_VFE_PAD_SINK)
+		switch (sel->target) {
+		case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+			fmt.pad = sel->pad;
+			fmt.which = sel->which;
+			ret = vfe_get_format(sd, cfg, &fmt);
+			if (ret < 0)
+				return ret;
+
+			sel->r.left = 0;
+			sel->r.top = 0;
+			sel->r.width = fmt.format.width;
+			sel->r.height = fmt.format.height;
+			break;
+		case V4L2_SEL_TGT_COMPOSE:
+			rect = __vfe_get_compose(line, cfg, sel->which);
+			if (rect == NULL)
+				return -EINVAL;
+
+			sel->r = *rect;
+			break;
+		default:
 			return -EINVAL;
+		}
+	else if (sel->pad == MSM_VFE_PAD_SRC)
+		switch (sel->target) {
+		case V4L2_SEL_TGT_CROP_BOUNDS:
+			rect = __vfe_get_compose(line, cfg, sel->which);
+			if (rect == NULL)
+				return -EINVAL;
 
-		sel->r = *compose;
-		break;
-	default:
-		return -EINVAL;
-	}
+			sel->r.left = rect->left;
+			sel->r.top = rect->top;
+			sel->r.width = rect->width;
+			sel->r.height = rect->height;
+			break;
+		case V4L2_SEL_TGT_CROP:
+			rect = __vfe_get_crop(line, cfg, sel->which);
+			if (rect == NULL)
+				return -EINVAL;
+
+			sel->r = *rect;
+			break;
+		default:
+			return -EINVAL;
+		}
 
 	return 0;
 }
@@ -2301,33 +2388,53 @@ int vfe_set_selection(struct v4l2_subdev *sd,
 			     struct v4l2_subdev_selection *sel)
 {
 	struct vfe_line *line = v4l2_get_subdevdata(sd);
-	struct v4l2_rect *compose;
-	struct v4l2_subdev_format fmt = { 0 };
+	struct v4l2_rect *rect;
 	int ret;
 
-	if (line->id != VFE_LINE_PIX || sel->pad != MSM_VFE_PAD_SINK)
+	if (line->id != VFE_LINE_PIX)
 		return -EINVAL;
 
-	if (sel->target != V4L2_SEL_TGT_COMPOSE)
-		return -EINVAL;
+	if (sel->target == V4L2_SEL_TGT_COMPOSE &&
+		sel->pad == MSM_VFE_PAD_SINK) {
+		struct v4l2_subdev_selection crop = { 0 };
 
-	compose = __vfe_get_compose(line, cfg, sel->which);
-	if (compose == NULL)
-		return -EINVAL;
+		rect = __vfe_get_compose(line, cfg, sel->which);
+		if (rect == NULL)
+			return -EINVAL;
+
+		vfe_try_compose(line, cfg, &sel->r, sel->which);
+		*rect = sel->r;
+
+		/* Reset source crop selection */
+		crop.which = sel->which;
+		crop.pad = MSM_VFE_PAD_SRC;
+		crop.target = V4L2_SEL_TGT_CROP;
+		crop.r = *rect;
+		ret = vfe_set_selection(sd, cfg, &crop);
+	} else if (sel->target == V4L2_SEL_TGT_CROP &&
+		sel->pad == MSM_VFE_PAD_SRC) {
+		struct v4l2_subdev_format fmt = { 0 };
+
+		rect = __vfe_get_crop(line, cfg, sel->which);
+		if (rect == NULL)
+			return -EINVAL;
 
-	vfe_try_compose(line, cfg, &sel->r, sel->which);
-	*compose = sel->r;
+		vfe_try_crop(line, cfg, &sel->r, sel->which);
+		*rect = sel->r;
 
-	/* Reset source pad format width and height */
-	fmt.which = sel->which;
-	fmt.pad = MSM_VFE_PAD_SRC;
-	ret = vfe_get_format(sd, cfg, &fmt);
-	if (ret < 0)
-		return ret;
+		/* Reset source pad format width and height */
+		fmt.which = sel->which;
+		fmt.pad = MSM_VFE_PAD_SRC;
+		ret = vfe_get_format(sd, cfg, &fmt);
+		if (ret < 0)
+			return ret;
 
-	fmt.format.width = compose->width;
-	fmt.format.height = compose->height;
-	ret = vfe_set_format(sd, cfg, &fmt);
+		fmt.format.width = rect->width;
+		fmt.format.height = rect->height;
+		ret = vfe_set_format(sd, cfg, &fmt);
+	} else {
+		ret = -EINVAL;
+	}
 
 	return ret;
 }
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.h b/drivers/media/platform/qcom/camss-8x16/camss-vfe.h
index 6518c7a..3651ece 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.h
+++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.h
@@ -81,6 +81,7 @@ struct vfe_line {
 	struct media_pad pads[MSM_VFE_PADS_NUM];
 	struct v4l2_mbus_framefmt fmt[MSM_VFE_PADS_NUM];
 	struct v4l2_rect compose;
+	struct v4l2_rect crop;
 	struct camss_video video_out;
 	struct vfe_output output;
 };
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v3 20/23] camss: vfe: Configure crop module in VFE
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
                   ` (18 preceding siblings ...)
  2017-07-17 10:33 ` [PATCH v3 19/23] camss: vfe: Add interface for cropping Todor Tomov
@ 2017-07-17 10:33 ` Todor Tomov
  2017-07-17 10:33 ` [PATCH v3 21/23] doc: media/v4l-drivers: Qualcomm Camera Subsystem - Scale and crop Todor Tomov
                   ` (4 subsequent siblings)
  24 siblings, 0 replies; 60+ messages in thread
From: Todor Tomov @ 2017-07-17 10:33 UTC (permalink / raw)
  To: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm
  Cc: Todor Tomov

Add crop module configuration support to be able to apply cropping.

Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
---
 drivers/media/platform/qcom/camss-8x16/camss-vfe.c | 41 +++++++++++++++++++++-
 1 file changed, 40 insertions(+), 1 deletion(-)

diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
index 17617c0..8fd0ed9 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
@@ -57,6 +57,7 @@
 #define VFE_0_MODULE_CFG_DEMUX			(1 << 2)
 #define VFE_0_MODULE_CFG_CHROMA_UPSAMPLE	(1 << 3)
 #define VFE_0_MODULE_CFG_SCALE_ENC		(1 << 23)
+#define VFE_0_MODULE_CFG_CROP_ENC		(1 << 27)
 
 #define VFE_0_CORE_CFG			0x01c
 #define VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR	0x4
@@ -194,6 +195,11 @@
 #define VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE	0x790
 #define VFE_0_SCALE_ENC_CBCR_V_PHASE		0x794
 
+#define VFE_0_CROP_ENC_Y_WIDTH			0x854
+#define VFE_0_CROP_ENC_Y_HEIGHT			0x858
+#define VFE_0_CROP_ENC_CBCR_WIDTH		0x85c
+#define VFE_0_CROP_ENC_CBCR_HEIGHT		0x860
+
 #define VFE_0_CLAMP_ENC_MAX_CFG			0x874
 #define VFE_0_CLAMP_ENC_MIN_CFG			0x878
 
@@ -717,6 +723,37 @@ static void vfe_set_scale_cfg(struct vfe_device *vfe, struct vfe_line *line)
 	writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_PHASE);
 }
 
+static void vfe_set_crop_cfg(struct vfe_device *vfe, struct vfe_line *line)
+{
+	u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
+	u32 reg;
+	u16 first, last;
+
+	first = line->crop.left;
+	last = line->crop.left + line->crop.width - 1;
+	reg = (first << 16) | last;
+	writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_WIDTH);
+
+	first = line->crop.top;
+	last = line->crop.top + line->crop.height - 1;
+	reg = (first << 16) | last;
+	writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_HEIGHT);
+
+	first = line->crop.left / 2;
+	last = line->crop.left / 2 + line->crop.width / 2 - 1;
+	reg = (first << 16) | last;
+	writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_WIDTH);
+
+	first = line->crop.top;
+	last = line->crop.top + line->crop.height - 1;
+	if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21) {
+		first = line->crop.top / 2;
+		last = line->crop.top / 2 + line->crop.height / 2 - 1;
+	}
+	reg = (first << 16) | last;
+	writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_HEIGHT);
+}
+
 static void vfe_set_clamp_cfg(struct vfe_device *vfe)
 {
 	writel_relaxed(0x00ffffff, vfe->base + VFE_0_CLAMP_ENC_MAX_CFG);
@@ -829,7 +866,8 @@ static void vfe_set_module_cfg(struct vfe_device *vfe, u8 enable)
 {
 	u32 val = VFE_0_MODULE_CFG_DEMUX |
 		  VFE_0_MODULE_CFG_CHROMA_UPSAMPLE |
-		  VFE_0_MODULE_CFG_SCALE_ENC;
+		  VFE_0_MODULE_CFG_SCALE_ENC |
+		  VFE_0_MODULE_CFG_CROP_ENC;
 
 	if (enable)
 		writel_relaxed(val, vfe->base + VFE_0_MODULE_CFG);
@@ -1304,6 +1342,7 @@ static int vfe_enable_output(struct vfe_line *line)
 		vfe_set_xbar_cfg(vfe, output, 1);
 		vfe_set_demux_cfg(vfe, line);
 		vfe_set_scale_cfg(vfe, line);
+		vfe_set_crop_cfg(vfe, line);
 		vfe_set_clamp_cfg(vfe);
 		vfe_set_camif_cmd(vfe, VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY);
 	}
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v3 21/23] doc: media/v4l-drivers: Qualcomm Camera Subsystem - Scale and crop
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
                   ` (19 preceding siblings ...)
  2017-07-17 10:33 ` [PATCH v3 20/23] camss: vfe: Configure crop module in VFE Todor Tomov
@ 2017-07-17 10:33 ` Todor Tomov
  2017-07-17 10:33 ` [PATCH v3 22/23] camss: Use optimal clock frequency rates Todor Tomov
                   ` (3 subsequent siblings)
  24 siblings, 0 replies; 60+ messages in thread
From: Todor Tomov @ 2017-07-17 10:33 UTC (permalink / raw)
  To: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm
  Cc: Todor Tomov

Update the Qualcomm Camera Subsystem driver document for VFE scale
and crop modules support.

Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
---
 Documentation/media/v4l-drivers/qcom_camss.rst | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/Documentation/media/v4l-drivers/qcom_camss.rst b/Documentation/media/v4l-drivers/qcom_camss.rst
index 4df5655..7e4ab6e 100644
--- a/Documentation/media/v4l-drivers/qcom_camss.rst
+++ b/Documentation/media/v4l-drivers/qcom_camss.rst
@@ -35,7 +35,8 @@ driver consists of:
   the CSIDs to the inputs of the VFE;
 - VFE (Video Front End) module. Contains a pipeline of image processing hardware
   blocks. The VFE has different input interfaces. The PIX input interface feeds
-  the input data to the image processing pipeline. Three RDI input interfaces
+  the input data to the image processing pipeline. The image processing pipeline
+  contains also a scale and crop module at the end. Three RDI input interfaces
   bypass the image processing pipeline. The VFE also contains the AXI bus
   interface which writes the output data to memory.
 
@@ -69,6 +70,11 @@ The current version of the driver supports:
     - NV12/NV21 (two plane YUV 4:2:0);
     - NV16/NV61 (two plane YUV 4:2:2).
 
+  - Scaling support. Configuration of the VFE Encoder Scale module
+    for downscalling with ratio up to 16x.
+
+  - Cropping support. Configuration of the VFE Encoder Crop module.
+
 - Concurrent and independent usage of two data inputs - could be camera sensors
   and/or TG.
 
@@ -130,6 +136,12 @@ not required to implement the currently supported functionality. The complete
 configuration on each hardware module is applied on STREAMON ioctl based on
 the current active media links, formats and controls set.
 
+The output size of the scaler module in the VFE is configured with the actual
+compose selection rectangle on the sink pad of the 'msm_vfe0_pix' entity.
+
+The crop output area of the crop module in the VFE is configured with the actual
+crop selection rectangle on the source pad of the 'msm_vfe0_pix' entity.
+
 
 Documentation
 -------------
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v3 22/23] camss: Use optimal clock frequency rates
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
                   ` (20 preceding siblings ...)
  2017-07-17 10:33 ` [PATCH v3 21/23] doc: media/v4l-drivers: Qualcomm Camera Subsystem - Scale and crop Todor Tomov
@ 2017-07-17 10:33 ` Todor Tomov
  2017-07-19 15:59     ` kbuild test robot
  2017-07-20 15:23   ` Sakari Ailus
  2017-07-17 10:33 ` [PATCH v3 23/23] doc: media/v4l-drivers: Qualcomm Camera Subsystem - Media graph Todor Tomov
                   ` (2 subsequent siblings)
  24 siblings, 2 replies; 60+ messages in thread
From: Todor Tomov @ 2017-07-17 10:33 UTC (permalink / raw)
  To: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm
  Cc: Todor Tomov

Use standard V4L2 control to get pixel clock rate from a sensor
linked in the media controller pipeline. Then calculate clock
rates on CSIPHY, CSID and VFE to use the lowest possible.

If the currnet pixel clock rate of the sensor cannot be read then
use the highest possible. This case covers also the CSID test
generator usage.

If VFE is already powered on by another pipeline, check that the
current VFE clock rate is high enough for the new pipeline.
If not return busy error code as VFE clock rate cannot be changed
while VFE is running.

Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
---
 .../media/platform/qcom/camss-8x16/camss-csid.c    | 324 ++++++++++++++-------
 .../media/platform/qcom/camss-8x16/camss-csid.h    |   2 +-
 .../media/platform/qcom/camss-8x16/camss-csiphy.c  | 112 +++++--
 .../media/platform/qcom/camss-8x16/camss-csiphy.h  |   2 +-
 .../media/platform/qcom/camss-8x16/camss-ispif.c   |  23 +-
 .../media/platform/qcom/camss-8x16/camss-ispif.h   |   4 +-
 drivers/media/platform/qcom/camss-8x16/camss-vfe.c | 289 +++++++++++++++---
 drivers/media/platform/qcom/camss-8x16/camss-vfe.h |   2 +-
 drivers/media/platform/qcom/camss-8x16/camss.c     |  51 +++-
 drivers/media/platform/qcom/camss-8x16/camss.h     |  17 +-
 10 files changed, 634 insertions(+), 192 deletions(-)

diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csid.c b/drivers/media/platform/qcom/camss-8x16/camss-csid.c
index 2bf3415..5c0e359 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-csid.c
+++ b/drivers/media/platform/qcom/camss-8x16/camss-csid.c
@@ -68,122 +68,224 @@ static const struct {
 	u8 data_type;
 	u8 decode_format;
 	u8 uncompr_bpp;
+	u8 spp; /* bus samples per pixel */
 } csid_input_fmts[] = {
 	{
 		MEDIA_BUS_FMT_UYVY8_2X8,
 		MEDIA_BUS_FMT_UYVY8_2X8,
 		DATA_TYPE_YUV422_8BIT,
 		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
-		16
+		8,
+		2
 	},
 	{
 		MEDIA_BUS_FMT_VYUY8_2X8,
 		MEDIA_BUS_FMT_VYUY8_2X8,
 		DATA_TYPE_YUV422_8BIT,
 		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
-		16
+		8,
+		2
 	},
 	{
 		MEDIA_BUS_FMT_YUYV8_2X8,
 		MEDIA_BUS_FMT_YUYV8_2X8,
 		DATA_TYPE_YUV422_8BIT,
 		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
-		16
+		8,
+		2
 	},
 	{
 		MEDIA_BUS_FMT_YVYU8_2X8,
 		MEDIA_BUS_FMT_YVYU8_2X8,
 		DATA_TYPE_YUV422_8BIT,
 		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
-		16
+		8,
+		2
 	},
 	{
 		MEDIA_BUS_FMT_SBGGR8_1X8,
 		MEDIA_BUS_FMT_SBGGR8_1X8,
 		DATA_TYPE_RAW_8BIT,
 		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
-		8
+		8,
+		1
 	},
 	{
 		MEDIA_BUS_FMT_SGBRG8_1X8,
 		MEDIA_BUS_FMT_SGBRG8_1X8,
 		DATA_TYPE_RAW_8BIT,
 		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
-		8
+		8,
+		1
 	},
 	{
 		MEDIA_BUS_FMT_SGRBG8_1X8,
 		MEDIA_BUS_FMT_SGRBG8_1X8,
 		DATA_TYPE_RAW_8BIT,
 		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
-		8
+		8,
+		1
 	},
 	{
 		MEDIA_BUS_FMT_SRGGB8_1X8,
 		MEDIA_BUS_FMT_SRGGB8_1X8,
 		DATA_TYPE_RAW_8BIT,
 		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
-		8
+		8,
+		1
 	},
 	{
 		MEDIA_BUS_FMT_SBGGR10_1X10,
 		MEDIA_BUS_FMT_SBGGR10_1X10,
 		DATA_TYPE_RAW_10BIT,
 		DECODE_FORMAT_UNCOMPRESSED_10_BIT,
-		10
+		10,
+		1
 	},
 	{
 		MEDIA_BUS_FMT_SGBRG10_1X10,
 		MEDIA_BUS_FMT_SGBRG10_1X10,
 		DATA_TYPE_RAW_10BIT,
 		DECODE_FORMAT_UNCOMPRESSED_10_BIT,
-		10
+		10,
+		1
 	},
 	{
 		MEDIA_BUS_FMT_SGRBG10_1X10,
 		MEDIA_BUS_FMT_SGRBG10_1X10,
 		DATA_TYPE_RAW_10BIT,
 		DECODE_FORMAT_UNCOMPRESSED_10_BIT,
-		10
+		10,
+		1
 	},
 	{
 		MEDIA_BUS_FMT_SRGGB10_1X10,
 		MEDIA_BUS_FMT_SRGGB10_1X10,
 		DATA_TYPE_RAW_10BIT,
 		DECODE_FORMAT_UNCOMPRESSED_10_BIT,
-		10
+		10,
+		1
 	},
 	{
 		MEDIA_BUS_FMT_SBGGR12_1X12,
 		MEDIA_BUS_FMT_SBGGR12_1X12,
 		DATA_TYPE_RAW_12BIT,
 		DECODE_FORMAT_UNCOMPRESSED_12_BIT,
-		12
+		12,
+		1
 	},
 	{
 		MEDIA_BUS_FMT_SGBRG12_1X12,
 		MEDIA_BUS_FMT_SGBRG12_1X12,
 		DATA_TYPE_RAW_12BIT,
 		DECODE_FORMAT_UNCOMPRESSED_12_BIT,
-		12
+		12,
+		1
 	},
 	{
 		MEDIA_BUS_FMT_SGRBG12_1X12,
 		MEDIA_BUS_FMT_SGRBG12_1X12,
 		DATA_TYPE_RAW_12BIT,
 		DECODE_FORMAT_UNCOMPRESSED_12_BIT,
-		12
+		12,
+		1
 	},
 	{
 		MEDIA_BUS_FMT_SRGGB12_1X12,
 		MEDIA_BUS_FMT_SRGGB12_1X12,
 		DATA_TYPE_RAW_12BIT,
 		DECODE_FORMAT_UNCOMPRESSED_12_BIT,
-		12
+		12,
+		1
 	}
 };
 
 /*
+ * csid_get_uncompressed - map media bus format to uncompressed media bus format
+ * @code: media bus format code
+ *
+ * Return uncompressed media bus format code
+ */
+static u32 csid_get_uncompressed(u32 code)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
+		if (code == csid_input_fmts[i].code)
+			break;
+
+	return csid_input_fmts[i].uncompressed;
+}
+
+/*
+ * csid_get_data_type - map media bus format to data type
+ * @code: media bus format code
+ *
+ * Return data type code
+ */
+static u8 csid_get_data_type(u32 code)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
+		if (code == csid_input_fmts[i].code)
+			break;
+
+	return csid_input_fmts[i].data_type;
+}
+
+/*
+ * csid_get_decode_format - map media bus format to decode format
+ * @code: media bus format code
+ *
+ * Return decode format code
+ */
+static u8 csid_get_decode_format(u32 code)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
+		if (code == csid_input_fmts[i].code)
+			break;
+
+	return csid_input_fmts[i].decode_format;
+}
+
+/*
+ * csid_get_bpp - map media bus format to bits per pixel
+ * @code: media bus format code
+ *
+ * Return number of bits per pixel
+ */
+static u8 csid_get_bpp(u32 code)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
+		if (code == csid_input_fmts[i].uncompressed)
+			break;
+
+	return csid_input_fmts[i].uncompr_bpp;
+}
+
+/*
+ * csid_get_spp - map media bus format to bus samples per pixel
+ * @code: media bus format code
+ *
+ * Return number of bus samples per pixel
+ */
+static u8 csid_get_spp(u32 code)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
+		if (code == csid_input_fmts[i].uncompressed)
+			break;
+
+	return csid_input_fmts[i].spp;
+}
+
+/*
  * csid_isr - CSID module interrupt handler
  * @irq: Interrupt line
  * @dev: CSID device
@@ -205,6 +307,68 @@ static irqreturn_t csid_isr(int irq, void *dev)
 }
 
 /*
+ * csid_set_clock_rates - Calculate and set clock rates on CSID module
+ * @csiphy: CSID device
+ */
+static int csid_set_clock_rates(struct csid_device *csid)
+{
+	struct device *dev = to_device_index(csid, csid->id);
+	u32 pixel_clock;
+	int i, j;
+	int ret;
+
+	ret = camss_get_pixel_clock(&csid->subdev.entity, &pixel_clock);
+	if (ret)
+		pixel_clock = 0;
+
+	for (i = 0; i < csid->nclocks; i++) {
+		struct camss_clock *clock = &csid->clock[i];
+
+		if (!strcmp(clock->name, "csi0") ||
+			!strcmp(clock->name, "csi1")) {
+			u8 bpp = csid_get_bpp(
+					csid->fmt[MSM_CSIPHY_PAD_SINK].code);
+			u8 num_lanes = csid->phy.lane_cnt;
+			u64 min_rate = pixel_clock * bpp / (2 * num_lanes * 4);
+			long rate;
+
+			min_rate = (min_rate * CAMSS_CLOCK_MARGIN_NUMERATOR) /
+						CAMSS_CLOCK_MARGIN_DENOMINATOR;
+
+			for (j = 0; j < clock->nfreqs; j++)
+				if (min_rate < clock->freq[j])
+					break;
+
+			if (j == clock->nfreqs) {
+				dev_err(dev,
+					"Pixel clock is too high for CSID\n");
+				return -EINVAL;
+			}
+
+			/* if sensor pixel clock is not available */
+			/* set highest possible CSID clock rate */
+			if (min_rate == 0)
+				j = clock->nfreqs - 1;
+
+			rate = clk_round_rate(clock->clk, clock->freq[j]);
+			if (rate < 0) {
+				dev_err(dev, "clk round rate failed: %ld\n",
+					rate);
+				return -EINVAL;
+			}
+
+			ret = clk_set_rate(clock->clk, rate);
+			if (ret < 0) {
+				dev_err(dev, "clk set rate failed: %d\n", ret);
+				return ret;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/*
  * csid_reset - Trigger reset on CSID module and wait to complete
  * @csid: CSID device
  *
@@ -249,6 +413,12 @@ static int csid_set_power(struct v4l2_subdev *sd, int on)
 		if (ret < 0)
 			return ret;
 
+		ret = csid_set_clock_rates(csid);
+		if (ret < 0) {
+			regulator_disable(csid->vdda);
+			return ret;
+		}
+
 		ret = camss_enable_clocks(csid->nclocks, csid->clock, dev);
 		if (ret < 0) {
 			regulator_disable(csid->vdda);
@@ -277,74 +447,6 @@ static int csid_set_power(struct v4l2_subdev *sd, int on)
 }
 
 /*
- * csid_get_uncompressed - map media bus format to uncompressed media bus format
- * @code: media bus format code
- *
- * Return uncompressed media bus format code
- */
-static u32 csid_get_uncompressed(u32 code)
-{
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
-		if (code == csid_input_fmts[i].code)
-			break;
-
-	return csid_input_fmts[i].uncompressed;
-}
-
-/*
- * csid_get_data_type - map media bus format to data type
- * @code: media bus format code
- *
- * Return data type code
- */
-static u8 csid_get_data_type(u32 code)
-{
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
-		if (code == csid_input_fmts[i].code)
-			break;
-
-	return csid_input_fmts[i].data_type;
-}
-
-/*
- * csid_get_decode_format - map media bus format to decode format
- * @code: media bus format code
- *
- * Return decode format code
- */
-static u8 csid_get_decode_format(u32 code)
-{
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
-		if (code == csid_input_fmts[i].code)
-			break;
-
-	return csid_input_fmts[i].decode_format;
-}
-
-/*
- * csid_get_bpp - map media bus format to bits per pixel
- * @code: media bus format code
- *
- * Return number of bits per pixel
- */
-static u8 csid_get_bpp(u32 code)
-{
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
-		if (code == csid_input_fmts[i].uncompressed)
-			break;
-
-	return csid_input_fmts[i].uncompr_bpp;
-}
-
-/*
  * csid_set_stream - Enable/disable streaming on CSID module
  * @sd: CSID V4L2 subdevice
  * @enable: Requested streaming state
@@ -381,10 +483,12 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable)
 
 		if (tg->enabled) {
 			/* Config Test Generator */
-			u8 bpp = csid_get_bpp(csid->fmt[MSM_CSID_PAD_SRC].code);
-			u32 num_bytes_per_line =
-				csid->fmt[MSM_CSID_PAD_SRC].width * bpp / 8;
-			u32 num_lines = csid->fmt[MSM_CSID_PAD_SRC].height;
+			struct v4l2_mbus_framefmt *f =
+					&csid->fmt[MSM_CSID_PAD_SRC];
+			u8 bpp = csid_get_bpp(f->code);
+			u8 spp = csid_get_spp(f->code);
+			u32 num_bytes_per_line = f->width * bpp * spp / 8;
+			u32 num_lines = f->height;
 
 			/* 31:24 V blank, 23:13 H blank, 3:2 num of active DT */
 			/* 1:0 VC */
@@ -787,7 +891,7 @@ int msm_csid_subdev_init(struct csid_device *csid,
 	struct device *dev = to_device_index(csid, id);
 	struct platform_device *pdev = to_platform_device(dev);
 	struct resource *r;
-	int i;
+	int i, j;
 	int ret;
 
 	csid->id = id;
@@ -834,26 +938,30 @@ int msm_csid_subdev_init(struct csid_device *csid,
 		return -ENOMEM;
 
 	for (i = 0; i < csid->nclocks; i++) {
-		csid->clock[i] = devm_clk_get(dev, res->clock[i]);
-		if (IS_ERR(csid->clock[i]))
-			return PTR_ERR(csid->clock[i]);
-
-		if (res->clock_rate[i]) {
-			long clk_rate = clk_round_rate(csid->clock[i],
-						       res->clock_rate[i]);
-			if (clk_rate < 0) {
-				dev_err(to_device_index(csid, csid->id),
-					"clk round rate failed: %ld\n",
-					clk_rate);
-				return -EINVAL;
-			}
-			ret = clk_set_rate(csid->clock[i], clk_rate);
-			if (ret < 0) {
-				dev_err(to_device_index(csid, csid->id),
-					"clk set rate failed: %d\n", ret);
-				return ret;
-			}
+		struct camss_clock *clock = &csid->clock[i];
+
+		clock->clk = devm_clk_get(dev, res->clock[i]);
+		if (IS_ERR(clock->clk))
+			return PTR_ERR(clock->clk);
+
+		clock->name = res->clock[i];
+
+		clock->nfreqs = 0;
+		while (res->clock_rate[i][clock->nfreqs])
+			clock->nfreqs++;
+
+		if (!clock->nfreqs) {
+			clock->freq = NULL;
+			continue;
 		}
+
+		clock->freq = devm_kzalloc(dev, clock->nfreqs *
+					   sizeof(*clock->freq), GFP_KERNEL);
+		if (!clock->freq)
+			return -ENOMEM;
+
+		for (j = 0; j < clock->nfreqs; j++)
+			clock->freq[j] = res->clock_rate[i][j];
 	}
 
 	/* Regulator */
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csid.h b/drivers/media/platform/qcom/camss-8x16/camss-csid.h
index 3186c11..4df7018 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-csid.h
+++ b/drivers/media/platform/qcom/camss-8x16/camss-csid.h
@@ -56,7 +56,7 @@ struct csid_device {
 	void __iomem *base;
 	u32 irq;
 	char irq_name[30];
-	struct clk **clock;
+	struct camss_clock *clock;
 	int nclocks;
 	struct regulator *vdda;
 	struct completion reset_complete;
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c b/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c
index 4e7ddc4..fef18b1 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c
+++ b/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c
@@ -156,6 +156,68 @@ static irqreturn_t csiphy_isr(int irq, void *dev)
 }
 
 /*
+ * csiphy_set_clock_rates - Calculate and set clock rates on CSIPHY module
+ * @csiphy: CSIPHY device
+ */
+static int csiphy_set_clock_rates(struct csiphy_device *csiphy)
+{
+	struct device *dev = to_device_index(csiphy, csiphy->id);
+	u32 pixel_clock;
+	int i, j;
+	int ret;
+
+	ret = camss_get_pixel_clock(&csiphy->subdev.entity, &pixel_clock);
+	if (ret)
+		pixel_clock = 0;
+
+	for (i = 0; i < csiphy->nclocks; i++) {
+		struct camss_clock *clock = &csiphy->clock[i];
+
+		if (!strcmp(clock->name, "csiphy0_timer") ||
+			!strcmp(clock->name, "csiphy1_timer")) {
+			u8 bpp = csiphy_get_bpp(
+					csiphy->fmt[MSM_CSIPHY_PAD_SINK].code);
+			u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data;
+			u64 min_rate = pixel_clock * bpp / (2 * num_lanes * 4);
+
+			min_rate = (min_rate * CAMSS_CLOCK_MARGIN_NUMERATOR) /
+						CAMSS_CLOCK_MARGIN_DENOMINATOR;
+
+			for (j = 0; j < clock->nfreqs; j++)
+				if (min_rate < clock->freq[j])
+					break;
+
+			if (j == clock->nfreqs) {
+				dev_err(dev,
+					"Pixel clock is too high for CSIPHY\n");
+				return -EINVAL;
+			}
+
+			/* if sensor pixel clock is not available */
+			/* set highest possible CSIPHY clock rate */
+			if (min_rate == 0)
+				j = clock->nfreqs - 1;
+
+			csiphy->timer_clk_rate = clk_round_rate(clock->clk,
+								clock->freq[j]);
+			if (csiphy->timer_clk_rate < 0) {
+				dev_err(dev, "clk round rate failed: %ld\n",
+					csiphy->timer_clk_rate);
+				return -EINVAL;
+			}
+
+			ret = clk_set_rate(clock->clk, csiphy->timer_clk_rate);
+			if (ret < 0) {
+				dev_err(dev, "clk set rate failed: %d\n", ret);
+				return ret;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/*
  * csiphy_reset - Perform software reset on CSIPHY module
  * @csiphy: CSIPHY device
  */
@@ -182,6 +244,10 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on)
 	if (on) {
 		u8 hw_version;
 
+		ret = csiphy_set_clock_rates(csiphy);
+		if (ret < 0)
+			return ret;
+
 		ret = camss_enable_clocks(csiphy->nclocks, csiphy->clock, dev);
 		if (ret < 0)
 			return ret;
@@ -610,7 +676,7 @@ int msm_csiphy_subdev_init(struct csiphy_device *csiphy,
 	struct device *dev = to_device_index(csiphy, id);
 	struct platform_device *pdev = to_platform_device(dev);
 	struct resource *r;
-	int i;
+	int i, j;
 	int ret;
 
 	csiphy->id = id;
@@ -665,30 +731,30 @@ int msm_csiphy_subdev_init(struct csiphy_device *csiphy,
 		return -ENOMEM;
 
 	for (i = 0; i < csiphy->nclocks; i++) {
-		csiphy->clock[i] = devm_clk_get(dev, res->clock[i]);
-		if (IS_ERR(csiphy->clock[i]))
-			return PTR_ERR(csiphy->clock[i]);
-
-		if (res->clock_rate[i]) {
-			long clk_rate = clk_round_rate(csiphy->clock[i],
-						       res->clock_rate[i]);
-			if (clk_rate < 0) {
-				dev_err(to_device_index(csiphy, csiphy->id),
-					"clk round rate failed: %ld\n",
-					clk_rate);
-				return -EINVAL;
-			}
-			ret = clk_set_rate(csiphy->clock[i], clk_rate);
-			if (ret < 0) {
-				dev_err(to_device_index(csiphy, csiphy->id),
-					"clk set rate failed: %d\n", ret);
-				return ret;
-			}
+		struct camss_clock *clock = &csiphy->clock[i];
 
-			if (!strcmp(res->clock[i], "csiphy0_timer") ||
-					!strcmp(res->clock[i], "csiphy1_timer"))
-				csiphy->timer_clk_rate = clk_rate;
+		clock->clk = devm_clk_get(dev, res->clock[i]);
+		if (IS_ERR(clock->clk))
+			return PTR_ERR(clock->clk);
+
+		clock->name = res->clock[i];
+
+		clock->nfreqs = 0;
+		while (res->clock_rate[i][clock->nfreqs])
+			clock->nfreqs++;
+
+		if (!clock->nfreqs) {
+			clock->freq = NULL;
+			continue;
 		}
+
+		clock->freq = devm_kzalloc(dev, clock->nfreqs *
+					   sizeof(*clock->freq), GFP_KERNEL);
+		if (!clock->freq)
+			return -ENOMEM;
+
+		for (j = 0; j < clock->nfreqs; j++)
+			clock->freq[j] = res->clock_rate[i][j];
 	}
 
 	return 0;
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csiphy.h b/drivers/media/platform/qcom/camss-8x16/camss-csiphy.h
index a6f23ed..04043ed 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-csiphy.h
+++ b/drivers/media/platform/qcom/camss-8x16/camss-csiphy.h
@@ -57,7 +57,7 @@ struct csiphy_device {
 	void __iomem *base_clk_mux;
 	u32 irq;
 	char irq_name[30];
-	struct clk **clock;
+	struct camss_clock *clock;
 	int nclocks;
 	long timer_clk_rate;
 	struct csiphy_config cfg;
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-ispif.c b/drivers/media/platform/qcom/camss-8x16/camss-ispif.c
index 04918c0..79ffb3c 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-ispif.c
+++ b/drivers/media/platform/qcom/camss-8x16/camss-ispif.c
@@ -928,9 +928,14 @@ int msm_ispif_subdev_init(struct ispif_device *ispif,
 		return -ENOMEM;
 
 	for (i = 0; i < ispif->nclocks; i++) {
-		ispif->clock[i] = devm_clk_get(dev, res->clock[i]);
-		if (IS_ERR(ispif->clock[i]))
-			return PTR_ERR(ispif->clock[i]);
+		struct camss_clock *clock = &ispif->clock[i];
+
+		clock->clk = devm_clk_get(dev, res->clock[i]);
+		if (IS_ERR(clock->clk))
+			return PTR_ERR(clock->clk);
+
+		clock->freq = NULL;
+		clock->nfreqs = 0;
 	}
 
 	ispif->nclocks_for_reset = 0;
@@ -943,10 +948,14 @@ int msm_ispif_subdev_init(struct ispif_device *ispif,
 		return -ENOMEM;
 
 	for (i = 0; i < ispif->nclocks_for_reset; i++) {
-		ispif->clock_for_reset[i] = devm_clk_get(dev,
-						res->clock_for_reset[i]);
-		if (IS_ERR(ispif->clock_for_reset[i]))
-			return PTR_ERR(ispif->clock_for_reset[i]);
+		struct camss_clock *clock = &ispif->clock_for_reset[i];
+
+		clock->clk = devm_clk_get(dev, res->clock_for_reset[i]);
+		if (IS_ERR(clock->clk))
+			return PTR_ERR(clock->clk);
+
+		clock->freq = NULL;
+		clock->nfreqs = 0;
 	}
 
 	init_completion(&ispif->reset_complete);
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-ispif.h b/drivers/media/platform/qcom/camss-8x16/camss-ispif.h
index 6a1c9bd..6659020 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-ispif.h
+++ b/drivers/media/platform/qcom/camss-8x16/camss-ispif.h
@@ -60,9 +60,9 @@ struct ispif_device {
 	void __iomem *base_clk_mux;
 	u32 irq;
 	char irq_name[30];
-	struct clk **clock;
+	struct camss_clock *clock;
 	int nclocks;
-	struct clk **clock_for_reset;
+	struct camss_clock  *clock_for_reset;
 	int nclocks_for_reset;
 	struct completion reset_complete;
 	int power_count;
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
index 8fd0ed9..d2e35b0 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
@@ -222,25 +222,93 @@
 
 #define SCALER_RATIO_MAX 16
 
-static const u32 vfe_formats[] = {
-	MEDIA_BUS_FMT_UYVY8_2X8,
-	MEDIA_BUS_FMT_VYUY8_2X8,
-	MEDIA_BUS_FMT_YUYV8_2X8,
-	MEDIA_BUS_FMT_YVYU8_2X8,
-	MEDIA_BUS_FMT_SBGGR8_1X8,
-	MEDIA_BUS_FMT_SGBRG8_1X8,
-	MEDIA_BUS_FMT_SGRBG8_1X8,
-	MEDIA_BUS_FMT_SRGGB8_1X8,
-	MEDIA_BUS_FMT_SBGGR10_1X10,
-	MEDIA_BUS_FMT_SGBRG10_1X10,
-	MEDIA_BUS_FMT_SGRBG10_1X10,
-	MEDIA_BUS_FMT_SRGGB10_1X10,
-	MEDIA_BUS_FMT_SBGGR12_1X12,
-	MEDIA_BUS_FMT_SGBRG12_1X12,
-	MEDIA_BUS_FMT_SGRBG12_1X12,
-	MEDIA_BUS_FMT_SRGGB12_1X12,
+static const struct {
+	u32 code;
+	u8 bpp;
+} vfe_formats[] = {
+	{
+		MEDIA_BUS_FMT_UYVY8_2X8,
+		8,
+	},
+	{
+		MEDIA_BUS_FMT_VYUY8_2X8,
+		8,
+	},
+	{
+		MEDIA_BUS_FMT_YUYV8_2X8,
+		8,
+	},
+	{
+		MEDIA_BUS_FMT_YVYU8_2X8,
+		8,
+	},
+	{
+		MEDIA_BUS_FMT_SBGGR8_1X8,
+		8,
+	},
+	{
+		MEDIA_BUS_FMT_SGBRG8_1X8,
+		8,
+	},
+	{
+		MEDIA_BUS_FMT_SGRBG8_1X8,
+		8,
+	},
+	{
+		MEDIA_BUS_FMT_SRGGB8_1X8,
+		8,
+	},
+	{
+		MEDIA_BUS_FMT_SBGGR10_1X10,
+		10,
+	},
+	{
+		MEDIA_BUS_FMT_SGBRG10_1X10,
+		10,
+	},
+	{
+		MEDIA_BUS_FMT_SGRBG10_1X10,
+		10,
+	},
+	{
+		MEDIA_BUS_FMT_SRGGB10_1X10,
+		10,
+	},
+	{
+		MEDIA_BUS_FMT_SBGGR12_1X12,
+		12,
+	},
+	{
+		MEDIA_BUS_FMT_SGBRG12_1X12,
+		12,
+	},
+	{
+		MEDIA_BUS_FMT_SGRBG12_1X12,
+		12,
+	},
+	{
+		MEDIA_BUS_FMT_SRGGB12_1X12,
+		12,
+	}
 };
 
+/*
+ * vfe_get_bpp - map media bus format to bits per pixel
+ * @code: media bus format code
+ *
+ * Return number of bits per pixel
+ */
+static u8 vfe_get_bpp(u32 code)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(vfe_formats); i++)
+		if (code == vfe_formats[i].code)
+			break;
+
+	return vfe_formats[i].bpp;
+}
+
 static inline void vfe_reg_clr(struct vfe_device *vfe, u32 reg, u32 clr_bits)
 {
 	u32 bits = readl_relaxed(vfe->base + reg);
@@ -1734,6 +1802,140 @@ static irqreturn_t vfe_isr(int irq, void *dev)
 }
 
 /*
+ * vfe_set_clock_rates - Calculate and set clock rates on VFE module
+ * @vfe: VFE device
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int vfe_set_clock_rates(struct vfe_device *vfe)
+{
+	struct device *dev = to_device(vfe);
+	u32 pixel_clock[MSM_VFE_LINE_NUM];
+	int i, j;
+	int ret;
+
+	for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) {
+		ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity,
+					    &pixel_clock[i]);
+		if (ret)
+			pixel_clock[i] = 0;
+	}
+
+	for (i = 0; i < vfe->nclocks; i++) {
+		struct camss_clock *clock = &vfe->clock[i];
+
+		if (!strcmp(clock->name, "camss_vfe_vfe")) {
+			u64 min_rate = 0;
+			long rate;
+
+			for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) {
+				u32 tmp;
+				u8 bpp;
+
+				if (i == VFE_LINE_PIX) {
+					tmp = pixel_clock[i];
+				} else {
+					bpp = vfe_get_bpp(vfe->line[i].
+						fmt[MSM_VFE_PAD_SINK].code);
+					tmp = pixel_clock[i] * bpp / 64;
+				}
+
+				if (min_rate < tmp)
+					min_rate = tmp;
+			}
+
+			min_rate = (min_rate * CAMSS_CLOCK_MARGIN_NUMERATOR) /
+						CAMSS_CLOCK_MARGIN_DENOMINATOR;
+
+			for (j = 0; j < clock->nfreqs; j++)
+				if (min_rate < clock->freq[j])
+					break;
+
+			if (j == clock->nfreqs) {
+				dev_err(dev,
+					"Pixel clock is too high for VFE");
+				return -EINVAL;
+			}
+
+			/* if sensor pixel clock is not available */
+			/* set highest possible VFE clock rate */
+			if (min_rate == 0)
+				j = clock->nfreqs - 1;
+
+			rate = clk_round_rate(clock->clk, clock->freq[j]);
+			if (rate < 0) {
+				dev_err(dev, "clk round rate failed: %ld\n",
+					rate);
+				return -EINVAL;
+			}
+
+			ret = clk_set_rate(clock->clk, rate);
+			if (ret < 0) {
+				dev_err(dev, "clk set rate failed: %d\n", ret);
+				return ret;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * vfe_check_clock_rates - Check current clock rates on VFE module
+ * @vfe: VFE device
+ *
+ * Return 0 if current clock rates are suitable for a new pipeline
+ * or a negative error code otherwise
+ */
+static int vfe_check_clock_rates(struct vfe_device *vfe)
+{
+	u32 pixel_clock[MSM_VFE_LINE_NUM];
+	int i;
+	int ret;
+
+	for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) {
+		ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity,
+					    &pixel_clock[i]);
+		if (ret)
+			pixel_clock[i] = 0;
+	}
+
+	for (i = 0; i < vfe->nclocks; i++) {
+		struct camss_clock *clock = &vfe->clock[i];
+
+		if (!strcmp(clock->name, "camss_vfe_vfe")) {
+			u64 min_rate = 0;
+			unsigned long rate;
+
+			for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) {
+				u32 tmp;
+				u8 bpp;
+
+				if (i == VFE_LINE_PIX) {
+					tmp = pixel_clock[i];
+				} else {
+					bpp = vfe_get_bpp(vfe->line[i].
+						fmt[MSM_VFE_PAD_SINK].code);
+					tmp = pixel_clock[i] * bpp / 64;
+				}
+
+				if (min_rate < tmp)
+					min_rate = tmp;
+			}
+
+			min_rate = (min_rate * CAMSS_CLOCK_MARGIN_NUMERATOR) /
+						CAMSS_CLOCK_MARGIN_DENOMINATOR;
+
+			rate = clk_get_rate(clock->clk);
+			if (rate < min_rate)
+				return -EBUSY;
+		}
+	}
+
+	return 0;
+}
+
+/*
  * vfe_get - Power up and reset VFE module
  * @vfe: VFE Device
  *
@@ -1746,6 +1948,10 @@ static int vfe_get(struct vfe_device *vfe)
 	mutex_lock(&vfe->power_lock);
 
 	if (vfe->power_count == 0) {
+		ret = vfe_set_clock_rates(vfe);
+		if (ret < 0)
+			goto error_clocks;
+
 		ret = camss_enable_clocks(vfe->nclocks, vfe->clock,
 					  to_device(vfe));
 		if (ret < 0)
@@ -1758,6 +1964,10 @@ static int vfe_get(struct vfe_device *vfe)
 		vfe_reset_output_maps(vfe);
 
 		vfe_init_outputs(vfe);
+	} else {
+		ret = vfe_check_clock_rates(vfe);
+		if (ret < 0)
+			goto error_clocks;
 	}
 	vfe->power_count++;
 
@@ -2041,7 +2251,7 @@ static void vfe_try_format(struct vfe_line *line,
 		/* Set format on sink pad */
 
 		for (i = 0; i < ARRAY_SIZE(vfe_formats); i++)
-			if (fmt->code == vfe_formats[i])
+			if (fmt->code == vfe_formats[i].code)
 				break;
 
 		/* If not found, use UYVY as default */
@@ -2213,7 +2423,7 @@ static int vfe_enum_mbus_code(struct v4l2_subdev *sd,
 		if (code->index >= ARRAY_SIZE(vfe_formats))
 			return -EINVAL;
 
-		code->code = vfe_formats[code->index];
+		code->code = vfe_formats[code->index].code;
 	} else {
 		if (code->index > 0)
 			return -EINVAL;
@@ -2517,7 +2727,7 @@ int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res)
 	struct resource *r;
 	struct camss *camss = to_camss(vfe);
 
-	int i;
+	int i, j;
 	int ret;
 
 	mutex_init(&vfe->power_lock);
@@ -2580,23 +2790,30 @@ int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res)
 		return -ENOMEM;
 
 	for (i = 0; i < vfe->nclocks; i++) {
-		vfe->clock[i] = devm_clk_get(dev, res->clock[i]);
-		if (IS_ERR(vfe->clock[i]))
-			return PTR_ERR(vfe->clock[i]);
-
-		if (res->clock_rate[i]) {
-			long clk_rate = clk_round_rate(vfe->clock[i],
-						       res->clock_rate[i]);
-			if (clk_rate < 0) {
-				dev_err(dev, "clk round rate failed\n");
-				return -EINVAL;
-			}
-			ret = clk_set_rate(vfe->clock[i], clk_rate);
-			if (ret < 0) {
-				dev_err(dev, "clk set rate failed\n");
-				return ret;
-			}
+		struct camss_clock *clock = &vfe->clock[i];
+
+		clock->clk = devm_clk_get(dev, res->clock[i]);
+		if (IS_ERR(clock->clk))
+			return PTR_ERR(clock->clk);
+
+		clock->name = res->clock[i];
+
+		clock->nfreqs = 0;
+		while (res->clock_rate[i][clock->nfreqs])
+			clock->nfreqs++;
+
+		if (!clock->nfreqs) {
+			clock->freq = NULL;
+			continue;
 		}
+
+		clock->freq = devm_kzalloc(dev, clock->nfreqs *
+					   sizeof(*clock->freq), GFP_KERNEL);
+		if (!clock->freq)
+			return -ENOMEM;
+
+		for (j = 0; j < clock->nfreqs; j++)
+			clock->freq[j] = res->clock_rate[i][j];
 	}
 
 	init_completion(&vfe->reset_complete);
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.h b/drivers/media/platform/qcom/camss-8x16/camss-vfe.h
index 3651ece..88c29d0 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.h
+++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.h
@@ -91,7 +91,7 @@ struct vfe_device {
 	void __iomem *base;
 	u32 irq;
 	char irq_name[30];
-	struct clk **clock;
+	struct camss_clock *clock;
 	int nclocks;
 	struct completion reset_complete;
 	struct completion halt_complete;
diff --git a/drivers/media/platform/qcom/camss-8x16/camss.c b/drivers/media/platform/qcom/camss-8x16/camss.c
index 097d4ec..dfce2e8 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss.c
+++ b/drivers/media/platform/qcom/camss-8x16/camss.c
@@ -39,7 +39,10 @@ static const struct resources csiphy_res[] = {
 		.regulator = { NULL },
 		.clock = { "camss_top_ahb", "ispif_ahb",
 			   "camss_ahb", "csiphy0_timer" },
-		.clock_rate = { 0, 0, 0, 200000000 },
+		.clock_rate = { { 0 },
+				{ 0 },
+				{ 0 },
+				{ 100000000, 200000000 } },
 		.reg = { "csiphy0", "csiphy0_clk_mux" },
 		.interrupt = { "csiphy0" }
 	},
@@ -49,7 +52,10 @@ static const struct resources csiphy_res[] = {
 		.regulator = { NULL },
 		.clock = { "camss_top_ahb", "ispif_ahb",
 			   "camss_ahb", "csiphy1_timer" },
-		.clock_rate = { 0, 0, 0, 200000000 },
+		.clock_rate = { { 0 },
+				{ 0 },
+				{ 0 },
+				{ 100000000, 200000000 } },
 		.reg = { "csiphy1", "csiphy1_clk_mux" },
 		.interrupt = { "csiphy1" }
 	}
@@ -62,7 +68,14 @@ static const struct resources csid_res[] = {
 		.clock = { "camss_top_ahb", "ispif_ahb",
 			   "csi0_ahb", "camss_ahb",
 			   "csi0", "csi0_phy", "csi0_pix", "csi0_rdi" },
-		.clock_rate = { 0, 0, 0, 0, 200000000, 0, 0, 0 },
+		.clock_rate = { { 0 },
+				{ 0 },
+				{ 0 },
+				{ 0 },
+				{ 100000000, 200000000 },
+				{ 0 },
+				{ 0 },
+				{ 0 } },
 		.reg = { "csid0" },
 		.interrupt = { "csid0" }
 	},
@@ -73,7 +86,14 @@ static const struct resources csid_res[] = {
 		.clock = { "camss_top_ahb", "ispif_ahb",
 			   "csi1_ahb", "camss_ahb",
 			   "csi1", "csi1_phy", "csi1_pix", "csi1_rdi" },
-		.clock_rate = { 0, 0, 0, 0, 200000000, 0, 0, 0 },
+		.clock_rate = { { 0 },
+				{ 0 },
+				{ 0 },
+				{ 0 },
+				{ 100000000, 200000000 },
+				{ 0 },
+				{ 0 },
+				{ 0 } },
 		.reg = { "csid1" },
 		.interrupt = { "csid1" }
 	},
@@ -95,7 +115,17 @@ static const struct resources vfe_res = {
 	.regulator = { NULL },
 	.clock = { "camss_top_ahb", "camss_vfe_vfe", "camss_csi_vfe",
 		   "iface", "bus", "camss_ahb" },
-	.clock_rate = { 0, 320000000, 0, 0, 0, 0, 0, 0 },
+	.clock_rate = { { 0 },
+			{ 50000000, 80000000, 100000000, 160000000,
+			  177780000, 200000000, 266670000, 320000000,
+			  400000000, 465000000 },
+			{ 0 },
+			{ 0 },
+			{ 0 },
+			{ 0 },
+			{ 0 },
+			{ 0 },
+			{ 0 } },
 	.reg = { "vfe0" },
 	.interrupt = { "vfe0" }
 };
@@ -108,13 +138,14 @@ static const struct resources vfe_res = {
  *
  * Return 0 on success or a negative error code otherwise
  */
-int camss_enable_clocks(int nclocks, struct clk **clock, struct device *dev)
+int camss_enable_clocks(int nclocks, struct camss_clock *clock,
+			struct device *dev)
 {
 	int ret;
 	int i;
 
 	for (i = 0; i < nclocks; i++) {
-		ret = clk_prepare_enable(clock[i]);
+		ret = clk_prepare_enable(clock[i].clk);
 		if (ret) {
 			dev_err(dev, "clock enable failed: %d\n", ret);
 			goto error;
@@ -125,7 +156,7 @@ int camss_enable_clocks(int nclocks, struct clk **clock, struct device *dev)
 
 error:
 	for (i--; i >= 0; i--)
-		clk_disable_unprepare(clock[i]);
+		clk_disable_unprepare(clock[i].clk);
 
 	return ret;
 }
@@ -135,12 +166,12 @@ int camss_enable_clocks(int nclocks, struct clk **clock, struct device *dev)
  * @nclocks: Number of clocks in clock array
  * @clock: Clock array
  */
-void camss_disable_clocks(int nclocks, struct clk **clock)
+void camss_disable_clocks(int nclocks, struct camss_clock *clock)
 {
 	int i;
 
 	for (i = nclocks - 1; i >= 0; i--)
-		clk_disable_unprepare(clock[i]);
+		clk_disable_unprepare(clock[i].clk);
 }
 
 /*
diff --git a/drivers/media/platform/qcom/camss-8x16/camss.h b/drivers/media/platform/qcom/camss-8x16/camss.h
index 4619d7634..640b7e2 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss.h
+++ b/drivers/media/platform/qcom/camss-8x16/camss.h
@@ -34,6 +34,9 @@
 #define CAMSS_CSID_NUM 2
 #define CAMSS_CSIPHY_NUM 2
 
+#define CAMSS_CLOCK_MARGIN_NUMERATOR 105
+#define CAMSS_CLOCK_MARGIN_DENOMINATOR 100
+
 #define to_camss(ptr_module)	\
 	container_of(ptr_module, struct camss, ptr_module)
 
@@ -55,7 +58,7 @@
 struct resources {
 	char *regulator[CAMSS_RES_MAX];
 	char *clock[CAMSS_RES_MAX];
-	s32 clock_rate[CAMSS_RES_MAX];
+	u32 clock_rate[CAMSS_RES_MAX][CAMSS_RES_MAX];
 	char *reg[CAMSS_RES_MAX];
 	char *interrupt[CAMSS_RES_MAX];
 };
@@ -89,8 +92,16 @@ struct camss_async_subdev {
 	struct v4l2_async_subdev asd;
 };
 
-int camss_enable_clocks(int nclocks, struct clk **clock, struct device *dev);
-void camss_disable_clocks(int nclocks, struct clk **clock);
+struct camss_clock {
+	struct clk *clk;
+	const char *name;
+	u32 *freq;
+	u32 nfreqs;
+};
+
+int camss_enable_clocks(int nclocks, struct camss_clock *clock,
+			struct device *dev);
+void camss_disable_clocks(int nclocks, struct camss_clock *clock);
 int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock);
 void camss_delete(struct camss *camss);
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* [PATCH v3 23/23] doc: media/v4l-drivers: Qualcomm Camera Subsystem - Media graph
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
                   ` (21 preceding siblings ...)
  2017-07-17 10:33 ` [PATCH v3 22/23] camss: Use optimal clock frequency rates Todor Tomov
@ 2017-07-17 10:33 ` Todor Tomov
  2017-07-19 10:54 ` [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Hans Verkuil
  2017-07-20 15:25 ` Sakari Ailus
  24 siblings, 0 replies; 60+ messages in thread
From: Todor Tomov @ 2017-07-17 10:33 UTC (permalink / raw)
  To: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm
  Cc: Todor Tomov

Update the Qualcomm Camera Subsystem driver document with a media
controller pipeline graph diagram.

Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
---
 Documentation/media/v4l-drivers/qcom_camss.rst     | 27 ++++++--------
 .../media/v4l-drivers/qcom_camss_graph.dot         | 41 ++++++++++++++++++++++
 2 files changed, 51 insertions(+), 17 deletions(-)
 create mode 100644 Documentation/media/v4l-drivers/qcom_camss_graph.dot

diff --git a/Documentation/media/v4l-drivers/qcom_camss.rst b/Documentation/media/v4l-drivers/qcom_camss.rst
index 7e4ab6e..7ae4a45 100644
--- a/Documentation/media/v4l-drivers/qcom_camss.rst
+++ b/Documentation/media/v4l-drivers/qcom_camss.rst
@@ -109,23 +109,16 @@ The considerations to split the driver in this particular way are as follows:
 
 Each VFE sub-device is linked to a separate video device node.
 
-The complete list of the media entities (V4L2 sub-devices and video device
-nodes) is as follows:
-
-- msm_csiphy0
-- msm_csiphy1
-- msm_csid0
-- msm_csid1
-- msm_ispif0
-- msm_ispif1
-- msm_vfe0_rdi0
-- msm_vfe0_video0
-- msm_vfe0_rdi1
-- msm_vfe0_video1
-- msm_vfe0_rdi2
-- msm_vfe0_video2
-- msm_vfe0_pix
-- msm_vfe0_video3
+The media controller pipeline graph is as follows (with connected two OV5645
+camera sensors):
+
+.. _qcom_camss_graph:
+
+.. kernel-figure:: qcom_camss_graph.dot
+    :alt:   qcom_camss_graph.dot
+    :align: center
+
+    Media pipeline graph
 
 
 Implementation
diff --git a/Documentation/media/v4l-drivers/qcom_camss_graph.dot b/Documentation/media/v4l-drivers/qcom_camss_graph.dot
new file mode 100644
index 0000000..827fc71
--- /dev/null
+++ b/Documentation/media/v4l-drivers/qcom_camss_graph.dot
@@ -0,0 +1,41 @@
+digraph board {
+	rankdir=TB
+	n00000001 [label="{{<port0> 0} | msm_csiphy0\n/dev/v4l-subdev0 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
+	n00000001:port1 -> n00000007:port0 [style=dashed]
+	n00000001:port1 -> n0000000a:port0 [style=dashed]
+	n00000004 [label="{{<port0> 0} | msm_csiphy1\n/dev/v4l-subdev1 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
+	n00000004:port1 -> n00000007:port0 [style=dashed]
+	n00000004:port1 -> n0000000a:port0 [style=dashed]
+	n00000007 [label="{{<port0> 0} | msm_csid0\n/dev/v4l-subdev2 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
+	n00000007:port1 -> n0000000d:port0 [style=dashed]
+	n00000007:port1 -> n00000010:port0 [style=dashed]
+	n0000000a [label="{{<port0> 0} | msm_csid1\n/dev/v4l-subdev3 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
+	n0000000a:port1 -> n0000000d:port0 [style=dashed]
+	n0000000a:port1 -> n00000010:port0 [style=dashed]
+	n0000000d [label="{{<port0> 0} | msm_ispif0\n/dev/v4l-subdev4 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
+	n0000000d:port1 -> n00000013:port0 [style=dashed]
+	n0000000d:port1 -> n0000001c:port0 [style=dashed]
+	n0000000d:port1 -> n00000025:port0 [style=dashed]
+	n0000000d:port1 -> n0000002e:port0 [style=dashed]
+	n00000010 [label="{{<port0> 0} | msm_ispif1\n/dev/v4l-subdev5 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
+	n00000010:port1 -> n00000013:port0 [style=dashed]
+	n00000010:port1 -> n0000001c:port0 [style=dashed]
+	n00000010:port1 -> n00000025:port0 [style=dashed]
+	n00000010:port1 -> n0000002e:port0 [style=dashed]
+	n00000013 [label="{{<port0> 0} | msm_vfe0_rdi0\n/dev/v4l-subdev6 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
+	n00000013:port1 -> n00000016 [style=bold]
+	n00000016 [label="msm_vfe0_video0\n/dev/video0", shape=box, style=filled, fillcolor=yellow]
+	n0000001c [label="{{<port0> 0} | msm_vfe0_rdi1\n/dev/v4l-subdev7 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
+	n0000001c:port1 -> n0000001f [style=bold]
+	n0000001f [label="msm_vfe0_video1\n/dev/video1", shape=box, style=filled, fillcolor=yellow]
+	n00000025 [label="{{<port0> 0} | msm_vfe0_rdi2\n/dev/v4l-subdev8 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
+	n00000025:port1 -> n00000028 [style=bold]
+	n00000028 [label="msm_vfe0_video2\n/dev/video2", shape=box, style=filled, fillcolor=yellow]
+	n0000002e [label="{{<port0> 0} | msm_vfe0_pix\n/dev/v4l-subdev9 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
+	n0000002e:port1 -> n00000031 [style=bold]
+	n00000031 [label="msm_vfe0_video3\n/dev/video3", shape=box, style=filled, fillcolor=yellow]
+	n00000057 [label="{{} | ov5645 1-0076\n/dev/v4l-subdev10 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
+	n00000057:port0 -> n00000001:port0 [style=bold]
+	n00000059 [label="{{} | ov5645 1-0074\n/dev/v4l-subdev11 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
+	n00000059:port0 -> n00000004:port0 [style=bold]
+}
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 12/23] media: camms: Add core files
  2017-07-17 10:33 ` [PATCH v3 12/23] media: camms: Add core files Todor Tomov
@ 2017-07-19 10:44   ` Hans Verkuil
  0 siblings, 0 replies; 60+ messages in thread
From: Hans Verkuil @ 2017-07-19 10:44 UTC (permalink / raw)
  To: Todor Tomov, mchehab, hans.verkuil, javier, s.nawrocki,
	sakari.ailus, linux-media, linux-kernel, linux-arm-msm

Hi Todor,

Just one comment:

On 17/07/17 12:33, Todor Tomov wrote:
> These files implement the platform driver code.
> 
> Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
> ---
>  drivers/media/platform/qcom/camss-8x16/camss.c | 705 +++++++++++++++++++++++++
>  drivers/media/platform/qcom/camss-8x16/camss.h |  97 ++++
>  2 files changed, 802 insertions(+)
>  create mode 100644 drivers/media/platform/qcom/camss-8x16/camss.c
>  create mode 100644 drivers/media/platform/qcom/camss-8x16/camss.h
> 
> diff --git a/drivers/media/platform/qcom/camss-8x16/camss.c b/drivers/media/platform/qcom/camss-8x16/camss.c
> new file mode 100644
> index 0000000..097d4ec
> --- /dev/null
> +++ b/drivers/media/platform/qcom/camss-8x16/camss.c
> @@ -0,0 +1,705 @@
> +/*
> + * camss.c
> + *
> + * Qualcomm MSM Camera Subsystem - Core
> + *
> + * Copyright (c) 2015, The Linux Foundation. All rights reserved.
> + * Copyright (C) 2015-2017 Linaro Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +#include <linux/clk.h>
> +#include <linux/media-bus-format.h>
> +#include <linux/media.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +#include <linux/of_graph.h>
> +#include <linux/slab.h>
> +#include <linux/videodev2.h>
> +
> +#include <media/media-device.h>
> +#include <media/v4l2-async.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-mc.h>
> +#include <media/v4l2-fwnode.h>
> +
> +#include "camss.h"
> +
> +static const struct resources csiphy_res[] = {
> +	/* CSIPHY0 */
> +	{
> +		.regulator = { NULL },
> +		.clock = { "camss_top_ahb", "ispif_ahb",
> +			   "camss_ahb", "csiphy0_timer" },
> +		.clock_rate = { 0, 0, 0, 200000000 },
> +		.reg = { "csiphy0", "csiphy0_clk_mux" },
> +		.interrupt = { "csiphy0" }
> +	},
> +
> +	/* CSIPHY1 */
> +	{
> +		.regulator = { NULL },
> +		.clock = { "camss_top_ahb", "ispif_ahb",
> +			   "camss_ahb", "csiphy1_timer" },
> +		.clock_rate = { 0, 0, 0, 200000000 },
> +		.reg = { "csiphy1", "csiphy1_clk_mux" },
> +		.interrupt = { "csiphy1" }
> +	}
> +};
> +
> +static const struct resources csid_res[] = {
> +	/* CSID0 */
> +	{
> +		.regulator = { "vdda" },
> +		.clock = { "camss_top_ahb", "ispif_ahb",
> +			   "csi0_ahb", "camss_ahb",
> +			   "csi0", "csi0_phy", "csi0_pix", "csi0_rdi" },
> +		.clock_rate = { 0, 0, 0, 0, 200000000, 0, 0, 0 },
> +		.reg = { "csid0" },
> +		.interrupt = { "csid0" }
> +	},
> +
> +	/* CSID1 */
> +	{
> +		.regulator = { "vdda" },
> +		.clock = { "camss_top_ahb", "ispif_ahb",
> +			   "csi1_ahb", "camss_ahb",
> +			   "csi1", "csi1_phy", "csi1_pix", "csi1_rdi" },
> +		.clock_rate = { 0, 0, 0, 0, 200000000, 0, 0, 0 },
> +		.reg = { "csid1" },
> +		.interrupt = { "csid1" }
> +	},
> +};
> +
> +static const struct resources_ispif ispif_res = {
> +	/* ISPIF */
> +	.clock = { "camss_top_ahb", "camss_ahb", "ispif_ahb",
> +		   "csi0", "csi0_pix", "csi0_rdi",
> +		   "csi1", "csi1_pix", "csi1_rdi" },
> +	.clock_for_reset = { "camss_vfe_vfe", "camss_csi_vfe" },
> +	.reg = { "ispif", "csi_clk_mux" },
> +	.interrupt = "ispif"
> +
> +};
> +
> +static const struct resources vfe_res = {
> +	/* VFE0 */
> +	.regulator = { NULL },
> +	.clock = { "camss_top_ahb", "camss_vfe_vfe", "camss_csi_vfe",
> +		   "iface", "bus", "camss_ahb" },
> +	.clock_rate = { 0, 320000000, 0, 0, 0, 0, 0, 0 },
> +	.reg = { "vfe0" },
> +	.interrupt = { "vfe0" }
> +};
> +
> +/*
> + * camss_enable_clocks - Enable multiple clocks
> + * @nclocks: Number of clocks in clock array
> + * @clock: Clock array
> + * @dev: Device
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +int camss_enable_clocks(int nclocks, struct clk **clock, struct device *dev)
> +{
> +	int ret;
> +	int i;
> +
> +	for (i = 0; i < nclocks; i++) {
> +		ret = clk_prepare_enable(clock[i]);
> +		if (ret) {
> +			dev_err(dev, "clock enable failed: %d\n", ret);
> +			goto error;
> +		}
> +	}
> +
> +	return 0;
> +
> +error:
> +	for (i--; i >= 0; i--)
> +		clk_disable_unprepare(clock[i]);
> +
> +	return ret;
> +}
> +
> +/*
> + * camss_disable_clocks - Disable multiple clocks
> + * @nclocks: Number of clocks in clock array
> + * @clock: Clock array
> + */
> +void camss_disable_clocks(int nclocks, struct clk **clock)
> +{
> +	int i;
> +
> +	for (i = nclocks - 1; i >= 0; i--)
> +		clk_disable_unprepare(clock[i]);
> +}
> +
> +/*
> + * camss_find_sensor - Find a linked media entity which represents a sensor
> + * @entity: Media entity to start searching from
> + *
> + * Return a pointer to sensor media entity or NULL if not found
> + */
> +static struct media_entity *camss_find_sensor(struct media_entity *entity)
> +{
> +	struct media_pad *pad;
> +
> +	while (1) {
> +		pad = &entity->pads[0];
> +		if (!(pad->flags & MEDIA_PAD_FL_SINK))
> +			return NULL;
> +
> +		pad = media_entity_remote_pad(pad);
> +		if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
> +			return NULL;
> +
> +		entity = pad->entity;
> +
> +		if (entity->function == MEDIA_ENT_F_CAM_SENSOR)
> +			return entity;
> +	}
> +}
> +
> +/*
> + * camss_get_pixel_clock - Get pixel clock rate from sensor
> + * @entity: Media entity in the current pipeline
> + * @pixel_clock: Received pixel clock value
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock)
> +{
> +	struct media_entity *sensor;
> +	struct v4l2_subdev *subdev;
> +	struct v4l2_ext_controls ctrls = { { 0 } };
> +	struct v4l2_ext_control ctrl = { 0 };
> +	int ret;
> +
> +	sensor = camss_find_sensor(entity);
> +	if (!sensor)
> +		return -ENODEV;
> +
> +	subdev = media_entity_to_v4l2_subdev(sensor);
> +
> +	ctrl.id = V4L2_CID_PIXEL_RATE;
> +
> +	ctrls.count = 1;
> +	ctrls.controls = &ctrl;
> +
> +	ret = v4l2_g_ext_ctrls(subdev->ctrl_handler, &ctrls);

Don't use v4l2_g_ext_ctrls for this, it is not meant for use in drivers.
Instead do this:

	struct v4l2_ctrl *ctrl = v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_PIXEL_RATE);

	if (ctrl == NULL)
		return -EINVAL;  // or some other error code

	*pixel_clock = v4l2_ctrl_g_ctrl_int64(ctrl);

> +	if (ret < 0)
> +		return ret;
> +
> +	*pixel_clock = ctrl.value64;
> +
> +	return 0;
> +}

Regards,

	Hans

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
                   ` (22 preceding siblings ...)
  2017-07-17 10:33 ` [PATCH v3 23/23] doc: media/v4l-drivers: Qualcomm Camera Subsystem - Media graph Todor Tomov
@ 2017-07-19 10:54 ` Hans Verkuil
  2017-07-21  7:39   ` Todor Tomov
  2017-07-31  9:20   ` Hans Verkuil
  2017-07-20 15:25 ` Sakari Ailus
  24 siblings, 2 replies; 60+ messages in thread
From: Hans Verkuil @ 2017-07-19 10:54 UTC (permalink / raw)
  To: Todor Tomov, mchehab, hans.verkuil, javier, s.nawrocki,
	sakari.ailus, linux-media, linux-kernel, linux-arm-msm

On 17/07/17 12:33, Todor Tomov wrote:
> This patchset adds basic support for the Qualcomm Camera Subsystem found
> on Qualcomm MSM8916 and APQ8016 processors.
> 
> The driver implements V4L2, Media controller and V4L2 subdev interfaces.
> Camera sensor using V4L2 subdev interface in the kernel is supported.
> 
> The driver is implemented using as a reference the Qualcomm Camera
> Subsystem driver for Android as found in Code Aurora [1].
> 
> The driver is tested on Dragonboard 410C (APQ8016) with one and two
> OV5645 camera sensors. media-ctl [2] and yavta [3] applications were
> used for testing. Also Gstreamer 1.10.4 with v4l2src plugin is supported.
> 
> More information is present in the document added by the third patch.

OK, so this looks pretty good. I have one comment for patch 12/23, and the
dt-bindings need to be acked.

I suggest you make a v3.1 for patch 12/23 and then I'll wait for the binding
ack. Once that's in (and there are no other comments) I will merge this.

Regards,

	Hans

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 13/23] media: camss: Enable building
  2017-07-17 10:33 ` [PATCH v3 13/23] media: camss: Enable building Todor Tomov
@ 2017-07-19 13:17     ` kbuild test robot
  0 siblings, 0 replies; 60+ messages in thread
From: kbuild test robot @ 2017-07-19 13:17 UTC (permalink / raw)
  Cc: kbuild-all, mchehab, hans.verkuil, javier, s.nawrocki,
	sakari.ailus, linux-media, linux-kernel, linux-arm-msm,
	Todor Tomov

[-- Attachment #1: Type: text/plain, Size: 883 bytes --]

Hi Todor,

[auto build test ERROR on linuxtv-media/master]
[also build test ERROR on v4.13-rc1]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Todor-Tomov/Qualcomm-8x16-Camera-Subsystem-driver/20170718-055348
base:   git://linuxtv.org/media_tree.git master
config: i386-allmodconfig (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All errors (new ones prefixed by >>):

   ERROR: "__udivdi3" [fs/ufs/ufs.ko] undefined!
>> ERROR: "__divdi3" [drivers/media/platform/qcom/camss-8x16/qcom-camss.ko] undefined!

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 60158 bytes --]

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 13/23] media: camss: Enable building
@ 2017-07-19 13:17     ` kbuild test robot
  0 siblings, 0 replies; 60+ messages in thread
From: kbuild test robot @ 2017-07-19 13:17 UTC (permalink / raw)
  To: Todor Tomov
  Cc: kbuild-all, mchehab, hans.verkuil, javier, s.nawrocki,
	sakari.ailus, linux-media, linux-kernel, linux-arm-msm,
	Todor Tomov

[-- Attachment #1: Type: text/plain, Size: 883 bytes --]

Hi Todor,

[auto build test ERROR on linuxtv-media/master]
[also build test ERROR on v4.13-rc1]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Todor-Tomov/Qualcomm-8x16-Camera-Subsystem-driver/20170718-055348
base:   git://linuxtv.org/media_tree.git master
config: i386-allmodconfig (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All errors (new ones prefixed by >>):

   ERROR: "__udivdi3" [fs/ufs/ufs.ko] undefined!
>> ERROR: "__divdi3" [drivers/media/platform/qcom/camss-8x16/qcom-camss.ko] undefined!

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 60158 bytes --]

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 22/23] camss: Use optimal clock frequency rates
  2017-07-17 10:33 ` [PATCH v3 22/23] camss: Use optimal clock frequency rates Todor Tomov
@ 2017-07-19 15:59     ` kbuild test robot
  2017-07-20 15:23   ` Sakari Ailus
  1 sibling, 0 replies; 60+ messages in thread
From: kbuild test robot @ 2017-07-19 15:59 UTC (permalink / raw)
  Cc: kbuild-all, mchehab, hans.verkuil, javier, s.nawrocki,
	sakari.ailus, linux-media, linux-kernel, linux-arm-msm,
	Todor Tomov

[-- Attachment #1: Type: text/plain, Size: 971 bytes --]

Hi Todor,

[auto build test ERROR on linuxtv-media/master]
[also build test ERROR on v4.13-rc1]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Todor-Tomov/Qualcomm-8x16-Camera-Subsystem-driver/20170718-055348
base:   git://linuxtv.org/media_tree.git master
config: i386-allmodconfig (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All errors (new ones prefixed by >>):

   ERROR: "__udivdi3" [fs/ufs/ufs.ko] undefined!
>> ERROR: "__udivdi3" [drivers/media/platform/qcom/camss-8x16/qcom-camss.ko] undefined!
   ERROR: "__divdi3" [drivers/media/platform/qcom/camss-8x16/qcom-camss.ko] undefined!

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 60158 bytes --]

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 22/23] camss: Use optimal clock frequency rates
@ 2017-07-19 15:59     ` kbuild test robot
  0 siblings, 0 replies; 60+ messages in thread
From: kbuild test robot @ 2017-07-19 15:59 UTC (permalink / raw)
  To: Todor Tomov
  Cc: kbuild-all, mchehab, hans.verkuil, javier, s.nawrocki,
	sakari.ailus, linux-media, linux-kernel, linux-arm-msm,
	Todor Tomov

[-- Attachment #1: Type: text/plain, Size: 971 bytes --]

Hi Todor,

[auto build test ERROR on linuxtv-media/master]
[also build test ERROR on v4.13-rc1]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Todor-Tomov/Qualcomm-8x16-Camera-Subsystem-driver/20170718-055348
base:   git://linuxtv.org/media_tree.git master
config: i386-allmodconfig (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All errors (new ones prefixed by >>):

   ERROR: "__udivdi3" [fs/ufs/ufs.ko] undefined!
>> ERROR: "__udivdi3" [drivers/media/platform/qcom/camss-8x16/qcom-camss.ko] undefined!
   ERROR: "__divdi3" [drivers/media/platform/qcom/camss-8x16/qcom-camss.ko] undefined!

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 60158 bytes --]

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 04/23] dt-bindings: media: Binding document for Qualcomm Camera subsystem driver
  2017-07-17 10:33 ` [PATCH v3 04/23] dt-bindings: media: Binding document for Qualcomm Camera subsystem driver Todor Tomov
@ 2017-07-20 10:13   ` Sakari Ailus
       [not found]     ` <20170720101345.eovx5ovuxr7sqpea-S+BSfZ9RZZmRSg0ZkenSGLdO1Tsj/99ntUK59QYPAWc@public.gmane.org>
  2017-07-20 10:15   ` Sakari Ailus
  2017-07-24 16:43   ` Rob Herring
  2 siblings, 1 reply; 60+ messages in thread
From: Sakari Ailus @ 2017-07-20 10:13 UTC (permalink / raw)
  To: Todor Tomov
  Cc: mchehab, hans.verkuil, javier, s.nawrocki, linux-media,
	linux-kernel, linux-arm-msm, Rob Herring, devicetree

Hi Todor,

On Mon, Jul 17, 2017 at 01:33:30PM +0300, Todor Tomov wrote:
> Add DT binding document for Qualcomm Camera subsystem driver.
> 
> CC: Rob Herring <robh+dt@kernel.org>
> CC: devicetree@vger.kernel.org
> Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
> ---
>  .../devicetree/bindings/media/qcom,camss.txt       | 191 +++++++++++++++++++++
>  1 file changed, 191 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/qcom,camss.txt
> 
> diff --git a/Documentation/devicetree/bindings/media/qcom,camss.txt b/Documentation/devicetree/bindings/media/qcom,camss.txt
> new file mode 100644
> index 0000000..f698498
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/qcom,camss.txt
> @@ -0,0 +1,191 @@
> +Qualcomm Camera Subsystem
> +
> +* Properties
> +
> +- compatible:
> +	Usage: required
> +	Value type: <stringlist>
> +	Definition: Should contain:
> +		- "qcom,msm8916-camss"
> +- reg:
> +	Usage: required
> +	Value type: <prop-encoded-array>
> +	Definition: Register ranges as listed in the reg-names property.
> +- reg-names:
> +	Usage: required
> +	Value type: <stringlist>
> +	Definition: Should contain the following entries:
> +		- "csiphy0"
> +		- "csiphy0_clk_mux"
> +		- "csiphy1"
> +		- "csiphy1_clk_mux"
> +		- "csid0"
> +		- "csid1"
> +		- "ispif"
> +		- "csi_clk_mux"
> +		- "vfe0"
> +- interrupts:
> +	Usage: required
> +	Value type: <prop-encoded-array>
> +	Definition: Interrupts as listed in the interrupt-names property.
> +- interrupt-names:
> +	Usage: required
> +	Value type: <stringlist>
> +	Definition: Should contain the following entries:
> +		- "csiphy0"
> +		- "csiphy1"
> +		- "csid0"
> +		- "csid1"
> +		- "ispif"
> +		- "vfe0"
> +- power-domains:
> +	Usage: required
> +	Value type: <prop-encoded-array>
> +	Definition: A phandle and power domain specifier pairs to the
> +		    power domain which is responsible for collapsing
> +		    and restoring power to the peripheral.
> +- clocks:
> +	Usage: required
> +	Value type: <prop-encoded-array>
> +	Definition: A list of phandle and clock specifier pairs as listed
> +		    in clock-names property.
> +- clock-names:
> +	Usage: required
> +	Value type: <stringlist>
> +	Definition: Should contain the following entries:
> +                - "camss_top_ahb"
> +                - "ispif_ahb"
> +                - "csiphy0_timer"
> +                - "csiphy1_timer"
> +                - "csi0_ahb"
> +                - "csi0"
> +                - "csi0_phy"
> +                - "csi0_pix"
> +                - "csi0_rdi"
> +                - "csi1_ahb"
> +                - "csi1"
> +                - "csi1_phy"
> +                - "csi1_pix"
> +                - "csi1_rdi"
> +                - "camss_ahb"
> +                - "camss_vfe_vfe"
> +                - "camss_csi_vfe"
> +                - "iface"
> +                - "bus"
> +- vdda-supply:
> +	Usage: required
> +	Value type: <phandle>
> +	Definition: A phandle to voltage supply for CSI2.
> +- iommus:
> +	Usage: required
> +	Value type: <prop-encoded-array>
> +	Definition: A list of phandle and IOMMU specifier pairs.
> +
> +* Nodes
> +
> +- ports:
> +	Usage: required
> +	Definition: As described in video-interfaces.txt in same directory.
> +	Properties:
> +		- reg:
> +			Usage: required
> +			Value type: <u32>
> +			Definition: Selects CSI2 PHY interface - PHY0 or PHY1.
> +	Endpoint node properties:
> +		- clock-lanes:
> +			Usage: required
> +			Value type: <u32>
> +			Definition: The clock lane.
> +		- data-lanes:
> +			Usage: required
> +			Value type: <prop-encoded-array>
> +			Definition: An array of data lanes.
> +
> +* An Example
> +
> +	camss: camss@1b00000 {
> +		compatible = "qcom,msm8916-camss";
> +		reg = <0x1b0ac00 0x200>,
> +			<0x1b00030 0x4>,
> +			<0x1b0b000 0x200>,
> +			<0x1b00038 0x4>,
> +			<0x1b08000 0x100>,
> +			<0x1b08400 0x100>,
> +			<0x1b0a000 0x500>,
> +			<0x1b00020 0x10>,
> +			<0x1b10000 0x1000>;
> +		reg-names = "csiphy0",
> +			"csiphy0_clk_mux",
> +			"csiphy1",
> +			"csiphy1_clk_mux",
> +			"csid0",
> +			"csid1",
> +			"ispif",
> +			"csi_clk_mux",
> +			"vfe0";
> +		interrupts = <GIC_SPI 78 0>,
> +			<GIC_SPI 79 0>,
> +			<GIC_SPI 51 0>,
> +			<GIC_SPI 52 0>,
> +			<GIC_SPI 55 0>,
> +			<GIC_SPI 57 0>;
> +		interrupt-names = "csiphy0",
> +			"csiphy1",
> +			"csid0",
> +			"csid1",
> +			"ispif",
> +			"vfe0";
> +		power-domains = <&gcc VFE_GDSC>;
> +		clocks = <&gcc GCC_CAMSS_TOP_AHB_CLK>,
> +			<&gcc GCC_CAMSS_ISPIF_AHB_CLK>,
> +			<&gcc GCC_CAMSS_CSI0PHYTIMER_CLK>,
> +			<&gcc GCC_CAMSS_CSI1PHYTIMER_CLK>,
> +			<&gcc GCC_CAMSS_CSI0_AHB_CLK>,
> +			<&gcc GCC_CAMSS_CSI0_CLK>,
> +			<&gcc GCC_CAMSS_CSI0PHY_CLK>,
> +			<&gcc GCC_CAMSS_CSI0PIX_CLK>,
> +			<&gcc GCC_CAMSS_CSI0RDI_CLK>,
> +			<&gcc GCC_CAMSS_CSI1_AHB_CLK>,
> +			<&gcc GCC_CAMSS_CSI1_CLK>,
> +			<&gcc GCC_CAMSS_CSI1PHY_CLK>,
> +			<&gcc GCC_CAMSS_CSI1PIX_CLK>,
> +			<&gcc GCC_CAMSS_CSI1RDI_CLK>,
> +			<&gcc GCC_CAMSS_AHB_CLK>,
> +			<&gcc GCC_CAMSS_VFE0_CLK>,
> +			<&gcc GCC_CAMSS_CSI_VFE0_CLK>,
> +			<&gcc GCC_CAMSS_VFE_AHB_CLK>,
> +			<&gcc GCC_CAMSS_VFE_AXI_CLK>;
> +                clock-names = "camss_top_ahb",
> +                        "ispif_ahb",
> +                        "csiphy0_timer",
> +                        "csiphy1_timer",
> +                        "csi0_ahb",
> +                        "csi0",
> +                        "csi0_phy",
> +                        "csi0_pix",
> +                        "csi0_rdi",
> +                        "csi1_ahb",
> +                        "csi1",
> +                        "csi1_phy",
> +                        "csi1_pix",
> +                        "csi1_rdi",
> +                        "camss_ahb",
> +                        "camss_vfe_vfe",
> +                        "camss_csi_vfe",
> +                        "iface",
> +                        "bus";
> +		vdda-supply = <&pm8916_l2>;
> +		iommus = <&apps_iommu 3>;
> +		ports {
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +			port@0 {
> +				reg = <0>;
> +				csiphy0_ep: endpoint {
> +					clock-lanes = <1>;
> +					data-lanes = <0 2>;

Do you support lane mapping? The values suggest "yes". That's something I
could improve in the documentation: if lane mapping isn't supported, then
the clock lane should be 0 and the data lanes from 1 to n.

Is the split of the lanes between the ports static and specific to the
hardware?

> +					remote-endpoint = <&ov5645_ep>;
> +				};
> +			};
> +		};
> +	};

-- 
Regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 04/23] dt-bindings: media: Binding document for Qualcomm Camera subsystem driver
  2017-07-17 10:33 ` [PATCH v3 04/23] dt-bindings: media: Binding document for Qualcomm Camera subsystem driver Todor Tomov
  2017-07-20 10:13   ` Sakari Ailus
@ 2017-07-20 10:15   ` Sakari Ailus
  2017-07-24 16:43   ` Rob Herring
  2 siblings, 0 replies; 60+ messages in thread
From: Sakari Ailus @ 2017-07-20 10:15 UTC (permalink / raw)
  To: Todor Tomov
  Cc: mchehab, hans.verkuil, javier, s.nawrocki, linux-media,
	linux-kernel, linux-arm-msm, Rob Herring, devicetree

On Mon, Jul 17, 2017 at 01:33:30PM +0300, Todor Tomov wrote:
> Add DT binding document for Qualcomm Camera subsystem driver.
> 
> CC: Rob Herring <robh+dt@kernel.org>
> CC: devicetree@vger.kernel.org
> Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
> ---
>  .../devicetree/bindings/media/qcom,camss.txt       | 191 +++++++++++++++++++++
>  1 file changed, 191 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/qcom,camss.txt
> 
> diff --git a/Documentation/devicetree/bindings/media/qcom,camss.txt b/Documentation/devicetree/bindings/media/qcom,camss.txt
> new file mode 100644
> index 0000000..f698498
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/qcom,camss.txt

qcom,camss.txt or qcom,camss-8x16.txt?

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 07/23] media: camss: Add CSIPHY files
  2017-07-17 10:33 ` [PATCH v3 07/23] media: camss: Add CSIPHY files Todor Tomov
@ 2017-07-20 12:15   ` Sakari Ailus
  0 siblings, 0 replies; 60+ messages in thread
From: Sakari Ailus @ 2017-07-20 12:15 UTC (permalink / raw)
  To: Todor Tomov
  Cc: mchehab, hans.verkuil, javier, s.nawrocki, linux-media,
	linux-kernel, linux-arm-msm

Hi Todor,

On Mon, Jul 17, 2017 at 01:33:33PM +0300, Todor Tomov wrote:
> These files control the CSIPHY modules which are responsible for the physical
> layer of the CSI2 receivers.
> 
> Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
> ---
>  .../media/platform/qcom/camss-8x16/camss-csiphy.c  | 816 +++++++++++++++++++++
>  .../media/platform/qcom/camss-8x16/camss-csiphy.h  |  77 ++
>  2 files changed, 893 insertions(+)
>  create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-csiphy.c
>  create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-csiphy.h
> 
> diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c b/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c
> new file mode 100644
> index 0000000..4e7ddc4
> --- /dev/null
> +++ b/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c
> @@ -0,0 +1,816 @@
> +/*
> + * camss-csiphy.c
> + *
> + * Qualcomm MSM Camera Subsystem - CSIPHY Module
> + *
> + * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
> + * Copyright (C) 2016-2017 Linaro Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <media/media-entity.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-subdev.h>
> +
> +#include "camss-csiphy.h"
> +#include "camss.h"
> +
> +#define MSM_CSIPHY_NAME "msm_csiphy"
> +
> +#define CAMSS_CSI_PHY_LNn_CFG2(n)		(0x004 + 0x40 * (n))
> +#define CAMSS_CSI_PHY_LNn_CFG3(n)		(0x008 + 0x40 * (n))
> +#define CAMSS_CSI_PHY_GLBL_RESET		0x140
> +#define CAMSS_CSI_PHY_GLBL_PWR_CFG		0x144
> +#define CAMSS_CSI_PHY_GLBL_IRQ_CMD		0x164
> +#define CAMSS_CSI_PHY_HW_VERSION		0x188
> +#define CAMSS_CSI_PHY_INTERRUPT_STATUSn(n)	(0x18c + 0x4 * (n))
> +#define CAMSS_CSI_PHY_INTERRUPT_MASKn(n)	(0x1ac + 0x4 * (n))
> +#define CAMSS_CSI_PHY_INTERRUPT_CLEARn(n)	(0x1cc + 0x4 * (n))
> +#define CAMSS_CSI_PHY_GLBL_T_INIT_CFG0		0x1ec
> +#define CAMSS_CSI_PHY_T_WAKEUP_CFG0		0x1f4
> +
> +static const struct {
> +	u32 code;
> +	u8 bpp;
> +} csiphy_formats[] = {
> +	{
> +		MEDIA_BUS_FMT_UYVY8_2X8,
> +		8,

I might put these on a single line per array entry. Up to you.

> +	},
> +	{
> +		MEDIA_BUS_FMT_VYUY8_2X8,
> +		8,
> +	},
> +	{
> +		MEDIA_BUS_FMT_YUYV8_2X8,
> +		8,
> +	},
> +	{
> +		MEDIA_BUS_FMT_YVYU8_2X8,
> +		8,
> +	},
> +	{
> +		MEDIA_BUS_FMT_SBGGR8_1X8,
> +		8,
> +	},
> +	{
> +		MEDIA_BUS_FMT_SGBRG8_1X8,
> +		8,
> +	},
> +	{
> +		MEDIA_BUS_FMT_SGRBG8_1X8,
> +		8,
> +	},
> +	{
> +		MEDIA_BUS_FMT_SRGGB8_1X8,
> +		8,
> +	},
> +	{
> +		MEDIA_BUS_FMT_SBGGR10_1X10,
> +		10,
> +	},
> +	{
> +		MEDIA_BUS_FMT_SGBRG10_1X10,
> +		10,
> +	},
> +	{
> +		MEDIA_BUS_FMT_SGRBG10_1X10,
> +		10,
> +	},
> +	{
> +		MEDIA_BUS_FMT_SRGGB10_1X10,
> +		10,
> +	},
> +	{
> +		MEDIA_BUS_FMT_SBGGR12_1X12,
> +		12,
> +	},
> +	{
> +		MEDIA_BUS_FMT_SGBRG12_1X12,
> +		12,
> +	},
> +	{
> +		MEDIA_BUS_FMT_SGRBG12_1X12,
> +		12,
> +	},
> +	{
> +		MEDIA_BUS_FMT_SRGGB12_1X12,
> +		12,
> +	}
> +};
> +
> +/*
> + * csiphy_get_bpp - map media bus format to bits per pixel
> + * @code: media bus format code
> + *
> + * Return number of bits per pixel
> + */
> +static u8 csiphy_get_bpp(u32 code)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(csiphy_formats); i++)
> +		if (code == csiphy_formats[i].code)
> +			break;

I'd return the entry here and have __WARN() and return 8 (or safe value) in
the end for extra safety.

> +
> +	return csiphy_formats[i].bpp;
> +}
> +
> +/*
> + * csiphy_isr - CSIPHY module interrupt handler
> + * @irq: Interrupt line
> + * @dev: CSIPHY device
> + *
> + * Return IRQ_HANDLED on success
> + */
> +static irqreturn_t csiphy_isr(int irq, void *dev)
> +{
> +	struct csiphy_device *csiphy = dev;
> +	u8 i;
> +
> +	for (i = 0; i < 8; i++) {
> +		u8 val = readl_relaxed(csiphy->base +
> +				       CAMSS_CSI_PHY_INTERRUPT_STATUSn(i));
> +		writel_relaxed(val, csiphy->base +
> +			       CAMSS_CSI_PHY_INTERRUPT_CLEARn(i));
> +		writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_IRQ_CMD);
> +		writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_IRQ_CMD);
> +		writel_relaxed(0x0, csiphy->base +
> +			       CAMSS_CSI_PHY_INTERRUPT_CLEARn(i));
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/*
> + * csiphy_reset - Perform software reset on CSIPHY module
> + * @csiphy: CSIPHY device
> + */
> +static void csiphy_reset(struct csiphy_device *csiphy)
> +{
> +	writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET);
> +	usleep_range(5000, 8000);
> +	writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET);
> +}
> +
> +/*
> + * csiphy_set_power - Power on/off CSIPHY module
> + * @sd: CSIPHY V4L2 subdevice
> + * @on: Requested power state
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int csiphy_set_power(struct v4l2_subdev *sd, int on)
> +{
> +	struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
> +	struct device *dev = to_device_index(csiphy, csiphy->id);
> +	int ret;
> +
> +	if (on) {

You could declare ret here.

> +		u8 hw_version;
> +
> +		ret = camss_enable_clocks(csiphy->nclocks, csiphy->clock, dev);
> +		if (ret < 0)
> +			return ret;
> +
> +		enable_irq(csiphy->irq);
> +
> +		csiphy_reset(csiphy);
> +
> +		hw_version = readl_relaxed(csiphy->base +
> +					   CAMSS_CSI_PHY_HW_VERSION);
> +		dev_dbg(dev, "CSIPHY HW Version = 0x%02x\n", hw_version);
> +	} else {
> +		disable_irq(csiphy->irq);
> +
> +		camss_disable_clocks(csiphy->nclocks, csiphy->clock);
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * csiphy_get_lane_mask - Calculate CSI2 lane mask configuration parameter
> + * @lane_cfg - CSI2 lane configuration
> + *
> + * Return lane mask
> + */
> +static u8 csiphy_get_lane_mask(struct csiphy_lanes_cfg *lane_cfg)
> +{
> +	u8 lane_mask;
> +	int i;
> +
> +	lane_mask = 1 << lane_cfg->clk.pos;
> +
> +	for (i = 0; i < lane_cfg->num_data; i++)
> +		lane_mask |= 1 << lane_cfg->data[i].pos;
> +
> +	return lane_mask;
> +}
> +
> +/*
> + * csiphy_settle_cnt_calc - Calculate settle count value
> + * @csiphy: CSIPHY device
> + *
> + * Helper function to calculate settle count value. This is
> + * based on the CSI2 T_hs_settle parameter which in turn
> + * is calculated based on the CSI2 transmitter pixel clock
> + * frequency.
> + *
> + * Return settle count value or 0 if the CSI2 pixel clock
> + * frequency is not available
> + */
> +static u8 csiphy_settle_cnt_calc(struct csiphy_device *csiphy)
> +{
> +	u8 bpp = csiphy_get_bpp(
> +			csiphy->fmt[MSM_CSIPHY_PAD_SINK].code);
> +	u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data;
> +	u32 pixel_clock; /* Hz */
> +	u32 mipi_clock; /* Hz */
> +	u32 ui; /* ps */
> +	u32 timer_period; /* ps */
> +	u32 t_hs_prepare_max; /* ps */
> +	u32 t_hs_prepare_zero_min; /* ps */
> +	u32 t_hs_settle; /* ps */
> +	u8 settle_cnt;
> +	int ret;
> +
> +	ret = camss_get_pixel_clock(&csiphy->subdev.entity, &pixel_clock);
> +	if (ret) {
> +		dev_err(to_device_index(csiphy, csiphy->id),
> +			"Cannot get CSI2 transmitter's pixel clock\n");
> +		return 0;
> +	}
> +
> +	mipi_clock = pixel_clock * bpp / (2 * num_lanes);
> +	ui = 1000000000000 / (mipi_clock * 2);

I don't think you lost anything if you divide by two seprately from
mipi_lock. On suitable mipi_clock you could get overflow to 0.

> +	t_hs_prepare_max = 85000 + 6 * ui;
> +	t_hs_prepare_zero_min = 145000 + 10 * ui;
> +	t_hs_settle = (t_hs_prepare_max + t_hs_prepare_zero_min) / 2;
> +
> +	timer_period = 1000000000000 / csiphy->timer_clk_rate;
> +	settle_cnt = t_hs_settle / timer_period;
> +
> +	return settle_cnt;
> +}
> +
> +/*
> + * csiphy_stream_on - Enable streaming on CSIPHY module
> + * @csiphy: CSIPHY device
> + *
> + * Helper function to enable streaming on CSIPHY module.
> + * Main configuration of CSIPHY module is also done here.
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int csiphy_stream_on(struct csiphy_device *csiphy)
> +{
> +	struct csiphy_config *cfg = &csiphy->cfg;
> +	u8 lane_mask = csiphy_get_lane_mask(&cfg->csi2->lane_cfg);
> +	u8 settle_cnt;
> +	u8 val;
> +	int i = 0;
> +
> +	settle_cnt = csiphy_settle_cnt_calc(csiphy);
> +	if (!settle_cnt)
> +		return -EINVAL;
> +
> +	val = readl_relaxed(csiphy->base_clk_mux);
> +	if (cfg->combo_mode && (lane_mask & 0x18) == 0x18) {
> +		val &= ~0xf0;
> +		val |= cfg->csid_id << 4;
> +	} else {
> +		val &= ~0xf;
> +		val |= cfg->csid_id;
> +	}
> +	writel_relaxed(val, csiphy->base_clk_mux);
> +
> +	writel_relaxed(0x1, csiphy->base +
> +		       CAMSS_CSI_PHY_GLBL_T_INIT_CFG0);
> +	writel_relaxed(0x1, csiphy->base +
> +		       CAMSS_CSI_PHY_T_WAKEUP_CFG0);
> +
> +	val = 0x1;
> +	val |= lane_mask << 1;
> +	writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG);
> +
> +	val = cfg->combo_mode << 4;
> +	writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET);
> +
> +	while (lane_mask) {
> +		if (lane_mask & 0x1) {
> +			writel_relaxed(0x10, csiphy->base +
> +				       CAMSS_CSI_PHY_LNn_CFG2(i));
> +			writel_relaxed(settle_cnt, csiphy->base +
> +				       CAMSS_CSI_PHY_LNn_CFG3(i));
> +			writel_relaxed(0x3f, csiphy->base +
> +				       CAMSS_CSI_PHY_INTERRUPT_MASKn(i));
> +			writel_relaxed(0x3f, csiphy->base +
> +				       CAMSS_CSI_PHY_INTERRUPT_CLEARn(i));
> +		}
> +
> +		lane_mask >>= 1;
> +		i++;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * csiphy_stream_off - Disable streaming on CSIPHY module
> + * @csiphy: CSIPHY device
> + *
> + * Helper function to disable streaming on CSIPHY module
> + */
> +static void csiphy_stream_off(struct csiphy_device *csiphy)
> +{
> +	u8 lane_mask = csiphy_get_lane_mask(&csiphy->cfg.csi2->lane_cfg);
> +	int i = 0;
> +
> +	while (lane_mask) {
> +		if (lane_mask & 0x1)
> +			writel_relaxed(0x0, csiphy->base +
> +				       CAMSS_CSI_PHY_LNn_CFG2(i));
> +
> +		lane_mask >>= 1;
> +		i++;
> +	}
> +
> +	writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG);
> +}
> +
> +
> +/*
> + * csiphy_set_stream - Enable/disable streaming on CSIPHY module
> + * @sd: CSIPHY V4L2 subdevice
> + * @enable: Requested streaming state
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int csiphy_set_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
> +	int ret = 0;
> +
> +	if (enable)
> +		ret = csiphy_stream_on(csiphy);
> +	else
> +		csiphy_stream_off(csiphy);
> +
> +	return ret;
> +}
> +
> +/*
> + * __csiphy_get_format - Get pointer to format structure
> + * @csiphy: CSIPHY device
> + * @cfg: V4L2 subdev pad configuration
> + * @pad: pad from which format is requested
> + * @which: TRY or ACTIVE format
> + *
> + * Return pointer to TRY or ACTIVE format structure
> + */
> +static struct v4l2_mbus_framefmt *
> +__csiphy_get_format(struct csiphy_device *csiphy,
> +		    struct v4l2_subdev_pad_config *cfg,
> +		    unsigned int pad,
> +		    enum v4l2_subdev_format_whence which)
> +{
> +	if (which == V4L2_SUBDEV_FORMAT_TRY)
> +		return v4l2_subdev_get_try_format(&csiphy->subdev, cfg, pad);
> +
> +	return &csiphy->fmt[pad];
> +}
> +
> +/*
> + * csiphy_try_format - Handle try format by pad subdev method
> + * @csiphy: CSIPHY device
> + * @cfg: V4L2 subdev pad configuration
> + * @pad: pad on which format is requested
> + * @fmt: pointer to v4l2 format structure
> + * @which: wanted subdev format
> + */
> +static void csiphy_try_format(struct csiphy_device *csiphy,
> +			      struct v4l2_subdev_pad_config *cfg,
> +			      unsigned int pad,
> +			      struct v4l2_mbus_framefmt *fmt,
> +			      enum v4l2_subdev_format_whence which)
> +{
> +	unsigned int i;
> +
> +	switch (pad) {
> +	case MSM_CSIPHY_PAD_SINK:
> +		/* Set format on sink pad */
> +
> +		for (i = 0; i < ARRAY_SIZE(csiphy_formats); i++)
> +			if (fmt->code == csiphy_formats[i].code)
> +				break;
> +
> +		/* If not found, use UYVY as default */
> +		if (i >= ARRAY_SIZE(csiphy_formats))
> +			fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
> +
> +		fmt->width = clamp_t(u32, fmt->width, 1, 8191);
> +		fmt->height = clamp_t(u32, fmt->height, 1, 8191);
> +
> +		if (fmt->field == V4L2_FIELD_ANY)
> +			fmt->field = V4L2_FIELD_NONE;
> +
> +		fmt->colorspace = V4L2_COLORSPACE_SRGB;
> +
> +		break;
> +
> +	case MSM_CSIPHY_PAD_SRC:
> +		/* Set and return a format same as sink pad */
> +
> +		*fmt = *__csiphy_get_format(csiphy, cfg, MSM_CSID_PAD_SINK,
> +					    which);
> +
> +		break;
> +	}
> +}
> +
> +/*
> + * csiphy_enum_mbus_code - Handle pixel format enumeration
> + * @sd: CSIPHY V4L2 subdevice
> + * @cfg: V4L2 subdev pad configuration
> + * @code: pointer to v4l2_subdev_mbus_code_enum structure
> + * return -EINVAL or zero on success
> + */
> +static int csiphy_enum_mbus_code(struct v4l2_subdev *sd,
> +				 struct v4l2_subdev_pad_config *cfg,
> +				 struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt *format;
> +
> +	if (code->pad == MSM_CSIPHY_PAD_SINK) {
> +		if (code->index >= ARRAY_SIZE(csiphy_formats))
> +			return -EINVAL;
> +
> +		code->code = csiphy_formats[code->index].code;
> +	} else {
> +		if (code->index > 0)
> +			return -EINVAL;
> +
> +		format = __csiphy_get_format(csiphy, cfg, MSM_CSIPHY_PAD_SINK,
> +					     code->which);
> +
> +		code->code = format->code;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * csiphy_enum_frame_size - Handle frame size enumeration
> + * @sd: CSIPHY V4L2 subdevice
> + * @cfg: V4L2 subdev pad configuration
> + * @fse: pointer to v4l2_subdev_frame_size_enum structure
> + * return -EINVAL or zero on success
> + */
> +static int csiphy_enum_frame_size(struct v4l2_subdev *sd,
> +				  struct v4l2_subdev_pad_config *cfg,
> +				  struct v4l2_subdev_frame_size_enum *fse)
> +{
> +	struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt format;
> +
> +	if (fse->index != 0)
> +		return -EINVAL;
> +
> +	format.code = fse->code;
> +	format.width = 1;
> +	format.height = 1;
> +	csiphy_try_format(csiphy, cfg, fse->pad, &format, fse->which);
> +	fse->min_width = format.width;
> +	fse->min_height = format.height;
> +
> +	if (format.code != fse->code)
> +		return -EINVAL;
> +
> +	format.code = fse->code;
> +	format.width = -1;
> +	format.height = -1;
> +	csiphy_try_format(csiphy, cfg, fse->pad, &format, fse->which);
> +	fse->max_width = format.width;
> +	fse->max_height = format.height;
> +
> +	return 0;
> +}
> +
> +/*
> + * csiphy_get_format - Handle get format by pads subdev method
> + * @sd: CSIPHY V4L2 subdevice
> + * @cfg: V4L2 subdev pad configuration
> + * @fmt: pointer to v4l2 subdev format structure
> + *
> + * Return -EINVAL or zero on success
> + */
> +static int csiphy_get_format(struct v4l2_subdev *sd,
> +			     struct v4l2_subdev_pad_config *cfg,
> +			     struct v4l2_subdev_format *fmt)
> +{
> +	struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt *format;
> +
> +	format = __csiphy_get_format(csiphy, cfg, fmt->pad, fmt->which);
> +	if (format == NULL)
> +		return -EINVAL;
> +
> +	fmt->format = *format;
> +
> +	return 0;
> +}
> +
> +/*
> + * csiphy_set_format - Handle set format by pads subdev method
> + * @sd: CSIPHY V4L2 subdevice
> + * @cfg: V4L2 subdev pad configuration
> + * @fmt: pointer to v4l2 subdev format structure
> + *
> + * Return -EINVAL or zero on success
> + */
> +static int csiphy_set_format(struct v4l2_subdev *sd,
> +			     struct v4l2_subdev_pad_config *cfg,
> +			     struct v4l2_subdev_format *fmt)
> +{
> +	struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt *format;
> +
> +	format = __csiphy_get_format(csiphy, cfg, fmt->pad, fmt->which);
> +	if (format == NULL)
> +		return -EINVAL;
> +
> +	csiphy_try_format(csiphy, cfg, fmt->pad, &fmt->format, fmt->which);
> +	*format = fmt->format;
> +
> +	/* Propagate the format from sink to source */
> +	if (fmt->pad == MSM_CSIPHY_PAD_SINK) {
> +		format = __csiphy_get_format(csiphy, cfg, MSM_CSIPHY_PAD_SRC,
> +					     fmt->which);
> +
> +		*format = fmt->format;
> +		csiphy_try_format(csiphy, cfg, MSM_CSIPHY_PAD_SRC, format,
> +				  fmt->which);
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * csiphy_init_formats - Initialize formats on all pads
> + * @sd: CSIPHY V4L2 subdevice
> + * @fh: V4L2 subdev file handle
> + *
> + * Initialize all pad formats with default values.
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int csiphy_init_formats(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_fh *fh)
> +{
> +	struct v4l2_subdev_format format = {
> +		.pad = MSM_CSIPHY_PAD_SINK,
> +		.which = fh ? V4L2_SUBDEV_FORMAT_TRY :
> +			      V4L2_SUBDEV_FORMAT_ACTIVE,
> +		.format = {
> +			.code = MEDIA_BUS_FMT_UYVY8_2X8,
> +			.width = 1920,
> +			.height = 1080
> +		}
> +	};
> +
> +	return csiphy_set_format(sd, fh ? fh->pad : NULL, &format);
> +}
> +
> +/*
> + * msm_csiphy_subdev_init - Initialize CSIPHY device structure and resources
> + * @csiphy: CSIPHY device
> + * @res: CSIPHY module resources table
> + * @id: CSIPHY module id
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +int msm_csiphy_subdev_init(struct csiphy_device *csiphy,
> +			   const struct resources *res, u8 id)
> +{
> +	struct device *dev = to_device_index(csiphy, id);
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct resource *r;
> +	int i;
> +	int ret;
> +
> +	csiphy->id = id;
> +	csiphy->cfg.combo_mode = 0;
> +
> +	/* Memory */
> +
> +	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]);
> +	csiphy->base = devm_ioremap_resource(dev, r);
> +	if (IS_ERR(csiphy->base)) {
> +		dev_err(dev, "could not map memory\n");
> +		return PTR_ERR(csiphy->base);
> +	}
> +
> +	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[1]);
> +	csiphy->base_clk_mux = devm_ioremap_resource(dev, r);
> +	if (IS_ERR(csiphy->base_clk_mux)) {
> +		dev_err(dev, "could not map memory\n");
> +		return PTR_ERR(csiphy->base_clk_mux);
> +	}
> +
> +	/* Interrupt */
> +
> +	r = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
> +					 res->interrupt[0]);
> +	if (!r) {
> +		dev_err(dev, "missing IRQ\n");
> +		return -EINVAL;
> +	}
> +
> +	csiphy->irq = r->start;
> +	snprintf(csiphy->irq_name, sizeof(csiphy->irq_name), "%s_%s%d",
> +		 dev_name(dev), MSM_CSIPHY_NAME, csiphy->id);
> +	ret = devm_request_irq(dev, csiphy->irq, csiphy_isr,
> +			       IRQF_TRIGGER_RISING, csiphy->irq_name, csiphy);
> +	if (ret < 0) {
> +		dev_err(dev, "request_irq failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	disable_irq(csiphy->irq);
> +
> +	/* Clocks */
> +
> +	csiphy->nclocks = 0;
> +	while (res->clock[csiphy->nclocks])
> +		csiphy->nclocks++;
> +
> +	csiphy->clock = devm_kzalloc(dev, csiphy->nclocks *
> +				     sizeof(*csiphy->clock), GFP_KERNEL);
> +	if (!csiphy->clock)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < csiphy->nclocks; i++) {
> +		csiphy->clock[i] = devm_clk_get(dev, res->clock[i]);
> +		if (IS_ERR(csiphy->clock[i]))
> +			return PTR_ERR(csiphy->clock[i]);
> +
> +		if (res->clock_rate[i]) {
> +			long clk_rate = clk_round_rate(csiphy->clock[i],
> +						       res->clock_rate[i]);
> +			if (clk_rate < 0) {
> +				dev_err(to_device_index(csiphy, csiphy->id),
> +					"clk round rate failed: %ld\n",
> +					clk_rate);
> +				return -EINVAL;
> +			}
> +			ret = clk_set_rate(csiphy->clock[i], clk_rate);
> +			if (ret < 0) {
> +				dev_err(to_device_index(csiphy, csiphy->id),
> +					"clk set rate failed: %d\n", ret);
> +				return ret;
> +			}
> +
> +			if (!strcmp(res->clock[i], "csiphy0_timer") ||
> +					!strcmp(res->clock[i], "csiphy1_timer"))
> +				csiphy->timer_clk_rate = clk_rate;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * csiphy_link_setup - Setup CSIPHY connections
> + * @entity: Pointer to media entity structure
> + * @local: Pointer to local pad
> + * @remote: Pointer to remote pad
> + * @flags: Link flags
> + *
> + * Rreturn 0 on success
> + */
> +static int csiphy_link_setup(struct media_entity *entity,
> +			     const struct media_pad *local,
> +			     const struct media_pad *remote, u32 flags)
> +{
> +	if ((local->flags & MEDIA_PAD_FL_SOURCE) &&
> +	    (flags & MEDIA_LNK_FL_ENABLED)) {
> +		struct v4l2_subdev *sd;
> +		struct csiphy_device *csiphy;
> +		struct csid_device *csid;
> +
> +		if (media_entity_remote_pad(local))
> +			return -EBUSY;
> +
> +		sd = media_entity_to_v4l2_subdev(entity);
> +		csiphy = v4l2_get_subdevdata(sd);
> +
> +		sd = media_entity_to_v4l2_subdev(remote->entity);
> +		csid = v4l2_get_subdevdata(sd);
> +
> +		csiphy->cfg.csid_id = csid->id;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_subdev_core_ops csiphy_core_ops = {
> +	.s_power = csiphy_set_power,
> +};
> +
> +static const struct v4l2_subdev_video_ops csiphy_video_ops = {
> +	.s_stream = csiphy_set_stream,
> +};
> +
> +static const struct v4l2_subdev_pad_ops csiphy_pad_ops = {
> +	.enum_mbus_code = csiphy_enum_mbus_code,
> +	.enum_frame_size = csiphy_enum_frame_size,
> +	.get_fmt = csiphy_get_format,
> +	.set_fmt = csiphy_set_format,
> +};
> +
> +static const struct v4l2_subdev_ops csiphy_v4l2_ops = {
> +	.core = &csiphy_core_ops,
> +	.video = &csiphy_video_ops,
> +	.pad = &csiphy_pad_ops,
> +};
> +
> +static const struct v4l2_subdev_internal_ops csiphy_v4l2_internal_ops = {
> +	.open = csiphy_init_formats,
> +};
> +
> +static const struct media_entity_operations csiphy_media_ops = {
> +	.link_setup = csiphy_link_setup,
> +	.link_validate = v4l2_subdev_link_validate,
> +};
> +
> +/*
> + * msm_csiphy_register_entity - Register subdev node for CSIPHY module
> + * @csiphy: CSIPHY device
> + * @v4l2_dev: V4L2 device
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +int msm_csiphy_register_entity(struct csiphy_device *csiphy,
> +			       struct v4l2_device *v4l2_dev)
> +{
> +	struct v4l2_subdev *sd = &csiphy->subdev;
> +	struct media_pad *pads = csiphy->pads;
> +	struct device *dev = to_device_index(csiphy, csiphy->id);
> +	int ret;
> +
> +	v4l2_subdev_init(sd, &csiphy_v4l2_ops);
> +	sd->internal_ops = &csiphy_v4l2_internal_ops;
> +	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +	snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d",
> +		 MSM_CSIPHY_NAME, csiphy->id);
> +	v4l2_set_subdevdata(sd, csiphy);
> +
> +	ret = csiphy_init_formats(sd, NULL);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to init format: %d\n", ret);
> +		return ret;
> +	}
> +
> +	pads[MSM_CSIPHY_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
> +	pads[MSM_CSIPHY_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
> +
> +	sd->entity.function = MEDIA_ENT_F_IO_V4L;
> +	sd->entity.ops = &csiphy_media_ops;
> +	ret = media_entity_pads_init(&sd->entity, MSM_CSIPHY_PADS_NUM, pads);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to init media entity: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = v4l2_device_register_subdev(v4l2_dev, sd);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to register subdev: %d\n", ret);
> +		media_entity_cleanup(&sd->entity);
> +	}
> +
> +	return ret;
> +}
> +
> +/*
> + * msm_csiphy_unregister_entity - Unregister CSIPHY module subdev node
> + * @csiphy: CSIPHY device
> + */
> +void msm_csiphy_unregister_entity(struct csiphy_device *csiphy)
> +{
> +	v4l2_device_unregister_subdev(&csiphy->subdev);
> +}
> diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csiphy.h b/drivers/media/platform/qcom/camss-8x16/camss-csiphy.h
> new file mode 100644
> index 0000000..a6f23ed
> --- /dev/null
> +++ b/drivers/media/platform/qcom/camss-8x16/camss-csiphy.h
> @@ -0,0 +1,77 @@
> +/*
> + * camss-csiphy.h
> + *
> + * Qualcomm MSM Camera Subsystem - CSIPHY Module
> + *
> + * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
> + * Copyright (C) 2016-2017 Linaro Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +#ifndef QC_MSM_CAMSS_CSIPHY_H
> +#define QC_MSM_CAMSS_CSIPHY_H
> +
> +#include <linux/clk.h>
> +#include <media/media-entity.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-mediabus.h>
> +#include <media/v4l2-subdev.h>
> +
> +#define MSM_CSIPHY_PAD_SINK 0
> +#define MSM_CSIPHY_PAD_SRC 1
> +#define MSM_CSIPHY_PADS_NUM 2
> +
> +struct csiphy_lane {
> +	u8 pos;
> +	u8 pol;
> +};
> +
> +struct csiphy_lanes_cfg {
> +	int num_data;
> +	struct csiphy_lane *data;
> +	struct csiphy_lane clk;
> +};
> +
> +struct csiphy_csi2_cfg {
> +	struct csiphy_lanes_cfg lane_cfg;
> +};
> +
> +struct csiphy_config {
> +	u8 combo_mode;
> +	u8 csid_id;
> +	struct csiphy_csi2_cfg *csi2;
> +};
> +
> +struct csiphy_device {
> +	u8 id;
> +	struct v4l2_subdev subdev;
> +	struct media_pad pads[MSM_CSIPHY_PADS_NUM];
> +	void __iomem *base;
> +	void __iomem *base_clk_mux;
> +	u32 irq;
> +	char irq_name[30];
> +	struct clk **clock;
> +	int nclocks;
> +	long timer_clk_rate;

u32? Or can this be more than 32 bits? In that case u64.

> +	struct csiphy_config cfg;
> +	struct v4l2_mbus_framefmt fmt[MSM_CSIPHY_PADS_NUM];
> +};
> +
> +struct resources;
> +
> +int msm_csiphy_subdev_init(struct csiphy_device *csiphy,
> +			   const struct resources *res, u8 id);
> +
> +int msm_csiphy_register_entity(struct csiphy_device *csiphy,
> +			       struct v4l2_device *v4l2_dev);
> +
> +void msm_csiphy_unregister_entity(struct csiphy_device *csiphy);
> +
> +#endif /* QC_MSM_CAMSS_CSIPHY_H */

-- 
Regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 08/23] media: camss: Add CSID files
  2017-07-17 10:33 ` [PATCH v3 08/23] media: camss: Add CSID files Todor Tomov
@ 2017-07-20 14:13   ` Sakari Ailus
  0 siblings, 0 replies; 60+ messages in thread
From: Sakari Ailus @ 2017-07-20 14:13 UTC (permalink / raw)
  To: Todor Tomov
  Cc: mchehab, hans.verkuil, javier, s.nawrocki, linux-media,
	linux-kernel, linux-arm-msm

Hi Todor,

A few comments below. Looks pretty good!

On Mon, Jul 17, 2017 at 01:33:34PM +0300, Todor Tomov wrote:
> These files control the CSID modules which handle the protocol and application
> layer of the CSI2 receivers.
> 
> Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
> ---
>  .../media/platform/qcom/camss-8x16/camss-csid.c    | 1073 ++++++++++++++++++++
>  .../media/platform/qcom/camss-8x16/camss-csid.h    |   82 ++
>  2 files changed, 1155 insertions(+)
>  create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-csid.c
>  create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-csid.h
> 
> diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csid.c b/drivers/media/platform/qcom/camss-8x16/camss-csid.c
> new file mode 100644
> index 0000000..2bf3415
> --- /dev/null
> +++ b/drivers/media/platform/qcom/camss-8x16/camss-csid.c
> @@ -0,0 +1,1073 @@
> +/*
> + * camss-csid.c
> + *
> + * Qualcomm MSM Camera Subsystem - CSID Module
> + *
> + * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
> + * Copyright (C) 2015-2017 Linaro Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +#include <linux/clk.h>
> +#include <linux/completion.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regulator/consumer.h>
> +#include <media/media-entity.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-subdev.h>
> +
> +#include "camss-csid.h"
> +#include "camss.h"
> +
> +#define MSM_CSID_NAME "msm_csid"
> +
> +#define CAMSS_CSID_HW_VERSION		0x0
> +#define CAMSS_CSID_CORE_CTRL_0		0x004
> +#define CAMSS_CSID_CORE_CTRL_1		0x008
> +#define CAMSS_CSID_RST_CMD		0x00c
> +#define CAMSS_CSID_CID_LUT_VC_n(n)	(0x010 + 0x4 * (n))
> +#define CAMSS_CSID_CID_n_CFG(n)		(0x020 + 0x4 * (n))
> +#define CAMSS_CSID_IRQ_CLEAR_CMD	0x060
> +#define CAMSS_CSID_IRQ_MASK		0x064
> +#define CAMSS_CSID_IRQ_STATUS		0x068
> +#define CAMSS_CSID_TG_CTRL		0x0a0
> +#define CAMSS_CSID_TG_VC_CFG		0x0a4
> +#define CAMSS_CSID_TG_VC_CFG_H_BLANKING		0x3ff
> +#define CAMSS_CSID_TG_VC_CFG_V_BLANKING		0x7f
> +#define CAMSS_CSID_TG_DT_n_CGG_0(n)	(0x0ac + 0xc * (n))
> +#define CAMSS_CSID_TG_DT_n_CGG_1(n)	(0x0b0 + 0xc * (n))
> +#define CAMSS_CSID_TG_DT_n_CGG_2(n)	(0x0b4 + 0xc * (n))
> +
> +#define DATA_TYPE_EMBEDDED_DATA_8BIT	0x12
> +#define DATA_TYPE_YUV422_8BIT		0x1e
> +#define DATA_TYPE_RAW_6BIT		0x28
> +#define DATA_TYPE_RAW_8BIT		0x2a
> +#define DATA_TYPE_RAW_10BIT		0x2b
> +#define DATA_TYPE_RAW_12BIT		0x2c
> +
> +#define DECODE_FORMAT_UNCOMPRESSED_6_BIT	0x0
> +#define DECODE_FORMAT_UNCOMPRESSED_8_BIT	0x1
> +#define DECODE_FORMAT_UNCOMPRESSED_10_BIT	0x2
> +#define DECODE_FORMAT_UNCOMPRESSED_12_BIT	0x3
> +
> +#define CSID_RESET_TIMEOUT_MS 500
> +
> +static const struct {
> +	u32 code;
> +	u32 uncompressed;
> +	u8 data_type;
> +	u8 decode_format;
> +	u8 uncompr_bpp;
> +} csid_input_fmts[] = {
> +	{
> +		MEDIA_BUS_FMT_UYVY8_2X8,
> +		MEDIA_BUS_FMT_UYVY8_2X8,
> +		DATA_TYPE_YUV422_8BIT,
> +		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
> +		16
> +	},
> +	{
> +		MEDIA_BUS_FMT_VYUY8_2X8,
> +		MEDIA_BUS_FMT_VYUY8_2X8,
> +		DATA_TYPE_YUV422_8BIT,
> +		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
> +		16
> +	},
> +	{
> +		MEDIA_BUS_FMT_YUYV8_2X8,
> +		MEDIA_BUS_FMT_YUYV8_2X8,
> +		DATA_TYPE_YUV422_8BIT,
> +		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
> +		16
> +	},
> +	{
> +		MEDIA_BUS_FMT_YVYU8_2X8,
> +		MEDIA_BUS_FMT_YVYU8_2X8,
> +		DATA_TYPE_YUV422_8BIT,
> +		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
> +		16
> +	},
> +	{
> +		MEDIA_BUS_FMT_SBGGR8_1X8,
> +		MEDIA_BUS_FMT_SBGGR8_1X8,
> +		DATA_TYPE_RAW_8BIT,
> +		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
> +		8
> +	},
> +	{
> +		MEDIA_BUS_FMT_SGBRG8_1X8,
> +		MEDIA_BUS_FMT_SGBRG8_1X8,
> +		DATA_TYPE_RAW_8BIT,
> +		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
> +		8
> +	},
> +	{
> +		MEDIA_BUS_FMT_SGRBG8_1X8,
> +		MEDIA_BUS_FMT_SGRBG8_1X8,
> +		DATA_TYPE_RAW_8BIT,
> +		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
> +		8
> +	},
> +	{
> +		MEDIA_BUS_FMT_SRGGB8_1X8,
> +		MEDIA_BUS_FMT_SRGGB8_1X8,
> +		DATA_TYPE_RAW_8BIT,
> +		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
> +		8
> +	},
> +	{
> +		MEDIA_BUS_FMT_SBGGR10_1X10,
> +		MEDIA_BUS_FMT_SBGGR10_1X10,
> +		DATA_TYPE_RAW_10BIT,
> +		DECODE_FORMAT_UNCOMPRESSED_10_BIT,
> +		10
> +	},
> +	{
> +		MEDIA_BUS_FMT_SGBRG10_1X10,
> +		MEDIA_BUS_FMT_SGBRG10_1X10,
> +		DATA_TYPE_RAW_10BIT,
> +		DECODE_FORMAT_UNCOMPRESSED_10_BIT,
> +		10
> +	},
> +	{
> +		MEDIA_BUS_FMT_SGRBG10_1X10,
> +		MEDIA_BUS_FMT_SGRBG10_1X10,
> +		DATA_TYPE_RAW_10BIT,
> +		DECODE_FORMAT_UNCOMPRESSED_10_BIT,
> +		10
> +	},
> +	{
> +		MEDIA_BUS_FMT_SRGGB10_1X10,
> +		MEDIA_BUS_FMT_SRGGB10_1X10,
> +		DATA_TYPE_RAW_10BIT,
> +		DECODE_FORMAT_UNCOMPRESSED_10_BIT,
> +		10
> +	},
> +	{
> +		MEDIA_BUS_FMT_SBGGR12_1X12,
> +		MEDIA_BUS_FMT_SBGGR12_1X12,
> +		DATA_TYPE_RAW_12BIT,
> +		DECODE_FORMAT_UNCOMPRESSED_12_BIT,
> +		12
> +	},
> +	{
> +		MEDIA_BUS_FMT_SGBRG12_1X12,
> +		MEDIA_BUS_FMT_SGBRG12_1X12,
> +		DATA_TYPE_RAW_12BIT,
> +		DECODE_FORMAT_UNCOMPRESSED_12_BIT,
> +		12
> +	},
> +	{
> +		MEDIA_BUS_FMT_SGRBG12_1X12,
> +		MEDIA_BUS_FMT_SGRBG12_1X12,
> +		DATA_TYPE_RAW_12BIT,
> +		DECODE_FORMAT_UNCOMPRESSED_12_BIT,
> +		12
> +	},
> +	{
> +		MEDIA_BUS_FMT_SRGGB12_1X12,
> +		MEDIA_BUS_FMT_SRGGB12_1X12,
> +		DATA_TYPE_RAW_12BIT,
> +		DECODE_FORMAT_UNCOMPRESSED_12_BIT,
> +		12
> +	}
> +};
> +
> +/*
> + * csid_isr - CSID module interrupt handler
> + * @irq: Interrupt line
> + * @dev: CSID device
> + *
> + * Return IRQ_HANDLED on success
> + */
> +static irqreturn_t csid_isr(int irq, void *dev)
> +{
> +	struct csid_device *csid = dev;
> +	u32 value;
> +
> +	value = readl_relaxed(csid->base + CAMSS_CSID_IRQ_STATUS);
> +	writel_relaxed(value, csid->base + CAMSS_CSID_IRQ_CLEAR_CMD);
> +
> +	if ((value >> 11) & 0x1)
> +		complete(&csid->reset_complete);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/*
> + * csid_reset - Trigger reset on CSID module and wait to complete
> + * @csid: CSID device
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int csid_reset(struct csid_device *csid)
> +{
> +	unsigned long time;
> +
> +	reinit_completion(&csid->reset_complete);
> +
> +	writel_relaxed(0x7fff, csid->base + CAMSS_CSID_RST_CMD);
> +
> +	time = wait_for_completion_timeout(&csid->reset_complete,
> +		msecs_to_jiffies(CSID_RESET_TIMEOUT_MS));
> +	if (!time) {
> +		dev_err(to_device_index(csid, csid->id),
> +			"CSID reset timeout\n");
> +		return -EIO;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * csid_set_power - Power on/off CSID module
> + * @sd: CSID V4L2 subdevice
> + * @on: Requested power state
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int csid_set_power(struct v4l2_subdev *sd, int on)
> +{
> +	struct csid_device *csid = v4l2_get_subdevdata(sd);
> +	struct device *dev = to_device_index(csid, csid->id);
> +	int ret;
> +
> +	if (on) {
> +		u32 hw_version;
> +
> +		ret = regulator_enable(csid->vdda);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = camss_enable_clocks(csid->nclocks, csid->clock, dev);
> +		if (ret < 0) {
> +			regulator_disable(csid->vdda);
> +			return ret;
> +		}
> +
> +		enable_irq(csid->irq);
> +
> +		ret = csid_reset(csid);
> +		if (ret < 0) {
> +			disable_irq(csid->irq);
> +			camss_disable_clocks(csid->nclocks, csid->clock);
> +			regulator_disable(csid->vdda);
> +			return ret;
> +		}
> +
> +		hw_version = readl_relaxed(csid->base + CAMSS_CSID_HW_VERSION);
> +		dev_dbg(dev, "CSID HW Version = 0x%08x\n", hw_version);
> +	} else {
> +		disable_irq(csid->irq);
> +		camss_disable_clocks(csid->nclocks, csid->clock);
> +		ret = regulator_disable(csid->vdda);
> +	}
> +
> +	return ret;
> +}
> +
> +/*
> + * csid_get_uncompressed - map media bus format to uncompressed media bus format
> + * @code: media bus format code
> + *
> + * Return uncompressed media bus format code
> + */
> +static u32 csid_get_uncompressed(u32 code)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
> +		if (code == csid_input_fmts[i].code)
> +			break;

Same here: return here, and unless a match is found, return a safe value
with __WARN(). The ones below as well.

You could also have a single function returning an entry to the array, and
just use the fields from that entry. Up to you.

> +
> +	return csid_input_fmts[i].uncompressed;
> +}
> +
> +/*
> + * csid_get_data_type - map media bus format to data type
> + * @code: media bus format code
> + *
> + * Return data type code
> + */
> +static u8 csid_get_data_type(u32 code)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
> +		if (code == csid_input_fmts[i].code)
> +			break;
> +
> +	return csid_input_fmts[i].data_type;
> +}
> +
> +/*
> + * csid_get_decode_format - map media bus format to decode format
> + * @code: media bus format code
> + *
> + * Return decode format code
> + */
> +static u8 csid_get_decode_format(u32 code)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
> +		if (code == csid_input_fmts[i].code)
> +			break;
> +
> +	return csid_input_fmts[i].decode_format;
> +}
> +
> +/*
> + * csid_get_bpp - map media bus format to bits per pixel
> + * @code: media bus format code
> + *
> + * Return number of bits per pixel
> + */
> +static u8 csid_get_bpp(u32 code)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
> +		if (code == csid_input_fmts[i].uncompressed)
> +			break;
> +
> +	return csid_input_fmts[i].uncompr_bpp;
> +}
> +
> +/*
> + * csid_set_stream - Enable/disable streaming on CSID module
> + * @sd: CSID V4L2 subdevice
> + * @enable: Requested streaming state
> + *
> + * Main configuration of CSID module is also done here.
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int csid_set_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct csid_device *csid = v4l2_get_subdevdata(sd);
> +	struct csid_testgen_config *tg = &csid->testgen;
> +	u32 val;
> +
> +	if (enable) {
> +		u8 vc = 0; /* Virtual Channel 0 */
> +		u8 cid = vc * 4;

Would you like to comment what is cid? Multiplying by four and then taking
modulo later on looks odd.

> +		u8 dt, dt_shift, df;
> +		int ret;
> +
> +		ret = v4l2_ctrl_handler_setup(&csid->ctrls);
> +		if (ret < 0) {
> +			dev_err(to_device_index(csid, csid->id),
> +				"could not sync v4l2 controls: %d\n", ret);
> +			return ret;
> +		}
> +
> +		if (!tg->enabled &&
> +		    !media_entity_remote_pad(&csid->pads[MSM_CSID_PAD_SINK])) {
> +			return -ENOLINK;
> +		}
> +
> +		dt = csid_get_data_type(csid->fmt[MSM_CSID_PAD_SRC].code);
> +
> +		if (tg->enabled) {
> +			/* Config Test Generator */
> +			u8 bpp = csid_get_bpp(csid->fmt[MSM_CSID_PAD_SRC].code);
> +			u32 num_bytes_per_line =
> +				csid->fmt[MSM_CSID_PAD_SRC].width * bpp / 8;
> +			u32 num_lines = csid->fmt[MSM_CSID_PAD_SRC].height;
> +
> +			/* 31:24 V blank, 23:13 H blank, 3:2 num of active DT */
> +			/* 1:0 VC */
> +			val = ((CAMSS_CSID_TG_VC_CFG_V_BLANKING & 0xff) << 24) |
> +			      ((CAMSS_CSID_TG_VC_CFG_H_BLANKING & 0x7ff) << 13);
> +			writel_relaxed(val, csid->base + CAMSS_CSID_TG_VC_CFG);
> +
> +			/* 28:16 bytes per lines, 12:0 num of lines */
> +			val = ((num_bytes_per_line & 0x1fff) << 16) |
> +			      (num_lines & 0x1fff);
> +			writel_relaxed(val, csid->base +
> +				       CAMSS_CSID_TG_DT_n_CGG_0(0));
> +
> +			/* 5:0 data type */
> +			val = dt;
> +			writel_relaxed(val, csid->base +
> +				       CAMSS_CSID_TG_DT_n_CGG_1(0));
> +
> +			/* 2:0 output test pattern */
> +			val = tg->payload_mode;
> +			writel_relaxed(val, csid->base +
> +				       CAMSS_CSID_TG_DT_n_CGG_2(0));
> +		} else {
> +			struct csid_phy_config *phy = &csid->phy;
> +
> +			val = phy->lane_cnt - 1;
> +			val |= phy->lane_assign << 4;
> +
> +			writel_relaxed(val,
> +				       csid->base + CAMSS_CSID_CORE_CTRL_0);
> +
> +			val = phy->csiphy_id << 17;
> +			val |= 0x9;
> +
> +			writel_relaxed(val,
> +				       csid->base + CAMSS_CSID_CORE_CTRL_1);
> +		}
> +
> +		/* Config LUT */
> +
> +		dt_shift = (cid % 4) * 8;
> +		df = csid_get_decode_format(csid->fmt[MSM_CSID_PAD_SINK].code);
> +
> +		val = readl_relaxed(csid->base + CAMSS_CSID_CID_LUT_VC_n(vc));
> +		val &= ~(0xff << dt_shift);
> +		val |= dt << dt_shift;
> +		writel_relaxed(val, csid->base + CAMSS_CSID_CID_LUT_VC_n(vc));
> +
> +		val = (df << 4) | 0x3;
> +		writel_relaxed(val, csid->base + CAMSS_CSID_CID_n_CFG(cid));
> +
> +		if (tg->enabled) {
> +			val = 0x00a06437;

Ouch. Magic numbers. Any idea what this might be about? It'd be nice to
have that the bits as #defines.

> +			writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL);
> +		}
> +	} else {
> +		if (tg->enabled) {
> +			val = 0x00a06436;
> +			writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * __csid_get_format - Get pointer to format structure
> + * @csid: CSID device
> + * @cfg: V4L2 subdev pad configuration
> + * @pad: pad from which format is requested
> + * @which: TRY or ACTIVE format
> + *
> + * Return pointer to TRY or ACTIVE format structure
> + */
> +static struct v4l2_mbus_framefmt *
> +__csid_get_format(struct csid_device *csid,
> +		  struct v4l2_subdev_pad_config *cfg,
> +		  unsigned int pad,
> +		  enum v4l2_subdev_format_whence which)
> +{
> +	if (which == V4L2_SUBDEV_FORMAT_TRY)
> +		return v4l2_subdev_get_try_format(&csid->subdev, cfg, pad);
> +
> +	return &csid->fmt[pad];
> +}
> +
> +/*
> + * csid_try_format - Handle try format by pad subdev method
> + * @csid: CSID device
> + * @cfg: V4L2 subdev pad configuration
> + * @pad: pad on which format is requested
> + * @fmt: pointer to v4l2 format structure
> + * @which: wanted subdev format
> + */
> +static void csid_try_format(struct csid_device *csid,
> +			    struct v4l2_subdev_pad_config *cfg,
> +			    unsigned int pad,
> +			    struct v4l2_mbus_framefmt *fmt,
> +			    enum v4l2_subdev_format_whence which)
> +{
> +	unsigned int i;
> +
> +	switch (pad) {
> +	case MSM_CSID_PAD_SINK:
> +		/* Set format on sink pad */
> +
> +		for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
> +			if (fmt->code == csid_input_fmts[i].code)
> +				break;
> +
> +		/* If not found, use UYVY as default */
> +		if (i >= ARRAY_SIZE(csid_input_fmts))
> +			fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
> +
> +		fmt->width = clamp_t(u32, fmt->width, 1, 8191);
> +		fmt->height = clamp_t(u32, fmt->height, 1, 8191);
> +
> +		if (fmt->field == V4L2_FIELD_ANY)
> +			fmt->field = V4L2_FIELD_NONE;
> +
> +		break;
> +
> +	case MSM_CSID_PAD_SRC:
> +		if (csid->testgen_mode->cur.val == 0) {
> +			/* Test generator is disabled, keep pad formats */
> +			/* in sync - set and return a format same as sink pad */
> +			struct v4l2_mbus_framefmt format;
> +
> +			format = *__csid_get_format(csid, cfg,
> +						    MSM_CSID_PAD_SINK, which);
> +			format.code = csid_get_uncompressed(format.code);
> +			*fmt = format;
> +		} else {
> +			/* Test generator is enabled, set format on source*/
> +			/* pad to allow test generator usage */
> +
> +			for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
> +				if (csid_input_fmts[i].uncompressed ==
> +								fmt->code)
> +					break;
> +
> +			/* If not found, use UYVY as default */
> +			if (i >= ARRAY_SIZE(csid_input_fmts))
> +				fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
> +
> +			fmt->width = clamp_t(u32, fmt->width, 1, 8191);
> +			fmt->height = clamp_t(u32, fmt->height, 1, 8191);
> +
> +			fmt->field = V4L2_FIELD_NONE;
> +		}
> +		break;
> +	}
> +
> +	fmt->colorspace = V4L2_COLORSPACE_SRGB;
> +}
> +
> +/*
> + * csid_enum_mbus_code - Handle pixel format enumeration
> + * @sd: CSID V4L2 subdevice
> + * @cfg: V4L2 subdev pad configuration
> + * @code: pointer to v4l2_subdev_mbus_code_enum structure
> + * return -EINVAL or zero on success
> + */
> +static int csid_enum_mbus_code(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_pad_config *cfg,
> +			       struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	struct csid_device *csid = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt *format;
> +
> +	if (code->pad == MSM_CSID_PAD_SINK) {
> +		if (code->index >= ARRAY_SIZE(csid_input_fmts))
> +			return -EINVAL;
> +
> +		code->code = csid_input_fmts[code->index].code;
> +	} else {
> +		if (csid->testgen_mode->cur.val == 0) {
> +			if (code->index > 0)
> +				return -EINVAL;
> +
> +			format = __csid_get_format(csid, cfg, MSM_CSID_PAD_SINK,
> +						   code->which);
> +
> +			code->code = csid_get_uncompressed(format->code);
> +		} else {
> +			if (code->index >= ARRAY_SIZE(csid_input_fmts))
> +				return -EINVAL;
> +
> +			code->code = csid_input_fmts[code->index].uncompressed;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * csid_enum_frame_size - Handle frame size enumeration
> + * @sd: CSID V4L2 subdevice
> + * @cfg: V4L2 subdev pad configuration
> + * @fse: pointer to v4l2_subdev_frame_size_enum structure
> + * return -EINVAL or zero on success
> + */
> +static int csid_enum_frame_size(struct v4l2_subdev *sd,
> +				struct v4l2_subdev_pad_config *cfg,
> +				struct v4l2_subdev_frame_size_enum *fse)
> +{
> +	struct csid_device *csid = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt format;
> +
> +	if (fse->index != 0)
> +		return -EINVAL;
> +
> +	format.code = fse->code;
> +	format.width = 1;
> +	format.height = 1;
> +	csid_try_format(csid, cfg, fse->pad, &format, fse->which);
> +	fse->min_width = format.width;
> +	fse->min_height = format.height;
> +
> +	if (format.code != fse->code)
> +		return -EINVAL;
> +
> +	format.code = fse->code;
> +	format.width = -1;
> +	format.height = -1;
> +	csid_try_format(csid, cfg, fse->pad, &format, fse->which);
> +	fse->max_width = format.width;
> +	fse->max_height = format.height;
> +
> +	return 0;
> +}
> +
> +/*
> + * csid_get_format - Handle get format by pads subdev method
> + * @sd: CSID V4L2 subdevice
> + * @cfg: V4L2 subdev pad configuration
> + * @fmt: pointer to v4l2 subdev format structure
> + *
> + * Return -EINVAL or zero on success
> + */
> +static int csid_get_format(struct v4l2_subdev *sd,
> +			   struct v4l2_subdev_pad_config *cfg,
> +			   struct v4l2_subdev_format *fmt)
> +{
> +	struct csid_device *csid = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt *format;
> +
> +	format = __csid_get_format(csid, cfg, fmt->pad, fmt->which);
> +	if (format == NULL)
> +		return -EINVAL;
> +
> +	fmt->format = *format;
> +
> +	return 0;
> +}
> +
> +/*
> + * csid_set_format - Handle set format by pads subdev method
> + * @sd: CSID V4L2 subdevice
> + * @cfg: V4L2 subdev pad configuration
> + * @fmt: pointer to v4l2 subdev format structure
> + *
> + * Return -EINVAL or zero on success
> + */
> +static int csid_set_format(struct v4l2_subdev *sd,
> +			   struct v4l2_subdev_pad_config *cfg,
> +			   struct v4l2_subdev_format *fmt)
> +{
> +	struct csid_device *csid = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt *format;
> +
> +	format = __csid_get_format(csid, cfg, fmt->pad, fmt->which);
> +	if (format == NULL)
> +		return -EINVAL;
> +
> +	csid_try_format(csid, cfg, fmt->pad, &fmt->format, fmt->which);
> +	*format = fmt->format;
> +
> +	/* Propagate the format from sink to source */
> +	if (fmt->pad == MSM_CSID_PAD_SINK) {
> +		format = __csid_get_format(csid, cfg, MSM_CSID_PAD_SRC,
> +					   fmt->which);
> +
> +		*format = fmt->format;
> +		csid_try_format(csid, cfg, MSM_CSID_PAD_SRC, format,
> +				fmt->which);
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * csid_init_formats - Initialize formats on all pads
> + * @sd: CSID V4L2 subdevice
> + * @fh: V4L2 subdev file handle
> + *
> + * Initialize all pad formats with default values.
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int csid_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> +	struct v4l2_subdev_format format = {
> +		.pad = MSM_CSID_PAD_SINK,
> +		.which = fh ? V4L2_SUBDEV_FORMAT_TRY :
> +			      V4L2_SUBDEV_FORMAT_ACTIVE,
> +		.format = {
> +			.code = MEDIA_BUS_FMT_UYVY8_2X8,
> +			.width = 1920,
> +			.height = 1080
> +		}
> +	};
> +
> +	return csid_set_format(sd, fh ? fh->pad : NULL, &format);
> +}
> +
> +static const char * const csid_test_pattern_menu[] = {
> +	"Disabled",
> +	"Incrementing",
> +	"Alternating 55/AA",
> +	"All Zeros",
> +	"All Ones",

Does this refer to bits? Or pixel values?

> +	"Random Data",

Pseudo-random data?

> +};
> +
> +/*
> + * csid_set_test_pattern - Set test generator's pattern mode
> + * @csid: CSID device
> + * @value: desired test pattern mode
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int csid_set_test_pattern(struct csid_device *csid, s32 value)
> +{
> +	struct csid_testgen_config *tg = &csid->testgen;
> +
> +	/* If CSID is linked to CSIPHY, do not allow to enable test generator */
> +	if (value && media_entity_remote_pad(&csid->pads[MSM_CSID_PAD_SINK]))
> +		return -EBUSY;
> +
> +	tg->enabled = !!value;
> +
> +	switch (value) {
> +	case 1:
> +		tg->payload_mode = CSID_PAYLOAD_MODE_INCREMENTING;
> +		break;
> +	case 2:
> +		tg->payload_mode = CSID_PAYLOAD_MODE_ALTERNATING_55_AA;
> +		break;
> +	case 3:
> +		tg->payload_mode = CSID_PAYLOAD_MODE_ALL_ZEROES;
> +		break;
> +	case 4:
> +		tg->payload_mode = CSID_PAYLOAD_MODE_ALL_ONES;
> +		break;
> +	case 5:
> +		tg->payload_mode = CSID_PAYLOAD_MODE_RANDOM;
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * csid_s_ctrl - Handle set control subdev method
> + * @ctrl: pointer to v4l2 control structure
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int csid_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct csid_device *csid = container_of(ctrl->handler,
> +						struct csid_device, ctrls);
> +	int ret = -EINVAL;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_TEST_PATTERN:
> +		ret = csid_set_test_pattern(csid, ctrl->val);
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static const struct v4l2_ctrl_ops csid_ctrl_ops = {
> +	.s_ctrl = csid_s_ctrl,
> +};
> +
> +/*
> + * msm_csid_subdev_init - Initialize CSID device structure and resources
> + * @csid: CSID device
> + * @res: CSID module resources table
> + * @id: CSID module id
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +int msm_csid_subdev_init(struct csid_device *csid,
> +			 const struct resources *res, u8 id)
> +{
> +	struct device *dev = to_device_index(csid, id);
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct resource *r;
> +	int i;
> +	int ret;
> +
> +	csid->id = id;
> +
> +	/* Memory */
> +
> +	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]);
> +	csid->base = devm_ioremap_resource(dev, r);
> +	if (IS_ERR(csid->base)) {
> +		dev_err(dev, "could not map memory\n");
> +		return PTR_ERR(csid->base);
> +	}
> +
> +	/* Interrupt */
> +
> +	r = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
> +					 res->interrupt[0]);
> +	if (!r) {
> +		dev_err(dev, "missing IRQ\n");
> +		return -EINVAL;
> +	}
> +
> +	csid->irq = r->start;
> +	snprintf(csid->irq_name, sizeof(csid->irq_name), "%s_%s%d",
> +		 dev_name(dev), MSM_CSID_NAME, csid->id);
> +	ret = devm_request_irq(dev, csid->irq, csid_isr,
> +		IRQF_TRIGGER_RISING, csid->irq_name, csid);
> +	if (ret < 0) {
> +		dev_err(dev, "request_irq failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	disable_irq(csid->irq);
> +
> +	/* Clocks */
> +
> +	csid->nclocks = 0;
> +	while (res->clock[csid->nclocks])
> +		csid->nclocks++;
> +
> +	csid->clock = devm_kzalloc(dev, csid->nclocks * sizeof(*csid->clock),
> +				    GFP_KERNEL);
> +	if (!csid->clock)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < csid->nclocks; i++) {
> +		csid->clock[i] = devm_clk_get(dev, res->clock[i]);
> +		if (IS_ERR(csid->clock[i]))
> +			return PTR_ERR(csid->clock[i]);
> +
> +		if (res->clock_rate[i]) {
> +			long clk_rate = clk_round_rate(csid->clock[i],
> +						       res->clock_rate[i]);
> +			if (clk_rate < 0) {
> +				dev_err(to_device_index(csid, csid->id),
> +					"clk round rate failed: %ld\n",
> +					clk_rate);
> +				return -EINVAL;
> +			}
> +			ret = clk_set_rate(csid->clock[i], clk_rate);
> +			if (ret < 0) {
> +				dev_err(to_device_index(csid, csid->id),
> +					"clk set rate failed: %d\n", ret);
> +				return ret;
> +			}
> +		}
> +	}
> +
> +	/* Regulator */
> +
> +	csid->vdda = devm_regulator_get(dev, res->regulator[0]);
> +	if (IS_ERR(csid->vdda)) {
> +		dev_err(dev, "could not get regulator\n");
> +		return PTR_ERR(csid->vdda);
> +	}
> +
> +	init_completion(&csid->reset_complete);
> +
> +	return 0;
> +}
> +
> +/*
> + * msm_csid_get_csid_id - Get CSID HW module id
> + * @entity: Pointer to CSID media entity structure
> + * @id: Return CSID HW module id here
> + */
> +void msm_csid_get_csid_id(struct media_entity *entity, u8 *id)
> +{
> +	struct v4l2_subdev *sd;
> +	struct csid_device *csid;
> +
> +	sd = media_entity_to_v4l2_subdev(entity);
> +	csid = v4l2_get_subdevdata(sd);

You could do this in variable declaration.

> +
> +	*id = csid->id;
> +}
> +
> +/*
> + * csid_get_lane_assign - Calculate CSI2 lane assign configuration parameter
> + * @lane_cfg - CSI2 lane configuration
> + *
> + * Return lane assign
> + */
> +static u32 csid_get_lane_assign(struct csiphy_lanes_cfg *lane_cfg)
> +{
> +	u32 lane_assign = 0;
> +	int i;
> +
> +	for (i = 0; i < lane_cfg->num_data; i++)
> +		lane_assign |= lane_cfg->data[i].pos << (i * 4);
> +
> +	return lane_assign;
> +}
> +
> +/*
> + * csid_link_setup - Setup CSID connections
> + * @entity: Pointer to media entity structure
> + * @local: Pointer to local pad
> + * @remote: Pointer to remote pad
> + * @flags: Link flags
> + *
> + * Return 0 on success
> + */
> +static int csid_link_setup(struct media_entity *entity,
> +			   const struct media_pad *local,
> +			   const struct media_pad *remote, u32 flags)
> +{
> +	if (flags & MEDIA_LNK_FL_ENABLED)
> +		if (media_entity_remote_pad(local))
> +			return -EBUSY;
> +
> +	if ((local->flags & MEDIA_PAD_FL_SINK) &&
> +	    (flags & MEDIA_LNK_FL_ENABLED)) {
> +		struct v4l2_subdev *sd;
> +		struct csid_device *csid;
> +		struct csiphy_device *csiphy;
> +		struct csiphy_lanes_cfg *lane_cfg;
> +		struct v4l2_subdev_format format = { 0 };
> +
> +		sd = media_entity_to_v4l2_subdev(entity);
> +		csid = v4l2_get_subdevdata(sd);

Ditto. Up to you.

> +
> +		/* If test generator is enabled */
> +		/* do not allow a link from CSIPHY to CSID */
> +		if (csid->testgen_mode->cur.val != 0)
> +			return -EBUSY;
> +
> +		sd = media_entity_to_v4l2_subdev(remote->entity);
> +		csiphy = v4l2_get_subdevdata(sd);
> +
> +		/* If a sensor is not linked to CSIPHY */
> +		/* do no allow a link from CSIPHY to CSID */
> +		if (!csiphy->cfg.csi2)
> +			return -EPERM;
> +
> +		csid->phy.csiphy_id = csiphy->id;
> +
> +		lane_cfg = &csiphy->cfg.csi2->lane_cfg;
> +		csid->phy.lane_cnt = lane_cfg->num_data;
> +		csid->phy.lane_assign = csid_get_lane_assign(lane_cfg);
> +
> +		/* Reset format on source pad to sink pad format */
> +		format.pad = MSM_CSID_PAD_SRC;
> +		format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +		csid_set_format(&csid->subdev, NULL, &format);
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_subdev_core_ops csid_core_ops = {
> +	.s_power = csid_set_power,
> +};
> +
> +static const struct v4l2_subdev_video_ops csid_video_ops = {
> +	.s_stream = csid_set_stream,
> +};
> +
> +static const struct v4l2_subdev_pad_ops csid_pad_ops = {
> +	.enum_mbus_code = csid_enum_mbus_code,
> +	.enum_frame_size = csid_enum_frame_size,
> +	.get_fmt = csid_get_format,
> +	.set_fmt = csid_set_format,
> +};
> +
> +static const struct v4l2_subdev_ops csid_v4l2_ops = {
> +	.core = &csid_core_ops,
> +	.video = &csid_video_ops,
> +	.pad = &csid_pad_ops,
> +};
> +
> +static const struct v4l2_subdev_internal_ops csid_v4l2_internal_ops = {
> +	.open = csid_init_formats,
> +};
> +
> +static const struct media_entity_operations csid_media_ops = {
> +	.link_setup = csid_link_setup,
> +	.link_validate = v4l2_subdev_link_validate,
> +};
> +
> +/*
> + * msm_csid_register_entity - Register subdev node for CSID module
> + * @csid: CSID device
> + * @v4l2_dev: V4L2 device
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +int msm_csid_register_entity(struct csid_device *csid,
> +			     struct v4l2_device *v4l2_dev)
> +{
> +	struct v4l2_subdev *sd = &csid->subdev;
> +	struct media_pad *pads = csid->pads;
> +	struct device *dev = to_device_index(csid, csid->id);
> +	int ret;
> +
> +	v4l2_subdev_init(sd, &csid_v4l2_ops);
> +	sd->internal_ops = &csid_v4l2_internal_ops;
> +	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +	snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d",
> +		 MSM_CSID_NAME, csid->id);
> +	v4l2_set_subdevdata(sd, csid);
> +
> +	ret = v4l2_ctrl_handler_init(&csid->ctrls, 1);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to init ctrl handler: %d\n", ret);
> +		return ret;
> +	}
> +
> +	csid->testgen_mode = v4l2_ctrl_new_std_menu_items(&csid->ctrls,
> +				&csid_ctrl_ops, V4L2_CID_TEST_PATTERN,
> +				ARRAY_SIZE(csid_test_pattern_menu) - 1, 0, 0,
> +				csid_test_pattern_menu);
> +
> +	if (csid->ctrls.error) {
> +		dev_err(dev, "Failed to init ctrl: %d\n", csid->ctrls.error);
> +		ret = csid->ctrls.error;
> +		goto free_ctrl;
> +	}
> +
> +	csid->subdev.ctrl_handler = &csid->ctrls;
> +
> +	ret = csid_init_formats(sd, NULL);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to init format: %d\n", ret);
> +		goto free_ctrl;
> +	}
> +
> +	pads[MSM_CSID_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
> +	pads[MSM_CSID_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
> +
> +	sd->entity.function = MEDIA_ENT_F_IO_V4L;
> +	sd->entity.ops = &csid_media_ops;
> +	ret = media_entity_pads_init(&sd->entity, MSM_CSID_PADS_NUM, pads);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to init media entity: %d\n", ret);
> +		goto free_ctrl;
> +	}
> +
> +	ret = v4l2_device_register_subdev(v4l2_dev, sd);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to register subdev: %d\n", ret);
> +		goto media_cleanup;
> +	}
> +
> +	return 0;
> +
> +media_cleanup:
> +	media_entity_cleanup(&sd->entity);
> +free_ctrl:
> +	v4l2_ctrl_handler_free(&csid->ctrls);
> +
> +	return ret;
> +}
> +
> +/*
> + * msm_csid_unregister_entity - Unregister CSID module subdev node
> + * @csid: CSID device
> + */
> +void msm_csid_unregister_entity(struct csid_device *csid)
> +{
> +	v4l2_device_unregister_subdev(&csid->subdev);
> +	v4l2_ctrl_handler_free(&csid->ctrls);

media_entity_cleanup() --- although it's currently empty. It's been a
while...

> +}
> diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csid.h b/drivers/media/platform/qcom/camss-8x16/camss-csid.h
> new file mode 100644
> index 0000000..3186c11
> --- /dev/null
> +++ b/drivers/media/platform/qcom/camss-8x16/camss-csid.h
> @@ -0,0 +1,82 @@
> +/*
> + * camss-csid.h
> + *
> + * Qualcomm MSM Camera Subsystem - CSID Module
> + *
> + * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
> + * Copyright (C) 2015-2017 Linaro Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +#ifndef QC_MSM_CAMSS_CSID_H
> +#define QC_MSM_CAMSS_CSID_H
> +
> +#include <linux/clk.h>
> +#include <media/media-entity.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-mediabus.h>
> +#include <media/v4l2-subdev.h>
> +
> +#define MSM_CSID_PAD_SINK 0
> +#define MSM_CSID_PAD_SRC 1
> +#define MSM_CSID_PADS_NUM 2
> +
> +enum csid_payload_mode {
> +	CSID_PAYLOAD_MODE_INCREMENTING = 0,
> +	CSID_PAYLOAD_MODE_ALTERNATING_55_AA = 1,
> +	CSID_PAYLOAD_MODE_ALL_ZEROES = 2,
> +	CSID_PAYLOAD_MODE_ALL_ONES = 3,
> +	CSID_PAYLOAD_MODE_RANDOM = 4,
> +	CSID_PAYLOAD_MODE_USER_SPECIFIED = 5,
> +};
> +
> +struct csid_testgen_config {
> +	u8 enabled;
> +	enum csid_payload_mode payload_mode;
> +};
> +
> +struct csid_phy_config {
> +	u8 csiphy_id;
> +	u8 lane_cnt;
> +	u32 lane_assign;
> +};
> +
> +struct csid_device {
> +	u8 id;
> +	struct v4l2_subdev subdev;
> +	struct media_pad pads[MSM_CSID_PADS_NUM];
> +	void __iomem *base;
> +	u32 irq;
> +	char irq_name[30];
> +	struct clk **clock;
> +	int nclocks;
> +	struct regulator *vdda;
> +	struct completion reset_complete;
> +	struct csid_testgen_config testgen;
> +	struct csid_phy_config phy;
> +	struct v4l2_mbus_framefmt fmt[MSM_CSID_PADS_NUM];
> +	struct v4l2_ctrl_handler ctrls;
> +	struct v4l2_ctrl *testgen_mode;
> +};
> +
> +struct resources;
> +
> +int msm_csid_subdev_init(struct csid_device *csid,
> +			 const struct resources *res, u8 id);
> +
> +int msm_csid_register_entity(struct csid_device *csid,
> +			     struct v4l2_device *v4l2_dev);
> +
> +void msm_csid_unregister_entity(struct csid_device *csid);
> +
> +void msm_csid_get_csid_id(struct media_entity *entity, u8 *id);
> +
> +#endif /* QC_MSM_CAMSS_CSID_H */

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 09/23] media: camss: Add ISPIF files
  2017-07-17 10:33 ` [PATCH v3 09/23] media: camss: Add ISPIF files Todor Tomov
@ 2017-07-20 14:51   ` Sakari Ailus
  0 siblings, 0 replies; 60+ messages in thread
From: Sakari Ailus @ 2017-07-20 14:51 UTC (permalink / raw)
  To: Todor Tomov
  Cc: mchehab, hans.verkuil, javier, s.nawrocki, linux-media,
	linux-kernel, linux-arm-msm

Hi Todor,

On Mon, Jul 17, 2017 at 01:33:35PM +0300, Todor Tomov wrote:
> These files control the ISPIF module which handles the routing of the data
> streams from the CSIDs to the inputs of the VFE.
> 
> Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
> ---
>  .../media/platform/qcom/camss-8x16/camss-ispif.c   | 1127 ++++++++++++++++++++
>  .../media/platform/qcom/camss-8x16/camss-ispif.h   |   85 ++
>  2 files changed, 1212 insertions(+)
>  create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-ispif.c
>  create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-ispif.h
> 
> diff --git a/drivers/media/platform/qcom/camss-8x16/camss-ispif.c b/drivers/media/platform/qcom/camss-8x16/camss-ispif.c
> new file mode 100644
> index 0000000..cc32085
> --- /dev/null
> +++ b/drivers/media/platform/qcom/camss-8x16/camss-ispif.c
> @@ -0,0 +1,1127 @@
> +/*
> + * camss-ispif.c
> + *
> + * Qualcomm MSM Camera Subsystem - ISPIF Module
> + *
> + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
> + * Copyright (C) 2015-2017 Linaro Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +#include <linux/clk.h>
> +#include <linux/completion.h>
> +#include <linux/interrupt.h>
> +#include <linux/iopoll.h>
> +#include <linux/kernel.h>
> +#include <linux/mutex.h>
> +#include <linux/platform_device.h>
> +#include <media/media-entity.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-subdev.h>
> +
> +#include "camss-ispif.h"
> +#include "camss.h"
> +
> +#define MSM_ISPIF_NAME "msm_ispif"
> +
> +#define ispif_line_array(ptr_line)	\
> +	((const struct ispif_line (*)[]) &(ptr_line[-(ptr_line->id)]))

Argh.

> +
> +#define to_ispif(ptr_line)	\
> +	container_of(ispif_line_array(ptr_line), struct ispif_device, ptr_line)
> +
> +#define ISPIF_RST_CMD_0			0x008
> +#define ISPIF_IRQ_GLOBAL_CLEAR_CMD	0x01c
> +#define ISPIF_VFE_m_CTRL_0(m)		(0x200 + 0x200 * (m))
> +#define ISPIF_VFE_m_CTRL_0_PIX0_LINE_BUF_EN	(1 << 6)
> +#define ISPIF_VFE_m_IRQ_MASK_0(m)	(0x208 + 0x200 * (m))
> +#define ISPIF_VFE_m_IRQ_MASK_0_PIX0_ENABLE	0x00001249
> +#define ISPIF_VFE_m_IRQ_MASK_0_PIX0_MASK	0x00001fff
> +#define ISPIF_VFE_m_IRQ_MASK_0_RDI0_ENABLE	0x02492000
> +#define ISPIF_VFE_m_IRQ_MASK_0_RDI0_MASK	0x03ffe000
> +#define ISPIF_VFE_m_IRQ_MASK_1(m)	(0x20c + 0x200 * (m))
> +#define ISPIF_VFE_m_IRQ_MASK_1_PIX1_ENABLE	0x00001249
> +#define ISPIF_VFE_m_IRQ_MASK_1_PIX1_MASK	0x00001fff
> +#define ISPIF_VFE_m_IRQ_MASK_1_RDI1_ENABLE	0x02492000
> +#define ISPIF_VFE_m_IRQ_MASK_1_RDI1_MASK	0x03ffe000
> +#define ISPIF_VFE_m_IRQ_MASK_2(m)	(0x210 + 0x200 * (m))
> +#define ISPIF_VFE_m_IRQ_MASK_2_RDI2_ENABLE	0x00001249
> +#define ISPIF_VFE_m_IRQ_MASK_2_RDI2_MASK	0x00001fff
> +#define ISPIF_VFE_m_IRQ_STATUS_0(m)	(0x21c + 0x200 * (m))
> +#define ISPIF_VFE_m_IRQ_STATUS_0_PIX0_OVERFLOW	(1 << 12)
> +#define ISPIF_VFE_m_IRQ_STATUS_0_RDI0_OVERFLOW	(1 << 25)
> +#define ISPIF_VFE_m_IRQ_STATUS_1(m)	(0x220 + 0x200 * (m))
> +#define ISPIF_VFE_m_IRQ_STATUS_1_PIX1_OVERFLOW	(1 << 12)
> +#define ISPIF_VFE_m_IRQ_STATUS_1_RDI1_OVERFLOW	(1 << 25)
> +#define ISPIF_VFE_m_IRQ_STATUS_2(m)	(0x224 + 0x200 * (m))
> +#define ISPIF_VFE_m_IRQ_STATUS_2_RDI2_OVERFLOW	(1 << 12)
> +#define ISPIF_VFE_m_IRQ_CLEAR_0(m)	(0x230 + 0x200 * (m))
> +#define ISPIF_VFE_m_IRQ_CLEAR_1(m)	(0x234 + 0x200 * (m))
> +#define ISPIF_VFE_m_IRQ_CLEAR_2(m)	(0x238 + 0x200 * (m))
> +#define ISPIF_VFE_m_INTF_INPUT_SEL(m)	(0x244 + 0x200 * (m))
> +#define ISPIF_VFE_m_INTF_CMD_0(m)	(0x248 + 0x200 * (m))
> +#define ISPIF_VFE_m_INTF_CMD_1(m)	(0x24c + 0x200 * (m))
> +#define ISPIF_VFE_m_PIX_INTF_n_CID_MASK(m, n)	\
> +					(0x254 + 0x200 * (m) + 0x4 * (n))
> +#define ISPIF_VFE_m_RDI_INTF_n_CID_MASK(m, n)	\
> +					(0x264 + 0x200 * (m) + 0x4 * (n))
> +#define ISPIF_VFE_m_PIX_INTF_n_STATUS(m, n)	\
> +					(0x2c0 + 0x200 * (m) + 0x4 * (n))
> +#define ISPIF_VFE_m_RDI_INTF_n_STATUS(m, n)	\
> +					(0x2d0 + 0x200 * (m) + 0x4 * (n))
> +
> +#define CSI_PIX_CLK_MUX_SEL		0x000
> +#define CSI_RDI_CLK_MUX_SEL		0x008
> +
> +#define ISPIF_TIMEOUT_SLEEP_US		1000
> +#define ISPIF_TIMEOUT_ALL_US		1000000
> +#define ISPIF_RESET_TIMEOUT_MS		500
> +
> +enum ispif_intf_cmd {
> +	CMD_DISABLE_FRAME_BOUNDARY = 0x0,
> +	CMD_ENABLE_FRAME_BOUNDARY = 0x1,
> +	CMD_DISABLE_IMMEDIATELY = 0x2,
> +	CMD_ALL_DISABLE_IMMEDIATELY = 0xaaaaaaaa,
> +	CMD_ALL_NO_CHANGE = 0xffffffff,
> +};
> +
> +static const u32 ispif_formats[] = {
> +	MEDIA_BUS_FMT_UYVY8_2X8,
> +	MEDIA_BUS_FMT_VYUY8_2X8,
> +	MEDIA_BUS_FMT_YUYV8_2X8,
> +	MEDIA_BUS_FMT_YVYU8_2X8,
> +	MEDIA_BUS_FMT_SBGGR8_1X8,
> +	MEDIA_BUS_FMT_SGBRG8_1X8,
> +	MEDIA_BUS_FMT_SGRBG8_1X8,
> +	MEDIA_BUS_FMT_SRGGB8_1X8,
> +	MEDIA_BUS_FMT_SBGGR10_1X10,
> +	MEDIA_BUS_FMT_SGBRG10_1X10,
> +	MEDIA_BUS_FMT_SGRBG10_1X10,
> +	MEDIA_BUS_FMT_SRGGB10_1X10,
> +	MEDIA_BUS_FMT_SBGGR12_1X12,
> +	MEDIA_BUS_FMT_SGBRG12_1X12,
> +	MEDIA_BUS_FMT_SGRBG12_1X12,
> +	MEDIA_BUS_FMT_SRGGB12_1X12,
> +};
> +
> +/*
> + * ispif_isr - ISPIF module interrupt handler
> + * @irq: Interrupt line
> + * @dev: ISPIF device
> + *
> + * Return IRQ_HANDLED on success
> + */
> +static irqreturn_t ispif_isr(int irq, void *dev)
> +{
> +	struct ispif_device *ispif = dev;
> +	u32 value0, value1, value2;
> +
> +	value0 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_0(0));
> +	value1 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_1(0));
> +	value2 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_2(0));
> +
> +	writel_relaxed(value0, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(0));
> +	writel_relaxed(value1, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(0));
> +	writel_relaxed(value2, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_2(0));
> +
> +	writel(0x1, ispif->base + ISPIF_IRQ_GLOBAL_CLEAR_CMD);
> +
> +	if ((value0 >> 27) & 0x1)
> +		complete(&ispif->reset_complete);
> +
> +	if (unlikely(value0 & ISPIF_VFE_m_IRQ_STATUS_0_PIX0_OVERFLOW))
> +		dev_err_ratelimited(to_device(ispif), "VFE0 pix0 overflow\n");
> +
> +	if (unlikely(value0 & ISPIF_VFE_m_IRQ_STATUS_0_RDI0_OVERFLOW))
> +		dev_err_ratelimited(to_device(ispif), "VFE0 rdi0 overflow\n");
> +
> +	if (unlikely(value1 & ISPIF_VFE_m_IRQ_STATUS_1_PIX1_OVERFLOW))
> +		dev_err_ratelimited(to_device(ispif), "VFE0 pix1 overflow\n");
> +
> +	if (unlikely(value1 & ISPIF_VFE_m_IRQ_STATUS_1_RDI1_OVERFLOW))
> +		dev_err_ratelimited(to_device(ispif), "VFE0 rdi1 overflow\n");
> +
> +	if (unlikely(value2 & ISPIF_VFE_m_IRQ_STATUS_2_RDI2_OVERFLOW))
> +		dev_err_ratelimited(to_device(ispif), "VFE0 rdi2 overflow\n");
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/*
> + * ispif_reset - Trigger reset on ISPIF module and wait to complete
> + * @ispif: ISPIF device
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int ispif_reset(struct ispif_device *ispif)
> +{
> +	unsigned long time;
> +	int ret;
> +
> +	ret = camss_enable_clocks(ispif->nclocks_for_reset,
> +				  ispif->clock_for_reset,
> +				  to_device(ispif));
> +	if (ret < 0)
> +		return ret;
> +
> +	reinit_completion(&ispif->reset_complete);
> +
> +	writel_relaxed(0x000f1fff, ispif->base + ISPIF_RST_CMD_0);

Bit definitions for the register would be nice, rather than a magic value.

> +
> +	time = wait_for_completion_timeout(&ispif->reset_complete,
> +		msecs_to_jiffies(ISPIF_RESET_TIMEOUT_MS));
> +	if (!time) {
> +		dev_err(to_device(ispif), "ISPIF reset timeout\n");
> +		return -EIO;
> +	}
> +
> +	camss_disable_clocks(ispif->nclocks_for_reset, ispif->clock_for_reset);
> +
> +	return 0;
> +}
> +
> +/*
> + * ispif_set_power - Power on/off ISPIF module
> + * @sd: ISPIF V4L2 subdevice
> + * @on: Requested power state
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int ispif_set_power(struct v4l2_subdev *sd, int on)
> +{
> +	struct ispif_line *line = v4l2_get_subdevdata(sd);
> +	struct ispif_device *ispif = to_ispif(line);
> +	struct device *dev = to_device(ispif);
> +	int ret = 0;
> +
> +	mutex_lock(&ispif->power_lock);
> +
> +	if (on) {
> +		if (ispif->power_count) {
> +			/* Power is already on */
> +			ispif->power_count++;
> +			goto exit;
> +		}
> +
> +		ret = camss_enable_clocks(ispif->nclocks, ispif->clock, dev);
> +		if (ret < 0)
> +			goto exit;
> +
> +		ret = ispif_reset(ispif);
> +		if (ret < 0) {
> +			camss_disable_clocks(ispif->nclocks, ispif->clock);
> +			goto exit;
> +		}
> +
> +		ispif->intf_cmd[line->vfe_id].cmd_0 = CMD_ALL_NO_CHANGE;
> +		ispif->intf_cmd[line->vfe_id].cmd_1 = CMD_ALL_NO_CHANGE;
> +
> +		ispif->power_count++;
> +	} else {
> +		if (ispif->power_count == 0) {
> +			dev_err(dev, "ispif power off on power_count == 0\n");
> +			goto exit;
> +		} else if (ispif->power_count == 1) {
> +			camss_disable_clocks(ispif->nclocks, ispif->clock);
> +		}
> +
> +		ispif->power_count--;
> +	}
> +
> +exit:
> +	mutex_unlock(&ispif->power_lock);
> +
> +	return ret;
> +}
> +
> +/*
> + * ispif_select_clk_mux - Select clock for PIX/RDI interface
> + * @ispif: ISPIF device
> + * @intf: VFE interface
> + * @csid: CSID HW module id
> + * @vfe: VFE HW module id
> + * @enable: enable or disable the selected clock
> + */
> +static void ispif_select_clk_mux(struct ispif_device *ispif,
> +				 enum ispif_intf intf, u8 csid,
> +				 u8 vfe, u8 enable)
> +{
> +	u32 val;
> +
> +	switch (intf) {
> +	case PIX0:
> +		val = readl_relaxed(ispif->base_clk_mux + CSI_PIX_CLK_MUX_SEL);
> +		val &= ~(0xf << (vfe * 8));
> +		if (enable)
> +			val |= (csid << (vfe * 8));
> +		writel_relaxed(val, ispif->base_clk_mux + CSI_PIX_CLK_MUX_SEL);
> +		break;
> +
> +	case RDI0:
> +		val = readl_relaxed(ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL);
> +		val &= ~(0xf << (vfe * 12));
> +		if (enable)
> +			val |= (csid << (vfe * 12));
> +		writel_relaxed(val, ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL);
> +		break;
> +
> +	case PIX1:
> +		val = readl_relaxed(ispif->base_clk_mux + CSI_PIX_CLK_MUX_SEL);
> +		val &= ~(0xf << (4 + (vfe * 8)));
> +		if (enable)
> +			val |= (csid << (4 + (vfe * 8)));
> +		writel_relaxed(val, ispif->base_clk_mux + CSI_PIX_CLK_MUX_SEL);
> +		break;
> +
> +	case RDI1:
> +		val = readl_relaxed(ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL);
> +		val &= ~(0xf << (4 + (vfe * 12)));
> +		if (enable)
> +			val |= (csid << (4 + (vfe * 12)));
> +		writel_relaxed(val, ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL);
> +		break;
> +
> +	case RDI2:
> +		val = readl_relaxed(ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL);
> +		val &= ~(0xf << (8 + (vfe * 12)));
> +		if (enable)
> +			val |= (csid << (8 + (vfe * 12)));
> +		writel_relaxed(val, ispif->base_clk_mux + CSI_RDI_CLK_MUX_SEL);
> +		break;
> +	}
> +
> +	mb();
> +}
> +
> +/*
> + * ispif_validate_intf_status - Validate current status of PIX/RDI interface
> + * @ispif: ISPIF device
> + * @intf: VFE interface
> + * @vfe: VFE HW module id
> + *
> + * Return 0 when interface is idle or -EBUSY otherwise
> + */
> +static int ispif_validate_intf_status(struct ispif_device *ispif,
> +				      enum ispif_intf intf, u8 vfe)
> +{
> +	int ret = 0;
> +	u32 val = 0;
> +
> +	switch (intf) {
> +	case PIX0:
> +		val = readl_relaxed(ispif->base +
> +			ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe, 0));
> +		break;
> +	case RDI0:
> +		val = readl_relaxed(ispif->base +
> +			ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 0));
> +		break;
> +	case PIX1:
> +		val = readl_relaxed(ispif->base +
> +			ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe, 1));
> +		break;
> +	case RDI1:
> +		val = readl_relaxed(ispif->base +
> +			ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 1));
> +		break;
> +	case RDI2:
> +		val = readl_relaxed(ispif->base +
> +			ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 2));
> +		break;
> +	}
> +
> +	if ((val & 0xf) != 0xf) {
> +		dev_err(to_device(ispif), "%s: ispif is busy: 0x%x\n",
> +			__func__, val);
> +		ret = -EBUSY;
> +	}
> +
> +	return ret;
> +}
> +
> +/*
> + * ispif_wait_for_stop - Wait for PIX/RDI interface to stop
> + * @ispif: ISPIF device
> + * @intf: VFE interface
> + * @vfe: VFE HW module id
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int ispif_wait_for_stop(struct ispif_device *ispif,
> +			       enum ispif_intf intf, u8 vfe)
> +{
> +	u32 addr = 0;
> +	u32 stop_flag = 0;
> +	int ret;
> +
> +	switch (intf) {
> +	case PIX0:
> +		addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe, 0);
> +		break;
> +	case RDI0:
> +		addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 0);
> +		break;
> +	case PIX1:
> +		addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe, 1);
> +		break;
> +	case RDI1:
> +		addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 1);
> +		break;
> +	case RDI2:
> +		addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe, 2);
> +		break;
> +	}
> +
> +	ret = readl_poll_timeout(ispif->base + addr,
> +				 stop_flag,
> +				 (stop_flag & 0xf) == 0xf,
> +				 ISPIF_TIMEOUT_SLEEP_US,
> +				 ISPIF_TIMEOUT_ALL_US);
> +	if (ret < 0)
> +		dev_err(to_device(ispif), "%s: ispif stop timeout\n",
> +			__func__);
> +
> +	return ret;
> +}
> +
> +/*
> + * ispif_select_csid - Select CSID HW module for input from
> + * @ispif: ISPIF device
> + * @intf: VFE interface
> + * @csid: CSID HW module id
> + * @vfe: VFE HW module id
> + * @enable: enable or disable the selected input
> + */
> +static void ispif_select_csid(struct ispif_device *ispif, enum ispif_intf intf,
> +			      u8 csid, u8 vfe, u8 enable)
> +{
> +	u32 val;
> +
> +	val = readl_relaxed(ispif->base + ISPIF_VFE_m_INTF_INPUT_SEL(vfe));
> +	switch (intf) {
> +	case PIX0:
> +		val &= ~(BIT(1) | BIT(0));
> +		if (enable)
> +			val |= csid;
> +		break;
> +	case RDI0:
> +		val &= ~(BIT(5) | BIT(4));
> +		if (enable)
> +			val |= (csid << 4);
> +		break;
> +	case PIX1:
> +		val &= ~(BIT(9) | BIT(8));
> +		if (enable)
> +			val |= (csid << 8);
> +		break;
> +	case RDI1:
> +		val &= ~(BIT(13) | BIT(12));
> +		if (enable)
> +			val |= (csid << 12);
> +		break;
> +	case RDI2:
> +		val &= ~(BIT(21) | BIT(20));
> +		if (enable)
> +			val |= (csid << 20);
> +		break;
> +	}
> +
> +	writel(val, ispif->base + ISPIF_VFE_m_INTF_INPUT_SEL(vfe));
> +}
> +
> +/*
> + * ispif_select_cid - Enable/disable desired CID
> + * @ispif: ISPIF device
> + * @intf: VFE interface
> + * @cid: desired CID to enable/disable
> + * @vfe: VFE HW module id
> + * @enable: enable or disable the desired CID
> + */
> +static void ispif_select_cid(struct ispif_device *ispif, enum ispif_intf intf,
> +			     u8 cid, u8 vfe, u8 enable)
> +{
> +	u32 cid_mask = 1 << cid;
> +	u32 addr = 0;
> +	u32 val;
> +
> +	switch (intf) {
> +	case PIX0:
> +		addr = ISPIF_VFE_m_PIX_INTF_n_CID_MASK(vfe, 0);
> +		break;
> +	case RDI0:
> +		addr = ISPIF_VFE_m_RDI_INTF_n_CID_MASK(vfe, 0);
> +		break;
> +	case PIX1:
> +		addr = ISPIF_VFE_m_PIX_INTF_n_CID_MASK(vfe, 1);
> +		break;
> +	case RDI1:
> +		addr = ISPIF_VFE_m_RDI_INTF_n_CID_MASK(vfe, 1);
> +		break;
> +	case RDI2:
> +		addr = ISPIF_VFE_m_RDI_INTF_n_CID_MASK(vfe, 2);
> +		break;
> +	}
> +
> +	val = readl_relaxed(ispif->base + addr);
> +	if (enable)
> +		val |= cid_mask;
> +	else
> +		val &= ~cid_mask;
> +
> +	writel(val, ispif->base + addr);
> +}
> +
> +/*
> + * ispif_config_irq - Enable/disable interrupts for PIX/RDI interface
> + * @ispif: ISPIF device
> + * @intf: VFE interface
> + * @vfe: VFE HW module id
> + * @enable: enable or disable
> + */
> +static void ispif_config_irq(struct ispif_device *ispif, enum ispif_intf intf,
> +			     u8 vfe, u8 enable)
> +{
> +	u32 val;
> +
> +	switch (intf) {
> +	case PIX0:
> +		val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_0(vfe));
> +		val &= ~ISPIF_VFE_m_IRQ_MASK_0_PIX0_MASK;
> +		if (enable)
> +			val |= ISPIF_VFE_m_IRQ_MASK_0_PIX0_ENABLE;
> +		writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_0(vfe));
> +		writel_relaxed(ISPIF_VFE_m_IRQ_MASK_0_PIX0_ENABLE,
> +			       ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(vfe));
> +		break;
> +	case RDI0:
> +		val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_0(vfe));
> +		val &= ~ISPIF_VFE_m_IRQ_MASK_0_RDI0_MASK;
> +		if (enable)
> +			val |= ISPIF_VFE_m_IRQ_MASK_0_RDI0_ENABLE;
> +		writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_0(vfe));
> +		writel_relaxed(ISPIF_VFE_m_IRQ_MASK_0_RDI0_ENABLE,
> +			       ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(vfe));
> +		break;
> +	case PIX1:
> +		val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_1(vfe));
> +		val &= ~ISPIF_VFE_m_IRQ_MASK_1_PIX1_MASK;
> +		if (enable)
> +			val |= ISPIF_VFE_m_IRQ_MASK_1_PIX1_ENABLE;
> +		writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_1(vfe));
> +		writel_relaxed(ISPIF_VFE_m_IRQ_MASK_1_PIX1_ENABLE,
> +			       ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(vfe));
> +		break;
> +	case RDI1:
> +		val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_1(vfe));
> +		val &= ~ISPIF_VFE_m_IRQ_MASK_1_RDI1_MASK;
> +		if (enable)
> +			val |= ISPIF_VFE_m_IRQ_MASK_1_RDI1_ENABLE;
> +		writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_1(vfe));
> +		writel_relaxed(ISPIF_VFE_m_IRQ_MASK_1_RDI1_ENABLE,
> +			       ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(vfe));
> +		break;
> +	case RDI2:
> +		val = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_MASK_2(vfe));
> +		val &= ~ISPIF_VFE_m_IRQ_MASK_2_RDI2_MASK;
> +		if (enable)
> +			val |= ISPIF_VFE_m_IRQ_MASK_2_RDI2_ENABLE;
> +		writel_relaxed(val, ispif->base + ISPIF_VFE_m_IRQ_MASK_2(vfe));
> +		writel_relaxed(ISPIF_VFE_m_IRQ_MASK_2_RDI2_ENABLE,
> +			       ispif->base + ISPIF_VFE_m_IRQ_CLEAR_2(vfe));
> +		break;
> +	}
> +
> +	writel(0x1, ispif->base + ISPIF_IRQ_GLOBAL_CLEAR_CMD);
> +}
> +
> +/*
> + * ispif_set_intf_cmd - Set command to enable/disable interface
> + * @ispif: ISPIF device
> + * @cmd: interface command
> + * @intf: VFE interface
> + * @vfe: VFE HW module id
> + * @vc: virtual channel
> + */
> +static void ispif_set_intf_cmd(struct ispif_device *ispif, u8 cmd,
> +			       enum ispif_intf intf, u8 vfe, u8 vc)
> +{
> +	u32 *val;
> +
> +	if (intf == RDI2) {
> +		val = &ispif->intf_cmd[vfe].cmd_1;
> +		*val &= ~(0x3 << (vc * 2 + 8));
> +		*val |= (cmd << (vc * 2 + 8));
> +		wmb();
> +		writel_relaxed(*val, ispif->base + ISPIF_VFE_m_INTF_CMD_1(vfe));
> +		wmb();
> +	} else {
> +		val = &ispif->intf_cmd[vfe].cmd_0;
> +		*val &= ~(0x3 << (vc * 2 + intf * 8));
> +		*val |= (cmd << (vc * 2 + intf * 8));
> +		wmb();
> +		writel_relaxed(*val, ispif->base + ISPIF_VFE_m_INTF_CMD_0(vfe));
> +		wmb();
> +	}
> +}
> +
> +/*
> + * ispif_set_stream - Enable/disable streaming on ISPIF module
> + * @sd: ISPIF V4L2 subdevice
> + * @enable: Requested streaming state
> + *
> + * Main configuration of ISPIF module is also done here.
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int ispif_set_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct ispif_line *line = v4l2_get_subdevdata(sd);
> +	struct ispif_device *ispif = to_ispif(line);
> +	enum ispif_intf intf = line->interface;
> +	u8 csid = line->csid_id;
> +	u8 vfe = line->vfe_id;
> +	u8 vc = 0; /* Virtual Channel 0 */
> +	u8 cid = vc * 4;
> +	int ret;
> +
> +	if (enable) {
> +		if (!media_entity_remote_pad(
> +					&line->pads[MSM_ISPIF_PAD_SINK])) {

Extra braces.

> +			return -ENOLINK;
> +		}
> +
> +		/* Config */
> +
> +		mutex_lock(&ispif->config_lock);
> +		ispif_select_clk_mux(ispif, intf, csid, vfe, 1);
> +
> +		ret = ispif_validate_intf_status(ispif, intf, vfe);
> +		if (ret < 0) {
> +			mutex_unlock(&ispif->config_lock);
> +			return ret;
> +		}
> +
> +		ispif_select_csid(ispif, intf, csid, vfe, 1);
> +		ispif_select_cid(ispif, intf, cid, vfe, 1);
> +		ispif_config_irq(ispif, intf, vfe, 1);
> +		ispif_set_intf_cmd(ispif, CMD_ENABLE_FRAME_BOUNDARY,
> +				   intf, vfe, vc);
> +	} else {
> +		mutex_lock(&ispif->config_lock);
> +		ispif_set_intf_cmd(ispif, CMD_DISABLE_FRAME_BOUNDARY,
> +				   intf, vfe, vc);
> +		mutex_unlock(&ispif->config_lock);
> +
> +		ret = ispif_wait_for_stop(ispif, intf, vfe);
> +		if (ret < 0)
> +			return ret;
> +
> +		mutex_lock(&ispif->config_lock);
> +		ispif_config_irq(ispif, intf, vfe, 0);
> +		ispif_select_cid(ispif, intf, cid, vfe, 0);
> +		ispif_select_csid(ispif, intf, csid, vfe, 0);
> +		ispif_select_clk_mux(ispif, intf, csid, vfe, 0);
> +	}
> +
> +	mutex_unlock(&ispif->config_lock);
> +
> +	return 0;
> +}
> +
> +/*
> + * __ispif_get_format - Get pointer to format structure
> + * @ispif: ISPIF line
> + * @cfg: V4L2 subdev pad configuration
> + * @pad: pad from which format is requested
> + * @which: TRY or ACTIVE format
> + *
> + * Return pointer to TRY or ACTIVE format structure
> + */
> +static struct v4l2_mbus_framefmt *
> +__ispif_get_format(struct ispif_line *line,
> +		   struct v4l2_subdev_pad_config *cfg,
> +		   unsigned int pad,
> +		   enum v4l2_subdev_format_whence which)
> +{
> +	if (which == V4L2_SUBDEV_FORMAT_TRY)
> +		return v4l2_subdev_get_try_format(&line->subdev, cfg, pad);
> +
> +	return &line->fmt[pad];
> +}
> +
> +/*
> + * ispif_try_format - Handle try format by pad subdev method
> + * @ispif: ISPIF line
> + * @cfg: V4L2 subdev pad configuration
> + * @pad: pad on which format is requested
> + * @fmt: pointer to v4l2 format structure
> + * @which: wanted subdev format
> + */
> +static void ispif_try_format(struct ispif_line *line,
> +			     struct v4l2_subdev_pad_config *cfg,
> +			     unsigned int pad,
> +			     struct v4l2_mbus_framefmt *fmt,
> +			     enum v4l2_subdev_format_whence which)
> +{
> +	unsigned int i;
> +
> +	switch (pad) {
> +	case MSM_ISPIF_PAD_SINK:
> +		/* Set format on sink pad */
> +
> +		for (i = 0; i < ARRAY_SIZE(ispif_formats); i++)
> +			if (fmt->code == ispif_formats[i])
> +				break;
> +
> +		/* If not found, use UYVY as default */
> +		if (i >= ARRAY_SIZE(ispif_formats))
> +			fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
> +
> +		fmt->width = clamp_t(u32, fmt->width, 1, 8191);
> +		fmt->height = clamp_t(u32, fmt->height, 1, 8191);
> +
> +		if (fmt->field == V4L2_FIELD_ANY)
> +			fmt->field = V4L2_FIELD_NONE;
> +
> +		break;
> +
> +	case MSM_ISPIF_PAD_SRC:
> +		/* Set and return a format same as sink pad */
> +
> +		*fmt = *__ispif_get_format(line, cfg, MSM_ISPIF_PAD_SINK,
> +					   which);
> +
> +		break;
> +	}
> +
> +	fmt->colorspace = V4L2_COLORSPACE_SRGB;
> +}
> +
> +/*
> + * ispif_enum_mbus_code - Handle pixel format enumeration
> + * @sd: ISPIF V4L2 subdevice
> + * @cfg: V4L2 subdev pad configuration
> + * @code: pointer to v4l2_subdev_mbus_code_enum structure
> + * return -EINVAL or zero on success
> + */
> +static int ispif_enum_mbus_code(struct v4l2_subdev *sd,
> +				struct v4l2_subdev_pad_config *cfg,
> +				struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	struct ispif_line *line = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt *format;
> +
> +	if (code->pad == MSM_ISPIF_PAD_SINK) {
> +		if (code->index >= ARRAY_SIZE(ispif_formats))
> +			return -EINVAL;
> +
> +		code->code = ispif_formats[code->index];
> +	} else {
> +		if (code->index > 0)
> +			return -EINVAL;
> +
> +		format = __ispif_get_format(line, cfg, MSM_ISPIF_PAD_SINK,
> +					    code->which);
> +
> +		code->code = format->code;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * ispif_enum_frame_size - Handle frame size enumeration
> + * @sd: ISPIF V4L2 subdevice
> + * @cfg: V4L2 subdev pad configuration
> + * @fse: pointer to v4l2_subdev_frame_size_enum structure
> + * return -EINVAL or zero on success
> + */
> +static int ispif_enum_frame_size(struct v4l2_subdev *sd,
> +				 struct v4l2_subdev_pad_config *cfg,
> +				 struct v4l2_subdev_frame_size_enum *fse)
> +{
> +	struct ispif_line *line = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt format;
> +
> +	if (fse->index != 0)
> +		return -EINVAL;
> +
> +	format.code = fse->code;
> +	format.width = 1;
> +	format.height = 1;
> +	ispif_try_format(line, cfg, fse->pad, &format, fse->which);
> +	fse->min_width = format.width;
> +	fse->min_height = format.height;
> +
> +	if (format.code != fse->code)
> +		return -EINVAL;
> +
> +	format.code = fse->code;
> +	format.width = -1;
> +	format.height = -1;
> +	ispif_try_format(line, cfg, fse->pad, &format, fse->which);
> +	fse->max_width = format.width;
> +	fse->max_height = format.height;
> +
> +	return 0;
> +}
> +
> +/*
> + * ispif_get_format - Handle get format by pads subdev method
> + * @sd: ISPIF V4L2 subdevice
> + * @cfg: V4L2 subdev pad configuration
> + * @fmt: pointer to v4l2 subdev format structure
> + *
> + * Return -EINVAL or zero on success
> + */
> +static int ispif_get_format(struct v4l2_subdev *sd,
> +			    struct v4l2_subdev_pad_config *cfg,
> +			    struct v4l2_subdev_format *fmt)
> +{
> +	struct ispif_line *line = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt *format;
> +
> +	format = __ispif_get_format(line, cfg, fmt->pad, fmt->which);
> +	if (format == NULL)
> +		return -EINVAL;
> +
> +	fmt->format = *format;
> +
> +	return 0;
> +}
> +
> +/*
> + * ispif_set_format - Handle set format by pads subdev method
> + * @sd: ISPIF V4L2 subdevice
> + * @cfg: V4L2 subdev pad configuration
> + * @fmt: pointer to v4l2 subdev format structure
> + *
> + * Return -EINVAL or zero on success
> + */
> +static int ispif_set_format(struct v4l2_subdev *sd,
> +			    struct v4l2_subdev_pad_config *cfg,
> +			    struct v4l2_subdev_format *fmt)
> +{
> +	struct ispif_line *line = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt *format;
> +
> +	format = __ispif_get_format(line, cfg, fmt->pad, fmt->which);
> +	if (format == NULL)
> +		return -EINVAL;
> +
> +	ispif_try_format(line, cfg, fmt->pad, &fmt->format, fmt->which);
> +	*format = fmt->format;
> +
> +	/* Propagate the format from sink to source */
> +	if (fmt->pad == MSM_ISPIF_PAD_SINK) {
> +		format = __ispif_get_format(line, cfg, MSM_ISPIF_PAD_SRC,
> +					    fmt->which);
> +
> +		*format = fmt->format;
> +		ispif_try_format(line, cfg, MSM_ISPIF_PAD_SRC, format,
> +				 fmt->which);
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * ispif_init_formats - Initialize formats on all pads
> + * @sd: ISPIF V4L2 subdevice
> + * @fh: V4L2 subdev file handle
> + *
> + * Initialize all pad formats with default values.
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int ispif_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> +	struct v4l2_subdev_format format = {
> +		.pad = MSM_ISPIF_PAD_SINK,
> +		.which = fh ? V4L2_SUBDEV_FORMAT_TRY :
> +			      V4L2_SUBDEV_FORMAT_ACTIVE,
> +		.format = {
> +			.code = MEDIA_BUS_FMT_UYVY8_2X8,
> +			.width = 1920,
> +			.height = 1080
> +		}
> +	};
> +
> +	return ispif_set_format(sd, fh ? fh->pad : NULL, &format);
> +}
> +
> +/*
> + * msm_ispif_subdev_init - Initialize ISPIF device structure and resources
> + * @ispif: ISPIF device
> + * @res: ISPIF module resources table
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +int msm_ispif_subdev_init(struct ispif_device *ispif,
> +			  const struct resources_ispif *res)
> +{
> +	struct device *dev = to_device(ispif);
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct resource *r;
> +	int i;
> +	int ret;
> +
> +	for (i = 0; i < ARRAY_SIZE(ispif->line); i++)
> +		ispif->line[i].id = i;
> +
> +	mutex_init(&ispif->power_lock);
> +	ispif->power_count = 0;
> +
> +	mutex_init(&ispif->config_lock);
> +
> +	/* Memory */
> +
> +	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]);
> +	ispif->base = devm_ioremap_resource(dev, r);
> +	if (IS_ERR(ispif->base)) {
> +		dev_err(dev, "could not map memory\n");
> +		return PTR_ERR(ispif->base);
> +	}
> +
> +	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[1]);
> +	ispif->base_clk_mux = devm_ioremap_resource(dev, r);
> +	if (IS_ERR(ispif->base_clk_mux)) {
> +		dev_err(dev, "could not map memory\n");
> +		return PTR_ERR(ispif->base_clk_mux);
> +	}
> +
> +	/* Interrupt */
> +
> +	r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res->interrupt);
> +
> +	if (!r) {
> +		dev_err(dev, "missing IRQ\n");
> +		return -EINVAL;
> +	}
> +
> +	ispif->irq = r->start;
> +	snprintf(ispif->irq_name, sizeof(ispif->irq_name), "%s_%s",
> +		 dev_name(dev), MSM_ISPIF_NAME);
> +	ret = devm_request_irq(dev, ispif->irq, ispif_isr,
> +			       IRQF_TRIGGER_RISING, ispif->irq_name, ispif);
> +	if (ret < 0) {
> +		dev_err(dev, "request_irq failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* Clocks */
> +
> +	ispif->nclocks = 0;
> +	while (res->clock[ispif->nclocks])
> +		ispif->nclocks++;
> +
> +	ispif->clock = devm_kzalloc(dev, ispif->nclocks * sizeof(*ispif->clock),
> +				    GFP_KERNEL);
> +	if (!ispif->clock)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < ispif->nclocks; i++) {
> +		ispif->clock[i] = devm_clk_get(dev, res->clock[i]);
> +		if (IS_ERR(ispif->clock[i]))
> +			return PTR_ERR(ispif->clock[i]);
> +	}
> +
> +	ispif->nclocks_for_reset = 0;
> +	while (res->clock_for_reset[ispif->nclocks_for_reset])
> +		ispif->nclocks_for_reset++;
> +
> +	ispif->clock_for_reset = devm_kzalloc(dev, ispif->nclocks_for_reset *
> +			sizeof(*ispif->clock_for_reset), GFP_KERNEL);
> +	if (!ispif->clock_for_reset)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < ispif->nclocks_for_reset; i++) {
> +		ispif->clock_for_reset[i] = devm_clk_get(dev,
> +						res->clock_for_reset[i]);
> +		if (IS_ERR(ispif->clock_for_reset[i]))
> +			return PTR_ERR(ispif->clock_for_reset[i]);
> +	}
> +
> +	init_completion(&ispif->reset_complete);
> +
> +	return 0;
> +}
> +
> +/*
> + * ispif_get_intf - Get ISPIF interface to use by VFE line id
> + * @line_id: VFE line id that the ISPIF line is connected to
> + *
> + * Return ISPIF interface to use
> + */
> +static enum ispif_intf ispif_get_intf(enum vfe_line_id line_id)
> +{
> +	switch (line_id) {
> +	case (VFE_LINE_RDI0):
> +		return RDI0;
> +	case (VFE_LINE_RDI1):
> +		return RDI1;
> +	case (VFE_LINE_RDI2):
> +		return RDI2;
> +	default:
> +		return RDI0;
> +	}
> +}
> +
> +/*
> + * ispif_link_setup - Setup ISPIF connections
> + * @entity: Pointer to media entity structure
> + * @local: Pointer to local pad
> + * @remote: Pointer to remote pad
> + * @flags: Link flags
> + *
> + * Return 0 on success
> + */
> +static int ispif_link_setup(struct media_entity *entity,
> +			    const struct media_pad *local,
> +			    const struct media_pad *remote, u32 flags)
> +{
> +	if (flags & MEDIA_LNK_FL_ENABLED) {
> +		if (media_entity_remote_pad(local))
> +			return -EBUSY;
> +
> +		if (local->flags & MEDIA_PAD_FL_SINK) {
> +			struct v4l2_subdev *sd;
> +			struct ispif_line *line;
> +
> +			sd = media_entity_to_v4l2_subdev(entity);
> +			line = v4l2_get_subdevdata(sd);
> +
> +			msm_csid_get_csid_id(remote->entity, &line->csid_id);
> +		} else { /* MEDIA_PAD_FL_SOURCE */
> +			struct v4l2_subdev *sd;
> +			struct ispif_line *line;
> +			enum vfe_line_id id;
> +
> +			sd = media_entity_to_v4l2_subdev(entity);
> +			line = v4l2_get_subdevdata(sd);
> +
> +			msm_vfe_get_vfe_id(remote->entity, &line->vfe_id);
> +			msm_vfe_get_vfe_line_id(remote->entity, &id);
> +			line->interface = ispif_get_intf(id);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_subdev_core_ops ispif_core_ops = {
> +	.s_power = ispif_set_power,
> +};
> +
> +static const struct v4l2_subdev_video_ops ispif_video_ops = {
> +	.s_stream = ispif_set_stream,
> +};
> +
> +static const struct v4l2_subdev_pad_ops ispif_pad_ops = {
> +	.enum_mbus_code = ispif_enum_mbus_code,
> +	.enum_frame_size = ispif_enum_frame_size,
> +	.get_fmt = ispif_get_format,
> +	.set_fmt = ispif_set_format,
> +};
> +
> +static const struct v4l2_subdev_ops ispif_v4l2_ops = {
> +	.core = &ispif_core_ops,
> +	.video = &ispif_video_ops,
> +	.pad = &ispif_pad_ops,
> +};
> +
> +static const struct v4l2_subdev_internal_ops ispif_v4l2_internal_ops = {
> +	.open = ispif_init_formats,
> +};
> +
> +static const struct media_entity_operations ispif_media_ops = {
> +	.link_setup = ispif_link_setup,
> +	.link_validate = v4l2_subdev_link_validate,
> +};
> +
> +/*
> + * msm_ispif_register_entities - Register subdev node for ISPIF module
> + * @ispif: ISPIF device
> + * @v4l2_dev: V4L2 device
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +int msm_ispif_register_entities(struct ispif_device *ispif,
> +				struct v4l2_device *v4l2_dev)
> +{
> +	struct device *dev = to_device(ispif);

I'd use something such as ispif_to_device(). Perhaps there won't be
to_device() defined anywhere else but it'd clash with this driver if there
is one. Up to you.

> +	int ret;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(ispif->line); i++) {
> +		struct v4l2_subdev *sd = &ispif->line[i].subdev;
> +		struct media_pad *pads = ispif->line[i].pads;
> +
> +		v4l2_subdev_init(sd, &ispif_v4l2_ops);
> +		sd->internal_ops = &ispif_v4l2_internal_ops;
> +		sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +		snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d",
> +			 MSM_ISPIF_NAME, i);
> +		v4l2_set_subdevdata(sd, &ispif->line[i]);
> +
> +		ret = ispif_init_formats(sd, NULL);
> +		if (ret < 0) {
> +			dev_err(dev, "Failed to init format: %d\n", ret);
> +			goto error;
> +		}
> +
> +		pads[MSM_ISPIF_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
> +		pads[MSM_ISPIF_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
> +
> +		sd->entity.function = MEDIA_ENT_F_IO_V4L;
> +		sd->entity.ops = &ispif_media_ops;
> +		ret = media_entity_pads_init(&sd->entity, MSM_ISPIF_PADS_NUM,
> +					     pads);
> +		if (ret < 0) {
> +			dev_err(dev, "Failed to init media entity: %d\n", ret);
> +			goto error;
> +		}
> +
> +		ret = v4l2_device_register_subdev(v4l2_dev, sd);
> +		if (ret < 0) {
> +			dev_err(dev, "Failed to register subdev: %d\n", ret);
> +			media_entity_cleanup(&sd->entity);
> +			goto error;
> +		}
> +	}
> +
> +	return 0;
> +
> +error:
> +	for (i--; i >= 0; i--) {
> +		struct v4l2_subdev *sd = &ispif->line[i].subdev;
> +
> +		v4l2_device_unregister_subdev(sd);
> +		media_entity_cleanup(&sd->entity);
> +	}
> +
> +	return ret;
> +}
> +
> +/*
> + * msm_ispif_unregister_entities - Unregister ISPIF module subdev node
> + * @ispif: ISPIF device
> + */
> +void msm_ispif_unregister_entities(struct ispif_device *ispif)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(ispif->line); i++) {
> +		struct v4l2_subdev *sd = &ispif->line[i].subdev;
> +
> +		v4l2_device_unregister_subdev(sd);
> +		media_entity_cleanup(&sd->entity);
> +	}
> +}
> diff --git a/drivers/media/platform/qcom/camss-8x16/camss-ispif.h b/drivers/media/platform/qcom/camss-8x16/camss-ispif.h
> new file mode 100644
> index 0000000..6a1c9bd
> --- /dev/null
> +++ b/drivers/media/platform/qcom/camss-8x16/camss-ispif.h
> @@ -0,0 +1,85 @@
> +/*
> + * camss-ispif.h
> + *
> + * Qualcomm MSM Camera Subsystem - ISPIF Module
> + *
> + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
> + * Copyright (C) 2015-2017 Linaro Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +#ifndef QC_MSM_CAMSS_ISPIF_H
> +#define QC_MSM_CAMSS_ISPIF_H
> +
> +#include <linux/clk.h>
> +#include <media/media-entity.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-subdev.h>
> +
> +/* Number of ISPIF lines - same as number of CSID hardware modules */
> +#define MSM_ISPIF_LINE_NUM 2
> +
> +#define MSM_ISPIF_PAD_SINK 0
> +#define MSM_ISPIF_PAD_SRC 1
> +#define MSM_ISPIF_PADS_NUM 2
> +
> +#define MSM_ISPIF_VFE_NUM 1
> +
> +enum ispif_intf {
> +	PIX0,
> +	RDI0,
> +	PIX1,
> +	RDI1,
> +	RDI2
> +};
> +
> +struct ispif_intf_cmd_reg {
> +	u32 cmd_0;
> +	u32 cmd_1;
> +};
> +
> +struct ispif_line {
> +	u8 id;
> +	u8 csid_id;
> +	u8 vfe_id;
> +	enum ispif_intf interface;
> +	struct v4l2_subdev subdev;
> +	struct media_pad pads[MSM_ISPIF_PADS_NUM];
> +	struct v4l2_mbus_framefmt fmt[MSM_ISPIF_PADS_NUM];
> +};
> +
> +struct ispif_device {
> +	void __iomem *base;
> +	void __iomem *base_clk_mux;
> +	u32 irq;
> +	char irq_name[30];
> +	struct clk **clock;
> +	int nclocks;
> +	struct clk **clock_for_reset;
> +	int nclocks_for_reset;
> +	struct completion reset_complete;
> +	int power_count;
> +	struct mutex power_lock;
> +	struct ispif_intf_cmd_reg intf_cmd[MSM_ISPIF_VFE_NUM];
> +	struct mutex config_lock;
> +	struct ispif_line line[MSM_ISPIF_LINE_NUM];
> +};
> +
> +struct resources_ispif;
> +
> +int msm_ispif_subdev_init(struct ispif_device *ispif,
> +			  const struct resources_ispif *res);
> +
> +int msm_ispif_register_entities(struct ispif_device *ispif,
> +				struct v4l2_device *v4l2_dev);
> +
> +void msm_ispif_unregister_entities(struct ispif_device *ispif);
> +
> +#endif /* QC_MSM_CAMSS_ISPIF_H */

-- 
Regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 10/23] media: camss: Add VFE files
  2017-07-17 10:33 ` [PATCH v3 10/23] media: camss: Add VFE files Todor Tomov
@ 2017-07-20 14:59   ` Sakari Ailus
  2017-07-25 14:02     ` Todor Tomov
  0 siblings, 1 reply; 60+ messages in thread
From: Sakari Ailus @ 2017-07-20 14:59 UTC (permalink / raw)
  To: Todor Tomov
  Cc: mchehab, hans.verkuil, javier, s.nawrocki, linux-media,
	linux-kernel, linux-arm-msm

Hi Todor,

On Mon, Jul 17, 2017 at 01:33:36PM +0300, Todor Tomov wrote:
> These files control the VFE module. The VFE has different input interfaces.
> The PIX input interface feeds the input data to an image processing pipeline.
> Three RDI input interfaces bypass the image processing pipeline. The VFE also
> contains the AXI bus interface which writes the output data to memory.
> 
> RDI interfaces are supported in this version. PIX interface is not supported.
> 
> Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
> ---
>  drivers/media/platform/qcom/camss-8x16/camss-vfe.c | 1913 ++++++++++++++++++++
>  drivers/media/platform/qcom/camss-8x16/camss-vfe.h |  114 ++
>  2 files changed, 2027 insertions(+)
>  create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-vfe.c
>  create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-vfe.h
> 
> diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
> new file mode 100644
> index 0000000..b6dd29b
> --- /dev/null
> +++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
> @@ -0,0 +1,1913 @@
> +/*
> + * camss-vfe.c
> + *
> + * Qualcomm MSM Camera Subsystem - VFE Module
> + *
> + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
> + * Copyright (C) 2015-2017 Linaro Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +#include <linux/clk.h>
> +#include <linux/completion.h>
> +#include <linux/interrupt.h>
> +#include <linux/iommu.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock_types.h>
> +#include <linux/spinlock.h>
> +#include <media/media-entity.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-subdev.h>
> +
> +#include "camss-vfe.h"
> +#include "camss.h"
> +
> +#define MSM_VFE_NAME "msm_vfe"
> +
> +#define vfe_line_array(ptr_line)	\
> +	((const struct vfe_line (*)[]) &(ptr_line[-(ptr_line->id)]))

If you have something like that used by multiple parts of the driver, it'd
be good to define it in a common header. (I ignore if the types are exactly
the same though. Ignore the comment if they're different.)

> +
> +#define to_vfe(ptr_line)	\
> +	container_of(vfe_line_array(ptr_line), struct vfe_device, ptr_line)
> +
> +#define VFE_0_HW_VERSION		0x000
> +
> +#define VFE_0_GLOBAL_RESET_CMD		0x00c
> +#define VFE_0_GLOBAL_RESET_CMD_CORE	(1 << 0)
> +#define VFE_0_GLOBAL_RESET_CMD_CAMIF	(1 << 1)
> +#define VFE_0_GLOBAL_RESET_CMD_BUS	(1 << 2)
> +#define VFE_0_GLOBAL_RESET_CMD_BUS_BDG	(1 << 3)
> +#define VFE_0_GLOBAL_RESET_CMD_REGISTER	(1 << 4)
> +#define VFE_0_GLOBAL_RESET_CMD_TIMER	(1 << 5)
> +#define VFE_0_GLOBAL_RESET_CMD_PM	(1 << 6)
> +#define VFE_0_GLOBAL_RESET_CMD_BUS_MISR	(1 << 7)
> +#define VFE_0_GLOBAL_RESET_CMD_TESTGEN	(1 << 8)
> +
> +#define VFE_0_IRQ_CMD			0x024
> +#define VFE_0_IRQ_CMD_GLOBAL_CLEAR	(1 << 0)
> +
> +#define VFE_0_IRQ_MASK_0		0x028
> +#define VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n)		(1 << ((n) + 5))
> +#define VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(n)	(1 << ((n) + 8))
> +#define VFE_0_IRQ_MASK_0_RESET_ACK			(1 << 31)
> +#define VFE_0_IRQ_MASK_1		0x02c
> +#define VFE_0_IRQ_MASK_1_VIOLATION			(1 << 7)
> +#define VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK		(1 << 8)
> +#define VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(n)	(1 << ((n) + 9))
> +
> +#define VFE_0_IRQ_CLEAR_0		0x030
> +#define VFE_0_IRQ_CLEAR_1		0x034
> +
> +#define VFE_0_IRQ_STATUS_0		0x038
> +#define VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n)		(1 << ((n) + 5))
> +#define VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(n)	(1 << ((n) + 8))
> +#define VFE_0_IRQ_STATUS_0_RESET_ACK			(1 << 31)
> +#define VFE_0_IRQ_STATUS_1		0x03c
> +#define VFE_0_IRQ_STATUS_1_VIOLATION			(1 << 7)
> +#define VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK		(1 << 8)
> +
> +#define VFE_0_VIOLATION_STATUS		0x48
> +
> +#define VFE_0_BUS_CMD			0x4c
> +#define VFE_0_BUS_CMD_Mx_RLD_CMD(x)	(1 << (x))
> +
> +#define VFE_0_BUS_CFG			0x050
> +
> +#define VFE_0_BUS_XBAR_CFG_x(x)		(0x58 + 0x4 * ((x) / 2))
> +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT		8
> +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0	5
> +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1	6
> +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2	7
> +
> +#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(n)		(0x06c + 0x24 * (n))
> +#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT	0
> +#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT	1
> +#define VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(n)	(0x070 + 0x24 * (n))
> +#define VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(n)	(0x074 + 0x24 * (n))
> +#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(n)		(0x078 + 0x24 * (n))
> +#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT	2
> +#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK	(0x1F << 2)
> +
> +#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(n)		(0x07c + 0x24 * (n))
> +#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT	16
> +#define VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(n)	\
> +							(0x088 + 0x24 * (n))
> +#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(n)	\
> +							(0x08c + 0x24 * (n))
> +#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF	0xffffffff
> +
> +#define VFE_0_BUS_PING_PONG_STATUS	0x268
> +
> +#define VFE_0_BUS_BDG_CMD		0x2c0
> +#define VFE_0_BUS_BDG_CMD_HALT_REQ	1
> +
> +#define VFE_0_BUS_BDG_QOS_CFG_0		0x2c4
> +#define VFE_0_BUS_BDG_QOS_CFG_1		0x2c8
> +#define VFE_0_BUS_BDG_QOS_CFG_2		0x2cc
> +#define VFE_0_BUS_BDG_QOS_CFG_3		0x2d0
> +#define VFE_0_BUS_BDG_QOS_CFG_4		0x2d4
> +#define VFE_0_BUS_BDG_QOS_CFG_5		0x2d8
> +#define VFE_0_BUS_BDG_QOS_CFG_6		0x2dc
> +#define VFE_0_BUS_BDG_QOS_CFG_7		0x2e0
> +
> +#define VFE_0_RDI_CFG_x(x)		(0x2e8 + (0x4 * (x)))
> +#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT	28
> +#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK	(0xf << 28)
> +#define VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT	4
> +#define VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK		(0xf << 4)
> +#define VFE_0_RDI_CFG_x_RDI_EN_BIT		(1 << 2)
> +#define VFE_0_RDI_CFG_x_MIPI_EN_BITS		0x3
> +#define VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(r)	(1 << (16 + (r)))
> +
> +#define VFE_0_REG_UPDATE			0x378
> +#define VFE_0_REG_UPDATE_RDIn(n)		(1 << (1 + (n)))
> +
> +#define VFE_0_CGC_OVERRIDE_1			0x974
> +#define VFE_0_CGC_OVERRIDE_1_IMAGE_Mx_CGC_OVERRIDE(x)	(1 << (x))
> +
> +/* VFE reset timeout */
> +#define VFE_RESET_TIMEOUT_MS 50
> +/* VFE halt timeout */
> +#define VFE_HALT_TIMEOUT_MS 100
> +/* Max number of frame drop updates per frame */
> +#define VFE_FRAME_DROP_UPDATES 5
> +/* Frame drop value. NOTE: VAL + UPDATES should not exceed 31 */
> +#define VFE_FRAME_DROP_VAL 20
> +
> +static const u32 vfe_formats[] = {
> +	MEDIA_BUS_FMT_UYVY8_2X8,
> +	MEDIA_BUS_FMT_VYUY8_2X8,
> +	MEDIA_BUS_FMT_YUYV8_2X8,
> +	MEDIA_BUS_FMT_YVYU8_2X8,
> +	MEDIA_BUS_FMT_SBGGR8_1X8,
> +	MEDIA_BUS_FMT_SGBRG8_1X8,
> +	MEDIA_BUS_FMT_SGRBG8_1X8,
> +	MEDIA_BUS_FMT_SRGGB8_1X8,
> +	MEDIA_BUS_FMT_SBGGR10_1X10,
> +	MEDIA_BUS_FMT_SGBRG10_1X10,
> +	MEDIA_BUS_FMT_SGRBG10_1X10,
> +	MEDIA_BUS_FMT_SRGGB10_1X10,
> +	MEDIA_BUS_FMT_SBGGR12_1X12,
> +	MEDIA_BUS_FMT_SGBRG12_1X12,
> +	MEDIA_BUS_FMT_SGRBG12_1X12,
> +	MEDIA_BUS_FMT_SRGGB12_1X12,
> +};
> +
> +static inline void vfe_reg_clr(struct vfe_device *vfe, u32 reg, u32 clr_bits)
> +{
> +	u32 bits = readl_relaxed(vfe->base + reg);
> +
> +	writel_relaxed(bits & ~clr_bits, vfe->base + reg);
> +}
> +
> +static inline void vfe_reg_set(struct vfe_device *vfe, u32 reg, u32 set_bits)
> +{
> +	u32 bits = readl_relaxed(vfe->base + reg);
> +
> +	writel_relaxed(bits | set_bits, vfe->base + reg);
> +}
> +
> +static void vfe_global_reset(struct vfe_device *vfe)
> +{
> +	u32 reset_bits = VFE_0_GLOBAL_RESET_CMD_TESTGEN		|
> +			 VFE_0_GLOBAL_RESET_CMD_BUS_MISR	|
> +			 VFE_0_GLOBAL_RESET_CMD_PM		|
> +			 VFE_0_GLOBAL_RESET_CMD_TIMER		|
> +			 VFE_0_GLOBAL_RESET_CMD_REGISTER	|
> +			 VFE_0_GLOBAL_RESET_CMD_BUS_BDG		|
> +			 VFE_0_GLOBAL_RESET_CMD_BUS		|
> +			 VFE_0_GLOBAL_RESET_CMD_CAMIF		|
> +			 VFE_0_GLOBAL_RESET_CMD_CORE;
> +
> +	writel_relaxed(reset_bits, vfe->base + VFE_0_GLOBAL_RESET_CMD);
> +}
> +
> +static void vfe_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable)
> +{
> +	if (enable)
> +		vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm),
> +			    1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT);
> +	else
> +		vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm),
> +			    1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT);
> +}
> +
> +static void vfe_wm_frame_based(struct vfe_device *vfe, u8 wm, u8 enable)
> +{
> +	if (enable)
> +		vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm),
> +			1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT);
> +	else
> +		vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm),
> +			1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT);
> +}
> +
> +static void vfe_wm_set_framedrop_period(struct vfe_device *vfe, u8 wm, u8 per)
> +{
> +	u32 reg;
> +
> +	reg = readl_relaxed(vfe->base +
> +			    VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm));
> +
> +	reg &= ~(VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK);
> +
> +	reg |= (per << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT)
> +		& VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK;
> +
> +	writel_relaxed(reg,
> +		       vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm));
> +}
> +
> +static void vfe_wm_set_framedrop_pattern(struct vfe_device *vfe, u8 wm,
> +					 u32 pattern)
> +{
> +	writel_relaxed(pattern,
> +	       vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(wm));
> +}
> +
> +static void vfe_wm_set_ub_cfg(struct vfe_device *vfe, u8 wm, u16 offset,
> +			      u16 depth)
> +{
> +	u32 reg;
> +
> +	reg = (offset << VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT) |
> +		depth;
> +	writel_relaxed(reg, vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(wm));
> +}
> +
> +static void vfe_bus_reload_wm(struct vfe_device *vfe, u8 wm)
> +{
> +	wmb();
> +	writel_relaxed(VFE_0_BUS_CMD_Mx_RLD_CMD(wm), vfe->base + VFE_0_BUS_CMD);
> +	wmb();
> +}
> +
> +static void vfe_wm_set_ping_addr(struct vfe_device *vfe, u8 wm, u32 addr)
> +{
> +	writel_relaxed(addr,
> +		       vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(wm));
> +}
> +
> +static void vfe_wm_set_pong_addr(struct vfe_device *vfe, u8 wm, u32 addr)
> +{
> +	writel_relaxed(addr,
> +		       vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(wm));
> +}
> +
> +static int vfe_wm_get_ping_pong_status(struct vfe_device *vfe, u8 wm)
> +{
> +	u32 reg;
> +
> +	reg = readl_relaxed(vfe->base + VFE_0_BUS_PING_PONG_STATUS);
> +
> +	return (reg >> wm) & 0x1;
> +}
> +
> +static void vfe_bus_enable_wr_if(struct vfe_device *vfe, u8 enable)
> +{
> +	if (enable)
> +		writel_relaxed(0x10000009, vfe->base + VFE_0_BUS_CFG);
> +	else
> +		writel_relaxed(0, vfe->base + VFE_0_BUS_CFG);
> +}
> +
> +static void vfe_bus_connect_wm_to_rdi(struct vfe_device *vfe, u8 wm,
> +				      enum vfe_line_id id)
> +{
> +	u32 reg;
> +
> +	reg = VFE_0_RDI_CFG_x_MIPI_EN_BITS;
> +	reg |= VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(id);
> +	vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), reg);
> +
> +	reg = VFE_0_RDI_CFG_x_RDI_EN_BIT;
> +	reg |= ((3 * id) << VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT) &
> +		VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK;
> +	vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id), reg);
> +
> +	switch (id) {
> +	case VFE_LINE_RDI0:
> +	default:
> +		reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 <<
> +		      VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
> +		break;
> +	case VFE_LINE_RDI1:
> +		reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 <<
> +		      VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
> +		break;
> +	case VFE_LINE_RDI2:
> +		reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 <<
> +		      VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
> +		break;
> +	}
> +
> +	if (wm % 2 == 1)
> +		reg <<= 16;
> +
> +	vfe_reg_set(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg);
> +
> +	writel_relaxed(VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF,
> +	       vfe->base +
> +	       VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(wm));
> +}
> +
> +static void vfe_bus_disconnect_wm_from_rdi(struct vfe_device *vfe, u8 wm,
> +					   enum vfe_line_id id)
> +{
> +	u32 reg;
> +
> +	reg = VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(id);
> +	vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(0), reg);
> +
> +	reg = VFE_0_RDI_CFG_x_RDI_EN_BIT;
> +	vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), reg);
> +
> +	switch (id) {
> +	case VFE_LINE_RDI0:
> +	default:
> +		reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 <<
> +		      VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
> +		break;
> +	case VFE_LINE_RDI1:
> +		reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 <<
> +		      VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
> +		break;
> +	case VFE_LINE_RDI2:
> +		reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 <<
> +		      VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
> +		break;
> +	}
> +
> +	if (wm % 2 == 1)
> +		reg <<= 16;
> +
> +	vfe_reg_clr(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg);
> +}
> +
> +static void vfe_set_rdi_cid(struct vfe_device *vfe, enum vfe_line_id id, u8 cid)
> +{
> +	vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id),
> +		    VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK);
> +
> +	vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id),
> +		    cid << VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT);
> +}
> +
> +static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
> +{
> +	vfe->reg_update |= VFE_0_REG_UPDATE_RDIn(line_id);
> +	wmb();
> +	writel_relaxed(vfe->reg_update, vfe->base + VFE_0_REG_UPDATE);
> +	wmb();
> +}
> +
> +static void vfe_enable_irq_wm_line(struct vfe_device *vfe, u8 wm,
> +				   enum vfe_line_id line_id, u8 enable)
> +{
> +	u32 irq_en0 = VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(wm) |
> +		      VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(line_id);
> +	u32 irq_en1 = VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(wm);
> +
> +	if (enable) {
> +		vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0);
> +		vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1);
> +	} else {
> +		vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0);
> +		vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1);
> +	}
> +}
> +
> +static void vfe_enable_irq_common(struct vfe_device *vfe)
> +{
> +	u32 irq_en0 = VFE_0_IRQ_MASK_0_RESET_ACK;
> +	u32 irq_en1 = VFE_0_IRQ_MASK_1_VIOLATION |
> +		      VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK;
> +
> +	vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0);
> +	vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1);
> +}
> +
> +/*
> + * vfe_reset - Trigger reset on VFE module and wait to complete
> + * @vfe: VFE device
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int vfe_reset(struct vfe_device *vfe)
> +{
> +	unsigned long time;
> +
> +	reinit_completion(&vfe->reset_complete);
> +
> +	vfe_global_reset(vfe);
> +
> +	time = wait_for_completion_timeout(&vfe->reset_complete,
> +		msecs_to_jiffies(VFE_RESET_TIMEOUT_MS));
> +	if (!time) {
> +		dev_err(to_device(vfe), "VFE reset timeout\n");
> +		return -EIO;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * vfe_halt - Trigger halt on VFE module and wait to complete
> + * @vfe: VFE device
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int vfe_halt(struct vfe_device *vfe)
> +{
> +	unsigned long time;
> +
> +	reinit_completion(&vfe->halt_complete);
> +
> +	writel_relaxed(VFE_0_BUS_BDG_CMD_HALT_REQ,
> +		       vfe->base + VFE_0_BUS_BDG_CMD);
> +
> +	time = wait_for_completion_timeout(&vfe->halt_complete,
> +		msecs_to_jiffies(VFE_HALT_TIMEOUT_MS));
> +	if (!time) {
> +		dev_err(to_device(vfe), "VFE halt timeout\n");
> +		return -EIO;
> +	}
> +
> +	return 0;
> +}
> +
> +static void vfe_init_outputs(struct vfe_device *vfe)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(vfe->line); i++) {
> +		struct vfe_output *output = &vfe->line[i].output;
> +
> +		output->state = VFE_OUTPUT_OFF;
> +		output->buf[0] = NULL;
> +		output->buf[1] = NULL;
> +		INIT_LIST_HEAD(&output->pending_bufs);
> +	}
> +}
> +
> +static void vfe_reset_output_maps(struct vfe_device *vfe)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++)
> +		vfe->wm_output_map[i] = VFE_LINE_NONE;
> +}
> +
> +static void vfe_set_qos(struct vfe_device *vfe)
> +{
> +	u32 val = 0xaaa5aaa5;
> +	u32 val7 = 0x0001aaa5;

Huh. What do these mean? :-)

> +
> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_0);
> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_1);
> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_2);
> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_3);
> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_4);
> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_5);
> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_6);
> +	writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7);
> +}
> +
> +static void vfe_set_cgc_override(struct vfe_device *vfe, u8 wm, u8 enable)
> +{
> +	u32 val = VFE_0_CGC_OVERRIDE_1_IMAGE_Mx_CGC_OVERRIDE(wm);
> +
> +	if (enable)
> +		vfe_reg_set(vfe, VFE_0_CGC_OVERRIDE_1, val);
> +	else
> +		vfe_reg_clr(vfe, VFE_0_CGC_OVERRIDE_1, val);
> +
> +	wmb();
> +}
> +
> +static void vfe_output_init_addrs(struct vfe_device *vfe,
> +				  struct vfe_output *output, u8 sync)
> +{
> +	u32 ping_addr = 0;
> +	u32 pong_addr = 0;
> +
> +	output->active_buf = 0;
> +
> +	if (output->buf[0])
> +		ping_addr = output->buf[0]->addr;
> +
> +	if (output->buf[1])
> +		pong_addr = output->buf[1]->addr;
> +	else
> +		pong_addr = ping_addr;
> +
> +	vfe_wm_set_ping_addr(vfe, output->wm_idx, ping_addr);
> +	vfe_wm_set_pong_addr(vfe, output->wm_idx, pong_addr);
> +	if (sync)
> +		vfe_bus_reload_wm(vfe, output->wm_idx);
> +}
> +
> +static void vfe_output_update_ping_addr(struct vfe_device *vfe,
> +					struct vfe_output *output, u8 sync)
> +{
> +	u32 addr = 0;
> +
> +	if (output->buf[0])
> +		addr = output->buf[0]->addr;
> +
> +	vfe_wm_set_ping_addr(vfe, output->wm_idx, addr);
> +	if (sync)
> +		vfe_bus_reload_wm(vfe, output->wm_idx);
> +}
> +
> +static void vfe_output_update_pong_addr(struct vfe_device *vfe,
> +					struct vfe_output *output, u8 sync)
> +{
> +	u32 addr = 0;
> +
> +	if (output->buf[1])
> +		addr = output->buf[1]->addr;
> +
> +	vfe_wm_set_pong_addr(vfe, output->wm_idx, addr);
> +	if (sync)
> +		vfe_bus_reload_wm(vfe, output->wm_idx);
> +
> +}
> +
> +static int vfe_reserve_wm(struct vfe_device *vfe, enum vfe_line_id line_id)
> +{
> +	int ret = -EBUSY;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++) {
> +		if (vfe->wm_output_map[i] == VFE_LINE_NONE) {
> +			vfe->wm_output_map[i] = line_id;
> +			ret = i;
> +			break;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static int vfe_release_wm(struct vfe_device *vfe, u8 wm)
> +{
> +	if (wm > ARRAY_SIZE(vfe->wm_output_map))
> +		return -EINVAL;
> +
> +	vfe->wm_output_map[wm] = VFE_LINE_NONE;
> +
> +	return 0;
> +}
> +
> +static void vfe_output_frame_drop(struct vfe_device *vfe,
> +				  struct vfe_output *output,
> +				  u32 drop_pattern)
> +{
> +	u8 drop_period;
> +
> +	/* We need to toggle update period to be valid on next frame */
> +	output->drop_update_idx++;
> +	output->drop_update_idx %= VFE_FRAME_DROP_UPDATES;
> +	drop_period = VFE_FRAME_DROP_VAL + output->drop_update_idx;
> +
> +	vfe_wm_set_framedrop_period(vfe, output->wm_idx, drop_period);
> +	vfe_wm_set_framedrop_pattern(vfe, output->wm_idx, drop_pattern);
> +	vfe_reg_update(vfe, container_of(output, struct vfe_line, output)->id);
> +
> +}
> +
> +static struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output)
> +{
> +	struct camss_buffer *buffer = NULL;
> +
> +	if (!list_empty(&output->pending_bufs)) {
> +		buffer = list_first_entry(&output->pending_bufs,
> +					  struct camss_buffer,
> +					  queue);
> +		list_del(&buffer->queue);
> +	}
> +
> +	return buffer;
> +}
> +
> +/*
> + * vfe_buf_add_pending - Add output buffer to list of pending
> + * @output: VFE output
> + * @buffer: Video buffer
> + */
> +static void vfe_buf_add_pending(struct vfe_output *output,
> +				struct camss_buffer *buffer)
> +{
> +	INIT_LIST_HEAD(&buffer->queue);
> +	list_add_tail(&buffer->queue, &output->pending_bufs);
> +}
> +
> +/*
> + * vfe_buf_flush_pending - Flush all pending buffers.
> + * @output: VFE output
> + * @state: vb2 buffer state
> + */
> +static void vfe_buf_flush_pending(struct vfe_output *output,
> +				  enum vb2_buffer_state state)
> +{
> +	struct camss_buffer *buf;
> +	struct camss_buffer *t;
> +
> +	list_for_each_entry_safe(buf, t, &output->pending_bufs, queue) {
> +		vb2_buffer_done(&buf->vb.vb2_buf, state);
> +		list_del(&buf->queue);
> +	}
> +}
> +
> +static void vfe_buf_update_wm_on_next(struct vfe_device *vfe,
> +				      struct vfe_output *output)
> +{
> +	switch (output->state) {
> +	case VFE_OUTPUT_CONTINUOUS:
> +		vfe_output_frame_drop(vfe, output, 3);
> +		break;
> +	case VFE_OUTPUT_SINGLE:
> +	default:
> +		dev_err_ratelimited(to_device(vfe),
> +				    "Next buf in wrong state! %d\n",
> +				    output->state);
> +		break;
> +	}
> +}
> +
> +static void vfe_buf_update_wm_on_last(struct vfe_device *vfe,
> +				      struct vfe_output *output)
> +{
> +	switch (output->state) {
> +	case VFE_OUTPUT_CONTINUOUS:
> +		output->state = VFE_OUTPUT_SINGLE;
> +		vfe_output_frame_drop(vfe, output, 1);
> +		break;
> +	case VFE_OUTPUT_SINGLE:
> +		output->state = VFE_OUTPUT_STOPPING;
> +		vfe_output_frame_drop(vfe, output, 0);
> +		break;
> +	default:
> +		dev_err_ratelimited(to_device(vfe),
> +				    "Last buff in wrong state! %d\n",
> +				    output->state);
> +		break;
> +	}
> +}
> +
> +static void vfe_buf_update_wm_on_new(struct vfe_device *vfe,
> +				     struct vfe_output *output,
> +				     struct camss_buffer *new_buf)
> +{
> +	int inactive_idx;
> +
> +	switch (output->state) {
> +	case VFE_OUTPUT_SINGLE:
> +		inactive_idx = !output->active_buf;
> +
> +		if (!output->buf[inactive_idx]) {
> +			output->buf[inactive_idx] = new_buf;
> +
> +			if (inactive_idx)
> +				vfe_output_update_pong_addr(vfe, output, 0);
> +			else
> +				vfe_output_update_ping_addr(vfe, output, 0);
> +
> +			vfe_output_frame_drop(vfe, output, 3);
> +			output->state = VFE_OUTPUT_CONTINUOUS;
> +		} else {
> +			vfe_buf_add_pending(output, new_buf);
> +			dev_err_ratelimited(to_device(vfe),
> +					    "Inactive buffer is busy\n");
> +		}
> +		break;
> +
> +	case VFE_OUTPUT_IDLE:
> +		if (!output->buf[0]) {
> +			output->buf[0] = new_buf;
> +
> +			vfe_output_init_addrs(vfe, output, 1);
> +
> +			vfe_output_frame_drop(vfe, output, 1);
> +			output->state = VFE_OUTPUT_SINGLE;
> +		} else {
> +			vfe_buf_add_pending(output, new_buf);
> +			dev_err_ratelimited(to_device(vfe),
> +					    "Output idle with buffer set!\n");
> +		}
> +		break;
> +
> +	case VFE_OUTPUT_CONTINUOUS:
> +	default:
> +		vfe_buf_add_pending(output, new_buf);
> +		break;
> +	}
> +}
> +
> +static int vfe_get_output(struct vfe_line *line)
> +{
> +	struct vfe_device *vfe = to_vfe(line);
> +	struct vfe_output *output;
> +	unsigned long flags;
> +	int wm_idx;
> +
> +	spin_lock_irqsave(&vfe->output_lock, flags);
> +
> +	output = &line->output;
> +	if (output->state != VFE_OUTPUT_OFF) {
> +		dev_err(to_device(vfe), "Output is running\n");
> +		goto error;
> +	}
> +	output->state = VFE_OUTPUT_RESERVED;
> +
> +	output->active_buf = 0;
> +
> +	/* We will use only one wm per output for now */
> +	wm_idx = vfe_reserve_wm(vfe, line->id);
> +	if (wm_idx < 0) {
> +		dev_err(to_device(vfe), "Can not reserve wm\n");
> +		goto error_get_wm;
> +	}
> +	output->drop_update_idx = 0;
> +	output->wm_idx = wm_idx;
> +
> +	spin_unlock_irqrestore(&vfe->output_lock, flags);
> +
> +	return 0;
> +
> +error_get_wm:
> +	output->state = VFE_OUTPUT_OFF;
> +error:
> +	spin_unlock_irqrestore(&vfe->output_lock, flags);
> +
> +	return -EINVAL;
> +}
> +
> +static int vfe_put_output(struct vfe_line *line)
> +{
> +	struct vfe_device *vfe = to_vfe(line);
> +	struct vfe_output *output = &line->output;
> +	unsigned long flags;
> +	int ret;
> +
> +	spin_lock_irqsave(&vfe->output_lock, flags);
> +
> +	ret = vfe_release_wm(vfe, output->wm_idx);
> +	if (ret < 0)
> +		goto out;
> +
> +	output->state = VFE_OUTPUT_OFF;
> +
> +out:
> +	spin_unlock_irqrestore(&vfe->output_lock, flags);
> +	return ret;
> +}
> +
> +static int vfe_enable_output(struct vfe_line *line)
> +{
> +	struct vfe_device *vfe = to_vfe(line);
> +	struct vfe_output *output = &line->output;
> +	unsigned long flags;
> +	u16 ub_size;
> +
> +	switch (vfe->id) {
> +	case 0:
> +		ub_size = MSM_VFE_VFE0_UB_SIZE_RDI;
> +		break;
> +	case 1:
> +		ub_size = MSM_VFE_VFE1_UB_SIZE_RDI;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	spin_lock_irqsave(&vfe->output_lock, flags);
> +
> +	vfe->reg_update &= ~VFE_0_REG_UPDATE_RDIn(line->id);
> +
> +	if (output->state != VFE_OUTPUT_RESERVED) {
> +		dev_err(to_device(vfe), "Output is not in reserved state %d\n",
> +			output->state);
> +		spin_unlock_irqrestore(&vfe->output_lock, flags);
> +		return -EINVAL;
> +	}
> +	output->state = VFE_OUTPUT_IDLE;
> +
> +	output->buf[0] = vfe_buf_get_pending(output);
> +	output->buf[1] = vfe_buf_get_pending(output);
> +
> +	if (!output->buf[0] && output->buf[1]) {
> +		output->buf[0] = output->buf[1];
> +		output->buf[1] = NULL;
> +	}
> +
> +	if (output->buf[0])
> +		output->state = VFE_OUTPUT_SINGLE;
> +
> +	if (output->buf[1])
> +		output->state = VFE_OUTPUT_CONTINUOUS;
> +
> +	switch (output->state) {
> +	case VFE_OUTPUT_SINGLE:
> +		vfe_output_frame_drop(vfe, output, 1);
> +		break;
> +	case VFE_OUTPUT_CONTINUOUS:
> +		vfe_output_frame_drop(vfe, output, 3);
> +		break;
> +	default:
> +		vfe_output_frame_drop(vfe, output, 0);
> +		break;
> +	}
> +
> +	output->sequence = 0;
> +
> +	vfe_output_init_addrs(vfe, output, 0);
> +
> +	vfe_set_cgc_override(vfe, output->wm_idx, 1);
> +
> +	vfe_enable_irq_wm_line(vfe, output->wm_idx, line->id, 1);
> +
> +	vfe_bus_connect_wm_to_rdi(vfe, output->wm_idx, line->id);
> +
> +	vfe_set_rdi_cid(vfe, line->id, 0);
> +
> +	vfe_wm_set_ub_cfg(vfe, output->wm_idx,
> +			  (ub_size + 1) * output->wm_idx, ub_size);
> +
> +	vfe_wm_frame_based(vfe, output->wm_idx, 1);
> +	vfe_wm_enable(vfe, output->wm_idx, 1);
> +
> +	vfe_bus_reload_wm(vfe, output->wm_idx);
> +
> +	vfe_reg_update(vfe, line->id);
> +
> +	spin_unlock_irqrestore(&vfe->output_lock, flags);
> +
> +	return 0;
> +}
> +
> +static int vfe_disable_output(struct vfe_line *line)
> +{
> +	struct vfe_device *vfe = to_vfe(line);
> +	struct vfe_output *output = &line->output;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&vfe->output_lock, flags);
> +
> +	vfe_wm_enable(vfe, output->wm_idx, 0);
> +	vfe_bus_disconnect_wm_from_rdi(vfe, output->wm_idx, line->id);
> +	vfe_reg_update(vfe, line->id);
> +
> +	spin_unlock_irqrestore(&vfe->output_lock, flags);
> +
> +	return 0;
> +}
> +
> +/*
> + * vfe_enable - Enable streaming on VFE line
> + * @line: VFE line
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int vfe_enable(struct vfe_line *line)
> +{
> +	struct vfe_device *vfe = to_vfe(line);
> +	int ret;
> +
> +	mutex_lock(&vfe->stream_lock);
> +
> +	if (!vfe->stream_count) {
> +		vfe_enable_irq_common(vfe);
> +
> +		vfe_bus_enable_wr_if(vfe, 1);
> +
> +		vfe_set_qos(vfe);
> +	}
> +
> +	vfe->stream_count++;
> +
> +	mutex_unlock(&vfe->stream_lock);
> +
> +	ret = vfe_get_output(line);
> +	if (ret < 0)
> +		goto error_get_output;
> +
> +	ret = vfe_enable_output(line);
> +	if (ret < 0)
> +		goto error_enable_output;
> +
> +	vfe->was_streaming = 1;
> +
> +	return 0;
> +
> +
> +error_enable_output:
> +	vfe_put_output(line);
> +
> +error_get_output:
> +	mutex_lock(&vfe->stream_lock);
> +
> +	if (vfe->stream_count == 1)
> +		vfe_bus_enable_wr_if(vfe, 0);
> +
> +	vfe->stream_count--;
> +
> +	mutex_unlock(&vfe->stream_lock);
> +
> +	return ret;
> +}
> +
> +/*
> + * vfe_disable - Disable streaming on VFE line
> + * @line: VFE line
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int vfe_disable(struct vfe_line *line)
> +{
> +	struct vfe_device *vfe = to_vfe(line);
> +
> +	mutex_lock(&vfe->stream_lock);
> +
> +	if (vfe->stream_count == 1)
> +		vfe_bus_enable_wr_if(vfe, 0);
> +
> +	vfe->stream_count--;
> +
> +	mutex_unlock(&vfe->stream_lock);
> +
> +	vfe_disable_output(line);
> +
> +	vfe_put_output(line);
> +
> +	return 0;
> +}
> +
> +/*
> + * vfe_isr_reg_update - Process reg update interrupt
> + * @vfe: VFE Device
> + * @line_id: VFE line
> + */
> +static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
> +{
> +	struct vfe_output *output;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&vfe->output_lock, flags);
> +	vfe->reg_update &= ~VFE_0_REG_UPDATE_RDIn(line_id);
> +
> +	output = &vfe->line[line_id].output;
> +	if (output->state == VFE_OUTPUT_STOPPING) {
> +		/* Release last buffer when hw is idle */
> +		if (output->last_buffer) {
> +			vb2_buffer_done(&output->last_buffer->vb.vb2_buf,
> +					VB2_BUF_STATE_DONE);
> +			output->last_buffer = NULL;
> +		}
> +		output->state = VFE_OUTPUT_IDLE;
> +
> +		/* Buffers received in stopping state are queued in */
> +		/* dma pending queue, start next capture here */
> +
> +		output->buf[0] = vfe_buf_get_pending(output);
> +		output->buf[1] = vfe_buf_get_pending(output);
> +
> +		if (!output->buf[0] && output->buf[1]) {
> +			output->buf[0] = output->buf[1];
> +			output->buf[1] = NULL;
> +		}
> +
> +		if (output->buf[0])
> +			output->state = VFE_OUTPUT_SINGLE;
> +
> +		if (output->buf[1])
> +			output->state = VFE_OUTPUT_CONTINUOUS;
> +
> +		switch (output->state) {
> +		case VFE_OUTPUT_SINGLE:
> +			vfe_output_frame_drop(vfe, output, 2);
> +			break;
> +		case VFE_OUTPUT_CONTINUOUS:
> +			vfe_output_frame_drop(vfe, output, 3);
> +			break;
> +		default:
> +			vfe_output_frame_drop(vfe, output, 0);
> +			break;
> +		}
> +
> +		vfe_output_init_addrs(vfe, output, 1);
> +	}
> +
> +	spin_unlock_irqrestore(&vfe->output_lock, flags);
> +}
> +
> +/*
> + * vfe_isr_wm_done - Process write master done interrupt
> + * @vfe: VFE Device
> + * @wm: Write master id
> + */
> +static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm)
> +{
> +	struct camss_buffer *ready_buf;
> +	struct vfe_output *output;
> +	dma_addr_t new_addr;
> +	unsigned long flags;
> +	u32 active_index;
> +	u64 ts = ktime_get_ns();
> +
> +	active_index = vfe_wm_get_ping_pong_status(vfe, wm);
> +
> +	spin_lock_irqsave(&vfe->output_lock, flags);
> +
> +	if (vfe->wm_output_map[wm] == VFE_LINE_NONE) {
> +		dev_err_ratelimited(to_device(vfe),
> +				    "Received wm done for unmapped index\n");
> +		goto out_unlock;
> +	}
> +	output = &vfe->line[vfe->wm_output_map[wm]].output;
> +
> +	if (output->active_buf == active_index) {
> +		dev_err_ratelimited(to_device(vfe),
> +				    "Active buffer mismatch!\n");
> +		goto out_unlock;
> +	}
> +	output->active_buf = active_index;
> +
> +	ready_buf = output->buf[!active_index];
> +	if (!ready_buf) {
> +		dev_err_ratelimited(to_device(vfe),
> +				    "Missing ready buf %d %d!\n",
> +				    !active_index, output->state);
> +		goto out_unlock;
> +	}
> +
> +	ready_buf->vb.vb2_buf.timestamp = ts;
> +	ready_buf->vb.sequence = output->sequence++;
> +
> +	/* Get next buffer */
> +	output->buf[!active_index] = vfe_buf_get_pending(output);
> +	if (!output->buf[!active_index]) {
> +		/* No next buffer - set same address */
> +		new_addr = ready_buf->addr;
> +		vfe_buf_update_wm_on_last(vfe, output);
> +	} else {
> +		new_addr = output->buf[!active_index]->addr;
> +		vfe_buf_update_wm_on_next(vfe, output);
> +	}
> +
> +	if (active_index)
> +		vfe_wm_set_ping_addr(vfe, wm, new_addr);
> +	else
> +		vfe_wm_set_pong_addr(vfe, wm, new_addr);
> +
> +	spin_unlock_irqrestore(&vfe->output_lock, flags);
> +
> +	if (output->state == VFE_OUTPUT_STOPPING)
> +		output->last_buffer = ready_buf;
> +	else
> +		vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
> +
> +	return;
> +
> +out_unlock:
> +	spin_unlock_irqrestore(&vfe->output_lock, flags);
> +}
> +
> +/*
> + * vfe_isr - ISPIF module interrupt handler
> + * @irq: Interrupt line
> + * @dev: VFE device
> + *
> + * Return IRQ_HANDLED on success
> + */
> +static irqreturn_t vfe_isr(int irq, void *dev)
> +{
> +	struct vfe_device *vfe = dev;
> +	u32 value0, value1;
> +	u32 violation;
> +	int i;
> +
> +	value0 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_0);
> +	value1 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_1);
> +
> +	writel_relaxed(value0, vfe->base + VFE_0_IRQ_CLEAR_0);
> +	writel_relaxed(value1, vfe->base + VFE_0_IRQ_CLEAR_1);
> +
> +	wmb();
> +	writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD);
> +
> +	if (value0 & VFE_0_IRQ_STATUS_0_RESET_ACK)
> +		complete(&vfe->reset_complete);
> +
> +	if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION) {
> +		violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS);
> +		dev_err_ratelimited(to_device(vfe),
> +				    "VFE: violation = 0x%08x\n", violation);
> +	}
> +
> +	if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK) {
> +		complete(&vfe->halt_complete);
> +		writel_relaxed(0x0, vfe->base + VFE_0_BUS_BDG_CMD);
> +	}
> +
> +	for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++)
> +		if (value0 & VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(i))
> +			vfe_isr_reg_update(vfe, i);
> +
> +	for (i = 0; i < MSM_VFE_IMAGE_MASTERS_NUM; i++)
> +		if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(i))
> +			vfe_isr_wm_done(vfe, i);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/*
> + * vfe_get - Power up and reset VFE module
> + * @vfe: VFE Device
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int vfe_get(struct vfe_device *vfe)
> +{
> +	int ret;
> +
> +	mutex_lock(&vfe->power_lock);
> +
> +	if (vfe->power_count == 0) {
> +		ret = camss_enable_clocks(vfe->nclocks, vfe->clock,
> +					  to_device(vfe));
> +		if (ret < 0)
> +			goto error_clocks;
> +
> +		ret = vfe_reset(vfe);
> +		if (ret < 0)
> +			goto error_reset;
> +
> +		vfe_reset_output_maps(vfe);
> +
> +		vfe_init_outputs(vfe);
> +	}
> +	vfe->power_count++;
> +
> +	mutex_unlock(&vfe->power_lock);
> +
> +	return 0;
> +
> +error_reset:
> +	camss_disable_clocks(vfe->nclocks, vfe->clock);
> +
> +error_clocks:
> +	mutex_unlock(&vfe->power_lock);
> +
> +	return ret;
> +}
> +
> +/*
> + * vfe_put - Power down VFE module
> + * @vfe: VFE Device
> + */
> +static void vfe_put(struct vfe_device *vfe)
> +{
> +	mutex_lock(&vfe->power_lock);
> +
> +	if (vfe->power_count == 0) {
> +		dev_err(to_device(vfe), "vfe power off on power_count == 0\n");
> +		goto exit;
> +	} else if (vfe->power_count == 1) {
> +		if (vfe->was_streaming) {
> +			vfe->was_streaming = 0;
> +			vfe_halt(vfe);
> +		}
> +		camss_disable_clocks(vfe->nclocks, vfe->clock);
> +	}
> +
> +	vfe->power_count--;
> +
> +exit:
> +	mutex_unlock(&vfe->power_lock);
> +}
> +
> +/*
> + * vfe_video_pad_to_line - Get pointer to VFE line by media pad
> + * @pad: Media pad
> + *
> + * Return pointer to vfe line structure
> + */
> +static struct vfe_line *vfe_video_pad_to_line(struct media_pad *pad)
> +{
> +	struct media_pad *vfe_pad;
> +	struct v4l2_subdev *subdev;
> +
> +	vfe_pad = media_entity_remote_pad(pad);
> +	if (vfe_pad == NULL)
> +		return NULL;
> +
> +	subdev = media_entity_to_v4l2_subdev(vfe_pad->entity);
> +
> +	return container_of(subdev, struct vfe_line, subdev);
> +}
> +
> +/*
> + * vfe_queue_buffer - Add empty buffer
> + * @vid: Video device structure
> + * @buf: Buffer to be enqueued
> + *
> + * Add an empty buffer - depending on the current number of buffers it will be
> + * put in pending buffer queue or directly given to the hardware to be filled.
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int vfe_queue_buffer(struct camss_video *vid,
> +			    struct camss_buffer *buf)
> +{
> +	struct vfe_device *vfe = &vid->camss->vfe;
> +	struct vfe_line *line;
> +	struct vfe_output *output;
> +	unsigned long flags;
> +
> +	line = vfe_video_pad_to_line(&vid->pad);
> +	if (!line) {
> +		dev_err(to_device(vfe), "Can not queue buffer\n");
> +		return -1;
> +	}
> +	output = &line->output;
> +
> +	spin_lock_irqsave(&vfe->output_lock, flags);
> +
> +	vfe_buf_update_wm_on_new(vfe, output, buf);
> +
> +	spin_unlock_irqrestore(&vfe->output_lock, flags);
> +
> +	return 0;
> +}
> +
> +/*
> + * vfe_flush_buffers - Return all vb2 buffers
> + * @vid: Video device structure
> + * @state: vb2 buffer state of the returned buffers
> + *
> + * Return all buffers to vb2. This includes queued pending buffers (still
> + * unused) and any buffers given to the hardware but again still not used.
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int vfe_flush_buffers(struct camss_video *vid,
> +			     enum vb2_buffer_state state)
> +{
> +	struct vfe_device *vfe = &vid->camss->vfe;
> +	struct vfe_line *line;
> +	struct vfe_output *output;
> +	unsigned long flags;
> +
> +	line = vfe_video_pad_to_line(&vid->pad);
> +	if (!line) {
> +		dev_err(to_device(vfe),	"Can not flush buffers\n");
> +		return -1;
> +	}
> +	output = &line->output;
> +
> +	spin_lock_irqsave(&vfe->output_lock, flags);
> +
> +	vfe_buf_flush_pending(output, state);
> +
> +	if (output->buf[0])
> +		vb2_buffer_done(&output->buf[0]->vb.vb2_buf, state);
> +
> +	if (output->buf[1])
> +		vb2_buffer_done(&output->buf[1]->vb.vb2_buf, state);
> +
> +	if (output->last_buffer) {
> +		vb2_buffer_done(&output->last_buffer->vb.vb2_buf, state);
> +		output->last_buffer = NULL;
> +	}
> +
> +	spin_unlock_irqrestore(&vfe->output_lock, flags);
> +
> +	return 0;
> +}
> +
> +/*
> + * vfe_set_power - Power on/off VFE module
> + * @sd: VFE V4L2 subdevice
> + * @on: Requested power state
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int vfe_set_power(struct v4l2_subdev *sd, int on)
> +{
> +	struct vfe_line *line = v4l2_get_subdevdata(sd);
> +	struct vfe_device *vfe = to_vfe(line);
> +	int ret;
> +
> +	if (on) {
> +		u32 hw_version;
> +
> +		ret = vfe_get(vfe);
> +		if (ret < 0)
> +			return ret;
> +
> +		hw_version = readl_relaxed(vfe->base + VFE_0_HW_VERSION);
> +		dev_dbg(to_device(vfe),
> +			"VFE HW Version = 0x%08x\n", hw_version);
> +	} else {
> +		vfe_put(vfe);
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * vfe_set_stream - Enable/disable streaming on VFE module
> + * @sd: VFE V4L2 subdevice
> + * @enable: Requested streaming state
> + *
> + * Main configuration of VFE module is triggered here.
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int vfe_set_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct vfe_line *line = v4l2_get_subdevdata(sd);
> +	struct vfe_device *vfe = to_vfe(line);
> +	int ret;
> +
> +	if (enable) {
> +		ret = vfe_enable(line);
> +		if (ret < 0)
> +			dev_err(to_device(vfe),
> +				"Failed to enable vfe outputs\n");
> +	} else {
> +		ret = vfe_disable(line);
> +		if (ret < 0)
> +			dev_err(to_device(vfe),
> +				"Failed to disable vfe outputs\n");
> +	}
> +
> +	return ret;
> +}
> +
> +/*
> + * __vfe_get_format - Get pointer to format structure
> + * @line: VFE line
> + * @cfg: V4L2 subdev pad configuration
> + * @pad: pad from which format is requested
> + * @which: TRY or ACTIVE format
> + *
> + * Return pointer to TRY or ACTIVE format structure
> + */
> +static struct v4l2_mbus_framefmt *
> +__vfe_get_format(struct vfe_line *line,
> +		 struct v4l2_subdev_pad_config *cfg,
> +		 unsigned int pad,
> +		 enum v4l2_subdev_format_whence which)
> +{
> +	if (which == V4L2_SUBDEV_FORMAT_TRY)
> +		return v4l2_subdev_get_try_format(&line->subdev, cfg, pad);
> +
> +	return &line->fmt[pad];
> +}
> +
> +
> +/*
> + * vfe_try_format - Handle try format by pad subdev method
> + * @line: VFE line
> + * @cfg: V4L2 subdev pad configuration
> + * @pad: pad on which format is requested
> + * @fmt: pointer to v4l2 format structure
> + * @which: wanted subdev format
> + */
> +static void vfe_try_format(struct vfe_line *line,
> +			   struct v4l2_subdev_pad_config *cfg,
> +			   unsigned int pad,
> +			   struct v4l2_mbus_framefmt *fmt,
> +			   enum v4l2_subdev_format_whence which)
> +{
> +	unsigned int i;
> +
> +	switch (pad) {
> +	case MSM_VFE_PAD_SINK:
> +		/* Set format on sink pad */
> +
> +		for (i = 0; i < ARRAY_SIZE(vfe_formats); i++)
> +			if (fmt->code == vfe_formats[i])
> +				break;
> +
> +		/* If not found, use UYVY as default */
> +		if (i >= ARRAY_SIZE(vfe_formats))
> +			fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
> +
> +		fmt->width = clamp_t(u32, fmt->width, 1, 8191);
> +		fmt->height = clamp_t(u32, fmt->height, 1, 8191);
> +
> +		if (fmt->field == V4L2_FIELD_ANY)
> +			fmt->field = V4L2_FIELD_NONE;
> +
> +		break;
> +
> +	case MSM_VFE_PAD_SRC:
> +		/* Set and return a format same as sink pad */
> +
> +		*fmt = *__vfe_get_format(line, cfg, MSM_VFE_PAD_SINK,
> +					 which);
> +
> +		break;
> +	}
> +
> +	fmt->colorspace = V4L2_COLORSPACE_SRGB;
> +}
> +
> +/*
> + * vfe_enum_mbus_code - Handle pixel format enumeration
> + * @sd: VFE V4L2 subdevice
> + * @cfg: V4L2 subdev pad configuration
> + * @code: pointer to v4l2_subdev_mbus_code_enum structure
> + *
> + * return -EINVAL or zero on success
> + */
> +static int vfe_enum_mbus_code(struct v4l2_subdev *sd,
> +			      struct v4l2_subdev_pad_config *cfg,
> +			      struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	struct vfe_line *line = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt *format;
> +
> +	if (code->pad == MSM_VFE_PAD_SINK) {
> +		if (code->index >= ARRAY_SIZE(vfe_formats))
> +			return -EINVAL;
> +
> +		code->code = vfe_formats[code->index];
> +	} else {
> +		if (code->index > 0)
> +			return -EINVAL;
> +
> +		format = __vfe_get_format(line, cfg, MSM_VFE_PAD_SINK,
> +					  code->which);
> +
> +		code->code = format->code;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * vfe_enum_frame_size - Handle frame size enumeration
> + * @sd: VFE V4L2 subdevice
> + * @cfg: V4L2 subdev pad configuration
> + * @fse: pointer to v4l2_subdev_frame_size_enum structure
> + *
> + * Return -EINVAL or zero on success
> + */
> +static int vfe_enum_frame_size(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_pad_config *cfg,
> +			       struct v4l2_subdev_frame_size_enum *fse)
> +{
> +	struct vfe_line *line = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt format;
> +
> +	if (fse->index != 0)
> +		return -EINVAL;
> +
> +	format.code = fse->code;
> +	format.width = 1;
> +	format.height = 1;
> +	vfe_try_format(line, cfg, fse->pad, &format, fse->which);
> +	fse->min_width = format.width;
> +	fse->min_height = format.height;
> +
> +	if (format.code != fse->code)
> +		return -EINVAL;
> +
> +	format.code = fse->code;
> +	format.width = -1;
> +	format.height = -1;
> +	vfe_try_format(line, cfg, fse->pad, &format, fse->which);
> +	fse->max_width = format.width;
> +	fse->max_height = format.height;
> +
> +	return 0;
> +}
> +
> +/*
> + * vfe_get_format - Handle get format by pads subdev method
> + * @sd: VFE V4L2 subdevice
> + * @cfg: V4L2 subdev pad configuration
> + * @fmt: pointer to v4l2 subdev format structure
> + *
> + * Return -EINVAL or zero on success
> + */
> +static int vfe_get_format(struct v4l2_subdev *sd,
> +			  struct v4l2_subdev_pad_config *cfg,
> +			  struct v4l2_subdev_format *fmt)
> +{
> +	struct vfe_line *line = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt *format;
> +
> +	format = __vfe_get_format(line, cfg, fmt->pad, fmt->which);
> +	if (format == NULL)
> +		return -EINVAL;
> +
> +	fmt->format = *format;
> +
> +	return 0;
> +}
> +
> +/*
> + * vfe_set_format - Handle set format by pads subdev method
> + * @sd: VFE V4L2 subdevice
> + * @cfg: V4L2 subdev pad configuration
> + * @fmt: pointer to v4l2 subdev format structure
> + *
> + * Return -EINVAL or zero on success
> + */
> +static int vfe_set_format(struct v4l2_subdev *sd,
> +			  struct v4l2_subdev_pad_config *cfg,
> +			  struct v4l2_subdev_format *fmt)
> +{
> +	struct vfe_line *line = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt *format;
> +
> +	format = __vfe_get_format(line, cfg, fmt->pad, fmt->which);
> +	if (format == NULL)
> +		return -EINVAL;
> +
> +	vfe_try_format(line, cfg, fmt->pad, &fmt->format, fmt->which);
> +	*format = fmt->format;
> +
> +	/* Propagate the format from sink to source */
> +	if (fmt->pad == MSM_VFE_PAD_SINK) {
> +		format = __vfe_get_format(line, cfg, MSM_VFE_PAD_SRC,
> +					  fmt->which);
> +
> +		*format = fmt->format;
> +		vfe_try_format(line, cfg, MSM_VFE_PAD_SRC, format,
> +			       fmt->which);
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * vfe_init_formats - Initialize formats on all pads
> + * @sd: VFE V4L2 subdevice
> + * @fh: V4L2 subdev file handle
> + *
> + * Initialize all pad formats with default values.
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int vfe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> +	struct v4l2_subdev_format format = {
> +		.pad = MSM_VFE_PAD_SINK,
> +		.which = fh ? V4L2_SUBDEV_FORMAT_TRY :
> +			      V4L2_SUBDEV_FORMAT_ACTIVE,
> +		.format = {
> +			.code = MEDIA_BUS_FMT_UYVY8_2X8,
> +			.width = 1920,
> +			.height = 1080
> +		}
> +	};
> +
> +	return vfe_set_format(sd, fh ? fh->pad : NULL, &format);
> +}
> +
> +/*
> + * msm_vfe_subdev_init - Initialize VFE device structure and resources
> + * @vfe: VFE device
> + * @res: VFE module resources table
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res)
> +{
> +	struct device *dev = to_device(vfe);
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct resource *r;
> +	struct camss *camss = to_camss(vfe);
> +
> +	int i;
> +	int ret;
> +
> +	mutex_init(&vfe->power_lock);
> +	vfe->power_count = 0;
> +
> +	mutex_init(&vfe->stream_lock);
> +	vfe->stream_count = 0;
> +
> +	spin_lock_init(&vfe->output_lock);
> +
> +	vfe->id = 0;
> +	vfe->reg_update = 0;
> +
> +	for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++) {
> +		vfe->line[i].video_out.type =
> +					V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> +		vfe->line[i].video_out.camss = camss;
> +		vfe->line[i].id = i;
> +	}
> +
> +	/* Memory */
> +
> +	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]);
> +	vfe->base = devm_ioremap_resource(dev, r);
> +	if (IS_ERR(vfe->base)) {
> +		dev_err(dev, "could not map memory\n");

mutex_destroy() for bothof the mutexes. The same below.

Do you have a corresponding cleanup function?

> +		return PTR_ERR(vfe->base);
> +	}
> +
> +	/* Interrupt */
> +
> +	r = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
> +					 res->interrupt[0]);
> +	if (!r) {
> +		dev_err(dev, "missing IRQ\n");
> +		return -EINVAL;
> +	}
> +
> +	vfe->irq = r->start;
> +	snprintf(vfe->irq_name, sizeof(vfe->irq_name), "%s_%s%d",
> +		 dev_name(dev), MSM_VFE_NAME, vfe->id);
> +	ret = devm_request_irq(dev, vfe->irq, vfe_isr,
> +			       IRQF_TRIGGER_RISING, vfe->irq_name, vfe);
> +	if (ret < 0) {
> +		dev_err(dev, "request_irq failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* Clocks */
> +
> +	vfe->nclocks = 0;
> +	while (res->clock[vfe->nclocks])
> +		vfe->nclocks++;
> +
> +	vfe->clock = devm_kzalloc(dev, vfe->nclocks * sizeof(*vfe->clock),
> +				  GFP_KERNEL);
> +	if (!vfe->clock)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < vfe->nclocks; i++) {
> +		vfe->clock[i] = devm_clk_get(dev, res->clock[i]);
> +		if (IS_ERR(vfe->clock[i]))
> +			return PTR_ERR(vfe->clock[i]);
> +
> +		if (res->clock_rate[i]) {
> +			long clk_rate = clk_round_rate(vfe->clock[i],
> +						       res->clock_rate[i]);
> +			if (clk_rate < 0) {
> +				dev_err(dev, "clk round rate failed\n");
> +				return -EINVAL;
> +			}
> +			ret = clk_set_rate(vfe->clock[i], clk_rate);
> +			if (ret < 0) {
> +				dev_err(dev, "clk set rate failed\n");
> +				return ret;
> +			}
> +		}
> +	}
> +
> +	init_completion(&vfe->reset_complete);
> +	init_completion(&vfe->halt_complete);
> +
> +	return 0;
> +}
> +
> +/*
> + * msm_vfe_get_vfe_id - Get VFE HW module id
> + * @entity: Pointer to VFE media entity structure
> + * @id: Return CSID HW module id here
> + */
> +void msm_vfe_get_vfe_id(struct media_entity *entity, u8 *id)
> +{
> +	struct v4l2_subdev *sd;
> +	struct vfe_line *line;
> +	struct vfe_device *vfe;
> +
> +	sd = media_entity_to_v4l2_subdev(entity);
> +	line = v4l2_get_subdevdata(sd);
> +	vfe = to_vfe(line);
> +
> +	*id = vfe->id;
> +}
> +
> +/*
> + * msm_vfe_get_vfe_line_id - Get VFE line id by media entity
> + * @entity: Pointer to VFE media entity structure
> + * @id: Return VFE line id here
> + */
> +void msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id)
> +{
> +	struct v4l2_subdev *sd;
> +	struct vfe_line *line;
> +
> +	sd = media_entity_to_v4l2_subdev(entity);
> +	line = v4l2_get_subdevdata(sd);
> +
> +	*id = line->id;
> +}
> +
> +/*
> + * vfe_link_setup - Setup VFE connections
> + * @entity: Pointer to media entity structure
> + * @local: Pointer to local pad
> + * @remote: Pointer to remote pad
> + * @flags: Link flags
> + *
> + * Return 0 on success
> + */
> +static int vfe_link_setup(struct media_entity *entity,
> +			  const struct media_pad *local,
> +			  const struct media_pad *remote, u32 flags)
> +{
> +	if (flags & MEDIA_LNK_FL_ENABLED)
> +		if (media_entity_remote_pad(local))
> +			return -EBUSY;
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_subdev_core_ops vfe_core_ops = {
> +	.s_power = vfe_set_power,
> +};
> +
> +static const struct v4l2_subdev_video_ops vfe_video_ops = {
> +	.s_stream = vfe_set_stream,
> +};
> +
> +static const struct v4l2_subdev_pad_ops vfe_pad_ops = {
> +	.enum_mbus_code = vfe_enum_mbus_code,
> +	.enum_frame_size = vfe_enum_frame_size,
> +	.get_fmt = vfe_get_format,
> +	.set_fmt = vfe_set_format,
> +};
> +
> +static const struct v4l2_subdev_ops vfe_v4l2_ops = {
> +	.core = &vfe_core_ops,
> +	.video = &vfe_video_ops,
> +	.pad = &vfe_pad_ops,
> +};
> +
> +static const struct v4l2_subdev_internal_ops vfe_v4l2_internal_ops = {
> +	.open = vfe_init_formats,
> +};
> +
> +static const struct media_entity_operations vfe_media_ops = {
> +	.link_setup = vfe_link_setup,
> +	.link_validate = v4l2_subdev_link_validate,
> +};
> +
> +static const struct camss_video_ops camss_vfe_video_ops = {
> +	.queue_buffer = vfe_queue_buffer,
> +	.flush_buffers = vfe_flush_buffers,
> +};
> +
> +void msm_vfe_stop_streaming(struct vfe_device *vfe)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(vfe->line); i++)
> +		msm_video_stop_streaming(&vfe->line[i].video_out);
> +}
> +
> +/*
> + * msm_vfe_register_entities - Register subdev node for VFE module
> + * @vfe: VFE device
> + * @v4l2_dev: V4L2 device
> + *
> + * Initialize and register a subdev node for the VFE module. Then
> + * call msm_video_register() to register the video device node which
> + * will be connected to this subdev node. Then actually create the
> + * media link between them.
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +int msm_vfe_register_entities(struct vfe_device *vfe,
> +			      struct v4l2_device *v4l2_dev)
> +{
> +	struct device *dev = to_device(vfe);
> +	struct v4l2_subdev *sd;
> +	struct media_pad *pads;
> +	struct camss_video *video_out;
> +	int ret;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(vfe->line); i++) {
> +		char name[32];
> +
> +		sd = &vfe->line[i].subdev;
> +		pads = vfe->line[i].pads;
> +		video_out = &vfe->line[i].video_out;
> +
> +		v4l2_subdev_init(sd, &vfe_v4l2_ops);
> +		sd->internal_ops = &vfe_v4l2_internal_ops;
> +		sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +		snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s%d",
> +			 MSM_VFE_NAME, vfe->id, "rdi", i);
> +		v4l2_set_subdevdata(sd, &vfe->line[i]);
> +
> +		ret = vfe_init_formats(sd, NULL);
> +		if (ret < 0) {
> +			dev_err(dev, "Failed to init format: %d\n", ret);
> +			goto error_init;
> +		}
> +
> +		pads[MSM_VFE_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
> +		pads[MSM_VFE_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
> +
> +		sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
> +		sd->entity.ops = &vfe_media_ops;
> +		ret = media_entity_pads_init(&sd->entity, MSM_VFE_PADS_NUM,
> +					     pads);
> +		if (ret < 0) {
> +			dev_err(dev, "Failed to init media entity: %d\n", ret);
> +			goto error_init;
> +		}
> +
> +		ret = v4l2_device_register_subdev(v4l2_dev, sd);
> +		if (ret < 0) {
> +			dev_err(dev, "Failed to register subdev: %d\n", ret);
> +			goto error_reg_subdev;
> +		}
> +
> +		video_out->ops = &camss_vfe_video_ops;
> +		snprintf(name, ARRAY_SIZE(name), "%s%d_%s%d",
> +			 MSM_VFE_NAME, vfe->id, "video", i);
> +		ret = msm_video_register(video_out, v4l2_dev, name);
> +		if (ret < 0) {
> +			dev_err(dev, "Failed to register video node: %d\n",
> +				ret);
> +			goto error_reg_video;
> +		}
> +
> +		ret = media_create_pad_link(
> +				&sd->entity, MSM_VFE_PAD_SRC,
> +				&video_out->vdev.entity, 0,
> +				MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
> +		if (ret < 0) {
> +			dev_err(dev, "Failed to link %s->%s entities: %d\n",
> +				sd->entity.name, video_out->vdev.entity.name,
> +				ret);
> +			goto error_link;
> +		}
> +
> +		ret = msm_video_init_format(video_out);
> +		if (ret < 0) {
> +			dev_err(dev, "Failed to init format: %d\n", ret);
> +			goto error_link;
> +		}
> +
> +	}
> +
> +	return 0;
> +
> +error_link:
> +	msm_video_unregister(video_out);
> +
> +error_reg_video:
> +	v4l2_device_unregister_subdev(sd);
> +
> +error_reg_subdev:
> +	media_entity_cleanup(&sd->entity);
> +
> +error_init:
> +	for (i--; i >= 0; i--) {
> +		sd = &vfe->line[i].subdev;
> +		video_out = &vfe->line[i].video_out;
> +
> +		msm_video_unregister(video_out);
> +		v4l2_device_unregister_subdev(sd);
> +		media_entity_cleanup(&sd->entity);
> +	}
> +
> +	return ret;
> +}
> +
> +/*
> + * msm_vfe_unregister_entities - Unregister VFE module subdev node
> + * @vfe: VFE device
> + */
> +void msm_vfe_unregister_entities(struct vfe_device *vfe)
> +{
> +	int i;
> +
> +	mutex_destroy(&vfe->power_lock);
> +	mutex_destroy(&vfe->stream_lock);
> +
> +	for (i = 0; i < ARRAY_SIZE(vfe->line); i++) {
> +		struct v4l2_subdev *sd = &vfe->line[i].subdev;
> +		struct camss_video *video_out = &vfe->line[i].video_out;
> +
> +		msm_video_unregister(video_out);
> +		v4l2_device_unregister_subdev(sd);
> +		media_entity_cleanup(&sd->entity);
> +	}
> +}
> diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.h b/drivers/media/platform/qcom/camss-8x16/camss-vfe.h
> new file mode 100644
> index 0000000..6d2fc57
> --- /dev/null
> +++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.h
> @@ -0,0 +1,114 @@
> +/*
> + * camss-vfe.h
> + *
> + * Qualcomm MSM Camera Subsystem - VFE Module
> + *
> + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
> + * Copyright (C) 2015-2017 Linaro Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +#ifndef QC_MSM_CAMSS_VFE_H
> +#define QC_MSM_CAMSS_VFE_H
> +
> +#include <linux/clk.h>
> +#include <linux/spinlock_types.h>
> +#include <media/media-entity.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-subdev.h>
> +
> +#include "camss-video.h"
> +
> +#define MSM_VFE_PAD_SINK 0
> +#define MSM_VFE_PAD_SRC 1
> +#define MSM_VFE_PADS_NUM 2
> +
> +#define MSM_VFE_LINE_NUM 3
> +#define MSM_VFE_IMAGE_MASTERS_NUM 7
> +
> +#define MSM_VFE_VFE0_UB_SIZE 1023
> +#define MSM_VFE_VFE0_UB_SIZE_RDI (MSM_VFE_VFE0_UB_SIZE / 3)
> +#define MSM_VFE_VFE1_UB_SIZE 1535
> +#define MSM_VFE_VFE1_UB_SIZE_RDI (MSM_VFE_VFE1_UB_SIZE / 3)
> +
> +enum vfe_output_state {
> +	VFE_OUTPUT_OFF,
> +	VFE_OUTPUT_RESERVED,
> +	VFE_OUTPUT_SINGLE,
> +	VFE_OUTPUT_CONTINUOUS,
> +	VFE_OUTPUT_IDLE,
> +	VFE_OUTPUT_STOPPING
> +};
> +
> +enum vfe_line_id {
> +	VFE_LINE_NONE = -1,
> +	VFE_LINE_RDI0 = 0,
> +	VFE_LINE_RDI1 = 1,
> +	VFE_LINE_RDI2 = 2
> +};
> +
> +struct vfe_output {
> +	u8 wm_idx;
> +
> +	int active_buf;
> +	struct camss_buffer *buf[2];
> +	struct camss_buffer *last_buffer;
> +	struct list_head pending_bufs;
> +
> +	unsigned int drop_update_idx;
> +
> +	enum vfe_output_state state;
> +	unsigned int sequence;
> +};
> +
> +struct vfe_line {
> +	enum vfe_line_id id;
> +	struct v4l2_subdev subdev;
> +	struct media_pad pads[MSM_VFE_PADS_NUM];
> +	struct v4l2_mbus_framefmt fmt[MSM_VFE_PADS_NUM];
> +	struct camss_video video_out;
> +	struct vfe_output output;
> +};
> +
> +struct vfe_device {
> +	u8 id;
> +	void __iomem *base;
> +	u32 irq;
> +	char irq_name[30];
> +	struct clk **clock;
> +	int nclocks;
> +	struct completion reset_complete;
> +	struct completion halt_complete;
> +	struct mutex power_lock;
> +	int power_count;
> +	struct mutex stream_lock;
> +	int stream_count;
> +	spinlock_t output_lock;
> +	enum vfe_line_id wm_output_map[MSM_VFE_IMAGE_MASTERS_NUM];
> +	struct vfe_line line[MSM_VFE_LINE_NUM];
> +	u32 reg_update;
> +	u8 was_streaming;
> +};
> +
> +struct resources;
> +
> +int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res);
> +
> +int msm_vfe_register_entities(struct vfe_device *vfe,
> +			      struct v4l2_device *v4l2_dev);
> +
> +void msm_vfe_unregister_entities(struct vfe_device *vfe);
> +
> +void msm_vfe_get_vfe_id(struct media_entity *entity, u8 *id);
> +void msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id);
> +
> +void msm_vfe_stop_streaming(struct vfe_device *vfe);
> +
> +#endif /* QC_MSM_CAMSS_VFE_H */

-- 
Regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 14/23] camss: vfe: Format conversion support using PIX interface
  2017-07-17 10:33 ` [PATCH v3 14/23] camss: vfe: Format conversion support using PIX interface Todor Tomov
@ 2017-07-20 15:11   ` Sakari Ailus
  0 siblings, 0 replies; 60+ messages in thread
From: Sakari Ailus @ 2017-07-20 15:11 UTC (permalink / raw)
  To: Todor Tomov
  Cc: mchehab, hans.verkuil, javier, s.nawrocki, linux-media,
	linux-kernel, linux-arm-msm

Hi Todor,

Are you trying to get acks by posting drivers so big and complex that it'd
be unwieldy to meaningfully review them? :-)

On Mon, Jul 17, 2017 at 01:33:40PM +0300, Todor Tomov wrote:
> Use VFE PIX input interface and do format conversion in VFE.
> 
> Supported input format is UYVY (single plane YUV 4:2:2) and
> its different sample order variations.
> 
> Supported output formats are:
> - NV12/NV21 (two plane YUV 4:2:0)
> - NV16/NV61 (two plane YUV 4:2:2)
> 
> Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
> ---
>  .../media/platform/qcom/camss-8x16/camss-ispif.c   |   2 +
>  drivers/media/platform/qcom/camss-8x16/camss-vfe.c | 673 ++++++++++++++++++---
>  drivers/media/platform/qcom/camss-8x16/camss-vfe.h |  13 +-
>  .../media/platform/qcom/camss-8x16/camss-video.c   | 332 +++++++---
>  .../media/platform/qcom/camss-8x16/camss-video.h   |   8 +-
>  5 files changed, 875 insertions(+), 153 deletions(-)
> 
> diff --git a/drivers/media/platform/qcom/camss-8x16/camss-ispif.c b/drivers/media/platform/qcom/camss-8x16/camss-ispif.c
> index cc32085..04918c0 100644
> --- a/drivers/media/platform/qcom/camss-8x16/camss-ispif.c
> +++ b/drivers/media/platform/qcom/camss-8x16/camss-ispif.c
> @@ -969,6 +969,8 @@ static enum ispif_intf ispif_get_intf(enum vfe_line_id line_id)
>  		return RDI1;
>  	case (VFE_LINE_RDI2):
>  		return RDI2;
> +	case (VFE_LINE_PIX):
> +		return PIX0;
>  	default:
>  		return RDI0;
>  	}
> diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
> index b6dd29b..bef0209 100644
> --- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
> +++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
> @@ -19,6 +19,7 @@
>  #include <linux/completion.h>
>  #include <linux/interrupt.h>
>  #include <linux/iommu.h>
> +#include <linux/iopoll.h>
>  #include <linux/mutex.h>
>  #include <linux/of.h>
>  #include <linux/platform_device.h>
> @@ -52,29 +53,53 @@
>  #define VFE_0_GLOBAL_RESET_CMD_BUS_MISR	(1 << 7)
>  #define VFE_0_GLOBAL_RESET_CMD_TESTGEN	(1 << 8)
>  
> +#define VFE_0_MODULE_CFG		0x018
> +#define VFE_0_MODULE_CFG_DEMUX			(1 << 2)
> +#define VFE_0_MODULE_CFG_CHROMA_UPSAMPLE	(1 << 3)
> +#define VFE_0_MODULE_CFG_SCALE_ENC		(1 << 23)
> +
> +#define VFE_0_CORE_CFG			0x01c
> +#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR	0x4
> +#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB	0x5
> +#define VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY	0x6
> +#define VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY	0x7
> +
>  #define VFE_0_IRQ_CMD			0x024
>  #define VFE_0_IRQ_CMD_GLOBAL_CLEAR	(1 << 0)
>  
>  #define VFE_0_IRQ_MASK_0		0x028
> +#define VFE_0_IRQ_MASK_0_CAMIF_SOF			(1 << 0)
> +#define VFE_0_IRQ_MASK_0_CAMIF_EOF			(1 << 1)
>  #define VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n)		(1 << ((n) + 5))
> +#define VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(n)		\
> +	((n) == VFE_LINE_PIX ? (1 << 4) : VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n))
>  #define VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(n)	(1 << ((n) + 8))
> +#define VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(n)	(1 << ((n) + 25))
>  #define VFE_0_IRQ_MASK_0_RESET_ACK			(1 << 31)
>  #define VFE_0_IRQ_MASK_1		0x02c
> +#define VFE_0_IRQ_MASK_1_CAMIF_ERROR			(1 << 0)
>  #define VFE_0_IRQ_MASK_1_VIOLATION			(1 << 7)
>  #define VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK		(1 << 8)
>  #define VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(n)	(1 << ((n) + 9))
> +#define VFE_0_IRQ_MASK_1_RDIn_SOF(n)			(1 << ((n) + 29))
>  
>  #define VFE_0_IRQ_CLEAR_0		0x030
>  #define VFE_0_IRQ_CLEAR_1		0x034
>  
>  #define VFE_0_IRQ_STATUS_0		0x038
> +#define VFE_0_IRQ_STATUS_0_CAMIF_SOF			(1 << 0)
>  #define VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n)		(1 << ((n) + 5))
> +#define VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(n)		\
> +	((n) == VFE_LINE_PIX ? (1 << 4) : VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n))
>  #define VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(n)	(1 << ((n) + 8))
> +#define VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(n)	(1 << ((n) + 25))
>  #define VFE_0_IRQ_STATUS_0_RESET_ACK			(1 << 31)
>  #define VFE_0_IRQ_STATUS_1		0x03c
>  #define VFE_0_IRQ_STATUS_1_VIOLATION			(1 << 7)
>  #define VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK		(1 << 8)
> +#define VFE_0_IRQ_STATUS_1_RDIn_SOF(n)			(1 << ((n) + 29))
>  
> +#define VFE_0_IRQ_COMPOSITE_MASK_0	0x40
>  #define VFE_0_VIOLATION_STATUS		0x48
>  
>  #define VFE_0_BUS_CMD			0x4c
> @@ -83,7 +108,10 @@
>  #define VFE_0_BUS_CFG			0x050
>  
>  #define VFE_0_BUS_XBAR_CFG_x(x)		(0x58 + 0x4 * ((x) / 2))
> +#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN			(1 << 1)
> +#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA	(0x3 << 4)
>  #define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT		8
> +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA		0
>  #define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0	5
>  #define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1	6
>  #define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2	7
> @@ -99,6 +127,8 @@
>  
>  #define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(n)		(0x07c + 0x24 * (n))
>  #define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT	16
> +#define VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(n)	(0x080 + 0x24 * (n))
> +#define VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(n)	(0x084 + 0x24 * (n))
>  #define VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(n)	\
>  							(0x088 + 0x24 * (n))
>  #define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(n)	\
> @@ -128,8 +158,41 @@
>  #define VFE_0_RDI_CFG_x_MIPI_EN_BITS		0x3
>  #define VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(r)	(1 << (16 + (r)))
>  
> +#define VFE_0_CAMIF_CMD				0x2f4
> +#define VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY	0
> +#define VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY	1
> +#define VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS	(1 << 2)
> +#define VFE_0_CAMIF_CFG				0x2f8
> +#define VFE_0_CAMIF_CFG_VFE_OUTPUT_EN		(1 << 6)
> +#define VFE_0_CAMIF_FRAME_CFG			0x300
> +#define VFE_0_CAMIF_WINDOW_WIDTH_CFG		0x304
> +#define VFE_0_CAMIF_WINDOW_HEIGHT_CFG		0x308
> +#define VFE_0_CAMIF_SUBSAMPLE_CFG_0		0x30c
> +#define VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN	0x314
> +#define VFE_0_CAMIF_STATUS			0x31c
> +#define VFE_0_CAMIF_STATUS_HALT			(1 << 31)
> +
>  #define VFE_0_REG_UPDATE			0x378
>  #define VFE_0_REG_UPDATE_RDIn(n)		(1 << (1 + (n)))
> +#define VFE_0_REG_UPDATE_line_n(n)		\
> +			((n) == VFE_LINE_PIX ? 1 : VFE_0_REG_UPDATE_RDIn(n))
> +
> +#define VFE_0_DEMUX_CFG				0x424
> +#define VFE_0_DEMUX_GAIN_0			0x428
> +#define VFE_0_DEMUX_GAIN_1			0x42c
> +#define VFE_0_DEMUX_EVEN_CFG			0x438
> +#define VFE_0_DEMUX_ODD_CFG			0x43c
> +
> +#define VFE_0_SCALE_ENC_CBCR_CFG		0x778
> +#define VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE	0x77c
> +#define VFE_0_SCALE_ENC_CBCR_H_PHASE		0x780
> +#define VFE_0_SCALE_ENC_CBCR_H_PAD		0x78c
> +#define VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE	0x790
> +#define VFE_0_SCALE_ENC_CBCR_V_PHASE		0x794
> +#define VFE_0_SCALE_ENC_CBCR_V_PAD		0x7a0
> +
> +#define VFE_0_CLAMP_ENC_MAX_CFG			0x874
> +#define VFE_0_CLAMP_ENC_MIN_CFG			0x878
>  
>  #define VFE_0_CGC_OVERRIDE_1			0x974
>  #define VFE_0_CGC_OVERRIDE_1_IMAGE_Mx_CGC_OVERRIDE(x)	(1 << (x))
> @@ -143,6 +206,11 @@
>  /* Frame drop value. NOTE: VAL + UPDATES should not exceed 31 */
>  #define VFE_FRAME_DROP_VAL 20
>  
> +#define VFE_NEXT_SOF_MS 500
> +
> +#define CAMIF_TIMEOUT_SLEEP_US 1000
> +#define CAMIF_TIMEOUT_ALL_US 1000000
> +
>  static const u32 vfe_formats[] = {
>  	MEDIA_BUS_FMT_UYVY8_2X8,
>  	MEDIA_BUS_FMT_VYUY8_2X8,
> @@ -211,6 +279,32 @@ static void vfe_wm_frame_based(struct vfe_device *vfe, u8 wm, u8 enable)
>  			1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT);
>  }
>  
> +static void vfe_wm_line_based(struct vfe_device *vfe, u32 wm,
> +			      u16 width, u16 height, u32 enable)
> +{
> +	u32 reg;
> +
> +	if (enable) {
> +		reg = height - 1;
> +		reg |= (width / 16 - 1) << 16;
> +
> +		writel_relaxed(reg, vfe->base +
> +			       VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm));
> +
> +		reg = 0x3;
> +		reg |= (height - 1) << 4;
> +		reg |= (width / 8) << 16;
> +
> +		writel_relaxed(reg, vfe->base +
> +			       VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm));
> +	} else {
> +		writel_relaxed(0, vfe->base +
> +			       VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm));
> +		writel_relaxed(0, vfe->base +
> +			       VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm));
> +	}
> +}
> +
>  static void vfe_wm_set_framedrop_period(struct vfe_device *vfe, u8 wm, u8 per)
>  {
>  	u32 reg;
> @@ -314,7 +408,10 @@ static void vfe_bus_connect_wm_to_rdi(struct vfe_device *vfe, u8 wm,
>  		reg <<= 16;
>  
>  	vfe_reg_set(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg);
> +}
>  
> +static void vfe_wm_set_subsample(struct vfe_device *vfe, u8 wm)
> +{
>  	writel_relaxed(VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF,
>  	       vfe->base +
>  	       VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(wm));
> @@ -353,6 +450,38 @@ static void vfe_bus_disconnect_wm_from_rdi(struct vfe_device *vfe, u8 wm,
>  	vfe_reg_clr(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg);
>  }
>  
> +static void vfe_set_xbar_cfg(struct vfe_device *vfe, struct vfe_output *output,
> +			     u8 enable)
> +{
> +	struct vfe_line *line = container_of(output, struct vfe_line, output);
> +	u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
> +	u32 reg;
> +	unsigned int i;
> +
> +	for (i = 0; i < output->wm_num; i++) {
> +		if (i == 0) {
> +			reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA <<
> +				VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
> +		} else if (i == 1) {
> +			reg = VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN;
> +			if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV16)
> +				reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA;
> +		}
> +
> +		if (output->wm_idx[i] % 2 == 1)
> +			reg <<= 16;
> +
> +		if (enable)
> +			vfe_reg_set(vfe,
> +				    VFE_0_BUS_XBAR_CFG_x(output->wm_idx[i]),
> +				    reg);
> +		else
> +			vfe_reg_clr(vfe,
> +				    VFE_0_BUS_XBAR_CFG_x(output->wm_idx[i]),
> +				    reg);
> +	}
> +}
> +
>  static void vfe_set_rdi_cid(struct vfe_device *vfe, enum vfe_line_id id, u8 cid)
>  {
>  	vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id),
> @@ -364,7 +493,7 @@ static void vfe_set_rdi_cid(struct vfe_device *vfe, enum vfe_line_id id, u8 cid)
>  
>  static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
>  {
> -	vfe->reg_update |= VFE_0_REG_UPDATE_RDIn(line_id);
> +	vfe->reg_update |= VFE_0_REG_UPDATE_line_n(line_id);
>  	wmb();
>  	writel_relaxed(vfe->reg_update, vfe->base + VFE_0_REG_UPDATE);
>  	wmb();
> @@ -374,8 +503,9 @@ static void vfe_enable_irq_wm_line(struct vfe_device *vfe, u8 wm,
>  				   enum vfe_line_id line_id, u8 enable)
>  {
>  	u32 irq_en0 = VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(wm) |
> -		      VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(line_id);
> -	u32 irq_en1 = VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(wm);
> +		      VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id);
> +	u32 irq_en1 = VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(wm) |
> +		      VFE_0_IRQ_MASK_1_RDIn_SOF(line_id);
>  
>  	if (enable) {
>  		vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0);
> @@ -386,6 +516,37 @@ static void vfe_enable_irq_wm_line(struct vfe_device *vfe, u8 wm,
>  	}
>  }
>  
> +static void vfe_enable_irq_pix_line(struct vfe_device *vfe, u8 comp,
> +				    enum vfe_line_id line_id, u8 enable)
> +{
> +	struct vfe_output *output = &vfe->line[line_id].output;
> +	unsigned int i;
> +	u32 irq_en0;
> +	u32 irq_en1;
> +	u32 comp_mask = 0;
> +
> +	irq_en0 = VFE_0_IRQ_MASK_0_CAMIF_SOF;
> +	irq_en0 |= VFE_0_IRQ_MASK_0_CAMIF_EOF;
> +	irq_en0 |= VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(comp);
> +	irq_en0 |= VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id);
> +	irq_en1 = VFE_0_IRQ_MASK_1_CAMIF_ERROR;
> +	for (i = 0; i < output->wm_num; i++) {
> +		irq_en1 |= VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(
> +							output->wm_idx[i]);
> +		comp_mask |= (1 << output->wm_idx[i]) << comp * 8;
> +	}
> +
> +	if (enable) {
> +		vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0);
> +		vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1);
> +		vfe_reg_set(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask);
> +	} else {
> +		vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0);
> +		vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1);
> +		vfe_reg_clr(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask);
> +	}
> +}
> +
>  static void vfe_enable_irq_common(struct vfe_device *vfe)
>  {
>  	u32 irq_en0 = VFE_0_IRQ_MASK_0_RESET_ACK;
> @@ -396,6 +557,83 @@ static void vfe_enable_irq_common(struct vfe_device *vfe)
>  	vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1);
>  }
>  
> +static void vfe_set_demux_cfg(struct vfe_device *vfe, struct vfe_line *line)
> +{
> +	u32 even_cfg, odd_cfg;
> +
> +	writel_relaxed(0x3, vfe->base + VFE_0_DEMUX_CFG);
> +	writel_relaxed(0x800080, vfe->base + VFE_0_DEMUX_GAIN_0);
> +	writel_relaxed(0x800080, vfe->base + VFE_0_DEMUX_GAIN_1);
> +
> +	switch (line->fmt[MSM_VFE_PAD_SINK].code) {
> +	case MEDIA_BUS_FMT_YUYV8_2X8:
> +		even_cfg = 0x9cac;
> +		odd_cfg = 0x9cac;

#Defines, please? Same below.

> +		break;
> +	case MEDIA_BUS_FMT_YVYU8_2X8:
> +		even_cfg = 0xac9c;
> +		odd_cfg = 0xac9c;
> +		break;
> +	case MEDIA_BUS_FMT_UYVY8_2X8:
> +	default:
> +		even_cfg = 0xc9ca;
> +		odd_cfg = 0xc9ca;
> +		break;
> +	case MEDIA_BUS_FMT_VYUY8_2X8:
> +		even_cfg = 0xcac9;
> +		odd_cfg = 0xcac9;
> +		break;
> +	}
> +
> +	writel_relaxed(even_cfg, vfe->base + VFE_0_DEMUX_EVEN_CFG);
> +	writel_relaxed(odd_cfg, vfe->base + VFE_0_DEMUX_ODD_CFG);
> +}
> +
> +static void vfe_set_scale_cfg(struct vfe_device *vfe, struct vfe_line *line)
> +{
> +	u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
> +	u32 reg;
> +	u16 input, output;
> +	u8 interp_reso;
> +	u32 phase_mult;
> +
> +	writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_CBCR_CFG);
> +
> +	input = line->fmt[MSM_VFE_PAD_SINK].width;
> +	output = line->fmt[MSM_VFE_PAD_SRC].width / 2;
> +	reg = (output << 16) | input;
> +	writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE);
> +
> +	interp_reso = 3;
> +	phase_mult = input * (1 << (13 + interp_reso)) / output;
> +	reg = (interp_reso << 20) | phase_mult;
> +	writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_PHASE);
> +
> +	reg = input;
> +	writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_PAD);
> +
> +	input = line->fmt[MSM_VFE_PAD_SINK].height;
> +	output = line->fmt[MSM_VFE_PAD_SRC].height;
> +	if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21)
> +		output = line->fmt[MSM_VFE_PAD_SRC].height / 2;
> +	reg = (output << 16) | input;
> +	writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE);
> +
> +	interp_reso = 3;
> +	phase_mult = input * (1 << (13 + interp_reso)) / output;
> +	reg = (interp_reso << 20) | phase_mult;
> +	writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_PHASE);
> +
> +	reg = input;
> +	writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_PAD);
> +}
> +
> +static void vfe_set_clamp_cfg(struct vfe_device *vfe)
> +{
> +	writel_relaxed(0x00ffffff, vfe->base + VFE_0_CLAMP_ENC_MAX_CFG);
> +	writel_relaxed(0x0, vfe->base + VFE_0_CLAMP_ENC_MIN_CFG);

Ditto.

> +}
> +
>  /*
>   * vfe_reset - Trigger reset on VFE module and wait to complete
>   * @vfe: VFE device

-- 
Regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 15/23] doc: media/v4l-drivers: Qualcomm Camera Subsystem - PIX Interface
  2017-07-17 10:33 ` [PATCH v3 15/23] doc: media/v4l-drivers: Qualcomm Camera Subsystem - PIX Interface Todor Tomov
@ 2017-07-20 15:13   ` Sakari Ailus
  0 siblings, 0 replies; 60+ messages in thread
From: Sakari Ailus @ 2017-07-20 15:13 UTC (permalink / raw)
  To: Todor Tomov
  Cc: mchehab, hans.verkuil, javier, s.nawrocki, linux-media,
	linux-kernel, linux-arm-msm

On Mon, Jul 17, 2017 at 01:33:41PM +0300, Todor Tomov wrote:
> Update Qualcomm Camera Subsystem driver document for the PIX interface
> and format conversion support.
> 
> Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
> ---
>  Documentation/media/v4l-drivers/qcom_camss.rst | 41 +++++++++++++++++++-------
>  1 file changed, 31 insertions(+), 10 deletions(-)
> 
> diff --git a/Documentation/media/v4l-drivers/qcom_camss.rst b/Documentation/media/v4l-drivers/qcom_camss.rst
> index 4707ea7..4df5655 100644
> --- a/Documentation/media/v4l-drivers/qcom_camss.rst
> +++ b/Documentation/media/v4l-drivers/qcom_camss.rst
> @@ -45,12 +45,31 @@ Supported functionality
>  
>  The current version of the driver supports:
>  
> -- input from camera sensor via CSIPHY;
> -- generation of test input data by the TG in CSID;
> -- raw dump of the input data to memory. RDI interface of VFE is supported.
> -  PIX interface (ISP processing, statistics engines, resize/crop, format
> -  conversion) is not supported in the current version;
> -- concurrent and independent usage of two data inputs - could be camera sensors
> +- Input from camera sensor via CSIPHY;
> +- Generation of test input data by the TG in CSID;
> +- RDI interface of VFE - raw dump of the input data to memory.
> +
> +  Supported formats:
> +
> +  - YUYV/UYVY/YVYU/VYUY (packed YUV 4:2:2);
> +  - MIPI RAW8 (8bit Bayer RAW);
> +  - MIPI RAW10 (10bit packed Bayer RAW);
> +  - MIPI RAW12 (12bit packed Bayer RAW).
> +
> +- PIX interface of VFE
> +
> +  - Format conversion of the input data.
> +
> +    Supported input formats:
> +
> +    - YUYV/UYVY/YVYU/VYUY (packed YUV 4:2:2).
> +
> +    Supported output formats:
> +
> +    - NV12/NV21 (two plane YUV 4:2:0);
> +    - NV16/NV61 (two plane YUV 4:2:2).

Could you add V4L2 format names to these?

> +
> +- Concurrent and independent usage of two data inputs - could be camera sensors
>    and/or TG.
>  
>  
> @@ -65,15 +84,15 @@ interface, the driver is split into V4L2 sub-devices as follows:
>  - 2 CSID sub-devices - each CSID is represented by a single sub-device;
>  - 2 ISPIF sub-devices - ISPIF is represented by a number of sub-devices equal
>    to the number of CSID sub-devices;
> -- 3 VFE sub-devices - VFE is represented by a number of sub-devices equal to
> -  the number of RDI input interfaces.
> +- 4 VFE sub-devices - VFE is represented by a number of sub-devices equal to
> +  the number of the input interfaces (3 RDI and 1 PIX).
>  
>  The considerations to split the driver in this particular way are as follows:
>  
>  - representing CSIPHY and CSID modules by a separate sub-device for each module
>    allows to model the hardware links between these modules;
> -- representing VFE by a separate sub-devices for each RDI input interface allows
> -  to use the three RDI interfaces concurently and independently as this is
> +- representing VFE by a separate sub-devices for each input interface allows
> +  to use the input interfaces concurently and independently as this is
>    supported by the hardware;
>  - representing ISPIF by a number of sub-devices equal to the number of CSID
>    sub-devices allows to create linear media controller pipelines when using two
> @@ -99,6 +118,8 @@ nodes) is as follows:
>  - msm_vfe0_video1
>  - msm_vfe0_rdi2
>  - msm_vfe0_video2
> +- msm_vfe0_pix
> +- msm_vfe0_video3
>  
>  
>  Implementation
> -- 
> 2.7.4
> 

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 16/23] camss: vfe: Support for frame padding
  2017-07-17 10:33 ` [PATCH v3 16/23] camss: vfe: Support for frame padding Todor Tomov
@ 2017-07-20 15:17   ` Sakari Ailus
  2018-09-03 15:38     ` Todor Tomov
  0 siblings, 1 reply; 60+ messages in thread
From: Sakari Ailus @ 2017-07-20 15:17 UTC (permalink / raw)
  To: Todor Tomov
  Cc: mchehab, hans.verkuil, javier, s.nawrocki, linux-media,
	linux-kernel, linux-arm-msm

Hi Todor,

On Mon, Jul 17, 2017 at 01:33:42PM +0300, Todor Tomov wrote:
> Add support for horizontal and vertical frame padding.
> 
> Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
> ---
>  drivers/media/platform/qcom/camss-8x16/camss-vfe.c | 86 +++++++++++++++++-----
>  .../media/platform/qcom/camss-8x16/camss-video.c   | 69 ++++++++++++-----
>  .../media/platform/qcom/camss-8x16/camss-video.h   |  2 +
>  3 files changed, 121 insertions(+), 36 deletions(-)
> 
> diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
> index bef0209..327f158 100644
> --- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
> +++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
> @@ -279,21 +279,75 @@ static void vfe_wm_frame_based(struct vfe_device *vfe, u8 wm, u8 enable)
>  			1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT);
>  }
>  
> +#define CALC_WORD(width, M, N) (((width) * (M) + (N) - 1) / (N))
> +
> +static int vfe_word_per_line(uint32_t format, uint32_t pixel_per_line)
> +{
> +	int val = 0;
> +
> +	switch (format) {
> +	case V4L2_PIX_FMT_NV12:
> +	case V4L2_PIX_FMT_NV21:
> +	case V4L2_PIX_FMT_NV16:
> +	case V4L2_PIX_FMT_NV61:
> +		val = CALC_WORD(pixel_per_line, 1, 8);
> +		break;
> +	case V4L2_PIX_FMT_YUYV:
> +	case V4L2_PIX_FMT_YVYU:
> +	case V4L2_PIX_FMT_UYVY:
> +	case V4L2_PIX_FMT_VYUY:
> +		val = CALC_WORD(pixel_per_line, 2, 8);
> +		break;
> +	}
> +
> +	return val;
> +}
> +
> +static void vfe_get_wm_sizes(struct v4l2_pix_format_mplane *pix, u8 plane,
> +			     u16 *width, u16 *height, u16 *bytesperline)
> +{
> +	switch (pix->pixelformat) {
> +	case V4L2_PIX_FMT_NV12:
> +	case V4L2_PIX_FMT_NV21:
> +		*width = pix->width;
> +		*height = pix->height;
> +		*bytesperline = pix->plane_fmt[0].bytesperline;
> +		if (plane == 1)
> +			*height /= 2;
> +		break;
> +	case V4L2_PIX_FMT_NV16:
> +	case V4L2_PIX_FMT_NV61:
> +		*width = pix->width;
> +		*height = pix->height;
> +		*bytesperline = pix->plane_fmt[0].bytesperline;
> +		break;
> +	}
> +}
> +
>  static void vfe_wm_line_based(struct vfe_device *vfe, u32 wm,
> -			      u16 width, u16 height, u32 enable)
> +			      struct v4l2_pix_format_mplane *pix,
> +			      u8 plane, u32 enable)
>  {
>  	u32 reg;
>  
>  	if (enable) {
> +		u16 width = 0, height = 0, bytesperline = 0, wpl;
> +
> +		vfe_get_wm_sizes(pix, plane, &width, &height, &bytesperline);
> +
> +		wpl = vfe_word_per_line(pix->pixelformat, width);
> +
>  		reg = height - 1;
> -		reg |= (width / 16 - 1) << 16;
> +		reg |= ((wpl + 1) / 2 - 1) << 16;
>  
>  		writel_relaxed(reg, vfe->base +
>  			       VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm));
>  
> +		wpl = vfe_word_per_line(pix->pixelformat, bytesperline);
> +
>  		reg = 0x3;
>  		reg |= (height - 1) << 4;
> -		reg |= (width / 8) << 16;
> +		reg |= wpl << 16;
>  
>  		writel_relaxed(reg, vfe->base +
>  			       VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm));
> @@ -1198,25 +1252,14 @@ static int vfe_enable_output(struct vfe_line *line)
>  	} else {
>  		ub_size /= output->wm_num;
>  		for (i = 0; i < output->wm_num; i++) {
> -			u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
> -
>  			vfe_set_cgc_override(vfe, output->wm_idx[i], 1);
>  			vfe_wm_set_subsample(vfe, output->wm_idx[i]);
>  			vfe_wm_set_ub_cfg(vfe, output->wm_idx[i],
>  					  (ub_size + 1) * output->wm_idx[i],
>  					  ub_size);
> -			if ((i == 1) &&	(p == V4L2_PIX_FMT_NV12 ||
> -						p == V4L2_PIX_FMT_NV21))
> -				vfe_wm_line_based(vfe, output->wm_idx[i],
> -						  line->fmt[MSM_VFE_PAD_SRC].width,
> -						  line->fmt[MSM_VFE_PAD_SRC].height / 2,
> -						  1);
> -			else
> -				vfe_wm_line_based(vfe, output->wm_idx[i],
> -						  line->fmt[MSM_VFE_PAD_SRC].width,
> -						  line->fmt[MSM_VFE_PAD_SRC].height,
> -						  1);
> -
> +			vfe_wm_line_based(vfe, output->wm_idx[i],
> +					&line->video_out.active_fmt.fmt.pix_mp,
> +					i, 1);
>  			vfe_wm_enable(vfe, output->wm_idx[i], 1);
>  			vfe_bus_reload_wm(vfe, output->wm_idx[i]);
>  		}
> @@ -1278,7 +1321,7 @@ static int vfe_disable_output(struct vfe_line *line)
>  		spin_unlock_irqrestore(&vfe->output_lock, flags);
>  	} else {
>  		for (i = 0; i < output->wm_num; i++) {
> -			vfe_wm_line_based(vfe, output->wm_idx[i], 0, 0, 0);
> +			vfe_wm_line_based(vfe, output->wm_idx[i], NULL, i, 0);
>  			vfe_set_cgc_override(vfe, output->wm_idx[i], 0);
>  		}
>  
> @@ -2363,9 +2406,14 @@ int msm_vfe_register_entities(struct vfe_device *vfe,
>  		}
>  
>  		video_out->ops = &camss_vfe_video_ops;
> +		video_out->bpl_alignment = 8;
> +		video_out->line_based = 0;
>  		video_out->fmt_tag = CAMSS_FMT_TAG_RDI;
> -		if (i == VFE_LINE_PIX)
> +		if (i == VFE_LINE_PIX) {
> +			video_out->bpl_alignment = 16;
> +			video_out->line_based = 1;
>  			video_out->fmt_tag = CAMSS_FMT_TAG_PIX;
> +		}
>  		snprintf(name, ARRAY_SIZE(name), "%s%d_%s%d",
>  			 MSM_VFE_NAME, vfe->id, "video", i);
>  		ret = msm_video_register(video_out, v4l2_dev, name);
> diff --git a/drivers/media/platform/qcom/camss-8x16/camss-video.c b/drivers/media/platform/qcom/camss-8x16/camss-video.c
> index c5ebf5c..5a2bf18 100644
> --- a/drivers/media/platform/qcom/camss-8x16/camss-video.c
> +++ b/drivers/media/platform/qcom/camss-8x16/camss-video.c
> @@ -194,13 +194,15 @@ static int video_find_format_n(u32 code, u32 index, enum camss_fmt_tag tag)
>   * @mbus: v4l2_mbus_framefmt format
>   * @pix: v4l2_pix_format_mplane format (output)
>   * @index: index of an entry in formats array to be used for the conversion
> + * @alignment: bytesperline alignment value
>   *
>   * Fill the output pix structure with information from the input mbus format.
>   *
>   * Return 0 on success or a negative error code otherwise
>   */
>  static int video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt *mbus,
> -				struct v4l2_pix_format_mplane *pix, int index)
> +				struct v4l2_pix_format_mplane *pix, int index,
> +				unsigned int alignment)
>  {
>  	const struct format_info *f;
>  	unsigned int i;
> @@ -214,7 +216,7 @@ static int video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt *mbus,
>  	for (i = 0; i < pix->num_planes; i++) {
>  		bytesperline = pix->width / f->hsub[i].numerator *
>  			f->hsub[i].denominator * f->bpp[i] / 8;
> -		bytesperline = ALIGN(bytesperline, 8);
> +		bytesperline = ALIGN(bytesperline, alignment);
>  		pix->plane_fmt[i].bytesperline = bytesperline;
>  		pix->plane_fmt[i].sizeimage = pix->height /
>  				f->vsub[i].numerator * f->vsub[i].denominator *
> @@ -267,7 +269,8 @@ static int video_get_subdev_format(struct camss_video *video,
>  	if (ret < 0)
>  		return ret;
>  
> -	return video_mbus_to_pix_mp(&fmt.format, &format->fmt.pix_mp, ret);
> +	return video_mbus_to_pix_mp(&fmt.format, &format->fmt.pix_mp, ret,
> +				    video->bpl_alignment);
>  }
>  
>  static int video_get_pixelformat(struct camss_video *video, u32 *pixelformat,
> @@ -395,7 +398,6 @@ static int video_check_format(struct camss_video *video)
>  	struct v4l2_pix_format_mplane *pix = &video->active_fmt.fmt.pix_mp;
>  	struct v4l2_pix_format_mplane *sd_pix;
>  	struct v4l2_format format;
> -	unsigned int i;
>  	int ret;
>  
>  	sd_pix = &format.fmt.pix_mp;
> @@ -411,13 +413,6 @@ static int video_check_format(struct camss_video *video)
>  	    pix->field != format.fmt.pix_mp.field)
>  		return -EINVAL;
>  
> -	for (i = 0; i < pix->num_planes; i++)
> -		if (pix->plane_fmt[i].bytesperline !=
> -				sd_pix->plane_fmt[i].bytesperline ||
> -		    pix->plane_fmt[i].sizeimage !=
> -				sd_pix->plane_fmt[i].sizeimage)
> -			return -EINVAL;
> -
>  	return 0;
>  }
>  
> @@ -542,28 +537,68 @@ static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
>  	return 0;
>  }
>  
> -static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
> +static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
>  {
>  	struct camss_video *video = video_drvdata(file);
> +	struct v4l2_plane_pix_format *p;
> +	u32 bytesperline[3] = { 0 };
> +	u32 sizeimage[3] = { 0 };
> +	u32 lines;
>  	int ret;
> +	int i;
>  
> -	if (vb2_is_busy(&video->vb2_q))
> -		return -EBUSY;
> +	if (video->line_based)
> +		for (i = 0; i < f->fmt.pix_mp.num_planes && i < 3; i++) {
> +			p = &f->fmt.pix_mp.plane_fmt[i];
> +			bytesperline[i] = clamp_t(u32, p->bytesperline,
> +						  1, 65528);
> +			sizeimage[i] = clamp_t(u32, p->sizeimage,
> +					       bytesperline[i],
> +					       bytesperline[i] * 4096);
> +		}
>  
>  	ret = video_get_subdev_format(video, f);
>  	if (ret < 0)
>  		return ret;

If you take the width and height from the sub-device format, then for the
user to figure out how big a buffer is needed for a particular format it
takes to change the sub-device format.

I wouldn't do this but keep the image dimensions on the video node
independent of what's configured on the sub-device.

This patch doesn't really change the behaviour, but a patch before this
one. That's where the fix should be (as well).

>  
> -	video->active_fmt = *f;
> +	if (video->line_based)
> +		for (i = 0; i < f->fmt.pix_mp.num_planes; i++) {
> +			p = &f->fmt.pix_mp.plane_fmt[i];
> +			p->bytesperline = clamp_t(u32, p->bytesperline,
> +						  1, 65528);
> +			p->sizeimage = clamp_t(u32, p->sizeimage,
> +					       p->bytesperline,
> +					       p->bytesperline * 4096);
> +			lines = p->sizeimage / p->bytesperline;
> +
> +			if (p->bytesperline < bytesperline[i])
> +				p->bytesperline = ALIGN(bytesperline[i], 8);
> +
> +			if (p->sizeimage < p->bytesperline * lines)
> +				p->sizeimage = p->bytesperline * lines;
> +
> +			if (p->sizeimage < sizeimage[i])
> +				p->sizeimage = sizeimage[i];
> +		}
>  
>  	return 0;
>  }
>  
> -static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
> +static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
>  {
>  	struct camss_video *video = video_drvdata(file);
> +	int ret;
> +
> +	if (vb2_is_busy(&video->vb2_q))
> +		return -EBUSY;
>  
> -	return video_get_subdev_format(video, f);
> +	ret = video_try_fmt(file, fh, f);
> +	if (ret < 0)
> +		return ret;
> +
> +	video->active_fmt = *f;
> +
> +	return 0;
>  }
>  
>  static int video_enum_input(struct file *file, void *fh,
> diff --git a/drivers/media/platform/qcom/camss-8x16/camss-video.h b/drivers/media/platform/qcom/camss-8x16/camss-video.h
> index eff6b3d..e36a75b 100644
> --- a/drivers/media/platform/qcom/camss-8x16/camss-video.h
> +++ b/drivers/media/platform/qcom/camss-8x16/camss-video.h
> @@ -57,6 +57,8 @@ struct camss_video {
>  	const struct camss_video_ops *ops;
>  	struct mutex lock;
>  	struct mutex q_lock;
> +	unsigned int bpl_alignment;
> +	unsigned int line_based;
>  	enum camss_fmt_tag fmt_tag;
>  };
>  

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 17/23] camss: vfe: Add interface for scaling
  2017-07-17 10:33 ` [PATCH v3 17/23] camss: vfe: Add interface for scaling Todor Tomov
@ 2017-07-20 15:20   ` Sakari Ailus
  2017-07-25 15:36     ` Todor Tomov
  0 siblings, 1 reply; 60+ messages in thread
From: Sakari Ailus @ 2017-07-20 15:20 UTC (permalink / raw)
  To: Todor Tomov
  Cc: mchehab, hans.verkuil, javier, s.nawrocki, linux-media,
	linux-kernel, linux-arm-msm

Hi Todor,

On Mon, Jul 17, 2017 at 01:33:43PM +0300, Todor Tomov wrote:
> Add compose selection ioctls to handle scaling configuration.
> 
> Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
> ---
>  drivers/media/platform/qcom/camss-8x16/camss-vfe.c | 189 ++++++++++++++++++++-
>  drivers/media/platform/qcom/camss-8x16/camss-vfe.h |   1 +
>  2 files changed, 188 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
> index 327f158..8ec6ce7 100644
> --- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
> +++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
> @@ -211,6 +211,8 @@
>  #define CAMIF_TIMEOUT_SLEEP_US 1000
>  #define CAMIF_TIMEOUT_ALL_US 1000000
>  
> +#define SCALER_RATIO_MAX 16
> +
>  static const u32 vfe_formats[] = {
>  	MEDIA_BUS_FMT_UYVY8_2X8,
>  	MEDIA_BUS_FMT_VYUY8_2X8,
> @@ -1905,6 +1907,25 @@ __vfe_get_format(struct vfe_line *line,
>  	return &line->fmt[pad];
>  }
>  
> +/*
> + * __vfe_get_compose - Get pointer to compose selection structure
> + * @line: VFE line
> + * @cfg: V4L2 subdev pad configuration
> + * @which: TRY or ACTIVE format
> + *
> + * Return pointer to TRY or ACTIVE compose rectangle structure
> + */
> +static struct v4l2_rect *
> +__vfe_get_compose(struct vfe_line *line,
> +		  struct v4l2_subdev_pad_config *cfg,
> +		  enum v4l2_subdev_format_whence which)
> +{
> +	if (which == V4L2_SUBDEV_FORMAT_TRY)
> +		return v4l2_subdev_get_try_compose(&line->subdev, cfg,
> +						   MSM_VFE_PAD_SINK);
> +
> +	return &line->compose;
> +}
>  
>  /*
>   * vfe_try_format - Handle try format by pad subdev method
> @@ -1951,7 +1972,14 @@ static void vfe_try_format(struct vfe_line *line,
>  		*fmt = *__vfe_get_format(line, cfg, MSM_VFE_PAD_SINK,
>  					 which);
>  
> -		if (line->id == VFE_LINE_PIX)
> +		if (line->id == VFE_LINE_PIX) {
> +			struct v4l2_rect *rect;
> +
> +			rect = __vfe_get_compose(line, cfg, which);
> +
> +			fmt->width = rect->width;
> +			fmt->height = rect->height;
> +
>  			switch (fmt->code) {
>  			case MEDIA_BUS_FMT_YUYV8_2X8:
>  				if (code == MEDIA_BUS_FMT_YUYV8_1_5X8)
> @@ -1979,6 +2007,7 @@ static void vfe_try_format(struct vfe_line *line,
>  					fmt->code = MEDIA_BUS_FMT_VYUY8_2X8;
>  				break;
>  			}
> +		}
>  
>  		break;
>  	}
> @@ -1987,6 +2016,50 @@ static void vfe_try_format(struct vfe_line *line,
>  }
>  
>  /*
> + * vfe_try_compose - Handle try compose selection by pad subdev method
> + * @line: VFE line
> + * @cfg: V4L2 subdev pad configuration
> + * @rect: pointer to v4l2 rect structure
> + * @which: wanted subdev format
> + */
> +static void vfe_try_compose(struct vfe_line *line,
> +			    struct v4l2_subdev_pad_config *cfg,
> +			    struct v4l2_rect *rect,
> +			    enum v4l2_subdev_format_whence which)
> +{
> +	struct v4l2_mbus_framefmt *fmt;
> +
> +	rect->width = rect->width - rect->left;
> +	rect->left = 0;

This is the compose rectangle i.e. left and top should be zero (unless it's
about composing on e.g. a frame buffer). No need to decrement from width;
similarly for height below.

> +	rect->height = rect->height - rect->top;
> +	rect->top = 0;
> +
> +	fmt = __vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, which);
> +
> +	if (rect->width > fmt->width)
> +		rect->width = fmt->width;
> +
> +	if (rect->height > fmt->height)
> +		rect->height = fmt->height;
> +
> +	if (fmt->width > rect->width * SCALER_RATIO_MAX)
> +		rect->width = (fmt->width + SCALER_RATIO_MAX - 1) /
> +							SCALER_RATIO_MAX;
> +
> +	rect->width &= ~0x1;
> +
> +	if (fmt->height > rect->height * SCALER_RATIO_MAX)
> +		rect->height = (fmt->height + SCALER_RATIO_MAX - 1) /
> +							SCALER_RATIO_MAX;
> +
> +	if (rect->width < 16)
> +		rect->width = 16;
> +
> +	if (rect->height < 4)
> +		rect->height = 4;
> +}
> +
> +/*
>   * vfe_enum_mbus_code - Handle pixel format enumeration
>   * @sd: VFE V4L2 subdevice
>   * @cfg: V4L2 subdev pad configuration
> @@ -2081,6 +2154,10 @@ static int vfe_get_format(struct v4l2_subdev *sd,
>  	return 0;
>  }
>  
> +static int vfe_set_selection(struct v4l2_subdev *sd,
> +			     struct v4l2_subdev_pad_config *cfg,
> +			     struct v4l2_subdev_selection *sel);
> +
>  /*
>   * vfe_set_format - Handle set format by pads subdev method
>   * @sd: VFE V4L2 subdevice
> @@ -2103,20 +2180,126 @@ static int vfe_set_format(struct v4l2_subdev *sd,
>  	vfe_try_format(line, cfg, fmt->pad, &fmt->format, fmt->which);
>  	*format = fmt->format;
>  
> -	/* Propagate the format from sink to source */
>  	if (fmt->pad == MSM_VFE_PAD_SINK) {
> +		struct v4l2_subdev_selection sel = { 0 };
> +		int ret;
> +
> +		/* Propagate the format from sink to source */
>  		format = __vfe_get_format(line, cfg, MSM_VFE_PAD_SRC,
>  					  fmt->which);
>  
>  		*format = fmt->format;
>  		vfe_try_format(line, cfg, MSM_VFE_PAD_SRC, format,
>  			       fmt->which);
> +
> +		if (line->id != VFE_LINE_PIX)
> +			return 0;
> +
> +		/* Reset sink pad compose selection */
> +		sel.which = fmt->which;
> +		sel.pad = MSM_VFE_PAD_SINK;
> +		sel.target = V4L2_SEL_TGT_COMPOSE;
> +		sel.r.width = fmt->format.width;
> +		sel.r.height = fmt->format.height;
> +		ret = vfe_set_selection(sd, cfg, &sel);
> +		if (ret < 0)
> +			return ret;
>  	}
>  
>  	return 0;
>  }
>  
>  /*
> + * vfe_get_selection - Handle get selection by pads subdev method
> + * @sd: VFE V4L2 subdevice
> + * @cfg: V4L2 subdev pad configuration
> + * @sel: pointer to v4l2 subdev selection structure
> + *
> + * Return -EINVAL or zero on success
> + */
> +static int vfe_get_selection(struct v4l2_subdev *sd,
> +			     struct v4l2_subdev_pad_config *cfg,
> +			     struct v4l2_subdev_selection *sel)
> +{
> +	struct vfe_line *line = v4l2_get_subdevdata(sd);
> +	struct v4l2_subdev_format fmt = { 0 };
> +	struct v4l2_rect *compose;
> +	int ret;
> +
> +	if (line->id != VFE_LINE_PIX || sel->pad != MSM_VFE_PAD_SINK)
> +		return -EINVAL;
> +
> +	switch (sel->target) {
> +	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
> +		fmt.pad = sel->pad;
> +		fmt.which = sel->which;
> +		ret = vfe_get_format(sd, cfg, &fmt);
> +		if (ret < 0)
> +			return ret;
> +		sel->r.left = 0;
> +		sel->r.top = 0;
> +		sel->r.width = fmt.format.width;
> +		sel->r.height = fmt.format.height;
> +		break;
> +	case V4L2_SEL_TGT_COMPOSE:
> +		compose = __vfe_get_compose(line, cfg, sel->which);
> +		if (compose == NULL)
> +			return -EINVAL;
> +
> +		sel->r = *compose;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * vfe_set_selection - Handle set selection by pads subdev method
> + * @sd: VFE V4L2 subdevice
> + * @cfg: V4L2 subdev pad configuration
> + * @sel: pointer to v4l2 subdev selection structure
> + *
> + * Return -EINVAL or zero on success
> + */
> +int vfe_set_selection(struct v4l2_subdev *sd,
> +			     struct v4l2_subdev_pad_config *cfg,
> +			     struct v4l2_subdev_selection *sel)
> +{
> +	struct vfe_line *line = v4l2_get_subdevdata(sd);
> +	struct v4l2_rect *compose;
> +	struct v4l2_subdev_format fmt = { 0 };
> +	int ret;
> +
> +	if (line->id != VFE_LINE_PIX || sel->pad != MSM_VFE_PAD_SINK)
> +		return -EINVAL;
> +
> +	if (sel->target != V4L2_SEL_TGT_COMPOSE)
> +		return -EINVAL;
> +
> +	compose = __vfe_get_compose(line, cfg, sel->which);
> +	if (compose == NULL)
> +		return -EINVAL;
> +
> +	vfe_try_compose(line, cfg, &sel->r, sel->which);
> +	*compose = sel->r;
> +
> +	/* Reset source pad format width and height */
> +	fmt.which = sel->which;
> +	fmt.pad = MSM_VFE_PAD_SRC;
> +	ret = vfe_get_format(sd, cfg, &fmt);
> +	if (ret < 0)
> +		return ret;
> +
> +	fmt.format.width = compose->width;
> +	fmt.format.height = compose->height;
> +	ret = vfe_set_format(sd, cfg, &fmt);
> +
> +	return ret;
> +}
> +
> +/*
>   * vfe_init_formats - Initialize formats on all pads
>   * @sd: VFE V4L2 subdevice
>   * @fh: V4L2 subdev file handle
> @@ -2310,6 +2493,8 @@ static const struct v4l2_subdev_pad_ops vfe_pad_ops = {
>  	.enum_frame_size = vfe_enum_frame_size,
>  	.get_fmt = vfe_get_format,
>  	.set_fmt = vfe_set_format,
> +	.get_selection = vfe_get_selection,
> +	.set_selection = vfe_set_selection,
>  };
>  
>  static const struct v4l2_subdev_ops vfe_v4l2_ops = {
> diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.h b/drivers/media/platform/qcom/camss-8x16/camss-vfe.h
> index b0598e4..6518c7a 100644
> --- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.h
> +++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.h
> @@ -80,6 +80,7 @@ struct vfe_line {
>  	struct v4l2_subdev subdev;
>  	struct media_pad pads[MSM_VFE_PADS_NUM];
>  	struct v4l2_mbus_framefmt fmt[MSM_VFE_PADS_NUM];
> +	struct v4l2_rect compose;
>  	struct camss_video video_out;
>  	struct vfe_output output;
>  };
> -- 
> 2.7.4
> 

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 22/23] camss: Use optimal clock frequency rates
  2017-07-17 10:33 ` [PATCH v3 22/23] camss: Use optimal clock frequency rates Todor Tomov
  2017-07-19 15:59     ` kbuild test robot
@ 2017-07-20 15:23   ` Sakari Ailus
  1 sibling, 0 replies; 60+ messages in thread
From: Sakari Ailus @ 2017-07-20 15:23 UTC (permalink / raw)
  To: Todor Tomov
  Cc: mchehab, hans.verkuil, javier, s.nawrocki, linux-media,
	linux-kernel, linux-arm-msm

Hi Todor,

On Mon, Jul 17, 2017 at 01:33:48PM +0300, Todor Tomov wrote:
> Use standard V4L2 control to get pixel clock rate from a sensor
> linked in the media controller pipeline. Then calculate clock
> rates on CSIPHY, CSID and VFE to use the lowest possible.
> 
> If the currnet pixel clock rate of the sensor cannot be read then
> use the highest possible. This case covers also the CSID test
> generator usage.
> 
> If VFE is already powered on by another pipeline, check that the
> current VFE clock rate is high enough for the new pipeline.
> If not return busy error code as VFE clock rate cannot be changed
> while VFE is running.
> 
> Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
> ---
>  .../media/platform/qcom/camss-8x16/camss-csid.c    | 324 ++++++++++++++-------
>  .../media/platform/qcom/camss-8x16/camss-csid.h    |   2 +-
>  .../media/platform/qcom/camss-8x16/camss-csiphy.c  | 112 +++++--
>  .../media/platform/qcom/camss-8x16/camss-csiphy.h  |   2 +-
>  .../media/platform/qcom/camss-8x16/camss-ispif.c   |  23 +-
>  .../media/platform/qcom/camss-8x16/camss-ispif.h   |   4 +-
>  drivers/media/platform/qcom/camss-8x16/camss-vfe.c | 289 +++++++++++++++---
>  drivers/media/platform/qcom/camss-8x16/camss-vfe.h |   2 +-
>  drivers/media/platform/qcom/camss-8x16/camss.c     |  51 +++-
>  drivers/media/platform/qcom/camss-8x16/camss.h     |  17 +-
>  10 files changed, 634 insertions(+), 192 deletions(-)
> 
> diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csid.c b/drivers/media/platform/qcom/camss-8x16/camss-csid.c
> index 2bf3415..5c0e359 100644
> --- a/drivers/media/platform/qcom/camss-8x16/camss-csid.c
> +++ b/drivers/media/platform/qcom/camss-8x16/camss-csid.c
> @@ -68,122 +68,224 @@ static const struct {
>  	u8 data_type;
>  	u8 decode_format;
>  	u8 uncompr_bpp;
> +	u8 spp; /* bus samples per pixel */
>  } csid_input_fmts[] = {
>  	{
>  		MEDIA_BUS_FMT_UYVY8_2X8,
>  		MEDIA_BUS_FMT_UYVY8_2X8,
>  		DATA_TYPE_YUV422_8BIT,
>  		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
> -		16
> +		8,
> +		2

If your original patch had added the comma on the previous line, this would
be simply adding a line per array entry.

>  	},
>  	{
>  		MEDIA_BUS_FMT_VYUY8_2X8,
>  		MEDIA_BUS_FMT_VYUY8_2X8,
>  		DATA_TYPE_YUV422_8BIT,
>  		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
> -		16
> +		8,
> +		2
>  	},
>  	{
>  		MEDIA_BUS_FMT_YUYV8_2X8,
>  		MEDIA_BUS_FMT_YUYV8_2X8,
>  		DATA_TYPE_YUV422_8BIT,
>  		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
> -		16
> +		8,
> +		2
>  	},
>  	{
>  		MEDIA_BUS_FMT_YVYU8_2X8,
>  		MEDIA_BUS_FMT_YVYU8_2X8,
>  		DATA_TYPE_YUV422_8BIT,
>  		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
> -		16
> +		8,
> +		2
>  	},
>  	{
>  		MEDIA_BUS_FMT_SBGGR8_1X8,
>  		MEDIA_BUS_FMT_SBGGR8_1X8,
>  		DATA_TYPE_RAW_8BIT,
>  		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
> -		8
> +		8,
> +		1
>  	},
>  	{
>  		MEDIA_BUS_FMT_SGBRG8_1X8,
>  		MEDIA_BUS_FMT_SGBRG8_1X8,
>  		DATA_TYPE_RAW_8BIT,
>  		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
> -		8
> +		8,
> +		1
>  	},
>  	{
>  		MEDIA_BUS_FMT_SGRBG8_1X8,
>  		MEDIA_BUS_FMT_SGRBG8_1X8,
>  		DATA_TYPE_RAW_8BIT,
>  		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
> -		8
> +		8,
> +		1
>  	},
>  	{
>  		MEDIA_BUS_FMT_SRGGB8_1X8,
>  		MEDIA_BUS_FMT_SRGGB8_1X8,
>  		DATA_TYPE_RAW_8BIT,
>  		DECODE_FORMAT_UNCOMPRESSED_8_BIT,
> -		8
> +		8,
> +		1
>  	},
>  	{
>  		MEDIA_BUS_FMT_SBGGR10_1X10,
>  		MEDIA_BUS_FMT_SBGGR10_1X10,
>  		DATA_TYPE_RAW_10BIT,
>  		DECODE_FORMAT_UNCOMPRESSED_10_BIT,
> -		10
> +		10,
> +		1
>  	},
>  	{
>  		MEDIA_BUS_FMT_SGBRG10_1X10,
>  		MEDIA_BUS_FMT_SGBRG10_1X10,
>  		DATA_TYPE_RAW_10BIT,
>  		DECODE_FORMAT_UNCOMPRESSED_10_BIT,
> -		10
> +		10,
> +		1
>  	},
>  	{
>  		MEDIA_BUS_FMT_SGRBG10_1X10,
>  		MEDIA_BUS_FMT_SGRBG10_1X10,
>  		DATA_TYPE_RAW_10BIT,
>  		DECODE_FORMAT_UNCOMPRESSED_10_BIT,
> -		10
> +		10,
> +		1
>  	},
>  	{
>  		MEDIA_BUS_FMT_SRGGB10_1X10,
>  		MEDIA_BUS_FMT_SRGGB10_1X10,
>  		DATA_TYPE_RAW_10BIT,
>  		DECODE_FORMAT_UNCOMPRESSED_10_BIT,
> -		10
> +		10,
> +		1
>  	},
>  	{
>  		MEDIA_BUS_FMT_SBGGR12_1X12,
>  		MEDIA_BUS_FMT_SBGGR12_1X12,
>  		DATA_TYPE_RAW_12BIT,
>  		DECODE_FORMAT_UNCOMPRESSED_12_BIT,
> -		12
> +		12,
> +		1
>  	},
>  	{
>  		MEDIA_BUS_FMT_SGBRG12_1X12,
>  		MEDIA_BUS_FMT_SGBRG12_1X12,
>  		DATA_TYPE_RAW_12BIT,
>  		DECODE_FORMAT_UNCOMPRESSED_12_BIT,
> -		12
> +		12,
> +		1
>  	},
>  	{
>  		MEDIA_BUS_FMT_SGRBG12_1X12,
>  		MEDIA_BUS_FMT_SGRBG12_1X12,
>  		DATA_TYPE_RAW_12BIT,
>  		DECODE_FORMAT_UNCOMPRESSED_12_BIT,
> -		12
> +		12,
> +		1
>  	},
>  	{
>  		MEDIA_BUS_FMT_SRGGB12_1X12,
>  		MEDIA_BUS_FMT_SRGGB12_1X12,
>  		DATA_TYPE_RAW_12BIT,
>  		DECODE_FORMAT_UNCOMPRESSED_12_BIT,
> -		12
> +		12,
> +		1
>  	}
>  };
>  
>  /*
> + * csid_get_uncompressed - map media bus format to uncompressed media bus format
> + * @code: media bus format code
> + *
> + * Return uncompressed media bus format code
> + */
> +static u32 csid_get_uncompressed(u32 code)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
> +		if (code == csid_input_fmts[i].code)
> +			break;
> +
> +	return csid_input_fmts[i].uncompressed;
> +}

I think my suggestion of returning an array entry (a struct) was a good
one. :-)

> +
> +/*
> + * csid_get_data_type - map media bus format to data type
> + * @code: media bus format code
> + *
> + * Return data type code
> + */
> +static u8 csid_get_data_type(u32 code)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
> +		if (code == csid_input_fmts[i].code)
> +			break;
> +
> +	return csid_input_fmts[i].data_type;
> +}
> +
> +/*
> + * csid_get_decode_format - map media bus format to decode format
> + * @code: media bus format code
> + *
> + * Return decode format code
> + */
> +static u8 csid_get_decode_format(u32 code)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
> +		if (code == csid_input_fmts[i].code)
> +			break;
> +
> +	return csid_input_fmts[i].decode_format;
> +}
> +
> +/*
> + * csid_get_bpp - map media bus format to bits per pixel
> + * @code: media bus format code
> + *
> + * Return number of bits per pixel
> + */
> +static u8 csid_get_bpp(u32 code)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
> +		if (code == csid_input_fmts[i].uncompressed)
> +			break;
> +
> +	return csid_input_fmts[i].uncompr_bpp;
> +}
> +
> +/*
> + * csid_get_spp - map media bus format to bus samples per pixel
> + * @code: media bus format code
> + *
> + * Return number of bus samples per pixel
> + */
> +static u8 csid_get_spp(u32 code)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
> +		if (code == csid_input_fmts[i].uncompressed)
> +			break;
> +
> +	return csid_input_fmts[i].spp;
> +}
> +
> +/*
>   * csid_isr - CSID module interrupt handler
>   * @irq: Interrupt line
>   * @dev: CSID device

-- 
Regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver
  2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
                   ` (23 preceding siblings ...)
  2017-07-19 10:54 ` [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Hans Verkuil
@ 2017-07-20 15:25 ` Sakari Ailus
  2017-07-21  7:50   ` Todor Tomov
  24 siblings, 1 reply; 60+ messages in thread
From: Sakari Ailus @ 2017-07-20 15:25 UTC (permalink / raw)
  To: Todor Tomov
  Cc: mchehab, hans.verkuil, javier, s.nawrocki, linux-media,
	linux-kernel, linux-arm-msm

Hi Todor,

On Mon, Jul 17, 2017 at 01:33:26PM +0300, Todor Tomov wrote:
> This patchset adds basic support for the Qualcomm Camera Subsystem found
> on Qualcomm MSM8916 and APQ8016 processors.
> 
> The driver implements V4L2, Media controller and V4L2 subdev interfaces.
> Camera sensor using V4L2 subdev interface in the kernel is supported.
> 
> The driver is implemented using as a reference the Qualcomm Camera
> Subsystem driver for Android as found in Code Aurora [1].
> 
> The driver is tested on Dragonboard 410C (APQ8016) with one and two
> OV5645 camera sensors. media-ctl [2] and yavta [3] applications were
> used for testing. Also Gstreamer 1.10.4 with v4l2src plugin is supported.
> 
> More information is present in the document added by the third patch.

After addressing the comments (please pay attention especially those
affecting the user space API behaviour) you can add:

Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>

Let me know if you have any further questions on the individual comments.

-- 
Regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver
  2017-07-19 10:54 ` [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Hans Verkuil
@ 2017-07-21  7:39   ` Todor Tomov
  2017-07-31  9:20   ` Hans Verkuil
  1 sibling, 0 replies; 60+ messages in thread
From: Todor Tomov @ 2017-07-21  7:39 UTC (permalink / raw)
  To: Hans Verkuil, mchehab, hans.verkuil, javier, s.nawrocki,
	sakari.ailus, linux-media, linux-kernel, linux-arm-msm

Hello Hans,

On 19.07.2017 13:54, Hans Verkuil wrote:
> On 17/07/17 12:33, Todor Tomov wrote:
>> This patchset adds basic support for the Qualcomm Camera Subsystem found
>> on Qualcomm MSM8916 and APQ8016 processors.
>>
>> The driver implements V4L2, Media controller and V4L2 subdev interfaces.
>> Camera sensor using V4L2 subdev interface in the kernel is supported.
>>
>> The driver is implemented using as a reference the Qualcomm Camera
>> Subsystem driver for Android as found in Code Aurora [1].
>>
>> The driver is tested on Dragonboard 410C (APQ8016) with one and two
>> OV5645 camera sensors. media-ctl [2] and yavta [3] applications were
>> used for testing. Also Gstreamer 1.10.4 with v4l2src plugin is supported.
>>
>> More information is present in the document added by the third patch.
> 
> OK, so this looks pretty good. I have one comment for patch 12/23, and the
> dt-bindings need to be acked.
> 
> I suggest you make a v3.1 for patch 12/23 and then I'll wait for the binding
> ack. Once that's in (and there are no other comments) I will merge this.

Thank you for the review!
I'll update patch 12/23 and will send in the next version along with the rest
of the fixes from the review.

> 
> Regards,
> 
> 	Hans
> 

-- 
Best regards,
Todor Tomov

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver
  2017-07-20 15:25 ` Sakari Ailus
@ 2017-07-21  7:50   ` Todor Tomov
  0 siblings, 0 replies; 60+ messages in thread
From: Todor Tomov @ 2017-07-21  7:50 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: mchehab, hans.verkuil, javier, s.nawrocki, linux-media,
	linux-kernel, linux-arm-msm

Hello Sakari,

Thank you for the review!

On 20.07.2017 18:25, Sakari Ailus wrote:
> Hi Todor,
> 
> On Mon, Jul 17, 2017 at 01:33:26PM +0300, Todor Tomov wrote:
>> This patchset adds basic support for the Qualcomm Camera Subsystem found
>> on Qualcomm MSM8916 and APQ8016 processors.
>>
>> The driver implements V4L2, Media controller and V4L2 subdev interfaces.
>> Camera sensor using V4L2 subdev interface in the kernel is supported.
>>
>> The driver is implemented using as a reference the Qualcomm Camera
>> Subsystem driver for Android as found in Code Aurora [1].
>>
>> The driver is tested on Dragonboard 410C (APQ8016) with one and two
>> OV5645 camera sensors. media-ctl [2] and yavta [3] applications were
>> used for testing. Also Gstreamer 1.10.4 with v4l2src plugin is supported.
>>
>> More information is present in the document added by the third patch.
> 
> After addressing the comments (please pay attention especially those
> affecting the user space API behaviour) you can add:
> 
> Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> 
> Let me know if you have any further questions on the individual comments.
> 

I'll prepare updates based on your comments. I'll reply to individual
comments only if there is something to discuss, the others I'll fix
directly.

Also, we had a discussion with Rob Herring about the device tree
binding and he requested some more opinions. If you have something
to say about this, please do. You can see the context here:

https://lkml.org/lkml/2017/6/19/280
https://lkml.org/lkml/2017/6/29/311


-- 
Best regards,
Todor Tomov

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 22/23] camss: Use optimal clock frequency rates
  2017-07-19 15:59     ` kbuild test robot
  (?)
@ 2017-07-21  7:55       ` Todor Tomov
  -1 siblings, 0 replies; 60+ messages in thread
From: Todor Tomov @ 2017-07-21  7:55 UTC (permalink / raw)
  Cc: mchehab, hans.verkuil, s.nawrocki, sakari.ailus, linux-media,
	linux-kernel, linux-arm-msm

Hello,

On 19.07.2017 18:59, kbuild test robot wrote:
> Hi Todor,
> 
> [auto build test ERROR on linuxtv-media/master]
> [also build test ERROR on v4.13-rc1]
> [if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
> 
> url:    https://github.com/0day-ci/linux/commits/Todor-Tomov/Qualcomm-8x16-Camera-Subsystem-driver/20170718-055348
> base:   git://linuxtv.org/media_tree.git master
> config: i386-allmodconfig (attached as .config)
> compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
> reproduce:
>         # save the attached .config to linux build tree
>         make ARCH=i386 
> 
> All errors (new ones prefixed by >>):
> 
>    ERROR: "__udivdi3" [fs/ufs/ufs.ko] undefined!
>>> ERROR: "__udivdi3" [drivers/media/platform/qcom/camss-8x16/qcom-camss.ko] undefined!
>    ERROR: "__divdi3" [drivers/media/platform/qcom/camss-8x16/qcom-camss.ko] undefined!

Just FYI,
I'll switch to using the proper division functions/macros so this error
will be fixed in the next version of the patchset.

> 
> ---
> 0-DAY kernel test infrastructure                Open Source Technology Center
> https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
> 

-- 
Best regards,
Todor Tomov

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 22/23] camss: Use optimal clock frequency rates
@ 2017-07-21  7:55       ` Todor Tomov
  0 siblings, 0 replies; 60+ messages in thread
From: Todor Tomov @ 2017-07-21  7:55 UTC (permalink / raw)
  Cc: mchehab, hans.verkuil, s.nawrocki, sakari.ailus, linux-media,
	linux-kernel, linux-arm-msm

Hello,

On 19.07.2017 18:59, kbuild test robot wrote:
> Hi Todor,
> 
> [auto build test ERROR on linuxtv-media/master]
> [also build test ERROR on v4.13-rc1]
> [if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
> 
> url:    https://github.com/0day-ci/linux/commits/Todor-Tomov/Qualcomm-8x16-Camera-Subsystem-driver/20170718-055348
> base:   git://linuxtv.org/media_tree.git master
> config: i386-allmodconfig (attached as .config)
> compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
> reproduce:
>         # save the attached .config to linux build tree
>         make ARCH=i386 
> 
> All errors (new ones prefixed by >>):
> 
>    ERROR: "__udivdi3" [fs/ufs/ufs.ko] undefined!
>>> ERROR: "__udivdi3" [drivers/media/platform/qcom/camss-8x16/qcom-camss.ko] undefined!
>    ERROR: "__divdi3" [drivers/media/platform/qcom/camss-8x16/qcom-camss.ko] undefined!

Just FYI,
I'll switch to using the proper division functions/macros so this error
will be fixed in the next version of the patchset.

> 
> ---
> 0-DAY kernel test infrastructure                Open Source Technology Center
> https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
> 

-- 
Best regards,
Todor Tomov

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 22/23] camss: Use optimal clock frequency rates
@ 2017-07-21  7:55       ` Todor Tomov
  0 siblings, 0 replies; 60+ messages in thread
From: Todor Tomov @ 2017-07-21  7:55 UTC (permalink / raw)
  Cc: mchehab, hans.verkuil, s.nawrocki, sakari.ailus, linux-media,
	linux-kernel, linux-arm-msm

Hello,

On 19.07.2017 18:59, kbuild test robot wrote:
> Hi Todor,
> 
> [auto build test ERROR on linuxtv-media/master]
> [also build test ERROR on v4.13-rc1]
> [if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
> 
> url:    https://github.com/0day-ci/linux/commits/Todor-Tomov/Qualcomm-8x16-Camera-Subsystem-driver/20170718-055348
> base:   git://linuxtv.org/media_tree.git master
> config: i386-allmodconfig (attached as .config)
> compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
> reproduce:
>         # save the attached .config to linux build tree
>         make ARCH=i386 
> 
> All errors (new ones prefixed by >>):
> 
>    ERROR: "__udivdi3" [fs/ufs/ufs.ko] undefined!
>>> ERROR: "__udivdi3" [drivers/media/platform/qcom/camss-8x16/qcom-camss.ko] undefined!
>    ERROR: "__divdi3" [drivers/media/platform/qcom/camss-8x16/qcom-camss.ko] undefined!

Just FYI,
I'll switch to using the proper division functions/macros so this error
will be fixed in the next version of the patchset.

> 
> ---
> 0-DAY kernel test infrastructure                Open Source Technology Center
> https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
> 

-- 
Best regards,
Todor Tomov

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 04/23] dt-bindings: media: Binding document for Qualcomm Camera subsystem driver
  2017-07-17 10:33 ` [PATCH v3 04/23] dt-bindings: media: Binding document for Qualcomm Camera subsystem driver Todor Tomov
  2017-07-20 10:13   ` Sakari Ailus
  2017-07-20 10:15   ` Sakari Ailus
@ 2017-07-24 16:43   ` Rob Herring
  2 siblings, 0 replies; 60+ messages in thread
From: Rob Herring @ 2017-07-24 16:43 UTC (permalink / raw)
  To: Todor Tomov
  Cc: mchehab, hans.verkuil, javier, s.nawrocki, sakari.ailus,
	linux-media, linux-kernel, linux-arm-msm, devicetree

On Mon, Jul 17, 2017 at 01:33:30PM +0300, Todor Tomov wrote:
> Add DT binding document for Qualcomm Camera subsystem driver.
> 
> CC: Rob Herring <robh+dt@kernel.org>
> CC: devicetree@vger.kernel.org
> Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
> ---
>  .../devicetree/bindings/media/qcom,camss.txt       | 191 +++++++++++++++++++++
>  1 file changed, 191 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/qcom,camss.txt

Acked-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 10/23] media: camss: Add VFE files
  2017-07-20 14:59   ` Sakari Ailus
@ 2017-07-25 14:02     ` Todor Tomov
  2017-08-04 18:02       ` Sakari Ailus
  0 siblings, 1 reply; 60+ messages in thread
From: Todor Tomov @ 2017-07-25 14:02 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: mchehab, hans.verkuil, javier, s.nawrocki, linux-media,
	linux-kernel, linux-arm-msm

Hi Sakari,

Thank you for the review.

On 20.07.2017 17:59, Sakari Ailus wrote:
> Hi Todor,
> 
> On Mon, Jul 17, 2017 at 01:33:36PM +0300, Todor Tomov wrote:
>> These files control the VFE module. The VFE has different input interfaces.
>> The PIX input interface feeds the input data to an image processing pipeline.
>> Three RDI input interfaces bypass the image processing pipeline. The VFE also
>> contains the AXI bus interface which writes the output data to memory.
>>
>> RDI interfaces are supported in this version. PIX interface is not supported.
>>
>> Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
>> ---
>>  drivers/media/platform/qcom/camss-8x16/camss-vfe.c | 1913 ++++++++++++++++++++
>>  drivers/media/platform/qcom/camss-8x16/camss-vfe.h |  114 ++
>>  2 files changed, 2027 insertions(+)
>>  create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-vfe.c
>>  create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-vfe.h
>>
>> diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
>> new file mode 100644
>> index 0000000..b6dd29b
>> --- /dev/null
>> +++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
>> @@ -0,0 +1,1913 @@

<snip>

>> +
>> +static void vfe_set_qos(struct vfe_device *vfe)
>> +{
>> +	u32 val = 0xaaa5aaa5;
>> +	u32 val7 = 0x0001aaa5;
> 
> Huh. What do these mean? :-)

For these here I don't have understanding of the values. I'll remove the magic
values here and on all the other places but these here I'll just move to a #define.

> 
>> +
>> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_0);
>> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_1);
>> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_2);
>> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_3);
>> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_4);
>> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_5);
>> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_6);
>> +	writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7);
>> +}
>> +

<snip>

>> +
>> +/*
>> + * msm_vfe_subdev_init - Initialize VFE device structure and resources
>> + * @vfe: VFE device
>> + * @res: VFE module resources table
>> + *
>> + * Return 0 on success or a negative error code otherwise
>> + */
>> +int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res)
>> +{
>> +	struct device *dev = to_device(vfe);
>> +	struct platform_device *pdev = to_platform_device(dev);
>> +	struct resource *r;
>> +	struct camss *camss = to_camss(vfe);
>> +
>> +	int i;
>> +	int ret;
>> +
>> +	mutex_init(&vfe->power_lock);
>> +	vfe->power_count = 0;
>> +
>> +	mutex_init(&vfe->stream_lock);
>> +	vfe->stream_count = 0;
>> +
>> +	spin_lock_init(&vfe->output_lock);
>> +
>> +	vfe->id = 0;
>> +	vfe->reg_update = 0;
>> +
>> +	for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++) {
>> +		vfe->line[i].video_out.type =
>> +					V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
>> +		vfe->line[i].video_out.camss = camss;
>> +		vfe->line[i].id = i;
>> +	}
>> +
>> +	/* Memory */
>> +
>> +	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]);
>> +	vfe->base = devm_ioremap_resource(dev, r);
>> +	if (IS_ERR(vfe->base)) {
>> +		dev_err(dev, "could not map memory\n");
> 
> mutex_destroy() for bothof the mutexes. The same below.
> 
> Do you have a corresponding cleanup function?

msm_vfe_subdev_init() and msm_vfe_register_entities() are called on probe().
msm_vfe_unregister_entities() is called on remove() - this is the cleanup
function. The mutexes are destroyed there. Is there something else that you
are concerned about?

> 
>> +		return PTR_ERR(vfe->base);
>> +	}
>> +
>> +	/* Interrupt */
>> +
>> +	r = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
>> +					 res->interrupt[0]);
>> +	if (!r) {
>> +		dev_err(dev, "missing IRQ\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	vfe->irq = r->start;
>> +	snprintf(vfe->irq_name, sizeof(vfe->irq_name), "%s_%s%d",
>> +		 dev_name(dev), MSM_VFE_NAME, vfe->id);
>> +	ret = devm_request_irq(dev, vfe->irq, vfe_isr,
>> +			       IRQF_TRIGGER_RISING, vfe->irq_name, vfe);
>> +	if (ret < 0) {
>> +		dev_err(dev, "request_irq failed: %d\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	/* Clocks */
>> +
>> +	vfe->nclocks = 0;
>> +	while (res->clock[vfe->nclocks])
>> +		vfe->nclocks++;
>> +
>> +	vfe->clock = devm_kzalloc(dev, vfe->nclocks * sizeof(*vfe->clock),
>> +				  GFP_KERNEL);
>> +	if (!vfe->clock)
>> +		return -ENOMEM;
>> +
>> +	for (i = 0; i < vfe->nclocks; i++) {
>> +		vfe->clock[i] = devm_clk_get(dev, res->clock[i]);
>> +		if (IS_ERR(vfe->clock[i]))
>> +			return PTR_ERR(vfe->clock[i]);
>> +
>> +		if (res->clock_rate[i]) {
>> +			long clk_rate = clk_round_rate(vfe->clock[i],
>> +						       res->clock_rate[i]);
>> +			if (clk_rate < 0) {
>> +				dev_err(dev, "clk round rate failed\n");
>> +				return -EINVAL;
>> +			}
>> +			ret = clk_set_rate(vfe->clock[i], clk_rate);
>> +			if (ret < 0) {
>> +				dev_err(dev, "clk set rate failed\n");
>> +				return ret;
>> +			}
>> +		}
>> +	}
>> +
>> +	init_completion(&vfe->reset_complete);
>> +	init_completion(&vfe->halt_complete);
>> +
>> +	return 0;
>> +}
>> +
>> +/*
>> + * msm_vfe_get_vfe_id - Get VFE HW module id
>> + * @entity: Pointer to VFE media entity structure
>> + * @id: Return CSID HW module id here
>> + */
>> +void msm_vfe_get_vfe_id(struct media_entity *entity, u8 *id)
>> +{
>> +	struct v4l2_subdev *sd;
>> +	struct vfe_line *line;
>> +	struct vfe_device *vfe;
>> +
>> +	sd = media_entity_to_v4l2_subdev(entity);
>> +	line = v4l2_get_subdevdata(sd);
>> +	vfe = to_vfe(line);
>> +
>> +	*id = vfe->id;
>> +}
>> +
>> +/*
>> + * msm_vfe_get_vfe_line_id - Get VFE line id by media entity
>> + * @entity: Pointer to VFE media entity structure
>> + * @id: Return VFE line id here
>> + */
>> +void msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id)
>> +{
>> +	struct v4l2_subdev *sd;
>> +	struct vfe_line *line;
>> +
>> +	sd = media_entity_to_v4l2_subdev(entity);
>> +	line = v4l2_get_subdevdata(sd);
>> +
>> +	*id = line->id;
>> +}
>> +
>> +/*
>> + * vfe_link_setup - Setup VFE connections
>> + * @entity: Pointer to media entity structure
>> + * @local: Pointer to local pad
>> + * @remote: Pointer to remote pad
>> + * @flags: Link flags
>> + *
>> + * Return 0 on success
>> + */
>> +static int vfe_link_setup(struct media_entity *entity,
>> +			  const struct media_pad *local,
>> +			  const struct media_pad *remote, u32 flags)
>> +{
>> +	if (flags & MEDIA_LNK_FL_ENABLED)
>> +		if (media_entity_remote_pad(local))
>> +			return -EBUSY;
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct v4l2_subdev_core_ops vfe_core_ops = {
>> +	.s_power = vfe_set_power,
>> +};
>> +
>> +static const struct v4l2_subdev_video_ops vfe_video_ops = {
>> +	.s_stream = vfe_set_stream,
>> +};
>> +
>> +static const struct v4l2_subdev_pad_ops vfe_pad_ops = {
>> +	.enum_mbus_code = vfe_enum_mbus_code,
>> +	.enum_frame_size = vfe_enum_frame_size,
>> +	.get_fmt = vfe_get_format,
>> +	.set_fmt = vfe_set_format,
>> +};
>> +
>> +static const struct v4l2_subdev_ops vfe_v4l2_ops = {
>> +	.core = &vfe_core_ops,
>> +	.video = &vfe_video_ops,
>> +	.pad = &vfe_pad_ops,
>> +};
>> +
>> +static const struct v4l2_subdev_internal_ops vfe_v4l2_internal_ops = {
>> +	.open = vfe_init_formats,
>> +};
>> +
>> +static const struct media_entity_operations vfe_media_ops = {
>> +	.link_setup = vfe_link_setup,
>> +	.link_validate = v4l2_subdev_link_validate,
>> +};
>> +
>> +static const struct camss_video_ops camss_vfe_video_ops = {
>> +	.queue_buffer = vfe_queue_buffer,
>> +	.flush_buffers = vfe_flush_buffers,
>> +};
>> +
>> +void msm_vfe_stop_streaming(struct vfe_device *vfe)
>> +{
>> +	int i;
>> +
>> +	for (i = 0; i < ARRAY_SIZE(vfe->line); i++)
>> +		msm_video_stop_streaming(&vfe->line[i].video_out);
>> +}
>> +
>> +/*
>> + * msm_vfe_register_entities - Register subdev node for VFE module
>> + * @vfe: VFE device
>> + * @v4l2_dev: V4L2 device
>> + *
>> + * Initialize and register a subdev node for the VFE module. Then
>> + * call msm_video_register() to register the video device node which
>> + * will be connected to this subdev node. Then actually create the
>> + * media link between them.
>> + *
>> + * Return 0 on success or a negative error code otherwise
>> + */
>> +int msm_vfe_register_entities(struct vfe_device *vfe,
>> +			      struct v4l2_device *v4l2_dev)
>> +{
>> +	struct device *dev = to_device(vfe);
>> +	struct v4l2_subdev *sd;
>> +	struct media_pad *pads;
>> +	struct camss_video *video_out;
>> +	int ret;
>> +	int i;
>> +
>> +	for (i = 0; i < ARRAY_SIZE(vfe->line); i++) {
>> +		char name[32];
>> +
>> +		sd = &vfe->line[i].subdev;
>> +		pads = vfe->line[i].pads;
>> +		video_out = &vfe->line[i].video_out;
>> +
>> +		v4l2_subdev_init(sd, &vfe_v4l2_ops);
>> +		sd->internal_ops = &vfe_v4l2_internal_ops;
>> +		sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
>> +		snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s%d",
>> +			 MSM_VFE_NAME, vfe->id, "rdi", i);
>> +		v4l2_set_subdevdata(sd, &vfe->line[i]);
>> +
>> +		ret = vfe_init_formats(sd, NULL);
>> +		if (ret < 0) {
>> +			dev_err(dev, "Failed to init format: %d\n", ret);
>> +			goto error_init;
>> +		}
>> +
>> +		pads[MSM_VFE_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
>> +		pads[MSM_VFE_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
>> +
>> +		sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
>> +		sd->entity.ops = &vfe_media_ops;
>> +		ret = media_entity_pads_init(&sd->entity, MSM_VFE_PADS_NUM,
>> +					     pads);
>> +		if (ret < 0) {
>> +			dev_err(dev, "Failed to init media entity: %d\n", ret);
>> +			goto error_init;
>> +		}
>> +
>> +		ret = v4l2_device_register_subdev(v4l2_dev, sd);
>> +		if (ret < 0) {
>> +			dev_err(dev, "Failed to register subdev: %d\n", ret);
>> +			goto error_reg_subdev;
>> +		}
>> +
>> +		video_out->ops = &camss_vfe_video_ops;
>> +		snprintf(name, ARRAY_SIZE(name), "%s%d_%s%d",
>> +			 MSM_VFE_NAME, vfe->id, "video", i);
>> +		ret = msm_video_register(video_out, v4l2_dev, name);
>> +		if (ret < 0) {
>> +			dev_err(dev, "Failed to register video node: %d\n",
>> +				ret);
>> +			goto error_reg_video;
>> +		}
>> +
>> +		ret = media_create_pad_link(
>> +				&sd->entity, MSM_VFE_PAD_SRC,
>> +				&video_out->vdev.entity, 0,
>> +				MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
>> +		if (ret < 0) {
>> +			dev_err(dev, "Failed to link %s->%s entities: %d\n",
>> +				sd->entity.name, video_out->vdev.entity.name,
>> +				ret);
>> +			goto error_link;
>> +		}
>> +
>> +		ret = msm_video_init_format(video_out);
>> +		if (ret < 0) {
>> +			dev_err(dev, "Failed to init format: %d\n", ret);
>> +			goto error_link;
>> +		}
>> +
>> +	}
>> +
>> +	return 0;
>> +
>> +error_link:
>> +	msm_video_unregister(video_out);
>> +
>> +error_reg_video:
>> +	v4l2_device_unregister_subdev(sd);
>> +
>> +error_reg_subdev:
>> +	media_entity_cleanup(&sd->entity);
>> +
>> +error_init:
>> +	for (i--; i >= 0; i--) {
>> +		sd = &vfe->line[i].subdev;
>> +		video_out = &vfe->line[i].video_out;
>> +
>> +		msm_video_unregister(video_out);
>> +		v4l2_device_unregister_subdev(sd);
>> +		media_entity_cleanup(&sd->entity);
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +/*
>> + * msm_vfe_unregister_entities - Unregister VFE module subdev node
>> + * @vfe: VFE device
>> + */
>> +void msm_vfe_unregister_entities(struct vfe_device *vfe)
>> +{
>> +	int i;
>> +
>> +	mutex_destroy(&vfe->power_lock);
>> +	mutex_destroy(&vfe->stream_lock);
>> +
>> +	for (i = 0; i < ARRAY_SIZE(vfe->line); i++) {
>> +		struct v4l2_subdev *sd = &vfe->line[i].subdev;
>> +		struct camss_video *video_out = &vfe->line[i].video_out;
>> +
>> +		msm_video_unregister(video_out);
>> +		v4l2_device_unregister_subdev(sd);
>> +		media_entity_cleanup(&sd->entity);
>> +	}
>> +}
>> diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.h b/drivers/media/platform/qcom/camss-8x16/camss-vfe.h
>> new file mode 100644
>> index 0000000..6d2fc57
>> --- /dev/null
>> +++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.h
>> @@ -0,0 +1,114 @@
>> +/*
>> + * camss-vfe.h
>> + *
>> + * Qualcomm MSM Camera Subsystem - VFE Module
>> + *
>> + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
>> + * Copyright (C) 2015-2017 Linaro Ltd.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 and
>> + * only version 2 as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +#ifndef QC_MSM_CAMSS_VFE_H
>> +#define QC_MSM_CAMSS_VFE_H
>> +
>> +#include <linux/clk.h>
>> +#include <linux/spinlock_types.h>
>> +#include <media/media-entity.h>
>> +#include <media/v4l2-device.h>
>> +#include <media/v4l2-subdev.h>
>> +
>> +#include "camss-video.h"
>> +
>> +#define MSM_VFE_PAD_SINK 0
>> +#define MSM_VFE_PAD_SRC 1
>> +#define MSM_VFE_PADS_NUM 2
>> +
>> +#define MSM_VFE_LINE_NUM 3
>> +#define MSM_VFE_IMAGE_MASTERS_NUM 7
>> +
>> +#define MSM_VFE_VFE0_UB_SIZE 1023
>> +#define MSM_VFE_VFE0_UB_SIZE_RDI (MSM_VFE_VFE0_UB_SIZE / 3)
>> +#define MSM_VFE_VFE1_UB_SIZE 1535
>> +#define MSM_VFE_VFE1_UB_SIZE_RDI (MSM_VFE_VFE1_UB_SIZE / 3)
>> +
>> +enum vfe_output_state {
>> +	VFE_OUTPUT_OFF,
>> +	VFE_OUTPUT_RESERVED,
>> +	VFE_OUTPUT_SINGLE,
>> +	VFE_OUTPUT_CONTINUOUS,
>> +	VFE_OUTPUT_IDLE,
>> +	VFE_OUTPUT_STOPPING
>> +};
>> +
>> +enum vfe_line_id {
>> +	VFE_LINE_NONE = -1,
>> +	VFE_LINE_RDI0 = 0,
>> +	VFE_LINE_RDI1 = 1,
>> +	VFE_LINE_RDI2 = 2
>> +};
>> +
>> +struct vfe_output {
>> +	u8 wm_idx;
>> +
>> +	int active_buf;
>> +	struct camss_buffer *buf[2];
>> +	struct camss_buffer *last_buffer;
>> +	struct list_head pending_bufs;
>> +
>> +	unsigned int drop_update_idx;
>> +
>> +	enum vfe_output_state state;
>> +	unsigned int sequence;
>> +};
>> +
>> +struct vfe_line {
>> +	enum vfe_line_id id;
>> +	struct v4l2_subdev subdev;
>> +	struct media_pad pads[MSM_VFE_PADS_NUM];
>> +	struct v4l2_mbus_framefmt fmt[MSM_VFE_PADS_NUM];
>> +	struct camss_video video_out;
>> +	struct vfe_output output;
>> +};
>> +
>> +struct vfe_device {
>> +	u8 id;
>> +	void __iomem *base;
>> +	u32 irq;
>> +	char irq_name[30];
>> +	struct clk **clock;
>> +	int nclocks;
>> +	struct completion reset_complete;
>> +	struct completion halt_complete;
>> +	struct mutex power_lock;
>> +	int power_count;
>> +	struct mutex stream_lock;
>> +	int stream_count;
>> +	spinlock_t output_lock;
>> +	enum vfe_line_id wm_output_map[MSM_VFE_IMAGE_MASTERS_NUM];
>> +	struct vfe_line line[MSM_VFE_LINE_NUM];
>> +	u32 reg_update;
>> +	u8 was_streaming;
>> +};
>> +
>> +struct resources;
>> +
>> +int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res);
>> +
>> +int msm_vfe_register_entities(struct vfe_device *vfe,
>> +			      struct v4l2_device *v4l2_dev);
>> +
>> +void msm_vfe_unregister_entities(struct vfe_device *vfe);
>> +
>> +void msm_vfe_get_vfe_id(struct media_entity *entity, u8 *id);
>> +void msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id);
>> +
>> +void msm_vfe_stop_streaming(struct vfe_device *vfe);
>> +
>> +#endif /* QC_MSM_CAMSS_VFE_H */
> 

-- 
Best regards,
Todor Tomov

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 17/23] camss: vfe: Add interface for scaling
  2017-07-20 15:20   ` Sakari Ailus
@ 2017-07-25 15:36     ` Todor Tomov
  2017-08-04 17:59       ` Sakari Ailus
  0 siblings, 1 reply; 60+ messages in thread
From: Todor Tomov @ 2017-07-25 15:36 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: mchehab, hans.verkuil, javier, s.nawrocki, linux-media,
	linux-kernel, linux-arm-msm

Hi Sakari,

Thank you for review.

On 20.07.2017 18:20, Sakari Ailus wrote:
> Hi Todor,
> 
> On Mon, Jul 17, 2017 at 01:33:43PM +0300, Todor Tomov wrote:
>> Add compose selection ioctls to handle scaling configuration.
>>
>> Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
>> ---
>>  drivers/media/platform/qcom/camss-8x16/camss-vfe.c | 189 ++++++++++++++++++++-
>>  drivers/media/platform/qcom/camss-8x16/camss-vfe.h |   1 +
>>  2 files changed, 188 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
>> index 327f158..8ec6ce7 100644
>> --- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
>> +++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
>> @@ -211,6 +211,8 @@
>>  #define CAMIF_TIMEOUT_SLEEP_US 1000
>>  #define CAMIF_TIMEOUT_ALL_US 1000000
>>  
>> +#define SCALER_RATIO_MAX 16
>> +
>>  static const u32 vfe_formats[] = {
>>  	MEDIA_BUS_FMT_UYVY8_2X8,
>>  	MEDIA_BUS_FMT_VYUY8_2X8,
>> @@ -1905,6 +1907,25 @@ __vfe_get_format(struct vfe_line *line,
>>  	return &line->fmt[pad];
>>  }
>>  
>> +/*
>> + * __vfe_get_compose - Get pointer to compose selection structure
>> + * @line: VFE line
>> + * @cfg: V4L2 subdev pad configuration
>> + * @which: TRY or ACTIVE format
>> + *
>> + * Return pointer to TRY or ACTIVE compose rectangle structure
>> + */
>> +static struct v4l2_rect *
>> +__vfe_get_compose(struct vfe_line *line,
>> +		  struct v4l2_subdev_pad_config *cfg,
>> +		  enum v4l2_subdev_format_whence which)
>> +{
>> +	if (which == V4L2_SUBDEV_FORMAT_TRY)
>> +		return v4l2_subdev_get_try_compose(&line->subdev, cfg,
>> +						   MSM_VFE_PAD_SINK);
>> +
>> +	return &line->compose;
>> +}
>>  
>>  /*
>>   * vfe_try_format - Handle try format by pad subdev method
>> @@ -1951,7 +1972,14 @@ static void vfe_try_format(struct vfe_line *line,
>>  		*fmt = *__vfe_get_format(line, cfg, MSM_VFE_PAD_SINK,
>>  					 which);
>>  
>> -		if (line->id == VFE_LINE_PIX)
>> +		if (line->id == VFE_LINE_PIX) {
>> +			struct v4l2_rect *rect;
>> +
>> +			rect = __vfe_get_compose(line, cfg, which);
>> +
>> +			fmt->width = rect->width;
>> +			fmt->height = rect->height;
>> +
>>  			switch (fmt->code) {
>>  			case MEDIA_BUS_FMT_YUYV8_2X8:
>>  				if (code == MEDIA_BUS_FMT_YUYV8_1_5X8)
>> @@ -1979,6 +2007,7 @@ static void vfe_try_format(struct vfe_line *line,
>>  					fmt->code = MEDIA_BUS_FMT_VYUY8_2X8;
>>  				break;
>>  			}
>> +		}
>>  
>>  		break;
>>  	}
>> @@ -1987,6 +2016,50 @@ static void vfe_try_format(struct vfe_line *line,
>>  }
>>  
>>  /*
>> + * vfe_try_compose - Handle try compose selection by pad subdev method
>> + * @line: VFE line
>> + * @cfg: V4L2 subdev pad configuration
>> + * @rect: pointer to v4l2 rect structure
>> + * @which: wanted subdev format
>> + */
>> +static void vfe_try_compose(struct vfe_line *line,
>> +			    struct v4l2_subdev_pad_config *cfg,
>> +			    struct v4l2_rect *rect,
>> +			    enum v4l2_subdev_format_whence which)
>> +{
>> +	struct v4l2_mbus_framefmt *fmt;
>> +
>> +	rect->width = rect->width - rect->left;
>> +	rect->left = 0;
> 
> This is the compose rectangle i.e. left and top should be zero (unless it's
> about composing on e.g. a frame buffer). No need to decrement from width;
> similarly for height below.

Yes, it is not composing, but does the user know that? If left and top are
set, it makes sense to keep the rectangle size unchanged I think - actually
decrement width and height (and then clear left and top).

> 
>> +	rect->height = rect->height - rect->top;
>> +	rect->top = 0;
>> +
>> +	fmt = __vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, which);
>> +
>> +	if (rect->width > fmt->width)
>> +		rect->width = fmt->width;
>> +
>> +	if (rect->height > fmt->height)
>> +		rect->height = fmt->height;
>> +
>> +	if (fmt->width > rect->width * SCALER_RATIO_MAX)
>> +		rect->width = (fmt->width + SCALER_RATIO_MAX - 1) /
>> +							SCALER_RATIO_MAX;
>> +
>> +	rect->width &= ~0x1;
>> +
>> +	if (fmt->height > rect->height * SCALER_RATIO_MAX)
>> +		rect->height = (fmt->height + SCALER_RATIO_MAX - 1) /
>> +							SCALER_RATIO_MAX;
>> +
>> +	if (rect->width < 16)
>> +		rect->width = 16;
>> +
>> +	if (rect->height < 4)
>> +		rect->height = 4;
>> +}
>> +

<snip>

-- 
Best regards,
Todor Tomov

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver
  2017-07-19 10:54 ` [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Hans Verkuil
  2017-07-21  7:39   ` Todor Tomov
@ 2017-07-31  9:20   ` Hans Verkuil
  1 sibling, 0 replies; 60+ messages in thread
From: Hans Verkuil @ 2017-07-31  9:20 UTC (permalink / raw)
  To: Todor Tomov, mchehab, hans.verkuil, javier, s.nawrocki,
	sakari.ailus, linux-media, linux-kernel, linux-arm-msm

On 07/19/2017 12:54 PM, Hans Verkuil wrote:
> On 17/07/17 12:33, Todor Tomov wrote:
>> This patchset adds basic support for the Qualcomm Camera Subsystem found
>> on Qualcomm MSM8916 and APQ8016 processors.
>>
>> The driver implements V4L2, Media controller and V4L2 subdev interfaces.
>> Camera sensor using V4L2 subdev interface in the kernel is supported.
>>
>> The driver is implemented using as a reference the Qualcomm Camera
>> Subsystem driver for Android as found in Code Aurora [1].
>>
>> The driver is tested on Dragonboard 410C (APQ8016) with one and two
>> OV5645 camera sensors. media-ctl [2] and yavta [3] applications were
>> used for testing. Also Gstreamer 1.10.4 with v4l2src plugin is supported.
>>
>> More information is present in the document added by the third patch.
> 
> OK, so this looks pretty good. I have one comment for patch 12/23, and the
> dt-bindings need to be acked.
> 
> I suggest you make a v3.1 for patch 12/23 and then I'll wait for the binding
> ack. Once that's in (and there are no other comments) I will merge this.

Since Sakari had a bunch of comments I'll mark this series as 'Changes Requested'
and wait for a v4.

Regards,

	Hans

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 04/23] dt-bindings: media: Binding document for Qualcomm Camera subsystem driver
  2017-07-20 10:13   ` Sakari Ailus
@ 2017-08-04 11:54         ` Todor Tomov
  0 siblings, 0 replies; 60+ messages in thread
From: Todor Tomov @ 2017-08-04 11:54 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: mchehab-DgEjT+Ai2ygdnm+yROfE0A,
	hans.verkuil-FYB4Gu1CFyUAvxtiuMwx3w,
	javier-JPH+aEBZ4P+UEJcrhfAQsw, s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ,
	linux-media-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA, Rob Herring,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hi Sakari,

Thank you for the review.

On 20.07.2017 13:13, Sakari Ailus wrote:
> Hi Todor,
> 
> On Mon, Jul 17, 2017 at 01:33:30PM +0300, Todor Tomov wrote:
>> Add DT binding document for Qualcomm Camera subsystem driver.
>>
>> CC: Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
>> CC: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>> Signed-off-by: Todor Tomov <todor.tomov-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> ---
>>  .../devicetree/bindings/media/qcom,camss.txt       | 191 +++++++++++++++++++++
>>  1 file changed, 191 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/media/qcom,camss.txt
>>
>> diff --git a/Documentation/devicetree/bindings/media/qcom,camss.txt b/Documentation/devicetree/bindings/media/qcom,camss.txt
>> new file mode 100644
>> index 0000000..f698498
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/media/qcom,camss.txt
>> @@ -0,0 +1,191 @@
>> +Qualcomm Camera Subsystem
>> +
>> +* Properties
>> +
>> +- compatible:
>> +	Usage: required
>> +	Value type: <stringlist>
>> +	Definition: Should contain:
>> +		- "qcom,msm8916-camss"
>> +- reg:
>> +	Usage: required
>> +	Value type: <prop-encoded-array>
>> +	Definition: Register ranges as listed in the reg-names property.
>> +- reg-names:
>> +	Usage: required
>> +	Value type: <stringlist>
>> +	Definition: Should contain the following entries:
>> +		- "csiphy0"
>> +		- "csiphy0_clk_mux"
>> +		- "csiphy1"
>> +		- "csiphy1_clk_mux"
>> +		- "csid0"
>> +		- "csid1"
>> +		- "ispif"
>> +		- "csi_clk_mux"
>> +		- "vfe0"
>> +- interrupts:
>> +	Usage: required
>> +	Value type: <prop-encoded-array>
>> +	Definition: Interrupts as listed in the interrupt-names property.
>> +- interrupt-names:
>> +	Usage: required
>> +	Value type: <stringlist>
>> +	Definition: Should contain the following entries:
>> +		- "csiphy0"
>> +		- "csiphy1"
>> +		- "csid0"
>> +		- "csid1"
>> +		- "ispif"
>> +		- "vfe0"
>> +- power-domains:
>> +	Usage: required
>> +	Value type: <prop-encoded-array>
>> +	Definition: A phandle and power domain specifier pairs to the
>> +		    power domain which is responsible for collapsing
>> +		    and restoring power to the peripheral.
>> +- clocks:
>> +	Usage: required
>> +	Value type: <prop-encoded-array>
>> +	Definition: A list of phandle and clock specifier pairs as listed
>> +		    in clock-names property.
>> +- clock-names:
>> +	Usage: required
>> +	Value type: <stringlist>
>> +	Definition: Should contain the following entries:
>> +                - "camss_top_ahb"
>> +                - "ispif_ahb"
>> +                - "csiphy0_timer"
>> +                - "csiphy1_timer"
>> +                - "csi0_ahb"
>> +                - "csi0"
>> +                - "csi0_phy"
>> +                - "csi0_pix"
>> +                - "csi0_rdi"
>> +                - "csi1_ahb"
>> +                - "csi1"
>> +                - "csi1_phy"
>> +                - "csi1_pix"
>> +                - "csi1_rdi"
>> +                - "camss_ahb"
>> +                - "camss_vfe_vfe"
>> +                - "camss_csi_vfe"
>> +                - "iface"
>> +                - "bus"
>> +- vdda-supply:
>> +	Usage: required
>> +	Value type: <phandle>
>> +	Definition: A phandle to voltage supply for CSI2.
>> +- iommus:
>> +	Usage: required
>> +	Value type: <prop-encoded-array>
>> +	Definition: A list of phandle and IOMMU specifier pairs.
>> +
>> +* Nodes
>> +
>> +- ports:
>> +	Usage: required
>> +	Definition: As described in video-interfaces.txt in same directory.
>> +	Properties:
>> +		- reg:
>> +			Usage: required
>> +			Value type: <u32>
>> +			Definition: Selects CSI2 PHY interface - PHY0 or PHY1.
>> +	Endpoint node properties:
>> +		- clock-lanes:
>> +			Usage: required
>> +			Value type: <u32>
>> +			Definition: The clock lane.
>> +		- data-lanes:
>> +			Usage: required
>> +			Value type: <prop-encoded-array>
>> +			Definition: An array of data lanes.
>> +
>> +* An Example
>> +
>> +	camss: camss@1b00000 {
>> +		compatible = "qcom,msm8916-camss";
>> +		reg = <0x1b0ac00 0x200>,
>> +			<0x1b00030 0x4>,
>> +			<0x1b0b000 0x200>,
>> +			<0x1b00038 0x4>,
>> +			<0x1b08000 0x100>,
>> +			<0x1b08400 0x100>,
>> +			<0x1b0a000 0x500>,
>> +			<0x1b00020 0x10>,
>> +			<0x1b10000 0x1000>;
>> +		reg-names = "csiphy0",
>> +			"csiphy0_clk_mux",
>> +			"csiphy1",
>> +			"csiphy1_clk_mux",
>> +			"csid0",
>> +			"csid1",
>> +			"ispif",
>> +			"csi_clk_mux",
>> +			"vfe0";
>> +		interrupts = <GIC_SPI 78 0>,
>> +			<GIC_SPI 79 0>,
>> +			<GIC_SPI 51 0>,
>> +			<GIC_SPI 52 0>,
>> +			<GIC_SPI 55 0>,
>> +			<GIC_SPI 57 0>;
>> +		interrupt-names = "csiphy0",
>> +			"csiphy1",
>> +			"csid0",
>> +			"csid1",
>> +			"ispif",
>> +			"vfe0";
>> +		power-domains = <&gcc VFE_GDSC>;
>> +		clocks = <&gcc GCC_CAMSS_TOP_AHB_CLK>,
>> +			<&gcc GCC_CAMSS_ISPIF_AHB_CLK>,
>> +			<&gcc GCC_CAMSS_CSI0PHYTIMER_CLK>,
>> +			<&gcc GCC_CAMSS_CSI1PHYTIMER_CLK>,
>> +			<&gcc GCC_CAMSS_CSI0_AHB_CLK>,
>> +			<&gcc GCC_CAMSS_CSI0_CLK>,
>> +			<&gcc GCC_CAMSS_CSI0PHY_CLK>,
>> +			<&gcc GCC_CAMSS_CSI0PIX_CLK>,
>> +			<&gcc GCC_CAMSS_CSI0RDI_CLK>,
>> +			<&gcc GCC_CAMSS_CSI1_AHB_CLK>,
>> +			<&gcc GCC_CAMSS_CSI1_CLK>,
>> +			<&gcc GCC_CAMSS_CSI1PHY_CLK>,
>> +			<&gcc GCC_CAMSS_CSI1PIX_CLK>,
>> +			<&gcc GCC_CAMSS_CSI1RDI_CLK>,
>> +			<&gcc GCC_CAMSS_AHB_CLK>,
>> +			<&gcc GCC_CAMSS_VFE0_CLK>,
>> +			<&gcc GCC_CAMSS_CSI_VFE0_CLK>,
>> +			<&gcc GCC_CAMSS_VFE_AHB_CLK>,
>> +			<&gcc GCC_CAMSS_VFE_AXI_CLK>;
>> +                clock-names = "camss_top_ahb",
>> +                        "ispif_ahb",
>> +                        "csiphy0_timer",
>> +                        "csiphy1_timer",
>> +                        "csi0_ahb",
>> +                        "csi0",
>> +                        "csi0_phy",
>> +                        "csi0_pix",
>> +                        "csi0_rdi",
>> +                        "csi1_ahb",
>> +                        "csi1",
>> +                        "csi1_phy",
>> +                        "csi1_pix",
>> +                        "csi1_rdi",
>> +                        "camss_ahb",
>> +                        "camss_vfe_vfe",
>> +                        "camss_csi_vfe",
>> +                        "iface",
>> +                        "bus";
>> +		vdda-supply = <&pm8916_l2>;
>> +		iommus = <&apps_iommu 3>;
>> +		ports {
>> +			#address-cells = <1>;
>> +			#size-cells = <0>;
>> +			port@0 {
>> +				reg = <0>;
>> +				csiphy0_ep: endpoint {
>> +					clock-lanes = <1>;
>> +					data-lanes = <0 2>;
> 
> Do you support lane mapping? The values suggest "yes". That's something I
> could improve in the documentation: if lane mapping isn't supported, then
> the clock lane should be 0 and the data lanes from 1 to n.

Lane mapping is supported only for the data lanes. The clock lane is always
the physical lane 1. This is why I think it makes sense to keep the value
of the clock lane to 1 as this is the physical lane 1 really. I'll add
explanation in the documentation for this and for the data lanes too so
it is clear.

> 
> Is the split of the lanes between the ports static and specific to the
> hardware?

Each port describes a separate CSIPHY so each has its own set of lanes.

> 
>> +					remote-endpoint = <&ov5645_ep>;
>> +				};
>> +			};
>> +		};
>> +	};
> 

-- 
Best regards,
Todor Tomov
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 04/23] dt-bindings: media: Binding document for Qualcomm Camera subsystem driver
@ 2017-08-04 11:54         ` Todor Tomov
  0 siblings, 0 replies; 60+ messages in thread
From: Todor Tomov @ 2017-08-04 11:54 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: mchehab, hans.verkuil, javier, s.nawrocki, linux-media,
	linux-kernel, linux-arm-msm, Rob Herring, devicetree

Hi Sakari,

Thank you for the review.

On 20.07.2017 13:13, Sakari Ailus wrote:
> Hi Todor,
> 
> On Mon, Jul 17, 2017 at 01:33:30PM +0300, Todor Tomov wrote:
>> Add DT binding document for Qualcomm Camera subsystem driver.
>>
>> CC: Rob Herring <robh+dt@kernel.org>
>> CC: devicetree@vger.kernel.org
>> Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
>> ---
>>  .../devicetree/bindings/media/qcom,camss.txt       | 191 +++++++++++++++++++++
>>  1 file changed, 191 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/media/qcom,camss.txt
>>
>> diff --git a/Documentation/devicetree/bindings/media/qcom,camss.txt b/Documentation/devicetree/bindings/media/qcom,camss.txt
>> new file mode 100644
>> index 0000000..f698498
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/media/qcom,camss.txt
>> @@ -0,0 +1,191 @@
>> +Qualcomm Camera Subsystem
>> +
>> +* Properties
>> +
>> +- compatible:
>> +	Usage: required
>> +	Value type: <stringlist>
>> +	Definition: Should contain:
>> +		- "qcom,msm8916-camss"
>> +- reg:
>> +	Usage: required
>> +	Value type: <prop-encoded-array>
>> +	Definition: Register ranges as listed in the reg-names property.
>> +- reg-names:
>> +	Usage: required
>> +	Value type: <stringlist>
>> +	Definition: Should contain the following entries:
>> +		- "csiphy0"
>> +		- "csiphy0_clk_mux"
>> +		- "csiphy1"
>> +		- "csiphy1_clk_mux"
>> +		- "csid0"
>> +		- "csid1"
>> +		- "ispif"
>> +		- "csi_clk_mux"
>> +		- "vfe0"
>> +- interrupts:
>> +	Usage: required
>> +	Value type: <prop-encoded-array>
>> +	Definition: Interrupts as listed in the interrupt-names property.
>> +- interrupt-names:
>> +	Usage: required
>> +	Value type: <stringlist>
>> +	Definition: Should contain the following entries:
>> +		- "csiphy0"
>> +		- "csiphy1"
>> +		- "csid0"
>> +		- "csid1"
>> +		- "ispif"
>> +		- "vfe0"
>> +- power-domains:
>> +	Usage: required
>> +	Value type: <prop-encoded-array>
>> +	Definition: A phandle and power domain specifier pairs to the
>> +		    power domain which is responsible for collapsing
>> +		    and restoring power to the peripheral.
>> +- clocks:
>> +	Usage: required
>> +	Value type: <prop-encoded-array>
>> +	Definition: A list of phandle and clock specifier pairs as listed
>> +		    in clock-names property.
>> +- clock-names:
>> +	Usage: required
>> +	Value type: <stringlist>
>> +	Definition: Should contain the following entries:
>> +                - "camss_top_ahb"
>> +                - "ispif_ahb"
>> +                - "csiphy0_timer"
>> +                - "csiphy1_timer"
>> +                - "csi0_ahb"
>> +                - "csi0"
>> +                - "csi0_phy"
>> +                - "csi0_pix"
>> +                - "csi0_rdi"
>> +                - "csi1_ahb"
>> +                - "csi1"
>> +                - "csi1_phy"
>> +                - "csi1_pix"
>> +                - "csi1_rdi"
>> +                - "camss_ahb"
>> +                - "camss_vfe_vfe"
>> +                - "camss_csi_vfe"
>> +                - "iface"
>> +                - "bus"
>> +- vdda-supply:
>> +	Usage: required
>> +	Value type: <phandle>
>> +	Definition: A phandle to voltage supply for CSI2.
>> +- iommus:
>> +	Usage: required
>> +	Value type: <prop-encoded-array>
>> +	Definition: A list of phandle and IOMMU specifier pairs.
>> +
>> +* Nodes
>> +
>> +- ports:
>> +	Usage: required
>> +	Definition: As described in video-interfaces.txt in same directory.
>> +	Properties:
>> +		- reg:
>> +			Usage: required
>> +			Value type: <u32>
>> +			Definition: Selects CSI2 PHY interface - PHY0 or PHY1.
>> +	Endpoint node properties:
>> +		- clock-lanes:
>> +			Usage: required
>> +			Value type: <u32>
>> +			Definition: The clock lane.
>> +		- data-lanes:
>> +			Usage: required
>> +			Value type: <prop-encoded-array>
>> +			Definition: An array of data lanes.
>> +
>> +* An Example
>> +
>> +	camss: camss@1b00000 {
>> +		compatible = "qcom,msm8916-camss";
>> +		reg = <0x1b0ac00 0x200>,
>> +			<0x1b00030 0x4>,
>> +			<0x1b0b000 0x200>,
>> +			<0x1b00038 0x4>,
>> +			<0x1b08000 0x100>,
>> +			<0x1b08400 0x100>,
>> +			<0x1b0a000 0x500>,
>> +			<0x1b00020 0x10>,
>> +			<0x1b10000 0x1000>;
>> +		reg-names = "csiphy0",
>> +			"csiphy0_clk_mux",
>> +			"csiphy1",
>> +			"csiphy1_clk_mux",
>> +			"csid0",
>> +			"csid1",
>> +			"ispif",
>> +			"csi_clk_mux",
>> +			"vfe0";
>> +		interrupts = <GIC_SPI 78 0>,
>> +			<GIC_SPI 79 0>,
>> +			<GIC_SPI 51 0>,
>> +			<GIC_SPI 52 0>,
>> +			<GIC_SPI 55 0>,
>> +			<GIC_SPI 57 0>;
>> +		interrupt-names = "csiphy0",
>> +			"csiphy1",
>> +			"csid0",
>> +			"csid1",
>> +			"ispif",
>> +			"vfe0";
>> +		power-domains = <&gcc VFE_GDSC>;
>> +		clocks = <&gcc GCC_CAMSS_TOP_AHB_CLK>,
>> +			<&gcc GCC_CAMSS_ISPIF_AHB_CLK>,
>> +			<&gcc GCC_CAMSS_CSI0PHYTIMER_CLK>,
>> +			<&gcc GCC_CAMSS_CSI1PHYTIMER_CLK>,
>> +			<&gcc GCC_CAMSS_CSI0_AHB_CLK>,
>> +			<&gcc GCC_CAMSS_CSI0_CLK>,
>> +			<&gcc GCC_CAMSS_CSI0PHY_CLK>,
>> +			<&gcc GCC_CAMSS_CSI0PIX_CLK>,
>> +			<&gcc GCC_CAMSS_CSI0RDI_CLK>,
>> +			<&gcc GCC_CAMSS_CSI1_AHB_CLK>,
>> +			<&gcc GCC_CAMSS_CSI1_CLK>,
>> +			<&gcc GCC_CAMSS_CSI1PHY_CLK>,
>> +			<&gcc GCC_CAMSS_CSI1PIX_CLK>,
>> +			<&gcc GCC_CAMSS_CSI1RDI_CLK>,
>> +			<&gcc GCC_CAMSS_AHB_CLK>,
>> +			<&gcc GCC_CAMSS_VFE0_CLK>,
>> +			<&gcc GCC_CAMSS_CSI_VFE0_CLK>,
>> +			<&gcc GCC_CAMSS_VFE_AHB_CLK>,
>> +			<&gcc GCC_CAMSS_VFE_AXI_CLK>;
>> +                clock-names = "camss_top_ahb",
>> +                        "ispif_ahb",
>> +                        "csiphy0_timer",
>> +                        "csiphy1_timer",
>> +                        "csi0_ahb",
>> +                        "csi0",
>> +                        "csi0_phy",
>> +                        "csi0_pix",
>> +                        "csi0_rdi",
>> +                        "csi1_ahb",
>> +                        "csi1",
>> +                        "csi1_phy",
>> +                        "csi1_pix",
>> +                        "csi1_rdi",
>> +                        "camss_ahb",
>> +                        "camss_vfe_vfe",
>> +                        "camss_csi_vfe",
>> +                        "iface",
>> +                        "bus";
>> +		vdda-supply = <&pm8916_l2>;
>> +		iommus = <&apps_iommu 3>;
>> +		ports {
>> +			#address-cells = <1>;
>> +			#size-cells = <0>;
>> +			port@0 {
>> +				reg = <0>;
>> +				csiphy0_ep: endpoint {
>> +					clock-lanes = <1>;
>> +					data-lanes = <0 2>;
> 
> Do you support lane mapping? The values suggest "yes". That's something I
> could improve in the documentation: if lane mapping isn't supported, then
> the clock lane should be 0 and the data lanes from 1 to n.

Lane mapping is supported only for the data lanes. The clock lane is always
the physical lane 1. This is why I think it makes sense to keep the value
of the clock lane to 1 as this is the physical lane 1 really. I'll add
explanation in the documentation for this and for the data lanes too so
it is clear.

> 
> Is the split of the lanes between the ports static and specific to the
> hardware?

Each port describes a separate CSIPHY so each has its own set of lanes.

> 
>> +					remote-endpoint = <&ov5645_ep>;
>> +				};
>> +			};
>> +		};
>> +	};
> 

-- 
Best regards,
Todor Tomov

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 17/23] camss: vfe: Add interface for scaling
  2017-07-25 15:36     ` Todor Tomov
@ 2017-08-04 17:59       ` Sakari Ailus
  0 siblings, 0 replies; 60+ messages in thread
From: Sakari Ailus @ 2017-08-04 17:59 UTC (permalink / raw)
  To: Todor Tomov
  Cc: mchehab, hans.verkuil, javier, s.nawrocki, linux-media,
	linux-kernel, linux-arm-msm

Hi Todor,

Todor Tomov wrote:
> Hi Sakari,
> 
> Thank you for review.
> 
> On 20.07.2017 18:20, Sakari Ailus wrote:
>> Hi Todor,
>>
>> On Mon, Jul 17, 2017 at 01:33:43PM +0300, Todor Tomov wrote:
>>> Add compose selection ioctls to handle scaling configuration.
>>>
>>> Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
>>> ---
>>>  drivers/media/platform/qcom/camss-8x16/camss-vfe.c | 189 ++++++++++++++++++++-
>>>  drivers/media/platform/qcom/camss-8x16/camss-vfe.h |   1 +
>>>  2 files changed, 188 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
>>> index 327f158..8ec6ce7 100644
>>> --- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
>>> +++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
>>> @@ -211,6 +211,8 @@
>>>  #define CAMIF_TIMEOUT_SLEEP_US 1000
>>>  #define CAMIF_TIMEOUT_ALL_US 1000000
>>>  
>>> +#define SCALER_RATIO_MAX 16
>>> +
>>>  static const u32 vfe_formats[] = {
>>>  	MEDIA_BUS_FMT_UYVY8_2X8,
>>>  	MEDIA_BUS_FMT_VYUY8_2X8,
>>> @@ -1905,6 +1907,25 @@ __vfe_get_format(struct vfe_line *line,
>>>  	return &line->fmt[pad];
>>>  }
>>>  
>>> +/*
>>> + * __vfe_get_compose - Get pointer to compose selection structure
>>> + * @line: VFE line
>>> + * @cfg: V4L2 subdev pad configuration
>>> + * @which: TRY or ACTIVE format
>>> + *
>>> + * Return pointer to TRY or ACTIVE compose rectangle structure
>>> + */
>>> +static struct v4l2_rect *
>>> +__vfe_get_compose(struct vfe_line *line,
>>> +		  struct v4l2_subdev_pad_config *cfg,
>>> +		  enum v4l2_subdev_format_whence which)
>>> +{
>>> +	if (which == V4L2_SUBDEV_FORMAT_TRY)
>>> +		return v4l2_subdev_get_try_compose(&line->subdev, cfg,
>>> +						   MSM_VFE_PAD_SINK);
>>> +
>>> +	return &line->compose;
>>> +}
>>>  
>>>  /*
>>>   * vfe_try_format - Handle try format by pad subdev method
>>> @@ -1951,7 +1972,14 @@ static void vfe_try_format(struct vfe_line *line,
>>>  		*fmt = *__vfe_get_format(line, cfg, MSM_VFE_PAD_SINK,
>>>  					 which);
>>>  
>>> -		if (line->id == VFE_LINE_PIX)
>>> +		if (line->id == VFE_LINE_PIX) {
>>> +			struct v4l2_rect *rect;
>>> +
>>> +			rect = __vfe_get_compose(line, cfg, which);
>>> +
>>> +			fmt->width = rect->width;
>>> +			fmt->height = rect->height;
>>> +
>>>  			switch (fmt->code) {
>>>  			case MEDIA_BUS_FMT_YUYV8_2X8:
>>>  				if (code == MEDIA_BUS_FMT_YUYV8_1_5X8)
>>> @@ -1979,6 +2007,7 @@ static void vfe_try_format(struct vfe_line *line,
>>>  					fmt->code = MEDIA_BUS_FMT_VYUY8_2X8;
>>>  				break;
>>>  			}
>>> +		}
>>>  
>>>  		break;
>>>  	}
>>> @@ -1987,6 +2016,50 @@ static void vfe_try_format(struct vfe_line *line,
>>>  }
>>>  
>>>  /*
>>> + * vfe_try_compose - Handle try compose selection by pad subdev method
>>> + * @line: VFE line
>>> + * @cfg: V4L2 subdev pad configuration
>>> + * @rect: pointer to v4l2 rect structure
>>> + * @which: wanted subdev format
>>> + */
>>> +static void vfe_try_compose(struct vfe_line *line,
>>> +			    struct v4l2_subdev_pad_config *cfg,
>>> +			    struct v4l2_rect *rect,
>>> +			    enum v4l2_subdev_format_whence which)
>>> +{
>>> +	struct v4l2_mbus_framefmt *fmt;
>>> +
>>> +	rect->width = rect->width - rect->left;
>>> +	rect->left = 0;
>>
>> This is the compose rectangle i.e. left and top should be zero (unless it's
>> about composing on e.g. a frame buffer). No need to decrement from width;
>> similarly for height below.
> 
> Yes, it is not composing, but does the user know that? If left and top are
> set, it makes sense to keep the rectangle size unchanged I think - actually
> decrement width and height (and then clear left and top).

The API documentation tells these values are zero. If the user specifies
non-zero values then I don't think that they should have an effect. This
behaviour is in line with other drivers AFAIK.

> 
>>
>>> +	rect->height = rect->height - rect->top;
>>> +	rect->top = 0;
>>> +
>>> +	fmt = __vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, which);
>>> +
>>> +	if (rect->width > fmt->width)
>>> +		rect->width = fmt->width;
>>> +
>>> +	if (rect->height > fmt->height)
>>> +		rect->height = fmt->height;
>>> +
>>> +	if (fmt->width > rect->width * SCALER_RATIO_MAX)
>>> +		rect->width = (fmt->width + SCALER_RATIO_MAX - 1) /
>>> +							SCALER_RATIO_MAX;
>>> +
>>> +	rect->width &= ~0x1;
>>> +
>>> +	if (fmt->height > rect->height * SCALER_RATIO_MAX)
>>> +		rect->height = (fmt->height + SCALER_RATIO_MAX - 1) /
>>> +							SCALER_RATIO_MAX;
>>> +
>>> +	if (rect->width < 16)
>>> +		rect->width = 16;
>>> +
>>> +	if (rect->height < 4)
>>> +		rect->height = 4;
>>> +}
>>> +
> 
> <snip>
> 


-- 
Sakari Ailus
sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 10/23] media: camss: Add VFE files
  2017-07-25 14:02     ` Todor Tomov
@ 2017-08-04 18:02       ` Sakari Ailus
  2017-08-07  6:49         ` Todor Tomov
  0 siblings, 1 reply; 60+ messages in thread
From: Sakari Ailus @ 2017-08-04 18:02 UTC (permalink / raw)
  To: Todor Tomov
  Cc: mchehab, hans.verkuil, javier, s.nawrocki, linux-media,
	linux-kernel, linux-arm-msm

Hi Todor,

Todor Tomov wrote:
> Hi Sakari,
> 
> Thank you for the review.
> 
> On 20.07.2017 17:59, Sakari Ailus wrote:
>> Hi Todor,
>>
>> On Mon, Jul 17, 2017 at 01:33:36PM +0300, Todor Tomov wrote:
>>> These files control the VFE module. The VFE has different input interfaces.
>>> The PIX input interface feeds the input data to an image processing pipeline.
>>> Three RDI input interfaces bypass the image processing pipeline. The VFE also
>>> contains the AXI bus interface which writes the output data to memory.
>>>
>>> RDI interfaces are supported in this version. PIX interface is not supported.
>>>
>>> Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
>>> ---
>>>  drivers/media/platform/qcom/camss-8x16/camss-vfe.c | 1913 ++++++++++++++++++++
>>>  drivers/media/platform/qcom/camss-8x16/camss-vfe.h |  114 ++
>>>  2 files changed, 2027 insertions(+)
>>>  create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-vfe.c
>>>  create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-vfe.h
>>>
>>> diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
>>> new file mode 100644
>>> index 0000000..b6dd29b
>>> --- /dev/null
>>> +++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
>>> @@ -0,0 +1,1913 @@
> 
> <snip>
> 
>>> +
>>> +static void vfe_set_qos(struct vfe_device *vfe)
>>> +{
>>> +	u32 val = 0xaaa5aaa5;
>>> +	u32 val7 = 0x0001aaa5;
>>
>> Huh. What do these mean? :-)
> 
> For these here I don't have understanding of the values. I'll remove the magic
> values here and on all the other places but these here I'll just move to a #define.

If there is no documentation then I guess that's all that can be done.
Works for me.

> 
>>
>>> +
>>> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_0);
>>> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_1);
>>> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_2);
>>> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_3);
>>> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_4);
>>> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_5);
>>> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_6);
>>> +	writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7);
>>> +}
>>> +
> 
> <snip>
> 
>>> +
>>> +/*
>>> + * msm_vfe_subdev_init - Initialize VFE device structure and resources
>>> + * @vfe: VFE device
>>> + * @res: VFE module resources table
>>> + *
>>> + * Return 0 on success or a negative error code otherwise
>>> + */
>>> +int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res)
>>> +{
>>> +	struct device *dev = to_device(vfe);
>>> +	struct platform_device *pdev = to_platform_device(dev);
>>> +	struct resource *r;
>>> +	struct camss *camss = to_camss(vfe);
>>> +
>>> +	int i;
>>> +	int ret;
>>> +
>>> +	mutex_init(&vfe->power_lock);
>>> +	vfe->power_count = 0;
>>> +
>>> +	mutex_init(&vfe->stream_lock);
>>> +	vfe->stream_count = 0;
>>> +
>>> +	spin_lock_init(&vfe->output_lock);
>>> +
>>> +	vfe->id = 0;
>>> +	vfe->reg_update = 0;
>>> +
>>> +	for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++) {
>>> +		vfe->line[i].video_out.type =
>>> +					V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
>>> +		vfe->line[i].video_out.camss = camss;
>>> +		vfe->line[i].id = i;
>>> +	}
>>> +
>>> +	/* Memory */
>>> +
>>> +	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]);
>>> +	vfe->base = devm_ioremap_resource(dev, r);
>>> +	if (IS_ERR(vfe->base)) {
>>> +		dev_err(dev, "could not map memory\n");
>>
>> mutex_destroy() for bothof the mutexes. The same below.
>>
>> Do you have a corresponding cleanup function?
> 
> msm_vfe_subdev_init() and msm_vfe_register_entities() are called on probe().
> msm_vfe_unregister_entities() is called on remove() - this is the cleanup
> function. The mutexes are destroyed there. Is there something else that you
> are concerned about?

What about the error case above then? Where are the mutexes destroyed?

-- 
Sakari Ailus
sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 04/23] dt-bindings: media: Binding document for Qualcomm Camera subsystem driver
  2017-08-04 11:54         ` Todor Tomov
@ 2017-08-05  8:19             ` Sakari Ailus
  -1 siblings, 0 replies; 60+ messages in thread
From: Sakari Ailus @ 2017-08-05  8:19 UTC (permalink / raw)
  To: Todor Tomov
  Cc: mchehab-DgEjT+Ai2ygdnm+yROfE0A,
	hans.verkuil-FYB4Gu1CFyUAvxtiuMwx3w,
	javier-JPH+aEBZ4P+UEJcrhfAQsw, s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ,
	linux-media-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA, Rob Herring,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hi Todor,

On Fri, Aug 04, 2017 at 02:54:21PM +0300, Todor Tomov wrote:
> Hi Sakari,
> 
> Thank you for the review.

You're welcome!

> 
> On 20.07.2017 13:13, Sakari Ailus wrote:
> > Hi Todor,
> > 
> > On Mon, Jul 17, 2017 at 01:33:30PM +0300, Todor Tomov wrote:
> >> Add DT binding document for Qualcomm Camera subsystem driver.
> >>
> >> CC: Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> >> CC: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> >> Signed-off-by: Todor Tomov <todor.tomov-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> >> ---
> >>  .../devicetree/bindings/media/qcom,camss.txt       | 191 +++++++++++++++++++++
> >>  1 file changed, 191 insertions(+)
> >>  create mode 100644 Documentation/devicetree/bindings/media/qcom,camss.txt
> >>
> >> diff --git a/Documentation/devicetree/bindings/media/qcom,camss.txt b/Documentation/devicetree/bindings/media/qcom,camss.txt
> >> new file mode 100644
> >> index 0000000..f698498
> >> --- /dev/null
> >> +++ b/Documentation/devicetree/bindings/media/qcom,camss.txt
> >> @@ -0,0 +1,191 @@
> >> +Qualcomm Camera Subsystem
> >> +
> >> +* Properties
> >> +
> >> +- compatible:
> >> +	Usage: required
> >> +	Value type: <stringlist>
> >> +	Definition: Should contain:
> >> +		- "qcom,msm8916-camss"
> >> +- reg:
> >> +	Usage: required
> >> +	Value type: <prop-encoded-array>
> >> +	Definition: Register ranges as listed in the reg-names property.
> >> +- reg-names:
> >> +	Usage: required
> >> +	Value type: <stringlist>
> >> +	Definition: Should contain the following entries:
> >> +		- "csiphy0"
> >> +		- "csiphy0_clk_mux"
> >> +		- "csiphy1"
> >> +		- "csiphy1_clk_mux"
> >> +		- "csid0"
> >> +		- "csid1"
> >> +		- "ispif"
> >> +		- "csi_clk_mux"
> >> +		- "vfe0"
> >> +- interrupts:
> >> +	Usage: required
> >> +	Value type: <prop-encoded-array>
> >> +	Definition: Interrupts as listed in the interrupt-names property.
> >> +- interrupt-names:
> >> +	Usage: required
> >> +	Value type: <stringlist>
> >> +	Definition: Should contain the following entries:
> >> +		- "csiphy0"
> >> +		- "csiphy1"
> >> +		- "csid0"
> >> +		- "csid1"
> >> +		- "ispif"
> >> +		- "vfe0"
> >> +- power-domains:
> >> +	Usage: required
> >> +	Value type: <prop-encoded-array>
> >> +	Definition: A phandle and power domain specifier pairs to the
> >> +		    power domain which is responsible for collapsing
> >> +		    and restoring power to the peripheral.
> >> +- clocks:
> >> +	Usage: required
> >> +	Value type: <prop-encoded-array>
> >> +	Definition: A list of phandle and clock specifier pairs as listed
> >> +		    in clock-names property.
> >> +- clock-names:
> >> +	Usage: required
> >> +	Value type: <stringlist>
> >> +	Definition: Should contain the following entries:
> >> +                - "camss_top_ahb"
> >> +                - "ispif_ahb"
> >> +                - "csiphy0_timer"
> >> +                - "csiphy1_timer"
> >> +                - "csi0_ahb"
> >> +                - "csi0"
> >> +                - "csi0_phy"
> >> +                - "csi0_pix"
> >> +                - "csi0_rdi"
> >> +                - "csi1_ahb"
> >> +                - "csi1"
> >> +                - "csi1_phy"
> >> +                - "csi1_pix"
> >> +                - "csi1_rdi"
> >> +                - "camss_ahb"
> >> +                - "camss_vfe_vfe"
> >> +                - "camss_csi_vfe"
> >> +                - "iface"
> >> +                - "bus"
> >> +- vdda-supply:
> >> +	Usage: required
> >> +	Value type: <phandle>
> >> +	Definition: A phandle to voltage supply for CSI2.
> >> +- iommus:
> >> +	Usage: required
> >> +	Value type: <prop-encoded-array>
> >> +	Definition: A list of phandle and IOMMU specifier pairs.
> >> +
> >> +* Nodes
> >> +
> >> +- ports:
> >> +	Usage: required
> >> +	Definition: As described in video-interfaces.txt in same directory.
> >> +	Properties:
> >> +		- reg:
> >> +			Usage: required
> >> +			Value type: <u32>
> >> +			Definition: Selects CSI2 PHY interface - PHY0 or PHY1.
> >> +	Endpoint node properties:
> >> +		- clock-lanes:
> >> +			Usage: required
> >> +			Value type: <u32>
> >> +			Definition: The clock lane.
> >> +		- data-lanes:
> >> +			Usage: required
> >> +			Value type: <prop-encoded-array>
> >> +			Definition: An array of data lanes.
> >> +
> >> +* An Example
> >> +
> >> +	camss: camss@1b00000 {
> >> +		compatible = "qcom,msm8916-camss";
> >> +		reg = <0x1b0ac00 0x200>,
> >> +			<0x1b00030 0x4>,
> >> +			<0x1b0b000 0x200>,
> >> +			<0x1b00038 0x4>,
> >> +			<0x1b08000 0x100>,
> >> +			<0x1b08400 0x100>,
> >> +			<0x1b0a000 0x500>,
> >> +			<0x1b00020 0x10>,
> >> +			<0x1b10000 0x1000>;
> >> +		reg-names = "csiphy0",
> >> +			"csiphy0_clk_mux",
> >> +			"csiphy1",
> >> +			"csiphy1_clk_mux",
> >> +			"csid0",
> >> +			"csid1",
> >> +			"ispif",
> >> +			"csi_clk_mux",
> >> +			"vfe0";
> >> +		interrupts = <GIC_SPI 78 0>,
> >> +			<GIC_SPI 79 0>,
> >> +			<GIC_SPI 51 0>,
> >> +			<GIC_SPI 52 0>,
> >> +			<GIC_SPI 55 0>,
> >> +			<GIC_SPI 57 0>;
> >> +		interrupt-names = "csiphy0",
> >> +			"csiphy1",
> >> +			"csid0",
> >> +			"csid1",
> >> +			"ispif",
> >> +			"vfe0";
> >> +		power-domains = <&gcc VFE_GDSC>;
> >> +		clocks = <&gcc GCC_CAMSS_TOP_AHB_CLK>,
> >> +			<&gcc GCC_CAMSS_ISPIF_AHB_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI0PHYTIMER_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI1PHYTIMER_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI0_AHB_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI0_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI0PHY_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI0PIX_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI0RDI_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI1_AHB_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI1_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI1PHY_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI1PIX_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI1RDI_CLK>,
> >> +			<&gcc GCC_CAMSS_AHB_CLK>,
> >> +			<&gcc GCC_CAMSS_VFE0_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI_VFE0_CLK>,
> >> +			<&gcc GCC_CAMSS_VFE_AHB_CLK>,
> >> +			<&gcc GCC_CAMSS_VFE_AXI_CLK>;
> >> +                clock-names = "camss_top_ahb",
> >> +                        "ispif_ahb",
> >> +                        "csiphy0_timer",
> >> +                        "csiphy1_timer",
> >> +                        "csi0_ahb",
> >> +                        "csi0",
> >> +                        "csi0_phy",
> >> +                        "csi0_pix",
> >> +                        "csi0_rdi",
> >> +                        "csi1_ahb",
> >> +                        "csi1",
> >> +                        "csi1_phy",
> >> +                        "csi1_pix",
> >> +                        "csi1_rdi",
> >> +                        "camss_ahb",
> >> +                        "camss_vfe_vfe",
> >> +                        "camss_csi_vfe",
> >> +                        "iface",
> >> +                        "bus";
> >> +		vdda-supply = <&pm8916_l2>;
> >> +		iommus = <&apps_iommu 3>;
> >> +		ports {
> >> +			#address-cells = <1>;
> >> +			#size-cells = <0>;
> >> +			port@0 {
> >> +				reg = <0>;
> >> +				csiphy0_ep: endpoint {
> >> +					clock-lanes = <1>;
> >> +					data-lanes = <0 2>;
> > 
> > Do you support lane mapping? The values suggest "yes". That's something I
> > could improve in the documentation: if lane mapping isn't supported, then
> > the clock lane should be 0 and the data lanes from 1 to n.
> 
> Lane mapping is supported only for the data lanes. The clock lane is always
> the physical lane 1. This is why I think it makes sense to keep the value
> of the clock lane to 1 as this is the physical lane 1 really. I'll add
> explanation in the documentation for this and for the data lanes too so
> it is clear.

Could you document this in the binding documentation? What I've seen
previously is that either the lanes can be mapped any way you want, or
there's just a single possible mapping.

> 
> > 
> > Is the split of the lanes between the ports static and specific to the
> > hardware?
> 
> Each port describes a separate CSIPHY so each has its own set of lanes.

Ack. I asked since if the lanes can be shared, the lane numbering needs to
be global to the device; otherwise there's no way to convey the lane/PHY
association using the standard bindings.

-- 
Regards,

Sakari Ailus
e-mail: sakari.ailus-X3B1VOXEql0@public.gmane.org	XMPP: sailus-PCDdDYkjdNMDXYZnReoRVg@public.gmane.org
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 04/23] dt-bindings: media: Binding document for Qualcomm Camera subsystem driver
@ 2017-08-05  8:19             ` Sakari Ailus
  0 siblings, 0 replies; 60+ messages in thread
From: Sakari Ailus @ 2017-08-05  8:19 UTC (permalink / raw)
  To: Todor Tomov
  Cc: mchehab, hans.verkuil, javier, s.nawrocki, linux-media,
	linux-kernel, linux-arm-msm, Rob Herring, devicetree

Hi Todor,

On Fri, Aug 04, 2017 at 02:54:21PM +0300, Todor Tomov wrote:
> Hi Sakari,
> 
> Thank you for the review.

You're welcome!

> 
> On 20.07.2017 13:13, Sakari Ailus wrote:
> > Hi Todor,
> > 
> > On Mon, Jul 17, 2017 at 01:33:30PM +0300, Todor Tomov wrote:
> >> Add DT binding document for Qualcomm Camera subsystem driver.
> >>
> >> CC: Rob Herring <robh+dt@kernel.org>
> >> CC: devicetree@vger.kernel.org
> >> Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
> >> ---
> >>  .../devicetree/bindings/media/qcom,camss.txt       | 191 +++++++++++++++++++++
> >>  1 file changed, 191 insertions(+)
> >>  create mode 100644 Documentation/devicetree/bindings/media/qcom,camss.txt
> >>
> >> diff --git a/Documentation/devicetree/bindings/media/qcom,camss.txt b/Documentation/devicetree/bindings/media/qcom,camss.txt
> >> new file mode 100644
> >> index 0000000..f698498
> >> --- /dev/null
> >> +++ b/Documentation/devicetree/bindings/media/qcom,camss.txt
> >> @@ -0,0 +1,191 @@
> >> +Qualcomm Camera Subsystem
> >> +
> >> +* Properties
> >> +
> >> +- compatible:
> >> +	Usage: required
> >> +	Value type: <stringlist>
> >> +	Definition: Should contain:
> >> +		- "qcom,msm8916-camss"
> >> +- reg:
> >> +	Usage: required
> >> +	Value type: <prop-encoded-array>
> >> +	Definition: Register ranges as listed in the reg-names property.
> >> +- reg-names:
> >> +	Usage: required
> >> +	Value type: <stringlist>
> >> +	Definition: Should contain the following entries:
> >> +		- "csiphy0"
> >> +		- "csiphy0_clk_mux"
> >> +		- "csiphy1"
> >> +		- "csiphy1_clk_mux"
> >> +		- "csid0"
> >> +		- "csid1"
> >> +		- "ispif"
> >> +		- "csi_clk_mux"
> >> +		- "vfe0"
> >> +- interrupts:
> >> +	Usage: required
> >> +	Value type: <prop-encoded-array>
> >> +	Definition: Interrupts as listed in the interrupt-names property.
> >> +- interrupt-names:
> >> +	Usage: required
> >> +	Value type: <stringlist>
> >> +	Definition: Should contain the following entries:
> >> +		- "csiphy0"
> >> +		- "csiphy1"
> >> +		- "csid0"
> >> +		- "csid1"
> >> +		- "ispif"
> >> +		- "vfe0"
> >> +- power-domains:
> >> +	Usage: required
> >> +	Value type: <prop-encoded-array>
> >> +	Definition: A phandle and power domain specifier pairs to the
> >> +		    power domain which is responsible for collapsing
> >> +		    and restoring power to the peripheral.
> >> +- clocks:
> >> +	Usage: required
> >> +	Value type: <prop-encoded-array>
> >> +	Definition: A list of phandle and clock specifier pairs as listed
> >> +		    in clock-names property.
> >> +- clock-names:
> >> +	Usage: required
> >> +	Value type: <stringlist>
> >> +	Definition: Should contain the following entries:
> >> +                - "camss_top_ahb"
> >> +                - "ispif_ahb"
> >> +                - "csiphy0_timer"
> >> +                - "csiphy1_timer"
> >> +                - "csi0_ahb"
> >> +                - "csi0"
> >> +                - "csi0_phy"
> >> +                - "csi0_pix"
> >> +                - "csi0_rdi"
> >> +                - "csi1_ahb"
> >> +                - "csi1"
> >> +                - "csi1_phy"
> >> +                - "csi1_pix"
> >> +                - "csi1_rdi"
> >> +                - "camss_ahb"
> >> +                - "camss_vfe_vfe"
> >> +                - "camss_csi_vfe"
> >> +                - "iface"
> >> +                - "bus"
> >> +- vdda-supply:
> >> +	Usage: required
> >> +	Value type: <phandle>
> >> +	Definition: A phandle to voltage supply for CSI2.
> >> +- iommus:
> >> +	Usage: required
> >> +	Value type: <prop-encoded-array>
> >> +	Definition: A list of phandle and IOMMU specifier pairs.
> >> +
> >> +* Nodes
> >> +
> >> +- ports:
> >> +	Usage: required
> >> +	Definition: As described in video-interfaces.txt in same directory.
> >> +	Properties:
> >> +		- reg:
> >> +			Usage: required
> >> +			Value type: <u32>
> >> +			Definition: Selects CSI2 PHY interface - PHY0 or PHY1.
> >> +	Endpoint node properties:
> >> +		- clock-lanes:
> >> +			Usage: required
> >> +			Value type: <u32>
> >> +			Definition: The clock lane.
> >> +		- data-lanes:
> >> +			Usage: required
> >> +			Value type: <prop-encoded-array>
> >> +			Definition: An array of data lanes.
> >> +
> >> +* An Example
> >> +
> >> +	camss: camss@1b00000 {
> >> +		compatible = "qcom,msm8916-camss";
> >> +		reg = <0x1b0ac00 0x200>,
> >> +			<0x1b00030 0x4>,
> >> +			<0x1b0b000 0x200>,
> >> +			<0x1b00038 0x4>,
> >> +			<0x1b08000 0x100>,
> >> +			<0x1b08400 0x100>,
> >> +			<0x1b0a000 0x500>,
> >> +			<0x1b00020 0x10>,
> >> +			<0x1b10000 0x1000>;
> >> +		reg-names = "csiphy0",
> >> +			"csiphy0_clk_mux",
> >> +			"csiphy1",
> >> +			"csiphy1_clk_mux",
> >> +			"csid0",
> >> +			"csid1",
> >> +			"ispif",
> >> +			"csi_clk_mux",
> >> +			"vfe0";
> >> +		interrupts = <GIC_SPI 78 0>,
> >> +			<GIC_SPI 79 0>,
> >> +			<GIC_SPI 51 0>,
> >> +			<GIC_SPI 52 0>,
> >> +			<GIC_SPI 55 0>,
> >> +			<GIC_SPI 57 0>;
> >> +		interrupt-names = "csiphy0",
> >> +			"csiphy1",
> >> +			"csid0",
> >> +			"csid1",
> >> +			"ispif",
> >> +			"vfe0";
> >> +		power-domains = <&gcc VFE_GDSC>;
> >> +		clocks = <&gcc GCC_CAMSS_TOP_AHB_CLK>,
> >> +			<&gcc GCC_CAMSS_ISPIF_AHB_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI0PHYTIMER_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI1PHYTIMER_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI0_AHB_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI0_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI0PHY_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI0PIX_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI0RDI_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI1_AHB_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI1_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI1PHY_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI1PIX_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI1RDI_CLK>,
> >> +			<&gcc GCC_CAMSS_AHB_CLK>,
> >> +			<&gcc GCC_CAMSS_VFE0_CLK>,
> >> +			<&gcc GCC_CAMSS_CSI_VFE0_CLK>,
> >> +			<&gcc GCC_CAMSS_VFE_AHB_CLK>,
> >> +			<&gcc GCC_CAMSS_VFE_AXI_CLK>;
> >> +                clock-names = "camss_top_ahb",
> >> +                        "ispif_ahb",
> >> +                        "csiphy0_timer",
> >> +                        "csiphy1_timer",
> >> +                        "csi0_ahb",
> >> +                        "csi0",
> >> +                        "csi0_phy",
> >> +                        "csi0_pix",
> >> +                        "csi0_rdi",
> >> +                        "csi1_ahb",
> >> +                        "csi1",
> >> +                        "csi1_phy",
> >> +                        "csi1_pix",
> >> +                        "csi1_rdi",
> >> +                        "camss_ahb",
> >> +                        "camss_vfe_vfe",
> >> +                        "camss_csi_vfe",
> >> +                        "iface",
> >> +                        "bus";
> >> +		vdda-supply = <&pm8916_l2>;
> >> +		iommus = <&apps_iommu 3>;
> >> +		ports {
> >> +			#address-cells = <1>;
> >> +			#size-cells = <0>;
> >> +			port@0 {
> >> +				reg = <0>;
> >> +				csiphy0_ep: endpoint {
> >> +					clock-lanes = <1>;
> >> +					data-lanes = <0 2>;
> > 
> > Do you support lane mapping? The values suggest "yes". That's something I
> > could improve in the documentation: if lane mapping isn't supported, then
> > the clock lane should be 0 and the data lanes from 1 to n.
> 
> Lane mapping is supported only for the data lanes. The clock lane is always
> the physical lane 1. This is why I think it makes sense to keep the value
> of the clock lane to 1 as this is the physical lane 1 really. I'll add
> explanation in the documentation for this and for the data lanes too so
> it is clear.

Could you document this in the binding documentation? What I've seen
previously is that either the lanes can be mapped any way you want, or
there's just a single possible mapping.

> 
> > 
> > Is the split of the lanes between the ports static and specific to the
> > hardware?
> 
> Each port describes a separate CSIPHY so each has its own set of lanes.

Ack. I asked since if the lanes can be shared, the lane numbering needs to
be global to the device; otherwise there's no way to convey the lane/PHY
association using the standard bindings.

-- 
Regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 10/23] media: camss: Add VFE files
  2017-08-04 18:02       ` Sakari Ailus
@ 2017-08-07  6:49         ` Todor Tomov
  0 siblings, 0 replies; 60+ messages in thread
From: Todor Tomov @ 2017-08-07  6:49 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: mchehab, hans.verkuil, s.nawrocki, linux-media, linux-kernel,
	linux-arm-msm

Hi Sakari,

On  4.08.2017 21:02, Sakari Ailus wrote:
> Hi Todor,
> 
> Todor Tomov wrote:
>> Hi Sakari,
>>
>> Thank you for the review.
>>
>> On 20.07.2017 17:59, Sakari Ailus wrote:
>>> Hi Todor,
>>>
>>> On Mon, Jul 17, 2017 at 01:33:36PM +0300, Todor Tomov wrote:
>>>> These files control the VFE module. The VFE has different input interfaces.
>>>> The PIX input interface feeds the input data to an image processing pipeline.
>>>> Three RDI input interfaces bypass the image processing pipeline. The VFE also
>>>> contains the AXI bus interface which writes the output data to memory.
>>>>
>>>> RDI interfaces are supported in this version. PIX interface is not supported.
>>>>
>>>> Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
>>>> ---
>>>>  drivers/media/platform/qcom/camss-8x16/camss-vfe.c | 1913 ++++++++++++++++++++
>>>>  drivers/media/platform/qcom/camss-8x16/camss-vfe.h |  114 ++
>>>>  2 files changed, 2027 insertions(+)
>>>>  create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-vfe.c
>>>>  create mode 100644 drivers/media/platform/qcom/camss-8x16/camss-vfe.h
>>>>
>>>> diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
>>>> new file mode 100644
>>>> index 0000000..b6dd29b
>>>> --- /dev/null
>>>> +++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
>>>> @@ -0,0 +1,1913 @@
>>
>> <snip>
>>
>>>> +
>>>> +static void vfe_set_qos(struct vfe_device *vfe)
>>>> +{
>>>> +	u32 val = 0xaaa5aaa5;
>>>> +	u32 val7 = 0x0001aaa5;
>>>
>>> Huh. What do these mean? :-)
>>
>> For these here I don't have understanding of the values. I'll remove the magic
>> values here and on all the other places but these here I'll just move to a #define.
> 
> If there is no documentation then I guess that's all that can be done.
> Works for me.
> 
>>
>>>
>>>> +
>>>> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_0);
>>>> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_1);
>>>> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_2);
>>>> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_3);
>>>> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_4);
>>>> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_5);
>>>> +	writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_6);
>>>> +	writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7);
>>>> +}
>>>> +
>>
>> <snip>
>>
>>>> +
>>>> +/*
>>>> + * msm_vfe_subdev_init - Initialize VFE device structure and resources
>>>> + * @vfe: VFE device
>>>> + * @res: VFE module resources table
>>>> + *
>>>> + * Return 0 on success or a negative error code otherwise
>>>> + */
>>>> +int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res)
>>>> +{
>>>> +	struct device *dev = to_device(vfe);
>>>> +	struct platform_device *pdev = to_platform_device(dev);
>>>> +	struct resource *r;
>>>> +	struct camss *camss = to_camss(vfe);
>>>> +
>>>> +	int i;
>>>> +	int ret;
>>>> +
>>>> +	mutex_init(&vfe->power_lock);
>>>> +	vfe->power_count = 0;
>>>> +
>>>> +	mutex_init(&vfe->stream_lock);
>>>> +	vfe->stream_count = 0;
>>>> +
>>>> +	spin_lock_init(&vfe->output_lock);
>>>> +
>>>> +	vfe->id = 0;
>>>> +	vfe->reg_update = 0;
>>>> +
>>>> +	for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++) {
>>>> +		vfe->line[i].video_out.type =
>>>> +					V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
>>>> +		vfe->line[i].video_out.camss = camss;
>>>> +		vfe->line[i].id = i;
>>>> +	}
>>>> +
>>>> +	/* Memory */
>>>> +
>>>> +	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]);
>>>> +	vfe->base = devm_ioremap_resource(dev, r);
>>>> +	if (IS_ERR(vfe->base)) {
>>>> +		dev_err(dev, "could not map memory\n");
>>>
>>> mutex_destroy() for bothof the mutexes. The same below.
>>>
>>> Do you have a corresponding cleanup function?
>>
>> msm_vfe_subdev_init() and msm_vfe_register_entities() are called on probe().
>> msm_vfe_unregister_entities() is called on remove() - this is the cleanup
>> function. The mutexes are destroyed there. Is there something else that you
>> are concerned about?
> 
> What about the error case above then? Where are the mutexes destroyed?
> 

Right, I'll add mutex_destroy() for the error cases too.


-- 
Best regards,
Todor Tomov

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 16/23] camss: vfe: Support for frame padding
  2017-07-20 15:17   ` Sakari Ailus
@ 2018-09-03 15:38     ` Todor Tomov
  2018-09-13 15:04       ` Sakari Ailus
  0 siblings, 1 reply; 60+ messages in thread
From: Todor Tomov @ 2018-09-03 15:38 UTC (permalink / raw)
  To: Sakari Ailus, hans.verkuil, linux-media
  Cc: Todor Tomov, mchehab, linux-kernel, linux-arm-msm

Hi Sakari and all,

I'm sorry to up this thread from an year ago but I'm currently thinking
about a problem which is related to this so I decided to ask here.

On 20.07.2017 18:17, Sakari Ailus wrote:
> Hi Todor,
> 
> On Mon, Jul 17, 2017 at 01:33:42PM +0300, Todor Tomov wrote:
>> Add support for horizontal and vertical frame padding.
>>
>> Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
>> ---
>>  drivers/media/platform/qcom/camss-8x16/camss-vfe.c | 86 +++++++++++++++++-----
>>  .../media/platform/qcom/camss-8x16/camss-video.c   | 69 ++++++++++++-----
>>  .../media/platform/qcom/camss-8x16/camss-video.h   |  2 +
>>  3 files changed, 121 insertions(+), 36 deletions(-)
>>

...

>> diff --git a/drivers/media/platform/qcom/camss-8x16/camss-video.c b/drivers/media/platform/qcom/camss-8x16/camss-video.c
>> index c5ebf5c..5a2bf18 100644
>> --- a/drivers/media/platform/qcom/camss-8x16/camss-video.c
>> +++ b/drivers/media/platform/qcom/camss-8x16/camss-video.c

...

>> @@ -542,28 +537,68 @@ static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
>>  	return 0;
>>  }
>>  
>> -static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
>> +static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
>>  {
>>  	struct camss_video *video = video_drvdata(file);
>> +	struct v4l2_plane_pix_format *p;
>> +	u32 bytesperline[3] = { 0 };
>> +	u32 sizeimage[3] = { 0 };
>> +	u32 lines;
>>  	int ret;
>> +	int i;
>>  
>> -	if (vb2_is_busy(&video->vb2_q))
>> -		return -EBUSY;
>> +	if (video->line_based)
>> +		for (i = 0; i < f->fmt.pix_mp.num_planes && i < 3; i++) {
>> +			p = &f->fmt.pix_mp.plane_fmt[i];
>> +			bytesperline[i] = clamp_t(u32, p->bytesperline,
>> +						  1, 65528);
>> +			sizeimage[i] = clamp_t(u32, p->sizeimage,
>> +					       bytesperline[i],
>> +					       bytesperline[i] * 4096);
>> +		}
>>  
>>  	ret = video_get_subdev_format(video, f);
>>  	if (ret < 0)
>>  		return ret;
> 
> If you take the width and height from the sub-device format, then for the
> user to figure out how big a buffer is needed for a particular format it
> takes to change the sub-device format.
> 
> I wouldn't do this but keep the image dimensions on the video node
> independent of what's configured on the sub-device.

So the question is whether the video device node should:
a) keep its format and framesize always in sync with what is set on the
   subdev node with active link to it. This means all s_fmt and enum_fmt
   will return only the value which is in sink with the subdev node;
b) allow to set all possible allowed formats and framesizes. This however
   allows the userspace to try to start the streaming with format and
   framesizes not in sync (on video and subdev node) in which case the
   start streaming will fail.

Currently the driver is implemented as in b) and I hit this problem (in b))
when I try to use it from opencv. I wonder how this can be overcome, the
userspace cannot be blamed that it tried to start streaming for format
and framesize which were allowed to be set.

Are there any new insights on this lately - what can be done to avoid
the problems in a) and b)?

Thank you.

> 
> This patch doesn't really change the behaviour, but a patch before this
> one. That's where the fix should be (as well).
> 
>>  
>> -	video->active_fmt = *f;
>> +	if (video->line_based)
>> +		for (i = 0; i < f->fmt.pix_mp.num_planes; i++) {
>> +			p = &f->fmt.pix_mp.plane_fmt[i];
>> +			p->bytesperline = clamp_t(u32, p->bytesperline,
>> +						  1, 65528);
>> +			p->sizeimage = clamp_t(u32, p->sizeimage,
>> +					       p->bytesperline,
>> +					       p->bytesperline * 4096);
>> +			lines = p->sizeimage / p->bytesperline;
>> +
>> +			if (p->bytesperline < bytesperline[i])
>> +				p->bytesperline = ALIGN(bytesperline[i], 8);
>> +
>> +			if (p->sizeimage < p->bytesperline * lines)
>> +				p->sizeimage = p->bytesperline * lines;
>> +
>> +			if (p->sizeimage < sizeimage[i])
>> +				p->sizeimage = sizeimage[i];
>> +		}
>>  
>>  	return 0;
>>  }
>>  
>> -static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
>> +static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
>>  {
>>  	struct camss_video *video = video_drvdata(file);
>> +	int ret;
>> +
>> +	if (vb2_is_busy(&video->vb2_q))
>> +		return -EBUSY;
>>  
>> -	return video_get_subdev_format(video, f);
>> +	ret = video_try_fmt(file, fh, f);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	video->active_fmt = *f;
>> +
>> +	return 0;
>>  }
>>  
>>  static int video_enum_input(struct file *file, void *fh,


-- 
Best regards,
Todor Tomov

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [PATCH v3 16/23] camss: vfe: Support for frame padding
  2018-09-03 15:38     ` Todor Tomov
@ 2018-09-13 15:04       ` Sakari Ailus
  0 siblings, 0 replies; 60+ messages in thread
From: Sakari Ailus @ 2018-09-13 15:04 UTC (permalink / raw)
  To: Todor Tomov
  Cc: hans.verkuil, linux-media, Todor Tomov, mchehab, linux-kernel,
	linux-arm-msm

Hi Todor,

Apologies for the late reply.

On Mon, Sep 03, 2018 at 06:38:20PM +0300, Todor Tomov wrote:
> Hi Sakari and all,
> 
> I'm sorry to up this thread from an year ago but I'm currently thinking
> about a problem which is related to this so I decided to ask here.
> 
> On 20.07.2017 18:17, Sakari Ailus wrote:
> > Hi Todor,
> > 
> > On Mon, Jul 17, 2017 at 01:33:42PM +0300, Todor Tomov wrote:
> >> Add support for horizontal and vertical frame padding.
> >>
> >> Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
> >> ---
> >>  drivers/media/platform/qcom/camss-8x16/camss-vfe.c | 86 +++++++++++++++++-----
> >>  .../media/platform/qcom/camss-8x16/camss-video.c   | 69 ++++++++++++-----
> >>  .../media/platform/qcom/camss-8x16/camss-video.h   |  2 +
> >>  3 files changed, 121 insertions(+), 36 deletions(-)
> >>
> 
> ...
> 
> >> diff --git a/drivers/media/platform/qcom/camss-8x16/camss-video.c b/drivers/media/platform/qcom/camss-8x16/camss-video.c
> >> index c5ebf5c..5a2bf18 100644
> >> --- a/drivers/media/platform/qcom/camss-8x16/camss-video.c
> >> +++ b/drivers/media/platform/qcom/camss-8x16/camss-video.c
> 
> ...
> 
> >> @@ -542,28 +537,68 @@ static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
> >>  	return 0;
> >>  }
> >>  
> >> -static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
> >> +static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
> >>  {
> >>  	struct camss_video *video = video_drvdata(file);
> >> +	struct v4l2_plane_pix_format *p;
> >> +	u32 bytesperline[3] = { 0 };
> >> +	u32 sizeimage[3] = { 0 };
> >> +	u32 lines;
> >>  	int ret;
> >> +	int i;
> >>  
> >> -	if (vb2_is_busy(&video->vb2_q))
> >> -		return -EBUSY;
> >> +	if (video->line_based)
> >> +		for (i = 0; i < f->fmt.pix_mp.num_planes && i < 3; i++) {
> >> +			p = &f->fmt.pix_mp.plane_fmt[i];
> >> +			bytesperline[i] = clamp_t(u32, p->bytesperline,
> >> +						  1, 65528);
> >> +			sizeimage[i] = clamp_t(u32, p->sizeimage,
> >> +					       bytesperline[i],
> >> +					       bytesperline[i] * 4096);
> >> +		}
> >>  
> >>  	ret = video_get_subdev_format(video, f);
> >>  	if (ret < 0)
> >>  		return ret;
> > 
> > If you take the width and height from the sub-device format, then for the
> > user to figure out how big a buffer is needed for a particular format it
> > takes to change the sub-device format.
> > 
> > I wouldn't do this but keep the image dimensions on the video node
> > independent of what's configured on the sub-device.
> 
> So the question is whether the video device node should:
> a) keep its format and framesize always in sync with what is set on the
>    subdev node with active link to it. This means all s_fmt and enum_fmt
>    will return only the value which is in sink with the subdev node;
> b) allow to set all possible allowed formats and framesizes. This however
>    allows the userspace to try to start the streaming with format and
>    framesizes not in sync (on video and subdev node) in which case the
>    start streaming will fail.
> 
> Currently the driver is implemented as in b) and I hit this problem (in b))
> when I try to use it from opencv. I wonder how this can be overcome, the
> userspace cannot be blamed that it tried to start streaming for format
> and framesize which were allowed to be set.
> 
> Are there any new insights on this lately - what can be done to avoid
> the problems in a) and b)?

I'd like to refer to Linux camera user space that hasn't yet materialised,
yet there are plans to implemnt that. One of the aims are to support
regular V4L2 applications on complex devices.

<URL:https://linuxtv.org/news.php?entry=2018-07-13.mchehab>

There's unfortunately nothing more concrete than that I could refer to at
the moment.

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 60+ messages in thread

end of thread, other threads:[~2018-09-13 15:04 UTC | newest]

Thread overview: 60+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-07-17 10:33 [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Todor Tomov
2017-07-17 10:33 ` [PATCH v3 01/23] [media] media: Make parameter of media_entity_remote_pad() const Todor Tomov
2017-07-17 10:33 ` [PATCH v3 02/23] [media] v4l2-mediabus: Add helper functions Todor Tomov
2017-07-17 10:33 ` [PATCH v3 03/23] v4l: Add packed Bayer raw12 pixel formats Todor Tomov
2017-07-17 10:33 ` [PATCH v3 04/23] dt-bindings: media: Binding document for Qualcomm Camera subsystem driver Todor Tomov
2017-07-20 10:13   ` Sakari Ailus
     [not found]     ` <20170720101345.eovx5ovuxr7sqpea-S+BSfZ9RZZmRSg0ZkenSGLdO1Tsj/99ntUK59QYPAWc@public.gmane.org>
2017-08-04 11:54       ` Todor Tomov
2017-08-04 11:54         ` Todor Tomov
     [not found]         ` <1dc5ab4d-5171-c6df-4300-2abcd0e5483b-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
2017-08-05  8:19           ` Sakari Ailus
2017-08-05  8:19             ` Sakari Ailus
2017-07-20 10:15   ` Sakari Ailus
2017-07-24 16:43   ` Rob Herring
2017-07-17 10:33 ` [PATCH v3 05/23] MAINTAINERS: Add " Todor Tomov
2017-07-17 10:33 ` [PATCH v3 06/23] doc: media/v4l-drivers: Add Qualcomm Camera Subsystem driver document Todor Tomov
2017-07-17 10:33 ` [PATCH v3 07/23] media: camss: Add CSIPHY files Todor Tomov
2017-07-20 12:15   ` Sakari Ailus
2017-07-17 10:33 ` [PATCH v3 08/23] media: camss: Add CSID files Todor Tomov
2017-07-20 14:13   ` Sakari Ailus
2017-07-17 10:33 ` [PATCH v3 09/23] media: camss: Add ISPIF files Todor Tomov
2017-07-20 14:51   ` Sakari Ailus
2017-07-17 10:33 ` [PATCH v3 10/23] media: camss: Add VFE files Todor Tomov
2017-07-20 14:59   ` Sakari Ailus
2017-07-25 14:02     ` Todor Tomov
2017-08-04 18:02       ` Sakari Ailus
2017-08-07  6:49         ` Todor Tomov
2017-07-17 10:33 ` [PATCH v3 11/23] media: camss: Add files which handle the video device nodes Todor Tomov
2017-07-17 10:33 ` [PATCH v3 12/23] media: camms: Add core files Todor Tomov
2017-07-19 10:44   ` Hans Verkuil
2017-07-17 10:33 ` [PATCH v3 13/23] media: camss: Enable building Todor Tomov
2017-07-19 13:17   ` kbuild test robot
2017-07-19 13:17     ` kbuild test robot
2017-07-17 10:33 ` [PATCH v3 14/23] camss: vfe: Format conversion support using PIX interface Todor Tomov
2017-07-20 15:11   ` Sakari Ailus
2017-07-17 10:33 ` [PATCH v3 15/23] doc: media/v4l-drivers: Qualcomm Camera Subsystem - PIX Interface Todor Tomov
2017-07-20 15:13   ` Sakari Ailus
2017-07-17 10:33 ` [PATCH v3 16/23] camss: vfe: Support for frame padding Todor Tomov
2017-07-20 15:17   ` Sakari Ailus
2018-09-03 15:38     ` Todor Tomov
2018-09-13 15:04       ` Sakari Ailus
2017-07-17 10:33 ` [PATCH v3 17/23] camss: vfe: Add interface for scaling Todor Tomov
2017-07-20 15:20   ` Sakari Ailus
2017-07-25 15:36     ` Todor Tomov
2017-08-04 17:59       ` Sakari Ailus
2017-07-17 10:33 ` [PATCH v3 18/23] camss: vfe: Configure scaler module in VFE Todor Tomov
2017-07-17 10:33 ` [PATCH v3 19/23] camss: vfe: Add interface for cropping Todor Tomov
2017-07-17 10:33 ` [PATCH v3 20/23] camss: vfe: Configure crop module in VFE Todor Tomov
2017-07-17 10:33 ` [PATCH v3 21/23] doc: media/v4l-drivers: Qualcomm Camera Subsystem - Scale and crop Todor Tomov
2017-07-17 10:33 ` [PATCH v3 22/23] camss: Use optimal clock frequency rates Todor Tomov
2017-07-19 15:59   ` kbuild test robot
2017-07-19 15:59     ` kbuild test robot
2017-07-21  7:55     ` Todor Tomov
2017-07-21  7:55       ` Todor Tomov
2017-07-21  7:55       ` Todor Tomov
2017-07-20 15:23   ` Sakari Ailus
2017-07-17 10:33 ` [PATCH v3 23/23] doc: media/v4l-drivers: Qualcomm Camera Subsystem - Media graph Todor Tomov
2017-07-19 10:54 ` [PATCH v3 00/23] Qualcomm 8x16 Camera Subsystem driver Hans Verkuil
2017-07-21  7:39   ` Todor Tomov
2017-07-31  9:20   ` Hans Verkuil
2017-07-20 15:25 ` Sakari Ailus
2017-07-21  7:50   ` Todor Tomov

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.