All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/5] Add V4L2 SDR (DRIF & MAX2175) driver
@ 2016-11-09 15:44 ` Ramesh Shanmugasundaram
  0 siblings, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-11-09 15:44 UTC (permalink / raw)
  To: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	mchehab-DgEjT+Ai2ygdnm+yROfE0A, hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
	sakari.ailus-VuQAYsv1563Yd54FQh9/CA, crope-X3B1VOXEql0
  Cc: chris.paterson2-zM6kxYcvzFBBDgjK7y7TUQ,
	laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
	geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ,
	linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA,
	Ramesh Shanmugasundaram

Hi All,

This patch set contains two drivers
 - R-Car Digital Radio Interface (DRIF) driver
 - Maxim's MAX2175 RF to Bits tuner driver

These patches were based on top of media-next repo
commit: 778de01402328ca88cda84491d819bfc949935ff

These two drivers combined together expose a V4L2 SDR device that is compliant
with the V4L2 framework [1]. The RFC series of these two drivers were
published few weeks ago and the discussion thread is here [2]. Agreed review
comments are incorporated in this series.

Brief description of devices below

DRIF:
-----
This is a receive only slave controller that receives data into a FIFO from a
master device and uses DMA engine to move it from device to memory. It is a
serial I/O like controller with a design goal to act as digital radio receiver
targeting SDR solutions.

MAX2175:
--------
This is a RF front end tuner device that supports tuning to different bands &
frequency. It supports I2S output with programmable word lengths & single/dual
data lines usage based on selected receive mode. Each receive mode is
designated with a sample rate.

+---------------------+                +---------------------+
|                     |-----SCK------->|                     |
| MAX2175 (master)    |-----SS-------->|  DRIF (slave)       |
|                     |-----SD0------->|                     |
|                     |-----SD1------->|                     |
+---------------------+                +---------------------+

New SDR formats:
----------------
The combined driver exposes new SDR formats. DRIF as such requires the receive
word length as the only programmable parameter in a normal case. Otherwise it is
agnostic of the tuner.

V4L2 framework requires publishing SDR formats about position of I & Q data
within the buffers. I have published three such formats to give an example. More
such formats are possible and will be added when needed.

References:
------------
[1] v4l2-compliance test report

root@salvator-x:~# v4l2-compliance -S /dev/swradio0
v4l2-compliance SHA   : 788b674f3827607c09c31be11c91638f816aa6ae

Driver Info:
        Driver name   : rcar_drif
        Card type     : R-Car DRIF
        Bus info      : platform:R-Car DRIF
        Driver version: 4.9.0
        Capabilities  : 0x85310000
                SDR Capture
                Tuner
                Read/Write
                Streaming
                Extended Pix Format
                Device Capabilities
        Device Caps   : 0x05310000
                SDR Capture
                Tuner
                Read/Write
                Streaming
                Extended Pix Format

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

Required ioctls:
        test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
        test second sdr 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

Input ioctls:
        test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK
        test VIDIOC_G/S_FREQUENCY: OK
        test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
        test VIDIOC_ENUMAUDIO: OK (Not Supported)
        test VIDIOC_G/S/ENUMINPUT: OK (Not Supported)
        test VIDIOC_G/S_AUDIO: OK (Not Supported)
        Inputs: 0 Audio Inputs: 0 Tuners: 1

Output ioctls:
        test VIDIOC_G/S_MODULATOR: OK (Not Supported)
        test VIDIOC_G/S_FREQUENCY: OK
        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)

        Control ioctls:
                test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
                test VIDIOC_QUERYCTRL: OK
                test VIDIOC_G/S_CTRL: OK
                test VIDIOC_G/S/TRY_EXT_CTRLS: OK
                test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
                test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
                Standard Controls: 5 Private Controls: 4

        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 (Not Supported)

Test input 0:


Total: 43, Succeeded: 43, Failed: 0, Warnings: 0
root@salvator-x:~#

[2] RFC patch set discussion thread
https://www.mail-archive.com/linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org/msg07968.html

Ramesh Shanmugasundaram (5):
  media: v4l2-ctrls: Reserve controls for MAX217X
  media: i2c: max2175: Add MAX2175 support
  media: Add new SDR formats SC16, SC18 & SC20
  doc_rst: media: New SDR formats SC16, SC18 & SC20
  media: platform: rcar_drif: Add DRIF support

 .../devicetree/bindings/media/i2c/max2175.txt      |   61 +
 .../devicetree/bindings/media/renesas,drif.txt     |  136 ++
 .../media/uapi/v4l/pixfmt-sdr-scu16be.rst          |   80 +
 .../media/uapi/v4l/pixfmt-sdr-scu18be.rst          |   80 +
 .../media/uapi/v4l/pixfmt-sdr-scu20be.rst          |   80 +
 Documentation/media/uapi/v4l/sdr-formats.rst       |    3 +
 drivers/media/i2c/Kconfig                          |    4 +
 drivers/media/i2c/Makefile                         |    2 +
 drivers/media/i2c/max2175/Kconfig                  |    8 +
 drivers/media/i2c/max2175/Makefile                 |    4 +
 drivers/media/i2c/max2175/max2175.c                | 1558 +++++++++++++++++++
 drivers/media/i2c/max2175/max2175.h                |  108 ++
 drivers/media/platform/Kconfig                     |   25 +
 drivers/media/platform/Makefile                    |    1 +
 drivers/media/platform/rcar_drif.c                 | 1574 ++++++++++++++++++++
 drivers/media/v4l2-core/v4l2-ioctl.c               |    3 +
 include/uapi/linux/v4l2-controls.h                 |    5 +
 include/uapi/linux/videodev2.h                     |    3 +
 18 files changed, 3735 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/max2175.txt
 create mode 100644 Documentation/devicetree/bindings/media/renesas,drif.txt
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-scu16be.rst
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-scu18be.rst
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-scu20be.rst
 create mode 100644 drivers/media/i2c/max2175/Kconfig
 create mode 100644 drivers/media/i2c/max2175/Makefile
 create mode 100644 drivers/media/i2c/max2175/max2175.c
 create mode 100644 drivers/media/i2c/max2175/max2175.h
 create mode 100644 drivers/media/platform/rcar_drif.c

-- 
1.9.1

--
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

* [PATCH 0/5] Add V4L2 SDR (DRIF & MAX2175) driver
@ 2016-11-09 15:44 ` Ramesh Shanmugasundaram
  0 siblings, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-11-09 15:44 UTC (permalink / raw)
  To: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope
  Cc: chris.paterson2, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc, Ramesh Shanmugasundaram

Hi All,

This patch set contains two drivers
 - R-Car Digital Radio Interface (DRIF) driver
 - Maxim's MAX2175 RF to Bits tuner driver

These patches were based on top of media-next repo
commit: 778de01402328ca88cda84491d819bfc949935ff

These two drivers combined together expose a V4L2 SDR device that is compliant
with the V4L2 framework [1]. The RFC series of these two drivers were
published few weeks ago and the discussion thread is here [2]. Agreed review
comments are incorporated in this series.

Brief description of devices below

DRIF:
-----
This is a receive only slave controller that receives data into a FIFO from a
master device and uses DMA engine to move it from device to memory. It is a
serial I/O like controller with a design goal to act as digital radio receiver
targeting SDR solutions.

MAX2175:
--------
This is a RF front end tuner device that supports tuning to different bands &
frequency. It supports I2S output with programmable word lengths & single/dual
data lines usage based on selected receive mode. Each receive mode is
designated with a sample rate.

+---------------------+                +---------------------+
|                     |-----SCK------->|                     |
| MAX2175 (master)    |-----SS-------->|  DRIF (slave)       |
|                     |-----SD0------->|                     |
|                     |-----SD1------->|                     |
+---------------------+                +---------------------+

New SDR formats:
----------------
The combined driver exposes new SDR formats. DRIF as such requires the receive
word length as the only programmable parameter in a normal case. Otherwise it is
agnostic of the tuner.

V4L2 framework requires publishing SDR formats about position of I & Q data
within the buffers. I have published three such formats to give an example. More
such formats are possible and will be added when needed.

References:
------------
[1] v4l2-compliance test report

root@salvator-x:~# v4l2-compliance -S /dev/swradio0
v4l2-compliance SHA   : 788b674f3827607c09c31be11c91638f816aa6ae

Driver Info:
        Driver name   : rcar_drif
        Card type     : R-Car DRIF
        Bus info      : platform:R-Car DRIF
        Driver version: 4.9.0
        Capabilities  : 0x85310000
                SDR Capture
                Tuner
                Read/Write
                Streaming
                Extended Pix Format
                Device Capabilities
        Device Caps   : 0x05310000
                SDR Capture
                Tuner
                Read/Write
                Streaming
                Extended Pix Format

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

Required ioctls:
        test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
        test second sdr 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

Input ioctls:
        test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK
        test VIDIOC_G/S_FREQUENCY: OK
        test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
        test VIDIOC_ENUMAUDIO: OK (Not Supported)
        test VIDIOC_G/S/ENUMINPUT: OK (Not Supported)
        test VIDIOC_G/S_AUDIO: OK (Not Supported)
        Inputs: 0 Audio Inputs: 0 Tuners: 1

Output ioctls:
        test VIDIOC_G/S_MODULATOR: OK (Not Supported)
        test VIDIOC_G/S_FREQUENCY: OK
        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)

        Control ioctls:
                test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
                test VIDIOC_QUERYCTRL: OK
                test VIDIOC_G/S_CTRL: OK
                test VIDIOC_G/S/TRY_EXT_CTRLS: OK
                test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
                test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
                Standard Controls: 5 Private Controls: 4

        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 (Not Supported)

Test input 0:


Total: 43, Succeeded: 43, Failed: 0, Warnings: 0
root@salvator-x:~#

[2] RFC patch set discussion thread
https://www.mail-archive.com/linux-renesas-soc@vger.kernel.org/msg07968.html

Ramesh Shanmugasundaram (5):
  media: v4l2-ctrls: Reserve controls for MAX217X
  media: i2c: max2175: Add MAX2175 support
  media: Add new SDR formats SC16, SC18 & SC20
  doc_rst: media: New SDR formats SC16, SC18 & SC20
  media: platform: rcar_drif: Add DRIF support

 .../devicetree/bindings/media/i2c/max2175.txt      |   61 +
 .../devicetree/bindings/media/renesas,drif.txt     |  136 ++
 .../media/uapi/v4l/pixfmt-sdr-scu16be.rst          |   80 +
 .../media/uapi/v4l/pixfmt-sdr-scu18be.rst          |   80 +
 .../media/uapi/v4l/pixfmt-sdr-scu20be.rst          |   80 +
 Documentation/media/uapi/v4l/sdr-formats.rst       |    3 +
 drivers/media/i2c/Kconfig                          |    4 +
 drivers/media/i2c/Makefile                         |    2 +
 drivers/media/i2c/max2175/Kconfig                  |    8 +
 drivers/media/i2c/max2175/Makefile                 |    4 +
 drivers/media/i2c/max2175/max2175.c                | 1558 +++++++++++++++++++
 drivers/media/i2c/max2175/max2175.h                |  108 ++
 drivers/media/platform/Kconfig                     |   25 +
 drivers/media/platform/Makefile                    |    1 +
 drivers/media/platform/rcar_drif.c                 | 1574 ++++++++++++++++++++
 drivers/media/v4l2-core/v4l2-ioctl.c               |    3 +
 include/uapi/linux/v4l2-controls.h                 |    5 +
 include/uapi/linux/videodev2.h                     |    3 +
 18 files changed, 3735 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/max2175.txt
 create mode 100644 Documentation/devicetree/bindings/media/renesas,drif.txt
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-scu16be.rst
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-scu18be.rst
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-scu20be.rst
 create mode 100644 drivers/media/i2c/max2175/Kconfig
 create mode 100644 drivers/media/i2c/max2175/Makefile
 create mode 100644 drivers/media/i2c/max2175/max2175.c
 create mode 100644 drivers/media/i2c/max2175/max2175.h
 create mode 100644 drivers/media/platform/rcar_drif.c

-- 
1.9.1


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

* [PATCH 1/5] media: v4l2-ctrls: Reserve controls for MAX217X
  2016-11-09 15:44 ` Ramesh Shanmugasundaram
  (?)
@ 2016-11-09 15:44 ` Ramesh Shanmugasundaram
  -1 siblings, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-11-09 15:44 UTC (permalink / raw)
  To: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope
  Cc: chris.paterson2, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc, Ramesh Shanmugasundaram

Reserve controls for MAX217X RF to Bits tuner (family) chips. These hybrid
radio receiver chips are highly programmable and hence reserving 32
controls.

Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>
---
 include/uapi/linux/v4l2-controls.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index b6a357a..b7404c9 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -180,6 +180,11 @@ enum v4l2_colorfx {
  * We reserve 16 controls for this driver. */
 #define V4L2_CID_USER_TC358743_BASE		(V4L2_CID_USER_BASE + 0x1080)
 
+/* The base for the max217x driver controls.
+ * We reserve 32 controls for this driver
+ */
+#define V4L2_CID_USER_MAX217X_BASE		(V4L2_CID_USER_BASE + 0x1090)
+
 /* MPEG-class control IDs */
 /* The MPEG controls are applicable to all codec controls
  * and the 'MPEG' part of the define is historical */
-- 
1.9.1

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

* [PATCH 2/5] media: i2c: max2175: Add MAX2175 support
  2016-11-09 15:44 ` Ramesh Shanmugasundaram
@ 2016-11-09 15:44     ` Ramesh Shanmugasundaram
  -1 siblings, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-11-09 15:44 UTC (permalink / raw)
  To: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	mchehab-DgEjT+Ai2ygdnm+yROfE0A, hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
	sakari.ailus-VuQAYsv1563Yd54FQh9/CA, crope-X3B1VOXEql0
  Cc: chris.paterson2-zM6kxYcvzFBBDgjK7y7TUQ,
	laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
	geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ,
	linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA,
	Ramesh Shanmugasundaram

This patch adds driver support for MAX2175 chip. This is Maxim
Integrated's RF to Bits tuner front end chip designed for software-defined
radio solutions. This driver exposes the tuner as a sub-device instance
with standard and custom controls to configure the device.

Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>
---
 .../devicetree/bindings/media/i2c/max2175.txt      |   61 +
 drivers/media/i2c/Kconfig                          |    4 +
 drivers/media/i2c/Makefile                         |    2 +
 drivers/media/i2c/max2175/Kconfig                  |    8 +
 drivers/media/i2c/max2175/Makefile                 |    4 +
 drivers/media/i2c/max2175/max2175.c                | 1558 ++++++++++++++++++++
 drivers/media/i2c/max2175/max2175.h                |  108 ++
 7 files changed, 1745 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/max2175.txt
 create mode 100644 drivers/media/i2c/max2175/Kconfig
 create mode 100644 drivers/media/i2c/max2175/Makefile
 create mode 100644 drivers/media/i2c/max2175/max2175.c
 create mode 100644 drivers/media/i2c/max2175/max2175.h

diff --git a/Documentation/devicetree/bindings/media/i2c/max2175.txt b/Documentation/devicetree/bindings/media/i2c/max2175.txt
new file mode 100644
index 0000000..69f0dad
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/max2175.txt
@@ -0,0 +1,61 @@
+Maxim Integrated MAX2175 RF to Bits tuner
+-----------------------------------------
+
+The MAX2175 IC is an advanced analog/digital hybrid-radio receiver with
+RF to Bits® front-end designed for software-defined radio solutions.
+
+Required properties:
+--------------------
+- compatible: "maxim,max2175" for MAX2175 RF-to-bits tuner.
+- clocks: phandle to the fixed xtal clock.
+- clock-names: name of the fixed xtal clock.
+- port: child port node of a tuner that defines the local and remote
+  endpoints. The remote endpoint is assumed to be an SDR device
+  that is capable of receiving the digital samples from the tuner.
+
+Optional properties:
+--------------------
+- maxim,slave	      : empty property indicates this is a slave of
+			another master tuner. This is used to define two
+			tuners in diversity mode (1 master, 1 slave). By
+			default each tuner is an individual master.
+- maxim,refout-load-pF: load capacitance value (in pF) on reference
+			output drive level. The possible load values are
+			 0pF (default - refout disabled)
+			10pF
+			20pF
+			30pF
+			40pF
+			60pF
+			70pF
+- maxim,am-hiz	      : empty property indicates AM Hi-Z filter path is
+			selected for AM antenna input. By default this
+			filter path is not used.
+
+Example:
+--------
+
+Board specific DTS file
+
+/* Fixed XTAL clock node */
+maxim_xtal: maximextal {
+	compatible = "fixed-clock";
+	#clock-cells = <0>;
+	clock-frequency = <36864000>;
+};
+
+/* A tuner device instance under i2c bus */
+max2175_0: tuner@60 {
+	compatible = "maxim,max2175";
+	reg = <0x60>;
+	clocks = <&maxim_xtal>;
+	clock-names = "xtal";
+	maxim,refout-load-pF = <10>;
+
+	port {
+		max2175_0_ep: endpoint {
+			remote-endpoint = <&slave_rx_v4l2_sdr_device>;
+		};
+	};
+
+};
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 2669b4b..66c73b0 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -749,6 +749,10 @@ config VIDEO_SAA6752HS
 	  To compile this driver as a module, choose M here: the
 	  module will be called saa6752hs.
 
+comment "SDR tuner chips"
+
+source "drivers/media/i2c/max2175/Kconfig"
+
 comment "Miscellaneous helper chips"
 
 config VIDEO_THS7303
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 92773b2..cfae721 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -6,6 +6,8 @@ obj-$(CONFIG_VIDEO_CX25840) += cx25840/
 obj-$(CONFIG_VIDEO_M5MOLS)	+= m5mols/
 obj-y				+= soc_camera/
 
+obj-$(CONFIG_SDR_MAX2175) 	+= max2175/
+
 obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o
 obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o
 obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o
diff --git a/drivers/media/i2c/max2175/Kconfig b/drivers/media/i2c/max2175/Kconfig
new file mode 100644
index 0000000..93a8f83
--- /dev/null
+++ b/drivers/media/i2c/max2175/Kconfig
@@ -0,0 +1,8 @@
+config SDR_MAX2175
+	tristate "Maxim 2175 RF to Bits tuner"
+	depends on VIDEO_V4L2 && MEDIA_SDR_SUPPORT && I2C
+	---help---
+	  Support for Maxim 2175 tuner
+
+	  To compile this driver as a module, choose M here; the
+	  module will be called max2175.
diff --git a/drivers/media/i2c/max2175/Makefile b/drivers/media/i2c/max2175/Makefile
new file mode 100644
index 0000000..9bb46ac
--- /dev/null
+++ b/drivers/media/i2c/max2175/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for Maxim RF to Bits tuner device
+#
+obj-$(CONFIG_SDR_MAX2175) += max2175.o
diff --git a/drivers/media/i2c/max2175/max2175.c b/drivers/media/i2c/max2175/max2175.c
new file mode 100644
index 0000000..ec45b52
--- /dev/null
+++ b/drivers/media/i2c/max2175/max2175.c
@@ -0,0 +1,1558 @@
+/*
+ * Maxim Integrated MAX2175 RF to Bits tuner driver
+ *
+ * This driver & most of the hard coded values are based on the reference
+ * application delivered by Maxim for this chip.
+ *
+ * Copyright (C) 2016 Maxim Integrated Products
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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/errno.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-of.h>
+
+#include "max2175.h"
+
+#define DRIVER_NAME "max2175"
+
+static unsigned int debug;
+module_param(debug, uint, 0644);
+MODULE_PARM_DESC(debug, "activate debug info");
+
+#define mxm_dbg(ctx, fmt, arg...) v4l2_dbg(1, debug, &ctx->sd, fmt, ## arg)
+
+/* Rx mode */
+struct max2175_rxmode {
+	enum max2175_band band;		/* Associated band */
+	u32 freq;			/* Default freq in Hz */
+	u8 i2s_word_size;		/* Bit value */
+	u8 i2s_modes[4];		/* Supported modes */
+};
+
+/* Register map to define preset values */
+struct max2175_reg_map {
+	u8 idx;				/* Register index */
+	u8 val;				/* Register value */
+};
+
+static const struct max2175_rxmode eu_rx_modes[] = {
+	/* EU modes */
+	[MAX2175_EU_FM_1_2] = { MAX2175_BAND_FM,
+				98256000, 1, { 0, 0, 0, 0 } },
+	[MAX2175_DAB_1_2]   = { MAX2175_BAND_VHF,
+				182640000, 0, { 0, 0, 0, 0 } },
+};
+
+static const struct max2175_rxmode na_rx_modes[] = {
+	/* NA modes */
+	[MAX2175_NA_FM_1_0] = { MAX2175_BAND_FM,
+				98255520, 1, { 0, 0, 0, 0 } },
+	[MAX2175_NA_FM_2_0] = { MAX2175_BAND_FM,
+				98255520, 6, { 0, 0, 0, 0 } },
+};
+
+/*
+ * Preset values:
+ * Based on Maxim MAX2175 Register Table revision: 130p10
+ */
+static const u8 full_fm_eu_1p0[] = {
+	0x15, 0x04, 0xb8, 0xe3, 0x35, 0x18, 0x7c, 0x00,
+	0x00, 0x7d, 0x40, 0x08, 0x70, 0x7a, 0x88, 0x91,
+	0x61, 0x61, 0x61, 0x61, 0x5a, 0x0f, 0x34, 0x1c,
+	0x14, 0x88, 0x33, 0x02, 0x00, 0x09, 0x00, 0x65,
+	0x9f, 0x2b, 0x80, 0x00, 0x95, 0x05, 0x2c, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
+	0x4a, 0x08, 0xa8, 0x0e, 0x0e, 0x2f, 0x7e, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x5e, 0xa9,
+	0xae, 0xbb, 0x57, 0x18, 0x3b, 0x03, 0x3b, 0x64,
+	0x40, 0x60, 0x00, 0x2a, 0xbf, 0x3f, 0xff, 0x9f,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00,
+	0xff, 0xfc, 0xef, 0x1c, 0x40, 0x00, 0x00, 0x02,
+	0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x40, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00,
+	0x00, 0x47, 0x00, 0x00, 0x11, 0x3f, 0x22, 0x00,
+	0xf1, 0x00, 0x41, 0x03, 0xb0, 0x00, 0x00, 0x00,
+	0x1b,
+};
+
+static const u8 full_fm_na_1p0[] = {
+	0x13, 0x08, 0x8d, 0xc0, 0x35, 0x18, 0x7d, 0x3f,
+	0x7d, 0x75, 0x40, 0x08, 0x70, 0x7a, 0x88, 0x91,
+	0x61, 0x61, 0x61, 0x61, 0x5c, 0x0f, 0x34, 0x1c,
+	0x14, 0x88, 0x33, 0x02, 0x00, 0x01, 0x00, 0x65,
+	0x9f, 0x2b, 0x80, 0x00, 0x95, 0x05, 0x2c, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
+	0x4a, 0x08, 0xa8, 0x0e, 0x0e, 0xaf, 0x7e, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x5e, 0xa9,
+	0xae, 0xbb, 0x57, 0x18, 0x3b, 0x03, 0x3b, 0x64,
+	0x40, 0x60, 0x00, 0x2a, 0xbf, 0x3f, 0xff, 0x9f,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00,
+	0xff, 0xfc, 0xef, 0x1c, 0x40, 0x00, 0x00, 0x02,
+	0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0x40, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00,
+	0x00, 0x35, 0x00, 0x00, 0x11, 0x3f, 0x22, 0x00,
+	0xf1, 0x00, 0x41, 0x03, 0xb0, 0x00, 0x00, 0x00,
+	0x1b,
+};
+
+/* DAB1.2 settings */
+static const struct max2175_reg_map dab12_map[] = {
+	{ 0x01, 0x13 }, { 0x02, 0x0d }, { 0x03, 0x15 }, { 0x04, 0x55 },
+	{ 0x05, 0x0a }, { 0x06, 0xa0 }, { 0x07, 0x40 }, { 0x08, 0x00 },
+	{ 0x09, 0x00 }, { 0x0a, 0x7d }, { 0x0b, 0x4a }, { 0x0c, 0x28 },
+	{ 0x0e, 0x43 }, { 0x0f, 0xb5 }, { 0x10, 0x31 }, { 0x11, 0x9e },
+	{ 0x12, 0x68 }, { 0x13, 0x9e }, { 0x14, 0x68 }, { 0x15, 0x58 },
+	{ 0x16, 0x2f }, { 0x17, 0x3f }, { 0x18, 0x40 }, { 0x1a, 0x88 },
+	{ 0x1b, 0xaa }, { 0x1c, 0x9a }, { 0x1d, 0x00 }, { 0x1e, 0x00 },
+	{ 0x23, 0x80 }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 },
+	{ 0x27, 0x00 }, { 0x32, 0x08 }, { 0x33, 0xf8 }, { 0x36, 0x2d },
+	{ 0x37, 0x7e }, { 0x55, 0xaf }, { 0x56, 0x3f }, { 0x57, 0xf8 },
+	{ 0x58, 0x99 }, { 0x76, 0x00 }, { 0x77, 0x00 }, { 0x78, 0x02 },
+	{ 0x79, 0x40 }, { 0x82, 0x00 }, { 0x83, 0x00 }, { 0x85, 0x00 },
+	{ 0x86, 0x20 },
+};
+
+/* EU FM 1.2 settings */
+static const struct max2175_reg_map fmeu1p2_map[] = {
+	{ 0x01, 0x15 }, { 0x02, 0x04 }, { 0x03, 0xb8 }, { 0x04, 0xe3 },
+	{ 0x05, 0x35 }, { 0x06, 0x18 }, { 0x07, 0x7c }, { 0x08, 0x00 },
+	{ 0x09, 0x00 }, { 0x0a, 0x73 }, { 0x0b, 0x40 }, { 0x0c, 0x08 },
+	{ 0x0e, 0x7a }, { 0x0f, 0x88 }, { 0x10, 0x91 }, { 0x11, 0x61 },
+	{ 0x12, 0x61 }, { 0x13, 0x61 }, { 0x14, 0x61 }, { 0x15, 0x5a },
+	{ 0x16, 0x0f }, { 0x17, 0x34 }, { 0x18, 0x1c }, { 0x1a, 0x88 },
+	{ 0x1b, 0x33 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x1e, 0x01 },
+	{ 0x23, 0x80 }, { 0x24, 0x00 }, { 0x25, 0x95 }, { 0x26, 0x05 },
+	{ 0x27, 0x2c }, { 0x32, 0x08 }, { 0x33, 0xa8 }, { 0x36, 0x2f },
+	{ 0x37, 0x7e }, { 0x55, 0xbf }, { 0x56, 0x3f }, { 0x57, 0xff },
+	{ 0x58, 0x9f }, { 0x76, 0xac }, { 0x77, 0x40 }, { 0x78, 0x00 },
+	{ 0x79, 0x00 }, { 0x82, 0x47 }, { 0x83, 0x00 }, { 0x85, 0x11 },
+	{ 0x86, 0x3f },
+};
+
+/* FM NA 1.0 settings */
+static const struct max2175_reg_map fmna1p0_map[] = {
+	{ 0x01, 0x13 }, { 0x02, 0x08 }, { 0x03, 0x8d }, { 0x04, 0xc0 },
+	{ 0x05, 0x35 }, { 0x06, 0x18 }, { 0x07, 0x7d }, { 0x08, 0x3f },
+	{ 0x09, 0x7d }, { 0x0a, 0x75 }, { 0x0b, 0x40 }, { 0x0c, 0x08 },
+	{ 0x0e, 0x7a }, { 0x0f, 0x88 }, { 0x10, 0x91 }, { 0x11, 0x61 },
+	{ 0x12, 0x61 }, { 0x13, 0x61 }, { 0x14, 0x61 }, { 0x15, 0x5c },
+	{ 0x16, 0x0f }, { 0x17, 0x34 }, { 0x18, 0x1c }, { 0x1a, 0x88 },
+	{ 0x1b, 0x33 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x1e, 0x01 },
+	{ 0x23, 0x80 }, { 0x24, 0x00 }, { 0x25, 0x95 }, { 0x26, 0x05 },
+	{ 0x27, 0x2c }, { 0x32, 0x08 }, { 0x33, 0xa8 }, { 0x36, 0xaf },
+	{ 0x37, 0x7e }, { 0x55, 0xbf }, { 0x56, 0x3f }, { 0x57, 0xff },
+	{ 0x58, 0x9f }, { 0x76, 0xa6 }, { 0x77, 0x40 }, { 0x78, 0x00 },
+	{ 0x79, 0x00 }, { 0x82, 0x35 }, { 0x83, 0x00 }, { 0x85, 0x11 },
+	{ 0x86, 0x3f },
+};
+
+/* FM NA 2.0 settings */
+static const struct max2175_reg_map fmna2p0_map[] = {
+	{ 0x01, 0x13 }, { 0x02, 0x08 }, { 0x03, 0x8d }, { 0x04, 0xc0 },
+	{ 0x05, 0x35 }, { 0x06, 0x18 }, { 0x07, 0x7c }, { 0x08, 0x54 },
+	{ 0x09, 0xa7 }, { 0x0a, 0x55 }, { 0x0b, 0x42 }, { 0x0c, 0x48 },
+	{ 0x0e, 0x7a }, { 0x0f, 0x88 }, { 0x10, 0x91 }, { 0x11, 0x61 },
+	{ 0x12, 0x61 }, { 0x13, 0x61 }, { 0x14, 0x61 }, { 0x15, 0x5c },
+	{ 0x16, 0x0f }, { 0x17, 0x34 }, { 0x18, 0x1c }, { 0x1a, 0x88 },
+	{ 0x1b, 0x33 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x1e, 0x01 },
+	{ 0x23, 0x80 }, { 0x24, 0x00 }, { 0x25, 0x95 }, { 0x26, 0x05 },
+	{ 0x27, 0x2c }, { 0x32, 0x08 }, { 0x33, 0xa8 }, { 0x36, 0xaf },
+	{ 0x37, 0x7e }, { 0x55, 0xbf }, { 0x56, 0x3f }, { 0x57, 0xff },
+	{ 0x58, 0x9f }, { 0x76, 0xac }, { 0x77, 0xc0 }, { 0x78, 0x00 },
+	{ 0x79, 0x00 }, { 0x82, 0x6b }, { 0x83, 0x00 }, { 0x85, 0x11 },
+	{ 0x86, 0x3f },
+};
+
+static const u16 ch_coeff_dab1[] = {
+	0x001c, 0x0007, 0xffcd, 0x0056, 0xffa4, 0x0033, 0x0027, 0xff61,
+	0x010e, 0xfec0, 0x0106, 0xffb8, 0xff1c, 0x023c, 0xfcb2, 0x039b,
+	0xfd4e, 0x0055, 0x036a, 0xf7de, 0x0d21, 0xee72, 0x1499, 0x6a51,
+};
+
+static const u16 ch_coeff_fmeu[] = {
+	0x0000, 0xffff, 0x0001, 0x0002, 0xfffa, 0xffff, 0x0015, 0xffec,
+	0xffde, 0x0054, 0xfff9, 0xff52, 0x00b8, 0x00a2, 0xfe0a, 0x00af,
+	0x02e3, 0xfc14, 0xfe89, 0x089d, 0xfa2e, 0xf30f, 0x25be, 0x4eb6,
+};
+
+static const u16 eq_coeff_fmeu1_ra02_m6db[] = {
+	0x0040, 0xffc6, 0xfffa, 0x002c, 0x000d, 0xff90, 0x0037, 0x006e,
+	0xffc0, 0xff5b, 0x006a, 0x00f0, 0xff57, 0xfe94, 0x0112, 0x0252,
+	0xfe0c, 0xfc6a, 0x0385, 0x0553, 0xfa49, 0xf789, 0x0b91, 0x1a10,
+};
+
+static const u16 ch_coeff_fmna[] = {
+	0x0001, 0x0003, 0xfffe, 0xfff4, 0x0000, 0x001f, 0x000c, 0xffbc,
+	0xffd3, 0x007d, 0x0075, 0xff33, 0xff01, 0x0131, 0x01ef, 0xfe60,
+	0xfc7a, 0x020e, 0x0656, 0xfd94, 0xf395, 0x02ab, 0x2857, 0x3d3f,
+};
+
+static const u16 eq_coeff_fmna1_ra02_m6db[] = {
+	0xfff1, 0xffe1, 0xffef, 0x000e, 0x0030, 0x002f, 0xfff6, 0xffa7,
+	0xff9d, 0x000a, 0x00a2, 0x00b5, 0xffea, 0xfed9, 0xfec5, 0x003d,
+	0x0217, 0x021b, 0xff5a, 0xfc2b, 0xfcbd, 0x02c4, 0x0ac3, 0x0e85,
+};
+
+static const u8 adc_presets[2][23] = {
+	{
+		0x83, 0x00, 0xcf, 0xb4, 0x0f, 0x2c, 0x0c, 0x49,
+		0x00, 0x00, 0x00, 0x8c,	0x02, 0x02, 0x00, 0x04,
+		0xec, 0x82, 0x4b, 0xcc, 0x01, 0x88, 0x0c,
+	},
+	{
+		0x83, 0x00, 0xcf, 0xb4,	0x0f, 0x2c, 0x0c, 0x49,
+		0x00, 0x00, 0x00, 0x8c,	0x02, 0x20, 0x33, 0x8c,
+		0x57, 0xd7, 0x59, 0xb7,	0x65, 0x0e, 0x0c,
+	},
+};
+
+/* Custom controls */
+#define V4L2_CID_MAX2175_I2S_ENABLE	(V4L2_CID_USER_MAX217X_BASE + 0x01)
+#define V4L2_CID_MAX2175_I2S_MODE	(V4L2_CID_USER_MAX217X_BASE + 0x02)
+#define V4L2_CID_MAX2175_HSLS		(V4L2_CID_USER_MAX217X_BASE + 0x03)
+#define V4L2_CID_MAX2175_RX_MODE	(V4L2_CID_USER_MAX217X_BASE + 0x04)
+
+/* Tuner bands */
+static const struct v4l2_frequency_band eu_bands_rf = {
+	.tuner = 0,
+	.type = V4L2_TUNER_RF,
+	.index = 0,
+	.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+	.rangelow   = 65000000,
+	.rangehigh  = 240000000,
+};
+
+static const struct v4l2_frequency_band na_bands_rf = {
+	.tuner = 0,
+	.type = V4L2_TUNER_RF,
+	.index = 0,
+	.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+	.rangelow   = 65000000,
+	.rangehigh  = 108000000,
+};
+
+/* Regmap settings */
+static const struct regmap_range max2175_regmap_volatile_range[] = {
+	regmap_reg_range(0x30, 0x35),
+	regmap_reg_range(0x3a, 0x45),
+	regmap_reg_range(0x59, 0x5e),
+	regmap_reg_range(0x73, 0x75),
+};
+
+static const struct regmap_access_table max2175_volatile_regs = {
+	.yes_ranges = max2175_regmap_volatile_range,
+	.n_yes_ranges = ARRAY_SIZE(max2175_regmap_volatile_range),
+};
+
+static const struct reg_default max2175_reg_defaults[] = {
+	{ 0x00, 0x07},
+};
+
+static const struct regmap_config max2175_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = 0xff,
+	.reg_defaults = max2175_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(max2175_reg_defaults),
+	.volatile_table = &max2175_volatile_regs,
+	.cache_type = REGCACHE_FLAT,
+};
+
+struct max2175 {
+	struct v4l2_subdev sd;		/* Sub-device */
+	struct i2c_client *client;	/* I2C client */
+
+	/* Controls */
+	struct v4l2_ctrl_handler ctrl_hdl;
+	struct v4l2_ctrl *lna_gain;	/* LNA gain value */
+	struct v4l2_ctrl *if_gain;	/* I/F gain value */
+	struct v4l2_ctrl *pll_lock;	/* PLL lock */
+	struct v4l2_ctrl *i2s_en;	/* I2S output enable */
+	struct v4l2_ctrl *i2s_mode;	/* I2S mode value */
+	struct v4l2_ctrl *hsls;		/* High-side/Low-side polarity */
+	struct v4l2_ctrl *rx_mode;	/* Receive mode */
+
+	/* Regmap */
+	struct regmap *regmap;
+
+	/* Cached configuration */
+	u32 freq;			/* Tuned freq In Hz */
+	const struct max2175_rxmode *rx_modes;		/* EU or NA modes */
+	const struct v4l2_frequency_band *bands_rf;	/* EU or NA bands */
+
+	/* Device settings */
+	unsigned long xtal_freq;	/* Ref Oscillator freq in Hz */
+	u32 decim_ratio;
+	bool master;			/* Master/Slave */
+	bool am_hiz;			/* AM Hi-Z filter usage */
+
+	/* ROM values */
+	u8 rom_bbf_bw_am;
+	u8 rom_bbf_bw_fm;
+	u8 rom_bbf_bw_dab;
+
+	/* Local copy of old setting */
+	u8 i2s_test;
+
+	/* Driver private variables */
+	bool mode_resolved;		/* Flag to sanity check settings */
+};
+
+static inline struct max2175 *max2175_from_sd(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct max2175, sd);
+}
+
+static inline struct max2175 *max2175_from_ctrl_hdl(struct v4l2_ctrl_handler *h)
+{
+	return container_of(h, struct max2175, ctrl_hdl);
+}
+
+/* Get bitval of a given val */
+static inline u8 max2175_get_bitval(u8 val, u8 msb, u8 lsb)
+{
+	return (val & GENMASK(msb, lsb)) >> lsb;
+}
+
+/* Read/Write bit(s) on top of regmap */
+static int max2175_read(struct max2175 *ctx, u8 idx, u8 *val)
+{
+	u32 regval;
+	int ret = regmap_read(ctx->regmap, idx, &regval);
+
+	if (ret)
+		v4l2_err(ctx->client, "read ret(%d): idx 0x%02x\n", ret, idx);
+
+	*val = regval;
+	return ret;
+}
+
+static int max2175_write(struct max2175 *ctx, u8 idx, u8 val)
+{
+	int ret = regmap_write(ctx->regmap, idx, val);
+
+	if (ret)
+		v4l2_err(ctx->client, "write ret(%d): idx 0x%02x val 0x%02x\n",
+			 ret, idx, val);
+	return ret;
+}
+
+static u8 max2175_read_bits(struct max2175 *ctx, u8 idx, u8 msb, u8 lsb)
+{
+	u8 val;
+
+	if (max2175_read(ctx, idx, &val))
+		return 0;
+
+	return max2175_get_bitval(val, msb, lsb);
+}
+
+static bool max2175_read_bit(struct max2175 *ctx, u8 idx, u8 bit)
+{
+	return !!max2175_read_bits(ctx, idx, bit, bit);
+}
+
+static int max2175_write_bits(struct max2175 *ctx, u8 idx,
+			     u8 msb, u8 lsb, u8 newval)
+{
+	int ret = regmap_update_bits(ctx->regmap, idx, GENMASK(msb, lsb),
+				     newval << lsb);
+
+	if (ret)
+		v4l2_err(ctx->client, "wbits ret(%d): idx 0x%02x\n", ret, idx);
+
+	return ret;
+}
+
+static int max2175_write_bit(struct max2175 *ctx, u8 idx, u8 bit, u8 newval)
+{
+	return max2175_write_bits(ctx, idx, bit, bit, newval);
+}
+
+/* Checks expected pattern every msec until timeout */
+static int max2175_poll_timeout(struct max2175 *ctx, u8 idx, u8 msb, u8 lsb,
+				u8 exp_bitval, u32 timeout_ms)
+{
+	unsigned int val;
+
+	return regmap_read_poll_timeout(ctx->regmap, idx, val,
+			(max2175_get_bitval(val, msb, lsb) == exp_bitval),
+			1000, timeout_ms * 1000);
+}
+
+static int max2175_poll_csm_ready(struct max2175 *ctx)
+{
+	int ret;
+
+	ret = max2175_poll_timeout(ctx, 69, 1, 1, 0, 50);
+	if (ret)
+		v4l2_err(ctx->client, "csm not ready\n");
+
+	return ret;
+}
+
+#define MAX2175_IS_BAND_AM(ctx)		\
+	(max2175_read_bits(ctx, 5, 1, 0) == MAX2175_BAND_AM)
+
+#define MAX2175_IS_BAND_VHF(ctx)	\
+	(max2175_read_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF)
+
+#define MAX2175_IS_FM_MODE(ctx)		\
+	(max2175_read_bits(ctx, 12, 5, 4) == 0)
+
+#define MAX2175_IS_FMHD_MODE(ctx)	\
+	(max2175_read_bits(ctx, 12, 5, 4) == 1)
+
+#define MAX2175_IS_DAB_MODE(ctx)	\
+	(max2175_read_bits(ctx, 12, 5, 4) == 2)
+
+static int max2175_band_from_freq(u32 freq)
+{
+	if (freq >= 144000 && freq <= 26100000)
+		return MAX2175_BAND_AM;
+	else if (freq >= 65000000 && freq <= 108000000)
+		return MAX2175_BAND_FM;
+	else
+		return MAX2175_BAND_VHF;
+}
+
+static int max2175_update_i2s_mode(struct max2175 *ctx, u32 rx_mode,
+				   u32 i2s_mode)
+{
+	max2175_write_bits(ctx, 29, 2, 0, i2s_mode);
+
+	/* Based on I2S mode value I2S_WORD_CNT values change */
+	switch (i2s_mode) {
+	case MAX2175_I2S_MODE3:
+		max2175_write_bits(ctx, 30, 6, 0, 1);
+		break;
+	case MAX2175_I2S_MODE2:
+	case MAX2175_I2S_MODE4:
+		max2175_write_bits(ctx, 30, 6, 0, 0);
+		break;
+	case MAX2175_I2S_MODE0:
+		max2175_write_bits(ctx, 30, 6, 0,
+			ctx->rx_modes[rx_mode].i2s_word_size);
+		break;
+	}
+	mxm_dbg(ctx, "update_i2s_mode %u, rx_mode %u\n", i2s_mode, rx_mode);
+	return 0;
+}
+
+static void max2175_i2s_enable(struct max2175 *ctx, bool enable)
+{
+	if (enable) {
+		/* Use old setting */
+		max2175_write_bits(ctx, 104, 3, 0, ctx->i2s_test);
+	} else {
+		/* Cache old setting */
+		ctx->i2s_test = max2175_read_bits(ctx, 104, 3, 0);
+		max2175_write_bits(ctx, 104, 3, 0, 9);	/* Keep SCK alive */
+	}
+	mxm_dbg(ctx, "i2s %sabled: old val %u\n", enable ? "en" : "dis",
+		ctx->i2s_test);
+}
+
+static void max2175_set_filter_coeffs(struct max2175 *ctx, u8 m_sel,
+				      u8 bank, const u16 *coeffs)
+{
+	unsigned int i;
+	u8 coeff_addr, upper_address = 24;
+
+	mxm_dbg(ctx, "set_filter_coeffs: m_sel %d bank %d\n", m_sel, bank);
+	max2175_write_bits(ctx, 114, 5, 4, m_sel);
+
+	if (m_sel == 2)
+		upper_address = 12;
+
+	for (i = 0; i < upper_address; i++) {
+		coeff_addr = i + bank * 24;
+		max2175_write(ctx, 115, coeffs[i] >> 8);
+		max2175_write(ctx, 116, coeffs[i]);
+		max2175_write(ctx, 117, coeff_addr | 1 << 7);
+	}
+	max2175_write_bit(ctx, 117, 7, 0);
+}
+
+static void max2175_load_fmeu_1p2(struct max2175 *ctx)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(fmeu1p2_map); i++)
+		max2175_write(ctx, fmeu1p2_map[i].idx, fmeu1p2_map[i].val);
+
+	ctx->decim_ratio = 36;
+
+	/* Load the Channel Filter Coefficients into channel filter bank #2 */
+	max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 0, ch_coeff_fmeu);
+	max2175_set_filter_coeffs(ctx, MAX2175_EQ_MSEL, 0,
+				  eq_coeff_fmeu1_ra02_m6db);
+}
+
+static void max2175_load_dab_1p2(struct max2175 *ctx)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(dab12_map); i++)
+		max2175_write(ctx, dab12_map[i].idx, dab12_map[i].val);
+
+	ctx->decim_ratio = 1;
+
+	/* Load the Channel Filter Coefficients into channel filter bank #2 */
+	max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 2, ch_coeff_dab1);
+}
+
+static void max2175_load_fmna_1p0(struct max2175 *ctx)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(fmna1p0_map); i++)
+		max2175_write(ctx, fmna1p0_map[i].idx, fmna1p0_map[i].val);
+}
+
+static void max2175_load_fmna_2p0(struct max2175 *ctx)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(fmna2p0_map); i++)
+		max2175_write(ctx, fmna2p0_map[i].idx, fmna2p0_map[i].val);
+}
+
+static void max2175_set_bbfilter(struct max2175 *ctx)
+{
+	if (MAX2175_IS_BAND_AM(ctx)) {
+		max2175_write_bits(ctx, 12, 3, 0, ctx->rom_bbf_bw_am);
+		mxm_dbg(ctx, "set_bbfilter AM: rom %d\n", ctx->rom_bbf_bw_am);
+	} else if (MAX2175_IS_DAB_MODE(ctx)) {
+		max2175_write_bits(ctx, 12, 3, 0, ctx->rom_bbf_bw_dab);
+		mxm_dbg(ctx, "set_bbfilter DAB: rom %d\n", ctx->rom_bbf_bw_dab);
+	} else {
+		max2175_write_bits(ctx, 12, 3, 0, ctx->rom_bbf_bw_fm);
+		mxm_dbg(ctx, "set_bbfilter FM: rom %d\n", ctx->rom_bbf_bw_fm);
+	}
+}
+
+static bool max2175_set_csm_mode(struct max2175 *ctx,
+			  enum max2175_csm_mode new_mode)
+{
+	int ret = max2175_poll_csm_ready(ctx);
+
+	if (ret)
+		return ret;
+
+	max2175_write_bits(ctx, 0, 2, 0, new_mode);
+	mxm_dbg(ctx, "set csm new mode %d\n", new_mode);
+
+	/* Wait for a fixed settle down time depending on new mode */
+	switch (new_mode) {
+	case MAX2175_PRESET_TUNE:
+		usleep_range(51100, 51500);	/* 51.1ms */
+		break;
+	/*
+	 * Other mode switches need different sleep values depending on band &
+	 * mode
+	 */
+	default:
+		break;
+	}
+
+	return max2175_poll_csm_ready(ctx);
+}
+
+static int max2175_csm_action(struct max2175 *ctx,
+			      enum max2175_csm_mode action)
+{
+	int ret;
+
+	mxm_dbg(ctx, "csm_action: %d\n", action);
+
+	/* Other actions can be added in future when needed */
+	ret = max2175_set_csm_mode(ctx, MAX2175_LOAD_TO_BUFFER);
+	if (ret)
+		return ret;
+	return max2175_set_csm_mode(ctx, MAX2175_PRESET_TUNE);
+}
+
+static int max2175_set_lo_freq(struct max2175 *ctx, u64 lo_freq)
+{
+	u64 scaled_lo_freq, scaled_npf, scaled_integer, scaled_fraction;
+	u32 frac_desired, int_desired, lo_mult = 1;
+	const u32 scale_factor = 1000000U;
+	u8 loband_bits = 0, vcodiv_bits = 0;
+	enum max2175_band band;
+	int ret;
+
+	/* Scale to larger number for precision */
+	scaled_lo_freq = lo_freq * scale_factor * 100;
+	band = max2175_read_bits(ctx, 5, 1, 0);
+
+	mxm_dbg(ctx, "set_lo_freq: scaled lo_freq %llu lo_freq %llu band %d\n",
+		scaled_lo_freq, lo_freq, band);
+
+	switch (band) {
+	case MAX2175_BAND_AM:
+		if (max2175_read_bit(ctx, 5, 7) == 0)
+			lo_mult = 16;
+		break;
+	case MAX2175_BAND_FM:
+		if (lo_freq <= 74700000) {
+			lo_mult = 16;
+		} else if (lo_freq > 74700000 && lo_freq <= 110000000) {
+			loband_bits = 1;
+		} else {
+			loband_bits = 1;
+			vcodiv_bits = 3;
+		}
+		lo_mult = 8;
+		break;
+	case MAX2175_BAND_VHF:
+		if (lo_freq <= 210000000) {
+			loband_bits = 2;
+			vcodiv_bits = 2;
+		} else {
+			loband_bits = 2;
+			vcodiv_bits = 1;
+		}
+		lo_mult = 4;
+		break;
+	default:
+		loband_bits = 3;
+		vcodiv_bits = 2;
+		lo_mult = 2;
+		break;
+	}
+
+	if (band == MAX2175_BAND_L)
+		scaled_npf = div_u64(div_u64(scaled_lo_freq, ctx->xtal_freq),
+				     lo_mult);
+	else
+		scaled_npf = div_u64(scaled_lo_freq, ctx->xtal_freq) * lo_mult;
+
+	scaled_npf = div_u64(scaled_npf, 100);
+	scaled_integer = div_u64(scaled_npf, scale_factor) * scale_factor;
+	int_desired = div_u64(scaled_npf, scale_factor);
+	scaled_fraction = scaled_npf - scaled_integer;
+	frac_desired = div_u64(scaled_fraction << 20, scale_factor);
+
+	/* Check CSM is not busy */
+	ret = max2175_poll_csm_ready(ctx);
+	if (ret)
+		return ret;
+
+	mxm_dbg(ctx, "loband %u vcodiv %u lo_mult %u scaled_npf %llu\n",
+		loband_bits, vcodiv_bits, lo_mult, scaled_npf);
+	mxm_dbg(ctx, "scaled int %llu frac %llu desired int %u frac %u\n",
+		scaled_integer, scaled_fraction, int_desired, frac_desired);
+
+	/* Write the calculated values to the appropriate registers */
+	max2175_write(ctx, 1, int_desired);
+	max2175_write_bits(ctx, 2, 3, 0, (frac_desired >> 16) & 0xf);
+	max2175_write(ctx, 3, frac_desired >> 8);
+	max2175_write(ctx, 4, frac_desired);
+	max2175_write_bits(ctx, 5, 3, 2, loband_bits);
+	max2175_write_bits(ctx, 6, 7, 6, vcodiv_bits);
+	return ret;
+}
+
+static int max2175_set_nco_freq(struct max2175 *ctx, s64 nco_freq_desired)
+{
+	s64  nco_freq, nco_val_desired;
+	u64 abs_nco_freq;
+	const u32 scale_factor = 1000000U;
+	u32 clock_rate, nco_reg;
+	int ret;
+
+	mxm_dbg(ctx, "set_nco_freq: freq %lld\n", nco_freq_desired);
+	clock_rate = ctx->xtal_freq / ctx->decim_ratio;
+	nco_freq = -nco_freq_desired;
+
+	if (nco_freq < 0)
+		abs_nco_freq = -nco_freq;
+	else
+		abs_nco_freq = nco_freq;
+
+	/* Scale up the values for precision */
+	if (abs_nco_freq < (clock_rate / 2)) {
+		nco_val_desired = div_s64(2 * nco_freq * scale_factor,
+					  clock_rate);
+	} else {
+		if (nco_freq < 0)
+			nco_val_desired =
+			div_s64(-2 * (clock_rate - abs_nco_freq) * scale_factor,
+				clock_rate);
+		else
+			nco_val_desired =
+			div_s64(2 * (clock_rate - abs_nco_freq) * scale_factor,
+				clock_rate);
+	}
+
+	/* Scale down to get the fraction */
+	if (nco_freq < 0)
+		nco_reg = 0x200000 + div_s64(nco_val_desired << 20,
+						  scale_factor);
+	else
+		nco_reg = div_s64(nco_val_desired << 20, scale_factor);
+
+	/* Check CSM is not busy */
+	ret = max2175_poll_csm_ready(ctx);
+	if (ret)
+		return ret;
+
+	mxm_dbg(ctx, "clk %u decim %u abs %llu desired %lld reg %u\n",
+		clock_rate, ctx->decim_ratio, abs_nco_freq,
+		nco_val_desired, nco_reg);
+
+	/* Write the calculated values to the appropriate registers */
+	max2175_write_bits(ctx, 7, 4, 0, (nco_reg >> 16) & 0x1f);
+	max2175_write(ctx, 8, nco_reg >> 8);
+	max2175_write(ctx, 9, nco_reg);
+	return ret;
+}
+
+static int max2175_set_rf_freq_non_am_bands(struct max2175 *ctx, u64 freq,
+					    u32 lo_pos)
+{
+	s64 adj_freq;
+	u64 low_if_freq;
+	int ret;
+
+	mxm_dbg(ctx, "rf_freq: non AM bands\n");
+
+	if (MAX2175_IS_FM_MODE(ctx))
+		low_if_freq = 128000;
+	else if (MAX2175_IS_FMHD_MODE(ctx))
+		low_if_freq = 228000;
+	else
+		return max2175_set_lo_freq(ctx, freq);
+
+	if (MAX2175_IS_BAND_VHF(ctx) == (lo_pos == MAX2175_LO_ABOVE_DESIRED))
+		adj_freq = freq + low_if_freq;
+	else
+		adj_freq = freq - low_if_freq;
+
+	ret = max2175_set_lo_freq(ctx, adj_freq);
+	if (ret)
+		return ret;
+
+	return max2175_set_nco_freq(ctx, low_if_freq);
+}
+
+static int max2175_set_rf_freq(struct max2175 *ctx, u64 freq, u32 lo_pos)
+{
+	int ret;
+
+	if (MAX2175_IS_BAND_AM(ctx))
+		ret = max2175_set_nco_freq(ctx, freq);
+	else
+		ret = max2175_set_rf_freq_non_am_bands(ctx, freq, lo_pos);
+
+	mxm_dbg(ctx, "set_rf_freq: ret %d freq %llu\n", ret, freq);
+	return ret;
+}
+
+static int max2175_tune_rf_freq(struct max2175 *ctx, u64 freq, u32 hsls)
+{
+	int ret;
+
+	ret = max2175_set_rf_freq(ctx, freq, hsls);
+	if (ret)
+		return ret;
+
+	ret = max2175_csm_action(ctx, MAX2175_BUFFER_PLUS_PRESET_TUNE);
+	if (ret)
+		return ret;
+
+	mxm_dbg(ctx, "tune_rf_freq: old %u new %llu\n", ctx->freq, freq);
+	ctx->freq = freq;
+	return ret;
+}
+
+static void max2175_set_hsls(struct max2175 *ctx, u32 lo_pos)
+{
+	mxm_dbg(ctx, "set_hsls: lo_pos %u\n", lo_pos);
+
+	if ((lo_pos == MAX2175_LO_BELOW_DESIRED) == MAX2175_IS_BAND_VHF(ctx))
+		max2175_write_bit(ctx, 5, 4, 1);
+	else
+		max2175_write_bit(ctx, 5, 4, 0);
+}
+
+static void max2175_set_eu_rx_mode(struct max2175 *ctx, u32 rx_mode)
+{
+	switch (rx_mode) {
+	case MAX2175_EU_FM_1_2:
+		max2175_load_fmeu_1p2(ctx);
+		break;
+
+	case MAX2175_DAB_1_2:
+		max2175_load_dab_1p2(ctx);
+		break;
+	}
+	/* Master is the default setting */
+	if (!ctx->master)
+		max2175_write_bit(ctx, 30, 7, 1);
+
+	/* Cache i2s_test value at this point */
+	ctx->i2s_test = max2175_read_bits(ctx, 104, 3, 0);
+}
+
+static void max2175_set_na_rx_mode(struct max2175 *ctx, u32 rx_mode)
+{
+	switch (rx_mode) {
+	case MAX2175_NA_FM_1_0:
+		max2175_load_fmna_1p0(ctx);
+		break;
+	case MAX2175_NA_FM_2_0:
+		max2175_load_fmna_2p0(ctx);
+		break;
+	}
+	/* Master is the default setting */
+	if (!ctx->master)
+		max2175_write_bit(ctx, 30, 7, 1);
+
+	/* Cache i2s_test value at this point */
+	ctx->i2s_test = max2175_read_bits(ctx, 104, 3, 0);
+	ctx->decim_ratio = 27;
+
+	/* Load the Channel Filter Coefficients into channel filter bank #2 */
+	max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 0, ch_coeff_fmna);
+	max2175_set_filter_coeffs(ctx, MAX2175_EQ_MSEL, 0,
+				  eq_coeff_fmna1_ra02_m6db);
+}
+
+static int max2175_set_rx_mode(struct max2175 *ctx, u32 rx_mode, u32 hsls)
+{
+	mxm_dbg(ctx, "set_rx_mode: %u am_hiz %u\n", rx_mode, ctx->am_hiz);
+	if (ctx->xtal_freq == MAX2175_EU_XTAL_FREQ)
+		max2175_set_eu_rx_mode(ctx, rx_mode);
+	else
+		max2175_set_na_rx_mode(ctx, rx_mode);
+
+	if (ctx->am_hiz) {
+		mxm_dbg(ctx, "setting AM HiZ related config\n");
+		max2175_write_bit(ctx, 50, 5, 1);
+		max2175_write_bit(ctx, 90, 7, 1);
+		max2175_write_bits(ctx, 73, 1, 0, 2);
+		max2175_write_bits(ctx, 80, 5, 0, 33);
+	}
+
+	/* Load BB filter trim values saved in ROM */
+	max2175_set_bbfilter(ctx);
+
+	/* Set HSLS */
+	max2175_set_hsls(ctx, hsls);
+
+	ctx->mode_resolved = true;
+	return 0;
+}
+
+static bool max2175_i2s_rx_mode_valid(struct max2175 *ctx,
+					 u32 mode, u32 i2s_mode)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(ctx->rx_modes[mode].i2s_modes); i++)
+		if (ctx->rx_modes[mode].i2s_modes[i] == i2s_mode)
+			return true;
+
+	v4l2_err(ctx->client, "i2s_mode %u not suitable for cur rx mode %u\n",
+		 i2s_mode, mode);
+	return false;
+}
+
+static int max2175_rx_mode_from_freq(struct max2175 *ctx, u32 freq, u32 *mode)
+{
+	unsigned int i;
+	int band = max2175_band_from_freq(freq);
+
+	/* Pick the first match always */
+	for (i = 0; i <= ctx->rx_mode->maximum; i++) {
+		if (ctx->rx_modes[i].band == band) {
+			*mode = i;
+			mxm_dbg(ctx, "rx_mode_from_freq: freq %u mode %d\n",
+				freq, *mode);
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+static bool max2175_freq_rx_mode_valid(struct max2175 *ctx,
+					 u32 mode, u32 freq)
+{
+	int band = max2175_band_from_freq(freq);
+
+	return (ctx->rx_modes[mode].band == band);
+}
+
+static void max2175_load_adc_presets(struct max2175 *ctx)
+{
+	unsigned int i, j;
+
+	for (i = 0; i < 2; i++)
+		for (j = 0; j < 23; j++)
+			max2175_write(ctx, 146 + j + i * 55, adc_presets[i][j]);
+}
+
+static int max2175_init_power_manager(struct max2175 *ctx)
+{
+	int ret;
+
+	/* Execute on-chip power-up/calibration */
+	max2175_write_bit(ctx, 99, 2, 0);
+	usleep_range(1000, 1500);
+	max2175_write_bit(ctx, 99, 2, 1);
+
+	/* Wait for the power manager to finish. */
+	ret = max2175_poll_timeout(ctx, 69, 7, 7, 1, 50);
+	if (ret)
+		v4l2_err(ctx->client, "init pm failed\n");
+	return ret;
+}
+
+static int max2175_recalibrate_adc(struct max2175 *ctx)
+{
+	int ret;
+
+	/* ADC Re-calibration */
+	max2175_write(ctx, 150, 0xff);
+	max2175_write(ctx, 205, 0xff);
+	max2175_write(ctx, 147, 0x20);
+	max2175_write(ctx, 147, 0x00);
+	max2175_write(ctx, 202, 0x20);
+	max2175_write(ctx, 202, 0x00);
+
+	ret = max2175_poll_timeout(ctx, 69, 4, 3, 3, 50);
+	if (ret)
+		v4l2_err(ctx->client, "adc recalibration failed\n");
+	return ret;
+}
+
+static u8 max2175_read_rom(struct max2175 *ctx, u8 row)
+{
+	u8 data;
+
+	max2175_write_bit(ctx, 56, 4, 0);
+	max2175_write_bits(ctx, 56, 3, 0, row);
+
+	usleep_range(2000, 2500);
+	max2175_read(ctx, 58, &data);
+
+	max2175_write_bits(ctx, 56, 3, 0, 0);
+
+	mxm_dbg(ctx, "read_rom: row %d data 0x%02x\n", row, data);
+	return data;
+}
+
+static void max2175_load_from_rom(struct max2175 *ctx)
+{
+	u8 data = 0;
+
+	data = max2175_read_rom(ctx, 0);
+	ctx->rom_bbf_bw_am = data & 0x0f;
+	max2175_write_bits(ctx, 81, 3, 0, data >> 4);
+
+	data = max2175_read_rom(ctx, 1);
+	ctx->rom_bbf_bw_fm = data & 0x0f;
+	ctx->rom_bbf_bw_dab = data >> 4;
+
+	data = max2175_read_rom(ctx, 2);
+	max2175_write_bits(ctx, 82, 4, 0, data & 0x1f);
+	max2175_write_bits(ctx, 82, 7, 5, data >> 5);
+
+	data = max2175_read_rom(ctx, 3);
+	if (ctx->am_hiz) {
+		data &= 0x0f;
+		data |= max2175_read_rom(ctx, 7) & 0x40 >> 2;
+		if (!data)
+			data |= 2;
+	} else {
+		data = data & 0xf0 >> 4;
+		data |= max2175_read_rom(ctx, 7) & 0x80 >> 3;
+		if (!data)
+			data |= 30;
+	}
+	max2175_write_bits(ctx, 80, 5, 0, data + 31);
+
+	data = max2175_read_rom(ctx, 6);
+	max2175_write_bits(ctx, 81, 7, 6, data >> 6);
+}
+
+static void max2175_load_full_fm_eu_1p0(struct max2175 *ctx)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(full_fm_eu_1p0); i++)
+		max2175_write(ctx, i + 1, full_fm_eu_1p0[i]);
+
+	usleep_range(5000, 5500);
+	ctx->decim_ratio = 36;
+}
+
+static void max2175_load_full_fm_na_1p0(struct max2175 *ctx)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(full_fm_na_1p0); i++)
+		max2175_write(ctx, i + 1, full_fm_na_1p0[i]);
+
+	usleep_range(5000, 5500);
+	ctx->decim_ratio = 27;
+}
+
+static int max2175_core_init(struct max2175 *ctx, u32 refout_bits)
+{
+	int ret;
+
+	/* MAX2175 uses 36.864MHz clock for EU & 40.154MHz for NA region */
+	if (ctx->xtal_freq == MAX2175_EU_XTAL_FREQ)
+		max2175_load_full_fm_eu_1p0(ctx);
+	else
+		max2175_load_full_fm_na_1p0(ctx);
+
+	/* The default settings assume master */
+	if (!ctx->master)
+		max2175_write_bit(ctx, 30, 7, 1);
+
+	mxm_dbg(ctx, "refout_bits %u\n", refout_bits);
+
+	/* Set REFOUT */
+	max2175_write_bits(ctx, 56, 7, 5, refout_bits);
+
+	/* ADC Reset */
+	max2175_write_bit(ctx, 99, 1, 0);
+	usleep_range(1000, 1500);
+	max2175_write_bit(ctx, 99, 1, 1);
+
+	/* Load ADC preset values */
+	max2175_load_adc_presets(ctx);
+
+	/* Initialize the power management state machine */
+	ret = max2175_init_power_manager(ctx);
+	if (ret)
+		return ret;
+
+	/* Recalibrate ADC */
+	ret = max2175_recalibrate_adc(ctx);
+	if (ret)
+		return ret;
+
+	/* Load ROM values to appropriate registers */
+	max2175_load_from_rom(ctx);
+
+	if (ctx->xtal_freq == MAX2175_EU_XTAL_FREQ) {
+		/* Load FIR coefficients into bank 0 */
+		max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 0,
+					  ch_coeff_fmeu);
+		max2175_set_filter_coeffs(ctx, MAX2175_EQ_MSEL, 0,
+					  eq_coeff_fmeu1_ra02_m6db);
+	} else {
+		/* Load FIR coefficients into bank 0 */
+		max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 0,
+					  ch_coeff_fmna);
+		max2175_set_filter_coeffs(ctx, MAX2175_EQ_MSEL, 0,
+					  eq_coeff_fmna1_ra02_m6db);
+	}
+	mxm_dbg(ctx, "core initialized\n");
+	return 0;
+}
+
+static void max2175_s_ctrl_i2s_mode(struct max2175 *ctx, u32 i2s_mode)
+{
+	mxm_dbg(ctx, "s_ctrl_i2s_mode: %u resolved %d\n", i2s_mode,
+		ctx->mode_resolved);
+
+	/*
+	 * Update i2s mode on device only when mode is resolved & it is valid
+	 * for the configured mode
+	 */
+	if (ctx->mode_resolved &&
+	    max2175_i2s_rx_mode_valid(ctx, ctx->rx_mode->val, i2s_mode))
+		max2175_update_i2s_mode(ctx, ctx->rx_mode->val, i2s_mode);
+}
+
+static void max2175_s_ctrl_rx_mode(struct max2175 *ctx, u32 rx_mode)
+{
+	/* Load mode. Range check already done */
+	max2175_set_rx_mode(ctx, rx_mode, ctx->hsls->val);
+
+	/* Get current i2s_mode and update if needed for given rx_mode */
+	if (max2175_i2s_rx_mode_valid(ctx, rx_mode, ctx->i2s_mode->val))
+		max2175_update_i2s_mode(ctx, rx_mode, ctx->i2s_mode->val);
+	else
+		ctx->i2s_mode->val = max2175_read_bits(ctx, 29, 2, 0);
+
+	mxm_dbg(ctx, "s_ctrl_rx_mode: %u curr freq %u\n", rx_mode, ctx->freq);
+
+	/* Check if current freq valid for mode & update */
+	if (max2175_freq_rx_mode_valid(ctx, rx_mode, ctx->freq))
+		max2175_tune_rf_freq(ctx, ctx->freq, ctx->hsls->val);
+	else
+		/* Use default freq of mode if current freq is not valid */
+		max2175_tune_rf_freq(ctx, ctx->rx_modes[rx_mode].freq,
+				     ctx->hsls->val);
+}
+
+static int max2175_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct max2175 *ctx = max2175_from_ctrl_hdl(ctrl->handler);
+	int ret = 0;
+
+	mxm_dbg(ctx, "s_ctrl: id 0x%x, val %u\n", ctrl->id, ctrl->val);
+	switch (ctrl->id) {
+	case V4L2_CID_MAX2175_I2S_ENABLE:
+		max2175_i2s_enable(ctx, ctrl->val == 1);
+		break;
+	case V4L2_CID_MAX2175_I2S_MODE:
+		max2175_s_ctrl_i2s_mode(ctx, ctrl->val);
+		break;
+	case V4L2_CID_MAX2175_HSLS:
+		max2175_set_hsls(ctx, ctx->hsls->val);
+		break;
+	case V4L2_CID_MAX2175_RX_MODE:
+		max2175_s_ctrl_rx_mode(ctx, ctrl->val);
+		break;
+	}
+
+	return ret;
+}
+
+static int max2175_get_lna_gain(struct max2175 *ctx)
+{
+	int gain = 0;
+	enum max2175_band band = max2175_read_bits(ctx, 5, 1, 0);
+
+	switch (band) {
+	case MAX2175_BAND_AM:
+		gain = max2175_read_bits(ctx, 51, 3, 1);
+		break;
+	case MAX2175_BAND_FM:
+		gain = max2175_read_bits(ctx, 50, 3, 1);
+		break;
+	case MAX2175_BAND_VHF:
+		gain = max2175_read_bits(ctx, 52, 3, 0);
+		break;
+	default:
+		break;
+	}
+	return gain;
+}
+
+static int max2175_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct max2175 *ctx = max2175_from_ctrl_hdl(ctrl->handler);
+
+	switch (ctrl->id) {
+	case V4L2_CID_RF_TUNER_LNA_GAIN:
+		ctrl->val = max2175_get_lna_gain(ctx);
+		break;
+	case V4L2_CID_RF_TUNER_IF_GAIN:
+		ctrl->val = max2175_read_bits(ctx, 49, 4, 0);
+		break;
+	case V4L2_CID_RF_TUNER_PLL_LOCK:
+		ctrl->val = (max2175_read_bits(ctx, 60, 7, 6) == 3);
+		break;
+	}
+	mxm_dbg(ctx, "g_volatile_ctrl: id 0x%x val %d\n", ctrl->id, ctrl->val);
+	return 0;
+};
+
+static int max2175_set_freq_and_mode(struct max2175 *ctx, u32 freq)
+{
+	u32 rx_mode;
+	int ret;
+
+	/* Get band from frequency */
+	ret = max2175_rx_mode_from_freq(ctx, freq, &rx_mode);
+	if (ret)
+		return ret;
+
+	mxm_dbg(ctx, "set_freq_and_mode: freq %u rx_mode %d\n", freq, rx_mode);
+
+	/* Load mode */
+	max2175_set_rx_mode(ctx, rx_mode, ctx->hsls->val);
+	ctx->rx_mode->val = rx_mode;
+
+	/* Get current i2s_mode and update if needed for given rx_mode */
+	if (max2175_i2s_rx_mode_valid(ctx, rx_mode, ctx->i2s_mode->val))
+		max2175_update_i2s_mode(ctx, rx_mode, ctx->i2s_mode->val);
+	else
+		ctx->i2s_mode->val = max2175_read_bits(ctx, 29, 2, 0);
+
+	/* Tune to the new freq given */
+	return max2175_tune_rf_freq(ctx, freq, ctx->hsls->val);
+}
+
+static int max2175_s_frequency(struct v4l2_subdev *sd,
+			       const struct v4l2_frequency *vf)
+{
+	struct max2175 *ctx = max2175_from_sd(sd);
+	u32 freq;
+	int ret = 0;
+
+	mxm_dbg(ctx, "s_freq: new %u curr %u, mode_resolved %d\n",
+		vf->frequency, ctx->freq, ctx->mode_resolved);
+
+	if (vf->tuner != 0)
+		return -EINVAL;
+
+	freq = clamp(vf->frequency, ctx->bands_rf->rangelow,
+		     ctx->bands_rf->rangehigh);
+
+	/* Check new freq valid for rx_mode if already resolved */
+	if (ctx->mode_resolved &&
+	    max2175_freq_rx_mode_valid(ctx, ctx->rx_mode->val, freq))
+		ret = max2175_tune_rf_freq(ctx, freq, ctx->hsls->val);
+	else
+		/* Find default rx_mode for freq and tune to it */
+		ret = max2175_set_freq_and_mode(ctx, freq);
+
+	mxm_dbg(ctx, "s_freq: ret %d curr %u mode_resolved %d mode %u\n",
+		ret, ctx->freq, ctx->mode_resolved, ctx->rx_mode->val);
+	return ret;
+}
+
+static int max2175_g_frequency(struct v4l2_subdev *sd,
+			       struct v4l2_frequency *vf)
+{
+	struct max2175 *ctx = max2175_from_sd(sd);
+	int ret = 0;
+
+	if (vf->tuner != 0)
+		return -EINVAL;
+
+	/* RF freq */
+	vf->type = V4L2_TUNER_RF;
+	vf->frequency = ctx->freq;
+	return ret;
+}
+
+static int max2175_enum_freq_bands(struct v4l2_subdev *sd,
+			    struct v4l2_frequency_band *band)
+{
+	struct max2175 *ctx = max2175_from_sd(sd);
+
+	if (band->tuner == 0 && band->index == 0)
+		*band = *ctx->bands_rf;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int max2175_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+	struct max2175 *ctx = max2175_from_sd(sd);
+
+	if (vt->index > 0)
+		return -EINVAL;
+
+	strlcpy(vt->name, "RF", sizeof(vt->name));
+	vt->type = V4L2_TUNER_RF;
+	vt->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
+	vt->rangelow = ctx->bands_rf->rangelow;
+	vt->rangehigh = ctx->bands_rf->rangehigh;
+	return 0;
+}
+
+static int max2175_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt)
+{
+	/* Check tuner index is valid */
+	if (vt->index > 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+static const struct v4l2_subdev_tuner_ops max2175_tuner_ops = {
+	.s_frequency = max2175_s_frequency,
+	.g_frequency = max2175_g_frequency,
+	.enum_freq_bands = max2175_enum_freq_bands,
+	.g_tuner = max2175_g_tuner,
+	.s_tuner = max2175_s_tuner,
+};
+
+static const struct v4l2_subdev_ops max2175_ops = {
+	.tuner = &max2175_tuner_ops,
+};
+
+static const struct v4l2_ctrl_ops max2175_ctrl_ops = {
+	.s_ctrl = max2175_s_ctrl,
+	.g_volatile_ctrl = max2175_g_volatile_ctrl,
+};
+
+static const struct v4l2_ctrl_config max2175_i2s_en = {
+	.ops = &max2175_ctrl_ops,
+	.id = V4L2_CID_MAX2175_I2S_ENABLE,
+	.name = "I2S Enable",
+	.type = V4L2_CTRL_TYPE_BOOLEAN,
+	.min = 0,
+	.max = 1,
+	.step = 1,
+	.def = 1,
+};
+
+static const char * const max2175_ctrl_i2s_modes[] = {
+	[MAX2175_I2S_MODE0]	= "i2s mode 0",
+	[MAX2175_I2S_MODE1]	= "i2s mode 1 (skipped)",
+	[MAX2175_I2S_MODE2]	= "i2s mode 2",
+	[MAX2175_I2S_MODE3]	= "i2s mode 3",
+	[MAX2175_I2S_MODE4]	= "i2s mode 4",
+};
+
+static const struct v4l2_ctrl_config max2175_i2s_mode = {
+	.ops = &max2175_ctrl_ops,
+	.id = V4L2_CID_MAX2175_I2S_MODE,
+	.name = "I2S MODE value",
+	.type = V4L2_CTRL_TYPE_MENU,
+	.max = ARRAY_SIZE(max2175_ctrl_i2s_modes) - 1,
+	.def = 0,
+	.menu_skip_mask = 0x02,
+	.qmenu = max2175_ctrl_i2s_modes,
+};
+
+static const struct v4l2_ctrl_config max2175_hsls = {
+	.ops = &max2175_ctrl_ops,
+	.id = V4L2_CID_MAX2175_HSLS,
+	.name = "HSLS above/below desired",
+	.type = V4L2_CTRL_TYPE_INTEGER,
+	.min = 0,
+	.max = 1,
+	.step = 1,
+	.def = 1,
+};
+
+static const char * const max2175_ctrl_eu_rx_modes[] = {
+	[MAX2175_EU_FM_1_2]	= "EU FM 1.2",
+	[MAX2175_DAB_1_2]	= "DAB 1.2",
+};
+
+static const char * const max2175_ctrl_na_rx_modes[] = {
+	[MAX2175_NA_FM_1_0]	= "NA FM 1.0",
+	[MAX2175_NA_FM_2_0]	= "NA FM 2.0",
+};
+
+static const struct v4l2_ctrl_config max2175_eu_rx_mode = {
+	.ops = &max2175_ctrl_ops,
+	.id = V4L2_CID_MAX2175_RX_MODE,
+	.name = "RX MODE",
+	.type = V4L2_CTRL_TYPE_MENU,
+	.max = ARRAY_SIZE(max2175_ctrl_eu_rx_modes) - 1,
+	.def = 0,
+	.qmenu = max2175_ctrl_eu_rx_modes,
+};
+
+static const struct v4l2_ctrl_config max2175_na_rx_mode = {
+	.ops = &max2175_ctrl_ops,
+	.id = V4L2_CID_MAX2175_RX_MODE,
+	.name = "RX MODE",
+	.type = V4L2_CTRL_TYPE_MENU,
+	.max = ARRAY_SIZE(max2175_ctrl_na_rx_modes) - 1,
+	.def = 0,
+	.qmenu = max2175_ctrl_na_rx_modes,
+};
+
+static int max2175_refout_load_to_bits(struct i2c_client *client, u32 load,
+				       u32 *bits)
+{
+	if (load >= 0 && load <= 40)
+		*bits = load / 10;
+	else if (load >= 60 && load <= 70)
+		*bits = load / 10 - 1;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int max2175_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct max2175 *ctx;
+	struct v4l2_subdev *sd;
+	struct v4l2_ctrl_handler *hdl;
+	struct clk *clk;
+	struct regmap *regmap;
+	bool master = true, am_hiz = false;
+	u32 refout_load, refout_bits = 0;	/* REFOUT disabled */
+	int ret;
+
+	/* Parse DT properties */
+	if (of_find_property(client->dev.of_node, "maxim,slave", NULL))
+		master = false;
+
+	if (of_find_property(client->dev.of_node, "maxim,am-hiz", NULL))
+		am_hiz = true;
+
+	if (!of_property_read_u32(client->dev.of_node, "maxim,refout-load-pF",
+				  &refout_load)) {
+		ret = max2175_refout_load_to_bits(client, refout_load,
+						  &refout_bits);
+		if (ret) {
+			dev_err(&client->dev, "invalid refout_load %u\n",
+				refout_load);
+			return -EINVAL;
+		}
+	}
+
+	clk = devm_clk_get(&client->dev, "xtal");
+	if (IS_ERR(clk)) {
+		ret = PTR_ERR(clk);
+		dev_err(&client->dev, "cannot get xtal clock %d\n", ret);
+		return -ENODEV;
+	}
+
+	regmap = devm_regmap_init_i2c(client, &max2175_regmap_config);
+	if (IS_ERR(regmap)) {
+		ret = PTR_ERR(regmap);
+		dev_err(&client->dev, "regmap init failed %d\n", ret);
+		return -ENODEV;
+	}
+
+	/* Alloc tuner context */
+	ctx = devm_kzalloc(&client->dev, sizeof(*ctx), GFP_KERNEL);
+	if (ctx == NULL)
+		return -ENOMEM;
+
+	sd = &ctx->sd;
+	ctx->master = master;
+	ctx->am_hiz = am_hiz;
+	ctx->mode_resolved = false;
+	ctx->regmap = regmap;
+	ctx->xtal_freq = clk_get_rate(clk);
+	dev_info(&client->dev, "xtal freq %luHz\n", ctx->xtal_freq);
+
+	v4l2_i2c_subdev_init(sd, client, &max2175_ops);
+	ctx->client = client;
+
+	sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	/* Controls */
+	hdl = &ctx->ctrl_hdl;
+	ret = v4l2_ctrl_handler_init(hdl, 7);
+	if (ret) {
+		dev_err(&client->dev, "ctrl handler init failed\n");
+		goto err;
+	}
+
+	ctx->lna_gain = v4l2_ctrl_new_std(hdl, &max2175_ctrl_ops,
+					  V4L2_CID_RF_TUNER_LNA_GAIN,
+					  0, 15, 1, 2);
+	ctx->lna_gain->flags |= (V4L2_CTRL_FLAG_VOLATILE |
+				 V4L2_CTRL_FLAG_READ_ONLY);
+	ctx->if_gain = v4l2_ctrl_new_std(hdl, &max2175_ctrl_ops,
+					 V4L2_CID_RF_TUNER_IF_GAIN,
+					 0, 31, 1, 0);
+	ctx->if_gain->flags |= (V4L2_CTRL_FLAG_VOLATILE |
+				V4L2_CTRL_FLAG_READ_ONLY);
+	ctx->pll_lock = v4l2_ctrl_new_std(hdl, &max2175_ctrl_ops,
+					  V4L2_CID_RF_TUNER_PLL_LOCK,
+					  0, 1, 1, 0);
+	ctx->pll_lock->flags |= (V4L2_CTRL_FLAG_VOLATILE |
+				 V4L2_CTRL_FLAG_READ_ONLY);
+	ctx->i2s_en = v4l2_ctrl_new_custom(hdl, &max2175_i2s_en, NULL);
+	ctx->i2s_mode = v4l2_ctrl_new_custom(hdl, &max2175_i2s_mode, NULL);
+	ctx->hsls = v4l2_ctrl_new_custom(hdl, &max2175_hsls, NULL);
+
+	if (ctx->xtal_freq == MAX2175_EU_XTAL_FREQ) {
+		ctx->rx_mode = v4l2_ctrl_new_custom(hdl,
+						    &max2175_eu_rx_mode, NULL);
+		ctx->rx_modes = eu_rx_modes;
+		ctx->bands_rf = &eu_bands_rf;
+	} else {
+		ctx->rx_mode = v4l2_ctrl_new_custom(hdl,
+						    &max2175_na_rx_mode, NULL);
+		ctx->rx_modes = na_rx_modes;
+		ctx->bands_rf = &na_bands_rf;
+	}
+	ctx->sd.ctrl_handler = &ctx->ctrl_hdl;
+
+	/* Set the defaults */
+	ctx->freq = ctx->bands_rf->rangelow;
+
+	/* Register subdev */
+	ret = v4l2_async_register_subdev(sd);
+	if (ret) {
+		dev_err(&client->dev, "register subdev failed\n");
+		goto err_reg;
+	}
+
+	/* Initialize device */
+	ret = max2175_core_init(ctx, refout_bits);
+	if (ret)
+		goto err_init;
+
+	dev_info(&client->dev, "probed\n");
+	return 0;
+
+err_init:
+	v4l2_async_unregister_subdev(sd);
+err_reg:
+	v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
+err:
+	return ret;
+}
+
+static int max2175_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct max2175 *ctx = max2175_from_sd(sd);
+
+	v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
+	v4l2_async_unregister_subdev(sd);
+	dev_info(&client->dev, "removed\n");
+	return 0;
+}
+
+static const struct i2c_device_id max2175_id[] = {
+	{ DRIVER_NAME, 0},
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, max2175_id);
+
+static const struct of_device_id max2175_of_ids[] = {
+	{ .compatible = "maxim, max2175", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, max2175_of_ids);
+
+static struct i2c_driver max2175_driver = {
+	.driver = {
+		.name	= DRIVER_NAME,
+		.of_match_table = max2175_of_ids,
+	},
+	.probe		= max2175_probe,
+	.remove		= max2175_remove,
+	.id_table	= max2175_id,
+};
+
+module_i2c_driver(max2175_driver);
+
+MODULE_DESCRIPTION("Maxim MAX2175 RF to Bits tuner driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Ramesh Shanmugasundaram <ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>");
diff --git a/drivers/media/i2c/max2175/max2175.h b/drivers/media/i2c/max2175/max2175.h
new file mode 100644
index 0000000..2d858aa
--- /dev/null
+++ b/drivers/media/i2c/max2175/max2175.h
@@ -0,0 +1,108 @@
+/*
+ * Maxim Integrated MAX2175 RF to Bits tuner driver
+ *
+ * This driver & most of the hard coded values are based on the reference
+ * application delivered by Maxim for this chip.
+ *
+ * Copyright (C) 2016 Maxim Integrated Products
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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 __MAX2175_H__
+#define __MAX2175_H__
+
+#define MAX2175_EU_XTAL_FREQ	36864000	/* In Hz */
+#define MAX2175_NA_XTAL_FREQ	40186125	/* In Hz */
+
+enum max2175_region {
+	MAX2175_REGION_EU = 0,	/* Europe */
+	MAX2175_REGION_NA,	/* North America */
+};
+
+
+enum max2175_band {
+	MAX2175_BAND_AM = 0,
+	MAX2175_BAND_FM,
+	MAX2175_BAND_VHF,
+	MAX2175_BAND_L,
+};
+
+enum max2175_eu_mode {
+	/* EU modes */
+	MAX2175_EU_FM_1_2 = 0,
+	MAX2175_DAB_1_2,
+
+	/* Other possible modes to add in future
+	 * MAX2175_DAB_1_0,
+	 * MAX2175_DAB_1_3,
+	 * MAX2175_EU_FM_2_2,
+	 * MAX2175_EU_FMHD_4_0,
+	 * MAX2175_EU_AM_1_0,
+	 * MAX2175_EU_AM_2_2,
+	 */
+};
+
+enum max2175_na_mode {
+	/* NA modes */
+	MAX2175_NA_FM_1_0 = 0,
+	MAX2175_NA_FM_2_0,
+
+	/* Other possible modes to add in future
+	 * MAX2175_NA_FMHD_1_0,
+	 * MAX2175_NA_FMHD_1_2,
+	 * MAX2175_NA_AM_1_0,
+	 * MAX2175_NA_AM_1_2,
+	 */
+};
+
+/* Supported I2S modes */
+enum {
+	MAX2175_I2S_MODE0 = 0,
+	MAX2175_I2S_MODE1,
+	MAX2175_I2S_MODE2,
+	MAX2175_I2S_MODE3,
+	MAX2175_I2S_MODE4,
+};
+
+/* Coefficient table groups */
+enum {
+	MAX2175_CH_MSEL = 0,
+	MAX2175_EQ_MSEL,
+	MAX2175_AA_MSEL,
+};
+
+/* HSLS LO injection polarity */
+enum {
+	MAX2175_LO_BELOW_DESIRED = 0,
+	MAX2175_LO_ABOVE_DESIRED,
+};
+
+/* Channel FSM modes */
+enum max2175_csm_mode {
+	MAX2175_LOAD_TO_BUFFER = 0,
+	MAX2175_PRESET_TUNE,
+	MAX2175_SEARCH,
+	MAX2175_AF_UPDATE,
+	MAX2175_JUMP_FAST_TUNE,
+	MAX2175_CHECK,
+	MAX2175_LOAD_AND_SWAP,
+	MAX2175_END,
+	MAX2175_BUFFER_PLUS_PRESET_TUNE,
+	MAX2175_BUFFER_PLUS_SEARCH,
+	MAX2175_BUFFER_PLUS_AF_UPDATE,
+	MAX2175_BUFFER_PLUS_JUMP_FAST_TUNE,
+	MAX2175_BUFFER_PLUS_CHECK,
+	MAX2175_BUFFER_PLUS_LOAD_AND_SWAP,
+	MAX2175_NO_ACTION
+};
+
+#endif /* __MAX2175_H__ */
-- 
1.9.1

--
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 related	[flat|nested] 60+ messages in thread

* [PATCH 2/5] media: i2c: max2175: Add MAX2175 support
@ 2016-11-09 15:44     ` Ramesh Shanmugasundaram
  0 siblings, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-11-09 15:44 UTC (permalink / raw)
  To: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope
  Cc: chris.paterson2, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc, Ramesh Shanmugasundaram

This patch adds driver support for MAX2175 chip. This is Maxim
Integrated's RF to Bits tuner front end chip designed for software-defined
radio solutions. This driver exposes the tuner as a sub-device instance
with standard and custom controls to configure the device.

Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>
---
 .../devicetree/bindings/media/i2c/max2175.txt      |   61 +
 drivers/media/i2c/Kconfig                          |    4 +
 drivers/media/i2c/Makefile                         |    2 +
 drivers/media/i2c/max2175/Kconfig                  |    8 +
 drivers/media/i2c/max2175/Makefile                 |    4 +
 drivers/media/i2c/max2175/max2175.c                | 1558 ++++++++++++++++++++
 drivers/media/i2c/max2175/max2175.h                |  108 ++
 7 files changed, 1745 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/max2175.txt
 create mode 100644 drivers/media/i2c/max2175/Kconfig
 create mode 100644 drivers/media/i2c/max2175/Makefile
 create mode 100644 drivers/media/i2c/max2175/max2175.c
 create mode 100644 drivers/media/i2c/max2175/max2175.h

diff --git a/Documentation/devicetree/bindings/media/i2c/max2175.txt b/Documentation/devicetree/bindings/media/i2c/max2175.txt
new file mode 100644
index 0000000..69f0dad
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/max2175.txt
@@ -0,0 +1,61 @@
+Maxim Integrated MAX2175 RF to Bits tuner
+-----------------------------------------
+
+The MAX2175 IC is an advanced analog/digital hybrid-radio receiver with
+RF to Bits® front-end designed for software-defined radio solutions.
+
+Required properties:
+--------------------
+- compatible: "maxim,max2175" for MAX2175 RF-to-bits tuner.
+- clocks: phandle to the fixed xtal clock.
+- clock-names: name of the fixed xtal clock.
+- port: child port node of a tuner that defines the local and remote
+  endpoints. The remote endpoint is assumed to be an SDR device
+  that is capable of receiving the digital samples from the tuner.
+
+Optional properties:
+--------------------
+- maxim,slave	      : empty property indicates this is a slave of
+			another master tuner. This is used to define two
+			tuners in diversity mode (1 master, 1 slave). By
+			default each tuner is an individual master.
+- maxim,refout-load-pF: load capacitance value (in pF) on reference
+			output drive level. The possible load values are
+			 0pF (default - refout disabled)
+			10pF
+			20pF
+			30pF
+			40pF
+			60pF
+			70pF
+- maxim,am-hiz	      : empty property indicates AM Hi-Z filter path is
+			selected for AM antenna input. By default this
+			filter path is not used.
+
+Example:
+--------
+
+Board specific DTS file
+
+/* Fixed XTAL clock node */
+maxim_xtal: maximextal {
+	compatible = "fixed-clock";
+	#clock-cells = <0>;
+	clock-frequency = <36864000>;
+};
+
+/* A tuner device instance under i2c bus */
+max2175_0: tuner@60 {
+	compatible = "maxim,max2175";
+	reg = <0x60>;
+	clocks = <&maxim_xtal>;
+	clock-names = "xtal";
+	maxim,refout-load-pF = <10>;
+
+	port {
+		max2175_0_ep: endpoint {
+			remote-endpoint = <&slave_rx_v4l2_sdr_device>;
+		};
+	};
+
+};
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 2669b4b..66c73b0 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -749,6 +749,10 @@ config VIDEO_SAA6752HS
 	  To compile this driver as a module, choose M here: the
 	  module will be called saa6752hs.
 
+comment "SDR tuner chips"
+
+source "drivers/media/i2c/max2175/Kconfig"
+
 comment "Miscellaneous helper chips"
 
 config VIDEO_THS7303
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 92773b2..cfae721 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -6,6 +6,8 @@ obj-$(CONFIG_VIDEO_CX25840) += cx25840/
 obj-$(CONFIG_VIDEO_M5MOLS)	+= m5mols/
 obj-y				+= soc_camera/
 
+obj-$(CONFIG_SDR_MAX2175) 	+= max2175/
+
 obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o
 obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o
 obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o
diff --git a/drivers/media/i2c/max2175/Kconfig b/drivers/media/i2c/max2175/Kconfig
new file mode 100644
index 0000000..93a8f83
--- /dev/null
+++ b/drivers/media/i2c/max2175/Kconfig
@@ -0,0 +1,8 @@
+config SDR_MAX2175
+	tristate "Maxim 2175 RF to Bits tuner"
+	depends on VIDEO_V4L2 && MEDIA_SDR_SUPPORT && I2C
+	---help---
+	  Support for Maxim 2175 tuner
+
+	  To compile this driver as a module, choose M here; the
+	  module will be called max2175.
diff --git a/drivers/media/i2c/max2175/Makefile b/drivers/media/i2c/max2175/Makefile
new file mode 100644
index 0000000..9bb46ac
--- /dev/null
+++ b/drivers/media/i2c/max2175/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for Maxim RF to Bits tuner device
+#
+obj-$(CONFIG_SDR_MAX2175) += max2175.o
diff --git a/drivers/media/i2c/max2175/max2175.c b/drivers/media/i2c/max2175/max2175.c
new file mode 100644
index 0000000..ec45b52
--- /dev/null
+++ b/drivers/media/i2c/max2175/max2175.c
@@ -0,0 +1,1558 @@
+/*
+ * Maxim Integrated MAX2175 RF to Bits tuner driver
+ *
+ * This driver & most of the hard coded values are based on the reference
+ * application delivered by Maxim for this chip.
+ *
+ * Copyright (C) 2016 Maxim Integrated Products
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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/errno.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-of.h>
+
+#include "max2175.h"
+
+#define DRIVER_NAME "max2175"
+
+static unsigned int debug;
+module_param(debug, uint, 0644);
+MODULE_PARM_DESC(debug, "activate debug info");
+
+#define mxm_dbg(ctx, fmt, arg...) v4l2_dbg(1, debug, &ctx->sd, fmt, ## arg)
+
+/* Rx mode */
+struct max2175_rxmode {
+	enum max2175_band band;		/* Associated band */
+	u32 freq;			/* Default freq in Hz */
+	u8 i2s_word_size;		/* Bit value */
+	u8 i2s_modes[4];		/* Supported modes */
+};
+
+/* Register map to define preset values */
+struct max2175_reg_map {
+	u8 idx;				/* Register index */
+	u8 val;				/* Register value */
+};
+
+static const struct max2175_rxmode eu_rx_modes[] = {
+	/* EU modes */
+	[MAX2175_EU_FM_1_2] = { MAX2175_BAND_FM,
+				98256000, 1, { 0, 0, 0, 0 } },
+	[MAX2175_DAB_1_2]   = { MAX2175_BAND_VHF,
+				182640000, 0, { 0, 0, 0, 0 } },
+};
+
+static const struct max2175_rxmode na_rx_modes[] = {
+	/* NA modes */
+	[MAX2175_NA_FM_1_0] = { MAX2175_BAND_FM,
+				98255520, 1, { 0, 0, 0, 0 } },
+	[MAX2175_NA_FM_2_0] = { MAX2175_BAND_FM,
+				98255520, 6, { 0, 0, 0, 0 } },
+};
+
+/*
+ * Preset values:
+ * Based on Maxim MAX2175 Register Table revision: 130p10
+ */
+static const u8 full_fm_eu_1p0[] = {
+	0x15, 0x04, 0xb8, 0xe3, 0x35, 0x18, 0x7c, 0x00,
+	0x00, 0x7d, 0x40, 0x08, 0x70, 0x7a, 0x88, 0x91,
+	0x61, 0x61, 0x61, 0x61, 0x5a, 0x0f, 0x34, 0x1c,
+	0x14, 0x88, 0x33, 0x02, 0x00, 0x09, 0x00, 0x65,
+	0x9f, 0x2b, 0x80, 0x00, 0x95, 0x05, 0x2c, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
+	0x4a, 0x08, 0xa8, 0x0e, 0x0e, 0x2f, 0x7e, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x5e, 0xa9,
+	0xae, 0xbb, 0x57, 0x18, 0x3b, 0x03, 0x3b, 0x64,
+	0x40, 0x60, 0x00, 0x2a, 0xbf, 0x3f, 0xff, 0x9f,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00,
+	0xff, 0xfc, 0xef, 0x1c, 0x40, 0x00, 0x00, 0x02,
+	0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x40, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00,
+	0x00, 0x47, 0x00, 0x00, 0x11, 0x3f, 0x22, 0x00,
+	0xf1, 0x00, 0x41, 0x03, 0xb0, 0x00, 0x00, 0x00,
+	0x1b,
+};
+
+static const u8 full_fm_na_1p0[] = {
+	0x13, 0x08, 0x8d, 0xc0, 0x35, 0x18, 0x7d, 0x3f,
+	0x7d, 0x75, 0x40, 0x08, 0x70, 0x7a, 0x88, 0x91,
+	0x61, 0x61, 0x61, 0x61, 0x5c, 0x0f, 0x34, 0x1c,
+	0x14, 0x88, 0x33, 0x02, 0x00, 0x01, 0x00, 0x65,
+	0x9f, 0x2b, 0x80, 0x00, 0x95, 0x05, 0x2c, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
+	0x4a, 0x08, 0xa8, 0x0e, 0x0e, 0xaf, 0x7e, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x5e, 0xa9,
+	0xae, 0xbb, 0x57, 0x18, 0x3b, 0x03, 0x3b, 0x64,
+	0x40, 0x60, 0x00, 0x2a, 0xbf, 0x3f, 0xff, 0x9f,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00,
+	0xff, 0xfc, 0xef, 0x1c, 0x40, 0x00, 0x00, 0x02,
+	0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0x40, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00,
+	0x00, 0x35, 0x00, 0x00, 0x11, 0x3f, 0x22, 0x00,
+	0xf1, 0x00, 0x41, 0x03, 0xb0, 0x00, 0x00, 0x00,
+	0x1b,
+};
+
+/* DAB1.2 settings */
+static const struct max2175_reg_map dab12_map[] = {
+	{ 0x01, 0x13 }, { 0x02, 0x0d }, { 0x03, 0x15 }, { 0x04, 0x55 },
+	{ 0x05, 0x0a }, { 0x06, 0xa0 }, { 0x07, 0x40 }, { 0x08, 0x00 },
+	{ 0x09, 0x00 }, { 0x0a, 0x7d }, { 0x0b, 0x4a }, { 0x0c, 0x28 },
+	{ 0x0e, 0x43 }, { 0x0f, 0xb5 }, { 0x10, 0x31 }, { 0x11, 0x9e },
+	{ 0x12, 0x68 }, { 0x13, 0x9e }, { 0x14, 0x68 }, { 0x15, 0x58 },
+	{ 0x16, 0x2f }, { 0x17, 0x3f }, { 0x18, 0x40 }, { 0x1a, 0x88 },
+	{ 0x1b, 0xaa }, { 0x1c, 0x9a }, { 0x1d, 0x00 }, { 0x1e, 0x00 },
+	{ 0x23, 0x80 }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 },
+	{ 0x27, 0x00 }, { 0x32, 0x08 }, { 0x33, 0xf8 }, { 0x36, 0x2d },
+	{ 0x37, 0x7e }, { 0x55, 0xaf }, { 0x56, 0x3f }, { 0x57, 0xf8 },
+	{ 0x58, 0x99 }, { 0x76, 0x00 }, { 0x77, 0x00 }, { 0x78, 0x02 },
+	{ 0x79, 0x40 }, { 0x82, 0x00 }, { 0x83, 0x00 }, { 0x85, 0x00 },
+	{ 0x86, 0x20 },
+};
+
+/* EU FM 1.2 settings */
+static const struct max2175_reg_map fmeu1p2_map[] = {
+	{ 0x01, 0x15 }, { 0x02, 0x04 }, { 0x03, 0xb8 }, { 0x04, 0xe3 },
+	{ 0x05, 0x35 }, { 0x06, 0x18 }, { 0x07, 0x7c }, { 0x08, 0x00 },
+	{ 0x09, 0x00 }, { 0x0a, 0x73 }, { 0x0b, 0x40 }, { 0x0c, 0x08 },
+	{ 0x0e, 0x7a }, { 0x0f, 0x88 }, { 0x10, 0x91 }, { 0x11, 0x61 },
+	{ 0x12, 0x61 }, { 0x13, 0x61 }, { 0x14, 0x61 }, { 0x15, 0x5a },
+	{ 0x16, 0x0f }, { 0x17, 0x34 }, { 0x18, 0x1c }, { 0x1a, 0x88 },
+	{ 0x1b, 0x33 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x1e, 0x01 },
+	{ 0x23, 0x80 }, { 0x24, 0x00 }, { 0x25, 0x95 }, { 0x26, 0x05 },
+	{ 0x27, 0x2c }, { 0x32, 0x08 }, { 0x33, 0xa8 }, { 0x36, 0x2f },
+	{ 0x37, 0x7e }, { 0x55, 0xbf }, { 0x56, 0x3f }, { 0x57, 0xff },
+	{ 0x58, 0x9f }, { 0x76, 0xac }, { 0x77, 0x40 }, { 0x78, 0x00 },
+	{ 0x79, 0x00 }, { 0x82, 0x47 }, { 0x83, 0x00 }, { 0x85, 0x11 },
+	{ 0x86, 0x3f },
+};
+
+/* FM NA 1.0 settings */
+static const struct max2175_reg_map fmna1p0_map[] = {
+	{ 0x01, 0x13 }, { 0x02, 0x08 }, { 0x03, 0x8d }, { 0x04, 0xc0 },
+	{ 0x05, 0x35 }, { 0x06, 0x18 }, { 0x07, 0x7d }, { 0x08, 0x3f },
+	{ 0x09, 0x7d }, { 0x0a, 0x75 }, { 0x0b, 0x40 }, { 0x0c, 0x08 },
+	{ 0x0e, 0x7a }, { 0x0f, 0x88 }, { 0x10, 0x91 }, { 0x11, 0x61 },
+	{ 0x12, 0x61 }, { 0x13, 0x61 }, { 0x14, 0x61 }, { 0x15, 0x5c },
+	{ 0x16, 0x0f }, { 0x17, 0x34 }, { 0x18, 0x1c }, { 0x1a, 0x88 },
+	{ 0x1b, 0x33 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x1e, 0x01 },
+	{ 0x23, 0x80 }, { 0x24, 0x00 }, { 0x25, 0x95 }, { 0x26, 0x05 },
+	{ 0x27, 0x2c }, { 0x32, 0x08 }, { 0x33, 0xa8 }, { 0x36, 0xaf },
+	{ 0x37, 0x7e }, { 0x55, 0xbf }, { 0x56, 0x3f }, { 0x57, 0xff },
+	{ 0x58, 0x9f }, { 0x76, 0xa6 }, { 0x77, 0x40 }, { 0x78, 0x00 },
+	{ 0x79, 0x00 }, { 0x82, 0x35 }, { 0x83, 0x00 }, { 0x85, 0x11 },
+	{ 0x86, 0x3f },
+};
+
+/* FM NA 2.0 settings */
+static const struct max2175_reg_map fmna2p0_map[] = {
+	{ 0x01, 0x13 }, { 0x02, 0x08 }, { 0x03, 0x8d }, { 0x04, 0xc0 },
+	{ 0x05, 0x35 }, { 0x06, 0x18 }, { 0x07, 0x7c }, { 0x08, 0x54 },
+	{ 0x09, 0xa7 }, { 0x0a, 0x55 }, { 0x0b, 0x42 }, { 0x0c, 0x48 },
+	{ 0x0e, 0x7a }, { 0x0f, 0x88 }, { 0x10, 0x91 }, { 0x11, 0x61 },
+	{ 0x12, 0x61 }, { 0x13, 0x61 }, { 0x14, 0x61 }, { 0x15, 0x5c },
+	{ 0x16, 0x0f }, { 0x17, 0x34 }, { 0x18, 0x1c }, { 0x1a, 0x88 },
+	{ 0x1b, 0x33 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x1e, 0x01 },
+	{ 0x23, 0x80 }, { 0x24, 0x00 }, { 0x25, 0x95 }, { 0x26, 0x05 },
+	{ 0x27, 0x2c }, { 0x32, 0x08 }, { 0x33, 0xa8 }, { 0x36, 0xaf },
+	{ 0x37, 0x7e }, { 0x55, 0xbf }, { 0x56, 0x3f }, { 0x57, 0xff },
+	{ 0x58, 0x9f }, { 0x76, 0xac }, { 0x77, 0xc0 }, { 0x78, 0x00 },
+	{ 0x79, 0x00 }, { 0x82, 0x6b }, { 0x83, 0x00 }, { 0x85, 0x11 },
+	{ 0x86, 0x3f },
+};
+
+static const u16 ch_coeff_dab1[] = {
+	0x001c, 0x0007, 0xffcd, 0x0056, 0xffa4, 0x0033, 0x0027, 0xff61,
+	0x010e, 0xfec0, 0x0106, 0xffb8, 0xff1c, 0x023c, 0xfcb2, 0x039b,
+	0xfd4e, 0x0055, 0x036a, 0xf7de, 0x0d21, 0xee72, 0x1499, 0x6a51,
+};
+
+static const u16 ch_coeff_fmeu[] = {
+	0x0000, 0xffff, 0x0001, 0x0002, 0xfffa, 0xffff, 0x0015, 0xffec,
+	0xffde, 0x0054, 0xfff9, 0xff52, 0x00b8, 0x00a2, 0xfe0a, 0x00af,
+	0x02e3, 0xfc14, 0xfe89, 0x089d, 0xfa2e, 0xf30f, 0x25be, 0x4eb6,
+};
+
+static const u16 eq_coeff_fmeu1_ra02_m6db[] = {
+	0x0040, 0xffc6, 0xfffa, 0x002c, 0x000d, 0xff90, 0x0037, 0x006e,
+	0xffc0, 0xff5b, 0x006a, 0x00f0, 0xff57, 0xfe94, 0x0112, 0x0252,
+	0xfe0c, 0xfc6a, 0x0385, 0x0553, 0xfa49, 0xf789, 0x0b91, 0x1a10,
+};
+
+static const u16 ch_coeff_fmna[] = {
+	0x0001, 0x0003, 0xfffe, 0xfff4, 0x0000, 0x001f, 0x000c, 0xffbc,
+	0xffd3, 0x007d, 0x0075, 0xff33, 0xff01, 0x0131, 0x01ef, 0xfe60,
+	0xfc7a, 0x020e, 0x0656, 0xfd94, 0xf395, 0x02ab, 0x2857, 0x3d3f,
+};
+
+static const u16 eq_coeff_fmna1_ra02_m6db[] = {
+	0xfff1, 0xffe1, 0xffef, 0x000e, 0x0030, 0x002f, 0xfff6, 0xffa7,
+	0xff9d, 0x000a, 0x00a2, 0x00b5, 0xffea, 0xfed9, 0xfec5, 0x003d,
+	0x0217, 0x021b, 0xff5a, 0xfc2b, 0xfcbd, 0x02c4, 0x0ac3, 0x0e85,
+};
+
+static const u8 adc_presets[2][23] = {
+	{
+		0x83, 0x00, 0xcf, 0xb4, 0x0f, 0x2c, 0x0c, 0x49,
+		0x00, 0x00, 0x00, 0x8c,	0x02, 0x02, 0x00, 0x04,
+		0xec, 0x82, 0x4b, 0xcc, 0x01, 0x88, 0x0c,
+	},
+	{
+		0x83, 0x00, 0xcf, 0xb4,	0x0f, 0x2c, 0x0c, 0x49,
+		0x00, 0x00, 0x00, 0x8c,	0x02, 0x20, 0x33, 0x8c,
+		0x57, 0xd7, 0x59, 0xb7,	0x65, 0x0e, 0x0c,
+	},
+};
+
+/* Custom controls */
+#define V4L2_CID_MAX2175_I2S_ENABLE	(V4L2_CID_USER_MAX217X_BASE + 0x01)
+#define V4L2_CID_MAX2175_I2S_MODE	(V4L2_CID_USER_MAX217X_BASE + 0x02)
+#define V4L2_CID_MAX2175_HSLS		(V4L2_CID_USER_MAX217X_BASE + 0x03)
+#define V4L2_CID_MAX2175_RX_MODE	(V4L2_CID_USER_MAX217X_BASE + 0x04)
+
+/* Tuner bands */
+static const struct v4l2_frequency_band eu_bands_rf = {
+	.tuner = 0,
+	.type = V4L2_TUNER_RF,
+	.index = 0,
+	.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+	.rangelow   = 65000000,
+	.rangehigh  = 240000000,
+};
+
+static const struct v4l2_frequency_band na_bands_rf = {
+	.tuner = 0,
+	.type = V4L2_TUNER_RF,
+	.index = 0,
+	.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+	.rangelow   = 65000000,
+	.rangehigh  = 108000000,
+};
+
+/* Regmap settings */
+static const struct regmap_range max2175_regmap_volatile_range[] = {
+	regmap_reg_range(0x30, 0x35),
+	regmap_reg_range(0x3a, 0x45),
+	regmap_reg_range(0x59, 0x5e),
+	regmap_reg_range(0x73, 0x75),
+};
+
+static const struct regmap_access_table max2175_volatile_regs = {
+	.yes_ranges = max2175_regmap_volatile_range,
+	.n_yes_ranges = ARRAY_SIZE(max2175_regmap_volatile_range),
+};
+
+static const struct reg_default max2175_reg_defaults[] = {
+	{ 0x00, 0x07},
+};
+
+static const struct regmap_config max2175_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = 0xff,
+	.reg_defaults = max2175_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(max2175_reg_defaults),
+	.volatile_table = &max2175_volatile_regs,
+	.cache_type = REGCACHE_FLAT,
+};
+
+struct max2175 {
+	struct v4l2_subdev sd;		/* Sub-device */
+	struct i2c_client *client;	/* I2C client */
+
+	/* Controls */
+	struct v4l2_ctrl_handler ctrl_hdl;
+	struct v4l2_ctrl *lna_gain;	/* LNA gain value */
+	struct v4l2_ctrl *if_gain;	/* I/F gain value */
+	struct v4l2_ctrl *pll_lock;	/* PLL lock */
+	struct v4l2_ctrl *i2s_en;	/* I2S output enable */
+	struct v4l2_ctrl *i2s_mode;	/* I2S mode value */
+	struct v4l2_ctrl *hsls;		/* High-side/Low-side polarity */
+	struct v4l2_ctrl *rx_mode;	/* Receive mode */
+
+	/* Regmap */
+	struct regmap *regmap;
+
+	/* Cached configuration */
+	u32 freq;			/* Tuned freq In Hz */
+	const struct max2175_rxmode *rx_modes;		/* EU or NA modes */
+	const struct v4l2_frequency_band *bands_rf;	/* EU or NA bands */
+
+	/* Device settings */
+	unsigned long xtal_freq;	/* Ref Oscillator freq in Hz */
+	u32 decim_ratio;
+	bool master;			/* Master/Slave */
+	bool am_hiz;			/* AM Hi-Z filter usage */
+
+	/* ROM values */
+	u8 rom_bbf_bw_am;
+	u8 rom_bbf_bw_fm;
+	u8 rom_bbf_bw_dab;
+
+	/* Local copy of old setting */
+	u8 i2s_test;
+
+	/* Driver private variables */
+	bool mode_resolved;		/* Flag to sanity check settings */
+};
+
+static inline struct max2175 *max2175_from_sd(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct max2175, sd);
+}
+
+static inline struct max2175 *max2175_from_ctrl_hdl(struct v4l2_ctrl_handler *h)
+{
+	return container_of(h, struct max2175, ctrl_hdl);
+}
+
+/* Get bitval of a given val */
+static inline u8 max2175_get_bitval(u8 val, u8 msb, u8 lsb)
+{
+	return (val & GENMASK(msb, lsb)) >> lsb;
+}
+
+/* Read/Write bit(s) on top of regmap */
+static int max2175_read(struct max2175 *ctx, u8 idx, u8 *val)
+{
+	u32 regval;
+	int ret = regmap_read(ctx->regmap, idx, &regval);
+
+	if (ret)
+		v4l2_err(ctx->client, "read ret(%d): idx 0x%02x\n", ret, idx);
+
+	*val = regval;
+	return ret;
+}
+
+static int max2175_write(struct max2175 *ctx, u8 idx, u8 val)
+{
+	int ret = regmap_write(ctx->regmap, idx, val);
+
+	if (ret)
+		v4l2_err(ctx->client, "write ret(%d): idx 0x%02x val 0x%02x\n",
+			 ret, idx, val);
+	return ret;
+}
+
+static u8 max2175_read_bits(struct max2175 *ctx, u8 idx, u8 msb, u8 lsb)
+{
+	u8 val;
+
+	if (max2175_read(ctx, idx, &val))
+		return 0;
+
+	return max2175_get_bitval(val, msb, lsb);
+}
+
+static bool max2175_read_bit(struct max2175 *ctx, u8 idx, u8 bit)
+{
+	return !!max2175_read_bits(ctx, idx, bit, bit);
+}
+
+static int max2175_write_bits(struct max2175 *ctx, u8 idx,
+			     u8 msb, u8 lsb, u8 newval)
+{
+	int ret = regmap_update_bits(ctx->regmap, idx, GENMASK(msb, lsb),
+				     newval << lsb);
+
+	if (ret)
+		v4l2_err(ctx->client, "wbits ret(%d): idx 0x%02x\n", ret, idx);
+
+	return ret;
+}
+
+static int max2175_write_bit(struct max2175 *ctx, u8 idx, u8 bit, u8 newval)
+{
+	return max2175_write_bits(ctx, idx, bit, bit, newval);
+}
+
+/* Checks expected pattern every msec until timeout */
+static int max2175_poll_timeout(struct max2175 *ctx, u8 idx, u8 msb, u8 lsb,
+				u8 exp_bitval, u32 timeout_ms)
+{
+	unsigned int val;
+
+	return regmap_read_poll_timeout(ctx->regmap, idx, val,
+			(max2175_get_bitval(val, msb, lsb) == exp_bitval),
+			1000, timeout_ms * 1000);
+}
+
+static int max2175_poll_csm_ready(struct max2175 *ctx)
+{
+	int ret;
+
+	ret = max2175_poll_timeout(ctx, 69, 1, 1, 0, 50);
+	if (ret)
+		v4l2_err(ctx->client, "csm not ready\n");
+
+	return ret;
+}
+
+#define MAX2175_IS_BAND_AM(ctx)		\
+	(max2175_read_bits(ctx, 5, 1, 0) == MAX2175_BAND_AM)
+
+#define MAX2175_IS_BAND_VHF(ctx)	\
+	(max2175_read_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF)
+
+#define MAX2175_IS_FM_MODE(ctx)		\
+	(max2175_read_bits(ctx, 12, 5, 4) == 0)
+
+#define MAX2175_IS_FMHD_MODE(ctx)	\
+	(max2175_read_bits(ctx, 12, 5, 4) == 1)
+
+#define MAX2175_IS_DAB_MODE(ctx)	\
+	(max2175_read_bits(ctx, 12, 5, 4) == 2)
+
+static int max2175_band_from_freq(u32 freq)
+{
+	if (freq >= 144000 && freq <= 26100000)
+		return MAX2175_BAND_AM;
+	else if (freq >= 65000000 && freq <= 108000000)
+		return MAX2175_BAND_FM;
+	else
+		return MAX2175_BAND_VHF;
+}
+
+static int max2175_update_i2s_mode(struct max2175 *ctx, u32 rx_mode,
+				   u32 i2s_mode)
+{
+	max2175_write_bits(ctx, 29, 2, 0, i2s_mode);
+
+	/* Based on I2S mode value I2S_WORD_CNT values change */
+	switch (i2s_mode) {
+	case MAX2175_I2S_MODE3:
+		max2175_write_bits(ctx, 30, 6, 0, 1);
+		break;
+	case MAX2175_I2S_MODE2:
+	case MAX2175_I2S_MODE4:
+		max2175_write_bits(ctx, 30, 6, 0, 0);
+		break;
+	case MAX2175_I2S_MODE0:
+		max2175_write_bits(ctx, 30, 6, 0,
+			ctx->rx_modes[rx_mode].i2s_word_size);
+		break;
+	}
+	mxm_dbg(ctx, "update_i2s_mode %u, rx_mode %u\n", i2s_mode, rx_mode);
+	return 0;
+}
+
+static void max2175_i2s_enable(struct max2175 *ctx, bool enable)
+{
+	if (enable) {
+		/* Use old setting */
+		max2175_write_bits(ctx, 104, 3, 0, ctx->i2s_test);
+	} else {
+		/* Cache old setting */
+		ctx->i2s_test = max2175_read_bits(ctx, 104, 3, 0);
+		max2175_write_bits(ctx, 104, 3, 0, 9);	/* Keep SCK alive */
+	}
+	mxm_dbg(ctx, "i2s %sabled: old val %u\n", enable ? "en" : "dis",
+		ctx->i2s_test);
+}
+
+static void max2175_set_filter_coeffs(struct max2175 *ctx, u8 m_sel,
+				      u8 bank, const u16 *coeffs)
+{
+	unsigned int i;
+	u8 coeff_addr, upper_address = 24;
+
+	mxm_dbg(ctx, "set_filter_coeffs: m_sel %d bank %d\n", m_sel, bank);
+	max2175_write_bits(ctx, 114, 5, 4, m_sel);
+
+	if (m_sel == 2)
+		upper_address = 12;
+
+	for (i = 0; i < upper_address; i++) {
+		coeff_addr = i + bank * 24;
+		max2175_write(ctx, 115, coeffs[i] >> 8);
+		max2175_write(ctx, 116, coeffs[i]);
+		max2175_write(ctx, 117, coeff_addr | 1 << 7);
+	}
+	max2175_write_bit(ctx, 117, 7, 0);
+}
+
+static void max2175_load_fmeu_1p2(struct max2175 *ctx)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(fmeu1p2_map); i++)
+		max2175_write(ctx, fmeu1p2_map[i].idx, fmeu1p2_map[i].val);
+
+	ctx->decim_ratio = 36;
+
+	/* Load the Channel Filter Coefficients into channel filter bank #2 */
+	max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 0, ch_coeff_fmeu);
+	max2175_set_filter_coeffs(ctx, MAX2175_EQ_MSEL, 0,
+				  eq_coeff_fmeu1_ra02_m6db);
+}
+
+static void max2175_load_dab_1p2(struct max2175 *ctx)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(dab12_map); i++)
+		max2175_write(ctx, dab12_map[i].idx, dab12_map[i].val);
+
+	ctx->decim_ratio = 1;
+
+	/* Load the Channel Filter Coefficients into channel filter bank #2 */
+	max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 2, ch_coeff_dab1);
+}
+
+static void max2175_load_fmna_1p0(struct max2175 *ctx)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(fmna1p0_map); i++)
+		max2175_write(ctx, fmna1p0_map[i].idx, fmna1p0_map[i].val);
+}
+
+static void max2175_load_fmna_2p0(struct max2175 *ctx)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(fmna2p0_map); i++)
+		max2175_write(ctx, fmna2p0_map[i].idx, fmna2p0_map[i].val);
+}
+
+static void max2175_set_bbfilter(struct max2175 *ctx)
+{
+	if (MAX2175_IS_BAND_AM(ctx)) {
+		max2175_write_bits(ctx, 12, 3, 0, ctx->rom_bbf_bw_am);
+		mxm_dbg(ctx, "set_bbfilter AM: rom %d\n", ctx->rom_bbf_bw_am);
+	} else if (MAX2175_IS_DAB_MODE(ctx)) {
+		max2175_write_bits(ctx, 12, 3, 0, ctx->rom_bbf_bw_dab);
+		mxm_dbg(ctx, "set_bbfilter DAB: rom %d\n", ctx->rom_bbf_bw_dab);
+	} else {
+		max2175_write_bits(ctx, 12, 3, 0, ctx->rom_bbf_bw_fm);
+		mxm_dbg(ctx, "set_bbfilter FM: rom %d\n", ctx->rom_bbf_bw_fm);
+	}
+}
+
+static bool max2175_set_csm_mode(struct max2175 *ctx,
+			  enum max2175_csm_mode new_mode)
+{
+	int ret = max2175_poll_csm_ready(ctx);
+
+	if (ret)
+		return ret;
+
+	max2175_write_bits(ctx, 0, 2, 0, new_mode);
+	mxm_dbg(ctx, "set csm new mode %d\n", new_mode);
+
+	/* Wait for a fixed settle down time depending on new mode */
+	switch (new_mode) {
+	case MAX2175_PRESET_TUNE:
+		usleep_range(51100, 51500);	/* 51.1ms */
+		break;
+	/*
+	 * Other mode switches need different sleep values depending on band &
+	 * mode
+	 */
+	default:
+		break;
+	}
+
+	return max2175_poll_csm_ready(ctx);
+}
+
+static int max2175_csm_action(struct max2175 *ctx,
+			      enum max2175_csm_mode action)
+{
+	int ret;
+
+	mxm_dbg(ctx, "csm_action: %d\n", action);
+
+	/* Other actions can be added in future when needed */
+	ret = max2175_set_csm_mode(ctx, MAX2175_LOAD_TO_BUFFER);
+	if (ret)
+		return ret;
+	return max2175_set_csm_mode(ctx, MAX2175_PRESET_TUNE);
+}
+
+static int max2175_set_lo_freq(struct max2175 *ctx, u64 lo_freq)
+{
+	u64 scaled_lo_freq, scaled_npf, scaled_integer, scaled_fraction;
+	u32 frac_desired, int_desired, lo_mult = 1;
+	const u32 scale_factor = 1000000U;
+	u8 loband_bits = 0, vcodiv_bits = 0;
+	enum max2175_band band;
+	int ret;
+
+	/* Scale to larger number for precision */
+	scaled_lo_freq = lo_freq * scale_factor * 100;
+	band = max2175_read_bits(ctx, 5, 1, 0);
+
+	mxm_dbg(ctx, "set_lo_freq: scaled lo_freq %llu lo_freq %llu band %d\n",
+		scaled_lo_freq, lo_freq, band);
+
+	switch (band) {
+	case MAX2175_BAND_AM:
+		if (max2175_read_bit(ctx, 5, 7) == 0)
+			lo_mult = 16;
+		break;
+	case MAX2175_BAND_FM:
+		if (lo_freq <= 74700000) {
+			lo_mult = 16;
+		} else if (lo_freq > 74700000 && lo_freq <= 110000000) {
+			loband_bits = 1;
+		} else {
+			loband_bits = 1;
+			vcodiv_bits = 3;
+		}
+		lo_mult = 8;
+		break;
+	case MAX2175_BAND_VHF:
+		if (lo_freq <= 210000000) {
+			loband_bits = 2;
+			vcodiv_bits = 2;
+		} else {
+			loband_bits = 2;
+			vcodiv_bits = 1;
+		}
+		lo_mult = 4;
+		break;
+	default:
+		loband_bits = 3;
+		vcodiv_bits = 2;
+		lo_mult = 2;
+		break;
+	}
+
+	if (band == MAX2175_BAND_L)
+		scaled_npf = div_u64(div_u64(scaled_lo_freq, ctx->xtal_freq),
+				     lo_mult);
+	else
+		scaled_npf = div_u64(scaled_lo_freq, ctx->xtal_freq) * lo_mult;
+
+	scaled_npf = div_u64(scaled_npf, 100);
+	scaled_integer = div_u64(scaled_npf, scale_factor) * scale_factor;
+	int_desired = div_u64(scaled_npf, scale_factor);
+	scaled_fraction = scaled_npf - scaled_integer;
+	frac_desired = div_u64(scaled_fraction << 20, scale_factor);
+
+	/* Check CSM is not busy */
+	ret = max2175_poll_csm_ready(ctx);
+	if (ret)
+		return ret;
+
+	mxm_dbg(ctx, "loband %u vcodiv %u lo_mult %u scaled_npf %llu\n",
+		loband_bits, vcodiv_bits, lo_mult, scaled_npf);
+	mxm_dbg(ctx, "scaled int %llu frac %llu desired int %u frac %u\n",
+		scaled_integer, scaled_fraction, int_desired, frac_desired);
+
+	/* Write the calculated values to the appropriate registers */
+	max2175_write(ctx, 1, int_desired);
+	max2175_write_bits(ctx, 2, 3, 0, (frac_desired >> 16) & 0xf);
+	max2175_write(ctx, 3, frac_desired >> 8);
+	max2175_write(ctx, 4, frac_desired);
+	max2175_write_bits(ctx, 5, 3, 2, loband_bits);
+	max2175_write_bits(ctx, 6, 7, 6, vcodiv_bits);
+	return ret;
+}
+
+static int max2175_set_nco_freq(struct max2175 *ctx, s64 nco_freq_desired)
+{
+	s64  nco_freq, nco_val_desired;
+	u64 abs_nco_freq;
+	const u32 scale_factor = 1000000U;
+	u32 clock_rate, nco_reg;
+	int ret;
+
+	mxm_dbg(ctx, "set_nco_freq: freq %lld\n", nco_freq_desired);
+	clock_rate = ctx->xtal_freq / ctx->decim_ratio;
+	nco_freq = -nco_freq_desired;
+
+	if (nco_freq < 0)
+		abs_nco_freq = -nco_freq;
+	else
+		abs_nco_freq = nco_freq;
+
+	/* Scale up the values for precision */
+	if (abs_nco_freq < (clock_rate / 2)) {
+		nco_val_desired = div_s64(2 * nco_freq * scale_factor,
+					  clock_rate);
+	} else {
+		if (nco_freq < 0)
+			nco_val_desired =
+			div_s64(-2 * (clock_rate - abs_nco_freq) * scale_factor,
+				clock_rate);
+		else
+			nco_val_desired =
+			div_s64(2 * (clock_rate - abs_nco_freq) * scale_factor,
+				clock_rate);
+	}
+
+	/* Scale down to get the fraction */
+	if (nco_freq < 0)
+		nco_reg = 0x200000 + div_s64(nco_val_desired << 20,
+						  scale_factor);
+	else
+		nco_reg = div_s64(nco_val_desired << 20, scale_factor);
+
+	/* Check CSM is not busy */
+	ret = max2175_poll_csm_ready(ctx);
+	if (ret)
+		return ret;
+
+	mxm_dbg(ctx, "clk %u decim %u abs %llu desired %lld reg %u\n",
+		clock_rate, ctx->decim_ratio, abs_nco_freq,
+		nco_val_desired, nco_reg);
+
+	/* Write the calculated values to the appropriate registers */
+	max2175_write_bits(ctx, 7, 4, 0, (nco_reg >> 16) & 0x1f);
+	max2175_write(ctx, 8, nco_reg >> 8);
+	max2175_write(ctx, 9, nco_reg);
+	return ret;
+}
+
+static int max2175_set_rf_freq_non_am_bands(struct max2175 *ctx, u64 freq,
+					    u32 lo_pos)
+{
+	s64 adj_freq;
+	u64 low_if_freq;
+	int ret;
+
+	mxm_dbg(ctx, "rf_freq: non AM bands\n");
+
+	if (MAX2175_IS_FM_MODE(ctx))
+		low_if_freq = 128000;
+	else if (MAX2175_IS_FMHD_MODE(ctx))
+		low_if_freq = 228000;
+	else
+		return max2175_set_lo_freq(ctx, freq);
+
+	if (MAX2175_IS_BAND_VHF(ctx) == (lo_pos == MAX2175_LO_ABOVE_DESIRED))
+		adj_freq = freq + low_if_freq;
+	else
+		adj_freq = freq - low_if_freq;
+
+	ret = max2175_set_lo_freq(ctx, adj_freq);
+	if (ret)
+		return ret;
+
+	return max2175_set_nco_freq(ctx, low_if_freq);
+}
+
+static int max2175_set_rf_freq(struct max2175 *ctx, u64 freq, u32 lo_pos)
+{
+	int ret;
+
+	if (MAX2175_IS_BAND_AM(ctx))
+		ret = max2175_set_nco_freq(ctx, freq);
+	else
+		ret = max2175_set_rf_freq_non_am_bands(ctx, freq, lo_pos);
+
+	mxm_dbg(ctx, "set_rf_freq: ret %d freq %llu\n", ret, freq);
+	return ret;
+}
+
+static int max2175_tune_rf_freq(struct max2175 *ctx, u64 freq, u32 hsls)
+{
+	int ret;
+
+	ret = max2175_set_rf_freq(ctx, freq, hsls);
+	if (ret)
+		return ret;
+
+	ret = max2175_csm_action(ctx, MAX2175_BUFFER_PLUS_PRESET_TUNE);
+	if (ret)
+		return ret;
+
+	mxm_dbg(ctx, "tune_rf_freq: old %u new %llu\n", ctx->freq, freq);
+	ctx->freq = freq;
+	return ret;
+}
+
+static void max2175_set_hsls(struct max2175 *ctx, u32 lo_pos)
+{
+	mxm_dbg(ctx, "set_hsls: lo_pos %u\n", lo_pos);
+
+	if ((lo_pos == MAX2175_LO_BELOW_DESIRED) == MAX2175_IS_BAND_VHF(ctx))
+		max2175_write_bit(ctx, 5, 4, 1);
+	else
+		max2175_write_bit(ctx, 5, 4, 0);
+}
+
+static void max2175_set_eu_rx_mode(struct max2175 *ctx, u32 rx_mode)
+{
+	switch (rx_mode) {
+	case MAX2175_EU_FM_1_2:
+		max2175_load_fmeu_1p2(ctx);
+		break;
+
+	case MAX2175_DAB_1_2:
+		max2175_load_dab_1p2(ctx);
+		break;
+	}
+	/* Master is the default setting */
+	if (!ctx->master)
+		max2175_write_bit(ctx, 30, 7, 1);
+
+	/* Cache i2s_test value at this point */
+	ctx->i2s_test = max2175_read_bits(ctx, 104, 3, 0);
+}
+
+static void max2175_set_na_rx_mode(struct max2175 *ctx, u32 rx_mode)
+{
+	switch (rx_mode) {
+	case MAX2175_NA_FM_1_0:
+		max2175_load_fmna_1p0(ctx);
+		break;
+	case MAX2175_NA_FM_2_0:
+		max2175_load_fmna_2p0(ctx);
+		break;
+	}
+	/* Master is the default setting */
+	if (!ctx->master)
+		max2175_write_bit(ctx, 30, 7, 1);
+
+	/* Cache i2s_test value at this point */
+	ctx->i2s_test = max2175_read_bits(ctx, 104, 3, 0);
+	ctx->decim_ratio = 27;
+
+	/* Load the Channel Filter Coefficients into channel filter bank #2 */
+	max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 0, ch_coeff_fmna);
+	max2175_set_filter_coeffs(ctx, MAX2175_EQ_MSEL, 0,
+				  eq_coeff_fmna1_ra02_m6db);
+}
+
+static int max2175_set_rx_mode(struct max2175 *ctx, u32 rx_mode, u32 hsls)
+{
+	mxm_dbg(ctx, "set_rx_mode: %u am_hiz %u\n", rx_mode, ctx->am_hiz);
+	if (ctx->xtal_freq == MAX2175_EU_XTAL_FREQ)
+		max2175_set_eu_rx_mode(ctx, rx_mode);
+	else
+		max2175_set_na_rx_mode(ctx, rx_mode);
+
+	if (ctx->am_hiz) {
+		mxm_dbg(ctx, "setting AM HiZ related config\n");
+		max2175_write_bit(ctx, 50, 5, 1);
+		max2175_write_bit(ctx, 90, 7, 1);
+		max2175_write_bits(ctx, 73, 1, 0, 2);
+		max2175_write_bits(ctx, 80, 5, 0, 33);
+	}
+
+	/* Load BB filter trim values saved in ROM */
+	max2175_set_bbfilter(ctx);
+
+	/* Set HSLS */
+	max2175_set_hsls(ctx, hsls);
+
+	ctx->mode_resolved = true;
+	return 0;
+}
+
+static bool max2175_i2s_rx_mode_valid(struct max2175 *ctx,
+					 u32 mode, u32 i2s_mode)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(ctx->rx_modes[mode].i2s_modes); i++)
+		if (ctx->rx_modes[mode].i2s_modes[i] == i2s_mode)
+			return true;
+
+	v4l2_err(ctx->client, "i2s_mode %u not suitable for cur rx mode %u\n",
+		 i2s_mode, mode);
+	return false;
+}
+
+static int max2175_rx_mode_from_freq(struct max2175 *ctx, u32 freq, u32 *mode)
+{
+	unsigned int i;
+	int band = max2175_band_from_freq(freq);
+
+	/* Pick the first match always */
+	for (i = 0; i <= ctx->rx_mode->maximum; i++) {
+		if (ctx->rx_modes[i].band == band) {
+			*mode = i;
+			mxm_dbg(ctx, "rx_mode_from_freq: freq %u mode %d\n",
+				freq, *mode);
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+static bool max2175_freq_rx_mode_valid(struct max2175 *ctx,
+					 u32 mode, u32 freq)
+{
+	int band = max2175_band_from_freq(freq);
+
+	return (ctx->rx_modes[mode].band == band);
+}
+
+static void max2175_load_adc_presets(struct max2175 *ctx)
+{
+	unsigned int i, j;
+
+	for (i = 0; i < 2; i++)
+		for (j = 0; j < 23; j++)
+			max2175_write(ctx, 146 + j + i * 55, adc_presets[i][j]);
+}
+
+static int max2175_init_power_manager(struct max2175 *ctx)
+{
+	int ret;
+
+	/* Execute on-chip power-up/calibration */
+	max2175_write_bit(ctx, 99, 2, 0);
+	usleep_range(1000, 1500);
+	max2175_write_bit(ctx, 99, 2, 1);
+
+	/* Wait for the power manager to finish. */
+	ret = max2175_poll_timeout(ctx, 69, 7, 7, 1, 50);
+	if (ret)
+		v4l2_err(ctx->client, "init pm failed\n");
+	return ret;
+}
+
+static int max2175_recalibrate_adc(struct max2175 *ctx)
+{
+	int ret;
+
+	/* ADC Re-calibration */
+	max2175_write(ctx, 150, 0xff);
+	max2175_write(ctx, 205, 0xff);
+	max2175_write(ctx, 147, 0x20);
+	max2175_write(ctx, 147, 0x00);
+	max2175_write(ctx, 202, 0x20);
+	max2175_write(ctx, 202, 0x00);
+
+	ret = max2175_poll_timeout(ctx, 69, 4, 3, 3, 50);
+	if (ret)
+		v4l2_err(ctx->client, "adc recalibration failed\n");
+	return ret;
+}
+
+static u8 max2175_read_rom(struct max2175 *ctx, u8 row)
+{
+	u8 data;
+
+	max2175_write_bit(ctx, 56, 4, 0);
+	max2175_write_bits(ctx, 56, 3, 0, row);
+
+	usleep_range(2000, 2500);
+	max2175_read(ctx, 58, &data);
+
+	max2175_write_bits(ctx, 56, 3, 0, 0);
+
+	mxm_dbg(ctx, "read_rom: row %d data 0x%02x\n", row, data);
+	return data;
+}
+
+static void max2175_load_from_rom(struct max2175 *ctx)
+{
+	u8 data = 0;
+
+	data = max2175_read_rom(ctx, 0);
+	ctx->rom_bbf_bw_am = data & 0x0f;
+	max2175_write_bits(ctx, 81, 3, 0, data >> 4);
+
+	data = max2175_read_rom(ctx, 1);
+	ctx->rom_bbf_bw_fm = data & 0x0f;
+	ctx->rom_bbf_bw_dab = data >> 4;
+
+	data = max2175_read_rom(ctx, 2);
+	max2175_write_bits(ctx, 82, 4, 0, data & 0x1f);
+	max2175_write_bits(ctx, 82, 7, 5, data >> 5);
+
+	data = max2175_read_rom(ctx, 3);
+	if (ctx->am_hiz) {
+		data &= 0x0f;
+		data |= max2175_read_rom(ctx, 7) & 0x40 >> 2;
+		if (!data)
+			data |= 2;
+	} else {
+		data = data & 0xf0 >> 4;
+		data |= max2175_read_rom(ctx, 7) & 0x80 >> 3;
+		if (!data)
+			data |= 30;
+	}
+	max2175_write_bits(ctx, 80, 5, 0, data + 31);
+
+	data = max2175_read_rom(ctx, 6);
+	max2175_write_bits(ctx, 81, 7, 6, data >> 6);
+}
+
+static void max2175_load_full_fm_eu_1p0(struct max2175 *ctx)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(full_fm_eu_1p0); i++)
+		max2175_write(ctx, i + 1, full_fm_eu_1p0[i]);
+
+	usleep_range(5000, 5500);
+	ctx->decim_ratio = 36;
+}
+
+static void max2175_load_full_fm_na_1p0(struct max2175 *ctx)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(full_fm_na_1p0); i++)
+		max2175_write(ctx, i + 1, full_fm_na_1p0[i]);
+
+	usleep_range(5000, 5500);
+	ctx->decim_ratio = 27;
+}
+
+static int max2175_core_init(struct max2175 *ctx, u32 refout_bits)
+{
+	int ret;
+
+	/* MAX2175 uses 36.864MHz clock for EU & 40.154MHz for NA region */
+	if (ctx->xtal_freq == MAX2175_EU_XTAL_FREQ)
+		max2175_load_full_fm_eu_1p0(ctx);
+	else
+		max2175_load_full_fm_na_1p0(ctx);
+
+	/* The default settings assume master */
+	if (!ctx->master)
+		max2175_write_bit(ctx, 30, 7, 1);
+
+	mxm_dbg(ctx, "refout_bits %u\n", refout_bits);
+
+	/* Set REFOUT */
+	max2175_write_bits(ctx, 56, 7, 5, refout_bits);
+
+	/* ADC Reset */
+	max2175_write_bit(ctx, 99, 1, 0);
+	usleep_range(1000, 1500);
+	max2175_write_bit(ctx, 99, 1, 1);
+
+	/* Load ADC preset values */
+	max2175_load_adc_presets(ctx);
+
+	/* Initialize the power management state machine */
+	ret = max2175_init_power_manager(ctx);
+	if (ret)
+		return ret;
+
+	/* Recalibrate ADC */
+	ret = max2175_recalibrate_adc(ctx);
+	if (ret)
+		return ret;
+
+	/* Load ROM values to appropriate registers */
+	max2175_load_from_rom(ctx);
+
+	if (ctx->xtal_freq == MAX2175_EU_XTAL_FREQ) {
+		/* Load FIR coefficients into bank 0 */
+		max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 0,
+					  ch_coeff_fmeu);
+		max2175_set_filter_coeffs(ctx, MAX2175_EQ_MSEL, 0,
+					  eq_coeff_fmeu1_ra02_m6db);
+	} else {
+		/* Load FIR coefficients into bank 0 */
+		max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 0,
+					  ch_coeff_fmna);
+		max2175_set_filter_coeffs(ctx, MAX2175_EQ_MSEL, 0,
+					  eq_coeff_fmna1_ra02_m6db);
+	}
+	mxm_dbg(ctx, "core initialized\n");
+	return 0;
+}
+
+static void max2175_s_ctrl_i2s_mode(struct max2175 *ctx, u32 i2s_mode)
+{
+	mxm_dbg(ctx, "s_ctrl_i2s_mode: %u resolved %d\n", i2s_mode,
+		ctx->mode_resolved);
+
+	/*
+	 * Update i2s mode on device only when mode is resolved & it is valid
+	 * for the configured mode
+	 */
+	if (ctx->mode_resolved &&
+	    max2175_i2s_rx_mode_valid(ctx, ctx->rx_mode->val, i2s_mode))
+		max2175_update_i2s_mode(ctx, ctx->rx_mode->val, i2s_mode);
+}
+
+static void max2175_s_ctrl_rx_mode(struct max2175 *ctx, u32 rx_mode)
+{
+	/* Load mode. Range check already done */
+	max2175_set_rx_mode(ctx, rx_mode, ctx->hsls->val);
+
+	/* Get current i2s_mode and update if needed for given rx_mode */
+	if (max2175_i2s_rx_mode_valid(ctx, rx_mode, ctx->i2s_mode->val))
+		max2175_update_i2s_mode(ctx, rx_mode, ctx->i2s_mode->val);
+	else
+		ctx->i2s_mode->val = max2175_read_bits(ctx, 29, 2, 0);
+
+	mxm_dbg(ctx, "s_ctrl_rx_mode: %u curr freq %u\n", rx_mode, ctx->freq);
+
+	/* Check if current freq valid for mode & update */
+	if (max2175_freq_rx_mode_valid(ctx, rx_mode, ctx->freq))
+		max2175_tune_rf_freq(ctx, ctx->freq, ctx->hsls->val);
+	else
+		/* Use default freq of mode if current freq is not valid */
+		max2175_tune_rf_freq(ctx, ctx->rx_modes[rx_mode].freq,
+				     ctx->hsls->val);
+}
+
+static int max2175_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct max2175 *ctx = max2175_from_ctrl_hdl(ctrl->handler);
+	int ret = 0;
+
+	mxm_dbg(ctx, "s_ctrl: id 0x%x, val %u\n", ctrl->id, ctrl->val);
+	switch (ctrl->id) {
+	case V4L2_CID_MAX2175_I2S_ENABLE:
+		max2175_i2s_enable(ctx, ctrl->val == 1);
+		break;
+	case V4L2_CID_MAX2175_I2S_MODE:
+		max2175_s_ctrl_i2s_mode(ctx, ctrl->val);
+		break;
+	case V4L2_CID_MAX2175_HSLS:
+		max2175_set_hsls(ctx, ctx->hsls->val);
+		break;
+	case V4L2_CID_MAX2175_RX_MODE:
+		max2175_s_ctrl_rx_mode(ctx, ctrl->val);
+		break;
+	}
+
+	return ret;
+}
+
+static int max2175_get_lna_gain(struct max2175 *ctx)
+{
+	int gain = 0;
+	enum max2175_band band = max2175_read_bits(ctx, 5, 1, 0);
+
+	switch (band) {
+	case MAX2175_BAND_AM:
+		gain = max2175_read_bits(ctx, 51, 3, 1);
+		break;
+	case MAX2175_BAND_FM:
+		gain = max2175_read_bits(ctx, 50, 3, 1);
+		break;
+	case MAX2175_BAND_VHF:
+		gain = max2175_read_bits(ctx, 52, 3, 0);
+		break;
+	default:
+		break;
+	}
+	return gain;
+}
+
+static int max2175_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct max2175 *ctx = max2175_from_ctrl_hdl(ctrl->handler);
+
+	switch (ctrl->id) {
+	case V4L2_CID_RF_TUNER_LNA_GAIN:
+		ctrl->val = max2175_get_lna_gain(ctx);
+		break;
+	case V4L2_CID_RF_TUNER_IF_GAIN:
+		ctrl->val = max2175_read_bits(ctx, 49, 4, 0);
+		break;
+	case V4L2_CID_RF_TUNER_PLL_LOCK:
+		ctrl->val = (max2175_read_bits(ctx, 60, 7, 6) == 3);
+		break;
+	}
+	mxm_dbg(ctx, "g_volatile_ctrl: id 0x%x val %d\n", ctrl->id, ctrl->val);
+	return 0;
+};
+
+static int max2175_set_freq_and_mode(struct max2175 *ctx, u32 freq)
+{
+	u32 rx_mode;
+	int ret;
+
+	/* Get band from frequency */
+	ret = max2175_rx_mode_from_freq(ctx, freq, &rx_mode);
+	if (ret)
+		return ret;
+
+	mxm_dbg(ctx, "set_freq_and_mode: freq %u rx_mode %d\n", freq, rx_mode);
+
+	/* Load mode */
+	max2175_set_rx_mode(ctx, rx_mode, ctx->hsls->val);
+	ctx->rx_mode->val = rx_mode;
+
+	/* Get current i2s_mode and update if needed for given rx_mode */
+	if (max2175_i2s_rx_mode_valid(ctx, rx_mode, ctx->i2s_mode->val))
+		max2175_update_i2s_mode(ctx, rx_mode, ctx->i2s_mode->val);
+	else
+		ctx->i2s_mode->val = max2175_read_bits(ctx, 29, 2, 0);
+
+	/* Tune to the new freq given */
+	return max2175_tune_rf_freq(ctx, freq, ctx->hsls->val);
+}
+
+static int max2175_s_frequency(struct v4l2_subdev *sd,
+			       const struct v4l2_frequency *vf)
+{
+	struct max2175 *ctx = max2175_from_sd(sd);
+	u32 freq;
+	int ret = 0;
+
+	mxm_dbg(ctx, "s_freq: new %u curr %u, mode_resolved %d\n",
+		vf->frequency, ctx->freq, ctx->mode_resolved);
+
+	if (vf->tuner != 0)
+		return -EINVAL;
+
+	freq = clamp(vf->frequency, ctx->bands_rf->rangelow,
+		     ctx->bands_rf->rangehigh);
+
+	/* Check new freq valid for rx_mode if already resolved */
+	if (ctx->mode_resolved &&
+	    max2175_freq_rx_mode_valid(ctx, ctx->rx_mode->val, freq))
+		ret = max2175_tune_rf_freq(ctx, freq, ctx->hsls->val);
+	else
+		/* Find default rx_mode for freq and tune to it */
+		ret = max2175_set_freq_and_mode(ctx, freq);
+
+	mxm_dbg(ctx, "s_freq: ret %d curr %u mode_resolved %d mode %u\n",
+		ret, ctx->freq, ctx->mode_resolved, ctx->rx_mode->val);
+	return ret;
+}
+
+static int max2175_g_frequency(struct v4l2_subdev *sd,
+			       struct v4l2_frequency *vf)
+{
+	struct max2175 *ctx = max2175_from_sd(sd);
+	int ret = 0;
+
+	if (vf->tuner != 0)
+		return -EINVAL;
+
+	/* RF freq */
+	vf->type = V4L2_TUNER_RF;
+	vf->frequency = ctx->freq;
+	return ret;
+}
+
+static int max2175_enum_freq_bands(struct v4l2_subdev *sd,
+			    struct v4l2_frequency_band *band)
+{
+	struct max2175 *ctx = max2175_from_sd(sd);
+
+	if (band->tuner == 0 && band->index == 0)
+		*band = *ctx->bands_rf;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int max2175_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+	struct max2175 *ctx = max2175_from_sd(sd);
+
+	if (vt->index > 0)
+		return -EINVAL;
+
+	strlcpy(vt->name, "RF", sizeof(vt->name));
+	vt->type = V4L2_TUNER_RF;
+	vt->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
+	vt->rangelow = ctx->bands_rf->rangelow;
+	vt->rangehigh = ctx->bands_rf->rangehigh;
+	return 0;
+}
+
+static int max2175_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt)
+{
+	/* Check tuner index is valid */
+	if (vt->index > 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+static const struct v4l2_subdev_tuner_ops max2175_tuner_ops = {
+	.s_frequency = max2175_s_frequency,
+	.g_frequency = max2175_g_frequency,
+	.enum_freq_bands = max2175_enum_freq_bands,
+	.g_tuner = max2175_g_tuner,
+	.s_tuner = max2175_s_tuner,
+};
+
+static const struct v4l2_subdev_ops max2175_ops = {
+	.tuner = &max2175_tuner_ops,
+};
+
+static const struct v4l2_ctrl_ops max2175_ctrl_ops = {
+	.s_ctrl = max2175_s_ctrl,
+	.g_volatile_ctrl = max2175_g_volatile_ctrl,
+};
+
+static const struct v4l2_ctrl_config max2175_i2s_en = {
+	.ops = &max2175_ctrl_ops,
+	.id = V4L2_CID_MAX2175_I2S_ENABLE,
+	.name = "I2S Enable",
+	.type = V4L2_CTRL_TYPE_BOOLEAN,
+	.min = 0,
+	.max = 1,
+	.step = 1,
+	.def = 1,
+};
+
+static const char * const max2175_ctrl_i2s_modes[] = {
+	[MAX2175_I2S_MODE0]	= "i2s mode 0",
+	[MAX2175_I2S_MODE1]	= "i2s mode 1 (skipped)",
+	[MAX2175_I2S_MODE2]	= "i2s mode 2",
+	[MAX2175_I2S_MODE3]	= "i2s mode 3",
+	[MAX2175_I2S_MODE4]	= "i2s mode 4",
+};
+
+static const struct v4l2_ctrl_config max2175_i2s_mode = {
+	.ops = &max2175_ctrl_ops,
+	.id = V4L2_CID_MAX2175_I2S_MODE,
+	.name = "I2S MODE value",
+	.type = V4L2_CTRL_TYPE_MENU,
+	.max = ARRAY_SIZE(max2175_ctrl_i2s_modes) - 1,
+	.def = 0,
+	.menu_skip_mask = 0x02,
+	.qmenu = max2175_ctrl_i2s_modes,
+};
+
+static const struct v4l2_ctrl_config max2175_hsls = {
+	.ops = &max2175_ctrl_ops,
+	.id = V4L2_CID_MAX2175_HSLS,
+	.name = "HSLS above/below desired",
+	.type = V4L2_CTRL_TYPE_INTEGER,
+	.min = 0,
+	.max = 1,
+	.step = 1,
+	.def = 1,
+};
+
+static const char * const max2175_ctrl_eu_rx_modes[] = {
+	[MAX2175_EU_FM_1_2]	= "EU FM 1.2",
+	[MAX2175_DAB_1_2]	= "DAB 1.2",
+};
+
+static const char * const max2175_ctrl_na_rx_modes[] = {
+	[MAX2175_NA_FM_1_0]	= "NA FM 1.0",
+	[MAX2175_NA_FM_2_0]	= "NA FM 2.0",
+};
+
+static const struct v4l2_ctrl_config max2175_eu_rx_mode = {
+	.ops = &max2175_ctrl_ops,
+	.id = V4L2_CID_MAX2175_RX_MODE,
+	.name = "RX MODE",
+	.type = V4L2_CTRL_TYPE_MENU,
+	.max = ARRAY_SIZE(max2175_ctrl_eu_rx_modes) - 1,
+	.def = 0,
+	.qmenu = max2175_ctrl_eu_rx_modes,
+};
+
+static const struct v4l2_ctrl_config max2175_na_rx_mode = {
+	.ops = &max2175_ctrl_ops,
+	.id = V4L2_CID_MAX2175_RX_MODE,
+	.name = "RX MODE",
+	.type = V4L2_CTRL_TYPE_MENU,
+	.max = ARRAY_SIZE(max2175_ctrl_na_rx_modes) - 1,
+	.def = 0,
+	.qmenu = max2175_ctrl_na_rx_modes,
+};
+
+static int max2175_refout_load_to_bits(struct i2c_client *client, u32 load,
+				       u32 *bits)
+{
+	if (load >= 0 && load <= 40)
+		*bits = load / 10;
+	else if (load >= 60 && load <= 70)
+		*bits = load / 10 - 1;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int max2175_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct max2175 *ctx;
+	struct v4l2_subdev *sd;
+	struct v4l2_ctrl_handler *hdl;
+	struct clk *clk;
+	struct regmap *regmap;
+	bool master = true, am_hiz = false;
+	u32 refout_load, refout_bits = 0;	/* REFOUT disabled */
+	int ret;
+
+	/* Parse DT properties */
+	if (of_find_property(client->dev.of_node, "maxim,slave", NULL))
+		master = false;
+
+	if (of_find_property(client->dev.of_node, "maxim,am-hiz", NULL))
+		am_hiz = true;
+
+	if (!of_property_read_u32(client->dev.of_node, "maxim,refout-load-pF",
+				  &refout_load)) {
+		ret = max2175_refout_load_to_bits(client, refout_load,
+						  &refout_bits);
+		if (ret) {
+			dev_err(&client->dev, "invalid refout_load %u\n",
+				refout_load);
+			return -EINVAL;
+		}
+	}
+
+	clk = devm_clk_get(&client->dev, "xtal");
+	if (IS_ERR(clk)) {
+		ret = PTR_ERR(clk);
+		dev_err(&client->dev, "cannot get xtal clock %d\n", ret);
+		return -ENODEV;
+	}
+
+	regmap = devm_regmap_init_i2c(client, &max2175_regmap_config);
+	if (IS_ERR(regmap)) {
+		ret = PTR_ERR(regmap);
+		dev_err(&client->dev, "regmap init failed %d\n", ret);
+		return -ENODEV;
+	}
+
+	/* Alloc tuner context */
+	ctx = devm_kzalloc(&client->dev, sizeof(*ctx), GFP_KERNEL);
+	if (ctx == NULL)
+		return -ENOMEM;
+
+	sd = &ctx->sd;
+	ctx->master = master;
+	ctx->am_hiz = am_hiz;
+	ctx->mode_resolved = false;
+	ctx->regmap = regmap;
+	ctx->xtal_freq = clk_get_rate(clk);
+	dev_info(&client->dev, "xtal freq %luHz\n", ctx->xtal_freq);
+
+	v4l2_i2c_subdev_init(sd, client, &max2175_ops);
+	ctx->client = client;
+
+	sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	/* Controls */
+	hdl = &ctx->ctrl_hdl;
+	ret = v4l2_ctrl_handler_init(hdl, 7);
+	if (ret) {
+		dev_err(&client->dev, "ctrl handler init failed\n");
+		goto err;
+	}
+
+	ctx->lna_gain = v4l2_ctrl_new_std(hdl, &max2175_ctrl_ops,
+					  V4L2_CID_RF_TUNER_LNA_GAIN,
+					  0, 15, 1, 2);
+	ctx->lna_gain->flags |= (V4L2_CTRL_FLAG_VOLATILE |
+				 V4L2_CTRL_FLAG_READ_ONLY);
+	ctx->if_gain = v4l2_ctrl_new_std(hdl, &max2175_ctrl_ops,
+					 V4L2_CID_RF_TUNER_IF_GAIN,
+					 0, 31, 1, 0);
+	ctx->if_gain->flags |= (V4L2_CTRL_FLAG_VOLATILE |
+				V4L2_CTRL_FLAG_READ_ONLY);
+	ctx->pll_lock = v4l2_ctrl_new_std(hdl, &max2175_ctrl_ops,
+					  V4L2_CID_RF_TUNER_PLL_LOCK,
+					  0, 1, 1, 0);
+	ctx->pll_lock->flags |= (V4L2_CTRL_FLAG_VOLATILE |
+				 V4L2_CTRL_FLAG_READ_ONLY);
+	ctx->i2s_en = v4l2_ctrl_new_custom(hdl, &max2175_i2s_en, NULL);
+	ctx->i2s_mode = v4l2_ctrl_new_custom(hdl, &max2175_i2s_mode, NULL);
+	ctx->hsls = v4l2_ctrl_new_custom(hdl, &max2175_hsls, NULL);
+
+	if (ctx->xtal_freq == MAX2175_EU_XTAL_FREQ) {
+		ctx->rx_mode = v4l2_ctrl_new_custom(hdl,
+						    &max2175_eu_rx_mode, NULL);
+		ctx->rx_modes = eu_rx_modes;
+		ctx->bands_rf = &eu_bands_rf;
+	} else {
+		ctx->rx_mode = v4l2_ctrl_new_custom(hdl,
+						    &max2175_na_rx_mode, NULL);
+		ctx->rx_modes = na_rx_modes;
+		ctx->bands_rf = &na_bands_rf;
+	}
+	ctx->sd.ctrl_handler = &ctx->ctrl_hdl;
+
+	/* Set the defaults */
+	ctx->freq = ctx->bands_rf->rangelow;
+
+	/* Register subdev */
+	ret = v4l2_async_register_subdev(sd);
+	if (ret) {
+		dev_err(&client->dev, "register subdev failed\n");
+		goto err_reg;
+	}
+
+	/* Initialize device */
+	ret = max2175_core_init(ctx, refout_bits);
+	if (ret)
+		goto err_init;
+
+	dev_info(&client->dev, "probed\n");
+	return 0;
+
+err_init:
+	v4l2_async_unregister_subdev(sd);
+err_reg:
+	v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
+err:
+	return ret;
+}
+
+static int max2175_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct max2175 *ctx = max2175_from_sd(sd);
+
+	v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
+	v4l2_async_unregister_subdev(sd);
+	dev_info(&client->dev, "removed\n");
+	return 0;
+}
+
+static const struct i2c_device_id max2175_id[] = {
+	{ DRIVER_NAME, 0},
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, max2175_id);
+
+static const struct of_device_id max2175_of_ids[] = {
+	{ .compatible = "maxim, max2175", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, max2175_of_ids);
+
+static struct i2c_driver max2175_driver = {
+	.driver = {
+		.name	= DRIVER_NAME,
+		.of_match_table = max2175_of_ids,
+	},
+	.probe		= max2175_probe,
+	.remove		= max2175_remove,
+	.id_table	= max2175_id,
+};
+
+module_i2c_driver(max2175_driver);
+
+MODULE_DESCRIPTION("Maxim MAX2175 RF to Bits tuner driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>");
diff --git a/drivers/media/i2c/max2175/max2175.h b/drivers/media/i2c/max2175/max2175.h
new file mode 100644
index 0000000..2d858aa
--- /dev/null
+++ b/drivers/media/i2c/max2175/max2175.h
@@ -0,0 +1,108 @@
+/*
+ * Maxim Integrated MAX2175 RF to Bits tuner driver
+ *
+ * This driver & most of the hard coded values are based on the reference
+ * application delivered by Maxim for this chip.
+ *
+ * Copyright (C) 2016 Maxim Integrated Products
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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 __MAX2175_H__
+#define __MAX2175_H__
+
+#define MAX2175_EU_XTAL_FREQ	36864000	/* In Hz */
+#define MAX2175_NA_XTAL_FREQ	40186125	/* In Hz */
+
+enum max2175_region {
+	MAX2175_REGION_EU = 0,	/* Europe */
+	MAX2175_REGION_NA,	/* North America */
+};
+
+
+enum max2175_band {
+	MAX2175_BAND_AM = 0,
+	MAX2175_BAND_FM,
+	MAX2175_BAND_VHF,
+	MAX2175_BAND_L,
+};
+
+enum max2175_eu_mode {
+	/* EU modes */
+	MAX2175_EU_FM_1_2 = 0,
+	MAX2175_DAB_1_2,
+
+	/* Other possible modes to add in future
+	 * MAX2175_DAB_1_0,
+	 * MAX2175_DAB_1_3,
+	 * MAX2175_EU_FM_2_2,
+	 * MAX2175_EU_FMHD_4_0,
+	 * MAX2175_EU_AM_1_0,
+	 * MAX2175_EU_AM_2_2,
+	 */
+};
+
+enum max2175_na_mode {
+	/* NA modes */
+	MAX2175_NA_FM_1_0 = 0,
+	MAX2175_NA_FM_2_0,
+
+	/* Other possible modes to add in future
+	 * MAX2175_NA_FMHD_1_0,
+	 * MAX2175_NA_FMHD_1_2,
+	 * MAX2175_NA_AM_1_0,
+	 * MAX2175_NA_AM_1_2,
+	 */
+};
+
+/* Supported I2S modes */
+enum {
+	MAX2175_I2S_MODE0 = 0,
+	MAX2175_I2S_MODE1,
+	MAX2175_I2S_MODE2,
+	MAX2175_I2S_MODE3,
+	MAX2175_I2S_MODE4,
+};
+
+/* Coefficient table groups */
+enum {
+	MAX2175_CH_MSEL = 0,
+	MAX2175_EQ_MSEL,
+	MAX2175_AA_MSEL,
+};
+
+/* HSLS LO injection polarity */
+enum {
+	MAX2175_LO_BELOW_DESIRED = 0,
+	MAX2175_LO_ABOVE_DESIRED,
+};
+
+/* Channel FSM modes */
+enum max2175_csm_mode {
+	MAX2175_LOAD_TO_BUFFER = 0,
+	MAX2175_PRESET_TUNE,
+	MAX2175_SEARCH,
+	MAX2175_AF_UPDATE,
+	MAX2175_JUMP_FAST_TUNE,
+	MAX2175_CHECK,
+	MAX2175_LOAD_AND_SWAP,
+	MAX2175_END,
+	MAX2175_BUFFER_PLUS_PRESET_TUNE,
+	MAX2175_BUFFER_PLUS_SEARCH,
+	MAX2175_BUFFER_PLUS_AF_UPDATE,
+	MAX2175_BUFFER_PLUS_JUMP_FAST_TUNE,
+	MAX2175_BUFFER_PLUS_CHECK,
+	MAX2175_BUFFER_PLUS_LOAD_AND_SWAP,
+	MAX2175_NO_ACTION
+};
+
+#endif /* __MAX2175_H__ */
-- 
1.9.1


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

* [PATCH 3/5] media: Add new SDR formats SC16, SC18 & SC20
  2016-11-09 15:44 ` Ramesh Shanmugasundaram
  (?)
  (?)
@ 2016-11-09 15:44 ` Ramesh Shanmugasundaram
  2016-11-11 13:24   ` Hans Verkuil
  -1 siblings, 1 reply; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-11-09 15:44 UTC (permalink / raw)
  To: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope
  Cc: chris.paterson2, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc, Ramesh Shanmugasundaram

This patch adds support for the three new SDR formats. These formats
were prefixed with "sliced" indicating I data constitutes the top half and
Q data constitutes the bottom half of the received buffer.

V4L2_SDR_FMT_SCU16BE - 14-bit complex (I & Q) unsigned big-endian sample
inside 16-bit. V4L2 FourCC: SC16

V4L2_SDR_FMT_SCU18BE - 16-bit complex (I & Q) unsigned big-endian sample
inside 18-bit. V4L2 FourCC: SC18

V4L2_SDR_FMT_SCU20BE - 18-bit complex (I & Q) unsigned big-endian sample
inside 20-bit. V4L2 FourCC: SC20

Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>
---
 drivers/media/v4l2-core/v4l2-ioctl.c | 3 +++
 include/uapi/linux/videodev2.h       | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 181381d..d36b386 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1207,6 +1207,9 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
 	case V4L2_SDR_FMT_CS8:		descr = "Complex S8"; break;
 	case V4L2_SDR_FMT_CS14LE:	descr = "Complex S14LE"; break;
 	case V4L2_SDR_FMT_RU12LE:	descr = "Real U12LE"; break;
+	case V4L2_SDR_FMT_SCU16BE:	descr = "Sliced Complex U16BE"; break;
+	case V4L2_SDR_FMT_SCU18BE:	descr = "Sliced Complex U18BE"; break;
+	case V4L2_SDR_FMT_SCU20BE:	descr = "Sliced Complex U20BE"; break;
 	case V4L2_TCH_FMT_DELTA_TD16:	descr = "16-bit signed deltas"; break;
 	case V4L2_TCH_FMT_DELTA_TD08:	descr = "8-bit signed deltas"; break;
 	case V4L2_TCH_FMT_TU16:		descr = "16-bit unsigned touch data"; break;
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 4364ce6..34a9c30 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -666,6 +666,9 @@ struct v4l2_pix_format {
 #define V4L2_SDR_FMT_CS8          v4l2_fourcc('C', 'S', '0', '8') /* complex s8 */
 #define V4L2_SDR_FMT_CS14LE       v4l2_fourcc('C', 'S', '1', '4') /* complex s14le */
 #define V4L2_SDR_FMT_RU12LE       v4l2_fourcc('R', 'U', '1', '2') /* real u12le */
+#define V4L2_SDR_FMT_SCU16BE	  v4l2_fourcc('S', 'C', '1', '6') /* sliced complex u16be */
+#define V4L2_SDR_FMT_SCU18BE	  v4l2_fourcc('S', 'C', '1', '8') /* sliced complex u18be */
+#define V4L2_SDR_FMT_SCU20BE	  v4l2_fourcc('S', 'C', '2', '0') /* sliced complex u20be */
 
 /* Touch formats - used for Touch devices */
 #define V4L2_TCH_FMT_DELTA_TD16	v4l2_fourcc('T', 'D', '1', '6') /* 16-bit signed deltas */
-- 
1.9.1

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

* [PATCH 4/5] doc_rst: media: New SDR formats SC16, SC18 & SC20
  2016-11-09 15:44 ` Ramesh Shanmugasundaram
@ 2016-11-09 15:44     ` Ramesh Shanmugasundaram
  -1 siblings, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-11-09 15:44 UTC (permalink / raw)
  To: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	mchehab-DgEjT+Ai2ygdnm+yROfE0A, hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
	sakari.ailus-VuQAYsv1563Yd54FQh9/CA, crope-X3B1VOXEql0
  Cc: chris.paterson2-zM6kxYcvzFBBDgjK7y7TUQ,
	laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
	geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ,
	linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA,
	Ramesh Shanmugasundaram

This patch adds documentation for the three new SDR formats

V4L2_SDR_FMT_SCU16BE
V4L2_SDR_FMT_SCU18BE
V4L2_SDR_FMT_SCU20BE

Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>
---
 .../media/uapi/v4l/pixfmt-sdr-scu16be.rst          | 80 ++++++++++++++++++++++
 .../media/uapi/v4l/pixfmt-sdr-scu18be.rst          | 80 ++++++++++++++++++++++
 .../media/uapi/v4l/pixfmt-sdr-scu20be.rst          | 80 ++++++++++++++++++++++
 Documentation/media/uapi/v4l/sdr-formats.rst       |  3 +
 4 files changed, 243 insertions(+)
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-scu16be.rst
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-scu18be.rst
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-scu20be.rst

diff --git a/Documentation/media/uapi/v4l/pixfmt-sdr-scu16be.rst b/Documentation/media/uapi/v4l/pixfmt-sdr-scu16be.rst
new file mode 100644
index 0000000..7525378
--- /dev/null
+++ b/Documentation/media/uapi/v4l/pixfmt-sdr-scu16be.rst
@@ -0,0 +1,80 @@
+.. -*- coding: utf-8; mode: rst -*-
+
+.. _V4L2-SDR-FMT-SCU16BE:
+
+******************************
+V4L2_SDR_FMT_SCU16BE ('SC16')
+******************************
+
+Sliced complex unsigned 16-bit big endian IQ sample
+
+Description
+===========
+
+This format contains a sequence of complex number samples. Each complex
+number consist of two parts called In-phase and Quadrature (IQ). Both I
+and Q are represented as a 16 bit unsigned big endian number stored in
+32 bit space. The remaining unused bits within the 32 bit space will be
+padded with 0. I value starts first and Q value starts at an offset
+equalling half of the buffer size (i.e.) offset = buffersize/2. Out of
+the 16 bits, bit 15:2 (14 bit) is data and bit 1:0 (2 bit) can be any
+value.
+
+**Byte Order.**
+Each cell is one byte.
+
+.. flat-table::
+    :header-rows:  1
+    :stub-columns: 0
+
+    * -  Offset:
+
+      -  Byte B0
+
+      -  Byte B1
+
+      -  Byte B2
+
+      -  Byte B3
+
+    * -  start + 0:
+
+      -  I'\ :sub:`0[13:6]`
+
+      -  I'\ :sub:`0[5:0]; B1[1:0]=pad`
+
+      -  pad
+
+      -  pad
+
+    * -  start + 4:
+
+      -  I'\ :sub:`1[13:6]`
+
+      -  I'\ :sub:`1[5:0]; B1[1:0]=pad`
+
+      -  pad
+
+      -  pad
+
+    * -  ...
+
+    * - start + offset:
+
+      -  Q'\ :sub:`0[13:6]`
+
+      -  Q'\ :sub:`0[5:0]; B1[1:0]=pad`
+
+      -  pad
+
+      -  pad
+
+    * - start + offset + 4:
+
+      -  Q'\ :sub:`1[13:6]`
+
+      -  Q'\ :sub:`1[5:0]; B1[1:0]=pad`
+
+      -  pad
+
+      -  pad
diff --git a/Documentation/media/uapi/v4l/pixfmt-sdr-scu18be.rst b/Documentation/media/uapi/v4l/pixfmt-sdr-scu18be.rst
new file mode 100644
index 0000000..0ce714d
--- /dev/null
+++ b/Documentation/media/uapi/v4l/pixfmt-sdr-scu18be.rst
@@ -0,0 +1,80 @@
+.. -*- coding: utf-8; mode: rst -*-
+
+.. _V4L2-SDR-FMT-SCU18BE:
+
+******************************
+V4L2_SDR_FMT_SCU18BE ('SC18')
+******************************
+
+Sliced complex unsigned 18-bit big endian IQ sample
+
+Description
+===========
+
+This format contains a sequence of complex number samples. Each complex
+number consist of two parts called In-phase and Quadrature (IQ). Both I
+and Q are represented as a 18 bit unsigned big endian number stored in
+32 bit space. The remaining unused bits within the 32 bit space will be
+padded with 0. I value starts first and Q value starts at an offset
+equalling half of the buffer size (i.e.) offset = buffersize/2. Out of
+the 18 bits, bit 17:2 (16 bit) is data and bit 1:0 (2 bit) can be any
+value.
+
+**Byte Order.**
+Each cell is one byte.
+
+.. flat-table::
+    :header-rows:  1
+    :stub-columns: 0
+
+    * -  Offset:
+
+      -  Byte B0
+
+      -  Byte B1
+
+      -  Byte B2
+
+      -  Byte B3
+
+    * -  start + 0:
+
+      -  I'\ :sub:`0[17:10]`
+
+      -  I'\ :sub:`0[9:2]`
+
+      -  I'\ :sub:`0[1:0]; B2[5:0]=pad`
+
+      -  pad
+
+    * -  start + 4:
+
+      -  I'\ :sub:`1[17:10]`
+
+      -  I'\ :sub:`1[9:2]`
+
+      -  I'\ :sub:`1[1:0]; B2[5:0]=pad`
+
+      -  pad
+
+    * -  ...
+
+    * - start + offset:
+
+      -  Q'\ :sub:`0[17:10]`
+
+      -  Q'\ :sub:`0[9:2]`
+
+      -  Q'\ :sub:`0[1:0]; B2[5:0]=pad`
+
+      -  pad
+
+    * - start + offset + 4:
+
+      -  Q'\ :sub:`1[17:10]`
+
+      -  Q'\ :sub:`1[9:2]`
+
+      -  Q'\ :sub:`1[1:0]; B2[5:0]=pad`
+
+      -  pad
diff --git a/Documentation/media/uapi/v4l/pixfmt-sdr-scu20be.rst b/Documentation/media/uapi/v4l/pixfmt-sdr-scu20be.rst
new file mode 100644
index 0000000..ff2fe51
--- /dev/null
+++ b/Documentation/media/uapi/v4l/pixfmt-sdr-scu20be.rst
@@ -0,0 +1,80 @@
+.. -*- coding: utf-8; mode: rst -*-
+
+.. _V4L2-SDR-FMT-SCU20BE:
+
+******************************
+V4L2_SDR_FMT_SCU20BE ('SC20')
+******************************
+
+Sliced complex unsigned 20-bit big endian IQ sample
+
+Description
+===========
+
+This format contains a sequence of complex number samples. Each complex
+number consist of two parts called In-phase and Quadrature (IQ). Both I
+and Q are represented as a 20 bit unsigned big endian number stored in
+32 bit space. The remaining unused bits within the 32 bit space will be
+padded with 0. I value starts first and Q value starts at an offset
+equalling half of the buffer size (i.e.) offset = buffersize/2. Out of
+the 20 bits, bit 19:2 (18 bit) is data and bit 1:0 (2 bit) can be any
+value.
+
+**Byte Order.**
+Each cell is one byte.
+
+.. flat-table::
+    :header-rows:  1
+    :stub-columns: 0
+
+    * -  Offset:
+
+      -  Byte B0
+
+      -  Byte B1
+
+      -  Byte B2
+
+      -  Byte B3
+
+    * -  start + 0:
+
+      -  I'\ :sub:`0[19:12]`
+
+      -  I'\ :sub:`0[11:4]`
+
+      -  I'\ :sub:`0[3:0]; B2[3:0]=pad`
+
+      -  pad
+
+    * -  start + 4:
+
+      -  I'\ :sub:`1[19:12]`
+
+      -  I'\ :sub:`1[11:4]`
+
+      -  I'\ :sub:`1[3:0]; B2[3:0]=pad`
+
+      -  pad
+
+    * -  ...
+
+    * - start + offset:
+
+      -  Q'\ :sub:`0[19:12]`
+
+      -  Q'\ :sub:`0[11:4]`
+
+      -  Q'\ :sub:`0[3:0]; B2[3:0]=pad`
+
+      -  pad
+
+    * - start + offset + 4:
+
+      -  Q'\ :sub:`1[19:12]`
+
+      -  Q'\ :sub:`1[11:4]`
+
+      -  Q'\ :sub:`1[3:0]; B2[3:0]=pad`
+
+      -  pad
diff --git a/Documentation/media/uapi/v4l/sdr-formats.rst b/Documentation/media/uapi/v4l/sdr-formats.rst
index f863c08..4c01cf9 100644
--- a/Documentation/media/uapi/v4l/sdr-formats.rst
+++ b/Documentation/media/uapi/v4l/sdr-formats.rst
@@ -17,3 +17,6 @@ These formats are used for :ref:`SDR <sdr>` interface only.
     pixfmt-sdr-cs08
     pixfmt-sdr-cs14le
     pixfmt-sdr-ru12le
+    pixfmt-sdr-scu16be
+    pixfmt-sdr-scu18be
+    pixfmt-sdr-scu20be
-- 
1.9.1

--
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 related	[flat|nested] 60+ messages in thread

* [PATCH 4/5] doc_rst: media: New SDR formats SC16, SC18 & SC20
@ 2016-11-09 15:44     ` Ramesh Shanmugasundaram
  0 siblings, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-11-09 15:44 UTC (permalink / raw)
  To: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope
  Cc: chris.paterson2, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc, Ramesh Shanmugasundaram

This patch adds documentation for the three new SDR formats

V4L2_SDR_FMT_SCU16BE
V4L2_SDR_FMT_SCU18BE
V4L2_SDR_FMT_SCU20BE

Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>
---
 .../media/uapi/v4l/pixfmt-sdr-scu16be.rst          | 80 ++++++++++++++++++++++
 .../media/uapi/v4l/pixfmt-sdr-scu18be.rst          | 80 ++++++++++++++++++++++
 .../media/uapi/v4l/pixfmt-sdr-scu20be.rst          | 80 ++++++++++++++++++++++
 Documentation/media/uapi/v4l/sdr-formats.rst       |  3 +
 4 files changed, 243 insertions(+)
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-scu16be.rst
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-scu18be.rst
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-scu20be.rst

diff --git a/Documentation/media/uapi/v4l/pixfmt-sdr-scu16be.rst b/Documentation/media/uapi/v4l/pixfmt-sdr-scu16be.rst
new file mode 100644
index 0000000..7525378
--- /dev/null
+++ b/Documentation/media/uapi/v4l/pixfmt-sdr-scu16be.rst
@@ -0,0 +1,80 @@
+.. -*- coding: utf-8; mode: rst -*-
+
+.. _V4L2-SDR-FMT-SCU16BE:
+
+******************************
+V4L2_SDR_FMT_SCU16BE ('SC16')
+******************************
+
+Sliced complex unsigned 16-bit big endian IQ sample
+
+Description
+===========
+
+This format contains a sequence of complex number samples. Each complex
+number consist of two parts called In-phase and Quadrature (IQ). Both I
+and Q are represented as a 16 bit unsigned big endian number stored in
+32 bit space. The remaining unused bits within the 32 bit space will be
+padded with 0. I value starts first and Q value starts at an offset
+equalling half of the buffer size (i.e.) offset = buffersize/2. Out of
+the 16 bits, bit 15:2 (14 bit) is data and bit 1:0 (2 bit) can be any
+value.
+
+**Byte Order.**
+Each cell is one byte.
+
+.. flat-table::
+    :header-rows:  1
+    :stub-columns: 0
+
+    * -  Offset:
+
+      -  Byte B0
+
+      -  Byte B1
+
+      -  Byte B2
+
+      -  Byte B3
+
+    * -  start + 0:
+
+      -  I'\ :sub:`0[13:6]`
+
+      -  I'\ :sub:`0[5:0]; B1[1:0]=pad`
+
+      -  pad
+
+      -  pad
+
+    * -  start + 4:
+
+      -  I'\ :sub:`1[13:6]`
+
+      -  I'\ :sub:`1[5:0]; B1[1:0]=pad`
+
+      -  pad
+
+      -  pad
+
+    * -  ...
+
+    * - start + offset:
+
+      -  Q'\ :sub:`0[13:6]`
+
+      -  Q'\ :sub:`0[5:0]; B1[1:0]=pad`
+
+      -  pad
+
+      -  pad
+
+    * - start + offset + 4:
+
+      -  Q'\ :sub:`1[13:6]`
+
+      -  Q'\ :sub:`1[5:0]; B1[1:0]=pad`
+
+      -  pad
+
+      -  pad
diff --git a/Documentation/media/uapi/v4l/pixfmt-sdr-scu18be.rst b/Documentation/media/uapi/v4l/pixfmt-sdr-scu18be.rst
new file mode 100644
index 0000000..0ce714d
--- /dev/null
+++ b/Documentation/media/uapi/v4l/pixfmt-sdr-scu18be.rst
@@ -0,0 +1,80 @@
+.. -*- coding: utf-8; mode: rst -*-
+
+.. _V4L2-SDR-FMT-SCU18BE:
+
+******************************
+V4L2_SDR_FMT_SCU18BE ('SC18')
+******************************
+
+Sliced complex unsigned 18-bit big endian IQ sample
+
+Description
+===========
+
+This format contains a sequence of complex number samples. Each complex
+number consist of two parts called In-phase and Quadrature (IQ). Both I
+and Q are represented as a 18 bit unsigned big endian number stored in
+32 bit space. The remaining unused bits within the 32 bit space will be
+padded with 0. I value starts first and Q value starts at an offset
+equalling half of the buffer size (i.e.) offset = buffersize/2. Out of
+the 18 bits, bit 17:2 (16 bit) is data and bit 1:0 (2 bit) can be any
+value.
+
+**Byte Order.**
+Each cell is one byte.
+
+.. flat-table::
+    :header-rows:  1
+    :stub-columns: 0
+
+    * -  Offset:
+
+      -  Byte B0
+
+      -  Byte B1
+
+      -  Byte B2
+
+      -  Byte B3
+
+    * -  start + 0:
+
+      -  I'\ :sub:`0[17:10]`
+
+      -  I'\ :sub:`0[9:2]`
+
+      -  I'\ :sub:`0[1:0]; B2[5:0]=pad`
+
+      -  pad
+
+    * -  start + 4:
+
+      -  I'\ :sub:`1[17:10]`
+
+      -  I'\ :sub:`1[9:2]`
+
+      -  I'\ :sub:`1[1:0]; B2[5:0]=pad`
+
+      -  pad
+
+    * -  ...
+
+    * - start + offset:
+
+      -  Q'\ :sub:`0[17:10]`
+
+      -  Q'\ :sub:`0[9:2]`
+
+      -  Q'\ :sub:`0[1:0]; B2[5:0]=pad`
+
+      -  pad
+
+    * - start + offset + 4:
+
+      -  Q'\ :sub:`1[17:10]`
+
+      -  Q'\ :sub:`1[9:2]`
+
+      -  Q'\ :sub:`1[1:0]; B2[5:0]=pad`
+
+      -  pad
diff --git a/Documentation/media/uapi/v4l/pixfmt-sdr-scu20be.rst b/Documentation/media/uapi/v4l/pixfmt-sdr-scu20be.rst
new file mode 100644
index 0000000..ff2fe51
--- /dev/null
+++ b/Documentation/media/uapi/v4l/pixfmt-sdr-scu20be.rst
@@ -0,0 +1,80 @@
+.. -*- coding: utf-8; mode: rst -*-
+
+.. _V4L2-SDR-FMT-SCU20BE:
+
+******************************
+V4L2_SDR_FMT_SCU20BE ('SC20')
+******************************
+
+Sliced complex unsigned 20-bit big endian IQ sample
+
+Description
+===========
+
+This format contains a sequence of complex number samples. Each complex
+number consist of two parts called In-phase and Quadrature (IQ). Both I
+and Q are represented as a 20 bit unsigned big endian number stored in
+32 bit space. The remaining unused bits within the 32 bit space will be
+padded with 0. I value starts first and Q value starts at an offset
+equalling half of the buffer size (i.e.) offset = buffersize/2. Out of
+the 20 bits, bit 19:2 (18 bit) is data and bit 1:0 (2 bit) can be any
+value.
+
+**Byte Order.**
+Each cell is one byte.
+
+.. flat-table::
+    :header-rows:  1
+    :stub-columns: 0
+
+    * -  Offset:
+
+      -  Byte B0
+
+      -  Byte B1
+
+      -  Byte B2
+
+      -  Byte B3
+
+    * -  start + 0:
+
+      -  I'\ :sub:`0[19:12]`
+
+      -  I'\ :sub:`0[11:4]`
+
+      -  I'\ :sub:`0[3:0]; B2[3:0]=pad`
+
+      -  pad
+
+    * -  start + 4:
+
+      -  I'\ :sub:`1[19:12]`
+
+      -  I'\ :sub:`1[11:4]`
+
+      -  I'\ :sub:`1[3:0]; B2[3:0]=pad`
+
+      -  pad
+
+    * -  ...
+
+    * - start + offset:
+
+      -  Q'\ :sub:`0[19:12]`
+
+      -  Q'\ :sub:`0[11:4]`
+
+      -  Q'\ :sub:`0[3:0]; B2[3:0]=pad`
+
+      -  pad
+
+    * - start + offset + 4:
+
+      -  Q'\ :sub:`1[19:12]`
+
+      -  Q'\ :sub:`1[11:4]`
+
+      -  Q'\ :sub:`1[3:0]; B2[3:0]=pad`
+
+      -  pad
diff --git a/Documentation/media/uapi/v4l/sdr-formats.rst b/Documentation/media/uapi/v4l/sdr-formats.rst
index f863c08..4c01cf9 100644
--- a/Documentation/media/uapi/v4l/sdr-formats.rst
+++ b/Documentation/media/uapi/v4l/sdr-formats.rst
@@ -17,3 +17,6 @@ These formats are used for :ref:`SDR <sdr>` interface only.
     pixfmt-sdr-cs08
     pixfmt-sdr-cs14le
     pixfmt-sdr-ru12le
+    pixfmt-sdr-scu16be
+    pixfmt-sdr-scu18be
+    pixfmt-sdr-scu20be
-- 
1.9.1


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

* [PATCH 5/5] media: platform: rcar_drif: Add DRIF support
  2016-11-09 15:44 ` Ramesh Shanmugasundaram
                   ` (3 preceding siblings ...)
  (?)
@ 2016-11-09 15:44 ` Ramesh Shanmugasundaram
  2016-11-10  9:22   ` Laurent Pinchart
  2016-11-11 13:38   ` Hans Verkuil
  -1 siblings, 2 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-11-09 15:44 UTC (permalink / raw)
  To: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope
  Cc: chris.paterson2, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc, Ramesh Shanmugasundaram

This patch adds Digital Radio Interface (DRIF) support to R-Car Gen3 SoCs.
The driver exposes each instance of DRIF as a V4L2 SDR device. A DRIF
device represents a channel and each channel can have one or two
sub-channels respectively depending on the target board.

DRIF supports only Rx functionality. It receives samples from a RF
frontend tuner chip it is interfaced with. The combination of DRIF and the
tuner device, which is registered as a sub-device, determines the receive
sample rate and format.

In order to be compliant as a V4L2 SDR device, DRIF needs to bind with
the tuner device, which can be provided by a third party vendor. DRIF acts
as a slave device and the tuner device acts as a master transmitting the
samples. The driver allows asynchronous binding of a tuner device that
is registered as a v4l2 sub-device. The driver can learn about the tuner
it is interfaced with based on port endpoint properties of the device in
device tree. The V4L2 SDR device inherits the controls exposed by the
tuner device.

The device can also be configured to use either one or both of the data
pins at runtime based on the master (tuner) configuration.

Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>
---
 .../devicetree/bindings/media/renesas,drif.txt     |  136 ++
 drivers/media/platform/Kconfig                     |   25 +
 drivers/media/platform/Makefile                    |    1 +
 drivers/media/platform/rcar_drif.c                 | 1574 ++++++++++++++++++++
 4 files changed, 1736 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/renesas,drif.txt
 create mode 100644 drivers/media/platform/rcar_drif.c

diff --git a/Documentation/devicetree/bindings/media/renesas,drif.txt b/Documentation/devicetree/bindings/media/renesas,drif.txt
new file mode 100644
index 0000000..d65368a
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
@@ -0,0 +1,136 @@
+Renesas R-Car Gen3 Digital Radio Interface controller (DRIF)
+------------------------------------------------------------
+
+R-Car Gen3 DRIF is a serial slave device. It interfaces with a master
+device as shown below
+
++---------------------+                +---------------------+
+|                     |-----SCK------->|CLK                  |
+|       Master        |-----SS-------->|SYNC  DRIFn (slave)  |
+|                     |-----SD0------->|D0                   |
+|                     |-----SD1------->|D1                   |
++---------------------+                +---------------------+
+
+Each DRIF channel (drifn) consists of two sub-channels (drifn0 & drifn1).
+The sub-channels are like two individual channels in itself that share the
+common CLK & SYNC. Each sub-channel has it's own dedicated resources like
+irq, dma channels, address space & clock.
+
+The device tree model represents the channel and each of it's sub-channel
+as a separate node. The parent channel ties the sub-channels together with
+their phandles.
+
+Required properties of a sub-channel:
+-------------------------------------
+- compatible: "renesas,r8a7795-drif" if DRIF controller is a part of R8A7795 SoC.
+	      "renesas,rcar-gen3-drif" for a generic R-Car Gen3 compatible device.
+	      When compatible with the generic version, nodes must list the
+	      SoC-specific version corresponding to the platform first
+	      followed by the generic version.
+- reg: offset and length of that sub-channel.
+- interrupts: associated with that sub-channel.
+- clocks: phandle and clock specifier of that sub-channel.
+- clock-names: clock input name string: "fck".
+- dmas: phandles to the DMA channel of that sub-channel.
+- dma-names: names of the DMA channel: "rx".
+
+Optional properties of a sub-channel:
+-------------------------------------
+- power-domains: phandle to the respective power domain.
+
+Required properties of a channel:
+---------------------------------
+- pinctrl-0: pin control group to be used for this channel.
+- pinctrl-names: must be "default".
+- sub-channels : phandles to the two sub-channels.
+
+Optional properties of a channel:
+---------------------------------
+- port: child port node of a channel that defines the local and remote
+        endpoints. The remote endpoint is assumed to be a tuner subdevice
+	endpoint.
+- renesas,syncmd       : sync mode
+			 0 (Frame start sync pulse mode. 1-bit width pulse
+			    indicates start of a frame)
+			 1 (L/R sync or I2S mode) (default)
+- renesas,lsb-first    : empty property indicates lsb bit is received first.
+			 When not defined msb bit is received first (default)
+- renesas,syncac-pol-high  : empty property indicates sync signal polarity.
+			 When defined, active high or high->low sync signal.
+			 When not defined, active low or low->high sync signal
+			 (default)
+- renesas,dtdl         : delay between sync signal and start of reception.
+			 Must contain one of the following values:
+			 0   (no bit delay)
+			 50  (0.5-clock-cycle delay)
+			 100 (1-clock-cycle delay) (default)
+			 150 (1.5-clock-cycle delay)
+			 200 (2-clock-cycle delay)
+- renesas,syncdl       : delay between end of reception and sync signal edge.
+			 Must contain one of the following values:
+			 0   (no bit delay) (default)
+			 50  (0.5-clock-cycle delay)
+			 100 (1-clock-cycle delay)
+			 150 (1.5-clock-cycle delay)
+			 200 (2-clock-cycle delay)
+			 300 (3-clock-cycle delay)
+
+Example
+--------
+
+SoC common dtsi file
+
+		drif00: rif@e6f40000 {
+			compatible = "renesas,r8a7795-drif",
+				     "renesas,rcar-gen3-drif";
+			reg = <0 0xe6f40000 0 0x64>;
+			interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&cpg CPG_MOD 515>;
+			clock-names = "fck";
+			dmas = <&dmac1 0x20>, <&dmac2 0x20>;
+			dma-names = "rx", "rx";
+			power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+			status = "disabled";
+		};
+
+		drif01: rif@e6f50000 {
+			compatible = "renesas,r8a7795-drif",
+				     "renesas,rcar-gen3-drif";
+			reg = <0 0xe6f50000 0 0x64>;
+			interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&cpg CPG_MOD 514>;
+			clock-names = "fck";
+			dmas = <&dmac1 0x22>, <&dmac2 0x22>;
+			dma-names = "rx", "rx";
+			power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+			status = "disabled";
+		};
+
+		drif0: rif@0 {
+			compatible = "renesas,r8a7795-drif",
+				     "renesas,rcar-gen3-drif";
+			sub-channels = <&drif00>, <&drif01>;
+			status = "disabled";
+		};
+
+Board specific dts file
+
+&drif00 {
+	status = "okay";
+};
+
+&drif01 {
+	status = "okay";
+};
+
+&drif0 {
+	pinctrl-0 = <&drif0_pins>;
+	pinctrl-names = "default";
+	renesas,syncac-pol-high;
+	status = "okay";
+	port {
+		drif0_ep: endpoint {
+		     remote-endpoint = <&tuner_subdev_ep>;
+		};
+	};
+};
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 754edbf1..0ae83a8 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -393,3 +393,28 @@ menuconfig DVB_PLATFORM_DRIVERS
 if DVB_PLATFORM_DRIVERS
 source "drivers/media/platform/sti/c8sectpfe/Kconfig"
 endif #DVB_PLATFORM_DRIVERS
+
+menuconfig SDR_PLATFORM_DRIVERS
+	bool "SDR platform devices"
+	depends on MEDIA_SDR_SUPPORT
+	default n
+	---help---
+	  Say Y here to enable support for platform-specific SDR Drivers.
+
+if SDR_PLATFORM_DRIVERS
+
+config VIDEO_RCAR_DRIF
+	tristate "Renesas Digitial Radio Interface (DRIF)"
+	depends on VIDEO_V4L2 && HAS_DMA
+	depends on ARCH_RENESAS
+	select VIDEOBUF2_VMALLOC
+	---help---
+	  Say Y if you want to enable R-Car Gen3 DRIF support. DRIF is Digital
+	  Radio Interface that interfaces with an RF front end chip. It is a
+	  receiver of digital data which uses DMA to transfer received data to
+	  a configured location for an application to use.
+
+	  To compile this driver as a module, choose M here; the module
+	  will be called rcar_drif.
+
+endif # SDR_PLATFORM_DRIVERS
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index f842933..49ce238 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -49,6 +49,7 @@ obj-$(CONFIG_SOC_CAMERA)		+= soc_camera/
 
 obj-$(CONFIG_VIDEO_RENESAS_FCP) 	+= rcar-fcp.o
 obj-$(CONFIG_VIDEO_RENESAS_JPU) 	+= rcar_jpu.o
+obj-$(CONFIG_VIDEO_RCAR_DRIF)		+= rcar_drif.o
 obj-$(CONFIG_VIDEO_RENESAS_VSP1)	+= vsp1/
 
 obj-y	+= omap/
diff --git a/drivers/media/platform/rcar_drif.c b/drivers/media/platform/rcar_drif.c
new file mode 100644
index 0000000..34dc282
--- /dev/null
+++ b/drivers/media/platform/rcar_drif.c
@@ -0,0 +1,1574 @@
+/*
+ * R-Car Gen3 Digital Radio Interface (DRIF) driver
+ *
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * The R-Car DRIF is a receive only MSIOF like controller with an
+ * external master device driving the SCK. It receives data into a FIFO,
+ * then this driver uses the SYS-DMAC engine to move the data from
+ * the device to memory.
+ *
+ * Each DRIF channel supports two sub-channels with their own resources
+ * like module clk, register set and dma.
+ *
+ * Depending on the master device, a DRIF channel can use
+ *  (1) both sub-channels (D0 & D1 pins) to receive data in parallel (or)
+ *  (2) one sub-channel (D0 or D1) to receive data
+ *
+ * The primary design goal of this controller is to act as Digitial Radio
+ * Interface that receives digital samples from a tuner device. Hence the
+ * driver exposes the device as a V4L2 SDR device. In order to qualify as
+ * a V4L2 SDR device, it should possess tuner interface as mandated by the
+ * framework. This driver expects a tuner driver (sub-device) to bind
+ * asynchronously with this device and the combined drivers shall expose
+ * a V4L2 compliant SDR device. The DRIF driver is independent of the
+ * tuner vendor.
+ */
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/ioctl.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-vmalloc.h>
+
+/* DRIF register offsets */
+#define RCAR_DRIF_SITMDR1			0x00
+#define RCAR_DRIF_SITMDR2			0x04
+#define RCAR_DRIF_SITMDR3			0x08
+#define RCAR_DRIF_SIRMDR1			0x10
+#define RCAR_DRIF_SIRMDR2			0x14
+#define RCAR_DRIF_SIRMDR3			0x18
+#define RCAR_DRIF_SICTR				0x28
+#define RCAR_DRIF_SIFCTR			0x30
+#define RCAR_DRIF_SISTR				0x40
+#define RCAR_DRIF_SIIER				0x44
+#define RCAR_DRIF_SIRFDR			0x60
+
+#define RCAR_DRIF_RFOVF			BIT(3)	/* Receive FIFO overflow */
+#define RCAR_DRIF_RFUDF			BIT(4)	/* Receive FIFO underflow */
+#define RCAR_DRIF_RFSERR		BIT(5)	/* Receive frame sync error */
+#define RCAR_DRIF_REOF			BIT(7)	/* Frame reception end */
+#define RCAR_DRIF_RDREQ			BIT(12) /* Receive data xfer req */
+#define RCAR_DRIF_RFFUL			BIT(13)	/* Receive FIFO full */
+
+/* SIRMDR1 */
+#define RCAR_DRIF_SIRMDR1_SYNCMD_FRAME		(0 << 28)
+#define RCAR_DRIF_SIRMDR1_SYNCMD_LR		(3 << 28)
+
+#define RCAR_DRIF_SIRMDR1_SYNCAC_POL_HIGH	(0 << 25)
+#define RCAR_DRIF_SIRMDR1_SYNCAC_POL_LOW	(1 << 25)
+
+#define RCAR_DRIF_SIRMDR1_MSB_FIRST		(0 << 24)
+#define RCAR_DRIF_SIRMDR1_LSB_FIRST		(1 << 24)
+
+#define RCAR_DRIF_SIRMDR1_DTDL_0		(0 << 20)
+#define RCAR_DRIF_SIRMDR1_DTDL_1		(1 << 20)
+#define RCAR_DRIF_SIRMDR1_DTDL_2		(2 << 20)
+#define RCAR_DRIF_SIRMDR1_DTDL_0PT5		(5 << 20)
+#define RCAR_DRIF_SIRMDR1_DTDL_1PT5		(6 << 20)
+
+#define RCAR_DRIF_SIRMDR1_SYNCDL_0		(0 << 20)
+#define RCAR_DRIF_SIRMDR1_SYNCDL_1		(1 << 20)
+#define RCAR_DRIF_SIRMDR1_SYNCDL_2		(2 << 20)
+#define RCAR_DRIF_SIRMDR1_SYNCDL_3		(3 << 20)
+#define RCAR_DRIF_SIRMDR1_SYNCDL_0PT5		(5 << 20)
+#define RCAR_DRIF_SIRMDR1_SYNCDL_1PT5		(6 << 20)
+
+#define RCAR_DRIF_MDR_GRPCNT(n)			(((n) - 1) << 30)
+#define RCAR_DRIF_MDR_BITLEN(n)			(((n) - 1) << 24)
+#define RCAR_DRIF_MDR_WDCNT(n)			(((n) - 1) << 16)
+
+/* Hidden Transmit register that controls CLK & SYNC */
+#define RCAR_DRIF_SITMDR1_PCON			BIT(30)
+
+#define RCAR_DRIF_SICTR_RX_RISING_EDGE		BIT(26)
+#define RCAR_DRIF_SICTR_RX_EN			BIT(8)
+#define RCAR_DRIF_SICTR_RESET			BIT(0)
+
+/* Constants */
+#define RCAR_DRIF_MAX_NUM_HWBUFS		32
+#define RCAR_DRIF_MAX_DEVS			4
+#define RCAR_DRIF_DEFAULT_NUM_HWBUFS		16
+#define RCAR_DRIF_DEFAULT_HWBUF_SIZE		(4 * PAGE_SIZE)
+#define RCAR_DRIF_MAX_SUBCHANS			2
+#define RCAR_SDR_BUFFER_SIZE			SZ_64K
+
+/* Internal buffer status flags */
+#define RCAR_DRIF_BUF_DONE			BIT(0)	/* DMA completed */
+#define RCAR_DRIF_BUF_OVERFLOW			BIT(1)	/* Overflow detected */
+
+#define to_rcar_drif_buf_pair(p, ch, idx)	(p->sch[!(ch)]->buf[idx])
+
+#define for_each_rcar_drif_subchannel(sch, schans_mask)			\
+	for_each_set_bit(sch, schans_mask, RCAR_DRIF_MAX_SUBCHANS)
+
+#define for_each_rcar_drif_subdev(sd, tmp, ch)				\
+	list_for_each_entry_safe(sd, tmp, &ch->v4l2_dev.subdevs, list)
+
+/* Debug */
+static unsigned int debug;
+module_param(debug, uint, 0644);
+MODULE_PARM_DESC(debug, "activate debug info");
+
+#define rdrif_dbg(level, ch, fmt, arg...)				\
+	v4l2_dbg(level, debug, &ch->v4l2_dev, fmt, ## arg)
+
+#define rdrif_err(ch, fmt, arg...)					\
+	v4l2_err(&ch->v4l2_dev, fmt, ## arg)
+
+/* Stream formats */
+struct rcar_drif_format {
+	char	*name;
+	u32	pixelformat;
+	u32	buffersize;
+	u32	wdlen;
+	u32	num_schans;
+};
+
+/* Format descriptions for capture and preview */
+static const struct rcar_drif_format formats[] = {
+	{
+		.name		= "Sliced Complex U16BE",
+		.pixelformat	= V4L2_SDR_FMT_SCU16BE,
+		.buffersize	= RCAR_SDR_BUFFER_SIZE,
+		.wdlen		= 16,
+		.num_schans	= 2,
+	},
+	{
+		.name		= "Sliced Complex U18BE",
+		.pixelformat	= V4L2_SDR_FMT_SCU18BE,
+		.buffersize	= RCAR_SDR_BUFFER_SIZE,
+		.wdlen		= 18,
+		.num_schans	= 2,
+	},
+	{
+		.name		= "Sliced Complex U20BE",
+		.pixelformat	= V4L2_SDR_FMT_SCU20BE,
+		.buffersize	= RCAR_SDR_BUFFER_SIZE,
+		.wdlen		= 20,
+		.num_schans	= 2,
+	},
+};
+
+static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats);
+
+/* Buffer for a received frame from one or both sub-channels */
+struct rcar_drif_frame_buf {
+	/* Common v4l buffer stuff -- must be first */
+	struct vb2_v4l2_buffer vb;
+	struct list_head list;
+};
+
+struct rcar_drif_async_subdev {
+	struct v4l2_subdev *sd;
+	struct v4l2_async_subdev asd;
+};
+
+/* DMA buffer */
+struct rcar_drif_hwbuf {
+	void *addr;			/* Cpu-side address */
+	unsigned int status;		/* Buffer status flags */
+};
+
+/* Sub-channel */
+struct rcar_drif_subchan {
+	struct rcar_drif_chan *parent;	/* Parent channel */
+	struct platform_device *pdev;	/* Sub-channel's pdev */
+	void __iomem *base;		/* Base register address */
+	resource_size_t start;		/* I/O resource offset */
+	struct dma_chan *dmach;		/* Reserved DMA channel */
+	struct clk *clkp;		/* Module clock */
+	struct rcar_drif_hwbuf *buf[RCAR_DRIF_MAX_NUM_HWBUFS];
+	dma_addr_t dma_handle;		/* Handle for all bufs */
+	unsigned int num;		/* Sub-channel number */
+};
+
+/* Channel */
+struct rcar_drif_chan {
+	struct device *dev;		/* Platform device */
+	struct video_device vdev;	/* SDR device */
+	struct v4l2_device v4l2_dev;	/* V4l2 device */
+
+	/* Videobuf2 queue and queued buffers list */
+	struct vb2_queue vb_queue;
+	struct list_head queued_bufs;
+	spinlock_t queued_bufs_lock;	/* Protects queued_bufs */
+
+	struct mutex v4l2_mutex;	/* To serialize ioctls */
+	struct mutex vb_queue_mutex;	/* To serialize streaming ioctls */
+	struct v4l2_ctrl_handler ctrl_hdl;	/* SDR control handler */
+	struct v4l2_async_notifier notifier;	/* For subdev (tuner) */
+
+	/* Current V4L2 SDR format array index */
+	unsigned int fmt_idx;
+
+	/* Device tree SYNC properties */
+	u32 mdr1;
+
+	/* Internals */
+	struct rcar_drif_subchan *sch[2]; /* DRIFx0 and DRIFx1 */
+	unsigned long hw_schans_mask;	/* Enabled sub-channels per DT */
+	unsigned long cur_schans_mask;	/* Used sub-channels for an SDR FMT */
+	u32 num_hw_schans;		/* Num of DT enabled sub-channels */
+	u32 num_cur_schans;		/* Num of used sub-channels */
+	u32 num_hwbufs;			/* Num of DMA buffers */
+	u32 hwbuf_size;			/* Each DMA buffer size */
+	u32 produced;			/* Buffers produced by dev */
+};
+
+/* Allocate buffer context */
+static int rcar_drif_alloc_bufctxt(struct rcar_drif_chan *ch)
+{
+	struct rcar_drif_hwbuf *bufctx;
+	unsigned int i, idx;
+
+	for_each_rcar_drif_subchannel(i, &ch->cur_schans_mask) {
+		bufctx = kcalloc(ch->num_hwbufs, sizeof(*bufctx), GFP_KERNEL);
+		if (!bufctx)
+			return -ENOMEM;
+
+		for (idx = 0; idx < ch->num_hwbufs; idx++)
+			ch->sch[i]->buf[idx] = bufctx + idx;
+	}
+	return 0;
+}
+
+/* Release buffer context */
+static void rcar_drif_release_bufctxt(struct rcar_drif_chan *ch)
+{
+	unsigned int i;
+
+	for_each_rcar_drif_subchannel(i, &ch->cur_schans_mask) {
+		kfree(ch->sch[i]->buf[0]);
+		ch->sch[i]->buf[0] = NULL;
+	}
+}
+
+/* Release DMA channel */
+static void rcar_drif_release_dmachannel(struct rcar_drif_chan *ch)
+{
+	unsigned int i;
+
+	for_each_rcar_drif_subchannel(i, &ch->cur_schans_mask)
+		if (ch->sch[i]->dmach) {
+			dma_release_channel(ch->sch[i]->dmach);
+			ch->sch[i]->dmach = NULL;
+		}
+}
+
+/* Allocate DMA channel */
+static int rcar_drif_alloc_dmachannel(struct rcar_drif_chan *ch)
+{
+	struct dma_slave_config dma_cfg;
+	unsigned int i;
+	int ret = -ENODEV;
+
+	for_each_rcar_drif_subchannel(i, &ch->cur_schans_mask) {
+		struct rcar_drif_subchan *sch = ch->sch[i];
+
+		sch->dmach = dma_request_slave_channel(&sch->pdev->dev, "rx");
+		if (!sch->dmach) {
+			rdrif_err(ch, "schan%u: dma channel req failed\n", i);
+			goto dmach_error;
+		}
+
+		/* Configure slave */
+		memset(&dma_cfg, 0, sizeof(dma_cfg));
+		dma_cfg.src_addr = (phys_addr_t)(sch->start + RCAR_DRIF_SIRFDR);
+		dma_cfg.dst_addr = 0;
+		dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		ret = dmaengine_slave_config(sch->dmach, &dma_cfg);
+		if (ret) {
+			rdrif_err(ch, "schan%u: dma slave config failed\n", i);
+			goto dmach_error;
+		}
+	}
+	return 0;
+
+dmach_error:
+	rcar_drif_release_dmachannel(ch);
+	return ret;
+}
+
+/* Release queued vb2 buffers */
+static void rcar_drif_release_queued_bufs(struct rcar_drif_chan *ch,
+					  enum vb2_buffer_state state)
+{
+	struct rcar_drif_frame_buf *fbuf, *tmp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ch->queued_bufs_lock, flags);
+	list_for_each_entry_safe(fbuf, tmp, &ch->queued_bufs, list) {
+		list_del(&fbuf->list);
+		vb2_buffer_done(&fbuf->vb.vb2_buf, state);
+	}
+	spin_unlock_irqrestore(&ch->queued_bufs_lock, flags);
+}
+
+/* Set MDR defaults */
+static inline void rcar_drif_set_mdr1(struct rcar_drif_chan *ch)
+{
+	unsigned int i;
+
+	/* Set defaults for both sub-channels */
+	for_each_rcar_drif_subchannel(i, &ch->cur_schans_mask) {
+		/* Refer MSIOF section in manual for this register setting */
+		writel(RCAR_DRIF_SITMDR1_PCON,
+		       ch->sch[i]->base + RCAR_DRIF_SITMDR1);
+
+		/* Setup MDR1 value */
+		writel(ch->mdr1, ch->sch[i]->base + RCAR_DRIF_SIRMDR1);
+
+		rdrif_dbg(2, ch, "schan%u: mdr1 = 0x%08x",
+			  i, readl(ch->sch[i]->base + RCAR_DRIF_SIRMDR1));
+	}
+}
+
+/* Extract bitlen and wdcnt from given word length */
+static int rcar_drif_convert_wdlen(struct rcar_drif_chan *ch,
+				   u32 wdlen, u32 *bitlen, u32 *wdcnt)
+{
+	unsigned int i, nr_wds;
+
+	/* FIFO register size is 32 bits */
+	for (i = 0; i < 32; i++) {
+		nr_wds = wdlen % (32 - i);
+		if (nr_wds == 0) {
+			*bitlen = (32 - i);
+			*wdcnt = (wdlen / *bitlen);
+			break;
+		}
+	}
+
+	/* Sanity check range */
+	if ((i == 32) || !((*bitlen >= 8) && (*bitlen <= 32)) ||
+	    !((*wdcnt >= 1) && (*wdcnt <= 64))) {
+		rdrif_err(ch, "invalid wdlen %u configured\n", wdlen);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* Set DRIF receive format */
+static int rcar_drif_set_format(struct rcar_drif_chan *ch)
+{
+	u32 bitlen, wdcnt, wdlen;
+	unsigned int i;
+	int ret = -EINVAL;
+
+	wdlen = formats[ch->fmt_idx].wdlen;
+	rdrif_dbg(2, ch, "setfmt: idx %u, wdlen %u, num_schans %u\n",
+		  ch->fmt_idx, wdlen, formats[ch->fmt_idx].num_schans);
+
+	/* Sanity check */
+	if (formats[ch->fmt_idx].num_schans > ch->num_cur_schans) {
+		rdrif_err(ch, "fmt idx %u current schans %u mismatch\n",
+			  ch->fmt_idx, ch->num_cur_schans);
+		return ret;
+	}
+
+	/* Get bitlen & wdcnt from wdlen */
+	ret = rcar_drif_convert_wdlen(ch, wdlen, &bitlen, &wdcnt);
+	if (ret)
+		return ret;
+
+	/* Setup group, bitlen & wdcnt */
+	for_each_rcar_drif_subchannel(i, &ch->cur_schans_mask) {
+		u32 mdr;
+
+		/* Two groups */
+		mdr = RCAR_DRIF_MDR_GRPCNT(2) | RCAR_DRIF_MDR_BITLEN(bitlen) |
+		       RCAR_DRIF_MDR_WDCNT(wdcnt);
+		writel(mdr, ch->sch[i]->base + RCAR_DRIF_SIRMDR2);
+
+		mdr = RCAR_DRIF_MDR_BITLEN(bitlen) | RCAR_DRIF_MDR_WDCNT(wdcnt);
+		writel(mdr, ch->sch[i]->base + RCAR_DRIF_SIRMDR3);
+
+		rdrif_dbg(2, ch, "schan%u: new mdr[2,3] = 0x%08x, 0x%08x\n",
+			  i, readl(ch->sch[i]->base + RCAR_DRIF_SIRMDR2),
+			  readl(ch->sch[i]->base + RCAR_DRIF_SIRMDR3));
+	}
+	return ret;
+}
+
+/* Release DMA buffers */
+static void rcar_drif_release_buf(struct rcar_drif_chan *ch)
+{
+	unsigned int i;
+
+	for_each_rcar_drif_subchannel(i, &ch->cur_schans_mask) {
+		struct rcar_drif_subchan *sch = ch->sch[i];
+
+		/* First entry of the sub-channel contains the dma buf ptr */
+		if (sch->buf[0] && sch->buf[0]->addr) {
+			dma_free_coherent(&sch->pdev->dev,
+					  ch->hwbuf_size * ch->num_hwbufs,
+					  sch->buf[0]->addr, sch->dma_handle);
+			sch->buf[0]->addr = NULL;
+		}
+	}
+}
+
+/* Request DMA buffers */
+static int rcar_drif_request_buf(struct rcar_drif_chan *ch)
+{
+	int ret = -ENOMEM;
+	unsigned int i, j;
+	void *addr;
+
+	for_each_rcar_drif_subchannel(i, &ch->cur_schans_mask) {
+		struct rcar_drif_subchan *sch = ch->sch[i];
+
+		/* Allocate DMA buffers */
+		addr = dma_alloc_coherent(&sch->pdev->dev,
+					  ch->hwbuf_size * ch->num_hwbufs,
+					  &sch->dma_handle, GFP_KERNEL);
+		if (!addr) {
+			rdrif_err(ch,
+			"schan%u: dma alloc failed. num_hwbufs %u size %u\n",
+			i, ch->num_hwbufs, ch->hwbuf_size);
+			goto alloc_error;
+		}
+
+		/* Split the chunk and populate bufctxt */
+		for (j = 0; j < ch->num_hwbufs; j++) {
+			sch->buf[j]->addr = addr + (j * ch->hwbuf_size);
+			sch->buf[j]->status = 0;
+		}
+	}
+
+	return 0;
+
+alloc_error:
+	return ret;
+}
+
+/* Setup vb_queue minimum buffer requirements */
+static int rcar_drif_queue_setup(struct vb2_queue *vq,
+			unsigned int *num_buffers, unsigned int *num_planes,
+			unsigned int sizes[], struct device *alloc_devs[])
+{
+	struct rcar_drif_chan *ch = vb2_get_drv_priv(vq);
+
+	/* Need at least 16 buffers */
+	if (vq->num_buffers + *num_buffers < 16)
+		*num_buffers = 16 - vq->num_buffers;
+
+	*num_planes = 1;
+	sizes[0] = PAGE_ALIGN(formats[ch->fmt_idx].buffersize);
+
+	rdrif_dbg(2, ch, "num_bufs %d sizes[0] %d\n", *num_buffers, sizes[0]);
+	return 0;
+}
+
+/* Enqueue buffer */
+static void rcar_drif_buf_queue(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct rcar_drif_chan *ch = vb2_get_drv_priv(vb->vb2_queue);
+	struct rcar_drif_frame_buf *fbuf =
+			container_of(vbuf, struct rcar_drif_frame_buf, vb);
+	unsigned long flags;
+
+	rdrif_dbg(2, ch, "buf_queue idx %u\n", vb->index);
+	spin_lock_irqsave(&ch->queued_bufs_lock, flags);
+	list_add_tail(&fbuf->list, &ch->queued_bufs);
+	spin_unlock_irqrestore(&ch->queued_bufs_lock, flags);
+}
+
+/* Deliver buffer to user space */
+static void rcar_drif_deliver_buf(struct rcar_drif_subchan *sch, u32 idx)
+{
+	struct rcar_drif_chan *ch = sch->parent;
+	struct rcar_drif_frame_buf *fbuf;
+	unsigned long flags;
+	bool overflow = false;
+
+	spin_lock_irqsave(&ch->queued_bufs_lock, flags);
+	fbuf = list_first_entry_or_null(&ch->queued_bufs, struct
+					rcar_drif_frame_buf, list);
+	if (!fbuf) {
+		/*
+		 * App is late in enqueing buffers. Samples lost & there will
+		 * be a gap in sequence number when app recovers
+		 */
+		rdrif_dbg(1, ch, "\napp late: schan%u: prod %u\n",
+			  sch->num, ch->produced);
+		ch->produced++;	/* Increment the produced count anyway */
+		spin_unlock_irqrestore(&ch->queued_bufs_lock, flags);
+		return;
+	}
+	list_del(&fbuf->list);
+	spin_unlock_irqrestore(&ch->queued_bufs_lock, flags);
+
+	if (ch->num_cur_schans == RCAR_DRIF_MAX_SUBCHANS) {
+		struct rcar_drif_hwbuf *buf_i, *buf_q;
+
+		if (sch->num) {
+			buf_i = to_rcar_drif_buf_pair(ch, sch->num, idx);
+			buf_q = sch->buf[idx];
+		} else {
+			buf_i = sch->buf[idx];
+			buf_q = to_rcar_drif_buf_pair(ch, sch->num, idx);
+		}
+		memcpy(vb2_plane_vaddr(&fbuf->vb.vb2_buf, 0),
+		       buf_i->addr, ch->hwbuf_size);
+		memcpy(vb2_plane_vaddr(&fbuf->vb.vb2_buf, 0) + ch->hwbuf_size,
+		       buf_q->addr, ch->hwbuf_size);
+
+		if ((buf_i->status | buf_q->status) & RCAR_DRIF_BUF_OVERFLOW) {
+			overflow = true;
+			/* Clear the flag in status */
+			buf_i->status &= ~RCAR_DRIF_BUF_OVERFLOW;
+			buf_q->status &= ~RCAR_DRIF_BUF_OVERFLOW;
+		}
+	} else {
+		struct rcar_drif_hwbuf *buf_iq = sch->buf[idx];
+
+		memcpy(vb2_plane_vaddr(&fbuf->vb.vb2_buf, 0),
+		       buf_iq->addr, ch->hwbuf_size);
+
+		if (buf_iq->status & RCAR_DRIF_BUF_OVERFLOW) {
+			overflow = true;
+			/* Clear the flag in status */
+			buf_iq->status &= ~RCAR_DRIF_BUF_OVERFLOW;
+		}
+	}
+
+	rdrif_dbg(2, ch, "schan%u: prod %u\n", sch->num, ch->produced);
+
+	fbuf->vb.field = V4L2_FIELD_NONE;
+	fbuf->vb.sequence = ch->produced++;
+	fbuf->vb.vb2_buf.timestamp = ktime_get_ns();
+	vb2_set_plane_payload(&fbuf->vb.vb2_buf, 0,
+			      formats[ch->fmt_idx].buffersize);
+
+	/* Set error state on overflow */
+	if (overflow)
+		vb2_buffer_done(&fbuf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+	else
+		vb2_buffer_done(&fbuf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+static inline bool rcar_drif_buf_pairs_done(struct rcar_drif_hwbuf *buf1,
+					    struct rcar_drif_hwbuf *buf2)
+{
+	return (buf1->status & buf2->status & RCAR_DRIF_BUF_DONE);
+}
+
+/* Check if DMA of the paired sub-channel is also complete */
+static void rcar_drif_check_dmapair(struct rcar_drif_subchan *sch, u32 idx)
+{
+	struct rcar_drif_chan *ch = sch->parent;
+	struct rcar_drif_hwbuf *buf, *pair;
+
+	buf = sch->buf[idx];
+	pair = to_rcar_drif_buf_pair(ch, sch->num, idx);
+	buf->status |= RCAR_DRIF_BUF_DONE;
+
+	rdrif_dbg(2, ch, "schan%u: status: buf %u pair %u\n",
+		  sch->num, buf->status, pair->status);
+
+	/* Check if both DMA buffers are done */
+	if (rcar_drif_buf_pairs_done(buf, pair)) {
+		/* Clear status flag */
+		buf->status &= ~RCAR_DRIF_BUF_DONE;
+		pair->status &= ~RCAR_DRIF_BUF_DONE;
+
+		/* Deliver buffer to user */
+		rcar_drif_deliver_buf(sch, idx);
+	}
+}
+
+/* DMA callback for each stage */
+static void rcar_drif_dma_complete(void *dma_async_param)
+{
+	struct rcar_drif_subchan *sch =
+		(struct rcar_drif_subchan *)dma_async_param;
+	struct rcar_drif_chan *ch = sch->parent;
+	struct rcar_drif_hwbuf *buf;
+	u32 str, idx;
+
+	mutex_lock(&ch->vb_queue_mutex);
+
+	/* DMA can be terminated while the callback was waiting on lock */
+	if (!vb2_is_streaming(&ch->vb_queue)) {
+		mutex_unlock(&ch->vb_queue_mutex);
+		return;
+	}
+
+	idx = ch->produced % ch->num_hwbufs;
+	buf = sch->buf[idx];
+
+	/* Check for DRIF errors */
+	str = readl(sch->base + RCAR_DRIF_SISTR);
+	if (unlikely(str & RCAR_DRIF_RFOVF)) {
+		/* Writing the same clears it */
+		writel(str, sch->base + RCAR_DRIF_SISTR);
+
+		/* Overflow: some samples are lost */
+		buf->status |= RCAR_DRIF_BUF_OVERFLOW;
+		rdrif_dbg(1, ch, "\nfifo overflow: schan%u: prod %d\n",
+			  sch->num, ch->produced);
+	}
+
+	/* Check if the rx'ed data can be delivered to user */
+	if (ch->num_cur_schans == RCAR_DRIF_MAX_SUBCHANS)
+		rcar_drif_check_dmapair(sch, idx);
+	else
+		rcar_drif_deliver_buf(sch, idx);
+
+	mutex_unlock(&ch->vb_queue_mutex);
+}
+
+static int rcar_drif_qbuf(struct rcar_drif_subchan *sch)
+{
+	struct rcar_drif_chan *ch = sch->parent;
+	dma_addr_t addr = sch->dma_handle;
+	struct dma_async_tx_descriptor *rxd;
+	dma_cookie_t cookie;
+	int ret = -EIO;
+
+	/* Setup cyclic DMA with given buffers */
+	rxd = dmaengine_prep_dma_cyclic(sch->dmach, addr,
+					ch->hwbuf_size * ch->num_hwbufs,
+					ch->hwbuf_size, DMA_DEV_TO_MEM,
+					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!rxd) {
+		rdrif_err(ch, "schan%u: prep dma cyclic failed\n", sch->num);
+		return ret;
+	}
+
+	/* Submit descriptor */
+	rxd->callback = rcar_drif_dma_complete;
+	rxd->callback_param = sch;
+	cookie = dmaengine_submit(rxd);
+	if (dma_submit_error(cookie)) {
+		rdrif_err(ch, "schan%u: dma submit failed\n", sch->num);
+		return ret;
+	}
+
+	dma_async_issue_pending(sch->dmach);
+	return 0;
+}
+
+/* Enable reception */
+static int rcar_drif_enable_rx(struct rcar_drif_chan *ch)
+{
+	unsigned int i;
+	u32 ctr;
+	int ret;
+
+	/*
+	 * When both sub-channels are enabled, they can be syncronized only by
+	 * the master
+	 */
+
+	/* Enable receive */
+	for_each_rcar_drif_subchannel(i, &ch->cur_schans_mask) {
+		ctr = readl(ch->sch[i]->base + RCAR_DRIF_SICTR);
+		ctr |= (RCAR_DRIF_SICTR_RX_RISING_EDGE |
+			 RCAR_DRIF_SICTR_RX_EN);
+		writel(ctr, ch->sch[i]->base + RCAR_DRIF_SICTR);
+	}
+
+	/* Check receive enabled */
+	for_each_rcar_drif_subchannel(i, &ch->cur_schans_mask) {
+		ret = readl_poll_timeout(ch->sch[i]->base + RCAR_DRIF_SICTR,
+					 ctr, ctr & RCAR_DRIF_SICTR_RX_EN,
+					 2, 500000);
+		if (ret) {
+			rdrif_err(ch, "schan%u: rx en failed. ctr 0x%08x\n",
+				  i, readl(ch->sch[i]->base + RCAR_DRIF_SICTR));
+			break;
+		}
+	}
+	return ret;
+}
+
+/* Disable reception */
+static void rcar_drif_disable_rx(struct rcar_drif_chan *ch)
+{
+	unsigned int i;
+	u32 ctr;
+	int ret;
+
+	/* Disable receive */
+	for_each_rcar_drif_subchannel(i, &ch->cur_schans_mask) {
+		ctr = readl(ch->sch[i]->base + RCAR_DRIF_SICTR);
+		ctr &= ~RCAR_DRIF_SICTR_RX_EN;
+		writel(ctr, ch->sch[i]->base + RCAR_DRIF_SICTR);
+	}
+
+	/* Check receive disabled */
+	for_each_rcar_drif_subchannel(i, &ch->cur_schans_mask) {
+		ret = readl_poll_timeout(ch->sch[i]->base + RCAR_DRIF_SICTR,
+					 ctr, !(ctr & RCAR_DRIF_SICTR_RX_EN),
+					 2, 500000);
+		if (ret)
+			dev_warn(&ch->vdev.dev,
+			"schan%u: failed to disable rx. ctr 0x%08x\n",
+			i, readl(ch->sch[i]->base + RCAR_DRIF_SICTR));
+	}
+}
+
+/* Start sub-channel */
+static int rcar_drif_start_subchan(struct rcar_drif_subchan *sch)
+{
+	struct rcar_drif_chan *ch = sch->parent;
+	u32 ctr, str;
+	int ret;
+
+	/* Reset receive */
+	writel(RCAR_DRIF_SICTR_RESET, sch->base + RCAR_DRIF_SICTR);
+	ret = readl_poll_timeout(sch->base + RCAR_DRIF_SICTR,
+					 ctr, !(ctr & RCAR_DRIF_SICTR_RESET),
+					 2, 500000);
+	if (ret) {
+		rdrif_err(ch, "schan%u: failed to reset rx. ctr 0x%08x\n",
+			  sch->num, readl(sch->base + RCAR_DRIF_SICTR));
+		return ret;
+	}
+
+	/* Queue buffers for DMA */
+	ret = rcar_drif_qbuf(sch);
+	if (ret)
+		return ret;
+
+	/* Clear status register flags */
+	str = RCAR_DRIF_RFFUL | RCAR_DRIF_REOF | RCAR_DRIF_RFSERR |
+		RCAR_DRIF_RFUDF | RCAR_DRIF_RFOVF;
+	writel(str, sch->base + RCAR_DRIF_SISTR);
+
+	/* Enable DMA receive interrupt */
+	writel(0x00009000, sch->base + RCAR_DRIF_SIIER);
+
+	return ret;
+}
+
+/* Start receive operation */
+static int rcar_drif_start(struct rcar_drif_chan *ch)
+{
+	unsigned int i;
+	int ret;
+
+	for_each_rcar_drif_subchannel(i, &ch->cur_schans_mask) {
+		ret = rcar_drif_start_subchan(ch->sch[i]);
+		if (ret)
+			goto start_error;
+	}
+
+	ch->produced = 0;
+
+	ret = rcar_drif_enable_rx(ch);
+	if (ret)
+		goto start_error;
+
+	rdrif_dbg(1, ch, "started\n");
+
+start_error:
+	return ret;
+}
+
+/* Stop sub-channel */
+static void rcar_drif_stop_subchan(struct rcar_drif_subchan *sch)
+{
+	struct rcar_drif_chan *ch = sch->parent;
+	int ret, retries = 3;
+
+	/* Disable DMA receive interrupt */
+	writel(0x00000000, sch->base + RCAR_DRIF_SIIER);
+
+	do {
+		/* Terminate all DMA transfers */
+		ret = dmaengine_terminate_sync(sch->dmach);
+		if (!ret)
+			break;
+		rdrif_dbg(2, ch, "stop retry\n");
+	} while (--retries);
+
+	WARN_ON(!retries);
+}
+
+/* Stop receive operation */
+static void rcar_drif_stop(struct rcar_drif_chan *ch)
+{
+	unsigned int i;
+
+	/* Disable Rx */
+	rcar_drif_disable_rx(ch);
+
+	for_each_rcar_drif_subchannel(i, &ch->cur_schans_mask)
+		rcar_drif_stop_subchan(ch->sch[i]);
+
+	rdrif_dbg(1, ch, "stopped: prod %u\n", ch->produced);
+}
+
+/* Start streaming */
+static int rcar_drif_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct rcar_drif_chan *ch = vb2_get_drv_priv(vq);
+	unsigned int i, j;
+	int ret;
+
+	rdrif_dbg(1, ch, "start streaming\n");
+	mutex_lock(&ch->v4l2_mutex);
+
+	for_each_rcar_drif_subchannel(i, &ch->cur_schans_mask) {
+		ret = clk_prepare_enable(ch->sch[i]->clkp);
+		if (ret)
+			goto start_error;
+	}
+
+	/* Set default MDRx settings */
+	rcar_drif_set_mdr1(ch);
+
+	/* Set new format */
+	ret = rcar_drif_set_format(ch);
+	if (ret)
+		goto start_error;
+
+	if (ch->num_cur_schans == RCAR_DRIF_MAX_SUBCHANS)
+		ch->hwbuf_size = formats[ch->fmt_idx].buffersize/2;
+	else
+		ch->hwbuf_size = formats[ch->fmt_idx].buffersize;
+
+	rdrif_dbg(1, ch, "num_hwbufs %u, hwbuf_size %u\n",
+		ch->num_hwbufs, ch->hwbuf_size);
+
+	/* Alloc DMA channel */
+	ret = rcar_drif_alloc_dmachannel(ch);
+	if (ret)
+		goto start_error;
+
+	/* Alloc buf context */
+	ret = rcar_drif_alloc_bufctxt(ch);
+	if (ret)
+		goto start_error;
+
+	/* Request buffers */
+	ret = rcar_drif_request_buf(ch);
+	if (ret)
+		goto start_error;
+
+	/* Start Rx */
+	ret = rcar_drif_start(ch);
+	if (ret)
+		goto start_error;
+
+	mutex_unlock(&ch->v4l2_mutex);
+	return ret;
+
+start_error:
+	rcar_drif_release_queued_bufs(ch, VB2_BUF_STATE_QUEUED);
+	rcar_drif_release_buf(ch);
+	rcar_drif_release_bufctxt(ch);
+	rcar_drif_release_dmachannel(ch);
+	for (j = 0; j < i; j++)
+		clk_disable_unprepare(ch->sch[j]->clkp);
+
+	mutex_unlock(&ch->v4l2_mutex);
+	return ret;
+}
+
+/* Stop streaming */
+static void rcar_drif_stop_streaming(struct vb2_queue *vq)
+{
+	struct rcar_drif_chan *ch = vb2_get_drv_priv(vq);
+	unsigned int i;
+
+	mutex_lock(&ch->v4l2_mutex);
+
+	/* Stop hardware streaming */
+	rcar_drif_stop(ch);
+
+	/* Return all queued buffers to vb2 */
+	rcar_drif_release_queued_bufs(ch, VB2_BUF_STATE_ERROR);
+
+	/* Release buf & buf context */
+	rcar_drif_release_buf(ch);
+	rcar_drif_release_bufctxt(ch);
+
+	/* Release DMA channel resources */
+	rcar_drif_release_dmachannel(ch);
+
+	for (i = 0; i < RCAR_DRIF_MAX_SUBCHANS; i++)
+		clk_disable_unprepare(ch->sch[i]->clkp);
+
+	mutex_unlock(&ch->v4l2_mutex);
+	rdrif_dbg(1, ch, "stopped streaming\n");
+}
+
+/* Vb2 ops */
+static struct vb2_ops rcar_drif_vb2_ops = {
+	.queue_setup            = rcar_drif_queue_setup,
+	.buf_queue              = rcar_drif_buf_queue,
+	.start_streaming        = rcar_drif_start_streaming,
+	.stop_streaming         = rcar_drif_stop_streaming,
+	.wait_prepare		= vb2_ops_wait_prepare,
+	.wait_finish		= vb2_ops_wait_finish,
+};
+
+static int rcar_drif_querycap(struct file *file, void *fh,
+			      struct v4l2_capability *cap)
+{
+	struct rcar_drif_chan *ch = video_drvdata(file);
+
+	strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+	strlcpy(cap->card, ch->vdev.name, sizeof(cap->card));
+	cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER |
+				   V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+		 ch->vdev.name);
+	return 0;
+}
+
+static int rcar_drif_set_default_format(struct rcar_drif_chan *ch)
+{
+	unsigned int i;
+
+	for (i = 0; i < NUM_FORMATS; i++) {
+		/* Find any matching fmt and set it as default */
+		if (ch->num_hw_schans == formats[i].num_schans) {
+			ch->fmt_idx = i;
+			ch->cur_schans_mask = ch->hw_schans_mask;
+			ch->num_cur_schans = ch->num_hw_schans;
+			dev_dbg(ch->dev, "default fmt[%u]: mask %lu num %u\n",
+				i, ch->cur_schans_mask, ch->num_cur_schans);
+			return 0;
+		}
+	}
+	dev_err(ch->dev, "no matching sdr fmt found\n");
+	return -EINVAL;
+}
+
+static int rcar_drif_enum_fmt_sdr_cap(struct file *file, void *priv,
+				      struct v4l2_fmtdesc *f)
+{
+	if (f->index >= NUM_FORMATS)
+		return -EINVAL;
+
+	strlcpy(f->description, formats[f->index].name, sizeof(f->description));
+	f->pixelformat = formats[f->index].pixelformat;
+	return 0;
+}
+
+static int rcar_drif_g_fmt_sdr_cap(struct file *file, void *priv,
+				   struct v4l2_format *f)
+{
+	struct rcar_drif_chan *ch = video_drvdata(file);
+
+	f->fmt.sdr.pixelformat = formats[ch->fmt_idx].pixelformat;
+	f->fmt.sdr.buffersize = formats[ch->fmt_idx].buffersize;
+	memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
+	return 0;
+}
+
+static int rcar_drif_s_fmt_sdr_cap(struct file *file, void *priv,
+				   struct v4l2_format *f)
+{
+	struct rcar_drif_chan *ch = video_drvdata(file);
+	struct vb2_queue *q = &ch->vb_queue;
+	unsigned int i;
+
+	if (vb2_is_busy(q))
+		return -EBUSY;
+
+	memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
+	for (i = 0; i < NUM_FORMATS; i++) {
+		if (formats[i].pixelformat == f->fmt.sdr.pixelformat) {
+			ch->fmt_idx  = i;
+			f->fmt.sdr.buffersize = formats[i].buffersize;
+
+			/*
+			 * If a format demands one sub-channel only out of two
+			 * enabled sub-channels then pick the 0th sub-channel
+			 */
+			if (formats[i].num_schans < ch->num_hw_schans) {
+				ch->cur_schans_mask = BIT(0);	/* Enable D0 */
+				ch->num_cur_schans = formats[i].num_schans;
+			} else {
+				ch->cur_schans_mask = ch->hw_schans_mask;
+				ch->num_cur_schans = ch->num_hw_schans;
+			}
+
+			rdrif_dbg(1, ch, "cur: idx %u mask %lu num %u\n",
+				  i, ch->cur_schans_mask, ch->num_cur_schans);
+			return 0;
+		}
+	}
+
+	if (rcar_drif_set_default_format(ch))
+		return -EINVAL;
+
+	f->fmt.sdr.pixelformat = formats[ch->fmt_idx].pixelformat;
+	f->fmt.sdr.buffersize = formats[ch->fmt_idx].buffersize;
+	return 0;
+}
+
+static int rcar_drif_try_fmt_sdr_cap(struct file *file, void *priv,
+				     struct v4l2_format *f)
+{
+	struct rcar_drif_chan *ch = video_drvdata(file);
+	unsigned int i;
+
+	memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
+	for (i = 0; i < NUM_FORMATS; i++) {
+		if (formats[i].pixelformat == f->fmt.sdr.pixelformat) {
+			f->fmt.sdr.buffersize = formats[i].buffersize;
+			return 0;
+		}
+	}
+
+	f->fmt.sdr.pixelformat = formats[ch->fmt_idx].pixelformat;
+	f->fmt.sdr.buffersize = formats[ch->fmt_idx].buffersize;
+	return 0;
+}
+
+/* Tuner subdev ioctls */
+static int rcar_drif_enum_freq_bands(struct file *file, void *priv,
+				     struct v4l2_frequency_band *band)
+{
+	struct rcar_drif_chan *ch = video_drvdata(file);
+	struct v4l2_subdev *sd, *tmp;
+	int ret = 0;
+
+	for_each_rcar_drif_subdev(sd, tmp, ch) {
+		ret = v4l2_subdev_call(sd, tuner, enum_freq_bands, band);
+		if (ret)
+			break;
+	}
+	return ret;
+}
+
+static int rcar_drif_g_frequency(struct file *file, void *priv,
+				 struct v4l2_frequency *f)
+{
+	struct rcar_drif_chan *ch = video_drvdata(file);
+	struct v4l2_subdev *sd, *tmp;
+	int ret = 0;
+
+	for_each_rcar_drif_subdev(sd, tmp, ch) {
+		ret = v4l2_subdev_call(sd, tuner, g_frequency, f);
+		if (ret)
+			break;
+	}
+	return ret;
+}
+
+static int rcar_drif_s_frequency(struct file *file, void *priv,
+				 const struct v4l2_frequency *f)
+{
+	struct rcar_drif_chan *ch = video_drvdata(file);
+	struct v4l2_subdev *sd, *tmp;
+	int ret = 0;
+
+	for_each_rcar_drif_subdev(sd, tmp, ch) {
+		ret = v4l2_subdev_call(sd, tuner, s_frequency, f);
+		if (ret)
+			break;
+	}
+	return ret;
+}
+
+static int rcar_drif_g_tuner(struct file *file, void *priv,
+			     struct v4l2_tuner *vt)
+{
+	struct rcar_drif_chan *ch = video_drvdata(file);
+	struct v4l2_subdev *sd, *tmp;
+	int ret = 0;
+
+	for_each_rcar_drif_subdev(sd, tmp, ch) {
+		ret = v4l2_subdev_call(sd, tuner, g_tuner, vt);
+		if (ret)
+			break;
+	}
+	return ret;
+}
+
+static int rcar_drif_s_tuner(struct file *file, void *priv,
+			     const struct v4l2_tuner *vt)
+{
+	struct rcar_drif_chan *ch = video_drvdata(file);
+	struct v4l2_subdev *sd, *tmp;
+	int ret = 0;
+
+	for_each_rcar_drif_subdev(sd, tmp, ch) {
+		ret = v4l2_subdev_call(sd, tuner, s_tuner, vt);
+		if (ret)
+			break;
+	}
+	return ret;
+}
+
+static const struct v4l2_ioctl_ops rcar_drif_ioctl_ops = {
+	.vidioc_querycap          = rcar_drif_querycap,
+
+	.vidioc_enum_fmt_sdr_cap  = rcar_drif_enum_fmt_sdr_cap,
+	.vidioc_g_fmt_sdr_cap     = rcar_drif_g_fmt_sdr_cap,
+	.vidioc_s_fmt_sdr_cap     = rcar_drif_s_fmt_sdr_cap,
+	.vidioc_try_fmt_sdr_cap   = rcar_drif_try_fmt_sdr_cap,
+
+	.vidioc_reqbufs           = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs       = vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf       = vb2_ioctl_prepare_buf,
+	.vidioc_querybuf          = vb2_ioctl_querybuf,
+	.vidioc_qbuf              = vb2_ioctl_qbuf,
+	.vidioc_dqbuf             = vb2_ioctl_dqbuf,
+
+	.vidioc_streamon          = vb2_ioctl_streamon,
+	.vidioc_streamoff         = vb2_ioctl_streamoff,
+
+	.vidioc_s_frequency       = rcar_drif_s_frequency,
+	.vidioc_g_frequency       = rcar_drif_g_frequency,
+	.vidioc_s_tuner		  = rcar_drif_s_tuner,
+	.vidioc_g_tuner		  = rcar_drif_g_tuner,
+	.vidioc_enum_freq_bands   = rcar_drif_enum_freq_bands,
+	.vidioc_subscribe_event   = v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+	.vidioc_log_status        = v4l2_ctrl_log_status,
+};
+
+static const struct v4l2_file_operations rcar_drif_fops = {
+	.owner                    = THIS_MODULE,
+	.open                     = v4l2_fh_open,
+	.release                  = vb2_fop_release,
+	.read                     = vb2_fop_read,
+	.poll                     = vb2_fop_poll,
+	.mmap                     = vb2_fop_mmap,
+	.unlocked_ioctl           = video_ioctl2,
+};
+
+static struct video_device rcar_drif_vdev = {
+	.name                     = "R-Car DRIF",
+	.release                  = video_device_release_empty,
+	.fops                     = &rcar_drif_fops,
+	.ioctl_ops                = &rcar_drif_ioctl_ops,
+};
+
+static int rcar_drif_notify_bound(struct v4l2_async_notifier *notifier,
+				   struct v4l2_subdev *subdev,
+				   struct v4l2_async_subdev *asd)
+{
+	struct rcar_drif_chan *ch =
+		container_of(notifier, struct rcar_drif_chan, notifier);
+
+	/* Nothing to do at this point */
+	rdrif_dbg(2, ch, "bound asd: %s\n", asd->match.of.node->name);
+	return 0;
+}
+
+/* Sub-device registered notification callback */
+static int rcar_drif_notify_complete(struct v4l2_async_notifier *notifier)
+{
+	struct rcar_drif_chan *ch =
+		container_of(notifier, struct rcar_drif_chan, notifier);
+	struct v4l2_subdev *sd, *tmp;
+	int ret;
+
+	v4l2_ctrl_handler_init(&ch->ctrl_hdl, 10);
+	ch->v4l2_dev.ctrl_handler = &ch->ctrl_hdl;
+
+	ret = v4l2_device_register_subdev_nodes(&ch->v4l2_dev);
+	if (ret) {
+		rdrif_err(ch, "failed register subdev nodes ret %d\n", ret);
+		return ret;
+	}
+
+	for_each_rcar_drif_subdev(sd, tmp, ch) {
+		ret = v4l2_ctrl_add_handler(ch->v4l2_dev.ctrl_handler,
+					    sd->ctrl_handler, NULL);
+		if (ret) {
+			rdrif_err(ch, "failed ctrl add hdlr ret %d\n", ret);
+			return ret;
+		}
+	}
+	rdrif_dbg(2, ch, "notify complete\n");
+	return 0;
+}
+
+/* Parse sub-devs (tuner) to find a matching device */
+static int rcar_drif_parse_subdevs(struct device *dev,
+				   struct v4l2_async_notifier *notifier)
+{
+	struct device_node *node = NULL;
+
+	notifier->subdevs = devm_kzalloc(dev, sizeof(*notifier->subdevs),
+					 GFP_KERNEL);
+	if (!notifier->subdevs)
+		return -ENOMEM;
+
+	node = of_graph_get_next_endpoint(dev->of_node, node);
+	if (node) {
+		struct rcar_drif_async_subdev *rsd;
+
+		rsd = devm_kzalloc(dev, sizeof(*rsd), GFP_KERNEL);
+		if (!rsd) {
+			of_node_put(node);
+			return -ENOMEM;
+		}
+
+		notifier->subdevs[notifier->num_subdevs] = &rsd->asd;
+		rsd->asd.match.of.node = of_graph_get_remote_port_parent(node);
+		of_node_put(node);
+		if (!rsd->asd.match.of.node) {
+			dev_warn(dev, "bad remote port parent\n");
+			return -EINVAL;
+		}
+
+		rsd->asd.match_type = V4L2_ASYNC_MATCH_OF;
+		notifier->num_subdevs++;
+	}
+	return 0;
+}
+
+/* SIRMDR1 configuration */
+static int rcar_drif_validate_syncmd(struct rcar_drif_chan *ch, u32 val)
+{
+	if (val > 1) {
+		dev_err(ch->dev, "invalid syncmd %u using L/R mode\n", val);
+		return -EINVAL;
+	}
+
+	ch->mdr1 &= ~(3 << 28);	/* Clear current settings */
+	if (val == 0)
+		ch->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCMD_FRAME;
+	else
+		ch->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCMD_LR;
+	return 0;
+}
+
+/* Get the dtdl or syncdl bits as in MSIOF */
+static u32 rcar_drif_get_dtdl_or_syncdl_bits(u32 dtdl_or_syncdl)
+{
+	/*
+	 * DTDL/SYNCDL bit	: dtdl/syncdl
+	 * b'000		: 0
+	 * b'001		: 100
+	 * b'010		: 200
+	 * b'011 (SYNCDL only)	: 300
+	 * b'101		: 50
+	 * b'110		: 150
+	 */
+	if (dtdl_or_syncdl % 100)
+		return dtdl_or_syncdl / 100 + 5;
+	else
+		return dtdl_or_syncdl / 100;
+}
+
+static int rcar_drif_validate_dtdl_syncdl(struct rcar_drif_chan *ch)
+{
+	struct device_node *np = ch->dev->of_node;
+	u32 dtdl = 100, syncdl = 0;
+
+	ch->mdr1 |= RCAR_DRIF_SIRMDR1_DTDL_1 | RCAR_DRIF_SIRMDR1_SYNCDL_0;
+	of_property_read_u32(np, "renesas,dtdl", &dtdl);
+	of_property_read_u32(np, "renesas,syncdl", &syncdl);
+
+	/* Sanity checks */
+	if (dtdl > 200 || syncdl > 300) {
+		dev_err(ch->dev, "invalid dtdl %u/syncdl %u\n", dtdl, syncdl);
+		return -EINVAL;
+	}
+	if ((dtdl + syncdl) % 100) {
+		dev_err(ch->dev, "sum of dtdl %u & syncdl %u not OK\n",
+			dtdl, syncdl);
+		return -EINVAL;
+	}
+	ch->mdr1 &= ~(7 << 20) & ~(7 << 16);	/* Clear current settings */
+	ch->mdr1 |= rcar_drif_get_dtdl_or_syncdl_bits(dtdl) << 20;
+	ch->mdr1 |= rcar_drif_get_dtdl_or_syncdl_bits(syncdl) << 16;
+	return 0;
+}
+
+static int rcar_drif_parse_properties(struct rcar_drif_chan *ch)
+{
+	struct device_node *np = ch->dev->of_node;
+	u32 syncmd;
+	int ret;
+
+	/* Set the defaults and check for overrides */
+	ch->mdr1 = RCAR_DRIF_SIRMDR1_SYNCMD_LR;
+	if (!of_property_read_u32(np, "renesas,syncmd", &syncmd)) {
+		ret = rcar_drif_validate_syncmd(ch, syncmd);
+		if (ret)
+			return ret;
+	}
+
+	if (of_find_property(np, "renesas,lsb-first", NULL))
+		ch->mdr1 |= RCAR_DRIF_SIRMDR1_LSB_FIRST;
+	else
+		ch->mdr1 |= RCAR_DRIF_SIRMDR1_MSB_FIRST;
+
+	if (of_find_property(np, "renesas,syncac-pol-high", NULL))
+		ch->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCAC_POL_HIGH;
+	else
+		ch->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCAC_POL_LOW;
+
+	return rcar_drif_validate_dtdl_syncdl(ch);
+}
+
+static u32 rcar_drif_enum_sub_channels(struct platform_device *pdev,
+					struct platform_device *s_pdev[])
+{
+	struct device_node *s_np;
+	u32 hw_schans_mask = 0;
+	unsigned int i;
+
+	for (i = 0; i < RCAR_DRIF_MAX_SUBCHANS; i++) {
+		s_np = of_parse_phandle(pdev->dev.of_node, "sub-channels", i);
+		if (s_np && of_device_is_available(s_np)) {
+			s_pdev[i] = of_find_device_by_node(s_np);
+			if (s_pdev[i]) {
+				hw_schans_mask |= BIT(i);
+				dev_dbg(&s_pdev[i]->dev, "schan%u ok\n", i);
+			}
+		}
+	}
+	return hw_schans_mask;
+}
+
+static int rcar_drif_probe(struct platform_device *pdev)
+{
+	struct platform_device *s_pdev[RCAR_DRIF_MAX_SUBCHANS];
+	unsigned long hw_schans_mask;
+	struct rcar_drif_chan *ch;
+	unsigned int i;
+	int ret;
+
+	/*
+	 * Sub-channel resources are managed by the parent channel instance.
+	 * The sub-channel instance helps only in registering with power domain
+	 * to aid in run-time pm support
+	 */
+	if (!of_find_property(pdev->dev.of_node, "sub-channels", NULL))
+		return 0;
+
+	/* Parent channel instance */
+	hw_schans_mask = rcar_drif_enum_sub_channels(pdev, s_pdev);
+	if (!hw_schans_mask) {
+		dev_err(&pdev->dev, "no sub-channels enabled\n");
+		return -ENODEV;
+	}
+
+
+	/* Reserve memory for driver structure */
+	ch = devm_kzalloc(&pdev->dev, sizeof(*ch), GFP_KERNEL);
+	if (!ch) {
+		ret = PTR_ERR(ch);
+		dev_err(&pdev->dev, "failed alloc drif context\n");
+		return ret;
+	}
+	ch->dev = &pdev->dev;
+
+	/* Parse device tree optional properties */
+	ret = rcar_drif_parse_properties(ch);
+	if (ret)
+		return ret;
+
+	dev_dbg(ch->dev, "parsed mdr1 0x%08x\n", ch->mdr1);
+
+	/* Setup enabled sub-channels */
+	for_each_rcar_drif_subchannel(i, &hw_schans_mask) {
+		struct clk *clkp;
+		struct resource	*res;
+		void __iomem *base;
+
+		/* Peripheral clock */
+		clkp = devm_clk_get(&s_pdev[i]->dev, "fck");
+		if (IS_ERR(clkp)) {
+			ret = PTR_ERR(clkp);
+			dev_err(&s_pdev[i]->dev, "clk get failed (%d)\n", ret);
+			return ret;
+		}
+
+		/* Register map */
+		res = platform_get_resource(s_pdev[i], IORESOURCE_MEM, 0);
+		base = devm_ioremap_resource(&s_pdev[i]->dev, res);
+		if (IS_ERR(base)) {
+			ret = PTR_ERR(base);
+			dev_err(&s_pdev[i]->dev, "ioremap failed (%d)\n", ret);
+			return ret;
+		}
+
+		/* Reserve memory for enabled sub-channel */
+		ch->sch[i] = devm_kzalloc(&pdev->dev, sizeof(*ch->sch[i]),
+					  GFP_KERNEL);
+		if (!ch->sch[i]) {
+			ret = PTR_ERR(ch);
+			dev_err(&s_pdev[i]->dev, "failed alloc sub-channel\n");
+			return ret;
+		}
+		ch->sch[i]->pdev = s_pdev[i];
+		ch->sch[i]->clkp = clkp;
+		ch->sch[i]->base = base;
+		ch->sch[i]->num = i;
+		ch->sch[i]->start = res->start;
+		ch->sch[i]->parent = ch;
+		ch->num_hw_schans++;
+	}
+	ch->hw_schans_mask = hw_schans_mask;
+
+	/* Validate any supported format for enabled sub-channels */
+	ret = rcar_drif_set_default_format(ch);
+	if (ret)
+		return ret;
+
+	/* Set defaults */
+	ch->num_hwbufs = RCAR_DRIF_DEFAULT_NUM_HWBUFS;
+	ch->hwbuf_size = RCAR_DRIF_DEFAULT_HWBUF_SIZE;
+
+	mutex_init(&ch->v4l2_mutex);
+	mutex_init(&ch->vb_queue_mutex);
+	spin_lock_init(&ch->queued_bufs_lock);
+	INIT_LIST_HEAD(&ch->queued_bufs);
+
+	/* Init videobuf2 queue structure */
+	ch->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
+	ch->vb_queue.io_modes = VB2_READ | VB2_MMAP | VB2_DMABUF;
+	ch->vb_queue.drv_priv = ch;
+	ch->vb_queue.buf_struct_size = sizeof(struct rcar_drif_frame_buf);
+	ch->vb_queue.ops = &rcar_drif_vb2_ops;
+	ch->vb_queue.mem_ops = &vb2_vmalloc_memops;
+	ch->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+
+	/* Init videobuf2 queue */
+	ret = vb2_queue_init(&ch->vb_queue);
+	if (ret) {
+		dev_err(ch->dev, "could not initialize vb2 queue\n");
+		return ret;
+	}
+
+	/* Init video_device structure */
+	ch->vdev = rcar_drif_vdev;
+	ch->vdev.lock = &ch->v4l2_mutex;
+	ch->vdev.queue = &ch->vb_queue;
+	ch->vdev.queue->lock = &ch->vb_queue_mutex;
+	ch->vdev.ctrl_handler = &ch->ctrl_hdl;
+	video_set_drvdata(&ch->vdev, ch);
+
+	/* Register the v4l2_device */
+	ret = v4l2_device_register(&pdev->dev, &ch->v4l2_dev);
+	if (ret) {
+		dev_err(ch->dev, "failed v4l2_device_register. ret %d\n", ret);
+		return ret;
+	}
+
+	ch->vdev.v4l2_dev = &ch->v4l2_dev;
+
+	/*
+	 * Parse subdevs after v4l2_device_register because if the subdev
+	 * is already probed, bound and complete will be called immediately
+	 */
+	ret = rcar_drif_parse_subdevs(&pdev->dev, &ch->notifier);
+	if (ret)
+		goto err_unreg_v4l2;
+
+	ch->notifier.bound = rcar_drif_notify_bound;
+	ch->notifier.complete = rcar_drif_notify_complete;
+
+	/* Register notifier */
+	ret = v4l2_async_notifier_register(&ch->v4l2_dev, &ch->notifier);
+	if (ret < 0) {
+		dev_err(ch->dev, "notifier registration failed\n");
+		goto err_unreg_v4l2;
+	}
+
+	/* Register SDR device */
+	ret = video_register_device(&ch->vdev, VFL_TYPE_SDR, -1);
+	if (ret) {
+		dev_err(ch->dev, "failed video_register_device. ret %d\n", ret);
+		goto err_unreg_notif;
+	}
+
+	platform_set_drvdata(pdev, ch);
+	dev_notice(ch->dev, "probed\n");
+	return 0;
+
+err_unreg_notif:
+	v4l2_async_notifier_unregister(&ch->notifier);
+err_unreg_v4l2:
+	v4l2_device_unregister(&ch->v4l2_dev);
+	return ret;
+}
+
+static int rcar_drif_remove(struct platform_device *pdev)
+{
+	struct rcar_drif_chan *ch = platform_get_drvdata(pdev);
+
+	if (!ch)
+		return 0;
+
+	/* Parent channel instance */
+	ch = platform_get_drvdata(pdev);
+	v4l2_ctrl_handler_free(ch->v4l2_dev.ctrl_handler);
+	v4l2_async_notifier_unregister(&ch->notifier);
+	v4l2_device_unregister(&ch->v4l2_dev);
+	video_unregister_device(&ch->vdev);
+	dev_notice(ch->dev, "removed\n");
+	return 0;
+}
+
+static int __maybe_unused rcar_drif_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int __maybe_unused rcar_drif_resume(struct device *dev)
+{
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(rcar_drif_pm_ops, rcar_drif_suspend,
+			 rcar_drif_resume);
+
+static const struct of_device_id rcar_drif_of_table[] = {
+	{ .compatible = "renesas,rcar-gen3-drif" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, rcar_drif_of_table);
+
+#define RCAR_DRIF_DRV_NAME "rcar_drif"
+static struct platform_driver rcar_drif_driver = {
+	.driver = {
+		.name = RCAR_DRIF_DRV_NAME,
+		.of_match_table = of_match_ptr(rcar_drif_of_table),
+		.pm = &rcar_drif_pm_ops,
+		},
+	.probe = rcar_drif_probe,
+	.remove = rcar_drif_remove,
+};
+
+module_platform_driver(rcar_drif_driver);
+
+MODULE_DESCRIPTION("Renesas R-Car Gen3 DRIF driver");
+MODULE_ALIAS("platform:" RCAR_DRIF_DRV_NAME);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>");
-- 
1.9.1

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

* Re: [PATCH 4/5] doc_rst: media: New SDR formats SC16, SC18 & SC20
  2016-11-09 15:44     ` Ramesh Shanmugasundaram
  (?)
@ 2016-11-10  8:18     ` Laurent Pinchart
  -1 siblings, 0 replies; 60+ messages in thread
From: Laurent Pinchart @ 2016-11-10  8:18 UTC (permalink / raw)
  To: Ramesh Shanmugasundaram
  Cc: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope,
	chris.paterson2, geert+renesas, linux-media, devicetree,
	linux-renesas-soc

Hi Ramesh,

Thank you for the patch.

On Wednesday 09 Nov 2016 15:44:43 Ramesh Shanmugasundaram wrote:
> This patch adds documentation for the three new SDR formats
> 
> V4L2_SDR_FMT_SCU16BE
> V4L2_SDR_FMT_SCU18BE
> V4L2_SDR_FMT_SCU20BE
> 
> Signed-off-by: Ramesh Shanmugasundaram
> <ramesh.shanmugasundaram@bp.renesas.com>
> ---
>  .../media/uapi/v4l/pixfmt-sdr-scu16be.rst          | 80 ++++++++++++++++++
>  .../media/uapi/v4l/pixfmt-sdr-scu18be.rst          | 80 ++++++++++++++++++
>  .../media/uapi/v4l/pixfmt-sdr-scu20be.rst          | 80 ++++++++++++++++++
>  Documentation/media/uapi/v4l/sdr-formats.rst       |  3 +
>  4 files changed, 243 insertions(+)
>  create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-scu16be.rst
>  create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-scu18be.rst
>  create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-scu20be.rst
> 
> diff --git a/Documentation/media/uapi/v4l/pixfmt-sdr-scu16be.rst
> b/Documentation/media/uapi/v4l/pixfmt-sdr-scu16be.rst new file mode 100644
> index 0000000..7525378
> --- /dev/null
> +++ b/Documentation/media/uapi/v4l/pixfmt-sdr-scu16be.rst
> @@ -0,0 +1,80 @@
> +.. -*- coding: utf-8; mode: rst -*-
> +
> +.. _V4L2-SDR-FMT-SCU16BE:
> +
> +******************************
> +V4L2_SDR_FMT_SCU16BE ('SC16')
> +******************************
> +
> +Sliced complex unsigned 16-bit big endian IQ sample
> +
> +Description
> +===========
> +
> +This format contains a sequence of complex number samples. Each complex
> +number consist of two parts called In-phase and Quadrature (IQ). Both I
> +and Q are represented as a 16 bit unsigned big endian number stored in
> +32 bit space. The remaining unused bits within the 32 bit space will be
> +padded with 0. I value starts first and Q value starts at an offset
> +equalling half of the buffer size (i.e.) offset = buffersize/2. Out of
> +the 16 bits, bit 15:2 (14 bit) is data and bit 1:0 (2 bit) can be any
> +value.

I've pinged Antti and Hans regarding single buffer vs. multiplanar, let's try 
to reach an agreement there.

> +
> +**Byte Order.**
> +Each cell is one byte.
> +
> +.. flat-table::
> +    :header-rows:  1
> +    :stub-columns: 0
> +
> +    * -  Offset:
> +

In the meantime, you can remove all the blank lines between table rows :-)

> +      -  Byte B0
> +
> +      -  Byte B1
> +
> +      -  Byte B2
> +
> +      -  Byte B3

[snip]

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 5/5] media: platform: rcar_drif: Add DRIF support
  2016-11-09 15:44 ` [PATCH 5/5] media: platform: rcar_drif: Add DRIF support Ramesh Shanmugasundaram
@ 2016-11-10  9:22   ` Laurent Pinchart
  2016-11-14 19:52       ` Rob Herring
  2016-11-11 13:38   ` Hans Verkuil
  1 sibling, 1 reply; 60+ messages in thread
From: Laurent Pinchart @ 2016-11-10  9:22 UTC (permalink / raw)
  To: Ramesh Shanmugasundaram
  Cc: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope,
	chris.paterson2, geert+renesas, linux-media, devicetree,
	linux-renesas-soc

Hi Ramesh,

Thank you for the patch.

On Wednesday 09 Nov 2016 15:44:44 Ramesh Shanmugasundaram wrote:
> This patch adds Digital Radio Interface (DRIF) support to R-Car Gen3 SoCs.
> The driver exposes each instance of DRIF as a V4L2 SDR device. A DRIF
> device represents a channel and each channel can have one or two
> sub-channels respectively depending on the target board.
> 
> DRIF supports only Rx functionality. It receives samples from a RF
> frontend tuner chip it is interfaced with. The combination of DRIF and the
> tuner device, which is registered as a sub-device, determines the receive
> sample rate and format.
> 
> In order to be compliant as a V4L2 SDR device, DRIF needs to bind with
> the tuner device, which can be provided by a third party vendor. DRIF acts
> as a slave device and the tuner device acts as a master transmitting the
> samples. The driver allows asynchronous binding of a tuner device that
> is registered as a v4l2 sub-device. The driver can learn about the tuner
> it is interfaced with based on port endpoint properties of the device in
> device tree. The V4L2 SDR device inherits the controls exposed by the
> tuner device.
> 
> The device can also be configured to use either one or both of the data
> pins at runtime based on the master (tuner) configuration.
> 
> Signed-off-by: Ramesh Shanmugasundaram
> <ramesh.shanmugasundaram@bp.renesas.com> ---
>  .../devicetree/bindings/media/renesas,drif.txt     |  136 ++
>  drivers/media/platform/Kconfig                     |   25 +
>  drivers/media/platform/Makefile                    |    1 +
>  drivers/media/platform/rcar_drif.c                 | 1574
> ++++++++++++++++++++ 4 files changed, 1736 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/renesas,drif.txt
> create mode 100644 drivers/media/platform/rcar_drif.c
> 
> diff --git a/Documentation/devicetree/bindings/media/renesas,drif.txt
> b/Documentation/devicetree/bindings/media/renesas,drif.txt new file mode
> 100644
> index 0000000..d65368a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> @@ -0,0 +1,136 @@
> +Renesas R-Car Gen3 Digital Radio Interface controller (DRIF)
> +------------------------------------------------------------
> +
> +R-Car Gen3 DRIF is a serial slave device. It interfaces with a master
> +device as shown below
> +
> ++---------------------+                +---------------------+
> +|                     |-----SCK------->|CLK                  |
> +|       Master        |-----SS-------->|SYNC  DRIFn (slave)  |
> +|                     |-----SD0------->|D0                   |
> +|                     |-----SD1------->|D1                   |
> ++---------------------+                +---------------------+
> +
> +Each DRIF channel (drifn) consists of two sub-channels (drifn0 & drifn1).
> +The sub-channels are like two individual channels in itself that share the
> +common CLK & SYNC. Each sub-channel has it's own dedicated resources like
> +irq, dma channels, address space & clock.
> +
> +The device tree model represents the channel and each of it's sub-channel
> +as a separate node. The parent channel ties the sub-channels together with
> +their phandles.
> +
> +Required properties of a sub-channel:
> +-------------------------------------
> +- compatible: "renesas,r8a7795-drif" if DRIF controller is a part of
> R8A7795 SoC.
> +	      "renesas,rcar-gen3-drif" for a generic R-Car Gen3 compatible
> device.
> +	      When compatible with the generic version, nodes must list the
> +	      SoC-specific version corresponding to the platform first
> +	      followed by the generic version.
> +- reg: offset and length of that sub-channel.
> +- interrupts: associated with that sub-channel.
> +- clocks: phandle and clock specifier of that sub-channel.
> +- clock-names: clock input name string: "fck".
> +- dmas: phandles to the DMA channel of that sub-channel.
> +- dma-names: names of the DMA channel: "rx".
> +
> +Optional properties of a sub-channel:
> +-------------------------------------
> +- power-domains: phandle to the respective power domain.
> +
> +Required properties of a channel:
> +---------------------------------
> +- pinctrl-0: pin control group to be used for this channel.
> +- pinctrl-names: must be "default".
> +- sub-channels : phandles to the two sub-channels.
> +
> +Optional properties of a channel:
> +---------------------------------
> +- port: child port node of a channel that defines the local and remote
> +        endpoints. The remote endpoint is assumed to be a tuner subdevice
> +	endpoint.
> +- renesas,syncmd       : sync mode
> +			 0 (Frame start sync pulse mode. 1-bit width pulse
> +			    indicates start of a frame)
> +			 1 (L/R sync or I2S mode) (default)
> +- renesas,lsb-first    : empty property indicates lsb bit is received
> first.
> +			 When not defined msb bit is received first (default)

Shouldn't those two properties be instead queried from the tuner at runtime 
through the V4L2 subdev API ?

> +- renesas,syncac-pol-high  : empty property indicates sync signal polarity.
> +			 When defined, active high or high->low sync signal.
> +			 When not defined, active low or low->high sync signal
> +			 (default)

This could be queried too, except that an inverter could be present on the 
board, so it has to be specified in DT. I would however try to standardize it 
the same way that hsync-active and vsync-active are standardized in 
Documentation/devicetree/bindings/media/video-interfaces.txt.

> +- renesas,dtdl         : delay between sync signal and start of reception.

Are this and the next property meant to account for PCB traces delays ?

> +			 Must contain one of the following values:
> +			 0   (no bit delay)
> +			 50  (0.5-clock-cycle delay)
> +			 100 (1-clock-cycle delay) (default)
> +			 150 (1.5-clock-cycle delay)
> +			 200 (2-clock-cycle delay)

How about specifying the property in half clock cycle units, from 0 to 4 ?

> +- renesas,syncdl       : delay between end of reception and sync signal
> edge.
> +			 Must contain one of the following values:
> +			 0   (no bit delay) (default)
> +			 50  (0.5-clock-cycle delay)
> +			 100 (1-clock-cycle delay)
> +			 150 (1.5-clock-cycle delay)
> +			 200 (2-clock-cycle delay)
> +			 300 (3-clock-cycle delay)
> +
> +Example
> +--------
> +
> +SoC common dtsi file
> +
> +		drif00: rif@e6f40000 {
> +			compatible = "renesas,r8a7795-drif",
> +				     "renesas,rcar-gen3-drif";
> +			reg = <0 0xe6f40000 0 0x64>;
> +			interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
> +			clocks = <&cpg CPG_MOD 515>;
> +			clock-names = "fck";
> +			dmas = <&dmac1 0x20>, <&dmac2 0x20>;
> +			dma-names = "rx", "rx";
> +			power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> +			status = "disabled";
> +		};
> +
> +		drif01: rif@e6f50000 {
> +			compatible = "renesas,r8a7795-drif",
> +				     "renesas,rcar-gen3-drif";
> +			reg = <0 0xe6f50000 0 0x64>;
> +			interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
> +			clocks = <&cpg CPG_MOD 514>;
> +			clock-names = "fck";
> +			dmas = <&dmac1 0x22>, <&dmac2 0x22>;
> +			dma-names = "rx", "rx";
> +			power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> +			status = "disabled";
> +		};
> +
> +		drif0: rif@0 {
> +			compatible = "renesas,r8a7795-drif",
> +				     "renesas,rcar-gen3-drif";
> +			sub-channels = <&drif00>, <&drif01>;
> +			status = "disabled";
> +		};

I'm afraid this really hurts my eyes, especially using the same compatible 
string for both the channel and sub-channel nodes.

We need to decide how to model the hardware in DT. Given that the two channels 
are mostly independent having separate DT nodes makes sense to me. However, as 
they share the clock and sync signals, somehow grouping them makes sense. I 
see three ways to do so, and there might be more.

1. Adding an extra DT node for the channels group, with phandles to the two 
channels. This is what you're proposing here.

2. Adding an extra DT node for the channels group, as a parent of the two 
channels.

3. Adding phandles to the channels, pointing to each other, or possibly a 
phandle from channel 0 pointing to channel 1.

Neither of these options seem perfect to me. I don't like option 1 as the 
group DT node really doesn't describe a hardware block. If we want to use a DT 
node to convey group information, option 2 seems better to me. However, it 
somehow abuses the DT parent-child model that is supposed to describe 
relationships from a control bus point of view. Option 3 has the drawback of 
not scaling properly, at least with phandles in both channels pointing to the 
other one.

Rob, Geert, tell me you have a fourth idea I haven't thought of that would 
solve all those problems :-)

> +Board specific dts file
> +
> +&drif00 {
> +	status = "okay";
> +};
> +
> +&drif01 {
> +	status = "okay";
> +};
> +
> +&drif0 {
> +	pinctrl-0 = <&drif0_pins>;
> +	pinctrl-names = "default";
> +	renesas,syncac-pol-high;
> +	status = "okay";
> +	port {
> +		drif0_ep: endpoint {
> +		     remote-endpoint = <&tuner_subdev_ep>;
> +		};
> +	};
> +};

[snip]

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 2/5] media: i2c: max2175: Add MAX2175 support
  2016-11-09 15:44     ` Ramesh Shanmugasundaram
  (?)
@ 2016-11-11  7:16     ` Antti Palosaari
       [not found]       ` <d9e9aac2-001c-491b-db34-a9eed6240722-X3B1VOXEql0@public.gmane.org>
  -1 siblings, 1 reply; 60+ messages in thread
From: Antti Palosaari @ 2016-11-11  7:16 UTC (permalink / raw)
  To: Ramesh Shanmugasundaram, robh+dt, mark.rutland, mchehab,
	hverkuil, sakari.ailus
  Cc: chris.paterson2, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc

Hello

On 11/09/2016 05:44 PM, Ramesh Shanmugasundaram wrote:

> +static int max2175_set_lo_freq(struct max2175 *ctx, u64 lo_freq)
> +{
> +	u64 scaled_lo_freq, scaled_npf, scaled_integer, scaled_fraction;
> +	u32 frac_desired, int_desired, lo_mult = 1;
> +	const u32 scale_factor = 1000000U;
> +	u8 loband_bits = 0, vcodiv_bits = 0;
> +	enum max2175_band band;
> +	int ret;
> +
> +	/* Scale to larger number for precision */
> +	scaled_lo_freq = lo_freq * scale_factor * 100;
> +	band = max2175_read_bits(ctx, 5, 1, 0);
> +
> +	mxm_dbg(ctx, "set_lo_freq: scaled lo_freq %llu lo_freq %llu band %d\n",
> +		scaled_lo_freq, lo_freq, band);
> +
> +	switch (band) {
> +	case MAX2175_BAND_AM:
> +		if (max2175_read_bit(ctx, 5, 7) == 0)
> +			lo_mult = 16;

else is lo_mult = 1. No idea if it is correct, but sounds very small 
output divider for low freq like am band. And on the other-hand local 
oscillator output divider, which I expect this to be, is usually 2 or more.

> +		break;
> +	case MAX2175_BAND_FM:
> +		if (lo_freq <= 74700000) {
> +			lo_mult = 16;

No meaning as you set it later 8.

> +		} else if (lo_freq > 74700000 && lo_freq <= 110000000) {
> +			loband_bits = 1;
> +		} else {
> +			loband_bits = 1;
> +			vcodiv_bits = 3;
> +		}
> +		lo_mult = 8;
> +		break;
> +	case MAX2175_BAND_VHF:
> +		if (lo_freq <= 210000000) {
> +			loband_bits = 2;
> +			vcodiv_bits = 2;
> +		} else {
> +			loband_bits = 2;
> +			vcodiv_bits = 1;
> +		}
> +		lo_mult = 4;
> +		break;
> +	default:
> +		loband_bits = 3;
> +		vcodiv_bits = 2;
> +		lo_mult = 2;
> +		break;
> +	}
> +
> +	if (band == MAX2175_BAND_L)
> +		scaled_npf = div_u64(div_u64(scaled_lo_freq, ctx->xtal_freq),
> +				     lo_mult);
> +	else
> +		scaled_npf = div_u64(scaled_lo_freq, ctx->xtal_freq) * lo_mult;
> +
> +	scaled_npf = div_u64(scaled_npf, 100);
> +	scaled_integer = div_u64(scaled_npf, scale_factor) * scale_factor;
> +	int_desired = div_u64(scaled_npf, scale_factor);
> +	scaled_fraction = scaled_npf - scaled_integer;
> +	frac_desired = div_u64(scaled_fraction << 20, scale_factor);
> +
> +	/* Check CSM is not busy */
> +	ret = max2175_poll_csm_ready(ctx);
> +	if (ret)
> +		return ret;
> +
> +	mxm_dbg(ctx, "loband %u vcodiv %u lo_mult %u scaled_npf %llu\n",
> +		loband_bits, vcodiv_bits, lo_mult, scaled_npf);
> +	mxm_dbg(ctx, "scaled int %llu frac %llu desired int %u frac %u\n",
> +		scaled_integer, scaled_fraction, int_desired, frac_desired);
> +
> +	/* Write the calculated values to the appropriate registers */
> +	max2175_write(ctx, 1, int_desired);
> +	max2175_write_bits(ctx, 2, 3, 0, (frac_desired >> 16) & 0xf);
> +	max2175_write(ctx, 3, frac_desired >> 8);
> +	max2175_write(ctx, 4, frac_desired);
> +	max2175_write_bits(ctx, 5, 3, 2, loband_bits);
> +	max2175_write_bits(ctx, 6, 7, 6, vcodiv_bits);
> +	return ret;
> +}

That synthesizer config is hard to understand. It seems to be 
fractional-N, with configurable N, K and output divider - like a school 
book example.

               +----------------------------+
               v                            |
      Fref   +----+     +-------+         +------+
     ------> | PD | --> |  VCO  | ------> | /N.F |
             +----+     +-------+         +------+
                          |
                          |
                          v
                        +-------+  Fout
                        | /Rout | ------>
                        +-------+

I made following look-up table in order to understand it:

band      lo freq band vcodiv div_out
   AM  <  50000000    0      0      16 // reg 5 bit 7 ?
   FM  <  74700000    0      0      16
   FM  < 110000000    1      0       8
   FM  < 160000000    1      3       8
  VHF  < 210000000    2      2       4
  VHF  < 600000000    2      1       4
    L  <2000000000    3      2       2

"vcodiv" looks unrelated to synth calculation, dunno what it is.

One which makes calculation very complex looking is that it is based of 
floating point calculus. On integer mathematics you should replace 
fractional part with fractional modulus (usually letter "K" is used for 
fractional modulus on PLL calc).

So that ends up something like:
1) select suitable lo output divider from desired output frequency
2) calculate vco frequency
3) convert vco frequency to N and K
* N = Fvco/Fref
* K = Fvco%Fref
4) convert K to control word (looks like << 20)
5) program values

Result should be calculus without scaling.

regards
Antti


-- 
http://palosaari.fi/

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

* Re: [PATCH 2/5] media: i2c: max2175: Add MAX2175 support
  2016-11-11  7:16     ` Antti Palosaari
@ 2016-11-11 11:50           ` Laurent Pinchart
  0 siblings, 0 replies; 60+ messages in thread
From: Laurent Pinchart @ 2016-11-11 11:50 UTC (permalink / raw)
  To: Antti Palosaari
  Cc: Ramesh Shanmugasundaram, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, mchehab-DgEjT+Ai2ygdnm+yROfE0A,
	hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
	sakari.ailus-VuQAYsv1563Yd54FQh9/CA,
	chris.paterson2-zM6kxYcvzFBBDgjK7y7TUQ,
	geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ,
	linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA

Hi Antti,

On Friday 11 Nov 2016 09:16:04 Antti Palosaari wrote:
> Hello
> 
> On 11/09/2016 05:44 PM, Ramesh Shanmugasundaram wrote:
> > +static int max2175_set_lo_freq(struct max2175 *ctx, u64 lo_freq)
> > +{
> > +	u64 scaled_lo_freq, scaled_npf, scaled_integer, scaled_fraction;
> > +	u32 frac_desired, int_desired, lo_mult = 1;
> > +	const u32 scale_factor = 1000000U;
> > +	u8 loband_bits = 0, vcodiv_bits = 0;
> > +	enum max2175_band band;
> > +	int ret;
> > +
> > +	/* Scale to larger number for precision */
> > +	scaled_lo_freq = lo_freq * scale_factor * 100;
> > +	band = max2175_read_bits(ctx, 5, 1, 0);
> > +
> > +	mxm_dbg(ctx, "set_lo_freq: scaled lo_freq %llu lo_freq %llu band 
%d\n",
> > +		scaled_lo_freq, lo_freq, band);
> > +
> > +	switch (band) {
> > +	case MAX2175_BAND_AM:
> > +		if (max2175_read_bit(ctx, 5, 7) == 0)
> > +			lo_mult = 16;
> 
> else is lo_mult = 1. No idea if it is correct, but sounds very small
> output divider for low freq like am band. And on the other-hand local
> oscillator output divider, which I expect this to be, is usually 2 or more.
> 
> > +		break;
> > +	case MAX2175_BAND_FM:
> > +		if (lo_freq <= 74700000) {
> > +			lo_mult = 16;
> 
> No meaning as you set it later 8.
> 
> > +		} else if (lo_freq > 74700000 && lo_freq <= 110000000) {
> > +			loband_bits = 1;
> > +		} else {
> > +			loband_bits = 1;
> > +			vcodiv_bits = 3;
> > +		}
> > +		lo_mult = 8;
> > +		break;
> > +	case MAX2175_BAND_VHF:
> > +		if (lo_freq <= 210000000) {
> > +			loband_bits = 2;
> > +			vcodiv_bits = 2;
> > +		} else {
> > +			loband_bits = 2;
> > +			vcodiv_bits = 1;
> > +		}
> > +		lo_mult = 4;
> > +		break;
> > +	default:
> > +		loband_bits = 3;
> > +		vcodiv_bits = 2;
> > +		lo_mult = 2;
> > +		break;
> > +	}
> > +
> > +	if (band == MAX2175_BAND_L)
> > +		scaled_npf = div_u64(div_u64(scaled_lo_freq, ctx->xtal_freq),
> > +				     lo_mult);
> > +	else
> > +		scaled_npf = div_u64(scaled_lo_freq, ctx->xtal_freq) * 
lo_mult;
> > +
> > +	scaled_npf = div_u64(scaled_npf, 100);
> > +	scaled_integer = div_u64(scaled_npf, scale_factor) * scale_factor;
> > +	int_desired = div_u64(scaled_npf, scale_factor);
> > +	scaled_fraction = scaled_npf - scaled_integer;
> > +	frac_desired = div_u64(scaled_fraction << 20, scale_factor);
> > +
> > +	/* Check CSM is not busy */
> > +	ret = max2175_poll_csm_ready(ctx);
> > +	if (ret)
> > +		return ret;
> > +
> > +	mxm_dbg(ctx, "loband %u vcodiv %u lo_mult %u scaled_npf %llu\n",
> > +		loband_bits, vcodiv_bits, lo_mult, scaled_npf);
> > +	mxm_dbg(ctx, "scaled int %llu frac %llu desired int %u frac %u\n",
> > +		scaled_integer, scaled_fraction, int_desired, frac_desired);
> > +
> > +	/* Write the calculated values to the appropriate registers */
> > +	max2175_write(ctx, 1, int_desired);
> > +	max2175_write_bits(ctx, 2, 3, 0, (frac_desired >> 16) & 0xf);
> > +	max2175_write(ctx, 3, frac_desired >> 8);
> > +	max2175_write(ctx, 4, frac_desired);
> > +	max2175_write_bits(ctx, 5, 3, 2, loband_bits);
> > +	max2175_write_bits(ctx, 6, 7, 6, vcodiv_bits);
> > +	return ret;
> > +}
> 
> That synthesizer config is hard to understand. It seems to be
> fractional-N, with configurable N, K and output divider - like a school
> book example.
> 
>                +----------------------------+
>                v                            |
>       Fref   +----+     +-------+         +------+
>      ------> | PD | --> |  VCO  | ------> | /N.F |
>              +----+     +-------+         +------+
>                           |
>                           |
>                           v
>                         +-------+  Fout
>                         | /Rout | ------>
>                         +-------+
>
> I made following look-up table in order to understand it:
> 
> band      lo freq band vcodiv div_out
>    AM  <  50000000    0      0      16 // reg 5 bit 7 ?
>    FM  <  74700000    0      0      16
>    FM  < 110000000    1      0       8
>    FM  < 160000000    1      3       8
>   VHF  < 210000000    2      2       4
>   VHF  < 600000000    2      1       4
>     L  <2000000000    3      2       2
> 
> "vcodiv" looks unrelated to synth calculation, dunno what it is.
> 
> One which makes calculation very complex looking is that it is based of
> floating point calculus. On integer mathematics you should replace
> fractional part with fractional modulus (usually letter "K" is used for
> fractional modulus on PLL calc).
> 
> So that ends up something like:
> 1) select suitable lo output divider from desired output frequency
> 2) calculate vco frequency
> 3) convert vco frequency to N and K
> * N = Fvco/Fref
> * K = Fvco%Fref
> 4) convert K to control word (looks like << 20)
> 5) program values
> 
> Result should be calculus without scaling.

Thanks for reviewing this part.

I'd like to add that we already have two PLL helpers in the media subsystem, 
in drivers/media/i2c/aptina-pll.c and drivers/media/i2c/smiapp-pll.c. As the 
PLL used here seems to be a classic one, it would make sense to also extract 
the code in a helper function that could be shared between drivers.

-- 
Regards,

Laurent Pinchart

--
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 2/5] media: i2c: max2175: Add MAX2175 support
@ 2016-11-11 11:50           ` Laurent Pinchart
  0 siblings, 0 replies; 60+ messages in thread
From: Laurent Pinchart @ 2016-11-11 11:50 UTC (permalink / raw)
  To: Antti Palosaari
  Cc: Ramesh Shanmugasundaram, robh+dt, mark.rutland, mchehab,
	hverkuil, sakari.ailus, chris.paterson2, geert+renesas,
	linux-media, devicetree, linux-renesas-soc

Hi Antti,

On Friday 11 Nov 2016 09:16:04 Antti Palosaari wrote:
> Hello
> 
> On 11/09/2016 05:44 PM, Ramesh Shanmugasundaram wrote:
> > +static int max2175_set_lo_freq(struct max2175 *ctx, u64 lo_freq)
> > +{
> > +	u64 scaled_lo_freq, scaled_npf, scaled_integer, scaled_fraction;
> > +	u32 frac_desired, int_desired, lo_mult = 1;
> > +	const u32 scale_factor = 1000000U;
> > +	u8 loband_bits = 0, vcodiv_bits = 0;
> > +	enum max2175_band band;
> > +	int ret;
> > +
> > +	/* Scale to larger number for precision */
> > +	scaled_lo_freq = lo_freq * scale_factor * 100;
> > +	band = max2175_read_bits(ctx, 5, 1, 0);
> > +
> > +	mxm_dbg(ctx, "set_lo_freq: scaled lo_freq %llu lo_freq %llu band 
%d\n",
> > +		scaled_lo_freq, lo_freq, band);
> > +
> > +	switch (band) {
> > +	case MAX2175_BAND_AM:
> > +		if (max2175_read_bit(ctx, 5, 7) == 0)
> > +			lo_mult = 16;
> 
> else is lo_mult = 1. No idea if it is correct, but sounds very small
> output divider for low freq like am band. And on the other-hand local
> oscillator output divider, which I expect this to be, is usually 2 or more.
> 
> > +		break;
> > +	case MAX2175_BAND_FM:
> > +		if (lo_freq <= 74700000) {
> > +			lo_mult = 16;
> 
> No meaning as you set it later 8.
> 
> > +		} else if (lo_freq > 74700000 && lo_freq <= 110000000) {
> > +			loband_bits = 1;
> > +		} else {
> > +			loband_bits = 1;
> > +			vcodiv_bits = 3;
> > +		}
> > +		lo_mult = 8;
> > +		break;
> > +	case MAX2175_BAND_VHF:
> > +		if (lo_freq <= 210000000) {
> > +			loband_bits = 2;
> > +			vcodiv_bits = 2;
> > +		} else {
> > +			loband_bits = 2;
> > +			vcodiv_bits = 1;
> > +		}
> > +		lo_mult = 4;
> > +		break;
> > +	default:
> > +		loband_bits = 3;
> > +		vcodiv_bits = 2;
> > +		lo_mult = 2;
> > +		break;
> > +	}
> > +
> > +	if (band == MAX2175_BAND_L)
> > +		scaled_npf = div_u64(div_u64(scaled_lo_freq, ctx->xtal_freq),
> > +				     lo_mult);
> > +	else
> > +		scaled_npf = div_u64(scaled_lo_freq, ctx->xtal_freq) * 
lo_mult;
> > +
> > +	scaled_npf = div_u64(scaled_npf, 100);
> > +	scaled_integer = div_u64(scaled_npf, scale_factor) * scale_factor;
> > +	int_desired = div_u64(scaled_npf, scale_factor);
> > +	scaled_fraction = scaled_npf - scaled_integer;
> > +	frac_desired = div_u64(scaled_fraction << 20, scale_factor);
> > +
> > +	/* Check CSM is not busy */
> > +	ret = max2175_poll_csm_ready(ctx);
> > +	if (ret)
> > +		return ret;
> > +
> > +	mxm_dbg(ctx, "loband %u vcodiv %u lo_mult %u scaled_npf %llu\n",
> > +		loband_bits, vcodiv_bits, lo_mult, scaled_npf);
> > +	mxm_dbg(ctx, "scaled int %llu frac %llu desired int %u frac %u\n",
> > +		scaled_integer, scaled_fraction, int_desired, frac_desired);
> > +
> > +	/* Write the calculated values to the appropriate registers */
> > +	max2175_write(ctx, 1, int_desired);
> > +	max2175_write_bits(ctx, 2, 3, 0, (frac_desired >> 16) & 0xf);
> > +	max2175_write(ctx, 3, frac_desired >> 8);
> > +	max2175_write(ctx, 4, frac_desired);
> > +	max2175_write_bits(ctx, 5, 3, 2, loband_bits);
> > +	max2175_write_bits(ctx, 6, 7, 6, vcodiv_bits);
> > +	return ret;
> > +}
> 
> That synthesizer config is hard to understand. It seems to be
> fractional-N, with configurable N, K and output divider - like a school
> book example.
> 
>                +----------------------------+
>                v                            |
>       Fref   +----+     +-------+         +------+
>      ------> | PD | --> |  VCO  | ------> | /N.F |
>              +----+     +-------+         +------+
>                           |
>                           |
>                           v
>                         +-------+  Fout
>                         | /Rout | ------>
>                         +-------+
>
> I made following look-up table in order to understand it:
> 
> band      lo freq band vcodiv div_out
>    AM  <  50000000    0      0      16 // reg 5 bit 7 ?
>    FM  <  74700000    0      0      16
>    FM  < 110000000    1      0       8
>    FM  < 160000000    1      3       8
>   VHF  < 210000000    2      2       4
>   VHF  < 600000000    2      1       4
>     L  <2000000000    3      2       2
> 
> "vcodiv" looks unrelated to synth calculation, dunno what it is.
> 
> One which makes calculation very complex looking is that it is based of
> floating point calculus. On integer mathematics you should replace
> fractional part with fractional modulus (usually letter "K" is used for
> fractional modulus on PLL calc).
> 
> So that ends up something like:
> 1) select suitable lo output divider from desired output frequency
> 2) calculate vco frequency
> 3) convert vco frequency to N and K
> * N = Fvco/Fref
> * K = Fvco%Fref
> 4) convert K to control word (looks like << 20)
> 5) program values
> 
> Result should be calculus without scaling.

Thanks for reviewing this part.

I'd like to add that we already have two PLL helpers in the media subsystem, 
in drivers/media/i2c/aptina-pll.c and drivers/media/i2c/smiapp-pll.c. As the 
PLL used here seems to be a classic one, it would make sense to also extract 
the code in a helper function that could be shared between drivers.

-- 
Regards,

Laurent Pinchart


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

* Re: [PATCH 2/5] media: i2c: max2175: Add MAX2175 support
  2016-11-09 15:44     ` Ramesh Shanmugasundaram
  (?)
  (?)
@ 2016-11-11 13:21     ` Hans Verkuil
  2016-11-11 13:48       ` Laurent Pinchart
  2016-11-14 15:54       ` Ramesh Shanmugasundaram
  -1 siblings, 2 replies; 60+ messages in thread
From: Hans Verkuil @ 2016-11-11 13:21 UTC (permalink / raw)
  To: Ramesh Shanmugasundaram, robh+dt, mark.rutland, mchehab,
	sakari.ailus, crope
  Cc: chris.paterson2, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc

Hi Ramesh,

A quick review:

On 11/09/2016 04:44 PM, Ramesh Shanmugasundaram wrote:
> This patch adds driver support for MAX2175 chip. This is Maxim
> Integrated's RF to Bits tuner front end chip designed for software-defined
> radio solutions. This driver exposes the tuner as a sub-device instance
> with standard and custom controls to configure the device.
> 
> Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>
> ---
>  .../devicetree/bindings/media/i2c/max2175.txt      |   61 +
>  drivers/media/i2c/Kconfig                          |    4 +
>  drivers/media/i2c/Makefile                         |    2 +
>  drivers/media/i2c/max2175/Kconfig                  |    8 +
>  drivers/media/i2c/max2175/Makefile                 |    4 +
>  drivers/media/i2c/max2175/max2175.c                | 1558 ++++++++++++++++++++
>  drivers/media/i2c/max2175/max2175.h                |  108 ++
>  7 files changed, 1745 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/i2c/max2175.txt
>  create mode 100644 drivers/media/i2c/max2175/Kconfig
>  create mode 100644 drivers/media/i2c/max2175/Makefile
>  create mode 100644 drivers/media/i2c/max2175/max2175.c
>  create mode 100644 drivers/media/i2c/max2175/max2175.h
> 

<snip>

> diff --git a/drivers/media/i2c/max2175/max2175.c b/drivers/media/i2c/max2175/max2175.c
> new file mode 100644
> index 0000000..ec45b52
> --- /dev/null
> +++ b/drivers/media/i2c/max2175/max2175.c
> @@ -0,0 +1,1558 @@

<snip>

> +/* Read/Write bit(s) on top of regmap */
> +static int max2175_read(struct max2175 *ctx, u8 idx, u8 *val)
> +{
> +	u32 regval;
> +	int ret = regmap_read(ctx->regmap, idx, &regval);
> +
> +	if (ret)
> +		v4l2_err(ctx->client, "read ret(%d): idx 0x%02x\n", ret, idx);
> +
> +	*val = regval;

Does regmap_read initialize regval even if it returns an error? If not,
then I would initialize regval to 0 to prevent *val being uninitialized.

> +	return ret;
> +}
> +
> +static int max2175_write(struct max2175 *ctx, u8 idx, u8 val)
> +{
> +	int ret = regmap_write(ctx->regmap, idx, val);
> +
> +	if (ret)
> +		v4l2_err(ctx->client, "write ret(%d): idx 0x%02x val 0x%02x\n",
> +			 ret, idx, val);
> +	return ret;
> +}
> +
> +static u8 max2175_read_bits(struct max2175 *ctx, u8 idx, u8 msb, u8 lsb)
> +{
> +	u8 val;
> +
> +	if (max2175_read(ctx, idx, &val))
> +		return 0;
> +
> +	return max2175_get_bitval(val, msb, lsb);
> +}
> +
> +static bool max2175_read_bit(struct max2175 *ctx, u8 idx, u8 bit)
> +{
> +	return !!max2175_read_bits(ctx, idx, bit, bit);
> +}
> +
> +static int max2175_write_bits(struct max2175 *ctx, u8 idx,
> +			     u8 msb, u8 lsb, u8 newval)
> +{
> +	int ret = regmap_update_bits(ctx->regmap, idx, GENMASK(msb, lsb),
> +				     newval << lsb);
> +
> +	if (ret)
> +		v4l2_err(ctx->client, "wbits ret(%d): idx 0x%02x\n", ret, idx);
> +
> +	return ret;
> +}
> +
> +static int max2175_write_bit(struct max2175 *ctx, u8 idx, u8 bit, u8 newval)
> +{
> +	return max2175_write_bits(ctx, idx, bit, bit, newval);
> +}
> +
> +/* Checks expected pattern every msec until timeout */
> +static int max2175_poll_timeout(struct max2175 *ctx, u8 idx, u8 msb, u8 lsb,
> +				u8 exp_bitval, u32 timeout_ms)
> +{
> +	unsigned int val;
> +
> +	return regmap_read_poll_timeout(ctx->regmap, idx, val,
> +			(max2175_get_bitval(val, msb, lsb) == exp_bitval),
> +			1000, timeout_ms * 1000);
> +}
> +
> +static int max2175_poll_csm_ready(struct max2175 *ctx)
> +{
> +	int ret;
> +
> +	ret = max2175_poll_timeout(ctx, 69, 1, 1, 0, 50);
> +	if (ret)
> +		v4l2_err(ctx->client, "csm not ready\n");
> +
> +	return ret;
> +}
> +
> +#define MAX2175_IS_BAND_AM(ctx)		\
> +	(max2175_read_bits(ctx, 5, 1, 0) == MAX2175_BAND_AM)
> +
> +#define MAX2175_IS_BAND_VHF(ctx)	\
> +	(max2175_read_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF)
> +
> +#define MAX2175_IS_FM_MODE(ctx)		\
> +	(max2175_read_bits(ctx, 12, 5, 4) == 0)
> +
> +#define MAX2175_IS_FMHD_MODE(ctx)	\
> +	(max2175_read_bits(ctx, 12, 5, 4) == 1)
> +
> +#define MAX2175_IS_DAB_MODE(ctx)	\
> +	(max2175_read_bits(ctx, 12, 5, 4) == 2)
> +
> +static int max2175_band_from_freq(u32 freq)
> +{
> +	if (freq >= 144000 && freq <= 26100000)
> +		return MAX2175_BAND_AM;
> +	else if (freq >= 65000000 && freq <= 108000000)
> +		return MAX2175_BAND_FM;
> +	else

No need for these 'else' keywords.

> +		return MAX2175_BAND_VHF;
> +}
> +
> +static int max2175_update_i2s_mode(struct max2175 *ctx, u32 rx_mode,
> +				   u32 i2s_mode)
> +{
> +	max2175_write_bits(ctx, 29, 2, 0, i2s_mode);
> +
> +	/* Based on I2S mode value I2S_WORD_CNT values change */
> +	switch (i2s_mode) {
> +	case MAX2175_I2S_MODE3:
> +		max2175_write_bits(ctx, 30, 6, 0, 1);
> +		break;
> +	case MAX2175_I2S_MODE2:
> +	case MAX2175_I2S_MODE4:
> +		max2175_write_bits(ctx, 30, 6, 0, 0);
> +		break;
> +	case MAX2175_I2S_MODE0:
> +		max2175_write_bits(ctx, 30, 6, 0,
> +			ctx->rx_modes[rx_mode].i2s_word_size);
> +		break;
> +	}
> +	mxm_dbg(ctx, "update_i2s_mode %u, rx_mode %u\n", i2s_mode, rx_mode);
> +	return 0;
> +}
> +
> +static void max2175_i2s_enable(struct max2175 *ctx, bool enable)
> +{
> +	if (enable) {
> +		/* Use old setting */
> +		max2175_write_bits(ctx, 104, 3, 0, ctx->i2s_test);
> +	} else {
> +		/* Cache old setting */
> +		ctx->i2s_test = max2175_read_bits(ctx, 104, 3, 0);
> +		max2175_write_bits(ctx, 104, 3, 0, 9);	/* Keep SCK alive */
> +	}
> +	mxm_dbg(ctx, "i2s %sabled: old val %u\n", enable ? "en" : "dis",
> +		ctx->i2s_test);
> +}
> +
> +static void max2175_set_filter_coeffs(struct max2175 *ctx, u8 m_sel,
> +				      u8 bank, const u16 *coeffs)
> +{
> +	unsigned int i;
> +	u8 coeff_addr, upper_address = 24;
> +
> +	mxm_dbg(ctx, "set_filter_coeffs: m_sel %d bank %d\n", m_sel, bank);
> +	max2175_write_bits(ctx, 114, 5, 4, m_sel);
> +
> +	if (m_sel == 2)
> +		upper_address = 12;
> +
> +	for (i = 0; i < upper_address; i++) {
> +		coeff_addr = i + bank * 24;
> +		max2175_write(ctx, 115, coeffs[i] >> 8);
> +		max2175_write(ctx, 116, coeffs[i]);
> +		max2175_write(ctx, 117, coeff_addr | 1 << 7);
> +	}
> +	max2175_write_bit(ctx, 117, 7, 0);
> +}
> +
> +static void max2175_load_fmeu_1p2(struct max2175 *ctx)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(fmeu1p2_map); i++)
> +		max2175_write(ctx, fmeu1p2_map[i].idx, fmeu1p2_map[i].val);
> +
> +	ctx->decim_ratio = 36;
> +
> +	/* Load the Channel Filter Coefficients into channel filter bank #2 */
> +	max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 0, ch_coeff_fmeu);
> +	max2175_set_filter_coeffs(ctx, MAX2175_EQ_MSEL, 0,
> +				  eq_coeff_fmeu1_ra02_m6db);
> +}
> +
> +static void max2175_load_dab_1p2(struct max2175 *ctx)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(dab12_map); i++)
> +		max2175_write(ctx, dab12_map[i].idx, dab12_map[i].val);
> +
> +	ctx->decim_ratio = 1;
> +
> +	/* Load the Channel Filter Coefficients into channel filter bank #2 */
> +	max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 2, ch_coeff_dab1);
> +}
> +
> +static void max2175_load_fmna_1p0(struct max2175 *ctx)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(fmna1p0_map); i++)
> +		max2175_write(ctx, fmna1p0_map[i].idx, fmna1p0_map[i].val);
> +}
> +
> +static void max2175_load_fmna_2p0(struct max2175 *ctx)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(fmna2p0_map); i++)
> +		max2175_write(ctx, fmna2p0_map[i].idx, fmna2p0_map[i].val);
> +}
> +
> +static void max2175_set_bbfilter(struct max2175 *ctx)
> +{
> +	if (MAX2175_IS_BAND_AM(ctx)) {
> +		max2175_write_bits(ctx, 12, 3, 0, ctx->rom_bbf_bw_am);
> +		mxm_dbg(ctx, "set_bbfilter AM: rom %d\n", ctx->rom_bbf_bw_am);
> +	} else if (MAX2175_IS_DAB_MODE(ctx)) {
> +		max2175_write_bits(ctx, 12, 3, 0, ctx->rom_bbf_bw_dab);
> +		mxm_dbg(ctx, "set_bbfilter DAB: rom %d\n", ctx->rom_bbf_bw_dab);
> +	} else {
> +		max2175_write_bits(ctx, 12, 3, 0, ctx->rom_bbf_bw_fm);
> +		mxm_dbg(ctx, "set_bbfilter FM: rom %d\n", ctx->rom_bbf_bw_fm);
> +	}
> +}
> +
> +static bool max2175_set_csm_mode(struct max2175 *ctx,
> +			  enum max2175_csm_mode new_mode)
> +{
> +	int ret = max2175_poll_csm_ready(ctx);
> +
> +	if (ret)
> +		return ret;
> +
> +	max2175_write_bits(ctx, 0, 2, 0, new_mode);
> +	mxm_dbg(ctx, "set csm new mode %d\n", new_mode);
> +
> +	/* Wait for a fixed settle down time depending on new mode */
> +	switch (new_mode) {
> +	case MAX2175_PRESET_TUNE:
> +		usleep_range(51100, 51500);	/* 51.1ms */
> +		break;
> +	/*
> +	 * Other mode switches need different sleep values depending on band &
> +	 * mode
> +	 */
> +	default:
> +		break;
> +	}
> +
> +	return max2175_poll_csm_ready(ctx);
> +}
> +
> +static int max2175_csm_action(struct max2175 *ctx,
> +			      enum max2175_csm_mode action)
> +{
> +	int ret;
> +
> +	mxm_dbg(ctx, "csm_action: %d\n", action);
> +
> +	/* Other actions can be added in future when needed */
> +	ret = max2175_set_csm_mode(ctx, MAX2175_LOAD_TO_BUFFER);
> +	if (ret)
> +		return ret;
> +	return max2175_set_csm_mode(ctx, MAX2175_PRESET_TUNE);
> +}
> +
> +static int max2175_set_lo_freq(struct max2175 *ctx, u64 lo_freq)
> +{
> +	u64 scaled_lo_freq, scaled_npf, scaled_integer, scaled_fraction;
> +	u32 frac_desired, int_desired, lo_mult = 1;
> +	const u32 scale_factor = 1000000U;
> +	u8 loband_bits = 0, vcodiv_bits = 0;
> +	enum max2175_band band;
> +	int ret;
> +
> +	/* Scale to larger number for precision */
> +	scaled_lo_freq = lo_freq * scale_factor * 100;
> +	band = max2175_read_bits(ctx, 5, 1, 0);
> +
> +	mxm_dbg(ctx, "set_lo_freq: scaled lo_freq %llu lo_freq %llu band %d\n",
> +		scaled_lo_freq, lo_freq, band);
> +
> +	switch (band) {
> +	case MAX2175_BAND_AM:
> +		if (max2175_read_bit(ctx, 5, 7) == 0)
> +			lo_mult = 16;
> +		break;
> +	case MAX2175_BAND_FM:
> +		if (lo_freq <= 74700000) {
> +			lo_mult = 16;
> +		} else if (lo_freq > 74700000 && lo_freq <= 110000000) {
> +			loband_bits = 1;
> +		} else {
> +			loband_bits = 1;
> +			vcodiv_bits = 3;
> +		}
> +		lo_mult = 8;
> +		break;
> +	case MAX2175_BAND_VHF:
> +		if (lo_freq <= 210000000) {
> +			loband_bits = 2;
> +			vcodiv_bits = 2;
> +		} else {
> +			loband_bits = 2;
> +			vcodiv_bits = 1;
> +		}
> +		lo_mult = 4;
> +		break;
> +	default:
> +		loband_bits = 3;
> +		vcodiv_bits = 2;
> +		lo_mult = 2;
> +		break;
> +	}
> +
> +	if (band == MAX2175_BAND_L)
> +		scaled_npf = div_u64(div_u64(scaled_lo_freq, ctx->xtal_freq),
> +				     lo_mult);
> +	else
> +		scaled_npf = div_u64(scaled_lo_freq, ctx->xtal_freq) * lo_mult;
> +
> +	scaled_npf = div_u64(scaled_npf, 100);
> +	scaled_integer = div_u64(scaled_npf, scale_factor) * scale_factor;
> +	int_desired = div_u64(scaled_npf, scale_factor);
> +	scaled_fraction = scaled_npf - scaled_integer;
> +	frac_desired = div_u64(scaled_fraction << 20, scale_factor);
> +
> +	/* Check CSM is not busy */
> +	ret = max2175_poll_csm_ready(ctx);
> +	if (ret)
> +		return ret;
> +
> +	mxm_dbg(ctx, "loband %u vcodiv %u lo_mult %u scaled_npf %llu\n",
> +		loband_bits, vcodiv_bits, lo_mult, scaled_npf);
> +	mxm_dbg(ctx, "scaled int %llu frac %llu desired int %u frac %u\n",
> +		scaled_integer, scaled_fraction, int_desired, frac_desired);
> +
> +	/* Write the calculated values to the appropriate registers */
> +	max2175_write(ctx, 1, int_desired);
> +	max2175_write_bits(ctx, 2, 3, 0, (frac_desired >> 16) & 0xf);
> +	max2175_write(ctx, 3, frac_desired >> 8);
> +	max2175_write(ctx, 4, frac_desired);
> +	max2175_write_bits(ctx, 5, 3, 2, loband_bits);
> +	max2175_write_bits(ctx, 6, 7, 6, vcodiv_bits);
> +	return ret;
> +}
> +
> +static int max2175_set_nco_freq(struct max2175 *ctx, s64 nco_freq_desired)
> +{
> +	s64  nco_freq, nco_val_desired;
> +	u64 abs_nco_freq;
> +	const u32 scale_factor = 1000000U;
> +	u32 clock_rate, nco_reg;
> +	int ret;
> +
> +	mxm_dbg(ctx, "set_nco_freq: freq %lld\n", nco_freq_desired);
> +	clock_rate = ctx->xtal_freq / ctx->decim_ratio;
> +	nco_freq = -nco_freq_desired;
> +
> +	if (nco_freq < 0)
> +		abs_nco_freq = -nco_freq;
> +	else
> +		abs_nco_freq = nco_freq;
> +
> +	/* Scale up the values for precision */
> +	if (abs_nco_freq < (clock_rate / 2)) {
> +		nco_val_desired = div_s64(2 * nco_freq * scale_factor,
> +					  clock_rate);
> +	} else {
> +		if (nco_freq < 0)
> +			nco_val_desired =
> +			div_s64(-2 * (clock_rate - abs_nco_freq) * scale_factor,
> +				clock_rate);
> +		else
> +			nco_val_desired =
> +			div_s64(2 * (clock_rate - abs_nco_freq) * scale_factor,
> +				clock_rate);
> +	}
> +
> +	/* Scale down to get the fraction */
> +	if (nco_freq < 0)
> +		nco_reg = 0x200000 + div_s64(nco_val_desired << 20,
> +						  scale_factor);
> +	else
> +		nco_reg = div_s64(nco_val_desired << 20, scale_factor);
> +
> +	/* Check CSM is not busy */
> +	ret = max2175_poll_csm_ready(ctx);
> +	if (ret)
> +		return ret;
> +
> +	mxm_dbg(ctx, "clk %u decim %u abs %llu desired %lld reg %u\n",
> +		clock_rate, ctx->decim_ratio, abs_nco_freq,
> +		nco_val_desired, nco_reg);
> +
> +	/* Write the calculated values to the appropriate registers */
> +	max2175_write_bits(ctx, 7, 4, 0, (nco_reg >> 16) & 0x1f);
> +	max2175_write(ctx, 8, nco_reg >> 8);
> +	max2175_write(ctx, 9, nco_reg);
> +	return ret;
> +}
> +
> +static int max2175_set_rf_freq_non_am_bands(struct max2175 *ctx, u64 freq,
> +					    u32 lo_pos)
> +{
> +	s64 adj_freq;
> +	u64 low_if_freq;
> +	int ret;
> +
> +	mxm_dbg(ctx, "rf_freq: non AM bands\n");
> +
> +	if (MAX2175_IS_FM_MODE(ctx))
> +		low_if_freq = 128000;
> +	else if (MAX2175_IS_FMHD_MODE(ctx))
> +		low_if_freq = 228000;
> +	else
> +		return max2175_set_lo_freq(ctx, freq);
> +
> +	if (MAX2175_IS_BAND_VHF(ctx) == (lo_pos == MAX2175_LO_ABOVE_DESIRED))
> +		adj_freq = freq + low_if_freq;
> +	else
> +		adj_freq = freq - low_if_freq;
> +
> +	ret = max2175_set_lo_freq(ctx, adj_freq);
> +	if (ret)
> +		return ret;
> +
> +	return max2175_set_nco_freq(ctx, low_if_freq);
> +}
> +
> +static int max2175_set_rf_freq(struct max2175 *ctx, u64 freq, u32 lo_pos)
> +{
> +	int ret;
> +
> +	if (MAX2175_IS_BAND_AM(ctx))
> +		ret = max2175_set_nco_freq(ctx, freq);
> +	else
> +		ret = max2175_set_rf_freq_non_am_bands(ctx, freq, lo_pos);
> +
> +	mxm_dbg(ctx, "set_rf_freq: ret %d freq %llu\n", ret, freq);
> +	return ret;
> +}
> +
> +static int max2175_tune_rf_freq(struct max2175 *ctx, u64 freq, u32 hsls)
> +{
> +	int ret;
> +
> +	ret = max2175_set_rf_freq(ctx, freq, hsls);
> +	if (ret)
> +		return ret;
> +
> +	ret = max2175_csm_action(ctx, MAX2175_BUFFER_PLUS_PRESET_TUNE);
> +	if (ret)
> +		return ret;
> +
> +	mxm_dbg(ctx, "tune_rf_freq: old %u new %llu\n", ctx->freq, freq);
> +	ctx->freq = freq;
> +	return ret;
> +}
> +
> +static void max2175_set_hsls(struct max2175 *ctx, u32 lo_pos)
> +{
> +	mxm_dbg(ctx, "set_hsls: lo_pos %u\n", lo_pos);
> +
> +	if ((lo_pos == MAX2175_LO_BELOW_DESIRED) == MAX2175_IS_BAND_VHF(ctx))
> +		max2175_write_bit(ctx, 5, 4, 1);
> +	else
> +		max2175_write_bit(ctx, 5, 4, 0);
> +}
> +
> +static void max2175_set_eu_rx_mode(struct max2175 *ctx, u32 rx_mode)
> +{
> +	switch (rx_mode) {
> +	case MAX2175_EU_FM_1_2:
> +		max2175_load_fmeu_1p2(ctx);
> +		break;
> +
> +	case MAX2175_DAB_1_2:
> +		max2175_load_dab_1p2(ctx);
> +		break;
> +	}
> +	/* Master is the default setting */
> +	if (!ctx->master)
> +		max2175_write_bit(ctx, 30, 7, 1);
> +
> +	/* Cache i2s_test value at this point */
> +	ctx->i2s_test = max2175_read_bits(ctx, 104, 3, 0);
> +}
> +
> +static void max2175_set_na_rx_mode(struct max2175 *ctx, u32 rx_mode)
> +{
> +	switch (rx_mode) {
> +	case MAX2175_NA_FM_1_0:
> +		max2175_load_fmna_1p0(ctx);
> +		break;
> +	case MAX2175_NA_FM_2_0:
> +		max2175_load_fmna_2p0(ctx);
> +		break;
> +	}
> +	/* Master is the default setting */
> +	if (!ctx->master)
> +		max2175_write_bit(ctx, 30, 7, 1);
> +
> +	/* Cache i2s_test value at this point */
> +	ctx->i2s_test = max2175_read_bits(ctx, 104, 3, 0);
> +	ctx->decim_ratio = 27;
> +
> +	/* Load the Channel Filter Coefficients into channel filter bank #2 */
> +	max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 0, ch_coeff_fmna);
> +	max2175_set_filter_coeffs(ctx, MAX2175_EQ_MSEL, 0,
> +				  eq_coeff_fmna1_ra02_m6db);
> +}
> +
> +static int max2175_set_rx_mode(struct max2175 *ctx, u32 rx_mode, u32 hsls)
> +{
> +	mxm_dbg(ctx, "set_rx_mode: %u am_hiz %u\n", rx_mode, ctx->am_hiz);
> +	if (ctx->xtal_freq == MAX2175_EU_XTAL_FREQ)
> +		max2175_set_eu_rx_mode(ctx, rx_mode);
> +	else
> +		max2175_set_na_rx_mode(ctx, rx_mode);
> +
> +	if (ctx->am_hiz) {
> +		mxm_dbg(ctx, "setting AM HiZ related config\n");
> +		max2175_write_bit(ctx, 50, 5, 1);
> +		max2175_write_bit(ctx, 90, 7, 1);
> +		max2175_write_bits(ctx, 73, 1, 0, 2);
> +		max2175_write_bits(ctx, 80, 5, 0, 33);
> +	}
> +
> +	/* Load BB filter trim values saved in ROM */
> +	max2175_set_bbfilter(ctx);
> +
> +	/* Set HSLS */
> +	max2175_set_hsls(ctx, hsls);
> +
> +	ctx->mode_resolved = true;
> +	return 0;
> +}
> +
> +static bool max2175_i2s_rx_mode_valid(struct max2175 *ctx,
> +					 u32 mode, u32 i2s_mode)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(ctx->rx_modes[mode].i2s_modes); i++)
> +		if (ctx->rx_modes[mode].i2s_modes[i] == i2s_mode)
> +			return true;
> +
> +	v4l2_err(ctx->client, "i2s_mode %u not suitable for cur rx mode %u\n",
> +		 i2s_mode, mode);
> +	return false;
> +}
> +
> +static int max2175_rx_mode_from_freq(struct max2175 *ctx, u32 freq, u32 *mode)
> +{
> +	unsigned int i;
> +	int band = max2175_band_from_freq(freq);
> +
> +	/* Pick the first match always */
> +	for (i = 0; i <= ctx->rx_mode->maximum; i++) {
> +		if (ctx->rx_modes[i].band == band) {
> +			*mode = i;
> +			mxm_dbg(ctx, "rx_mode_from_freq: freq %u mode %d\n",
> +				freq, *mode);
> +			return 0;
> +		}
> +	}
> +	return -EINVAL;
> +}
> +
> +static bool max2175_freq_rx_mode_valid(struct max2175 *ctx,
> +					 u32 mode, u32 freq)
> +{
> +	int band = max2175_band_from_freq(freq);
> +
> +	return (ctx->rx_modes[mode].band == band);
> +}
> +
> +static void max2175_load_adc_presets(struct max2175 *ctx)
> +{
> +	unsigned int i, j;
> +
> +	for (i = 0; i < 2; i++)
> +		for (j = 0; j < 23; j++)
> +			max2175_write(ctx, 146 + j + i * 55, adc_presets[i][j]);
> +}
> +
> +static int max2175_init_power_manager(struct max2175 *ctx)
> +{
> +	int ret;
> +
> +	/* Execute on-chip power-up/calibration */
> +	max2175_write_bit(ctx, 99, 2, 0);
> +	usleep_range(1000, 1500);
> +	max2175_write_bit(ctx, 99, 2, 1);
> +
> +	/* Wait for the power manager to finish. */
> +	ret = max2175_poll_timeout(ctx, 69, 7, 7, 1, 50);
> +	if (ret)
> +		v4l2_err(ctx->client, "init pm failed\n");
> +	return ret;
> +}
> +
> +static int max2175_recalibrate_adc(struct max2175 *ctx)
> +{
> +	int ret;
> +
> +	/* ADC Re-calibration */
> +	max2175_write(ctx, 150, 0xff);
> +	max2175_write(ctx, 205, 0xff);
> +	max2175_write(ctx, 147, 0x20);
> +	max2175_write(ctx, 147, 0x00);
> +	max2175_write(ctx, 202, 0x20);
> +	max2175_write(ctx, 202, 0x00);
> +
> +	ret = max2175_poll_timeout(ctx, 69, 4, 3, 3, 50);
> +	if (ret)
> +		v4l2_err(ctx->client, "adc recalibration failed\n");
> +	return ret;
> +}
> +
> +static u8 max2175_read_rom(struct max2175 *ctx, u8 row)
> +{
> +	u8 data;
> +
> +	max2175_write_bit(ctx, 56, 4, 0);
> +	max2175_write_bits(ctx, 56, 3, 0, row);
> +
> +	usleep_range(2000, 2500);
> +	max2175_read(ctx, 58, &data);
> +
> +	max2175_write_bits(ctx, 56, 3, 0, 0);
> +
> +	mxm_dbg(ctx, "read_rom: row %d data 0x%02x\n", row, data);
> +	return data;
> +}
> +
> +static void max2175_load_from_rom(struct max2175 *ctx)
> +{
> +	u8 data = 0;
> +
> +	data = max2175_read_rom(ctx, 0);
> +	ctx->rom_bbf_bw_am = data & 0x0f;
> +	max2175_write_bits(ctx, 81, 3, 0, data >> 4);
> +
> +	data = max2175_read_rom(ctx, 1);
> +	ctx->rom_bbf_bw_fm = data & 0x0f;
> +	ctx->rom_bbf_bw_dab = data >> 4;
> +
> +	data = max2175_read_rom(ctx, 2);
> +	max2175_write_bits(ctx, 82, 4, 0, data & 0x1f);
> +	max2175_write_bits(ctx, 82, 7, 5, data >> 5);
> +
> +	data = max2175_read_rom(ctx, 3);
> +	if (ctx->am_hiz) {
> +		data &= 0x0f;
> +		data |= max2175_read_rom(ctx, 7) & 0x40 >> 2;
> +		if (!data)
> +			data |= 2;
> +	} else {
> +		data = data & 0xf0 >> 4;
> +		data |= max2175_read_rom(ctx, 7) & 0x80 >> 3;
> +		if (!data)
> +			data |= 30;
> +	}
> +	max2175_write_bits(ctx, 80, 5, 0, data + 31);
> +
> +	data = max2175_read_rom(ctx, 6);
> +	max2175_write_bits(ctx, 81, 7, 6, data >> 6);
> +}
> +
> +static void max2175_load_full_fm_eu_1p0(struct max2175 *ctx)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(full_fm_eu_1p0); i++)
> +		max2175_write(ctx, i + 1, full_fm_eu_1p0[i]);
> +
> +	usleep_range(5000, 5500);
> +	ctx->decim_ratio = 36;
> +}
> +
> +static void max2175_load_full_fm_na_1p0(struct max2175 *ctx)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(full_fm_na_1p0); i++)
> +		max2175_write(ctx, i + 1, full_fm_na_1p0[i]);
> +
> +	usleep_range(5000, 5500);
> +	ctx->decim_ratio = 27;
> +}
> +
> +static int max2175_core_init(struct max2175 *ctx, u32 refout_bits)
> +{
> +	int ret;
> +
> +	/* MAX2175 uses 36.864MHz clock for EU & 40.154MHz for NA region */
> +	if (ctx->xtal_freq == MAX2175_EU_XTAL_FREQ)
> +		max2175_load_full_fm_eu_1p0(ctx);
> +	else
> +		max2175_load_full_fm_na_1p0(ctx);
> +
> +	/* The default settings assume master */
> +	if (!ctx->master)
> +		max2175_write_bit(ctx, 30, 7, 1);
> +
> +	mxm_dbg(ctx, "refout_bits %u\n", refout_bits);
> +
> +	/* Set REFOUT */
> +	max2175_write_bits(ctx, 56, 7, 5, refout_bits);
> +
> +	/* ADC Reset */
> +	max2175_write_bit(ctx, 99, 1, 0);
> +	usleep_range(1000, 1500);
> +	max2175_write_bit(ctx, 99, 1, 1);
> +
> +	/* Load ADC preset values */
> +	max2175_load_adc_presets(ctx);
> +
> +	/* Initialize the power management state machine */
> +	ret = max2175_init_power_manager(ctx);
> +	if (ret)
> +		return ret;
> +
> +	/* Recalibrate ADC */
> +	ret = max2175_recalibrate_adc(ctx);
> +	if (ret)
> +		return ret;
> +
> +	/* Load ROM values to appropriate registers */
> +	max2175_load_from_rom(ctx);
> +
> +	if (ctx->xtal_freq == MAX2175_EU_XTAL_FREQ) {
> +		/* Load FIR coefficients into bank 0 */
> +		max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 0,
> +					  ch_coeff_fmeu);
> +		max2175_set_filter_coeffs(ctx, MAX2175_EQ_MSEL, 0,
> +					  eq_coeff_fmeu1_ra02_m6db);
> +	} else {
> +		/* Load FIR coefficients into bank 0 */
> +		max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 0,
> +					  ch_coeff_fmna);
> +		max2175_set_filter_coeffs(ctx, MAX2175_EQ_MSEL, 0,
> +					  eq_coeff_fmna1_ra02_m6db);
> +	}
> +	mxm_dbg(ctx, "core initialized\n");
> +	return 0;
> +}
> +
> +static void max2175_s_ctrl_i2s_mode(struct max2175 *ctx, u32 i2s_mode)
> +{
> +	mxm_dbg(ctx, "s_ctrl_i2s_mode: %u resolved %d\n", i2s_mode,
> +		ctx->mode_resolved);
> +
> +	/*
> +	 * Update i2s mode on device only when mode is resolved & it is valid
> +	 * for the configured mode
> +	 */
> +	if (ctx->mode_resolved &&
> +	    max2175_i2s_rx_mode_valid(ctx, ctx->rx_mode->val, i2s_mode))
> +		max2175_update_i2s_mode(ctx, ctx->rx_mode->val, i2s_mode);
> +}
> +
> +static void max2175_s_ctrl_rx_mode(struct max2175 *ctx, u32 rx_mode)
> +{
> +	/* Load mode. Range check already done */
> +	max2175_set_rx_mode(ctx, rx_mode, ctx->hsls->val);
> +
> +	/* Get current i2s_mode and update if needed for given rx_mode */
> +	if (max2175_i2s_rx_mode_valid(ctx, rx_mode, ctx->i2s_mode->val))
> +		max2175_update_i2s_mode(ctx, rx_mode, ctx->i2s_mode->val);
> +	else
> +		ctx->i2s_mode->val = max2175_read_bits(ctx, 29, 2, 0);
> +
> +	mxm_dbg(ctx, "s_ctrl_rx_mode: %u curr freq %u\n", rx_mode, ctx->freq);
> +
> +	/* Check if current freq valid for mode & update */
> +	if (max2175_freq_rx_mode_valid(ctx, rx_mode, ctx->freq))
> +		max2175_tune_rf_freq(ctx, ctx->freq, ctx->hsls->val);
> +	else
> +		/* Use default freq of mode if current freq is not valid */
> +		max2175_tune_rf_freq(ctx, ctx->rx_modes[rx_mode].freq,
> +				     ctx->hsls->val);
> +}
> +
> +static int max2175_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct max2175 *ctx = max2175_from_ctrl_hdl(ctrl->handler);
> +	int ret = 0;
> +
> +	mxm_dbg(ctx, "s_ctrl: id 0x%x, val %u\n", ctrl->id, ctrl->val);
> +	switch (ctrl->id) {
> +	case V4L2_CID_MAX2175_I2S_ENABLE:
> +		max2175_i2s_enable(ctx, ctrl->val == 1);
> +		break;
> +	case V4L2_CID_MAX2175_I2S_MODE:
> +		max2175_s_ctrl_i2s_mode(ctx, ctrl->val);
> +		break;
> +	case V4L2_CID_MAX2175_HSLS:
> +		max2175_set_hsls(ctx, ctx->hsls->val);
> +		break;
> +	case V4L2_CID_MAX2175_RX_MODE:
> +		max2175_s_ctrl_rx_mode(ctx, ctrl->val);
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static int max2175_get_lna_gain(struct max2175 *ctx)
> +{
> +	int gain = 0;
> +	enum max2175_band band = max2175_read_bits(ctx, 5, 1, 0);
> +
> +	switch (band) {
> +	case MAX2175_BAND_AM:
> +		gain = max2175_read_bits(ctx, 51, 3, 1);
> +		break;
> +	case MAX2175_BAND_FM:
> +		gain = max2175_read_bits(ctx, 50, 3, 1);
> +		break;
> +	case MAX2175_BAND_VHF:
> +		gain = max2175_read_bits(ctx, 52, 3, 0);
> +		break;
> +	default:
> +		break;
> +	}
> +	return gain;
> +}
> +
> +static int max2175_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct max2175 *ctx = max2175_from_ctrl_hdl(ctrl->handler);
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_RF_TUNER_LNA_GAIN:
> +		ctrl->val = max2175_get_lna_gain(ctx);
> +		break;
> +	case V4L2_CID_RF_TUNER_IF_GAIN:
> +		ctrl->val = max2175_read_bits(ctx, 49, 4, 0);
> +		break;
> +	case V4L2_CID_RF_TUNER_PLL_LOCK:
> +		ctrl->val = (max2175_read_bits(ctx, 60, 7, 6) == 3);
> +		break;
> +	}
> +	mxm_dbg(ctx, "g_volatile_ctrl: id 0x%x val %d\n", ctrl->id, ctrl->val);
> +	return 0;
> +};
> +
> +static int max2175_set_freq_and_mode(struct max2175 *ctx, u32 freq)
> +{
> +	u32 rx_mode;
> +	int ret;
> +
> +	/* Get band from frequency */
> +	ret = max2175_rx_mode_from_freq(ctx, freq, &rx_mode);
> +	if (ret)
> +		return ret;
> +
> +	mxm_dbg(ctx, "set_freq_and_mode: freq %u rx_mode %d\n", freq, rx_mode);
> +
> +	/* Load mode */
> +	max2175_set_rx_mode(ctx, rx_mode, ctx->hsls->val);
> +	ctx->rx_mode->val = rx_mode;
> +
> +	/* Get current i2s_mode and update if needed for given rx_mode */
> +	if (max2175_i2s_rx_mode_valid(ctx, rx_mode, ctx->i2s_mode->val))
> +		max2175_update_i2s_mode(ctx, rx_mode, ctx->i2s_mode->val);
> +	else
> +		ctx->i2s_mode->val = max2175_read_bits(ctx, 29, 2, 0);
> +
> +	/* Tune to the new freq given */
> +	return max2175_tune_rf_freq(ctx, freq, ctx->hsls->val);
> +}
> +
> +static int max2175_s_frequency(struct v4l2_subdev *sd,
> +			       const struct v4l2_frequency *vf)
> +{
> +	struct max2175 *ctx = max2175_from_sd(sd);
> +	u32 freq;
> +	int ret = 0;
> +
> +	mxm_dbg(ctx, "s_freq: new %u curr %u, mode_resolved %d\n",
> +		vf->frequency, ctx->freq, ctx->mode_resolved);
> +
> +	if (vf->tuner != 0)
> +		return -EINVAL;
> +
> +	freq = clamp(vf->frequency, ctx->bands_rf->rangelow,
> +		     ctx->bands_rf->rangehigh);
> +
> +	/* Check new freq valid for rx_mode if already resolved */
> +	if (ctx->mode_resolved &&
> +	    max2175_freq_rx_mode_valid(ctx, ctx->rx_mode->val, freq))
> +		ret = max2175_tune_rf_freq(ctx, freq, ctx->hsls->val);
> +	else
> +		/* Find default rx_mode for freq and tune to it */
> +		ret = max2175_set_freq_and_mode(ctx, freq);
> +
> +	mxm_dbg(ctx, "s_freq: ret %d curr %u mode_resolved %d mode %u\n",
> +		ret, ctx->freq, ctx->mode_resolved, ctx->rx_mode->val);
> +	return ret;
> +}
> +
> +static int max2175_g_frequency(struct v4l2_subdev *sd,
> +			       struct v4l2_frequency *vf)
> +{
> +	struct max2175 *ctx = max2175_from_sd(sd);
> +	int ret = 0;
> +
> +	if (vf->tuner != 0)
> +		return -EINVAL;
> +
> +	/* RF freq */
> +	vf->type = V4L2_TUNER_RF;
> +	vf->frequency = ctx->freq;
> +	return ret;
> +}
> +
> +static int max2175_enum_freq_bands(struct v4l2_subdev *sd,
> +			    struct v4l2_frequency_band *band)
> +{
> +	struct max2175 *ctx = max2175_from_sd(sd);
> +
> +	if (band->tuner == 0 && band->index == 0)
> +		*band = *ctx->bands_rf;
> +	else
> +		return -EINVAL;

This is a bit ugly. I would invert the condition and return -EINVAL.
Then assign *band and return 0.

> +
> +	return 0;
> +}
> +
> +static int max2175_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
> +{
> +	struct max2175 *ctx = max2175_from_sd(sd);
> +
> +	if (vt->index > 0)
> +		return -EINVAL;
> +
> +	strlcpy(vt->name, "RF", sizeof(vt->name));
> +	vt->type = V4L2_TUNER_RF;
> +	vt->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
> +	vt->rangelow = ctx->bands_rf->rangelow;
> +	vt->rangehigh = ctx->bands_rf->rangehigh;
> +	return 0;
> +}
> +
> +static int max2175_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt)
> +{
> +	/* Check tuner index is valid */
> +	if (vt->index > 0)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_subdev_tuner_ops max2175_tuner_ops = {
> +	.s_frequency = max2175_s_frequency,
> +	.g_frequency = max2175_g_frequency,
> +	.enum_freq_bands = max2175_enum_freq_bands,
> +	.g_tuner = max2175_g_tuner,
> +	.s_tuner = max2175_s_tuner,
> +};
> +
> +static const struct v4l2_subdev_ops max2175_ops = {
> +	.tuner = &max2175_tuner_ops,
> +};
> +
> +static const struct v4l2_ctrl_ops max2175_ctrl_ops = {
> +	.s_ctrl = max2175_s_ctrl,
> +	.g_volatile_ctrl = max2175_g_volatile_ctrl,
> +};
> +
> +static const struct v4l2_ctrl_config max2175_i2s_en = {
> +	.ops = &max2175_ctrl_ops,
> +	.id = V4L2_CID_MAX2175_I2S_ENABLE,
> +	.name = "I2S Enable",
> +	.type = V4L2_CTRL_TYPE_BOOLEAN,
> +	.min = 0,
> +	.max = 1,
> +	.step = 1,
> +	.def = 1,
> +};
> +
> +static const char * const max2175_ctrl_i2s_modes[] = {
> +	[MAX2175_I2S_MODE0]	= "i2s mode 0",
> +	[MAX2175_I2S_MODE1]	= "i2s mode 1 (skipped)",
> +	[MAX2175_I2S_MODE2]	= "i2s mode 2",
> +	[MAX2175_I2S_MODE3]	= "i2s mode 3",
> +	[MAX2175_I2S_MODE4]	= "i2s mode 4",
> +};
> +
> +static const struct v4l2_ctrl_config max2175_i2s_mode = {
> +	.ops = &max2175_ctrl_ops,
> +	.id = V4L2_CID_MAX2175_I2S_MODE,
> +	.name = "I2S MODE value",
> +	.type = V4L2_CTRL_TYPE_MENU,
> +	.max = ARRAY_SIZE(max2175_ctrl_i2s_modes) - 1,
> +	.def = 0,
> +	.menu_skip_mask = 0x02,
> +	.qmenu = max2175_ctrl_i2s_modes,
> +};

Is this something that is changed dynamically? It looks more like a
device tree thing (it's not clear what it does, so obviously I
can't be sure).

> +
> +static const struct v4l2_ctrl_config max2175_hsls = {
> +	.ops = &max2175_ctrl_ops,
> +	.id = V4L2_CID_MAX2175_HSLS,
> +	.name = "HSLS above/below desired",
> +	.type = V4L2_CTRL_TYPE_INTEGER,
> +	.min = 0,
> +	.max = 1,
> +	.step = 1,
> +	.def = 1,
> +};
> +
> +static const char * const max2175_ctrl_eu_rx_modes[] = {
> +	[MAX2175_EU_FM_1_2]	= "EU FM 1.2",
> +	[MAX2175_DAB_1_2]	= "DAB 1.2",
> +};
> +
> +static const char * const max2175_ctrl_na_rx_modes[] = {
> +	[MAX2175_NA_FM_1_0]	= "NA FM 1.0",
> +	[MAX2175_NA_FM_2_0]	= "NA FM 2.0",
> +};
> +
> +static const struct v4l2_ctrl_config max2175_eu_rx_mode = {
> +	.ops = &max2175_ctrl_ops,
> +	.id = V4L2_CID_MAX2175_RX_MODE,
> +	.name = "RX MODE",
> +	.type = V4L2_CTRL_TYPE_MENU,
> +	.max = ARRAY_SIZE(max2175_ctrl_eu_rx_modes) - 1,
> +	.def = 0,
> +	.qmenu = max2175_ctrl_eu_rx_modes,
> +};
> +
> +static const struct v4l2_ctrl_config max2175_na_rx_mode = {
> +	.ops = &max2175_ctrl_ops,
> +	.id = V4L2_CID_MAX2175_RX_MODE,
> +	.name = "RX MODE",
> +	.type = V4L2_CTRL_TYPE_MENU,
> +	.max = ARRAY_SIZE(max2175_ctrl_na_rx_modes) - 1,
> +	.def = 0,
> +	.qmenu = max2175_ctrl_na_rx_modes,
> +};

Please document all these controls better. This is part of the public API, so
you need to give more information what this means exactly.

> +
> +static int max2175_refout_load_to_bits(struct i2c_client *client, u32 load,
> +				       u32 *bits)
> +{
> +	if (load >= 0 && load <= 40)
> +		*bits = load / 10;
> +	else if (load >= 60 && load <= 70)
> +		*bits = load / 10 - 1;
> +	else
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static int max2175_probe(struct i2c_client *client,
> +			const struct i2c_device_id *id)
> +{
> +	struct max2175 *ctx;
> +	struct v4l2_subdev *sd;
> +	struct v4l2_ctrl_handler *hdl;
> +	struct clk *clk;
> +	struct regmap *regmap;
> +	bool master = true, am_hiz = false;
> +	u32 refout_load, refout_bits = 0;	/* REFOUT disabled */
> +	int ret;
> +
> +	/* Parse DT properties */
> +	if (of_find_property(client->dev.of_node, "maxim,slave", NULL))
> +		master = false;
> +
> +	if (of_find_property(client->dev.of_node, "maxim,am-hiz", NULL))
> +		am_hiz = true;
> +
> +	if (!of_property_read_u32(client->dev.of_node, "maxim,refout-load-pF",
> +				  &refout_load)) {
> +		ret = max2175_refout_load_to_bits(client, refout_load,
> +						  &refout_bits);
> +		if (ret) {
> +			dev_err(&client->dev, "invalid refout_load %u\n",
> +				refout_load);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	clk = devm_clk_get(&client->dev, "xtal");
> +	if (IS_ERR(clk)) {
> +		ret = PTR_ERR(clk);
> +		dev_err(&client->dev, "cannot get xtal clock %d\n", ret);
> +		return -ENODEV;
> +	}
> +
> +	regmap = devm_regmap_init_i2c(client, &max2175_regmap_config);
> +	if (IS_ERR(regmap)) {
> +		ret = PTR_ERR(regmap);
> +		dev_err(&client->dev, "regmap init failed %d\n", ret);
> +		return -ENODEV;
> +	}
> +
> +	/* Alloc tuner context */
> +	ctx = devm_kzalloc(&client->dev, sizeof(*ctx), GFP_KERNEL);
> +	if (ctx == NULL)
> +		return -ENOMEM;
> +
> +	sd = &ctx->sd;
> +	ctx->master = master;
> +	ctx->am_hiz = am_hiz;
> +	ctx->mode_resolved = false;
> +	ctx->regmap = regmap;
> +	ctx->xtal_freq = clk_get_rate(clk);
> +	dev_info(&client->dev, "xtal freq %luHz\n", ctx->xtal_freq);
> +
> +	v4l2_i2c_subdev_init(sd, client, &max2175_ops);
> +	ctx->client = client;
> +
> +	sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> +
> +	/* Controls */
> +	hdl = &ctx->ctrl_hdl;
> +	ret = v4l2_ctrl_handler_init(hdl, 7);
> +	if (ret) {
> +		dev_err(&client->dev, "ctrl handler init failed\n");
> +		goto err;
> +	}
> +
> +	ctx->lna_gain = v4l2_ctrl_new_std(hdl, &max2175_ctrl_ops,
> +					  V4L2_CID_RF_TUNER_LNA_GAIN,
> +					  0, 15, 1, 2);
> +	ctx->lna_gain->flags |= (V4L2_CTRL_FLAG_VOLATILE |
> +				 V4L2_CTRL_FLAG_READ_ONLY);
> +	ctx->if_gain = v4l2_ctrl_new_std(hdl, &max2175_ctrl_ops,
> +					 V4L2_CID_RF_TUNER_IF_GAIN,
> +					 0, 31, 1, 0);
> +	ctx->if_gain->flags |= (V4L2_CTRL_FLAG_VOLATILE |
> +				V4L2_CTRL_FLAG_READ_ONLY);
> +	ctx->pll_lock = v4l2_ctrl_new_std(hdl, &max2175_ctrl_ops,
> +					  V4L2_CID_RF_TUNER_PLL_LOCK,
> +					  0, 1, 1, 0);
> +	ctx->pll_lock->flags |= (V4L2_CTRL_FLAG_VOLATILE |
> +				 V4L2_CTRL_FLAG_READ_ONLY);
> +	ctx->i2s_en = v4l2_ctrl_new_custom(hdl, &max2175_i2s_en, NULL);
> +	ctx->i2s_mode = v4l2_ctrl_new_custom(hdl, &max2175_i2s_mode, NULL);
> +	ctx->hsls = v4l2_ctrl_new_custom(hdl, &max2175_hsls, NULL);
> +
> +	if (ctx->xtal_freq == MAX2175_EU_XTAL_FREQ) {
> +		ctx->rx_mode = v4l2_ctrl_new_custom(hdl,
> +						    &max2175_eu_rx_mode, NULL);
> +		ctx->rx_modes = eu_rx_modes;
> +		ctx->bands_rf = &eu_bands_rf;
> +	} else {
> +		ctx->rx_mode = v4l2_ctrl_new_custom(hdl,
> +						    &max2175_na_rx_mode, NULL);
> +		ctx->rx_modes = na_rx_modes;
> +		ctx->bands_rf = &na_bands_rf;
> +	}
> +	ctx->sd.ctrl_handler = &ctx->ctrl_hdl;
> +
> +	/* Set the defaults */
> +	ctx->freq = ctx->bands_rf->rangelow;
> +
> +	/* Register subdev */
> +	ret = v4l2_async_register_subdev(sd);
> +	if (ret) {
> +		dev_err(&client->dev, "register subdev failed\n");
> +		goto err_reg;
> +	}
> +
> +	/* Initialize device */
> +	ret = max2175_core_init(ctx, refout_bits);
> +	if (ret)
> +		goto err_init;
> +
> +	dev_info(&client->dev, "probed\n");
> +	return 0;
> +
> +err_init:
> +	v4l2_async_unregister_subdev(sd);
> +err_reg:
> +	v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
> +err:
> +	return ret;
> +}
> +
> +static int max2175_remove(struct i2c_client *client)
> +{
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct max2175 *ctx = max2175_from_sd(sd);
> +
> +	v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
> +	v4l2_async_unregister_subdev(sd);
> +	dev_info(&client->dev, "removed\n");
> +	return 0;
> +}
> +
> +static const struct i2c_device_id max2175_id[] = {
> +	{ DRIVER_NAME, 0},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(i2c, max2175_id);
> +
> +static const struct of_device_id max2175_of_ids[] = {
> +	{ .compatible = "maxim, max2175", },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, max2175_of_ids);
> +
> +static struct i2c_driver max2175_driver = {
> +	.driver = {
> +		.name	= DRIVER_NAME,
> +		.of_match_table = max2175_of_ids,
> +	},
> +	.probe		= max2175_probe,
> +	.remove		= max2175_remove,
> +	.id_table	= max2175_id,
> +};
> +
> +module_i2c_driver(max2175_driver);
> +
> +MODULE_DESCRIPTION("Maxim MAX2175 RF to Bits tuner driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>");
> diff --git a/drivers/media/i2c/max2175/max2175.h b/drivers/media/i2c/max2175/max2175.h
> new file mode 100644
> index 0000000..2d858aa
> --- /dev/null
> +++ b/drivers/media/i2c/max2175/max2175.h
> @@ -0,0 +1,108 @@
> +/*
> + * Maxim Integrated MAX2175 RF to Bits tuner driver
> + *
> + * This driver & most of the hard coded values are based on the reference
> + * application delivered by Maxim for this chip.
> + *
> + * Copyright (C) 2016 Maxim Integrated Products
> + * Copyright (C) 2016 Renesas Electronics Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * 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 __MAX2175_H__
> +#define __MAX2175_H__
> +
> +#define MAX2175_EU_XTAL_FREQ	36864000	/* In Hz */
> +#define MAX2175_NA_XTAL_FREQ	40186125	/* In Hz */
> +
> +enum max2175_region {
> +	MAX2175_REGION_EU = 0,	/* Europe */
> +	MAX2175_REGION_NA,	/* North America */
> +};
> +
> +
> +enum max2175_band {
> +	MAX2175_BAND_AM = 0,
> +	MAX2175_BAND_FM,
> +	MAX2175_BAND_VHF,
> +	MAX2175_BAND_L,
> +};
> +
> +enum max2175_eu_mode {
> +	/* EU modes */
> +	MAX2175_EU_FM_1_2 = 0,
> +	MAX2175_DAB_1_2,
> +
> +	/* Other possible modes to add in future
> +	 * MAX2175_DAB_1_0,
> +	 * MAX2175_DAB_1_3,
> +	 * MAX2175_EU_FM_2_2,
> +	 * MAX2175_EU_FMHD_4_0,
> +	 * MAX2175_EU_AM_1_0,
> +	 * MAX2175_EU_AM_2_2,
> +	 */
> +};
> +
> +enum max2175_na_mode {
> +	/* NA modes */
> +	MAX2175_NA_FM_1_0 = 0,
> +	MAX2175_NA_FM_2_0,
> +
> +	/* Other possible modes to add in future
> +	 * MAX2175_NA_FMHD_1_0,
> +	 * MAX2175_NA_FMHD_1_2,
> +	 * MAX2175_NA_AM_1_0,
> +	 * MAX2175_NA_AM_1_2,
> +	 */
> +};
> +
> +/* Supported I2S modes */
> +enum {
> +	MAX2175_I2S_MODE0 = 0,
> +	MAX2175_I2S_MODE1,
> +	MAX2175_I2S_MODE2,
> +	MAX2175_I2S_MODE3,
> +	MAX2175_I2S_MODE4,
> +};
> +
> +/* Coefficient table groups */
> +enum {
> +	MAX2175_CH_MSEL = 0,
> +	MAX2175_EQ_MSEL,
> +	MAX2175_AA_MSEL,
> +};
> +
> +/* HSLS LO injection polarity */
> +enum {
> +	MAX2175_LO_BELOW_DESIRED = 0,
> +	MAX2175_LO_ABOVE_DESIRED,
> +};
> +
> +/* Channel FSM modes */
> +enum max2175_csm_mode {
> +	MAX2175_LOAD_TO_BUFFER = 0,
> +	MAX2175_PRESET_TUNE,
> +	MAX2175_SEARCH,
> +	MAX2175_AF_UPDATE,
> +	MAX2175_JUMP_FAST_TUNE,
> +	MAX2175_CHECK,
> +	MAX2175_LOAD_AND_SWAP,
> +	MAX2175_END,
> +	MAX2175_BUFFER_PLUS_PRESET_TUNE,
> +	MAX2175_BUFFER_PLUS_SEARCH,
> +	MAX2175_BUFFER_PLUS_AF_UPDATE,
> +	MAX2175_BUFFER_PLUS_JUMP_FAST_TUNE,
> +	MAX2175_BUFFER_PLUS_CHECK,
> +	MAX2175_BUFFER_PLUS_LOAD_AND_SWAP,
> +	MAX2175_NO_ACTION
> +};
> +
> +#endif /* __MAX2175_H__ */
> 

Regards,

	Hans

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

* Re: [PATCH 3/5] media: Add new SDR formats SC16, SC18 & SC20
  2016-11-09 15:44 ` [PATCH 3/5] media: Add new SDR formats SC16, SC18 & SC20 Ramesh Shanmugasundaram
@ 2016-11-11 13:24   ` Hans Verkuil
  2016-11-11 13:48     ` Laurent Pinchart
                       ` (2 more replies)
  0 siblings, 3 replies; 60+ messages in thread
From: Hans Verkuil @ 2016-11-11 13:24 UTC (permalink / raw)
  To: Ramesh Shanmugasundaram, robh+dt, mark.rutland, mchehab,
	sakari.ailus, crope
  Cc: chris.paterson2, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc

On 11/09/2016 04:44 PM, Ramesh Shanmugasundaram wrote:
> This patch adds support for the three new SDR formats. These formats
> were prefixed with "sliced" indicating I data constitutes the top half and
> Q data constitutes the bottom half of the received buffer.

The standard terminology for video formats is "planar". I am leaning towards
using that here as well.

Any opinions on this?

	Hans

> 
> V4L2_SDR_FMT_SCU16BE - 14-bit complex (I & Q) unsigned big-endian sample
> inside 16-bit. V4L2 FourCC: SC16
> 
> V4L2_SDR_FMT_SCU18BE - 16-bit complex (I & Q) unsigned big-endian sample
> inside 18-bit. V4L2 FourCC: SC18
> 
> V4L2_SDR_FMT_SCU20BE - 18-bit complex (I & Q) unsigned big-endian sample
> inside 20-bit. V4L2 FourCC: SC20
> 
> Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>
> ---
>  drivers/media/v4l2-core/v4l2-ioctl.c | 3 +++
>  include/uapi/linux/videodev2.h       | 3 +++
>  2 files changed, 6 insertions(+)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
> index 181381d..d36b386 100644
> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> @@ -1207,6 +1207,9 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
>  	case V4L2_SDR_FMT_CS8:		descr = "Complex S8"; break;
>  	case V4L2_SDR_FMT_CS14LE:	descr = "Complex S14LE"; break;
>  	case V4L2_SDR_FMT_RU12LE:	descr = "Real U12LE"; break;
> +	case V4L2_SDR_FMT_SCU16BE:	descr = "Sliced Complex U16BE"; break;
> +	case V4L2_SDR_FMT_SCU18BE:	descr = "Sliced Complex U18BE"; break;
> +	case V4L2_SDR_FMT_SCU20BE:	descr = "Sliced Complex U20BE"; break;
>  	case V4L2_TCH_FMT_DELTA_TD16:	descr = "16-bit signed deltas"; break;
>  	case V4L2_TCH_FMT_DELTA_TD08:	descr = "8-bit signed deltas"; break;
>  	case V4L2_TCH_FMT_TU16:		descr = "16-bit unsigned touch data"; break;
> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> index 4364ce6..34a9c30 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -666,6 +666,9 @@ struct v4l2_pix_format {
>  #define V4L2_SDR_FMT_CS8          v4l2_fourcc('C', 'S', '0', '8') /* complex s8 */
>  #define V4L2_SDR_FMT_CS14LE       v4l2_fourcc('C', 'S', '1', '4') /* complex s14le */
>  #define V4L2_SDR_FMT_RU12LE       v4l2_fourcc('R', 'U', '1', '2') /* real u12le */
> +#define V4L2_SDR_FMT_SCU16BE	  v4l2_fourcc('S', 'C', '1', '6') /* sliced complex u16be */
> +#define V4L2_SDR_FMT_SCU18BE	  v4l2_fourcc('S', 'C', '1', '8') /* sliced complex u18be */
> +#define V4L2_SDR_FMT_SCU20BE	  v4l2_fourcc('S', 'C', '2', '0') /* sliced complex u20be */
>  
>  /* Touch formats - used for Touch devices */
>  #define V4L2_TCH_FMT_DELTA_TD16	v4l2_fourcc('T', 'D', '1', '6') /* 16-bit signed deltas */
> 

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

* Re: [PATCH 5/5] media: platform: rcar_drif: Add DRIF support
  2016-11-09 15:44 ` [PATCH 5/5] media: platform: rcar_drif: Add DRIF support Ramesh Shanmugasundaram
  2016-11-10  9:22   ` Laurent Pinchart
@ 2016-11-11 13:38   ` Hans Verkuil
       [not found]     ` <9b772894-f6ef-d5ad-4601-735f2321ce0c-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
  1 sibling, 1 reply; 60+ messages in thread
From: Hans Verkuil @ 2016-11-11 13:38 UTC (permalink / raw)
  To: Ramesh Shanmugasundaram, robh+dt, mark.rutland, mchehab,
	sakari.ailus, crope
  Cc: chris.paterson2, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc

On 11/09/2016 04:44 PM, Ramesh Shanmugasundaram wrote:
> This patch adds Digital Radio Interface (DRIF) support to R-Car Gen3 SoCs.
> The driver exposes each instance of DRIF as a V4L2 SDR device. A DRIF
> device represents a channel and each channel can have one or two
> sub-channels respectively depending on the target board.
> 
> DRIF supports only Rx functionality. It receives samples from a RF
> frontend tuner chip it is interfaced with. The combination of DRIF and the
> tuner device, which is registered as a sub-device, determines the receive
> sample rate and format.
> 
> In order to be compliant as a V4L2 SDR device, DRIF needs to bind with
> the tuner device, which can be provided by a third party vendor. DRIF acts
> as a slave device and the tuner device acts as a master transmitting the
> samples. The driver allows asynchronous binding of a tuner device that
> is registered as a v4l2 sub-device. The driver can learn about the tuner
> it is interfaced with based on port endpoint properties of the device in
> device tree. The V4L2 SDR device inherits the controls exposed by the
> tuner device.
> 
> The device can also be configured to use either one or both of the data
> pins at runtime based on the master (tuner) configuration.
> 
> Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>
> ---
>  .../devicetree/bindings/media/renesas,drif.txt     |  136 ++
>  drivers/media/platform/Kconfig                     |   25 +
>  drivers/media/platform/Makefile                    |    1 +
>  drivers/media/platform/rcar_drif.c                 | 1574 ++++++++++++++++++++
>  4 files changed, 1736 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/renesas,drif.txt
>  create mode 100644 drivers/media/platform/rcar_drif.c
> 
> diff --git a/Documentation/devicetree/bindings/media/renesas,drif.txt b/Documentation/devicetree/bindings/media/renesas,drif.txt
> new file mode 100644
> index 0000000..d65368a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> @@ -0,0 +1,136 @@
> +Renesas R-Car Gen3 Digital Radio Interface controller (DRIF)
> +------------------------------------------------------------
> +
> +R-Car Gen3 DRIF is a serial slave device. It interfaces with a master
> +device as shown below
> +
> ++---------------------+                +---------------------+
> +|                     |-----SCK------->|CLK                  |
> +|       Master        |-----SS-------->|SYNC  DRIFn (slave)  |
> +|                     |-----SD0------->|D0                   |
> +|                     |-----SD1------->|D1                   |
> ++---------------------+                +---------------------+
> +
> +Each DRIF channel (drifn) consists of two sub-channels (drifn0 & drifn1).
> +The sub-channels are like two individual channels in itself that share the
> +common CLK & SYNC. Each sub-channel has it's own dedicated resources like
> +irq, dma channels, address space & clock.
> +
> +The device tree model represents the channel and each of it's sub-channel
> +as a separate node. The parent channel ties the sub-channels together with
> +their phandles.
> +
> +Required properties of a sub-channel:
> +-------------------------------------
> +- compatible: "renesas,r8a7795-drif" if DRIF controller is a part of R8A7795 SoC.
> +	      "renesas,rcar-gen3-drif" for a generic R-Car Gen3 compatible device.
> +	      When compatible with the generic version, nodes must list the
> +	      SoC-specific version corresponding to the platform first
> +	      followed by the generic version.
> +- reg: offset and length of that sub-channel.
> +- interrupts: associated with that sub-channel.
> +- clocks: phandle and clock specifier of that sub-channel.
> +- clock-names: clock input name string: "fck".
> +- dmas: phandles to the DMA channel of that sub-channel.
> +- dma-names: names of the DMA channel: "rx".
> +
> +Optional properties of a sub-channel:
> +-------------------------------------
> +- power-domains: phandle to the respective power domain.
> +
> +Required properties of a channel:
> +---------------------------------
> +- pinctrl-0: pin control group to be used for this channel.
> +- pinctrl-names: must be "default".
> +- sub-channels : phandles to the two sub-channels.
> +
> +Optional properties of a channel:
> +---------------------------------
> +- port: child port node of a channel that defines the local and remote
> +        endpoints. The remote endpoint is assumed to be a tuner subdevice
> +	endpoint.
> +- renesas,syncmd       : sync mode
> +			 0 (Frame start sync pulse mode. 1-bit width pulse
> +			    indicates start of a frame)
> +			 1 (L/R sync or I2S mode) (default)
> +- renesas,lsb-first    : empty property indicates lsb bit is received first.
> +			 When not defined msb bit is received first (default)
> +- renesas,syncac-pol-high  : empty property indicates sync signal polarity.
> +			 When defined, active high or high->low sync signal.
> +			 When not defined, active low or low->high sync signal
> +			 (default)
> +- renesas,dtdl         : delay between sync signal and start of reception.
> +			 Must contain one of the following values:
> +			 0   (no bit delay)
> +			 50  (0.5-clock-cycle delay)
> +			 100 (1-clock-cycle delay) (default)
> +			 150 (1.5-clock-cycle delay)
> +			 200 (2-clock-cycle delay)
> +- renesas,syncdl       : delay between end of reception and sync signal edge.
> +			 Must contain one of the following values:
> +			 0   (no bit delay) (default)
> +			 50  (0.5-clock-cycle delay)
> +			 100 (1-clock-cycle delay)
> +			 150 (1.5-clock-cycle delay)
> +			 200 (2-clock-cycle delay)
> +			 300 (3-clock-cycle delay)
> +
> +Example
> +--------
> +
> +SoC common dtsi file
> +
> +		drif00: rif@e6f40000 {
> +			compatible = "renesas,r8a7795-drif",
> +				     "renesas,rcar-gen3-drif";
> +			reg = <0 0xe6f40000 0 0x64>;
> +			interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
> +			clocks = <&cpg CPG_MOD 515>;
> +			clock-names = "fck";
> +			dmas = <&dmac1 0x20>, <&dmac2 0x20>;
> +			dma-names = "rx", "rx";
> +			power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> +			status = "disabled";
> +		};
> +
> +		drif01: rif@e6f50000 {
> +			compatible = "renesas,r8a7795-drif",
> +				     "renesas,rcar-gen3-drif";
> +			reg = <0 0xe6f50000 0 0x64>;
> +			interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
> +			clocks = <&cpg CPG_MOD 514>;
> +			clock-names = "fck";
> +			dmas = <&dmac1 0x22>, <&dmac2 0x22>;
> +			dma-names = "rx", "rx";
> +			power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> +			status = "disabled";
> +		};
> +
> +		drif0: rif@0 {
> +			compatible = "renesas,r8a7795-drif",
> +				     "renesas,rcar-gen3-drif";
> +			sub-channels = <&drif00>, <&drif01>;
> +			status = "disabled";
> +		};
> +
> +Board specific dts file
> +
> +&drif00 {
> +	status = "okay";
> +};
> +
> +&drif01 {
> +	status = "okay";
> +};
> +
> +&drif0 {
> +	pinctrl-0 = <&drif0_pins>;
> +	pinctrl-names = "default";
> +	renesas,syncac-pol-high;
> +	status = "okay";
> +	port {
> +		drif0_ep: endpoint {
> +		     remote-endpoint = <&tuner_subdev_ep>;
> +		};
> +	};
> +};
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index 754edbf1..0ae83a8 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -393,3 +393,28 @@ menuconfig DVB_PLATFORM_DRIVERS
>  if DVB_PLATFORM_DRIVERS
>  source "drivers/media/platform/sti/c8sectpfe/Kconfig"
>  endif #DVB_PLATFORM_DRIVERS
> +
> +menuconfig SDR_PLATFORM_DRIVERS
> +	bool "SDR platform devices"
> +	depends on MEDIA_SDR_SUPPORT
> +	default n
> +	---help---
> +	  Say Y here to enable support for platform-specific SDR Drivers.
> +
> +if SDR_PLATFORM_DRIVERS
> +
> +config VIDEO_RCAR_DRIF
> +	tristate "Renesas Digitial Radio Interface (DRIF)"
> +	depends on VIDEO_V4L2 && HAS_DMA
> +	depends on ARCH_RENESAS
> +	select VIDEOBUF2_VMALLOC
> +	---help---
> +	  Say Y if you want to enable R-Car Gen3 DRIF support. DRIF is Digital
> +	  Radio Interface that interfaces with an RF front end chip. It is a
> +	  receiver of digital data which uses DMA to transfer received data to
> +	  a configured location for an application to use.
> +
> +	  To compile this driver as a module, choose M here; the module
> +	  will be called rcar_drif.
> +
> +endif # SDR_PLATFORM_DRIVERS
> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> index f842933..49ce238 100644
> --- a/drivers/media/platform/Makefile
> +++ b/drivers/media/platform/Makefile
> @@ -49,6 +49,7 @@ obj-$(CONFIG_SOC_CAMERA)		+= soc_camera/
>  
>  obj-$(CONFIG_VIDEO_RENESAS_FCP) 	+= rcar-fcp.o
>  obj-$(CONFIG_VIDEO_RENESAS_JPU) 	+= rcar_jpu.o
> +obj-$(CONFIG_VIDEO_RCAR_DRIF)		+= rcar_drif.o
>  obj-$(CONFIG_VIDEO_RENESAS_VSP1)	+= vsp1/
>  
>  obj-y	+= omap/
> diff --git a/drivers/media/platform/rcar_drif.c b/drivers/media/platform/rcar_drif.c
> new file mode 100644
> index 0000000..34dc282
> --- /dev/null
> +++ b/drivers/media/platform/rcar_drif.c
> @@ -0,0 +1,1574 @@

<snip>

+#define for_each_rcar_drif_subdev(sd, tmp, ch)				\
+	list_for_each_entry_safe(sd, tmp, &ch->v4l2_dev.subdevs, list)
+

Please don't use this. media/v4l2-device.h has a bunch of similar functions
for this. Use those instead.

<snip>

> +static int rcar_drif_querycap(struct file *file, void *fh,
> +			      struct v4l2_capability *cap)
> +{
> +	struct rcar_drif_chan *ch = video_drvdata(file);
> +
> +	strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
> +	strlcpy(cap->card, ch->vdev.name, sizeof(cap->card));
> +	cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER |
> +				   V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
> +	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;

Set device_caps in struct video_device and drop it here.

The core will fill in cap->device_caps and cap->capabilities for you.

> +	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
> +		 ch->vdev.name);
> +	return 0;
> +}
> +
> +static int rcar_drif_set_default_format(struct rcar_drif_chan *ch)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < NUM_FORMATS; i++) {
> +		/* Find any matching fmt and set it as default */
> +		if (ch->num_hw_schans == formats[i].num_schans) {
> +			ch->fmt_idx = i;
> +			ch->cur_schans_mask = ch->hw_schans_mask;
> +			ch->num_cur_schans = ch->num_hw_schans;
> +			dev_dbg(ch->dev, "default fmt[%u]: mask %lu num %u\n",
> +				i, ch->cur_schans_mask, ch->num_cur_schans);
> +			return 0;
> +		}
> +	}
> +	dev_err(ch->dev, "no matching sdr fmt found\n");
> +	return -EINVAL;
> +}
> +
> +static int rcar_drif_enum_fmt_sdr_cap(struct file *file, void *priv,
> +				      struct v4l2_fmtdesc *f)
> +{
> +	if (f->index >= NUM_FORMATS)
> +		return -EINVAL;
> +
> +	strlcpy(f->description, formats[f->index].name, sizeof(f->description));

Drop this. The core fills that in for you.

> +	f->pixelformat = formats[f->index].pixelformat;
> +	return 0;
> +}
> +
> +static int rcar_drif_g_fmt_sdr_cap(struct file *file, void *priv,
> +				   struct v4l2_format *f)
> +{
> +	struct rcar_drif_chan *ch = video_drvdata(file);
> +
> +	f->fmt.sdr.pixelformat = formats[ch->fmt_idx].pixelformat;
> +	f->fmt.sdr.buffersize = formats[ch->fmt_idx].buffersize;
> +	memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
> +	return 0;
> +}
> +
> +static int rcar_drif_s_fmt_sdr_cap(struct file *file, void *priv,
> +				   struct v4l2_format *f)
> +{
> +	struct rcar_drif_chan *ch = video_drvdata(file);
> +	struct vb2_queue *q = &ch->vb_queue;
> +	unsigned int i;
> +
> +	if (vb2_is_busy(q))
> +		return -EBUSY;
> +
> +	memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
> +	for (i = 0; i < NUM_FORMATS; i++) {
> +		if (formats[i].pixelformat == f->fmt.sdr.pixelformat) {
> +			ch->fmt_idx  = i;
> +			f->fmt.sdr.buffersize = formats[i].buffersize;
> +
> +			/*
> +			 * If a format demands one sub-channel only out of two
> +			 * enabled sub-channels then pick the 0th sub-channel
> +			 */
> +			if (formats[i].num_schans < ch->num_hw_schans) {
> +				ch->cur_schans_mask = BIT(0);	/* Enable D0 */
> +				ch->num_cur_schans = formats[i].num_schans;
> +			} else {
> +				ch->cur_schans_mask = ch->hw_schans_mask;
> +				ch->num_cur_schans = ch->num_hw_schans;
> +			}
> +
> +			rdrif_dbg(1, ch, "cur: idx %u mask %lu num %u\n",
> +				  i, ch->cur_schans_mask, ch->num_cur_schans);
> +			return 0;
> +		}
> +	}
> +
> +	if (rcar_drif_set_default_format(ch))
> +		return -EINVAL;
> +
> +	f->fmt.sdr.pixelformat = formats[ch->fmt_idx].pixelformat;
> +	f->fmt.sdr.buffersize = formats[ch->fmt_idx].buffersize;
> +	return 0;
> +}
> +
> +static int rcar_drif_try_fmt_sdr_cap(struct file *file, void *priv,
> +				     struct v4l2_format *f)
> +{
> +	struct rcar_drif_chan *ch = video_drvdata(file);
> +	unsigned int i;
> +
> +	memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
> +	for (i = 0; i < NUM_FORMATS; i++) {
> +		if (formats[i].pixelformat == f->fmt.sdr.pixelformat) {
> +			f->fmt.sdr.buffersize = formats[i].buffersize;
> +			return 0;
> +		}
> +	}
> +
> +	f->fmt.sdr.pixelformat = formats[ch->fmt_idx].pixelformat;
> +	f->fmt.sdr.buffersize = formats[ch->fmt_idx].buffersize;
> +	return 0;
> +}
> +
> +/* Tuner subdev ioctls */
> +static int rcar_drif_enum_freq_bands(struct file *file, void *priv,
> +				     struct v4l2_frequency_band *band)
> +{
> +	struct rcar_drif_chan *ch = video_drvdata(file);
> +	struct v4l2_subdev *sd, *tmp;
> +	int ret = 0;
> +
> +	for_each_rcar_drif_subdev(sd, tmp, ch) {
> +		ret = v4l2_subdev_call(sd, tuner, enum_freq_bands, band);
> +		if (ret)
> +			break;
> +	}
> +	return ret;
> +}
> +
> +static int rcar_drif_g_frequency(struct file *file, void *priv,
> +				 struct v4l2_frequency *f)
> +{
> +	struct rcar_drif_chan *ch = video_drvdata(file);
> +	struct v4l2_subdev *sd, *tmp;
> +	int ret = 0;
> +
> +	for_each_rcar_drif_subdev(sd, tmp, ch) {
> +		ret = v4l2_subdev_call(sd, tuner, g_frequency, f);
> +		if (ret)
> +			break;
> +	}
> +	return ret;
> +}
> +
> +static int rcar_drif_s_frequency(struct file *file, void *priv,
> +				 const struct v4l2_frequency *f)
> +{
> +	struct rcar_drif_chan *ch = video_drvdata(file);
> +	struct v4l2_subdev *sd, *tmp;
> +	int ret = 0;
> +
> +	for_each_rcar_drif_subdev(sd, tmp, ch) {
> +		ret = v4l2_subdev_call(sd, tuner, s_frequency, f);
> +		if (ret)
> +			break;
> +	}
> +	return ret;
> +}
> +
> +static int rcar_drif_g_tuner(struct file *file, void *priv,
> +			     struct v4l2_tuner *vt)
> +{
> +	struct rcar_drif_chan *ch = video_drvdata(file);
> +	struct v4l2_subdev *sd, *tmp;
> +	int ret = 0;
> +
> +	for_each_rcar_drif_subdev(sd, tmp, ch) {
> +		ret = v4l2_subdev_call(sd, tuner, g_tuner, vt);
> +		if (ret)
> +			break;
> +	}
> +	return ret;
> +}
> +
> +static int rcar_drif_s_tuner(struct file *file, void *priv,
> +			     const struct v4l2_tuner *vt)
> +{
> +	struct rcar_drif_chan *ch = video_drvdata(file);
> +	struct v4l2_subdev *sd, *tmp;
> +	int ret = 0;
> +
> +	for_each_rcar_drif_subdev(sd, tmp, ch) {
> +		ret = v4l2_subdev_call(sd, tuner, s_tuner, vt);
> +		if (ret)
> +			break;
> +	}
> +	return ret;
> +}
> +
> +static const struct v4l2_ioctl_ops rcar_drif_ioctl_ops = {
> +	.vidioc_querycap          = rcar_drif_querycap,
> +
> +	.vidioc_enum_fmt_sdr_cap  = rcar_drif_enum_fmt_sdr_cap,
> +	.vidioc_g_fmt_sdr_cap     = rcar_drif_g_fmt_sdr_cap,
> +	.vidioc_s_fmt_sdr_cap     = rcar_drif_s_fmt_sdr_cap,
> +	.vidioc_try_fmt_sdr_cap   = rcar_drif_try_fmt_sdr_cap,
> +
> +	.vidioc_reqbufs           = vb2_ioctl_reqbufs,
> +	.vidioc_create_bufs       = vb2_ioctl_create_bufs,
> +	.vidioc_prepare_buf       = vb2_ioctl_prepare_buf,
> +	.vidioc_querybuf          = vb2_ioctl_querybuf,
> +	.vidioc_qbuf              = vb2_ioctl_qbuf,
> +	.vidioc_dqbuf             = vb2_ioctl_dqbuf,
> +
> +	.vidioc_streamon          = vb2_ioctl_streamon,
> +	.vidioc_streamoff         = vb2_ioctl_streamoff,
> +
> +	.vidioc_s_frequency       = rcar_drif_s_frequency,
> +	.vidioc_g_frequency       = rcar_drif_g_frequency,
> +	.vidioc_s_tuner		  = rcar_drif_s_tuner,
> +	.vidioc_g_tuner		  = rcar_drif_g_tuner,
> +	.vidioc_enum_freq_bands   = rcar_drif_enum_freq_bands,
> +	.vidioc_subscribe_event   = v4l2_ctrl_subscribe_event,
> +	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> +	.vidioc_log_status        = v4l2_ctrl_log_status,
> +};
> +
> +static const struct v4l2_file_operations rcar_drif_fops = {
> +	.owner                    = THIS_MODULE,
> +	.open                     = v4l2_fh_open,
> +	.release                  = vb2_fop_release,
> +	.read                     = vb2_fop_read,
> +	.poll                     = vb2_fop_poll,
> +	.mmap                     = vb2_fop_mmap,
> +	.unlocked_ioctl           = video_ioctl2,
> +};
> +
> +static struct video_device rcar_drif_vdev = {
> +	.name                     = "R-Car DRIF",
> +	.release                  = video_device_release_empty,
> +	.fops                     = &rcar_drif_fops,
> +	.ioctl_ops                = &rcar_drif_ioctl_ops,
> +};
> +
> +static int rcar_drif_notify_bound(struct v4l2_async_notifier *notifier,
> +				   struct v4l2_subdev *subdev,
> +				   struct v4l2_async_subdev *asd)
> +{
> +	struct rcar_drif_chan *ch =
> +		container_of(notifier, struct rcar_drif_chan, notifier);
> +
> +	/* Nothing to do at this point */
> +	rdrif_dbg(2, ch, "bound asd: %s\n", asd->match.of.node->name);
> +	return 0;
> +}
> +
> +/* Sub-device registered notification callback */
> +static int rcar_drif_notify_complete(struct v4l2_async_notifier *notifier)
> +{
> +	struct rcar_drif_chan *ch =
> +		container_of(notifier, struct rcar_drif_chan, notifier);
> +	struct v4l2_subdev *sd, *tmp;
> +	int ret;
> +
> +	v4l2_ctrl_handler_init(&ch->ctrl_hdl, 10);
> +	ch->v4l2_dev.ctrl_handler = &ch->ctrl_hdl;
> +
> +	ret = v4l2_device_register_subdev_nodes(&ch->v4l2_dev);
> +	if (ret) {
> +		rdrif_err(ch, "failed register subdev nodes ret %d\n", ret);
> +		return ret;
> +	}
> +
> +	for_each_rcar_drif_subdev(sd, tmp, ch) {
> +		ret = v4l2_ctrl_add_handler(ch->v4l2_dev.ctrl_handler,
> +					    sd->ctrl_handler, NULL);
> +		if (ret) {
> +			rdrif_err(ch, "failed ctrl add hdlr ret %d\n", ret);
> +			return ret;
> +		}
> +	}
> +	rdrif_dbg(2, ch, "notify complete\n");
> +	return 0;
> +}
> +
> +/* Parse sub-devs (tuner) to find a matching device */
> +static int rcar_drif_parse_subdevs(struct device *dev,
> +				   struct v4l2_async_notifier *notifier)
> +{
> +	struct device_node *node = NULL;
> +
> +	notifier->subdevs = devm_kzalloc(dev, sizeof(*notifier->subdevs),
> +					 GFP_KERNEL);
> +	if (!notifier->subdevs)
> +		return -ENOMEM;
> +
> +	node = of_graph_get_next_endpoint(dev->of_node, node);

Do:

	if (!node)
		return 0;

And the remainder can be shifted one tab to the left.

> +	if (node) {
> +		struct rcar_drif_async_subdev *rsd;
> +
> +		rsd = devm_kzalloc(dev, sizeof(*rsd), GFP_KERNEL);
> +		if (!rsd) {
> +			of_node_put(node);
> +			return -ENOMEM;
> +		}
> +
> +		notifier->subdevs[notifier->num_subdevs] = &rsd->asd;
> +		rsd->asd.match.of.node = of_graph_get_remote_port_parent(node);
> +		of_node_put(node);
> +		if (!rsd->asd.match.of.node) {
> +			dev_warn(dev, "bad remote port parent\n");
> +			return -EINVAL;
> +		}
> +
> +		rsd->asd.match_type = V4L2_ASYNC_MATCH_OF;
> +		notifier->num_subdevs++;
> +	}
> +	return 0;
> +}
> +
> +/* SIRMDR1 configuration */
> +static int rcar_drif_validate_syncmd(struct rcar_drif_chan *ch, u32 val)
> +{
> +	if (val > 1) {
> +		dev_err(ch->dev, "invalid syncmd %u using L/R mode\n", val);
> +		return -EINVAL;
> +	}
> +
> +	ch->mdr1 &= ~(3 << 28);	/* Clear current settings */
> +	if (val == 0)
> +		ch->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCMD_FRAME;
> +	else
> +		ch->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCMD_LR;
> +	return 0;
> +}
> +
> +/* Get the dtdl or syncdl bits as in MSIOF */
> +static u32 rcar_drif_get_dtdl_or_syncdl_bits(u32 dtdl_or_syncdl)
> +{
> +	/*
> +	 * DTDL/SYNCDL bit	: dtdl/syncdl
> +	 * b'000		: 0
> +	 * b'001		: 100
> +	 * b'010		: 200
> +	 * b'011 (SYNCDL only)	: 300
> +	 * b'101		: 50
> +	 * b'110		: 150
> +	 */
> +	if (dtdl_or_syncdl % 100)
> +		return dtdl_or_syncdl / 100 + 5;
> +	else

Line can be dropped.

> +		return dtdl_or_syncdl / 100;
> +}
> +
> +static int rcar_drif_validate_dtdl_syncdl(struct rcar_drif_chan *ch)
> +{
> +	struct device_node *np = ch->dev->of_node;
> +	u32 dtdl = 100, syncdl = 0;
> +
> +	ch->mdr1 |= RCAR_DRIF_SIRMDR1_DTDL_1 | RCAR_DRIF_SIRMDR1_SYNCDL_0;
> +	of_property_read_u32(np, "renesas,dtdl", &dtdl);
> +	of_property_read_u32(np, "renesas,syncdl", &syncdl);
> +
> +	/* Sanity checks */
> +	if (dtdl > 200 || syncdl > 300) {
> +		dev_err(ch->dev, "invalid dtdl %u/syncdl %u\n", dtdl, syncdl);
> +		return -EINVAL;
> +	}
> +	if ((dtdl + syncdl) % 100) {
> +		dev_err(ch->dev, "sum of dtdl %u & syncdl %u not OK\n",
> +			dtdl, syncdl);
> +		return -EINVAL;
> +	}
> +	ch->mdr1 &= ~(7 << 20) & ~(7 << 16);	/* Clear current settings */
> +	ch->mdr1 |= rcar_drif_get_dtdl_or_syncdl_bits(dtdl) << 20;
> +	ch->mdr1 |= rcar_drif_get_dtdl_or_syncdl_bits(syncdl) << 16;
> +	return 0;
> +}
> +
> +static int rcar_drif_parse_properties(struct rcar_drif_chan *ch)
> +{
> +	struct device_node *np = ch->dev->of_node;
> +	u32 syncmd;
> +	int ret;
> +
> +	/* Set the defaults and check for overrides */
> +	ch->mdr1 = RCAR_DRIF_SIRMDR1_SYNCMD_LR;
> +	if (!of_property_read_u32(np, "renesas,syncmd", &syncmd)) {
> +		ret = rcar_drif_validate_syncmd(ch, syncmd);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (of_find_property(np, "renesas,lsb-first", NULL))
> +		ch->mdr1 |= RCAR_DRIF_SIRMDR1_LSB_FIRST;
> +	else
> +		ch->mdr1 |= RCAR_DRIF_SIRMDR1_MSB_FIRST;
> +
> +	if (of_find_property(np, "renesas,syncac-pol-high", NULL))
> +		ch->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCAC_POL_HIGH;
> +	else
> +		ch->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCAC_POL_LOW;
> +
> +	return rcar_drif_validate_dtdl_syncdl(ch);
> +}
> +
> +static u32 rcar_drif_enum_sub_channels(struct platform_device *pdev,
> +					struct platform_device *s_pdev[])
> +{
> +	struct device_node *s_np;
> +	u32 hw_schans_mask = 0;
> +	unsigned int i;
> +
> +	for (i = 0; i < RCAR_DRIF_MAX_SUBCHANS; i++) {
> +		s_np = of_parse_phandle(pdev->dev.of_node, "sub-channels", i);
> +		if (s_np && of_device_is_available(s_np)) {
> +			s_pdev[i] = of_find_device_by_node(s_np);
> +			if (s_pdev[i]) {
> +				hw_schans_mask |= BIT(i);
> +				dev_dbg(&s_pdev[i]->dev, "schan%u ok\n", i);
> +			}
> +		}
> +	}
> +	return hw_schans_mask;
> +}
> +
> +static int rcar_drif_probe(struct platform_device *pdev)
> +{
> +	struct platform_device *s_pdev[RCAR_DRIF_MAX_SUBCHANS];
> +	unsigned long hw_schans_mask;
> +	struct rcar_drif_chan *ch;
> +	unsigned int i;
> +	int ret;
> +
> +	/*
> +	 * Sub-channel resources are managed by the parent channel instance.
> +	 * The sub-channel instance helps only in registering with power domain
> +	 * to aid in run-time pm support
> +	 */
> +	if (!of_find_property(pdev->dev.of_node, "sub-channels", NULL))
> +		return 0;
> +
> +	/* Parent channel instance */
> +	hw_schans_mask = rcar_drif_enum_sub_channels(pdev, s_pdev);
> +	if (!hw_schans_mask) {
> +		dev_err(&pdev->dev, "no sub-channels enabled\n");
> +		return -ENODEV;
> +	}
> +
> +
> +	/* Reserve memory for driver structure */
> +	ch = devm_kzalloc(&pdev->dev, sizeof(*ch), GFP_KERNEL);
> +	if (!ch) {
> +		ret = PTR_ERR(ch);
> +		dev_err(&pdev->dev, "failed alloc drif context\n");
> +		return ret;
> +	}
> +	ch->dev = &pdev->dev;
> +
> +	/* Parse device tree optional properties */
> +	ret = rcar_drif_parse_properties(ch);
> +	if (ret)
> +		return ret;
> +
> +	dev_dbg(ch->dev, "parsed mdr1 0x%08x\n", ch->mdr1);
> +
> +	/* Setup enabled sub-channels */
> +	for_each_rcar_drif_subchannel(i, &hw_schans_mask) {
> +		struct clk *clkp;
> +		struct resource	*res;
> +		void __iomem *base;
> +
> +		/* Peripheral clock */
> +		clkp = devm_clk_get(&s_pdev[i]->dev, "fck");
> +		if (IS_ERR(clkp)) {
> +			ret = PTR_ERR(clkp);
> +			dev_err(&s_pdev[i]->dev, "clk get failed (%d)\n", ret);
> +			return ret;
> +		}
> +
> +		/* Register map */
> +		res = platform_get_resource(s_pdev[i], IORESOURCE_MEM, 0);
> +		base = devm_ioremap_resource(&s_pdev[i]->dev, res);
> +		if (IS_ERR(base)) {
> +			ret = PTR_ERR(base);
> +			dev_err(&s_pdev[i]->dev, "ioremap failed (%d)\n", ret);
> +			return ret;
> +		}
> +
> +		/* Reserve memory for enabled sub-channel */
> +		ch->sch[i] = devm_kzalloc(&pdev->dev, sizeof(*ch->sch[i]),
> +					  GFP_KERNEL);
> +		if (!ch->sch[i]) {
> +			ret = PTR_ERR(ch);
> +			dev_err(&s_pdev[i]->dev, "failed alloc sub-channel\n");
> +			return ret;
> +		}
> +		ch->sch[i]->pdev = s_pdev[i];
> +		ch->sch[i]->clkp = clkp;
> +		ch->sch[i]->base = base;
> +		ch->sch[i]->num = i;
> +		ch->sch[i]->start = res->start;
> +		ch->sch[i]->parent = ch;
> +		ch->num_hw_schans++;
> +	}
> +	ch->hw_schans_mask = hw_schans_mask;
> +
> +	/* Validate any supported format for enabled sub-channels */
> +	ret = rcar_drif_set_default_format(ch);
> +	if (ret)
> +		return ret;
> +
> +	/* Set defaults */
> +	ch->num_hwbufs = RCAR_DRIF_DEFAULT_NUM_HWBUFS;
> +	ch->hwbuf_size = RCAR_DRIF_DEFAULT_HWBUF_SIZE;
> +
> +	mutex_init(&ch->v4l2_mutex);
> +	mutex_init(&ch->vb_queue_mutex);
> +	spin_lock_init(&ch->queued_bufs_lock);
> +	INIT_LIST_HEAD(&ch->queued_bufs);
> +
> +	/* Init videobuf2 queue structure */
> +	ch->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
> +	ch->vb_queue.io_modes = VB2_READ | VB2_MMAP | VB2_DMABUF;
> +	ch->vb_queue.drv_priv = ch;
> +	ch->vb_queue.buf_struct_size = sizeof(struct rcar_drif_frame_buf);
> +	ch->vb_queue.ops = &rcar_drif_vb2_ops;
> +	ch->vb_queue.mem_ops = &vb2_vmalloc_memops;
> +	ch->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> +
> +	/* Init videobuf2 queue */
> +	ret = vb2_queue_init(&ch->vb_queue);
> +	if (ret) {
> +		dev_err(ch->dev, "could not initialize vb2 queue\n");
> +		return ret;
> +	}
> +
> +	/* Init video_device structure */
> +	ch->vdev = rcar_drif_vdev;

Don't embed video_device, use video_device_alloc instead. A lot of drivers
embed this, but it turns out not to be a good idea. So new drivers should
use video_device_alloc.

> +	ch->vdev.lock = &ch->v4l2_mutex;
> +	ch->vdev.queue = &ch->vb_queue;
> +	ch->vdev.queue->lock = &ch->vb_queue_mutex;
> +	ch->vdev.ctrl_handler = &ch->ctrl_hdl;
> +	video_set_drvdata(&ch->vdev, ch);
> +
> +	/* Register the v4l2_device */
> +	ret = v4l2_device_register(&pdev->dev, &ch->v4l2_dev);
> +	if (ret) {
> +		dev_err(ch->dev, "failed v4l2_device_register. ret %d\n", ret);
> +		return ret;
> +	}
> +
> +	ch->vdev.v4l2_dev = &ch->v4l2_dev;
> +
> +	/*
> +	 * Parse subdevs after v4l2_device_register because if the subdev
> +	 * is already probed, bound and complete will be called immediately
> +	 */
> +	ret = rcar_drif_parse_subdevs(&pdev->dev, &ch->notifier);
> +	if (ret)
> +		goto err_unreg_v4l2;
> +
> +	ch->notifier.bound = rcar_drif_notify_bound;
> +	ch->notifier.complete = rcar_drif_notify_complete;
> +
> +	/* Register notifier */
> +	ret = v4l2_async_notifier_register(&ch->v4l2_dev, &ch->notifier);
> +	if (ret < 0) {
> +		dev_err(ch->dev, "notifier registration failed\n");
> +		goto err_unreg_v4l2;
> +	}
> +
> +	/* Register SDR device */
> +	ret = video_register_device(&ch->vdev, VFL_TYPE_SDR, -1);
> +	if (ret) {
> +		dev_err(ch->dev, "failed video_register_device. ret %d\n", ret);
> +		goto err_unreg_notif;
> +	}
> +
> +	platform_set_drvdata(pdev, ch);
> +	dev_notice(ch->dev, "probed\n");
> +	return 0;
> +
> +err_unreg_notif:
> +	v4l2_async_notifier_unregister(&ch->notifier);
> +err_unreg_v4l2:
> +	v4l2_device_unregister(&ch->v4l2_dev);
> +	return ret;
> +}
> +
> +static int rcar_drif_remove(struct platform_device *pdev)
> +{
> +	struct rcar_drif_chan *ch = platform_get_drvdata(pdev);
> +
> +	if (!ch)
> +		return 0;
> +
> +	/* Parent channel instance */
> +	ch = platform_get_drvdata(pdev);
> +	v4l2_ctrl_handler_free(ch->v4l2_dev.ctrl_handler);
> +	v4l2_async_notifier_unregister(&ch->notifier);
> +	v4l2_device_unregister(&ch->v4l2_dev);
> +	video_unregister_device(&ch->vdev);
> +	dev_notice(ch->dev, "removed\n");
> +	return 0;
> +}
> +
> +static int __maybe_unused rcar_drif_suspend(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static int __maybe_unused rcar_drif_resume(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(rcar_drif_pm_ops, rcar_drif_suspend,
> +			 rcar_drif_resume);
> +
> +static const struct of_device_id rcar_drif_of_table[] = {
> +	{ .compatible = "renesas,rcar-gen3-drif" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, rcar_drif_of_table);
> +
> +#define RCAR_DRIF_DRV_NAME "rcar_drif"
> +static struct platform_driver rcar_drif_driver = {
> +	.driver = {
> +		.name = RCAR_DRIF_DRV_NAME,
> +		.of_match_table = of_match_ptr(rcar_drif_of_table),
> +		.pm = &rcar_drif_pm_ops,
> +		},
> +	.probe = rcar_drif_probe,
> +	.remove = rcar_drif_remove,
> +};
> +
> +module_platform_driver(rcar_drif_driver);
> +
> +MODULE_DESCRIPTION("Renesas R-Car Gen3 DRIF driver");
> +MODULE_ALIAS("platform:" RCAR_DRIF_DRV_NAME);
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>");
> 

Regards,

	Hans

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

* Re: [PATCH 2/5] media: i2c: max2175: Add MAX2175 support
  2016-11-11 13:21     ` Hans Verkuil
@ 2016-11-11 13:48       ` Laurent Pinchart
  2016-11-11 13:58         ` Hans Verkuil
  2016-11-14 15:54       ` Ramesh Shanmugasundaram
  1 sibling, 1 reply; 60+ messages in thread
From: Laurent Pinchart @ 2016-11-11 13:48 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Ramesh Shanmugasundaram, robh+dt, mark.rutland, mchehab,
	sakari.ailus, crope, chris.paterson2, geert+renesas, linux-media,
	devicetree, linux-renesas-soc

Hi Hans,

On Friday 11 Nov 2016 14:21:22 Hans Verkuil wrote:
> On 11/09/2016 04:44 PM, Ramesh Shanmugasundaram wrote:
> > This patch adds driver support for MAX2175 chip. This is Maxim
> > Integrated's RF to Bits tuner front end chip designed for software-defined
> > radio solutions. This driver exposes the tuner as a sub-device instance
> > with standard and custom controls to configure the device.
> > 
> > Signed-off-by: Ramesh Shanmugasundaram
> > <ramesh.shanmugasundaram@bp.renesas.com> ---
> > 
> >  .../devicetree/bindings/media/i2c/max2175.txt      |   61 +
> >  drivers/media/i2c/Kconfig                          |    4 +
> >  drivers/media/i2c/Makefile                         |    2 +
> >  drivers/media/i2c/max2175/Kconfig                  |    8 +
> >  drivers/media/i2c/max2175/Makefile                 |    4 +
> >  drivers/media/i2c/max2175/max2175.c                | 1558 +++++++++++++++
> >  drivers/media/i2c/max2175/max2175.h                |  108 ++
> >  7 files changed, 1745 insertions(+)
> >  create mode 100644
> >  Documentation/devicetree/bindings/media/i2c/max2175.txt
> >  create mode 100644 drivers/media/i2c/max2175/Kconfig
> >  create mode 100644 drivers/media/i2c/max2175/Makefile
> >  create mode 100644 drivers/media/i2c/max2175/max2175.c
> >  create mode 100644 drivers/media/i2c/max2175/max2175.h
> 
> <snip>
> 
> > diff --git a/drivers/media/i2c/max2175/max2175.c
> > b/drivers/media/i2c/max2175/max2175.c new file mode 100644
> > index 0000000..ec45b52
> > --- /dev/null
> > +++ b/drivers/media/i2c/max2175/max2175.c
> > @@ -0,0 +1,1558 @@
> 
> <snip>
> 
> > +/* Read/Write bit(s) on top of regmap */
> > +static int max2175_read(struct max2175 *ctx, u8 idx, u8 *val)
> > +{
> > +	u32 regval;
> > +	int ret = regmap_read(ctx->regmap, idx, &regval);
> > +
> > +	if (ret)
> > +		v4l2_err(ctx->client, "read ret(%d): idx 0x%02x\n", ret, idx);

By the way, I think I've seen a proposal to get rid of v4l2_err() in favour of 
dev_err(), was I dreaming or should this patch use dev_err() already ?

> > +
> > +	*val = regval;
> 
> Does regmap_read initialize regval even if it returns an error? If not,
> then I would initialize regval to 0 to prevent *val being uninitialized.

Better than that the error should be propagated to the caller and handled.

> > +	return ret;
> > +}

[snip]

> > +static int max2175_band_from_freq(u32 freq)
> > +{
> > +	if (freq >= 144000 && freq <= 26100000)
> > +		return MAX2175_BAND_AM;
> > +	else if (freq >= 65000000 && freq <= 108000000)
> > +		return MAX2175_BAND_FM;
> > +	else
> 
> No need for these 'else' keywords.

Indeed by in my opinion they improve readability :-)

> > +		return MAX2175_BAND_VHF;
> > +}

[snip]

> > +static const struct v4l2_ctrl_config max2175_na_rx_mode = {
> > +	.ops = &max2175_ctrl_ops,
> > +	.id = V4L2_CID_MAX2175_RX_MODE,
> > +	.name = "RX MODE",
> > +	.type = V4L2_CTRL_TYPE_MENU,
> > +	.max = ARRAY_SIZE(max2175_ctrl_na_rx_modes) - 1,
> > +	.def = 0,
> > +	.qmenu = max2175_ctrl_na_rx_modes,
> > +};
> 
> Please document all these controls better. This is part of the public API,
> so you need to give more information what this means exactly.

Should that go to Documentation/media/v4l-drivers/ ? If so "[PATCH v4 3/4] 
v4l: Add Renesas R-Car FDP1 Driver" can be used as an example.

[snip]

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 3/5] media: Add new SDR formats SC16, SC18 & SC20
  2016-11-11 13:24   ` Hans Verkuil
@ 2016-11-11 13:48     ` Laurent Pinchart
  2016-11-11 13:49     ` Laurent Pinchart
  2016-11-14 16:20     ` Ramesh Shanmugasundaram
  2 siblings, 0 replies; 60+ messages in thread
From: Laurent Pinchart @ 2016-11-11 13:48 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Ramesh Shanmugasundaram, robh+dt, mark.rutland, mchehab,
	sakari.ailus, crope, chris.paterson2, geert+renesas, linux-media,
	devicetree, linux-renesas-soc

Hi Hans,

On Friday 11 Nov 2016 14:24:41 Hans Verkuil wrote:
> On 11/09/2016 04:44 PM, Ramesh Shanmugasundaram wrote:
> > This patch adds support for the three new SDR formats. These formats
> > were prefixed with "sliced" indicating I data constitutes the top half and
> > Q data constitutes the bottom half of the received buffer.
> 
> The standard terminology for video formats is "planar". I am leaning towards
> using that here as well.
> 
> Any opinions on this?

I like that as well, but I'm obviously biased :-)

> > V4L2_SDR_FMT_SCU16BE - 14-bit complex (I & Q) unsigned big-endian sample
> > inside 16-bit. V4L2 FourCC: SC16
> > 
> > V4L2_SDR_FMT_SCU18BE - 16-bit complex (I & Q) unsigned big-endian sample
> > inside 18-bit. V4L2 FourCC: SC18
> > 
> > V4L2_SDR_FMT_SCU20BE - 18-bit complex (I & Q) unsigned big-endian sample
> > inside 20-bit. V4L2 FourCC: SC20
> > 
> > Signed-off-by: Ramesh Shanmugasundaram
> > <ramesh.shanmugasundaram@bp.renesas.com> ---
> > 
> >  drivers/media/v4l2-core/v4l2-ioctl.c | 3 +++
> >  include/uapi/linux/videodev2.h       | 3 +++
> >  2 files changed, 6 insertions(+)
> > 
> > diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c
> > b/drivers/media/v4l2-core/v4l2-ioctl.c index 181381d..d36b386 100644
> > --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> > +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> > @@ -1207,6 +1207,9 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc
> > *fmt)> 
> >  	case V4L2_SDR_FMT_CS8:		descr = "Complex S8"; break;
> >  	case V4L2_SDR_FMT_CS14LE:	descr = "Complex S14LE"; break;
> >  	case V4L2_SDR_FMT_RU12LE:	descr = "Real U12LE"; break;
> > 
> > +	case V4L2_SDR_FMT_SCU16BE:	descr = "Sliced Complex U16BE"; break;
> > +	case V4L2_SDR_FMT_SCU18BE:	descr = "Sliced Complex U18BE"; break;
> > +	case V4L2_SDR_FMT_SCU20BE:	descr = "Sliced Complex U20BE"; break;
> > 
> >  	case V4L2_TCH_FMT_DELTA_TD16:	descr = "16-bit signed deltas"; break;
> >  	case V4L2_TCH_FMT_DELTA_TD08:	descr = "8-bit signed deltas"; break;
> >  	case V4L2_TCH_FMT_TU16:		descr = "16-bit unsigned touch data"; 
break;
> > 
> > diff --git a/include/uapi/linux/videodev2.h
> > b/include/uapi/linux/videodev2.h index 4364ce6..34a9c30 100644
> > --- a/include/uapi/linux/videodev2.h
> > +++ b/include/uapi/linux/videodev2.h
> > @@ -666,6 +666,9 @@ struct v4l2_pix_format {
> > 
> >  #define V4L2_SDR_FMT_CS8          v4l2_fourcc('C', 'S', '0', '8') /*
> >  complex s8 */ #define V4L2_SDR_FMT_CS14LE       v4l2_fourcc('C', 'S',
> >  '1', '4') /* complex s14le */ #define V4L2_SDR_FMT_RU12LE      
> >  v4l2_fourcc('R', 'U', '1', '2') /* real u12le */> 
> > +#define V4L2_SDR_FMT_SCU16BE	  v4l2_fourcc('S', 'C', '1', '6') /* 
sliced
> > complex u16be */ +#define V4L2_SDR_FMT_SCU18BE	  v4l2_fourcc('S', 
'C',
> > '1', '8') /* sliced complex u18be */ +#define V4L2_SDR_FMT_SCU20BE	 
> > v4l2_fourcc('S', 'C', '2', '0') /* sliced complex u20be */> 
> >  /* Touch formats - used for Touch devices */
> >  #define V4L2_TCH_FMT_DELTA_TD16	v4l2_fourcc('T', 'D', '1', '6') /* 16-
bit
> >  signed deltas */

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 3/5] media: Add new SDR formats SC16, SC18 & SC20
  2016-11-11 13:24   ` Hans Verkuil
  2016-11-11 13:48     ` Laurent Pinchart
@ 2016-11-11 13:49     ` Laurent Pinchart
  2016-11-14 16:20     ` Ramesh Shanmugasundaram
  2 siblings, 0 replies; 60+ messages in thread
From: Laurent Pinchart @ 2016-11-11 13:49 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Ramesh Shanmugasundaram, robh+dt, mark.rutland, mchehab,
	sakari.ailus, crope, chris.paterson2, geert+renesas, linux-media,
	devicetree, linux-renesas-soc

Hi Hans,

On Friday 11 Nov 2016 14:24:41 Hans Verkuil wrote:
> On 11/09/2016 04:44 PM, Ramesh Shanmugasundaram wrote:
> > This patch adds support for the three new SDR formats. These formats
> > were prefixed with "sliced" indicating I data constitutes the top half and
> > Q data constitutes the bottom half of the received buffer.
> 
> The standard terminology for video formats is "planar". I am leaning towards
> using that here as well.

Another important question is whether we should introduce an SDR planar API. 
That's what we would like your opinion on.

> > V4L2_SDR_FMT_SCU16BE - 14-bit complex (I & Q) unsigned big-endian sample
> > inside 16-bit. V4L2 FourCC: SC16
> > 
> > V4L2_SDR_FMT_SCU18BE - 16-bit complex (I & Q) unsigned big-endian sample
> > inside 18-bit. V4L2 FourCC: SC18
> > 
> > V4L2_SDR_FMT_SCU20BE - 18-bit complex (I & Q) unsigned big-endian sample
> > inside 20-bit. V4L2 FourCC: SC20
> > 
> > Signed-off-by: Ramesh Shanmugasundaram
> > <ramesh.shanmugasundaram@bp.renesas.com> ---
> > 
> >  drivers/media/v4l2-core/v4l2-ioctl.c | 3 +++
> >  include/uapi/linux/videodev2.h       | 3 +++
> >  2 files changed, 6 insertions(+)
> > 
> > diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c
> > b/drivers/media/v4l2-core/v4l2-ioctl.c index 181381d..d36b386 100644
> > --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> > +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> > @@ -1207,6 +1207,9 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc
> > *fmt)> 
> >  	case V4L2_SDR_FMT_CS8:		descr = "Complex S8"; break;
> >  	case V4L2_SDR_FMT_CS14LE:	descr = "Complex S14LE"; break;
> >  	case V4L2_SDR_FMT_RU12LE:	descr = "Real U12LE"; break;
> > 
> > +	case V4L2_SDR_FMT_SCU16BE:	descr = "Sliced Complex U16BE"; break;
> > +	case V4L2_SDR_FMT_SCU18BE:	descr = "Sliced Complex U18BE"; break;
> > +	case V4L2_SDR_FMT_SCU20BE:	descr = "Sliced Complex U20BE"; break;
> > 
> >  	case V4L2_TCH_FMT_DELTA_TD16:	descr = "16-bit signed deltas"; break;
> >  	case V4L2_TCH_FMT_DELTA_TD08:	descr = "8-bit signed deltas"; break;
> >  	case V4L2_TCH_FMT_TU16:		descr = "16-bit unsigned touch data"; 
break;
> > 
> > diff --git a/include/uapi/linux/videodev2.h
> > b/include/uapi/linux/videodev2.h index 4364ce6..34a9c30 100644
> > --- a/include/uapi/linux/videodev2.h
> > +++ b/include/uapi/linux/videodev2.h
> > @@ -666,6 +666,9 @@ struct v4l2_pix_format {
> > 
> >  #define V4L2_SDR_FMT_CS8          v4l2_fourcc('C', 'S', '0', '8') /*
> >  complex s8 */ #define V4L2_SDR_FMT_CS14LE       v4l2_fourcc('C', 'S',
> >  '1', '4') /* complex s14le */ #define V4L2_SDR_FMT_RU12LE      
> >  v4l2_fourcc('R', 'U', '1', '2') /* real u12le */> 
> > +#define V4L2_SDR_FMT_SCU16BE	  v4l2_fourcc('S', 'C', '1', '6') /* 
sliced
> > complex u16be */ +#define V4L2_SDR_FMT_SCU18BE	  v4l2_fourcc('S', 
'C',
> > '1', '8') /* sliced complex u18be */ +#define V4L2_SDR_FMT_SCU20BE	 
> > v4l2_fourcc('S', 'C', '2', '0') /* sliced complex u20be */> 
> >  /* Touch formats - used for Touch devices */
> >  #define V4L2_TCH_FMT_DELTA_TD16	v4l2_fourcc('T', 'D', '1', '6') /* 16-
bit
> >  signed deltas */

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 2/5] media: i2c: max2175: Add MAX2175 support
  2016-11-11 13:48       ` Laurent Pinchart
@ 2016-11-11 13:58         ` Hans Verkuil
  0 siblings, 0 replies; 60+ messages in thread
From: Hans Verkuil @ 2016-11-11 13:58 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Ramesh Shanmugasundaram, robh+dt, mark.rutland, mchehab,
	sakari.ailus, crope, chris.paterson2, geert+renesas, linux-media,
	devicetree, linux-renesas-soc

On 11/11/2016 02:48 PM, Laurent Pinchart wrote:
> Hi Hans,
> 
> On Friday 11 Nov 2016 14:21:22 Hans Verkuil wrote:
>> On 11/09/2016 04:44 PM, Ramesh Shanmugasundaram wrote:
>>> This patch adds driver support for MAX2175 chip. This is Maxim
>>> Integrated's RF to Bits tuner front end chip designed for software-defined
>>> radio solutions. This driver exposes the tuner as a sub-device instance
>>> with standard and custom controls to configure the device.
>>>
>>> Signed-off-by: Ramesh Shanmugasundaram
>>> <ramesh.shanmugasundaram@bp.renesas.com> ---
>>>
>>>  .../devicetree/bindings/media/i2c/max2175.txt      |   61 +
>>>  drivers/media/i2c/Kconfig                          |    4 +
>>>  drivers/media/i2c/Makefile                         |    2 +
>>>  drivers/media/i2c/max2175/Kconfig                  |    8 +
>>>  drivers/media/i2c/max2175/Makefile                 |    4 +
>>>  drivers/media/i2c/max2175/max2175.c                | 1558 +++++++++++++++
>>>  drivers/media/i2c/max2175/max2175.h                |  108 ++
>>>  7 files changed, 1745 insertions(+)
>>>  create mode 100644
>>>  Documentation/devicetree/bindings/media/i2c/max2175.txt
>>>  create mode 100644 drivers/media/i2c/max2175/Kconfig
>>>  create mode 100644 drivers/media/i2c/max2175/Makefile
>>>  create mode 100644 drivers/media/i2c/max2175/max2175.c
>>>  create mode 100644 drivers/media/i2c/max2175/max2175.h
>>
>> <snip>
>>
>>> diff --git a/drivers/media/i2c/max2175/max2175.c
>>> b/drivers/media/i2c/max2175/max2175.c new file mode 100644
>>> index 0000000..ec45b52
>>> --- /dev/null
>>> +++ b/drivers/media/i2c/max2175/max2175.c
>>> @@ -0,0 +1,1558 @@
>>
>> <snip>
>>
>>> +/* Read/Write bit(s) on top of regmap */
>>> +static int max2175_read(struct max2175 *ctx, u8 idx, u8 *val)
>>> +{
>>> +	u32 regval;
>>> +	int ret = regmap_read(ctx->regmap, idx, &regval);
>>> +
>>> +	if (ret)
>>> +		v4l2_err(ctx->client, "read ret(%d): idx 0x%02x\n", ret, idx);
> 
> By the way, I think I've seen a proposal to get rid of v4l2_err() in favour of 
> dev_err(), was I dreaming or should this patch use dev_err() already ?

I haven't seen anything, but I may have missed it. I haven't been on top of the
mailinglist lately. I'm fine with both.

> 
>>> +
>>> +	*val = regval;
>>
>> Does regmap_read initialize regval even if it returns an error? If not,
>> then I would initialize regval to 0 to prevent *val being uninitialized.
> 
> Better than that the error should be propagated to the caller and handled.
> 
>>> +	return ret;
>>> +}
> 
> [snip]
> 
>>> +static int max2175_band_from_freq(u32 freq)
>>> +{
>>> +	if (freq >= 144000 && freq <= 26100000)
>>> +		return MAX2175_BAND_AM;
>>> +	else if (freq >= 65000000 && freq <= 108000000)
>>> +		return MAX2175_BAND_FM;
>>> +	else
>>
>> No need for these 'else' keywords.
> 
> Indeed by in my opinion they improve readability :-)
> 
>>> +		return MAX2175_BAND_VHF;
>>> +}
> 
> [snip]
> 
>>> +static const struct v4l2_ctrl_config max2175_na_rx_mode = {
>>> +	.ops = &max2175_ctrl_ops,
>>> +	.id = V4L2_CID_MAX2175_RX_MODE,
>>> +	.name = "RX MODE",
>>> +	.type = V4L2_CTRL_TYPE_MENU,
>>> +	.max = ARRAY_SIZE(max2175_ctrl_na_rx_modes) - 1,
>>> +	.def = 0,
>>> +	.qmenu = max2175_ctrl_na_rx_modes,
>>> +};
>>
>> Please document all these controls better. This is part of the public API,
>> so you need to give more information what this means exactly.
> 
> Should that go to Documentation/media/v4l-drivers/ ? If so "[PATCH v4 3/4] 
> v4l: Add Renesas R-Car FDP1 Driver" can be used as an example.

I think that's the preferred place going forward. But a comment should be
added here pointing to that file so there is a better chance of keeping
the doc and code in sync.

> 
> [snip]
> 

	Hans

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

* RE: [PATCH 2/5] media: i2c: max2175: Add MAX2175 support
  2016-11-11 13:21     ` Hans Verkuil
  2016-11-11 13:48       ` Laurent Pinchart
@ 2016-11-14 15:54       ` Ramesh Shanmugasundaram
  1 sibling, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-11-14 15:54 UTC (permalink / raw)
  To: Hans Verkuil, robh+dt, mark.rutland, mchehab, sakari.ailus, crope
  Cc: Chris Paterson, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc

Hi Hans,

Thank you for the review comments.

> On 11/09/2016 04:44 PM, Ramesh Shanmugasundaram wrote:
> > This patch adds driver support for MAX2175 chip. This is Maxim
> > Integrated's RF to Bits tuner front end chip designed for
> > software-defined radio solutions. This driver exposes the tuner as a
> > sub-device instance with standard and custom controls to configure the
> device.
> >
> > Signed-off-by: Ramesh Shanmugasundaram
> > <ramesh.shanmugasundaram@bp.renesas.com>
> > ---
> >  .../devicetree/bindings/media/i2c/max2175.txt      |   61 +
> >  drivers/media/i2c/Kconfig                          |    4 +
> >  drivers/media/i2c/Makefile                         |    2 +
> >  drivers/media/i2c/max2175/Kconfig                  |    8 +
> >  drivers/media/i2c/max2175/Makefile                 |    4 +
> >  drivers/media/i2c/max2175/max2175.c                | 1558
> ++++++++++++++++++++
> >  drivers/media/i2c/max2175/max2175.h                |  108 ++
> >  7 files changed, 1745 insertions(+)
> >  create mode 100644
> > Documentation/devicetree/bindings/media/i2c/max2175.txt
> >  create mode 100644 drivers/media/i2c/max2175/Kconfig  create mode
> > 100644 drivers/media/i2c/max2175/Makefile
> >  create mode 100644 drivers/media/i2c/max2175/max2175.c
> >  create mode 100644 drivers/media/i2c/max2175/max2175.h
> >
> 
> <snip>
> 
> > diff --git a/drivers/media/i2c/max2175/max2175.c
> > b/drivers/media/i2c/max2175/max2175.c
> > new file mode 100644
> > index 0000000..ec45b52
> > --- /dev/null
> > +++ b/drivers/media/i2c/max2175/max2175.c
> > @@ -0,0 +1,1558 @@
> 
> <snip>
> 
> > +/* Read/Write bit(s) on top of regmap */ static int
> > +max2175_read(struct max2175 *ctx, u8 idx, u8 *val) {
> > +	u32 regval;
> > +	int ret = regmap_read(ctx->regmap, idx, &regval);
> > +
> > +	if (ret)
> > +		v4l2_err(ctx->client, "read ret(%d): idx 0x%02x\n", ret, idx);
> > +
> > +	*val = regval;
> 
> Does regmap_read initialize regval even if it returns an error? If not,
> then I would initialize regval to 0 to prevent *val being uninitialized.

Agreed.

> 
> > +	return ret;
> > +}
> > +
> > +static int max2175_write(struct max2175 *ctx, u8 idx, u8 val) {
> > +	int ret = regmap_write(ctx->regmap, idx, val);
> > +
> > +	if (ret)
> > +		v4l2_err(ctx->client, "write ret(%d): idx 0x%02x val
> 0x%02x\n",
> > +			 ret, idx, val);
> > +	return ret;
> > +}
> > +
> > +static u8 max2175_read_bits(struct max2175 *ctx, u8 idx, u8 msb, u8
> > +lsb) {
> > +	u8 val;
> > +
> > +	if (max2175_read(ctx, idx, &val))
> > +		return 0;
> > +
> > +	return max2175_get_bitval(val, msb, lsb); }
> > +
> > +static bool max2175_read_bit(struct max2175 *ctx, u8 idx, u8 bit) {
> > +	return !!max2175_read_bits(ctx, idx, bit, bit); }
> > +
> > +static int max2175_write_bits(struct max2175 *ctx, u8 idx,
> > +			     u8 msb, u8 lsb, u8 newval)
> > +{
> > +	int ret = regmap_update_bits(ctx->regmap, idx, GENMASK(msb, lsb),
> > +				     newval << lsb);
> > +
> > +	if (ret)
> > +		v4l2_err(ctx->client, "wbits ret(%d): idx 0x%02x\n", ret,
> idx);
> > +
> > +	return ret;
> > +}
> > +
> > +static int max2175_write_bit(struct max2175 *ctx, u8 idx, u8 bit, u8
> > +newval) {
> > +	return max2175_write_bits(ctx, idx, bit, bit, newval); }
> > +
> > +/* Checks expected pattern every msec until timeout */ static int
> > +max2175_poll_timeout(struct max2175 *ctx, u8 idx, u8 msb, u8 lsb,
> > +				u8 exp_bitval, u32 timeout_ms)
> > +{
> > +	unsigned int val;
> > +
> > +	return regmap_read_poll_timeout(ctx->regmap, idx, val,
> > +			(max2175_get_bitval(val, msb, lsb) == exp_bitval),
> > +			1000, timeout_ms * 1000);
> > +}
> > +
> > +static int max2175_poll_csm_ready(struct max2175 *ctx) {
> > +	int ret;
> > +
> > +	ret = max2175_poll_timeout(ctx, 69, 1, 1, 0, 50);
> > +	if (ret)
> > +		v4l2_err(ctx->client, "csm not ready\n");
> > +
> > +	return ret;
> > +}
> > +
> > +#define MAX2175_IS_BAND_AM(ctx)		\
> > +	(max2175_read_bits(ctx, 5, 1, 0) == MAX2175_BAND_AM)
> > +
> > +#define MAX2175_IS_BAND_VHF(ctx)	\
> > +	(max2175_read_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF)
> > +
> > +#define MAX2175_IS_FM_MODE(ctx)		\
> > +	(max2175_read_bits(ctx, 12, 5, 4) == 0)
> > +
> > +#define MAX2175_IS_FMHD_MODE(ctx)	\
> > +	(max2175_read_bits(ctx, 12, 5, 4) == 1)
> > +
> > +#define MAX2175_IS_DAB_MODE(ctx)	\
> > +	(max2175_read_bits(ctx, 12, 5, 4) == 2)
> > +
> > +static int max2175_band_from_freq(u32 freq) {
> > +	if (freq >= 144000 && freq <= 26100000)
> > +		return MAX2175_BAND_AM;
> > +	else if (freq >= 65000000 && freq <= 108000000)
> > +		return MAX2175_BAND_FM;
> > +	else
> 
> No need for these 'else' keywords.

Agreed.

> 
> > +		return MAX2175_BAND_VHF;
> > +}
> > +
> > +static int max2175_update_i2s_mode(struct max2175 *ctx, u32 rx_mode,
> > +				   u32 i2s_mode)
> > +{
> > +	max2175_write_bits(ctx, 29, 2, 0, i2s_mode);
> > +
> > +	/* Based on I2S mode value I2S_WORD_CNT values change */
> > +	switch (i2s_mode) {
> > +	case MAX2175_I2S_MODE3:
> > +		max2175_write_bits(ctx, 30, 6, 0, 1);
> > +		break;
> > +	case MAX2175_I2S_MODE2:
> > +	case MAX2175_I2S_MODE4:
> > +		max2175_write_bits(ctx, 30, 6, 0, 0);
> > +		break;
> > +	case MAX2175_I2S_MODE0:
> > +		max2175_write_bits(ctx, 30, 6, 0,
> > +			ctx->rx_modes[rx_mode].i2s_word_size);
> > +		break;
> > +	}
> > +	mxm_dbg(ctx, "update_i2s_mode %u, rx_mode %u\n", i2s_mode, rx_mode);
> > +	return 0;
> > +}

[snip]

> > +
> > +static int max2175_enum_freq_bands(struct v4l2_subdev *sd,
> > +			    struct v4l2_frequency_band *band) {
> > +	struct max2175 *ctx = max2175_from_sd(sd);
> > +
> > +	if (band->tuner == 0 && band->index == 0)
> > +		*band = *ctx->bands_rf;
> > +	else
> > +		return -EINVAL;
> 
> This is a bit ugly. I would invert the condition and return -EINVAL.
> Then assign *band and return 0.

Agreed.

> 
> > +
> > +	return 0;
> > +}
> > +
> > +static int max2175_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner
> > +*vt) {
> > +	struct max2175 *ctx = max2175_from_sd(sd);
> > +
> > +	if (vt->index > 0)
> > +		return -EINVAL;
> > +
> > +	strlcpy(vt->name, "RF", sizeof(vt->name));
> > +	vt->type = V4L2_TUNER_RF;
> > +	vt->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
> > +	vt->rangelow = ctx->bands_rf->rangelow;
> > +	vt->rangehigh = ctx->bands_rf->rangehigh;
> > +	return 0;
> > +}
> > +
> > +static int max2175_s_tuner(struct v4l2_subdev *sd, const struct
> > +v4l2_tuner *vt) {
> > +	/* Check tuner index is valid */
> > +	if (vt->index > 0)
> > +		return -EINVAL;
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct v4l2_subdev_tuner_ops max2175_tuner_ops = {
> > +	.s_frequency = max2175_s_frequency,
> > +	.g_frequency = max2175_g_frequency,
> > +	.enum_freq_bands = max2175_enum_freq_bands,
> > +	.g_tuner = max2175_g_tuner,
> > +	.s_tuner = max2175_s_tuner,
> > +};
> > +
> > +static const struct v4l2_subdev_ops max2175_ops = {
> > +	.tuner = &max2175_tuner_ops,
> > +};
> > +
> > +static const struct v4l2_ctrl_ops max2175_ctrl_ops = {
> > +	.s_ctrl = max2175_s_ctrl,
> > +	.g_volatile_ctrl = max2175_g_volatile_ctrl, };
> > +
> > +static const struct v4l2_ctrl_config max2175_i2s_en = {
> > +	.ops = &max2175_ctrl_ops,
> > +	.id = V4L2_CID_MAX2175_I2S_ENABLE,
> > +	.name = "I2S Enable",
> > +	.type = V4L2_CTRL_TYPE_BOOLEAN,
> > +	.min = 0,
> > +	.max = 1,
> > +	.step = 1,
> > +	.def = 1,
> > +};
> > +
> > +static const char * const max2175_ctrl_i2s_modes[] = {
> > +	[MAX2175_I2S_MODE0]	= "i2s mode 0",
> > +	[MAX2175_I2S_MODE1]	= "i2s mode 1 (skipped)",
> > +	[MAX2175_I2S_MODE2]	= "i2s mode 2",
> > +	[MAX2175_I2S_MODE3]	= "i2s mode 3",
> > +	[MAX2175_I2S_MODE4]	= "i2s mode 4",
> > +};
> > +
> > +static const struct v4l2_ctrl_config max2175_i2s_mode = {
> > +	.ops = &max2175_ctrl_ops,
> > +	.id = V4L2_CID_MAX2175_I2S_MODE,
> > +	.name = "I2S MODE value",
> > +	.type = V4L2_CTRL_TYPE_MENU,
> > +	.max = ARRAY_SIZE(max2175_ctrl_i2s_modes) - 1,
> > +	.def = 0,
> > +	.menu_skip_mask = 0x02,
> > +	.qmenu = max2175_ctrl_i2s_modes,
> > +};
> 
> Is this something that is changed dynamically? It looks more like a device
> tree thing (it's not clear what it does, so obviously I can't be sure).

Yes. It can be changed dynamically. 

> 
> > +
> > +static const struct v4l2_ctrl_config max2175_hsls = {
> > +	.ops = &max2175_ctrl_ops,
> > +	.id = V4L2_CID_MAX2175_HSLS,
> > +	.name = "HSLS above/below desired",
> > +	.type = V4L2_CTRL_TYPE_INTEGER,
> > +	.min = 0,
> > +	.max = 1,
> > +	.step = 1,
> > +	.def = 1,
> > +};
> > +
> > +static const char * const max2175_ctrl_eu_rx_modes[] = {
> > +	[MAX2175_EU_FM_1_2]	= "EU FM 1.2",
> > +	[MAX2175_DAB_1_2]	= "DAB 1.2",
> > +};
> > +
> > +static const char * const max2175_ctrl_na_rx_modes[] = {
> > +	[MAX2175_NA_FM_1_0]	= "NA FM 1.0",
> > +	[MAX2175_NA_FM_2_0]	= "NA FM 2.0",
> > +};
> > +
> > +static const struct v4l2_ctrl_config max2175_eu_rx_mode = {
> > +	.ops = &max2175_ctrl_ops,
> > +	.id = V4L2_CID_MAX2175_RX_MODE,
> > +	.name = "RX MODE",
> > +	.type = V4L2_CTRL_TYPE_MENU,
> > +	.max = ARRAY_SIZE(max2175_ctrl_eu_rx_modes) - 1,
> > +	.def = 0,
> > +	.qmenu = max2175_ctrl_eu_rx_modes,
> > +};
> > +
> > +static const struct v4l2_ctrl_config max2175_na_rx_mode = {
> > +	.ops = &max2175_ctrl_ops,
> > +	.id = V4L2_CID_MAX2175_RX_MODE,
> > +	.name = "RX MODE",
> > +	.type = V4L2_CTRL_TYPE_MENU,
> > +	.max = ARRAY_SIZE(max2175_ctrl_na_rx_modes) - 1,
> > +	.def = 0,
> > +	.qmenu = max2175_ctrl_na_rx_modes,
> > +};
> 
> Please document all these controls better. This is part of the public API,
> so you need to give more information what this means exactly.

Thanks. Now, I have added a one-liner and a bit descriptive explanation at Documentation/media/v4l-drivers dir as you & Laurent concluded.

Thanks,
Ramesh

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

* RE: [PATCH 5/5] media: platform: rcar_drif: Add DRIF support
  2016-11-11 13:38   ` Hans Verkuil
@ 2016-11-14 16:11         ` Ramesh Shanmugasundaram
  0 siblings, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-11-14 16:11 UTC (permalink / raw)
  To: Hans Verkuil, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, mchehab-DgEjT+Ai2ygdnm+yROfE0A,
	sakari.ailus-VuQAYsv1563Yd54FQh9/CA, crope-X3B1VOXEql0
  Cc: Chris Paterson, laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
	geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ,
	linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA

Hi Hans,

Thanks for the review comments.

> Subject: Re: [PATCH 5/5] media: platform: rcar_drif: Add DRIF support
> 
> On 11/09/2016 04:44 PM, Ramesh Shanmugasundaram wrote:
> > This patch adds Digital Radio Interface (DRIF) support to R-Car Gen3
> SoCs.
> > The driver exposes each instance of DRIF as a V4L2 SDR device. A DRIF
> > device represents a channel and each channel can have one or two
> > sub-channels respectively depending on the target board.
> >
> > DRIF supports only Rx functionality. It receives samples from a RF
> > frontend tuner chip it is interfaced with. The combination of DRIF and
> > the tuner device, which is registered as a sub-device, determines the
> > receive sample rate and format.
> >
> > In order to be compliant as a V4L2 SDR device, DRIF needs to bind with
> > the tuner device, which can be provided by a third party vendor. DRIF
> > acts as a slave device and the tuner device acts as a master
> > transmitting the samples. The driver allows asynchronous binding of a
> > tuner device that is registered as a v4l2 sub-device. The driver can
> > learn about the tuner it is interfaced with based on port endpoint
> > properties of the device in device tree. The V4L2 SDR device inherits
> > the controls exposed by the tuner device.
> >
> > The device can also be configured to use either one or both of the
> > data pins at runtime based on the master (tuner) configuration.
> >
> > Signed-off-by: Ramesh Shanmugasundaram
> > <ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>
> > ---
> >  .../devicetree/bindings/media/renesas,drif.txt     |  136 ++
> >  drivers/media/platform/Kconfig                     |   25 +
> >  drivers/media/platform/Makefile                    |    1 +
> >  drivers/media/platform/rcar_drif.c                 | 1574
> ++++++++++++++++++++
> >  4 files changed, 1736 insertions(+)
> >  create mode 100644
> > Documentation/devicetree/bindings/media/renesas,drif.txt
> >  create mode 100644 drivers/media/platform/rcar_drif.c
> >
> > diff --git a/Documentation/devicetree/bindings/media/renesas,drif.txt
> > b/Documentation/devicetree/bindings/media/renesas,drif.txt
> > new file mode 100644
> > index 0000000..d65368a
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> > @@ -0,0 +1,136 @@
> > +Renesas R-Car Gen3 Digital Radio Interface controller (DRIF)
> > +------------------------------------------------------------
> > +
> > +R-Car Gen3 DRIF is a serial slave device. It interfaces with a master
> > +device as shown below
> > +
> > ++---------------------+                +---------------------+
> > +|                     |-----SCK------->|CLK                  |
> > +|       Master        |-----SS-------->|SYNC  DRIFn (slave)  |
> > +|                     |-----SD0------->|D0                   |
> > +|                     |-----SD1------->|D1                   |
> > ++---------------------+                +---------------------+
> > +
> > +Each DRIF channel (drifn) consists of two sub-channels (drifn0 &
> drifn1).
> > +The sub-channels are like two individual channels in itself that
> > +share the common CLK & SYNC. Each sub-channel has it's own dedicated
> > +resources like irq, dma channels, address space & clock.
> > +
> > +The device tree model represents the channel and each of it's
> > +sub-channel as a separate node. The parent channel ties the
> > +sub-channels together with their phandles.
> > +
> > +Required properties of a sub-channel:
> > +-------------------------------------
> > +- compatible: "renesas,r8a7795-drif" if DRIF controller is a part of
> R8A7795 SoC.
> > +	      "renesas,rcar-gen3-drif" for a generic R-Car Gen3 compatible
> device.
> > +	      When compatible with the generic version, nodes must list the
> > +	      SoC-specific version corresponding to the platform first
> > +	      followed by the generic version.
> > +- reg: offset and length of that sub-channel.
> > +- interrupts: associated with that sub-channel.
> > +- clocks: phandle and clock specifier of that sub-channel.
> > +- clock-names: clock input name string: "fck".
> > +- dmas: phandles to the DMA channel of that sub-channel.
> > +- dma-names: names of the DMA channel: "rx".
> > +
> > +Optional properties of a sub-channel:
> > +-------------------------------------
> > +- power-domains: phandle to the respective power domain.
> > +
> > +Required properties of a channel:
> > +---------------------------------
> > +- pinctrl-0: pin control group to be used for this channel.
> > +- pinctrl-names: must be "default".
> > +- sub-channels : phandles to the two sub-channels.
> > +
> > +Optional properties of a channel:
> > +---------------------------------
> > +- port: child port node of a channel that defines the local and remote
> > +        endpoints. The remote endpoint is assumed to be a tuner
> subdevice
> > +	endpoint.
> > +- renesas,syncmd       : sync mode
> > +			 0 (Frame start sync pulse mode. 1-bit width pulse
> > +			    indicates start of a frame)
> > +			 1 (L/R sync or I2S mode) (default)
> > +- renesas,lsb-first    : empty property indicates lsb bit is received
> first.
> > +			 When not defined msb bit is received first (default)
> > +- renesas,syncac-pol-high  : empty property indicates sync signal
> polarity.
> > +			 When defined, active high or high->low sync signal.
> > +			 When not defined, active low or low->high sync signal
> > +			 (default)
> > +- renesas,dtdl         : delay between sync signal and start of
> reception.
> > +			 Must contain one of the following values:
> > +			 0   (no bit delay)
> > +			 50  (0.5-clock-cycle delay)
> > +			 100 (1-clock-cycle delay) (default)
> > +			 150 (1.5-clock-cycle delay)
> > +			 200 (2-clock-cycle delay)
> > +- renesas,syncdl       : delay between end of reception and sync signal
> edge.
> > +			 Must contain one of the following values:
> > +			 0   (no bit delay) (default)
> > +			 50  (0.5-clock-cycle delay)
> > +			 100 (1-clock-cycle delay)
> > +			 150 (1.5-clock-cycle delay)
> > +			 200 (2-clock-cycle delay)
> > +			 300 (3-clock-cycle delay)
> > +
> > +Example
> > +--------
> > +
> > +SoC common dtsi file
> > +
> > +		drif00: rif@e6f40000 {
> > +			compatible = "renesas,r8a7795-drif",
> > +				     "renesas,rcar-gen3-drif";
> > +			reg = <0 0xe6f40000 0 0x64>;
> > +			interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
> > +			clocks = <&cpg CPG_MOD 515>;
> > +			clock-names = "fck";
> > +			dmas = <&dmac1 0x20>, <&dmac2 0x20>;
> > +			dma-names = "rx", "rx";
> > +			power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> > +			status = "disabled";
> > +		};
> > +
> > +		drif01: rif@e6f50000 {
> > +			compatible = "renesas,r8a7795-drif",
> > +				     "renesas,rcar-gen3-drif";
> > +			reg = <0 0xe6f50000 0 0x64>;
> > +			interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
> > +			clocks = <&cpg CPG_MOD 514>;
> > +			clock-names = "fck";
> > +			dmas = <&dmac1 0x22>, <&dmac2 0x22>;
> > +			dma-names = "rx", "rx";
> > +			power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> > +			status = "disabled";
> > +		};
> > +
> > +		drif0: rif@0 {
> > +			compatible = "renesas,r8a7795-drif",
> > +				     "renesas,rcar-gen3-drif";
> > +			sub-channels = <&drif00>, <&drif01>;
> > +			status = "disabled";
> > +		};
> > +
> > +Board specific dts file
> > +
> > +&drif00 {
> > +	status = "okay";
> > +};
> > +
> > +&drif01 {
> > +	status = "okay";
> > +};
> > +
> > +&drif0 {
> > +	pinctrl-0 = <&drif0_pins>;
> > +	pinctrl-names = "default";
> > +	renesas,syncac-pol-high;
> > +	status = "okay";
> > +	port {
> > +		drif0_ep: endpoint {
> > +		     remote-endpoint = <&tuner_subdev_ep>;
> > +		};
> > +	};
> > +};
> > diff --git a/drivers/media/platform/Kconfig
> > b/drivers/media/platform/Kconfig index 754edbf1..0ae83a8 100644
> > --- a/drivers/media/platform/Kconfig
> > +++ b/drivers/media/platform/Kconfig
> > @@ -393,3 +393,28 @@ menuconfig DVB_PLATFORM_DRIVERS  if
> > DVB_PLATFORM_DRIVERS  source
> > "drivers/media/platform/sti/c8sectpfe/Kconfig"
> >  endif #DVB_PLATFORM_DRIVERS
> > +
> > +menuconfig SDR_PLATFORM_DRIVERS
> > +	bool "SDR platform devices"
> > +	depends on MEDIA_SDR_SUPPORT
> > +	default n
> > +	---help---
> > +	  Say Y here to enable support for platform-specific SDR Drivers.
> > +
> > +if SDR_PLATFORM_DRIVERS
> > +
> > +config VIDEO_RCAR_DRIF
> > +	tristate "Renesas Digitial Radio Interface (DRIF)"
> > +	depends on VIDEO_V4L2 && HAS_DMA
> > +	depends on ARCH_RENESAS
> > +	select VIDEOBUF2_VMALLOC
> > +	---help---
> > +	  Say Y if you want to enable R-Car Gen3 DRIF support. DRIF is
> Digital
> > +	  Radio Interface that interfaces with an RF front end chip. It is a
> > +	  receiver of digital data which uses DMA to transfer received data
> to
> > +	  a configured location for an application to use.
> > +
> > +	  To compile this driver as a module, choose M here; the module
> > +	  will be called rcar_drif.
> > +
> > +endif # SDR_PLATFORM_DRIVERS
> > diff --git a/drivers/media/platform/Makefile
> > b/drivers/media/platform/Makefile index f842933..49ce238 100644
> > --- a/drivers/media/platform/Makefile
> > +++ b/drivers/media/platform/Makefile
> > @@ -49,6 +49,7 @@ obj-$(CONFIG_SOC_CAMERA)		+= soc_camera/
> >
> >  obj-$(CONFIG_VIDEO_RENESAS_FCP) 	+= rcar-fcp.o
> >  obj-$(CONFIG_VIDEO_RENESAS_JPU) 	+= rcar_jpu.o
> > +obj-$(CONFIG_VIDEO_RCAR_DRIF)		+= rcar_drif.o
> >  obj-$(CONFIG_VIDEO_RENESAS_VSP1)	+= vsp1/
> >
> >  obj-y	+= omap/
> > diff --git a/drivers/media/platform/rcar_drif.c
> > b/drivers/media/platform/rcar_drif.c
> > new file mode 100644
> > index 0000000..34dc282
> > --- /dev/null
> > +++ b/drivers/media/platform/rcar_drif.c
> > @@ -0,0 +1,1574 @@
> 
> <snip>
> 
> +#define for_each_rcar_drif_subdev(sd, tmp, ch)				\
> +	list_for_each_entry_safe(sd, tmp, &ch->v4l2_dev.subdevs, list)
> +
> 
> Please don't use this. media/v4l2-device.h has a bunch of similar
> functions for this. Use those instead.

Thanks. Agreed.

> 
> <snip>
> 
> > +static int rcar_drif_querycap(struct file *file, void *fh,
> > +			      struct v4l2_capability *cap) {
> > +	struct rcar_drif_chan *ch = video_drvdata(file);
> > +
> > +	strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
> > +	strlcpy(cap->card, ch->vdev.name, sizeof(cap->card));
> > +	cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER |
> > +				   V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
> > +	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
> 
> Set device_caps in struct video_device and drop it here.
> 
> The core will fill in cap->device_caps and cap->capabilities for you.

Agreed.

> 
> > +	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
> > +		 ch->vdev.name);
> > +	return 0;
> > +}
> > +
> > +static int rcar_drif_set_default_format(struct rcar_drif_chan *ch) {
> > +	unsigned int i;
> > +
> > +	for (i = 0; i < NUM_FORMATS; i++) {
> > +		/* Find any matching fmt and set it as default */
> > +		if (ch->num_hw_schans == formats[i].num_schans) {
> > +			ch->fmt_idx = i;
> > +			ch->cur_schans_mask = ch->hw_schans_mask;
> > +			ch->num_cur_schans = ch->num_hw_schans;
> > +			dev_dbg(ch->dev, "default fmt[%u]: mask %lu num %u\n",
> > +				i, ch->cur_schans_mask, ch->num_cur_schans);
> > +			return 0;
> > +		}
> > +	}
> > +	dev_err(ch->dev, "no matching sdr fmt found\n");
> > +	return -EINVAL;
> > +}
> > +
> > +static int rcar_drif_enum_fmt_sdr_cap(struct file *file, void *priv,
> > +				      struct v4l2_fmtdesc *f)
> > +{
> > +	if (f->index >= NUM_FORMATS)
> > +		return -EINVAL;
> > +
> > +	strlcpy(f->description, formats[f->index].name,
> > +sizeof(f->description));
> 
> Drop this. The core fills that in for you.
> 

Agreed.

> > +	f->pixelformat = formats[f->index].pixelformat;
> > +	return 0;
> > +}
> > +
> > +static int rcar_drif_g_fmt_sdr_cap(struct file *file, void *priv,
> > +				   struct v4l2_format *f)
> > +{
> > +	struct rcar_drif_chan *ch = video_drvdata(file);
> > +
> > +	f->fmt.sdr.pixelformat = formats[ch->fmt_idx].pixelformat;
> > +	f->fmt.sdr.buffersize = formats[ch->fmt_idx].buffersize;
> > +	memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
> > +	return 0;
> > +}
> > +

[snip]

> > +/* Parse sub-devs (tuner) to find a matching device */ static int
> > +rcar_drif_parse_subdevs(struct device *dev,
> > +				   struct v4l2_async_notifier *notifier) {
> > +	struct device_node *node = NULL;
> > +
> > +	notifier->subdevs = devm_kzalloc(dev, sizeof(*notifier->subdevs),
> > +					 GFP_KERNEL);
> > +	if (!notifier->subdevs)
> > +		return -ENOMEM;
> > +
> > +	node = of_graph_get_next_endpoint(dev->of_node, node);
> 
> Do:
> 
> 	if (!node)
> 		return 0;
> 
> And the remainder can be shifted one tab to the left.

Agreed.

> 
> > +	if (node) {
> > +		struct rcar_drif_async_subdev *rsd;
> > +
> > +		rsd = devm_kzalloc(dev, sizeof(*rsd), GFP_KERNEL);
> > +		if (!rsd) {
> > +			of_node_put(node);
> > +			return -ENOMEM;
> > +		}
> > +
> > +		notifier->subdevs[notifier->num_subdevs] = &rsd->asd;
> > +		rsd->asd.match.of.node =
> of_graph_get_remote_port_parent(node);
> > +		of_node_put(node);
> > +		if (!rsd->asd.match.of.node) {
> > +			dev_warn(dev, "bad remote port parent\n");
> > +			return -EINVAL;
> > +		}
> > +
> > +		rsd->asd.match_type = V4L2_ASYNC_MATCH_OF;
> > +		notifier->num_subdevs++;
> > +	}
> > +	return 0;
> > +}
> > +
> > +/* SIRMDR1 configuration */
> > +static int rcar_drif_validate_syncmd(struct rcar_drif_chan *ch, u32
> > +val) {
> > +	if (val > 1) {
> > +		dev_err(ch->dev, "invalid syncmd %u using L/R mode\n", val);
> > +		return -EINVAL;
> > +	}
> > +
> > +	ch->mdr1 &= ~(3 << 28);	/* Clear current settings */
> > +	if (val == 0)
> > +		ch->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCMD_FRAME;
> > +	else
> > +		ch->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCMD_LR;
> > +	return 0;
> > +}
> > +
> > +/* Get the dtdl or syncdl bits as in MSIOF */ static u32
> > +rcar_drif_get_dtdl_or_syncdl_bits(u32 dtdl_or_syncdl) {
> > +	/*
> > +	 * DTDL/SYNCDL bit	: dtdl/syncdl
> > +	 * b'000		: 0
> > +	 * b'001		: 100
> > +	 * b'010		: 200
> > +	 * b'011 (SYNCDL only)	: 300
> > +	 * b'101		: 50
> > +	 * b'110		: 150
> > +	 */
> > +	if (dtdl_or_syncdl % 100)
> > +		return dtdl_or_syncdl / 100 + 5;
> > +	else
> 
> Line can be dropped.

Agreed.

> 
> > +		return dtdl_or_syncdl / 100;
> > +}
> > +
> > +static int rcar_drif_validate_dtdl_syncdl(struct rcar_drif_chan *ch)
> > +{
> > +	struct device_node *np = ch->dev->of_node;
> > +	u32 dtdl = 100, syncdl = 0;
> > +
> > +	ch->mdr1 |= RCAR_DRIF_SIRMDR1_DTDL_1 | RCAR_DRIF_SIRMDR1_SYNCDL_0;
> > +	of_property_read_u32(np, "renesas,dtdl", &dtdl);
> > +	of_property_read_u32(np, "renesas,syncdl", &syncdl);
> > +
> > +	/* Sanity checks */
> > +	if (dtdl > 200 || syncdl > 300) {
> > +		dev_err(ch->dev, "invalid dtdl %u/syncdl %u\n", dtdl, syncdl);
> > +		return -EINVAL;
> > +	}
> > +	if ((dtdl + syncdl) % 100) {
> > +		dev_err(ch->dev, "sum of dtdl %u & syncdl %u not OK\n",
> > +			dtdl, syncdl);
> > +		return -EINVAL;
> > +	}
> > +	ch->mdr1 &= ~(7 << 20) & ~(7 << 16);	/* Clear current settings
> */
> > +	ch->mdr1 |= rcar_drif_get_dtdl_or_syncdl_bits(dtdl) << 20;
> > +	ch->mdr1 |= rcar_drif_get_dtdl_or_syncdl_bits(syncdl) << 16;
> > +	return 0;
> > +}
> > +
> > +static int rcar_drif_parse_properties(struct rcar_drif_chan *ch) {
> > +	struct device_node *np = ch->dev->of_node;
> > +	u32 syncmd;
> > +	int ret;
> > +
> > +	/* Set the defaults and check for overrides */
> > +	ch->mdr1 = RCAR_DRIF_SIRMDR1_SYNCMD_LR;
> > +	if (!of_property_read_u32(np, "renesas,syncmd", &syncmd)) {
> > +		ret = rcar_drif_validate_syncmd(ch, syncmd);
> > +		if (ret)
> > +			return ret;
> > +	}
> > +
> > +	if (of_find_property(np, "renesas,lsb-first", NULL))
> > +		ch->mdr1 |= RCAR_DRIF_SIRMDR1_LSB_FIRST;
> > +	else
> > +		ch->mdr1 |= RCAR_DRIF_SIRMDR1_MSB_FIRST;
> > +
> > +	if (of_find_property(np, "renesas,syncac-pol-high", NULL))
> > +		ch->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCAC_POL_HIGH;
> > +	else
> > +		ch->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCAC_POL_LOW;
> > +
> > +	return rcar_drif_validate_dtdl_syncdl(ch);
> > +}
> > +
> > +static u32 rcar_drif_enum_sub_channels(struct platform_device *pdev,
> > +					struct platform_device *s_pdev[]) {
> > +	struct device_node *s_np;
> > +	u32 hw_schans_mask = 0;
> > +	unsigned int i;
> > +
> > +	for (i = 0; i < RCAR_DRIF_MAX_SUBCHANS; i++) {
> > +		s_np = of_parse_phandle(pdev->dev.of_node, "sub-channels", i);
> > +		if (s_np && of_device_is_available(s_np)) {
> > +			s_pdev[i] = of_find_device_by_node(s_np);
> > +			if (s_pdev[i]) {
> > +				hw_schans_mask |= BIT(i);
> > +				dev_dbg(&s_pdev[i]->dev, "schan%u ok\n", i);
> > +			}
> > +		}
> > +	}
> > +	return hw_schans_mask;
> > +}
> > +
> > +static int rcar_drif_probe(struct platform_device *pdev) {
> > +	struct platform_device *s_pdev[RCAR_DRIF_MAX_SUBCHANS];
> > +	unsigned long hw_schans_mask;
> > +	struct rcar_drif_chan *ch;
> > +	unsigned int i;
> > +	int ret;
> > +
> > +	/*
> > +	 * Sub-channel resources are managed by the parent channel instance.
> > +	 * The sub-channel instance helps only in registering with power
> domain
> > +	 * to aid in run-time pm support
> > +	 */
> > +	if (!of_find_property(pdev->dev.of_node, "sub-channels", NULL))
> > +		return 0;
> > +
> > +	/* Parent channel instance */
> > +	hw_schans_mask = rcar_drif_enum_sub_channels(pdev, s_pdev);
> > +	if (!hw_schans_mask) {
> > +		dev_err(&pdev->dev, "no sub-channels enabled\n");
> > +		return -ENODEV;
> > +	}
> > +
> > +
> > +	/* Reserve memory for driver structure */
> > +	ch = devm_kzalloc(&pdev->dev, sizeof(*ch), GFP_KERNEL);
> > +	if (!ch) {
> > +		ret = PTR_ERR(ch);
> > +		dev_err(&pdev->dev, "failed alloc drif context\n");
> > +		return ret;
> > +	}
> > +	ch->dev = &pdev->dev;
> > +
> > +	/* Parse device tree optional properties */
> > +	ret = rcar_drif_parse_properties(ch);
> > +	if (ret)
> > +		return ret;
> > +
> > +	dev_dbg(ch->dev, "parsed mdr1 0x%08x\n", ch->mdr1);
> > +
> > +	/* Setup enabled sub-channels */
> > +	for_each_rcar_drif_subchannel(i, &hw_schans_mask) {
> > +		struct clk *clkp;
> > +		struct resource	*res;
> > +		void __iomem *base;
> > +
> > +		/* Peripheral clock */
> > +		clkp = devm_clk_get(&s_pdev[i]->dev, "fck");
> > +		if (IS_ERR(clkp)) {
> > +			ret = PTR_ERR(clkp);
> > +			dev_err(&s_pdev[i]->dev, "clk get failed (%d)\n", ret);
> > +			return ret;
> > +		}
> > +
> > +		/* Register map */
> > +		res = platform_get_resource(s_pdev[i], IORESOURCE_MEM, 0);
> > +		base = devm_ioremap_resource(&s_pdev[i]->dev, res);
> > +		if (IS_ERR(base)) {
> > +			ret = PTR_ERR(base);
> > +			dev_err(&s_pdev[i]->dev, "ioremap failed (%d)\n", ret);
> > +			return ret;
> > +		}
> > +
> > +		/* Reserve memory for enabled sub-channel */
> > +		ch->sch[i] = devm_kzalloc(&pdev->dev, sizeof(*ch->sch[i]),
> > +					  GFP_KERNEL);
> > +		if (!ch->sch[i]) {
> > +			ret = PTR_ERR(ch);
> > +			dev_err(&s_pdev[i]->dev, "failed alloc sub-channel\n");
> > +			return ret;
> > +		}
> > +		ch->sch[i]->pdev = s_pdev[i];
> > +		ch->sch[i]->clkp = clkp;
> > +		ch->sch[i]->base = base;
> > +		ch->sch[i]->num = i;
> > +		ch->sch[i]->start = res->start;
> > +		ch->sch[i]->parent = ch;
> > +		ch->num_hw_schans++;
> > +	}
> > +	ch->hw_schans_mask = hw_schans_mask;
> > +
> > +	/* Validate any supported format for enabled sub-channels */
> > +	ret = rcar_drif_set_default_format(ch);
> > +	if (ret)
> > +		return ret;
> > +
> > +	/* Set defaults */
> > +	ch->num_hwbufs = RCAR_DRIF_DEFAULT_NUM_HWBUFS;
> > +	ch->hwbuf_size = RCAR_DRIF_DEFAULT_HWBUF_SIZE;
> > +
> > +	mutex_init(&ch->v4l2_mutex);
> > +	mutex_init(&ch->vb_queue_mutex);
> > +	spin_lock_init(&ch->queued_bufs_lock);
> > +	INIT_LIST_HEAD(&ch->queued_bufs);
> > +
> > +	/* Init videobuf2 queue structure */
> > +	ch->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
> > +	ch->vb_queue.io_modes = VB2_READ | VB2_MMAP | VB2_DMABUF;
> > +	ch->vb_queue.drv_priv = ch;
> > +	ch->vb_queue.buf_struct_size = sizeof(struct rcar_drif_frame_buf);
> > +	ch->vb_queue.ops = &rcar_drif_vb2_ops;
> > +	ch->vb_queue.mem_ops = &vb2_vmalloc_memops;
> > +	ch->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> > +
> > +	/* Init videobuf2 queue */
> > +	ret = vb2_queue_init(&ch->vb_queue);
> > +	if (ret) {
> > +		dev_err(ch->dev, "could not initialize vb2 queue\n");
> > +		return ret;
> > +	}
> > +
> > +	/* Init video_device structure */
> > +	ch->vdev = rcar_drif_vdev;
> 
> Don't embed video_device, use video_device_alloc instead. A lot of drivers
> embed this, but it turns out not to be a good idea. So new drivers should
> use video_device_alloc.

Agreed. 

Thanks,
Ramesh
--
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 5/5] media: platform: rcar_drif: Add DRIF support
@ 2016-11-14 16:11         ` Ramesh Shanmugasundaram
  0 siblings, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-11-14 16:11 UTC (permalink / raw)
  To: Hans Verkuil, robh+dt, mark.rutland, mchehab, sakari.ailus, crope
  Cc: Chris Paterson, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc

Hi Hans,

Thanks for the review comments.

> Subject: Re: [PATCH 5/5] media: platform: rcar_drif: Add DRIF support
> 
> On 11/09/2016 04:44 PM, Ramesh Shanmugasundaram wrote:
> > This patch adds Digital Radio Interface (DRIF) support to R-Car Gen3
> SoCs.
> > The driver exposes each instance of DRIF as a V4L2 SDR device. A DRIF
> > device represents a channel and each channel can have one or two
> > sub-channels respectively depending on the target board.
> >
> > DRIF supports only Rx functionality. It receives samples from a RF
> > frontend tuner chip it is interfaced with. The combination of DRIF and
> > the tuner device, which is registered as a sub-device, determines the
> > receive sample rate and format.
> >
> > In order to be compliant as a V4L2 SDR device, DRIF needs to bind with
> > the tuner device, which can be provided by a third party vendor. DRIF
> > acts as a slave device and the tuner device acts as a master
> > transmitting the samples. The driver allows asynchronous binding of a
> > tuner device that is registered as a v4l2 sub-device. The driver can
> > learn about the tuner it is interfaced with based on port endpoint
> > properties of the device in device tree. The V4L2 SDR device inherits
> > the controls exposed by the tuner device.
> >
> > The device can also be configured to use either one or both of the
> > data pins at runtime based on the master (tuner) configuration.
> >
> > Signed-off-by: Ramesh Shanmugasundaram
> > <ramesh.shanmugasundaram@bp.renesas.com>
> > ---
> >  .../devicetree/bindings/media/renesas,drif.txt     |  136 ++
> >  drivers/media/platform/Kconfig                     |   25 +
> >  drivers/media/platform/Makefile                    |    1 +
> >  drivers/media/platform/rcar_drif.c                 | 1574
> ++++++++++++++++++++
> >  4 files changed, 1736 insertions(+)
> >  create mode 100644
> > Documentation/devicetree/bindings/media/renesas,drif.txt
> >  create mode 100644 drivers/media/platform/rcar_drif.c
> >
> > diff --git a/Documentation/devicetree/bindings/media/renesas,drif.txt
> > b/Documentation/devicetree/bindings/media/renesas,drif.txt
> > new file mode 100644
> > index 0000000..d65368a
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> > @@ -0,0 +1,136 @@
> > +Renesas R-Car Gen3 Digital Radio Interface controller (DRIF)
> > +------------------------------------------------------------
> > +
> > +R-Car Gen3 DRIF is a serial slave device. It interfaces with a master
> > +device as shown below
> > +
> > ++---------------------+                +---------------------+
> > +|                     |-----SCK------->|CLK                  |
> > +|       Master        |-----SS-------->|SYNC  DRIFn (slave)  |
> > +|                     |-----SD0------->|D0                   |
> > +|                     |-----SD1------->|D1                   |
> > ++---------------------+                +---------------------+
> > +
> > +Each DRIF channel (drifn) consists of two sub-channels (drifn0 &
> drifn1).
> > +The sub-channels are like two individual channels in itself that
> > +share the common CLK & SYNC. Each sub-channel has it's own dedicated
> > +resources like irq, dma channels, address space & clock.
> > +
> > +The device tree model represents the channel and each of it's
> > +sub-channel as a separate node. The parent channel ties the
> > +sub-channels together with their phandles.
> > +
> > +Required properties of a sub-channel:
> > +-------------------------------------
> > +- compatible: "renesas,r8a7795-drif" if DRIF controller is a part of
> R8A7795 SoC.
> > +	      "renesas,rcar-gen3-drif" for a generic R-Car Gen3 compatible
> device.
> > +	      When compatible with the generic version, nodes must list the
> > +	      SoC-specific version corresponding to the platform first
> > +	      followed by the generic version.
> > +- reg: offset and length of that sub-channel.
> > +- interrupts: associated with that sub-channel.
> > +- clocks: phandle and clock specifier of that sub-channel.
> > +- clock-names: clock input name string: "fck".
> > +- dmas: phandles to the DMA channel of that sub-channel.
> > +- dma-names: names of the DMA channel: "rx".
> > +
> > +Optional properties of a sub-channel:
> > +-------------------------------------
> > +- power-domains: phandle to the respective power domain.
> > +
> > +Required properties of a channel:
> > +---------------------------------
> > +- pinctrl-0: pin control group to be used for this channel.
> > +- pinctrl-names: must be "default".
> > +- sub-channels : phandles to the two sub-channels.
> > +
> > +Optional properties of a channel:
> > +---------------------------------
> > +- port: child port node of a channel that defines the local and remote
> > +        endpoints. The remote endpoint is assumed to be a tuner
> subdevice
> > +	endpoint.
> > +- renesas,syncmd       : sync mode
> > +			 0 (Frame start sync pulse mode. 1-bit width pulse
> > +			    indicates start of a frame)
> > +			 1 (L/R sync or I2S mode) (default)
> > +- renesas,lsb-first    : empty property indicates lsb bit is received
> first.
> > +			 When not defined msb bit is received first (default)
> > +- renesas,syncac-pol-high  : empty property indicates sync signal
> polarity.
> > +			 When defined, active high or high->low sync signal.
> > +			 When not defined, active low or low->high sync signal
> > +			 (default)
> > +- renesas,dtdl         : delay between sync signal and start of
> reception.
> > +			 Must contain one of the following values:
> > +			 0   (no bit delay)
> > +			 50  (0.5-clock-cycle delay)
> > +			 100 (1-clock-cycle delay) (default)
> > +			 150 (1.5-clock-cycle delay)
> > +			 200 (2-clock-cycle delay)
> > +- renesas,syncdl       : delay between end of reception and sync signal
> edge.
> > +			 Must contain one of the following values:
> > +			 0   (no bit delay) (default)
> > +			 50  (0.5-clock-cycle delay)
> > +			 100 (1-clock-cycle delay)
> > +			 150 (1.5-clock-cycle delay)
> > +			 200 (2-clock-cycle delay)
> > +			 300 (3-clock-cycle delay)
> > +
> > +Example
> > +--------
> > +
> > +SoC common dtsi file
> > +
> > +		drif00: rif@e6f40000 {
> > +			compatible = "renesas,r8a7795-drif",
> > +				     "renesas,rcar-gen3-drif";
> > +			reg = <0 0xe6f40000 0 0x64>;
> > +			interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
> > +			clocks = <&cpg CPG_MOD 515>;
> > +			clock-names = "fck";
> > +			dmas = <&dmac1 0x20>, <&dmac2 0x20>;
> > +			dma-names = "rx", "rx";
> > +			power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> > +			status = "disabled";
> > +		};
> > +
> > +		drif01: rif@e6f50000 {
> > +			compatible = "renesas,r8a7795-drif",
> > +				     "renesas,rcar-gen3-drif";
> > +			reg = <0 0xe6f50000 0 0x64>;
> > +			interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
> > +			clocks = <&cpg CPG_MOD 514>;
> > +			clock-names = "fck";
> > +			dmas = <&dmac1 0x22>, <&dmac2 0x22>;
> > +			dma-names = "rx", "rx";
> > +			power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> > +			status = "disabled";
> > +		};
> > +
> > +		drif0: rif@0 {
> > +			compatible = "renesas,r8a7795-drif",
> > +				     "renesas,rcar-gen3-drif";
> > +			sub-channels = <&drif00>, <&drif01>;
> > +			status = "disabled";
> > +		};
> > +
> > +Board specific dts file
> > +
> > +&drif00 {
> > +	status = "okay";
> > +};
> > +
> > +&drif01 {
> > +	status = "okay";
> > +};
> > +
> > +&drif0 {
> > +	pinctrl-0 = <&drif0_pins>;
> > +	pinctrl-names = "default";
> > +	renesas,syncac-pol-high;
> > +	status = "okay";
> > +	port {
> > +		drif0_ep: endpoint {
> > +		     remote-endpoint = <&tuner_subdev_ep>;
> > +		};
> > +	};
> > +};
> > diff --git a/drivers/media/platform/Kconfig
> > b/drivers/media/platform/Kconfig index 754edbf1..0ae83a8 100644
> > --- a/drivers/media/platform/Kconfig
> > +++ b/drivers/media/platform/Kconfig
> > @@ -393,3 +393,28 @@ menuconfig DVB_PLATFORM_DRIVERS  if
> > DVB_PLATFORM_DRIVERS  source
> > "drivers/media/platform/sti/c8sectpfe/Kconfig"
> >  endif #DVB_PLATFORM_DRIVERS
> > +
> > +menuconfig SDR_PLATFORM_DRIVERS
> > +	bool "SDR platform devices"
> > +	depends on MEDIA_SDR_SUPPORT
> > +	default n
> > +	---help---
> > +	  Say Y here to enable support for platform-specific SDR Drivers.
> > +
> > +if SDR_PLATFORM_DRIVERS
> > +
> > +config VIDEO_RCAR_DRIF
> > +	tristate "Renesas Digitial Radio Interface (DRIF)"
> > +	depends on VIDEO_V4L2 && HAS_DMA
> > +	depends on ARCH_RENESAS
> > +	select VIDEOBUF2_VMALLOC
> > +	---help---
> > +	  Say Y if you want to enable R-Car Gen3 DRIF support. DRIF is
> Digital
> > +	  Radio Interface that interfaces with an RF front end chip. It is a
> > +	  receiver of digital data which uses DMA to transfer received data
> to
> > +	  a configured location for an application to use.
> > +
> > +	  To compile this driver as a module, choose M here; the module
> > +	  will be called rcar_drif.
> > +
> > +endif # SDR_PLATFORM_DRIVERS
> > diff --git a/drivers/media/platform/Makefile
> > b/drivers/media/platform/Makefile index f842933..49ce238 100644
> > --- a/drivers/media/platform/Makefile
> > +++ b/drivers/media/platform/Makefile
> > @@ -49,6 +49,7 @@ obj-$(CONFIG_SOC_CAMERA)		+= soc_camera/
> >
> >  obj-$(CONFIG_VIDEO_RENESAS_FCP) 	+= rcar-fcp.o
> >  obj-$(CONFIG_VIDEO_RENESAS_JPU) 	+= rcar_jpu.o
> > +obj-$(CONFIG_VIDEO_RCAR_DRIF)		+= rcar_drif.o
> >  obj-$(CONFIG_VIDEO_RENESAS_VSP1)	+= vsp1/
> >
> >  obj-y	+= omap/
> > diff --git a/drivers/media/platform/rcar_drif.c
> > b/drivers/media/platform/rcar_drif.c
> > new file mode 100644
> > index 0000000..34dc282
> > --- /dev/null
> > +++ b/drivers/media/platform/rcar_drif.c
> > @@ -0,0 +1,1574 @@
> 
> <snip>
> 
> +#define for_each_rcar_drif_subdev(sd, tmp, ch)				\
> +	list_for_each_entry_safe(sd, tmp, &ch->v4l2_dev.subdevs, list)
> +
> 
> Please don't use this. media/v4l2-device.h has a bunch of similar
> functions for this. Use those instead.

Thanks. Agreed.

> 
> <snip>
> 
> > +static int rcar_drif_querycap(struct file *file, void *fh,
> > +			      struct v4l2_capability *cap) {
> > +	struct rcar_drif_chan *ch = video_drvdata(file);
> > +
> > +	strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
> > +	strlcpy(cap->card, ch->vdev.name, sizeof(cap->card));
> > +	cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER |
> > +				   V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
> > +	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
> 
> Set device_caps in struct video_device and drop it here.
> 
> The core will fill in cap->device_caps and cap->capabilities for you.

Agreed.

> 
> > +	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
> > +		 ch->vdev.name);
> > +	return 0;
> > +}
> > +
> > +static int rcar_drif_set_default_format(struct rcar_drif_chan *ch) {
> > +	unsigned int i;
> > +
> > +	for (i = 0; i < NUM_FORMATS; i++) {
> > +		/* Find any matching fmt and set it as default */
> > +		if (ch->num_hw_schans == formats[i].num_schans) {
> > +			ch->fmt_idx = i;
> > +			ch->cur_schans_mask = ch->hw_schans_mask;
> > +			ch->num_cur_schans = ch->num_hw_schans;
> > +			dev_dbg(ch->dev, "default fmt[%u]: mask %lu num %u\n",
> > +				i, ch->cur_schans_mask, ch->num_cur_schans);
> > +			return 0;
> > +		}
> > +	}
> > +	dev_err(ch->dev, "no matching sdr fmt found\n");
> > +	return -EINVAL;
> > +}
> > +
> > +static int rcar_drif_enum_fmt_sdr_cap(struct file *file, void *priv,
> > +				      struct v4l2_fmtdesc *f)
> > +{
> > +	if (f->index >= NUM_FORMATS)
> > +		return -EINVAL;
> > +
> > +	strlcpy(f->description, formats[f->index].name,
> > +sizeof(f->description));
> 
> Drop this. The core fills that in for you.
> 

Agreed.

> > +	f->pixelformat = formats[f->index].pixelformat;
> > +	return 0;
> > +}
> > +
> > +static int rcar_drif_g_fmt_sdr_cap(struct file *file, void *priv,
> > +				   struct v4l2_format *f)
> > +{
> > +	struct rcar_drif_chan *ch = video_drvdata(file);
> > +
> > +	f->fmt.sdr.pixelformat = formats[ch->fmt_idx].pixelformat;
> > +	f->fmt.sdr.buffersize = formats[ch->fmt_idx].buffersize;
> > +	memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
> > +	return 0;
> > +}
> > +

[snip]

> > +/* Parse sub-devs (tuner) to find a matching device */ static int
> > +rcar_drif_parse_subdevs(struct device *dev,
> > +				   struct v4l2_async_notifier *notifier) {
> > +	struct device_node *node = NULL;
> > +
> > +	notifier->subdevs = devm_kzalloc(dev, sizeof(*notifier->subdevs),
> > +					 GFP_KERNEL);
> > +	if (!notifier->subdevs)
> > +		return -ENOMEM;
> > +
> > +	node = of_graph_get_next_endpoint(dev->of_node, node);
> 
> Do:
> 
> 	if (!node)
> 		return 0;
> 
> And the remainder can be shifted one tab to the left.

Agreed.

> 
> > +	if (node) {
> > +		struct rcar_drif_async_subdev *rsd;
> > +
> > +		rsd = devm_kzalloc(dev, sizeof(*rsd), GFP_KERNEL);
> > +		if (!rsd) {
> > +			of_node_put(node);
> > +			return -ENOMEM;
> > +		}
> > +
> > +		notifier->subdevs[notifier->num_subdevs] = &rsd->asd;
> > +		rsd->asd.match.of.node =
> of_graph_get_remote_port_parent(node);
> > +		of_node_put(node);
> > +		if (!rsd->asd.match.of.node) {
> > +			dev_warn(dev, "bad remote port parent\n");
> > +			return -EINVAL;
> > +		}
> > +
> > +		rsd->asd.match_type = V4L2_ASYNC_MATCH_OF;
> > +		notifier->num_subdevs++;
> > +	}
> > +	return 0;
> > +}
> > +
> > +/* SIRMDR1 configuration */
> > +static int rcar_drif_validate_syncmd(struct rcar_drif_chan *ch, u32
> > +val) {
> > +	if (val > 1) {
> > +		dev_err(ch->dev, "invalid syncmd %u using L/R mode\n", val);
> > +		return -EINVAL;
> > +	}
> > +
> > +	ch->mdr1 &= ~(3 << 28);	/* Clear current settings */
> > +	if (val == 0)
> > +		ch->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCMD_FRAME;
> > +	else
> > +		ch->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCMD_LR;
> > +	return 0;
> > +}
> > +
> > +/* Get the dtdl or syncdl bits as in MSIOF */ static u32
> > +rcar_drif_get_dtdl_or_syncdl_bits(u32 dtdl_or_syncdl) {
> > +	/*
> > +	 * DTDL/SYNCDL bit	: dtdl/syncdl
> > +	 * b'000		: 0
> > +	 * b'001		: 100
> > +	 * b'010		: 200
> > +	 * b'011 (SYNCDL only)	: 300
> > +	 * b'101		: 50
> > +	 * b'110		: 150
> > +	 */
> > +	if (dtdl_or_syncdl % 100)
> > +		return dtdl_or_syncdl / 100 + 5;
> > +	else
> 
> Line can be dropped.

Agreed.

> 
> > +		return dtdl_or_syncdl / 100;
> > +}
> > +
> > +static int rcar_drif_validate_dtdl_syncdl(struct rcar_drif_chan *ch)
> > +{
> > +	struct device_node *np = ch->dev->of_node;
> > +	u32 dtdl = 100, syncdl = 0;
> > +
> > +	ch->mdr1 |= RCAR_DRIF_SIRMDR1_DTDL_1 | RCAR_DRIF_SIRMDR1_SYNCDL_0;
> > +	of_property_read_u32(np, "renesas,dtdl", &dtdl);
> > +	of_property_read_u32(np, "renesas,syncdl", &syncdl);
> > +
> > +	/* Sanity checks */
> > +	if (dtdl > 200 || syncdl > 300) {
> > +		dev_err(ch->dev, "invalid dtdl %u/syncdl %u\n", dtdl, syncdl);
> > +		return -EINVAL;
> > +	}
> > +	if ((dtdl + syncdl) % 100) {
> > +		dev_err(ch->dev, "sum of dtdl %u & syncdl %u not OK\n",
> > +			dtdl, syncdl);
> > +		return -EINVAL;
> > +	}
> > +	ch->mdr1 &= ~(7 << 20) & ~(7 << 16);	/* Clear current settings
> */
> > +	ch->mdr1 |= rcar_drif_get_dtdl_or_syncdl_bits(dtdl) << 20;
> > +	ch->mdr1 |= rcar_drif_get_dtdl_or_syncdl_bits(syncdl) << 16;
> > +	return 0;
> > +}
> > +
> > +static int rcar_drif_parse_properties(struct rcar_drif_chan *ch) {
> > +	struct device_node *np = ch->dev->of_node;
> > +	u32 syncmd;
> > +	int ret;
> > +
> > +	/* Set the defaults and check for overrides */
> > +	ch->mdr1 = RCAR_DRIF_SIRMDR1_SYNCMD_LR;
> > +	if (!of_property_read_u32(np, "renesas,syncmd", &syncmd)) {
> > +		ret = rcar_drif_validate_syncmd(ch, syncmd);
> > +		if (ret)
> > +			return ret;
> > +	}
> > +
> > +	if (of_find_property(np, "renesas,lsb-first", NULL))
> > +		ch->mdr1 |= RCAR_DRIF_SIRMDR1_LSB_FIRST;
> > +	else
> > +		ch->mdr1 |= RCAR_DRIF_SIRMDR1_MSB_FIRST;
> > +
> > +	if (of_find_property(np, "renesas,syncac-pol-high", NULL))
> > +		ch->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCAC_POL_HIGH;
> > +	else
> > +		ch->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCAC_POL_LOW;
> > +
> > +	return rcar_drif_validate_dtdl_syncdl(ch);
> > +}
> > +
> > +static u32 rcar_drif_enum_sub_channels(struct platform_device *pdev,
> > +					struct platform_device *s_pdev[]) {
> > +	struct device_node *s_np;
> > +	u32 hw_schans_mask = 0;
> > +	unsigned int i;
> > +
> > +	for (i = 0; i < RCAR_DRIF_MAX_SUBCHANS; i++) {
> > +		s_np = of_parse_phandle(pdev->dev.of_node, "sub-channels", i);
> > +		if (s_np && of_device_is_available(s_np)) {
> > +			s_pdev[i] = of_find_device_by_node(s_np);
> > +			if (s_pdev[i]) {
> > +				hw_schans_mask |= BIT(i);
> > +				dev_dbg(&s_pdev[i]->dev, "schan%u ok\n", i);
> > +			}
> > +		}
> > +	}
> > +	return hw_schans_mask;
> > +}
> > +
> > +static int rcar_drif_probe(struct platform_device *pdev) {
> > +	struct platform_device *s_pdev[RCAR_DRIF_MAX_SUBCHANS];
> > +	unsigned long hw_schans_mask;
> > +	struct rcar_drif_chan *ch;
> > +	unsigned int i;
> > +	int ret;
> > +
> > +	/*
> > +	 * Sub-channel resources are managed by the parent channel instance.
> > +	 * The sub-channel instance helps only in registering with power
> domain
> > +	 * to aid in run-time pm support
> > +	 */
> > +	if (!of_find_property(pdev->dev.of_node, "sub-channels", NULL))
> > +		return 0;
> > +
> > +	/* Parent channel instance */
> > +	hw_schans_mask = rcar_drif_enum_sub_channels(pdev, s_pdev);
> > +	if (!hw_schans_mask) {
> > +		dev_err(&pdev->dev, "no sub-channels enabled\n");
> > +		return -ENODEV;
> > +	}
> > +
> > +
> > +	/* Reserve memory for driver structure */
> > +	ch = devm_kzalloc(&pdev->dev, sizeof(*ch), GFP_KERNEL);
> > +	if (!ch) {
> > +		ret = PTR_ERR(ch);
> > +		dev_err(&pdev->dev, "failed alloc drif context\n");
> > +		return ret;
> > +	}
> > +	ch->dev = &pdev->dev;
> > +
> > +	/* Parse device tree optional properties */
> > +	ret = rcar_drif_parse_properties(ch);
> > +	if (ret)
> > +		return ret;
> > +
> > +	dev_dbg(ch->dev, "parsed mdr1 0x%08x\n", ch->mdr1);
> > +
> > +	/* Setup enabled sub-channels */
> > +	for_each_rcar_drif_subchannel(i, &hw_schans_mask) {
> > +		struct clk *clkp;
> > +		struct resource	*res;
> > +		void __iomem *base;
> > +
> > +		/* Peripheral clock */
> > +		clkp = devm_clk_get(&s_pdev[i]->dev, "fck");
> > +		if (IS_ERR(clkp)) {
> > +			ret = PTR_ERR(clkp);
> > +			dev_err(&s_pdev[i]->dev, "clk get failed (%d)\n", ret);
> > +			return ret;
> > +		}
> > +
> > +		/* Register map */
> > +		res = platform_get_resource(s_pdev[i], IORESOURCE_MEM, 0);
> > +		base = devm_ioremap_resource(&s_pdev[i]->dev, res);
> > +		if (IS_ERR(base)) {
> > +			ret = PTR_ERR(base);
> > +			dev_err(&s_pdev[i]->dev, "ioremap failed (%d)\n", ret);
> > +			return ret;
> > +		}
> > +
> > +		/* Reserve memory for enabled sub-channel */
> > +		ch->sch[i] = devm_kzalloc(&pdev->dev, sizeof(*ch->sch[i]),
> > +					  GFP_KERNEL);
> > +		if (!ch->sch[i]) {
> > +			ret = PTR_ERR(ch);
> > +			dev_err(&s_pdev[i]->dev, "failed alloc sub-channel\n");
> > +			return ret;
> > +		}
> > +		ch->sch[i]->pdev = s_pdev[i];
> > +		ch->sch[i]->clkp = clkp;
> > +		ch->sch[i]->base = base;
> > +		ch->sch[i]->num = i;
> > +		ch->sch[i]->start = res->start;
> > +		ch->sch[i]->parent = ch;
> > +		ch->num_hw_schans++;
> > +	}
> > +	ch->hw_schans_mask = hw_schans_mask;
> > +
> > +	/* Validate any supported format for enabled sub-channels */
> > +	ret = rcar_drif_set_default_format(ch);
> > +	if (ret)
> > +		return ret;
> > +
> > +	/* Set defaults */
> > +	ch->num_hwbufs = RCAR_DRIF_DEFAULT_NUM_HWBUFS;
> > +	ch->hwbuf_size = RCAR_DRIF_DEFAULT_HWBUF_SIZE;
> > +
> > +	mutex_init(&ch->v4l2_mutex);
> > +	mutex_init(&ch->vb_queue_mutex);
> > +	spin_lock_init(&ch->queued_bufs_lock);
> > +	INIT_LIST_HEAD(&ch->queued_bufs);
> > +
> > +	/* Init videobuf2 queue structure */
> > +	ch->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
> > +	ch->vb_queue.io_modes = VB2_READ | VB2_MMAP | VB2_DMABUF;
> > +	ch->vb_queue.drv_priv = ch;
> > +	ch->vb_queue.buf_struct_size = sizeof(struct rcar_drif_frame_buf);
> > +	ch->vb_queue.ops = &rcar_drif_vb2_ops;
> > +	ch->vb_queue.mem_ops = &vb2_vmalloc_memops;
> > +	ch->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> > +
> > +	/* Init videobuf2 queue */
> > +	ret = vb2_queue_init(&ch->vb_queue);
> > +	if (ret) {
> > +		dev_err(ch->dev, "could not initialize vb2 queue\n");
> > +		return ret;
> > +	}
> > +
> > +	/* Init video_device structure */
> > +	ch->vdev = rcar_drif_vdev;
> 
> Don't embed video_device, use video_device_alloc instead. A lot of drivers
> embed this, but it turns out not to be a good idea. So new drivers should
> use video_device_alloc.

Agreed. 

Thanks,
Ramesh

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

* RE: [PATCH 3/5] media: Add new SDR formats SC16, SC18 & SC20
  2016-11-11 13:24   ` Hans Verkuil
  2016-11-11 13:48     ` Laurent Pinchart
  2016-11-11 13:49     ` Laurent Pinchart
@ 2016-11-14 16:20     ` Ramesh Shanmugasundaram
  2016-11-14 16:52       ` Hans Verkuil
  2 siblings, 1 reply; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-11-14 16:20 UTC (permalink / raw)
  To: Hans Verkuil, robh+dt, mark.rutland, mchehab, sakari.ailus, crope
  Cc: Chris Paterson, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc

Hi Hans,

Thanks for the review comments.

> Subject: Re: [PATCH 3/5] media: Add new SDR formats SC16, SC18 & SC20
> 
> On 11/09/2016 04:44 PM, Ramesh Shanmugasundaram wrote:
> > This patch adds support for the three new SDR formats. These formats
> > were prefixed with "sliced" indicating I data constitutes the top half
> > and Q data constitutes the bottom half of the received buffer.
> 
> The standard terminology for video formats is "planar". I am leaning
> towards using that here as well.
> 
> Any opinions on this?

Shall I rename the formats as "PC16", "PC18" & "PC20"?
For formats that do IQ IQ IQ... I shall use the regular formats "CUXX" when I introduce them.

Thanks,
Ramesh

> 
> 	Hans
> 
> >
> > V4L2_SDR_FMT_SCU16BE - 14-bit complex (I & Q) unsigned big-endian
> > sample inside 16-bit. V4L2 FourCC: SC16
> >
> > V4L2_SDR_FMT_SCU18BE - 16-bit complex (I & Q) unsigned big-endian
> > sample inside 18-bit. V4L2 FourCC: SC18
> >
> > V4L2_SDR_FMT_SCU20BE - 18-bit complex (I & Q) unsigned big-endian
> > sample inside 20-bit. V4L2 FourCC: SC20
> >
> > Signed-off-by: Ramesh Shanmugasundaram
> > <ramesh.shanmugasundaram@bp.renesas.com>
> > ---
> >  drivers/media/v4l2-core/v4l2-ioctl.c | 3 +++
> >  include/uapi/linux/videodev2.h       | 3 +++
> >  2 files changed, 6 insertions(+)
> >
> > diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c
> > b/drivers/media/v4l2-core/v4l2-ioctl.c
> > index 181381d..d36b386 100644
> > --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> > +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> > @@ -1207,6 +1207,9 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc
> *fmt)
> >  	case V4L2_SDR_FMT_CS8:		descr = "Complex S8"; break;
> >  	case V4L2_SDR_FMT_CS14LE:	descr = "Complex S14LE"; break;
> >  	case V4L2_SDR_FMT_RU12LE:	descr = "Real U12LE"; break;
> > +	case V4L2_SDR_FMT_SCU16BE:	descr = "Sliced Complex U16BE"; break;
> > +	case V4L2_SDR_FMT_SCU18BE:	descr = "Sliced Complex U18BE"; break;
> > +	case V4L2_SDR_FMT_SCU20BE:	descr = "Sliced Complex U20BE"; break;
> >  	case V4L2_TCH_FMT_DELTA_TD16:	descr = "16-bit signed deltas"; break;
> >  	case V4L2_TCH_FMT_DELTA_TD08:	descr = "8-bit signed deltas"; break;
> >  	case V4L2_TCH_FMT_TU16:		descr = "16-bit unsigned touch data";
> break;
> > diff --git a/include/uapi/linux/videodev2.h
> > b/include/uapi/linux/videodev2.h index 4364ce6..34a9c30 100644
> > --- a/include/uapi/linux/videodev2.h
> > +++ b/include/uapi/linux/videodev2.h
> > @@ -666,6 +666,9 @@ struct v4l2_pix_format {
> >  #define V4L2_SDR_FMT_CS8          v4l2_fourcc('C', 'S', '0', '8') /*
> complex s8 */
> >  #define V4L2_SDR_FMT_CS14LE       v4l2_fourcc('C', 'S', '1', '4') /*
> complex s14le */
> >  #define V4L2_SDR_FMT_RU12LE       v4l2_fourcc('R', 'U', '1', '2') /*
> real u12le */
> > +#define V4L2_SDR_FMT_SCU16BE	  v4l2_fourcc('S', 'C', '1', '6') /*
> sliced complex u16be */
> > +#define V4L2_SDR_FMT_SCU18BE	  v4l2_fourcc('S', 'C', '1', '8') /*
> sliced complex u18be */
> > +#define V4L2_SDR_FMT_SCU20BE	  v4l2_fourcc('S', 'C', '2', '0') /*
> sliced complex u20be */
> >
> >  /* Touch formats - used for Touch devices */
> >  #define V4L2_TCH_FMT_DELTA_TD16	v4l2_fourcc('T', 'D', '1', '6') /* 16-
> bit signed deltas */
> >

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

* Re: [PATCH 3/5] media: Add new SDR formats SC16, SC18 & SC20
  2016-11-14 16:20     ` Ramesh Shanmugasundaram
@ 2016-11-14 16:52       ` Hans Verkuil
  0 siblings, 0 replies; 60+ messages in thread
From: Hans Verkuil @ 2016-11-14 16:52 UTC (permalink / raw)
  To: Ramesh Shanmugasundaram, robh+dt, mark.rutland, mchehab,
	sakari.ailus, crope
  Cc: Chris Paterson, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc

On 11/14/2016 05:20 PM, Ramesh Shanmugasundaram wrote:
> Hi Hans,
> 
> Thanks for the review comments.
> 
>> Subject: Re: [PATCH 3/5] media: Add new SDR formats SC16, SC18 & SC20
>>
>> On 11/09/2016 04:44 PM, Ramesh Shanmugasundaram wrote:
>>> This patch adds support for the three new SDR formats. These formats
>>> were prefixed with "sliced" indicating I data constitutes the top half
>>> and Q data constitutes the bottom half of the received buffer.
>>
>> The standard terminology for video formats is "planar". I am leaning
>> towards using that here as well.
>>
>> Any opinions on this?
> 
> Shall I rename the formats as "PC16", "PC18" & "PC20"?

Sounds reasonable to me!

	Hans

> For formats that do IQ IQ IQ... I shall use the regular formats "CUXX" when I introduce them.
> 
> Thanks,
> Ramesh
> 
>>
>> 	Hans
>>
>>>
>>> V4L2_SDR_FMT_SCU16BE - 14-bit complex (I & Q) unsigned big-endian
>>> sample inside 16-bit. V4L2 FourCC: SC16
>>>
>>> V4L2_SDR_FMT_SCU18BE - 16-bit complex (I & Q) unsigned big-endian
>>> sample inside 18-bit. V4L2 FourCC: SC18
>>>
>>> V4L2_SDR_FMT_SCU20BE - 18-bit complex (I & Q) unsigned big-endian
>>> sample inside 20-bit. V4L2 FourCC: SC20
>>>
>>> Signed-off-by: Ramesh Shanmugasundaram
>>> <ramesh.shanmugasundaram@bp.renesas.com>
>>> ---
>>>  drivers/media/v4l2-core/v4l2-ioctl.c | 3 +++
>>>  include/uapi/linux/videodev2.h       | 3 +++
>>>  2 files changed, 6 insertions(+)
>>>
>>> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c
>>> b/drivers/media/v4l2-core/v4l2-ioctl.c
>>> index 181381d..d36b386 100644
>>> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
>>> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
>>> @@ -1207,6 +1207,9 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc
>> *fmt)
>>>  	case V4L2_SDR_FMT_CS8:		descr = "Complex S8"; break;
>>>  	case V4L2_SDR_FMT_CS14LE:	descr = "Complex S14LE"; break;
>>>  	case V4L2_SDR_FMT_RU12LE:	descr = "Real U12LE"; break;
>>> +	case V4L2_SDR_FMT_SCU16BE:	descr = "Sliced Complex U16BE"; break;
>>> +	case V4L2_SDR_FMT_SCU18BE:	descr = "Sliced Complex U18BE"; break;
>>> +	case V4L2_SDR_FMT_SCU20BE:	descr = "Sliced Complex U20BE"; break;
>>>  	case V4L2_TCH_FMT_DELTA_TD16:	descr = "16-bit signed deltas"; break;
>>>  	case V4L2_TCH_FMT_DELTA_TD08:	descr = "8-bit signed deltas"; break;
>>>  	case V4L2_TCH_FMT_TU16:		descr = "16-bit unsigned touch data";
>> break;
>>> diff --git a/include/uapi/linux/videodev2.h
>>> b/include/uapi/linux/videodev2.h index 4364ce6..34a9c30 100644
>>> --- a/include/uapi/linux/videodev2.h
>>> +++ b/include/uapi/linux/videodev2.h
>>> @@ -666,6 +666,9 @@ struct v4l2_pix_format {
>>>  #define V4L2_SDR_FMT_CS8          v4l2_fourcc('C', 'S', '0', '8') /*
>> complex s8 */
>>>  #define V4L2_SDR_FMT_CS14LE       v4l2_fourcc('C', 'S', '1', '4') /*
>> complex s14le */
>>>  #define V4L2_SDR_FMT_RU12LE       v4l2_fourcc('R', 'U', '1', '2') /*
>> real u12le */
>>> +#define V4L2_SDR_FMT_SCU16BE	  v4l2_fourcc('S', 'C', '1', '6') /*
>> sliced complex u16be */
>>> +#define V4L2_SDR_FMT_SCU18BE	  v4l2_fourcc('S', 'C', '1', '8') /*
>> sliced complex u18be */
>>> +#define V4L2_SDR_FMT_SCU20BE	  v4l2_fourcc('S', 'C', '2', '0') /*
>> sliced complex u20be */
>>>
>>>  /* Touch formats - used for Touch devices */
>>>  #define V4L2_TCH_FMT_DELTA_TD16	v4l2_fourcc('T', 'D', '1', '6') /* 16-
>> bit signed deltas */
>>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH 2/5] media: i2c: max2175: Add MAX2175 support
  2016-11-09 15:44     ` Ramesh Shanmugasundaram
  (?)
@ 2016-11-14 19:41         ` Rob Herring
  -1 siblings, 0 replies; 60+ messages in thread
From: Rob Herring @ 2016-11-14 19:41 UTC (permalink / raw)
  To: Ramesh Shanmugasundaram
  Cc: mark.rutland-5wv7dgnIgG8, mchehab-DgEjT+Ai2ygdnm+yROfE0A,
	hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
	sakari.ailus-VuQAYsv1563Yd54FQh9/CA, crope-X3B1VOXEql0,
	chris.paterson2-zM6kxYcvzFBBDgjK7y7TUQ,
	laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
	geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ,
	linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA

On Wed, Nov 09, 2016 at 03:44:41PM +0000, Ramesh Shanmugasundaram wrote:
> This patch adds driver support for MAX2175 chip. This is Maxim
> Integrated's RF to Bits tuner front end chip designed for software-defined
> radio solutions. This driver exposes the tuner as a sub-device instance
> with standard and custom controls to configure the device.
> 
> Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>
> ---
>  .../devicetree/bindings/media/i2c/max2175.txt      |   61 +

It's preferred that bindings are a separate patch.

>  drivers/media/i2c/Kconfig                          |    4 +
>  drivers/media/i2c/Makefile                         |    2 +
>  drivers/media/i2c/max2175/Kconfig                  |    8 +
>  drivers/media/i2c/max2175/Makefile                 |    4 +
>  drivers/media/i2c/max2175/max2175.c                | 1558 ++++++++++++++++++++
>  drivers/media/i2c/max2175/max2175.h                |  108 ++
>  7 files changed, 1745 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/i2c/max2175.txt
>  create mode 100644 drivers/media/i2c/max2175/Kconfig
>  create mode 100644 drivers/media/i2c/max2175/Makefile
>  create mode 100644 drivers/media/i2c/max2175/max2175.c
>  create mode 100644 drivers/media/i2c/max2175/max2175.h
> 
> diff --git a/Documentation/devicetree/bindings/media/i2c/max2175.txt b/Documentation/devicetree/bindings/media/i2c/max2175.txt
> new file mode 100644
> index 0000000..69f0dad
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/i2c/max2175.txt
> @@ -0,0 +1,61 @@
> +Maxim Integrated MAX2175 RF to Bits tuner
> +-----------------------------------------
> +
> +The MAX2175 IC is an advanced analog/digital hybrid-radio receiver with
> +RF to Bits® front-end designed for software-defined radio solutions.
> +
> +Required properties:
> +--------------------
> +- compatible: "maxim,max2175" for MAX2175 RF-to-bits tuner.
> +- clocks: phandle to the fixed xtal clock.
> +- clock-names: name of the fixed xtal clock.
> +- port: child port node of a tuner that defines the local and remote
> +  endpoints. The remote endpoint is assumed to be an SDR device
> +  that is capable of receiving the digital samples from the tuner.
> +
> +Optional properties:
> +--------------------
> +- maxim,slave	      : empty property indicates this is a slave of
> +			another master tuner. This is used to define two
> +			tuners in diversity mode (1 master, 1 slave). By
> +			default each tuner is an individual master.
> +- maxim,refout-load-pF: load capacitance value (in pF) on reference

Please add 'pF' to property-units.txt.

> +			output drive level. The possible load values are
> +			 0pF (default - refout disabled)
> +			10pF
> +			20pF
> +			30pF
> +			40pF
> +			60pF
> +			70pF
> +- maxim,am-hiz	      : empty property indicates AM Hi-Z filter path is
> +			selected for AM antenna input. By default this
> +			filter path is not used.
> +
> +Example:
> +--------
> +
> +Board specific DTS file
> +
> +/* Fixed XTAL clock node */
> +maxim_xtal: maximextal {

clock {

> +	compatible = "fixed-clock";
> +	#clock-cells = <0>;
> +	clock-frequency = <36864000>;
> +};
> +
> +/* A tuner device instance under i2c bus */
> +max2175_0: tuner@60 {
> +	compatible = "maxim,max2175";
> +	reg = <0x60>;
> +	clocks = <&maxim_xtal>;
> +	clock-names = "xtal";
> +	maxim,refout-load-pF = <10>;
> +
> +	port {
> +		max2175_0_ep: endpoint {
> +			remote-endpoint = <&slave_rx_v4l2_sdr_device>;

'v4l2' is not something that should appear in a DT.

> +		};
> +	};
> +
> +};
--
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 2/5] media: i2c: max2175: Add MAX2175 support
@ 2016-11-14 19:41         ` Rob Herring
  0 siblings, 0 replies; 60+ messages in thread
From: Rob Herring @ 2016-11-14 19:41 UTC (permalink / raw)
  To: Ramesh Shanmugasundaram
  Cc: mark.rutland, mchehab, hverkuil, sakari.ailus, crope,
	chris.paterson2, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc

On Wed, Nov 09, 2016 at 03:44:41PM +0000, Ramesh Shanmugasundaram wrote:
> This patch adds driver support for MAX2175 chip. This is Maxim
> Integrated's RF to Bits tuner front end chip designed for software-defined
> radio solutions. This driver exposes the tuner as a sub-device instance
> with standard and custom controls to configure the device.
> 
> Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>
> ---
>  .../devicetree/bindings/media/i2c/max2175.txt      |   61 +

It's preferred that bindings are a separate patch.

>  drivers/media/i2c/Kconfig                          |    4 +
>  drivers/media/i2c/Makefile                         |    2 +
>  drivers/media/i2c/max2175/Kconfig                  |    8 +
>  drivers/media/i2c/max2175/Makefile                 |    4 +
>  drivers/media/i2c/max2175/max2175.c                | 1558 ++++++++++++++++++++
>  drivers/media/i2c/max2175/max2175.h                |  108 ++
>  7 files changed, 1745 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/i2c/max2175.txt
>  create mode 100644 drivers/media/i2c/max2175/Kconfig
>  create mode 100644 drivers/media/i2c/max2175/Makefile
>  create mode 100644 drivers/media/i2c/max2175/max2175.c
>  create mode 100644 drivers/media/i2c/max2175/max2175.h
> 
> diff --git a/Documentation/devicetree/bindings/media/i2c/max2175.txt b/Documentation/devicetree/bindings/media/i2c/max2175.txt
> new file mode 100644
> index 0000000..69f0dad
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/i2c/max2175.txt
> @@ -0,0 +1,61 @@
> +Maxim Integrated MAX2175 RF to Bits tuner
> +-----------------------------------------
> +
> +The MAX2175 IC is an advanced analog/digital hybrid-radio receiver with
> +RF to Bits® front-end designed for software-defined radio solutions.
> +
> +Required properties:
> +--------------------
> +- compatible: "maxim,max2175" for MAX2175 RF-to-bits tuner.
> +- clocks: phandle to the fixed xtal clock.
> +- clock-names: name of the fixed xtal clock.
> +- port: child port node of a tuner that defines the local and remote
> +  endpoints. The remote endpoint is assumed to be an SDR device
> +  that is capable of receiving the digital samples from the tuner.
> +
> +Optional properties:
> +--------------------
> +- maxim,slave	      : empty property indicates this is a slave of
> +			another master tuner. This is used to define two
> +			tuners in diversity mode (1 master, 1 slave). By
> +			default each tuner is an individual master.
> +- maxim,refout-load-pF: load capacitance value (in pF) on reference

Please add 'pF' to property-units.txt.

> +			output drive level. The possible load values are
> +			 0pF (default - refout disabled)
> +			10pF
> +			20pF
> +			30pF
> +			40pF
> +			60pF
> +			70pF
> +- maxim,am-hiz	      : empty property indicates AM Hi-Z filter path is
> +			selected for AM antenna input. By default this
> +			filter path is not used.
> +
> +Example:
> +--------
> +
> +Board specific DTS file
> +
> +/* Fixed XTAL clock node */
> +maxim_xtal: maximextal {

clock {

> +	compatible = "fixed-clock";
> +	#clock-cells = <0>;
> +	clock-frequency = <36864000>;
> +};
> +
> +/* A tuner device instance under i2c bus */
> +max2175_0: tuner@60 {
> +	compatible = "maxim,max2175";
> +	reg = <0x60>;
> +	clocks = <&maxim_xtal>;
> +	clock-names = "xtal";
> +	maxim,refout-load-pF = <10>;
> +
> +	port {
> +		max2175_0_ep: endpoint {
> +			remote-endpoint = <&slave_rx_v4l2_sdr_device>;

'v4l2' is not something that should appear in a DT.

> +		};
> +	};
> +
> +};

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

* Re: [PATCH 2/5] media: i2c: max2175: Add MAX2175 support
@ 2016-11-14 19:41         ` Rob Herring
  0 siblings, 0 replies; 60+ messages in thread
From: Rob Herring @ 2016-11-14 19:41 UTC (permalink / raw)
  To: Ramesh Shanmugasundaram
  Cc: mark.rutland, mchehab, hverkuil, sakari.ailus, crope,
	chris.paterson2, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc

On Wed, Nov 09, 2016 at 03:44:41PM +0000, Ramesh Shanmugasundaram wrote:
> This patch adds driver support for MAX2175 chip. This is Maxim
> Integrated's RF to Bits tuner front end chip designed for software-defined
> radio solutions. This driver exposes the tuner as a sub-device instance
> with standard and custom controls to configure the device.
> 
> Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>
> ---
>  .../devicetree/bindings/media/i2c/max2175.txt      |   61 +

It's preferred that bindings are a separate patch.

>  drivers/media/i2c/Kconfig                          |    4 +
>  drivers/media/i2c/Makefile                         |    2 +
>  drivers/media/i2c/max2175/Kconfig                  |    8 +
>  drivers/media/i2c/max2175/Makefile                 |    4 +
>  drivers/media/i2c/max2175/max2175.c                | 1558 ++++++++++++++++++++
>  drivers/media/i2c/max2175/max2175.h                |  108 ++
>  7 files changed, 1745 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/i2c/max2175.txt
>  create mode 100644 drivers/media/i2c/max2175/Kconfig
>  create mode 100644 drivers/media/i2c/max2175/Makefile
>  create mode 100644 drivers/media/i2c/max2175/max2175.c
>  create mode 100644 drivers/media/i2c/max2175/max2175.h
> 
> diff --git a/Documentation/devicetree/bindings/media/i2c/max2175.txt b/Documentation/devicetree/bindings/media/i2c/max2175.txt
> new file mode 100644
> index 0000000..69f0dad
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/i2c/max2175.txt
> @@ -0,0 +1,61 @@
> +Maxim Integrated MAX2175 RF to Bits tuner
> +-----------------------------------------
> +
> +The MAX2175 IC is an advanced analog/digital hybrid-radio receiver with
> +RF to Bits� front-end designed for software-defined radio solutions.
> +
> +Required properties:
> +--------------------
> +- compatible: "maxim,max2175" for MAX2175 RF-to-bits tuner.
> +- clocks: phandle to the fixed xtal clock.
> +- clock-names: name of the fixed xtal clock.
> +- port: child port node of a tuner that defines the local and remote
> +  endpoints. The remote endpoint is assumed to be an SDR device
> +  that is capable of receiving the digital samples from the tuner.
> +
> +Optional properties:
> +--------------------
> +- maxim,slave	      : empty property indicates this is a slave of
> +			another master tuner. This is used to define two
> +			tuners in diversity mode (1 master, 1 slave). By
> +			default each tuner is an individual master.
> +- maxim,refout-load-pF: load capacitance value (in pF) on reference

Please add 'pF' to property-units.txt.

> +			output drive level. The possible load values are
> +			 0pF (default - refout disabled)
> +			10pF
> +			20pF
> +			30pF
> +			40pF
> +			60pF
> +			70pF
> +- maxim,am-hiz	      : empty property indicates AM Hi-Z filter path is
> +			selected for AM antenna input. By default this
> +			filter path is not used.
> +
> +Example:
> +--------
> +
> +Board specific DTS file
> +
> +/* Fixed XTAL clock node */
> +maxim_xtal: maximextal {

clock {

> +	compatible = "fixed-clock";
> +	#clock-cells = <0>;
> +	clock-frequency = <36864000>;
> +};
> +
> +/* A tuner device instance under i2c bus */
> +max2175_0: tuner@60 {
> +	compatible = "maxim,max2175";
> +	reg = <0x60>;
> +	clocks = <&maxim_xtal>;
> +	clock-names = "xtal";
> +	maxim,refout-load-pF = <10>;
> +
> +	port {
> +		max2175_0_ep: endpoint {
> +			remote-endpoint = <&slave_rx_v4l2_sdr_device>;

'v4l2' is not something that should appear in a DT.

> +		};
> +	};
> +
> +};

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

* Re: [PATCH 5/5] media: platform: rcar_drif: Add DRIF support
  2016-11-10  9:22   ` Laurent Pinchart
@ 2016-11-14 19:52       ` Rob Herring
  0 siblings, 0 replies; 60+ messages in thread
From: Rob Herring @ 2016-11-14 19:52 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Ramesh Shanmugasundaram, mark.rutland-5wv7dgnIgG8,
	mchehab-DgEjT+Ai2ygdnm+yROfE0A, hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
	sakari.ailus-VuQAYsv1563Yd54FQh9/CA, crope-X3B1VOXEql0,
	chris.paterson2-zM6kxYcvzFBBDgjK7y7TUQ,
	geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ,
	linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA

On Thu, Nov 10, 2016 at 11:22:20AM +0200, Laurent Pinchart wrote:
> Hi Ramesh,
> 
> Thank you for the patch.
> 
> On Wednesday 09 Nov 2016 15:44:44 Ramesh Shanmugasundaram wrote:
> > This patch adds Digital Radio Interface (DRIF) support to R-Car Gen3 SoCs.
> > The driver exposes each instance of DRIF as a V4L2 SDR device. A DRIF
> > device represents a channel and each channel can have one or two
> > sub-channels respectively depending on the target board.
> > 
> > DRIF supports only Rx functionality. It receives samples from a RF
> > frontend tuner chip it is interfaced with. The combination of DRIF and the
> > tuner device, which is registered as a sub-device, determines the receive
> > sample rate and format.
> > 
> > In order to be compliant as a V4L2 SDR device, DRIF needs to bind with
> > the tuner device, which can be provided by a third party vendor. DRIF acts
> > as a slave device and the tuner device acts as a master transmitting the
> > samples. The driver allows asynchronous binding of a tuner device that
> > is registered as a v4l2 sub-device. The driver can learn about the tuner
> > it is interfaced with based on port endpoint properties of the device in
> > device tree. The V4L2 SDR device inherits the controls exposed by the
> > tuner device.
> > 
> > The device can also be configured to use either one or both of the data
> > pins at runtime based on the master (tuner) configuration.
> > 
> > Signed-off-by: Ramesh Shanmugasundaram
> > <ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org> ---
> >  .../devicetree/bindings/media/renesas,drif.txt     |  136 ++
> >  drivers/media/platform/Kconfig                     |   25 +
> >  drivers/media/platform/Makefile                    |    1 +
> >  drivers/media/platform/rcar_drif.c                 | 1574
> > ++++++++++++++++++++ 4 files changed, 1736 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/media/renesas,drif.txt
> > create mode 100644 drivers/media/platform/rcar_drif.c
> > 
> > diff --git a/Documentation/devicetree/bindings/media/renesas,drif.txt
> > b/Documentation/devicetree/bindings/media/renesas,drif.txt new file mode
> > 100644
> > index 0000000..d65368a
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> > @@ -0,0 +1,136 @@
> > +Renesas R-Car Gen3 Digital Radio Interface controller (DRIF)
> > +------------------------------------------------------------
> > +
> > +R-Car Gen3 DRIF is a serial slave device. It interfaces with a master
> > +device as shown below
> > +
> > ++---------------------+                +---------------------+
> > +|                     |-----SCK------->|CLK                  |
> > +|       Master        |-----SS-------->|SYNC  DRIFn (slave)  |
> > +|                     |-----SD0------->|D0                   |
> > +|                     |-----SD1------->|D1                   |
> > ++---------------------+                +---------------------+
> > +
> > +Each DRIF channel (drifn) consists of two sub-channels (drifn0 & drifn1).
> > +The sub-channels are like two individual channels in itself that share the
> > +common CLK & SYNC. Each sub-channel has it's own dedicated resources like
> > +irq, dma channels, address space & clock.
> > +
> > +The device tree model represents the channel and each of it's sub-channel
> > +as a separate node. The parent channel ties the sub-channels together with
> > +their phandles.
> > +
> > +Required properties of a sub-channel:
> > +-------------------------------------
> > +- compatible: "renesas,r8a7795-drif" if DRIF controller is a part of
> > R8A7795 SoC.
> > +	      "renesas,rcar-gen3-drif" for a generic R-Car Gen3 compatible
> > device.
> > +	      When compatible with the generic version, nodes must list the
> > +	      SoC-specific version corresponding to the platform first
> > +	      followed by the generic version.
> > +- reg: offset and length of that sub-channel.
> > +- interrupts: associated with that sub-channel.
> > +- clocks: phandle and clock specifier of that sub-channel.
> > +- clock-names: clock input name string: "fck".
> > +- dmas: phandles to the DMA channel of that sub-channel.
> > +- dma-names: names of the DMA channel: "rx".
> > +
> > +Optional properties of a sub-channel:
> > +-------------------------------------
> > +- power-domains: phandle to the respective power domain.
> > +
> > +Required properties of a channel:
> > +---------------------------------
> > +- pinctrl-0: pin control group to be used for this channel.
> > +- pinctrl-names: must be "default".
> > +- sub-channels : phandles to the two sub-channels.
> > +
> > +Optional properties of a channel:
> > +---------------------------------
> > +- port: child port node of a channel that defines the local and remote
> > +        endpoints. The remote endpoint is assumed to be a tuner subdevice
> > +	endpoint.
> > +- renesas,syncmd       : sync mode
> > +			 0 (Frame start sync pulse mode. 1-bit width pulse
> > +			    indicates start of a frame)
> > +			 1 (L/R sync or I2S mode) (default)
> > +- renesas,lsb-first    : empty property indicates lsb bit is received
> > first.
> > +			 When not defined msb bit is received first (default)
> 
> Shouldn't those two properties be instead queried from the tuner at runtime 
> through the V4L2 subdev API ?
> 
> > +- renesas,syncac-pol-high  : empty property indicates sync signal polarity.
> > +			 When defined, active high or high->low sync signal.
> > +			 When not defined, active low or low->high sync signal
> > +			 (default)
> 
> This could be queried too, except that an inverter could be present on the 
> board, so it has to be specified in DT. I would however try to standardize it 
> the same way that hsync-active and vsync-active are standardized in 
> Documentation/devicetree/bindings/media/video-interfaces.txt.
> 
> > +- renesas,dtdl         : delay between sync signal and start of reception.
> 
> Are this and the next property meant to account for PCB traces delays ?
> 
> > +			 Must contain one of the following values:
> > +			 0   (no bit delay)
> > +			 50  (0.5-clock-cycle delay)
> > +			 100 (1-clock-cycle delay) (default)
> > +			 150 (1.5-clock-cycle delay)
> > +			 200 (2-clock-cycle delay)
> 
> How about specifying the property in half clock cycle units, from 0 to 4 ?
> 
> > +- renesas,syncdl       : delay between end of reception and sync signal
> > edge.
> > +			 Must contain one of the following values:
> > +			 0   (no bit delay) (default)
> > +			 50  (0.5-clock-cycle delay)
> > +			 100 (1-clock-cycle delay)
> > +			 150 (1.5-clock-cycle delay)
> > +			 200 (2-clock-cycle delay)
> > +			 300 (3-clock-cycle delay)
> > +
> > +Example
> > +--------
> > +
> > +SoC common dtsi file
> > +
> > +		drif00: rif@e6f40000 {
> > +			compatible = "renesas,r8a7795-drif",
> > +				     "renesas,rcar-gen3-drif";
> > +			reg = <0 0xe6f40000 0 0x64>;
> > +			interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
> > +			clocks = <&cpg CPG_MOD 515>;
> > +			clock-names = "fck";
> > +			dmas = <&dmac1 0x20>, <&dmac2 0x20>;
> > +			dma-names = "rx", "rx";

rx, rx? That doesn't make sense. While we don't explicitly disallow 
this, I'm thinking we should. I wonder if there's any case this is 
valid. If not, then a dtc check for this could be added.

> > +			power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> > +			status = "disabled";
> > +		};
> > +
> > +		drif01: rif@e6f50000 {
> > +			compatible = "renesas,r8a7795-drif",
> > +				     "renesas,rcar-gen3-drif";
> > +			reg = <0 0xe6f50000 0 0x64>;
> > +			interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
> > +			clocks = <&cpg CPG_MOD 514>;
> > +			clock-names = "fck";
> > +			dmas = <&dmac1 0x22>, <&dmac2 0x22>;
> > +			dma-names = "rx", "rx";
> > +			power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> > +			status = "disabled";
> > +		};
> > +
> > +		drif0: rif@0 {
> > +			compatible = "renesas,r8a7795-drif",
> > +				     "renesas,rcar-gen3-drif";
> > +			sub-channels = <&drif00>, <&drif01>;
> > +			status = "disabled";
> > +		};
> 
> I'm afraid this really hurts my eyes, especially using the same compatible 
> string for both the channel and sub-channel nodes.
> 
> We need to decide how to model the hardware in DT. Given that the two channels 
> are mostly independent having separate DT nodes makes sense to me. However, as 
> they share the clock and sync signals, somehow grouping them makes sense. I 
> see three ways to do so, and there might be more.
> 
> 1. Adding an extra DT node for the channels group, with phandles to the two 
> channels. This is what you're proposing here.
> 
> 2. Adding an extra DT node for the channels group, as a parent of the two 
> channels.
> 
> 3. Adding phandles to the channels, pointing to each other, or possibly a 
> phandle from channel 0 pointing to channel 1.
> 
> Neither of these options seem perfect to me. I don't like option 1 as the 
> group DT node really doesn't describe a hardware block. If we want to use a DT 
> node to convey group information, option 2 seems better to me. However, it 
> somehow abuses the DT parent-child model that is supposed to describe 
> relationships from a control bus point of view. Option 3 has the drawback of 
> not scaling properly, at least with phandles in both channels pointing to the 
> other one.
> 
> Rob, Geert, tell me you have a fourth idea I haven't thought of that would 
> solve all those problems :-)

What's the purpose/need for grouping them?

I'm fine with Option 2, though I want to make sure it is really needed.

Rob
--
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 5/5] media: platform: rcar_drif: Add DRIF support
@ 2016-11-14 19:52       ` Rob Herring
  0 siblings, 0 replies; 60+ messages in thread
From: Rob Herring @ 2016-11-14 19:52 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Ramesh Shanmugasundaram, mark.rutland, mchehab, hverkuil,
	sakari.ailus, crope, chris.paterson2, geert+renesas, linux-media,
	devicetree, linux-renesas-soc

On Thu, Nov 10, 2016 at 11:22:20AM +0200, Laurent Pinchart wrote:
> Hi Ramesh,
> 
> Thank you for the patch.
> 
> On Wednesday 09 Nov 2016 15:44:44 Ramesh Shanmugasundaram wrote:
> > This patch adds Digital Radio Interface (DRIF) support to R-Car Gen3 SoCs.
> > The driver exposes each instance of DRIF as a V4L2 SDR device. A DRIF
> > device represents a channel and each channel can have one or two
> > sub-channels respectively depending on the target board.
> > 
> > DRIF supports only Rx functionality. It receives samples from a RF
> > frontend tuner chip it is interfaced with. The combination of DRIF and the
> > tuner device, which is registered as a sub-device, determines the receive
> > sample rate and format.
> > 
> > In order to be compliant as a V4L2 SDR device, DRIF needs to bind with
> > the tuner device, which can be provided by a third party vendor. DRIF acts
> > as a slave device and the tuner device acts as a master transmitting the
> > samples. The driver allows asynchronous binding of a tuner device that
> > is registered as a v4l2 sub-device. The driver can learn about the tuner
> > it is interfaced with based on port endpoint properties of the device in
> > device tree. The V4L2 SDR device inherits the controls exposed by the
> > tuner device.
> > 
> > The device can also be configured to use either one or both of the data
> > pins at runtime based on the master (tuner) configuration.
> > 
> > Signed-off-by: Ramesh Shanmugasundaram
> > <ramesh.shanmugasundaram@bp.renesas.com> ---
> >  .../devicetree/bindings/media/renesas,drif.txt     |  136 ++
> >  drivers/media/platform/Kconfig                     |   25 +
> >  drivers/media/platform/Makefile                    |    1 +
> >  drivers/media/platform/rcar_drif.c                 | 1574
> > ++++++++++++++++++++ 4 files changed, 1736 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/media/renesas,drif.txt
> > create mode 100644 drivers/media/platform/rcar_drif.c
> > 
> > diff --git a/Documentation/devicetree/bindings/media/renesas,drif.txt
> > b/Documentation/devicetree/bindings/media/renesas,drif.txt new file mode
> > 100644
> > index 0000000..d65368a
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> > @@ -0,0 +1,136 @@
> > +Renesas R-Car Gen3 Digital Radio Interface controller (DRIF)
> > +------------------------------------------------------------
> > +
> > +R-Car Gen3 DRIF is a serial slave device. It interfaces with a master
> > +device as shown below
> > +
> > ++---------------------+                +---------------------+
> > +|                     |-----SCK------->|CLK                  |
> > +|       Master        |-----SS-------->|SYNC  DRIFn (slave)  |
> > +|                     |-----SD0------->|D0                   |
> > +|                     |-----SD1------->|D1                   |
> > ++---------------------+                +---------------------+
> > +
> > +Each DRIF channel (drifn) consists of two sub-channels (drifn0 & drifn1).
> > +The sub-channels are like two individual channels in itself that share the
> > +common CLK & SYNC. Each sub-channel has it's own dedicated resources like
> > +irq, dma channels, address space & clock.
> > +
> > +The device tree model represents the channel and each of it's sub-channel
> > +as a separate node. The parent channel ties the sub-channels together with
> > +their phandles.
> > +
> > +Required properties of a sub-channel:
> > +-------------------------------------
> > +- compatible: "renesas,r8a7795-drif" if DRIF controller is a part of
> > R8A7795 SoC.
> > +	      "renesas,rcar-gen3-drif" for a generic R-Car Gen3 compatible
> > device.
> > +	      When compatible with the generic version, nodes must list the
> > +	      SoC-specific version corresponding to the platform first
> > +	      followed by the generic version.
> > +- reg: offset and length of that sub-channel.
> > +- interrupts: associated with that sub-channel.
> > +- clocks: phandle and clock specifier of that sub-channel.
> > +- clock-names: clock input name string: "fck".
> > +- dmas: phandles to the DMA channel of that sub-channel.
> > +- dma-names: names of the DMA channel: "rx".
> > +
> > +Optional properties of a sub-channel:
> > +-------------------------------------
> > +- power-domains: phandle to the respective power domain.
> > +
> > +Required properties of a channel:
> > +---------------------------------
> > +- pinctrl-0: pin control group to be used for this channel.
> > +- pinctrl-names: must be "default".
> > +- sub-channels : phandles to the two sub-channels.
> > +
> > +Optional properties of a channel:
> > +---------------------------------
> > +- port: child port node of a channel that defines the local and remote
> > +        endpoints. The remote endpoint is assumed to be a tuner subdevice
> > +	endpoint.
> > +- renesas,syncmd       : sync mode
> > +			 0 (Frame start sync pulse mode. 1-bit width pulse
> > +			    indicates start of a frame)
> > +			 1 (L/R sync or I2S mode) (default)
> > +- renesas,lsb-first    : empty property indicates lsb bit is received
> > first.
> > +			 When not defined msb bit is received first (default)
> 
> Shouldn't those two properties be instead queried from the tuner at runtime 
> through the V4L2 subdev API ?
> 
> > +- renesas,syncac-pol-high  : empty property indicates sync signal polarity.
> > +			 When defined, active high or high->low sync signal.
> > +			 When not defined, active low or low->high sync signal
> > +			 (default)
> 
> This could be queried too, except that an inverter could be present on the 
> board, so it has to be specified in DT. I would however try to standardize it 
> the same way that hsync-active and vsync-active are standardized in 
> Documentation/devicetree/bindings/media/video-interfaces.txt.
> 
> > +- renesas,dtdl         : delay between sync signal and start of reception.
> 
> Are this and the next property meant to account for PCB traces delays ?
> 
> > +			 Must contain one of the following values:
> > +			 0   (no bit delay)
> > +			 50  (0.5-clock-cycle delay)
> > +			 100 (1-clock-cycle delay) (default)
> > +			 150 (1.5-clock-cycle delay)
> > +			 200 (2-clock-cycle delay)
> 
> How about specifying the property in half clock cycle units, from 0 to 4 ?
> 
> > +- renesas,syncdl       : delay between end of reception and sync signal
> > edge.
> > +			 Must contain one of the following values:
> > +			 0   (no bit delay) (default)
> > +			 50  (0.5-clock-cycle delay)
> > +			 100 (1-clock-cycle delay)
> > +			 150 (1.5-clock-cycle delay)
> > +			 200 (2-clock-cycle delay)
> > +			 300 (3-clock-cycle delay)
> > +
> > +Example
> > +--------
> > +
> > +SoC common dtsi file
> > +
> > +		drif00: rif@e6f40000 {
> > +			compatible = "renesas,r8a7795-drif",
> > +				     "renesas,rcar-gen3-drif";
> > +			reg = <0 0xe6f40000 0 0x64>;
> > +			interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
> > +			clocks = <&cpg CPG_MOD 515>;
> > +			clock-names = "fck";
> > +			dmas = <&dmac1 0x20>, <&dmac2 0x20>;
> > +			dma-names = "rx", "rx";

rx, rx? That doesn't make sense. While we don't explicitly disallow 
this, I'm thinking we should. I wonder if there's any case this is 
valid. If not, then a dtc check for this could be added.

> > +			power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> > +			status = "disabled";
> > +		};
> > +
> > +		drif01: rif@e6f50000 {
> > +			compatible = "renesas,r8a7795-drif",
> > +				     "renesas,rcar-gen3-drif";
> > +			reg = <0 0xe6f50000 0 0x64>;
> > +			interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
> > +			clocks = <&cpg CPG_MOD 514>;
> > +			clock-names = "fck";
> > +			dmas = <&dmac1 0x22>, <&dmac2 0x22>;
> > +			dma-names = "rx", "rx";
> > +			power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> > +			status = "disabled";
> > +		};
> > +
> > +		drif0: rif@0 {
> > +			compatible = "renesas,r8a7795-drif",
> > +				     "renesas,rcar-gen3-drif";
> > +			sub-channels = <&drif00>, <&drif01>;
> > +			status = "disabled";
> > +		};
> 
> I'm afraid this really hurts my eyes, especially using the same compatible 
> string for both the channel and sub-channel nodes.
> 
> We need to decide how to model the hardware in DT. Given that the two channels 
> are mostly independent having separate DT nodes makes sense to me. However, as 
> they share the clock and sync signals, somehow grouping them makes sense. I 
> see three ways to do so, and there might be more.
> 
> 1. Adding an extra DT node for the channels group, with phandles to the two 
> channels. This is what you're proposing here.
> 
> 2. Adding an extra DT node for the channels group, as a parent of the two 
> channels.
> 
> 3. Adding phandles to the channels, pointing to each other, or possibly a 
> phandle from channel 0 pointing to channel 1.
> 
> Neither of these options seem perfect to me. I don't like option 1 as the 
> group DT node really doesn't describe a hardware block. If we want to use a DT 
> node to convey group information, option 2 seems better to me. However, it 
> somehow abuses the DT parent-child model that is supposed to describe 
> relationships from a control bus point of view. Option 3 has the drawback of 
> not scaling properly, at least with phandles in both channels pointing to the 
> other one.
> 
> Rob, Geert, tell me you have a fourth idea I haven't thought of that would 
> solve all those problems :-)

What's the purpose/need for grouping them?

I'm fine with Option 2, though I want to make sure it is really needed.

Rob

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

* Re: [PATCH 5/5] media: platform: rcar_drif: Add DRIF support
  2016-11-14 19:52       ` Rob Herring
@ 2016-11-14 20:04         ` Geert Uytterhoeven
  -1 siblings, 0 replies; 60+ messages in thread
From: Geert Uytterhoeven @ 2016-11-14 20:04 UTC (permalink / raw)
  To: Rob Herring
  Cc: Laurent Pinchart, Ramesh Shanmugasundaram, Mark Rutland,
	Mauro Carvalho Chehab, Hans Verkuil, Sakari Ailus,
	Antti Palosaari, Chris Paterson, Geert Uytterhoeven,
	Linux Media Mailing List, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Linux-Renesas

Hi Rob,

On Mon, Nov 14, 2016 at 8:52 PM, Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> On Thu, Nov 10, 2016 at 11:22:20AM +0200, Laurent Pinchart wrote:
>> On Wednesday 09 Nov 2016 15:44:44 Ramesh Shanmugasundaram wrote:
>> > --- /dev/null
>> > +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
>> > @@ -0,0 +1,136 @@

>> > +Example
>> > +--------
>> > +
>> > +SoC common dtsi file
>> > +
>> > +           drif00: rif@e6f40000 {
>> > +                   compatible = "renesas,r8a7795-drif",
>> > +                                "renesas,rcar-gen3-drif";
>> > +                   reg = <0 0xe6f40000 0 0x64>;
>> > +                   interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
>> > +                   clocks = <&cpg CPG_MOD 515>;
>> > +                   clock-names = "fck";
>> > +                   dmas = <&dmac1 0x20>, <&dmac2 0x20>;
>> > +                   dma-names = "rx", "rx";
>
> rx, rx? That doesn't make sense. While we don't explicitly disallow
> this, I'm thinking we should. I wonder if there's any case this is
> valid. If not, then a dtc check for this could be added.

The device can be used with either dmac1 or dmac2.
Which one is used is decided at run time, based on the availability of
DMA channels per DMAC, which is a limited resource.

>> > +                   power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
>> > +                   status = "disabled";
>> > +           };
>> > +
>> > +           drif01: rif@e6f50000 {
>> > +                   compatible = "renesas,r8a7795-drif",
>> > +                                "renesas,rcar-gen3-drif";
>> > +                   reg = <0 0xe6f50000 0 0x64>;
>> > +                   interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
>> > +                   clocks = <&cpg CPG_MOD 514>;
>> > +                   clock-names = "fck";
>> > +                   dmas = <&dmac1 0x22>, <&dmac2 0x22>;
>> > +                   dma-names = "rx", "rx";
>> > +                   power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
>> > +                   status = "disabled";
>> > +           };
>> > +
>> > +           drif0: rif@0 {
>> > +                   compatible = "renesas,r8a7795-drif",
>> > +                                "renesas,rcar-gen3-drif";
>> > +                   sub-channels = <&drif00>, <&drif01>;
>> > +                   status = "disabled";
>> > +           };
>>
>> I'm afraid this really hurts my eyes, especially using the same compatible
>> string for both the channel and sub-channel nodes.
>>
>> We need to decide how to model the hardware in DT. Given that the two channels
>> are mostly independent having separate DT nodes makes sense to me. However, as
>> they share the clock and sync signals, somehow grouping them makes sense. I
>> see three ways to do so, and there might be more.
>>
>> 1. Adding an extra DT node for the channels group, with phandles to the two
>> channels. This is what you're proposing here.
>>
>> 2. Adding an extra DT node for the channels group, as a parent of the two
>> channels.
>>
>> 3. Adding phandles to the channels, pointing to each other, or possibly a
>> phandle from channel 0 pointing to channel 1.
>>
>> Neither of these options seem perfect to me. I don't like option 1 as the
>> group DT node really doesn't describe a hardware block. If we want to use a DT
>> node to convey group information, option 2 seems better to me. However, it
>> somehow abuses the DT parent-child model that is supposed to describe
>> relationships from a control bus point of view. Option 3 has the drawback of
>> not scaling properly, at least with phandles in both channels pointing to the
>> other one.
>>
>> Rob, Geert, tell me you have a fourth idea I haven't thought of that would
>> solve all those problems :-)
>
> What's the purpose/need for grouping them?
>
> I'm fine with Option 2, though I want to make sure it is really needed.

Each half of a DRIF pair is basically an SPI slave controller without TX
capability, sharing clock and chip-select between the two halves.
Hence you can use either one half to receive 1 bit per clock pulse,
or both halves to receive 2 bits per clock pulse.
You cannot use both halves for independent operation due to the signal
sharing.

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert-Td1EMuHUCqxL1ZNQvxDV9g@public.gmane.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
--
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 5/5] media: platform: rcar_drif: Add DRIF support
@ 2016-11-14 20:04         ` Geert Uytterhoeven
  0 siblings, 0 replies; 60+ messages in thread
From: Geert Uytterhoeven @ 2016-11-14 20:04 UTC (permalink / raw)
  To: Rob Herring
  Cc: Laurent Pinchart, Ramesh Shanmugasundaram, Mark Rutland,
	Mauro Carvalho Chehab, Hans Verkuil, Sakari Ailus,
	Antti Palosaari, Chris Paterson, Geert Uytterhoeven,
	Linux Media Mailing List, devicetree, Linux-Renesas

Hi Rob,

On Mon, Nov 14, 2016 at 8:52 PM, Rob Herring <robh@kernel.org> wrote:
> On Thu, Nov 10, 2016 at 11:22:20AM +0200, Laurent Pinchart wrote:
>> On Wednesday 09 Nov 2016 15:44:44 Ramesh Shanmugasundaram wrote:
>> > --- /dev/null
>> > +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
>> > @@ -0,0 +1,136 @@

>> > +Example
>> > +--------
>> > +
>> > +SoC common dtsi file
>> > +
>> > +           drif00: rif@e6f40000 {
>> > +                   compatible = "renesas,r8a7795-drif",
>> > +                                "renesas,rcar-gen3-drif";
>> > +                   reg = <0 0xe6f40000 0 0x64>;
>> > +                   interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
>> > +                   clocks = <&cpg CPG_MOD 515>;
>> > +                   clock-names = "fck";
>> > +                   dmas = <&dmac1 0x20>, <&dmac2 0x20>;
>> > +                   dma-names = "rx", "rx";
>
> rx, rx? That doesn't make sense. While we don't explicitly disallow
> this, I'm thinking we should. I wonder if there's any case this is
> valid. If not, then a dtc check for this could be added.

The device can be used with either dmac1 or dmac2.
Which one is used is decided at run time, based on the availability of
DMA channels per DMAC, which is a limited resource.

>> > +                   power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
>> > +                   status = "disabled";
>> > +           };
>> > +
>> > +           drif01: rif@e6f50000 {
>> > +                   compatible = "renesas,r8a7795-drif",
>> > +                                "renesas,rcar-gen3-drif";
>> > +                   reg = <0 0xe6f50000 0 0x64>;
>> > +                   interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
>> > +                   clocks = <&cpg CPG_MOD 514>;
>> > +                   clock-names = "fck";
>> > +                   dmas = <&dmac1 0x22>, <&dmac2 0x22>;
>> > +                   dma-names = "rx", "rx";
>> > +                   power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
>> > +                   status = "disabled";
>> > +           };
>> > +
>> > +           drif0: rif@0 {
>> > +                   compatible = "renesas,r8a7795-drif",
>> > +                                "renesas,rcar-gen3-drif";
>> > +                   sub-channels = <&drif00>, <&drif01>;
>> > +                   status = "disabled";
>> > +           };
>>
>> I'm afraid this really hurts my eyes, especially using the same compatible
>> string for both the channel and sub-channel nodes.
>>
>> We need to decide how to model the hardware in DT. Given that the two channels
>> are mostly independent having separate DT nodes makes sense to me. However, as
>> they share the clock and sync signals, somehow grouping them makes sense. I
>> see three ways to do so, and there might be more.
>>
>> 1. Adding an extra DT node for the channels group, with phandles to the two
>> channels. This is what you're proposing here.
>>
>> 2. Adding an extra DT node for the channels group, as a parent of the two
>> channels.
>>
>> 3. Adding phandles to the channels, pointing to each other, or possibly a
>> phandle from channel 0 pointing to channel 1.
>>
>> Neither of these options seem perfect to me. I don't like option 1 as the
>> group DT node really doesn't describe a hardware block. If we want to use a DT
>> node to convey group information, option 2 seems better to me. However, it
>> somehow abuses the DT parent-child model that is supposed to describe
>> relationships from a control bus point of view. Option 3 has the drawback of
>> not scaling properly, at least with phandles in both channels pointing to the
>> other one.
>>
>> Rob, Geert, tell me you have a fourth idea I haven't thought of that would
>> solve all those problems :-)
>
> What's the purpose/need for grouping them?
>
> I'm fine with Option 2, though I want to make sure it is really needed.

Each half of a DRIF pair is basically an SPI slave controller without TX
capability, sharing clock and chip-select between the two halves.
Hence you can use either one half to receive 1 bit per clock pulse,
or both halves to receive 2 bits per clock pulse.
You cannot use both halves for independent operation due to the signal
sharing.

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

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

* RE: [PATCH 5/5] media: platform: rcar_drif: Add DRIF support
  2016-11-14 20:04         ` Geert Uytterhoeven
@ 2016-11-15 15:09             ` Ramesh Shanmugasundaram
  -1 siblings, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-11-15 15:09 UTC (permalink / raw)
  To: Geert Uytterhoeven, Rob Herring
  Cc: Laurent Pinchart, Mark Rutland, Mauro Carvalho Chehab,
	Hans Verkuil, Sakari Ailus, Antti Palosaari, Chris Paterson,
	Geert Uytterhoeven, Linux Media Mailing List,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Linux-Renesas

Hi Rob, Geert, Laurent,

Thank you for the review comments.

> On Mon, Nov 14, 2016 at 8:52 PM, Rob Herring <robh@kernel.org> wrote:
> > On Thu, Nov 10, 2016 at 11:22:20AM +0200, Laurent Pinchart wrote:
> >> On Wednesday 09 Nov 2016 15:44:44 Ramesh Shanmugasundaram wrote:
> >> > --- /dev/null
> >> > +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> >> > @@ -0,0 +1,136 @@
> 
> >> > +Example
> >> > +--------
> >> > +
> >> > +SoC common dtsi file
> >> > +
> >> > +           drif00: rif@e6f40000 {
> >> > +                   compatible = "renesas,r8a7795-drif",
> >> > +                                "renesas,rcar-gen3-drif";
> >> > +                   reg = <0 0xe6f40000 0 0x64>;
> >> > +                   interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
> >> > +                   clocks = <&cpg CPG_MOD 515>;
> >> > +                   clock-names = "fck";
> >> > +                   dmas = <&dmac1 0x20>, <&dmac2 0x20>;
> >> > +                   dma-names = "rx", "rx";
> >
> > rx, rx? That doesn't make sense. While we don't explicitly disallow
> > this, I'm thinking we should. I wonder if there's any case this is
> > valid. If not, then a dtc check for this could be added.
> 
> The device can be used with either dmac1 or dmac2.
> Which one is used is decided at run time, based on the availability of DMA
> channels per DMAC, which is a limited resource.
> 

Yep. 

> >> > +                   power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> >> > +                   status = "disabled";
> >> > +           };
> >> > +
> >> > +           drif01: rif@e6f50000 {
> >> > +                   compatible = "renesas,r8a7795-drif",
> >> > +                                "renesas,rcar-gen3-drif";
> >> > +                   reg = <0 0xe6f50000 0 0x64>;
> >> > +                   interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
> >> > +                   clocks = <&cpg CPG_MOD 514>;
> >> > +                   clock-names = "fck";
> >> > +                   dmas = <&dmac1 0x22>, <&dmac2 0x22>;
> >> > +                   dma-names = "rx", "rx";
> >> > +                   power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> >> > +                   status = "disabled";
> >> > +           };
> >> > +
> >> > +           drif0: rif@0 {
> >> > +                   compatible = "renesas,r8a7795-drif",
> >> > +                                "renesas,rcar-gen3-drif";
> >> > +                   sub-channels = <&drif00>, <&drif01>;
> >> > +                   status = "disabled";
> >> > +           };
> >>
> >> I'm afraid this really hurts my eyes, especially using the same
> >> compatible string for both the channel and sub-channel nodes.
> >>
> >> We need to decide how to model the hardware in DT. Given that the two
> >> channels are mostly independent having separate DT nodes makes sense
> >> to me. However, as they share the clock and sync signals, somehow
> >> grouping them makes sense. I see three ways to do so, and there might
> be more.
> >>
> >> 1. Adding an extra DT node for the channels group, with phandles to
> >> the two channels. This is what you're proposing here.
> >>
> >> 2. Adding an extra DT node for the channels group, as a parent of the
> >> two channels.
> >>
> >> 3. Adding phandles to the channels, pointing to each other, or
> >> possibly a phandle from channel 0 pointing to channel 1.
> >>
> >> Neither of these options seem perfect to me. I don't like option 1 as
> >> the group DT node really doesn't describe a hardware block. If we
> >> want to use a DT node to convey group information, option 2 seems
> >> better to me. However, it somehow abuses the DT parent-child model
> >> that is supposed to describe relationships from a control bus point
> >> of view. Option 3 has the drawback of not scaling properly, at least
> >> with phandles in both channels pointing to the other one.
> >>
> >> Rob, Geert, tell me you have a fourth idea I haven't thought of that
> >> would solve all those problems :-)
> >
> > What's the purpose/need for grouping them?
> >
> > I'm fine with Option 2, though I want to make sure it is really needed.
> 
> Each half of a DRIF pair is basically an SPI slave controller without TX
> capability, sharing clock and chip-select between the two halves.
> Hence you can use either one half to receive 1 bit per clock pulse, or
> both halves to receive 2 bits per clock pulse.
> You cannot use both halves for independent operation due to the signal
> sharing.

Is the below model looks OK? I assume this is Option 2. Any preferences on the "parent" compatible string please?

---------------------------------------------------------
drif0: rif@0 {
        compatible = "renesas,rcar-gen3-drif", "simple-bus"; 
        #address-cells = <2>;
        #size-cells = <2>;
        status = "disabled";

        drif00: rif@e6f40000 {
                compatible = "renesas,r8a7795-drif",
                             "renesas,rcar-gen3-drif";
                reg = <0 0xe6f40000 0 0x64>;
                interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
                clocks = <&cpg CPG_MOD 515>;
                clock-names = "fck";
                dmas = <&dmac1 0x20>, <&dmac2 0x20>;
                dma-names = "rx", "rx";
                power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
                status = "disabled";
        };  

        drif01: rif@e6f50000 {
                compatible = "renesas,r8a7795-drif",
                             "renesas,rcar-gen3-drif";
                reg = <0 0xe6f50000 0 0x64>;
                interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
                clocks = <&cpg CPG_MOD 514>;
                clock-names = "fck";
                dmas = <&dmac1 0x22>, <&dmac2 0x22>;
                dma-names = "rx", "rx";
                power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
                status = "disabled";
        };  
};
---------------------------------------------------------

Thanks in advance,
Ramesh

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

* RE: [PATCH 5/5] media: platform: rcar_drif: Add DRIF support
@ 2016-11-15 15:09             ` Ramesh Shanmugasundaram
  0 siblings, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-11-15 15:09 UTC (permalink / raw)
  To: Geert Uytterhoeven, Rob Herring
  Cc: Laurent Pinchart, Mark Rutland, Mauro Carvalho Chehab,
	Hans Verkuil, Sakari Ailus, Antti Palosaari, Chris Paterson,
	Geert Uytterhoeven, Linux Media Mailing List, devicetree,
	Linux-Renesas

Hi Rob, Geert, Laurent,

Thank you for the review comments.

> On Mon, Nov 14, 2016 at 8:52 PM, Rob Herring <robh@kernel.org> wrote:
> > On Thu, Nov 10, 2016 at 11:22:20AM +0200, Laurent Pinchart wrote:
> >> On Wednesday 09 Nov 2016 15:44:44 Ramesh Shanmugasundaram wrote:
> >> > --- /dev/null
> >> > +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> >> > @@ -0,0 +1,136 @@
> 
> >> > +Example
> >> > +--------
> >> > +
> >> > +SoC common dtsi file
> >> > +
> >> > +           drif00: rif@e6f40000 {
> >> > +                   compatible = "renesas,r8a7795-drif",
> >> > +                                "renesas,rcar-gen3-drif";
> >> > +                   reg = <0 0xe6f40000 0 0x64>;
> >> > +                   interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
> >> > +                   clocks = <&cpg CPG_MOD 515>;
> >> > +                   clock-names = "fck";
> >> > +                   dmas = <&dmac1 0x20>, <&dmac2 0x20>;
> >> > +                   dma-names = "rx", "rx";
> >
> > rx, rx? That doesn't make sense. While we don't explicitly disallow
> > this, I'm thinking we should. I wonder if there's any case this is
> > valid. If not, then a dtc check for this could be added.
> 
> The device can be used with either dmac1 or dmac2.
> Which one is used is decided at run time, based on the availability of DMA
> channels per DMAC, which is a limited resource.
> 

Yep. 

> >> > +                   power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> >> > +                   status = "disabled";
> >> > +           };
> >> > +
> >> > +           drif01: rif@e6f50000 {
> >> > +                   compatible = "renesas,r8a7795-drif",
> >> > +                                "renesas,rcar-gen3-drif";
> >> > +                   reg = <0 0xe6f50000 0 0x64>;
> >> > +                   interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
> >> > +                   clocks = <&cpg CPG_MOD 514>;
> >> > +                   clock-names = "fck";
> >> > +                   dmas = <&dmac1 0x22>, <&dmac2 0x22>;
> >> > +                   dma-names = "rx", "rx";
> >> > +                   power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> >> > +                   status = "disabled";
> >> > +           };
> >> > +
> >> > +           drif0: rif@0 {
> >> > +                   compatible = "renesas,r8a7795-drif",
> >> > +                                "renesas,rcar-gen3-drif";
> >> > +                   sub-channels = <&drif00>, <&drif01>;
> >> > +                   status = "disabled";
> >> > +           };
> >>
> >> I'm afraid this really hurts my eyes, especially using the same
> >> compatible string for both the channel and sub-channel nodes.
> >>
> >> We need to decide how to model the hardware in DT. Given that the two
> >> channels are mostly independent having separate DT nodes makes sense
> >> to me. However, as they share the clock and sync signals, somehow
> >> grouping them makes sense. I see three ways to do so, and there might
> be more.
> >>
> >> 1. Adding an extra DT node for the channels group, with phandles to
> >> the two channels. This is what you're proposing here.
> >>
> >> 2. Adding an extra DT node for the channels group, as a parent of the
> >> two channels.
> >>
> >> 3. Adding phandles to the channels, pointing to each other, or
> >> possibly a phandle from channel 0 pointing to channel 1.
> >>
> >> Neither of these options seem perfect to me. I don't like option 1 as
> >> the group DT node really doesn't describe a hardware block. If we
> >> want to use a DT node to convey group information, option 2 seems
> >> better to me. However, it somehow abuses the DT parent-child model
> >> that is supposed to describe relationships from a control bus point
> >> of view. Option 3 has the drawback of not scaling properly, at least
> >> with phandles in both channels pointing to the other one.
> >>
> >> Rob, Geert, tell me you have a fourth idea I haven't thought of that
> >> would solve all those problems :-)
> >
> > What's the purpose/need for grouping them?
> >
> > I'm fine with Option 2, though I want to make sure it is really needed.
> 
> Each half of a DRIF pair is basically an SPI slave controller without TX
> capability, sharing clock and chip-select between the two halves.
> Hence you can use either one half to receive 1 bit per clock pulse, or
> both halves to receive 2 bits per clock pulse.
> You cannot use both halves for independent operation due to the signal
> sharing.

Is the below model looks OK? I assume this is Option 2. Any preferences on the "parent" compatible string please?

---------------------------------------------------------
drif0: rif@0 {
        compatible = "renesas,rcar-gen3-drif", "simple-bus"; 
        #address-cells = <2>;
        #size-cells = <2>;
        status = "disabled";

        drif00: rif@e6f40000 {
                compatible = "renesas,r8a7795-drif",
                             "renesas,rcar-gen3-drif";
                reg = <0 0xe6f40000 0 0x64>;
                interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
                clocks = <&cpg CPG_MOD 515>;
                clock-names = "fck";
                dmas = <&dmac1 0x20>, <&dmac2 0x20>;
                dma-names = "rx", "rx";
                power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
                status = "disabled";
        };  

        drif01: rif@e6f50000 {
                compatible = "renesas,r8a7795-drif",
                             "renesas,rcar-gen3-drif";
                reg = <0 0xe6f50000 0 0x64>;
                interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
                clocks = <&cpg CPG_MOD 514>;
                clock-names = "fck";
                dmas = <&dmac1 0x22>, <&dmac2 0x22>;
                dma-names = "rx", "rx";
                power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
                status = "disabled";
        };  
};
---------------------------------------------------------

Thanks in advance,
Ramesh

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

* RE: [PATCH 2/5] media: i2c: max2175: Add MAX2175 support
  2016-11-14 19:41         ` Rob Herring
@ 2016-11-17 12:41           ` Ramesh Shanmugasundaram
  -1 siblings, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-11-17 12:41 UTC (permalink / raw)
  To: Rob Herring
  Cc: mark.rutland-5wv7dgnIgG8, mchehab-DgEjT+Ai2ygdnm+yROfE0A,
	hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
	sakari.ailus-VuQAYsv1563Yd54FQh9/CA, crope-X3B1VOXEql0,
	Chris Paterson, laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
	geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ,
	linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA

Hi Rob,

Thanks for the review comments.

> On Wed, Nov 09, 2016 at 03:44:41PM +0000, Ramesh Shanmugasundaram wrote:
> > This patch adds driver support for MAX2175 chip. This is Maxim
> > Integrated's RF to Bits tuner front end chip designed for
> > software-defined radio solutions. This driver exposes the tuner as a
> > sub-device instance with standard and custom controls to configure the
> device.
> >
> > Signed-off-by: Ramesh Shanmugasundaram
> > <ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>
> > ---
> >  .../devicetree/bindings/media/i2c/max2175.txt      |   61 +
> 
> It's preferred that bindings are a separate patch.

OK. I will do the same for the other driver.

> 
> >  drivers/media/i2c/Kconfig                          |    4 +
> >  drivers/media/i2c/Makefile                         |    2 +
> >  drivers/media/i2c/max2175/Kconfig                  |    8 +
> >  drivers/media/i2c/max2175/Makefile                 |    4 +
> >  drivers/media/i2c/max2175/max2175.c                | 1558
> ++++++++++++++++++++
> >  drivers/media/i2c/max2175/max2175.h                |  108 ++
> >  7 files changed, 1745 insertions(+)
> >  create mode 100644
> > Documentation/devicetree/bindings/media/i2c/max2175.txt
> >  create mode 100644 drivers/media/i2c/max2175/Kconfig  create mode
> > 100644 drivers/media/i2c/max2175/Makefile
> >  create mode 100644 drivers/media/i2c/max2175/max2175.c
> >  create mode 100644 drivers/media/i2c/max2175/max2175.h
> >
> > diff --git a/Documentation/devicetree/bindings/media/i2c/max2175.txt
> > b/Documentation/devicetree/bindings/media/i2c/max2175.txt
> > new file mode 100644
> > index 0000000..69f0dad
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/i2c/max2175.txt
> > @@ -0,0 +1,61 @@
> > +Maxim Integrated MAX2175 RF to Bits tuner
> > +-----------------------------------------
> > +
> > +The MAX2175 IC is an advanced analog/digital hybrid-radio receiver
> > +with RF to Bits(r) front-end designed for software-defined radio
> solutions.
> > +
> > +Required properties:
> > +--------------------
> > +- compatible: "maxim,max2175" for MAX2175 RF-to-bits tuner.
> > +- clocks: phandle to the fixed xtal clock.
> > +- clock-names: name of the fixed xtal clock.
> > +- port: child port node of a tuner that defines the local and remote
> > +  endpoints. The remote endpoint is assumed to be an SDR device
> > +  that is capable of receiving the digital samples from the tuner.
> > +
> > +Optional properties:
> > +--------------------
> > +- maxim,slave	      : empty property indicates this is a slave of
> > +			another master tuner. This is used to define two
> > +			tuners in diversity mode (1 master, 1 slave). By
> > +			default each tuner is an individual master.
> > +- maxim,refout-load-pF: load capacitance value (in pF) on reference
> 
> Please add 'pF' to property-units.txt.

Agreed.

> 
> > +			output drive level. The possible load values are
> > +			 0pF (default - refout disabled)
> > +			10pF
> > +			20pF
> > +			30pF
> > +			40pF
> > +			60pF
> > +			70pF
> > +- maxim,am-hiz	      : empty property indicates AM Hi-Z filter path is
> > +			selected for AM antenna input. By default this
> > +			filter path is not used.
> > +
> > +Example:
> > +--------
> > +
> > +Board specific DTS file
> > +
> > +/* Fixed XTAL clock node */
> > +maxim_xtal: maximextal {
> 
> clock {

Agreed.

> 
> > +	compatible = "fixed-clock";
> > +	#clock-cells = <0>;
> > +	clock-frequency = <36864000>;
> > +};
> > +
> > +/* A tuner device instance under i2c bus */
> > +max2175_0: tuner@60 {
> > +	compatible = "maxim,max2175";
> > +	reg = <0x60>;
> > +	clocks = <&maxim_xtal>;
> > +	clock-names = "xtal";
> > +	maxim,refout-load-pF = <10>;
> > +
> > +	port {
> > +		max2175_0_ep: endpoint {
> > +			remote-endpoint = <&slave_rx_v4l2_sdr_device>;
> 
> 'v4l2' is not something that should appear in a DT.

OK. I'll leave it as "slave_rx_device".

Thanks,
Ramesh
--
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 2/5] media: i2c: max2175: Add MAX2175 support
@ 2016-11-17 12:41           ` Ramesh Shanmugasundaram
  0 siblings, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-11-17 12:41 UTC (permalink / raw)
  To: Rob Herring
  Cc: mark.rutland, mchehab, hverkuil, sakari.ailus, crope,
	Chris Paterson, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc

Hi Rob,

Thanks for the review comments.

> On Wed, Nov 09, 2016 at 03:44:41PM +0000, Ramesh Shanmugasundaram wrote:
> > This patch adds driver support for MAX2175 chip. This is Maxim
> > Integrated's RF to Bits tuner front end chip designed for
> > software-defined radio solutions. This driver exposes the tuner as a
> > sub-device instance with standard and custom controls to configure the
> device.
> >
> > Signed-off-by: Ramesh Shanmugasundaram
> > <ramesh.shanmugasundaram@bp.renesas.com>
> > ---
> >  .../devicetree/bindings/media/i2c/max2175.txt      |   61 +
> 
> It's preferred that bindings are a separate patch.

OK. I will do the same for the other driver.

> 
> >  drivers/media/i2c/Kconfig                          |    4 +
> >  drivers/media/i2c/Makefile                         |    2 +
> >  drivers/media/i2c/max2175/Kconfig                  |    8 +
> >  drivers/media/i2c/max2175/Makefile                 |    4 +
> >  drivers/media/i2c/max2175/max2175.c                | 1558
> ++++++++++++++++++++
> >  drivers/media/i2c/max2175/max2175.h                |  108 ++
> >  7 files changed, 1745 insertions(+)
> >  create mode 100644
> > Documentation/devicetree/bindings/media/i2c/max2175.txt
> >  create mode 100644 drivers/media/i2c/max2175/Kconfig  create mode
> > 100644 drivers/media/i2c/max2175/Makefile
> >  create mode 100644 drivers/media/i2c/max2175/max2175.c
> >  create mode 100644 drivers/media/i2c/max2175/max2175.h
> >
> > diff --git a/Documentation/devicetree/bindings/media/i2c/max2175.txt
> > b/Documentation/devicetree/bindings/media/i2c/max2175.txt
> > new file mode 100644
> > index 0000000..69f0dad
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/i2c/max2175.txt
> > @@ -0,0 +1,61 @@
> > +Maxim Integrated MAX2175 RF to Bits tuner
> > +-----------------------------------------
> > +
> > +The MAX2175 IC is an advanced analog/digital hybrid-radio receiver
> > +with RF to Bits(r) front-end designed for software-defined radio
> solutions.
> > +
> > +Required properties:
> > +--------------------
> > +- compatible: "maxim,max2175" for MAX2175 RF-to-bits tuner.
> > +- clocks: phandle to the fixed xtal clock.
> > +- clock-names: name of the fixed xtal clock.
> > +- port: child port node of a tuner that defines the local and remote
> > +  endpoints. The remote endpoint is assumed to be an SDR device
> > +  that is capable of receiving the digital samples from the tuner.
> > +
> > +Optional properties:
> > +--------------------
> > +- maxim,slave	      : empty property indicates this is a slave of
> > +			another master tuner. This is used to define two
> > +			tuners in diversity mode (1 master, 1 slave). By
> > +			default each tuner is an individual master.
> > +- maxim,refout-load-pF: load capacitance value (in pF) on reference
> 
> Please add 'pF' to property-units.txt.

Agreed.

> 
> > +			output drive level. The possible load values are
> > +			 0pF (default - refout disabled)
> > +			10pF
> > +			20pF
> > +			30pF
> > +			40pF
> > +			60pF
> > +			70pF
> > +- maxim,am-hiz	      : empty property indicates AM Hi-Z filter path is
> > +			selected for AM antenna input. By default this
> > +			filter path is not used.
> > +
> > +Example:
> > +--------
> > +
> > +Board specific DTS file
> > +
> > +/* Fixed XTAL clock node */
> > +maxim_xtal: maximextal {
> 
> clock {

Agreed.

> 
> > +	compatible = "fixed-clock";
> > +	#clock-cells = <0>;
> > +	clock-frequency = <36864000>;
> > +};
> > +
> > +/* A tuner device instance under i2c bus */
> > +max2175_0: tuner@60 {
> > +	compatible = "maxim,max2175";
> > +	reg = <0x60>;
> > +	clocks = <&maxim_xtal>;
> > +	clock-names = "xtal";
> > +	maxim,refout-load-pF = <10>;
> > +
> > +	port {
> > +		max2175_0_ep: endpoint {
> > +			remote-endpoint = <&slave_rx_v4l2_sdr_device>;
> 
> 'v4l2' is not something that should appear in a DT.

OK. I'll leave it as "slave_rx_device".

Thanks,
Ramesh

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

* [PATCH v2 0/7] Add V4L2 SDR (DRIF & MAX2175) driver
  2016-11-09 15:44 ` Ramesh Shanmugasundaram
                   ` (4 preceding siblings ...)
  (?)
@ 2016-12-21  8:10 ` Ramesh Shanmugasundaram
  2016-12-21  8:10   ` [PATCH v2 1/7] media: v4l2-ctrls: Reserve controls for MAX217X Ramesh Shanmugasundaram
                     ` (6 more replies)
  -1 siblings, 7 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-12-21  8:10 UTC (permalink / raw)
  To: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope
  Cc: chris.paterson2, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc, Ramesh Shanmugasundaram

This patch set contains two drivers
 - Renesas R-Car Digital Radio Interface (DRIF) driver
 - Maxim's MAX2175 RF to Bits tuner driver

These patches were based on top of media-next repo
commit: 65390ea01ce678379da32b01f39fcfac4903f256

These two drivers combined together expose a V4L2 SDR device that is compliant
with the V4L2 framework [1]. Agreed review comments are incorporated in this
series.

The rcar_drif device is modelled using "renesas,bonding" property. The
discussion on this property is available here [2].

Change history:

v1 -> v2:
 - SDR formats renamed as "planar" instead of sliced (Hans)
 - Documentation formatting correction (Laurent)

 rcar_drif:
 - DT model using "bonding" property
 - Addressed Laurent's coments on bindings - DT optional parameters rename & rework
 - Addressed Han's comments on driver
 - Addressed Geert's comments on DT

 max2175:
 - Avoided scaling using method proposed by Antti. Thanks
 - Bindings is a separate patch (Rob)
 - Addressed Rob's comment on bindings
 - Added Custom controls documentation (Laurent)

Ramesh Shanmugasundaram (7):
  media: v4l2-ctrls: Reserve controls for MAX217X
  dt-bindings: media: Add MAX2175 binding description
  media: i2c: max2175: Add MAX2175 support
  media: Add new SDR formats PC16, PC18 & PC20
  doc_rst: media: New SDR formats PC16, PC18 & PC20
  dt-bindings: media: Add Renesas R-Car DRIF binding
  media: platform: rcar_drif: Add DRIF support

[1] v4l2-compliance report:

root@salvator-x:~# v4l2-compliance -S /dev/swradio0
v4l2-compliance SHA   : 188e604d57bec065078ff772c802b93ddb6def4b

Driver Info:
        Driver name   : rcar_drif
        Card type     : R-Car DRIF
        Bus info      : platform:R-Car DRIF
        Driver version: 4.9.0
        Capabilities  : 0x85310000
                SDR Capture
                Tuner
                Read/Write
                Streaming
                Extended Pix Format
                Device Capabilities
        Device Caps   : 0x05310000
                SDR Capture
                Tuner
                Read/Write
                Streaming
                Extended Pix Format

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

Required ioctls:
        test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
        test second sdr 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
        test VIDIOC_LOG_STATUS: OK

Input ioctls:
        test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK
        test VIDIOC_G/S_FREQUENCY: OK
        test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
        test VIDIOC_ENUMAUDIO: OK (Not Supported)
        test VIDIOC_G/S/ENUMINPUT: OK (Not Supported)
        test VIDIOC_G/S_AUDIO: OK (Not Supported)
        Inputs: 0 Audio Inputs: 0 Tuners: 1

Output ioctls:
        test VIDIOC_G/S_MODULATOR: OK (Not Supported)
        test VIDIOC_G/S_FREQUENCY: OK
        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)

        Control ioctls:
                test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
                test VIDIOC_QUERYCTRL: OK
                test VIDIOC_G/S_CTRL: OK
                test VIDIOC_G/S/TRY_EXT_CTRLS: OK
                test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
                test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
                Standard Controls: 5 Private Controls: 3

        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 (Not Supported)

Test input 0:


Total: 43, Succeeded: 43, Failed: 0, Warnings: 0
root@salvator-x:~#

[2] "bonding" DT property discussion (https://www.mail-archive.com/linux-renesas-soc@vger.kernel.org/msg09415.html)

 .../devicetree/bindings/media/i2c/max2175.txt      |   61 +
 .../devicetree/bindings/media/renesas,drif.txt     |  202 +++
 .../devicetree/bindings/property-units.txt         |    1 +
 .../media/uapi/v4l/pixfmt-sdr-pcu16be.rst          |   55 +
 .../media/uapi/v4l/pixfmt-sdr-pcu18be.rst          |   55 +
 .../media/uapi/v4l/pixfmt-sdr-pcu20be.rst          |   55 +
 Documentation/media/uapi/v4l/sdr-formats.rst       |    3 +
 Documentation/media/v4l-drivers/index.rst          |    1 +
 Documentation/media/v4l-drivers/max2175.rst        |   60 +
 drivers/media/i2c/Kconfig                          |    4 +
 drivers/media/i2c/Makefile                         |    2 +
 drivers/media/i2c/max2175/Kconfig                  |    8 +
 drivers/media/i2c/max2175/Makefile                 |    4 +
 drivers/media/i2c/max2175/max2175.c                | 1438 ++++++++++++++++++
 drivers/media/i2c/max2175/max2175.h                |  108 ++
 drivers/media/platform/Kconfig                     |   25 +
 drivers/media/platform/Makefile                    |    1 +
 drivers/media/platform/rcar_drif.c                 | 1593 ++++++++++++++++++++
 drivers/media/v4l2-core/v4l2-ioctl.c               |    3 +
 include/uapi/linux/v4l2-controls.h                 |    5 +
 include/uapi/linux/videodev2.h                     |    3 +
 21 files changed, 3687 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/max2175.txt
 create mode 100644 Documentation/devicetree/bindings/media/renesas,drif.txt
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-pcu16be.rst
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-pcu18be.rst
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-pcu20be.rst
 create mode 100644 Documentation/media/v4l-drivers/max2175.rst
 create mode 100644 drivers/media/i2c/max2175/Kconfig
 create mode 100644 drivers/media/i2c/max2175/Makefile
 create mode 100644 drivers/media/i2c/max2175/max2175.c
 create mode 100644 drivers/media/i2c/max2175/max2175.h
 create mode 100644 drivers/media/platform/rcar_drif.c

-- 
1.9.1

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

* [PATCH v2 1/7] media: v4l2-ctrls: Reserve controls for MAX217X
  2016-12-21  8:10 ` [PATCH v2 0/7] Add V4L2 SDR (DRIF & MAX2175) driver Ramesh Shanmugasundaram
@ 2016-12-21  8:10   ` Ramesh Shanmugasundaram
  2016-12-21  8:10   ` [PATCH v2 2/7] dt-bindings: media: Add MAX2175 binding description Ramesh Shanmugasundaram
                     ` (5 subsequent siblings)
  6 siblings, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-12-21  8:10 UTC (permalink / raw)
  To: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope
  Cc: chris.paterson2, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc, Ramesh Shanmugasundaram

Reserve controls for MAX217X RF to Bits tuner family. These hybrid
radio receiver chips are highly programmable and hence reserving 32
controls.

Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>
---
 include/uapi/linux/v4l2-controls.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 0d2e1e0..83b28b4 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -180,6 +180,11 @@ enum v4l2_colorfx {
  * We reserve 16 controls for this driver. */
 #define V4L2_CID_USER_TC358743_BASE		(V4L2_CID_USER_BASE + 0x1080)
 
+/* The base for the max217x driver controls.
+ * We reserve 32 controls for this driver
+ */
+#define V4L2_CID_USER_MAX217X_BASE		(V4L2_CID_USER_BASE + 0x1090)
+
 /* MPEG-class control IDs */
 /* The MPEG controls are applicable to all codec controls
  * and the 'MPEG' part of the define is historical */
-- 
1.9.1

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

* [PATCH v2 2/7] dt-bindings: media: Add MAX2175 binding description
  2016-12-21  8:10 ` [PATCH v2 0/7] Add V4L2 SDR (DRIF & MAX2175) driver Ramesh Shanmugasundaram
  2016-12-21  8:10   ` [PATCH v2 1/7] media: v4l2-ctrls: Reserve controls for MAX217X Ramesh Shanmugasundaram
@ 2016-12-21  8:10   ` Ramesh Shanmugasundaram
  2016-12-21  8:10   ` [PATCH v2 3/7] media: i2c: max2175: Add MAX2175 support Ramesh Shanmugasundaram
                     ` (4 subsequent siblings)
  6 siblings, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-12-21  8:10 UTC (permalink / raw)
  To: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope
  Cc: chris.paterson2, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc, Ramesh Shanmugasundaram

Add device tree binding documentation for MAX2175 Rf to bits tuner
device.

Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>
---
 .../devicetree/bindings/media/i2c/max2175.txt      | 61 ++++++++++++++++++++++
 .../devicetree/bindings/property-units.txt         |  1 +
 2 files changed, 62 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/max2175.txt

diff --git a/Documentation/devicetree/bindings/media/i2c/max2175.txt b/Documentation/devicetree/bindings/media/i2c/max2175.txt
new file mode 100644
index 0000000..f591ab4
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/max2175.txt
@@ -0,0 +1,61 @@
+Maxim Integrated MAX2175 RF to Bits tuner
+-----------------------------------------
+
+The MAX2175 IC is an advanced analog/digital hybrid-radio receiver with
+RF to Bits® front-end designed for software-defined radio solutions.
+
+Required properties:
+--------------------
+- compatible: "maxim,max2175" for MAX2175 RF-to-bits tuner.
+- clocks: phandle to the fixed xtal clock.
+- clock-names: name of the fixed xtal clock.
+- port: child port node of a tuner that defines the local and remote
+  endpoints. The remote endpoint is assumed to be an SDR device
+  that is capable of receiving the digital samples from the tuner.
+
+Optional properties:
+--------------------
+- maxim,slave	      : phandle to the master tuner if it is a slave. This
+			is used to define two tuners in diversity mode
+			(1 master, 1 slave). By default each tuner is an
+			individual master.
+- maxim,refout-load-pF: load capacitance value (in pF) on reference
+			output drive level. The possible load values are
+			 0 (default - refout disabled)
+			10
+			20
+			30
+			40
+			60
+			70
+- maxim,am-hiz	      : empty property indicates AM Hi-Z filter path is
+			selected for AM antenna input. By default this
+			filter path is not used.
+
+Example:
+--------
+
+Board specific DTS file
+
+/* Fixed XTAL clock node */
+maxim_xtal: clock {
+	compatible = "fixed-clock";
+	#clock-cells = <0>;
+	clock-frequency = <36864000>;
+};
+
+/* A tuner device instance under i2c bus */
+max2175_0: tuner@60 {
+	compatible = "maxim,max2175";
+	reg = <0x60>;
+	clocks = <&maxim_xtal>;
+	clock-names = "xtal";
+	maxim,refout-load-pF = <10>;
+
+	port {
+		max2175_0_ep: endpoint {
+			remote-endpoint = <&slave_rx_device>;
+		};
+	};
+
+};
diff --git a/Documentation/devicetree/bindings/property-units.txt b/Documentation/devicetree/bindings/property-units.txt
index 12278d7..f1f1c22 100644
--- a/Documentation/devicetree/bindings/property-units.txt
+++ b/Documentation/devicetree/bindings/property-units.txt
@@ -28,6 +28,7 @@ Electricity
 -ohms		: Ohms
 -micro-ohms	: micro Ohms
 -microvolt	: micro volts
+-pF		: pico farads
 
 Temperature
 ----------------------------------------
-- 
1.9.1

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

* [PATCH v2 3/7] media: i2c: max2175: Add MAX2175 support
  2016-12-21  8:10 ` [PATCH v2 0/7] Add V4L2 SDR (DRIF & MAX2175) driver Ramesh Shanmugasundaram
  2016-12-21  8:10   ` [PATCH v2 1/7] media: v4l2-ctrls: Reserve controls for MAX217X Ramesh Shanmugasundaram
  2016-12-21  8:10   ` [PATCH v2 2/7] dt-bindings: media: Add MAX2175 binding description Ramesh Shanmugasundaram
@ 2016-12-21  8:10   ` Ramesh Shanmugasundaram
  2016-12-21  8:10   ` [PATCH v2 4/7] media: Add new SDR formats PC16, PC18 & PC20 Ramesh Shanmugasundaram
                     ` (3 subsequent siblings)
  6 siblings, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-12-21  8:10 UTC (permalink / raw)
  To: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope
  Cc: chris.paterson2, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc, Ramesh Shanmugasundaram

This patch adds driver support for the MAX2175 chip. This is Maxim
Integrated's RF to Bits tuner front end chip designed for software-defined
radio solutions. This driver exposes the tuner as a sub-device instance
with standard and custom controls to configure the device.

Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>
---
 Documentation/media/v4l-drivers/index.rst   |    1 +
 Documentation/media/v4l-drivers/max2175.rst |   60 ++
 drivers/media/i2c/Kconfig                   |    4 +
 drivers/media/i2c/Makefile                  |    2 +
 drivers/media/i2c/max2175/Kconfig           |    8 +
 drivers/media/i2c/max2175/Makefile          |    4 +
 drivers/media/i2c/max2175/max2175.c         | 1438 +++++++++++++++++++++++++++
 drivers/media/i2c/max2175/max2175.h         |  108 ++
 8 files changed, 1625 insertions(+)
 create mode 100644 Documentation/media/v4l-drivers/max2175.rst
 create mode 100644 drivers/media/i2c/max2175/Kconfig
 create mode 100644 drivers/media/i2c/max2175/Makefile
 create mode 100644 drivers/media/i2c/max2175/max2175.c
 create mode 100644 drivers/media/i2c/max2175/max2175.h

diff --git a/Documentation/media/v4l-drivers/index.rst b/Documentation/media/v4l-drivers/index.rst
index a606d1c..d8cade5 100644
--- a/Documentation/media/v4l-drivers/index.rst
+++ b/Documentation/media/v4l-drivers/index.rst
@@ -42,6 +42,7 @@ For more details see the file COPYING in the source distribution of Linux.
 	davinci-vpbe
 	fimc
 	ivtv
+        max2175
 	meye
 	omap3isp
 	omap4_camera
diff --git a/Documentation/media/v4l-drivers/max2175.rst b/Documentation/media/v4l-drivers/max2175.rst
new file mode 100644
index 0000000..201af8f
--- /dev/null
+++ b/Documentation/media/v4l-drivers/max2175.rst
@@ -0,0 +1,60 @@
+Maxim Integrated MAX2175 RF to bits tuner driver
+================================================
+
+The MAX2175 driver implements the following driver-specific controls:
+
+``V4L2_CID_MAX2175_I2S_ENABLE``
+-------------------------------
+    Enable/Disable I2S output of the tuner.
+
+.. flat-table::
+    :header-rows:  0
+    :stub-columns: 0
+    :widths:       1 4
+
+    * - ``(0)``
+      - I2S output is disabled.
+    * - ``(1)``
+      - I2S output is enabled.
+
+``V4L2_CID_MAX2175_HSLS``
+-------------------------
+    The high-side/low-side (HSLS) control of the tuner for a given band.
+
+.. flat-table::
+    :header-rows:  0
+    :stub-columns: 0
+    :widths:       1 4
+
+    * - ``(0)``
+      - The LO frequency position is below the desired frequency.
+    * - ``(1)``
+      - The LO frequency position is above the desired frequency.
+
+``V4L2_CID_MAX2175_RX_MODE (menu)``
+-----------------------------------
+    The Rx mode controls a number of preset parameters of the tuner like sck
+    rate, sampling rate etc. These multiple settings are provided under one
+    single label called Rx mode in the datasheet. The list below shows the
+    supported modes with a brief description.
+
+.. flat-table::
+    :header-rows:  0
+    :stub-columns: 0
+    :widths:       1 4
+
+    * - ``"Europe modes"``
+    * - ``"FM 1.2" (0)``
+      - This configures FM band with a sample rate of 0.512 million
+        samples/sec with a 10.24 MHz sck.
+    * - ``"DAB 1.2" (1)``
+      - This configures VHF band with a sample rate of 2.048 million
+        samples/sec with a 32.768 MHz sck.
+
+    * - ``"North America modes"``
+    * - ``"FM 1.0" (0)``
+      - This configures FM band with a sample rate of 0.7441875 million
+        samples/sec with a 14.88375 MHz sck.
+    * - ``"DAB 1.2" (1)``
+      - This configures FM band with a sample rate of 0.372 million
+        samples/sec with a 7.441875 MHz sck.
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index b31fa6f..d6ebeab 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -749,6 +749,10 @@ config VIDEO_SAA6752HS
 	  To compile this driver as a module, choose M here: the
 	  module will be called saa6752hs.
 
+comment "SDR tuner chips"
+
+source "drivers/media/i2c/max2175/Kconfig"
+
 comment "Miscellaneous helper chips"
 
 config VIDEO_THS7303
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 92773b2..cfae721 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -6,6 +6,8 @@ obj-$(CONFIG_VIDEO_CX25840) += cx25840/
 obj-$(CONFIG_VIDEO_M5MOLS)	+= m5mols/
 obj-y				+= soc_camera/
 
+obj-$(CONFIG_SDR_MAX2175) 	+= max2175/
+
 obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o
 obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o
 obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o
diff --git a/drivers/media/i2c/max2175/Kconfig b/drivers/media/i2c/max2175/Kconfig
new file mode 100644
index 0000000..93a8f83
--- /dev/null
+++ b/drivers/media/i2c/max2175/Kconfig
@@ -0,0 +1,8 @@
+config SDR_MAX2175
+	tristate "Maxim 2175 RF to Bits tuner"
+	depends on VIDEO_V4L2 && MEDIA_SDR_SUPPORT && I2C
+	---help---
+	  Support for Maxim 2175 tuner
+
+	  To compile this driver as a module, choose M here; the
+	  module will be called max2175.
diff --git a/drivers/media/i2c/max2175/Makefile b/drivers/media/i2c/max2175/Makefile
new file mode 100644
index 0000000..9bb46ac
--- /dev/null
+++ b/drivers/media/i2c/max2175/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for Maxim RF to Bits tuner device
+#
+obj-$(CONFIG_SDR_MAX2175) += max2175.o
diff --git a/drivers/media/i2c/max2175/max2175.c b/drivers/media/i2c/max2175/max2175.c
new file mode 100644
index 0000000..e04c56c
--- /dev/null
+++ b/drivers/media/i2c/max2175/max2175.c
@@ -0,0 +1,1438 @@
+/*
+ * Maxim Integrated MAX2175 RF to Bits tuner driver
+ *
+ * This driver & most of the hard coded values are based on the reference
+ * application delivered by Maxim for this device.
+ *
+ * Copyright (C) 2016 Maxim Integrated Products
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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/errno.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-of.h>
+
+#include "max2175.h"
+
+#define DRIVER_NAME "max2175"
+
+static unsigned int debug;
+module_param(debug, uint, 0644);
+MODULE_PARM_DESC(debug, "activate debug info");
+
+#define mxm_dbg(ctx, fmt, arg...) v4l2_dbg(1, debug, &ctx->sd, fmt, ## arg)
+#define mxm_err(ctx, fmt, arg...) dev_err(&ctx->client->dev, fmt, ## arg)
+
+/* Rx mode */
+struct max2175_rxmode {
+	enum max2175_band band;		/* Associated band */
+	u32 freq;			/* Default freq in Hz */
+	u8 i2s_word_size;		/* Bit value */
+};
+
+/* Register map to define preset values */
+struct max2175_reg_map {
+	u8 idx;				/* Register index */
+	u8 val;				/* Register value */
+};
+
+static const struct max2175_rxmode eu_rx_modes[] = {
+	/* EU modes */
+	[MAX2175_EU_FM_1_2] = { MAX2175_BAND_FM, 98256000, 1 },
+	[MAX2175_DAB_1_2]   = { MAX2175_BAND_VHF, 182640000, 0 },
+};
+
+static const struct max2175_rxmode na_rx_modes[] = {
+	/* NA modes */
+	[MAX2175_NA_FM_1_0] = { MAX2175_BAND_FM, 98255520, 1 },
+	[MAX2175_NA_FM_2_0] = { MAX2175_BAND_FM, 98255520, 6 },
+};
+
+/*
+ * Preset values:
+ * Based on Maxim MAX2175 Register Table revision: 130p10
+ */
+static const u8 full_fm_eu_1p0[] = {
+	0x15, 0x04, 0xb8, 0xe3, 0x35, 0x18, 0x7c, 0x00,
+	0x00, 0x7d, 0x40, 0x08, 0x70, 0x7a, 0x88, 0x91,
+	0x61, 0x61, 0x61, 0x61, 0x5a, 0x0f, 0x34, 0x1c,
+	0x14, 0x88, 0x33, 0x02, 0x00, 0x09, 0x00, 0x65,
+	0x9f, 0x2b, 0x80, 0x00, 0x95, 0x05, 0x2c, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
+	0x4a, 0x08, 0xa8, 0x0e, 0x0e, 0x2f, 0x7e, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x5e, 0xa9,
+	0xae, 0xbb, 0x57, 0x18, 0x3b, 0x03, 0x3b, 0x64,
+	0x40, 0x60, 0x00, 0x2a, 0xbf, 0x3f, 0xff, 0x9f,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00,
+	0xff, 0xfc, 0xef, 0x1c, 0x40, 0x00, 0x00, 0x02,
+	0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x40, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00,
+	0x00, 0x47, 0x00, 0x00, 0x11, 0x3f, 0x22, 0x00,
+	0xf1, 0x00, 0x41, 0x03, 0xb0, 0x00, 0x00, 0x00,
+	0x1b,
+};
+
+static const u8 full_fm_na_1p0[] = {
+	0x13, 0x08, 0x8d, 0xc0, 0x35, 0x18, 0x7d, 0x3f,
+	0x7d, 0x75, 0x40, 0x08, 0x70, 0x7a, 0x88, 0x91,
+	0x61, 0x61, 0x61, 0x61, 0x5c, 0x0f, 0x34, 0x1c,
+	0x14, 0x88, 0x33, 0x02, 0x00, 0x01, 0x00, 0x65,
+	0x9f, 0x2b, 0x80, 0x00, 0x95, 0x05, 0x2c, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
+	0x4a, 0x08, 0xa8, 0x0e, 0x0e, 0xaf, 0x7e, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x5e, 0xa9,
+	0xae, 0xbb, 0x57, 0x18, 0x3b, 0x03, 0x3b, 0x64,
+	0x40, 0x60, 0x00, 0x2a, 0xbf, 0x3f, 0xff, 0x9f,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00,
+	0xff, 0xfc, 0xef, 0x1c, 0x40, 0x00, 0x00, 0x02,
+	0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0x40, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00,
+	0x00, 0x35, 0x00, 0x00, 0x11, 0x3f, 0x22, 0x00,
+	0xf1, 0x00, 0x41, 0x03, 0xb0, 0x00, 0x00, 0x00,
+	0x1b,
+};
+
+/* DAB1.2 settings */
+static const struct max2175_reg_map dab12_map[] = {
+	{ 0x01, 0x13 }, { 0x02, 0x0d }, { 0x03, 0x15 }, { 0x04, 0x55 },
+	{ 0x05, 0x0a }, { 0x06, 0xa0 }, { 0x07, 0x40 }, { 0x08, 0x00 },
+	{ 0x09, 0x00 }, { 0x0a, 0x7d }, { 0x0b, 0x4a }, { 0x0c, 0x28 },
+	{ 0x0e, 0x43 }, { 0x0f, 0xb5 }, { 0x10, 0x31 }, { 0x11, 0x9e },
+	{ 0x12, 0x68 }, { 0x13, 0x9e }, { 0x14, 0x68 }, { 0x15, 0x58 },
+	{ 0x16, 0x2f }, { 0x17, 0x3f }, { 0x18, 0x40 }, { 0x1a, 0x88 },
+	{ 0x1b, 0xaa }, { 0x1c, 0x9a }, { 0x1d, 0x00 }, { 0x1e, 0x00 },
+	{ 0x23, 0x80 }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 },
+	{ 0x27, 0x00 }, { 0x32, 0x08 }, { 0x33, 0xf8 }, { 0x36, 0x2d },
+	{ 0x37, 0x7e }, { 0x55, 0xaf }, { 0x56, 0x3f }, { 0x57, 0xf8 },
+	{ 0x58, 0x99 }, { 0x76, 0x00 }, { 0x77, 0x00 }, { 0x78, 0x02 },
+	{ 0x79, 0x40 }, { 0x82, 0x00 }, { 0x83, 0x00 }, { 0x85, 0x00 },
+	{ 0x86, 0x20 },
+};
+
+/* EU FM 1.2 settings */
+static const struct max2175_reg_map fmeu1p2_map[] = {
+	{ 0x01, 0x15 }, { 0x02, 0x04 }, { 0x03, 0xb8 }, { 0x04, 0xe3 },
+	{ 0x05, 0x35 }, { 0x06, 0x18 }, { 0x07, 0x7c }, { 0x08, 0x00 },
+	{ 0x09, 0x00 }, { 0x0a, 0x73 }, { 0x0b, 0x40 }, { 0x0c, 0x08 },
+	{ 0x0e, 0x7a }, { 0x0f, 0x88 }, { 0x10, 0x91 }, { 0x11, 0x61 },
+	{ 0x12, 0x61 }, { 0x13, 0x61 }, { 0x14, 0x61 }, { 0x15, 0x5a },
+	{ 0x16, 0x0f }, { 0x17, 0x34 }, { 0x18, 0x1c }, { 0x1a, 0x88 },
+	{ 0x1b, 0x33 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x1e, 0x01 },
+	{ 0x23, 0x80 }, { 0x24, 0x00 }, { 0x25, 0x95 }, { 0x26, 0x05 },
+	{ 0x27, 0x2c }, { 0x32, 0x08 }, { 0x33, 0xa8 }, { 0x36, 0x2f },
+	{ 0x37, 0x7e }, { 0x55, 0xbf }, { 0x56, 0x3f }, { 0x57, 0xff },
+	{ 0x58, 0x9f }, { 0x76, 0xac }, { 0x77, 0x40 }, { 0x78, 0x00 },
+	{ 0x79, 0x00 }, { 0x82, 0x47 }, { 0x83, 0x00 }, { 0x85, 0x11 },
+	{ 0x86, 0x3f },
+};
+
+/* FM NA 1.0 settings */
+static const struct max2175_reg_map fmna1p0_map[] = {
+	{ 0x01, 0x13 }, { 0x02, 0x08 }, { 0x03, 0x8d }, { 0x04, 0xc0 },
+	{ 0x05, 0x35 }, { 0x06, 0x18 }, { 0x07, 0x7d }, { 0x08, 0x3f },
+	{ 0x09, 0x7d }, { 0x0a, 0x75 }, { 0x0b, 0x40 }, { 0x0c, 0x08 },
+	{ 0x0e, 0x7a }, { 0x0f, 0x88 }, { 0x10, 0x91 }, { 0x11, 0x61 },
+	{ 0x12, 0x61 }, { 0x13, 0x61 }, { 0x14, 0x61 }, { 0x15, 0x5c },
+	{ 0x16, 0x0f }, { 0x17, 0x34 }, { 0x18, 0x1c }, { 0x1a, 0x88 },
+	{ 0x1b, 0x33 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x1e, 0x01 },
+	{ 0x23, 0x80 }, { 0x24, 0x00 }, { 0x25, 0x95 }, { 0x26, 0x05 },
+	{ 0x27, 0x2c }, { 0x32, 0x08 }, { 0x33, 0xa8 }, { 0x36, 0xaf },
+	{ 0x37, 0x7e }, { 0x55, 0xbf }, { 0x56, 0x3f }, { 0x57, 0xff },
+	{ 0x58, 0x9f }, { 0x76, 0xa6 }, { 0x77, 0x40 }, { 0x78, 0x00 },
+	{ 0x79, 0x00 }, { 0x82, 0x35 }, { 0x83, 0x00 }, { 0x85, 0x11 },
+	{ 0x86, 0x3f },
+};
+
+/* FM NA 2.0 settings */
+static const struct max2175_reg_map fmna2p0_map[] = {
+	{ 0x01, 0x13 }, { 0x02, 0x08 }, { 0x03, 0x8d }, { 0x04, 0xc0 },
+	{ 0x05, 0x35 }, { 0x06, 0x18 }, { 0x07, 0x7c }, { 0x08, 0x54 },
+	{ 0x09, 0xa7 }, { 0x0a, 0x55 }, { 0x0b, 0x42 }, { 0x0c, 0x48 },
+	{ 0x0e, 0x7a }, { 0x0f, 0x88 }, { 0x10, 0x91 }, { 0x11, 0x61 },
+	{ 0x12, 0x61 }, { 0x13, 0x61 }, { 0x14, 0x61 }, { 0x15, 0x5c },
+	{ 0x16, 0x0f }, { 0x17, 0x34 }, { 0x18, 0x1c }, { 0x1a, 0x88 },
+	{ 0x1b, 0x33 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x1e, 0x01 },
+	{ 0x23, 0x80 }, { 0x24, 0x00 }, { 0x25, 0x95 }, { 0x26, 0x05 },
+	{ 0x27, 0x2c }, { 0x32, 0x08 }, { 0x33, 0xa8 }, { 0x36, 0xaf },
+	{ 0x37, 0x7e }, { 0x55, 0xbf }, { 0x56, 0x3f }, { 0x57, 0xff },
+	{ 0x58, 0x9f }, { 0x76, 0xac }, { 0x77, 0xc0 }, { 0x78, 0x00 },
+	{ 0x79, 0x00 }, { 0x82, 0x6b }, { 0x83, 0x00 }, { 0x85, 0x11 },
+	{ 0x86, 0x3f },
+};
+
+static const u16 ch_coeff_dab1[] = {
+	0x001c, 0x0007, 0xffcd, 0x0056, 0xffa4, 0x0033, 0x0027, 0xff61,
+	0x010e, 0xfec0, 0x0106, 0xffb8, 0xff1c, 0x023c, 0xfcb2, 0x039b,
+	0xfd4e, 0x0055, 0x036a, 0xf7de, 0x0d21, 0xee72, 0x1499, 0x6a51,
+};
+
+static const u16 ch_coeff_fmeu[] = {
+	0x0000, 0xffff, 0x0001, 0x0002, 0xfffa, 0xffff, 0x0015, 0xffec,
+	0xffde, 0x0054, 0xfff9, 0xff52, 0x00b8, 0x00a2, 0xfe0a, 0x00af,
+	0x02e3, 0xfc14, 0xfe89, 0x089d, 0xfa2e, 0xf30f, 0x25be, 0x4eb6,
+};
+
+static const u16 eq_coeff_fmeu1_ra02_m6db[] = {
+	0x0040, 0xffc6, 0xfffa, 0x002c, 0x000d, 0xff90, 0x0037, 0x006e,
+	0xffc0, 0xff5b, 0x006a, 0x00f0, 0xff57, 0xfe94, 0x0112, 0x0252,
+	0xfe0c, 0xfc6a, 0x0385, 0x0553, 0xfa49, 0xf789, 0x0b91, 0x1a10,
+};
+
+static const u16 ch_coeff_fmna[] = {
+	0x0001, 0x0003, 0xfffe, 0xfff4, 0x0000, 0x001f, 0x000c, 0xffbc,
+	0xffd3, 0x007d, 0x0075, 0xff33, 0xff01, 0x0131, 0x01ef, 0xfe60,
+	0xfc7a, 0x020e, 0x0656, 0xfd94, 0xf395, 0x02ab, 0x2857, 0x3d3f,
+};
+
+static const u16 eq_coeff_fmna1_ra02_m6db[] = {
+	0xfff1, 0xffe1, 0xffef, 0x000e, 0x0030, 0x002f, 0xfff6, 0xffa7,
+	0xff9d, 0x000a, 0x00a2, 0x00b5, 0xffea, 0xfed9, 0xfec5, 0x003d,
+	0x0217, 0x021b, 0xff5a, 0xfc2b, 0xfcbd, 0x02c4, 0x0ac3, 0x0e85,
+};
+
+static const u8 adc_presets[2][23] = {
+	{
+		0x83, 0x00, 0xcf, 0xb4, 0x0f, 0x2c, 0x0c, 0x49,
+		0x00, 0x00, 0x00, 0x8c,	0x02, 0x02, 0x00, 0x04,
+		0xec, 0x82, 0x4b, 0xcc, 0x01, 0x88, 0x0c,
+	},
+	{
+		0x83, 0x00, 0xcf, 0xb4,	0x0f, 0x2c, 0x0c, 0x49,
+		0x00, 0x00, 0x00, 0x8c,	0x02, 0x20, 0x33, 0x8c,
+		0x57, 0xd7, 0x59, 0xb7,	0x65, 0x0e, 0x0c,
+	},
+};
+
+/* Custom controls */
+#define V4L2_CID_MAX2175_I2S_ENABLE	(V4L2_CID_USER_MAX217X_BASE + 0x01)
+#define V4L2_CID_MAX2175_HSLS		(V4L2_CID_USER_MAX217X_BASE + 0x02)
+#define V4L2_CID_MAX2175_RX_MODE	(V4L2_CID_USER_MAX217X_BASE + 0x03)
+
+/* Tuner bands */
+static const struct v4l2_frequency_band eu_bands_rf = {
+	.tuner = 0,
+	.type = V4L2_TUNER_RF,
+	.index = 0,
+	.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+	.rangelow   = 65000000,
+	.rangehigh  = 240000000,
+};
+
+static const struct v4l2_frequency_band na_bands_rf = {
+	.tuner = 0,
+	.type = V4L2_TUNER_RF,
+	.index = 0,
+	.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+	.rangelow   = 65000000,
+	.rangehigh  = 108000000,
+};
+
+/* Regmap settings */
+static const struct regmap_range max2175_regmap_volatile_range[] = {
+	regmap_reg_range(0x30, 0x35),
+	regmap_reg_range(0x3a, 0x45),
+	regmap_reg_range(0x59, 0x5e),
+	regmap_reg_range(0x73, 0x75),
+};
+
+static const struct regmap_access_table max2175_volatile_regs = {
+	.yes_ranges = max2175_regmap_volatile_range,
+	.n_yes_ranges = ARRAY_SIZE(max2175_regmap_volatile_range),
+};
+
+static const struct reg_default max2175_reg_defaults[] = {
+	{ 0x00, 0x07},
+};
+
+static const struct regmap_config max2175_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = 0xff,
+	.reg_defaults = max2175_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(max2175_reg_defaults),
+	.volatile_table = &max2175_volatile_regs,
+	.cache_type = REGCACHE_FLAT,
+};
+
+struct max2175 {
+	struct v4l2_subdev sd;		/* Sub-device */
+	struct i2c_client *client;	/* I2C client */
+
+	/* Controls */
+	struct v4l2_ctrl_handler ctrl_hdl;
+	struct v4l2_ctrl *lna_gain;	/* LNA gain value */
+	struct v4l2_ctrl *if_gain;	/* I/F gain value */
+	struct v4l2_ctrl *pll_lock;	/* PLL lock */
+	struct v4l2_ctrl *i2s_en;	/* I2S output enable */
+	struct v4l2_ctrl *hsls;		/* High-side/Low-side polarity */
+	struct v4l2_ctrl *rx_mode;	/* Receive mode */
+
+	/* Regmap */
+	struct regmap *regmap;
+
+	/* Cached configuration */
+	u32 freq;			/* Tuned freq In Hz */
+	const struct max2175_rxmode *rx_modes;		/* EU or NA modes */
+	const struct v4l2_frequency_band *bands_rf;	/* EU or NA bands */
+
+	/* Device settings */
+	unsigned long xtal_freq;	/* Ref Oscillator freq in Hz */
+	u32 decim_ratio;
+	bool master;			/* Master/Slave */
+	bool am_hiz;			/* AM Hi-Z filter usage */
+
+	/* ROM values */
+	u8 rom_bbf_bw_am;
+	u8 rom_bbf_bw_fm;
+	u8 rom_bbf_bw_dab;
+
+	/* Driver private variables */
+	bool mode_resolved;		/* Flag to sanity check settings */
+};
+
+static inline struct max2175 *max2175_from_sd(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct max2175, sd);
+}
+
+static inline struct max2175 *max2175_from_ctrl_hdl(struct v4l2_ctrl_handler *h)
+{
+	return container_of(h, struct max2175, ctrl_hdl);
+}
+
+/* Get bitval of a given val */
+static inline u8 max2175_get_bitval(u8 val, u8 msb, u8 lsb)
+{
+	return (val & GENMASK(msb, lsb)) >> lsb;
+}
+
+/* Read/Write bit(s) on top of regmap */
+static int max2175_read(struct max2175 *ctx, u8 idx, u8 *val)
+{
+	u32 regval = 0;
+	int ret = regmap_read(ctx->regmap, idx, &regval);
+
+	if (ret)
+		mxm_err(ctx, "read ret(%d): idx 0x%02x\n", ret, idx);
+
+	*val = regval;
+	return ret;
+}
+
+static int max2175_write(struct max2175 *ctx, u8 idx, u8 val)
+{
+	int ret = regmap_write(ctx->regmap, idx, val);
+
+	if (ret)
+		mxm_err(ctx, "write ret(%d): idx 0x%02x val 0x%02x\n",
+			ret, idx, val);
+	return ret;
+}
+
+static u8 max2175_read_bits(struct max2175 *ctx, u8 idx, u8 msb, u8 lsb)
+{
+	u8 val;
+
+	if (max2175_read(ctx, idx, &val))
+		return 0;
+
+	return max2175_get_bitval(val, msb, lsb);
+}
+
+static int max2175_write_bits(struct max2175 *ctx, u8 idx,
+			     u8 msb, u8 lsb, u8 newval)
+{
+	int ret = regmap_update_bits(ctx->regmap, idx, GENMASK(msb, lsb),
+				     newval << lsb);
+
+	if (ret)
+		mxm_err(ctx, "wbits ret(%d): idx 0x%02x\n", ret, idx);
+
+	return ret;
+}
+
+static int max2175_write_bit(struct max2175 *ctx, u8 idx, u8 bit, u8 newval)
+{
+	return max2175_write_bits(ctx, idx, bit, bit, newval);
+}
+
+/* Checks expected pattern every msec until timeout */
+static int max2175_poll_timeout(struct max2175 *ctx, u8 idx, u8 msb, u8 lsb,
+				u8 exp_bitval, u32 timeout_ms)
+{
+	unsigned int val;
+
+	return regmap_read_poll_timeout(ctx->regmap, idx, val,
+			(max2175_get_bitval(val, msb, lsb) == exp_bitval),
+			1000, timeout_ms * 1000);
+}
+
+static int max2175_poll_csm_ready(struct max2175 *ctx)
+{
+	int ret;
+
+	ret = max2175_poll_timeout(ctx, 69, 1, 1, 0, 50);
+	if (ret)
+		mxm_err(ctx, "csm not ready\n");
+
+	return ret;
+}
+
+#define MAX2175_IS_BAND_AM(ctx)		\
+	(max2175_read_bits(ctx, 5, 1, 0) == MAX2175_BAND_AM)
+
+#define MAX2175_IS_BAND_VHF(ctx)	\
+	(max2175_read_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF)
+
+#define MAX2175_IS_FM_MODE(ctx)		\
+	(max2175_read_bits(ctx, 12, 5, 4) == 0)
+
+#define MAX2175_IS_FMHD_MODE(ctx)	\
+	(max2175_read_bits(ctx, 12, 5, 4) == 1)
+
+#define MAX2175_IS_DAB_MODE(ctx)	\
+	(max2175_read_bits(ctx, 12, 5, 4) == 2)
+
+static int max2175_band_from_freq(u32 freq)
+{
+	if (freq >= 144000 && freq <= 26100000)
+		return MAX2175_BAND_AM;
+	else if (freq >= 65000000 && freq <= 108000000)
+		return MAX2175_BAND_FM;
+
+	return MAX2175_BAND_VHF;
+}
+
+static void max2175_i2s_enable(struct max2175 *ctx, bool enable)
+{
+	if (enable)
+		/* Stuff bits are zeroed */
+		max2175_write_bits(ctx, 104, 3, 0, 2);
+	else
+		/* Keep SCK alive */
+		max2175_write_bits(ctx, 104, 3, 0, 9);
+	mxm_dbg(ctx, "i2s %sabled\n", enable ? "en" : "dis");
+}
+
+static void max2175_set_filter_coeffs(struct max2175 *ctx, u8 m_sel,
+				      u8 bank, const u16 *coeffs)
+{
+	unsigned int i;
+	u8 coeff_addr, upper_address = 24;
+
+	mxm_dbg(ctx, "set_filter_coeffs: m_sel %d bank %d\n", m_sel, bank);
+	max2175_write_bits(ctx, 114, 5, 4, m_sel);
+
+	if (m_sel == 2)
+		upper_address = 12;
+
+	for (i = 0; i < upper_address; i++) {
+		coeff_addr = i + bank * 24;
+		max2175_write(ctx, 115, coeffs[i] >> 8);
+		max2175_write(ctx, 116, coeffs[i]);
+		max2175_write(ctx, 117, coeff_addr | 1 << 7);
+	}
+	max2175_write_bit(ctx, 117, 7, 0);
+}
+
+static void max2175_load_fmeu_1p2(struct max2175 *ctx)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(fmeu1p2_map); i++)
+		max2175_write(ctx, fmeu1p2_map[i].idx, fmeu1p2_map[i].val);
+
+	ctx->decim_ratio = 36;
+
+	/* Load the Channel Filter Coefficients into channel filter bank #2 */
+	max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 0, ch_coeff_fmeu);
+	max2175_set_filter_coeffs(ctx, MAX2175_EQ_MSEL, 0,
+				  eq_coeff_fmeu1_ra02_m6db);
+}
+
+static void max2175_load_dab_1p2(struct max2175 *ctx)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(dab12_map); i++)
+		max2175_write(ctx, dab12_map[i].idx, dab12_map[i].val);
+
+	ctx->decim_ratio = 1;
+
+	/* Load the Channel Filter Coefficients into channel filter bank #2 */
+	max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 2, ch_coeff_dab1);
+}
+
+static void max2175_load_fmna_1p0(struct max2175 *ctx)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(fmna1p0_map); i++)
+		max2175_write(ctx, fmna1p0_map[i].idx, fmna1p0_map[i].val);
+}
+
+static void max2175_load_fmna_2p0(struct max2175 *ctx)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(fmna2p0_map); i++)
+		max2175_write(ctx, fmna2p0_map[i].idx, fmna2p0_map[i].val);
+}
+
+static void max2175_set_bbfilter(struct max2175 *ctx)
+{
+	if (MAX2175_IS_BAND_AM(ctx)) {
+		max2175_write_bits(ctx, 12, 3, 0, ctx->rom_bbf_bw_am);
+		mxm_dbg(ctx, "set_bbfilter AM: rom %d\n", ctx->rom_bbf_bw_am);
+	} else if (MAX2175_IS_DAB_MODE(ctx)) {
+		max2175_write_bits(ctx, 12, 3, 0, ctx->rom_bbf_bw_dab);
+		mxm_dbg(ctx, "set_bbfilter DAB: rom %d\n", ctx->rom_bbf_bw_dab);
+	} else {
+		max2175_write_bits(ctx, 12, 3, 0, ctx->rom_bbf_bw_fm);
+		mxm_dbg(ctx, "set_bbfilter FM: rom %d\n", ctx->rom_bbf_bw_fm);
+	}
+}
+
+static bool max2175_set_csm_mode(struct max2175 *ctx,
+			  enum max2175_csm_mode new_mode)
+{
+	int ret = max2175_poll_csm_ready(ctx);
+
+	if (ret)
+		return ret;
+
+	max2175_write_bits(ctx, 0, 2, 0, new_mode);
+	mxm_dbg(ctx, "set csm new mode %d\n", new_mode);
+
+	/* Wait for a fixed settle down time depending on new mode */
+	switch (new_mode) {
+	case MAX2175_PRESET_TUNE:
+		usleep_range(51100, 51500);	/* 51.1ms */
+		break;
+	/*
+	 * Other mode switches need different sleep values depending on band &
+	 * mode
+	 */
+	default:
+		break;
+	}
+
+	return max2175_poll_csm_ready(ctx);
+}
+
+static int max2175_csm_action(struct max2175 *ctx,
+			      enum max2175_csm_mode action)
+{
+	int ret;
+
+	mxm_dbg(ctx, "csm_action: %d\n", action);
+
+	/* Other actions can be added in future when needed */
+	ret = max2175_set_csm_mode(ctx, MAX2175_LOAD_TO_BUFFER);
+	if (ret)
+		return ret;
+	return max2175_set_csm_mode(ctx, MAX2175_PRESET_TUNE);
+}
+
+static int max2175_set_lo_freq(struct max2175 *ctx, u32 lo_freq)
+{
+	u8 lo_mult, loband_bits = 0, vcodiv_bits = 0;
+	u32 int_desired, frac_desired;
+	enum max2175_band band;
+	int ret;
+
+	band = max2175_read_bits(ctx, 5, 1, 0);
+	switch (band) {
+	case MAX2175_BAND_AM:
+		lo_mult = 16;
+		break;
+	case MAX2175_BAND_FM:
+		if (lo_freq <= 74700000) {
+			lo_mult = 16;
+		} else if (lo_freq > 74700000 && lo_freq <= 110000000) {
+			loband_bits = 1;
+			lo_mult = 8;
+		} else {
+			loband_bits = 1;
+			vcodiv_bits = 3;
+			lo_mult = 8;
+		}
+		break;
+	case MAX2175_BAND_VHF:
+		if (lo_freq <= 210000000)
+			vcodiv_bits = 2;
+		else
+			vcodiv_bits = 1;
+
+		loband_bits = 2;
+		lo_mult = 4;
+		break;
+	default:
+		loband_bits = 3;
+		vcodiv_bits = 2;
+		lo_mult = 2;
+		break;
+	}
+
+	if (band == MAX2175_BAND_L)
+		lo_freq /= lo_mult;
+	else
+		lo_freq *= lo_mult;
+
+	int_desired = lo_freq / ctx->xtal_freq;
+	frac_desired = div_u64((u64)(lo_freq % ctx->xtal_freq) << 20,
+			       ctx->xtal_freq);
+
+	/* Check CSM is not busy */
+	ret = max2175_poll_csm_ready(ctx);
+	if (ret)
+		return ret;
+
+	mxm_dbg(ctx, "lo_mult %u int %u  frac %u\n",
+		lo_mult, int_desired, frac_desired);
+
+	/* Write the calculated values to the appropriate registers */
+	max2175_write(ctx, 1, int_desired);
+	max2175_write_bits(ctx, 2, 3, 0, (frac_desired >> 16) & 0xf);
+	max2175_write(ctx, 3, frac_desired >> 8);
+	max2175_write(ctx, 4, frac_desired);
+	max2175_write_bits(ctx, 5, 3, 2, loband_bits);
+	max2175_write_bits(ctx, 6, 7, 6, vcodiv_bits);
+	return ret;
+}
+
+/*
+ * Helper similar to DIV_ROUND_CLOSEST but an inline function that accepts s64
+ * dividend and s32 divisor
+ */
+static inline s64 max2175_round_closest(s64 dividend, s32 divisor)
+{
+	if ((dividend > 0 && divisor > 0) || (dividend < 0 && divisor < 0))
+		return div_s64(dividend + divisor / 2, divisor);
+
+	return div_s64(dividend - divisor / 2, divisor);
+}
+
+static int max2175_set_nco_freq(struct max2175 *ctx, s32 nco_freq)
+{
+	s32 clock_rate = ctx->xtal_freq / ctx->decim_ratio;
+	u32 nco_reg, abs_nco_freq = abs(nco_freq);
+	s64 nco_val_desired;
+	int ret;
+
+	if (abs_nco_freq < clock_rate / 2) {
+		nco_val_desired = 2 * nco_freq;
+	} else {
+		nco_val_desired = 2 * (clock_rate - abs_nco_freq);
+		if (nco_freq < 0)
+			nco_val_desired = -nco_val_desired;
+	}
+
+	nco_reg = max2175_round_closest(nco_val_desired << 20, clock_rate);
+
+	if (nco_freq < 0)
+		nco_reg += 0x200000;
+
+	/* Check CSM is not busy */
+	ret = max2175_poll_csm_ready(ctx);
+	if (ret)
+		return ret;
+
+	mxm_dbg(ctx, "freq %d desired %lld reg %u\n",
+		nco_freq, nco_val_desired, nco_reg);
+
+	/* Write the calculated values to the appropriate registers */
+	max2175_write_bits(ctx, 7, 4, 0, (nco_reg >> 16) & 0x1f);
+	max2175_write(ctx, 8, nco_reg >> 8);
+	max2175_write(ctx, 9, nco_reg);
+	return ret;
+}
+
+static int max2175_set_rf_freq_non_am_bands(struct max2175 *ctx, u64 freq,
+					    u32 lo_pos)
+{
+	s64 adj_freq, low_if_freq;
+	int ret;
+
+	mxm_dbg(ctx, "rf_freq: non AM bands\n");
+
+	if (MAX2175_IS_FM_MODE(ctx))
+		low_if_freq = 128000;
+	else if (MAX2175_IS_FMHD_MODE(ctx))
+		low_if_freq = 228000;
+	else
+		return max2175_set_lo_freq(ctx, freq);
+
+	if (MAX2175_IS_BAND_VHF(ctx) == (lo_pos == MAX2175_LO_ABOVE_DESIRED))
+		adj_freq = freq + low_if_freq;
+	else
+		adj_freq = freq - low_if_freq;
+
+	ret = max2175_set_lo_freq(ctx, adj_freq);
+	if (ret)
+		return ret;
+
+	return max2175_set_nco_freq(ctx, -low_if_freq);
+}
+
+static int max2175_set_rf_freq(struct max2175 *ctx, u64 freq, u32 lo_pos)
+{
+	int ret;
+
+	if (MAX2175_IS_BAND_AM(ctx))
+		ret = max2175_set_nco_freq(ctx, freq);
+	else
+		ret = max2175_set_rf_freq_non_am_bands(ctx, freq, lo_pos);
+
+	mxm_dbg(ctx, "set_rf_freq: ret %d freq %llu\n", ret, freq);
+	return ret;
+}
+
+static int max2175_tune_rf_freq(struct max2175 *ctx, u64 freq, u32 hsls)
+{
+	int ret;
+
+	ret = max2175_set_rf_freq(ctx, freq, hsls);
+	if (ret)
+		return ret;
+
+	ret = max2175_csm_action(ctx, MAX2175_BUFFER_PLUS_PRESET_TUNE);
+	if (ret)
+		return ret;
+
+	mxm_dbg(ctx, "tune_rf_freq: old %u new %llu\n", ctx->freq, freq);
+	ctx->freq = freq;
+	return ret;
+}
+
+static void max2175_set_hsls(struct max2175 *ctx, u32 lo_pos)
+{
+	mxm_dbg(ctx, "set_hsls: lo_pos %u\n", lo_pos);
+
+	if ((lo_pos == MAX2175_LO_BELOW_DESIRED) == MAX2175_IS_BAND_VHF(ctx))
+		max2175_write_bit(ctx, 5, 4, 1);
+	else
+		max2175_write_bit(ctx, 5, 4, 0);
+}
+
+static void max2175_set_eu_rx_mode(struct max2175 *ctx, u32 rx_mode)
+{
+	switch (rx_mode) {
+	case MAX2175_EU_FM_1_2:
+		max2175_load_fmeu_1p2(ctx);
+		break;
+
+	case MAX2175_DAB_1_2:
+		max2175_load_dab_1p2(ctx);
+		break;
+	}
+	/* Master is the default setting */
+	if (!ctx->master)
+		max2175_write_bit(ctx, 30, 7, 1);
+}
+
+static void max2175_set_na_rx_mode(struct max2175 *ctx, u32 rx_mode)
+{
+	switch (rx_mode) {
+	case MAX2175_NA_FM_1_0:
+		max2175_load_fmna_1p0(ctx);
+		break;
+	case MAX2175_NA_FM_2_0:
+		max2175_load_fmna_2p0(ctx);
+		break;
+	}
+	/* Master is the default setting */
+	if (!ctx->master)
+		max2175_write_bit(ctx, 30, 7, 1);
+
+	ctx->decim_ratio = 27;
+
+	/* Load the Channel Filter Coefficients into channel filter bank #2 */
+	max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 0, ch_coeff_fmna);
+	max2175_set_filter_coeffs(ctx, MAX2175_EQ_MSEL, 0,
+				  eq_coeff_fmna1_ra02_m6db);
+}
+
+static int max2175_set_rx_mode(struct max2175 *ctx, u32 rx_mode)
+{
+	mxm_dbg(ctx, "set_rx_mode: %u am_hiz %u\n", rx_mode, ctx->am_hiz);
+	if (ctx->xtal_freq == MAX2175_EU_XTAL_FREQ)
+		max2175_set_eu_rx_mode(ctx, rx_mode);
+	else
+		max2175_set_na_rx_mode(ctx, rx_mode);
+
+	if (ctx->am_hiz) {
+		mxm_dbg(ctx, "setting AM HiZ related config\n");
+		max2175_write_bit(ctx, 50, 5, 1);
+		max2175_write_bit(ctx, 90, 7, 1);
+		max2175_write_bits(ctx, 73, 1, 0, 2);
+		max2175_write_bits(ctx, 80, 5, 0, 33);
+	}
+
+	/* Load BB filter trim values saved in ROM */
+	max2175_set_bbfilter(ctx);
+
+	/* Set HSLS */
+	max2175_set_hsls(ctx, ctx->hsls->cur.val);
+
+	/* Use i2s enable settings */
+	max2175_i2s_enable(ctx, ctx->i2s_en->cur.val);
+
+	ctx->mode_resolved = true;
+	return 0;
+}
+
+static int max2175_rx_mode_from_freq(struct max2175 *ctx, u32 freq, u32 *mode)
+{
+	unsigned int i;
+	int band = max2175_band_from_freq(freq);
+
+	/* Pick the first match always */
+	for (i = 0; i <= ctx->rx_mode->maximum; i++) {
+		if (ctx->rx_modes[i].band == band) {
+			*mode = i;
+			mxm_dbg(ctx, "rx_mode_from_freq: freq %u mode %d\n",
+				freq, *mode);
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+static bool max2175_freq_rx_mode_valid(struct max2175 *ctx,
+					 u32 mode, u32 freq)
+{
+	int band = max2175_band_from_freq(freq);
+
+	return (ctx->rx_modes[mode].band == band);
+}
+
+static void max2175_load_adc_presets(struct max2175 *ctx)
+{
+	unsigned int i, j;
+
+	for (i = 0; i < ARRAY_SIZE(adc_presets); i++)
+		for (j = 0; j < ARRAY_SIZE(adc_presets[0]); j++)
+			max2175_write(ctx, 146 + j + i * 55, adc_presets[i][j]);
+}
+
+static int max2175_init_power_manager(struct max2175 *ctx)
+{
+	int ret;
+
+	/* Execute on-chip power-up/calibration */
+	max2175_write_bit(ctx, 99, 2, 0);
+	usleep_range(1000, 1500);
+	max2175_write_bit(ctx, 99, 2, 1);
+
+	/* Wait for the power manager to finish. */
+	ret = max2175_poll_timeout(ctx, 69, 7, 7, 1, 50);
+	if (ret)
+		mxm_err(ctx, "init pm failed\n");
+	return ret;
+}
+
+static int max2175_recalibrate_adc(struct max2175 *ctx)
+{
+	int ret;
+
+	/* ADC Re-calibration */
+	max2175_write(ctx, 150, 0xff);
+	max2175_write(ctx, 205, 0xff);
+	max2175_write(ctx, 147, 0x20);
+	max2175_write(ctx, 147, 0x00);
+	max2175_write(ctx, 202, 0x20);
+	max2175_write(ctx, 202, 0x00);
+
+	ret = max2175_poll_timeout(ctx, 69, 4, 3, 3, 50);
+	if (ret)
+		mxm_err(ctx, "adc recalibration failed\n");
+	return ret;
+}
+
+static u8 max2175_read_rom(struct max2175 *ctx, u8 row)
+{
+	u8 data;
+
+	max2175_write_bit(ctx, 56, 4, 0);
+	max2175_write_bits(ctx, 56, 3, 0, row);
+
+	usleep_range(2000, 2500);
+	max2175_read(ctx, 58, &data);
+
+	max2175_write_bits(ctx, 56, 3, 0, 0);
+
+	mxm_dbg(ctx, "read_rom: row %d data 0x%02x\n", row, data);
+	return data;
+}
+
+static void max2175_load_from_rom(struct max2175 *ctx)
+{
+	u8 data = 0;
+
+	data = max2175_read_rom(ctx, 0);
+	ctx->rom_bbf_bw_am = data & 0x0f;
+	max2175_write_bits(ctx, 81, 3, 0, data >> 4);
+
+	data = max2175_read_rom(ctx, 1);
+	ctx->rom_bbf_bw_fm = data & 0x0f;
+	ctx->rom_bbf_bw_dab = data >> 4;
+
+	data = max2175_read_rom(ctx, 2);
+	max2175_write_bits(ctx, 82, 4, 0, data & 0x1f);
+	max2175_write_bits(ctx, 82, 7, 5, data >> 5);
+
+	data = max2175_read_rom(ctx, 3);
+	if (ctx->am_hiz) {
+		data &= 0x0f;
+		data |= max2175_read_rom(ctx, 7) & 0x40 >> 2;
+		if (!data)
+			data |= 2;
+	} else {
+		data = data & 0xf0 >> 4;
+		data |= max2175_read_rom(ctx, 7) & 0x80 >> 3;
+		if (!data)
+			data |= 30;
+	}
+	max2175_write_bits(ctx, 80, 5, 0, data + 31);
+
+	data = max2175_read_rom(ctx, 6);
+	max2175_write_bits(ctx, 81, 7, 6, data >> 6);
+}
+
+static void max2175_load_full_fm_eu_1p0(struct max2175 *ctx)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(full_fm_eu_1p0); i++)
+		max2175_write(ctx, i + 1, full_fm_eu_1p0[i]);
+
+	usleep_range(5000, 5500);
+	ctx->decim_ratio = 36;
+}
+
+static void max2175_load_full_fm_na_1p0(struct max2175 *ctx)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(full_fm_na_1p0); i++)
+		max2175_write(ctx, i + 1, full_fm_na_1p0[i]);
+
+	usleep_range(5000, 5500);
+	ctx->decim_ratio = 27;
+}
+
+static int max2175_core_init(struct max2175 *ctx, u32 refout_bits)
+{
+	int ret;
+
+	/* MAX2175 uses 36.864MHz clock for EU & 40.154MHz for NA region */
+	if (ctx->xtal_freq == MAX2175_EU_XTAL_FREQ)
+		max2175_load_full_fm_eu_1p0(ctx);
+	else
+		max2175_load_full_fm_na_1p0(ctx);
+
+	/* The default settings assume master */
+	if (!ctx->master)
+		max2175_write_bit(ctx, 30, 7, 1);
+
+	mxm_dbg(ctx, "refout_bits %u\n", refout_bits);
+
+	/* Set REFOUT */
+	max2175_write_bits(ctx, 56, 7, 5, refout_bits);
+
+	/* ADC Reset */
+	max2175_write_bit(ctx, 99, 1, 0);
+	usleep_range(1000, 1500);
+	max2175_write_bit(ctx, 99, 1, 1);
+
+	/* Load ADC preset values */
+	max2175_load_adc_presets(ctx);
+
+	/* Initialize the power management state machine */
+	ret = max2175_init_power_manager(ctx);
+	if (ret)
+		return ret;
+
+	/* Recalibrate ADC */
+	ret = max2175_recalibrate_adc(ctx);
+	if (ret)
+		return ret;
+
+	/* Load ROM values to appropriate registers */
+	max2175_load_from_rom(ctx);
+
+	if (ctx->xtal_freq == MAX2175_EU_XTAL_FREQ) {
+		/* Load FIR coefficients into bank 0 */
+		max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 0,
+					  ch_coeff_fmeu);
+		max2175_set_filter_coeffs(ctx, MAX2175_EQ_MSEL, 0,
+					  eq_coeff_fmeu1_ra02_m6db);
+	} else {
+		/* Load FIR coefficients into bank 0 */
+		max2175_set_filter_coeffs(ctx, MAX2175_CH_MSEL, 0,
+					  ch_coeff_fmna);
+		max2175_set_filter_coeffs(ctx, MAX2175_EQ_MSEL, 0,
+					  eq_coeff_fmna1_ra02_m6db);
+	}
+	mxm_dbg(ctx, "core initialized\n");
+	return 0;
+}
+
+static void max2175_s_ctrl_rx_mode(struct max2175 *ctx, u32 rx_mode)
+{
+	/* Load mode. Range check already done */
+	max2175_set_rx_mode(ctx, rx_mode);
+
+	mxm_dbg(ctx, "s_ctrl_rx_mode: %u curr freq %u\n", rx_mode, ctx->freq);
+
+	/* Check if current freq valid for mode & update */
+	if (max2175_freq_rx_mode_valid(ctx, rx_mode, ctx->freq))
+		max2175_tune_rf_freq(ctx, ctx->freq, ctx->hsls->cur.val);
+	else
+		/* Use default freq of mode if current freq is not valid */
+		max2175_tune_rf_freq(ctx, ctx->rx_modes[rx_mode].freq,
+				     ctx->hsls->cur.val);
+}
+
+static int max2175_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct max2175 *ctx = max2175_from_ctrl_hdl(ctrl->handler);
+
+	mxm_dbg(ctx, "s_ctrl: id 0x%x, val %u\n", ctrl->id, ctrl->val);
+	switch (ctrl->id) {
+	case V4L2_CID_MAX2175_I2S_ENABLE:
+		max2175_i2s_enable(ctx, ctrl->val);
+		break;
+	case V4L2_CID_MAX2175_HSLS:
+		max2175_set_hsls(ctx, ctx->hsls->val);
+		break;
+	case V4L2_CID_MAX2175_RX_MODE:
+		max2175_s_ctrl_rx_mode(ctx, ctrl->val);
+		break;
+	}
+
+	return 0;
+}
+
+static u32 max2175_get_lna_gain(struct max2175 *ctx)
+{
+	enum max2175_band band = max2175_read_bits(ctx, 5, 1, 0);
+
+	switch (band) {
+	case MAX2175_BAND_AM:
+		return max2175_read_bits(ctx, 51, 3, 0);
+	case MAX2175_BAND_FM:
+		return max2175_read_bits(ctx, 50, 3, 0);
+	case MAX2175_BAND_VHF:
+		return max2175_read_bits(ctx, 52, 5, 0);
+	default:
+		return 0;
+	}
+}
+
+static int max2175_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct max2175 *ctx = max2175_from_ctrl_hdl(ctrl->handler);
+
+	switch (ctrl->id) {
+	case V4L2_CID_RF_TUNER_LNA_GAIN:
+		ctrl->val = max2175_get_lna_gain(ctx);
+		break;
+	case V4L2_CID_RF_TUNER_IF_GAIN:
+		ctrl->val = max2175_read_bits(ctx, 49, 4, 0);
+		break;
+	case V4L2_CID_RF_TUNER_PLL_LOCK:
+		ctrl->val = (max2175_read_bits(ctx, 60, 7, 6) == 3);
+		break;
+	}
+	mxm_dbg(ctx, "g_volatile_ctrl: id 0x%x val %d\n", ctrl->id, ctrl->val);
+	return 0;
+};
+
+static int max2175_set_freq_and_mode(struct max2175 *ctx, u32 freq)
+{
+	u32 rx_mode;
+	int ret;
+
+	/* Get band from frequency */
+	ret = max2175_rx_mode_from_freq(ctx, freq, &rx_mode);
+	if (ret)
+		return ret;
+
+	mxm_dbg(ctx, "set_freq_and_mode: freq %u rx_mode %d\n", freq, rx_mode);
+
+	/* Load mode */
+	max2175_set_rx_mode(ctx, rx_mode);
+	ctx->rx_mode->cur.val = rx_mode;
+
+	/* Tune to the new freq given */
+	return max2175_tune_rf_freq(ctx, freq, ctx->hsls->cur.val);
+}
+
+static int max2175_s_frequency(struct v4l2_subdev *sd,
+			       const struct v4l2_frequency *vf)
+{
+	struct max2175 *ctx = max2175_from_sd(sd);
+	u32 freq;
+	int ret = 0;
+
+	mxm_dbg(ctx, "s_freq: new %u curr %u, mode_resolved %d\n",
+		vf->frequency, ctx->freq, ctx->mode_resolved);
+
+	if (vf->tuner != 0)
+		return -EINVAL;
+
+	freq = clamp(vf->frequency, ctx->bands_rf->rangelow,
+		     ctx->bands_rf->rangehigh);
+
+	/* Check new freq valid for rx_mode if already resolved */
+	if (ctx->mode_resolved &&
+	    max2175_freq_rx_mode_valid(ctx, ctx->rx_mode->cur.val, freq))
+		ret = max2175_tune_rf_freq(ctx, freq, ctx->hsls->cur.val);
+	else
+		/* Find default rx_mode for freq and tune to it */
+		ret = max2175_set_freq_and_mode(ctx, freq);
+
+	mxm_dbg(ctx, "s_freq: ret %d curr %u mode_resolved %d mode %u\n",
+		ret, ctx->freq, ctx->mode_resolved, ctx->rx_mode->cur.val);
+	return ret;
+}
+
+static int max2175_g_frequency(struct v4l2_subdev *sd,
+			       struct v4l2_frequency *vf)
+{
+	struct max2175 *ctx = max2175_from_sd(sd);
+	int ret = 0;
+
+	if (vf->tuner != 0)
+		return -EINVAL;
+
+	/* RF freq */
+	vf->type = V4L2_TUNER_RF;
+	vf->frequency = ctx->freq;
+	return ret;
+}
+
+static int max2175_enum_freq_bands(struct v4l2_subdev *sd,
+			    struct v4l2_frequency_band *band)
+{
+	struct max2175 *ctx = max2175_from_sd(sd);
+
+	if (band->tuner != 0 || band->index != 0)
+		return -EINVAL;
+
+	*band = *ctx->bands_rf;
+	return 0;
+}
+
+static int max2175_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+	struct max2175 *ctx = max2175_from_sd(sd);
+
+	if (vt->index > 0)
+		return -EINVAL;
+
+	strlcpy(vt->name, "RF", sizeof(vt->name));
+	vt->type = V4L2_TUNER_RF;
+	vt->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
+	vt->rangelow = ctx->bands_rf->rangelow;
+	vt->rangehigh = ctx->bands_rf->rangehigh;
+	return 0;
+}
+
+static int max2175_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt)
+{
+	/* Check tuner index is valid */
+	if (vt->index > 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+static const struct v4l2_subdev_tuner_ops max2175_tuner_ops = {
+	.s_frequency = max2175_s_frequency,
+	.g_frequency = max2175_g_frequency,
+	.enum_freq_bands = max2175_enum_freq_bands,
+	.g_tuner = max2175_g_tuner,
+	.s_tuner = max2175_s_tuner,
+};
+
+static const struct v4l2_subdev_ops max2175_ops = {
+	.tuner = &max2175_tuner_ops,
+};
+
+static const struct v4l2_ctrl_ops max2175_ctrl_ops = {
+	.s_ctrl = max2175_s_ctrl,
+	.g_volatile_ctrl = max2175_g_volatile_ctrl,
+};
+
+static const struct v4l2_ctrl_config max2175_i2s_en = {
+	.ops = &max2175_ctrl_ops,
+	.id = V4L2_CID_MAX2175_I2S_ENABLE,
+	.name = "I2S Enable",
+	.type = V4L2_CTRL_TYPE_BOOLEAN,
+	.min = 0,
+	.max = 1,
+	.step = 1,
+	.def = 1,
+};
+
+/*
+ * HSLS value control LO freq adjacent location configuration.
+ * Refer to Documentation/media/v4l-drivers/max2175 for more details.
+ */
+static const struct v4l2_ctrl_config max2175_hsls = {
+	.ops = &max2175_ctrl_ops,
+	.id = V4L2_CID_MAX2175_HSLS,
+	.name = "HSLS above/below desired",
+	.type = V4L2_CTRL_TYPE_INTEGER,
+	.min = 0,
+	.max = 1,
+	.step = 1,
+	.def = 1,
+};
+
+/*
+ * Rx modes below are a set of preset configurations that decides the tuner's
+ * sck and sample rate of transmission. They are separate for EU & NA regions.
+ * Refer to Documentation/media/v4l-drivers/max2175 for more details.
+ */
+static const char * const max2175_ctrl_eu_rx_modes[] = {
+	[MAX2175_EU_FM_1_2]	= "EU FM 1.2",
+	[MAX2175_DAB_1_2]	= "DAB 1.2",
+};
+
+static const char * const max2175_ctrl_na_rx_modes[] = {
+	[MAX2175_NA_FM_1_0]	= "NA FM 1.0",
+	[MAX2175_NA_FM_2_0]	= "NA FM 2.0",
+};
+
+static const struct v4l2_ctrl_config max2175_eu_rx_mode = {
+	.ops = &max2175_ctrl_ops,
+	.id = V4L2_CID_MAX2175_RX_MODE,
+	.name = "RX MODE",
+	.type = V4L2_CTRL_TYPE_MENU,
+	.max = ARRAY_SIZE(max2175_ctrl_eu_rx_modes) - 1,
+	.def = 0,
+	.qmenu = max2175_ctrl_eu_rx_modes,
+};
+
+static const struct v4l2_ctrl_config max2175_na_rx_mode = {
+	.ops = &max2175_ctrl_ops,
+	.id = V4L2_CID_MAX2175_RX_MODE,
+	.name = "RX MODE",
+	.type = V4L2_CTRL_TYPE_MENU,
+	.max = ARRAY_SIZE(max2175_ctrl_na_rx_modes) - 1,
+	.def = 0,
+	.qmenu = max2175_ctrl_na_rx_modes,
+};
+
+static int max2175_refout_load_to_bits(struct i2c_client *client, u32 load,
+				       u32 *bits)
+{
+	if (load >= 0 && load <= 40)
+		*bits = load / 10;
+	else if (load >= 60 && load <= 70)
+		*bits = load / 10 - 1;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int max2175_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	bool master = true, am_hiz = false;
+	u32 refout_load, refout_bits = 0;	/* REFOUT disabled */
+	struct v4l2_ctrl_handler *hdl;
+	struct device_node *np;
+	struct v4l2_subdev *sd;
+	struct regmap *regmap;
+	struct max2175 *ctx;
+	struct clk *clk;
+	int ret;
+
+	/* Parse DT properties */
+	np = of_parse_phandle(client->dev.of_node, "slave", 0);
+	if (np)
+		master = false;			/* Slave tuner */
+
+	if (of_find_property(client->dev.of_node, "maxim,am-hiz", NULL))
+		am_hiz = true;
+
+	if (!of_property_read_u32(client->dev.of_node, "maxim,refout-load-pF",
+				  &refout_load)) {
+		ret = max2175_refout_load_to_bits(client, refout_load,
+						  &refout_bits);
+		if (ret) {
+			dev_err(&client->dev, "invalid refout_load %u\n",
+				refout_load);
+			return -EINVAL;
+		}
+	}
+
+	clk = devm_clk_get(&client->dev, "xtal");
+	if (IS_ERR(clk)) {
+		ret = PTR_ERR(clk);
+		dev_err(&client->dev, "cannot get xtal clock %d\n", ret);
+		return -ENODEV;
+	}
+
+	regmap = devm_regmap_init_i2c(client, &max2175_regmap_config);
+	if (IS_ERR(regmap)) {
+		ret = PTR_ERR(regmap);
+		dev_err(&client->dev, "regmap init failed %d\n", ret);
+		return -ENODEV;
+	}
+
+	/* Alloc tuner context */
+	ctx = devm_kzalloc(&client->dev, sizeof(*ctx), GFP_KERNEL);
+	if (ctx == NULL)
+		return -ENOMEM;
+
+	sd = &ctx->sd;
+	ctx->master = master;
+	ctx->am_hiz = am_hiz;
+	ctx->mode_resolved = false;
+	ctx->regmap = regmap;
+	ctx->xtal_freq = clk_get_rate(clk);
+	dev_info(&client->dev, "xtal freq %luHz\n", ctx->xtal_freq);
+
+	v4l2_i2c_subdev_init(sd, client, &max2175_ops);
+	ctx->client = client;
+
+	sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	/* Controls */
+	hdl = &ctx->ctrl_hdl;
+	ret = v4l2_ctrl_handler_init(hdl, 7);
+	if (ret) {
+		dev_err(&client->dev, "ctrl handler init failed\n");
+		goto err;
+	}
+
+	ctx->lna_gain = v4l2_ctrl_new_std(hdl, &max2175_ctrl_ops,
+					  V4L2_CID_RF_TUNER_LNA_GAIN,
+					  0, 63, 1, 0);
+	ctx->lna_gain->flags |= (V4L2_CTRL_FLAG_VOLATILE |
+				 V4L2_CTRL_FLAG_READ_ONLY);
+	ctx->if_gain = v4l2_ctrl_new_std(hdl, &max2175_ctrl_ops,
+					 V4L2_CID_RF_TUNER_IF_GAIN,
+					 0, 31, 1, 0);
+	ctx->if_gain->flags |= (V4L2_CTRL_FLAG_VOLATILE |
+				V4L2_CTRL_FLAG_READ_ONLY);
+	ctx->pll_lock = v4l2_ctrl_new_std(hdl, &max2175_ctrl_ops,
+					  V4L2_CID_RF_TUNER_PLL_LOCK,
+					  0, 1, 1, 0);
+	ctx->pll_lock->flags |= (V4L2_CTRL_FLAG_VOLATILE |
+				 V4L2_CTRL_FLAG_READ_ONLY);
+	ctx->i2s_en = v4l2_ctrl_new_custom(hdl, &max2175_i2s_en, NULL);
+	ctx->hsls = v4l2_ctrl_new_custom(hdl, &max2175_hsls, NULL);
+
+	if (ctx->xtal_freq == MAX2175_EU_XTAL_FREQ) {
+		ctx->rx_mode = v4l2_ctrl_new_custom(hdl,
+						    &max2175_eu_rx_mode, NULL);
+		ctx->rx_modes = eu_rx_modes;
+		ctx->bands_rf = &eu_bands_rf;
+	} else {
+		ctx->rx_mode = v4l2_ctrl_new_custom(hdl,
+						    &max2175_na_rx_mode, NULL);
+		ctx->rx_modes = na_rx_modes;
+		ctx->bands_rf = &na_bands_rf;
+	}
+	ctx->sd.ctrl_handler = &ctx->ctrl_hdl;
+
+	/* Set the defaults */
+	ctx->freq = ctx->bands_rf->rangelow;
+
+	/* Register subdev */
+	ret = v4l2_async_register_subdev(sd);
+	if (ret) {
+		dev_err(&client->dev, "register subdev failed\n");
+		goto err_reg;
+	}
+
+	/* Initialize device */
+	ret = max2175_core_init(ctx, refout_bits);
+	if (ret)
+		goto err_init;
+
+	ret = v4l2_ctrl_handler_setup(hdl);
+	if (ret)
+		goto err_init;
+
+	dev_info(&client->dev, "probed\n");
+	return 0;
+
+err_init:
+	v4l2_async_unregister_subdev(sd);
+err_reg:
+	v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
+err:
+	return ret;
+}
+
+static int max2175_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct max2175 *ctx = max2175_from_sd(sd);
+
+	v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
+	v4l2_async_unregister_subdev(sd);
+	dev_info(&client->dev, "removed\n");
+	return 0;
+}
+
+static const struct i2c_device_id max2175_id[] = {
+	{ DRIVER_NAME, 0},
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, max2175_id);
+
+static const struct of_device_id max2175_of_ids[] = {
+	{ .compatible = "maxim, max2175", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, max2175_of_ids);
+
+static struct i2c_driver max2175_driver = {
+	.driver = {
+		.name	= DRIVER_NAME,
+		.of_match_table = max2175_of_ids,
+	},
+	.probe		= max2175_probe,
+	.remove		= max2175_remove,
+	.id_table	= max2175_id,
+};
+
+module_i2c_driver(max2175_driver);
+
+MODULE_DESCRIPTION("Maxim MAX2175 RF to Bits tuner driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>");
diff --git a/drivers/media/i2c/max2175/max2175.h b/drivers/media/i2c/max2175/max2175.h
new file mode 100644
index 0000000..3c70f49
--- /dev/null
+++ b/drivers/media/i2c/max2175/max2175.h
@@ -0,0 +1,108 @@
+/*
+ * Maxim Integrated MAX2175 RF to Bits tuner driver
+ *
+ * This driver & most of the hard coded values are based on the reference
+ * application delivered by Maxim for this device.
+ *
+ * Copyright (C) 2016 Maxim Integrated Products
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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 __MAX2175_H__
+#define __MAX2175_H__
+
+#define MAX2175_EU_XTAL_FREQ	36864000	/* In Hz */
+#define MAX2175_NA_XTAL_FREQ	40186125	/* In Hz */
+
+enum max2175_region {
+	MAX2175_REGION_EU = 0,	/* Europe */
+	MAX2175_REGION_NA,	/* North America */
+};
+
+
+enum max2175_band {
+	MAX2175_BAND_AM = 0,
+	MAX2175_BAND_FM,
+	MAX2175_BAND_VHF,
+	MAX2175_BAND_L,
+};
+
+enum max2175_eu_mode {
+	/* EU modes */
+	MAX2175_EU_FM_1_2 = 0,
+	MAX2175_DAB_1_2,
+
+	/* Other possible modes to add in future
+	 * MAX2175_DAB_1_0,
+	 * MAX2175_DAB_1_3,
+	 * MAX2175_EU_FM_2_2,
+	 * MAX2175_EU_FMHD_4_0,
+	 * MAX2175_EU_AM_1_0,
+	 * MAX2175_EU_AM_2_2,
+	 */
+};
+
+enum max2175_na_mode {
+	/* NA modes */
+	MAX2175_NA_FM_1_0 = 0,
+	MAX2175_NA_FM_2_0,
+
+	/* Other possible modes to add in future
+	 * MAX2175_NA_FMHD_1_0,
+	 * MAX2175_NA_FMHD_1_2,
+	 * MAX2175_NA_AM_1_0,
+	 * MAX2175_NA_AM_1_2,
+	 */
+};
+
+/* Supported I2S modes */
+enum {
+	MAX2175_I2S_MODE0 = 0,
+	MAX2175_I2S_MODE1,
+	MAX2175_I2S_MODE2,
+	MAX2175_I2S_MODE3,
+	MAX2175_I2S_MODE4,
+};
+
+/* Coefficient table groups */
+enum {
+	MAX2175_CH_MSEL = 0,
+	MAX2175_EQ_MSEL,
+	MAX2175_AA_MSEL,
+};
+
+/* HSLS LO injection polarity */
+enum {
+	MAX2175_LO_BELOW_DESIRED = 0,
+	MAX2175_LO_ABOVE_DESIRED,
+};
+
+/* Channel FSM modes */
+enum max2175_csm_mode {
+	MAX2175_LOAD_TO_BUFFER = 0,
+	MAX2175_PRESET_TUNE,
+	MAX2175_SEARCH,
+	MAX2175_AF_UPDATE,
+	MAX2175_JUMP_FAST_TUNE,
+	MAX2175_CHECK,
+	MAX2175_LOAD_AND_SWAP,
+	MAX2175_END,
+	MAX2175_BUFFER_PLUS_PRESET_TUNE,
+	MAX2175_BUFFER_PLUS_SEARCH,
+	MAX2175_BUFFER_PLUS_AF_UPDATE,
+	MAX2175_BUFFER_PLUS_JUMP_FAST_TUNE,
+	MAX2175_BUFFER_PLUS_CHECK,
+	MAX2175_BUFFER_PLUS_LOAD_AND_SWAP,
+	MAX2175_NO_ACTION
+};
+
+#endif /* __MAX2175_H__ */
-- 
1.9.1

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

* [PATCH v2 4/7] media: Add new SDR formats PC16, PC18 & PC20
  2016-12-21  8:10 ` [PATCH v2 0/7] Add V4L2 SDR (DRIF & MAX2175) driver Ramesh Shanmugasundaram
                     ` (2 preceding siblings ...)
  2016-12-21  8:10   ` [PATCH v2 3/7] media: i2c: max2175: Add MAX2175 support Ramesh Shanmugasundaram
@ 2016-12-21  8:10   ` Ramesh Shanmugasundaram
       [not found]   ` <1482307838-47415-1-git-send-email-ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>
                     ` (2 subsequent siblings)
  6 siblings, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-12-21  8:10 UTC (permalink / raw)
  To: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope
  Cc: chris.paterson2, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc, Ramesh Shanmugasundaram

This patch adds support for the three new SDR formats. These formats
were prefixed with "planar" indicating I & Q data are not interleaved
as in other formats. Here, I & Q data constitutes the top half and bottom
half of the received buffer.

V4L2_SDR_FMT_PCU16BE - 14-bit complex (I & Q) unsigned big-endian sample
inside 16-bit. V4L2 FourCC: PC16

V4L2_SDR_FMT_PCU18BE - 16-bit complex (I & Q) unsigned big-endian sample
inside 18-bit. V4L2 FourCC: PC18

V4L2_SDR_FMT_PCU20BE - 18-bit complex (I & Q) unsigned big-endian sample
inside 20-bit. V4L2 FourCC: PC20

Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>
---
 drivers/media/v4l2-core/v4l2-ioctl.c | 3 +++
 include/uapi/linux/videodev2.h       | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 0c3f238..fdf6c913 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1213,6 +1213,9 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
 	case V4L2_SDR_FMT_CS8:		descr = "Complex S8"; break;
 	case V4L2_SDR_FMT_CS14LE:	descr = "Complex S14LE"; break;
 	case V4L2_SDR_FMT_RU12LE:	descr = "Real U12LE"; break;
+	case V4L2_SDR_FMT_PCU16BE:	descr = "Planar Complex U16BE"; break;
+	case V4L2_SDR_FMT_PCU18BE:	descr = "Planar Complex U18BE"; break;
+	case V4L2_SDR_FMT_PCU20BE:	descr = "Planar Complex U20BE"; break;
 	case V4L2_TCH_FMT_DELTA_TD16:	descr = "16-bit signed deltas"; break;
 	case V4L2_TCH_FMT_DELTA_TD08:	descr = "8-bit signed deltas"; break;
 	case V4L2_TCH_FMT_TU16:		descr = "16-bit unsigned touch data"; break;
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 46e8a2e3..26a31c8 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -669,6 +669,9 @@ struct v4l2_pix_format {
 #define V4L2_SDR_FMT_CS8          v4l2_fourcc('C', 'S', '0', '8') /* complex s8 */
 #define V4L2_SDR_FMT_CS14LE       v4l2_fourcc('C', 'S', '1', '4') /* complex s14le */
 #define V4L2_SDR_FMT_RU12LE       v4l2_fourcc('R', 'U', '1', '2') /* real u12le */
+#define V4L2_SDR_FMT_PCU16BE	  v4l2_fourcc('P', 'C', '1', '6') /* planar complex u16be */
+#define V4L2_SDR_FMT_PCU18BE	  v4l2_fourcc('P', 'C', '1', '8') /* planar complex u18be */
+#define V4L2_SDR_FMT_PCU20BE	  v4l2_fourcc('P', 'C', '2', '0') /* planar complex u20be */
 
 /* Touch formats - used for Touch devices */
 #define V4L2_TCH_FMT_DELTA_TD16	v4l2_fourcc('T', 'D', '1', '6') /* 16-bit signed deltas */
-- 
1.9.1

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

* [PATCH v2 5/7] doc_rst: media: New SDR formats PC16, PC18 & PC20
  2016-12-21  8:10 ` [PATCH v2 0/7] Add V4L2 SDR (DRIF & MAX2175) driver Ramesh Shanmugasundaram
@ 2016-12-21  8:10       ` Ramesh Shanmugasundaram
  2016-12-21  8:10   ` [PATCH v2 2/7] dt-bindings: media: Add MAX2175 binding description Ramesh Shanmugasundaram
                         ` (5 subsequent siblings)
  6 siblings, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-12-21  8:10 UTC (permalink / raw)
  To: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	mchehab-DgEjT+Ai2ygdnm+yROfE0A, hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
	sakari.ailus-VuQAYsv1563Yd54FQh9/CA, crope-X3B1VOXEql0
  Cc: chris.paterson2-zM6kxYcvzFBBDgjK7y7TUQ,
	laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
	geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ,
	linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA,
	Ramesh Shanmugasundaram

This patch adds documentation for the three new SDR formats

V4L2_SDR_FMT_PCU16BE
V4L2_SDR_FMT_PCU18BE
V4L2_SDR_FMT_PCU20BE

Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>
---
 .../media/uapi/v4l/pixfmt-sdr-pcu16be.rst          | 55 ++++++++++++++++++++++
 .../media/uapi/v4l/pixfmt-sdr-pcu18be.rst          | 55 ++++++++++++++++++++++
 .../media/uapi/v4l/pixfmt-sdr-pcu20be.rst          | 55 ++++++++++++++++++++++
 Documentation/media/uapi/v4l/sdr-formats.rst       |  3 ++
 4 files changed, 168 insertions(+)
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-pcu16be.rst
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-pcu18be.rst
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-pcu20be.rst

diff --git a/Documentation/media/uapi/v4l/pixfmt-sdr-pcu16be.rst b/Documentation/media/uapi/v4l/pixfmt-sdr-pcu16be.rst
new file mode 100644
index 0000000..2de1b1a
--- /dev/null
+++ b/Documentation/media/uapi/v4l/pixfmt-sdr-pcu16be.rst
@@ -0,0 +1,55 @@
+.. -*- coding: utf-8; mode: rst -*-
+
+.. _V4L2-SDR-FMT-PCU16BE:
+
+******************************
+V4L2_SDR_FMT_PCU16BE ('PC16')
+******************************
+
+Planar complex unsigned 16-bit big endian IQ sample
+
+Description
+===========
+
+This format contains a sequence of complex number samples. Each complex
+number consist of two parts called In-phase and Quadrature (IQ). Both I
+and Q are represented as a 16 bit unsigned big endian number stored in
+32 bit space. The remaining unused bits within the 32 bit space will be
+padded with 0. I value starts first and Q value starts at an offset
+equalling half of the buffer size (i.e.) offset = buffersize/2. Out of
+the 16 bits, bit 15:2 (14 bit) is data and bit 1:0 (2 bit) can be any
+value.
+
+**Byte Order.**
+Each cell is one byte.
+
+.. flat-table::
+    :header-rows:  1
+    :stub-columns: 0
+
+    * -  Offset:
+      -  Byte B0
+      -  Byte B1
+      -  Byte B2
+      -  Byte B3
+    * -  start + 0:
+      -  I'\ :sub:`0[13:6]`
+      -  I'\ :sub:`0[5:0]; B1[1:0]=pad`
+      -  pad
+      -  pad
+    * -  start + 4:
+      -  I'\ :sub:`1[13:6]`
+      -  I'\ :sub:`1[5:0]; B1[1:0]=pad`
+      -  pad
+      -  pad
+    * -  ...
+    * - start + offset:
+      -  Q'\ :sub:`0[13:6]`
+      -  Q'\ :sub:`0[5:0]; B1[1:0]=pad`
+      -  pad
+      -  pad
+    * - start + offset + 4:
+      -  Q'\ :sub:`1[13:6]`
+      -  Q'\ :sub:`1[5:0]; B1[1:0]=pad`
+      -  pad
+      -  pad
diff --git a/Documentation/media/uapi/v4l/pixfmt-sdr-pcu18be.rst b/Documentation/media/uapi/v4l/pixfmt-sdr-pcu18be.rst
new file mode 100644
index 0000000..da8b26b
--- /dev/null
+++ b/Documentation/media/uapi/v4l/pixfmt-sdr-pcu18be.rst
@@ -0,0 +1,55 @@
+.. -*- coding: utf-8; mode: rst -*-
+
+.. _V4L2-SDR-FMT-PCU18BE:
+
+******************************
+V4L2_SDR_FMT_PCU18BE ('PC18')
+******************************
+
+Planar complex unsigned 18-bit big endian IQ sample
+
+Description
+===========
+
+This format contains a sequence of complex number samples. Each complex
+number consist of two parts called In-phase and Quadrature (IQ). Both I
+and Q are represented as a 18 bit unsigned big endian number stored in
+32 bit space. The remaining unused bits within the 32 bit space will be
+padded with 0. I value starts first and Q value starts at an offset
+equalling half of the buffer size (i.e.) offset = buffersize/2. Out of
+the 18 bits, bit 17:2 (16 bit) is data and bit 1:0 (2 bit) can be any
+value.
+
+**Byte Order.**
+Each cell is one byte.
+
+.. flat-table::
+    :header-rows:  1
+    :stub-columns: 0
+
+    * -  Offset:
+      -  Byte B0
+      -  Byte B1
+      -  Byte B2
+      -  Byte B3
+    * -  start + 0:
+      -  I'\ :sub:`0[17:10]`
+      -  I'\ :sub:`0[9:2]`
+      -  I'\ :sub:`0[1:0]; B2[5:0]=pad`
+      -  pad
+    * -  start + 4:
+      -  I'\ :sub:`1[17:10]`
+      -  I'\ :sub:`1[9:2]`
+      -  I'\ :sub:`1[1:0]; B2[5:0]=pad`
+      -  pad
+    * -  ...
+    * - start + offset:
+      -  Q'\ :sub:`0[17:10]`
+      -  Q'\ :sub:`0[9:2]`
+      -  Q'\ :sub:`0[1:0]; B2[5:0]=pad`
+      -  pad
+    * - start + offset + 4:
+      -  Q'\ :sub:`1[17:10]`
+      -  Q'\ :sub:`1[9:2]`
+      -  Q'\ :sub:`1[1:0]; B2[5:0]=pad`
+      -  pad
diff --git a/Documentation/media/uapi/v4l/pixfmt-sdr-pcu20be.rst b/Documentation/media/uapi/v4l/pixfmt-sdr-pcu20be.rst
new file mode 100644
index 0000000..b073be5
--- /dev/null
+++ b/Documentation/media/uapi/v4l/pixfmt-sdr-pcu20be.rst
@@ -0,0 +1,55 @@
+.. -*- coding: utf-8; mode: rst -*-
+.. _V4L2-SDR-FMT-PCU20BE:
+
+******************************
+V4L2_SDR_FMT_PCU20BE ('PC20')
+******************************
+
+Planar complex unsigned 20-bit big endian IQ sample
+
+Description
+===========
+
+This format contains a sequence of complex number samples. Each complex
+number consist of two parts called In-phase and Quadrature (IQ). Both I
+and Q are represented as a 20 bit unsigned big endian number stored in
+32 bit space. The remaining unused bits within the 32 bit space will be
+padded with 0. I value starts first and Q value starts at an offset
+equalling half of the buffer size (i.e.) offset = buffersize/2. Out of
+the 20 bits, bit 19:2 (18 bit) is data and bit 1:0 (2 bit) can be any
+value.
+
+**Byte Order.**
+Each cell is one byte.
+
+.. flat-table::
+    :header-rows:  1
+    :stub-columns: 0
+
+    * -  Offset:
+      -  Byte B0
+      -  Byte B1
+      -  Byte B2
+      -  Byte B3
+    * -  start + 0:
+      -  I'\ :sub:`0[19:12]`
+      -  I'\ :sub:`0[11:4]`
+      -  I'\ :sub:`0[3:0]; B2[3:0]=pad`
+      -  pad
+    * -  start + 4:
+      -  I'\ :sub:`1[19:12]`
+      -  I'\ :sub:`1[11:4]`
+      -  I'\ :sub:`1[3:0]; B2[3:0]=pad`
+      -  pad
+    * -  ...
+    * - start + offset:
+      -  Q'\ :sub:`0[19:12]`
+      -  Q'\ :sub:`0[11:4]`
+      -  Q'\ :sub:`0[3:0]; B2[3:0]=pad`
+      -  pad
+    * - start + offset + 4:
+      -  Q'\ :sub:`1[19:12]`
+      -  Q'\ :sub:`1[11:4]`
+      -  Q'\ :sub:`1[3:0]; B2[3:0]=pad`
+      -  pad
+
diff --git a/Documentation/media/uapi/v4l/sdr-formats.rst b/Documentation/media/uapi/v4l/sdr-formats.rst
index f863c08..2037f5b 100644
--- a/Documentation/media/uapi/v4l/sdr-formats.rst
+++ b/Documentation/media/uapi/v4l/sdr-formats.rst
@@ -17,3 +17,6 @@ These formats are used for :ref:`SDR <sdr>` interface only.
     pixfmt-sdr-cs08
     pixfmt-sdr-cs14le
     pixfmt-sdr-ru12le
+    pixfmt-sdr-pcu16be
+    pixfmt-sdr-pcu18be
+    pixfmt-sdr-pcu20be
-- 
1.9.1

--
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 related	[flat|nested] 60+ messages in thread

* [PATCH v2 5/7] doc_rst: media: New SDR formats PC16, PC18 & PC20
@ 2016-12-21  8:10       ` Ramesh Shanmugasundaram
  0 siblings, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-12-21  8:10 UTC (permalink / raw)
  To: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope
  Cc: chris.paterson2, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc, Ramesh Shanmugasundaram

This patch adds documentation for the three new SDR formats

V4L2_SDR_FMT_PCU16BE
V4L2_SDR_FMT_PCU18BE
V4L2_SDR_FMT_PCU20BE

Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>
---
 .../media/uapi/v4l/pixfmt-sdr-pcu16be.rst          | 55 ++++++++++++++++++++++
 .../media/uapi/v4l/pixfmt-sdr-pcu18be.rst          | 55 ++++++++++++++++++++++
 .../media/uapi/v4l/pixfmt-sdr-pcu20be.rst          | 55 ++++++++++++++++++++++
 Documentation/media/uapi/v4l/sdr-formats.rst       |  3 ++
 4 files changed, 168 insertions(+)
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-pcu16be.rst
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-pcu18be.rst
 create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-pcu20be.rst

diff --git a/Documentation/media/uapi/v4l/pixfmt-sdr-pcu16be.rst b/Documentation/media/uapi/v4l/pixfmt-sdr-pcu16be.rst
new file mode 100644
index 0000000..2de1b1a
--- /dev/null
+++ b/Documentation/media/uapi/v4l/pixfmt-sdr-pcu16be.rst
@@ -0,0 +1,55 @@
+.. -*- coding: utf-8; mode: rst -*-
+
+.. _V4L2-SDR-FMT-PCU16BE:
+
+******************************
+V4L2_SDR_FMT_PCU16BE ('PC16')
+******************************
+
+Planar complex unsigned 16-bit big endian IQ sample
+
+Description
+===========
+
+This format contains a sequence of complex number samples. Each complex
+number consist of two parts called In-phase and Quadrature (IQ). Both I
+and Q are represented as a 16 bit unsigned big endian number stored in
+32 bit space. The remaining unused bits within the 32 bit space will be
+padded with 0. I value starts first and Q value starts at an offset
+equalling half of the buffer size (i.e.) offset = buffersize/2. Out of
+the 16 bits, bit 15:2 (14 bit) is data and bit 1:0 (2 bit) can be any
+value.
+
+**Byte Order.**
+Each cell is one byte.
+
+.. flat-table::
+    :header-rows:  1
+    :stub-columns: 0
+
+    * -  Offset:
+      -  Byte B0
+      -  Byte B1
+      -  Byte B2
+      -  Byte B3
+    * -  start + 0:
+      -  I'\ :sub:`0[13:6]`
+      -  I'\ :sub:`0[5:0]; B1[1:0]=pad`
+      -  pad
+      -  pad
+    * -  start + 4:
+      -  I'\ :sub:`1[13:6]`
+      -  I'\ :sub:`1[5:0]; B1[1:0]=pad`
+      -  pad
+      -  pad
+    * -  ...
+    * - start + offset:
+      -  Q'\ :sub:`0[13:6]`
+      -  Q'\ :sub:`0[5:0]; B1[1:0]=pad`
+      -  pad
+      -  pad
+    * - start + offset + 4:
+      -  Q'\ :sub:`1[13:6]`
+      -  Q'\ :sub:`1[5:0]; B1[1:0]=pad`
+      -  pad
+      -  pad
diff --git a/Documentation/media/uapi/v4l/pixfmt-sdr-pcu18be.rst b/Documentation/media/uapi/v4l/pixfmt-sdr-pcu18be.rst
new file mode 100644
index 0000000..da8b26b
--- /dev/null
+++ b/Documentation/media/uapi/v4l/pixfmt-sdr-pcu18be.rst
@@ -0,0 +1,55 @@
+.. -*- coding: utf-8; mode: rst -*-
+
+.. _V4L2-SDR-FMT-PCU18BE:
+
+******************************
+V4L2_SDR_FMT_PCU18BE ('PC18')
+******************************
+
+Planar complex unsigned 18-bit big endian IQ sample
+
+Description
+===========
+
+This format contains a sequence of complex number samples. Each complex
+number consist of two parts called In-phase and Quadrature (IQ). Both I
+and Q are represented as a 18 bit unsigned big endian number stored in
+32 bit space. The remaining unused bits within the 32 bit space will be
+padded with 0. I value starts first and Q value starts at an offset
+equalling half of the buffer size (i.e.) offset = buffersize/2. Out of
+the 18 bits, bit 17:2 (16 bit) is data and bit 1:0 (2 bit) can be any
+value.
+
+**Byte Order.**
+Each cell is one byte.
+
+.. flat-table::
+    :header-rows:  1
+    :stub-columns: 0
+
+    * -  Offset:
+      -  Byte B0
+      -  Byte B1
+      -  Byte B2
+      -  Byte B3
+    * -  start + 0:
+      -  I'\ :sub:`0[17:10]`
+      -  I'\ :sub:`0[9:2]`
+      -  I'\ :sub:`0[1:0]; B2[5:0]=pad`
+      -  pad
+    * -  start + 4:
+      -  I'\ :sub:`1[17:10]`
+      -  I'\ :sub:`1[9:2]`
+      -  I'\ :sub:`1[1:0]; B2[5:0]=pad`
+      -  pad
+    * -  ...
+    * - start + offset:
+      -  Q'\ :sub:`0[17:10]`
+      -  Q'\ :sub:`0[9:2]`
+      -  Q'\ :sub:`0[1:0]; B2[5:0]=pad`
+      -  pad
+    * - start + offset + 4:
+      -  Q'\ :sub:`1[17:10]`
+      -  Q'\ :sub:`1[9:2]`
+      -  Q'\ :sub:`1[1:0]; B2[5:0]=pad`
+      -  pad
diff --git a/Documentation/media/uapi/v4l/pixfmt-sdr-pcu20be.rst b/Documentation/media/uapi/v4l/pixfmt-sdr-pcu20be.rst
new file mode 100644
index 0000000..b073be5
--- /dev/null
+++ b/Documentation/media/uapi/v4l/pixfmt-sdr-pcu20be.rst
@@ -0,0 +1,55 @@
+.. -*- coding: utf-8; mode: rst -*-
+.. _V4L2-SDR-FMT-PCU20BE:
+
+******************************
+V4L2_SDR_FMT_PCU20BE ('PC20')
+******************************
+
+Planar complex unsigned 20-bit big endian IQ sample
+
+Description
+===========
+
+This format contains a sequence of complex number samples. Each complex
+number consist of two parts called In-phase and Quadrature (IQ). Both I
+and Q are represented as a 20 bit unsigned big endian number stored in
+32 bit space. The remaining unused bits within the 32 bit space will be
+padded with 0. I value starts first and Q value starts at an offset
+equalling half of the buffer size (i.e.) offset = buffersize/2. Out of
+the 20 bits, bit 19:2 (18 bit) is data and bit 1:0 (2 bit) can be any
+value.
+
+**Byte Order.**
+Each cell is one byte.
+
+.. flat-table::
+    :header-rows:  1
+    :stub-columns: 0
+
+    * -  Offset:
+      -  Byte B0
+      -  Byte B1
+      -  Byte B2
+      -  Byte B3
+    * -  start + 0:
+      -  I'\ :sub:`0[19:12]`
+      -  I'\ :sub:`0[11:4]`
+      -  I'\ :sub:`0[3:0]; B2[3:0]=pad`
+      -  pad
+    * -  start + 4:
+      -  I'\ :sub:`1[19:12]`
+      -  I'\ :sub:`1[11:4]`
+      -  I'\ :sub:`1[3:0]; B2[3:0]=pad`
+      -  pad
+    * -  ...
+    * - start + offset:
+      -  Q'\ :sub:`0[19:12]`
+      -  Q'\ :sub:`0[11:4]`
+      -  Q'\ :sub:`0[3:0]; B2[3:0]=pad`
+      -  pad
+    * - start + offset + 4:
+      -  Q'\ :sub:`1[19:12]`
+      -  Q'\ :sub:`1[11:4]`
+      -  Q'\ :sub:`1[3:0]; B2[3:0]=pad`
+      -  pad
+
diff --git a/Documentation/media/uapi/v4l/sdr-formats.rst b/Documentation/media/uapi/v4l/sdr-formats.rst
index f863c08..2037f5b 100644
--- a/Documentation/media/uapi/v4l/sdr-formats.rst
+++ b/Documentation/media/uapi/v4l/sdr-formats.rst
@@ -17,3 +17,6 @@ These formats are used for :ref:`SDR <sdr>` interface only.
     pixfmt-sdr-cs08
     pixfmt-sdr-cs14le
     pixfmt-sdr-ru12le
+    pixfmt-sdr-pcu16be
+    pixfmt-sdr-pcu18be
+    pixfmt-sdr-pcu20be
-- 
1.9.1


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

* [PATCH v2 6/7] dt-bindings: media: Add Renesas R-Car DRIF binding
  2016-12-21  8:10 ` [PATCH v2 0/7] Add V4L2 SDR (DRIF & MAX2175) driver Ramesh Shanmugasundaram
                     ` (4 preceding siblings ...)
       [not found]   ` <1482307838-47415-1-git-send-email-ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>
@ 2016-12-21  8:10   ` Ramesh Shanmugasundaram
       [not found]     ` <1482307838-47415-7-git-send-email-ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>
  2016-12-21  8:10   ` [PATCH v2 7/7] media: platform: rcar_drif: Add DRIF support Ramesh Shanmugasundaram
  6 siblings, 1 reply; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-12-21  8:10 UTC (permalink / raw)
  To: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope
  Cc: chris.paterson2, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc, Ramesh Shanmugasundaram

Add binding documentation for Renesas R-Car Digital Radio Interface
(DRIF) controller.

Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>
---
 .../devicetree/bindings/media/renesas,drif.txt     | 202 +++++++++++++++++++++
 1 file changed, 202 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/renesas,drif.txt

diff --git a/Documentation/devicetree/bindings/media/renesas,drif.txt b/Documentation/devicetree/bindings/media/renesas,drif.txt
new file mode 100644
index 0000000..1f3feaf
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
@@ -0,0 +1,202 @@
+Renesas R-Car Gen3 Digital Radio Interface controller (DRIF)
+------------------------------------------------------------
+
+R-Car Gen3 DRIF is a SPI like receive only slave device. A general
+representation of DRIF interfacing with a master device is shown below.
+
++---------------------+                +---------------------+
+|                     |-----SCK------->|CLK                  |
+|       Master        |-----SS-------->|SYNC  DRIFn (slave)  |
+|                     |-----SD0------->|D0                   |
+|                     |-----SD1------->|D1                   |
++---------------------+                +---------------------+
+
+As per datasheet, each DRIF channel (drifn) is made up of two internal
+channels (drifn0 & drifn1). These two internal channels share the common
+CLK & SYNC. Each internal channel has its own dedicated resources like
+irq, dma channels, address space & clock. This internal split is not
+visible to the external master device.
+
+The device tree model represents each internal channel as a separate node.
+The internal channels sharing the CLK & SYNC are tied together by their
+phandles using a new property called "renesas,bonding". For the rest of
+the documentation, unless explicitly stated, the word channel implies an
+internal channel.
+
+When both internal channels are enabled they need to be managed together
+as one (i.e.) they cannot operate alone as independent devices. Out of the
+two, one of them needs to act as a primary device that accepts common
+properties of both the internal channels. This channel is identified by a
+new property called "renesas,primary-bond".
+
+To summarize,
+   - When both the internal channels that are bonded together are enabled,
+     the zeroth channel is selected as primary-bond. This channels accepts
+     properties common to all the members of the bond.
+   - When only one of the bonded channels need to be enabled, the property
+     "renesas,bonding" or "renesas,primary-bond" will have no effect. That
+     enabled channel can act alone as any other independent device.
+
+Required properties of an internal channel:
+-------------------------------------------
+- compatible: "renesas,r8a7795-drif" if DRIF controller is a part of R8A7795 SoC.
+	      "renesas,rcar-gen3-drif" for a generic R-Car Gen3 compatible device.
+	      When compatible with the generic version, nodes must list the
+	      SoC-specific version corresponding to the platform first
+	      followed by the generic version.
+- reg: offset and length of that channel.
+- interrupts: associated with that channel.
+- clocks: phandle and clock specifier of that channel.
+- clock-names: clock input name string: "fck".
+- dmas: phandles to the DMA channels.
+- dma-names: names of the DMA channel: "rx".
+- renesas,bonding: phandle to the other channel.
+
+Optional properties of an internal channel:
+-------------------------------------------
+- power-domains: phandle to the respective power domain.
+
+Required properties of an internal channel when:
+	- It is the only enabled channel of the bond (or)
+	- If it acts as primary among enabled bonds
+--------------------------------------------------------
+- pinctrl-0: pin control group to be used for this channel.
+- pinctrl-names: must be "default".
+- renesas,primary-bond: empty property indicating the channel acts as primary
+			among the bonded channels.
+- port: child port node of a channel that defines the local and remote
+	endpoints. The remote endpoint is assumed to be a third party tuner
+	device endpoint.
+
+Optional properties of an internal channel when:
+	- It is the only enabled channel of the bond (or)
+	- If it acts as primary among enabled bonds
+--------------------------------------------------------
+- renesas,syncmd       : sync mode
+			 0 (Frame start sync pulse mode. 1-bit width pulse
+			    indicates start of a frame)
+			 1 (L/R sync or I2S mode) (default)
+- renesas,lsb-first    : empty property indicates lsb bit is received first.
+			 When not defined msb bit is received first (default)
+- renesas,syncac-active: Indicates sync signal polarity, 0/1 for low/high
+			 respectively. The default is 1 (active high)
+- renesas,dtdl         : delay between sync signal and start of reception.
+			 The possible values are represented in 0.5 clock
+			 cycle units and the range is 0 to 4. The default
+			 value is 2 (i.e.) 1 clock cycle delay.
+- renesas,syncdl       : delay between end of reception and sync signal edge.
+			 The possible values are represented in 0.5 clock
+			 cycle units and the range is 0 to 4 & 6. The default
+			 value is 0 (i.e.) no delay.
+
+Example
+--------
+
+SoC common dtsi file
+
+		drif00: rif@e6f40000 {
+			compatible = "renesas,r8a7795-drif",
+				     "renesas,rcar-gen3-drif";
+			reg = <0 0xe6f40000 0 0x64>;
+			interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&cpg CPG_MOD 515>;
+			clock-names = "fck";
+			dmas = <&dmac1 0x20>, <&dmac2 0x20>;
+			dma-names = "rx", "rx";
+			power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+			renesas,bonding = <&drif01>;
+			status = "disabled";
+		};
+
+		drif01: rif@e6f50000 {
+			compatible = "renesas,r8a7795-drif",
+				     "renesas,rcar-gen3-drif";
+			reg = <0 0xe6f50000 0 0x64>;
+			interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&cpg CPG_MOD 514>;
+			clock-names = "fck";
+			dmas = <&dmac1 0x22>, <&dmac2 0x22>;
+			dma-names = "rx", "rx";
+			power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+			renesas,bonding = <&drif00>;
+			status = "disabled";
+		};
+
+
+Board specific dts file
+
+(1) Both internal channels enabled, primary-bond = 0
+-----------------------------------------------------
+
+When interfacing with a third party tuner device with two data pins as shown
+below.
+
++---------------------+                +---------------------+
+|                     |-----SCK------->|CLK                  |
+|       Master        |-----SS-------->|SYNC  DRIFn (slave)  |
+|                     |-----SD0------->|D0                   |
+|                     |-----SD1------->|D1                   |
++---------------------+                +---------------------+
+
+pfc {
+	...
+
+	drif0_pins: drif0 {
+		groups = "drif0_ctrl_a", "drif0_data0_a",
+				 "drif0_data1_a";
+		function = "drif0";
+	};
+	...
+}
+
+&drif00 {
+	pinctrl-0 = <&drif0_pins>;
+	pinctrl-names = "default";
+	renesas,syncac-active = <1>;
+	renesas,primary-bond;
+	status = "okay";
+	port {
+		drif0_ep: endpoint {
+		     remote-endpoint = <&tuner_ep>;
+		};
+	};
+};
+
+&drif01 {
+	status = "okay";
+};
+
+(2) Internal channel 1 alone is enabled:
+----------------------------------------
+
+When interfacing with a third party tuner device with one data pin as shown
+below.
+
++---------------------+                +---------------------+
+|                     |-----SCK------->|CLK                  |
+|       Master        |-----SS-------->|SYNC  DRIFn (slave)  |
+|                     |                |D0 (unused)          |
+|                     |-----SD-------->|D1                   |
++---------------------+                +---------------------+
+
+pfc {
+	...
+
+	drif0_pins: drif0 {
+		groups = "drif0_ctrl_a", "drif0_data1_a";
+		function = "drif0";
+	};
+	...
+}
+
+&drif01 {
+	pinctrl-0 = <&drif0_pins>;
+	pinctrl-names = "default";
+	renesas,syncac-active = <0>;
+	status = "okay";
+	port {
+		drif0_ep: endpoint {
+		     remote-endpoint = <&tuner_ep>;
+		};
+	};
+};
-- 
1.9.1

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

* [PATCH v2 7/7] media: platform: rcar_drif: Add DRIF support
  2016-12-21  8:10 ` [PATCH v2 0/7] Add V4L2 SDR (DRIF & MAX2175) driver Ramesh Shanmugasundaram
                     ` (5 preceding siblings ...)
  2016-12-21  8:10   ` [PATCH v2 6/7] dt-bindings: media: Add Renesas R-Car DRIF binding Ramesh Shanmugasundaram
@ 2016-12-21  8:10   ` Ramesh Shanmugasundaram
  6 siblings, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-12-21  8:10 UTC (permalink / raw)
  To: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope
  Cc: chris.paterson2, laurent.pinchart, geert+renesas, linux-media,
	devicetree, linux-renesas-soc, Ramesh Shanmugasundaram

This patch adds Digital Radio Interface (DRIF) support to R-Car Gen3 SoCs.
The driver exposes each instance of DRIF as a V4L2 SDR device. A DRIF
device represents a channel and each channel can have one or two
sub-channels respectively depending on the target board.

DRIF supports only Rx functionality. It receives samples from a RF
frontend tuner chip it is interfaced with. The combination of DRIF and the
tuner device, which is registered as a sub-device, determines the receive
sample rate and format.

In order to be compliant as a V4L2 SDR device, DRIF needs to bind with
the tuner device, which can be provided by a third party vendor. DRIF acts
as a slave device and the tuner device acts as a master transmitting the
samples. The driver allows asynchronous binding of a tuner device that
is registered as a v4l2 sub-device. The driver can learn about the tuner
it is interfaced with based on port endpoint properties of the device in
device tree. The V4L2 SDR device inherits the controls exposed by the
tuner device.

The device can also be configured to use either one or both of the data
pins at runtime based on the master (tuner) configuration.

Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>
---
 drivers/media/platform/Kconfig     |   25 +
 drivers/media/platform/Makefile    |    1 +
 drivers/media/platform/rcar_drif.c | 1593 ++++++++++++++++++++++++++++++++++++
 3 files changed, 1619 insertions(+)
 create mode 100644 drivers/media/platform/rcar_drif.c

diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index d944421..d288146 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -421,3 +421,28 @@ menuconfig DVB_PLATFORM_DRIVERS
 if DVB_PLATFORM_DRIVERS
 source "drivers/media/platform/sti/c8sectpfe/Kconfig"
 endif #DVB_PLATFORM_DRIVERS
+
+menuconfig SDR_PLATFORM_DRIVERS
+	bool "SDR platform devices"
+	depends on MEDIA_SDR_SUPPORT
+	default n
+	---help---
+	  Say Y here to enable support for platform-specific SDR Drivers.
+
+if SDR_PLATFORM_DRIVERS
+
+config VIDEO_RCAR_DRIF
+	tristate "Renesas Digitial Radio Interface (DRIF)"
+	depends on VIDEO_V4L2 && HAS_DMA
+	depends on ARCH_RENESAS
+	select VIDEOBUF2_VMALLOC
+	---help---
+	  Say Y if you want to enable R-Car Gen3 DRIF support. DRIF is Digital
+	  Radio Interface that interfaces with an RF front end chip. It is a
+	  receiver of digital data which uses DMA to transfer received data to
+	  a configured location for an application to use.
+
+	  To compile this driver as a module, choose M here; the module
+	  will be called rcar_drif.
+
+endif # SDR_PLATFORM_DRIVERS
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 5b3cb27..1c2daaf 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_VIDEO_SH_VOU)		+= sh_vou.o
 
 obj-$(CONFIG_SOC_CAMERA)		+= soc_camera/
 
+obj-$(CONFIG_VIDEO_RCAR_DRIF)		+= rcar_drif.o
 obj-$(CONFIG_VIDEO_RENESAS_FCP) 	+= rcar-fcp.o
 obj-$(CONFIG_VIDEO_RENESAS_FDP1)	+= rcar_fdp1.o
 obj-$(CONFIG_VIDEO_RENESAS_JPU) 	+= rcar_jpu.o
diff --git a/drivers/media/platform/rcar_drif.c b/drivers/media/platform/rcar_drif.c
new file mode 100644
index 0000000..0827bd8
--- /dev/null
+++ b/drivers/media/platform/rcar_drif.c
@@ -0,0 +1,1593 @@
+/*
+ * R-Car Gen3 Digital Radio Interface (DRIF) driver
+ *
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * The R-Car DRIF is a receive only MSIOF like controller with an
+ * external master device driving the SCK. It receives data into a FIFO,
+ * then this driver uses the SYS-DMAC engine to move the data from
+ * the device to memory.
+ *
+ * Each DRIF channel DRIFx (as per datasheet) contains two internal
+ * channels DRIFx0 & DRIFx1 within itself with each having its own resources
+ * like module clk, register set, irq and dma. These internal channels share
+ * common CLK & SYNC from master. The two data pins D0 & D1 shall be
+ * considered to represent the two internal channels. This internal split
+ * is not visible to the master device.
+ *
+ * Depending on the master device, a DRIF channel can use
+ *  (1) both internal channels (D0 & D1) to receive data in parallel (or)
+ *  (2) one internal channel (D0 or D1) to receive data
+ *
+ * The primary design goal of this controller is to act as Digitial Radio
+ * Interface that receives digital samples from a tuner device. Hence the
+ * driver exposes the device as a V4L2 SDR device. In order to qualify as
+ * a V4L2 SDR device, it should possess tuner interface as mandated by the
+ * framework. This driver expects a tuner driver (sub-device) to bind
+ * asynchronously with this device and the combined drivers shall expose
+ * a V4L2 compliant SDR device. The DRIF driver is independent of the
+ * tuner vendor.
+ */
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/ioctl.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-vmalloc.h>
+
+/* DRIF register offsets */
+#define RCAR_DRIF_SITMDR1			0x00
+#define RCAR_DRIF_SITMDR2			0x04
+#define RCAR_DRIF_SITMDR3			0x08
+#define RCAR_DRIF_SIRMDR1			0x10
+#define RCAR_DRIF_SIRMDR2			0x14
+#define RCAR_DRIF_SIRMDR3			0x18
+#define RCAR_DRIF_SICTR				0x28
+#define RCAR_DRIF_SIFCTR			0x30
+#define RCAR_DRIF_SISTR				0x40
+#define RCAR_DRIF_SIIER				0x44
+#define RCAR_DRIF_SIRFDR			0x60
+
+#define RCAR_DRIF_RFOVF			BIT(3)	/* Receive FIFO overflow */
+#define RCAR_DRIF_RFUDF			BIT(4)	/* Receive FIFO underflow */
+#define RCAR_DRIF_RFSERR		BIT(5)	/* Receive frame sync error */
+#define RCAR_DRIF_REOF			BIT(7)	/* Frame reception end */
+#define RCAR_DRIF_RDREQ			BIT(12) /* Receive data xfer req */
+#define RCAR_DRIF_RFFUL			BIT(13)	/* Receive FIFO full */
+
+/* SIRMDR1 */
+#define RCAR_DRIF_SIRMDR1_SYNCMD_FRAME		(0 << 28)
+#define RCAR_DRIF_SIRMDR1_SYNCMD_LR		(3 << 28)
+
+#define RCAR_DRIF_SIRMDR1_SYNCAC_POL_HIGH	(0 << 25)
+#define RCAR_DRIF_SIRMDR1_SYNCAC_POL_LOW	(1 << 25)
+
+#define RCAR_DRIF_SIRMDR1_MSB_FIRST		(0 << 24)
+#define RCAR_DRIF_SIRMDR1_LSB_FIRST		(1 << 24)
+
+#define RCAR_DRIF_SIRMDR1_DTDL_0		(0 << 20)
+#define RCAR_DRIF_SIRMDR1_DTDL_1		(1 << 20)
+#define RCAR_DRIF_SIRMDR1_DTDL_2		(2 << 20)
+#define RCAR_DRIF_SIRMDR1_DTDL_0PT5		(5 << 20)
+#define RCAR_DRIF_SIRMDR1_DTDL_1PT5		(6 << 20)
+
+#define RCAR_DRIF_SIRMDR1_SYNCDL_0		(0 << 20)
+#define RCAR_DRIF_SIRMDR1_SYNCDL_1		(1 << 20)
+#define RCAR_DRIF_SIRMDR1_SYNCDL_2		(2 << 20)
+#define RCAR_DRIF_SIRMDR1_SYNCDL_3		(3 << 20)
+#define RCAR_DRIF_SIRMDR1_SYNCDL_0PT5		(5 << 20)
+#define RCAR_DRIF_SIRMDR1_SYNCDL_1PT5		(6 << 20)
+
+#define RCAR_DRIF_MDR_GRPCNT(n)			(((n) - 1) << 30)
+#define RCAR_DRIF_MDR_BITLEN(n)			(((n) - 1) << 24)
+#define RCAR_DRIF_MDR_WDCNT(n)			(((n) - 1) << 16)
+
+/* Hidden Transmit register that controls CLK & SYNC */
+#define RCAR_DRIF_SITMDR1_PCON			BIT(30)
+
+#define RCAR_DRIF_SICTR_RX_RISING_EDGE		BIT(26)
+#define RCAR_DRIF_SICTR_RX_EN			BIT(8)
+#define RCAR_DRIF_SICTR_RESET			BIT(0)
+
+/* Constants */
+#define RCAR_DRIF_MAX_NUM_HWBUFS		32
+#define RCAR_DRIF_MAX_DEVS			4
+#define RCAR_DRIF_DEFAULT_NUM_HWBUFS		16
+#define RCAR_DRIF_DEFAULT_HWBUF_SIZE		(4 * PAGE_SIZE)
+#define RCAR_DRIF_MAX_CHANNEL			2
+#define RCAR_SDR_BUFFER_SIZE			SZ_64K
+
+/* Internal buffer status flags */
+#define RCAR_DRIF_BUF_DONE			BIT(0)	/* DMA completed */
+#define RCAR_DRIF_BUF_OVERFLOW			BIT(1)	/* Overflow detected */
+
+#define to_rcar_drif_buf_pair(sdr, ch_num, idx)	(sdr->ch[!(ch_num)]->buf[idx])
+
+static const unsigned int num_hwbufs = 32;
+#define for_each_rcar_drif_channel(ch, ch_mask)			\
+	for_each_set_bit(ch, ch_mask, RCAR_DRIF_MAX_CHANNEL)
+
+/* Debug */
+static unsigned int debug;
+module_param(debug, uint, 0644);
+MODULE_PARM_DESC(debug, "activate debug info");
+
+#define rdrif_dbg(level, sdr, fmt, arg...)				\
+	v4l2_dbg(level, debug, &sdr->v4l2_dev, fmt, ## arg)
+
+#define rdrif_err(sdr, fmt, arg...)					\
+	dev_err(sdr->v4l2_dev.dev, fmt, ## arg)
+
+/* Stream formats */
+struct rcar_drif_format {
+	u32	pixelformat;
+	u32	buffersize;
+	u32	wdlen;
+	u32	num_ch;
+};
+
+/* Format descriptions for capture and preview */
+static const struct rcar_drif_format formats[] = {
+	{
+		.pixelformat	= V4L2_SDR_FMT_PCU16BE,
+		.buffersize	= RCAR_SDR_BUFFER_SIZE,
+		.wdlen		= 16,
+		.num_ch	= 2,
+	},
+	{
+		.pixelformat	= V4L2_SDR_FMT_PCU18BE,
+		.buffersize	= RCAR_SDR_BUFFER_SIZE,
+		.wdlen		= 18,
+		.num_ch	= 2,
+	},
+	{
+		.pixelformat	= V4L2_SDR_FMT_PCU20BE,
+		.buffersize	= RCAR_SDR_BUFFER_SIZE,
+		.wdlen		= 20,
+		.num_ch	= 2,
+	},
+};
+
+static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats);
+
+/* Buffer for a received frame from one or both internal channels */
+struct rcar_drif_frame_buf {
+	/* Common v4l buffer stuff -- must be first */
+	struct vb2_v4l2_buffer vb;
+	struct list_head list;
+};
+
+struct rcar_drif_async_subdev {
+	struct v4l2_subdev *sd;
+	struct v4l2_async_subdev asd;
+};
+
+/* DMA buffer */
+struct rcar_drif_hwbuf {
+	void *addr;			/* CPU-side address */
+	unsigned int status;		/* Buffer status flags */
+};
+
+/* Internal channel */
+struct rcar_drif {
+	struct rcar_drif_sdr *sdr;	/* Group device */
+	struct platform_device *pdev;	/* Channel's pdev */
+	void __iomem *base;		/* Base register address */
+	resource_size_t start;		/* I/O resource offset */
+	struct dma_chan *dmach;		/* Reserved DMA channel */
+	struct clk *clkp;		/* Module clock */
+	struct rcar_drif_hwbuf *buf[RCAR_DRIF_MAX_NUM_HWBUFS];
+	dma_addr_t dma_handle;		/* Handle for all bufs */
+	unsigned int num;		/* Channel number */
+	bool acting_sdr;		/* Channel acting as SDR device */
+};
+
+/* DRIF V4L2 SDR */
+struct rcar_drif_sdr {
+	struct device *dev;		/* Platform device */
+	struct video_device *vdev;	/* V4L2 SDR device */
+	struct v4l2_device v4l2_dev;	/* V4L2 device */
+
+	/* Videobuf2 queue and queued buffers list */
+	struct vb2_queue vb_queue;
+	struct list_head queued_bufs;
+	spinlock_t queued_bufs_lock;	/* Protects queued_bufs */
+
+	struct mutex v4l2_mutex;	/* To serialize ioctls */
+	struct mutex vb_queue_mutex;	/* To serialize streaming ioctls */
+	struct v4l2_ctrl_handler ctrl_hdl;	/* SDR control handler */
+	struct v4l2_async_notifier notifier;	/* For subdev (tuner) */
+
+	/* Current V4L2 SDR format array index */
+	unsigned int fmt_idx;
+
+	/* Device tree SYNC properties */
+	u32 mdr1;
+
+	/* Internals */
+	struct rcar_drif *ch[RCAR_DRIF_MAX_CHANNEL]; /* DRIFx0,1 */
+	unsigned long hw_ch_mask;	/* Enabled channels per DT */
+	unsigned long cur_ch_mask;	/* Used channels for an SDR FMT */
+	u32 num_hw_ch;			/* Num of DT enabled channels */
+	u32 num_cur_ch;			/* Num of used channels */
+	u32 hwbuf_size;			/* Each DMA buffer size */
+	u32 produced;			/* Buffers produced by sdr dev */
+};
+
+/* Allocate buffer context */
+static int rcar_drif_alloc_bufctxt(struct rcar_drif_sdr *sdr)
+{
+	struct rcar_drif_hwbuf *bufctx;
+	unsigned int i, idx;
+
+	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+		bufctx = kcalloc(num_hwbufs, sizeof(*bufctx), GFP_KERNEL);
+		if (!bufctx)
+			return -ENOMEM;
+
+		for (idx = 0; idx < num_hwbufs; idx++)
+			sdr->ch[i]->buf[idx] = bufctx + idx;
+	}
+	return 0;
+}
+
+/* Release buffer context */
+static void rcar_drif_release_bufctxt(struct rcar_drif_sdr *sdr)
+{
+	unsigned int i;
+
+	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+		kfree(sdr->ch[i]->buf[0]);
+		sdr->ch[i]->buf[0] = NULL;
+	}
+}
+
+/* Release DMA channel */
+static void rcar_drif_release_dmachannel(struct rcar_drif_sdr *sdr)
+{
+	unsigned int i;
+
+	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask)
+		if (sdr->ch[i]->dmach) {
+			dma_release_channel(sdr->ch[i]->dmach);
+			sdr->ch[i]->dmach = NULL;
+		}
+}
+
+/* Allocate DMA channel */
+static int rcar_drif_alloc_dmachannel(struct rcar_drif_sdr *sdr)
+{
+	struct dma_slave_config dma_cfg;
+	unsigned int i;
+	int ret = -ENODEV;
+
+	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+		struct rcar_drif *ch = sdr->ch[i];
+
+		ch->dmach = dma_request_slave_channel(&ch->pdev->dev, "rx");
+		if (!ch->dmach) {
+			rdrif_err(sdr, "ch%u: dma channel req failed\n", i);
+			goto dmach_error;
+		}
+
+		/* Configure slave */
+		memset(&dma_cfg, 0, sizeof(dma_cfg));
+		dma_cfg.src_addr = (phys_addr_t)(ch->start + RCAR_DRIF_SIRFDR);
+		dma_cfg.dst_addr = 0;
+		dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		ret = dmaengine_slave_config(ch->dmach, &dma_cfg);
+		if (ret) {
+			rdrif_err(sdr, "ch%u: dma slave config failed\n", i);
+			goto dmach_error;
+		}
+	}
+	return 0;
+
+dmach_error:
+	rcar_drif_release_dmachannel(sdr);
+	return ret;
+}
+
+/* Release queued vb2 buffers */
+static void rcar_drif_release_queued_bufs(struct rcar_drif_sdr *sdr,
+					  enum vb2_buffer_state state)
+{
+	struct rcar_drif_frame_buf *fbuf, *tmp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sdr->queued_bufs_lock, flags);
+	list_for_each_entry_safe(fbuf, tmp, &sdr->queued_bufs, list) {
+		list_del(&fbuf->list);
+		vb2_buffer_done(&fbuf->vb.vb2_buf, state);
+	}
+	spin_unlock_irqrestore(&sdr->queued_bufs_lock, flags);
+}
+
+/* Set MDR defaults */
+static inline void rcar_drif_set_mdr1(struct rcar_drif_sdr *sdr)
+{
+	unsigned int i;
+
+	/* Set defaults for both internal channels */
+	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+		/* Refer MSIOF section in manual for this register setting */
+		writel(RCAR_DRIF_SITMDR1_PCON,
+		       sdr->ch[i]->base + RCAR_DRIF_SITMDR1);
+
+		/* Setup MDR1 value */
+		writel(sdr->mdr1, sdr->ch[i]->base + RCAR_DRIF_SIRMDR1);
+
+		rdrif_dbg(2, sdr, "ch%u: mdr1 = 0x%08x",
+			  i, readl(sdr->ch[i]->base + RCAR_DRIF_SIRMDR1));
+	}
+}
+
+/* Extract bitlen and wdcnt from given word length */
+static int rcar_drif_convert_wdlen(struct rcar_drif_sdr *sdr,
+				   u32 wdlen, u32 *bitlen, u32 *wdcnt)
+{
+	unsigned int i, nr_wds;
+
+	/* FIFO register size is 32 bits */
+	for (i = 0; i < 32; i++) {
+		nr_wds = wdlen % (32 - i);
+		if (nr_wds == 0) {
+			*bitlen = 32 - i;
+			*wdcnt = wdlen / *bitlen;
+			break;
+		}
+	}
+
+	/* Sanity check range */
+	if (i == 32 || !(*bitlen >= 8 && *bitlen <= 32) ||
+	    !(*wdcnt >= 1 && *wdcnt <= 64)) {
+		rdrif_err(sdr, "invalid wdlen %u configured\n", wdlen);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* Set DRIF receive format */
+static int rcar_drif_set_format(struct rcar_drif_sdr *sdr)
+{
+	u32 bitlen, wdcnt, wdlen;
+	unsigned int i;
+	int ret = -EINVAL;
+
+	wdlen = formats[sdr->fmt_idx].wdlen;
+	rdrif_dbg(2, sdr, "setfmt: idx %u, wdlen %u, num_ch %u\n",
+		  sdr->fmt_idx, wdlen, formats[sdr->fmt_idx].num_ch);
+
+	/* Sanity check */
+	if (formats[sdr->fmt_idx].num_ch > sdr->num_cur_ch) {
+		rdrif_err(sdr, "fmt idx %u current ch %u mismatch\n",
+			  sdr->fmt_idx, sdr->num_cur_ch);
+		return ret;
+	}
+
+	/* Get bitlen & wdcnt from wdlen */
+	ret = rcar_drif_convert_wdlen(sdr, wdlen, &bitlen, &wdcnt);
+	if (ret)
+		return ret;
+
+	/* Setup group, bitlen & wdcnt */
+	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+		u32 mdr;
+
+		/* Two groups */
+		mdr = RCAR_DRIF_MDR_GRPCNT(2) | RCAR_DRIF_MDR_BITLEN(bitlen) |
+		       RCAR_DRIF_MDR_WDCNT(wdcnt);
+		writel(mdr, sdr->ch[i]->base + RCAR_DRIF_SIRMDR2);
+
+		mdr = RCAR_DRIF_MDR_BITLEN(bitlen) | RCAR_DRIF_MDR_WDCNT(wdcnt);
+		writel(mdr, sdr->ch[i]->base + RCAR_DRIF_SIRMDR3);
+
+		rdrif_dbg(2, sdr, "ch%u: new mdr[2,3] = 0x%08x, 0x%08x\n",
+			  i, readl(sdr->ch[i]->base + RCAR_DRIF_SIRMDR2),
+			  readl(sdr->ch[i]->base + RCAR_DRIF_SIRMDR3));
+	}
+	return ret;
+}
+
+/* Release DMA buffers */
+static void rcar_drif_release_buf(struct rcar_drif_sdr *sdr)
+{
+	unsigned int i;
+
+	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+		struct rcar_drif *ch = sdr->ch[i];
+
+		/* First entry contains the dma buf ptr */
+		if (ch->buf[0] && ch->buf[0]->addr) {
+			dma_free_coherent(&ch->pdev->dev,
+					  sdr->hwbuf_size * num_hwbufs,
+					  ch->buf[0]->addr, ch->dma_handle);
+			ch->buf[0]->addr = NULL;
+		}
+	}
+}
+
+/* Request DMA buffers */
+static int rcar_drif_request_buf(struct rcar_drif_sdr *sdr)
+{
+	int ret = -ENOMEM;
+	unsigned int i, j;
+	void *addr;
+
+	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+		struct rcar_drif *ch = sdr->ch[i];
+
+		/* Allocate DMA buffers */
+		addr = dma_alloc_coherent(&ch->pdev->dev,
+					  sdr->hwbuf_size * num_hwbufs,
+					  &ch->dma_handle, GFP_KERNEL);
+		if (!addr) {
+			rdrif_err(sdr,
+			"ch%u: dma alloc failed. num_hwbufs %u size %u\n",
+			i, num_hwbufs, sdr->hwbuf_size);
+			goto alloc_error;
+		}
+
+		/* Split the chunk and populate bufctxt */
+		for (j = 0; j < num_hwbufs; j++) {
+			ch->buf[j]->addr = addr + (j * sdr->hwbuf_size);
+			ch->buf[j]->status = 0;
+		}
+	}
+
+	return 0;
+
+alloc_error:
+	return ret;
+}
+
+/* Setup vb_queue minimum buffer requirements */
+static int rcar_drif_queue_setup(struct vb2_queue *vq,
+			unsigned int *num_buffers, unsigned int *num_planes,
+			unsigned int sizes[], struct device *alloc_devs[])
+{
+	struct rcar_drif_sdr *sdr = vb2_get_drv_priv(vq);
+
+	/* Need at least 16 buffers */
+	if (vq->num_buffers + *num_buffers < 16)
+		*num_buffers = 16 - vq->num_buffers;
+
+	*num_planes = 1;
+	sizes[0] = PAGE_ALIGN(formats[sdr->fmt_idx].buffersize);
+
+	rdrif_dbg(2, sdr, "num_bufs %d sizes[0] %d\n", *num_buffers, sizes[0]);
+	return 0;
+}
+
+/* Enqueue buffer */
+static void rcar_drif_buf_queue(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct rcar_drif_sdr *sdr = vb2_get_drv_priv(vb->vb2_queue);
+	struct rcar_drif_frame_buf *fbuf =
+			container_of(vbuf, struct rcar_drif_frame_buf, vb);
+	unsigned long flags;
+
+	rdrif_dbg(2, sdr, "buf_queue idx %u\n", vb->index);
+	spin_lock_irqsave(&sdr->queued_bufs_lock, flags);
+	list_add_tail(&fbuf->list, &sdr->queued_bufs);
+	spin_unlock_irqrestore(&sdr->queued_bufs_lock, flags);
+}
+
+/* Get a frame buf from list */
+static struct rcar_drif_frame_buf *
+rcar_drif_get_fbuf(struct rcar_drif_sdr *sdr)
+{
+	struct rcar_drif_frame_buf *fbuf;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sdr->queued_bufs_lock, flags);
+	fbuf = list_first_entry_or_null(&sdr->queued_bufs, struct
+					rcar_drif_frame_buf, list);
+	if (!fbuf) {
+		/*
+		 * App is late in enqueing buffers. Samples lost & there will
+		 * be a gap in sequence number when app recovers
+		 */
+		rdrif_dbg(1, sdr, "\napp late: prod %u\n", sdr->produced);
+		sdr->produced++; /* Increment the produced count anyway */
+		spin_unlock_irqrestore(&sdr->queued_bufs_lock, flags);
+		return NULL;
+	}
+	list_del(&fbuf->list);
+	spin_unlock_irqrestore(&sdr->queued_bufs_lock, flags);
+
+	return fbuf;
+}
+
+static inline bool rcar_drif_buf_pairs_done(struct rcar_drif_hwbuf *buf1,
+					    struct rcar_drif_hwbuf *buf2)
+{
+	return (buf1->status & buf2->status & RCAR_DRIF_BUF_DONE);
+}
+
+/* Channel DMA complete */
+static void rcar_drif_channel_complete(struct rcar_drif *ch, u32 idx)
+{
+	u32 str;
+
+	ch->buf[idx]->status |= RCAR_DRIF_BUF_DONE;
+
+	/* Check for DRIF errors */
+	str = readl(ch->base + RCAR_DRIF_SISTR);
+	if (unlikely(str & RCAR_DRIF_RFOVF)) {
+		/* Writing the same clears it */
+		writel(str, ch->base + RCAR_DRIF_SISTR);
+
+		/* Overflow: some samples are lost */
+		ch->buf[idx]->status |= RCAR_DRIF_BUF_OVERFLOW;
+	}
+}
+
+/* Deliver buffer to user */
+static void rcar_drif_deliver_buf(struct rcar_drif *ch)
+{
+	struct rcar_drif_sdr *sdr = ch->sdr;
+	u32 idx = sdr->produced % num_hwbufs;
+	struct rcar_drif_frame_buf *fbuf;
+	bool overflow = false;
+
+	rcar_drif_channel_complete(ch, idx);
+
+	if (sdr->num_cur_ch == RCAR_DRIF_MAX_CHANNEL) {
+		struct rcar_drif_hwbuf *bufi, *bufq;
+
+		if (ch->num) {
+			bufi = to_rcar_drif_buf_pair(sdr, ch->num, idx);
+			bufq = ch->buf[idx];
+		} else {
+			bufi = ch->buf[idx];
+			bufq = to_rcar_drif_buf_pair(sdr, ch->num, idx);
+		}
+
+		/* Check if both DMA buffers are done */
+		if (!rcar_drif_buf_pairs_done(bufi, bufq))
+			return;
+
+		/* Clear buf done status */
+		bufi->status &= ~RCAR_DRIF_BUF_DONE;
+		bufq->status &= ~RCAR_DRIF_BUF_DONE;
+
+		/* Get fbuf */
+		fbuf = rcar_drif_get_fbuf(sdr);
+		if (!fbuf)
+			return;
+
+		memcpy(vb2_plane_vaddr(&fbuf->vb.vb2_buf, 0),
+		       bufi->addr, sdr->hwbuf_size);
+		memcpy(vb2_plane_vaddr(&fbuf->vb.vb2_buf, 0) + sdr->hwbuf_size,
+		       bufq->addr, sdr->hwbuf_size);
+
+		if ((bufi->status | bufq->status) & RCAR_DRIF_BUF_OVERFLOW) {
+			overflow = true;
+			/* Clear the flag in status */
+			bufi->status &= ~RCAR_DRIF_BUF_OVERFLOW;
+			bufq->status &= ~RCAR_DRIF_BUF_OVERFLOW;
+		}
+	} else {
+		struct rcar_drif_hwbuf *bufiq;
+
+		/* Get fbuf */
+		fbuf = rcar_drif_get_fbuf(sdr);
+		if (!fbuf)
+			return;
+
+		bufiq = ch->buf[idx];
+
+		memcpy(vb2_plane_vaddr(&fbuf->vb.vb2_buf, 0),
+		       bufiq->addr, sdr->hwbuf_size);
+
+		if (bufiq->status & RCAR_DRIF_BUF_OVERFLOW) {
+			overflow = true;
+			/* Clear the flag in status */
+			bufiq->status &= ~RCAR_DRIF_BUF_OVERFLOW;
+		}
+	}
+
+	rdrif_dbg(2, sdr, "ch%u: prod %u\n", ch->num, sdr->produced);
+
+	fbuf->vb.field = V4L2_FIELD_NONE;
+	fbuf->vb.sequence = sdr->produced++;
+	fbuf->vb.vb2_buf.timestamp = ktime_get_ns();
+	vb2_set_plane_payload(&fbuf->vb.vb2_buf, 0,
+			      formats[sdr->fmt_idx].buffersize);
+
+	/* Set error state on overflow */
+	if (overflow)
+		vb2_buffer_done(&fbuf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+	else
+		vb2_buffer_done(&fbuf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+/* DMA callback for each stage */
+static void rcar_drif_dma_complete(void *dma_async_param)
+{
+	struct rcar_drif *ch = dma_async_param;
+	struct rcar_drif_sdr *sdr = ch->sdr;
+
+	mutex_lock(&sdr->vb_queue_mutex);
+
+	/* DMA can be terminated while the callback was waiting on lock */
+	if (!vb2_is_streaming(&sdr->vb_queue))
+		goto stopped;
+
+	rcar_drif_deliver_buf(ch);
+stopped:
+	mutex_unlock(&sdr->vb_queue_mutex);
+}
+
+static int rcar_drif_qbuf(struct rcar_drif *ch)
+{
+	struct rcar_drif_sdr *sdr = ch->sdr;
+	dma_addr_t addr = ch->dma_handle;
+	struct dma_async_tx_descriptor *rxd;
+	dma_cookie_t cookie;
+	int ret = -EIO;
+
+	/* Setup cyclic DMA with given buffers */
+	rxd = dmaengine_prep_dma_cyclic(ch->dmach, addr,
+					sdr->hwbuf_size * num_hwbufs,
+					sdr->hwbuf_size, DMA_DEV_TO_MEM,
+					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!rxd) {
+		rdrif_err(sdr, "ch%u: prep dma cyclic failed\n", ch->num);
+		return ret;
+	}
+
+	/* Submit descriptor */
+	rxd->callback = rcar_drif_dma_complete;
+	rxd->callback_param = ch;
+	cookie = dmaengine_submit(rxd);
+	if (dma_submit_error(cookie)) {
+		rdrif_err(sdr, "ch%u: dma submit failed\n", ch->num);
+		return ret;
+	}
+
+	dma_async_issue_pending(ch->dmach);
+	return 0;
+}
+
+/* Enable reception */
+static int rcar_drif_enable_rx(struct rcar_drif_sdr *sdr)
+{
+	unsigned int i;
+	u32 ctr;
+	int ret;
+
+	/*
+	 * When both internal channels are enabled, they can be synchronized
+	 * only by the master
+	 */
+
+	/* Enable receive */
+	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+		ctr = readl(sdr->ch[i]->base + RCAR_DRIF_SICTR);
+		ctr |= (RCAR_DRIF_SICTR_RX_RISING_EDGE |
+			 RCAR_DRIF_SICTR_RX_EN);
+		writel(ctr, sdr->ch[i]->base + RCAR_DRIF_SICTR);
+	}
+
+	/* Check receive enabled */
+	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+		ret = readl_poll_timeout(sdr->ch[i]->base + RCAR_DRIF_SICTR,
+					 ctr, ctr & RCAR_DRIF_SICTR_RX_EN,
+					 2, 500000);
+		if (ret) {
+			rdrif_err(sdr, "ch%u: rx en failed. ctr 0x%08x\n",
+				  i, readl(sdr->ch[i]->base + RCAR_DRIF_SICTR));
+			break;
+		}
+	}
+	return ret;
+}
+
+/* Disable reception */
+static void rcar_drif_disable_rx(struct rcar_drif_sdr *sdr)
+{
+	unsigned int i;
+	u32 ctr;
+	int ret;
+
+	/* Disable receive */
+	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+		ctr = readl(sdr->ch[i]->base + RCAR_DRIF_SICTR);
+		ctr &= ~RCAR_DRIF_SICTR_RX_EN;
+		writel(ctr, sdr->ch[i]->base + RCAR_DRIF_SICTR);
+	}
+
+	/* Check receive disabled */
+	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+		ret = readl_poll_timeout(sdr->ch[i]->base + RCAR_DRIF_SICTR,
+					 ctr, !(ctr & RCAR_DRIF_SICTR_RX_EN),
+					 2, 500000);
+		if (ret)
+			dev_warn(&sdr->vdev->dev,
+			"ch%u: failed to disable rx. ctr 0x%08x\n",
+			i, readl(sdr->ch[i]->base + RCAR_DRIF_SICTR));
+	}
+}
+
+/* Start channel */
+static int rcar_drif_start_channel(struct rcar_drif *ch)
+{
+	struct rcar_drif_sdr *sdr = ch->sdr;
+	u32 ctr, str;
+	int ret;
+
+	/* Reset receive */
+	writel(RCAR_DRIF_SICTR_RESET, ch->base + RCAR_DRIF_SICTR);
+	ret = readl_poll_timeout(ch->base + RCAR_DRIF_SICTR,
+					 ctr, !(ctr & RCAR_DRIF_SICTR_RESET),
+					 2, 500000);
+	if (ret) {
+		rdrif_err(sdr, "ch%u: failed to reset rx. ctr 0x%08x\n",
+			  ch->num, readl(ch->base + RCAR_DRIF_SICTR));
+		return ret;
+	}
+
+	/* Queue buffers for DMA */
+	ret = rcar_drif_qbuf(ch);
+	if (ret)
+		return ret;
+
+	/* Clear status register flags */
+	str = RCAR_DRIF_RFFUL | RCAR_DRIF_REOF | RCAR_DRIF_RFSERR |
+		RCAR_DRIF_RFUDF | RCAR_DRIF_RFOVF;
+	writel(str, ch->base + RCAR_DRIF_SISTR);
+
+	/* Enable DMA receive interrupt */
+	writel(0x00009000, ch->base + RCAR_DRIF_SIIER);
+
+	return ret;
+}
+
+/* Start receive operation */
+static int rcar_drif_start(struct rcar_drif_sdr *sdr)
+{
+	unsigned int i;
+	int ret;
+
+	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+		ret = rcar_drif_start_channel(sdr->ch[i]);
+		if (ret)
+			goto start_error;
+	}
+
+	sdr->produced = 0;
+	ret = rcar_drif_enable_rx(sdr);
+start_error:
+	return ret;
+}
+
+/* Stop channel */
+static void rcar_drif_stop_channel(struct rcar_drif *ch)
+{
+	struct rcar_drif_sdr *sdr = ch->sdr;
+	int ret, retries = 3;
+
+	/* Disable DMA receive interrupt */
+	writel(0x00000000, ch->base + RCAR_DRIF_SIIER);
+
+	do {
+		/* Terminate all DMA transfers */
+		ret = dmaengine_terminate_sync(ch->dmach);
+		if (!ret)
+			break;
+		rdrif_dbg(2, sdr, "stop retry\n");
+	} while (--retries);
+
+	WARN_ON(!retries);
+}
+
+/* Stop receive operation */
+static void rcar_drif_stop(struct rcar_drif_sdr *sdr)
+{
+	unsigned int i;
+
+	/* Disable Rx */
+	rcar_drif_disable_rx(sdr);
+
+	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask)
+		rcar_drif_stop_channel(sdr->ch[i]);
+}
+
+/* Start streaming */
+static int rcar_drif_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct rcar_drif_sdr *sdr = vb2_get_drv_priv(vq);
+	unsigned int i, j;
+	int ret;
+
+	mutex_lock(&sdr->v4l2_mutex);
+
+	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+		ret = clk_prepare_enable(sdr->ch[i]->clkp);
+		if (ret)
+			goto start_error;
+	}
+
+	/* Set default MDRx settings */
+	rcar_drif_set_mdr1(sdr);
+
+	/* Set new format */
+	ret = rcar_drif_set_format(sdr);
+	if (ret)
+		goto start_error;
+
+	if (sdr->num_cur_ch == RCAR_DRIF_MAX_CHANNEL)
+		sdr->hwbuf_size =
+		formats[sdr->fmt_idx].buffersize / RCAR_DRIF_MAX_CHANNEL;
+	else
+		sdr->hwbuf_size = formats[sdr->fmt_idx].buffersize;
+
+	rdrif_dbg(1, sdr, "num_hwbufs %u, hwbuf_size %u\n",
+		num_hwbufs, sdr->hwbuf_size);
+
+	/* Alloc DMA channel */
+	ret = rcar_drif_alloc_dmachannel(sdr);
+	if (ret)
+		goto start_error;
+
+	/* Alloc buf context */
+	ret = rcar_drif_alloc_bufctxt(sdr);
+	if (ret)
+		goto start_error;
+
+	/* Request buffers */
+	ret = rcar_drif_request_buf(sdr);
+	if (ret)
+		goto start_error;
+
+	/* Start Rx */
+	ret = rcar_drif_start(sdr);
+	if (ret)
+		goto start_error;
+
+	mutex_unlock(&sdr->v4l2_mutex);
+	rdrif_dbg(1, sdr, "started\n");
+	return ret;
+
+start_error:
+	rcar_drif_release_queued_bufs(sdr, VB2_BUF_STATE_QUEUED);
+	rcar_drif_release_buf(sdr);
+	rcar_drif_release_bufctxt(sdr);
+	rcar_drif_release_dmachannel(sdr);
+	for (j = 0; j < i; j++)
+		clk_disable_unprepare(sdr->ch[j]->clkp);
+
+	mutex_unlock(&sdr->v4l2_mutex);
+	return ret;
+}
+
+/* Stop streaming */
+static void rcar_drif_stop_streaming(struct vb2_queue *vq)
+{
+	struct rcar_drif_sdr *sdr = vb2_get_drv_priv(vq);
+	unsigned int i;
+
+	mutex_lock(&sdr->v4l2_mutex);
+
+	/* Stop hardware streaming */
+	rcar_drif_stop(sdr);
+
+	/* Return all queued buffers to vb2 */
+	rcar_drif_release_queued_bufs(sdr, VB2_BUF_STATE_ERROR);
+
+	/* Release buf & buf context */
+	rcar_drif_release_buf(sdr);
+	rcar_drif_release_bufctxt(sdr);
+
+	/* Release DMA channel resources */
+	rcar_drif_release_dmachannel(sdr);
+
+	for_each_rcar_drif_channel(i, &sdr->cur_ch_mask)
+		clk_disable_unprepare(sdr->ch[i]->clkp);
+
+	mutex_unlock(&sdr->v4l2_mutex);
+	rdrif_dbg(1, sdr, "stopped: prod %u\n", sdr->produced);
+}
+
+/* Vb2 ops */
+static struct vb2_ops rcar_drif_vb2_ops = {
+	.queue_setup            = rcar_drif_queue_setup,
+	.buf_queue              = rcar_drif_buf_queue,
+	.start_streaming        = rcar_drif_start_streaming,
+	.stop_streaming         = rcar_drif_stop_streaming,
+	.wait_prepare		= vb2_ops_wait_prepare,
+	.wait_finish		= vb2_ops_wait_finish,
+};
+
+static int rcar_drif_querycap(struct file *file, void *fh,
+			      struct v4l2_capability *cap)
+{
+	struct rcar_drif_sdr *sdr = video_drvdata(file);
+
+	strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+	strlcpy(cap->card, sdr->vdev->name, sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+		 sdr->vdev->name);
+	return 0;
+}
+
+static int rcar_drif_set_default_format(struct rcar_drif_sdr *sdr)
+{
+	unsigned int i;
+
+	for (i = 0; i < NUM_FORMATS; i++) {
+		/* Matching fmt based on required channels is set as default */
+		if (sdr->num_hw_ch == formats[i].num_ch) {
+			sdr->fmt_idx = i;
+			sdr->cur_ch_mask = sdr->hw_ch_mask;
+			sdr->num_cur_ch = sdr->num_hw_ch;
+			dev_dbg(sdr->dev, "default fmt[%u]: mask %lu num %u\n",
+				i, sdr->cur_ch_mask, sdr->num_cur_ch);
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+static int rcar_drif_enum_fmt_sdr_cap(struct file *file, void *priv,
+				      struct v4l2_fmtdesc *f)
+{
+	if (f->index >= NUM_FORMATS)
+		return -EINVAL;
+
+	f->pixelformat = formats[f->index].pixelformat;
+	return 0;
+}
+
+static int rcar_drif_g_fmt_sdr_cap(struct file *file, void *priv,
+				   struct v4l2_format *f)
+{
+	struct rcar_drif_sdr *sdr = video_drvdata(file);
+
+	f->fmt.sdr.pixelformat = formats[sdr->fmt_idx].pixelformat;
+	f->fmt.sdr.buffersize = formats[sdr->fmt_idx].buffersize;
+	memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
+	return 0;
+}
+
+static int rcar_drif_s_fmt_sdr_cap(struct file *file, void *priv,
+				   struct v4l2_format *f)
+{
+	struct rcar_drif_sdr *sdr = video_drvdata(file);
+	struct vb2_queue *q = &sdr->vb_queue;
+	unsigned int i;
+
+	if (vb2_is_busy(q))
+		return -EBUSY;
+
+	memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
+	for (i = 0; i < NUM_FORMATS; i++) {
+		if (formats[i].pixelformat == f->fmt.sdr.pixelformat) {
+			sdr->fmt_idx  = i;
+			f->fmt.sdr.buffersize = formats[i].buffersize;
+
+			/*
+			 * If a format demands one channel only out of two
+			 * enabled channels, pick the 0th channel.
+			 */
+			if (formats[i].num_ch < sdr->num_hw_ch) {
+				sdr->cur_ch_mask = BIT(0);
+				sdr->num_cur_ch = formats[i].num_ch;
+			} else {
+				sdr->cur_ch_mask = sdr->hw_ch_mask;
+				sdr->num_cur_ch = sdr->num_hw_ch;
+			}
+
+			rdrif_dbg(1, sdr, "cur: idx %u mask %lu num %u\n",
+				  i, sdr->cur_ch_mask, sdr->num_cur_ch);
+			return 0;
+		}
+	}
+
+	if (rcar_drif_set_default_format(sdr)) {
+		rdrif_err(sdr, "cannot set default format\n");
+		return -EINVAL;
+	}
+
+	f->fmt.sdr.pixelformat = formats[sdr->fmt_idx].pixelformat;
+	f->fmt.sdr.buffersize = formats[sdr->fmt_idx].buffersize;
+	return 0;
+}
+
+static int rcar_drif_try_fmt_sdr_cap(struct file *file, void *priv,
+				     struct v4l2_format *f)
+{
+	struct rcar_drif_sdr *sdr = video_drvdata(file);
+	unsigned int i;
+
+	memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
+	for (i = 0; i < NUM_FORMATS; i++) {
+		if (formats[i].pixelformat == f->fmt.sdr.pixelformat) {
+			f->fmt.sdr.buffersize = formats[i].buffersize;
+			return 0;
+		}
+	}
+
+	f->fmt.sdr.pixelformat = formats[sdr->fmt_idx].pixelformat;
+	f->fmt.sdr.buffersize = formats[sdr->fmt_idx].buffersize;
+	return 0;
+}
+
+/* Tuner subdev ioctls */
+static int rcar_drif_enum_freq_bands(struct file *file, void *priv,
+				     struct v4l2_frequency_band *band)
+{
+	struct rcar_drif_sdr *sdr = video_drvdata(file);
+	struct v4l2_subdev *sd;
+	int ret = 0;
+
+	v4l2_device_for_each_subdev(sd, &sdr->v4l2_dev) {
+		ret = v4l2_subdev_call(sd, tuner, enum_freq_bands, band);
+		if (ret)
+			break;
+	}
+	return ret;
+}
+
+static int rcar_drif_g_frequency(struct file *file, void *priv,
+				 struct v4l2_frequency *f)
+{
+	struct rcar_drif_sdr *sdr = video_drvdata(file);
+	struct v4l2_subdev *sd;
+	int ret = 0;
+
+	v4l2_device_for_each_subdev(sd, &sdr->v4l2_dev) {
+		ret = v4l2_subdev_call(sd, tuner, g_frequency, f);
+		if (ret)
+			break;
+	}
+	return ret;
+}
+
+static int rcar_drif_s_frequency(struct file *file, void *priv,
+				 const struct v4l2_frequency *f)
+{
+	struct rcar_drif_sdr *sdr = video_drvdata(file);
+	struct v4l2_subdev *sd;
+	int ret = 0;
+
+	v4l2_device_for_each_subdev(sd, &sdr->v4l2_dev) {
+		ret = v4l2_subdev_call(sd, tuner, s_frequency, f);
+		if (ret)
+			break;
+	}
+	return ret;
+}
+
+static int rcar_drif_g_tuner(struct file *file, void *priv,
+			     struct v4l2_tuner *vt)
+{
+	struct rcar_drif_sdr *sdr = video_drvdata(file);
+	struct v4l2_subdev *sd;
+	int ret = 0;
+
+	v4l2_device_for_each_subdev(sd, &sdr->v4l2_dev) {
+		ret = v4l2_subdev_call(sd, tuner, g_tuner, vt);
+		if (ret)
+			break;
+	}
+	return ret;
+}
+
+static int rcar_drif_s_tuner(struct file *file, void *priv,
+			     const struct v4l2_tuner *vt)
+{
+	struct rcar_drif_sdr *sdr = video_drvdata(file);
+	struct v4l2_subdev *sd;
+	int ret = 0;
+
+	v4l2_device_for_each_subdev(sd, &sdr->v4l2_dev) {
+		ret = v4l2_subdev_call(sd, tuner, s_tuner, vt);
+		if (ret)
+			break;
+	}
+	return ret;
+}
+
+static const struct v4l2_ioctl_ops rcar_drif_ioctl_ops = {
+	.vidioc_querycap          = rcar_drif_querycap,
+
+	.vidioc_enum_fmt_sdr_cap  = rcar_drif_enum_fmt_sdr_cap,
+	.vidioc_g_fmt_sdr_cap     = rcar_drif_g_fmt_sdr_cap,
+	.vidioc_s_fmt_sdr_cap     = rcar_drif_s_fmt_sdr_cap,
+	.vidioc_try_fmt_sdr_cap   = rcar_drif_try_fmt_sdr_cap,
+
+	.vidioc_reqbufs           = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs       = vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf       = vb2_ioctl_prepare_buf,
+	.vidioc_querybuf          = vb2_ioctl_querybuf,
+	.vidioc_qbuf              = vb2_ioctl_qbuf,
+	.vidioc_dqbuf             = vb2_ioctl_dqbuf,
+
+	.vidioc_streamon          = vb2_ioctl_streamon,
+	.vidioc_streamoff         = vb2_ioctl_streamoff,
+
+	.vidioc_s_frequency       = rcar_drif_s_frequency,
+	.vidioc_g_frequency       = rcar_drif_g_frequency,
+	.vidioc_s_tuner		  = rcar_drif_s_tuner,
+	.vidioc_g_tuner		  = rcar_drif_g_tuner,
+	.vidioc_enum_freq_bands   = rcar_drif_enum_freq_bands,
+	.vidioc_subscribe_event   = v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+	.vidioc_log_status        = v4l2_ctrl_log_status,
+};
+
+static const struct v4l2_file_operations rcar_drif_fops = {
+	.owner                    = THIS_MODULE,
+	.open                     = v4l2_fh_open,
+	.release                  = vb2_fop_release,
+	.read                     = vb2_fop_read,
+	.poll                     = vb2_fop_poll,
+	.mmap                     = vb2_fop_mmap,
+	.unlocked_ioctl           = video_ioctl2,
+};
+
+static int rcar_drif_notify_bound(struct v4l2_async_notifier *notifier,
+				   struct v4l2_subdev *subdev,
+				   struct v4l2_async_subdev *asd)
+{
+	struct rcar_drif_sdr *sdr =
+		container_of(notifier, struct rcar_drif_sdr, notifier);
+
+	/* Nothing to do at this point */
+	rdrif_dbg(2, sdr, "bound asd: %s\n", asd->match.of.node->name);
+	return 0;
+}
+
+/* Sub-device registered notification callback */
+static int rcar_drif_notify_complete(struct v4l2_async_notifier *notifier)
+{
+	struct rcar_drif_sdr *sdr =
+		container_of(notifier, struct rcar_drif_sdr, notifier);
+	struct v4l2_subdev *sd;
+	int ret;
+
+	sdr->v4l2_dev.ctrl_handler = &sdr->ctrl_hdl;
+
+	ret = v4l2_device_register_subdev_nodes(&sdr->v4l2_dev);
+	if (ret) {
+		rdrif_err(sdr, "failed register subdev nodes ret %d\n", ret);
+		return ret;
+	}
+
+	v4l2_device_for_each_subdev(sd, &sdr->v4l2_dev) {
+		ret = v4l2_ctrl_add_handler(sdr->v4l2_dev.ctrl_handler,
+					    sd->ctrl_handler, NULL);
+		if (ret) {
+			rdrif_err(sdr, "failed ctrl add hdlr ret %d\n", ret);
+			return ret;
+		}
+	}
+	rdrif_dbg(2, sdr, "notify complete\n");
+	return 0;
+}
+
+/* Parse sub-devs (tuner) to find a matching device */
+static int rcar_drif_parse_subdevs(struct device *dev,
+				   struct v4l2_async_notifier *notifier)
+{
+	struct device_node *node;
+	struct rcar_drif_async_subdev *rsd;
+
+	notifier->subdevs = devm_kzalloc(dev, sizeof(*notifier->subdevs),
+					 GFP_KERNEL);
+	if (!notifier->subdevs)
+		return -ENOMEM;
+
+	node = of_graph_get_next_endpoint(dev->of_node, NULL);
+	if (!node)
+		return 0;
+
+
+	rsd = devm_kzalloc(dev, sizeof(*rsd), GFP_KERNEL);
+	if (!rsd) {
+		of_node_put(node);
+		return -ENOMEM;
+	}
+
+	notifier->subdevs[notifier->num_subdevs] = &rsd->asd;
+	rsd->asd.match.of.node = of_graph_get_remote_port_parent(node);
+	of_node_put(node);
+	if (!rsd->asd.match.of.node) {
+		dev_warn(dev, "bad remote port parent\n");
+		return -EINVAL;
+	}
+
+	rsd->asd.match_type = V4L2_ASYNC_MATCH_OF;
+	notifier->num_subdevs++;
+	return 0;
+}
+
+/* SIRMDR1 configuration */
+static int rcar_drif_validate_syncmd(struct rcar_drif_sdr *sdr, u32 val)
+{
+	if (val > 1) {
+		dev_err(sdr->dev, "invalid syncmd %u\n", val);
+		return -EINVAL;
+	}
+
+	sdr->mdr1 &= ~(3 << 28);	/* Clear current settings */
+	if (val == 0)
+		sdr->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCMD_FRAME;
+	else
+		sdr->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCMD_LR;
+	return 0;
+}
+
+/* Get the dtdl or syncdl bits */
+static u32 rcar_drif_get_dtdl_or_syncdl_bits(u32 dtdl_or_syncdl)
+{
+	/*
+	 * DTDL/SYNCDL bit	: dtdl/syncdl
+	 * b'000		: 0
+	 * b'001		: 2
+	 * b'010		: 4
+	 * b'011 (SYNCDL only)	: 6
+	 * b'101		: 1
+	 * b'110		: 3
+	 */
+	if (dtdl_or_syncdl % 2)
+		return dtdl_or_syncdl / 2 + 5;
+
+	return dtdl_or_syncdl / 2;
+}
+
+/* Validate DT properties dtdl and syncdl */
+static int rcar_drif_validate_dtdl_syncdl(struct rcar_drif_sdr *sdr)
+{
+	struct device_node *np = sdr->dev->of_node;
+	u32 dtdl = 2, syncdl = 0;	/* delay in 0.5 clock cycle units */
+
+	sdr->mdr1 |= RCAR_DRIF_SIRMDR1_DTDL_1 | RCAR_DRIF_SIRMDR1_SYNCDL_0;
+	of_property_read_u32(np, "renesas,dtdl", &dtdl);
+	of_property_read_u32(np, "renesas,syncdl", &syncdl);
+
+	/* Sanity checks */
+	if (dtdl > 4 || syncdl > 6) {
+		dev_err(sdr->dev, "invalid dtdl %u/syncdl %u\n", dtdl, syncdl);
+		return -EINVAL;
+	}
+	if ((dtdl + syncdl) % 2) {
+		dev_err(sdr->dev, "sum of dtdl %u & syncdl %u not OK\n",
+			dtdl, syncdl);
+		return -EINVAL;
+	}
+	sdr->mdr1 &= ~(7 << 20) & ~(7 << 16);	/* Clear current settings */
+	sdr->mdr1 |= rcar_drif_get_dtdl_or_syncdl_bits(dtdl) << 20;
+	sdr->mdr1 |= rcar_drif_get_dtdl_or_syncdl_bits(syncdl) << 16;
+	return 0;
+}
+
+static int rcar_drif_parse_properties(struct rcar_drif_sdr *sdr)
+{
+	struct device_node *np = sdr->dev->of_node;
+	u32 val;
+	int ret;
+
+	/* Set the defaults and check for overrides */
+	sdr->mdr1 = RCAR_DRIF_SIRMDR1_SYNCMD_LR;
+	if (!of_property_read_u32(np, "renesas,syncmd", &val)) {
+		ret = rcar_drif_validate_syncmd(sdr, val);
+		if (ret)
+			return ret;
+	}
+
+	if (of_find_property(np, "renesas,lsb-first", NULL))
+		sdr->mdr1 |= RCAR_DRIF_SIRMDR1_LSB_FIRST;
+	else
+		sdr->mdr1 |= RCAR_DRIF_SIRMDR1_MSB_FIRST;
+
+	sdr->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCAC_POL_HIGH;
+	if (!of_property_read_u32(np, "renesas,syncac-active", &val))
+		sdr->mdr1 |= val ? RCAR_DRIF_SIRMDR1_SYNCAC_POL_HIGH :
+			RCAR_DRIF_SIRMDR1_SYNCAC_POL_LOW;
+
+	return rcar_drif_validate_dtdl_syncdl(sdr);
+}
+
+static bool rcar_drif_primary_bond(struct platform_device *pdev)
+{
+	if (of_find_property(pdev->dev.of_node, "renesas,primary-bond", NULL))
+		return true;
+
+	return false;
+}
+
+static struct platform_device *rcar_drif_enabled_bond(struct platform_device *p)
+{
+	struct device_node *np;
+
+	np = of_parse_phandle(p->dev.of_node, "renesas,bonding", 0);
+	if (np && of_device_is_available(np))
+		return of_find_device_by_node(np);
+
+	return NULL;
+}
+
+static int rcar_drif_channel_probe(struct platform_device *pdev)
+{
+	struct rcar_drif *ch;
+	struct resource	*res;
+	void __iomem *base;
+	struct clk *clkp;
+	int ret;
+
+	/* Peripheral clock */
+	clkp = devm_clk_get(&pdev->dev, "fck");
+	if (IS_ERR(clkp)) {
+		ret = PTR_ERR(clkp);
+		dev_err(&pdev->dev, "clk get failed (%d)\n", ret);
+		return ret;
+	}
+
+	/* Register map */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base)) {
+		ret = PTR_ERR(base);
+		dev_err(&pdev->dev, "ioremap failed (%d)\n", ret);
+		return ret;
+	}
+
+	/* Reserve memory for enabled channel */
+	ch = devm_kzalloc(&pdev->dev, sizeof(*ch), GFP_KERNEL);
+	if (!ch) {
+		ret = PTR_ERR(ch);
+		dev_err(&pdev->dev, "failed alloc channel\n");
+		return ret;
+	}
+	ch->pdev = pdev;
+	ch->clkp = clkp;
+	ch->base = base;
+	ch->start = res->start;
+	platform_set_drvdata(pdev, ch);
+	return 0;
+}
+
+static int rcar_drif_probe(struct platform_device *pdev)
+{
+	struct rcar_drif *ch, *b_ch = NULL;
+	struct platform_device *b_pdev;
+	struct rcar_drif_sdr *sdr;
+	int ret;
+
+	/* Probe internal channel */
+	ret = rcar_drif_channel_probe(pdev);
+	if (ret)
+		return ret;
+
+	/* Check if both channels of the bond are enabled */
+	b_pdev = rcar_drif_enabled_bond(pdev);
+	if (b_pdev) {
+		/* Check if current channel acting as primary-bond */
+		if (!rcar_drif_primary_bond(pdev)) {
+			dev_notice(&pdev->dev, "probed\n");
+			return 0;
+		}
+
+		/* Check if the other device is probed */
+		b_ch = platform_get_drvdata(b_pdev);
+		if (!b_ch) {
+			dev_info(&pdev->dev, "defer probe\n");
+			return -EPROBE_DEFER;
+		}
+		/* Set the other channel number */
+		b_ch->num = 1;
+	}
+
+	/* Channel acting as SDR instance */
+	ch = platform_get_drvdata(pdev);
+	ch->acting_sdr = true;
+
+	/* Reserve memory for SDR structure */
+	sdr = devm_kzalloc(&pdev->dev, sizeof(*sdr), GFP_KERNEL);
+	if (!sdr) {
+		ret = PTR_ERR(sdr);
+		dev_err(&pdev->dev, "failed alloc drif context\n");
+		return ret;
+	}
+	sdr->dev = &pdev->dev;
+	sdr->hw_ch_mask = BIT(ch->num);
+
+	/* Establish links between SDR and channel(s) */
+	ch->sdr = sdr;
+	sdr->ch[ch->num] = ch;
+	if (b_ch) {
+		sdr->ch[b_ch->num] = b_ch;
+		b_ch->sdr = sdr;
+		sdr->hw_ch_mask |= BIT(b_ch->num);
+	}
+	sdr->num_hw_ch = hweight_long(sdr->hw_ch_mask);
+
+	/* Parse device tree properties */
+	ret = rcar_drif_parse_properties(sdr);
+	if (ret)
+		return ret;
+
+	dev_dbg(sdr->dev, "parsed mdr1 0x%08x\n", sdr->mdr1);
+
+	/* Validate any supported format for enabled channels */
+	ret = rcar_drif_set_default_format(sdr);
+	if (ret) {
+		dev_err(sdr->dev, "failed to set default format\n");
+		return ret;
+	}
+
+	/* Set defaults */
+	sdr->hwbuf_size = RCAR_DRIF_DEFAULT_HWBUF_SIZE;
+
+	mutex_init(&sdr->v4l2_mutex);
+	mutex_init(&sdr->vb_queue_mutex);
+	spin_lock_init(&sdr->queued_bufs_lock);
+	INIT_LIST_HEAD(&sdr->queued_bufs);
+
+	/* Init videobuf2 queue structure */
+	sdr->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
+	sdr->vb_queue.io_modes = VB2_READ | VB2_MMAP | VB2_DMABUF;
+	sdr->vb_queue.drv_priv = sdr;
+	sdr->vb_queue.buf_struct_size = sizeof(struct rcar_drif_frame_buf);
+	sdr->vb_queue.ops = &rcar_drif_vb2_ops;
+	sdr->vb_queue.mem_ops = &vb2_vmalloc_memops;
+	sdr->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+
+	/* Init videobuf2 queue */
+	ret = vb2_queue_init(&sdr->vb_queue);
+	if (ret) {
+		dev_err(sdr->dev, "could not initialize vb2 queue\n");
+		return ret;
+	}
+
+	/* Register the v4l2_device */
+	ret = v4l2_device_register(&pdev->dev, &sdr->v4l2_dev);
+	if (ret) {
+		dev_err(sdr->dev, "failed v4l2_device_register (%d)\n", ret);
+		return ret;
+	}
+
+	/*
+	 * Parse subdevs after v4l2_device_register because if the subdev
+	 * is already probed, bound and complete will be called immediately
+	 */
+	ret = rcar_drif_parse_subdevs(&pdev->dev, &sdr->notifier);
+	if (ret)
+		goto err_unreg_v4l2;
+
+	sdr->notifier.bound = rcar_drif_notify_bound;
+	sdr->notifier.complete = rcar_drif_notify_complete;
+
+	v4l2_ctrl_handler_init(&sdr->ctrl_hdl, 10);
+
+	/* Register notifier */
+	ret = v4l2_async_notifier_register(&sdr->v4l2_dev, &sdr->notifier);
+	if (ret < 0) {
+		dev_err(sdr->dev, "notifier registration failed (%d)\n", ret);
+		goto err_unreg_v4l2;
+	}
+
+	/* Init video_device structure */
+	sdr->vdev = video_device_alloc();
+	if (!sdr->vdev) {
+		ret = -ENOMEM;
+		goto err_unreg_notif;
+	}
+	snprintf(sdr->vdev->name, sizeof(sdr->vdev->name), "R-Car DRIF");
+	sdr->vdev->fops = &rcar_drif_fops;
+	sdr->vdev->ioctl_ops = &rcar_drif_ioctl_ops;
+	sdr->vdev->release = video_device_release;
+	sdr->vdev->lock = &sdr->v4l2_mutex;
+	sdr->vdev->queue = &sdr->vb_queue;
+	sdr->vdev->queue->lock = &sdr->vb_queue_mutex;
+	sdr->vdev->ctrl_handler = &sdr->ctrl_hdl;
+	sdr->vdev->v4l2_dev = &sdr->v4l2_dev;
+	sdr->vdev->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER |
+		V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
+	video_set_drvdata(sdr->vdev, sdr);
+
+	/* Register V4L2 SDR device */
+	ret = video_register_device(sdr->vdev, VFL_TYPE_SDR, -1);
+	if (ret) {
+		dev_err(sdr->dev, "failed video_register_device (%d)\n", ret);
+		goto err_unreg_notif;
+	}
+
+	dev_notice(sdr->dev, "probed\n");
+	return 0;
+
+err_unreg_notif:
+	video_device_release(sdr->vdev);
+	v4l2_async_notifier_unregister(&sdr->notifier);
+err_unreg_v4l2:
+	v4l2_device_unregister(&sdr->v4l2_dev);
+	return ret;
+}
+
+static int rcar_drif_remove(struct platform_device *pdev)
+{
+	struct rcar_drif *ch = platform_get_drvdata(pdev);
+	struct rcar_drif_sdr *sdr = ch->sdr;
+
+	if (!ch->acting_sdr) {
+		/* Nothing to do */
+		dev_notice(&pdev->dev, "removed\n");
+		return 0;
+	}
+
+	/* SDR instance */
+	v4l2_ctrl_handler_free(sdr->v4l2_dev.ctrl_handler);
+	v4l2_async_notifier_unregister(&sdr->notifier);
+	v4l2_device_unregister(&sdr->v4l2_dev);
+	video_unregister_device(sdr->vdev);
+	dev_notice(&pdev->dev, "removed\n");
+	return 0;
+}
+
+static int __maybe_unused rcar_drif_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int __maybe_unused rcar_drif_resume(struct device *dev)
+{
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(rcar_drif_pm_ops, rcar_drif_suspend,
+			 rcar_drif_resume);
+
+static const struct of_device_id rcar_drif_of_table[] = {
+	{ .compatible = "renesas,rcar-gen3-drif" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, rcar_drif_of_table);
+
+#define RCAR_DRIF_DRV_NAME "rcar_drif"
+static struct platform_driver rcar_drif_driver = {
+	.driver = {
+		.name = RCAR_DRIF_DRV_NAME,
+		.of_match_table = of_match_ptr(rcar_drif_of_table),
+		.pm = &rcar_drif_pm_ops,
+		},
+	.probe = rcar_drif_probe,
+	.remove = rcar_drif_remove,
+};
+
+module_platform_driver(rcar_drif_driver);
+
+MODULE_DESCRIPTION("Renesas R-Car Gen3 DRIF driver");
+MODULE_ALIAS("platform:" RCAR_DRIF_DRV_NAME);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>");
-- 
1.9.1

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

* Re: [PATCH v2 6/7] dt-bindings: media: Add Renesas R-Car DRIF binding
  2016-12-21  8:10   ` [PATCH v2 6/7] dt-bindings: media: Add Renesas R-Car DRIF binding Ramesh Shanmugasundaram
@ 2016-12-22 17:05         ` Laurent Pinchart
  0 siblings, 0 replies; 60+ messages in thread
From: Laurent Pinchart @ 2016-12-22 17:05 UTC (permalink / raw)
  To: Ramesh Shanmugasundaram
  Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	mchehab-DgEjT+Ai2ygdnm+yROfE0A, hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
	sakari.ailus-VuQAYsv1563Yd54FQh9/CA, crope-X3B1VOXEql0,
	chris.paterson2-zM6kxYcvzFBBDgjK7y7TUQ,
	geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ,
	linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA

Hi Ramesh,

Thank you for the patch.

On Wednesday 21 Dec 2016 08:10:37 Ramesh Shanmugasundaram wrote:
> Add binding documentation for Renesas R-Car Digital Radio Interface
> (DRIF) controller.
> 
> Signed-off-by: Ramesh Shanmugasundaram
> <ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org> ---
>  .../devicetree/bindings/media/renesas,drif.txt     | 202 ++++++++++++++++++
>  1 file changed, 202 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/renesas,drif.txt
> 
> diff --git a/Documentation/devicetree/bindings/media/renesas,drif.txt
> b/Documentation/devicetree/bindings/media/renesas,drif.txt new file mode
> 100644
> index 0000000..1f3feaf
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> @@ -0,0 +1,202 @@
> +Renesas R-Car Gen3 Digital Radio Interface controller (DRIF)
> +------------------------------------------------------------
> +
> +R-Car Gen3 DRIF is a SPI like receive only slave device. A general
> +representation of DRIF interfacing with a master device is shown below.
> +
> ++---------------------+                +---------------------+
> +|                     |-----SCK------->|CLK                  |
> +|       Master        |-----SS-------->|SYNC  DRIFn (slave)  |
> +|                     |-----SD0------->|D0                   |
> +|                     |-----SD1------->|D1                   |
> ++---------------------+                +---------------------+
> +
> +As per datasheet, each DRIF channel (drifn) is made up of two internal
> +channels (drifn0 & drifn1). These two internal channels share the common
> +CLK & SYNC. Each internal channel has its own dedicated resources like
> +irq, dma channels, address space & clock. This internal split is not
> +visible to the external master device.
> +
> +The device tree model represents each internal channel as a separate node.
> +The internal channels sharing the CLK & SYNC are tied together by their
> +phandles using a new property called "renesas,bonding". For the rest of
> +the documentation, unless explicitly stated, the word channel implies an
> +internal channel.
> +
> +When both internal channels are enabled they need to be managed together
> +as one (i.e.) they cannot operate alone as independent devices. Out of the
> +two, one of them needs to act as a primary device that accepts common
> +properties of both the internal channels. This channel is identified by a
> +new property called "renesas,primary-bond".
> +
> +To summarize,
> +   - When both the internal channels that are bonded together are enabled,
> +     the zeroth channel is selected as primary-bond. This channels accepts
> +     properties common to all the members of the bond.
> +   - When only one of the bonded channels need to be enabled, the property
> +     "renesas,bonding" or "renesas,primary-bond" will have no effect. That
> +     enabled channel can act alone as any other independent device.
> +
> +Required properties of an internal channel:
> +-------------------------------------------
> +- compatible: "renesas,r8a7795-drif" if DRIF controller is a part of
> R8A7795 SoC.
> +	      "renesas,rcar-gen3-drif" for a generic R-Car Gen3 compatible
> device.
> +	      When compatible with the generic version, nodes must list the
> +	      SoC-specific version corresponding to the platform first
> +	      followed by the generic version.
> +- reg: offset and length of that channel.
> +- interrupts: associated with that channel.
> +- clocks: phandle and clock specifier of that channel.
> +- clock-names: clock input name string: "fck".
> +- dmas: phandles to the DMA channels.
> +- dma-names: names of the DMA channel: "rx".
> +- renesas,bonding: phandle to the other channel.
> +
> +Optional properties of an internal channel:
> +-------------------------------------------
> +- power-domains: phandle to the respective power domain.
> +
> +Required properties of an internal channel when:
> +	- It is the only enabled channel of the bond (or)
> +	- If it acts as primary among enabled bonds
> +--------------------------------------------------------
> +- pinctrl-0: pin control group to be used for this channel.
> +- pinctrl-names: must be "default".
> +- renesas,primary-bond: empty property indicating the channel acts as
> primary
> +			among the bonded channels.
> +- port: child port node of a channel that defines the local and remote
> +	endpoints. The remote endpoint is assumed to be a third party tuner
> +	device endpoint.
> +
> +Optional properties of an internal channel when:
> +	- It is the only enabled channel of the bond (or)
> +	- If it acts as primary among enabled bonds
> +--------------------------------------------------------
> +- renesas,syncmd       : sync mode
> +			 0 (Frame start sync pulse mode. 1-bit width pulse
> +			    indicates start of a frame)
> +			 1 (L/R sync or I2S mode) (default)
> +- renesas,lsb-first    : empty property indicates lsb bit is received
> first.
> +			 When not defined msb bit is received first (default)
> +- renesas,syncac-active: Indicates sync signal polarity, 0/1 for low/high
> +			 respectively. The default is 1 (active high)
> +- renesas,dtdl         : delay between sync signal and start of reception.
> +			 The possible values are represented in 0.5 clock
> +			 cycle units and the range is 0 to 4. The default
> +			 value is 2 (i.e.) 1 clock cycle delay.
> +- renesas,syncdl       : delay between end of reception and sync signal
> edge.
> +			 The possible values are represented in 0.5 clock
> +			 cycle units and the range is 0 to 4 & 6. The default
> +			 value is 0 (i.e.) no delay.

Most of these properties are pretty similar to the video bus properties 
defined at the endpoint level in 
Documentation/devicetree/bindings/media/video-interfaces.txt. I believe it 
would make sense to use OF graph and try to standardize these properties 
similarly.

> +
> +Example
> +--------
> +
> +SoC common dtsi file
> +
> +		drif00: rif@e6f40000 {
> +			compatible = "renesas,r8a7795-drif",
> +				     "renesas,rcar-gen3-drif";
> +			reg = <0 0xe6f40000 0 0x64>;
> +			interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
> +			clocks = <&cpg CPG_MOD 515>;
> +			clock-names = "fck";
> +			dmas = <&dmac1 0x20>, <&dmac2 0x20>;
> +			dma-names = "rx", "rx";
> +			power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> +			renesas,bonding = <&drif01>;
> +			status = "disabled";
> +		};
> +
> +		drif01: rif@e6f50000 {
> +			compatible = "renesas,r8a7795-drif",
> +				     "renesas,rcar-gen3-drif";
> +			reg = <0 0xe6f50000 0 0x64>;
> +			interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
> +			clocks = <&cpg CPG_MOD 514>;
> +			clock-names = "fck";
> +			dmas = <&dmac1 0x22>, <&dmac2 0x22>;
> +			dma-names = "rx", "rx";
> +			power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> +			renesas,bonding = <&drif00>;
> +			status = "disabled";
> +		};
> +
> +
> +Board specific dts file
> +
> +(1) Both internal channels enabled, primary-bond = 0
> +-----------------------------------------------------
> +
> +When interfacing with a third party tuner device with two data pins as
> shown +below.
> +
> ++---------------------+                +---------------------+
> +|                     |-----SCK------->|CLK                  |
> +|       Master        |-----SS-------->|SYNC  DRIFn (slave)  |
> +|                     |-----SD0------->|D0                   |
> +|                     |-----SD1------->|D1                   |
> ++---------------------+                +---------------------+
> +
> +pfc {
> +	...
> +
> +	drif0_pins: drif0 {
> +		groups = "drif0_ctrl_a", "drif0_data0_a",
> +				 "drif0_data1_a";
> +		function = "drif0";
> +	};
> +	...
> +}
> +
> +&drif00 {
> +	pinctrl-0 = <&drif0_pins>;
> +	pinctrl-names = "default";
> +	renesas,syncac-active = <1>;
> +	renesas,primary-bond;
> +	status = "okay";
> +	port {
> +		drif0_ep: endpoint {
> +		     remote-endpoint = <&tuner_ep>;
> +		};
> +	};
> +};
> +
> +&drif01 {
> +	status = "okay";
> +};
> +
> +(2) Internal channel 1 alone is enabled:
> +----------------------------------------
> +
> +When interfacing with a third party tuner device with one data pin as shown
> +below.
> +
> ++---------------------+                +---------------------+
> +|                     |-----SCK------->|CLK                  |
> +|       Master        |-----SS-------->|SYNC  DRIFn (slave)  |
> +|                     |                |D0 (unused)          |
> +|                     |-----SD-------->|D1                   |
> ++---------------------+                +---------------------+
> +
> +pfc {
> +	...
> +
> +	drif0_pins: drif0 {
> +		groups = "drif0_ctrl_a", "drif0_data1_a";
> +		function = "drif0";
> +	};
> +	...
> +}
> +
> +&drif01 {
> +	pinctrl-0 = <&drif0_pins>;
> +	pinctrl-names = "default";
> +	renesas,syncac-active = <0>;
> +	status = "okay";
> +	port {
> +		drif0_ep: endpoint {
> +		     remote-endpoint = <&tuner_ep>;
> +		};
> +	};
> +};

-- 
Regards,

Laurent Pinchart

--
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 v2 6/7] dt-bindings: media: Add Renesas R-Car DRIF binding
@ 2016-12-22 17:05         ` Laurent Pinchart
  0 siblings, 0 replies; 60+ messages in thread
From: Laurent Pinchart @ 2016-12-22 17:05 UTC (permalink / raw)
  To: Ramesh Shanmugasundaram
  Cc: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope,
	chris.paterson2, geert+renesas, linux-media, devicetree,
	linux-renesas-soc

Hi Ramesh,

Thank you for the patch.

On Wednesday 21 Dec 2016 08:10:37 Ramesh Shanmugasundaram wrote:
> Add binding documentation for Renesas R-Car Digital Radio Interface
> (DRIF) controller.
> 
> Signed-off-by: Ramesh Shanmugasundaram
> <ramesh.shanmugasundaram@bp.renesas.com> ---
>  .../devicetree/bindings/media/renesas,drif.txt     | 202 ++++++++++++++++++
>  1 file changed, 202 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/renesas,drif.txt
> 
> diff --git a/Documentation/devicetree/bindings/media/renesas,drif.txt
> b/Documentation/devicetree/bindings/media/renesas,drif.txt new file mode
> 100644
> index 0000000..1f3feaf
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> @@ -0,0 +1,202 @@
> +Renesas R-Car Gen3 Digital Radio Interface controller (DRIF)
> +------------------------------------------------------------
> +
> +R-Car Gen3 DRIF is a SPI like receive only slave device. A general
> +representation of DRIF interfacing with a master device is shown below.
> +
> ++---------------------+                +---------------------+
> +|                     |-----SCK------->|CLK                  |
> +|       Master        |-----SS-------->|SYNC  DRIFn (slave)  |
> +|                     |-----SD0------->|D0                   |
> +|                     |-----SD1------->|D1                   |
> ++---------------------+                +---------------------+
> +
> +As per datasheet, each DRIF channel (drifn) is made up of two internal
> +channels (drifn0 & drifn1). These two internal channels share the common
> +CLK & SYNC. Each internal channel has its own dedicated resources like
> +irq, dma channels, address space & clock. This internal split is not
> +visible to the external master device.
> +
> +The device tree model represents each internal channel as a separate node.
> +The internal channels sharing the CLK & SYNC are tied together by their
> +phandles using a new property called "renesas,bonding". For the rest of
> +the documentation, unless explicitly stated, the word channel implies an
> +internal channel.
> +
> +When both internal channels are enabled they need to be managed together
> +as one (i.e.) they cannot operate alone as independent devices. Out of the
> +two, one of them needs to act as a primary device that accepts common
> +properties of both the internal channels. This channel is identified by a
> +new property called "renesas,primary-bond".
> +
> +To summarize,
> +   - When both the internal channels that are bonded together are enabled,
> +     the zeroth channel is selected as primary-bond. This channels accepts
> +     properties common to all the members of the bond.
> +   - When only one of the bonded channels need to be enabled, the property
> +     "renesas,bonding" or "renesas,primary-bond" will have no effect. That
> +     enabled channel can act alone as any other independent device.
> +
> +Required properties of an internal channel:
> +-------------------------------------------
> +- compatible: "renesas,r8a7795-drif" if DRIF controller is a part of
> R8A7795 SoC.
> +	      "renesas,rcar-gen3-drif" for a generic R-Car Gen3 compatible
> device.
> +	      When compatible with the generic version, nodes must list the
> +	      SoC-specific version corresponding to the platform first
> +	      followed by the generic version.
> +- reg: offset and length of that channel.
> +- interrupts: associated with that channel.
> +- clocks: phandle and clock specifier of that channel.
> +- clock-names: clock input name string: "fck".
> +- dmas: phandles to the DMA channels.
> +- dma-names: names of the DMA channel: "rx".
> +- renesas,bonding: phandle to the other channel.
> +
> +Optional properties of an internal channel:
> +-------------------------------------------
> +- power-domains: phandle to the respective power domain.
> +
> +Required properties of an internal channel when:
> +	- It is the only enabled channel of the bond (or)
> +	- If it acts as primary among enabled bonds
> +--------------------------------------------------------
> +- pinctrl-0: pin control group to be used for this channel.
> +- pinctrl-names: must be "default".
> +- renesas,primary-bond: empty property indicating the channel acts as
> primary
> +			among the bonded channels.
> +- port: child port node of a channel that defines the local and remote
> +	endpoints. The remote endpoint is assumed to be a third party tuner
> +	device endpoint.
> +
> +Optional properties of an internal channel when:
> +	- It is the only enabled channel of the bond (or)
> +	- If it acts as primary among enabled bonds
> +--------------------------------------------------------
> +- renesas,syncmd       : sync mode
> +			 0 (Frame start sync pulse mode. 1-bit width pulse
> +			    indicates start of a frame)
> +			 1 (L/R sync or I2S mode) (default)
> +- renesas,lsb-first    : empty property indicates lsb bit is received
> first.
> +			 When not defined msb bit is received first (default)
> +- renesas,syncac-active: Indicates sync signal polarity, 0/1 for low/high
> +			 respectively. The default is 1 (active high)
> +- renesas,dtdl         : delay between sync signal and start of reception.
> +			 The possible values are represented in 0.5 clock
> +			 cycle units and the range is 0 to 4. The default
> +			 value is 2 (i.e.) 1 clock cycle delay.
> +- renesas,syncdl       : delay between end of reception and sync signal
> edge.
> +			 The possible values are represented in 0.5 clock
> +			 cycle units and the range is 0 to 4 & 6. The default
> +			 value is 0 (i.e.) no delay.

Most of these properties are pretty similar to the video bus properties 
defined at the endpoint level in 
Documentation/devicetree/bindings/media/video-interfaces.txt. I believe it 
would make sense to use OF graph and try to standardize these properties 
similarly.

> +
> +Example
> +--------
> +
> +SoC common dtsi file
> +
> +		drif00: rif@e6f40000 {
> +			compatible = "renesas,r8a7795-drif",
> +				     "renesas,rcar-gen3-drif";
> +			reg = <0 0xe6f40000 0 0x64>;
> +			interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
> +			clocks = <&cpg CPG_MOD 515>;
> +			clock-names = "fck";
> +			dmas = <&dmac1 0x20>, <&dmac2 0x20>;
> +			dma-names = "rx", "rx";
> +			power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> +			renesas,bonding = <&drif01>;
> +			status = "disabled";
> +		};
> +
> +		drif01: rif@e6f50000 {
> +			compatible = "renesas,r8a7795-drif",
> +				     "renesas,rcar-gen3-drif";
> +			reg = <0 0xe6f50000 0 0x64>;
> +			interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
> +			clocks = <&cpg CPG_MOD 514>;
> +			clock-names = "fck";
> +			dmas = <&dmac1 0x22>, <&dmac2 0x22>;
> +			dma-names = "rx", "rx";
> +			power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> +			renesas,bonding = <&drif00>;
> +			status = "disabled";
> +		};
> +
> +
> +Board specific dts file
> +
> +(1) Both internal channels enabled, primary-bond = 0
> +-----------------------------------------------------
> +
> +When interfacing with a third party tuner device with two data pins as
> shown +below.
> +
> ++---------------------+                +---------------------+
> +|                     |-----SCK------->|CLK                  |
> +|       Master        |-----SS-------->|SYNC  DRIFn (slave)  |
> +|                     |-----SD0------->|D0                   |
> +|                     |-----SD1------->|D1                   |
> ++---------------------+                +---------------------+
> +
> +pfc {
> +	...
> +
> +	drif0_pins: drif0 {
> +		groups = "drif0_ctrl_a", "drif0_data0_a",
> +				 "drif0_data1_a";
> +		function = "drif0";
> +	};
> +	...
> +}
> +
> +&drif00 {
> +	pinctrl-0 = <&drif0_pins>;
> +	pinctrl-names = "default";
> +	renesas,syncac-active = <1>;
> +	renesas,primary-bond;
> +	status = "okay";
> +	port {
> +		drif0_ep: endpoint {
> +		     remote-endpoint = <&tuner_ep>;
> +		};
> +	};
> +};
> +
> +&drif01 {
> +	status = "okay";
> +};
> +
> +(2) Internal channel 1 alone is enabled:
> +----------------------------------------
> +
> +When interfacing with a third party tuner device with one data pin as shown
> +below.
> +
> ++---------------------+                +---------------------+
> +|                     |-----SCK------->|CLK                  |
> +|       Master        |-----SS-------->|SYNC  DRIFn (slave)  |
> +|                     |                |D0 (unused)          |
> +|                     |-----SD-------->|D1                   |
> ++---------------------+                +---------------------+
> +
> +pfc {
> +	...
> +
> +	drif0_pins: drif0 {
> +		groups = "drif0_ctrl_a", "drif0_data1_a";
> +		function = "drif0";
> +	};
> +	...
> +}
> +
> +&drif01 {
> +	pinctrl-0 = <&drif0_pins>;
> +	pinctrl-names = "default";
> +	renesas,syncac-active = <0>;
> +	status = "okay";
> +	port {
> +		drif0_ep: endpoint {
> +		     remote-endpoint = <&tuner_ep>;
> +		};
> +	};
> +};

-- 
Regards,

Laurent Pinchart


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

* Re: [PATCH v2 6/7] dt-bindings: media: Add Renesas R-Car DRIF binding
  2016-12-22 17:05         ` Laurent Pinchart
  (?)
@ 2016-12-22 19:05         ` Geert Uytterhoeven
  2017-01-03 15:20           ` Ramesh Shanmugasundaram
  -1 siblings, 1 reply; 60+ messages in thread
From: Geert Uytterhoeven @ 2016-12-22 19:05 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Ramesh Shanmugasundaram, Rob Herring, Mark Rutland,
	Mauro Carvalho Chehab, Hans Verkuil, Sakari Ailus,
	Antti Palosaari, Chris Paterson, Geert Uytterhoeven,
	Linux Media Mailing List, devicetree, Linux-Renesas

Hi Laurent,

On Thu, Dec 22, 2016 at 6:05 PM, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
> On Wednesday 21 Dec 2016 08:10:37 Ramesh Shanmugasundaram wrote:
>> Add binding documentation for Renesas R-Car Digital Radio Interface
>> (DRIF) controller.
>>
>> Signed-off-by: Ramesh Shanmugasundaram
>> <ramesh.shanmugasundaram@bp.renesas.com> ---
>>  .../devicetree/bindings/media/renesas,drif.txt     | 202 ++++++++++++++++++
>>  1 file changed, 202 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/media/renesas,drif.txt
>>
>> diff --git a/Documentation/devicetree/bindings/media/renesas,drif.txt
>> b/Documentation/devicetree/bindings/media/renesas,drif.txt new file mode
>> 100644
>> index 0000000..1f3feaf
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt

>> +Optional properties of an internal channel when:
>> +     - It is the only enabled channel of the bond (or)
>> +     - If it acts as primary among enabled bonds
>> +--------------------------------------------------------
>> +- renesas,syncmd       : sync mode
>> +                      0 (Frame start sync pulse mode. 1-bit width pulse
>> +                         indicates start of a frame)
>> +                      1 (L/R sync or I2S mode) (default)
>> +- renesas,lsb-first    : empty property indicates lsb bit is received
>> first.
>> +                      When not defined msb bit is received first (default)
>> +- renesas,syncac-active: Indicates sync signal polarity, 0/1 for low/high
>> +                      respectively. The default is 1 (active high)
>> +- renesas,dtdl         : delay between sync signal and start of reception.
>> +                      The possible values are represented in 0.5 clock
>> +                      cycle units and the range is 0 to 4. The default
>> +                      value is 2 (i.e.) 1 clock cycle delay.
>> +- renesas,syncdl       : delay between end of reception and sync signal
>> edge.
>> +                      The possible values are represented in 0.5 clock
>> +                      cycle units and the range is 0 to 4 & 6. The default
>> +                      value is 0 (i.e.) no delay.
>
> Most of these properties are pretty similar to the video bus properties
> defined at the endpoint level in
> Documentation/devicetree/bindings/media/video-interfaces.txt. I believe it
> would make sense to use OF graph and try to standardize these properties
> similarly.

Note that the last two properties match the those in
Documentation/devicetree/bindings/spi/sh-msiof.txt.
We may want to use one DRIF channel as a plain SPI slave with the
(modified) MSIOF driver in the future.

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

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

* RE: [PATCH v2 6/7] dt-bindings: media: Add Renesas R-Car DRIF binding
  2016-12-22 19:05         ` Geert Uytterhoeven
@ 2017-01-03 15:20           ` Ramesh Shanmugasundaram
  2017-01-09 13:36             ` Hans Verkuil
  0 siblings, 1 reply; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2017-01-03 15:20 UTC (permalink / raw)
  To: Geert Uytterhoeven, Laurent Pinchart
  Cc: Rob Herring, Mark Rutland, Mauro Carvalho Chehab, Hans Verkuil,
	Sakari Ailus, Antti Palosaari, Chris Paterson,
	Geert Uytterhoeven, Linux Media Mailing List, devicetree,
	Linux-Renesas

Hi Laurent, Geert,

Thanks for the review comments.

> > On Wednesday 21 Dec 2016 08:10:37 Ramesh Shanmugasundaram wrote:
> >> Add binding documentation for Renesas R-Car Digital Radio Interface
> >> (DRIF) controller.
> >>
> >> Signed-off-by: Ramesh Shanmugasundaram
> >> <ramesh.shanmugasundaram@bp.renesas.com> ---
> >>  .../devicetree/bindings/media/renesas,drif.txt     | 202
> ++++++++++++++++++
> >>  1 file changed, 202 insertions(+)
> >>  create mode 100644
> >> Documentation/devicetree/bindings/media/renesas,drif.txt
> >>
> >> diff --git a/Documentation/devicetree/bindings/media/renesas,drif.txt
> >> b/Documentation/devicetree/bindings/media/renesas,drif.txt new file
> >> mode
> >> 100644
> >> index 0000000..1f3feaf
> >> --- /dev/null
> >> +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> 
> >> +Optional properties of an internal channel when:
> >> +     - It is the only enabled channel of the bond (or)
> >> +     - If it acts as primary among enabled bonds
> >> +--------------------------------------------------------
> >> +- renesas,syncmd       : sync mode
> >> +                      0 (Frame start sync pulse mode. 1-bit width
> pulse
> >> +                         indicates start of a frame)
> >> +                      1 (L/R sync or I2S mode) (default)
> >> +- renesas,lsb-first    : empty property indicates lsb bit is received
> >> first.
> >> +                      When not defined msb bit is received first
> >> +(default)
> >> +- renesas,syncac-active: Indicates sync signal polarity, 0/1 for
> low/high
> >> +                      respectively. The default is 1 (active high)
> >> +- renesas,dtdl         : delay between sync signal and start of
> reception.
> >> +                      The possible values are represented in 0.5 clock
> >> +                      cycle units and the range is 0 to 4. The default
> >> +                      value is 2 (i.e.) 1 clock cycle delay.
> >> +- renesas,syncdl       : delay between end of reception and sync
> signal
> >> edge.
> >> +                      The possible values are represented in 0.5 clock
> >> +                      cycle units and the range is 0 to 4 & 6. The
> default
> >> +                      value is 0 (i.e.) no delay.
> >
> > Most of these properties are pretty similar to the video bus
> > properties defined at the endpoint level in
> > Documentation/devicetree/bindings/media/video-interfaces.txt. I
> > believe it would make sense to use OF graph and try to standardize
> > these properties similarly.
> 
> Note that the last two properties match the those in
> Documentation/devicetree/bindings/spi/sh-msiof.txt.
> We may want to use one DRIF channel as a plain SPI slave with the
> (modified) MSIOF driver in the future.

Should I leave it as it is or modify these as in video-interfaces.txt? Shall we conclude on this please?

Thanks,
Ramesh

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

* Re: [PATCH v2 6/7] dt-bindings: media: Add Renesas R-Car DRIF binding
  2017-01-03 15:20           ` Ramesh Shanmugasundaram
@ 2017-01-09 13:36             ` Hans Verkuil
  2017-01-09 14:42               ` Ramesh Shanmugasundaram
       [not found]               ` <cca1ade8-01ef-8eab-f4b1-7dd7f204fdea-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
  0 siblings, 2 replies; 60+ messages in thread
From: Hans Verkuil @ 2017-01-09 13:36 UTC (permalink / raw)
  To: Ramesh Shanmugasundaram, Geert Uytterhoeven, Laurent Pinchart
  Cc: Rob Herring, Mark Rutland, Mauro Carvalho Chehab, Sakari Ailus,
	Antti Palosaari, Chris Paterson, Geert Uytterhoeven,
	Linux Media Mailing List, devicetree, Linux-Renesas

On 01/03/2017 04:20 PM, Ramesh Shanmugasundaram wrote:
> Hi Laurent, Geert,
> 
> Thanks for the review comments.
> 
>>> On Wednesday 21 Dec 2016 08:10:37 Ramesh Shanmugasundaram wrote:
>>>> Add binding documentation for Renesas R-Car Digital Radio Interface
>>>> (DRIF) controller.
>>>>
>>>> Signed-off-by: Ramesh Shanmugasundaram
>>>> <ramesh.shanmugasundaram@bp.renesas.com> ---
>>>>  .../devicetree/bindings/media/renesas,drif.txt     | 202
>> ++++++++++++++++++
>>>>  1 file changed, 202 insertions(+)
>>>>  create mode 100644
>>>> Documentation/devicetree/bindings/media/renesas,drif.txt
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/media/renesas,drif.txt
>>>> b/Documentation/devicetree/bindings/media/renesas,drif.txt new file
>>>> mode
>>>> 100644
>>>> index 0000000..1f3feaf
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
>>
>>>> +Optional properties of an internal channel when:
>>>> +     - It is the only enabled channel of the bond (or)
>>>> +     - If it acts as primary among enabled bonds
>>>> +--------------------------------------------------------
>>>> +- renesas,syncmd       : sync mode
>>>> +                      0 (Frame start sync pulse mode. 1-bit width
>> pulse
>>>> +                         indicates start of a frame)
>>>> +                      1 (L/R sync or I2S mode) (default)
>>>> +- renesas,lsb-first    : empty property indicates lsb bit is received
>>>> first.
>>>> +                      When not defined msb bit is received first
>>>> +(default)
>>>> +- renesas,syncac-active: Indicates sync signal polarity, 0/1 for
>> low/high

Shouldn't this be 'renesas,sync-active' instead of syncac-active?

I'm not sure if syncac is intended or if it is a typo.

>>>> +                      respectively. The default is 1 (active high)
>>>> +- renesas,dtdl         : delay between sync signal and start of
>> reception.
>>>> +                      The possible values are represented in 0.5 clock
>>>> +                      cycle units and the range is 0 to 4. The default
>>>> +                      value is 2 (i.e.) 1 clock cycle delay.
>>>> +- renesas,syncdl       : delay between end of reception and sync
>> signal
>>>> edge.
>>>> +                      The possible values are represented in 0.5 clock
>>>> +                      cycle units and the range is 0 to 4 & 6. The
>> default
>>>> +                      value is 0 (i.e.) no delay.
>>>
>>> Most of these properties are pretty similar to the video bus
>>> properties defined at the endpoint level in
>>> Documentation/devicetree/bindings/media/video-interfaces.txt. I
>>> believe it would make sense to use OF graph and try to standardize
>>> these properties similarly.

Other than sync-active, is there really anything else that is similar? And
even the sync-active isn't a good fit since here there is only one sync
signal instead of two for video (h and vsync).

Regards,

	Hans

>> Note that the last two properties match the those in
>> Documentation/devicetree/bindings/spi/sh-msiof.txt.
>> We may want to use one DRIF channel as a plain SPI slave with the
>> (modified) MSIOF driver in the future.
> 
> Should I leave it as it is or modify these as in video-interfaces.txt? Shall we conclude on this please?
> 
> Thanks,
> Ramesh
> N�����r��y���b�X��ǧv�^�)޺{.n�+����{���bj)���w*\x1fjg���\x1e�����ݢj/���z�ޖ��2�ޙ���&�)ߡ�a��\x7f��\x1e�G���h�\x0f�j:+v���w�٥
> 

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

* RE: [PATCH v2 6/7] dt-bindings: media: Add Renesas R-Car DRIF binding
  2017-01-09 13:36             ` Hans Verkuil
@ 2017-01-09 14:42               ` Ramesh Shanmugasundaram
       [not found]               ` <cca1ade8-01ef-8eab-f4b1-7dd7f204fdea-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
  1 sibling, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2017-01-09 14:42 UTC (permalink / raw)
  To: Hans Verkuil, Geert Uytterhoeven, Laurent Pinchart
  Cc: Rob Herring, Mark Rutland, Mauro Carvalho Chehab, Sakari Ailus,
	Antti Palosaari, Chris Paterson, Geert Uytterhoeven,
	Linux Media Mailing List, devicetree, Linux-Renesas

Hi Hans,

Thanks for the review.

> >>> On Wednesday 21 Dec 2016 08:10:37 Ramesh Shanmugasundaram wrote:
> >>>> Add binding documentation for Renesas R-Car Digital Radio Interface
> >>>> (DRIF) controller.
> >>>>
> >>>> Signed-off-by: Ramesh Shanmugasundaram
> >>>> <ramesh.shanmugasundaram@bp.renesas.com> ---
> >>>>  .../devicetree/bindings/media/renesas,drif.txt     | 202
> >> ++++++++++++++++++
> >>>>  1 file changed, 202 insertions(+)
> >>>>  create mode 100644
> >>>> Documentation/devicetree/bindings/media/renesas,drif.txt
> >>>>
> >>>> diff --git
> >>>> a/Documentation/devicetree/bindings/media/renesas,drif.txt
> >>>> b/Documentation/devicetree/bindings/media/renesas,drif.txt new file
> >>>> mode
> >>>> 100644
> >>>> index 0000000..1f3feaf
> >>>> --- /dev/null
> >>>> +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> >>
> >>>> +Optional properties of an internal channel when:
> >>>> +     - It is the only enabled channel of the bond (or)
> >>>> +     - If it acts as primary among enabled bonds
> >>>> +--------------------------------------------------------
> >>>> +- renesas,syncmd       : sync mode
> >>>> +                      0 (Frame start sync pulse mode. 1-bit width
> >> pulse
> >>>> +                         indicates start of a frame)
> >>>> +                      1 (L/R sync or I2S mode) (default)
> >>>> +- renesas,lsb-first    : empty property indicates lsb bit is
> received
> >>>> first.
> >>>> +                      When not defined msb bit is received first
> >>>> +(default)
> >>>> +- renesas,syncac-active: Indicates sync signal polarity, 0/1 for
> >> low/high
> 
> Shouldn't this be 'renesas,sync-active' instead of syncac-active?
> 
> I'm not sure if syncac is intended or if it is a typo.

Yes, "syncac" is intended. I kept the same name as in h/w manual for easy reference. Same for other properties - syncmd, dtdl & syncdl.

> 
> >>>> +                      respectively. The default is 1 (active high)
> >>>> +- renesas,dtdl         : delay between sync signal and start of
> >> reception.
> >>>> +                      The possible values are represented in 0.5
> clock
> >>>> +                      cycle units and the range is 0 to 4. The
> default
> >>>> +                      value is 2 (i.e.) 1 clock cycle delay.
> >>>> +- renesas,syncdl       : delay between end of reception and sync
> >> signal
> >>>> edge.
> >>>> +                      The possible values are represented in 0.5
> clock
> >>>> +                      cycle units and the range is 0 to 4 & 6. The
> >> default
> >>>> +                      value is 0 (i.e.) no delay.
> >>>
> >>> Most of these properties are pretty similar to the video bus
> >>> properties defined at the endpoint level in
> >>> Documentation/devicetree/bindings/media/video-interfaces.txt. I
> >>> believe it would make sense to use OF graph and try to standardize
> >>> these properties similarly.
> 
> Other than sync-active, is there really anything else that is similar? And
> even the sync-active isn't a good fit since here there is only one sync
> signal instead of two for video (h and vsync).
> 
> Regards,
> 
> 	Hans
> 

Thanks,
Ramesh

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

* Re: [PATCH v2 6/7] dt-bindings: media: Add Renesas R-Car DRIF binding
  2017-01-09 13:36             ` Hans Verkuil
@ 2017-01-09 23:09                   ` Laurent Pinchart
       [not found]               ` <cca1ade8-01ef-8eab-f4b1-7dd7f204fdea-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
  1 sibling, 0 replies; 60+ messages in thread
From: Laurent Pinchart @ 2017-01-09 23:09 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Ramesh Shanmugasundaram, Geert Uytterhoeven, Rob Herring,
	Mark Rutland, Mauro Carvalho Chehab, Sakari Ailus,
	Antti Palosaari, Chris Paterson, Geert Uytterhoeven,
	Linux Media Mailing List, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Linux-Renesas

Hello Hans,

On Monday 09 Jan 2017 14:36:55 Hans Verkuil wrote:
> On 01/03/2017 04:20 PM, Ramesh Shanmugasundaram wrote:
> >>> On Wednesday 21 Dec 2016 08:10:37 Ramesh Shanmugasundaram wrote:
> >>>> Add binding documentation for Renesas R-Car Digital Radio Interface
> >>>> (DRIF) controller.
> >>>> 
> >>>> Signed-off-by: Ramesh Shanmugasundaram
> >>>> <ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org> ---
> >>>> 
> >>>>  .../devicetree/bindings/media/renesas,drif.txt     | 202 +++++++++++++
> >>>>  1 file changed, 202 insertions(+)
> >>>>  create mode 100644
> >>>> 
> >>>> Documentation/devicetree/bindings/media/renesas,drif.txt
> >>>> 
> >>>> diff --git a/Documentation/devicetree/bindings/media/renesas,drif.txt
> >>>> b/Documentation/devicetree/bindings/media/renesas,drif.txt new file
> >>>> mode 100644
> >>>> index 0000000..1f3feaf
> >>>> --- /dev/null
> >>>> +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> >>>> 
> >>>> +Optional properties of an internal channel when:
> >>>> +     - It is the only enabled channel of the bond (or)
> >>>> +     - If it acts as primary among enabled bonds
> >>>> +--------------------------------------------------------
> >>>> +- renesas,syncmd       : sync mode
> >>>> +                      0 (Frame start sync pulse mode. 1-bit width
> >>>> pulse
> >>>> +                         indicates start of a frame)
> >>>> +                      1 (L/R sync or I2S mode) (default)
> >>>> +- renesas,lsb-first    : empty property indicates lsb bit is received
> >>>> first.
> >>>> +                      When not defined msb bit is received first
> >>>> +(default)
> >>>> +- renesas,syncac-active: Indicates sync signal polarity, 0/1 for
> >>>> low/high
> 
> Shouldn't this be 'renesas,sync-active' instead of syncac-active?
> 
> I'm not sure if syncac is intended or if it is a typo.
> 
> >>>> +                      respectively. The default is 1 (active high)
> >>>> +- renesas,dtdl         : delay between sync signal and start of
> >>>> reception.
> >>>> +                      The possible values are represented in 0.5 clock
> >>>> +                      cycle units and the range is 0 to 4. The default
> >>>> +                      value is 2 (i.e.) 1 clock cycle delay.
> >>>> +- renesas,syncdl       : delay between end of reception and sync
> >>>> signal edge.
> >>>> +                      The possible values are represented in 0.5 clock
> >>>> +                      cycle units and the range is 0 to 4 & 6. The
> >>>> default
> >>>> +                      value is 0 (i.e.) no delay.
> >>> 
> >>> Most of these properties are pretty similar to the video bus
> >>> properties defined at the endpoint level in
> >>> Documentation/devicetree/bindings/media/video-interfaces.txt. I
> >>> believe it would make sense to use OF graph and try to standardize
> >>> these properties similarly.
> 
> Other than sync-active, is there really anything else that is similar? And
> even the sync-active isn't a good fit since here there is only one sync
> signal instead of two for video (h and vsync).

That's why I said similar, not identical :-) My point is that, if we consider 
that we could connect multiple sources to the DRIF, using OF graph would make 
sense, and the above properties should then be defined per endpoint. If we 
define them per endpoint we should then also try standardize the ones that are 
not really Renesas-specific (that's at least syncac-active). For the syncmd 
and lsb-first properties, it could also make sense to query them from the 
connected subdev at runtime, as they're similar in purpose to formats and 
media bus configuration (struct v4l2_mbus_config).

I'm not an SDR expert, so I'd like to have your opinion on this.

> >> Note that the last two properties match the those in
> >> Documentation/devicetree/bindings/spi/sh-msiof.txt.
> >> We may want to use one DRIF channel as a plain SPI slave with the
> >> (modified) MSIOF driver in the future.
> > 
> > Should I leave it as it is or modify these as in video-interfaces.txt?
> > Shall we conclude on this please?

-- 
Regards,

Laurent Pinchart

--
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 v2 6/7] dt-bindings: media: Add Renesas R-Car DRIF binding
@ 2017-01-09 23:09                   ` Laurent Pinchart
  0 siblings, 0 replies; 60+ messages in thread
From: Laurent Pinchart @ 2017-01-09 23:09 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Ramesh Shanmugasundaram, Geert Uytterhoeven, Rob Herring,
	Mark Rutland, Mauro Carvalho Chehab, Sakari Ailus,
	Antti Palosaari, Chris Paterson, Geert Uytterhoeven,
	Linux Media Mailing List, devicetree, Linux-Renesas

Hello Hans,

On Monday 09 Jan 2017 14:36:55 Hans Verkuil wrote:
> On 01/03/2017 04:20 PM, Ramesh Shanmugasundaram wrote:
> >>> On Wednesday 21 Dec 2016 08:10:37 Ramesh Shanmugasundaram wrote:
> >>>> Add binding documentation for Renesas R-Car Digital Radio Interface
> >>>> (DRIF) controller.
> >>>> 
> >>>> Signed-off-by: Ramesh Shanmugasundaram
> >>>> <ramesh.shanmugasundaram@bp.renesas.com> ---
> >>>> 
> >>>>  .../devicetree/bindings/media/renesas,drif.txt     | 202 +++++++++++++
> >>>>  1 file changed, 202 insertions(+)
> >>>>  create mode 100644
> >>>> 
> >>>> Documentation/devicetree/bindings/media/renesas,drif.txt
> >>>> 
> >>>> diff --git a/Documentation/devicetree/bindings/media/renesas,drif.txt
> >>>> b/Documentation/devicetree/bindings/media/renesas,drif.txt new file
> >>>> mode 100644
> >>>> index 0000000..1f3feaf
> >>>> --- /dev/null
> >>>> +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> >>>> 
> >>>> +Optional properties of an internal channel when:
> >>>> +     - It is the only enabled channel of the bond (or)
> >>>> +     - If it acts as primary among enabled bonds
> >>>> +--------------------------------------------------------
> >>>> +- renesas,syncmd       : sync mode
> >>>> +                      0 (Frame start sync pulse mode. 1-bit width
> >>>> pulse
> >>>> +                         indicates start of a frame)
> >>>> +                      1 (L/R sync or I2S mode) (default)
> >>>> +- renesas,lsb-first    : empty property indicates lsb bit is received
> >>>> first.
> >>>> +                      When not defined msb bit is received first
> >>>> +(default)
> >>>> +- renesas,syncac-active: Indicates sync signal polarity, 0/1 for
> >>>> low/high
> 
> Shouldn't this be 'renesas,sync-active' instead of syncac-active?
> 
> I'm not sure if syncac is intended or if it is a typo.
> 
> >>>> +                      respectively. The default is 1 (active high)
> >>>> +- renesas,dtdl         : delay between sync signal and start of
> >>>> reception.
> >>>> +                      The possible values are represented in 0.5 clock
> >>>> +                      cycle units and the range is 0 to 4. The default
> >>>> +                      value is 2 (i.e.) 1 clock cycle delay.
> >>>> +- renesas,syncdl       : delay between end of reception and sync
> >>>> signal edge.
> >>>> +                      The possible values are represented in 0.5 clock
> >>>> +                      cycle units and the range is 0 to 4 & 6. The
> >>>> default
> >>>> +                      value is 0 (i.e.) no delay.
> >>> 
> >>> Most of these properties are pretty similar to the video bus
> >>> properties defined at the endpoint level in
> >>> Documentation/devicetree/bindings/media/video-interfaces.txt. I
> >>> believe it would make sense to use OF graph and try to standardize
> >>> these properties similarly.
> 
> Other than sync-active, is there really anything else that is similar? And
> even the sync-active isn't a good fit since here there is only one sync
> signal instead of two for video (h and vsync).

That's why I said similar, not identical :-) My point is that, if we consider 
that we could connect multiple sources to the DRIF, using OF graph would make 
sense, and the above properties should then be defined per endpoint. If we 
define them per endpoint we should then also try standardize the ones that are 
not really Renesas-specific (that's at least syncac-active). For the syncmd 
and lsb-first properties, it could also make sense to query them from the 
connected subdev at runtime, as they're similar in purpose to formats and 
media bus configuration (struct v4l2_mbus_config).

I'm not an SDR expert, so I'd like to have your opinion on this.

> >> Note that the last two properties match the those in
> >> Documentation/devicetree/bindings/spi/sh-msiof.txt.
> >> We may want to use one DRIF channel as a plain SPI slave with the
> >> (modified) MSIOF driver in the future.
> > 
> > Should I leave it as it is or modify these as in video-interfaces.txt?
> > Shall we conclude on this please?

-- 
Regards,

Laurent Pinchart


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

* RE: [PATCH v2 6/7] dt-bindings: media: Add Renesas R-Car DRIF binding
  2017-01-09 23:09                   ` Laurent Pinchart
@ 2017-01-10  9:31                     ` Ramesh Shanmugasundaram
  -1 siblings, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2017-01-10  9:31 UTC (permalink / raw)
  To: Laurent Pinchart, Hans Verkuil
  Cc: Geert Uytterhoeven, Rob Herring, Mark Rutland,
	Mauro Carvalho Chehab, Sakari Ailus, Antti Palosaari,
	Chris Paterson, Geert Uytterhoeven, Linux Media Mailing List,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Linux-Renesas

Hi Laurent,

> > >>> On Wednesday 21 Dec 2016 08:10:37 Ramesh Shanmugasundaram wrote:
> > >>>> Add binding documentation for Renesas R-Car Digital Radio
> > >>>> Interface
> > >>>> (DRIF) controller.
> > >>>>
> > >>>> Signed-off-by: Ramesh Shanmugasundaram
> > >>>> <ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org> ---
> > >>>>
> > >>>>  .../devicetree/bindings/media/renesas,drif.txt     | 202
> +++++++++++++
> > >>>>  1 file changed, 202 insertions(+)  create mode 100644
> > >>>>
> > >>>> Documentation/devicetree/bindings/media/renesas,drif.txt
> > >>>>
> > >>>> diff --git
> > >>>> a/Documentation/devicetree/bindings/media/renesas,drif.txt
> > >>>> b/Documentation/devicetree/bindings/media/renesas,drif.txt new
> > >>>> file mode 100644 index 0000000..1f3feaf
> > >>>> --- /dev/null
> > >>>> +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> > >>>>
> > >>>> +Optional properties of an internal channel when:
> > >>>> +     - It is the only enabled channel of the bond (or)
> > >>>> +     - If it acts as primary among enabled bonds
> > >>>> +--------------------------------------------------------
> > >>>> +- renesas,syncmd       : sync mode
> > >>>> +                      0 (Frame start sync pulse mode. 1-bit
> > >>>> +width
> > >>>> pulse
> > >>>> +                         indicates start of a frame)
> > >>>> +                      1 (L/R sync or I2S mode) (default)
> > >>>> +- renesas,lsb-first    : empty property indicates lsb bit is
> received
> > >>>> first.
> > >>>> +                      When not defined msb bit is received first
> > >>>> +(default)
> > >>>> +- renesas,syncac-active: Indicates sync signal polarity, 0/1 for
> > >>>> low/high
> >
> > Shouldn't this be 'renesas,sync-active' instead of syncac-active?
> >
> > I'm not sure if syncac is intended or if it is a typo.
> >
> > >>>> +                      respectively. The default is 1 (active high)
> > >>>> +- renesas,dtdl         : delay between sync signal and start of
> > >>>> reception.
> > >>>> +                      The possible values are represented in 0.5
> clock
> > >>>> +                      cycle units and the range is 0 to 4. The
> default
> > >>>> +                      value is 2 (i.e.) 1 clock cycle delay.
> > >>>> +- renesas,syncdl       : delay between end of reception and sync
> > >>>> signal edge.
> > >>>> +                      The possible values are represented in 0.5
> clock
> > >>>> +                      cycle units and the range is 0 to 4 & 6.
> > >>>> + The
> > >>>> default
> > >>>> +                      value is 0 (i.e.) no delay.
> > >>>
> > >>> Most of these properties are pretty similar to the video bus
> > >>> properties defined at the endpoint level in
> > >>> Documentation/devicetree/bindings/media/video-interfaces.txt. I
> > >>> believe it would make sense to use OF graph and try to standardize
> > >>> these properties similarly.
> >
> > Other than sync-active, is there really anything else that is similar?
> > And even the sync-active isn't a good fit since here there is only one
> > sync signal instead of two for video (h and vsync).
> 
> That's why I said similar, not identical :-) My point is that, if we
> consider that we could connect multiple sources to the DRIF, using OF
> graph would make sense, and the above properties should then be defined
> per endpoint.

Thanks for the clarifications. I have some questions.

- Assuming two devices are interfaced with DRIF and they are represented using two endpoints, the control signal related properties of DRIF might still need to be same for both endpoints? For e.g. syncac-active cannot be different in both endpoints?

- I suppose "lsb-first", "dtdl" & "syncdl" may be defined per endpoint. However, h/w manual says same register values needs to be programmed for both the internal channels of a channel. Same with "syncmd" property.

We could still define them as per endpoint property with a note that they need to be same. But I am not sure if that is what you intended?

 If we define them per endpoint we should then also try
> standardize the ones that are not really Renesas-specific (that's at least
> syncac-active).

OK. I will call it "sync-active".

 For the syncmd and lsb-first properties, it could also
> make sense to query them from the connected subdev at runtime, as they're
> similar in purpose to formats and media bus configuration (struct
> v4l2_mbus_config).

May I know in bit more detail about what you had in mind? Please correct me if my understanding is wrong here but when I looked at the code

1) mbus_config is part of subdev_video_ops only. I assume we don't want to support this as part of tuner subdev. The next closest is pad_ops with "struct v4l2_mbus_framefmt" but it is fully video specific config unless I come up with new MEDIA_BUS_FMT_xxxx in media-bus-format.h and use the code field? For e.g.
	
#define MEDIA_BUS_FMT_SDR_I2S_PADHI_BE       0x7001
#define MEDIA_BUS_FMT_SDR_I2S_PADHI_LE       0x7002

2) The framework does not seem to mandate pad ops for all subdev. As the tuner can be any third party subdev, is it fair to assume that these properties can be queried from subdev?

3) Assuming pad ops is not available on the subdev shouldn't we still need a way to define these properties on DRIF DT?

> 
> I'm not an SDR expert, so I'd like to have your opinion on this.
> 
> > >> Note that the last two properties match the those in
> > >> Documentation/devicetree/bindings/spi/sh-msiof.txt.
> > >> We may want to use one DRIF channel as a plain SPI slave with the
> > >> (modified) MSIOF driver in the future.
> > >
> > > Should I leave it as it is or modify these as in video-interfaces.txt?
> > > Shall we conclude on this please?
> 

Thanks,
Ramesh

--
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 v2 6/7] dt-bindings: media: Add Renesas R-Car DRIF binding
@ 2017-01-10  9:31                     ` Ramesh Shanmugasundaram
  0 siblings, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2017-01-10  9:31 UTC (permalink / raw)
  To: Laurent Pinchart, Hans Verkuil
  Cc: Geert Uytterhoeven, Rob Herring, Mark Rutland,
	Mauro Carvalho Chehab, Sakari Ailus, Antti Palosaari,
	Chris Paterson, Geert Uytterhoeven, Linux Media Mailing List,
	devicetree, Linux-Renesas

Hi Laurent,

> > >>> On Wednesday 21 Dec 2016 08:10:37 Ramesh Shanmugasundaram wrote:
> > >>>> Add binding documentation for Renesas R-Car Digital Radio
> > >>>> Interface
> > >>>> (DRIF) controller.
> > >>>>
> > >>>> Signed-off-by: Ramesh Shanmugasundaram
> > >>>> <ramesh.shanmugasundaram@bp.renesas.com> ---
> > >>>>
> > >>>>  .../devicetree/bindings/media/renesas,drif.txt     | 202
> +++++++++++++
> > >>>>  1 file changed, 202 insertions(+)  create mode 100644
> > >>>>
> > >>>> Documentation/devicetree/bindings/media/renesas,drif.txt
> > >>>>
> > >>>> diff --git
> > >>>> a/Documentation/devicetree/bindings/media/renesas,drif.txt
> > >>>> b/Documentation/devicetree/bindings/media/renesas,drif.txt new
> > >>>> file mode 100644 index 0000000..1f3feaf
> > >>>> --- /dev/null
> > >>>> +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> > >>>>
> > >>>> +Optional properties of an internal channel when:
> > >>>> +     - It is the only enabled channel of the bond (or)
> > >>>> +     - If it acts as primary among enabled bonds
> > >>>> +--------------------------------------------------------
> > >>>> +- renesas,syncmd       : sync mode
> > >>>> +                      0 (Frame start sync pulse mode. 1-bit
> > >>>> +width
> > >>>> pulse
> > >>>> +                         indicates start of a frame)
> > >>>> +                      1 (L/R sync or I2S mode) (default)
> > >>>> +- renesas,lsb-first    : empty property indicates lsb bit is
> received
> > >>>> first.
> > >>>> +                      When not defined msb bit is received first
> > >>>> +(default)
> > >>>> +- renesas,syncac-active: Indicates sync signal polarity, 0/1 for
> > >>>> low/high
> >
> > Shouldn't this be 'renesas,sync-active' instead of syncac-active?
> >
> > I'm not sure if syncac is intended or if it is a typo.
> >
> > >>>> +                      respectively. The default is 1 (active high)
> > >>>> +- renesas,dtdl         : delay between sync signal and start of
> > >>>> reception.
> > >>>> +                      The possible values are represented in 0.5
> clock
> > >>>> +                      cycle units and the range is 0 to 4. The
> default
> > >>>> +                      value is 2 (i.e.) 1 clock cycle delay.
> > >>>> +- renesas,syncdl       : delay between end of reception and sync
> > >>>> signal edge.
> > >>>> +                      The possible values are represented in 0.5
> clock
> > >>>> +                      cycle units and the range is 0 to 4 & 6.
> > >>>> + The
> > >>>> default
> > >>>> +                      value is 0 (i.e.) no delay.
> > >>>
> > >>> Most of these properties are pretty similar to the video bus
> > >>> properties defined at the endpoint level in
> > >>> Documentation/devicetree/bindings/media/video-interfaces.txt. I
> > >>> believe it would make sense to use OF graph and try to standardize
> > >>> these properties similarly.
> >
> > Other than sync-active, is there really anything else that is similar?
> > And even the sync-active isn't a good fit since here there is only one
> > sync signal instead of two for video (h and vsync).
> 
> That's why I said similar, not identical :-) My point is that, if we
> consider that we could connect multiple sources to the DRIF, using OF
> graph would make sense, and the above properties should then be defined
> per endpoint.

Thanks for the clarifications. I have some questions.

- Assuming two devices are interfaced with DRIF and they are represented using two endpoints, the control signal related properties of DRIF might still need to be same for both endpoints? For e.g. syncac-active cannot be different in both endpoints?

- I suppose "lsb-first", "dtdl" & "syncdl" may be defined per endpoint. However, h/w manual says same register values needs to be programmed for both the internal channels of a channel. Same with "syncmd" property.

We could still define them as per endpoint property with a note that they need to be same. But I am not sure if that is what you intended?

 If we define them per endpoint we should then also try
> standardize the ones that are not really Renesas-specific (that's at least
> syncac-active).

OK. I will call it "sync-active".

 For the syncmd and lsb-first properties, it could also
> make sense to query them from the connected subdev at runtime, as they're
> similar in purpose to formats and media bus configuration (struct
> v4l2_mbus_config).

May I know in bit more detail about what you had in mind? Please correct me if my understanding is wrong here but when I looked at the code

1) mbus_config is part of subdev_video_ops only. I assume we don't want to support this as part of tuner subdev. The next closest is pad_ops with "struct v4l2_mbus_framefmt" but it is fully video specific config unless I come up with new MEDIA_BUS_FMT_xxxx in media-bus-format.h and use the code field? For e.g.
	
#define MEDIA_BUS_FMT_SDR_I2S_PADHI_BE       0x7001
#define MEDIA_BUS_FMT_SDR_I2S_PADHI_LE       0x7002

2) The framework does not seem to mandate pad ops for all subdev. As the tuner can be any third party subdev, is it fair to assume that these properties can be queried from subdev?

3) Assuming pad ops is not available on the subdev shouldn't we still need a way to define these properties on DRIF DT?

> 
> I'm not an SDR expert, so I'd like to have your opinion on this.
> 
> > >> Note that the last two properties match the those in
> > >> Documentation/devicetree/bindings/spi/sh-msiof.txt.
> > >> We may want to use one DRIF channel as a plain SPI slave with the
> > >> (modified) MSIOF driver in the future.
> > >
> > > Should I leave it as it is or modify these as in video-interfaces.txt?
> > > Shall we conclude on this please?
> 

Thanks,
Ramesh


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

* RE: [PATCH v2 6/7] dt-bindings: media: Add Renesas R-Car DRIF binding
  2017-01-10  9:31                     ` Ramesh Shanmugasundaram
  (?)
@ 2017-01-19 17:46                     ` Chris Paterson
  -1 siblings, 0 replies; 60+ messages in thread
From: Chris Paterson @ 2017-01-19 17:46 UTC (permalink / raw)
  To: Ramesh Shanmugasundaram, Laurent Pinchart, Hans Verkuil
  Cc: Geert Uytterhoeven, Rob Herring, Mark Rutland,
	Mauro Carvalho Chehab, Sakari Ailus, Antti Palosaari,
	Geert Uytterhoeven, Linux Media Mailing List, devicetree,
	Linux-Renesas

Hello Hans,

Do you have any further feedback on this?

Thanks, Chris


> From: Ramesh Shanmugasundaram
> Sent: 10 January 2017 09:31
> Hi Laurent,
> 
> > > >>> On Wednesday 21 Dec 2016 08:10:37 Ramesh Shanmugasundaram
> wrote:
> > > >>>> Add binding documentation for Renesas R-Car Digital Radio
> > > >>>> Interface
> > > >>>> (DRIF) controller.
> > > >>>>
> > > >>>> Signed-off-by: Ramesh Shanmugasundaram
> > > >>>> <ramesh.shanmugasundaram@bp.renesas.com> ---
> > > >>>>
> > > >>>>  .../devicetree/bindings/media/renesas,drif.txt     | 202
> > +++++++++++++
> > > >>>>  1 file changed, 202 insertions(+)  create mode 100644
> > > >>>>
> > > >>>> Documentation/devicetree/bindings/media/renesas,drif.txt
> > > >>>>
> > > >>>> diff --git
> > > >>>> a/Documentation/devicetree/bindings/media/renesas,drif.txt
> > > >>>> b/Documentation/devicetree/bindings/media/renesas,drif.txt new
> > > >>>> file mode 100644 index 0000000..1f3feaf
> > > >>>> --- /dev/null
> > > >>>> +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> > > >>>>
> > > >>>> +Optional properties of an internal channel when:
> > > >>>> +     - It is the only enabled channel of the bond (or)
> > > >>>> +     - If it acts as primary among enabled bonds
> > > >>>> +--------------------------------------------------------
> > > >>>> +- renesas,syncmd       : sync mode
> > > >>>> +                      0 (Frame start sync pulse mode. 1-bit
> > > >>>> +width
> > > >>>> pulse
> > > >>>> +                         indicates start of a frame)
> > > >>>> +                      1 (L/R sync or I2S mode) (default)
> > > >>>> +- renesas,lsb-first    : empty property indicates lsb bit is
> > received
> > > >>>> first.
> > > >>>> +                      When not defined msb bit is received
> > > >>>> +first
> > > >>>> +(default)
> > > >>>> +- renesas,syncac-active: Indicates sync signal polarity, 0/1
> > > >>>> +for
> > > >>>> low/high
> > >
> > > Shouldn't this be 'renesas,sync-active' instead of syncac-active?
> > >
> > > I'm not sure if syncac is intended or if it is a typo.
> > >
> > > >>>> +                      respectively. The default is 1 (active high)
> > > >>>> +- renesas,dtdl         : delay between sync signal and start of
> > > >>>> reception.
> > > >>>> +                      The possible values are represented in
> > > >>>> + 0.5
> > clock
> > > >>>> +                      cycle units and the range is 0 to 4. The
> > default
> > > >>>> +                      value is 2 (i.e.) 1 clock cycle delay.
> > > >>>> +- renesas,syncdl       : delay between end of reception and sync
> > > >>>> signal edge.
> > > >>>> +                      The possible values are represented in
> > > >>>> + 0.5
> > clock
> > > >>>> +                      cycle units and the range is 0 to 4 & 6.
> > > >>>> + The
> > > >>>> default
> > > >>>> +                      value is 0 (i.e.) no delay.
> > > >>>
> > > >>> Most of these properties are pretty similar to the video bus
> > > >>> properties defined at the endpoint level in
> > > >>> Documentation/devicetree/bindings/media/video-interfaces.txt. I
> > > >>> believe it would make sense to use OF graph and try to
> > > >>> standardize these properties similarly.
> > >
> > > Other than sync-active, is there really anything else that is similar?
> > > And even the sync-active isn't a good fit since here there is only
> > > one sync signal instead of two for video (h and vsync).
> >
> > That's why I said similar, not identical :-) My point is that, if we
> > consider that we could connect multiple sources to the DRIF, using OF
> > graph would make sense, and the above properties should then be
> > defined per endpoint.
> 
> Thanks for the clarifications. I have some questions.
> 
> - Assuming two devices are interfaced with DRIF and they are represented
> using two endpoints, the control signal related properties of DRIF might still
> need to be same for both endpoints? For e.g. syncac-active cannot be
> different in both endpoints?
> 
> - I suppose "lsb-first", "dtdl" & "syncdl" may be defined per endpoint.
> However, h/w manual says same register values needs to be programmed
> for both the internal channels of a channel. Same with "syncmd" property.
> 
> We could still define them as per endpoint property with a note that they
> need to be same. But I am not sure if that is what you intended?
> 
>  If we define them per endpoint we should then also try
> > standardize the ones that are not really Renesas-specific (that's at
> > least syncac-active).
> 
> OK. I will call it "sync-active".
> 
>  For the syncmd and lsb-first properties, it could also
> > make sense to query them from the connected subdev at runtime, as
> > they're similar in purpose to formats and media bus configuration
> > (struct v4l2_mbus_config).
> 
> May I know in bit more detail about what you had in mind? Please correct me
> if my understanding is wrong here but when I looked at the code
> 
> 1) mbus_config is part of subdev_video_ops only. I assume we don't want to
> support this as part of tuner subdev. The next closest is pad_ops with "struct
> v4l2_mbus_framefmt" but it is fully video specific config unless I come up
> with new MEDIA_BUS_FMT_xxxx in media-bus-format.h and use the code
> field? For e.g.
> 
> #define MEDIA_BUS_FMT_SDR_I2S_PADHI_BE       0x7001
> #define MEDIA_BUS_FMT_SDR_I2S_PADHI_LE       0x7002
> 
> 2) The framework does not seem to mandate pad ops for all subdev. As the
> tuner can be any third party subdev, is it fair to assume that these properties
> can be queried from subdev?
> 
> 3) Assuming pad ops is not available on the subdev shouldn't we still need a
> way to define these properties on DRIF DT?
> 
> >
> > I'm not an SDR expert, so I'd like to have your opinion on this.
> >
> > > >> Note that the last two properties match the those in
> > > >> Documentation/devicetree/bindings/spi/sh-msiof.txt.
> > > >> We may want to use one DRIF channel as a plain SPI slave with the
> > > >> (modified) MSIOF driver in the future.
> > > >
> > > > Should I leave it as it is or modify these as in video-interfaces.txt?
> > > > Shall we conclude on this please?
> >
> 
> Thanks,
> Ramesh

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

* Re: [PATCH v2 6/7] dt-bindings: media: Add Renesas R-Car DRIF binding
  2017-01-10  9:31                     ` Ramesh Shanmugasundaram
  (?)
  (?)
@ 2017-01-27 11:20                     ` Hans Verkuil
       [not found]                       ` <e96cb1cd-cd4a-60f2-cf46-dc837638d115-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
  -1 siblings, 1 reply; 60+ messages in thread
From: Hans Verkuil @ 2017-01-27 11:20 UTC (permalink / raw)
  To: Ramesh Shanmugasundaram, Laurent Pinchart
  Cc: Geert Uytterhoeven, Rob Herring, Mark Rutland,
	Mauro Carvalho Chehab, Sakari Ailus, Antti Palosaari,
	Chris Paterson, Geert Uytterhoeven, Linux Media Mailing List,
	devicetree, Linux-Renesas

On 01/10/2017 10:31 AM, Ramesh Shanmugasundaram wrote:
> Hi Laurent,
> 
>>>>>> On Wednesday 21 Dec 2016 08:10:37 Ramesh Shanmugasundaram wrote:
>>>>>>> Add binding documentation for Renesas R-Car Digital Radio
>>>>>>> Interface
>>>>>>> (DRIF) controller.
>>>>>>>
>>>>>>> Signed-off-by: Ramesh Shanmugasundaram
>>>>>>> <ramesh.shanmugasundaram@bp.renesas.com> ---
>>>>>>>
>>>>>>>  .../devicetree/bindings/media/renesas,drif.txt     | 202
>> +++++++++++++
>>>>>>>  1 file changed, 202 insertions(+)  create mode 100644
>>>>>>>
>>>>>>> Documentation/devicetree/bindings/media/renesas,drif.txt
>>>>>>>
>>>>>>> diff --git
>>>>>>> a/Documentation/devicetree/bindings/media/renesas,drif.txt
>>>>>>> b/Documentation/devicetree/bindings/media/renesas,drif.txt new
>>>>>>> file mode 100644 index 0000000..1f3feaf
>>>>>>> --- /dev/null
>>>>>>> +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
>>>>>>>
>>>>>>> +Optional properties of an internal channel when:
>>>>>>> +     - It is the only enabled channel of the bond (or)
>>>>>>> +     - If it acts as primary among enabled bonds
>>>>>>> +--------------------------------------------------------
>>>>>>> +- renesas,syncmd       : sync mode
>>>>>>> +                      0 (Frame start sync pulse mode. 1-bit
>>>>>>> +width
>>>>>>> pulse
>>>>>>> +                         indicates start of a frame)
>>>>>>> +                      1 (L/R sync or I2S mode) (default)
>>>>>>> +- renesas,lsb-first    : empty property indicates lsb bit is
>> received
>>>>>>> first.
>>>>>>> +                      When not defined msb bit is received first
>>>>>>> +(default)
>>>>>>> +- renesas,syncac-active: Indicates sync signal polarity, 0/1 for
>>>>>>> low/high
>>>
>>> Shouldn't this be 'renesas,sync-active' instead of syncac-active?
>>>
>>> I'm not sure if syncac is intended or if it is a typo.
>>>
>>>>>>> +                      respectively. The default is 1 (active high)
>>>>>>> +- renesas,dtdl         : delay between sync signal and start of
>>>>>>> reception.
>>>>>>> +                      The possible values are represented in 0.5
>> clock
>>>>>>> +                      cycle units and the range is 0 to 4. The
>> default
>>>>>>> +                      value is 2 (i.e.) 1 clock cycle delay.
>>>>>>> +- renesas,syncdl       : delay between end of reception and sync
>>>>>>> signal edge.
>>>>>>> +                      The possible values are represented in 0.5
>> clock
>>>>>>> +                      cycle units and the range is 0 to 4 & 6.
>>>>>>> + The
>>>>>>> default
>>>>>>> +                      value is 0 (i.e.) no delay.

Are these properties actually going to be used by anyone? Just curious.

>>>>>>
>>>>>> Most of these properties are pretty similar to the video bus
>>>>>> properties defined at the endpoint level in
>>>>>> Documentation/devicetree/bindings/media/video-interfaces.txt. I
>>>>>> believe it would make sense to use OF graph and try to standardize
>>>>>> these properties similarly.
>>>
>>> Other than sync-active, is there really anything else that is similar?
>>> And even the sync-active isn't a good fit since here there is only one
>>> sync signal instead of two for video (h and vsync).
>>
>> That's why I said similar, not identical :-) My point is that, if we
>> consider that we could connect multiple sources to the DRIF, using OF
>> graph would make sense, and the above properties should then be defined
>> per endpoint.
> 
> Thanks for the clarifications. I have some questions.
> 
> - Assuming two devices are interfaced with DRIF and they are represented using two endpoints, the control signal related properties of DRIF might still need to be same for both endpoints? For e.g. syncac-active cannot be different in both endpoints?

Usually that's the case, but HW designers are weird :-)

> 
> - I suppose "lsb-first", "dtdl" & "syncdl" may be defined per endpoint. However, h/w manual says same register values needs to be programmed for both the internal channels of a channel. Same with "syncmd" property.
> 
> We could still define them as per endpoint property with a note that they need to be same. But I am not sure if that is what you intended?
> 
>  If we define them per endpoint we should then also try
>> standardize the ones that are not really Renesas-specific (that's at least
>> syncac-active).
> 
> OK. I will call it "sync-active".

That's better, yes.

> 
>  For the syncmd and lsb-first properties, it could also
>> make sense to query them from the connected subdev at runtime, as they're
>> similar in purpose to formats and media bus configuration (struct
>> v4l2_mbus_config).

I consider this unlikely. I wouldn't spend time on that as this can always be
done later.

> May I know in bit more detail about what you had in mind? Please correct me if my understanding is wrong here but when I looked at the code
> 
> 1) mbus_config is part of subdev_video_ops only. I assume we don't want to support this as part of tuner subdev. The next closest is pad_ops with "struct v4l2_mbus_framefmt" but it is fully video specific config unless I come up with new MEDIA_BUS_FMT_xxxx in media-bus-format.h and use the code field? For e.g.
> 	
> #define MEDIA_BUS_FMT_SDR_I2S_PADHI_BE       0x7001
> #define MEDIA_BUS_FMT_SDR_I2S_PADHI_LE       0x7002
> 
> 2) The framework does not seem to mandate pad ops for all subdev. As the tuner can be any third party subdev, is it fair to assume that these properties can be queried from subdev?
> 
> 3) Assuming pad ops is not available on the subdev shouldn't we still need a way to define these properties on DRIF DT?
> 
>>
>> I'm not an SDR expert, so I'd like to have your opinion on this.

Neither am I :-)

I think using the endpoint idea does make sense since this helps in describing the
routing. I am not sure what properties to put in there, though.

Can you describe a bit more which properties belong to which syncmd mode? That will
help me make a decision.

Regards,

	Hans

>>
>>>>> Note that the last two properties match the those in
>>>>> Documentation/devicetree/bindings/spi/sh-msiof.txt.
>>>>> We may want to use one DRIF channel as a plain SPI slave with the
>>>>> (modified) MSIOF driver in the future.
>>>>
>>>> Should I leave it as it is or modify these as in video-interfaces.txt?
>>>> Shall we conclude on this please?
>>
> 
> Thanks,
> Ramesh
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* RE: [PATCH v2 6/7] dt-bindings: media: Add Renesas R-Car DRIF binding
  2017-01-27 11:20                     ` Hans Verkuil
@ 2017-01-27 13:51                           ` Ramesh Shanmugasundaram
  0 siblings, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2017-01-27 13:51 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart
  Cc: Geert Uytterhoeven, Rob Herring, Mark Rutland,
	Mauro Carvalho Chehab, Sakari Ailus, Antti Palosaari,
	Chris Paterson, Geert Uytterhoeven, Linux Media Mailing List,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Linux-Renesas

Hi Hans,

Many thanks for the response & comments.

> Subject: Re: [PATCH v2 6/7] dt-bindings: media: Add Renesas R-Car DRIF
> binding
> 
> On 01/10/2017 10:31 AM, Ramesh Shanmugasundaram wrote:
> > Hi Laurent,
> >
> >>>>>> On Wednesday 21 Dec 2016 08:10:37 Ramesh Shanmugasundaram wrote:
> >>>>>>> Add binding documentation for Renesas R-Car Digital Radio
> >>>>>>> Interface
> >>>>>>> (DRIF) controller.
> >>>>>>>
> >>>>>>> Signed-off-by: Ramesh Shanmugasundaram
> >>>>>>> <ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org> ---
> >>>>>>>
> >>>>>>>  .../devicetree/bindings/media/renesas,drif.txt     | 202
> >> +++++++++++++
> >>>>>>>  1 file changed, 202 insertions(+)  create mode 100644
> >>>>>>>
> >>>>>>> Documentation/devicetree/bindings/media/renesas,drif.txt
> >>>>>>>
> >>>>>>> diff --git
> >>>>>>> a/Documentation/devicetree/bindings/media/renesas,drif.txt
> >>>>>>> b/Documentation/devicetree/bindings/media/renesas,drif.txt new
> >>>>>>> file mode 100644 index 0000000..1f3feaf
> >>>>>>> --- /dev/null
> >>>>>>> +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> >>>>>>>
> >>>>>>> +Optional properties of an internal channel when:
> >>>>>>> +     - It is the only enabled channel of the bond (or)
> >>>>>>> +     - If it acts as primary among enabled bonds
> >>>>>>> +--------------------------------------------------------
> >>>>>>> +- renesas,syncmd       : sync mode
> >>>>>>> +                      0 (Frame start sync pulse mode. 1-bit
> >>>>>>> +width
> >>>>>>> pulse
> >>>>>>> +                         indicates start of a frame)
> >>>>>>> +                      1 (L/R sync or I2S mode) (default)
> >>>>>>> +- renesas,lsb-first    : empty property indicates lsb bit is
> >> received
> >>>>>>> first.
> >>>>>>> +                      When not defined msb bit is received
> >>>>>>> +first
> >>>>>>> +(default)
> >>>>>>> +- renesas,syncac-active: Indicates sync signal polarity, 0/1
> >>>>>>> +for
> >>>>>>> low/high
> >>>
> >>> Shouldn't this be 'renesas,sync-active' instead of syncac-active?
> >>>
> >>> I'm not sure if syncac is intended or if it is a typo.
> >>>
> >>>>>>> +                      respectively. The default is 1 (active
> high)
> >>>>>>> +- renesas,dtdl         : delay between sync signal and start of
> >>>>>>> reception.
> >>>>>>> +                      The possible values are represented in
> >>>>>>> + 0.5
> >> clock
> >>>>>>> +                      cycle units and the range is 0 to 4. The
> >> default
> >>>>>>> +                      value is 2 (i.e.) 1 clock cycle delay.
> >>>>>>> +- renesas,syncdl       : delay between end of reception and sync
> >>>>>>> signal edge.
> >>>>>>> +                      The possible values are represented in
> >>>>>>> + 0.5
> >> clock
> >>>>>>> +                      cycle units and the range is 0 to 4 & 6.
> >>>>>>> + The
> >>>>>>> default
> >>>>>>> +                      value is 0 (i.e.) no delay.
> 
> Are these properties actually going to be used by anyone? Just curious.

Yes. Each of this property should be set appropriately based on the master device it interfaces with. 

> 
> >>>>>>
> >>>>>> Most of these properties are pretty similar to the video bus
> >>>>>> properties defined at the endpoint level in
> >>>>>> Documentation/devicetree/bindings/media/video-interfaces.txt. I
> >>>>>> believe it would make sense to use OF graph and try to
> >>>>>> standardize these properties similarly.
> >>>
> >>> Other than sync-active, is there really anything else that is similar?
> >>> And even the sync-active isn't a good fit since here there is only
> >>> one sync signal instead of two for video (h and vsync).
> >>
> >> That's why I said similar, not identical :-) My point is that, if we
> >> consider that we could connect multiple sources to the DRIF, using OF
> >> graph would make sense, and the above properties should then be
> >> defined per endpoint.
> >
> > Thanks for the clarifications. I have some questions.
> >
> > - Assuming two devices are interfaced with DRIF and they are represented
> using two endpoints, the control signal related properties of DRIF might
> still need to be same for both endpoints? For e.g. syncac-active cannot be
> different in both endpoints?
> 
> Usually that's the case, but HW designers are weird :-)
> 
> >
> > - I suppose "lsb-first", "dtdl" & "syncdl" may be defined per endpoint.
> However, h/w manual says same register values needs to be programmed for
> both the internal channels of a channel. Same with "syncmd" property.
> >
> > We could still define them as per endpoint property with a note that
> they need to be same. But I am not sure if that is what you intended?
> >
> >  If we define them per endpoint we should then also try
> >> standardize the ones that are not really Renesas-specific (that's at
> >> least syncac-active).
> >
> > OK. I will call it "sync-active".
> 
> That's better, yes.

Thanks.

> 
> >
> >  For the syncmd and lsb-first properties, it could also
> >> make sense to query them from the connected subdev at runtime, as
> >> they're similar in purpose to formats and media bus configuration
> >> (struct v4l2_mbus_config).
> 
> I consider this unlikely. I wouldn't spend time on that as this can always
> be done later.
> 
> > May I know in bit more detail about what you had in mind? Please
> > correct me if my understanding is wrong here but when I looked at the
> > code
> >
> > 1) mbus_config is part of subdev_video_ops only. I assume we don't want
> to support this as part of tuner subdev. The next closest is pad_ops with
> "struct v4l2_mbus_framefmt" but it is fully video specific config unless I
> come up with new MEDIA_BUS_FMT_xxxx in media-bus-format.h and use the code
> field? For e.g.
> >
> > #define MEDIA_BUS_FMT_SDR_I2S_PADHI_BE       0x7001
> > #define MEDIA_BUS_FMT_SDR_I2S_PADHI_LE       0x7002
> >
> > 2) The framework does not seem to mandate pad ops for all subdev. As the
> tuner can be any third party subdev, is it fair to assume that these
> properties can be queried from subdev?
> >
> > 3) Assuming pad ops is not available on the subdev shouldn't we still
> need a way to define these properties on DRIF DT?
> >
> >>
> >> I'm not an SDR expert, so I'd like to have your opinion on this.
> 
> Neither am I :-)
> 
> I think using the endpoint idea does make sense since this helps in
> describing the routing. 

OK.

I am not sure what properties to put in there,
> though.
> 
> Can you describe a bit more which properties belong to which syncmd mode?
> That will help me make a decision.

OK. The documentation for this control is not accurate in many places of the h/w manual. My observations are based on tests with MAX2175 tuner master device using I2S mode.

Applicable parameters for both Frame Start Sync pulse & I2S modes
 - sync-active
 - lsb/msb first
 - dtdl
 
For I2S mode, syncdl property should be set to value zero only. 

Below are timing diagram examples.

Frame start sync pulse mode:
-----------------------------

   <------------ 1 frame ------------------>
   .---.   .---.   .---.   .---.   .---.   .---.   .---.
___|   |___|   |___|   |___|   |___|   |___|   |___|   |___    SCK

   .-------.                               .-------.
___|       |_______________________________|       |_______    SYNC/SS

   |                                       |
   |-No data delay          No sync delay  |
__  ______  ______  ______  ______  ______  ______  ______
  \/      \/      \/      \/      \/      \/      \/           Rx Data
__/\______/\______/\______/\______/\______/\______/\______

   |-1 bit delay            No sync delay  |
__________  ______  ______  ______  ______  ______  ______
          \/      \/      \/      \/      \/      \/           Rx Data
__________/\______/\______/\______/\______/\______/\______

1 bit delay => DTDL = 1 (i.e.) 1 clock cycle delay
No Sync delay => SYNCDL = 0

I2S mode:
----------

   <------------ 1 frame -------------------------->
   .---.   .---.   .---.   .---.   .---.   .---.   .---.
___|   |___|   |___|   |___|   |___|   |___|   |___|   |___    SCK


___                         _______________________
   |                       |                       |          SYNC/SS
   +-----------------------+                       +-------


   |                                               |
   |-No data delay                   No sync delay |
__  ______  ______  ______  ______  ______  ______  ______
  \/      \/      \/      \/      \/      \/      \/           Rx Data
__/\______/\______/\______/\______/\______/\______/\______

   |-1 bit delay                   No sync delay  |
__________  ______  ______  ______  ______  ______  ______
          \/      \/      \/      \/      \/      \/           Rx Data
__________/\______/\______/\______/\______/\______/\______

Please share your thoughts.

Thanks,
Ramesh
--
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 v2 6/7] dt-bindings: media: Add Renesas R-Car DRIF binding
@ 2017-01-27 13:51                           ` Ramesh Shanmugasundaram
  0 siblings, 0 replies; 60+ messages in thread
From: Ramesh Shanmugasundaram @ 2017-01-27 13:51 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart
  Cc: Geert Uytterhoeven, Rob Herring, Mark Rutland,
	Mauro Carvalho Chehab, Sakari Ailus, Antti Palosaari,
	Chris Paterson, Geert Uytterhoeven, Linux Media Mailing List,
	devicetree, Linux-Renesas

Hi Hans,

Many thanks for the response & comments.

> Subject: Re: [PATCH v2 6/7] dt-bindings: media: Add Renesas R-Car DRIF
> binding
> 
> On 01/10/2017 10:31 AM, Ramesh Shanmugasundaram wrote:
> > Hi Laurent,
> >
> >>>>>> On Wednesday 21 Dec 2016 08:10:37 Ramesh Shanmugasundaram wrote:
> >>>>>>> Add binding documentation for Renesas R-Car Digital Radio
> >>>>>>> Interface
> >>>>>>> (DRIF) controller.
> >>>>>>>
> >>>>>>> Signed-off-by: Ramesh Shanmugasundaram
> >>>>>>> <ramesh.shanmugasundaram@bp.renesas.com> ---
> >>>>>>>
> >>>>>>>  .../devicetree/bindings/media/renesas,drif.txt     | 202
> >> +++++++++++++
> >>>>>>>  1 file changed, 202 insertions(+)  create mode 100644
> >>>>>>>
> >>>>>>> Documentation/devicetree/bindings/media/renesas,drif.txt
> >>>>>>>
> >>>>>>> diff --git
> >>>>>>> a/Documentation/devicetree/bindings/media/renesas,drif.txt
> >>>>>>> b/Documentation/devicetree/bindings/media/renesas,drif.txt new
> >>>>>>> file mode 100644 index 0000000..1f3feaf
> >>>>>>> --- /dev/null
> >>>>>>> +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> >>>>>>>
> >>>>>>> +Optional properties of an internal channel when:
> >>>>>>> +     - It is the only enabled channel of the bond (or)
> >>>>>>> +     - If it acts as primary among enabled bonds
> >>>>>>> +--------------------------------------------------------
> >>>>>>> +- renesas,syncmd       : sync mode
> >>>>>>> +                      0 (Frame start sync pulse mode. 1-bit
> >>>>>>> +width
> >>>>>>> pulse
> >>>>>>> +                         indicates start of a frame)
> >>>>>>> +                      1 (L/R sync or I2S mode) (default)
> >>>>>>> +- renesas,lsb-first    : empty property indicates lsb bit is
> >> received
> >>>>>>> first.
> >>>>>>> +                      When not defined msb bit is received
> >>>>>>> +first
> >>>>>>> +(default)
> >>>>>>> +- renesas,syncac-active: Indicates sync signal polarity, 0/1
> >>>>>>> +for
> >>>>>>> low/high
> >>>
> >>> Shouldn't this be 'renesas,sync-active' instead of syncac-active?
> >>>
> >>> I'm not sure if syncac is intended or if it is a typo.
> >>>
> >>>>>>> +                      respectively. The default is 1 (active
> high)
> >>>>>>> +- renesas,dtdl         : delay between sync signal and start of
> >>>>>>> reception.
> >>>>>>> +                      The possible values are represented in
> >>>>>>> + 0.5
> >> clock
> >>>>>>> +                      cycle units and the range is 0 to 4. The
> >> default
> >>>>>>> +                      value is 2 (i.e.) 1 clock cycle delay.
> >>>>>>> +- renesas,syncdl       : delay between end of reception and sync
> >>>>>>> signal edge.
> >>>>>>> +                      The possible values are represented in
> >>>>>>> + 0.5
> >> clock
> >>>>>>> +                      cycle units and the range is 0 to 4 & 6.
> >>>>>>> + The
> >>>>>>> default
> >>>>>>> +                      value is 0 (i.e.) no delay.
> 
> Are these properties actually going to be used by anyone? Just curious.

Yes. Each of this property should be set appropriately based on the master device it interfaces with. 

> 
> >>>>>>
> >>>>>> Most of these properties are pretty similar to the video bus
> >>>>>> properties defined at the endpoint level in
> >>>>>> Documentation/devicetree/bindings/media/video-interfaces.txt. I
> >>>>>> believe it would make sense to use OF graph and try to
> >>>>>> standardize these properties similarly.
> >>>
> >>> Other than sync-active, is there really anything else that is similar?
> >>> And even the sync-active isn't a good fit since here there is only
> >>> one sync signal instead of two for video (h and vsync).
> >>
> >> That's why I said similar, not identical :-) My point is that, if we
> >> consider that we could connect multiple sources to the DRIF, using OF
> >> graph would make sense, and the above properties should then be
> >> defined per endpoint.
> >
> > Thanks for the clarifications. I have some questions.
> >
> > - Assuming two devices are interfaced with DRIF and they are represented
> using two endpoints, the control signal related properties of DRIF might
> still need to be same for both endpoints? For e.g. syncac-active cannot be
> different in both endpoints?
> 
> Usually that's the case, but HW designers are weird :-)
> 
> >
> > - I suppose "lsb-first", "dtdl" & "syncdl" may be defined per endpoint.
> However, h/w manual says same register values needs to be programmed for
> both the internal channels of a channel. Same with "syncmd" property.
> >
> > We could still define them as per endpoint property with a note that
> they need to be same. But I am not sure if that is what you intended?
> >
> >  If we define them per endpoint we should then also try
> >> standardize the ones that are not really Renesas-specific (that's at
> >> least syncac-active).
> >
> > OK. I will call it "sync-active".
> 
> That's better, yes.

Thanks.

> 
> >
> >  For the syncmd and lsb-first properties, it could also
> >> make sense to query them from the connected subdev at runtime, as
> >> they're similar in purpose to formats and media bus configuration
> >> (struct v4l2_mbus_config).
> 
> I consider this unlikely. I wouldn't spend time on that as this can always
> be done later.
> 
> > May I know in bit more detail about what you had in mind? Please
> > correct me if my understanding is wrong here but when I looked at the
> > code
> >
> > 1) mbus_config is part of subdev_video_ops only. I assume we don't want
> to support this as part of tuner subdev. The next closest is pad_ops with
> "struct v4l2_mbus_framefmt" but it is fully video specific config unless I
> come up with new MEDIA_BUS_FMT_xxxx in media-bus-format.h and use the code
> field? For e.g.
> >
> > #define MEDIA_BUS_FMT_SDR_I2S_PADHI_BE       0x7001
> > #define MEDIA_BUS_FMT_SDR_I2S_PADHI_LE       0x7002
> >
> > 2) The framework does not seem to mandate pad ops for all subdev. As the
> tuner can be any third party subdev, is it fair to assume that these
> properties can be queried from subdev?
> >
> > 3) Assuming pad ops is not available on the subdev shouldn't we still
> need a way to define these properties on DRIF DT?
> >
> >>
> >> I'm not an SDR expert, so I'd like to have your opinion on this.
> 
> Neither am I :-)
> 
> I think using the endpoint idea does make sense since this helps in
> describing the routing. 

OK.

I am not sure what properties to put in there,
> though.
> 
> Can you describe a bit more which properties belong to which syncmd mode?
> That will help me make a decision.

OK. The documentation for this control is not accurate in many places of the h/w manual. My observations are based on tests with MAX2175 tuner master device using I2S mode.

Applicable parameters for both Frame Start Sync pulse & I2S modes
 - sync-active
 - lsb/msb first
 - dtdl
 
For I2S mode, syncdl property should be set to value zero only. 

Below are timing diagram examples.

Frame start sync pulse mode:
-----------------------------

   <------------ 1 frame ------------------>
   .---.   .---.   .---.   .---.   .---.   .---.   .---.
___|   |___|   |___|   |___|   |___|   |___|   |___|   |___    SCK

   .-------.                               .-------.
___|       |_______________________________|       |_______    SYNC/SS

   |                                       |
   |-No data delay          No sync delay  |
__  ______  ______  ______  ______  ______  ______  ______
  \/      \/      \/      \/      \/      \/      \/           Rx Data
__/\______/\______/\______/\______/\______/\______/\______

   |-1 bit delay            No sync delay  |
__________  ______  ______  ______  ______  ______  ______
          \/      \/      \/      \/      \/      \/           Rx Data
__________/\______/\______/\______/\______/\______/\______

1 bit delay => DTDL = 1 (i.e.) 1 clock cycle delay
No Sync delay => SYNCDL = 0

I2S mode:
----------

   <------------ 1 frame -------------------------->
   .---.   .---.   .---.   .---.   .---.   .---.   .---.
___|   |___|   |___|   |___|   |___|   |___|   |___|   |___    SCK


___                         _______________________
   |                       |                       |          SYNC/SS
   +-----------------------+                       +-------


   |                                               |
   |-No data delay                   No sync delay |
__  ______  ______  ______  ______  ______  ______  ______
  \/      \/      \/      \/      \/      \/      \/           Rx Data
__/\______/\______/\______/\______/\______/\______/\______

   |-1 bit delay                   No sync delay  |
__________  ______  ______  ______  ______  ______  ______
          \/      \/      \/      \/      \/      \/           Rx Data
__________/\______/\______/\______/\______/\______/\______

Please share your thoughts.

Thanks,
Ramesh

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

end of thread, other threads:[~2017-01-27 13:51 UTC | newest]

Thread overview: 60+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-11-09 15:44 [PATCH 0/5] Add V4L2 SDR (DRIF & MAX2175) driver Ramesh Shanmugasundaram
2016-11-09 15:44 ` Ramesh Shanmugasundaram
2016-11-09 15:44 ` [PATCH 1/5] media: v4l2-ctrls: Reserve controls for MAX217X Ramesh Shanmugasundaram
2016-11-09 15:44 ` [PATCH 3/5] media: Add new SDR formats SC16, SC18 & SC20 Ramesh Shanmugasundaram
2016-11-11 13:24   ` Hans Verkuil
2016-11-11 13:48     ` Laurent Pinchart
2016-11-11 13:49     ` Laurent Pinchart
2016-11-14 16:20     ` Ramesh Shanmugasundaram
2016-11-14 16:52       ` Hans Verkuil
     [not found] ` <1478706284-59134-1-git-send-email-ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>
2016-11-09 15:44   ` [PATCH 2/5] media: i2c: max2175: Add MAX2175 support Ramesh Shanmugasundaram
2016-11-09 15:44     ` Ramesh Shanmugasundaram
2016-11-11  7:16     ` Antti Palosaari
     [not found]       ` <d9e9aac2-001c-491b-db34-a9eed6240722-X3B1VOXEql0@public.gmane.org>
2016-11-11 11:50         ` Laurent Pinchart
2016-11-11 11:50           ` Laurent Pinchart
2016-11-11 13:21     ` Hans Verkuil
2016-11-11 13:48       ` Laurent Pinchart
2016-11-11 13:58         ` Hans Verkuil
2016-11-14 15:54       ` Ramesh Shanmugasundaram
     [not found]     ` <1478706284-59134-3-git-send-email-ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>
2016-11-14 19:41       ` Rob Herring
2016-11-14 19:41         ` Rob Herring
2016-11-14 19:41         ` Rob Herring
2016-11-17 12:41         ` Ramesh Shanmugasundaram
2016-11-17 12:41           ` Ramesh Shanmugasundaram
2016-11-09 15:44   ` [PATCH 4/5] doc_rst: media: New SDR formats SC16, SC18 & SC20 Ramesh Shanmugasundaram
2016-11-09 15:44     ` Ramesh Shanmugasundaram
2016-11-10  8:18     ` Laurent Pinchart
2016-11-09 15:44 ` [PATCH 5/5] media: platform: rcar_drif: Add DRIF support Ramesh Shanmugasundaram
2016-11-10  9:22   ` Laurent Pinchart
2016-11-14 19:52     ` Rob Herring
2016-11-14 19:52       ` Rob Herring
2016-11-14 20:04       ` Geert Uytterhoeven
2016-11-14 20:04         ` Geert Uytterhoeven
     [not found]         ` <CAMuHMdW5t4irT+2kHDo8TyLcW_eE7yyDa_SYG9Y-Wj99zRN5mQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2016-11-15 15:09           ` Ramesh Shanmugasundaram
2016-11-15 15:09             ` Ramesh Shanmugasundaram
2016-11-11 13:38   ` Hans Verkuil
     [not found]     ` <9b772894-f6ef-d5ad-4601-735f2321ce0c-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
2016-11-14 16:11       ` Ramesh Shanmugasundaram
2016-11-14 16:11         ` Ramesh Shanmugasundaram
2016-12-21  8:10 ` [PATCH v2 0/7] Add V4L2 SDR (DRIF & MAX2175) driver Ramesh Shanmugasundaram
2016-12-21  8:10   ` [PATCH v2 1/7] media: v4l2-ctrls: Reserve controls for MAX217X Ramesh Shanmugasundaram
2016-12-21  8:10   ` [PATCH v2 2/7] dt-bindings: media: Add MAX2175 binding description Ramesh Shanmugasundaram
2016-12-21  8:10   ` [PATCH v2 3/7] media: i2c: max2175: Add MAX2175 support Ramesh Shanmugasundaram
2016-12-21  8:10   ` [PATCH v2 4/7] media: Add new SDR formats PC16, PC18 & PC20 Ramesh Shanmugasundaram
     [not found]   ` <1482307838-47415-1-git-send-email-ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>
2016-12-21  8:10     ` [PATCH v2 5/7] doc_rst: media: New " Ramesh Shanmugasundaram
2016-12-21  8:10       ` Ramesh Shanmugasundaram
2016-12-21  8:10   ` [PATCH v2 6/7] dt-bindings: media: Add Renesas R-Car DRIF binding Ramesh Shanmugasundaram
     [not found]     ` <1482307838-47415-7-git-send-email-ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>
2016-12-22 17:05       ` Laurent Pinchart
2016-12-22 17:05         ` Laurent Pinchart
2016-12-22 19:05         ` Geert Uytterhoeven
2017-01-03 15:20           ` Ramesh Shanmugasundaram
2017-01-09 13:36             ` Hans Verkuil
2017-01-09 14:42               ` Ramesh Shanmugasundaram
     [not found]               ` <cca1ade8-01ef-8eab-f4b1-7dd7f204fdea-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
2017-01-09 23:09                 ` Laurent Pinchart
2017-01-09 23:09                   ` Laurent Pinchart
2017-01-10  9:31                   ` Ramesh Shanmugasundaram
2017-01-10  9:31                     ` Ramesh Shanmugasundaram
2017-01-19 17:46                     ` Chris Paterson
2017-01-27 11:20                     ` Hans Verkuil
     [not found]                       ` <e96cb1cd-cd4a-60f2-cf46-dc837638d115-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
2017-01-27 13:51                         ` Ramesh Shanmugasundaram
2017-01-27 13:51                           ` Ramesh Shanmugasundaram
2016-12-21  8:10   ` [PATCH v2 7/7] media: platform: rcar_drif: Add DRIF support Ramesh Shanmugasundaram

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.