All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC 0/5] Add V4L2 SDR (DRIF & MAX2175) driver
@ 2016-10-12 14:10 Ramesh Shanmugasundaram
  2016-10-12 14:10 ` [RFC 1/5] media: i2c: max2175: Add MAX2175 support Ramesh Shanmugasundaram
                   ` (4 more replies)
  0 siblings, 5 replies; 39+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-10-12 14:10 UTC (permalink / raw)
  To: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope
  Cc: chris.paterson2, laurent.pinchart, geert, linux-media,
	devicetree, linux-renesas-soc, Ramesh Shanmugasundaram

Hi All,

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

These patches were based on top of Mauro's media-next repo
commit: 02a628e5c024cf67bf10bc822fb9169713f8ea74

These two drivers combined together expose a V4L2 SDR device that is compliant
with the V4L2 framework.

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

Driver design:
--------------

Some important design decisions are listed below

- DRIF registers as main V4L2 SDR device & MAX2175 registers as V4L2 sub-device.
- DRIF expects a tuner device to attach asynchronously at runtime and the module
  loading order is independent of each other.
- The tuner device can be provided by any third party vendor (in this case
  MAX2175).
- The mapping between DRIF & tuner device is achieved through device tree port
  & endpoint configuration of the board.

  e.g.

  drif node:

        port {
                drif0_ep: endpoint {
                     remote-endpoint = <&max2175_0_ep>;
                };
	};

  max2175 node:

	port {
		max2175_0_ep: endpoint {
			remote-endpoint = <&drif0_ep>;
		};
	};

- The combined driver is V4L2 compliant as here [1]
- In this RFC patch the MAX2175 tuner supports only DAB1.2 mode, which uses
  16bit word length.

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.

Topic for discussion:
---------------------
One of the design goals is keep DRIF & tuner driver as much independent as
possible. However, there are few things that are up for discussion

- SDR formats:
  --------------
  For e.g. when MAX2175 is configured in one of the FM modes it can do 80bit
  word length. By default only 18bits would be valid data in each data line
  and 2 bits would be status bits and the rest are stuff bits (zeros typically).
  However, it also supports a mode for some cases where it can do
  I&Q multiplexing by using SD0 data line alone. In that case, the device
  can send the samples in one of the following combinations

  <-------------- 80 bits ---------------------------->
  <--14I + 2Status + 14Q + 2Status--- 48 stuff bits --> I2S mode = 2
  <--18I + 2Status + 18Q + 2Status--- 40 stuff bits --> I2S mode = 3
  <--16I + 16Q ---------------------- 48 stuff bits --> I2S mode = 4

  DRIF will receive these 80bit word as four 32bits each having 20bit of valid
  data. Usually the 80bit word is received as

  <---------------32bits----------->
  <--20bits data---- 12bits zeros--> 0
  <--20bits data---- 12bits zeros--> 1
  <--20bits data---- 12bits zeros--> 2
  <--20bits data---- 12bits zeros--> 3

  For e.g. if MAX2175 uses 80bits=>14I+2+14Q+2 format, the placement of I & Q
  bits in DRIF buffers would be as below

  <-------------------------32bits---------------------->
  <--- 20bits = I[13:0]+2+Q[13:10]---- 12bit zeros------> 0
  <--- 20bits = Q[9:0]+2+ stuffbits[7:0]---12bit zeros--> 1
  <--- 20bits = stuffbits  ----------- 12bit zeros------> 2
  <--- 20bits = stuffbits  ----------- 12bit zeros------> 3

  As you can see neither MAX2175 or DRIF alone can fully define the
  V4L2 SDR format. It is a combination of both.

  - Should we define a SDR format for each such possibility?
  - If a new tuner vendors wants to add support, existing formats MAY still be
    reusable and new formats may be needed based on the tuner capability.

I would appreciate any feedback you may have?


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

root@salvator-x:~# v4l2-compliance -S /dev/swradio0
v4l2-compliance SHA   : 7c2664b9a9b411d8b183009146e4f8548ca1d81a

Driver Info:
        Driver name   : rcar_drif
        Card type     : R-Car DRIF
        Bus info      : platform:R-Car DRIF
        Driver version: 4.8.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: 5

        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:~#
Ramesh Shanmugasundaram (5):
  media: i2c: max2175: Add MAX2175 support
  media: v4l2-ctrls: Reserve controls for MAX217X
  media: platform: rcar_drif: Add DRIF support
  media: Add new SDR formats SC16, SC18 & SC20
  doc_rst: media: New SDR formats SC16, SC18 & SC20

 .../devicetree/bindings/media/i2c/max2175.txt      |   60 +
 .../devicetree/bindings/media/renesas,drif.txt     |  109 ++
 .../media/uapi/v4l/pixfmt-sdr-scu16be.rst          |   44 +
 .../media/uapi/v4l/pixfmt-sdr-scu18be.rst          |   48 +
 .../media/uapi/v4l/pixfmt-sdr-scu20be.rst          |   48 +
 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                | 1624 ++++++++++++++++++++
 drivers/media/i2c/max2175/max2175.h                |  124 ++
 drivers/media/platform/Kconfig                     |   25 +
 drivers/media/platform/Makefile                    |    1 +
 drivers/media/platform/rcar_drif.c                 | 1534 ++++++++++++++++++
 drivers/media/v4l2-core/v4l2-ioctl.c               |    3 +
 include/uapi/linux/v4l2-controls.h                 |    5 +
 include/uapi/linux/videodev2.h                     |    3 +
 18 files changed, 3649 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] 39+ messages in thread

* [RFC 1/5] media: i2c: max2175: Add MAX2175 support
  2016-10-12 14:10 [RFC 0/5] Add V4L2 SDR (DRIF & MAX2175) driver Ramesh Shanmugasundaram
@ 2016-10-12 14:10 ` Ramesh Shanmugasundaram
       [not found]   ` <1476281429-27603-2-git-send-email-ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>
  2016-10-18 19:25   ` Laurent Pinchart
  2016-10-12 14:10 ` [RFC 2/5] media: v4l2-ctrls: Reserve controls for MAX217X Ramesh Shanmugasundaram
                   ` (3 subsequent siblings)
  4 siblings, 2 replies; 39+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-10-12 14:10 UTC (permalink / raw)
  To: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope
  Cc: chris.paterson2, laurent.pinchart, geert, 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      |   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                | 1624 ++++++++++++++++++++
 drivers/media/i2c/max2175/max2175.h                |  124 ++
 7 files changed, 1826 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..2250d5f
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/max2175.txt
@@ -0,0 +1,60 @@
+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: video interface 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: load capacitance value (in pF) on reference output
+		     drive level. The mapping of these load values to
+		     respective bit values are given below.
+		     0 - Reference output disabled
+		     1 - 10pF load
+		     2 - 20pF load
+		     3 - 30pF load
+		     4 - 40pF load
+		     5 - 60pF load
+		     6 - 70pF load
+
+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 {
+	#clock-cells = <0>;
+	compatible = "maxim,max2175";
+	reg = <0x60>;
+	clocks = <&maxim_xtal>;
+	clock-names = "xtal";
+	maxim,refout-load = <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..a8a0664
--- /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 && VIDEO_V4L2_SUBDEV_API
+	---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..71b60c2
--- /dev/null
+++ b/drivers/media/i2c/max2175/max2175.c
@@ -0,0 +1,1624 @@
+/*
+ * 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/errno.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.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 max2175_debug;
+module_param(max2175_debug, uint, 0644);
+MODULE_PARM_DESC(max2175_debug, "activate debug info");
+
+#define mxm_dbg(ctx, fmt, arg...) \
+	v4l2_dbg(1, max2175_debug, &ctx->sd, fmt, ## arg)
+
+/* NOTE: Any addition/deletion in the below list should be reflected in
+ * max2175_modetag enum
+ */
+static const struct max2175_rxmode eu_rx_modes[] = { /* Indexed by EU modetag */
+	/* EU modes */
+	{ MAX2175_BAND_VHF, 182640000, 0, { 0, 0, 0, 0 } },
+};
+
+static const struct max2175_rxmode na_rx_modes[] = { /* Indexed by NA modetag */
+	/* NA modes */
+	{ MAX2175_BAND_FM, 98255520, 1, { 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,
+};
+
+static const struct max2175_regmap 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 },
+};
+
+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_EN		(V4L2_CID_USER_MAX217X_BASE + 0x01)
+#define V4L2_CID_MAX2175_I2S_MODE	(V4L2_CID_USER_MAX217X_BASE + 0x02)
+#define V4L2_CID_MAX2175_AM_HIZ		(V4L2_CID_USER_MAX217X_BASE + 0x03)
+#define V4L2_CID_MAX2175_HSLS		(V4L2_CID_USER_MAX217X_BASE + 0x04)
+#define V4L2_CID_MAX2175_RX_MODE	(V4L2_CID_USER_MAX217X_BASE + 0x05)
+
+/* Tuner bands */
+static const struct v4l2_frequency_band bands_rf = {
+	.tuner = 0,
+	.type = V4L2_TUNER_RF,
+	.index = 0,
+	.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+	.rangelow   = 160000000,
+	.rangehigh  = 240000000,
+};
+
+struct max2175_ctx {
+	struct v4l2_subdev sd;
+	struct i2c_client *client;
+	struct device *dev;
+
+	/* Cached configuration */
+	u8 regs[256];
+	enum max2175_modetag mode;	/* Receive mode tag */
+	u32 freq;			/* In Hz */
+	struct max2175_rxmode *rx_modes;
+
+	/* Device settings */
+	bool master;
+	u32 decim_ratio;
+	u64 xtal_freq;
+
+	/* ROM values */
+	u8 rom_bbf_bw_am;
+	u8 rom_bbf_bw_fm;
+	u8 rom_bbf_bw_dab;
+
+	/* Local copy of old settings */
+	u8 i2s_test;
+
+	u8 nbd_gain;
+	u8 nbd_threshold;
+	u8 wbd_gain;
+	u8 wbd_threshold;
+	u8 bbd_threshold;
+	u8 bbdclip_threshold;
+	u8 lt_wbd_threshold;
+	u8 lt_wbd_gain;
+
+	/* 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 *am_hiz;	/* AM High impledance input */
+	struct v4l2_ctrl *hsls;		/* High-side/Low-side polarity */
+	struct v4l2_ctrl *rx_mode;	/* Receive mode */
+
+	/* Driver private variables */
+	bool mode_resolved;		/* Flag to sanity check settings */
+};
+
+/* Helpers */
+
+static int max2175_reg_write(struct max2175_ctx *ctx, u8 idx, u8 val)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(ctx->client, idx, val);
+	if (ret) {
+		v4l2_err(ctx->client,
+			 "write: idx 0x%02x, val 0x%02x\n", idx, val);
+		return ret;
+	}
+
+	/* Update local store */
+	ctx->regs[idx] = val;
+	if (max2175_debug >= 2)
+		mxm_dbg(ctx, "write: reg[0x%02x] = 0x%02x\n", idx, val);
+
+	return ret;
+}
+
+static int max2175_reg_read(struct max2175_ctx *ctx, u8 idx)
+{
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(ctx->client, idx);
+	if (ret < 0) {
+		v4l2_err(ctx->client, "read: idx 0x%02x\n", idx);
+		return ret;
+	}
+
+	if (max2175_debug >= 2)
+		mxm_dbg(ctx, "read: reg[0x%02x] = 0x%02x\n", idx, ret);
+
+	return ret;
+}
+
+/* Flush local copy to device from idx to idx+len (inclusive) */
+static void max2175_flush_regstore(struct max2175_ctx *ctx, u8 idx, u8 len)
+{
+	u8 i;
+
+	for (i = idx; i <= len; i++)
+		max2175_reg_write(ctx, i, ctx->regs[i]);
+}
+
+/* General bitops helpers */
+static inline u8  __max2175_get_bits(u8 val, u8 msb, u8 lsb)
+{
+	return ((val & GENMASK(msb, lsb)) >> lsb);
+}
+
+static inline u8  __max2175_set_bits(u8 val, u8 msb, u8 lsb, u8 newval)
+{
+	u8 mask = GENMASK(msb, lsb);
+
+	return ((val & ~mask) | ((newval << lsb) & mask));
+}
+
+/* Local store bitops helpers */
+static u8 max2175_get_bits(struct max2175_ctx *ctx, u8 idx, u8 msb, u8 lsb)
+{
+	if (max2175_debug >= 2)
+		mxm_dbg(ctx, "get_bits: idx:%u msb:%u lsb:%u\n",
+			idx, msb, lsb);
+	return __max2175_get_bits(ctx->regs[idx], msb, lsb);
+}
+
+static bool max2175_get_bit(struct max2175_ctx *ctx, u8 idx, u8 bit)
+{
+	return !!max2175_get_bits(ctx, idx, bit, bit);
+}
+
+static void max2175_set_bits(struct max2175_ctx *ctx, u8 idx,
+		      u8 msb, u8 lsb, u8 newval)
+{
+	if (max2175_debug >= 2)
+		mxm_dbg(ctx, "set_bits: idx:%u msb:%u lsb:%u newval:%u\n",
+			idx, msb, lsb, newval);
+	ctx->regs[idx] = __max2175_set_bits(ctx->regs[idx], msb, lsb,
+					      newval);
+}
+
+static void max2175_set_bit(struct max2175_ctx *ctx, u8 idx, u8 bit, u8 newval)
+{
+	max2175_set_bits(ctx, idx, bit, bit, newval);
+}
+
+/* Device register bitops helpers */
+static u8 max2175_read_bits(struct max2175_ctx *ctx, u8 idx, u8 msb, u8 lsb)
+{
+	return __max2175_get_bits(max2175_reg_read(ctx, idx), msb, lsb);
+}
+
+static void max2175_write_bits(struct max2175_ctx *ctx, u8 idx, u8 msb,
+			u8 lsb, u8 newval)
+{
+	/* Update local copy & write to device */
+	max2175_set_bits(ctx, idx, msb, lsb, newval);
+	max2175_reg_write(ctx, idx, ctx->regs[idx]);
+}
+
+static void max2175_write_bit(struct max2175_ctx *ctx, u8 idx, u8 bit,
+			      u8 newval)
+{
+	if (max2175_debug >= 2)
+		mxm_dbg(ctx, "idx %u, bit %u, newval %u\n", idx, bit, newval);
+	max2175_write_bits(ctx, idx, bit, bit, newval);
+}
+
+static int max2175_poll_timeout(struct max2175_ctx *ctx, u8 idx, u8 msb, u8 lsb,
+				u8 exp_val, u32 timeout)
+{
+	unsigned long end = jiffies + msecs_to_jiffies(timeout);
+	int ret;
+
+	do {
+		ret = max2175_read_bits(ctx, idx, msb, lsb);
+		if (ret < 0)
+			return ret;
+		if (ret == exp_val)
+			return 0;
+
+		usleep_range(1000, 1500);
+	} while (time_is_after_jiffies(end));
+
+	return -EBUSY;
+}
+
+static int max2175_poll_csm_ready(struct max2175_ctx *ctx)
+{
+	return max2175_poll_timeout(ctx, 69, 1, 1, 0, 50);
+}
+
+#define MAX2175_IS_BAND_AM(ctx)		\
+	(max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_AM)
+
+#define MAX2175_IS_FM_MODE(ctx)		\
+	(max2175_get_bits(ctx, 12, 5, 4) == 0)
+
+#define MAX2175_IS_FMHD_MODE(ctx)	\
+	(max2175_get_bits(ctx, 12, 5, 4) == 1)
+
+#define MAX2175_IS_DAB_MODE(ctx)	\
+	(max2175_get_bits(ctx, 12, 5, 4) == 2)
+
+static int max2175_band_from_freq(u64 freq)
+{
+	if (freq >= 144000 && freq <= 26100000)
+		return MAX2175_BAND_AM;
+	else if (freq >= 65000000 && freq <= 108000000)
+		return MAX2175_BAND_FM;
+	else if (freq >= 160000000 && freq <= 240000000)
+		return MAX2175_BAND_VHF;
+
+	/* Add other bands on need basis */
+	return -ENOTSUPP;
+}
+
+static int max2175_update_i2s_mode(struct max2175_ctx *ctx, u32 i2s_mode)
+{
+	/* Only change if it's new */
+	if (max2175_read_bits(ctx, 29, 2, 0) == i2s_mode)
+		return 0;
+
+	max2175_write_bits(ctx, 29, 2, 0, i2s_mode);
+
+	/* Based on I2S mode value I2S_WORD_CNT values change */
+	if (i2s_mode == MAX2175_I2S_MODE3) {
+		max2175_write_bits(ctx, 30, 6, 0, 1);
+	} else if (i2s_mode == MAX2175_I2S_MODE2 ||
+		   i2s_mode == MAX2175_I2S_MODE4) {
+		max2175_write_bits(ctx, 30, 6, 0, 0);
+	} else if (i2s_mode == MAX2175_I2S_MODE0) {
+		max2175_write_bits(ctx, 30, 6, 0,
+				   ctx->rx_modes[ctx->mode].i2s_word_size);
+	} else {
+		v4l2_err(ctx->client,
+			 "failed: i2s_mode %u unsupported\n", i2s_mode);
+		return 1;
+	}
+	mxm_dbg(ctx, "updated i2s_mode %u\n", i2s_mode);
+	return 0;
+}
+
+static void max2175_i2s_enable(struct max2175_ctx *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, 1);
+	}
+	mxm_dbg(ctx, "i2s %s: old val %u\n", enable ? "enabled" : "disabled",
+		ctx->i2s_test);
+}
+
+static void max2175_set_filter_coeffs(struct max2175_ctx *ctx, u8 m_sel,
+				      u8 bank, const u16 *coeffs)
+{
+	u8 i, coeff_addr, upper_address;
+
+	mxm_dbg(ctx, "start: 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;
+	else
+		upper_address = 24;
+
+	max2175_set_bit(ctx, 117, 7, 1);
+	for (i = 0; i < upper_address; i++) {
+		coeff_addr = i + (bank * 24);
+		max2175_set_bits(ctx, 115, 7, 0,
+				 (u8)((coeffs[i] >> 8) & 0xff));
+		max2175_set_bits(ctx, 116, 7, 0, (u8)(coeffs[i] & 0xff));
+		max2175_set_bits(ctx, 117, 6, 0, coeff_addr);
+		max2175_flush_regstore(ctx, 115, 3);
+	}
+	max2175_write_bit(ctx, 117, 7, 0);
+}
+
+static void max2175_load_dab_1p2(struct max2175_ctx *ctx)
+{
+	u32 i;
+
+	/* Master is already set on init */
+	for (i = 0; i < ARRAY_SIZE(dab12_map); i++)
+		max2175_reg_write(ctx, dab12_map[i].idx, dab12_map[i].val);
+
+	/* The default settings assume master */
+	if (!ctx->master)
+		max2175_write_bit(ctx, 30, 7, 1);
+
+	/* Cache i2s_test value at this point */
+	ctx->i2s_test = max2175_get_bits(ctx, 104, 3, 0);
+	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_set_bbfilter(struct max2175_ctx *ctx)
+{
+	if (MAX2175_IS_BAND_AM(ctx)) {
+		max2175_write_bits(ctx, 12, 3, 0, ctx->rom_bbf_bw_am);
+		mxm_dbg(ctx, "max2175_set_bbfilter: AM: rom value = %d\n",
+			ctx->rom_bbf_bw_am);
+	} else {
+		/* FM or DAB mode */
+		if (MAX2175_IS_DAB_MODE(ctx)) {
+			/* DAB mode. Load ROM values */
+			max2175_write_bits(ctx, 12, 3, 0, ctx->rom_bbf_bw_dab);
+			mxm_dbg(ctx, "max2175_set_bbfilter: DAB: rom value = %d\n",
+				ctx->rom_bbf_bw_dab);
+		} else {
+			/* FM or FM HD mode. Load ROM values */
+			max2175_write_bits(ctx, 12, 3, 0, ctx->rom_bbf_bw_fm);
+			mxm_dbg(ctx, "max2175_set_bbfilter: FM: rom value = %d\n",
+				ctx->rom_bbf_bw_fm);
+		}
+	}
+}
+
+static bool max2175_set_csm_mode(struct max2175_ctx *ctx,
+			  enum max2175_csm_mode new_mode)
+{
+	int ret = max2175_poll_csm_ready(ctx);
+
+	if (ret) {
+		v4l2_err(ctx->client, "csm not ready: new mode %d\n", new_mode);
+		return ret;
+	}
+
+	mxm_dbg(ctx, "set csm mode: new mode %d\n", new_mode);
+
+	max2175_write_bits(ctx, 0, 2, 0, new_mode);
+
+	/* Wait for a fixed settle down time depending on new mode and band */
+	switch (new_mode) {
+	case MAX2175_CSM_MODE_JUMP_FAST_TUNE:
+		if (MAX2175_IS_BAND_AM(ctx)) {
+			usleep_range(1250, 1500);	/* 1.25ms */
+		} else {
+			if (MAX2175_IS_DAB_MODE(ctx))
+				usleep_range(3000, 3500);	/* 3ms */
+			else
+				usleep_range(1250, 1500);	/* 1.25ms */
+		}
+		break;
+
+	/* Other mode switches can be added in the future if needed */
+	default:
+		break;
+	}
+
+	ret = max2175_poll_csm_ready(ctx);
+	if (ret) {
+		v4l2_err(ctx->client, "csm did not settle: new mode %d\n",
+			 new_mode);
+		return ret;
+	}
+	return ret;
+}
+
+static int max2175_csm_action(struct max2175_ctx *ctx,
+			      enum max2175_csm_mode action)
+{
+	int ret;
+	int load_buffer = MAX2175_CSM_MODE_LOAD_TO_BUFFER;
+
+	mxm_dbg(ctx, "csm action: %d\n", action);
+
+	/* Perform one or two CSM mode commands. */
+	switch (action)	{
+	case MAX2175_CSM_MODE_NO_ACTION:
+		/* Take no FSM Action. */
+		return 0;
+	case MAX2175_CSM_MODE_LOAD_TO_BUFFER:
+	case MAX2175_CSM_MODE_PRESET_TUNE:
+	case MAX2175_CSM_MODE_SEARCH:
+	case MAX2175_CSM_MODE_AF_UPDATE:
+	case MAX2175_CSM_MODE_JUMP_FAST_TUNE:
+	case MAX2175_CSM_MODE_CHECK:
+	case MAX2175_CSM_MODE_LOAD_AND_SWAP:
+	case MAX2175_CSM_MODE_END:
+		ret = max2175_set_csm_mode(ctx, action);
+		break;
+	case MAX2175_CSM_MODE_BUFFER_PLUS_PRESET_TUNE:
+		ret = max2175_set_csm_mode(ctx, load_buffer);
+		if (ret) {
+			v4l2_err(ctx->client, "csm action %d load buf failed\n",
+				 action);
+			break;
+		}
+		ret = max2175_set_csm_mode(ctx, MAX2175_CSM_MODE_PRESET_TUNE);
+		break;
+	case MAX2175_CSM_MODE_BUFFER_PLUS_SEARCH:
+		ret = max2175_set_csm_mode(ctx, load_buffer);
+		if (ret) {
+			v4l2_err(ctx->client, "csm action %d load buf failed\n",
+				 action);
+			break;
+		}
+		ret = max2175_set_csm_mode(ctx, MAX2175_CSM_MODE_SEARCH);
+		break;
+	case MAX2175_CSM_MODE_BUFFER_PLUS_AF_UPDATE:
+		ret = max2175_set_csm_mode(ctx, load_buffer);
+		if (ret) {
+			v4l2_err(ctx->client, "csm action %d load buf failed\n",
+				 action);
+			break;
+		}
+		ret = max2175_set_csm_mode(ctx, MAX2175_CSM_MODE_AF_UPDATE);
+		break;
+	case MAX2175_CSM_MODE_BUFFER_PLUS_JUMP_FAST_TUNE:
+		ret = max2175_set_csm_mode(ctx, load_buffer);
+		if (ret) {
+			v4l2_err(ctx->client, "csm action %d load buf failed\n",
+				 action);
+			break;
+		}
+		ret = max2175_set_csm_mode(ctx,
+					   MAX2175_CSM_MODE_JUMP_FAST_TUNE);
+		break;
+	case MAX2175_CSM_MODE_BUFFER_PLUS_CHECK:
+		ret = max2175_set_csm_mode(ctx, load_buffer);
+		if (ret) {
+			v4l2_err(ctx->client, "csm action %d load buf failed\n",
+				 action);
+			break;
+		}
+		ret = max2175_set_csm_mode(ctx, MAX2175_CSM_MODE_CHECK);
+		break;
+	case MAX2175_CSM_MODE_BUFFER_PLUS_LOAD_AND_SWAP:
+		ret = max2175_set_csm_mode(ctx, load_buffer);
+		if (ret) {
+			v4l2_err(ctx->client, "csm action %d load buf failed\n",
+				 action);
+			break;
+		}
+		ret = max2175_set_csm_mode(ctx, MAX2175_CSM_MODE_LOAD_AND_SWAP);
+		break;
+	default:
+		ret = max2175_set_csm_mode(ctx, MAX2175_CSM_MODE_NO_ACTION);
+		break;
+	}
+	return ret;
+}
+
+static int max2175_set_lo_freq(struct max2175_ctx *ctx, u64 lo_freq)
+{
+	int ret;
+	u32 lo_mult;
+	u64 scaled_lo_freq;
+	const u64 scale_factor = 1000000ULL;
+	u64 scaled_npf, scaled_integer, scaled_fraction;
+	u32 frac_desired, int_desired;
+	u8 loband_bits, vcodiv_bits;
+
+	scaled_lo_freq = lo_freq;
+	/* Scale to larger number for precision */
+	scaled_lo_freq = scaled_lo_freq * scale_factor * 100;
+
+	mxm_dbg(ctx, "scaled lo_freq %llu lo_freq %llu\n",
+		scaled_lo_freq, lo_freq);
+
+	if (MAX2175_IS_BAND_AM(ctx)) {
+		if (max2175_get_bit(ctx, 5, 7) == 0)
+			loband_bits = 0;
+			vcodiv_bits = 0;
+			lo_mult = 16;
+	} else if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_FM) {
+		if (lo_freq <= 74700000) {
+			loband_bits = 0;
+			vcodiv_bits = 0;
+			lo_mult = 16;
+		} else if ((lo_freq > 74700000) && (lo_freq <= 110000000)) {
+			loband_bits = 1;
+			vcodiv_bits = 0;
+		} else {
+			loband_bits = 1;
+			vcodiv_bits = 3;
+		}
+		lo_mult = 8;
+	} else if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF) {
+		if (lo_freq <= 210000000) {
+			loband_bits = 2;
+			vcodiv_bits = 2;
+		} else {
+			loband_bits = 2;
+			vcodiv_bits = 1;
+		}
+		lo_mult = 4;
+	} else {
+		loband_bits = 3;
+		vcodiv_bits = 2;
+		lo_mult = 2;
+	}
+
+	if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_L)
+		scaled_npf = (scaled_lo_freq / ctx->xtal_freq / lo_mult) / 100;
+	else
+		scaled_npf = (scaled_lo_freq / ctx->xtal_freq * lo_mult) / 100;
+
+	scaled_integer = scaled_npf / scale_factor * scale_factor;
+	int_desired = (u32)(scaled_npf / scale_factor);
+	scaled_fraction = scaled_npf - scaled_integer;
+	frac_desired = (u32)(scaled_fraction * 1048576 / scale_factor);
+
+	/* Check CSM is not busy */
+	ret = max2175_poll_csm_ready(ctx);
+	if (ret) {
+		v4l2_err(ctx->client, "lo_freq: csm busy. freq %llu\n",
+			 lo_freq);
+		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_set_bits(ctx, 5, 3, 2, loband_bits);
+	max2175_set_bits(ctx, 6, 7, 6, vcodiv_bits);
+	max2175_set_bits(ctx, 1, 7, 0, (u8)(int_desired & 0xff));
+	max2175_set_bits(ctx, 2, 3, 0, (u8)((frac_desired >> 16) & 0x1f));
+	max2175_set_bits(ctx, 3, 7, 0, (u8)((frac_desired >> 8) & 0xff));
+	max2175_set_bits(ctx, 4, 7, 0, (u8)(frac_desired & 0xff));
+	/* Flush the above registers to device */
+	max2175_flush_regstore(ctx, 1, 6);
+	return ret;
+}
+
+static int max2175_set_nco_freq(struct max2175_ctx *ctx, s64 nco_freq_desired)
+{
+	int ret;
+	u64 clock_rate, abs_nco_freq;
+	s64  nco_freq, nco_val_desired;
+	u32 nco_reg;
+	const u64 scale_factor = 1000000ULL;
+
+	mxm_dbg(ctx, "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 = (2 * nco_freq * scale_factor) / clock_rate;
+	} else {
+		if (nco_freq < 0)
+			nco_val_desired = (-2 * (clock_rate - abs_nco_freq) *
+				scale_factor) / clock_rate;
+		else
+			nco_val_desired = (2 * (clock_rate - abs_nco_freq) *
+				scale_factor) / clock_rate;
+	}
+
+	/* Scale down to get the fraction */
+	if (nco_freq < 0)
+		nco_reg = 0x200000 + ((nco_val_desired * 1048576) /
+				      scale_factor);
+	else
+		nco_reg = (nco_val_desired * 1048576) / scale_factor;
+
+	/* Check CSM is not busy */
+	ret = max2175_poll_csm_ready(ctx);
+	if (ret) {
+		v4l2_err(ctx->client, "nco_freq: csm busy. freq %llu\n",
+			 nco_freq);
+		return ret;
+	}
+
+	mxm_dbg(ctx, "clk %llu 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_set_bits(ctx, 7, 4, 0, (u8)((nco_reg >> 16) & 0x1f));
+	max2175_set_bits(ctx, 8, 7, 0, (u8)((nco_reg >> 8) & 0xff));
+	max2175_set_bits(ctx, 9, 7, 0, (u8)(nco_reg & 0xff));
+	max2175_flush_regstore(ctx, 7, 3);  /* Send all 3 NCO registers. */
+	return ret;
+}
+
+static int max2175_set_rf_freq_non_am_bands(struct max2175_ctx *ctx, u64 freq,
+					    u32 lo_pos)
+{
+	int ret;
+	s64 adj_freq;
+	u64 low_if_freq;
+
+	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_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF) {
+		if (lo_pos == MAX2175_LO_ABOVE_DESIRED)
+			adj_freq = freq + low_if_freq;
+		else
+			adj_freq = freq - low_if_freq;
+	} else {
+		if (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 *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);
+
+	if (!ret)
+		mxm_dbg(ctx, "set rf_freq: freq = %llu. done\n", freq);
+
+	return ret;
+}
+
+static int max2175_tune_rf_freq(struct max2175_ctx *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_CSM_MODE_BUFFER_PLUS_JUMP_FAST_TUNE);
+	if (ret)
+		return ret;
+
+	mxm_dbg(ctx, "tuned rf_freq: old %u new %llu\n", ctx->freq, freq);
+	ctx->freq = freq;
+	return ret;
+}
+
+static void max2175_set_hsls(struct max2175_ctx *ctx, u32 lo_pos)
+{
+	mxm_dbg(ctx, "set_hsls: lo_pos = %u\n", lo_pos);
+
+	if (lo_pos == MAX2175_LO_BELOW_DESIRED)	{
+		if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF)
+			max2175_write_bit(ctx, 5, 4, 1);
+		else
+			max2175_write_bit(ctx, 5, 4, 0);
+	} else {
+		if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF)
+			max2175_write_bit(ctx, 5, 4, 0);
+		else
+			max2175_write_bit(ctx, 5, 4, 1);
+	}
+}
+
+static int max2175_set_rx_mode(struct max2175_ctx *ctx, u32 rx_mode,
+			       u32 am_hiz, u32 hsls)
+{
+	mxm_dbg(ctx, "receive mode: %u am_hiz %u\n", rx_mode, am_hiz);
+
+	switch (rx_mode) {
+	case MAX2175_DAB_1_2:
+		max2175_load_dab_1p2(ctx);
+		break;
+
+	/* Other modes can be added in future if needed */
+	default:
+		v4l2_err(ctx->client, "invalid rx_mode %u\n", rx_mode);
+		return -EINVAL;
+	}
+
+	if (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);
+	}
+
+	/* Store some of the initial values loaded, which will be needed at
+	 * a later stage
+	 */
+	ctx->nbd_threshold = max2175_get_bits(ctx, 14, 7, 4);
+	ctx->nbd_gain = max2175_get_bits(ctx, 16, 7, 5);
+	ctx->wbd_threshold = max2175_get_bits(ctx, 14, 3, 0);
+	ctx->wbd_gain = max2175_get_bits(ctx, 15, 5, 4);
+	ctx->bbd_threshold = max2175_get_bits(ctx, 15, 3, 0);
+	ctx->bbdclip_threshold = max2175_get_bits(ctx, 35, 7, 5);
+	ctx->lt_wbd_threshold = max2175_get_bits(ctx, 32, 3, 0);
+	ctx->lt_wbd_gain = max2175_get_bits(ctx, 32, 5, 4);
+
+	/* Load BB filter trim values saved in ROM */
+	max2175_set_bbfilter(ctx);
+
+	/* Set HSLS */
+	max2175_set_hsls(ctx, hsls);
+
+	ctx->mode = rx_mode;
+	ctx->mode_resolved = true;
+	return 0;
+}
+
+static bool max2175_is_i2s_rx_mode_valid(struct max2175_ctx *ctx,
+					 u32 mode, u32 i2s_mode)
+{
+	u32 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 rx mode %u\n",
+		 i2s_mode, mode);
+	return false;
+}
+
+static int max2175_mode_from_freq(struct max2175_ctx *ctx, u32 freq, u32 *mode)
+{
+	u32 i;
+	int band;
+
+	band = max2175_band_from_freq(freq);
+	if (band < 0) {
+		v4l2_err(ctx->client, "mode from freq failed: %u ret %d\n",
+			 freq, band);
+		return 1;
+	}
+
+	/* 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, "mode from freq: freq %u mode %d\n",
+				freq, *mode);
+			return 0;
+		}
+	}
+	v4l2_err(ctx->client, "mode from freq failed: freq %u\n", freq);
+	return 1;
+}
+
+static bool max2175_is_freq_rx_mode_valid(struct max2175_ctx *ctx,
+					 u32 mode, u32 freq)
+{
+	int band;
+
+	band = max2175_band_from_freq(freq);
+	if (band < 0) {
+		v4l2_err(ctx->client, "freq rx mode failed: freq %u ret %d\n",
+			 freq, band);
+		return band;
+	}
+
+	mxm_dbg(ctx, "freq rx mode valid: freq %u mode %u\n", freq, mode);
+	return (ctx->rx_modes[mode].band == band);
+}
+
+static void max2175_load_adc_presets(struct max2175_ctx *ctx)
+{
+	u32 i;
+
+	for (i = 0; i < 2; i++) {
+		memcpy(&ctx->regs[146 + (i * 55)], &adc_presets[i][0], 23);
+		max2175_flush_regstore(ctx, 146 + (i * 55), 23);
+	}
+}
+
+static int max2175_init_power_manager(struct max2175_ctx *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 *ctx)
+{
+	int ret;
+
+	/* ADC Re-calibration */
+	max2175_reg_write(ctx, 150, 0xff);
+	max2175_reg_write(ctx, 205, 0xff);
+	max2175_reg_write(ctx, 147, 0x20);
+	max2175_reg_write(ctx, 147, 0x00);
+	max2175_reg_write(ctx, 202, 0x20);
+	max2175_reg_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 *ctx, u8 row)
+{
+	u8 data;
+
+	max2175_write_bit(ctx, 56, 4, 0);
+	max2175_write_bits(ctx, 56, 3, 0, row);
+	usleep_range(2000, 2500);
+
+	data = max2175_reg_read(ctx, 58);
+	max2175_write_bits(ctx, 56, 3, 0, 0);
+
+	mxm_dbg(ctx, "read rom: row 0x%02x, data 0x%02x\n", row, data);
+	return data;
+}
+
+static void max2175_load_from_rom(struct max2175_ctx *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) & 0xf);
+
+	data = max2175_read_rom(ctx, 1);
+	ctx->rom_bbf_bw_fm = (data & 0x0f);
+	ctx->rom_bbf_bw_dab = (data & 0xf0) >> 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->val) {
+		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_fm_eu_1p0_full(struct max2175_ctx *ctx)
+{
+	memcpy(&ctx->regs[1], full_fm_eu_1p0, sizeof(full_fm_eu_1p0));
+	ctx->decim_ratio = 36;
+}
+
+static void max2175_load_fm_na_1p0_full(struct max2175_ctx *ctx)
+{
+	memcpy(&ctx->regs[1], full_fm_na_1p0, sizeof(full_fm_na_1p0));
+	ctx->decim_ratio = 27;
+}
+
+static int max2175_core_init(struct max2175_ctx *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_fm_eu_1p0_full(ctx);
+	else
+		max2175_load_fm_na_1p0_full(ctx);
+
+	/* The default settings assume master */
+	if (!ctx->master)
+		max2175_set_bit(ctx, 30, 7, 1);
+
+	mxm_dbg(ctx, "refout_bits %u\n", refout_bits);
+	/* Set REFOUT */
+	max2175_set_bits(ctx, 56, 7, 5, refout_bits);
+
+	/* Send out all the registers to device except register 0 */
+	max2175_flush_regstore(ctx, 1, 145);
+	usleep_range(5000, 5500);
+
+	/* 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;
+	mxm_dbg(ctx, "init pm done\n");
+
+	/* Recalibrate ADC */
+	ret = max2175_recalibrate_adc(ctx);
+	if (ret)
+		return ret;
+	mxm_dbg(ctx, "adc recalibration done\n");
+
+	/* 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;
+}
+
+#define max2175_ctx_from_sd(x)	\
+	container_of(x, struct max2175_ctx, sd)
+#define max2175_ctx_from_ctrl(x)	\
+	container_of(x, struct max2175_ctx, ctrl_hdl)
+
+static void max2175_s_ctrl_i2s_mode(struct max2175_ctx *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_is_i2s_rx_mode_valid(ctx, ctx->mode, i2s_mode))
+		max2175_update_i2s_mode(ctx, i2s_mode);
+}
+
+static void max2175_s_ctrl_rx_mode(struct max2175_ctx *ctx, u32 rx_mode)
+{
+	int ret;
+
+	/* Load mode. Range check already done */
+	ret = max2175_set_rx_mode(ctx, rx_mode,
+				  ctx->am_hiz->val, ctx->hsls->val);
+	if (ret)
+		return;
+
+	/* Get current i2s_mode and update if needed for given rx_mode */
+	if (max2175_is_i2s_rx_mode_valid(ctx, rx_mode, ctx->i2s_mode->val))
+		max2175_update_i2s_mode(ctx, 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_is_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 *ctx = max2175_ctx_from_ctrl(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_EN:
+		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_AM_HIZ:
+		v4l2_ctrl_activate(ctx->am_hiz, false);
+		break;
+	case V4L2_CID_MAX2175_HSLS:
+		v4l2_ctrl_activate(ctx->hsls, false);
+		break;
+	case V4L2_CID_MAX2175_RX_MODE:
+		mxm_dbg(ctx, "rx mode %u\n", ctrl->val);
+		max2175_s_ctrl_rx_mode(ctx, ctrl->val);
+		break;
+	default:
+		v4l2_err(ctx->client, "s:invalid ctrl id 0x%x\n", ctrl->id);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int max2175_get_lna_gain(struct max2175_ctx *ctx)
+{
+	int gain = 0;
+	enum max2175_band band = max2175_get_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:
+		v4l2_err(ctx->client, "invalid band %d to get rf gain\n", band);
+		break;
+	}
+	return gain;
+}
+
+static int max2175_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct max2175_ctx *ctx = max2175_ctx_from_ctrl(ctrl->handler);
+
+	/* Only the dynamically changing values need to be in g_volatile_ctrl */
+	mxm_dbg(ctx, "g_volatile_ctrl: id 0x%x\n", ctrl->id);
+	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;
+	default:
+		v4l2_err(ctx->client, "g:invalid ctrl id 0x%x\n", ctrl->id);
+		return -EINVAL;
+	}
+	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 *ctx, u32 freq)
+{
+	u32 rx_mode;
+	int ret;
+
+	/* Get band from frequency */
+	ret = max2175_mode_from_freq(ctx, freq, &rx_mode);
+	if (ret) {
+		v4l2_err(ctx->client, "set freq mode failed: freq %u ret %d\n",
+			 freq, ret);
+		return ret;
+	}
+	mxm_dbg(ctx, "set_freq_and_mode: freq %u rx_mode %d\n", freq, rx_mode);
+
+	/* Load mode */
+	ret = max2175_set_rx_mode(ctx, rx_mode, ctx->am_hiz->val,
+				  ctx->hsls->val);
+	if (ret)
+		return ret;
+
+	/* Get current i2s_mode and update if needed for given rx_mode */
+	if (max2175_is_i2s_rx_mode_valid(ctx, rx_mode, ctx->i2s_mode->val))
+		max2175_update_i2s_mode(ctx, ctx->i2s_mode->val);
+	else
+		v4l2_ctrl_s_ctrl(ctx->i2s_mode,
+				 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 *ctx = max2175_ctx_from_sd(sd);
+	u32 freq;
+	int ret = 0;
+
+	mxm_dbg(ctx, "s_freq: tuner %u type %u freq: new %u, curr %u\n",
+		vf->tuner, vf->type, vf->frequency, ctx->freq);
+
+	if (vf->tuner != 0) {
+		v4l2_err(ctx->client, "s_freq: invalid tuner %u\n", vf->tuner);
+		return -EINVAL;
+	}
+	/* RF freq */
+	freq = clamp(vf->frequency, bands_rf.rangelow, bands_rf.rangehigh);
+	if (ctx->mode_resolved)
+		/* Check new freq valid for mode */
+		if (max2175_is_freq_rx_mode_valid(ctx, ctx->mode, freq))
+			ret = max2175_tune_rf_freq(ctx, freq, ctx->hsls->val);
+		else
+			/* Find default mode for freq and tune to it */
+			ret = max2175_set_freq_and_mode(ctx, freq);
+	else
+		ret = max2175_set_freq_and_mode(ctx, freq);
+
+	mxm_dbg(ctx, "s_freq: ret %d mode %d freq: curr %u\n",
+		ret, ctx->mode, ctx->freq);
+	return ret;
+}
+
+static int max2175_g_frequency(struct v4l2_subdev *sd,
+			       struct v4l2_frequency *vf)
+{
+	struct max2175_ctx *ctx = max2175_ctx_from_sd(sd);
+	int ret = 0;
+
+	if (vf->tuner != 0) {
+		v4l2_err(ctx->client, "g_freq: invalid tuner %u\n", vf->tuner);
+		return -EINVAL;
+	}
+
+	/* RF freq */
+	vf->type = V4L2_TUNER_RF;
+	vf->frequency = ctx->freq;
+	mxm_dbg(ctx, "g_freq: ret %d tuner %u, type %u, vf %u\n",
+		ret, vf->tuner, vf->type, vf->frequency);
+
+	return ret;
+}
+
+static int max2175_enum_freq_bands(struct v4l2_subdev *sd,
+			    struct v4l2_frequency_band *band)
+{
+	struct max2175_ctx *ctx = max2175_ctx_from_sd(sd);
+	int ret = 0;
+
+	if (band->tuner == 0 && band->index == 0)
+		*band = bands_rf;
+	else
+		ret = -EINVAL;
+
+	mxm_dbg(ctx,
+		"enum_freq: ret %d tuner %u type %u index %u low %u high %u\n",
+		ret, band->tuner, band->type, band->index, band->rangelow,
+		band->rangehigh);
+	return ret;
+}
+
+static int max2175_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+	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 = bands_rf.rangelow;
+	vt->rangehigh = 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_EN,
+	.name = "I2S Enable",
+	.type = V4L2_CTRL_TYPE_BOOLEAN,
+	.min = 0,
+	.max = 1,
+	.step = 1,
+	.def = 1,
+};
+
+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_INTEGER,
+	.min = 0,
+	.max = 4,
+	.step = 1,
+	.def = 0,
+};
+
+static const struct v4l2_ctrl_config max2175_am_hiz = {
+	.ops = &max2175_ctrl_ops,
+	.id = V4L2_CID_MAX2175_AM_HIZ,
+	.name = "AM High impedance input",
+	.type = V4L2_CTRL_TYPE_BOOLEAN,
+	.min = 0,
+	.max = 1,
+	.step = 1,
+	.def = 0,
+};
+
+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,
+};
+
+
+/* NOTE: Any addition/deletion in the below list should be reflected in
+ * max2175_modetag enum
+ */
+static const char * const max2175_ctrl_eu_rx_mode_strings[] = {
+	"DAB 1.2",
+	"NULL",
+};
+
+static const char * const max2175_ctrl_na_rx_mode_strings[] = {
+	"NA FM 1.0",
+	"NULL",
+};
+
+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_mode_strings) - 2,
+	.def = 0,
+	.qmenu = max2175_ctrl_eu_rx_mode_strings,
+};
+
+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_mode_strings) - 2,
+	.def = 0,
+	.qmenu = max2175_ctrl_na_rx_mode_strings,
+};
+
+static u32 max2175_refout_load_to_bits(struct i2c_client *client, u32 load)
+{
+	u32 bits = 0;	/* REFOUT disabled */
+
+	if (load >= 0 && load <= 40)
+		bits = load / 10;
+	else if (load >= 60 && load <= 70)
+		bits = load / 10 - 1;
+	else
+		dev_warn(&client->dev, "invalid refout_load %u\n", load);
+
+	return bits;
+}
+
+static int max2175_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct max2175_ctx *ctx;
+	struct device *dev = &client->dev;
+	struct v4l2_subdev *sd;
+	struct v4l2_ctrl_handler *hdl;
+	struct clk *clk;
+	bool master = true;
+	u32 refout_load, refout_bits = 0;	/* REFOUT disabled */
+	int ret;
+
+	/* Check if the adapter supports the needed features */
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_BYTE_DATA)) {
+		dev_err(&client->dev, "i2c check failed\n");
+		return -EIO;
+	}
+
+	if (of_find_property(client->dev.of_node, "maxim,slave", NULL))
+		master = false;
+
+	if (!of_property_read_u32(client->dev.of_node, "maxim,refout-load",
+				 &refout_load))
+		refout_bits = max2175_refout_load_to_bits(client, refout_load);
+
+	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;
+	}
+
+	ctx = kzalloc(sizeof(struct max2175_ctx),
+			     GFP_KERNEL);
+	if (ctx == NULL)
+		return -ENOMEM;
+
+	sd = &ctx->sd;
+	ctx->master = master;
+	ctx->mode_resolved = false;
+
+	/* Set the defaults */
+	ctx->freq = bands_rf.rangelow;
+
+	ctx->xtal_freq = clk_get_rate(clk);
+	dev_info(&client->dev, "xtal freq %lluHz\n", ctx->xtal_freq);
+
+	v4l2_i2c_subdev_init(sd, client, &max2175_ops);
+	ctx->client = client;
+
+	sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+	ctx->dev = dev;
+
+	/* Controls */
+	hdl = &ctx->ctrl_hdl;
+	ret = v4l2_ctrl_handler_init(hdl, 8);
+	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->am_hiz = v4l2_ctrl_new_custom(hdl, &max2175_am_hiz, 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 = (struct max2175_rxmode *)eu_rx_modes;
+	} else {
+		ctx->rx_mode = v4l2_ctrl_new_custom(hdl,
+						    &max2175_na_rx_mode, NULL);
+		ctx->rx_modes = (struct max2175_rxmode *)na_rx_modes;
+	}
+	ctx->sd.ctrl_handler = &ctx->ctrl_hdl;
+
+	ret = v4l2_async_register_subdev(sd);
+	if (ret) {
+		dev_err(&client->dev, "register subdev failed\n");
+		goto err_reg;
+	}
+	dev_info(&client->dev, "subdev registered\n");
+
+	/* Initialize device */
+	ret = max2175_core_init(ctx, refout_bits);
+	if (ret)
+		goto err_init;
+
+	mxm_dbg(ctx, "probed\n");
+	return 0;
+
+err_init:
+	v4l2_async_unregister_subdev(sd);
+err_reg:
+	v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
+err:
+	kfree(ctx);
+	return ret;
+}
+
+static int max2175_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct max2175_ctx *ctx = max2175_ctx_from_sd(sd);
+
+	v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
+	v4l2_async_unregister_subdev(sd);
+	mxm_dbg(ctx, "removed\n");
+	kfree(ctx);
+	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..61a508b
--- /dev/null
+++ b/drivers/media/i2c/max2175/max2175.h
@@ -0,0 +1,124 @@
+/*
+ * 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__
+
+#include <linux/types.h>
+
+enum max2175_region {
+	MAX2175_REGION_EU = 0,	/* Europe */
+	MAX2175_REGION_NA,	/* North America */
+};
+
+#define MAX2175_EU_XTAL_FREQ	(36864000)	/* In Hz */
+#define MAX2175_NA_XTAL_FREQ	(40186125)	/* In Hz */
+
+enum max2175_band {
+	MAX2175_BAND_AM = 0,
+	MAX2175_BAND_FM,
+	MAX2175_BAND_VHF,
+	MAX2175_BAND_L,
+};
+
+/* NOTE: Any addition/deletion in the below enum should be reflected in
+ * V4L2_CID_MAX2175_RX_MODE ctrl strings
+ */
+enum max2175_modetag {
+	/* EU modes */
+	MAX2175_DAB_1_2 = 0,
+
+	/* Other possible modes to add in future
+	 * MAX2175_DAB_1_0,
+	 * MAX2175_DAB_1_3,
+	 * MAX2175_EU_FM_2_2,
+	 * MAX2175_EU_FM_1_0,
+	 * MAX2175_EU_FMHD_4_0,
+	 * MAX2175_EU_AM_1_0,
+	 * MAX2175_EU_AM_2_2,
+	 */
+
+	/* NA modes */
+	MAX2175_NA_FM_1_0 = 0,
+
+	/* Other possible modes to add in future
+	 * MAX2175_NA_FM_1_2,
+	 * 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_CSM_MODE_LOAD_TO_BUFFER = 0,
+	MAX2175_CSM_MODE_PRESET_TUNE,
+	MAX2175_CSM_MODE_SEARCH,
+	MAX2175_CSM_MODE_AF_UPDATE,
+	MAX2175_CSM_MODE_JUMP_FAST_TUNE,
+	MAX2175_CSM_MODE_CHECK,
+	MAX2175_CSM_MODE_LOAD_AND_SWAP,
+	MAX2175_CSM_MODE_END,
+	MAX2175_CSM_MODE_BUFFER_PLUS_PRESET_TUNE,
+	MAX2175_CSM_MODE_BUFFER_PLUS_SEARCH,
+	MAX2175_CSM_MODE_BUFFER_PLUS_AF_UPDATE,
+	MAX2175_CSM_MODE_BUFFER_PLUS_JUMP_FAST_TUNE,
+	MAX2175_CSM_MODE_BUFFER_PLUS_CHECK,
+	MAX2175_CSM_MODE_BUFFER_PLUS_LOAD_AND_SWAP,
+	MAX2175_CSM_MODE_NO_ACTION
+};
+
+/* 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 */
+struct max2175_regmap {
+	u8 idx;				/* Register index */
+	u8 val;				/* Register value */
+};
+
+#endif /* __MAX2175_H__ */
-- 
1.9.1

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

* [RFC 2/5] media: v4l2-ctrls: Reserve controls for MAX217X
  2016-10-12 14:10 [RFC 0/5] Add V4L2 SDR (DRIF & MAX2175) driver Ramesh Shanmugasundaram
  2016-10-12 14:10 ` [RFC 1/5] media: i2c: max2175: Add MAX2175 support Ramesh Shanmugasundaram
@ 2016-10-12 14:10 ` Ramesh Shanmugasundaram
  2016-10-12 14:10 ` [RFC 3/5] media: platform: rcar_drif: Add DRIF support Ramesh Shanmugasundaram
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 39+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-10-12 14:10 UTC (permalink / raw)
  To: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope
  Cc: chris.paterson2, laurent.pinchart, geert, 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] 39+ messages in thread

* [RFC 3/5] media: platform: rcar_drif: Add DRIF support
  2016-10-12 14:10 [RFC 0/5] Add V4L2 SDR (DRIF & MAX2175) driver Ramesh Shanmugasundaram
  2016-10-12 14:10 ` [RFC 1/5] media: i2c: max2175: Add MAX2175 support Ramesh Shanmugasundaram
  2016-10-12 14:10 ` [RFC 2/5] media: v4l2-ctrls: Reserve controls for MAX217X Ramesh Shanmugasundaram
@ 2016-10-12 14:10 ` Ramesh Shanmugasundaram
  2016-10-18 13:13   ` Rob Herring
  2016-10-18 14:29   ` Geert Uytterhoeven
  2016-10-12 14:10 ` [RFC 4/5] media: Add new SDR formats SC16, SC18 & SC20 Ramesh Shanmugasundaram
  2016-10-12 14:10 ` [RFC 5/5] doc_rst: media: New " Ramesh Shanmugasundaram
  4 siblings, 2 replies; 39+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-10-12 14:10 UTC (permalink / raw)
  To: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope
  Cc: chris.paterson2, laurent.pinchart, geert, 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     |  109 ++
 drivers/media/platform/Kconfig                     |   25 +
 drivers/media/platform/Makefile                    |    1 +
 drivers/media/platform/rcar_drif.c                 | 1534 ++++++++++++++++++++
 4 files changed, 1669 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..24239d9
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
@@ -0,0 +1,109 @@
+Renesas R-Car Gen3 DRIF controller (DRIF)
+-----------------------------------------
+
+Required properties:
+--------------------
+- compatible: "renesas,drif-r8a7795" 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 each sub-channel.
+- interrupts: associated with each sub-channel.
+- clocks: phandles and clock specifiers for each sub-channel.
+- clock-names: clock input name strings: "fck0", "fck1".
+- pinctrl-0: pin control group to be used for this controller.
+- pinctrl-names: must be "default".
+- dmas: phandles to the DMA channels for each sub-channel.
+- dma-names: names for the DMA channels: "rx0", "rx1".
+
+Required child nodes:
+---------------------
+- Each DRIF channel can have one or both of the sub-channels enabled in a
+  setup. The sub-channels are represented as a child node. The name of the
+  child nodes are "sub-channel0" and "sub-channel1" respectively. Each child
+  node supports the "status" property only, which is used to enable/disable
+  the respective sub-channel.
+
+Optional properties:
+--------------------
+- port: video interface child port node of a channel that defines the local
+  and remote endpoints. The remote endpoint is assumed to a tuner subdevice
+  endpoint.
+- power-domains: phandle to respective power domain.
+- 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
+
+drif0: rif@e6f40000 {
+	compatible = "renesas,drif-r8a7795",
+		   "renesas,rcar-gen3-drif";
+	reg = <0 0xe6f40000 0 0x64>, <0 0xe6f50000 0 0x64>;
+	interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>,
+		   <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
+	clocks = <&cpg CPG_MOD 515>, <&cpg CPG_MOD 514>;
+	clock-names = "fck0", "fck1";
+	dmas = <&dmac1 0x20>, <&dmac1 0x22>;
+	dma-names = "rx0", "rx1";
+	power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+	status = "disabled";
+
+	sub-channel0 {
+		status = "disabled";
+	};
+
+	sub-channel1 {
+		status = "disabled";
+	};
+
+};
+
+Board specific dts file
+
+&drif0 {
+	pinctrl-0 = <&drif0_pins>;
+	pinctrl-names = "default";
+	status = "okay";
+
+	sub-channel0 {
+		status = "okay";
+	};
+
+	sub-channel1 {
+		status = "okay";
+	};
+
+	port {
+		drif0_ep: endpoint {
+		     remote-endpoint = <&tuner_subdev_ep>;
+		};
+	};
+};
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index e9fc288..0ba6c1f 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -377,3 +377,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 && VIDEO_V4L2_SUBDEV_API && 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 40b18d1..e3e38b4 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..39d8637
--- /dev/null
+++ b/drivers/media/platform/rcar_drif.c
@@ -0,0 +1,1534 @@
+/*
+ * 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.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>
+
+/* Internal flags */
+#define RCAR_DRIF_BUF_DONE			BIT(0)
+#define RCAR_DRIF_BUF_OVERFLOW			BIT(1)
+
+#define RCAR_DRIF_BUF_PAIRS_DONE(buf1, buf2)	\
+	((buf1->status & buf2->status & RCAR_DRIF_BUF_DONE) == \
+	 RCAR_DRIF_BUF_DONE)
+
+/* 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 */
+
+/* Configuration */
+#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			(64 * 1024)
+
+/* A tuner for each sub channel */
+#define RCAR_DRIF_MAX_SUBDEVS			RCAR_DRIF_MAX_SUBCHANS
+
+/* 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,
+	},
+};
+
+#define to_rcar_drif_buf_pair(p, ch, idx)		\
+	(p->schan[!ch]->buf[idx])
+
+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 */
+	int status;			/* Buffer status flags */
+};
+
+/* Sub-channel resources */
+struct rcar_drif_subchan {
+	struct rcar_drif_chan *parent;	/* Parent ctx */
+	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 */
+	u8 num;				/* Sub-channel number */
+};
+
+/* Channel context */
+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) */
+	u32 num_subdevs;		/* Num of sub devices used */
+
+	/* Current V4L2 SDR format array index */
+	u32 fmt_idx;
+
+	/* Device tree SYNC properties */
+	u32 mdr1;
+
+	/* Internals */
+	struct rcar_drif_subchan *schan[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;			/* Counter: Buf produced by dev */
+};
+
+/* Allocate buffer context */
+static int rcar_drif_alloc_bufctxt(struct rcar_drif_chan *ch)
+{
+	struct rcar_drif_hwbuf *bufctx;
+	u32 i, idx;
+
+	/* Alloc buf contexts for given number of sub-channels */
+	for_each_set_bit(i, &ch->cur_schans_mask, RCAR_DRIF_MAX_SUBCHANS) {
+		bufctx = kcalloc(ch->num_hwbufs, sizeof(*bufctx), GFP_KERNEL);
+		if (!bufctx)
+			return 1;
+
+		for (idx = 0; idx < ch->num_hwbufs; idx++)
+			ch->schan[i]->buf[idx] = bufctx + idx;
+	}
+	return 0;
+}
+
+/* Release buffer context */
+static void rcar_drif_release_bufctxt(struct rcar_drif_chan *ch)
+{
+	u32 i;
+
+	/* First entry of first sub channel is the bufctxt ptr always */
+	for_each_set_bit(i, &ch->cur_schans_mask, RCAR_DRIF_MAX_SUBCHANS) {
+		kfree(ch->schan[i]->buf[0]);
+		ch->schan[i]->buf[0] = NULL;
+	}
+}
+
+/* Release DMA channel */
+static void rcar_drif_release_dmachannel(struct rcar_drif_chan *ch)
+{
+	u32 i;
+
+	for_each_set_bit(i, &ch->cur_schans_mask, RCAR_DRIF_MAX_SUBCHANS)
+		if (ch->schan[i]->dmach) {
+			dma_release_channel(ch->schan[i]->dmach);
+			ch->schan[i]->dmach = NULL;
+		}
+}
+
+/* Allocate DMA channel */
+static int rcar_drif_alloc_dmachannel(struct rcar_drif_chan *ch)
+{
+	u32 i;
+	struct dma_slave_config dma_cfg;
+	int ret = -ENODEV;
+
+	for_each_set_bit(i, &ch->cur_schans_mask, RCAR_DRIF_MAX_SUBCHANS) {
+		ch->schan[i]->dmach =
+			dma_request_slave_channel(ch->dev, (i ? "rx1" : "rx0"));
+		if (!ch->schan[i]->dmach) {
+			dev_err(&ch->vdev.dev,
+				"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)(ch->schan[i]->start +
+						 RCAR_DRIF_SIRFDR);
+		dma_cfg.dst_addr = 0;
+		dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		ret = dmaengine_slave_config(ch->schan[i]->dmach, &dma_cfg);
+		if (ret) {
+			dev_err(&ch->vdev.dev,
+				"schan%u: dma slave config failed\n", i);
+			goto dmach_error;
+		}
+		dev_dbg(&ch->vdev.dev, "schan%u: dma slave configed\n", i);
+	}
+
+	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)
+{
+	u32 i;
+
+	/* Set defaults for both sub-channels */
+	for_each_set_bit(i, &ch->cur_schans_mask, RCAR_DRIF_MAX_SUBCHANS) {
+		/* Refer MSIOF section in manual for this register setting */
+		writel(RCAR_DRIF_SITMDR1_PCON,
+		       ch->schan[i]->base + RCAR_DRIF_SITMDR1);
+
+		/* Setup MDR1 value */
+		writel(ch->mdr1, ch->schan[i]->base + RCAR_DRIF_SIRMDR1);
+
+		dev_dbg(&ch->vdev.dev,
+			"schan%u: SIRMDR1 = 0x%08x",
+			i, readl(ch->schan[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)
+{
+	u32 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))) {
+		dev_err(&ch->vdev.dev, "invalid wdlen %u configured\n",
+			wdlen);
+		return 1;
+	}
+
+	return 0;
+}
+
+/* Set DRIF receive format */
+static int rcar_drif_set_format(struct rcar_drif_chan *ch)
+{
+	u32 i, bitlen, wdcnt, wdlen;
+
+	wdlen = formats[ch->fmt_idx].wdlen;
+	dev_dbg(&ch->vdev.dev, "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) {
+		dev_err(ch->dev, "fmt idx %u current schans %u mismatch\n",
+			ch->fmt_idx, ch->num_cur_schans);
+		return 1;
+	}
+
+	if (rcar_drif_convert_wdlen(ch, wdlen, &bitlen, &wdcnt))
+		return 1;
+
+	/* Setup group, bitlen & word count */
+	for_each_set_bit(i, &ch->cur_schans_mask, RCAR_DRIF_MAX_SUBCHANS) {
+		u32 mdr;
+
+		/* Two groups */
+		mdr = RCAR_DRIF_MDR_GRPCNT(2) | RCAR_DRIF_MDR_BITLEN(bitlen) |
+		       RCAR_DRIF_MDR_WDCNT(wdcnt);
+		writel(mdr, ch->schan[i]->base + RCAR_DRIF_SIRMDR2);
+
+		mdr = RCAR_DRIF_MDR_BITLEN(bitlen) | RCAR_DRIF_MDR_WDCNT(wdcnt);
+		writel(mdr, ch->schan[i]->base + RCAR_DRIF_SIRMDR3);
+
+		dev_dbg(&ch->vdev.dev,
+			"schan%u: new SIRMDR[2,3] = 0x%08x 0x%08x\n",
+			i, readl(ch->schan[i]->base + RCAR_DRIF_SIRMDR2),
+			readl(ch->schan[i]->base + RCAR_DRIF_SIRMDR3));
+	}
+	return 0;
+}
+
+/* Release DMA buffers */
+static void rcar_drif_release_buf(struct rcar_drif_chan *ch)
+{
+	u32 i;
+
+	for_each_set_bit(i, &ch->cur_schans_mask, RCAR_DRIF_MAX_SUBCHANS) {
+		struct rcar_drif_subchan *schan = ch->schan[i];
+
+		/* First entry of the sub-channel contains the dma buf ptr */
+		if (schan->buf[0] && schan->buf[0]->addr) {
+			dma_free_coherent(ch->dev,
+				ch->hwbuf_size * ch->num_hwbufs,
+				schan->buf[0]->addr, schan->dma_handle);
+			schan->buf[0]->addr = NULL;
+		}
+	}
+}
+
+/* Request DMA buffers */
+static int rcar_drif_request_buf(struct rcar_drif_chan *ch)
+{
+	int ret = 0;
+	u32 i, j;
+	void *addr;
+
+	for_each_set_bit(i, &ch->cur_schans_mask, RCAR_DRIF_MAX_SUBCHANS) {
+		struct rcar_drif_subchan *schan = ch->schan[i];
+
+		/* Allocate DMA buffers */
+		addr = dma_alloc_coherent(ch->dev,
+				ch->hwbuf_size * ch->num_hwbufs,
+				&schan->dma_handle, GFP_KERNEL);
+		if (!addr) {
+			dev_err(&ch->vdev.dev,
+				"dma alloc failed. num_hwbufs %u hwbuf_size %u\n",
+				ch->num_hwbufs, ch->hwbuf_size);
+			ret = 1;
+			goto alloc_error;
+		}
+
+		for (j = 0; j < ch->num_hwbufs; j++) {
+			schan->buf[j]->addr = addr + (j * ch->hwbuf_size);
+			schan->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);
+
+	dev_dbg(&ch->vdev.dev, "num_buffers %u vq->num_buffers %u\n",
+		*num_buffers, vq->num_buffers);
+
+	/* 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);
+
+	dev_dbg(&ch->vdev.dev, "nbuf %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;
+
+	dev_dbg(&ch->vdev.dev, "buf_queue idx %u len %u, used %d\n", vb->index,
+		vb->planes[0].length, vb->planes[0].bytesused);
+	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 *schan, u32 idx)
+{
+	struct rcar_drif_chan *ch = schan->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. There will be a gap in
+		 * sequence number when app recovers
+		 */
+		dev_dbg(&ch->vdev.dev, "app late: schan%u: prod %u\n",
+			schan->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 (schan->num) {
+			buf_i = to_rcar_drif_buf_pair(ch, schan->num, idx);
+			buf_q = schan->buf[idx];
+		} else {
+			buf_q = to_rcar_drif_buf_pair(ch, schan->num, idx);
+			buf_i = schan->buf[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) {
+			/* Clear the flag in status */
+			buf_i->status &= ~RCAR_DRIF_BUF_OVERFLOW;
+			buf_q->status &= ~RCAR_DRIF_BUF_OVERFLOW;
+			overflow = true;
+		}
+	} else {
+		struct rcar_drif_hwbuf *buf_iq;
+
+		buf_iq = schan->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) {
+			/* Clear the flag in status */
+			buf_iq->status &= ~RCAR_DRIF_BUF_OVERFLOW;
+			overflow = true;
+		}
+	}
+
+	dev_dbg(&ch->vdev.dev, "schan%u: produced %u\n",
+		schan->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);
+	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);
+}
+
+/* Check if DMA of the paired sub-channel is also complete */
+static void rcar_drif_dmapair_complete(struct rcar_drif_subchan *schan, u32 idx)
+{
+	struct rcar_drif_chan *ch = schan->parent;
+	struct rcar_drif_hwbuf *buf, *pair;
+
+	buf = schan->buf[idx];
+	pair = to_rcar_drif_buf_pair(ch, schan->num, idx);
+	buf->status |= RCAR_DRIF_BUF_DONE;
+	dev_dbg(&ch->vdev.dev, "schan%u: status: buf 0x%08x pair 0x%08x\n",
+		schan->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(schan, idx);
+	}
+}
+
+/* DMA callback for each stage */
+static void rcar_drif_dma_complete(void *dma_async_param)
+{
+	struct rcar_drif_subchan *schan =
+		(struct rcar_drif_subchan *)dma_async_param;
+	struct rcar_drif_chan *ch = schan->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 = schan->buf[idx];
+
+	/* Check for DRIF errors */
+	str = readl(schan->base + RCAR_DRIF_SISTR);
+	if (str & RCAR_DRIF_RFOVF) {
+		/* Writing the same clears it */
+		writel(str, schan->base + RCAR_DRIF_SISTR);
+
+		/* Overflow: some samples are lost */
+		buf->status |= RCAR_DRIF_BUF_OVERFLOW;
+		dev_dbg(&ch->vdev.dev, "fifo overflow: schan%u: prod %d\n",
+			schan->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_dmapair_complete(schan, idx);
+	else
+		rcar_drif_deliver_buf(schan, idx);
+
+	mutex_unlock(&ch->vb_queue_mutex);
+}
+
+static int rcar_drif_qbuf(struct rcar_drif_subchan *schan)
+{
+	struct rcar_drif_chan *ch = schan->parent;
+	dma_addr_t addr = schan->dma_handle;
+	struct dma_async_tx_descriptor *rxd;
+	dma_cookie_t cookie;
+
+	/* Setup cyclic DMA with given buffers */
+	rxd = dmaengine_prep_dma_cyclic(schan->dmach, addr,
+					ch->hwbuf_size * ch->num_hwbufs,
+					ch->hwbuf_size, DMA_DEV_TO_MEM,
+					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!rxd) {
+		dev_err(&ch->vdev.dev, "schan%u: prep dma cyclic failed\n",
+			schan->num);
+		return 1;
+	}
+
+	/* Submit descriptor */
+	rxd->callback = rcar_drif_dma_complete;
+	rxd->callback_param = schan;
+	dev_dbg(&ch->vdev.dev, "schan%u: param %p\n", schan->num, schan);
+	cookie = dmaengine_submit(rxd);
+	if (dma_submit_error(cookie)) {
+		dev_err(&ch->vdev.dev, "schan%u: dma submit failed\n",
+			schan->num);
+		return 1;
+	}
+
+	dma_async_issue_pending(schan->dmach);
+	return 0;
+}
+
+/* Enable reception */
+static int rcar_drif_enable_rx(struct rcar_drif_chan *ch)
+{
+	u32 ctr, i;
+	int ret;
+
+	/*
+	 * When both sub-channels are enabled, they can be syncronized only by
+	 * the master
+	 */
+
+	/* Enable receive */
+	for_each_set_bit(i, &ch->cur_schans_mask, RCAR_DRIF_MAX_SUBCHANS) {
+		ctr = readl(ch->schan[i]->base + RCAR_DRIF_SICTR);
+		ctr |= (RCAR_DRIF_SICTR_RX_RISING_EDGE |
+			 RCAR_DRIF_SICTR_RX_EN);
+		writel(ctr, ch->schan[i]->base + RCAR_DRIF_SICTR);
+	}
+
+	/* Check receive enabled */
+	for_each_set_bit(i, &ch->cur_schans_mask, RCAR_DRIF_MAX_SUBCHANS) {
+		ret = readl_poll_timeout(ch->schan[i]->base + RCAR_DRIF_SICTR,
+					 ctr, ctr & RCAR_DRIF_SICTR_RX_EN,
+					 2, 500000);
+		if (ret) {
+			dev_err(&ch->vdev.dev,
+			"schan%u: failed to enable rx. CTR 0x%08x\n",
+			i, readl(ch->schan[i]->base + RCAR_DRIF_SICTR));
+			break;
+		}
+	}
+	return ret;
+}
+
+/* Disable reception */
+static void rcar_drif_disable_rx(struct rcar_drif_chan *ch)
+{
+	u32 ctr, i;
+	int ret;
+
+	/* Disable receive */
+	for_each_set_bit(i, &ch->cur_schans_mask, RCAR_DRIF_MAX_SUBCHANS) {
+		ctr = readl(ch->schan[i]->base + RCAR_DRIF_SICTR);
+		ctr &= ~RCAR_DRIF_SICTR_RX_EN;
+		writel(ctr, ch->schan[i]->base + RCAR_DRIF_SICTR);
+	}
+
+	/* Check receive disabled */
+	for_each_set_bit(i, &ch->cur_schans_mask, RCAR_DRIF_MAX_SUBCHANS) {
+		ret = readl_poll_timeout(ch->schan[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->schan[i]->base + RCAR_DRIF_SICTR));
+	}
+}
+
+/* Start sub-channel */
+static int rcar_drif_start_subchan(struct rcar_drif_subchan *schan)
+{
+	struct rcar_drif_chan *ch = schan->parent;
+	u32 ctr, str;
+	int ret;
+
+	/* Reset receive */
+	writel(RCAR_DRIF_SICTR_RESET, schan->base + RCAR_DRIF_SICTR);
+	ret = readl_poll_timeout(schan->base + RCAR_DRIF_SICTR,
+					 ctr, !(ctr & RCAR_DRIF_SICTR_RESET),
+					 2, 500000);
+	if (ret) {
+		dev_err(&ch->vdev.dev,
+			"schan%u: failed to reset rx. CTR 0x%08x\n",
+			schan->num, readl(schan->base + RCAR_DRIF_SICTR));
+		return ret;
+	}
+
+	/* Queue buffers for DMA */
+	if (rcar_drif_qbuf(schan)) {
+		dev_err(&ch->vdev.dev,
+			"failed to queue schan %u DMA transfer.\n", schan->num);
+		return 1;
+	}
+
+	/* Clear status register flags */
+	str = RCAR_DRIF_RFFUL | RCAR_DRIF_REOF | RCAR_DRIF_RFSERR |
+		RCAR_DRIF_RFUDF | RCAR_DRIF_RFOVF;
+	writel(str, schan->base + RCAR_DRIF_SISTR);
+
+	/* Enable DMA receive interrupt */
+	writel(0x00009000, schan->base + RCAR_DRIF_SIIER);
+
+	return 0;
+}
+
+/* Start receive operation */
+static int rcar_drif_start(struct rcar_drif_chan *ch)
+{
+	u32 i;
+	int ret;
+
+	for_each_set_bit(i, &ch->cur_schans_mask, RCAR_DRIF_MAX_SUBCHANS) {
+		ret = rcar_drif_start_subchan(ch->schan[i]);
+		if (ret)
+			goto start_error;
+	}
+
+	/* Reset counters */
+	ch->produced = 0;
+
+	/* Enable Rx */
+	ret = rcar_drif_enable_rx(ch);
+	if (ret)
+		goto start_error;
+
+	dev_dbg(&ch->vdev.dev, "started\n");
+
+start_error:
+	return ret;
+}
+
+/* Stop sub-channel */
+static void rcar_drif_stop_subchan(struct rcar_drif_subchan *schan)
+{
+	struct rcar_drif_chan *ch = schan->parent;
+	int ret, retries = 3;
+
+	/* Disable DMA receive interrupt */
+	writel(0x00000000, schan->base + RCAR_DRIF_SIIER);
+
+	do {
+		/* Terminate all DMA transfers */
+		ret = dmaengine_terminate_sync(schan->dmach);
+		if (!ret)
+			break;
+		dev_dbg(&ch->vdev.dev, "stop retry\n");
+	} while (--retries);
+
+	WARN_ON(!retries);
+}
+
+/* Stop receive operation */
+static void rcar_drif_stop(struct rcar_drif_chan *ch)
+{
+	u32 i;
+
+	/* Disable Rx */
+	rcar_drif_disable_rx(ch);
+
+	for_each_set_bit(i, &ch->cur_schans_mask, RCAR_DRIF_MAX_SUBCHANS)
+		rcar_drif_stop_subchan(ch->schan[i]);
+
+	dev_dbg(&ch->vdev.dev, "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);
+	int ret, i, j;
+
+	dev_dbg(&ch->vdev.dev, "start streaming\n");
+	mutex_lock(&ch->v4l2_mutex);
+
+	for_each_set_bit(i, &ch->cur_schans_mask, RCAR_DRIF_MAX_SUBCHANS) {
+		ret = clk_prepare_enable(ch->schan[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;
+
+	dev_dbg(&ch->vdev.dev, "num_hwbufs %u, hwbuf_size %u\n",
+		ch->num_hwbufs, ch->hwbuf_size);
+
+	if (rcar_drif_alloc_dmachannel(ch)) {
+		ret = -ENODEV;
+		goto start_error;
+	}
+
+	/* Alloc buf context */
+	if (rcar_drif_alloc_bufctxt(ch)) {
+		ret = -ENOMEM;
+		goto start_error;
+	}
+
+	if (rcar_drif_request_buf(ch)) {
+		ret = -ENOMEM;
+		goto start_error;
+	}
+
+	dev_dbg(&ch->vdev.dev, "dma setup successful\n");
+
+	ret = rcar_drif_start(ch);
+	if (ret)
+		goto start_error;
+
+	mutex_unlock(&ch->v4l2_mutex);
+	return ret;
+
+start_error:
+	/* Return all queued buffers to vb2 */
+	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->schan[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);
+	u32 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->schan[i]->clkp);
+
+	mutex_unlock(&ch->v4l2_mutex);
+	dev_dbg(&ch->vdev.dev, "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)
+{
+	u32 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;
+		}
+	}
+	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;
+	u32 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 subch 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;
+			}
+
+			dev_dbg(&ch->vdev.dev, "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);
+	u32 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;
+}
+
+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;
+
+	list_for_each_entry_safe(sd, tmp, &ch->v4l2_dev.subdevs, list)
+		ret = v4l2_subdev_call(sd, tuner, enum_freq_bands, band);
+
+	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;
+
+	list_for_each_entry_safe(sd, tmp, &ch->v4l2_dev.subdevs, list)
+		ret = v4l2_subdev_call(sd, tuner, g_frequency, f);
+
+	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;
+
+	list_for_each_entry_safe(sd, tmp, &ch->v4l2_dev.subdevs, list)
+		ret = v4l2_subdev_call(sd, tuner, s_frequency, f);
+
+	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;
+
+	list_for_each_entry_safe(sd, tmp, &ch->v4l2_dev.subdevs, list)
+		ret = v4l2_subdev_call(sd, tuner, g_tuner, vt);
+
+	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;
+
+	list_for_each_entry_safe(sd, tmp, &ch->v4l2_dev.subdevs, list)
+		ret = v4l2_subdev_call(sd, tuner, s_tuner, vt);
+
+	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_template = {
+	.name                     = "R-Car DRIF",
+	.release                  = video_device_release_empty,
+	.fops                     = &rcar_drif_fops,
+	.ioctl_ops                = &rcar_drif_ioctl_ops,
+};
+
+static void rcar_drif_video_release(struct v4l2_device *v)
+{
+	struct rcar_drif_chan *ch = container_of(v, struct rcar_drif_chan,
+						v4l2_dev);
+	u32 i;
+
+	dev_dbg(ch->dev, "video release\n");
+	v4l2_device_unregister(&ch->v4l2_dev);
+	for_each_set_bit(i, &ch->hw_schans_mask, RCAR_DRIF_MAX_SUBCHANS)
+		kfree(ch->schan[i]);
+	kfree(ch);
+}
+
+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);
+	dev_dbg(ch->dev, "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;
+	dev_dbg(ch->dev, "notify complete\n");
+	ret = v4l2_device_register_subdev_nodes(&ch->v4l2_dev);
+	if (ret) {
+		dev_err(ch->dev, "failed register subdev nodes ret %d\n", ret);
+		return ret;
+	}
+
+	list_for_each_entry_safe(sd, tmp, &ch->v4l2_dev.subdevs, list) {
+		ret = v4l2_ctrl_add_handler(ch->v4l2_dev.ctrl_handler,
+					    sd->ctrl_handler, NULL);
+		if (ret) {
+			dev_err(ch->dev, "failed ctrl add hdlr ret %d\n", ret);
+			return ret;
+		}
+	}
+	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_kcalloc(dev, RCAR_DRIF_MAX_SUBDEVS,
+				sizeof(*notifier->subdevs), GFP_KERNEL);
+	if (!notifier->subdevs)
+		return -ENOMEM;
+
+	while (notifier->num_subdevs < RCAR_DRIF_MAX_SUBDEVS &&
+	       (node = of_graph_get_next_endpoint(dev->of_node, node))) {
+		struct rcar_drif_async_subdev *rsd;
+
+		dev_dbg(dev, "node name %s\n", node->name);
+		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 void rcar_drif_validate_syncmd(struct rcar_drif_chan *ch, u32 val)
+{
+	if (val > 1) {
+		dev_warn(ch->dev, "invalid syncmd %u using L/R mode\n", val);
+		return;
+	}
+
+	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;
+}
+
+/* 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 void rcar_drif_validate_dtdl_syncdl(struct rcar_drif_chan *ch, u32 dtdl,
+					   u32 syncdl)
+{
+	/* Sanity checks */
+	if (dtdl > 200 || syncdl > 300) {
+		dev_warn(ch->dev, "invalid dtdl %u or syncdl %u\n",
+			 dtdl, syncdl);
+		return;
+	}
+	if ((dtdl + syncdl) % 100) {
+		dev_warn(ch->dev, "sum of dtdl %u or syncdl %u not OK\n",
+			 dtdl, syncdl);
+		return;
+	}
+	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);
+}
+
+static void rcar_drif_parse_properties(struct rcar_drif_chan *ch)
+{
+	u32 val, dtdl = 100, syncdl = 0;
+	struct device_node *np = ch->dev->of_node;
+
+	/* Set the defaults and check for overrides */
+	ch->mdr1 = RCAR_DRIF_SIRMDR1_SYNCMD_LR;	/* Default */
+	if (!of_property_read_u32(np, "renesas,syncmd", &val))
+		rcar_drif_validate_syncmd(ch, val);
+
+	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;
+
+	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);
+	rcar_drif_validate_dtdl_syncdl(ch, dtdl, syncdl);
+
+	dev_info(ch->dev, "Parsed SIRMDR1 0x%08x\n", ch->mdr1);
+}
+
+static int rcar_drif_probe(struct platform_device *pdev)
+{
+	struct rcar_drif_chan *ch;
+	struct device_node *of_child;
+	unsigned long hw_schans_mask = 0;
+	int ret;
+	u32 i;
+
+	of_child = of_get_child_by_name(pdev->dev.of_node, "sub-channel0");
+	if (of_child && of_device_is_available(of_child))
+		hw_schans_mask |= BIT(0);	/* Sub-channel 0 */
+
+	of_child = of_get_child_by_name(pdev->dev.of_node, "sub-channel1");
+	if (of_child && of_device_is_available(of_child))
+		hw_schans_mask |= BIT(1);	/* Sub-channel 1 */
+
+	/* Reserve memory for driver structure */
+	ch = kzalloc(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 */
+	rcar_drif_parse_properties(ch);
+
+	/* Setup enabled sub-channels */
+	for_each_set_bit(i, &hw_schans_mask, RCAR_DRIF_MAX_SUBCHANS) {
+		struct clk *clkp;
+		struct resource	*res;
+		void __iomem *base;
+
+		/* Peripheral clock */
+		clkp = devm_clk_get(&pdev->dev, (i == 0 ? "fck0" : "fck1"));
+		if (IS_ERR(clkp)) {
+			ret = PTR_ERR(clkp);
+			dev_err(&pdev->dev, "schan%u: clk get failed\n", i);
+			goto err_free_mem;
+		}
+
+		/* Register map */
+		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+		base = devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(base)) {
+			ret = PTR_ERR(base);
+			dev_err(&pdev->dev, "schan%u: io res get failed\n", i);
+			break;
+		}
+
+		/* Reserve memory for enabled sub-channels */
+		ch->schan[i] = kzalloc(sizeof(*ch->schan[i]), GFP_KERNEL);
+		if (!ch->schan[i]) {
+			ret = PTR_ERR(ch);
+			dev_err(&pdev->dev, "failed alloc sub-channel\n");
+			goto err_free_mem;
+		}
+		ch->schan[i]->clkp = clkp;
+		ch->schan[i]->base = base;
+		ch->schan[i]->num = i;
+		ch->schan[i]->start = res->start;
+		ch->schan[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)
+		goto err_free_mem;
+
+	/* 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");
+		goto err_free_mem;
+	}
+
+	/* Init video_device structure */
+	ch->vdev = rcar_drif_template;
+	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 structure */
+	ch->v4l2_dev.release = rcar_drif_video_release;
+	ret = v4l2_device_register(&pdev->dev, &ch->v4l2_dev);
+	if (ret) {
+		dev_err(ch->dev, "Failed to register v4l2-device (%d)\n", ret);
+		goto err_free_mem;
+	}
+
+	ch->vdev.v4l2_dev = &ch->v4l2_dev;
+
+	/* Parse subdevs after v4l2_device_register because if the subdev
+	 * is already probed, bound and complete will get called immediately
+	 */
+	ret = rcar_drif_parse_subdevs(&pdev->dev, &ch->notifier);
+	if (ret)
+		goto err_free_mem;
+
+	dev_info(ch->dev, "Num of subdevs %d\n", ch->notifier.num_subdevs);
+	ch->notifier.bound = rcar_drif_notify_bound;
+	ch->notifier.complete = rcar_drif_notify_complete;
+
+	ret = v4l2_async_notifier_register(&ch->v4l2_dev, &ch->notifier);
+	if (ret < 0) {
+		dev_err(ch->dev, "notifier registration failed\n");
+		goto err_unregister_v4l2_dev;
+	}
+
+	/* Register SDR device */
+	ret = video_register_device(&ch->vdev, VFL_TYPE_SDR, -1);
+	if (ret) {
+		dev_err(ch->dev, "Failed to register as video device (%d)\n",
+				ret);
+		goto err_unregister;
+	}
+
+	platform_set_drvdata(pdev, ch);
+	dev_notice(ch->dev, "probed\n");
+	return 0;
+
+err_unregister:
+	v4l2_async_notifier_unregister(&ch->notifier);
+err_unregister_v4l2_dev:
+	v4l2_device_unregister(&ch->v4l2_dev);
+err_free_mem:
+	for_each_set_bit(i, &hw_schans_mask, RCAR_DRIF_MAX_SUBCHANS)
+		kfree(ch->schan[i]);
+	kfree(ch);
+	return ret;
+}
+
+static int rcar_drif_remove(struct platform_device *pdev)
+{
+	struct rcar_drif_chan *ch = platform_get_drvdata(pdev);
+
+	v4l2_ctrl_handler_free(ch->v4l2_dev.ctrl_handler);
+	v4l2_async_notifier_unregister(&ch->notifier);
+	video_unregister_device(&ch->vdev);
+	dev_notice(ch->dev, "removed\n");
+	v4l2_device_put(&ch->v4l2_dev);
+	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] 39+ messages in thread

* [RFC 4/5] media: Add new SDR formats SC16, SC18 & SC20
  2016-10-12 14:10 [RFC 0/5] Add V4L2 SDR (DRIF & MAX2175) driver Ramesh Shanmugasundaram
                   ` (2 preceding siblings ...)
  2016-10-12 14:10 ` [RFC 3/5] media: platform: rcar_drif: Add DRIF support Ramesh Shanmugasundaram
@ 2016-10-12 14:10 ` Ramesh Shanmugasundaram
  2016-10-12 14:10 ` [RFC 5/5] doc_rst: media: New " Ramesh Shanmugasundaram
  4 siblings, 0 replies; 39+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-10-12 14:10 UTC (permalink / raw)
  To: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope
  Cc: chris.paterson2, laurent.pinchart, geert, 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 cd3641b..2b9be1c 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1259,6 +1259,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 2b12548..369a199 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -643,6 +643,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] 39+ messages in thread

* [RFC 5/5] doc_rst: media: New SDR formats SC16, SC18 & SC20
  2016-10-12 14:10 [RFC 0/5] Add V4L2 SDR (DRIF & MAX2175) driver Ramesh Shanmugasundaram
                   ` (3 preceding siblings ...)
  2016-10-12 14:10 ` [RFC 4/5] media: Add new SDR formats SC16, SC18 & SC20 Ramesh Shanmugasundaram
@ 2016-10-12 14:10 ` Ramesh Shanmugasundaram
       [not found]   ` <1476281429-27603-6-git-send-email-ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>
  4 siblings, 1 reply; 39+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-10-12 14:10 UTC (permalink / raw)
  To: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope
  Cc: chris.paterson2, laurent.pinchart, geert, 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          | 44 ++++++++++++++++++++
 .../media/uapi/v4l/pixfmt-sdr-scu18be.rst          | 48 ++++++++++++++++++++++
 .../media/uapi/v4l/pixfmt-sdr-scu20be.rst          | 48 ++++++++++++++++++++++
 Documentation/media/uapi/v4l/sdr-formats.rst       |  3 ++
 4 files changed, 143 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..d6c2123
--- /dev/null
+++ b/Documentation/media/uapi/v4l/pixfmt-sdr-scu16be.rst
@@ -0,0 +1,44 @@
+.. -*- coding: utf-8; mode: rst -*-
+
+.. _V4L2-SDR-FMT-SCU16BE:
+
+******************************
+V4L2_SDR_FMT_SCU16BE ('SCU16')
+******************************
+
+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. I value
+starts first and Q value starts at an offset equalling half of the buffer
+size. 14 bit data is stored in 16 bit space with unused stuffed bits
+padded with 0.
+
+**Byte Order.**
+Each cell is one byte.
+
+
+.. flat-table::
+    :header-rows:  0
+    :stub-columns: 0
+
+    -  .. row 1
+
+       -  start + 0:
+
+       -  I'\ :sub:`0[D13:D6]`
+
+       -  I'\ :sub:`0[D5:D0]`
+
+    -  .. row 2
+
+       -  start + buffer_size/2:
+
+       -  Q'\ :sub:`0[D13:D6]`
+
+       -  Q'\ :sub:`0[D5:D0]`
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..e6e0aff
--- /dev/null
+++ b/Documentation/media/uapi/v4l/pixfmt-sdr-scu18be.rst
@@ -0,0 +1,48 @@
+.. -*- coding: utf-8; mode: rst -*-
+
+.. _V4L2-SDR-FMT-SCU18BE:
+
+******************************
+V4L2_SDR_FMT_SCU18BE ('SCU18')
+******************************
+
+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. I value
+starts first and Q value starts at an offset equalling half of the buffer
+size. 16 bit data is stored in 18 bit space with unused stuffed bits
+padded with 0.
+
+**Byte Order.**
+Each cell is one byte.
+
+
+.. flat-table::
+    :header-rows:  0
+    :stub-columns: 0
+
+    -  .. row 1
+
+       -  start + 0:
+
+       -  I'\ :sub:`0[D17:D10]`
+
+       -  I'\ :sub:`0[D9:D2]`
+
+       -  I'\ :sub:`0[D1:D0]`
+
+    -  .. row 2
+
+       -  start + buffer_size/2:
+
+       -  Q'\ :sub:`0[D17:D10]`
+
+       -  Q'\ :sub:`0[D9:D2]`
+
+       -  Q'\ :sub:`0[D1:D0]`
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..374e0a3
--- /dev/null
+++ b/Documentation/media/uapi/v4l/pixfmt-sdr-scu20be.rst
@@ -0,0 +1,48 @@
+.. -*- coding: utf-8; mode: rst -*-
+
+.. _V4L2-SDR-FMT-SCU20BE:
+
+******************************
+V4L2_SDR_FMT_SCU20BE ('SCU20')
+******************************
+
+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. I value
+starts first and Q value starts at an offset equalling half of the buffer
+size. 18 bit data is stored in 20 bit space with unused stuffed bits
+padded with 0.
+
+**Byte Order.**
+Each cell is one byte.
+
+
+.. flat-table::
+    :header-rows:  0
+    :stub-columns: 0
+
+    -  .. row 1
+
+       -  start + 0:
+
+       -  I'\ :sub:`0[D19:D12]`
+
+       -  I'\ :sub:`0[D11:D4]`
+
+       -  I'\ :sub:`0[D3:D0]`
+
+    -  .. row 2
+
+       -  start + buffer_size/2:
+
+       -  Q'\ :sub:`0[D19:D12]`
+
+       -  Q'\ :sub:`0[D11:D4]`
+
+       -  Q'\ :sub:`0[D3:D0]`
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] 39+ messages in thread

* Re: [RFC 1/5] media: i2c: max2175: Add MAX2175 support
  2016-10-12 14:10 ` [RFC 1/5] media: i2c: max2175: Add MAX2175 support Ramesh Shanmugasundaram
@ 2016-10-15 12:42       ` Geert Uytterhoeven
  2016-10-18 19:25   ` Laurent Pinchart
  1 sibling, 0 replies; 39+ messages in thread
From: Geert Uytterhoeven @ 2016-10-15 12:42 UTC (permalink / raw)
  To: Ramesh Shanmugasundaram
  Cc: Rob Herring, Mark Rutland, Mauro Carvalho Chehab, Hans Verkuil,
	Sakari Ailus, Antti Palosaari, Chris Paterson, Laurent Pinchart,
	Linux Media Mailing List, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Linux-Renesas

Hi Ramesh,

On Wed, Oct 12, 2016 at 4:10 PM, Ramesh Shanmugasundaram
<ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org> 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 < for your patch!amesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>

Thanks for your patch!

> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/i2c/max2175.txt
> @@ -0,0 +1,60 @@
> +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: video interface 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: load capacitance value (in pF) on reference output
> +                    drive level. The mapping of these load values to
> +                    respective bit values are given below.
> +                    0 - Reference output disabled
> +                    1 - 10pF load
> +                    2 - 20pF load
> +                    3 - 30pF load
> +                    4 - 40pF load
> +                    5 - 60pF load
> +                    6 - 70pF load

For properties involving units, usually the unit is made part of the
property name, e.g. maxim,refout-load-pF = 40.
This avoids confusion, and allows for extension later.

> +/* A tuner device instance under i2c bus */
> +max2175_0: tuner@60 {
> +       #clock-cells = <0>;
> +       compatible = "maxim,max2175";
> +       reg = <0x60>;
> +       clocks = <&maxim_xtal>;
> +       clock-names = "xtal";
> +       maxim,refout-load = <10>;

10 is not listed above. Perhaps you meant 10 pF?

> --- /dev/null
> +++ b/drivers/media/i2c/max2175/max2175.c
> @@ -0,0 +1,1624 @@

> +/* NOTE: Any addition/deletion in the below list should be reflected in
> + * max2175_modetag enum
> + */

You can drop the above comment if you make this explicit using C99
designated initializers, cfr. below.

> +static const struct max2175_rxmode eu_rx_modes[] = { /* Indexed by EU modetag */
> +       /* EU modes */
> +       { MAX2175_BAND_VHF, 182640000, 0, { 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[] = { /* Indexed by NA modetag */
> +       /* NA modes */
> +       { MAX2175_BAND_FM, 98255520, 1, { 0, 0, 0, 0 } },

[MAX2175_NA_FM_1_0] = { MAX2175_BAND_FM, 98255520, 1, { 0, 0, 0, 0 } },

> +struct max2175_ctx {
> +       struct v4l2_subdev sd;
> +       struct i2c_client *client;
> +       struct device *dev;
> +
> +       /* Cached configuration */
> +       u8 regs[256];
> +       enum max2175_modetag mode;      /* Receive mode tag */
> +       u32 freq;                       /* In Hz */
> +       struct max2175_rxmode *rx_modes;
> +
> +       /* Device settings */
> +       bool master;
> +       u32 decim_ratio;
> +       u64 xtal_freq;
> +
> +       /* ROM values */
> +       u8 rom_bbf_bw_am;
> +       u8 rom_bbf_bw_fm;
> +       u8 rom_bbf_bw_dab;
> +
> +       /* Local copy of old settings */
> +       u8 i2s_test;
> +
> +       u8 nbd_gain;
> +       u8 nbd_threshold;
> +       u8 wbd_gain;
> +       u8 wbd_threshold;
> +       u8 bbd_threshold;
> +       u8 bbdclip_threshold;
> +       u8 lt_wbd_threshold;
> +       u8 lt_wbd_gain;
> +
> +       /* 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 *am_hiz;       /* AM High impledance input */
> +       struct v4l2_ctrl *hsls;         /* High-side/Low-side polarity */
> +       struct v4l2_ctrl *rx_mode;      /* Receive mode */
> +
> +       /* Driver private variables */
> +       bool mode_resolved;             /* Flag to sanity check settings */
> +};

Sorting the struct members by decreasing size helps to avoid gaps due to
alignment restrictions, and may reduce memory consumption.

> +/* Flush local copy to device from idx to idx+len (inclusive) */
> +static void max2175_flush_regstore(struct max2175_ctx *ctx, u8 idx, u8 len)
> +{
> +       u8 i;

I'd just use unsigned int for loop counters.

> +
> +       for (i = idx; i <= len; i++)
> +               max2175_reg_write(ctx, i, ctx->regs[i]);
> +}

> +static int max2175_update_i2s_mode(struct max2175_ctx *ctx, u32 i2s_mode)
> +{
> +       /* Only change if it's new */
> +       if (max2175_read_bits(ctx, 29, 2, 0) == i2s_mode)

Many magic numbers, not only here (29, 2), but everywhere.
Can you please add #defines for these?

> +               return 0;
> +
> +       max2175_write_bits(ctx, 29, 2, 0, i2s_mode);
> +
> +       /* Based on I2S mode value I2S_WORD_CNT values change */
> +       if (i2s_mode == MAX2175_I2S_MODE3) {
> +               max2175_write_bits(ctx, 30, 6, 0, 1);
> +       } else if (i2s_mode == MAX2175_I2S_MODE2 ||
> +                  i2s_mode == MAX2175_I2S_MODE4) {
> +               max2175_write_bits(ctx, 30, 6, 0, 0);
> +       } else if (i2s_mode == MAX2175_I2S_MODE0) {
> +               max2175_write_bits(ctx, 30, 6, 0,
> +                                  ctx->rx_modes[ctx->mode].i2s_word_size);
> +       } else {
> +               v4l2_err(ctx->client,
> +                        "failed: i2s_mode %u unsupported\n", i2s_mode);
> +               return 1;
> +       }

switch (i2s_mode) { ... }

> +       mxm_dbg(ctx, "updated i2s_mode %u\n", i2s_mode);
> +       return 0;
> +}

> +static void max2175_set_filter_coeffs(struct max2175_ctx *ctx, u8 m_sel,
> +                                     u8 bank, const u16 *coeffs)
> +{
> +       u8 i, coeff_addr, upper_address;

I'd just use unsigned int for these.

> +
> +       mxm_dbg(ctx, "start: 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;
> +       else
> +               upper_address = 24;
> +
> +       max2175_set_bit(ctx, 117, 7, 1);
> +       for (i = 0; i < upper_address; i++) {
> +               coeff_addr = i + (bank * 24);
> +               max2175_set_bits(ctx, 115, 7, 0,
> +                                (u8)((coeffs[i] >> 8) & 0xff));
> +               max2175_set_bits(ctx, 116, 7, 0, (u8)(coeffs[i] & 0xff));

I don't think you need the casts to u8, or the masking with 0xff.

> +               max2175_set_bits(ctx, 117, 6, 0, coeff_addr);
> +               max2175_flush_regstore(ctx, 115, 3);
> +       }
> +       max2175_write_bit(ctx, 117, 7, 0);
> +}
> +
> +static void max2175_load_dab_1p2(struct max2175_ctx *ctx)
> +{
> +       u32 i;

unsigned int?

> +static int max2175_set_lo_freq(struct max2175_ctx *ctx, u64 lo_freq)
> +{
> +       int ret;
> +       u32 lo_mult;
> +       u64 scaled_lo_freq;
> +       const u64 scale_factor = 1000000ULL;
> +       u64 scaled_npf, scaled_integer, scaled_fraction;
> +       u32 frac_desired, int_desired;
> +       u8 loband_bits, vcodiv_bits;
> +
> +       scaled_lo_freq = lo_freq;
> +       /* Scale to larger number for precision */
> +       scaled_lo_freq = scaled_lo_freq * scale_factor * 100;
> +
> +       mxm_dbg(ctx, "scaled lo_freq %llu lo_freq %llu\n",
> +               scaled_lo_freq, lo_freq);
> +
> +       if (MAX2175_IS_BAND_AM(ctx)) {
> +               if (max2175_get_bit(ctx, 5, 7) == 0)
> +                       loband_bits = 0;
> +                       vcodiv_bits = 0;
> +                       lo_mult = 16;
> +       } else if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_FM) {
> +               if (lo_freq <= 74700000) {
> +                       loband_bits = 0;
> +                       vcodiv_bits = 0;
> +                       lo_mult = 16;
> +               } else if ((lo_freq > 74700000) && (lo_freq <= 110000000)) {
> +                       loband_bits = 1;
> +                       vcodiv_bits = 0;
> +               } else {
> +                       loband_bits = 1;
> +                       vcodiv_bits = 3;
> +               }
> +               lo_mult = 8;
> +       } else if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF) {
> +               if (lo_freq <= 210000000) {
> +                       loband_bits = 2;
> +                       vcodiv_bits = 2;
> +               } else {
> +                       loband_bits = 2;
> +                       vcodiv_bits = 1;
> +               }
> +               lo_mult = 4;
> +       } else {
> +               loband_bits = 3;
> +               vcodiv_bits = 2;
> +               lo_mult = 2;
> +       }
> +
> +       if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_L)
> +               scaled_npf = (scaled_lo_freq / ctx->xtal_freq / lo_mult) / 100;
> +       else
> +               scaled_npf = (scaled_lo_freq / ctx->xtal_freq * lo_mult) / 100;

Please use one of the div64*() functions for divisions involving 64-bit
quantities (try to build for 32-bit and see). More of these below...

> +       scaled_integer = scaled_npf / scale_factor * scale_factor;
> +       int_desired = (u32)(scaled_npf / scale_factor);
> +       scaled_fraction = scaled_npf - scaled_integer;
> +       frac_desired = (u32)(scaled_fraction * 1048576 / scale_factor);

> +       /* Write the calculated values to the appropriate registers */
> +       max2175_set_bits(ctx, 5, 3, 2, loband_bits);
> +       max2175_set_bits(ctx, 6, 7, 6, vcodiv_bits);
> +       max2175_set_bits(ctx, 1, 7, 0, (u8)(int_desired & 0xff));
> +       max2175_set_bits(ctx, 2, 3, 0, (u8)((frac_desired >> 16) & 0x1f));
> +       max2175_set_bits(ctx, 3, 7, 0, (u8)((frac_desired >> 8) & 0xff));
> +       max2175_set_bits(ctx, 4, 7, 0, (u8)(frac_desired & 0xff));

No need for casts etc.

> +       /* Flush the above registers to device */
> +       max2175_flush_regstore(ctx, 1, 6);
> +       return ret;
> +}
> +
> +static int max2175_set_nco_freq(struct max2175_ctx *ctx, s64 nco_freq_desired)
> +{
> +       int ret;
> +       u64 clock_rate, abs_nco_freq;
> +       s64  nco_freq, nco_val_desired;
> +       u32 nco_reg;
> +       const u64 scale_factor = 1000000ULL;
> +
> +       mxm_dbg(ctx, "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 = (2 * nco_freq * scale_factor) / clock_rate;
> +       } else {
> +               if (nco_freq < 0)
> +                       nco_val_desired = (-2 * (clock_rate - abs_nco_freq) *
> +                               scale_factor) / clock_rate;
> +               else
> +                       nco_val_desired = (2 * (clock_rate - abs_nco_freq) *
> +                               scale_factor) / clock_rate;
> +       }
> +
> +       /* Scale down to get the fraction */
> +       if (nco_freq < 0)
> +               nco_reg = 0x200000 + ((nco_val_desired * 1048576) /
> +                                     scale_factor);
> +       else
> +               nco_reg = (nco_val_desired * 1048576) / scale_factor;

More 64-bit divisions. In addition, the dividers are 64-bit too.
Can't they be 32-bit?

> +static int max2175_probe(struct i2c_client *client,
> +                       const struct i2c_device_id *id)
> +{
> +       struct max2175_ctx *ctx;
> +       struct device *dev = &client->dev;
> +       struct v4l2_subdev *sd;
> +       struct v4l2_ctrl_handler *hdl;
> +       struct clk *clk;
> +       bool master = true;
> +       u32 refout_load, refout_bits = 0;       /* REFOUT disabled */
> +       int ret;
> +
> +       /* Check if the adapter supports the needed features */
> +       if (!i2c_check_functionality(client->adapter,
> +                                    I2C_FUNC_SMBUS_BYTE_DATA)) {
> +               dev_err(&client->dev, "i2c check failed\n");
> +               return -EIO;
> +       }
> +
> +       if (of_find_property(client->dev.of_node, "maxim,slave", NULL))
> +               master = false;
> +
> +       if (!of_property_read_u32(client->dev.of_node, "maxim,refout-load",
> +                                &refout_load))
> +               refout_bits = max2175_refout_load_to_bits(client, refout_load);
> +
> +       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;
> +       }
> +
> +       ctx = kzalloc(sizeof(struct max2175_ctx),
> +                            GFP_KERNEL);

devm_kzalloc()?

> +       if (ctx == NULL)
> +               return -ENOMEM;
> +
> +       sd = &ctx->sd;
> +       ctx->master = master;
> +       ctx->mode_resolved = false;
> +
> +       /* Set the defaults */
> +       ctx->freq = bands_rf.rangelow;
> +
> +       ctx->xtal_freq = clk_get_rate(clk);
> +       dev_info(&client->dev, "xtal freq %lluHz\n", ctx->xtal_freq);
> +
> +       v4l2_i2c_subdev_init(sd, client, &max2175_ops);
> +       ctx->client = client;
> +
> +       sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> +       ctx->dev = dev;
> +
> +       /* Controls */
> +       hdl = &ctx->ctrl_hdl;
> +       ret = v4l2_ctrl_handler_init(hdl, 8);
> +       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->am_hiz = v4l2_ctrl_new_custom(hdl, &max2175_am_hiz, 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 = (struct max2175_rxmode *)eu_rx_modes;
> +       } else {
> +               ctx->rx_mode = v4l2_ctrl_new_custom(hdl,
> +                                                   &max2175_na_rx_mode, NULL);
> +               ctx->rx_modes = (struct max2175_rxmode *)na_rx_modes;
> +       }

The casts are meant to cast away constness. Can that be avoided?

> --- /dev/null
> +++ b/drivers/media/i2c/max2175/max2175.h

> +/* NOTE: Any addition/deletion in the below enum should be reflected in
> + * V4L2_CID_MAX2175_RX_MODE ctrl strings

Which strings exactly?

> + */
> +enum max2175_modetag {
> +       /* EU modes */
> +       MAX2175_DAB_1_2 = 0,
> +
> +       /* Other possible modes to add in future
> +        * MAX2175_DAB_1_0,
> +        * MAX2175_DAB_1_3,
> +        * MAX2175_EU_FM_2_2,
> +        * MAX2175_EU_FM_1_0,
> +        * MAX2175_EU_FMHD_4_0,
> +        * MAX2175_EU_AM_1_0,
> +        * MAX2175_EU_AM_2_2,
> +        */
> +
> +       /* NA modes */
> +       MAX2175_NA_FM_1_0 = 0,
> +
> +       /* Other possible modes to add in future
> +        * MAX2175_NA_FM_1_2,
> +        * MAX2175_NA_FMHD_1_0,
> +        * MAX2175_NA_FMHD_1_2,
> +        * MAX2175_NA_AM_1_0,
> +        * MAX2175_NA_AM_1_2,
> +        */

The MAX2175_NA_* definitions share their values with the MAX2175_DAB_* and
future MAX2175_EU_* values. Do you have to use a single enum for both?

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
--
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] 39+ messages in thread

* Re: [RFC 1/5] media: i2c: max2175: Add MAX2175 support
@ 2016-10-15 12:42       ` Geert Uytterhoeven
  0 siblings, 0 replies; 39+ messages in thread
From: Geert Uytterhoeven @ 2016-10-15 12:42 UTC (permalink / raw)
  To: Ramesh Shanmugasundaram
  Cc: Rob Herring, Mark Rutland, Mauro Carvalho Chehab, Hans Verkuil,
	Sakari Ailus, Antti Palosaari, Chris Paterson, Laurent Pinchart,
	Linux Media Mailing List, devicetree, Linux-Renesas

Hi Ramesh,

On Wed, Oct 12, 2016 at 4:10 PM, Ramesh Shanmugasundaram
<ramesh.shanmugasundaram@bp.renesas.com> 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 < for your patch!amesh.shanmugasundaram@bp.renesas.com>

Thanks for your patch!

> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/i2c/max2175.txt
> @@ -0,0 +1,60 @@
> +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: video interface 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: load capacitance value (in pF) on reference output
> +                    drive level. The mapping of these load values to
> +                    respective bit values are given below.
> +                    0 - Reference output disabled
> +                    1 - 10pF load
> +                    2 - 20pF load
> +                    3 - 30pF load
> +                    4 - 40pF load
> +                    5 - 60pF load
> +                    6 - 70pF load

For properties involving units, usually the unit is made part of the
property name, e.g. maxim,refout-load-pF = 40.
This avoids confusion, and allows for extension later.

> +/* A tuner device instance under i2c bus */
> +max2175_0: tuner@60 {
> +       #clock-cells = <0>;
> +       compatible = "maxim,max2175";
> +       reg = <0x60>;
> +       clocks = <&maxim_xtal>;
> +       clock-names = "xtal";
> +       maxim,refout-load = <10>;

10 is not listed above. Perhaps you meant 10 pF?

> --- /dev/null
> +++ b/drivers/media/i2c/max2175/max2175.c
> @@ -0,0 +1,1624 @@

> +/* NOTE: Any addition/deletion in the below list should be reflected in
> + * max2175_modetag enum
> + */

You can drop the above comment if you make this explicit using C99
designated initializers, cfr. below.

> +static const struct max2175_rxmode eu_rx_modes[] = { /* Indexed by EU modetag */
> +       /* EU modes */
> +       { MAX2175_BAND_VHF, 182640000, 0, { 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[] = { /* Indexed by NA modetag */
> +       /* NA modes */
> +       { MAX2175_BAND_FM, 98255520, 1, { 0, 0, 0, 0 } },

[MAX2175_NA_FM_1_0] = { MAX2175_BAND_FM, 98255520, 1, { 0, 0, 0, 0 } },

> +struct max2175_ctx {
> +       struct v4l2_subdev sd;
> +       struct i2c_client *client;
> +       struct device *dev;
> +
> +       /* Cached configuration */
> +       u8 regs[256];
> +       enum max2175_modetag mode;      /* Receive mode tag */
> +       u32 freq;                       /* In Hz */
> +       struct max2175_rxmode *rx_modes;
> +
> +       /* Device settings */
> +       bool master;
> +       u32 decim_ratio;
> +       u64 xtal_freq;
> +
> +       /* ROM values */
> +       u8 rom_bbf_bw_am;
> +       u8 rom_bbf_bw_fm;
> +       u8 rom_bbf_bw_dab;
> +
> +       /* Local copy of old settings */
> +       u8 i2s_test;
> +
> +       u8 nbd_gain;
> +       u8 nbd_threshold;
> +       u8 wbd_gain;
> +       u8 wbd_threshold;
> +       u8 bbd_threshold;
> +       u8 bbdclip_threshold;
> +       u8 lt_wbd_threshold;
> +       u8 lt_wbd_gain;
> +
> +       /* 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 *am_hiz;       /* AM High impledance input */
> +       struct v4l2_ctrl *hsls;         /* High-side/Low-side polarity */
> +       struct v4l2_ctrl *rx_mode;      /* Receive mode */
> +
> +       /* Driver private variables */
> +       bool mode_resolved;             /* Flag to sanity check settings */
> +};

Sorting the struct members by decreasing size helps to avoid gaps due to
alignment restrictions, and may reduce memory consumption.

> +/* Flush local copy to device from idx to idx+len (inclusive) */
> +static void max2175_flush_regstore(struct max2175_ctx *ctx, u8 idx, u8 len)
> +{
> +       u8 i;

I'd just use unsigned int for loop counters.

> +
> +       for (i = idx; i <= len; i++)
> +               max2175_reg_write(ctx, i, ctx->regs[i]);
> +}

> +static int max2175_update_i2s_mode(struct max2175_ctx *ctx, u32 i2s_mode)
> +{
> +       /* Only change if it's new */
> +       if (max2175_read_bits(ctx, 29, 2, 0) == i2s_mode)

Many magic numbers, not only here (29, 2), but everywhere.
Can you please add #defines for these?

> +               return 0;
> +
> +       max2175_write_bits(ctx, 29, 2, 0, i2s_mode);
> +
> +       /* Based on I2S mode value I2S_WORD_CNT values change */
> +       if (i2s_mode == MAX2175_I2S_MODE3) {
> +               max2175_write_bits(ctx, 30, 6, 0, 1);
> +       } else if (i2s_mode == MAX2175_I2S_MODE2 ||
> +                  i2s_mode == MAX2175_I2S_MODE4) {
> +               max2175_write_bits(ctx, 30, 6, 0, 0);
> +       } else if (i2s_mode == MAX2175_I2S_MODE0) {
> +               max2175_write_bits(ctx, 30, 6, 0,
> +                                  ctx->rx_modes[ctx->mode].i2s_word_size);
> +       } else {
> +               v4l2_err(ctx->client,
> +                        "failed: i2s_mode %u unsupported\n", i2s_mode);
> +               return 1;
> +       }

switch (i2s_mode) { ... }

> +       mxm_dbg(ctx, "updated i2s_mode %u\n", i2s_mode);
> +       return 0;
> +}

> +static void max2175_set_filter_coeffs(struct max2175_ctx *ctx, u8 m_sel,
> +                                     u8 bank, const u16 *coeffs)
> +{
> +       u8 i, coeff_addr, upper_address;

I'd just use unsigned int for these.

> +
> +       mxm_dbg(ctx, "start: 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;
> +       else
> +               upper_address = 24;
> +
> +       max2175_set_bit(ctx, 117, 7, 1);
> +       for (i = 0; i < upper_address; i++) {
> +               coeff_addr = i + (bank * 24);
> +               max2175_set_bits(ctx, 115, 7, 0,
> +                                (u8)((coeffs[i] >> 8) & 0xff));
> +               max2175_set_bits(ctx, 116, 7, 0, (u8)(coeffs[i] & 0xff));

I don't think you need the casts to u8, or the masking with 0xff.

> +               max2175_set_bits(ctx, 117, 6, 0, coeff_addr);
> +               max2175_flush_regstore(ctx, 115, 3);
> +       }
> +       max2175_write_bit(ctx, 117, 7, 0);
> +}
> +
> +static void max2175_load_dab_1p2(struct max2175_ctx *ctx)
> +{
> +       u32 i;

unsigned int?

> +static int max2175_set_lo_freq(struct max2175_ctx *ctx, u64 lo_freq)
> +{
> +       int ret;
> +       u32 lo_mult;
> +       u64 scaled_lo_freq;
> +       const u64 scale_factor = 1000000ULL;
> +       u64 scaled_npf, scaled_integer, scaled_fraction;
> +       u32 frac_desired, int_desired;
> +       u8 loband_bits, vcodiv_bits;
> +
> +       scaled_lo_freq = lo_freq;
> +       /* Scale to larger number for precision */
> +       scaled_lo_freq = scaled_lo_freq * scale_factor * 100;
> +
> +       mxm_dbg(ctx, "scaled lo_freq %llu lo_freq %llu\n",
> +               scaled_lo_freq, lo_freq);
> +
> +       if (MAX2175_IS_BAND_AM(ctx)) {
> +               if (max2175_get_bit(ctx, 5, 7) == 0)
> +                       loband_bits = 0;
> +                       vcodiv_bits = 0;
> +                       lo_mult = 16;
> +       } else if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_FM) {
> +               if (lo_freq <= 74700000) {
> +                       loband_bits = 0;
> +                       vcodiv_bits = 0;
> +                       lo_mult = 16;
> +               } else if ((lo_freq > 74700000) && (lo_freq <= 110000000)) {
> +                       loband_bits = 1;
> +                       vcodiv_bits = 0;
> +               } else {
> +                       loband_bits = 1;
> +                       vcodiv_bits = 3;
> +               }
> +               lo_mult = 8;
> +       } else if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF) {
> +               if (lo_freq <= 210000000) {
> +                       loband_bits = 2;
> +                       vcodiv_bits = 2;
> +               } else {
> +                       loband_bits = 2;
> +                       vcodiv_bits = 1;
> +               }
> +               lo_mult = 4;
> +       } else {
> +               loband_bits = 3;
> +               vcodiv_bits = 2;
> +               lo_mult = 2;
> +       }
> +
> +       if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_L)
> +               scaled_npf = (scaled_lo_freq / ctx->xtal_freq / lo_mult) / 100;
> +       else
> +               scaled_npf = (scaled_lo_freq / ctx->xtal_freq * lo_mult) / 100;

Please use one of the div64*() functions for divisions involving 64-bit
quantities (try to build for 32-bit and see). More of these below...

> +       scaled_integer = scaled_npf / scale_factor * scale_factor;
> +       int_desired = (u32)(scaled_npf / scale_factor);
> +       scaled_fraction = scaled_npf - scaled_integer;
> +       frac_desired = (u32)(scaled_fraction * 1048576 / scale_factor);

> +       /* Write the calculated values to the appropriate registers */
> +       max2175_set_bits(ctx, 5, 3, 2, loband_bits);
> +       max2175_set_bits(ctx, 6, 7, 6, vcodiv_bits);
> +       max2175_set_bits(ctx, 1, 7, 0, (u8)(int_desired & 0xff));
> +       max2175_set_bits(ctx, 2, 3, 0, (u8)((frac_desired >> 16) & 0x1f));
> +       max2175_set_bits(ctx, 3, 7, 0, (u8)((frac_desired >> 8) & 0xff));
> +       max2175_set_bits(ctx, 4, 7, 0, (u8)(frac_desired & 0xff));

No need for casts etc.

> +       /* Flush the above registers to device */
> +       max2175_flush_regstore(ctx, 1, 6);
> +       return ret;
> +}
> +
> +static int max2175_set_nco_freq(struct max2175_ctx *ctx, s64 nco_freq_desired)
> +{
> +       int ret;
> +       u64 clock_rate, abs_nco_freq;
> +       s64  nco_freq, nco_val_desired;
> +       u32 nco_reg;
> +       const u64 scale_factor = 1000000ULL;
> +
> +       mxm_dbg(ctx, "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 = (2 * nco_freq * scale_factor) / clock_rate;
> +       } else {
> +               if (nco_freq < 0)
> +                       nco_val_desired = (-2 * (clock_rate - abs_nco_freq) *
> +                               scale_factor) / clock_rate;
> +               else
> +                       nco_val_desired = (2 * (clock_rate - abs_nco_freq) *
> +                               scale_factor) / clock_rate;
> +       }
> +
> +       /* Scale down to get the fraction */
> +       if (nco_freq < 0)
> +               nco_reg = 0x200000 + ((nco_val_desired * 1048576) /
> +                                     scale_factor);
> +       else
> +               nco_reg = (nco_val_desired * 1048576) / scale_factor;

More 64-bit divisions. In addition, the dividers are 64-bit too.
Can't they be 32-bit?

> +static int max2175_probe(struct i2c_client *client,
> +                       const struct i2c_device_id *id)
> +{
> +       struct max2175_ctx *ctx;
> +       struct device *dev = &client->dev;
> +       struct v4l2_subdev *sd;
> +       struct v4l2_ctrl_handler *hdl;
> +       struct clk *clk;
> +       bool master = true;
> +       u32 refout_load, refout_bits = 0;       /* REFOUT disabled */
> +       int ret;
> +
> +       /* Check if the adapter supports the needed features */
> +       if (!i2c_check_functionality(client->adapter,
> +                                    I2C_FUNC_SMBUS_BYTE_DATA)) {
> +               dev_err(&client->dev, "i2c check failed\n");
> +               return -EIO;
> +       }
> +
> +       if (of_find_property(client->dev.of_node, "maxim,slave", NULL))
> +               master = false;
> +
> +       if (!of_property_read_u32(client->dev.of_node, "maxim,refout-load",
> +                                &refout_load))
> +               refout_bits = max2175_refout_load_to_bits(client, refout_load);
> +
> +       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;
> +       }
> +
> +       ctx = kzalloc(sizeof(struct max2175_ctx),
> +                            GFP_KERNEL);

devm_kzalloc()?

> +       if (ctx == NULL)
> +               return -ENOMEM;
> +
> +       sd = &ctx->sd;
> +       ctx->master = master;
> +       ctx->mode_resolved = false;
> +
> +       /* Set the defaults */
> +       ctx->freq = bands_rf.rangelow;
> +
> +       ctx->xtal_freq = clk_get_rate(clk);
> +       dev_info(&client->dev, "xtal freq %lluHz\n", ctx->xtal_freq);
> +
> +       v4l2_i2c_subdev_init(sd, client, &max2175_ops);
> +       ctx->client = client;
> +
> +       sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> +       ctx->dev = dev;
> +
> +       /* Controls */
> +       hdl = &ctx->ctrl_hdl;
> +       ret = v4l2_ctrl_handler_init(hdl, 8);
> +       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->am_hiz = v4l2_ctrl_new_custom(hdl, &max2175_am_hiz, 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 = (struct max2175_rxmode *)eu_rx_modes;
> +       } else {
> +               ctx->rx_mode = v4l2_ctrl_new_custom(hdl,
> +                                                   &max2175_na_rx_mode, NULL);
> +               ctx->rx_modes = (struct max2175_rxmode *)na_rx_modes;
> +       }

The casts are meant to cast away constness. Can that be avoided?

> --- /dev/null
> +++ b/drivers/media/i2c/max2175/max2175.h

> +/* NOTE: Any addition/deletion in the below enum should be reflected in
> + * V4L2_CID_MAX2175_RX_MODE ctrl strings

Which strings exactly?

> + */
> +enum max2175_modetag {
> +       /* EU modes */
> +       MAX2175_DAB_1_2 = 0,
> +
> +       /* Other possible modes to add in future
> +        * MAX2175_DAB_1_0,
> +        * MAX2175_DAB_1_3,
> +        * MAX2175_EU_FM_2_2,
> +        * MAX2175_EU_FM_1_0,
> +        * MAX2175_EU_FMHD_4_0,
> +        * MAX2175_EU_AM_1_0,
> +        * MAX2175_EU_AM_2_2,
> +        */
> +
> +       /* NA modes */
> +       MAX2175_NA_FM_1_0 = 0,
> +
> +       /* Other possible modes to add in future
> +        * MAX2175_NA_FM_1_2,
> +        * MAX2175_NA_FMHD_1_0,
> +        * MAX2175_NA_FMHD_1_2,
> +        * MAX2175_NA_AM_1_0,
> +        * MAX2175_NA_AM_1_2,
> +        */

The MAX2175_NA_* definitions share their values with the MAX2175_DAB_* and
future MAX2175_EU_* values. Do you have to use a single enum for both?

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] 39+ messages in thread

* Re: [RFC 3/5] media: platform: rcar_drif: Add DRIF support
  2016-10-12 14:10 ` [RFC 3/5] media: platform: rcar_drif: Add DRIF support Ramesh Shanmugasundaram
@ 2016-10-18 13:13   ` Rob Herring
  2016-10-18 15:13     ` Ramesh Shanmugasundaram
  2016-10-18 14:29   ` Geert Uytterhoeven
  1 sibling, 1 reply; 39+ messages in thread
From: Rob Herring @ 2016-10-18 13:13 UTC (permalink / raw)
  To: Ramesh Shanmugasundaram
  Cc: mark.rutland, mchehab, hverkuil, sakari.ailus, crope,
	chris.paterson2, laurent.pinchart, geert, linux-media,
	devicetree, linux-renesas-soc

On Wed, Oct 12, 2016 at 03:10:27PM +0100, 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     |  109 ++
>  drivers/media/platform/Kconfig                     |   25 +
>  drivers/media/platform/Makefile                    |    1 +
>  drivers/media/platform/rcar_drif.c                 | 1534 ++++++++++++++++++++
>  4 files changed, 1669 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..24239d9
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> @@ -0,0 +1,109 @@
> +Renesas R-Car Gen3 DRIF controller (DRIF)

Define what is DRIF here, not just in the commit text.

> +-----------------------------------------
> +
> +Required properties:
> +--------------------
> +- compatible: "renesas,drif-r8a7795" if DRIF controller is a part of R8A7795 SoC.

renesas,r8a7795-drif would be the normal ordering.

> +	      "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 each sub-channel.
> +- interrupts: associated with each sub-channel.
> +- clocks: phandles and clock specifiers for each sub-channel.
> +- clock-names: clock input name strings: "fck0", "fck1".
> +- pinctrl-0: pin control group to be used for this controller.
> +- pinctrl-names: must be "default".
> +- dmas: phandles to the DMA channels for each sub-channel.
> +- dma-names: names for the DMA channels: "rx0", "rx1".
> +
> +Required child nodes:
> +---------------------
> +- Each DRIF channel can have one or both of the sub-channels enabled in a
> +  setup. The sub-channels are represented as a child node. The name of the
> +  child nodes are "sub-channel0" and "sub-channel1" respectively. Each child
> +  node supports the "status" property only, which is used to enable/disable
> +  the respective sub-channel.
> +
> +Optional properties:
> +--------------------
> +- port: video interface child port node of a channel that defines the local

This is an audio device, why does it have a video port?

> +  and remote endpoints. The remote endpoint is assumed to a tuner subdevice
> +  endpoint.
> +- power-domains: phandle to respective power domain.
> +- 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
> +
> +drif0: rif@e6f40000 {
> +	compatible = "renesas,drif-r8a7795",
> +		   "renesas,rcar-gen3-drif";
> +	reg = <0 0xe6f40000 0 0x64>, <0 0xe6f50000 0 0x64>;
> +	interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>,
> +		   <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
> +	clocks = <&cpg CPG_MOD 515>, <&cpg CPG_MOD 514>;
> +	clock-names = "fck0", "fck1";
> +	dmas = <&dmac1 0x20>, <&dmac1 0x22>;
> +	dma-names = "rx0", "rx1";
> +	power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> +	status = "disabled";
> +
> +	sub-channel0 {
> +		status = "disabled";
> +	};
> +
> +	sub-channel1 {
> +		status = "disabled";
> +	};
> +
> +};
> +
> +Board specific dts file
> +
> +&drif0 {
> +	pinctrl-0 = <&drif0_pins>;
> +	pinctrl-names = "default";
> +	status = "okay";
> +
> +	sub-channel0 {
> +		status = "okay";
> +	};
> +
> +	sub-channel1 {
> +		status = "okay";
> +	};
> +
> +	port {
> +		drif0_ep: endpoint {
> +		     remote-endpoint = <&tuner_subdev_ep>;
> +		};
> +	};
> +};

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

* Re: [RFC 3/5] media: platform: rcar_drif: Add DRIF support
  2016-10-12 14:10 ` [RFC 3/5] media: platform: rcar_drif: Add DRIF support Ramesh Shanmugasundaram
  2016-10-18 13:13   ` Rob Herring
@ 2016-10-18 14:29   ` Geert Uytterhoeven
  2016-10-18 18:26     ` Laurent Pinchart
       [not found]     ` <CAMuHMdXvGEm3bdNOsa6Q1FLB9yMSTAzO4nHcCb-pnYYwg6f6Cg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
  1 sibling, 2 replies; 39+ messages in thread
From: Geert Uytterhoeven @ 2016-10-18 14:29 UTC (permalink / raw)
  To: Ramesh Shanmugasundaram
  Cc: Rob Herring, Mark Rutland, Mauro Carvalho Chehab, Hans Verkuil,
	Sakari Ailus, Antti Palosaari, Chris Paterson, Laurent Pinchart,
	Linux Media Mailing List, devicetree, Linux-Renesas

Hi Ramesh,

On Wed, Oct 12, 2016 at 4:10 PM, Ramesh Shanmugasundaram
<ramesh.shanmugasundaram@bp.renesas.com> 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.

Thanks for your patch!

> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> @@ -0,0 +1,109 @@
> +Renesas R-Car Gen3 DRIF controller (DRIF)
> +-----------------------------------------
> +
> +Required properties:
> +--------------------
> +- compatible: "renesas,drif-r8a7795" if DRIF controller is a part of R8A7795 SoC.

"renesas,r8a7795-drif", as Rob already pointed out.

> +             "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 each sub-channel.
> +- interrupts: associated with each sub-channel.
> +- clocks: phandles and clock specifiers for each sub-channel.
> +- clock-names: clock input name strings: "fck0", "fck1".
> +- pinctrl-0: pin control group to be used for this controller.
> +- pinctrl-names: must be "default".
> +- dmas: phandles to the DMA channels for each sub-channel.
> +- dma-names: names for the DMA channels: "rx0", "rx1".
> +
> +Required child nodes:
> +---------------------
> +- Each DRIF channel can have one or both of the sub-channels enabled in a
> +  setup. The sub-channels are represented as a child node. The name of the
> +  child nodes are "sub-channel0" and "sub-channel1" respectively. Each child
> +  node supports the "status" property only, which is used to enable/disable
> +  the respective sub-channel.

> +Example
> +--------
> +
> +SoC common dtsi file
> +
> +drif0: rif@e6f40000 {
> +       compatible = "renesas,drif-r8a7795",
> +                  "renesas,rcar-gen3-drif";
> +       reg = <0 0xe6f40000 0 0x64>, <0 0xe6f50000 0 0x64>;
> +       interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>,
> +                  <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
> +       clocks = <&cpg CPG_MOD 515>, <&cpg CPG_MOD 514>;
> +       clock-names = "fck0", "fck1";
> +       dmas = <&dmac1 0x20>, <&dmac1 0x22>;
> +       dma-names = "rx0", "rx1";

I could not find the DMAC channels in the datasheet?
Most modules are either tied to dmac0, or two both dmac1 and dmac2.
In the latter case, you want to list two sets of dmas, one for each DMAC.

> +       power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> +       status = "disabled";
> +
> +       sub-channel0 {
> +               status = "disabled";
> +       };
> +
> +       sub-channel1 {
> +               status = "disabled";
> +       };
> +
> +};

As you're modelling this in DT under a single device node, this means you
cannot use runtime PM to manage the module clocks of the individual channels.

An alternative could be to have two separate nodes for each channel,
and tie them together using a phandle.

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] 39+ messages in thread

* RE: [RFC 1/5] media: i2c: max2175: Add MAX2175 support
  2016-10-15 12:42       ` Geert Uytterhoeven
@ 2016-10-18 15:04           ` Ramesh Shanmugasundaram
  -1 siblings, 0 replies; 39+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-10-18 15:04 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Rob Herring, Mark Rutland, Mauro Carvalho Chehab, Hans Verkuil,
	Sakari Ailus, Antti Palosaari, Chris Paterson, Laurent Pinchart,
	Linux Media Mailing List, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Linux-Renesas

Hi Geert,

Thank you for the review. 

> Subject: Re: [RFC 1/5] media: i2c: max2175: Add MAX2175 support
> 
[...]
> 
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/i2c/max2175.txt
> > @@ -0,0 +1,60 @@
> > +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: video interface 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: load capacitance value (in pF) on reference output
> > +                    drive level. The mapping of these load values to
> > +                    respective bit values are given below.
> > +                    0 - Reference output disabled
> > +                    1 - 10pF load
> > +                    2 - 20pF load
> > +                    3 - 30pF load
> > +                    4 - 40pF load
> > +                    5 - 60pF load
> > +                    6 - 70pF load
> 
> For properties involving units, usually the unit is made part of the
> property name, e.g. maxim,refout-load-pF = 40.
> This avoids confusion, and allows for extension later.

Agreed. I have modified it as

- maxim,refout-load-pF: load capacitance value (in pF) on reference
                        output drive level. The default is refout disabled
                        or no load. The possible load values are 
                        10pF
                        20pF
                        30pF
                        40pF
                        60pF
                        70pF

> 
> > +/* A tuner device instance under i2c bus */
> > +max2175_0: tuner@60 {
> > +       #clock-cells = <0>;
> > +       compatible = "maxim,max2175";
> > +       reg = <0x60>;
> > +       clocks = <&maxim_xtal>;
> > +       clock-names = "xtal";
> > +       maxim,refout-load = <10>;
> 
> 10 is not listed above. Perhaps you meant 10 pF?

Yes.

> 
> > --- /dev/null
> > +++ b/drivers/media/i2c/max2175/max2175.c
> > @@ -0,0 +1,1624 @@
> 
> > +/* NOTE: Any addition/deletion in the below list should be reflected
> > +in
> > + * max2175_modetag enum
> > + */
> 
> You can drop the above comment if you make this explicit using C99
> designated initializers, cfr. below.
> 
> > +static const struct max2175_rxmode eu_rx_modes[] = { /* Indexed by EU
> modetag */
> > +       /* EU modes */
> > +       { MAX2175_BAND_VHF, 182640000, 0, { 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[] = { /* Indexed by NA
> modetag */
> > +       /* NA modes */
> > +       { MAX2175_BAND_FM, 98255520, 1, { 0, 0, 0, 0 } },
> 
> [MAX2175_NA_FM_1_0] = { MAX2175_BAND_FM, 98255520, 1, { 0, 0, 0, 0 } },
> 

Thank you. Using designated initializers now.

> > +struct max2175_ctx {
> > +       struct v4l2_subdev sd;
> > +       struct i2c_client *client;
> > +       struct device *dev;
> > +
> > +       /* Cached configuration */
> > +       u8 regs[256];
> > +       enum max2175_modetag mode;      /* Receive mode tag */
> > +       u32 freq;                       /* In Hz */
> > +       struct max2175_rxmode *rx_modes;
> > +
> > +       /* Device settings */
> > +       bool master;
> > +       u32 decim_ratio;
> > +       u64 xtal_freq;
> > +
> > +       /* ROM values */
> > +       u8 rom_bbf_bw_am;
> > +       u8 rom_bbf_bw_fm;
> > +       u8 rom_bbf_bw_dab;
> > +
> > +       /* Local copy of old settings */
> > +       u8 i2s_test;
> > +
> > +       u8 nbd_gain;
> > +       u8 nbd_threshold;
> > +       u8 wbd_gain;
> > +       u8 wbd_threshold;
> > +       u8 bbd_threshold;
> > +       u8 bbdclip_threshold;
> > +       u8 lt_wbd_threshold;
> > +       u8 lt_wbd_gain;
> > +
> > +       /* 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 *am_hiz;       /* AM High impledance input */
> > +       struct v4l2_ctrl *hsls;         /* High-side/Low-side polarity
> */
> > +       struct v4l2_ctrl *rx_mode;      /* Receive mode */
> > +
> > +       /* Driver private variables */
> > +       bool mode_resolved;             /* Flag to sanity check settings
> */
> > +};
> 
> Sorting the struct members by decreasing size helps to avoid gaps due to
> alignment restrictions, and may reduce memory consumption.

OK. I have corrected it to certain extent but still having groups (not strict decreasing order - for better readability). I have pushed the u8 group members to the end though.

> 
> > +/* Flush local copy to device from idx to idx+len (inclusive) */
> > +static void max2175_flush_regstore(struct max2175_ctx *ctx, u8 idx,
> > +u8 len) {
> > +       u8 i;
> 
> I'd just use unsigned int for loop counters.
> 

Agreed.

> > +
> > +       for (i = idx; i <= len; i++)
> > +               max2175_reg_write(ctx, i, ctx->regs[i]); }
> 
> > +static int max2175_update_i2s_mode(struct max2175_ctx *ctx, u32
> > +i2s_mode) {
> > +       /* Only change if it's new */
> > +       if (max2175_read_bits(ctx, 29, 2, 0) == i2s_mode)
> 
> Many magic numbers, not only here (29, 2), but everywhere.
> Can you please add #defines for these?

The Tuner provider is unwilling to disclose all register details. I agree on the readability issue with this restriction but this is somewhat true for some sensitive IPs in the media subsystem.

> 
> > +               return 0;
> > +
> > +       max2175_write_bits(ctx, 29, 2, 0, i2s_mode);
> > +
> > +       /* Based on I2S mode value I2S_WORD_CNT values change */
> > +       if (i2s_mode == MAX2175_I2S_MODE3) {
> > +               max2175_write_bits(ctx, 30, 6, 0, 1);
> > +       } else if (i2s_mode == MAX2175_I2S_MODE2 ||
> > +                  i2s_mode == MAX2175_I2S_MODE4) {
> > +               max2175_write_bits(ctx, 30, 6, 0, 0);
> > +       } else if (i2s_mode == MAX2175_I2S_MODE0) {
> > +               max2175_write_bits(ctx, 30, 6, 0,
> > +                                  ctx->rx_modes[ctx-
> >mode].i2s_word_size);
> > +       } else {
> > +               v4l2_err(ctx->client,
> > +                        "failed: i2s_mode %u unsupported\n", i2s_mode);
> > +               return 1;
> > +       }
> 
> switch (i2s_mode) { ... }

Agreed.

> 
> > +       mxm_dbg(ctx, "updated i2s_mode %u\n", i2s_mode);
> > +       return 0;
> > +}
> 
> > +static void max2175_set_filter_coeffs(struct max2175_ctx *ctx, u8
> m_sel,
> > +                                     u8 bank, const u16 *coeffs) {
> > +       u8 i, coeff_addr, upper_address;
> 
> I'd just use unsigned int for these.
> 

Agreed.

> > +
> > +       mxm_dbg(ctx, "start: 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;
> > +       else
> > +               upper_address = 24;
> > +
> > +       max2175_set_bit(ctx, 117, 7, 1);
> > +       for (i = 0; i < upper_address; i++) {
> > +               coeff_addr = i + (bank * 24);
> > +               max2175_set_bits(ctx, 115, 7, 0,
> > +                                (u8)((coeffs[i] >> 8) & 0xff));
> > +               max2175_set_bits(ctx, 116, 7, 0, (u8)(coeffs[i] &
> > + 0xff));
> 
> I don't think you need the casts to u8, or the masking with 0xff.

Yes. Corrected.

> 
> > +               max2175_set_bits(ctx, 117, 6, 0, coeff_addr);
> > +               max2175_flush_regstore(ctx, 115, 3);
> > +       }
> > +       max2175_write_bit(ctx, 117, 7, 0); }
> > +
> > +static void max2175_load_dab_1p2(struct max2175_ctx *ctx) {
> > +       u32 i;
> 
> unsigned int?

Agreed.

> 
> > +static int max2175_set_lo_freq(struct max2175_ctx *ctx, u64 lo_freq)
> > +{
> > +       int ret;
> > +       u32 lo_mult;
> > +       u64 scaled_lo_freq;
> > +       const u64 scale_factor = 1000000ULL;
> > +       u64 scaled_npf, scaled_integer, scaled_fraction;
> > +       u32 frac_desired, int_desired;
> > +       u8 loband_bits, vcodiv_bits;
> > +
> > +       scaled_lo_freq = lo_freq;
> > +       /* Scale to larger number for precision */
> > +       scaled_lo_freq = scaled_lo_freq * scale_factor * 100;
> > +
> > +       mxm_dbg(ctx, "scaled lo_freq %llu lo_freq %llu\n",
> > +               scaled_lo_freq, lo_freq);
> > +
> > +       if (MAX2175_IS_BAND_AM(ctx)) {
> > +               if (max2175_get_bit(ctx, 5, 7) == 0)
> > +                       loband_bits = 0;
> > +                       vcodiv_bits = 0;
> > +                       lo_mult = 16;
> > +       } else if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_FM) {
> > +               if (lo_freq <= 74700000) {
> > +                       loband_bits = 0;
> > +                       vcodiv_bits = 0;
> > +                       lo_mult = 16;
> > +               } else if ((lo_freq > 74700000) && (lo_freq <=
> 110000000)) {
> > +                       loband_bits = 1;
> > +                       vcodiv_bits = 0;
> > +               } else {
> > +                       loband_bits = 1;
> > +                       vcodiv_bits = 3;
> > +               }
> > +               lo_mult = 8;
> > +       } else if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF) {
> > +               if (lo_freq <= 210000000) {
> > +                       loband_bits = 2;
> > +                       vcodiv_bits = 2;
> > +               } else {
> > +                       loband_bits = 2;
> > +                       vcodiv_bits = 1;
> > +               }
> > +               lo_mult = 4;
> > +       } else {
> > +               loband_bits = 3;
> > +               vcodiv_bits = 2;
> > +               lo_mult = 2;
> > +       }
> > +
> > +       if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_L)
> > +               scaled_npf = (scaled_lo_freq / ctx->xtal_freq / lo_mult)
> / 100;
> > +       else
> > +               scaled_npf = (scaled_lo_freq / ctx->xtal_freq *
> > + lo_mult) / 100;
> 
> Please use one of the div64*() functions for divisions involving 64-bit
> quantities (try to build for 32-bit and see). More of these below...

Thanks. All 64bit divisions are corrected to div_x64 variants. You are right - kbuild reported MIPS build failure because of this. I tried only x86 for 32-bit.

> 
> > +       scaled_integer = scaled_npf / scale_factor * scale_factor;
> > +       int_desired = (u32)(scaled_npf / scale_factor);
> > +       scaled_fraction = scaled_npf - scaled_integer;
> > +       frac_desired = (u32)(scaled_fraction * 1048576 /
> > + scale_factor);
> 
> > +       /* Write the calculated values to the appropriate registers */
> > +       max2175_set_bits(ctx, 5, 3, 2, loband_bits);
> > +       max2175_set_bits(ctx, 6, 7, 6, vcodiv_bits);
> > +       max2175_set_bits(ctx, 1, 7, 0, (u8)(int_desired & 0xff));
> > +       max2175_set_bits(ctx, 2, 3, 0, (u8)((frac_desired >> 16) &
> 0x1f));
> > +       max2175_set_bits(ctx, 3, 7, 0, (u8)((frac_desired >> 8) &
> 0xff));
> > +       max2175_set_bits(ctx, 4, 7, 0, (u8)(frac_desired & 0xff));
> 
> No need for casts etc.

Yes, corrected.

> 
> > +       /* Flush the above registers to device */
> > +       max2175_flush_regstore(ctx, 1, 6);
> > +       return ret;
> > +}
> > +
> > +static int max2175_set_nco_freq(struct max2175_ctx *ctx, s64
> > +nco_freq_desired) {
> > +       int ret;
> > +       u64 clock_rate, abs_nco_freq;
> > +       s64  nco_freq, nco_val_desired;
> > +       u32 nco_reg;
> > +       const u64 scale_factor = 1000000ULL;
> > +
> > +       mxm_dbg(ctx, "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 = (2 * nco_freq * scale_factor) /
> clock_rate;
> > +       } else {
> > +               if (nco_freq < 0)
> > +                       nco_val_desired = (-2 * (clock_rate -
> abs_nco_freq) *
> > +                               scale_factor) / clock_rate;
> > +               else
> > +                       nco_val_desired = (2 * (clock_rate -
> abs_nco_freq) *
> > +                               scale_factor) / clock_rate;
> > +       }
> > +
> > +       /* Scale down to get the fraction */
> > +       if (nco_freq < 0)
> > +               nco_reg = 0x200000 + ((nco_val_desired * 1048576) /
> > +                                     scale_factor);
> > +       else
> > +               nco_reg = (nco_val_desired * 1048576) / scale_factor;
> 
> More 64-bit divisions. In addition, the dividers are 64-bit too.
> Can't they be 32-bit?
> 

Agreed. The dividers need not be 32-bit. Corrected everywhere.

> > +static int max2175_probe(struct i2c_client *client,
> > +                       const struct i2c_device_id *id) {
> > +       struct max2175_ctx *ctx;
> > +       struct device *dev = &client->dev;
> > +       struct v4l2_subdev *sd;
> > +       struct v4l2_ctrl_handler *hdl;
> > +       struct clk *clk;
> > +       bool master = true;
> > +       u32 refout_load, refout_bits = 0;       /* REFOUT disabled */
> > +       int ret;
> > +
> > +       /* Check if the adapter supports the needed features */
> > +       if (!i2c_check_functionality(client->adapter,
> > +                                    I2C_FUNC_SMBUS_BYTE_DATA)) {
> > +               dev_err(&client->dev, "i2c check failed\n");
> > +               return -EIO;
> > +       }
> > +
> > +       if (of_find_property(client->dev.of_node, "maxim,slave", NULL))
> > +               master = false;
> > +
> > +       if (!of_property_read_u32(client->dev.of_node, "maxim,refout-
> load",
> > +                                &refout_load))
> > +               refout_bits = max2175_refout_load_to_bits(client,
> > + refout_load);
> > +
> > +       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;
> > +       }
> > +
> > +       ctx = kzalloc(sizeof(struct max2175_ctx),
> > +                            GFP_KERNEL);
> 
> devm_kzalloc()?

Agreed & corrected. 

> 
> > +       if (ctx == NULL)
> > +               return -ENOMEM;
> > +
> > +       sd = &ctx->sd;
> > +       ctx->master = master;
> > +       ctx->mode_resolved = false;
> > +
> > +       /* Set the defaults */
> > +       ctx->freq = bands_rf.rangelow;
> > +
> > +       ctx->xtal_freq = clk_get_rate(clk);
> > +       dev_info(&client->dev, "xtal freq %lluHz\n", ctx->xtal_freq);
> > +
> > +       v4l2_i2c_subdev_init(sd, client, &max2175_ops);
> > +       ctx->client = client;
> > +
> > +       sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> > +       ctx->dev = dev;
> > +
> > +       /* Controls */
> > +       hdl = &ctx->ctrl_hdl;
> > +       ret = v4l2_ctrl_handler_init(hdl, 8);
> > +       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->am_hiz = v4l2_ctrl_new_custom(hdl, &max2175_am_hiz, 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 = (struct max2175_rxmode *)eu_rx_modes;
> > +       } else {
> > +               ctx->rx_mode = v4l2_ctrl_new_custom(hdl,
> > +                                                   &max2175_na_rx_mode,
> NULL);
> > +               ctx->rx_modes = (struct max2175_rxmode *)na_rx_modes;
> > +       }
> 
> The casts are meant to cast away constness. Can that be avoided?

Agreed. Made ctx->rx_modes member as const and avoided the cast here.
> 
> > --- /dev/null
> > +++ b/drivers/media/i2c/max2175/max2175.h
> 
> > +/* NOTE: Any addition/deletion in the below enum should be reflected
> > +in
> > + * V4L2_CID_MAX2175_RX_MODE ctrl strings
> 
> Which strings exactly?

It is supposed to be these two

+ */
+static const char * const max2175_ctrl_eu_rx_mode_strings[] = {
+	"DAB 1.2",
+	"NULL",
+};
+
+static const char * const max2175_ctrl_na_rx_mode_strings[] = {
+	"NA FM 1.0",
+	"NULL",
+};
+

I have corrected them now to use designated initializers. Thanks.

> 
> > + */
> > +enum max2175_modetag {
> > +       /* EU modes */
> > +       MAX2175_DAB_1_2 = 0,
> > +
> > +       /* Other possible modes to add in future
> > +        * MAX2175_DAB_1_0,
> > +        * MAX2175_DAB_1_3,
> > +        * MAX2175_EU_FM_2_2,
> > +        * MAX2175_EU_FM_1_0,
> > +        * MAX2175_EU_FMHD_4_0,
> > +        * MAX2175_EU_AM_1_0,
> > +        * MAX2175_EU_AM_2_2,
> > +        */
> > +
> > +       /* NA modes */
> > +       MAX2175_NA_FM_1_0 = 0,
> > +
> > +       /* Other possible modes to add in future
> > +        * MAX2175_NA_FM_1_2,
> > +        * MAX2175_NA_FMHD_1_0,
> > +        * MAX2175_NA_FMHD_1_2,
> > +        * MAX2175_NA_AM_1_0,
> > +        * MAX2175_NA_AM_1_2,
> > +        */
> 
> The MAX2175_NA_* definitions share their values with the MAX2175_DAB_* and
> future MAX2175_EU_* values. Do you have to use a single enum for both?
> 

I have made them separate enums now. NA_ modes were not tested yet.

I have also fixed few bugs in the meantime. I'll wait for this week for other media maintainers & experts to comment on the driver model & the new SDR format open item. Based on the response, I will post a new [PATCH] or [RFC v2] with these comments updated.

Thanks,
Ramesh

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

* RE: [RFC 1/5] media: i2c: max2175: Add MAX2175 support
@ 2016-10-18 15:04           ` Ramesh Shanmugasundaram
  0 siblings, 0 replies; 39+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-10-18 15:04 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Rob Herring, Mark Rutland, Mauro Carvalho Chehab, Hans Verkuil,
	Sakari Ailus, Antti Palosaari, Chris Paterson, Laurent Pinchart,
	Linux Media Mailing List, devicetree, Linux-Renesas

Hi Geert,

Thank you for the review. 

> Subject: Re: [RFC 1/5] media: i2c: max2175: Add MAX2175 support
> 
[...]
> 
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/i2c/max2175.txt
> > @@ -0,0 +1,60 @@
> > +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: video interface 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: load capacitance value (in pF) on reference output
> > +                    drive level. The mapping of these load values to
> > +                    respective bit values are given below.
> > +                    0 - Reference output disabled
> > +                    1 - 10pF load
> > +                    2 - 20pF load
> > +                    3 - 30pF load
> > +                    4 - 40pF load
> > +                    5 - 60pF load
> > +                    6 - 70pF load
> 
> For properties involving units, usually the unit is made part of the
> property name, e.g. maxim,refout-load-pF = 40.
> This avoids confusion, and allows for extension later.

Agreed. I have modified it as

- maxim,refout-load-pF: load capacitance value (in pF) on reference
                        output drive level. The default is refout disabled
                        or no load. The possible load values are 
                        10pF
                        20pF
                        30pF
                        40pF
                        60pF
                        70pF

> 
> > +/* A tuner device instance under i2c bus */
> > +max2175_0: tuner@60 {
> > +       #clock-cells = <0>;
> > +       compatible = "maxim,max2175";
> > +       reg = <0x60>;
> > +       clocks = <&maxim_xtal>;
> > +       clock-names = "xtal";
> > +       maxim,refout-load = <10>;
> 
> 10 is not listed above. Perhaps you meant 10 pF?

Yes.

> 
> > --- /dev/null
> > +++ b/drivers/media/i2c/max2175/max2175.c
> > @@ -0,0 +1,1624 @@
> 
> > +/* NOTE: Any addition/deletion in the below list should be reflected
> > +in
> > + * max2175_modetag enum
> > + */
> 
> You can drop the above comment if you make this explicit using C99
> designated initializers, cfr. below.
> 
> > +static const struct max2175_rxmode eu_rx_modes[] = { /* Indexed by EU
> modetag */
> > +       /* EU modes */
> > +       { MAX2175_BAND_VHF, 182640000, 0, { 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[] = { /* Indexed by NA
> modetag */
> > +       /* NA modes */
> > +       { MAX2175_BAND_FM, 98255520, 1, { 0, 0, 0, 0 } },
> 
> [MAX2175_NA_FM_1_0] = { MAX2175_BAND_FM, 98255520, 1, { 0, 0, 0, 0 } },
> 

Thank you. Using designated initializers now.

> > +struct max2175_ctx {
> > +       struct v4l2_subdev sd;
> > +       struct i2c_client *client;
> > +       struct device *dev;
> > +
> > +       /* Cached configuration */
> > +       u8 regs[256];
> > +       enum max2175_modetag mode;      /* Receive mode tag */
> > +       u32 freq;                       /* In Hz */
> > +       struct max2175_rxmode *rx_modes;
> > +
> > +       /* Device settings */
> > +       bool master;
> > +       u32 decim_ratio;
> > +       u64 xtal_freq;
> > +
> > +       /* ROM values */
> > +       u8 rom_bbf_bw_am;
> > +       u8 rom_bbf_bw_fm;
> > +       u8 rom_bbf_bw_dab;
> > +
> > +       /* Local copy of old settings */
> > +       u8 i2s_test;
> > +
> > +       u8 nbd_gain;
> > +       u8 nbd_threshold;
> > +       u8 wbd_gain;
> > +       u8 wbd_threshold;
> > +       u8 bbd_threshold;
> > +       u8 bbdclip_threshold;
> > +       u8 lt_wbd_threshold;
> > +       u8 lt_wbd_gain;
> > +
> > +       /* 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 *am_hiz;       /* AM High impledance input */
> > +       struct v4l2_ctrl *hsls;         /* High-side/Low-side polarity
> */
> > +       struct v4l2_ctrl *rx_mode;      /* Receive mode */
> > +
> > +       /* Driver private variables */
> > +       bool mode_resolved;             /* Flag to sanity check settings
> */
> > +};
> 
> Sorting the struct members by decreasing size helps to avoid gaps due to
> alignment restrictions, and may reduce memory consumption.

OK. I have corrected it to certain extent but still having groups (not strict decreasing order - for better readability). I have pushed the u8 group members to the end though.

> 
> > +/* Flush local copy to device from idx to idx+len (inclusive) */
> > +static void max2175_flush_regstore(struct max2175_ctx *ctx, u8 idx,
> > +u8 len) {
> > +       u8 i;
> 
> I'd just use unsigned int for loop counters.
> 

Agreed.

> > +
> > +       for (i = idx; i <= len; i++)
> > +               max2175_reg_write(ctx, i, ctx->regs[i]); }
> 
> > +static int max2175_update_i2s_mode(struct max2175_ctx *ctx, u32
> > +i2s_mode) {
> > +       /* Only change if it's new */
> > +       if (max2175_read_bits(ctx, 29, 2, 0) == i2s_mode)
> 
> Many magic numbers, not only here (29, 2), but everywhere.
> Can you please add #defines for these?

The Tuner provider is unwilling to disclose all register details. I agree on the readability issue with this restriction but this is somewhat true for some sensitive IPs in the media subsystem.

> 
> > +               return 0;
> > +
> > +       max2175_write_bits(ctx, 29, 2, 0, i2s_mode);
> > +
> > +       /* Based on I2S mode value I2S_WORD_CNT values change */
> > +       if (i2s_mode == MAX2175_I2S_MODE3) {
> > +               max2175_write_bits(ctx, 30, 6, 0, 1);
> > +       } else if (i2s_mode == MAX2175_I2S_MODE2 ||
> > +                  i2s_mode == MAX2175_I2S_MODE4) {
> > +               max2175_write_bits(ctx, 30, 6, 0, 0);
> > +       } else if (i2s_mode == MAX2175_I2S_MODE0) {
> > +               max2175_write_bits(ctx, 30, 6, 0,
> > +                                  ctx->rx_modes[ctx-
> >mode].i2s_word_size);
> > +       } else {
> > +               v4l2_err(ctx->client,
> > +                        "failed: i2s_mode %u unsupported\n", i2s_mode);
> > +               return 1;
> > +       }
> 
> switch (i2s_mode) { ... }

Agreed.

> 
> > +       mxm_dbg(ctx, "updated i2s_mode %u\n", i2s_mode);
> > +       return 0;
> > +}
> 
> > +static void max2175_set_filter_coeffs(struct max2175_ctx *ctx, u8
> m_sel,
> > +                                     u8 bank, const u16 *coeffs) {
> > +       u8 i, coeff_addr, upper_address;
> 
> I'd just use unsigned int for these.
> 

Agreed.

> > +
> > +       mxm_dbg(ctx, "start: 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;
> > +       else
> > +               upper_address = 24;
> > +
> > +       max2175_set_bit(ctx, 117, 7, 1);
> > +       for (i = 0; i < upper_address; i++) {
> > +               coeff_addr = i + (bank * 24);
> > +               max2175_set_bits(ctx, 115, 7, 0,
> > +                                (u8)((coeffs[i] >> 8) & 0xff));
> > +               max2175_set_bits(ctx, 116, 7, 0, (u8)(coeffs[i] &
> > + 0xff));
> 
> I don't think you need the casts to u8, or the masking with 0xff.

Yes. Corrected.

> 
> > +               max2175_set_bits(ctx, 117, 6, 0, coeff_addr);
> > +               max2175_flush_regstore(ctx, 115, 3);
> > +       }
> > +       max2175_write_bit(ctx, 117, 7, 0); }
> > +
> > +static void max2175_load_dab_1p2(struct max2175_ctx *ctx) {
> > +       u32 i;
> 
> unsigned int?

Agreed.

> 
> > +static int max2175_set_lo_freq(struct max2175_ctx *ctx, u64 lo_freq)
> > +{
> > +       int ret;
> > +       u32 lo_mult;
> > +       u64 scaled_lo_freq;
> > +       const u64 scale_factor = 1000000ULL;
> > +       u64 scaled_npf, scaled_integer, scaled_fraction;
> > +       u32 frac_desired, int_desired;
> > +       u8 loband_bits, vcodiv_bits;
> > +
> > +       scaled_lo_freq = lo_freq;
> > +       /* Scale to larger number for precision */
> > +       scaled_lo_freq = scaled_lo_freq * scale_factor * 100;
> > +
> > +       mxm_dbg(ctx, "scaled lo_freq %llu lo_freq %llu\n",
> > +               scaled_lo_freq, lo_freq);
> > +
> > +       if (MAX2175_IS_BAND_AM(ctx)) {
> > +               if (max2175_get_bit(ctx, 5, 7) == 0)
> > +                       loband_bits = 0;
> > +                       vcodiv_bits = 0;
> > +                       lo_mult = 16;
> > +       } else if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_FM) {
> > +               if (lo_freq <= 74700000) {
> > +                       loband_bits = 0;
> > +                       vcodiv_bits = 0;
> > +                       lo_mult = 16;
> > +               } else if ((lo_freq > 74700000) && (lo_freq <=
> 110000000)) {
> > +                       loband_bits = 1;
> > +                       vcodiv_bits = 0;
> > +               } else {
> > +                       loband_bits = 1;
> > +                       vcodiv_bits = 3;
> > +               }
> > +               lo_mult = 8;
> > +       } else if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF) {
> > +               if (lo_freq <= 210000000) {
> > +                       loband_bits = 2;
> > +                       vcodiv_bits = 2;
> > +               } else {
> > +                       loband_bits = 2;
> > +                       vcodiv_bits = 1;
> > +               }
> > +               lo_mult = 4;
> > +       } else {
> > +               loband_bits = 3;
> > +               vcodiv_bits = 2;
> > +               lo_mult = 2;
> > +       }
> > +
> > +       if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_L)
> > +               scaled_npf = (scaled_lo_freq / ctx->xtal_freq / lo_mult)
> / 100;
> > +       else
> > +               scaled_npf = (scaled_lo_freq / ctx->xtal_freq *
> > + lo_mult) / 100;
> 
> Please use one of the div64*() functions for divisions involving 64-bit
> quantities (try to build for 32-bit and see). More of these below...

Thanks. All 64bit divisions are corrected to div_x64 variants. You are right - kbuild reported MIPS build failure because of this. I tried only x86 for 32-bit.

> 
> > +       scaled_integer = scaled_npf / scale_factor * scale_factor;
> > +       int_desired = (u32)(scaled_npf / scale_factor);
> > +       scaled_fraction = scaled_npf - scaled_integer;
> > +       frac_desired = (u32)(scaled_fraction * 1048576 /
> > + scale_factor);
> 
> > +       /* Write the calculated values to the appropriate registers */
> > +       max2175_set_bits(ctx, 5, 3, 2, loband_bits);
> > +       max2175_set_bits(ctx, 6, 7, 6, vcodiv_bits);
> > +       max2175_set_bits(ctx, 1, 7, 0, (u8)(int_desired & 0xff));
> > +       max2175_set_bits(ctx, 2, 3, 0, (u8)((frac_desired >> 16) &
> 0x1f));
> > +       max2175_set_bits(ctx, 3, 7, 0, (u8)((frac_desired >> 8) &
> 0xff));
> > +       max2175_set_bits(ctx, 4, 7, 0, (u8)(frac_desired & 0xff));
> 
> No need for casts etc.

Yes, corrected.

> 
> > +       /* Flush the above registers to device */
> > +       max2175_flush_regstore(ctx, 1, 6);
> > +       return ret;
> > +}
> > +
> > +static int max2175_set_nco_freq(struct max2175_ctx *ctx, s64
> > +nco_freq_desired) {
> > +       int ret;
> > +       u64 clock_rate, abs_nco_freq;
> > +       s64  nco_freq, nco_val_desired;
> > +       u32 nco_reg;
> > +       const u64 scale_factor = 1000000ULL;
> > +
> > +       mxm_dbg(ctx, "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 = (2 * nco_freq * scale_factor) /
> clock_rate;
> > +       } else {
> > +               if (nco_freq < 0)
> > +                       nco_val_desired = (-2 * (clock_rate -
> abs_nco_freq) *
> > +                               scale_factor) / clock_rate;
> > +               else
> > +                       nco_val_desired = (2 * (clock_rate -
> abs_nco_freq) *
> > +                               scale_factor) / clock_rate;
> > +       }
> > +
> > +       /* Scale down to get the fraction */
> > +       if (nco_freq < 0)
> > +               nco_reg = 0x200000 + ((nco_val_desired * 1048576) /
> > +                                     scale_factor);
> > +       else
> > +               nco_reg = (nco_val_desired * 1048576) / scale_factor;
> 
> More 64-bit divisions. In addition, the dividers are 64-bit too.
> Can't they be 32-bit?
> 

Agreed. The dividers need not be 32-bit. Corrected everywhere.

> > +static int max2175_probe(struct i2c_client *client,
> > +                       const struct i2c_device_id *id) {
> > +       struct max2175_ctx *ctx;
> > +       struct device *dev = &client->dev;
> > +       struct v4l2_subdev *sd;
> > +       struct v4l2_ctrl_handler *hdl;
> > +       struct clk *clk;
> > +       bool master = true;
> > +       u32 refout_load, refout_bits = 0;       /* REFOUT disabled */
> > +       int ret;
> > +
> > +       /* Check if the adapter supports the needed features */
> > +       if (!i2c_check_functionality(client->adapter,
> > +                                    I2C_FUNC_SMBUS_BYTE_DATA)) {
> > +               dev_err(&client->dev, "i2c check failed\n");
> > +               return -EIO;
> > +       }
> > +
> > +       if (of_find_property(client->dev.of_node, "maxim,slave", NULL))
> > +               master = false;
> > +
> > +       if (!of_property_read_u32(client->dev.of_node, "maxim,refout-
> load",
> > +                                &refout_load))
> > +               refout_bits = max2175_refout_load_to_bits(client,
> > + refout_load);
> > +
> > +       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;
> > +       }
> > +
> > +       ctx = kzalloc(sizeof(struct max2175_ctx),
> > +                            GFP_KERNEL);
> 
> devm_kzalloc()?

Agreed & corrected. 

> 
> > +       if (ctx == NULL)
> > +               return -ENOMEM;
> > +
> > +       sd = &ctx->sd;
> > +       ctx->master = master;
> > +       ctx->mode_resolved = false;
> > +
> > +       /* Set the defaults */
> > +       ctx->freq = bands_rf.rangelow;
> > +
> > +       ctx->xtal_freq = clk_get_rate(clk);
> > +       dev_info(&client->dev, "xtal freq %lluHz\n", ctx->xtal_freq);
> > +
> > +       v4l2_i2c_subdev_init(sd, client, &max2175_ops);
> > +       ctx->client = client;
> > +
> > +       sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> > +       ctx->dev = dev;
> > +
> > +       /* Controls */
> > +       hdl = &ctx->ctrl_hdl;
> > +       ret = v4l2_ctrl_handler_init(hdl, 8);
> > +       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->am_hiz = v4l2_ctrl_new_custom(hdl, &max2175_am_hiz, 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 = (struct max2175_rxmode *)eu_rx_modes;
> > +       } else {
> > +               ctx->rx_mode = v4l2_ctrl_new_custom(hdl,
> > +                                                   &max2175_na_rx_mode,
> NULL);
> > +               ctx->rx_modes = (struct max2175_rxmode *)na_rx_modes;
> > +       }
> 
> The casts are meant to cast away constness. Can that be avoided?

Agreed. Made ctx->rx_modes member as const and avoided the cast here.
> 
> > --- /dev/null
> > +++ b/drivers/media/i2c/max2175/max2175.h
> 
> > +/* NOTE: Any addition/deletion in the below enum should be reflected
> > +in
> > + * V4L2_CID_MAX2175_RX_MODE ctrl strings
> 
> Which strings exactly?

It is supposed to be these two

+ */
+static const char * const max2175_ctrl_eu_rx_mode_strings[] = {
+	"DAB 1.2",
+	"NULL",
+};
+
+static const char * const max2175_ctrl_na_rx_mode_strings[] = {
+	"NA FM 1.0",
+	"NULL",
+};
+

I have corrected them now to use designated initializers. Thanks.

> 
> > + */
> > +enum max2175_modetag {
> > +       /* EU modes */
> > +       MAX2175_DAB_1_2 = 0,
> > +
> > +       /* Other possible modes to add in future
> > +        * MAX2175_DAB_1_0,
> > +        * MAX2175_DAB_1_3,
> > +        * MAX2175_EU_FM_2_2,
> > +        * MAX2175_EU_FM_1_0,
> > +        * MAX2175_EU_FMHD_4_0,
> > +        * MAX2175_EU_AM_1_0,
> > +        * MAX2175_EU_AM_2_2,
> > +        */
> > +
> > +       /* NA modes */
> > +       MAX2175_NA_FM_1_0 = 0,
> > +
> > +       /* Other possible modes to add in future
> > +        * MAX2175_NA_FM_1_2,
> > +        * MAX2175_NA_FMHD_1_0,
> > +        * MAX2175_NA_FMHD_1_2,
> > +        * MAX2175_NA_AM_1_0,
> > +        * MAX2175_NA_AM_1_2,
> > +        */
> 
> The MAX2175_NA_* definitions share their values with the MAX2175_DAB_* and
> future MAX2175_EU_* values. Do you have to use a single enum for both?
> 

I have made them separate enums now. NA_ modes were not tested yet.

I have also fixed few bugs in the meantime. I'll wait for this week for other media maintainers & experts to comment on the driver model & the new SDR format open item. Based on the response, I will post a new [PATCH] or [RFC v2] with these comments updated.

Thanks,
Ramesh

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

* RE: [RFC 3/5] media: platform: rcar_drif: Add DRIF support
  2016-10-18 13:13   ` Rob Herring
@ 2016-10-18 15:13     ` Ramesh Shanmugasundaram
  0 siblings, 0 replies; 39+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-10-18 15:13 UTC (permalink / raw)
  To: Rob Herring
  Cc: mark.rutland, mchehab, hverkuil, sakari.ailus, crope,
	Chris Paterson, laurent.pinchart, geert, linux-media, devicetree,
	linux-renesas-soc

Hi Rob,

Thank you for the review comments.

> Subject: Re: [RFC 3/5] media: platform: rcar_drif: Add DRIF support
> 
> On Wed, Oct 12, 2016 at 03:10:27PM +0100, 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     |  109 ++
> >  drivers/media/platform/Kconfig                     |   25 +
> >  drivers/media/platform/Makefile                    |    1 +
> >  drivers/media/platform/rcar_drif.c                 | 1534
> ++++++++++++++++++++
> >  4 files changed, 1669 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..24239d9
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> > @@ -0,0 +1,109 @@
> > +Renesas R-Car Gen3 DRIF controller (DRIF)
> 
> Define what is DRIF here, not just in the commit text.

Agreed.

> 
> > +-----------------------------------------
> > +
> > +Required properties:
> > +--------------------
> > +- compatible: "renesas,drif-r8a7795" if DRIF controller is a part of
> R8A7795 SoC.
> 
> renesas,r8a7795-drif would be the normal ordering.

Agreed.

> 
> > +	      "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 each sub-channel.
> > +- interrupts: associated with each sub-channel.
> > +- clocks: phandles and clock specifiers for each sub-channel.
> > +- clock-names: clock input name strings: "fck0", "fck1".
> > +- pinctrl-0: pin control group to be used for this controller.
> > +- pinctrl-names: must be "default".
> > +- dmas: phandles to the DMA channels for each sub-channel.
> > +- dma-names: names for the DMA channels: "rx0", "rx1".
> > +
> > +Required child nodes:
> > +---------------------
> > +- Each DRIF channel can have one or both of the sub-channels enabled
> > +in a
> > +  setup. The sub-channels are represented as a child node. The name
> > +of the
> > +  child nodes are "sub-channel0" and "sub-channel1" respectively.
> > +Each child
> > +  node supports the "status" property only, which is used to
> > +enable/disable
> > +  the respective sub-channel.
> > +
> > +Optional properties:
> > +--------------------
> > +- port: video interface child port node of a channel that defines the
> > +local
> 
> This is an audio device, why does it have a video port?

Apologies for the wording. I intend to refer a regular port node like mentioned here - https://www.kernel.org/doc/Documentation/devicetree/bindings/graph.txt

> 
> > +  and remote endpoints. The remote endpoint is assumed to a tuner
> > +subdevice
> > +  endpoint.
> > +- power-domains: phandle to respective power domain.
> > +- 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
> > +
> > +drif0: rif@e6f40000 {
> > +	compatible = "renesas,drif-r8a7795",
> > +		   "renesas,rcar-gen3-drif";
> > +	reg = <0 0xe6f40000 0 0x64>, <0 0xe6f50000 0 0x64>;
> > +	interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>,
> > +		   <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
> > +	clocks = <&cpg CPG_MOD 515>, <&cpg CPG_MOD 514>;
> > +	clock-names = "fck0", "fck1";
> > +	dmas = <&dmac1 0x20>, <&dmac1 0x22>;
> > +	dma-names = "rx0", "rx1";
> > +	power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> > +	status = "disabled";
> > +
> > +	sub-channel0 {
> > +		status = "disabled";
> > +	};
> > +
> > +	sub-channel1 {
> > +		status = "disabled";
> > +	};
> > +
> > +};
> > +
> > +Board specific dts file
> > +
> > +&drif0 {
> > +	pinctrl-0 = <&drif0_pins>;
> > +	pinctrl-names = "default";
> > +	status = "okay";
> > +
> > +	sub-channel0 {
> > +		status = "okay";
> > +	};
> > +
> > +	sub-channel1 {
> > +		status = "okay";
> > +	};
> > +
> > +	port {
> > +		drif0_ep: endpoint {
> > +		     remote-endpoint = <&tuner_subdev_ep>;
> > +		};
> > +	};
> > +};

Thanks,
Ramesh

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

* Re: [RFC 3/5] media: platform: rcar_drif: Add DRIF support
  2016-10-18 14:29   ` Geert Uytterhoeven
@ 2016-10-18 18:26     ` Laurent Pinchart
  2016-10-21 13:17         ` Ramesh Shanmugasundaram
       [not found]     ` <CAMuHMdXvGEm3bdNOsa6Q1FLB9yMSTAzO4nHcCb-pnYYwg6f6Cg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
  1 sibling, 1 reply; 39+ messages in thread
From: Laurent Pinchart @ 2016-10-18 18:26 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Ramesh Shanmugasundaram, Rob Herring, Mark Rutland,
	Mauro Carvalho Chehab, Hans Verkuil, Sakari Ailus,
	Antti Palosaari, Chris Paterson, Linux Media Mailing List,
	devicetree, Linux-Renesas

Hello,

On Tuesday 18 Oct 2016 16:29:24 Geert Uytterhoeven wrote:
> On Wed, Oct 12, 2016 at 4:10 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.
> 
> Thanks for your patch!
> 
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> > @@ -0,0 +1,109 @@
> > +Renesas R-Car Gen3 DRIF controller (DRIF)
> > +-----------------------------------------
> > +
> > +Required properties:
> > +--------------------
> > +- compatible: "renesas,drif-r8a7795" if DRIF controller is a part of
> > R8A7795 SoC.
>
> "renesas,r8a7795-drif", as Rob already pointed out.
> 
> > +             "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 each sub-channel.
> > +- interrupts: associated with each sub-channel.
> > +- clocks: phandles and clock specifiers for each sub-channel.
> > +- clock-names: clock input name strings: "fck0", "fck1".
> > +- pinctrl-0: pin control group to be used for this controller.
> > +- pinctrl-names: must be "default".
> > +- dmas: phandles to the DMA channels for each sub-channel.
> > +- dma-names: names for the DMA channels: "rx0", "rx1".
> > +
> > +Required child nodes:
> > +---------------------
> > +- Each DRIF channel can have one or both of the sub-channels enabled in a
> > +  setup. The sub-channels are represented as a child node. The name of
> > the
> > +  child nodes are "sub-channel0" and "sub-channel1" respectively. Each
> > child
> > +  node supports the "status" property only, which is used to
> > enable/disable
> > +  the respective sub-channel.
> > 
> > +Example
> > +--------
> > +
> > +SoC common dtsi file
> > +
> > +drif0: rif@e6f40000 {
> > +       compatible = "renesas,drif-r8a7795",
> > +                  "renesas,rcar-gen3-drif";
> > +       reg = <0 0xe6f40000 0 0x64>, <0 0xe6f50000 0 0x64>;
> > +       interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>,
> > +                  <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
> > +       clocks = <&cpg CPG_MOD 515>, <&cpg CPG_MOD 514>;
> > +       clock-names = "fck0", "fck1";
> > +       dmas = <&dmac1 0x20>, <&dmac1 0x22>;
> > +       dma-names = "rx0", "rx1";
> 
> I could not find the DMAC channels in the datasheet?
> Most modules are either tied to dmac0, or two both dmac1 and dmac2.
> In the latter case, you want to list two sets of dmas, one for each DMAC.
> 
> > +       power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> > +       status = "disabled";
> > +
> > +       sub-channel0 {
> > +               status = "disabled";
> > +       };
> > +
> > +       sub-channel1 {
> > +               status = "disabled";
> > +       };
> > +
> > +};
> 
> As you're modelling this in DT under a single device node, this means you
> cannot use runtime PM to manage the module clocks of the individual
> channels.
> 
> An alternative could be to have two separate nodes for each channel,
> and tie them together using a phandle.

A quick glance at the documentation shows no dependency between the two 
channels at a software level. They both share the same clock and 
synchronization input pins, but from a hardware point of view that seems to be 
it. It thus looks like we could indeed model the two channels as separate 
nodes, without tying them together.

-- 
Regards,

Laurent Pinchart

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

* Re: [RFC 5/5] doc_rst: media: New SDR formats SC16, SC18 & SC20
  2016-10-12 14:10 ` [RFC 5/5] doc_rst: media: New " Ramesh Shanmugasundaram
@ 2016-10-18 18:35       ` Laurent Pinchart
  0 siblings, 0 replies; 39+ messages in thread
From: Laurent Pinchart @ 2016-10-18 18:35 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-Td1EMuHUCqxL1ZNQvxDV9g, linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA

Hi Ramesh,

Thank you for the patch.

On Wednesday 12 Oct 2016 15:10:29 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-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org> ---
>  .../media/uapi/v4l/pixfmt-sdr-scu16be.rst          | 44 ++++++++++++++++++
>  .../media/uapi/v4l/pixfmt-sdr-scu18be.rst          | 48 +++++++++++++++++++
>  .../media/uapi/v4l/pixfmt-sdr-scu20be.rst          | 48 +++++++++++++++++++
>  Documentation/media/uapi/v4l/sdr-formats.rst       |  3 ++
>  4 files changed, 143 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..d6c2123
> --- /dev/null
> +++ b/Documentation/media/uapi/v4l/pixfmt-sdr-scu16be.rst
> @@ -0,0 +1,44 @@
> +.. -*- coding: utf-8; mode: rst -*-
> +
> +.. _V4L2-SDR-FMT-SCU16BE:
> +
> +******************************
> +V4L2_SDR_FMT_SCU16BE ('SCU16')

The value between parentheses is the ASCII representation of the 4CC, it 
should be SC16. Same comment for the other formats.

> +******************************
> +
> +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. I value
> +starts first and Q value starts at an offset equalling half of the buffer
> +size. 14 bit data is stored in 16 bit space with unused stuffed bits
> +padded with 0.

Please specify here how the 14-bit numbers are aligned (i.e. padding in bits 
15:14 or bits 1:0 or any other strange option). Same comment for the other 
formats.

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

Please use the more compact table stable

	* - start + 0:
	  - I'\ :sub:`0[D13:D6]`
	  ...

Same comment for the other formats.

> +
> +       -  start + 0:
> +
> +       -  I'\ :sub:`0[D13:D6]`
> +
> +       -  I'\ :sub:`0[D5:D0]`
> +
> +    -  .. row 2
> +
> +       -  start + buffer_size/2:
> +
> +       -  Q'\ :sub:`0[D13:D6]`
> +
> +       -  Q'\ :sub:`0[D5:D0]`

The format looks planar, does it use one V4L2 plane (as does NV12) or two V4L2 
planes (as does NV12M) ? Same question for the other formats.

> 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..e6e0aff
> --- /dev/null
> +++ b/Documentation/media/uapi/v4l/pixfmt-sdr-scu18be.rst
> @@ -0,0 +1,48 @@
> +.. -*- coding: utf-8; mode: rst -*-
> +
> +.. _V4L2-SDR-FMT-SCU18BE:
> +
> +******************************
> +V4L2_SDR_FMT_SCU18BE ('SCU18')
> +******************************
> +
> +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. I value
> +starts first and Q value starts at an offset equalling half of the buffer
> +size. 16 bit data is stored in 18 bit space with unused stuffed bits
> +padded with 0.

Your example below suggests that 18 bit data is stored in 24 bits. Similar 
comment for SCU20.

> +
> +**Byte Order.**
> +Each cell is one byte.
> +
> +
> +.. flat-table::
> +    :header-rows:  0
> +    :stub-columns: 0
> +
> +    -  .. row 1
> +
> +       -  start + 0:
> +
> +       -  I'\ :sub:`0[D17:D10]`
> +
> +       -  I'\ :sub:`0[D9:D2]`
> +
> +       -  I'\ :sub:`0[D1:D0]`
> +
> +    -  .. row 2
> +
> +       -  start + buffer_size/2:
> +
> +       -  Q'\ :sub:`0[D17:D10]`
> +
> +       -  Q'\ :sub:`0[D9:D2]`
> +
> +       -  Q'\ :sub:`0[D1:D0]`
> 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..374e0a3
> --- /dev/null
> +++ b/Documentation/media/uapi/v4l/pixfmt-sdr-scu20be.rst
> @@ -0,0 +1,48 @@
> +.. -*- coding: utf-8; mode: rst -*-
> +
> +.. _V4L2-SDR-FMT-SCU20BE:
> +
> +******************************
> +V4L2_SDR_FMT_SCU20BE ('SCU20')
> +******************************
> +
> +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. I value
> +starts first and Q value starts at an offset equalling half of the buffer
> +size. 18 bit data is stored in 20 bit space with unused stuffed bits
> +padded with 0.
> +
> +**Byte Order.**
> +Each cell is one byte.
> +
> +
> +.. flat-table::
> +    :header-rows:  0
> +    :stub-columns: 0
> +
> +    -  .. row 1
> +
> +       -  start + 0:
> +
> +       -  I'\ :sub:`0[D19:D12]`
> +
> +       -  I'\ :sub:`0[D11:D4]`
> +
> +       -  I'\ :sub:`0[D3:D0]`
> +
> +    -  .. row 2
> +
> +       -  start + buffer_size/2:
> +
> +       -  Q'\ :sub:`0[D19:D12]`
> +
> +       -  Q'\ :sub:`0[D11:D4]`
> +
> +       -  Q'\ :sub:`0[D3:D0]`
> 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

-- 
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] 39+ messages in thread

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

Hi Ramesh,

Thank you for the patch.

On Wednesday 12 Oct 2016 15:10:29 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          | 44 ++++++++++++++++++
>  .../media/uapi/v4l/pixfmt-sdr-scu18be.rst          | 48 +++++++++++++++++++
>  .../media/uapi/v4l/pixfmt-sdr-scu20be.rst          | 48 +++++++++++++++++++
>  Documentation/media/uapi/v4l/sdr-formats.rst       |  3 ++
>  4 files changed, 143 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..d6c2123
> --- /dev/null
> +++ b/Documentation/media/uapi/v4l/pixfmt-sdr-scu16be.rst
> @@ -0,0 +1,44 @@
> +.. -*- coding: utf-8; mode: rst -*-
> +
> +.. _V4L2-SDR-FMT-SCU16BE:
> +
> +******************************
> +V4L2_SDR_FMT_SCU16BE ('SCU16')

The value between parentheses is the ASCII representation of the 4CC, it 
should be SC16. Same comment for the other formats.

> +******************************
> +
> +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. I value
> +starts first and Q value starts at an offset equalling half of the buffer
> +size. 14 bit data is stored in 16 bit space with unused stuffed bits
> +padded with 0.

Please specify here how the 14-bit numbers are aligned (i.e. padding in bits 
15:14 or bits 1:0 or any other strange option). Same comment for the other 
formats.

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

Please use the more compact table stable

	* - start + 0:
	  - I'\ :sub:`0[D13:D6]`
	  ...

Same comment for the other formats.

> +
> +       -  start + 0:
> +
> +       -  I'\ :sub:`0[D13:D6]`
> +
> +       -  I'\ :sub:`0[D5:D0]`
> +
> +    -  .. row 2
> +
> +       -  start + buffer_size/2:
> +
> +       -  Q'\ :sub:`0[D13:D6]`
> +
> +       -  Q'\ :sub:`0[D5:D0]`

The format looks planar, does it use one V4L2 plane (as does NV12) or two V4L2 
planes (as does NV12M) ? Same question for the other formats.

> 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..e6e0aff
> --- /dev/null
> +++ b/Documentation/media/uapi/v4l/pixfmt-sdr-scu18be.rst
> @@ -0,0 +1,48 @@
> +.. -*- coding: utf-8; mode: rst -*-
> +
> +.. _V4L2-SDR-FMT-SCU18BE:
> +
> +******************************
> +V4L2_SDR_FMT_SCU18BE ('SCU18')
> +******************************
> +
> +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. I value
> +starts first and Q value starts at an offset equalling half of the buffer
> +size. 16 bit data is stored in 18 bit space with unused stuffed bits
> +padded with 0.

Your example below suggests that 18 bit data is stored in 24 bits. Similar 
comment for SCU20.

> +
> +**Byte Order.**
> +Each cell is one byte.
> +
> +
> +.. flat-table::
> +    :header-rows:  0
> +    :stub-columns: 0
> +
> +    -  .. row 1
> +
> +       -  start + 0:
> +
> +       -  I'\ :sub:`0[D17:D10]`
> +
> +       -  I'\ :sub:`0[D9:D2]`
> +
> +       -  I'\ :sub:`0[D1:D0]`
> +
> +    -  .. row 2
> +
> +       -  start + buffer_size/2:
> +
> +       -  Q'\ :sub:`0[D17:D10]`
> +
> +       -  Q'\ :sub:`0[D9:D2]`
> +
> +       -  Q'\ :sub:`0[D1:D0]`
> 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..374e0a3
> --- /dev/null
> +++ b/Documentation/media/uapi/v4l/pixfmt-sdr-scu20be.rst
> @@ -0,0 +1,48 @@
> +.. -*- coding: utf-8; mode: rst -*-
> +
> +.. _V4L2-SDR-FMT-SCU20BE:
> +
> +******************************
> +V4L2_SDR_FMT_SCU20BE ('SCU20')
> +******************************
> +
> +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. I value
> +starts first and Q value starts at an offset equalling half of the buffer
> +size. 18 bit data is stored in 20 bit space with unused stuffed bits
> +padded with 0.
> +
> +**Byte Order.**
> +Each cell is one byte.
> +
> +
> +.. flat-table::
> +    :header-rows:  0
> +    :stub-columns: 0
> +
> +    -  .. row 1
> +
> +       -  start + 0:
> +
> +       -  I'\ :sub:`0[D19:D12]`
> +
> +       -  I'\ :sub:`0[D11:D4]`
> +
> +       -  I'\ :sub:`0[D3:D0]`
> +
> +    -  .. row 2
> +
> +       -  start + buffer_size/2:
> +
> +       -  Q'\ :sub:`0[D19:D12]`
> +
> +       -  Q'\ :sub:`0[D11:D4]`
> +
> +       -  Q'\ :sub:`0[D3:D0]`
> 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

-- 
Regards,

Laurent Pinchart


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

* Re: [RFC 1/5] media: i2c: max2175: Add MAX2175 support
  2016-10-12 14:10 ` [RFC 1/5] media: i2c: max2175: Add MAX2175 support Ramesh Shanmugasundaram
       [not found]   ` <1476281429-27603-2-git-send-email-ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>
@ 2016-10-18 19:25   ` Laurent Pinchart
  2016-10-21 14:49     ` Ramesh Shanmugasundaram
  1 sibling, 1 reply; 39+ messages in thread
From: Laurent Pinchart @ 2016-10-18 19:25 UTC (permalink / raw)
  To: Ramesh Shanmugasundaram
  Cc: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope,
	chris.paterson2, geert, linux-media, devicetree,
	linux-renesas-soc

Hi Ramesh,

Thank you for the patch.

On Wednesday 12 Oct 2016 15:10:25 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      |   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                | 1624 +++++++++++++++++
>  drivers/media/i2c/max2175/max2175.h                |  124 ++
>  7 files changed, 1826 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..2250d5f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/i2c/max2175.txt
> @@ -0,0 +1,60 @@
> +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: video interface child port node of a tuner that defines the local

As Rob pointed out in his review of patch 3/5, this isn't video.

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

Would it be useful to make that property a phandle to the master tuner, to 
give drivers a way to access the master ? I haven't checked whether there 
could be use cases for that.

> +- maxim,refout-load: load capacitance value (in pF) on reference output
> +		     drive level. The mapping of these load values to
> +		     respective bit values are given below.
> +		     0 - Reference output disabled
> +		     1 - 10pF load
> +		     2 - 20pF load
> +		     3 - 30pF load
> +		     4 - 40pF load
> +		     5 - 60pF load
> +		     6 - 70pF load

As Geert pointed out, you can simply specify the value in pF.

> +
> +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 {
> +	#clock-cells = <0>;

Is the tuner a clock provider ? If it isn't you don't need this property.

> +	compatible = "maxim,max2175";
> +	reg = <0x60>;
> +	clocks = <&maxim_xtal>;
> +	clock-names = "xtal";
> +	maxim,refout-load = <10>;
> +
> +	port {
> +		max2175_0_ep: endpoint {
> +			remote-endpoint = <&slave_rx_v4l2_sdr_device>;
> +		};
> +	};
> +
> +};

[snip]

> 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

If there's a single source file you might want to move it to 
drivers/media/i2c/.

> diff --git a/drivers/media/i2c/max2175/max2175.c
> b/drivers/media/i2c/max2175/max2175.c new file mode 100644
> index 0000000..71b60c2
> --- /dev/null
> +++ b/drivers/media/i2c/max2175/max2175.c
> @@ -0,0 +1,1624 @@
> +/*
> + * 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/errno.h>
> +#include <linux/i2c.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/delay.h>

You can move delay.h right below clk.h and everything will be in alphabetical 
order :-)

> +#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 max2175_debug;
> +module_param(max2175_debug, uint, 0644);
> +MODULE_PARM_DESC(max2175_debug, "activate debug info");

You can name the parameter "debug".

> +#define mxm_dbg(ctx, fmt, arg...) \
> +	v4l2_dbg(1, max2175_debug, &ctx->sd, fmt, ## arg)

[snip]

> +/* Preset values:
> + * Based on Maxim MAX2175 Register Table revision: 130p10
> + */

The preferred multi-line comment style is

/*
 * foo
 * bar
 */

[snip]

> +struct max2175_ctx {

Nitpicking, such a structure would usually be named max2175 or max2175_device. 
Context seems to imply that you can have multiple of them per device.

> +	struct v4l2_subdev sd;
> +	struct i2c_client *client;
> +	struct device *dev;
> +
> +	/* Cached configuration */
> +	u8 regs[256];

If you want to cache register values you should use regmap.

> +	enum max2175_modetag mode;	/* Receive mode tag */
> +	u32 freq;			/* In Hz */
> +	struct max2175_rxmode *rx_modes;
> +
> +	/* Device settings */
> +	bool master;
> +	u32 decim_ratio;
> +	u64 xtal_freq;
> +
> +	/* ROM values */
> +	u8 rom_bbf_bw_am;
> +	u8 rom_bbf_bw_fm;
> +	u8 rom_bbf_bw_dab;
> +
> +	/* Local copy of old settings */
> +	u8 i2s_test;
> +
> +	u8 nbd_gain;
> +	u8 nbd_threshold;
> +	u8 wbd_gain;
> +	u8 wbd_threshold;
> +	u8 bbd_threshold;
> +	u8 bbdclip_threshold;
> +	u8 lt_wbd_threshold;
> +	u8 lt_wbd_gain;
> +
> +	/* 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 *am_hiz;	/* AM High impledance input */
> +	struct v4l2_ctrl *hsls;		/* High-side/Low-side polarity */
> +	struct v4l2_ctrl *rx_mode;	/* Receive mode */
> +
> +	/* Driver private variables */
> +	bool mode_resolved;		/* Flag to sanity check settings */
> +};

[snip]

> +/* Local store bitops helpers */
> +static u8 max2175_get_bits(struct max2175_ctx *ctx, u8 idx, u8 msb, u8 lsb)
> +{
> +	if (max2175_debug >= 2)
> +		mxm_dbg(ctx, "get_bits: idx:%u msb:%u lsb:%u\n",
> +			idx, msb, lsb);

Do we really need such detailed debugging ?

> +	return __max2175_get_bits(ctx->regs[idx], msb, lsb);
> +}
> +
> +static bool max2175_get_bit(struct max2175_ctx *ctx, u8 idx, u8 bit)
> +{
> +	return !!max2175_get_bits(ctx, idx, bit, bit);
> +}
> +
> +static void max2175_set_bits(struct max2175_ctx *ctx, u8 idx,
> +		      u8 msb, u8 lsb, u8 newval)
> +{
> +	if (max2175_debug >= 2)
> +		mxm_dbg(ctx, "set_bits: idx:%u msb:%u lsb:%u newval:%u\n",
> +			idx, msb, lsb, newval);
> +	ctx->regs[idx] = __max2175_set_bits(ctx->regs[idx], msb, lsb,
> +					      newval);
> +}
> +
> +static void max2175_set_bit(struct max2175_ctx *ctx, u8 idx, u8 bit, u8
> newval)
> +{
> +	max2175_set_bits(ctx, idx, bit, bit, newval);
> +}
> +
> +/* Device register bitops helpers */
> +static u8 max2175_read_bits(struct max2175_ctx *ctx, u8 idx, u8 msb, u8
> lsb)
> +{
> +	return __max2175_get_bits(max2175_reg_read(ctx, idx), msb, lsb);
> +}
> +
> +static void max2175_write_bits(struct max2175_ctx *ctx, u8 idx, u8 msb,
> +			u8 lsb, u8 newval)
> +{
> +	/* Update local copy & write to device */
> +	max2175_set_bits(ctx, idx, msb, lsb, newval);
> +	max2175_reg_write(ctx, idx, ctx->regs[idx]);
> +}
> +
> +static void max2175_write_bit(struct max2175_ctx *ctx, u8 idx, u8 bit,
> +			      u8 newval)
> +{
> +	if (max2175_debug >= 2)
> +		mxm_dbg(ctx, "idx %u, bit %u, newval %u\n", idx, bit, newval);
> +	max2175_write_bits(ctx, idx, bit, bit, newval);
> +}

Really, please use regmap :-)

> +static int max2175_poll_timeout(struct max2175_ctx *ctx, u8 idx, u8 msb, u8
> lsb,
> +				u8 exp_val, u32 timeout)
> +{
> +	unsigned long end = jiffies + msecs_to_jiffies(timeout);
> +	int ret;
> +
> +	do {
> +		ret = max2175_read_bits(ctx, idx, msb, lsb);
> +		if (ret < 0)
> +			return ret;
> +		if (ret == exp_val)
> +			return 0;
> +
> +		usleep_range(1000, 1500);
> +	} while (time_is_after_jiffies(end));
> +
> +	return -EBUSY;
> +}
> +
> +static int max2175_poll_csm_ready(struct max2175_ctx *ctx)
> +{
> +	return max2175_poll_timeout(ctx, 69, 1, 1, 0, 50);

Please define macros for register addresses and values, this is just 
unreadable.

> +}

[snip]

> +
> +#define MAX2175_IS_BAND_AM(ctx)		\
> +	(max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_AM)
> +
> +#define MAX2175_IS_FM_MODE(ctx)		\
> +	(max2175_get_bits(ctx, 12, 5, 4) == 0)
> +
> +#define MAX2175_IS_FMHD_MODE(ctx)	\
> +	(max2175_get_bits(ctx, 12, 5, 4) == 1)
> +
> +#define MAX2175_IS_DAB_MODE(ctx)	\
> +	(max2175_get_bits(ctx, 12, 5, 4) == 2)
> +
> +static int max2175_band_from_freq(u64 freq)
> +{
> +	if (freq >= 144000 && freq <= 26100000)
> +		return MAX2175_BAND_AM;
> +	else if (freq >= 65000000 && freq <= 108000000)
> +		return MAX2175_BAND_FM;
> +	else if (freq >= 160000000 && freq <= 240000000)
> +		return MAX2175_BAND_VHF;
> +
> +	/* Add other bands on need basis */
> +	return -ENOTSUPP;
> +}
> +
> +static int max2175_update_i2s_mode(struct max2175_ctx *ctx, u32 i2s_mode)
> +{
> +	/* Only change if it's new */
> +	if (max2175_read_bits(ctx, 29, 2, 0) == i2s_mode)
> +		return 0;
> +
> +	max2175_write_bits(ctx, 29, 2, 0, i2s_mode);
> +
> +	/* Based on I2S mode value I2S_WORD_CNT values change */
> +	if (i2s_mode == MAX2175_I2S_MODE3) {
> +		max2175_write_bits(ctx, 30, 6, 0, 1);
> +	} else if (i2s_mode == MAX2175_I2S_MODE2 ||
> +		   i2s_mode == MAX2175_I2S_MODE4) {
> +		max2175_write_bits(ctx, 30, 6, 0, 0);
> +	} else if (i2s_mode == MAX2175_I2S_MODE0) {
> +		max2175_write_bits(ctx, 30, 6, 0,
> +				   ctx->rx_modes[ctx->mode].i2s_word_size);
> +	} else {
> +		v4l2_err(ctx->client,
> +			 "failed: i2s_mode %u unsupported\n", i2s_mode);

This should never happen as the control framework will validate control 
values.

> +		return 1;

Error codes should be negative.

> +	}
> +	mxm_dbg(ctx, "updated i2s_mode %u\n", i2s_mode);
> +	return 0;
> +}

[snip]

> +static void max2175_load_dab_1p2(struct max2175_ctx *ctx)
> +{
> +	u32 i;

unsigned int will do, no need for an explicitly sized type.

> +
> +	/* Master is already set on init */
> +	for (i = 0; i < ARRAY_SIZE(dab12_map); i++)
> +		max2175_reg_write(ctx, dab12_map[i].idx, dab12_map[i].val);
> +
> +	/* The default settings assume master */
> +	if (!ctx->master)
> +		max2175_write_bit(ctx, 30, 7, 1);
> +
> +	/* Cache i2s_test value at this point */
> +	ctx->i2s_test = max2175_get_bits(ctx, 104, 3, 0);
> +	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);
> +}

[snip]

> +static bool max2175_set_csm_mode(struct max2175_ctx *ctx,
> +			  enum max2175_csm_mode new_mode)
> +{
> +	int ret = max2175_poll_csm_ready(ctx);
> +
> +	if (ret) {
> +		v4l2_err(ctx->client, "csm not ready: new mode %d\n", 
new_mode);
> +		return ret;
> +	}
> +
> +	mxm_dbg(ctx, "set csm mode: new mode %d\n", new_mode);
> +
> +	max2175_write_bits(ctx, 0, 2, 0, new_mode);
> +
> +	/* Wait for a fixed settle down time depending on new mode and band */
> +	switch (new_mode) {
> +	case MAX2175_CSM_MODE_JUMP_FAST_TUNE:
> +		if (MAX2175_IS_BAND_AM(ctx)) {
> +			usleep_range(1250, 1500);	/* 1.25ms */
> +		} else {
> +			if (MAX2175_IS_DAB_MODE(ctx))
> +				usleep_range(3000, 3500);	/* 3ms */
> +			else
> +				usleep_range(1250, 1500);	/* 1.25ms */
> +		}

You can write this as

		if (MAX2175_IS_BAND_AM(ctx))
			usleep_range(1250, 1500);	/* 1.25ms */
		else if (MAX2175_IS_DAB_MODE(ctx))
			usleep_range(3000, 3500);	/* 3ms */
		else
			usleep_range(1250, 1500);	/* 1.25ms */


> +		break;
> +
> +	/* Other mode switches can be added in the future if needed */
> +	default:
> +		break;
> +	}
> +
> +	ret = max2175_poll_csm_ready(ctx);
> +	if (ret) {
> +		v4l2_err(ctx->client, "csm did not settle: new mode %d\n",
> +			 new_mode);
> +		return ret;
> +	}
> +	return ret;
> +}

[snip]

> +
> +static int max2175_csm_action(struct max2175_ctx *ctx,
> +			      enum max2175_csm_mode action)
> +{
> +	int ret;
> +	int load_buffer = MAX2175_CSM_MODE_LOAD_TO_BUFFER;
> +
> +	mxm_dbg(ctx, "csm action: %d\n", action);
> +
> +	/* Perform one or two CSM mode commands. */
> +	switch (action)	{
> +	case MAX2175_CSM_MODE_NO_ACTION:
> +		/* Take no FSM Action. */
> +		return 0;
> +	case MAX2175_CSM_MODE_LOAD_TO_BUFFER:
> +	case MAX2175_CSM_MODE_PRESET_TUNE:
> +	case MAX2175_CSM_MODE_SEARCH:
> +	case MAX2175_CSM_MODE_AF_UPDATE:
> +	case MAX2175_CSM_MODE_JUMP_FAST_TUNE:
> +	case MAX2175_CSM_MODE_CHECK:
> +	case MAX2175_CSM_MODE_LOAD_AND_SWAP:
> +	case MAX2175_CSM_MODE_END:
> +		ret = max2175_set_csm_mode(ctx, action);
> +		break;
> +	case MAX2175_CSM_MODE_BUFFER_PLUS_PRESET_TUNE:
> +		ret = max2175_set_csm_mode(ctx, load_buffer);
> +		if (ret) {
> +			v4l2_err(ctx->client, "csm action %d load buf 
failed\n",
> +				 action);
> +			break;
> +		}
> +		ret = max2175_set_csm_mode(ctx, MAX2175_CSM_MODE_PRESET_TUNE);
> +		break;
> +	case MAX2175_CSM_MODE_BUFFER_PLUS_SEARCH:
> +		ret = max2175_set_csm_mode(ctx, load_buffer);
> +		if (ret) {
> +			v4l2_err(ctx->client, "csm action %d load buf 
failed\n",
> +				 action);
> +			break;
> +		}

Don't duplicate the error messages, move them after the switch statement.

> +		ret = max2175_set_csm_mode(ctx, MAX2175_CSM_MODE_SEARCH);
> +		break;
> +	case MAX2175_CSM_MODE_BUFFER_PLUS_AF_UPDATE:
> +		ret = max2175_set_csm_mode(ctx, load_buffer);
> +		if (ret) {
> +			v4l2_err(ctx->client, "csm action %d load buf 
failed\n",
> +				 action);
> +			break;
> +		}
> +		ret = max2175_set_csm_mode(ctx, MAX2175_CSM_MODE_AF_UPDATE);
> +		break;
> +	case MAX2175_CSM_MODE_BUFFER_PLUS_JUMP_FAST_TUNE:

This function is only called with action set to 
MAX2175_CSM_MODE_BUFFER_PLUS_JUMP_FAST_TUNE. I'd remove the rest of the code 
for now, unless you have a plan to use it soon.

> +		ret = max2175_set_csm_mode(ctx, load_buffer);
> +		if (ret) {
> +			v4l2_err(ctx->client, "csm action %d load buf 
failed\n",
> +				 action);
> +			break;
> +		}
> +		ret = max2175_set_csm_mode(ctx,
> +					   MAX2175_CSM_MODE_JUMP_FAST_TUNE);
> +		break;
> +	case MAX2175_CSM_MODE_BUFFER_PLUS_CHECK:
> +		ret = max2175_set_csm_mode(ctx, load_buffer);
> +		if (ret) {
> +			v4l2_err(ctx->client, "csm action %d load buf 
failed\n",
> +				 action);
> +			break;
> +		}
> +		ret = max2175_set_csm_mode(ctx, MAX2175_CSM_MODE_CHECK);
> +		break;
> +	case MAX2175_CSM_MODE_BUFFER_PLUS_LOAD_AND_SWAP:
> +		ret = max2175_set_csm_mode(ctx, load_buffer);
> +		if (ret) {
> +			v4l2_err(ctx->client, "csm action %d load buf 
failed\n",
> +				 action);
> +			break;
> +		}
> +		ret = max2175_set_csm_mode(ctx, 
MAX2175_CSM_MODE_LOAD_AND_SWAP);
> +		break;
> +	default:
> +		ret = max2175_set_csm_mode(ctx, MAX2175_CSM_MODE_NO_ACTION);
> +		break;
> +	}
> +	return ret;
> +}
> +
> +static int max2175_set_lo_freq(struct max2175_ctx *ctx, u64 lo_freq)
> +{
> +	int ret;
> +	u32 lo_mult;
> +	u64 scaled_lo_freq;
> +	const u64 scale_factor = 1000000ULL;
> +	u64 scaled_npf, scaled_integer, scaled_fraction;
> +	u32 frac_desired, int_desired;
> +	u8 loband_bits, vcodiv_bits;

Do you really support frequencies above 4GHz ? If not most of the 64-bit 
values could be stored in 32 bits.

> +
> +	scaled_lo_freq = lo_freq;
> +	/* Scale to larger number for precision */
> +	scaled_lo_freq = scaled_lo_freq * scale_factor * 100;
> +
> +	mxm_dbg(ctx, "scaled lo_freq %llu lo_freq %llu\n",
> +		scaled_lo_freq, lo_freq);
> +
> +	if (MAX2175_IS_BAND_AM(ctx)) {
> +		if (max2175_get_bit(ctx, 5, 7) == 0)
> +			loband_bits = 0;
> +			vcodiv_bits = 0;
> +			lo_mult = 16;
> +	} else if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_FM) {
> +		if (lo_freq <= 74700000) {
> +			loband_bits = 0;
> +			vcodiv_bits = 0;
> +			lo_mult = 16;
> +		} else if ((lo_freq > 74700000) && (lo_freq <= 110000000)) {

No need for the inner parentheses.

> +			loband_bits = 1;
> +			vcodiv_bits = 0;
> +		} else {
> +			loband_bits = 1;
> +			vcodiv_bits = 3;
> +		}
> +		lo_mult = 8;
> +	} else if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF) {
> +		if (lo_freq <= 210000000) {
> +			loband_bits = 2;
> +			vcodiv_bits = 2;
> +		} else {
> +			loband_bits = 2;
> +			vcodiv_bits = 1;
> +		}
> +		lo_mult = 4;
> +	} else {
> +		loband_bits = 3;
> +		vcodiv_bits = 2;
> +		lo_mult = 2;
> +	}
> +
> +	if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_L)
> +		scaled_npf = (scaled_lo_freq / ctx->xtal_freq / lo_mult) / 
100;
> +	else
> +		scaled_npf = (scaled_lo_freq / ctx->xtal_freq * lo_mult) / 
100;
> +
> +	scaled_integer = scaled_npf / scale_factor * scale_factor;
> +	int_desired = (u32)(scaled_npf / scale_factor);
> +	scaled_fraction = scaled_npf - scaled_integer;
> +	frac_desired = (u32)(scaled_fraction * 1048576 / scale_factor);
> +
> +	/* Check CSM is not busy */
> +	ret = max2175_poll_csm_ready(ctx);
> +	if (ret) {
> +		v4l2_err(ctx->client, "lo_freq: csm busy. freq %llu\n",
> +			 lo_freq);
> +		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_set_bits(ctx, 5, 3, 2, loband_bits);
> +	max2175_set_bits(ctx, 6, 7, 6, vcodiv_bits);
> +	max2175_set_bits(ctx, 1, 7, 0, (u8)(int_desired & 0xff));
> +	max2175_set_bits(ctx, 2, 3, 0, (u8)((frac_desired >> 16) & 0x1f));
> +	max2175_set_bits(ctx, 3, 7, 0, (u8)((frac_desired >> 8) & 0xff));
> +	max2175_set_bits(ctx, 4, 7, 0, (u8)(frac_desired & 0xff));
> +	/* Flush the above registers to device */
> +	max2175_flush_regstore(ctx, 1, 6);
> +	return ret;
> +}

[snip]

> +static int max2175_set_rf_freq_non_am_bands(struct max2175_ctx *ctx, u64
> freq,
> +					    u32 lo_pos)
> +{
> +	int ret;
> +	s64 adj_freq;
> +	u64 low_if_freq;
> +
> +	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_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF) {

You perform this check in multiple places, you could create a helper function.

> +		if (lo_pos == MAX2175_LO_ABOVE_DESIRED)
> +			adj_freq = freq + low_if_freq;
> +		else
> +			adj_freq = freq - low_if_freq;
> +	} else {
> +		if (lo_pos == MAX2175_LO_ABOVE_DESIRED)
> +			adj_freq = freq - low_if_freq;
> +		else
> +			adj_freq = freq + low_if_freq;
> +	}

This could be written

	if ((max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF) ==
	    (lo_pos == MAX2175_LO_ABOVE_DESIRED))
		adj_freq = freq + low_if_freq;
	else
		adj_freq = freq - low_if_freq;

Same for the other similar constructs in the driver. Up to you.

> +
> +	ret = max2175_set_lo_freq(ctx, adj_freq);
> +	if (ret)
> +		return ret;
> +
> +	return max2175_set_nco_freq(ctx, low_if_freq);
> +}

[snip]

> +#define max2175_ctx_from_sd(x)	\
> +	container_of(x, struct max2175_ctx, sd)
> +#define max2175_ctx_from_ctrl(x)	\
> +	container_of(x, struct max2175_ctx, ctrl_hdl)

I'd move it right after the structure definition, and turn them into static 
inline functions.

> +static int max2175_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct max2175_ctx *ctx = max2175_ctx_from_ctrl(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_EN:
> +		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_AM_HIZ:
> +		v4l2_ctrl_activate(ctx->am_hiz, false);
> +		break;
> +	case V4L2_CID_MAX2175_HSLS:
> +		v4l2_ctrl_activate(ctx->hsls, false);
> +		break;
> +	case V4L2_CID_MAX2175_RX_MODE:
> +		mxm_dbg(ctx, "rx mode %u\n", ctrl->val);
> +		max2175_s_ctrl_rx_mode(ctx, ctrl->val);
> +		break;
> +	default:
> +		v4l2_err(ctx->client, "s:invalid ctrl id 0x%x\n", ctrl->id);
> +		ret = -EINVAL;

This should never happen. The driver has too many error and debug messages 
overall.

> +	}
> +
> +	return ret;
> +}
> +
> +static int max2175_get_lna_gain(struct max2175_ctx *ctx)
> +{
> +	int gain = 0;
> +	enum max2175_band band = max2175_get_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:
> +		v4l2_err(ctx->client, "invalid band %d to get rf gain\n", 
band);

Can this happen ?

> +		break;
> +	}
> +	return gain;
> +}
> +
> +static int max2175_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct max2175_ctx *ctx = max2175_ctx_from_ctrl(ctrl->handler);
> +
> +	/* Only the dynamically changing values need to be in g_volatile_ctrl 
*/
> +	mxm_dbg(ctx, "g_volatile_ctrl: id 0x%x\n", ctrl->id);
> +	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;
> +	default:
> +		v4l2_err(ctx->client, "g:invalid ctrl id 0x%x\n", ctrl->id);

This should never happen either.

> +		return -EINVAL;
> +	}
> +	mxm_dbg(ctx, "g_volatile_ctrl: id 0x%x val %d\n", ctrl->id, ctrl-
>val);
> +	return 0;
> +};

[snip]

> +static const struct v4l2_ctrl_config max2175_i2s_en = {
> +	.ops = &max2175_ctrl_ops,
> +	.id = V4L2_CID_MAX2175_I2S_EN,

V4L2_CID_MAX2175_I2S_ENABLE ?

> +	.name = "I2S Enable",
> +	.type = V4L2_CTRL_TYPE_BOOLEAN,
> +	.min = 0,
> +	.max = 1,
> +	.step = 1,
> +	.def = 1,
> +};
> +
> +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_INTEGER,

Should this be a menu control ?

> +	.min = 0,
> +	.max = 4,
> +	.step = 1,
> +	.def = 0,
> +};
> +
> +static const struct v4l2_ctrl_config max2175_am_hiz = {
> +	.ops = &max2175_ctrl_ops,
> +	.id = V4L2_CID_MAX2175_AM_HIZ,
> +	.name = "AM High impedance input",
> +	.type = V4L2_CTRL_TYPE_BOOLEAN,
> +	.min = 0,
> +	.max = 1,
> +	.step = 1,
> +	.def = 0,
> +};
> +
> +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,
> +};
> +
> +
> +/* NOTE: Any addition/deletion in the below list should be reflected in
> + * max2175_modetag enum
> + */
> +static const char * const max2175_ctrl_eu_rx_mode_strings[] = {
> +	"DAB 1.2",
> +	"NULL",

Do you really mean "NULL", not NULL ?

> +};
> +
> +static const char * const max2175_ctrl_na_rx_mode_strings[] = {
> +	"NA FM 1.0",
> +	"NULL",
> +};
> +
> +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_mode_strings) - 2,

If there's a single mode supported I'd skip adding those controls for now.

> +	.def = 0,
> +	.qmenu = max2175_ctrl_eu_rx_mode_strings,
> +};
> +
> +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_mode_strings) - 2,
> +	.def = 0,
> +	.qmenu = max2175_ctrl_na_rx_mode_strings,
> +};
> +
> +static u32 max2175_refout_load_to_bits(struct i2c_client *client, u32 load)
> +{
> +	u32 bits = 0;	/* REFOUT disabled */
> +
> +	if (load >= 0 && load <= 40)
> +		bits = load / 10;
> +	else if (load >= 60 && load <= 70)
> +		bits = load / 10 - 1;
> +	else
> +		dev_warn(&client->dev, "invalid refout_load %u\n", load);

Your DT bindings specify 0 as a valid value.

An invalid value specified in DT should be a fatal error.

> +
> +	return bits;
> +}
> +
> +static int max2175_probe(struct i2c_client *client,
> +			const struct i2c_device_id *id)
> +{
> +	struct max2175_ctx *ctx;
> +	struct device *dev = &client->dev;
> +	struct v4l2_subdev *sd;
> +	struct v4l2_ctrl_handler *hdl;
> +	struct clk *clk;
> +	bool master = true;
> +	u32 refout_load, refout_bits = 0;	/* REFOUT disabled */
> +	int ret;
> +
> +	/* Check if the adapter supports the needed features */
> +	if (!i2c_check_functionality(client->adapter,
> +				     I2C_FUNC_SMBUS_BYTE_DATA)) {
> +		dev_err(&client->dev, "i2c check failed\n");
> +		return -EIO;
> +	}
> +
> +	if (of_find_property(client->dev.of_node, "maxim,slave", NULL))
> +		master = false;
> +
> +	if (!of_property_read_u32(client->dev.of_node, "maxim,refout-load",
> +				 &refout_load))
> +		refout_bits = max2175_refout_load_to_bits(client, 
refout_load);
> +
> +	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;
> +	}
> +
> +	ctx = kzalloc(sizeof(struct max2175_ctx),

sizeof(*ctx)

> +			     GFP_KERNEL);

This fits on one line.

> +	if (ctx == NULL)
> +		return -ENOMEM;
> +
> +	sd = &ctx->sd;
> +	ctx->master = master;
> +	ctx->mode_resolved = false;
> +
> +	/* Set the defaults */
> +	ctx->freq = bands_rf.rangelow;
> +
> +	ctx->xtal_freq = clk_get_rate(clk);
> +	dev_info(&client->dev, "xtal freq %lluHz\n", ctx->xtal_freq);
> +
> +	v4l2_i2c_subdev_init(sd, client, &max2175_ops);
> +	ctx->client = client;
> +
> +	sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> +	ctx->dev = dev;
> +
> +	/* Controls */
> +	hdl = &ctx->ctrl_hdl;
> +	ret = v4l2_ctrl_handler_init(hdl, 8);
> +	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->am_hiz = v4l2_ctrl_new_custom(hdl, &max2175_am_hiz, 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 = (struct max2175_rxmode *)eu_rx_modes;
> +	} else {
> +		ctx->rx_mode = v4l2_ctrl_new_custom(hdl,
> +						    &max2175_na_rx_mode, 
NULL);
> +		ctx->rx_modes = (struct max2175_rxmode *)na_rx_modes;
> +	}
> +	ctx->sd.ctrl_handler = &ctx->ctrl_hdl;
> +
> +	ret = v4l2_async_register_subdev(sd);
> +	if (ret) {
> +		dev_err(&client->dev, "register subdev failed\n");
> +		goto err_reg;
> +	}
> +	dev_info(&client->dev, "subdev registered\n");
> +
> +	/* Initialize device */
> +	ret = max2175_core_init(ctx, refout_bits);
> +	if (ret)
> +		goto err_init;
> +
> +	mxm_dbg(ctx, "probed\n");
> +	return 0;
> +
> +err_init:
> +	v4l2_async_unregister_subdev(sd);
> +err_reg:
> +	v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
> +err:
> +	kfree(ctx);
> +	return ret;
> +}
> +
> +static int max2175_remove(struct i2c_client *client)
> +{
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct max2175_ctx *ctx = max2175_ctx_from_sd(sd);
> +
> +	v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
> +	v4l2_async_unregister_subdev(sd);
> +	mxm_dbg(ctx, "removed\n");
> +	kfree(ctx);
> +	return 0;
> +}
> +
> +static const struct i2c_device_id max2175_id[] = {
> +	{ DRIVER_NAME, 0},
> +	{},
> +};
> +

No need for a blank line here.

> +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..61a508b
> --- /dev/null
> +++ b/drivers/media/i2c/max2175/max2175.h
> @@ -0,0 +1,124 @@
> +/*
> + * 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__
> +
> +#include <linux/types.h>
> +
> +enum max2175_region {
> +	MAX2175_REGION_EU = 0,	/* Europe */
> +	MAX2175_REGION_NA,	/* North America */
> +};
> +
> +#define MAX2175_EU_XTAL_FREQ	(36864000)	/* In Hz */
> +#define MAX2175_NA_XTAL_FREQ	(40186125)	/* In Hz */
> +
> +enum max2175_band {
> +	MAX2175_BAND_AM = 0,
> +	MAX2175_BAND_FM,
> +	MAX2175_BAND_VHF,
> +	MAX2175_BAND_L,
> +};
> +
> +/* NOTE: Any addition/deletion in the below enum should be reflected in
> + * V4L2_CID_MAX2175_RX_MODE ctrl strings
> + */
> +enum max2175_modetag {
> +	/* EU modes */
> +	MAX2175_DAB_1_2 = 0,
> +
> +	/* Other possible modes to add in future
> +	 * MAX2175_DAB_1_0,
> +	 * MAX2175_DAB_1_3,
> +	 * MAX2175_EU_FM_2_2,
> +	 * MAX2175_EU_FM_1_0,
> +	 * MAX2175_EU_FMHD_4_0,
> +	 * MAX2175_EU_AM_1_0,
> +	 * MAX2175_EU_AM_2_2,
> +	 */
> +
> +	/* NA modes */
> +	MAX2175_NA_FM_1_0 = 0,
> +
> +	/* Other possible modes to add in future
> +	 * MAX2175_NA_FM_1_2,
> +	 * 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_CSM_MODE_LOAD_TO_BUFFER = 0,
> +	MAX2175_CSM_MODE_PRESET_TUNE,
> +	MAX2175_CSM_MODE_SEARCH,
> +	MAX2175_CSM_MODE_AF_UPDATE,
> +	MAX2175_CSM_MODE_JUMP_FAST_TUNE,
> +	MAX2175_CSM_MODE_CHECK,
> +	MAX2175_CSM_MODE_LOAD_AND_SWAP,
> +	MAX2175_CSM_MODE_END,
> +	MAX2175_CSM_MODE_BUFFER_PLUS_PRESET_TUNE,
> +	MAX2175_CSM_MODE_BUFFER_PLUS_SEARCH,
> +	MAX2175_CSM_MODE_BUFFER_PLUS_AF_UPDATE,
> +	MAX2175_CSM_MODE_BUFFER_PLUS_JUMP_FAST_TUNE,
> +	MAX2175_CSM_MODE_BUFFER_PLUS_CHECK,
> +	MAX2175_CSM_MODE_BUFFER_PLUS_LOAD_AND_SWAP,
> +	MAX2175_CSM_MODE_NO_ACTION
> +};
> +
> +/* 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 */
> +struct max2175_regmap {
> +	u8 idx;				/* Register index */
> +	u8 val;				/* Register value */
> +};

As no other file than max2175.c includes this, I would move at least the 
structure definitions to the .c file.

> +#endif /* __MAX2175_H__ */

-- 
Regards,

Laurent Pinchart

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

* RE: [RFC 3/5] media: platform: rcar_drif: Add DRIF support
  2016-10-18 14:29   ` Geert Uytterhoeven
@ 2016-10-21 13:15         ` Ramesh Shanmugasundaram
       [not found]     ` <CAMuHMdXvGEm3bdNOsa6Q1FLB9yMSTAzO4nHcCb-pnYYwg6f6Cg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
  1 sibling, 0 replies; 39+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-10-21 13:15 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Rob Herring, Mark Rutland, Mauro Carvalho Chehab, Hans Verkuil,
	Sakari Ailus, Antti Palosaari, Chris Paterson, Laurent Pinchart,
	Linux Media Mailing List, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Linux-Renesas

Hi Geert,

Thank you for the review comments.

> On Wed, Oct 12, 2016 at 4:10 PM, Ramesh Shanmugasundaram
> <ramesh.shanmugasundaram@bp.renesas.com> 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.
> 
> Thanks for your patch!
> 
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> > @@ -0,0 +1,109 @@
> > +Renesas R-Car Gen3 DRIF controller (DRIF)
> > +-----------------------------------------
> > +
> > +Required properties:
> > +--------------------
> > +- compatible: "renesas,drif-r8a7795" if DRIF controller is a part of
> R8A7795 SoC.
> 
> "renesas,r8a7795-drif", as Rob already pointed out.

Agreed.

> 
> > +             "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 each sub-channel.
> > +- interrupts: associated with each sub-channel.
> > +- clocks: phandles and clock specifiers for each sub-channel.
> > +- clock-names: clock input name strings: "fck0", "fck1".
> > +- pinctrl-0: pin control group to be used for this controller.
> > +- pinctrl-names: must be "default".
> > +- dmas: phandles to the DMA channels for each sub-channel.
> > +- dma-names: names for the DMA channels: "rx0", "rx1".
> > +
> > +Required child nodes:
> > +---------------------
> > +- Each DRIF channel can have one or both of the sub-channels enabled
> > +in a
> > +  setup. The sub-channels are represented as a child node. The name
> > +of the
> > +  child nodes are "sub-channel0" and "sub-channel1" respectively.
> > +Each child
> > +  node supports the "status" property only, which is used to
> > +enable/disable
> > +  the respective sub-channel.
> 
> > +Example
> > +--------
> > +
> > +SoC common dtsi file
> > +
> > +drif0: rif@e6f40000 {
> > +       compatible = "renesas,drif-r8a7795",
> > +                  "renesas,rcar-gen3-drif";
> > +       reg = <0 0xe6f40000 0 0x64>, <0 0xe6f50000 0 0x64>;
> > +       interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>,
> > +                  <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
> > +       clocks = <&cpg CPG_MOD 515>, <&cpg CPG_MOD 514>;
> > +       clock-names = "fck0", "fck1";
> > +       dmas = <&dmac1 0x20>, <&dmac1 0x22>;
> > +       dma-names = "rx0", "rx1";
> 
> I could not find the DMAC channels in the datasheet?

It is mentioned only in v0.5 h/w manual. v0.52 manual introduced DRIF chapter but then some of the old references were missing :-(. There are few more doc anomalies, which I shall document in the next version of the patch.

> Most modules are either tied to dmac0, or two both dmac1 and dmac2.
> In the latter case, you want to list two sets of dmas, one for each DMAC.

You are right. I have added both dmac1 & 2 now.

> 
> > +       power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> > +       status = "disabled";
> > +
> > +       sub-channel0 {
> > +               status = "disabled";
> > +       };
> > +
> > +       sub-channel1 {
> > +               status = "disabled";
> > +       };
> > +
> > +};
> 
> As you're modelling this in DT under a single device node, this means you
> cannot use runtime PM to manage the module clocks of the individual
> channels.
> 
> An alternative could be to have two separate nodes for each channel, and
> tie them together using a phandle.

I agree & thanks for the suggestion. Is the below model looks anything closer? Appreciate your inputs.

dtsi
-------

                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";
                        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";
                        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 dts file would have something like this

&drif00 {
        status = "okay";
};      
        
&drif01 {
        status = "okay";
};      
        
&drif0 {
        pinctrl-0 = <&drif0_pins>;
        pinctrl-names = "default";
        status = "okay";

        port {
                drif0_ep: endpoint {
                     remote-endpoint = <&max2175_0_ep>;
                };
        };
};
-------
The drif0X driver instance will help only in registering with genpd.
The parent drif0 instance will parse "sub-channels" phandles and use the resources of respective enabled sub-channels using it's pdev. 

Thanks,
Ramesh


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

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

Hi Geert,

Thank you for the review comments.

> On Wed, Oct 12, 2016 at 4:10 PM, Ramesh Shanmugasundaram
> <ramesh.shanmugasundaram@bp.renesas.com> 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.
> 
> Thanks for your patch!
> 
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> > @@ -0,0 +1,109 @@
> > +Renesas R-Car Gen3 DRIF controller (DRIF)
> > +-----------------------------------------
> > +
> > +Required properties:
> > +--------------------
> > +- compatible: "renesas,drif-r8a7795" if DRIF controller is a part of
> R8A7795 SoC.
> 
> "renesas,r8a7795-drif", as Rob already pointed out.

Agreed.

> 
> > +             "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 each sub-channel.
> > +- interrupts: associated with each sub-channel.
> > +- clocks: phandles and clock specifiers for each sub-channel.
> > +- clock-names: clock input name strings: "fck0", "fck1".
> > +- pinctrl-0: pin control group to be used for this controller.
> > +- pinctrl-names: must be "default".
> > +- dmas: phandles to the DMA channels for each sub-channel.
> > +- dma-names: names for the DMA channels: "rx0", "rx1".
> > +
> > +Required child nodes:
> > +---------------------
> > +- Each DRIF channel can have one or both of the sub-channels enabled
> > +in a
> > +  setup. The sub-channels are represented as a child node. The name
> > +of the
> > +  child nodes are "sub-channel0" and "sub-channel1" respectively.
> > +Each child
> > +  node supports the "status" property only, which is used to
> > +enable/disable
> > +  the respective sub-channel.
> 
> > +Example
> > +--------
> > +
> > +SoC common dtsi file
> > +
> > +drif0: rif@e6f40000 {
> > +       compatible = "renesas,drif-r8a7795",
> > +                  "renesas,rcar-gen3-drif";
> > +       reg = <0 0xe6f40000 0 0x64>, <0 0xe6f50000 0 0x64>;
> > +       interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>,
> > +                  <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
> > +       clocks = <&cpg CPG_MOD 515>, <&cpg CPG_MOD 514>;
> > +       clock-names = "fck0", "fck1";
> > +       dmas = <&dmac1 0x20>, <&dmac1 0x22>;
> > +       dma-names = "rx0", "rx1";
> 
> I could not find the DMAC channels in the datasheet?

It is mentioned only in v0.5 h/w manual. v0.52 manual introduced DRIF chapter but then some of the old references were missing :-(. There are few more doc anomalies, which I shall document in the next version of the patch.

> Most modules are either tied to dmac0, or two both dmac1 and dmac2.
> In the latter case, you want to list two sets of dmas, one for each DMAC.

You are right. I have added both dmac1 & 2 now.

> 
> > +       power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> > +       status = "disabled";
> > +
> > +       sub-channel0 {
> > +               status = "disabled";
> > +       };
> > +
> > +       sub-channel1 {
> > +               status = "disabled";
> > +       };
> > +
> > +};
> 
> As you're modelling this in DT under a single device node, this means you
> cannot use runtime PM to manage the module clocks of the individual
> channels.
> 
> An alternative could be to have two separate nodes for each channel, and
> tie them together using a phandle.

I agree & thanks for the suggestion. Is the below model looks anything closer? Appreciate your inputs.

dtsi
-------

                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";
                        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";
                        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 dts file would have something like this

&drif00 {
        status = "okay";
};      
        
&drif01 {
        status = "okay";
};      
        
&drif0 {
        pinctrl-0 = <&drif0_pins>;
        pinctrl-names = "default";
        status = "okay";

        port {
                drif0_ep: endpoint {
                     remote-endpoint = <&max2175_0_ep>;
                };
        };
};
-------
The drif0X driver instance will help only in registering with genpd.
The parent drif0 instance will parse "sub-channels" phandles and use the resources of respective enabled sub-channels using it's pdev. 

Thanks,
Ramesh


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

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

Hi Laurent,

Thank you for the review comments.

> On Tuesday 18 Oct 2016 16:29:24 Geert Uytterhoeven wrote:
> > On Wed, Oct 12, 2016 at 4:10 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.
> >
> > Thanks for your patch!
> >
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> > > @@ -0,0 +1,109 @@
> > > +Renesas R-Car Gen3 DRIF controller (DRIF)
> > > +-----------------------------------------
> > > +
> > > +Required properties:
> > > +--------------------
> > > +- compatible: "renesas,drif-r8a7795" if DRIF controller is a part
> > > +of
> > > R8A7795 SoC.
> >
> > "renesas,r8a7795-drif", as Rob already pointed out.
> >
> > > +             "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 each sub-channel.
> > > +- interrupts: associated with each sub-channel.
> > > +- clocks: phandles and clock specifiers for each sub-channel.
> > > +- clock-names: clock input name strings: "fck0", "fck1".
> > > +- pinctrl-0: pin control group to be used for this controller.
> > > +- pinctrl-names: must be "default".
> > > +- dmas: phandles to the DMA channels for each sub-channel.
> > > +- dma-names: names for the DMA channels: "rx0", "rx1".
> > > +
> > > +Required child nodes:
> > > +---------------------
> > > +- Each DRIF channel can have one or both of the sub-channels
> > > +enabled in a
> > > +  setup. The sub-channels are represented as a child node. The name
> > > +of
> > > the
> > > +  child nodes are "sub-channel0" and "sub-channel1" respectively.
> > > + Each
> > > child
> > > +  node supports the "status" property only, which is used to
> > > enable/disable
> > > +  the respective sub-channel.
> > >
> > > +Example
> > > +--------
> > > +
> > > +SoC common dtsi file
> > > +
> > > +drif0: rif@e6f40000 {
> > > +       compatible = "renesas,drif-r8a7795",
> > > +                  "renesas,rcar-gen3-drif";
> > > +       reg = <0 0xe6f40000 0 0x64>, <0 0xe6f50000 0 0x64>;
> > > +       interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>,
> > > +                  <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
> > > +       clocks = <&cpg CPG_MOD 515>, <&cpg CPG_MOD 514>;
> > > +       clock-names = "fck0", "fck1";
> > > +       dmas = <&dmac1 0x20>, <&dmac1 0x22>;
> > > +       dma-names = "rx0", "rx1";
> >
> > I could not find the DMAC channels in the datasheet?
> > Most modules are either tied to dmac0, or two both dmac1 and dmac2.
> > In the latter case, you want to list two sets of dmas, one for each
> DMAC.
> >
> > > +       power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> > > +       status = "disabled";
> > > +
> > > +       sub-channel0 {
> > > +               status = "disabled";
> > > +       };
> > > +
> > > +       sub-channel1 {
> > > +               status = "disabled";
> > > +       };
> > > +
> > > +};
> >
> > As you're modelling this in DT under a single device node, this means
> > you cannot use runtime PM to manage the module clocks of the
> > individual channels.
> >
> > An alternative could be to have two separate nodes for each channel,
> > and tie them together using a phandle.
> 
> A quick glance at the documentation shows no dependency between the two
> channels at a software level. They both share the same clock and
> synchronization input pins, but from a hardware point of view that seems
> to be it. It thus looks like we could indeed model the two channels as
> separate nodes, without tying them together.

Thanks & I agree with your suggestion to keep each DRIF sub-channels as separate node. However, I would still like to tie them as Geert pointed out because

1) DRIF, as you may know, is a botched MSIOF slave. H/W manual states there are 4 channels each having SCK, WS & two data lines D0 & D1) but in reality each sub-channel (SCK,WS & Dx) can indeed act alone (they have their own resources) except that they share SCK & WS as you pointed out. It is not clean design but it tries to market a radio interface use case - I & Q data from master received on D0 & D1 and this data is relational. For the master and the end user application, this internal split is not visible.

2) If you look at PFC, when a DRIF channel is selected using MOD_SEL it enables both the sub-channels. This shows the intended use case. Ofcourse the imperfection cannot be hidden - you could still configure one of the D0/D1 pins as GPIO even after PFC selects them both :-(. 

3) Manual states "When setting to registers, it needs to set the same values to two register sets located per channel" - This driver is doing that by accessing each sub-channel based on DT setup. This way driver exposes one interface per channel and provides user the data from two data pins as if it comes from one device interface.

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] 39+ messages in thread

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

Hi Laurent,

Thank you for the review comments.

> On Tuesday 18 Oct 2016 16:29:24 Geert Uytterhoeven wrote:
> > On Wed, Oct 12, 2016 at 4:10 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.
> >
> > Thanks for your patch!
> >
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
> > > @@ -0,0 +1,109 @@
> > > +Renesas R-Car Gen3 DRIF controller (DRIF)
> > > +-----------------------------------------
> > > +
> > > +Required properties:
> > > +--------------------
> > > +- compatible: "renesas,drif-r8a7795" if DRIF controller is a part
> > > +of
> > > R8A7795 SoC.
> >
> > "renesas,r8a7795-drif", as Rob already pointed out.
> >
> > > +             "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 each sub-channel.
> > > +- interrupts: associated with each sub-channel.
> > > +- clocks: phandles and clock specifiers for each sub-channel.
> > > +- clock-names: clock input name strings: "fck0", "fck1".
> > > +- pinctrl-0: pin control group to be used for this controller.
> > > +- pinctrl-names: must be "default".
> > > +- dmas: phandles to the DMA channels for each sub-channel.
> > > +- dma-names: names for the DMA channels: "rx0", "rx1".
> > > +
> > > +Required child nodes:
> > > +---------------------
> > > +- Each DRIF channel can have one or both of the sub-channels
> > > +enabled in a
> > > +  setup. The sub-channels are represented as a child node. The name
> > > +of
> > > the
> > > +  child nodes are "sub-channel0" and "sub-channel1" respectively.
> > > + Each
> > > child
> > > +  node supports the "status" property only, which is used to
> > > enable/disable
> > > +  the respective sub-channel.
> > >
> > > +Example
> > > +--------
> > > +
> > > +SoC common dtsi file
> > > +
> > > +drif0: rif@e6f40000 {
> > > +       compatible = "renesas,drif-r8a7795",
> > > +                  "renesas,rcar-gen3-drif";
> > > +       reg = <0 0xe6f40000 0 0x64>, <0 0xe6f50000 0 0x64>;
> > > +       interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>,
> > > +                  <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
> > > +       clocks = <&cpg CPG_MOD 515>, <&cpg CPG_MOD 514>;
> > > +       clock-names = "fck0", "fck1";
> > > +       dmas = <&dmac1 0x20>, <&dmac1 0x22>;
> > > +       dma-names = "rx0", "rx1";
> >
> > I could not find the DMAC channels in the datasheet?
> > Most modules are either tied to dmac0, or two both dmac1 and dmac2.
> > In the latter case, you want to list two sets of dmas, one for each
> DMAC.
> >
> > > +       power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
> > > +       status = "disabled";
> > > +
> > > +       sub-channel0 {
> > > +               status = "disabled";
> > > +       };
> > > +
> > > +       sub-channel1 {
> > > +               status = "disabled";
> > > +       };
> > > +
> > > +};
> >
> > As you're modelling this in DT under a single device node, this means
> > you cannot use runtime PM to manage the module clocks of the
> > individual channels.
> >
> > An alternative could be to have two separate nodes for each channel,
> > and tie them together using a phandle.
> 
> A quick glance at the documentation shows no dependency between the two
> channels at a software level. They both share the same clock and
> synchronization input pins, but from a hardware point of view that seems
> to be it. It thus looks like we could indeed model the two channels as
> separate nodes, without tying them together.

Thanks & I agree with your suggestion to keep each DRIF sub-channels as separate node. However, I would still like to tie them as Geert pointed out because

1) DRIF, as you may know, is a botched MSIOF slave. H/W manual states there are 4 channels each having SCK, WS & two data lines D0 & D1) but in reality each sub-channel (SCK,WS & Dx) can indeed act alone (they have their own resources) except that they share SCK & WS as you pointed out. It is not clean design but it tries to market a radio interface use case - I & Q data from master received on D0 & D1 and this data is relational. For the master and the end user application, this internal split is not visible.

2) If you look at PFC, when a DRIF channel is selected using MOD_SEL it enables both the sub-channels. This shows the intended use case. Ofcourse the imperfection cannot be hidden - you could still configure one of the D0/D1 pins as GPIO even after PFC selects them both :-(. 

3) Manual states "When setting to registers, it needs to set the same values to two register sets located per channel" - This driver is doing that by accessing each sub-channel based on DT setup. This way driver exposes one interface per channel and provides user the data from two data pins as if it comes from one device interface.

Thanks,
Ramesh

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

* RE: [RFC 1/5] media: i2c: max2175: Add MAX2175 support
  2016-10-18 19:25   ` Laurent Pinchart
@ 2016-10-21 14:49     ` Ramesh Shanmugasundaram
  2016-11-10  8:46       ` Laurent Pinchart
  0 siblings, 1 reply; 39+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-10-21 14:49 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope,
	Chris Paterson, geert, linux-media, devicetree,
	linux-renesas-soc

Hi Laurent,

Thank you for the review comments.

> On Wednesday 12 Oct 2016 15:10:25 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      |   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                | 1624
> +++++++++++++++++
> >  drivers/media/i2c/max2175/max2175.h                |  124 ++
> >  7 files changed, 1826 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..2250d5f
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/i2c/max2175.txt
> > @@ -0,0 +1,60 @@
> > +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: video interface child port node of a tuner that defines the
> > +local
> 
> As Rob pointed out in his review of patch 3/5, this isn't video.

Agreed & corrected.

> 
> > +  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.
> 
> Would it be useful to make that property a phandle to the master tuner, to
> give drivers a way to access the master ? I haven't checked whether there
> could be use cases for that.

As of now, I cannot find any use case for it from the datasheet. In future, we could add if such need arise.

> 
> > +- maxim,refout-load: load capacitance value (in pF) on reference output
> > +		     drive level. The mapping of these load values to
> > +		     respective bit values are given below.
> > +		     0 - Reference output disabled
> > +		     1 - 10pF load
> > +		     2 - 20pF load
> > +		     3 - 30pF load
> > +		     4 - 40pF load
> > +		     5 - 60pF load
> > +		     6 - 70pF load
> 
> As Geert pointed out, you can simply specify the value in pF.

Agreed & corrected.

> 
> > +
> > +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 {
> > +	#clock-cells = <0>;
> 
> Is the tuner a clock provider ? If it isn't you don't need this property.

Thanks. It's a copy/paste mistake :-(. Corrected.

> 
> > +	compatible = "maxim,max2175";
> > +	reg = <0x60>;
> > +	clocks = <&maxim_xtal>;
> > +	clock-names = "xtal";
> > +	maxim,refout-load = <10>;
> > +
> > +	port {
> > +		max2175_0_ep: endpoint {
> > +			remote-endpoint = <&slave_rx_v4l2_sdr_device>;
> > +		};
> > +	};
> > +
> > +};
> 
> [snip]
> 
> > 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
> 
> If there's a single source file you might want to move it to
> drivers/media/i2c/.

MAX2175 is huge with lot more modes and functionality. When more modes are added (it's pre-set hex values), we may have to introduce the new file containing that the hex values alone. Hence, I thought of a folder. However, I cannot tell when the next set of modes will be added. So I'll remove the folder in the next patch.

> 
> > diff --git a/drivers/media/i2c/max2175/max2175.c
> > b/drivers/media/i2c/max2175/max2175.c new file mode 100644 index
> > 0000000..71b60c2
> > --- /dev/null
> > +++ b/drivers/media/i2c/max2175/max2175.c
> > @@ -0,0 +1,1624 @@
> > +/*
> > + * 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/errno.h>
> > +#include <linux/i2c.h>
> > +#include <linux/init.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/slab.h>
> > +#include <linux/delay.h>
> 
> You can move delay.h right below clk.h and everything will be in
> alphabetical order :-)

Agreed.

> 
> > +#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 max2175_debug;
> > +module_param(max2175_debug, uint, 0644);
> > +MODULE_PARM_DESC(max2175_debug, "activate debug info");
> 
> You can name the parameter "debug".

Agreed

> 
> > +#define mxm_dbg(ctx, fmt, arg...) \
> > +	v4l2_dbg(1, max2175_debug, &ctx->sd, fmt, ## arg)
> 
> [snip]
> 
> > +/* Preset values:
> > + * Based on Maxim MAX2175 Register Table revision: 130p10  */
> 
> The preferred multi-line comment style is
> 
> /*
>  * foo
>  * bar
>  */
> 
Agreed. I used it because checkpatch moaned but then I see Linus's comment :-).

> [snip]
> 
> > +struct max2175_ctx {
> 
> Nitpicking, such a structure would usually be named max2175 or
> max2175_device.
> Context seems to imply that you can have multiple of them per device.

Thanks for the explanation. I have changed it to max2175.

> 
> > +	struct v4l2_subdev sd;
> > +	struct i2c_client *client;
> > +	struct device *dev;
> > +
> > +	/* Cached configuration */
> > +	u8 regs[256];
> 
> If you want to cache register values you should use regmap.

Thanks. I did not know about regmap. I'll try this.

> 
> > +	enum max2175_modetag mode;	/* Receive mode tag */
> > +	u32 freq;			/* In Hz */
> > +	struct max2175_rxmode *rx_modes;
> > +
> > +	/* Device settings */
> > +	bool master;
> > +	u32 decim_ratio;
> > +	u64 xtal_freq;
> > +
> > +	/* ROM values */
> > +	u8 rom_bbf_bw_am;
> > +	u8 rom_bbf_bw_fm;
> > +	u8 rom_bbf_bw_dab;
> > +
> > +	/* Local copy of old settings */
> > +	u8 i2s_test;
> > +
> > +	u8 nbd_gain;
> > +	u8 nbd_threshold;
> > +	u8 wbd_gain;
> > +	u8 wbd_threshold;
> > +	u8 bbd_threshold;
> > +	u8 bbdclip_threshold;
> > +	u8 lt_wbd_threshold;
> > +	u8 lt_wbd_gain;
> > +
> > +	/* 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 *am_hiz;	/* AM High impledance input */
> > +	struct v4l2_ctrl *hsls;		/* High-side/Low-side polarity */
> > +	struct v4l2_ctrl *rx_mode;	/* Receive mode */
> > +
> > +	/* Driver private variables */
> > +	bool mode_resolved;		/* Flag to sanity check settings */
> > +};
> 
> [snip]
> 
> > +/* Local store bitops helpers */
> > +static u8 max2175_get_bits(struct max2175_ctx *ctx, u8 idx, u8 msb,
> > +u8 lsb) {
> > +	if (max2175_debug >= 2)
> > +		mxm_dbg(ctx, "get_bits: idx:%u msb:%u lsb:%u\n",
> > +			idx, msb, lsb);
> 
> Do we really need such detailed debugging ?

Unfortunately yes. I'll try to remove this after some more testing or may be later as a separate patch.
I have converted a Windows GUI app code to this driver. Most of the register details & configuration sequence are not available in the datasheet. I used similar pattern of log message as in GUI logs because it helps in testing :-(

> 
> > +	return __max2175_get_bits(ctx->regs[idx], msb, lsb); }
> > +
> > +static bool max2175_get_bit(struct max2175_ctx *ctx, u8 idx, u8 bit)
> > +{
> > +	return !!max2175_get_bits(ctx, idx, bit, bit); }
> > +
> > +static void max2175_set_bits(struct max2175_ctx *ctx, u8 idx,
> > +		      u8 msb, u8 lsb, u8 newval)
> > +{
> > +	if (max2175_debug >= 2)
> > +		mxm_dbg(ctx, "set_bits: idx:%u msb:%u lsb:%u newval:%u\n",
> > +			idx, msb, lsb, newval);
> > +	ctx->regs[idx] = __max2175_set_bits(ctx->regs[idx], msb, lsb,
> > +					      newval);
> > +}
> > +
> > +static void max2175_set_bit(struct max2175_ctx *ctx, u8 idx, u8 bit,
> > +u8
> > newval)
> > +{
> > +	max2175_set_bits(ctx, idx, bit, bit, newval); }
> > +
> > +/* Device register bitops helpers */
> > +static u8 max2175_read_bits(struct max2175_ctx *ctx, u8 idx, u8 msb,
> > +u8
> > lsb)
> > +{
> > +	return __max2175_get_bits(max2175_reg_read(ctx, idx), msb, lsb); }
> > +
> > +static void max2175_write_bits(struct max2175_ctx *ctx, u8 idx, u8 msb,
> > +			u8 lsb, u8 newval)
> > +{
> > +	/* Update local copy & write to device */
> > +	max2175_set_bits(ctx, idx, msb, lsb, newval);
> > +	max2175_reg_write(ctx, idx, ctx->regs[idx]); }
> > +
> > +static void max2175_write_bit(struct max2175_ctx *ctx, u8 idx, u8 bit,
> > +			      u8 newval)
> > +{
> > +	if (max2175_debug >= 2)
> > +		mxm_dbg(ctx, "idx %u, bit %u, newval %u\n", idx, bit, newval);
> > +	max2175_write_bits(ctx, idx, bit, bit, newval); }
> 
> Really, please use regmap :-)

Agreed.

> 
> > +static int max2175_poll_timeout(struct max2175_ctx *ctx, u8 idx, u8
> > +msb, u8
> > lsb,
> > +				u8 exp_val, u32 timeout)
> > +{
> > +	unsigned long end = jiffies + msecs_to_jiffies(timeout);
> > +	int ret;
> > +
> > +	do {
> > +		ret = max2175_read_bits(ctx, idx, msb, lsb);
> > +		if (ret < 0)
> > +			return ret;
> > +		if (ret == exp_val)
> > +			return 0;
> > +
> > +		usleep_range(1000, 1500);
> > +	} while (time_is_after_jiffies(end));
> > +
> > +	return -EBUSY;
> > +}
> > +
> > +static int max2175_poll_csm_ready(struct max2175_ctx *ctx) {
> > +	return max2175_poll_timeout(ctx, 69, 1, 1, 0, 50);
> 
> Please define macros for register addresses and values, this is just
> unreadable.

The Tuner provider is unwilling to disclose all register details. I agree on the readability issue with this restriction but this is somewhat true for some sensitive IPs in the media subsystem.

> 
> > +}
> 
> [snip]
> 
> > +
> > +#define MAX2175_IS_BAND_AM(ctx)		\
> > +	(max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_AM)
> > +
> > +#define MAX2175_IS_FM_MODE(ctx)		\
> > +	(max2175_get_bits(ctx, 12, 5, 4) == 0)
> > +
> > +#define MAX2175_IS_FMHD_MODE(ctx)	\
> > +	(max2175_get_bits(ctx, 12, 5, 4) == 1)
> > +
> > +#define MAX2175_IS_DAB_MODE(ctx)	\
> > +	(max2175_get_bits(ctx, 12, 5, 4) == 2)
> > +
> > +static int max2175_band_from_freq(u64 freq) {
> > +	if (freq >= 144000 && freq <= 26100000)
> > +		return MAX2175_BAND_AM;
> > +	else if (freq >= 65000000 && freq <= 108000000)
> > +		return MAX2175_BAND_FM;
> > +	else if (freq >= 160000000 && freq <= 240000000)
> > +		return MAX2175_BAND_VHF;
> > +
> > +	/* Add other bands on need basis */
> > +	return -ENOTSUPP;
> > +}
> > +
> > +static int max2175_update_i2s_mode(struct max2175_ctx *ctx, u32
> > +i2s_mode) {
> > +	/* Only change if it's new */
> > +	if (max2175_read_bits(ctx, 29, 2, 0) == i2s_mode)
> > +		return 0;
> > +
> > +	max2175_write_bits(ctx, 29, 2, 0, i2s_mode);
> > +
> > +	/* Based on I2S mode value I2S_WORD_CNT values change */
> > +	if (i2s_mode == MAX2175_I2S_MODE3) {
> > +		max2175_write_bits(ctx, 30, 6, 0, 1);
> > +	} else if (i2s_mode == MAX2175_I2S_MODE2 ||
> > +		   i2s_mode == MAX2175_I2S_MODE4) {
> > +		max2175_write_bits(ctx, 30, 6, 0, 0);
> > +	} else if (i2s_mode == MAX2175_I2S_MODE0) {
> > +		max2175_write_bits(ctx, 30, 6, 0,
> > +				   ctx->rx_modes[ctx->mode].i2s_word_size);
> > +	} else {
> > +		v4l2_err(ctx->client,
> > +			 "failed: i2s_mode %u unsupported\n", i2s_mode);
> 
> This should never happen as the control framework will validate control
> values.

Agreed.

> 
> > +		return 1;
> 
> Error codes should be negative.

Agreed.

> 
> > +	}
> > +	mxm_dbg(ctx, "updated i2s_mode %u\n", i2s_mode);
> > +	return 0;
> > +}
> 
> [snip]
> 
> > +static void max2175_load_dab_1p2(struct max2175_ctx *ctx) {
> > +	u32 i;
> 
> unsigned int will do, no need for an explicitly sized type.

Agreed.

> 
> > +
> > +	/* Master is already set on init */
> > +	for (i = 0; i < ARRAY_SIZE(dab12_map); i++)
> > +		max2175_reg_write(ctx, dab12_map[i].idx, dab12_map[i].val);
> > +
> > +	/* The default settings assume master */
> > +	if (!ctx->master)
> > +		max2175_write_bit(ctx, 30, 7, 1);
> > +
> > +	/* Cache i2s_test value at this point */
> > +	ctx->i2s_test = max2175_get_bits(ctx, 104, 3, 0);
> > +	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); }
> 
> [snip]
> 
> > +static bool max2175_set_csm_mode(struct max2175_ctx *ctx,
> > +			  enum max2175_csm_mode new_mode)
> > +{
> > +	int ret = max2175_poll_csm_ready(ctx);
> > +
> > +	if (ret) {
> > +		v4l2_err(ctx->client, "csm not ready: new mode %d\n",
> new_mode);
> > +		return ret;
> > +	}
> > +
> > +	mxm_dbg(ctx, "set csm mode: new mode %d\n", new_mode);
> > +
> > +	max2175_write_bits(ctx, 0, 2, 0, new_mode);
> > +
> > +	/* Wait for a fixed settle down time depending on new mode and band
> */
> > +	switch (new_mode) {
> > +	case MAX2175_CSM_MODE_JUMP_FAST_TUNE:
> > +		if (MAX2175_IS_BAND_AM(ctx)) {
> > +			usleep_range(1250, 1500);	/* 1.25ms */
> > +		} else {
> > +			if (MAX2175_IS_DAB_MODE(ctx))
> > +				usleep_range(3000, 3500);	/* 3ms */
> > +			else
> > +				usleep_range(1250, 1500);	/* 1.25ms */
> > +		}
> 
> You can write this as
> 
> 		if (MAX2175_IS_BAND_AM(ctx))
> 			usleep_range(1250, 1500);	/* 1.25ms */
> 		else if (MAX2175_IS_DAB_MODE(ctx))
> 			usleep_range(3000, 3500);	/* 3ms */
> 		else
> 			usleep_range(1250, 1500);	/* 1.25ms */

Agreed.

> 
> 
> > +		break;
> > +
> > +	/* Other mode switches can be added in the future if needed */
> > +	default:
> > +		break;
> > +	}
> > +
> > +	ret = max2175_poll_csm_ready(ctx);
> > +	if (ret) {
> > +		v4l2_err(ctx->client, "csm did not settle: new mode %d\n",
> > +			 new_mode);
> > +		return ret;
> > +	}
> > +	return ret;
> > +}
> 
> [snip]
> 
> > +
> > +static int max2175_csm_action(struct max2175_ctx *ctx,
> > +			      enum max2175_csm_mode action) {
> > +	int ret;
> > +	int load_buffer = MAX2175_CSM_MODE_LOAD_TO_BUFFER;
> > +
> > +	mxm_dbg(ctx, "csm action: %d\n", action);
> > +
> > +	/* Perform one or two CSM mode commands. */
> > +	switch (action)	{
> > +	case MAX2175_CSM_MODE_NO_ACTION:
> > +		/* Take no FSM Action. */
> > +		return 0;
> > +	case MAX2175_CSM_MODE_LOAD_TO_BUFFER:
> > +	case MAX2175_CSM_MODE_PRESET_TUNE:
> > +	case MAX2175_CSM_MODE_SEARCH:
> > +	case MAX2175_CSM_MODE_AF_UPDATE:
> > +	case MAX2175_CSM_MODE_JUMP_FAST_TUNE:
> > +	case MAX2175_CSM_MODE_CHECK:
> > +	case MAX2175_CSM_MODE_LOAD_AND_SWAP:
> > +	case MAX2175_CSM_MODE_END:
> > +		ret = max2175_set_csm_mode(ctx, action);
> > +		break;
> > +	case MAX2175_CSM_MODE_BUFFER_PLUS_PRESET_TUNE:
> > +		ret = max2175_set_csm_mode(ctx, load_buffer);
> > +		if (ret) {
> > +			v4l2_err(ctx->client, "csm action %d load buf
> failed\n",
> > +				 action);
> > +			break;
> > +		}
> > +		ret = max2175_set_csm_mode(ctx, MAX2175_CSM_MODE_PRESET_TUNE);
> > +		break;
> > +	case MAX2175_CSM_MODE_BUFFER_PLUS_SEARCH:
> > +		ret = max2175_set_csm_mode(ctx, load_buffer);
> > +		if (ret) {
> > +			v4l2_err(ctx->client, "csm action %d load buf
> failed\n",
> > +				 action);
> > +			break;
> > +		}
> 
> Don't duplicate the error messages, move them after the switch statement.

Agreed. The error messages are not needed as the csm_mode function logs the mode argument in error log. Cleaned up.

> 
> > +		ret = max2175_set_csm_mode(ctx, MAX2175_CSM_MODE_SEARCH);
> > +		break;
> > +	case MAX2175_CSM_MODE_BUFFER_PLUS_AF_UPDATE:
> > +		ret = max2175_set_csm_mode(ctx, load_buffer);
> > +		if (ret) {
> > +			v4l2_err(ctx->client, "csm action %d load buf
> failed\n",
> > +				 action);
> > +			break;
> > +		}
> > +		ret = max2175_set_csm_mode(ctx, MAX2175_CSM_MODE_AF_UPDATE);
> > +		break;
> > +	case MAX2175_CSM_MODE_BUFFER_PLUS_JUMP_FAST_TUNE:
> 
> This function is only called with action set to
> MAX2175_CSM_MODE_BUFFER_PLUS_JUMP_FAST_TUNE. I'd remove the rest of the
> code for now, unless you have a plan to use it soon.

I'll remove. As mentioned earlier, I cannot tell how soon the other modes will be added. There are few other places with placeholders for other modes. I'll try to clean.

> 
> > +		ret = max2175_set_csm_mode(ctx, load_buffer);
> > +		if (ret) {
> > +			v4l2_err(ctx->client, "csm action %d load buf
> failed\n",
> > +				 action);
> > +			break;
> > +		}
> > +		ret = max2175_set_csm_mode(ctx,
> > +					   MAX2175_CSM_MODE_JUMP_FAST_TUNE);
> > +		break;
> > +	case MAX2175_CSM_MODE_BUFFER_PLUS_CHECK:
> > +		ret = max2175_set_csm_mode(ctx, load_buffer);
> > +		if (ret) {
> > +			v4l2_err(ctx->client, "csm action %d load buf
> failed\n",
> > +				 action);
> > +			break;
> > +		}
> > +		ret = max2175_set_csm_mode(ctx, MAX2175_CSM_MODE_CHECK);
> > +		break;
> > +	case MAX2175_CSM_MODE_BUFFER_PLUS_LOAD_AND_SWAP:
> > +		ret = max2175_set_csm_mode(ctx, load_buffer);
> > +		if (ret) {
> > +			v4l2_err(ctx->client, "csm action %d load buf
> failed\n",
> > +				 action);
> > +			break;
> > +		}
> > +		ret = max2175_set_csm_mode(ctx,
> MAX2175_CSM_MODE_LOAD_AND_SWAP);
> > +		break;
> > +	default:
> > +		ret = max2175_set_csm_mode(ctx, MAX2175_CSM_MODE_NO_ACTION);
> > +		break;
> > +	}
> > +	return ret;
> > +}
> > +
> > +static int max2175_set_lo_freq(struct max2175_ctx *ctx, u64 lo_freq)
> > +{
> > +	int ret;
> > +	u32 lo_mult;
> > +	u64 scaled_lo_freq;
> > +	const u64 scale_factor = 1000000ULL;
> > +	u64 scaled_npf, scaled_integer, scaled_fraction;
> > +	u32 frac_desired, int_desired;
> > +	u8 loband_bits, vcodiv_bits;
> 
> Do you really support frequencies above 4GHz ? 

Nope. 

If not most of the 64-bit
> values could be stored in 32 bits.

The 64bit variables are needed to extract the fractional part (upto 6 digit precision) out of floating point divisions (original user space code).

> 
> > +
> > +	scaled_lo_freq = lo_freq;
> > +	/* Scale to larger number for precision */
> > +	scaled_lo_freq = scaled_lo_freq * scale_factor * 100;
> > +
> > +	mxm_dbg(ctx, "scaled lo_freq %llu lo_freq %llu\n",
> > +		scaled_lo_freq, lo_freq);
> > +
> > +	if (MAX2175_IS_BAND_AM(ctx)) {
> > +		if (max2175_get_bit(ctx, 5, 7) == 0)
> > +			loband_bits = 0;
> > +			vcodiv_bits = 0;
> > +			lo_mult = 16;
> > +	} else if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_FM) {
> > +		if (lo_freq <= 74700000) {
> > +			loband_bits = 0;
> > +			vcodiv_bits = 0;
> > +			lo_mult = 16;
> > +		} else if ((lo_freq > 74700000) && (lo_freq <= 110000000)) {
> 
> No need for the inner parentheses.

Agreed.

> 
> > +			loband_bits = 1;
> > +			vcodiv_bits = 0;
> > +		} else {
> > +			loband_bits = 1;
> > +			vcodiv_bits = 3;
> > +		}
> > +		lo_mult = 8;
> > +	} else if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF) {
> > +		if (lo_freq <= 210000000) {
> > +			loband_bits = 2;
> > +			vcodiv_bits = 2;
> > +		} else {
> > +			loband_bits = 2;
> > +			vcodiv_bits = 1;
> > +		}
> > +		lo_mult = 4;
> > +	} else {
> > +		loband_bits = 3;
> > +		vcodiv_bits = 2;
> > +		lo_mult = 2;
> > +	}
> > +
> > +	if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_L)
> > +		scaled_npf = (scaled_lo_freq / ctx->xtal_freq / lo_mult) /
> 100;
> > +	else
> > +		scaled_npf = (scaled_lo_freq / ctx->xtal_freq * lo_mult) /
> 100;
> > +
> > +	scaled_integer = scaled_npf / scale_factor * scale_factor;
> > +	int_desired = (u32)(scaled_npf / scale_factor);
> > +	scaled_fraction = scaled_npf - scaled_integer;
> > +	frac_desired = (u32)(scaled_fraction * 1048576 / scale_factor);
> > +
> > +	/* Check CSM is not busy */
> > +	ret = max2175_poll_csm_ready(ctx);
> > +	if (ret) {
> > +		v4l2_err(ctx->client, "lo_freq: csm busy. freq %llu\n",
> > +			 lo_freq);
> > +		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_set_bits(ctx, 5, 3, 2, loband_bits);
> > +	max2175_set_bits(ctx, 6, 7, 6, vcodiv_bits);
> > +	max2175_set_bits(ctx, 1, 7, 0, (u8)(int_desired & 0xff));
> > +	max2175_set_bits(ctx, 2, 3, 0, (u8)((frac_desired >> 16) & 0x1f));
> > +	max2175_set_bits(ctx, 3, 7, 0, (u8)((frac_desired >> 8) & 0xff));
> > +	max2175_set_bits(ctx, 4, 7, 0, (u8)(frac_desired & 0xff));
> > +	/* Flush the above registers to device */
> > +	max2175_flush_regstore(ctx, 1, 6);
> > +	return ret;
> > +}
> 
> [snip]
> 
> > +static int max2175_set_rf_freq_non_am_bands(struct max2175_ctx *ctx,
> > +u64
> > freq,
> > +					    u32 lo_pos)
> > +{
> > +	int ret;
> > +	s64 adj_freq;
> > +	u64 low_if_freq;
> > +
> > +	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_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF) {
> 
> You perform this check in multiple places, you could create a helper
> function.

Agreed.

> 
> > +		if (lo_pos == MAX2175_LO_ABOVE_DESIRED)
> > +			adj_freq = freq + low_if_freq;
> > +		else
> > +			adj_freq = freq - low_if_freq;
> > +	} else {
> > +		if (lo_pos == MAX2175_LO_ABOVE_DESIRED)
> > +			adj_freq = freq - low_if_freq;
> > +		else
> > +			adj_freq = freq + low_if_freq;
> > +	}
> 
> This could be written
> 
> 	if ((max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF) ==
> 	    (lo_pos == MAX2175_LO_ABOVE_DESIRED))
> 		adj_freq = freq + low_if_freq;
> 	else
> 		adj_freq = freq - low_if_freq;
> 
> Same for the other similar constructs in the driver. Up to you.

Agreed. Nice :-)

> 
> > +
> > +	ret = max2175_set_lo_freq(ctx, adj_freq);
> > +	if (ret)
> > +		return ret;
> > +
> > +	return max2175_set_nco_freq(ctx, low_if_freq); }
> 
> [snip]
> 
> > +#define max2175_ctx_from_sd(x)	\
> > +	container_of(x, struct max2175_ctx, sd)
> > +#define max2175_ctx_from_ctrl(x)	\
> > +	container_of(x, struct max2175_ctx, ctrl_hdl)
> 
> I'd move it right after the structure definition, and turn them into
> static inline functions.

Agreed.

> 
> > +static int max2175_s_ctrl(struct v4l2_ctrl *ctrl) {
> > +	struct max2175_ctx *ctx = max2175_ctx_from_ctrl(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_EN:
> > +		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_AM_HIZ:
> > +		v4l2_ctrl_activate(ctx->am_hiz, false);
> > +		break;
> > +	case V4L2_CID_MAX2175_HSLS:
> > +		v4l2_ctrl_activate(ctx->hsls, false);
> > +		break;
> > +	case V4L2_CID_MAX2175_RX_MODE:
> > +		mxm_dbg(ctx, "rx mode %u\n", ctrl->val);
> > +		max2175_s_ctrl_rx_mode(ctx, ctrl->val);
> > +		break;
> > +	default:
> > +		v4l2_err(ctx->client, "s:invalid ctrl id 0x%x\n", ctrl->id);
> > +		ret = -EINVAL;
> 
> This should never happen. The driver has too many error and debug messages
> overall.

Agreed :-). Will clean up.
 
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +static int max2175_get_lna_gain(struct max2175_ctx *ctx) {
> > +	int gain = 0;
> > +	enum max2175_band band = max2175_get_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:
> > +		v4l2_err(ctx->client, "invalid band %d to get rf gain\n",
> band);
> 
> Can this happen ?

Yes, there is "L-band". It is a paranoia check as I am testing by comparing logs sometimes :-(

> 
> > +		break;
> > +	}
> > +	return gain;
> > +}
> > +
> > +static int max2175_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > +	struct max2175_ctx *ctx = max2175_ctx_from_ctrl(ctrl->handler);
> > +
> > +	/* Only the dynamically changing values need to be in
> g_volatile_ctrl
> */
> > +	mxm_dbg(ctx, "g_volatile_ctrl: id 0x%x\n", ctrl->id);
> > +	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;
> > +	default:
> > +		v4l2_err(ctx->client, "g:invalid ctrl id 0x%x\n", ctrl->id);
> 
> This should never happen either.

I agree.

> 
> > +		return -EINVAL;
> > +	}
> > +	mxm_dbg(ctx, "g_volatile_ctrl: id 0x%x val %d\n", ctrl->id, ctrl-
> >val);
> > +	return 0;
> > +};
> 
> [snip]
> 
> > +static const struct v4l2_ctrl_config max2175_i2s_en = {
> > +	.ops = &max2175_ctrl_ops,
> > +	.id = V4L2_CID_MAX2175_I2S_EN,
> 
> V4L2_CID_MAX2175_I2S_ENABLE ?

Agreed.

> 
> > +	.name = "I2S Enable",
> > +	.type = V4L2_CTRL_TYPE_BOOLEAN,
> > +	.min = 0,
> > +	.max = 1,
> > +	.step = 1,
> > +	.def = 1,
> > +};
> > +
> > +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_INTEGER,
> 
> Should this be a menu control ?

Hmm... the strings would be named "i2s mode x"? Will that be OK? 

> 
> > +	.min = 0,
> > +	.max = 4,
> > +	.step = 1,
> > +	.def = 0,
> > +};
> > +
> > +static const struct v4l2_ctrl_config max2175_am_hiz = {
> > +	.ops = &max2175_ctrl_ops,
> > +	.id = V4L2_CID_MAX2175_AM_HIZ,
> > +	.name = "AM High impedance input",
> > +	.type = V4L2_CTRL_TYPE_BOOLEAN,
> > +	.min = 0,
> > +	.max = 1,
> > +	.step = 1,
> > +	.def = 0,
> > +};
> > +
> > +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,
> > +};
> > +
> > +
> > +/* NOTE: Any addition/deletion in the below list should be reflected in
> > + * max2175_modetag enum
> > + */
> > +static const char * const max2175_ctrl_eu_rx_mode_strings[] = {
> > +	"DAB 1.2",
> > +	"NULL",
> 
> Do you really mean "NULL", not NULL ?

Sorry, That's cut/paste from vivid-ctrl. I have cleaned it up with designated initializers as Geert pointed out.

> 
> > +};
> > +
> > +static const char * const max2175_ctrl_na_rx_mode_strings[] = {
> > +	"NA FM 1.0",
> > +	"NULL",
> > +};
> > +
> > +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_mode_strings) - 2,
> 
> If there's a single mode supported I'd skip adding those controls for now.

I'll try to add one more mode support if time permits. The menu looks much readable with v4l2-ctl & comparing the same with datasheet.

> 
> > +	.def = 0,
> > +	.qmenu = max2175_ctrl_eu_rx_mode_strings,
> > +};
> > +
> > +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_mode_strings) - 2,
> > +	.def = 0,
> > +	.qmenu = max2175_ctrl_na_rx_mode_strings,
> > +};
> > +
> > +static u32 max2175_refout_load_to_bits(struct i2c_client *client, u32
> load)
> > +{
> > +	u32 bits = 0;	/* REFOUT disabled */
> > +
> > +	if (load >= 0 && load <= 40)
> > +		bits = load / 10;
> > +	else if (load >= 60 && load <= 70)
> > +		bits = load / 10 - 1;
> > +	else
> > +		dev_warn(&client->dev, "invalid refout_load %u\n", load);
> 
> Your DT bindings specify 0 as a valid value.

Agreed. The DT values are changed to 10-70 range.

> 
> An invalid value specified in DT should be a fatal error.

OK. Will correct this.

> 
> > +
> > +	return bits;
> > +}
> > +
> > +static int max2175_probe(struct i2c_client *client,
> > +			const struct i2c_device_id *id)
> > +{
> > +	struct max2175_ctx *ctx;
> > +	struct device *dev = &client->dev;
> > +	struct v4l2_subdev *sd;
> > +	struct v4l2_ctrl_handler *hdl;
> > +	struct clk *clk;
> > +	bool master = true;
> > +	u32 refout_load, refout_bits = 0;	/* REFOUT disabled */
> > +	int ret;
> > +
> > +	/* Check if the adapter supports the needed features */
> > +	if (!i2c_check_functionality(client->adapter,
> > +				     I2C_FUNC_SMBUS_BYTE_DATA)) {
> > +		dev_err(&client->dev, "i2c check failed\n");
> > +		return -EIO;
> > +	}
> > +
> > +	if (of_find_property(client->dev.of_node, "maxim,slave", NULL))
> > +		master = false;
> > +
> > +	if (!of_property_read_u32(client->dev.of_node, "maxim,refout-load",
> > +				 &refout_load))
> > +		refout_bits = max2175_refout_load_to_bits(client,
> refout_load);
> > +
> > +	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;
> > +	}
> > +
> > +	ctx = kzalloc(sizeof(struct max2175_ctx),
> 
> sizeof(*ctx)
> 
> > +			     GFP_KERNEL);
> 
> This fits on one line.

Yes, corrected in last cleanup.

> 
> > +	if (ctx == NULL)
> > +		return -ENOMEM;
> > +
> > +	sd = &ctx->sd;
> > +	ctx->master = master;
> > +	ctx->mode_resolved = false;
> > +
> > +	/* Set the defaults */
> > +	ctx->freq = bands_rf.rangelow;
> > +
> > +	ctx->xtal_freq = clk_get_rate(clk);
> > +	dev_info(&client->dev, "xtal freq %lluHz\n", ctx->xtal_freq);
> > +
> > +	v4l2_i2c_subdev_init(sd, client, &max2175_ops);
> > +	ctx->client = client;
> > +
> > +	sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> > +	ctx->dev = dev;
> > +
> > +	/* Controls */
> > +	hdl = &ctx->ctrl_hdl;
> > +	ret = v4l2_ctrl_handler_init(hdl, 8);
> > +	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->am_hiz = v4l2_ctrl_new_custom(hdl, &max2175_am_hiz, 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 = (struct max2175_rxmode *)eu_rx_modes;
> > +	} else {
> > +		ctx->rx_mode = v4l2_ctrl_new_custom(hdl,
> > +						    &max2175_na_rx_mode,
> NULL);
> > +		ctx->rx_modes = (struct max2175_rxmode *)na_rx_modes;
> > +	}
> > +	ctx->sd.ctrl_handler = &ctx->ctrl_hdl;
> > +
> > +	ret = v4l2_async_register_subdev(sd);
> > +	if (ret) {
> > +		dev_err(&client->dev, "register subdev failed\n");
> > +		goto err_reg;
> > +	}
> > +	dev_info(&client->dev, "subdev registered\n");
> > +
> > +	/* Initialize device */
> > +	ret = max2175_core_init(ctx, refout_bits);
> > +	if (ret)
> > +		goto err_init;
> > +
> > +	mxm_dbg(ctx, "probed\n");
> > +	return 0;
> > +
> > +err_init:
> > +	v4l2_async_unregister_subdev(sd);
> > +err_reg:
> > +	v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
> > +err:
> > +	kfree(ctx);
> > +	return ret;
> > +}
> > +
> > +static int max2175_remove(struct i2c_client *client)
> > +{
> > +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> > +	struct max2175_ctx *ctx = max2175_ctx_from_sd(sd);
> > +
> > +	v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
> > +	v4l2_async_unregister_subdev(sd);
> > +	mxm_dbg(ctx, "removed\n");
> > +	kfree(ctx);
> > +	return 0;
> > +}
> > +
> > +static const struct i2c_device_id max2175_id[] = {
> > +	{ DRIVER_NAME, 0},
> > +	{},
> > +};
> > +
> 
> No need for a blank line here.

Agreed.

> 
> > +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..61a508b
> > --- /dev/null
> > +++ b/drivers/media/i2c/max2175/max2175.h
> > @@ -0,0 +1,124 @@
> > +/*
> > + * 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__
> > +
> > +#include <linux/types.h>
> > +
> > +enum max2175_region {
> > +	MAX2175_REGION_EU = 0,	/* Europe */
> > +	MAX2175_REGION_NA,	/* North America */
> > +};
> > +
> > +#define MAX2175_EU_XTAL_FREQ	(36864000)	/* In Hz */
> > +#define MAX2175_NA_XTAL_FREQ	(40186125)	/* In Hz */
> > +
> > +enum max2175_band {
> > +	MAX2175_BAND_AM = 0,
> > +	MAX2175_BAND_FM,
> > +	MAX2175_BAND_VHF,
> > +	MAX2175_BAND_L,
> > +};
> > +
> > +/* NOTE: Any addition/deletion in the below enum should be reflected in
> > + * V4L2_CID_MAX2175_RX_MODE ctrl strings
> > + */
> > +enum max2175_modetag {
> > +	/* EU modes */
> > +	MAX2175_DAB_1_2 = 0,
> > +
> > +	/* Other possible modes to add in future
> > +	 * MAX2175_DAB_1_0,
> > +	 * MAX2175_DAB_1_3,
> > +	 * MAX2175_EU_FM_2_2,
> > +	 * MAX2175_EU_FM_1_0,
> > +	 * MAX2175_EU_FMHD_4_0,
> > +	 * MAX2175_EU_AM_1_0,
> > +	 * MAX2175_EU_AM_2_2,
> > +	 */
> > +
> > +	/* NA modes */
> > +	MAX2175_NA_FM_1_0 = 0,
> > +
> > +	/* Other possible modes to add in future
> > +	 * MAX2175_NA_FM_1_2,
> > +	 * 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_CSM_MODE_LOAD_TO_BUFFER = 0,
> > +	MAX2175_CSM_MODE_PRESET_TUNE,
> > +	MAX2175_CSM_MODE_SEARCH,
> > +	MAX2175_CSM_MODE_AF_UPDATE,
> > +	MAX2175_CSM_MODE_JUMP_FAST_TUNE,
> > +	MAX2175_CSM_MODE_CHECK,
> > +	MAX2175_CSM_MODE_LOAD_AND_SWAP,
> > +	MAX2175_CSM_MODE_END,
> > +	MAX2175_CSM_MODE_BUFFER_PLUS_PRESET_TUNE,
> > +	MAX2175_CSM_MODE_BUFFER_PLUS_SEARCH,
> > +	MAX2175_CSM_MODE_BUFFER_PLUS_AF_UPDATE,
> > +	MAX2175_CSM_MODE_BUFFER_PLUS_JUMP_FAST_TUNE,
> > +	MAX2175_CSM_MODE_BUFFER_PLUS_CHECK,
> > +	MAX2175_CSM_MODE_BUFFER_PLUS_LOAD_AND_SWAP,
> > +	MAX2175_CSM_MODE_NO_ACTION
> > +};
> > +
> > +/* 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 */
> > +struct max2175_regmap {
> > +	u8 idx;				/* Register index */
> > +	u8 val;				/* Register value */
> > +};
> 
> As no other file than max2175.c includes this, I would move at least the
> structure definitions to the .c file.

Agreed.

Thanks a lot for all the comments and suggestions. I'll post the next version soon.

Thanks,
Ramesh

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

* RE: [RFC 5/5] doc_rst: media: New SDR formats SC16, SC18 & SC20
  2016-10-18 18:35       ` Laurent Pinchart
  (?)
@ 2016-10-24 10:19       ` Ramesh Shanmugasundaram
  -1 siblings, 0 replies; 39+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-10-24 10:19 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope,
	Chris Paterson, geert, linux-media, devicetree,
	linux-renesas-soc

Hi Laurent,

Thank you for the review comments.

> On Wednesday 12 Oct 2016 15:10:29 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          | 44
> ++++++++++++++++++
> >  .../media/uapi/v4l/pixfmt-sdr-scu18be.rst          | 48
> +++++++++++++++++++
> >  .../media/uapi/v4l/pixfmt-sdr-scu20be.rst          | 48
> +++++++++++++++++++
> >  Documentation/media/uapi/v4l/sdr-formats.rst       |  3 ++
> >  4 files changed, 143 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..d6c2123
> > --- /dev/null
> > +++ b/Documentation/media/uapi/v4l/pixfmt-sdr-scu16be.rst
> > @@ -0,0 +1,44 @@
> > +.. -*- coding: utf-8; mode: rst -*-
> > +
> > +.. _V4L2-SDR-FMT-SCU16BE:
> > +
> > +******************************
> > +V4L2_SDR_FMT_SCU16BE ('SCU16')
> 
> The value between parentheses is the ASCII representation of the 4CC, it
> should be SC16. Same comment for the other formats.

Agreed. I corrected it after I sent the patch :-(.

> 
> > +******************************
> > +
> > +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. I value starts first and Q value starts at an offset
> > +equalling half of the buffer size. 14 bit data is stored in 16 bit
> > +space with unused stuffed bits padded with 0.
> 
> Please specify here how the 14-bit numbers are aligned (i.e. padding in
> bits
> 15:14 or bits 1:0 or any other strange option). Same comment for the other
> formats.

You are right. Actually the representation would be something like below. I will correct this for all the 3 formats. Thanks.

<------------------------32bits---------------------->
<--14 bit data + 2bit status---- 16bit padded zeros-->
<--14 bit data + 2bit status---- 16bit padded zeros-->

> 
> > +
> > +**Byte Order.**
> > +Each cell is one byte.
> > +
> > +
> > +.. flat-table::
> > +    :header-rows:  0
> > +    :stub-columns: 0
> > +
> > +    -  .. row 1
> 
> Please use the more compact table stable

Agreed.

> 
> 	* - start + 0:
> 	  - I'\ :sub:`0[D13:D6]`
> 	  ...
> 
> Same comment for the other formats.

Agreed.

> 
> > +
> > +       -  start + 0:
> > +
> > +       -  I'\ :sub:`0[D13:D6]`
> > +
> > +       -  I'\ :sub:`0[D5:D0]`
> > +
> > +    -  .. row 2
> > +
> > +       -  start + buffer_size/2:
> > +
> > +       -  Q'\ :sub:`0[D13:D6]`
> > +
> > +       -  Q'\ :sub:`0[D5:D0]`
> 
> The format looks planar, does it use one V4L2 plane (as does NV12) or two
> V4L2 planes (as does NV12M) ? Same question for the other formats.

Thank you for bringing up this topic. This is one of the key design dilemma.

The I & Q data for these three SDR formats comes from two different DMA channels and hence two separate pointers -> we could say it is v4l2 multi-planar. Right now, I am making it look like a single plane by presenting the data in one single buffer ptr. 

For e.g. multi-planar SC16 format would look something like this

<------------------------32bits---------------------->
<--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 0 
<--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 4 
...
<--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 0 
<--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 4 

My concerns are

1) These formats are not a standard as the video "Image Formats". These formats are possible when we use DRIF + MAX2175 combination. If we interface with a different tuner vendor, the above format(s) MAY/MAY NOT be re-usable. We do not know at this point. This is the main open item for discussion in the cover letter.

2) MPLANE support within V4L2 seems specific to video. Please correct me if this is wrong interpretation.
	- struct v4l2_format contains v4l2_sdr_format and v4l2_pix_format_mplane as members of union. Should I create a new v4l2_sdr_format_mplane? If I have to use v4l2_pix_format_mplane most of the video specific members would be unused (it would be similar to using v4l2_pix_format itself instead of v4l2_sdr_format)?
	
	- The above decision (accomodate SDR & MPLANE) needs to be propagated across the framework. Is this the preferred approach?
	
It goes back to point (1). As of today, the change set for this combo (DRIF+MAX2175) introduces new SDR formats only. Should it add further SDR+MPLANE support to the framework as well?

I would appreciate your suggestions on this regard.

> 
> > 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..e6e0aff
> > --- /dev/null
> > +++ b/Documentation/media/uapi/v4l/pixfmt-sdr-scu18be.rst
> > @@ -0,0 +1,48 @@
> > +.. -*- coding: utf-8; mode: rst -*-
> > +
> > +.. _V4L2-SDR-FMT-SCU18BE:
> > +
> > +******************************
> > +V4L2_SDR_FMT_SCU18BE ('SCU18')
> > +******************************
> > +
> > +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. I value starts first and Q value starts at an offset
> > +equalling half of the buffer size. 16 bit data is stored in 18 bit
> > +space with unused stuffed bits padded with 0.
> 
> Your example below suggests that 18 bit data is stored in 24 bits. Similar
> comment for SCU20.

Agreed. The corrected representation is as I mentioned in the earlier comment.

Thanks,
Ramesh

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

* RE: [RFC 5/5] doc_rst: media: New SDR formats SC16, SC18 & SC20
  2016-10-18 18:35       ` Laurent Pinchart
@ 2016-11-02  9:00         ` Ramesh Shanmugasundaram
  -1 siblings, 0 replies; 39+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-11-02  9:00 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	mchehab-DgEjT+Ai2ygdnm+yROfE0A, hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
	sakari.ailus-VuQAYsv1563Yd54FQh9/CA, crope-X3B1VOXEql0,
	Chris Paterson, geert-Td1EMuHUCqxL1ZNQvxDV9g,
	linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA

Hi Laurent,

Any further thoughts on the SDR format please (especially the comment below). I would appreciate your feedback.

> > On Wednesday 12 Oct 2016 15:10:29 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
[snip]
> > > +
> > > +       -  start + 0:
> > > +
> > > +       -  I'\ :sub:`0[D13:D6]`
> > > +
> > > +       -  I'\ :sub:`0[D5:D0]`
> > > +
> > > +    -  .. row 2
> > > +
> > > +       -  start + buffer_size/2:
> > > +
> > > +       -  Q'\ :sub:`0[D13:D6]`
> > > +
> > > +       -  Q'\ :sub:`0[D5:D0]`
> >
> > The format looks planar, does it use one V4L2 plane (as does NV12) or
> > two
> > V4L2 planes (as does NV12M) ? Same question for the other formats.
> 
> Thank you for bringing up this topic. This is one of the key design
> dilemma.
> 
> The I & Q data for these three SDR formats comes from two different DMA
> channels and hence two separate pointers -> we could say it is v4l2 multi-
> planar. Right now, I am making it look like a single plane by presenting
> the data in one single buffer ptr.
> 
> For e.g. multi-planar SC16 format would look something like this
> 
> <------------------------32bits---------------------->
> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 0
> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 4 ...
> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 0
> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 4
> 
> My concerns are
> 
> 1) These formats are not a standard as the video "Image Formats". These
> formats are possible when we use DRIF + MAX2175 combination. If we
> interface with a different tuner vendor, the above format(s) MAY/MAY NOT
> be re-usable. We do not know at this point. This is the main open item for
> discussion in the cover letter.
> 
> 2) MPLANE support within V4L2 seems specific to video. Please correct me
> if this is wrong interpretation.
> 	- struct v4l2_format contains v4l2_sdr_format and
> v4l2_pix_format_mplane as members of union. Should I create a new
> v4l2_sdr_format_mplane? If I have to use v4l2_pix_format_mplane most of
> the video specific members would be unused (it would be similar to using
> v4l2_pix_format itself instead of v4l2_sdr_format)?
> 
> 	- The above decision (accomodate SDR & MPLANE) needs to be
> propagated across the framework. Is this the preferred approach?
> 
> It goes back to point (1). As of today, the change set for this combo
> (DRIF+MAX2175) introduces new SDR formats only. Should it add further
> SDR+MPLANE support to the framework as well?
> 
> I would appreciate your suggestions on this regard.
> 

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] 39+ messages in thread

* RE: [RFC 5/5] doc_rst: media: New SDR formats SC16, SC18 & SC20
@ 2016-11-02  9:00         ` Ramesh Shanmugasundaram
  0 siblings, 0 replies; 39+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-11-02  9:00 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope,
	Chris Paterson, geert, linux-media, devicetree,
	linux-renesas-soc

Hi Laurent,

Any further thoughts on the SDR format please (especially the comment below). I would appreciate your feedback.

> > On Wednesday 12 Oct 2016 15:10:29 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
[snip]
> > > +
> > > +       -  start + 0:
> > > +
> > > +       -  I'\ :sub:`0[D13:D6]`
> > > +
> > > +       -  I'\ :sub:`0[D5:D0]`
> > > +
> > > +    -  .. row 2
> > > +
> > > +       -  start + buffer_size/2:
> > > +
> > > +       -  Q'\ :sub:`0[D13:D6]`
> > > +
> > > +       -  Q'\ :sub:`0[D5:D0]`
> >
> > The format looks planar, does it use one V4L2 plane (as does NV12) or
> > two
> > V4L2 planes (as does NV12M) ? Same question for the other formats.
> 
> Thank you for bringing up this topic. This is one of the key design
> dilemma.
> 
> The I & Q data for these three SDR formats comes from two different DMA
> channels and hence two separate pointers -> we could say it is v4l2 multi-
> planar. Right now, I am making it look like a single plane by presenting
> the data in one single buffer ptr.
> 
> For e.g. multi-planar SC16 format would look something like this
> 
> <------------------------32bits---------------------->
> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 0
> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 4 ...
> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 0
> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 4
> 
> My concerns are
> 
> 1) These formats are not a standard as the video "Image Formats". These
> formats are possible when we use DRIF + MAX2175 combination. If we
> interface with a different tuner vendor, the above format(s) MAY/MAY NOT
> be re-usable. We do not know at this point. This is the main open item for
> discussion in the cover letter.
> 
> 2) MPLANE support within V4L2 seems specific to video. Please correct me
> if this is wrong interpretation.
> 	- struct v4l2_format contains v4l2_sdr_format and
> v4l2_pix_format_mplane as members of union. Should I create a new
> v4l2_sdr_format_mplane? If I have to use v4l2_pix_format_mplane most of
> the video specific members would be unused (it would be similar to using
> v4l2_pix_format itself instead of v4l2_sdr_format)?
> 
> 	- The above decision (accomodate SDR & MPLANE) needs to be
> propagated across the framework. Is this the preferred approach?
> 
> It goes back to point (1). As of today, the change set for this combo
> (DRIF+MAX2175) introduces new SDR formats only. Should it add further
> SDR+MPLANE support to the framework as well?
> 
> I would appreciate your suggestions on this regard.
> 

Thanks,
Ramesh

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

* Re: [RFC 5/5] doc_rst: media: New SDR formats SC16, SC18 & SC20
  2016-11-02  9:00         ` Ramesh Shanmugasundaram
@ 2016-11-02 20:58             ` Laurent Pinchart
  -1 siblings, 0 replies; 39+ messages in thread
From: Laurent Pinchart @ 2016-11-02 20:58 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 Paterson, geert-Td1EMuHUCqxL1ZNQvxDV9g,
	linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA

Hi Ramesh,

On Wednesday 02 Nov 2016 09:00:00 Ramesh Shanmugasundaram wrote:
> Hi Laurent,
> 
> Any further thoughts on the SDR format please (especially the comment
> below). I would appreciate your feedback.
>
> >> On Wednesday 12 Oct 2016 15:10:29 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
> 
> [snip]
> 
> >>> +
> >>> +       -  start + 0:
> >>> +
> >>> +       -  I'\ :sub:`0[D13:D6]`
> >>> +
> >>> +       -  I'\ :sub:`0[D5:D0]`
> >>> +
> >>> +    -  .. row 2
> >>> +
> >>> +       -  start + buffer_size/2:
> >>> +
> >>> +       -  Q'\ :sub:`0[D13:D6]`
> >>> +
> >>> +       -  Q'\ :sub:`0[D5:D0]`
> >> 
> >> The format looks planar, does it use one V4L2 plane (as does NV12) or
> >> two V4L2 planes (as does NV12M) ? Same question for the other formats.
> > 
> > Thank you for bringing up this topic. This is one of the key design
> > dilemma.
> > 
> > The I & Q data for these three SDR formats comes from two different DMA
> > channels and hence two separate pointers -> we could say it is v4l2 multi-
> > planar. Right now, I am making it look like a single plane by presenting
> > the data in one single buffer ptr.
> > 
> > For e.g. multi-planar SC16 format would look something like this
> > 
> > <------------------------32bits---------------------->
> > <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 0
> > <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 4 ...
> > <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 0
> > <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 4
> > 
> > My concerns are
> > 
> > 1) These formats are not a standard as the video "Image Formats". These
> > formats are possible when we use DRIF + MAX2175 combination. If we
> > interface with a different tuner vendor, the above format(s) MAY/MAY NOT
> > be re-usable. We do not know at this point. This is the main open item for
> > discussion in the cover letter.

If the formats are really device-specific then they should be documented 
accordingly and not made generic.

> > 2) MPLANE support within V4L2 seems specific to video. Please correct me
> > if this is wrong interpretation.
> > 
> > - struct v4l2_format contains v4l2_sdr_format and
> > v4l2_pix_format_mplane as members of union. Should I create a new
> > v4l2_sdr_format_mplane? If I have to use v4l2_pix_format_mplane most of
> > the video specific members would be unused (it would be similar to using
> > v4l2_pix_format itself instead of v4l2_sdr_format)?

I have no answer to that question as I'm not familiar with SDR. Antti, you've 
added v4l2_sdr_format to the API, what's your opinion ? Hans, as you've acked 
the patch, your input would be appreciated as well.

> > - The above decision (accomodate SDR & MPLANE) needs to be
> > propagated across the framework. Is this the preferred approach?
> > 
> > It goes back to point (1). As of today, the change set for this combo
> > (DRIF+MAX2175) introduces new SDR formats only. Should it add further
> > SDR+MPLANE support to the framework as well?
> > 
> > I would appreciate your suggestions on this regard.

-- 
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] 39+ messages in thread

* Re: [RFC 5/5] doc_rst: media: New SDR formats SC16, SC18 & SC20
@ 2016-11-02 20:58             ` Laurent Pinchart
  0 siblings, 0 replies; 39+ messages in thread
From: Laurent Pinchart @ 2016-11-02 20:58 UTC (permalink / raw)
  To: Ramesh Shanmugasundaram
  Cc: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope,
	Chris Paterson, geert, linux-media, devicetree,
	linux-renesas-soc

Hi Ramesh,

On Wednesday 02 Nov 2016 09:00:00 Ramesh Shanmugasundaram wrote:
> Hi Laurent,
> 
> Any further thoughts on the SDR format please (especially the comment
> below). I would appreciate your feedback.
>
> >> On Wednesday 12 Oct 2016 15:10:29 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
> 
> [snip]
> 
> >>> +
> >>> +       -  start + 0:
> >>> +
> >>> +       -  I'\ :sub:`0[D13:D6]`
> >>> +
> >>> +       -  I'\ :sub:`0[D5:D0]`
> >>> +
> >>> +    -  .. row 2
> >>> +
> >>> +       -  start + buffer_size/2:
> >>> +
> >>> +       -  Q'\ :sub:`0[D13:D6]`
> >>> +
> >>> +       -  Q'\ :sub:`0[D5:D0]`
> >> 
> >> The format looks planar, does it use one V4L2 plane (as does NV12) or
> >> two V4L2 planes (as does NV12M) ? Same question for the other formats.
> > 
> > Thank you for bringing up this topic. This is one of the key design
> > dilemma.
> > 
> > The I & Q data for these three SDR formats comes from two different DMA
> > channels and hence two separate pointers -> we could say it is v4l2 multi-
> > planar. Right now, I am making it look like a single plane by presenting
> > the data in one single buffer ptr.
> > 
> > For e.g. multi-planar SC16 format would look something like this
> > 
> > <------------------------32bits---------------------->
> > <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 0
> > <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 4 ...
> > <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 0
> > <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 4
> > 
> > My concerns are
> > 
> > 1) These formats are not a standard as the video "Image Formats". These
> > formats are possible when we use DRIF + MAX2175 combination. If we
> > interface with a different tuner vendor, the above format(s) MAY/MAY NOT
> > be re-usable. We do not know at this point. This is the main open item for
> > discussion in the cover letter.

If the formats are really device-specific then they should be documented 
accordingly and not made generic.

> > 2) MPLANE support within V4L2 seems specific to video. Please correct me
> > if this is wrong interpretation.
> > 
> > - struct v4l2_format contains v4l2_sdr_format and
> > v4l2_pix_format_mplane as members of union. Should I create a new
> > v4l2_sdr_format_mplane? If I have to use v4l2_pix_format_mplane most of
> > the video specific members would be unused (it would be similar to using
> > v4l2_pix_format itself instead of v4l2_sdr_format)?

I have no answer to that question as I'm not familiar with SDR. Antti, you've 
added v4l2_sdr_format to the API, what's your opinion ? Hans, as you've acked 
the patch, your input would be appreciated as well.

> > - The above decision (accomodate SDR & MPLANE) needs to be
> > propagated across the framework. Is this the preferred approach?
> > 
> > It goes back to point (1). As of today, the change set for this combo
> > (DRIF+MAX2175) introduces new SDR formats only. Should it add further
> > SDR+MPLANE support to the framework as well?
> > 
> > I would appreciate your suggestions on this regard.

-- 
Regards,

Laurent Pinchart


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

* Re: [RFC 5/5] doc_rst: media: New SDR formats SC16, SC18 & SC20
  2016-11-02 20:58             ` Laurent Pinchart
@ 2016-11-03 20:36               ` Antti Palosaari
  -1 siblings, 0 replies; 39+ messages in thread
From: Antti Palosaari @ 2016-11-03 20:36 UTC (permalink / raw)
  To: Laurent Pinchart, Ramesh Shanmugasundaram
  Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	mchehab-DgEjT+Ai2ygdnm+yROfE0A, hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
	sakari.ailus-VuQAYsv1563Yd54FQh9/CA, Chris Paterson,
	geert-Td1EMuHUCqxL1ZNQvxDV9g, linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA

Hello

On 11/02/2016 10:58 PM, Laurent Pinchart wrote:
> Hi Ramesh,
>
> On Wednesday 02 Nov 2016 09:00:00 Ramesh Shanmugasundaram wrote:
>> Hi Laurent,
>>
>> Any further thoughts on the SDR format please (especially the comment
>> below). I would appreciate your feedback.
>>
>>>> On Wednesday 12 Oct 2016 15:10:29 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
>>
>> [snip]
>>
>>>>> +
>>>>> +       -  start + 0:
>>>>> +
>>>>> +       -  I'\ :sub:`0[D13:D6]`
>>>>> +
>>>>> +       -  I'\ :sub:`0[D5:D0]`
>>>>> +
>>>>> +    -  .. row 2
>>>>> +
>>>>> +       -  start + buffer_size/2:
>>>>> +
>>>>> +       -  Q'\ :sub:`0[D13:D6]`
>>>>> +
>>>>> +       -  Q'\ :sub:`0[D5:D0]`
>>>>
>>>> The format looks planar, does it use one V4L2 plane (as does NV12) or
>>>> two V4L2 planes (as does NV12M) ? Same question for the other formats.
>>>
>>> Thank you for bringing up this topic. This is one of the key design
>>> dilemma.
>>>
>>> The I & Q data for these three SDR formats comes from two different DMA
>>> channels and hence two separate pointers -> we could say it is v4l2 multi-
>>> planar. Right now, I am making it look like a single plane by presenting
>>> the data in one single buffer ptr.
>>>
>>> For e.g. multi-planar SC16 format would look something like this
>>>
>>> <------------------------32bits---------------------->
>>> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 0
>>> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 4 ...
>>> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 0
>>> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 4
>>>
>>> My concerns are
>>>
>>> 1) These formats are not a standard as the video "Image Formats". These
>>> formats are possible when we use DRIF + MAX2175 combination. If we
>>> interface with a different tuner vendor, the above format(s) MAY/MAY NOT
>>> be re-usable. We do not know at this point. This is the main open item for
>>> discussion in the cover letter.
>
> If the formats are really device-specific then they should be documented
> accordingly and not made generic.
>
>>> 2) MPLANE support within V4L2 seems specific to video. Please correct me
>>> if this is wrong interpretation.
>>>
>>> - struct v4l2_format contains v4l2_sdr_format and
>>> v4l2_pix_format_mplane as members of union. Should I create a new
>>> v4l2_sdr_format_mplane? If I have to use v4l2_pix_format_mplane most of
>>> the video specific members would be unused (it would be similar to using
>>> v4l2_pix_format itself instead of v4l2_sdr_format)?
>
> I have no answer to that question as I'm not familiar with SDR. Antti, you've
> added v4l2_sdr_format to the API, what's your opinion ? Hans, as you've acked
> the patch, your input would be appreciated as well.

If I understood correctly this hardware provides I and Q samples via 
different channels and driver now combines those channels as a 
sequential IQ sample pairs. I have never seen any other than hw which 
provides IQ IQ IQ IQ ... IQ.
This is
I I I I ... I
Q Q Q Q ... Q
I am not very familiar with planars, but it sounds like it is correct 
approach. So I think should be added rather than emulate packet 
sequential format.

>
>>> - The above decision (accomodate SDR & MPLANE) needs to be
>>> propagated across the framework. Is this the preferred approach?
>>>
>>> It goes back to point (1). As of today, the change set for this combo
>>> (DRIF+MAX2175) introduces new SDR formats only. Should it add further
>>> SDR+MPLANE support to the framework as well?
>>>
>>> I would appreciate your suggestions on this regard.
>

regards
Antti

-- 
http://palosaari.fi/
--
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] 39+ messages in thread

* Re: [RFC 5/5] doc_rst: media: New SDR formats SC16, SC18 & SC20
@ 2016-11-03 20:36               ` Antti Palosaari
  0 siblings, 0 replies; 39+ messages in thread
From: Antti Palosaari @ 2016-11-03 20:36 UTC (permalink / raw)
  To: Laurent Pinchart, Ramesh Shanmugasundaram
  Cc: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus,
	Chris Paterson, geert, linux-media, devicetree,
	linux-renesas-soc

Hello

On 11/02/2016 10:58 PM, Laurent Pinchart wrote:
> Hi Ramesh,
>
> On Wednesday 02 Nov 2016 09:00:00 Ramesh Shanmugasundaram wrote:
>> Hi Laurent,
>>
>> Any further thoughts on the SDR format please (especially the comment
>> below). I would appreciate your feedback.
>>
>>>> On Wednesday 12 Oct 2016 15:10:29 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
>>
>> [snip]
>>
>>>>> +
>>>>> +       -  start + 0:
>>>>> +
>>>>> +       -  I'\ :sub:`0[D13:D6]`
>>>>> +
>>>>> +       -  I'\ :sub:`0[D5:D0]`
>>>>> +
>>>>> +    -  .. row 2
>>>>> +
>>>>> +       -  start + buffer_size/2:
>>>>> +
>>>>> +       -  Q'\ :sub:`0[D13:D6]`
>>>>> +
>>>>> +       -  Q'\ :sub:`0[D5:D0]`
>>>>
>>>> The format looks planar, does it use one V4L2 plane (as does NV12) or
>>>> two V4L2 planes (as does NV12M) ? Same question for the other formats.
>>>
>>> Thank you for bringing up this topic. This is one of the key design
>>> dilemma.
>>>
>>> The I & Q data for these three SDR formats comes from two different DMA
>>> channels and hence two separate pointers -> we could say it is v4l2 multi-
>>> planar. Right now, I am making it look like a single plane by presenting
>>> the data in one single buffer ptr.
>>>
>>> For e.g. multi-planar SC16 format would look something like this
>>>
>>> <------------------------32bits---------------------->
>>> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 0
>>> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 4 ...
>>> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 0
>>> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 4
>>>
>>> My concerns are
>>>
>>> 1) These formats are not a standard as the video "Image Formats". These
>>> formats are possible when we use DRIF + MAX2175 combination. If we
>>> interface with a different tuner vendor, the above format(s) MAY/MAY NOT
>>> be re-usable. We do not know at this point. This is the main open item for
>>> discussion in the cover letter.
>
> If the formats are really device-specific then they should be documented
> accordingly and not made generic.
>
>>> 2) MPLANE support within V4L2 seems specific to video. Please correct me
>>> if this is wrong interpretation.
>>>
>>> - struct v4l2_format contains v4l2_sdr_format and
>>> v4l2_pix_format_mplane as members of union. Should I create a new
>>> v4l2_sdr_format_mplane? If I have to use v4l2_pix_format_mplane most of
>>> the video specific members would be unused (it would be similar to using
>>> v4l2_pix_format itself instead of v4l2_sdr_format)?
>
> I have no answer to that question as I'm not familiar with SDR. Antti, you've
> added v4l2_sdr_format to the API, what's your opinion ? Hans, as you've acked
> the patch, your input would be appreciated as well.

If I understood correctly this hardware provides I and Q samples via 
different channels and driver now combines those channels as a 
sequential IQ sample pairs. I have never seen any other than hw which 
provides IQ IQ IQ IQ ... IQ.
This is
I I I I ... I
Q Q Q Q ... Q
I am not very familiar with planars, but it sounds like it is correct 
approach. So I think should be added rather than emulate packet 
sequential format.

>
>>> - The above decision (accomodate SDR & MPLANE) needs to be
>>> propagated across the framework. Is this the preferred approach?
>>>
>>> It goes back to point (1). As of today, the change set for this combo
>>> (DRIF+MAX2175) introduces new SDR formats only. Should it add further
>>> SDR+MPLANE support to the framework as well?
>>>
>>> I would appreciate your suggestions on this regard.
>

regards
Antti

-- 
http://palosaari.fi/

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

* RE: [RFC 5/5] doc_rst: media: New SDR formats SC16, SC18 & SC20
  2016-11-03 20:36               ` Antti Palosaari
  (?)
@ 2016-11-04  9:23               ` Ramesh Shanmugasundaram
  2016-11-10  8:08                 ` Laurent Pinchart
  -1 siblings, 1 reply; 39+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-11-04  9:23 UTC (permalink / raw)
  To: Antti Palosaari, Laurent Pinchart
  Cc: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus,
	Chris Paterson, geert, linux-media, devicetree,
	linux-renesas-soc

Hi Antti,

Thanks for the response.

> Subject: Re: [RFC 5/5] doc_rst: media: New SDR formats SC16, SC18 & SC20
> 
> Hello
> 
> On 11/02/2016 10:58 PM, Laurent Pinchart wrote:
> > Hi Ramesh,
> >
> > On Wednesday 02 Nov 2016 09:00:00 Ramesh Shanmugasundaram wrote:
> >> Hi Laurent,
> >>
> >> Any further thoughts on the SDR format please (especially the comment
> >> below). I would appreciate your feedback.
> >>
> >>>> On Wednesday 12 Oct 2016 15:10:29 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
> >>
> >> [snip]
> >>
> >>>>> +
> >>>>> +       -  start + 0:
> >>>>> +
> >>>>> +       -  I'\ :sub:`0[D13:D6]`
> >>>>> +
> >>>>> +       -  I'\ :sub:`0[D5:D0]`
> >>>>> +
> >>>>> +    -  .. row 2
> >>>>> +
> >>>>> +       -  start + buffer_size/2:
> >>>>> +
> >>>>> +       -  Q'\ :sub:`0[D13:D6]`
> >>>>> +
> >>>>> +       -  Q'\ :sub:`0[D5:D0]`
> >>>>
> >>>> The format looks planar, does it use one V4L2 plane (as does NV12)
> >>>> or two V4L2 planes (as does NV12M) ? Same question for the other
> formats.
> >>>
> >>> Thank you for bringing up this topic. This is one of the key design
> >>> dilemma.
> >>>
> >>> The I & Q data for these three SDR formats comes from two different
> >>> DMA channels and hence two separate pointers -> we could say it is
> >>> v4l2 multi- planar. Right now, I am making it look like a single
> >>> plane by presenting the data in one single buffer ptr.
> >>>
> >>> For e.g. multi-planar SC16 format would look something like this
> >>>
> >>> <------------------------32bits---------------------->
> >>> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 0
> >>> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 4
> ...
> >>> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 0
> >>> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 4
> >>>
> >>> My concerns are
> >>>
> >>> 1) These formats are not a standard as the video "Image Formats".
> >>> These formats are possible when we use DRIF + MAX2175 combination.
> >>> If we interface with a different tuner vendor, the above format(s)
> >>> MAY/MAY NOT be re-usable. We do not know at this point. This is the
> >>> main open item for discussion in the cover letter.
> >
> > If the formats are really device-specific then they should be
> > documented accordingly and not made generic.
> >
> >>> 2) MPLANE support within V4L2 seems specific to video. Please
> >>> correct me if this is wrong interpretation.
> >>>
> >>> - struct v4l2_format contains v4l2_sdr_format and
> >>> v4l2_pix_format_mplane as members of union. Should I create a new
> >>> v4l2_sdr_format_mplane? If I have to use v4l2_pix_format_mplane most
> >>> of the video specific members would be unused (it would be similar
> >>> to using v4l2_pix_format itself instead of v4l2_sdr_format)?
> >
> > I have no answer to that question as I'm not familiar with SDR. Antti,
> > you've added v4l2_sdr_format to the API, what's your opinion ? Hans,
> > as you've acked the patch, your input would be appreciated as well.
> 
> If I understood correctly this hardware provides I and Q samples via
> different channels and driver now combines those channels as a sequential
> IQ sample pairs. 

The driver combines the two buffer ptrs and present as one single buffer. For a buffer of size 200

ptr + 0   : I I I I ... I
ptr + 100 : Q Q Q Q ... Q

>I have never seen any other than hw which provides IQ IQ
> IQ IQ ... IQ.

There are some modes where this h/w combo can also do IQ IQ IQ pattern. Those modes are not added in the RFC patchset.

> This is
> I I I I ... I
> Q Q Q Q ... Q
> I am not very familiar with planars, but it sounds like it is correct
> approach. So I think should be added rather than emulate packet sequential
> format.

My understanding of V4L2 MPLANE constructs is limited to a quick code read only. At this point MPLANE support seems specific to video. SDR is defined as separate format like v4l2_pix_format. Questions would be - should we define new SDR_MPLANE? or merge SDR format with pix format & reuse existing MPLANE with some SDR extensions (if possible)? These seem big design decisions. Any suggestions please?

For my use case, MPLANE support does not seem to add significant benefit except it may be syntactically correct. I am doing cyclic DMA with a small set of h/w buffers and copying each stage to one mmapped vmalloc vb2_buffer at two offsets. If we add MPLANE support, it can be two non-contiguous buffer pointers. 

> 
> >
> >>> - The above decision (accomodate SDR & MPLANE) needs to be
> >>> propagated across the framework. Is this the preferred approach?
> >>>
> >>> It goes back to point (1). As of today, the change set for this
> >>> combo
> >>> (DRIF+MAX2175) introduces new SDR formats only. Should it add
> >>> further
> >>> SDR+MPLANE support to the framework as well?
> >>>
> >>> I would appreciate your suggestions on this regard.


Thanks,
Ramesh

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

* Re: [RFC 5/5] doc_rst: media: New SDR formats SC16, SC18 & SC20
  2016-11-04  9:23               ` Ramesh Shanmugasundaram
@ 2016-11-10  8:08                 ` Laurent Pinchart
  2016-11-11  4:54                   ` Antti Palosaari
  2016-11-11 13:53                   ` Hans Verkuil
  0 siblings, 2 replies; 39+ messages in thread
From: Laurent Pinchart @ 2016-11-10  8:08 UTC (permalink / raw)
  To: Ramesh Shanmugasundaram
  Cc: Antti Palosaari, robh+dt, mark.rutland, mchehab, hverkuil,
	sakari.ailus, Chris Paterson, geert, linux-media, devicetree,
	linux-renesas-soc

Antti, Hans, ping ? Please see below.

On Friday 04 Nov 2016 09:23:29 Ramesh Shanmugasundaram wrote:
> > On 11/02/2016 10:58 PM, Laurent Pinchart wrote:
> >> On Wednesday 02 Nov 2016 09:00:00 Ramesh Shanmugasundaram wrote:
> >>>>> On Wednesday 12 Oct 2016 15:10:29 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
> >>>
> >>> [snip]
> >>>
> >>>>>> +
> >>>>>> +       -  start + 0:
> >>>>>> +
> >>>>>> +       -  I'\ :sub:`0[D13:D6]`
> >>>>>> +
> >>>>>> +       -  I'\ :sub:`0[D5:D0]`
> >>>>>> +
> >>>>>> +    -  .. row 2
> >>>>>> +
> >>>>>> +       -  start + buffer_size/2:
> >>>>>> +
> >>>>>> +       -  Q'\ :sub:`0[D13:D6]`
> >>>>>> +
> >>>>>> +       -  Q'\ :sub:`0[D5:D0]`
> >>>>>
> >>>>>
> >>>>>
> >>>>> The format looks planar, does it use one V4L2 plane (as does NV12)
> >>>>> or two V4L2 planes (as does NV12M) ? Same question for the other
> >>>>> formats.
> >>>>
> >>>> Thank you for bringing up this topic. This is one of the key design
> >>>> dilemma.
> >>>>
> >>>> The I & Q data for these three SDR formats comes from two different
> >>>> DMA channels and hence two separate pointers -> we could say it is
> >>>> v4l2 multi- planar. Right now, I am making it look like a single
> >>>> plane by presenting the data in one single buffer ptr.
> >>>>
> >>>> For e.g. multi-planar SC16 format would look something like this
> >>>>
> >>>> <------------------------32bits---------------------->
> >>>> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 0
> >>>> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 4
> >>>> ...
> >>>> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 0
> >>>> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 4
> >>>>
> >>>> My concerns are
> >>>>
> >>>> 1) These formats are not a standard as the video "Image Formats".
> >>>> These formats are possible when we use DRIF + MAX2175 combination.
> >>>> If we interface with a different tuner vendor, the above format(s)
> >>>> MAY/MAY NOT be re-usable. We do not know at this point. This is the
> >>>> main open item for discussion in the cover letter.
> >>
> >> If the formats are really device-specific then they should be
> >> documented accordingly and not made generic.
> >>
> >>>> 2) MPLANE support within V4L2 seems specific to video. Please
> >>>> correct me if this is wrong interpretation.
> >>>>
> >>>> - struct v4l2_format contains v4l2_sdr_format and
> >>>> v4l2_pix_format_mplane as members of union. Should I create a new
> >>>> v4l2_sdr_format_mplane? If I have to use v4l2_pix_format_mplane most
> >>>> of the video specific members would be unused (it would be similar
> >>>> to using v4l2_pix_format itself instead of v4l2_sdr_format)?
> >>
> >> I have no answer to that question as I'm not familiar with SDR. Antti,
> >> you've added v4l2_sdr_format to the API, what's your opinion ? Hans,
> >> as you've acked the patch, your input would be appreciated as well.
> > 
> > If I understood correctly this hardware provides I and Q samples via
> > different channels and driver now combines those channels as a sequential
> > IQ sample pairs. 
> 
> The driver combines the two buffer ptrs and present as one single buffer.
> For a buffer of size 200
>
> ptr + 0   : I I I I ... I
> ptr + 100 : Q Q Q Q ... Q
> 
> > I have never seen any other than hw which provides IQ IQ IQ IQ ... IQ.
> 
> There are some modes where this h/w combo can also do IQ IQ IQ pattern.
> Those modes are not added in the RFC patchset.
> 
> > This is
> > I I I I ... I
> > Q Q Q Q ... Q
> > I am not very familiar with planars, but it sounds like it is correct
> > approach. So I think should be added rather than emulate packet
> > sequential format.
> 
> My understanding of V4L2 MPLANE constructs is limited to a quick code read
> only. At this point MPLANE support seems specific to video. SDR is defined
> as separate format like v4l2_pix_format. Questions would be - should we
> define new SDR_MPLANE? or merge SDR format with pix format & reuse existing
> MPLANE with some SDR extensions (if possible)? These seem big design
> decisions. Any suggestions please?
>
> For my use case, MPLANE support does not seem to add significant benefit
> except it may be syntactically correct. I am doing cyclic DMA with a small
> set of h/w buffers and copying each stage to one mmapped vmalloc vb2_buffer
> at two offsets. If we add MPLANE support, it can be two non-contiguous
> buffer pointers. 
>
> >>>> - The above decision (accomodate SDR & MPLANE) needs to be
> >>>> propagated across the framework. Is this the preferred approach?
> >>>>
> >>>> It goes back to point (1). As of today, the change set for this
> >>>> combo (DRIF+MAX2175) introduces new SDR formats only. Should it add
> >>>> further SDR+MPLANE support to the framework as well?
> >>>>
> >>>> I would appreciate your suggestions on this regard.

-- 
Regards,

Laurent Pinchart

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

* Re: [RFC 1/5] media: i2c: max2175: Add MAX2175 support
  2016-10-21 14:49     ` Ramesh Shanmugasundaram
@ 2016-11-10  8:46       ` Laurent Pinchart
  0 siblings, 0 replies; 39+ messages in thread
From: Laurent Pinchart @ 2016-11-10  8:46 UTC (permalink / raw)
  To: Ramesh Shanmugasundaram
  Cc: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope,
	Chris Paterson, geert, linux-media, devicetree,
	linux-renesas-soc

Hi Ramesh,

On Friday 21 Oct 2016 14:49:30 Ramesh Shanmugasundaram wrote:
> > On Wednesday 12 Oct 2016 15:10:25 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      |   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                | 1624 +++++++++++++
> >>  drivers/media/i2c/max2175/max2175.h                |  124 ++
> >>  7 files changed, 1826 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..2250d5f
> >> --- /dev/null
> >> +++ b/Documentation/devicetree/bindings/media/i2c/max2175.txt
> >> @@ -0,0 +1,60 @@

[snip]

> >> +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.
> > 
> > Would it be useful to make that property a phandle to the master tuner, to
> > give drivers a way to access the master ? I haven't checked whether there
> > could be use cases for that.
> 
> As of now, I cannot find any use case for it from the datasheet. In future,
> we could add if such need arise.

My point is that making the maxim,slave property a phandle now would allow 
handling such future cases without any change to the DT bindings.

[snip]

> > > diff --git a/drivers/media/i2c/max2175/max2175.c
> > > b/drivers/media/i2c/max2175/max2175.c new file mode 100644 index
> > > 0000000..71b60c2
> > > --- /dev/null
> > > +++ b/drivers/media/i2c/max2175/max2175.c

[snip]

> > > +static int max2175_poll_csm_ready(struct max2175_ctx *ctx) {
> > > +	return max2175_poll_timeout(ctx, 69, 1, 1, 0, 50);
> > 
> > Please define macros for register addresses and values, this is just
> > unreadable.
> 
> The Tuner provider is unwilling to disclose all register details. I agree on
> the readability issue with this restriction but this is somewhat true for
> some sensitive IPs in the media subsystem.

Is it the case that you don't have access to the information, or that you have 
been forbidden to disclose them by the tuner manufacturer ?

> > > +}

[snip]

> > > +static int max2175_set_lo_freq(struct max2175_ctx *ctx, u64 lo_freq)
> > > +{
> > > +	int ret;
> > > +	u32 lo_mult;
> > > +	u64 scaled_lo_freq;
> > > +	const u64 scale_factor = 1000000ULL;
> > > +	u64 scaled_npf, scaled_integer, scaled_fraction;
> > > +	u32 frac_desired, int_desired;
> > > +	u8 loband_bits, vcodiv_bits;
> > 
> > Do you really support frequencies above 4GHz ?
> 
> Nope.
> 
> If not most of the 64-bit
> 
> > values could be stored in 32 bits.
> 
> The 64bit variables are needed to extract the fractional part (upto 6 digit
> precision) out of floating point divisions (original user space code).

OK. The code would be more efficient if you made the scaling factor a power of 
two though. 1048576 could be a good value.

> >> +
> >> +	scaled_lo_freq = lo_freq;
> >> +	/* Scale to larger number for precision */
> >> +	scaled_lo_freq = scaled_lo_freq * scale_factor * 100;

I just noticed that you could write the two lines as

	scaled_lo_freq = lo_freq * scale_factor * 100;

By the way, why do you multiply by 100 here, and...

> >> +	mxm_dbg(ctx, "scaled lo_freq %llu lo_freq %llu\n",
> >> +		scaled_lo_freq, lo_freq);
> >> +
> >> +	if (MAX2175_IS_BAND_AM(ctx)) {
> >> +		if (max2175_get_bit(ctx, 5, 7) == 0)
> >> +			loband_bits = 0;
> >> +			vcodiv_bits = 0;
> >> +			lo_mult = 16;
> >> +	} else if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_FM) {
> >> +		if (lo_freq <= 74700000) {
> >> +			loband_bits = 0;
> >> +			vcodiv_bits = 0;
> >> +			lo_mult = 16;
> >> +		} else if ((lo_freq > 74700000) && (lo_freq <= 110000000)) {
> > 
> > No need for the inner parentheses.
> 
> Agreed.
> 
> >> +			loband_bits = 1;
> >> +			vcodiv_bits = 0;
> >> +		} else {
> >> +			loband_bits = 1;
> >> +			vcodiv_bits = 3;
> >> +		}
> >> +		lo_mult = 8;
> >> +	} else if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_VHF) {
> >> +		if (lo_freq <= 210000000) {
> >> +			loband_bits = 2;
> >> +			vcodiv_bits = 2;
> >> +		} else {
> >> +			loband_bits = 2;
> >> +			vcodiv_bits = 1;
> >> +		}
> >> +		lo_mult = 4;
> >> +	} else {
> >> +		loband_bits = 3;
> >> +		vcodiv_bits = 2;
> >> +		lo_mult = 2;
> >> +	}
> >> +
> >> +	if (max2175_get_bits(ctx, 5, 1, 0) == MAX2175_BAND_L)
> >> +		scaled_npf = (scaled_lo_freq / ctx->xtal_freq / lo_mult) /
> >> 100;
> >> +	else
> >> +		scaled_npf = (scaled_lo_freq / ctx->xtal_freq * lo_mult) /
> >> 100;

... divide by 100 here without using the value in-between nor after ?

> >> +
> >> +	scaled_integer = scaled_npf / scale_factor * scale_factor;
> >> +	int_desired = (u32)(scaled_npf / scale_factor);
> >> +	scaled_fraction = scaled_npf - scaled_integer;
> >> +	frac_desired = (u32)(scaled_fraction * 1048576 / scale_factor);
> >> +
> >> +	/* Check CSM is not busy */
> >> +	ret = max2175_poll_csm_ready(ctx);
> >> +	if (ret) {
> >> +		v4l2_err(ctx->client, "lo_freq: csm busy. freq %llu\n",
> >> +			 lo_freq);
> >> +		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_set_bits(ctx, 5, 3, 2, loband_bits);
> >> +	max2175_set_bits(ctx, 6, 7, 6, vcodiv_bits);
> >> +	max2175_set_bits(ctx, 1, 7, 0, (u8)(int_desired & 0xff));
> >> +	max2175_set_bits(ctx, 2, 3, 0, (u8)((frac_desired >> 16) & 0x1f));
> >> +	max2175_set_bits(ctx, 3, 7, 0, (u8)((frac_desired >> 8) & 0xff));
> >> +	max2175_set_bits(ctx, 4, 7, 0, (u8)(frac_desired & 0xff));
> >> +	/* Flush the above registers to device */
> >> +	max2175_flush_regstore(ctx, 1, 6);
> >> +	return ret;
> >> +}

[snip]

> >> +static int max2175_get_lna_gain(struct max2175_ctx *ctx) {
> >> +	int gain = 0;
> >> +	enum max2175_band band = max2175_get_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:
> >> +		v4l2_err(ctx->client, "invalid band %d to get rf gain\n",
> >> band);
> > 
> > Can this happen ?
> 
> Yes, there is "L-band". It is a paranoia check as I am testing by comparing
> logs sometimes :-(

OK. By the way, you could get rid of the gain variable by returning directly 
in the case statements.

> >> +		break;
> >> +	}
> >> +	return gain;
> >> +}

[snip]

> >> +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_INTEGER,
> > 
> > Should this be a menu control ?
> 
> Hmm... the strings would be named "i2s mode x"? Will that be OK?

How about describing the modes instead ? Are they standardized ?

> >> +	.min = 0,
> >> +	.max = 4,
> >> +	.step = 1,
> >> +	.def = 0,
> >> +};

[snip]

-- 
Regards,

Laurent Pinchart

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

* Re: [RFC 5/5] doc_rst: media: New SDR formats SC16, SC18 & SC20
  2016-11-10  8:08                 ` Laurent Pinchart
@ 2016-11-11  4:54                   ` Antti Palosaari
  2016-11-11 13:53                   ` Hans Verkuil
  1 sibling, 0 replies; 39+ messages in thread
From: Antti Palosaari @ 2016-11-11  4:54 UTC (permalink / raw)
  To: Laurent Pinchart, Ramesh Shanmugasundaram
  Cc: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus,
	Chris Paterson, geert, linux-media, devicetree,
	linux-renesas-soc

Hello

On 11/10/2016 10:08 AM, Laurent Pinchart wrote:
> Antti, Hans, ping ? Please see below.
>
> On Friday 04 Nov 2016 09:23:29 Ramesh Shanmugasundaram wrote:
>>> On 11/02/2016 10:58 PM, Laurent Pinchart wrote:
>>>> On Wednesday 02 Nov 2016 09:00:00 Ramesh Shanmugasundaram wrote:
>>>>>>> On Wednesday 12 Oct 2016 15:10:29 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
>>>>>
>>>>> [snip]
>>>>>
>>>>>>>> +
>>>>>>>> +       -  start + 0:
>>>>>>>> +
>>>>>>>> +       -  I'\ :sub:`0[D13:D6]`
>>>>>>>> +
>>>>>>>> +       -  I'\ :sub:`0[D5:D0]`
>>>>>>>> +
>>>>>>>> +    -  .. row 2
>>>>>>>> +
>>>>>>>> +       -  start + buffer_size/2:
>>>>>>>> +
>>>>>>>> +       -  Q'\ :sub:`0[D13:D6]`
>>>>>>>> +
>>>>>>>> +       -  Q'\ :sub:`0[D5:D0]`
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> The format looks planar, does it use one V4L2 plane (as does NV12)
>>>>>>> or two V4L2 planes (as does NV12M) ? Same question for the other
>>>>>>> formats.
>>>>>>
>>>>>> Thank you for bringing up this topic. This is one of the key design
>>>>>> dilemma.
>>>>>>
>>>>>> The I & Q data for these three SDR formats comes from two different
>>>>>> DMA channels and hence two separate pointers -> we could say it is
>>>>>> v4l2 multi- planar. Right now, I am making it look like a single
>>>>>> plane by presenting the data in one single buffer ptr.
>>>>>>
>>>>>> For e.g. multi-planar SC16 format would look something like this
>>>>>>
>>>>>> <------------------------32bits---------------------->
>>>>>> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 0
>>>>>> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 4
>>>>>> ...
>>>>>> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 0
>>>>>> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 4
>>>>>>
>>>>>> My concerns are
>>>>>>
>>>>>> 1) These formats are not a standard as the video "Image Formats".
>>>>>> These formats are possible when we use DRIF + MAX2175 combination.
>>>>>> If we interface with a different tuner vendor, the above format(s)
>>>>>> MAY/MAY NOT be re-usable. We do not know at this point. This is the
>>>>>> main open item for discussion in the cover letter.
>>>>
>>>> If the formats are really device-specific then they should be
>>>> documented accordingly and not made generic.
>>>>
>>>>>> 2) MPLANE support within V4L2 seems specific to video. Please
>>>>>> correct me if this is wrong interpretation.
>>>>>>
>>>>>> - struct v4l2_format contains v4l2_sdr_format and
>>>>>> v4l2_pix_format_mplane as members of union. Should I create a new
>>>>>> v4l2_sdr_format_mplane? If I have to use v4l2_pix_format_mplane most
>>>>>> of the video specific members would be unused (it would be similar
>>>>>> to using v4l2_pix_format itself instead of v4l2_sdr_format)?
>>>>
>>>> I have no answer to that question as I'm not familiar with SDR. Antti,
>>>> you've added v4l2_sdr_format to the API, what's your opinion ? Hans,
>>>> as you've acked the patch, your input would be appreciated as well.
>>>
>>> If I understood correctly this hardware provides I and Q samples via
>>> different channels and driver now combines those channels as a sequential
>>> IQ sample pairs.
>>
>> The driver combines the two buffer ptrs and present as one single buffer.
>> For a buffer of size 200
>>
>> ptr + 0   : I I I I ... I
>> ptr + 100 : Q Q Q Q ... Q
>>
>>> I have never seen any other than hw which provides IQ IQ IQ IQ ... IQ.
>>
>> There are some modes where this h/w combo can also do IQ IQ IQ pattern.
>> Those modes are not added in the RFC patchset.
>>
>>> This is
>>> I I I I ... I
>>> Q Q Q Q ... Q
>>> I am not very familiar with planars, but it sounds like it is correct
>>> approach. So I think should be added rather than emulate packet
>>> sequential format.
>>
>> My understanding of V4L2 MPLANE constructs is limited to a quick code read
>> only. At this point MPLANE support seems specific to video. SDR is defined
>> as separate format like v4l2_pix_format. Questions would be - should we
>> define new SDR_MPLANE? or merge SDR format with pix format & reuse existing
>> MPLANE with some SDR extensions (if possible)? These seem big design
>> decisions. Any suggestions please?

struct v4l2_format contains union that has own format definition for 
video, video mplane and sdr (+many others). Basically on api there is 
own definitions for each type, so I think possible sdr mplane should be 
similarly own types and definitions.

>> For my use case, MPLANE support does not seem to add significant benefit
>> except it may be syntactically correct. I am doing cyclic DMA with a small
>> set of h/w buffers and copying each stage to one mmapped vmalloc vb2_buffer
>> at two offsets. If we add MPLANE support, it can be two non-contiguous
>> buffer pointers.

If there is no clear idea about need of mplane then that's also fine for me.

And whole mplane concept is new for me. I have never played with any 
v4l2 video formats nor mplane video formats.

I would still like to hear what Hans think about adding mplane.

>>
>>>>>> - The above decision (accomodate SDR & MPLANE) needs to be
>>>>>> propagated across the framework. Is this the preferred approach?
>>>>>>
>>>>>> It goes back to point (1). As of today, the change set for this
>>>>>> combo (DRIF+MAX2175) introduces new SDR formats only. Should it add
>>>>>> further SDR+MPLANE support to the framework as well?
>>>>>>
>>>>>> I would appreciate your suggestions on this regard.
>

regards
Antti

-- 
http://palosaari.fi/

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

* Re: [RFC 5/5] doc_rst: media: New SDR formats SC16, SC18 & SC20
  2016-11-10  8:08                 ` Laurent Pinchart
  2016-11-11  4:54                   ` Antti Palosaari
@ 2016-11-11 13:53                   ` Hans Verkuil
       [not found]                     ` <8438b944-216e-3237-c312-92a674fd4541-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
  1 sibling, 1 reply; 39+ messages in thread
From: Hans Verkuil @ 2016-11-11 13:53 UTC (permalink / raw)
  To: Laurent Pinchart, Ramesh Shanmugasundaram
  Cc: Antti Palosaari, robh+dt, mark.rutland, mchehab, sakari.ailus,
	Chris Paterson, geert, linux-media, devicetree,
	linux-renesas-soc

On 11/10/2016 09:08 AM, Laurent Pinchart wrote:
> Antti, Hans, ping ? Please see below.
> 
> On Friday 04 Nov 2016 09:23:29 Ramesh Shanmugasundaram wrote:
>>> On 11/02/2016 10:58 PM, Laurent Pinchart wrote:
>>>> On Wednesday 02 Nov 2016 09:00:00 Ramesh Shanmugasundaram wrote:
>>>>>>> On Wednesday 12 Oct 2016 15:10:29 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
>>>>>
>>>>> [snip]
>>>>>
>>>>>>>> +
>>>>>>>> +       -  start + 0:
>>>>>>>> +
>>>>>>>> +       -  I'\ :sub:`0[D13:D6]`
>>>>>>>> +
>>>>>>>> +       -  I'\ :sub:`0[D5:D0]`
>>>>>>>> +
>>>>>>>> +    -  .. row 2
>>>>>>>> +
>>>>>>>> +       -  start + buffer_size/2:
>>>>>>>> +
>>>>>>>> +       -  Q'\ :sub:`0[D13:D6]`
>>>>>>>> +
>>>>>>>> +       -  Q'\ :sub:`0[D5:D0]`
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> The format looks planar, does it use one V4L2 plane (as does NV12)
>>>>>>> or two V4L2 planes (as does NV12M) ? Same question for the other
>>>>>>> formats.
>>>>>>
>>>>>> Thank you for bringing up this topic. This is one of the key design
>>>>>> dilemma.
>>>>>>
>>>>>> The I & Q data for these three SDR formats comes from two different
>>>>>> DMA channels and hence two separate pointers -> we could say it is
>>>>>> v4l2 multi- planar. Right now, I am making it look like a single
>>>>>> plane by presenting the data in one single buffer ptr.
>>>>>>
>>>>>> For e.g. multi-planar SC16 format would look something like this
>>>>>>
>>>>>> <------------------------32bits---------------------->
>>>>>> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 0
>>>>>> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 4
>>>>>> ...
>>>>>> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 0
>>>>>> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 4
>>>>>>
>>>>>> My concerns are
>>>>>>
>>>>>> 1) These formats are not a standard as the video "Image Formats".
>>>>>> These formats are possible when we use DRIF + MAX2175 combination.
>>>>>> If we interface with a different tuner vendor, the above format(s)
>>>>>> MAY/MAY NOT be re-usable. We do not know at this point. This is the
>>>>>> main open item for discussion in the cover letter.
>>>>
>>>> If the formats are really device-specific then they should be
>>>> documented accordingly and not made generic.
>>>>
>>>>>> 2) MPLANE support within V4L2 seems specific to video. Please
>>>>>> correct me if this is wrong interpretation.
>>>>>>
>>>>>> - struct v4l2_format contains v4l2_sdr_format and
>>>>>> v4l2_pix_format_mplane as members of union. Should I create a new
>>>>>> v4l2_sdr_format_mplane? If I have to use v4l2_pix_format_mplane most
>>>>>> of the video specific members would be unused (it would be similar
>>>>>> to using v4l2_pix_format itself instead of v4l2_sdr_format)?
>>>>
>>>> I have no answer to that question as I'm not familiar with SDR. Antti,
>>>> you've added v4l2_sdr_format to the API, what's your opinion ? Hans,
>>>> as you've acked the patch, your input would be appreciated as well.
>>>
>>> If I understood correctly this hardware provides I and Q samples via
>>> different channels and driver now combines those channels as a sequential
>>> IQ sample pairs. 
>>
>> The driver combines the two buffer ptrs and present as one single buffer.
>> For a buffer of size 200
>>
>> ptr + 0   : I I I I ... I
>> ptr + 100 : Q Q Q Q ... Q
>>
>>> I have never seen any other than hw which provides IQ IQ IQ IQ ... IQ.
>>
>> There are some modes where this h/w combo can also do IQ IQ IQ pattern.
>> Those modes are not added in the RFC patchset.
>>
>>> This is
>>> I I I I ... I
>>> Q Q Q Q ... Q
>>> I am not very familiar with planars, but it sounds like it is correct
>>> approach. So I think should be added rather than emulate packet
>>> sequential format.
>>
>> My understanding of V4L2 MPLANE constructs is limited to a quick code read
>> only. At this point MPLANE support seems specific to video. SDR is defined
>> as separate format like v4l2_pix_format. Questions would be - should we
>> define new SDR_MPLANE? or merge SDR format with pix format & reuse existing
>> MPLANE with some SDR extensions (if possible)? These seem big design
>> decisions. Any suggestions please?
>>
>> For my use case, MPLANE support does not seem to add significant benefit
>> except it may be syntactically correct. I am doing cyclic DMA with a small
>> set of h/w buffers and copying each stage to one mmapped vmalloc vb2_buffer
>> at two offsets. If we add MPLANE support, it can be two non-contiguous
>> buffer pointers. 
>>
>>>>>> - The above decision (accomodate SDR & MPLANE) needs to be
>>>>>> propagated across the framework. Is this the preferred approach?
>>>>>>
>>>>>> It goes back to point (1). As of today, the change set for this
>>>>>> combo (DRIF+MAX2175) introduces new SDR formats only. Should it add
>>>>>> further SDR+MPLANE support to the framework as well?
>>>>>>
>>>>>> I would appreciate your suggestions on this regard.
> 

Some terminology first:

Planar formats separate the data into different memory areas: in this case
one part is all I and one part is all Q. This as opposed to interleaved
formats (IQIQIQIQ....).

As long as both planes fit in the same buffer all is fine. Since that is
the case here there is no need to introduce a new MPLANE API.

The MPLANE API was added for video to handle cases where the two planes
had to be in two different non-contiguous buffers.

So instead of passing one buffer pointer, you need to pass two or more
buffer pointers.

In hindsight we should have called it the MBUFFER API.

Oh well...

Anyway, since there is no problem here apparently to keep both planes
in one buffer there is also no need to introduce a SDR_MPLANE.

Regards,

	Hans

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

* Re: [RFC 5/5] doc_rst: media: New SDR formats SC16, SC18 & SC20
  2016-11-11 13:53                   ` Hans Verkuil
@ 2016-11-11 13:57                         ` Laurent Pinchart
  0 siblings, 0 replies; 39+ messages in thread
From: Laurent Pinchart @ 2016-11-11 13:57 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Ramesh Shanmugasundaram, Antti Palosaari,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	mchehab-DgEjT+Ai2ygdnm+yROfE0A,
	sakari.ailus-VuQAYsv1563Yd54FQh9/CA, Chris Paterson,
	geert-Td1EMuHUCqxL1ZNQvxDV9g, linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA

Hi Hans,

On Friday 11 Nov 2016 14:53:58 Hans Verkuil wrote:
> On 11/10/2016 09:08 AM, Laurent Pinchart wrote:
> > Antti, Hans, ping ? Please see below.
> > 
> > On Friday 04 Nov 2016 09:23:29 Ramesh Shanmugasundaram wrote:
> >>> On 11/02/2016 10:58 PM, Laurent Pinchart wrote:
> >>>> On Wednesday 02 Nov 2016 09:00:00 Ramesh Shanmugasundaram wrote:
> >>>>>>> On Wednesday 12 Oct 2016 15:10:29 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
> >>>>> 
> >>>>> [snip]
> >>>>> 
> >>>>>>>> +
> >>>>>>>> +       -  start + 0:
> >>>>>>>> +
> >>>>>>>> +       -  I'\ :sub:`0[D13:D6]`
> >>>>>>>> +
> >>>>>>>> +       -  I'\ :sub:`0[D5:D0]`
> >>>>>>>> +
> >>>>>>>> +    -  .. row 2
> >>>>>>>> +
> >>>>>>>> +       -  start + buffer_size/2:
> >>>>>>>> +
> >>>>>>>> +       -  Q'\ :sub:`0[D13:D6]`
> >>>>>>>> +
> >>>>>>>> +       -  Q'\ :sub:`0[D5:D0]`
> >>>>>>> 
> >>>>>>> The format looks planar, does it use one V4L2 plane (as does NV12)
> >>>>>>> or two V4L2 planes (as does NV12M) ? Same question for the other
> >>>>>>> formats.
> >>>>>> 
> >>>>>> Thank you for bringing up this topic. This is one of the key design
> >>>>>> dilemma.
> >>>>>> 
> >>>>>> The I & Q data for these three SDR formats comes from two different
> >>>>>> DMA channels and hence two separate pointers -> we could say it is
> >>>>>> v4l2 multi- planar. Right now, I am making it look like a single
> >>>>>> plane by presenting the data in one single buffer ptr.
> >>>>>> 
> >>>>>> For e.g. multi-planar SC16 format would look something like this
> >>>>>> 
> >>>>>> <------------------------32bits---------------------->
> >>>>>> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 0
> >>>>>> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 4
> >>>>>> ...
> >>>>>> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 0
> >>>>>> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 4
> >>>>>> 
> >>>>>> My concerns are
> >>>>>> 
> >>>>>> 1) These formats are not a standard as the video "Image Formats".
> >>>>>> These formats are possible when we use DRIF + MAX2175 combination.
> >>>>>> If we interface with a different tuner vendor, the above format(s)
> >>>>>> MAY/MAY NOT be re-usable. We do not know at this point. This is the
> >>>>>> main open item for discussion in the cover letter.
> >>>> 
> >>>> If the formats are really device-specific then they should be
> >>>> documented accordingly and not made generic.
> >>>> 
> >>>>>> 2) MPLANE support within V4L2 seems specific to video. Please
> >>>>>> correct me if this is wrong interpretation.
> >>>>>> 
> >>>>>> - struct v4l2_format contains v4l2_sdr_format and
> >>>>>> v4l2_pix_format_mplane as members of union. Should I create a new
> >>>>>> v4l2_sdr_format_mplane? If I have to use v4l2_pix_format_mplane most
> >>>>>> of the video specific members would be unused (it would be similar
> >>>>>> to using v4l2_pix_format itself instead of v4l2_sdr_format)?
> >>>> 
> >>>> I have no answer to that question as I'm not familiar with SDR. Antti,
> >>>> you've added v4l2_sdr_format to the API, what's your opinion ? Hans,
> >>>> as you've acked the patch, your input would be appreciated as well.
> >>> 
> >>> If I understood correctly this hardware provides I and Q samples via
> >>> different channels and driver now combines those channels as a
> >>> sequential
> >>> IQ sample pairs.
> >> 
> >> The driver combines the two buffer ptrs and present as one single buffer.
> >> For a buffer of size 200
> >> 
> >> ptr + 0   : I I I I ... I
> >> ptr + 100 : Q Q Q Q ... Q
> >> 
> >>> I have never seen any other than hw which provides IQ IQ IQ IQ ... IQ.
> >> 
> >> There are some modes where this h/w combo can also do IQ IQ IQ pattern.
> >> Those modes are not added in the RFC patchset.
> >> 
> >>> This is
> >>> I I I I ... I
> >>> Q Q Q Q ... Q
> >>> I am not very familiar with planars, but it sounds like it is correct
> >>> approach. So I think should be added rather than emulate packet
> >>> sequential format.
> >> 
> >> My understanding of V4L2 MPLANE constructs is limited to a quick code
> >> read
> >> only. At this point MPLANE support seems specific to video. SDR is
> >> defined
> >> as separate format like v4l2_pix_format. Questions would be - should we
> >> define new SDR_MPLANE? or merge SDR format with pix format & reuse
> >> existing
> >> MPLANE with some SDR extensions (if possible)? These seem big design
> >> decisions. Any suggestions please?
> >> 
> >> For my use case, MPLANE support does not seem to add significant benefit
> >> except it may be syntactically correct. I am doing cyclic DMA with a
> >> small
> >> set of h/w buffers and copying each stage to one mmapped vmalloc
> >> vb2_buffer
> >> at two offsets. If we add MPLANE support, it can be two non-contiguous
> >> buffer pointers.
> >> 
> >>>>>> - The above decision (accomodate SDR & MPLANE) needs to be
> >>>>>> propagated across the framework. Is this the preferred approach?
> >>>>>> 
> >>>>>> It goes back to point (1). As of today, the change set for this
> >>>>>> combo (DRIF+MAX2175) introduces new SDR formats only. Should it add
> >>>>>> further SDR+MPLANE support to the framework as well?
> >>>>>> 
> >>>>>> I would appreciate your suggestions on this regard.
> 
> Some terminology first:
> 
> Planar formats separate the data into different memory areas: in this case
> one part is all I and one part is all Q. This as opposed to interleaved
> formats (IQIQIQIQ....).
> 
> As long as both planes fit in the same buffer all is fine. Since that is
> the case here there is no need to introduce a new MPLANE API.
> 
> The MPLANE API was added for video to handle cases where the two planes
> had to be in two different non-contiguous buffers.

Not only that, it can also be used for cases where storing the two planes in 
separate buffers can be beneficial, even if a single contiguous buffer could 
work.

> So instead of passing one buffer pointer, you need to pass two or more
> buffer pointers.
> 
> In hindsight we should have called it the MBUFFER API.

The name was badly chosen, yes.

> Oh well...
> 
> Anyway, since there is no problem here apparently to keep both planes
> in one buffer there is also no need to introduce a SDR_MPLANE.

The question here is whether there could be a benefit in separating I and Q 
data in two buffers compared to storing them in the same buffer.

-- 
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] 39+ messages in thread

* Re: [RFC 5/5] doc_rst: media: New SDR formats SC16, SC18 & SC20
@ 2016-11-11 13:57                         ` Laurent Pinchart
  0 siblings, 0 replies; 39+ messages in thread
From: Laurent Pinchart @ 2016-11-11 13:57 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Ramesh Shanmugasundaram, Antti Palosaari, robh+dt, mark.rutland,
	mchehab, sakari.ailus, Chris Paterson, geert, linux-media,
	devicetree, linux-renesas-soc

Hi Hans,

On Friday 11 Nov 2016 14:53:58 Hans Verkuil wrote:
> On 11/10/2016 09:08 AM, Laurent Pinchart wrote:
> > Antti, Hans, ping ? Please see below.
> > 
> > On Friday 04 Nov 2016 09:23:29 Ramesh Shanmugasundaram wrote:
> >>> On 11/02/2016 10:58 PM, Laurent Pinchart wrote:
> >>>> On Wednesday 02 Nov 2016 09:00:00 Ramesh Shanmugasundaram wrote:
> >>>>>>> On Wednesday 12 Oct 2016 15:10:29 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
> >>>>> 
> >>>>> [snip]
> >>>>> 
> >>>>>>>> +
> >>>>>>>> +       -  start + 0:
> >>>>>>>> +
> >>>>>>>> +       -  I'\ :sub:`0[D13:D6]`
> >>>>>>>> +
> >>>>>>>> +       -  I'\ :sub:`0[D5:D0]`
> >>>>>>>> +
> >>>>>>>> +    -  .. row 2
> >>>>>>>> +
> >>>>>>>> +       -  start + buffer_size/2:
> >>>>>>>> +
> >>>>>>>> +       -  Q'\ :sub:`0[D13:D6]`
> >>>>>>>> +
> >>>>>>>> +       -  Q'\ :sub:`0[D5:D0]`
> >>>>>>> 
> >>>>>>> The format looks planar, does it use one V4L2 plane (as does NV12)
> >>>>>>> or two V4L2 planes (as does NV12M) ? Same question for the other
> >>>>>>> formats.
> >>>>>> 
> >>>>>> Thank you for bringing up this topic. This is one of the key design
> >>>>>> dilemma.
> >>>>>> 
> >>>>>> The I & Q data for these three SDR formats comes from two different
> >>>>>> DMA channels and hence two separate pointers -> we could say it is
> >>>>>> v4l2 multi- planar. Right now, I am making it look like a single
> >>>>>> plane by presenting the data in one single buffer ptr.
> >>>>>> 
> >>>>>> For e.g. multi-planar SC16 format would look something like this
> >>>>>> 
> >>>>>> <------------------------32bits---------------------->
> >>>>>> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 0
> >>>>>> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 4
> >>>>>> ...
> >>>>>> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 0
> >>>>>> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 4
> >>>>>> 
> >>>>>> My concerns are
> >>>>>> 
> >>>>>> 1) These formats are not a standard as the video "Image Formats".
> >>>>>> These formats are possible when we use DRIF + MAX2175 combination.
> >>>>>> If we interface with a different tuner vendor, the above format(s)
> >>>>>> MAY/MAY NOT be re-usable. We do not know at this point. This is the
> >>>>>> main open item for discussion in the cover letter.
> >>>> 
> >>>> If the formats are really device-specific then they should be
> >>>> documented accordingly and not made generic.
> >>>> 
> >>>>>> 2) MPLANE support within V4L2 seems specific to video. Please
> >>>>>> correct me if this is wrong interpretation.
> >>>>>> 
> >>>>>> - struct v4l2_format contains v4l2_sdr_format and
> >>>>>> v4l2_pix_format_mplane as members of union. Should I create a new
> >>>>>> v4l2_sdr_format_mplane? If I have to use v4l2_pix_format_mplane most
> >>>>>> of the video specific members would be unused (it would be similar
> >>>>>> to using v4l2_pix_format itself instead of v4l2_sdr_format)?
> >>>> 
> >>>> I have no answer to that question as I'm not familiar with SDR. Antti,
> >>>> you've added v4l2_sdr_format to the API, what's your opinion ? Hans,
> >>>> as you've acked the patch, your input would be appreciated as well.
> >>> 
> >>> If I understood correctly this hardware provides I and Q samples via
> >>> different channels and driver now combines those channels as a
> >>> sequential
> >>> IQ sample pairs.
> >> 
> >> The driver combines the two buffer ptrs and present as one single buffer.
> >> For a buffer of size 200
> >> 
> >> ptr + 0   : I I I I ... I
> >> ptr + 100 : Q Q Q Q ... Q
> >> 
> >>> I have never seen any other than hw which provides IQ IQ IQ IQ ... IQ.
> >> 
> >> There are some modes where this h/w combo can also do IQ IQ IQ pattern.
> >> Those modes are not added in the RFC patchset.
> >> 
> >>> This is
> >>> I I I I ... I
> >>> Q Q Q Q ... Q
> >>> I am not very familiar with planars, but it sounds like it is correct
> >>> approach. So I think should be added rather than emulate packet
> >>> sequential format.
> >> 
> >> My understanding of V4L2 MPLANE constructs is limited to a quick code
> >> read
> >> only. At this point MPLANE support seems specific to video. SDR is
> >> defined
> >> as separate format like v4l2_pix_format. Questions would be - should we
> >> define new SDR_MPLANE? or merge SDR format with pix format & reuse
> >> existing
> >> MPLANE with some SDR extensions (if possible)? These seem big design
> >> decisions. Any suggestions please?
> >> 
> >> For my use case, MPLANE support does not seem to add significant benefit
> >> except it may be syntactically correct. I am doing cyclic DMA with a
> >> small
> >> set of h/w buffers and copying each stage to one mmapped vmalloc
> >> vb2_buffer
> >> at two offsets. If we add MPLANE support, it can be two non-contiguous
> >> buffer pointers.
> >> 
> >>>>>> - The above decision (accomodate SDR & MPLANE) needs to be
> >>>>>> propagated across the framework. Is this the preferred approach?
> >>>>>> 
> >>>>>> It goes back to point (1). As of today, the change set for this
> >>>>>> combo (DRIF+MAX2175) introduces new SDR formats only. Should it add
> >>>>>> further SDR+MPLANE support to the framework as well?
> >>>>>> 
> >>>>>> I would appreciate your suggestions on this regard.
> 
> Some terminology first:
> 
> Planar formats separate the data into different memory areas: in this case
> one part is all I and one part is all Q. This as opposed to interleaved
> formats (IQIQIQIQ....).
> 
> As long as both planes fit in the same buffer all is fine. Since that is
> the case here there is no need to introduce a new MPLANE API.
> 
> The MPLANE API was added for video to handle cases where the two planes
> had to be in two different non-contiguous buffers.

Not only that, it can also be used for cases where storing the two planes in 
separate buffers can be beneficial, even if a single contiguous buffer could 
work.

> So instead of passing one buffer pointer, you need to pass two or more
> buffer pointers.
> 
> In hindsight we should have called it the MBUFFER API.

The name was badly chosen, yes.

> Oh well...
> 
> Anyway, since there is no problem here apparently to keep both planes
> in one buffer there is also no need to introduce a SDR_MPLANE.

The question here is whether there could be a benefit in separating I and Q 
data in two buffers compared to storing them in the same buffer.

-- 
Regards,

Laurent Pinchart


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

* Re: [RFC 5/5] doc_rst: media: New SDR formats SC16, SC18 & SC20
  2016-11-11 13:57                         ` Laurent Pinchart
  (?)
@ 2016-11-11 14:00                         ` Hans Verkuil
       [not found]                           ` <fb15b6f3-6c5c-0922-8655-aabd4799d158-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
  -1 siblings, 1 reply; 39+ messages in thread
From: Hans Verkuil @ 2016-11-11 14:00 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Ramesh Shanmugasundaram, Antti Palosaari, robh+dt, mark.rutland,
	mchehab, sakari.ailus, Chris Paterson, geert, linux-media,
	devicetree, linux-renesas-soc

On 11/11/2016 02:57 PM, Laurent Pinchart wrote:
> Hi Hans,
> 
> On Friday 11 Nov 2016 14:53:58 Hans Verkuil wrote:
>> On 11/10/2016 09:08 AM, Laurent Pinchart wrote:
>>> Antti, Hans, ping ? Please see below.
>>>
>>> On Friday 04 Nov 2016 09:23:29 Ramesh Shanmugasundaram wrote:
>>>>> On 11/02/2016 10:58 PM, Laurent Pinchart wrote:
>>>>>> On Wednesday 02 Nov 2016 09:00:00 Ramesh Shanmugasundaram wrote:
>>>>>>>>> On Wednesday 12 Oct 2016 15:10:29 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
>>>>>>>
>>>>>>> [snip]
>>>>>>>
>>>>>>>>>> +
>>>>>>>>>> +       -  start + 0:
>>>>>>>>>> +
>>>>>>>>>> +       -  I'\ :sub:`0[D13:D6]`
>>>>>>>>>> +
>>>>>>>>>> +       -  I'\ :sub:`0[D5:D0]`
>>>>>>>>>> +
>>>>>>>>>> +    -  .. row 2
>>>>>>>>>> +
>>>>>>>>>> +       -  start + buffer_size/2:
>>>>>>>>>> +
>>>>>>>>>> +       -  Q'\ :sub:`0[D13:D6]`
>>>>>>>>>> +
>>>>>>>>>> +       -  Q'\ :sub:`0[D5:D0]`
>>>>>>>>>
>>>>>>>>> The format looks planar, does it use one V4L2 plane (as does NV12)
>>>>>>>>> or two V4L2 planes (as does NV12M) ? Same question for the other
>>>>>>>>> formats.
>>>>>>>>
>>>>>>>> Thank you for bringing up this topic. This is one of the key design
>>>>>>>> dilemma.
>>>>>>>>
>>>>>>>> The I & Q data for these three SDR formats comes from two different
>>>>>>>> DMA channels and hence two separate pointers -> we could say it is
>>>>>>>> v4l2 multi- planar. Right now, I am making it look like a single
>>>>>>>> plane by presenting the data in one single buffer ptr.
>>>>>>>>
>>>>>>>> For e.g. multi-planar SC16 format would look something like this
>>>>>>>>
>>>>>>>> <------------------------32bits---------------------->
>>>>>>>> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 0
>>>>>>>> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0 + 4
>>>>>>>> ...
>>>>>>>> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 0
>>>>>>>> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1 + 4
>>>>>>>>
>>>>>>>> My concerns are
>>>>>>>>
>>>>>>>> 1) These formats are not a standard as the video "Image Formats".
>>>>>>>> These formats are possible when we use DRIF + MAX2175 combination.
>>>>>>>> If we interface with a different tuner vendor, the above format(s)
>>>>>>>> MAY/MAY NOT be re-usable. We do not know at this point. This is the
>>>>>>>> main open item for discussion in the cover letter.
>>>>>>
>>>>>> If the formats are really device-specific then they should be
>>>>>> documented accordingly and not made generic.
>>>>>>
>>>>>>>> 2) MPLANE support within V4L2 seems specific to video. Please
>>>>>>>> correct me if this is wrong interpretation.
>>>>>>>>
>>>>>>>> - struct v4l2_format contains v4l2_sdr_format and
>>>>>>>> v4l2_pix_format_mplane as members of union. Should I create a new
>>>>>>>> v4l2_sdr_format_mplane? If I have to use v4l2_pix_format_mplane most
>>>>>>>> of the video specific members would be unused (it would be similar
>>>>>>>> to using v4l2_pix_format itself instead of v4l2_sdr_format)?
>>>>>>
>>>>>> I have no answer to that question as I'm not familiar with SDR. Antti,
>>>>>> you've added v4l2_sdr_format to the API, what's your opinion ? Hans,
>>>>>> as you've acked the patch, your input would be appreciated as well.
>>>>>
>>>>> If I understood correctly this hardware provides I and Q samples via
>>>>> different channels and driver now combines those channels as a
>>>>> sequential
>>>>> IQ sample pairs.
>>>>
>>>> The driver combines the two buffer ptrs and present as one single buffer.
>>>> For a buffer of size 200
>>>>
>>>> ptr + 0   : I I I I ... I
>>>> ptr + 100 : Q Q Q Q ... Q
>>>>
>>>>> I have never seen any other than hw which provides IQ IQ IQ IQ ... IQ.
>>>>
>>>> There are some modes where this h/w combo can also do IQ IQ IQ pattern.
>>>> Those modes are not added in the RFC patchset.
>>>>
>>>>> This is
>>>>> I I I I ... I
>>>>> Q Q Q Q ... Q
>>>>> I am not very familiar with planars, but it sounds like it is correct
>>>>> approach. So I think should be added rather than emulate packet
>>>>> sequential format.
>>>>
>>>> My understanding of V4L2 MPLANE constructs is limited to a quick code
>>>> read
>>>> only. At this point MPLANE support seems specific to video. SDR is
>>>> defined
>>>> as separate format like v4l2_pix_format. Questions would be - should we
>>>> define new SDR_MPLANE? or merge SDR format with pix format & reuse
>>>> existing
>>>> MPLANE with some SDR extensions (if possible)? These seem big design
>>>> decisions. Any suggestions please?
>>>>
>>>> For my use case, MPLANE support does not seem to add significant benefit
>>>> except it may be syntactically correct. I am doing cyclic DMA with a
>>>> small
>>>> set of h/w buffers and copying each stage to one mmapped vmalloc
>>>> vb2_buffer
>>>> at two offsets. If we add MPLANE support, it can be two non-contiguous
>>>> buffer pointers.
>>>>
>>>>>>>> - The above decision (accomodate SDR & MPLANE) needs to be
>>>>>>>> propagated across the framework. Is this the preferred approach?
>>>>>>>>
>>>>>>>> It goes back to point (1). As of today, the change set for this
>>>>>>>> combo (DRIF+MAX2175) introduces new SDR formats only. Should it add
>>>>>>>> further SDR+MPLANE support to the framework as well?
>>>>>>>>
>>>>>>>> I would appreciate your suggestions on this regard.
>>
>> Some terminology first:
>>
>> Planar formats separate the data into different memory areas: in this case
>> one part is all I and one part is all Q. This as opposed to interleaved
>> formats (IQIQIQIQ....).
>>
>> As long as both planes fit in the same buffer all is fine. Since that is
>> the case here there is no need to introduce a new MPLANE API.
>>
>> The MPLANE API was added for video to handle cases where the two planes
>> had to be in two different non-contiguous buffers.
> 
> Not only that, it can also be used for cases where storing the two planes in 
> separate buffers can be beneficial, even if a single contiguous buffer could 
> work.
> 
>> So instead of passing one buffer pointer, you need to pass two or more
>> buffer pointers.
>>
>> In hindsight we should have called it the MBUFFER API.
> 
> The name was badly chosen, yes.
> 
>> Oh well...
>>
>> Anyway, since there is no problem here apparently to keep both planes
>> in one buffer there is also no need to introduce a SDR_MPLANE.
> 
> The question here is whether there could be a benefit in separating I and Q 
> data in two buffers compared to storing them in the same buffer.
> 

The MPLANE API is very messy and introducing something like SDR_MPLANE is not
something I would promote. If we want that, then we should first make a new
v4l2_buffer struct that simplifies MPLANE handling (we discussed that before).

Regards,

	Hans

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

* RE: [RFC 5/5] doc_rst: media: New SDR formats SC16, SC18 & SC20
  2016-11-11 14:00                         ` Hans Verkuil
@ 2016-11-14 15:53                               ` Ramesh Shanmugasundaram
  0 siblings, 0 replies; 39+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-11-14 15:53 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart
  Cc: Antti Palosaari, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, mchehab-DgEjT+Ai2ygdnm+yROfE0A,
	sakari.ailus-VuQAYsv1563Yd54FQh9/CA, Chris Paterson,
	geert-Td1EMuHUCqxL1ZNQvxDV9g, linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA

Hello Laurent, Antti, Hans,

> Subject: Re: [RFC 5/5] doc_rst: media: New SDR formats SC16, SC18 & SC20
> 
> On 11/11/2016 02:57 PM, Laurent Pinchart wrote:
> > Hi Hans,
> >
> > On Friday 11 Nov 2016 14:53:58 Hans Verkuil wrote:
> >> On 11/10/2016 09:08 AM, Laurent Pinchart wrote:
> >>> Antti, Hans, ping ? Please see below.
> >>>
> >>> On Friday 04 Nov 2016 09:23:29 Ramesh Shanmugasundaram wrote:
> >>>>> On 11/02/2016 10:58 PM, Laurent Pinchart wrote:
> >>>>>> On Wednesday 02 Nov 2016 09:00:00 Ramesh Shanmugasundaram wrote:
> >>>>>>>>> On Wednesday 12 Oct 2016 15:10:29 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
> >>>>>>>
> >>>>>>> [snip]
> >>>>>>>
> >>>>>>>>>> +
> >>>>>>>>>> +       -  start + 0:
> >>>>>>>>>> +
> >>>>>>>>>> +       -  I'\ :sub:`0[D13:D6]`
> >>>>>>>>>> +
> >>>>>>>>>> +       -  I'\ :sub:`0[D5:D0]`
> >>>>>>>>>> +
> >>>>>>>>>> +    -  .. row 2
> >>>>>>>>>> +
> >>>>>>>>>> +       -  start + buffer_size/2:
> >>>>>>>>>> +
> >>>>>>>>>> +       -  Q'\ :sub:`0[D13:D6]`
> >>>>>>>>>> +
> >>>>>>>>>> +       -  Q'\ :sub:`0[D5:D0]`
> >>>>>>>>>
> >>>>>>>>> The format looks planar, does it use one V4L2 plane (as does
> >>>>>>>>> NV12) or two V4L2 planes (as does NV12M) ? Same question for
> >>>>>>>>> the other formats.
> >>>>>>>>
> >>>>>>>> Thank you for bringing up this topic. This is one of the key
> >>>>>>>> design dilemma.
> >>>>>>>>
> >>>>>>>> The I & Q data for these three SDR formats comes from two
> >>>>>>>> different DMA channels and hence two separate pointers -> we
> >>>>>>>> could say it is
> >>>>>>>> v4l2 multi- planar. Right now, I am making it look like a
> >>>>>>>> single plane by presenting the data in one single buffer ptr.
> >>>>>>>>
> >>>>>>>> For e.g. multi-planar SC16 format would look something like
> >>>>>>>> this
> >>>>>>>>
> >>>>>>>> <------------------------32bits---------------------->
> >>>>>>>> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0
> >>>>>>>> + 0
> >>>>>>>> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0
> >>>>>>>> + 4 ...
> >>>>>>>> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1
> >>>>>>>> + 0
> >>>>>>>> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1
> >>>>>>>> + 4
> >>>>>>>>
> >>>>>>>> My concerns are
> >>>>>>>>
> >>>>>>>> 1) These formats are not a standard as the video "Image Formats".
> >>>>>>>> These formats are possible when we use DRIF + MAX2175
> combination.
> >>>>>>>> If we interface with a different tuner vendor, the above
> >>>>>>>> format(s) MAY/MAY NOT be re-usable. We do not know at this
> >>>>>>>> point. This is the main open item for discussion in the cover
> letter.
> >>>>>>
> >>>>>> If the formats are really device-specific then they should be
> >>>>>> documented accordingly and not made generic.
> >>>>>>
> >>>>>>>> 2) MPLANE support within V4L2 seems specific to video. Please
> >>>>>>>> correct me if this is wrong interpretation.
> >>>>>>>>
> >>>>>>>> - struct v4l2_format contains v4l2_sdr_format and
> >>>>>>>> v4l2_pix_format_mplane as members of union. Should I create a
> >>>>>>>> new v4l2_sdr_format_mplane? If I have to use
> >>>>>>>> v4l2_pix_format_mplane most of the video specific members would
> >>>>>>>> be unused (it would be similar to using v4l2_pix_format itself
> instead of v4l2_sdr_format)?
> >>>>>>
> >>>>>> I have no answer to that question as I'm not familiar with SDR.
> >>>>>> Antti, you've added v4l2_sdr_format to the API, what's your
> >>>>>> opinion ? Hans, as you've acked the patch, your input would be
> appreciated as well.
> >>>>>
> >>>>> If I understood correctly this hardware provides I and Q samples
> >>>>> via different channels and driver now combines those channels as a
> >>>>> sequential IQ sample pairs.
> >>>>
> >>>> The driver combines the two buffer ptrs and present as one single
> buffer.
> >>>> For a buffer of size 200
> >>>>
> >>>> ptr + 0   : I I I I ... I
> >>>> ptr + 100 : Q Q Q Q ... Q
> >>>>
> >>>>> I have never seen any other than hw which provides IQ IQ IQ IQ ...
> IQ.
> >>>>
> >>>> There are some modes where this h/w combo can also do IQ IQ IQ
> pattern.
> >>>> Those modes are not added in the RFC patchset.
> >>>>
> >>>>> This is
> >>>>> I I I I ... I
> >>>>> Q Q Q Q ... Q
> >>>>> I am not very familiar with planars, but it sounds like it is
> >>>>> correct approach. So I think should be added rather than emulate
> >>>>> packet sequential format.
> >>>>
> >>>> My understanding of V4L2 MPLANE constructs is limited to a quick
> >>>> code read only. At this point MPLANE support seems specific to
> >>>> video. SDR is defined as separate format like v4l2_pix_format.
> >>>> Questions would be - should we define new SDR_MPLANE? or merge SDR
> >>>> format with pix format & reuse existing MPLANE with some SDR
> >>>> extensions (if possible)? These seem big design decisions. Any
> >>>> suggestions please?
> >>>>
> >>>> For my use case, MPLANE support does not seem to add significant
> >>>> benefit except it may be syntactically correct. I am doing cyclic
> >>>> DMA with a small set of h/w buffers and copying each stage to one
> >>>> mmapped vmalloc vb2_buffer at two offsets. If we add MPLANE
> >>>> support, it can be two non-contiguous buffer pointers.
> >>>>
> >>>>>>>> - The above decision (accomodate SDR & MPLANE) needs to be
> >>>>>>>> propagated across the framework. Is this the preferred approach?
> >>>>>>>>
> >>>>>>>> It goes back to point (1). As of today, the change set for this
> >>>>>>>> combo (DRIF+MAX2175) introduces new SDR formats only. Should it
> >>>>>>>> add further SDR+MPLANE support to the framework as well?
> >>>>>>>>
> >>>>>>>> I would appreciate your suggestions on this regard.
> >>
> >> Some terminology first:
> >>
> >> Planar formats separate the data into different memory areas: in this
> >> case one part is all I and one part is all Q. This as opposed to
> >> interleaved formats (IQIQIQIQ....).
> >>
> >> As long as both planes fit in the same buffer all is fine. Since that
> >> is the case here there is no need to introduce a new MPLANE API.
> >>
> >> The MPLANE API was added for video to handle cases where the two
> >> planes had to be in two different non-contiguous buffers.
> >
> > Not only that, it can also be used for cases where storing the two
> > planes in separate buffers can be beneficial, even if a single
> > contiguous buffer could work.
> >
> >> So instead of passing one buffer pointer, you need to pass two or
> >> more buffer pointers.
> >>
> >> In hindsight we should have called it the MBUFFER API.
> >
> > The name was badly chosen, yes.
> >
> >> Oh well...
> >>
> >> Anyway, since there is no problem here apparently to keep both planes
> >> in one buffer there is also no need to introduce a SDR_MPLANE.
> >
> > The question here is whether there could be a benefit in separating I
> > and Q data in two buffers compared to storing them in the same buffer.
> >
> 
> The MPLANE API is very messy and introducing something like SDR_MPLANE is
> not something I would promote. If we want that, then we should first make
> a new v4l2_buffer struct that simplifies MPLANE handling (we discussed
> that before).

Thank you for the comments and closure on this topic.

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] 39+ messages in thread

* RE: [RFC 5/5] doc_rst: media: New SDR formats SC16, SC18 & SC20
@ 2016-11-14 15:53                               ` Ramesh Shanmugasundaram
  0 siblings, 0 replies; 39+ messages in thread
From: Ramesh Shanmugasundaram @ 2016-11-14 15:53 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart
  Cc: Antti Palosaari, robh+dt, mark.rutland, mchehab, sakari.ailus,
	Chris Paterson, geert, linux-media, devicetree,
	linux-renesas-soc

Hello Laurent, Antti, Hans,

> Subject: Re: [RFC 5/5] doc_rst: media: New SDR formats SC16, SC18 & SC20
> 
> On 11/11/2016 02:57 PM, Laurent Pinchart wrote:
> > Hi Hans,
> >
> > On Friday 11 Nov 2016 14:53:58 Hans Verkuil wrote:
> >> On 11/10/2016 09:08 AM, Laurent Pinchart wrote:
> >>> Antti, Hans, ping ? Please see below.
> >>>
> >>> On Friday 04 Nov 2016 09:23:29 Ramesh Shanmugasundaram wrote:
> >>>>> On 11/02/2016 10:58 PM, Laurent Pinchart wrote:
> >>>>>> On Wednesday 02 Nov 2016 09:00:00 Ramesh Shanmugasundaram wrote:
> >>>>>>>>> On Wednesday 12 Oct 2016 15:10:29 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
> >>>>>>>
> >>>>>>> [snip]
> >>>>>>>
> >>>>>>>>>> +
> >>>>>>>>>> +       -  start + 0:
> >>>>>>>>>> +
> >>>>>>>>>> +       -  I'\ :sub:`0[D13:D6]`
> >>>>>>>>>> +
> >>>>>>>>>> +       -  I'\ :sub:`0[D5:D0]`
> >>>>>>>>>> +
> >>>>>>>>>> +    -  .. row 2
> >>>>>>>>>> +
> >>>>>>>>>> +       -  start + buffer_size/2:
> >>>>>>>>>> +
> >>>>>>>>>> +       -  Q'\ :sub:`0[D13:D6]`
> >>>>>>>>>> +
> >>>>>>>>>> +       -  Q'\ :sub:`0[D5:D0]`
> >>>>>>>>>
> >>>>>>>>> The format looks planar, does it use one V4L2 plane (as does
> >>>>>>>>> NV12) or two V4L2 planes (as does NV12M) ? Same question for
> >>>>>>>>> the other formats.
> >>>>>>>>
> >>>>>>>> Thank you for bringing up this topic. This is one of the key
> >>>>>>>> design dilemma.
> >>>>>>>>
> >>>>>>>> The I & Q data for these three SDR formats comes from two
> >>>>>>>> different DMA channels and hence two separate pointers -> we
> >>>>>>>> could say it is
> >>>>>>>> v4l2 multi- planar. Right now, I am making it look like a
> >>>>>>>> single plane by presenting the data in one single buffer ptr.
> >>>>>>>>
> >>>>>>>> For e.g. multi-planar SC16 format would look something like
> >>>>>>>> this
> >>>>>>>>
> >>>>>>>> <------------------------32bits---------------------->
> >>>>>>>> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0
> >>>>>>>> + 0
> >>>>>>>> <--I(14 bit data) + 2bit status--16bit padded zeros--> : start0
> >>>>>>>> + 4 ...
> >>>>>>>> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1
> >>>>>>>> + 0
> >>>>>>>> <--Q(14 bit data) + 2bit status--16bit padded zeros--> : start1
> >>>>>>>> + 4
> >>>>>>>>
> >>>>>>>> My concerns are
> >>>>>>>>
> >>>>>>>> 1) These formats are not a standard as the video "Image Formats".
> >>>>>>>> These formats are possible when we use DRIF + MAX2175
> combination.
> >>>>>>>> If we interface with a different tuner vendor, the above
> >>>>>>>> format(s) MAY/MAY NOT be re-usable. We do not know at this
> >>>>>>>> point. This is the main open item for discussion in the cover
> letter.
> >>>>>>
> >>>>>> If the formats are really device-specific then they should be
> >>>>>> documented accordingly and not made generic.
> >>>>>>
> >>>>>>>> 2) MPLANE support within V4L2 seems specific to video. Please
> >>>>>>>> correct me if this is wrong interpretation.
> >>>>>>>>
> >>>>>>>> - struct v4l2_format contains v4l2_sdr_format and
> >>>>>>>> v4l2_pix_format_mplane as members of union. Should I create a
> >>>>>>>> new v4l2_sdr_format_mplane? If I have to use
> >>>>>>>> v4l2_pix_format_mplane most of the video specific members would
> >>>>>>>> be unused (it would be similar to using v4l2_pix_format itself
> instead of v4l2_sdr_format)?
> >>>>>>
> >>>>>> I have no answer to that question as I'm not familiar with SDR.
> >>>>>> Antti, you've added v4l2_sdr_format to the API, what's your
> >>>>>> opinion ? Hans, as you've acked the patch, your input would be
> appreciated as well.
> >>>>>
> >>>>> If I understood correctly this hardware provides I and Q samples
> >>>>> via different channels and driver now combines those channels as a
> >>>>> sequential IQ sample pairs.
> >>>>
> >>>> The driver combines the two buffer ptrs and present as one single
> buffer.
> >>>> For a buffer of size 200
> >>>>
> >>>> ptr + 0   : I I I I ... I
> >>>> ptr + 100 : Q Q Q Q ... Q
> >>>>
> >>>>> I have never seen any other than hw which provides IQ IQ IQ IQ ...
> IQ.
> >>>>
> >>>> There are some modes where this h/w combo can also do IQ IQ IQ
> pattern.
> >>>> Those modes are not added in the RFC patchset.
> >>>>
> >>>>> This is
> >>>>> I I I I ... I
> >>>>> Q Q Q Q ... Q
> >>>>> I am not very familiar with planars, but it sounds like it is
> >>>>> correct approach. So I think should be added rather than emulate
> >>>>> packet sequential format.
> >>>>
> >>>> My understanding of V4L2 MPLANE constructs is limited to a quick
> >>>> code read only. At this point MPLANE support seems specific to
> >>>> video. SDR is defined as separate format like v4l2_pix_format.
> >>>> Questions would be - should we define new SDR_MPLANE? or merge SDR
> >>>> format with pix format & reuse existing MPLANE with some SDR
> >>>> extensions (if possible)? These seem big design decisions. Any
> >>>> suggestions please?
> >>>>
> >>>> For my use case, MPLANE support does not seem to add significant
> >>>> benefit except it may be syntactically correct. I am doing cyclic
> >>>> DMA with a small set of h/w buffers and copying each stage to one
> >>>> mmapped vmalloc vb2_buffer at two offsets. If we add MPLANE
> >>>> support, it can be two non-contiguous buffer pointers.
> >>>>
> >>>>>>>> - The above decision (accomodate SDR & MPLANE) needs to be
> >>>>>>>> propagated across the framework. Is this the preferred approach?
> >>>>>>>>
> >>>>>>>> It goes back to point (1). As of today, the change set for this
> >>>>>>>> combo (DRIF+MAX2175) introduces new SDR formats only. Should it
> >>>>>>>> add further SDR+MPLANE support to the framework as well?
> >>>>>>>>
> >>>>>>>> I would appreciate your suggestions on this regard.
> >>
> >> Some terminology first:
> >>
> >> Planar formats separate the data into different memory areas: in this
> >> case one part is all I and one part is all Q. This as opposed to
> >> interleaved formats (IQIQIQIQ....).
> >>
> >> As long as both planes fit in the same buffer all is fine. Since that
> >> is the case here there is no need to introduce a new MPLANE API.
> >>
> >> The MPLANE API was added for video to handle cases where the two
> >> planes had to be in two different non-contiguous buffers.
> >
> > Not only that, it can also be used for cases where storing the two
> > planes in separate buffers can be beneficial, even if a single
> > contiguous buffer could work.
> >
> >> So instead of passing one buffer pointer, you need to pass two or
> >> more buffer pointers.
> >>
> >> In hindsight we should have called it the MBUFFER API.
> >
> > The name was badly chosen, yes.
> >
> >> Oh well...
> >>
> >> Anyway, since there is no problem here apparently to keep both planes
> >> in one buffer there is also no need to introduce a SDR_MPLANE.
> >
> > The question here is whether there could be a benefit in separating I
> > and Q data in two buffers compared to storing them in the same buffer.
> >
> 
> The MPLANE API is very messy and introducing something like SDR_MPLANE is
> not something I would promote. If we want that, then we should first make
> a new v4l2_buffer struct that simplifies MPLANE handling (we discussed
> that before).

Thank you for the comments and closure on this topic.

Thanks,
Ramesh

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

end of thread, other threads:[~2016-11-14 15:53 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-10-12 14:10 [RFC 0/5] Add V4L2 SDR (DRIF & MAX2175) driver Ramesh Shanmugasundaram
2016-10-12 14:10 ` [RFC 1/5] media: i2c: max2175: Add MAX2175 support Ramesh Shanmugasundaram
     [not found]   ` <1476281429-27603-2-git-send-email-ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>
2016-10-15 12:42     ` Geert Uytterhoeven
2016-10-15 12:42       ` Geert Uytterhoeven
     [not found]       ` <CAMuHMdUYQoJL4h8prEpontF4YH8Ha+SWDdeZHYEV3_uMZ-SBXw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2016-10-18 15:04         ` Ramesh Shanmugasundaram
2016-10-18 15:04           ` Ramesh Shanmugasundaram
2016-10-18 19:25   ` Laurent Pinchart
2016-10-21 14:49     ` Ramesh Shanmugasundaram
2016-11-10  8:46       ` Laurent Pinchart
2016-10-12 14:10 ` [RFC 2/5] media: v4l2-ctrls: Reserve controls for MAX217X Ramesh Shanmugasundaram
2016-10-12 14:10 ` [RFC 3/5] media: platform: rcar_drif: Add DRIF support Ramesh Shanmugasundaram
2016-10-18 13:13   ` Rob Herring
2016-10-18 15:13     ` Ramesh Shanmugasundaram
2016-10-18 14:29   ` Geert Uytterhoeven
2016-10-18 18:26     ` Laurent Pinchart
2016-10-21 13:17       ` Ramesh Shanmugasundaram
2016-10-21 13:17         ` Ramesh Shanmugasundaram
     [not found]     ` <CAMuHMdXvGEm3bdNOsa6Q1FLB9yMSTAzO4nHcCb-pnYYwg6f6Cg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2016-10-21 13:15       ` Ramesh Shanmugasundaram
2016-10-21 13:15         ` Ramesh Shanmugasundaram
2016-10-12 14:10 ` [RFC 4/5] media: Add new SDR formats SC16, SC18 & SC20 Ramesh Shanmugasundaram
2016-10-12 14:10 ` [RFC 5/5] doc_rst: media: New " Ramesh Shanmugasundaram
     [not found]   ` <1476281429-27603-6-git-send-email-ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>
2016-10-18 18:35     ` Laurent Pinchart
2016-10-18 18:35       ` Laurent Pinchart
2016-10-24 10:19       ` Ramesh Shanmugasundaram
2016-11-02  9:00       ` Ramesh Shanmugasundaram
2016-11-02  9:00         ` Ramesh Shanmugasundaram
     [not found]         ` <SG2PR06MB10389152CEC59BB77A5DA7DDC3A00-ESzmfEwOt/zfc7TNChRnj20DtJ1/0DrXvxpqHgZTriW3zl9H0oFU5g@public.gmane.org>
2016-11-02 20:58           ` Laurent Pinchart
2016-11-02 20:58             ` Laurent Pinchart
2016-11-03 20:36             ` Antti Palosaari
2016-11-03 20:36               ` Antti Palosaari
2016-11-04  9:23               ` Ramesh Shanmugasundaram
2016-11-10  8:08                 ` Laurent Pinchart
2016-11-11  4:54                   ` Antti Palosaari
2016-11-11 13:53                   ` Hans Verkuil
     [not found]                     ` <8438b944-216e-3237-c312-92a674fd4541-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
2016-11-11 13:57                       ` Laurent Pinchart
2016-11-11 13:57                         ` Laurent Pinchart
2016-11-11 14:00                         ` Hans Verkuil
     [not found]                           ` <fb15b6f3-6c5c-0922-8655-aabd4799d158-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
2016-11-14 15:53                             ` Ramesh Shanmugasundaram
2016-11-14 15:53                               ` 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.